aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2023-06-09 18:22:32 -0700
committerGravatar GitHub <noreply@github.com> 2023-06-09 18:22:32 -0700
commit0f018ea2159f7bad499d8a2f50837a4d888b2344 (patch)
tree4393d986da63ff9d8d54b9e996e4a72eeea23290
parenta8dc41cd9f411c97d6936e7d0169324df2bb585f (diff)
downloadbun-0f018ea2159f7bad499d8a2f50837a4d888b2344.tar.gz
bun-0f018ea2159f7bad499d8a2f50837a4d888b2344.tar.zst
bun-0f018ea2159f7bad499d8a2f50837a4d888b2344.zip
Fixes #3206 (#3262)
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
-rw-r--r--src/js_parser.zig122
-rw-r--r--test/bundler/bundler_edgecase.test.ts3
2 files changed, 78 insertions, 47 deletions
diff --git a/src/js_parser.zig b/src/js_parser.zig
index 5dc69f469..b844aa9b4 100644
--- a/src/js_parser.zig
+++ b/src/js_parser.zig
@@ -3057,6 +3057,10 @@ pub const Parser = struct {
var did_import_fast_refresh = false;
_ = did_import_fast_refresh;
+ // This is a workaround for broken module environment checks in packages like lodash-es
+ // https://github.com/lodash/lodash/issues/5660
+ var force_esm = false;
+
if (comptime FeatureFlags.unwrap_commonjs_to_esm) {
if (p.imports_to_convert_from_require.items.len > 0) {
var all_stmts = p.allocator.alloc(Stmt, p.imports_to_convert_from_require.items.len) catch unreachable;
@@ -3094,49 +3098,69 @@ pub const Parser = struct {
var export_names = p.commonjs_named_exports.keys();
if (!p.commonjs_named_exports_deoptimized) {
- // We make this safe by doing toCommonJS() at runtime
- for (export_refs, export_names) |*export_ref, alias| {
- if (export_ref.needs_decl) {
- var this_stmts = p.allocator.alloc(Stmt, 2) catch unreachable;
- var decls = p.allocator.alloc(Decl, 1) catch unreachable;
- const ref = export_ref.loc_ref.ref.?;
- decls[0] = .{
- .binding = p.b(B.Identifier{ .ref = ref }, export_ref.loc_ref.loc),
- .value = null,
- };
- var declared_symbols = DeclaredSymbol.List.initCapacity(p.allocator, 1) catch unreachable;
- declared_symbols.appendAssumeCapacity(.{ .ref = ref, .is_top_level = true });
- this_stmts[0] = p.s(
- S.Local{
- .kind = .k_var,
- .is_export = false,
- .was_commonjs_export = true,
- .decls = Decl.List.init(decls),
- },
- export_ref.loc_ref.loc,
- );
- p.module_scope.generated.push(p.allocator, ref) catch unreachable;
- var clause_items = p.allocator.alloc(js_ast.ClauseItem, 1) catch unreachable;
- clause_items[0] = js_ast.ClauseItem{
- .alias = alias,
- .alias_loc = export_ref.loc_ref.loc,
- .name = export_ref.loc_ref,
- };
- this_stmts[1] = p.s(
- S.ExportClause{
- .items = clause_items,
- .is_single_line = true,
- },
- export_ref.loc_ref.loc,
- );
- export_ref.needs_decl = false;
- before.append(.{
- .stmts = this_stmts,
- .declared_symbols = declared_symbols,
- .tag = .commonjs_named_export,
- .can_be_removed_if_unused = p.stmtsCanBeRemovedIfUnused(this_stmts),
- }) catch unreachable;
+ // This is a workaround for packages which have broken ESM checks
+ // If they never actually assign to exports.foo, only check for it
+ // and the package specifies type "module"
+ // and the package uses ESM syntax
+ // We should just say
+ // You're ESM and lying about it.
+ if (p.options.module_type == .esm and p.has_es_module_syntax) {
+ var needs_decl_count: usize = 0;
+ for (export_refs) |*export_ref| {
+ needs_decl_count += @as(usize, @boolToInt(export_ref.needs_decl));
+ }
+
+ if (needs_decl_count == export_names.len) {
+ force_esm = true;
+ }
+ }
+
+ if (!force_esm) {
+ // We make this safe by doing toCommonJS() at runtime
+ for (export_refs, export_names) |*export_ref, alias| {
+ if (export_ref.needs_decl) {
+ var this_stmts = p.allocator.alloc(Stmt, 2) catch unreachable;
+ var decls = p.allocator.alloc(Decl, 1) catch unreachable;
+ const ref = export_ref.loc_ref.ref.?;
+ decls[0] = .{
+ .binding = p.b(B.Identifier{ .ref = ref }, export_ref.loc_ref.loc),
+ .value = null,
+ };
+ var declared_symbols = DeclaredSymbol.List.initCapacity(p.allocator, 1) catch unreachable;
+ declared_symbols.appendAssumeCapacity(.{ .ref = ref, .is_top_level = true });
+ this_stmts[0] = p.s(
+ S.Local{
+ .kind = .k_var,
+ .is_export = false,
+ .was_commonjs_export = true,
+ .decls = Decl.List.init(decls),
+ },
+ export_ref.loc_ref.loc,
+ );
+ p.module_scope.generated.push(p.allocator, ref) catch unreachable;
+ var clause_items = p.allocator.alloc(js_ast.ClauseItem, 1) catch unreachable;
+ clause_items[0] = js_ast.ClauseItem{
+ .alias = alias,
+ .alias_loc = export_ref.loc_ref.loc,
+ .name = export_ref.loc_ref,
+ };
+
+ this_stmts[1] = p.s(
+ S.ExportClause{
+ .items = clause_items,
+ .is_single_line = true,
+ },
+ export_ref.loc_ref.loc,
+ );
+ export_ref.needs_decl = false;
+ before.append(.{
+ .stmts = this_stmts,
+ .declared_symbols = declared_symbols,
+ .tag = .commonjs_named_export,
+ .can_be_removed_if_unused = p.stmtsCanBeRemovedIfUnused(this_stmts),
+ }) catch unreachable;
+ }
}
}
}
@@ -3345,7 +3369,8 @@ pub const Parser = struct {
var exports_kind = js_ast.ExportsKind.none;
const exports_ref_usage_count = p.symbols.items[p.exports_ref.innerIndex()].use_count_estimate;
const uses_exports_ref = exports_ref_usage_count > 0;
- if (uses_exports_ref and p.commonjs_named_exports.count() > 0) {
+
+ if (uses_exports_ref and p.commonjs_named_exports.count() > 0 and !force_esm) {
p.deoptimizeCommonJSNamedExports();
}
@@ -3401,7 +3426,7 @@ pub const Parser = struct {
}
}
- if (exports_kind == .esm and p.commonjs_named_exports.count() > 0 and !p.unwrap_all_requires) {
+ if (exports_kind == .esm and p.commonjs_named_exports.count() > 0 and !p.unwrap_all_requires and !force_esm) {
exports_kind = .esm_with_dynamic_fallback_from_cjs;
}
@@ -4757,6 +4782,7 @@ fn NewParser_(
/// Used for transforming export default -> module.exports
has_export_default: bool = false,
+ has_export_keyword: bool = false,
is_file_considered_to_have_esm_exports: bool = false,
@@ -6409,7 +6435,7 @@ fn NewParser_(
try p.pushScopeForVisitPass(js_ast.Scope.Kind.entry, locModuleScope);
p.fn_or_arrow_data_visit.is_outside_fn_or_arrow = true;
p.module_scope = p.current_scope;
- p.has_es_module_syntax = p.esm_import_keyword.len > 0 or p.esm_export_keyword.len > 0 or p.top_level_await_keyword.len > 0;
+ p.has_es_module_syntax = p.has_es_module_syntax or p.esm_import_keyword.len > 0 or p.esm_export_keyword.len > 0 or p.top_level_await_keyword.len > 0;
if (p.lexer.jsx_pragma.jsx()) |factory| {
p.options.jsx.factory = options.JSX.Pragma.memberListToComponentsIfDifferent(p.allocator, p.options.jsx.factory, factory.text) catch unreachable;
@@ -8786,6 +8812,7 @@ fn NewParser_(
break :default_name_getter createDefaultName(p, defaultLoc) catch unreachable;
};
p.has_export_default = true;
+ p.has_es_module_syntax = true;
return p.s(
S.ExportDefault{ .default_name = default_name, .value = js_ast.StmtOrExpr{ .stmt = stmt } },
loc,
@@ -8898,6 +8925,7 @@ fn NewParser_(
}
try p.lexer.expectOrInsertSemicolon();
+ p.has_es_module_syntax = true;
return p.s(S.ExportStar{
.namespace_ref = namespace_ref,
.alias = alias,
@@ -8948,7 +8976,7 @@ fn NewParser_(
p.import_records.items[import_record_index].calls_runtime_re_export_fn = true;
}
p.current_scope.is_after_const_local_prefix = true;
-
+ p.has_es_module_syntax = true;
return p.s(
S.ExportFrom{
.items = export_clause.clauses,
@@ -8970,7 +8998,7 @@ fn NewParser_(
return p.s(S.TypeScript{}, loc);
}
}
-
+ p.has_es_module_syntax = true;
return p.s(S.ExportClause{ .items = export_clause.clauses, .is_single_line = export_clause.is_single_line }, loc);
},
T.t_equals => {
diff --git a/test/bundler/bundler_edgecase.test.ts b/test/bundler/bundler_edgecase.test.ts
index 9d15cd031..1b9b60a68 100644
--- a/test/bundler/bundler_edgecase.test.ts
+++ b/test/bundler/bundler_edgecase.test.ts
@@ -919,6 +919,9 @@ describe("bundler", () => {
if(isBuffer !== 1) throw 'fail';
console.log('pass');
`,
+ "/node_modules/lodash-es/package.json": /* json */ `
+ { "name": "lodash-es", "type": "module"}
+ `,
"/node_modules/lodash-es/isBuffer.js": /* js */ `
var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;