diff options
author | 2021-12-15 15:16:53 -0800 | |
---|---|---|
committer | 2021-12-15 15:16:53 -0800 | |
commit | 62fb5ea9e396c5296f845d32f1aae5629fbf5836 (patch) | |
tree | b65c610b98ab6301a96b6180b064a89fcb49f4cc | |
parent | a6ec858750a8a4c19b0bc94ecec00955f8b523c4 (diff) | |
download | bun-jarred/live-bindings.tar.gz bun-jarred/live-bindings.tar.zst bun-jarred/live-bindings.zip |
wip fix live bindingsjarred/live-bindings
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | src/fallback.version | 2 | ||||
-rw-r--r-- | src/import_record.zig | 57 | ||||
-rw-r--r-- | src/js_ast.zig | 3 | ||||
-rw-r--r-- | src/js_parser/js_parser.zig | 309 | ||||
-rw-r--r-- | src/js_printer.zig | 404 |
6 files changed, 359 insertions, 418 deletions
@@ -438,6 +438,8 @@ s2n-mac: CC=$(CC) CXX=$(CXX) cmake --build ./build -j$(CPUS); \ CC=$(CC) CXX=$(CXX) CTEST_PARALLEL_LEVEL=$(CPUS) ninja -C build cp $(DEPS_DIR)/s2n-tls/build/lib/libs2n.a $(DEPS_DIR)/libs2n.a + +libcrypto-old: unlink $(DEPS_DIR)/libcrypto.a || echo ""; ln $(LIBCRYPTO_STATIC_LIB) $(DEPS_DIR)/libcrypto.a || echo ""; diff --git a/src/fallback.version b/src/fallback.version index 74aeaab27..23bd52db7 100644 --- a/src/fallback.version +++ b/src/fallback.version @@ -1 +1 @@ -2bbe5942da63d2ba
\ No newline at end of file +796022f759787f0a
\ No newline at end of file diff --git a/src/import_record.zig b/src/import_record.zig index aa82e4731..4d4199004 100644 --- a/src/import_record.zig +++ b/src/import_record.zig @@ -44,61 +44,64 @@ pub const ImportRecord = struct { range: logger.Range, path: fs.Path, - // 0 is invalid + /// 0 is invalid module_id: u32 = 0, source_index: Ref.Int = std.math.maxInt(Ref.Int), print_mode: PrintMode = .normal, - // True for the following cases: - // - // try { require('x') } catch { handle } - // try { await import('x') } catch { handle } - // try { require.resolve('x') } catch { handle } - // import('x').catch(handle) - // import('x').then(_, handle) - // - // In these cases we shouldn't generate an error if the path could not be - // resolved. + /// True for the following cases: + /// + /// try { require('x') } catch { handle } + /// try { await import('x') } catch { handle } + /// try { require.resolve('x') } catch { handle } + /// import('x').catch(handle) + /// import('x').then(_, handle) + /// + /// In these cases we shouldn't generate an error if the path could not be + /// resolved. handles_import_errors: bool = false, is_internal: bool = false, - // This tells the printer that we should print as export var $moduleID = ... - // Instead of using the path. + /// This tells the printer that we should print as export var $moduleID = ... + /// Instead of using the path. is_bundled: bool = false, - // Sometimes the parser creates an import record and decides it isn't needed. - // For example, TypeScript code may have import statements that later turn - // out to be type-only imports after analyzing the whole file. + /// Sometimes the parser creates an import record and decides it isn't needed. + /// For example, TypeScript code may have import statements that later turn + /// out to be type-only imports after analyzing the whole file. is_unused: bool = false, - // If this is true, the import contains syntax like "* as ns". This is used - // to determine whether modules that have no exports need to be wrapped in a - // CommonJS wrapper or not. + /// If this is true, the import contains syntax like "* as ns". This is used + /// to determine whether modules that have no exports need to be wrapped in a + /// CommonJS wrapper or not. contains_import_star: bool = false, - // If this is true, the import contains an import for the alias "default", - // either via the "import x from" or "import {default as x} from" syntax. + /// If this is true, the import contains an import for the alias "default", + /// either via the "import x from" or "import {default as x} from" syntax. contains_default_alias: bool = false, - // If true, this "export * from 'path'" statement is evaluated at run-time by - // calling the "__reExport()" helper function + /// If true, this "export * from 'path'" statement is evaluated at run-time by + /// calling the "__reExport()" helper function calls_run_time_re_export_fn: bool = false, - // Tell the printer to wrap this call to "require()" in "__toModule(...)" + /// Tell the printer to wrap this call to "require()" in "__toModule(...)" wrap_with_to_module: bool = false, - // True for require calls like this: "try { require() } catch {}". In this - // case we shouldn't generate an error if the path could not be resolved. + /// True for require calls like this: "try { require() } catch {}". In this + /// case we shouldn't generate an error if the path could not be resolved. is_inside_try_body: bool = false, - // If true, this was originally written as a bare "import 'file'" statement + /// If true, this was originally written as a bare "import 'file'" statement was_originally_bare_import: bool = false, was_originally_require: bool = false, + /// If a macro used <import>, it will be tracked here. + was_injected_by_macro: bool = false, + kind: ImportKind, pub const PrintMode = enum { diff --git a/src/js_ast.zig b/src/js_ast.zig index e926ae3b7..73ddac0f4 100644 --- a/src/js_ast.zig +++ b/src/js_ast.zig @@ -417,6 +417,7 @@ pub const G = struct { pub const NamespaceAlias = struct { namespace_ref: Ref, alias: string, + import_record_index: u32 = std.math.maxInt(u32), }; pub const ExportStarAlias = struct { @@ -974,6 +975,8 @@ pub const E = struct { // false, this could potentially have been a member access expression such // as "ns.foo" off of an imported namespace object. was_originally_identifier: bool = false, + + was_from_macro: bool = false, }; // This is similar to EIdentifier but it represents class-private fields and diff --git a/src/js_parser/js_parser.zig b/src/js_parser/js_parser.zig index 38ce0f270..4d10c0b5e 100644 --- a/src/js_parser/js_parser.zig +++ b/src/js_parser/js_parser.zig @@ -20,6 +20,8 @@ const ScopeOrderList = std.ArrayListUnmanaged(?ScopeOrder); const JSXFactoryName = "JSX"; const JSXAutomaticName = "jsx_module"; +// kept as a static reference +const exports_string_name: string = "exports"; const MacroRefs = std.AutoArrayHashMap(Ref, u32); // If we are currently in a hoisted child of the module scope, relocate these @@ -437,124 +439,77 @@ pub const ImportScanner = struct { } } - if (p.options.trim_unused_imports) { - if (st.star_name_loc != null or did_remove_star_loc) { - // -- Original Comment -- - // If we're bundling a star import and the namespace is only ever - // used for property accesses, then convert each unique property to - // a clause item in the import statement and remove the star import. - // That will cause the bundler to bundle them more efficiently when - // both this module and the imported module are in the same group. - // - // Before: - // - // import * as ns from 'foo' - // console.log(ns.a, ns.b) - // - // After: - // - // import {a, b} from 'foo' - // console.log(a, b) - // - // This is not done if the namespace itself is used, because in that - // case the code for the namespace will have to be generated. This is - // determined by the symbol count because the parser only counts the - // star import as used if it was used for something other than a - // property access: - // - // import * as ns from 'foo' - // console.log(ns, ns.a, ns.b) - // - // -- Original Comment -- - - // jarred: we don't use the same grouping mechanism as esbuild - // but, we do this anyway. - // The reasons why are: - // * It makes static analysis for other tools simpler. - // * I imagine browsers may someday do some optimizations - // when it's "easier" to know only certain modules are used - // For example, if you're importing a component from a design system - // it's really stupid to import all 1,000 components from that design system - // when you just want <Button /> - const namespace_ref = st.namespace_ref; - const convert_star_to_clause = !p.options.enable_bundling and !p.options.can_import_from_bundle and p.symbols.items[namespace_ref.inner_index].use_count_estimate == 0; - - if (convert_star_to_clause and !keep_unused_imports) { - st.star_name_loc = null; - } - - // "import_items_for_namespace" has property accesses off the namespace - if (p.import_items_for_namespace.getPtr(namespace_ref)) |import_items| { - var count = import_items.count(); - if (count > 0) { - // Sort keys for determinism - var sorted: []string = try p.allocator.alloc(string, count); - var iter = import_items.iterator(); - var i: usize = 0; - while (iter.next()) |item| { - sorted[i] = item.key_ptr.*; - i += 1; - } - strings.sortAsc(sorted); - - defer p.allocator.free(sorted); - if (convert_star_to_clause) { - // Create an import clause for these items. Named imports will be - // automatically created later on since there is now a clause. - var items = try p.allocator.alloc(js_ast.ClauseItem, count); - try p.declared_symbols.ensureUnusedCapacity(count); - i = 0; - for (sorted) |alias| { - const name: LocRef = import_items.get(alias) orelse unreachable; - const original_name = p.symbols.items[name.ref.?.inner_index].original_name; - items[i] = js_ast.ClauseItem{ - .alias = alias, - .alias_loc = name.loc, - .name = name, - .original_name = original_name, - }; - p.declared_symbols.appendAssumeCapacity(js_ast.DeclaredSymbol{ - .ref = name.ref.?, - .is_top_level = true, - }); + if (st.star_name_loc != null or did_remove_star_loc) { + // -- Original Comment -- + // If we're bundling a star import and the namespace is only ever + // used for property accesses, then convert each unique property to + // a clause item in the import statement and remove the star import. + // That will cause the bundler to bundle them more efficiently when + // both this module and the imported module are in the same group. + // + // Before: + // + // import * as ns from 'foo' + // console.log(ns.a, ns.b) + // + // After: + // + // import {a, b} from 'foo' + // console.log(a, b) + // + // This is not done if the namespace itself is used, because in that + // case the code for the namespace will have to be generated. This is + // determined by the symbol count because the parser only counts the + // star import as used if it was used for something other than a + // property access: + // + // import * as ns from 'foo' + // console.log(ns, ns.a, ns.b) + // + // -- Original Comment -- - i += 1; - } + // jarred: we don't use the same grouping mechanism as esbuild + // but, we do this anyway. + // The reasons why are: + // * It makes static analysis for other tools simpler. + // * I imagine browsers may someday do some optimizations + // when it's "easier" to know only certain modules are used + // For example, if you're importing a component from a design system + // it's really stupid to import all 1,000 components from that design system + // when you just want <Button /> + const namespace_ref = st.namespace_ref; + const convert_star_to_clause = !p.options.enable_bundling and !p.options.can_import_from_bundle and p.symbols.items[namespace_ref.inner_index].use_count_estimate == 0; - if (st.items.len > 0) { - p.panic("The syntax \"import {{x}}, * as y from 'path'\" isn't valid", .{}); - } + if (convert_star_to_clause and !keep_unused_imports) { + st.star_name_loc = null; + } - st.items = items; - } else { - // If we aren't converting this star import to a clause, still - // create named imports for these property accesses. This will - // cause missing imports to generate useful warnings. - // - // It will also improve bundling efficiency for internal imports - // by still converting property accesses off the namespace into - // bare identifiers even if the namespace is still needed. - - for (sorted) |alias| { - const name: LocRef = import_items.get(alias) orelse unreachable; - const name_ref = name.ref.?; - - try p.named_imports.put(name_ref, js_ast.NamedImport{ - .alias = alias, - .alias_loc = name.loc, - .namespace_ref = st.namespace_ref, - .import_record_index = st.import_record_index, - }); - - record.contains_default_alias = record.contains_default_alias or strings.eqlComptime(alias, "default"); - - // Make sure the printer prints this as a property access - var symbol: *Symbol = &p.symbols.items[name_ref.inner_index]; - symbol.namespace_alias = G.NamespaceAlias{ .namespace_ref = st.namespace_ref, .alias = alias }; - } - } - } - } + const default_name: string = if (st.default_name != null) + p.loadNameFromRef(st.default_name.?.ref.?) + else + ""; + + for (st.items) |item| { + const is_default = strings.eqlComptime(item.alias, "default"); + record.contains_default_alias = record.contains_default_alias or is_default; + + const name: LocRef = item.name; + const name_ref = name.ref.?; + + try p.named_imports.put(name_ref, js_ast.NamedImport{ + .alias = item.alias, + .alias_loc = name.loc, + .namespace_ref = st.namespace_ref, + .import_record_index = st.import_record_index, + }); + + // Make sure the printer prints this as a property access + var symbol: *Symbol = &p.symbols.items[name_ref.inner_index]; + symbol.namespace_alias = G.NamespaceAlias{ + .namespace_ref = st.namespace_ref, + .alias = item.original_name, + .import_record_index = st.import_record_index, + }; } } @@ -2002,11 +1957,8 @@ pub const Parser = struct { // The problem with our scan pass approach is type-only imports. // We don't have accurate symbol counts. // So we don't have a good way to distuingish between a type-only import and not. - switch (comptime ParserType) { - TSXImportScanner, TypeScriptImportScanner => { - p.parse_pass_symbol_uses = &scan_pass.used_symbols; - }, - else => {}, + if (comptime ParserType.parser_features.typescript) { + p.parse_pass_symbol_uses = &scan_pass.used_symbols; } // Parse the file in the first pass, but do not bind symbols @@ -2020,31 +1972,28 @@ pub const Parser = struct { _ = try p.parseStmtsUpTo(js_lexer.T.t_end_of_file, &opts); // - switch (comptime ParserType) { - TSXImportScanner, TypeScriptImportScanner => { - for (scan_pass.import_records.items) |*import_record| { - // Mark everything as unused - // Except: - // - export * as ns from 'foo'; - // - export * from 'foo'; - // - import 'foo'; - // - import("foo") - // - require("foo") - import_record.is_unused = import_record.is_unused or - (import_record.kind == .stmt and - !import_record.was_originally_bare_import and - !import_record.calls_run_time_re_export_fn); - } - - var iter = scan_pass.used_symbols.iterator(); - while (iter.next()) |entry| { - const val = entry.value_ptr; - if (val.used) { - scan_pass.import_records.items[val.import_record_index].is_unused = false; - } + if (comptime ParserType.parser_features.typescript) { + for (scan_pass.import_records.items) |*import_record| { + // Mark everything as unused + // Except: + // - export * as ns from 'foo'; + // - export * from 'foo'; + // - import 'foo'; + // - import("foo") + // - require("foo") + import_record.is_unused = import_record.is_unused or + (import_record.kind == .stmt and + !import_record.was_originally_bare_import and + !import_record.calls_run_time_re_export_fn); + } + + var iter = scan_pass.used_symbols.iterator(); + while (iter.next()) |entry| { + const val = entry.value_ptr; + if (val.used) { + scan_pass.import_records.items[val.import_record_index].is_unused = false; } - }, - else => {}, + } } // Symbol use counts are unavailable @@ -2269,7 +2218,7 @@ pub const Parser = struct { declared_symbols[declared_symbols_i] = .{ .ref = automatic_namespace_ref, .is_top_level = true }; declared_symbols_i += 1; - const automatic_identifier = p.e(E.Identifier{ .ref = automatic_namespace_ref }, loc); + const automatic_identifier = p.e(E.ImportIdentifier{ .ref = automatic_namespace_ref }, loc); const dot_call_target = brk: { if (p.options.can_import_from_bundle or p.options.enable_bundling) { break :brk automatic_identifier; @@ -2374,7 +2323,7 @@ pub const Parser = struct { } if (jsx_classic_symbol.use_count_estimate > 0) { - const classic_identifier = p.e(E.Identifier{ .ref = classic_namespace_ref }, loc); + const classic_identifier = p.e(E.ImportIdentifier{ .ref = classic_namespace_ref }, loc); const dot_call_target = brk: { // var react = $aopaSD123(); @@ -2905,7 +2854,7 @@ pub fn NewParser( const NamedImportsType = if (only_scan_imports_and_do_not_visit) *js_ast.Ast.NamedImports else js_ast.Ast.NamedImports; const NeedsJSXType = if (only_scan_imports_and_do_not_visit) bool else void; const ParsePassSymbolUsageType = if (only_scan_imports_and_do_not_visit and is_typescript_enabled) *ScanPassResult.ParsePassSymbolUsageMap else void; - + pub const parser_features = js_parser_features; const P = @This(); pub const jsx_transform_type: JSXTransformType = js_parser_jsx; macro: MacroState = undefined, @@ -3248,8 +3197,7 @@ pub fn NewParser( const args = p.allocator.alloc(Expr, 1) catch unreachable; args[0] = p.e( - E.Identifier{ - .can_be_removed_if_unused = true, + E.ImportIdentifier{ .ref = namespace_ref, }, arg.loc, @@ -6268,6 +6216,8 @@ pub fn NewParser( @intCast(u16, @boolToInt(stmt.default_name != null)) + @intCast(u16, @boolToInt(stmt.star_name_loc != null)); + try item_refs.ensureUnusedCapacity(total_count); + // Link the default item to the namespace if (stmt.default_name) |*name_loc| { const name = p.loadNameFromRef(name_loc.ref orelse unreachable); @@ -6300,10 +6250,11 @@ pub fn NewParser( remap_count += 1; } } + + item_refs.putAssumeCapacity(name, name_loc.*); } if (stmt.items.len > 0) { - try item_refs.ensureCapacity(@intCast(u32, stmt.items.len)); for (stmt.items) |*item| { const name = p.loadNameFromRef(item.name.ref orelse unreachable); const ref = try p.declareSymbol(.import, item.name.loc, name); @@ -9672,9 +9623,8 @@ pub fn NewParser( const record_id = p.addImportRecord(.stmt, this.loc, import_data.path); var record: *ImportRecord = &p.import_records.items[record_id]; - + record.was_injected_by_macro = true; p.macro.imports.ensureUnusedCapacity(import_data.import.items.len) catch unreachable; - var import = import_data.import; import.import_record_index = record_id; @@ -9697,10 +9647,9 @@ pub fn NewParser( p.macro.imports.putAssumeCapacity(js_ast.Macro.JSNode.SymbolMap.generateImportHash(import_hash_name, import_data.path), name_ref); // Ensure we don't accidentally think this is an export from - clause.original_name = ""; } - p.macro.prepend_stmts.append(p.s(import, this.loc)) catch unreachable; + p.macro.prepend_stmts.append(p.visitSingleStmt(p.s(import, this.loc), StmtsKind.none)) catch unreachable; } }; @@ -11340,6 +11289,7 @@ pub fn NewParser( if (p.macro.refs.get(ref)) |import_record_id| { const name = p.symbols.items[ref.inner_index].original_name; const record = &p.import_records.items[import_record_id]; + const prepend_offset = p.macro.prepend_stmts.items.len; // We must visit it to convert inline_identifiers and record usage const macro_result = (p.options.macro_context.call( record.path.text, @@ -11353,6 +11303,7 @@ pub fn NewParser( MacroVisitor, MacroVisitor{ .p = p, .loc = expr.loc }, ) catch return expr); + if (macro_result.data != .e_template) { return p.visitExpr(macro_result); } @@ -11367,16 +11318,18 @@ pub fn NewParser( p.panic("Internal error: missing identifier from macro: {d}", .{id}); }; - const ident = E.ImportIdentifier{ - .was_originally_identifier = false, - .ref = ref, - }; - if (!p.is_control_flow_dead) { p.recordUsage(ref); } - return p.e(ident, expr.loc); + return p.e( + E.ImportIdentifier{ + .was_originally_identifier = false, + .was_from_macro = true, + .ref = ref, + }, + expr.loc, + ); }, .e_binary => |e_| { @@ -12618,7 +12571,7 @@ pub fn NewParser( } fn jsxStringsToMemberExpressionAutomatic(p: *P, loc: logger.Loc, is_static: bool) Expr { - return p.jsxStringsToMemberExpression(loc, if (is_static and !p.options.jsx.development) p.jsxs_runtime.ref else p.jsx_runtime.ref); + return p.jsxStringsToMemberExpression(loc, if (is_static) p.jsxs_runtime.ref else p.jsx_runtime.ref); } fn maybeRelocateVarsToTopLevel(p: *P, decls: []G.Decl, mode: RelocateVars.Mode) RelocateVars { @@ -12859,7 +12812,7 @@ pub fn NewParser( // When bundling, replace ExportDefault with __exportDefault(exportsRef, expr); if (p.options.enable_bundling) { var export_default_args = p.allocator.alloc(Expr, 2) catch unreachable; - export_default_args[0] = p.e(E.Identifier{ .ref = p.exports_ref }, expr.loc); + export_default_args[0] = p.@"module.exports"(expr.loc); export_default_args[1] = data.value.expr; stmts.append(p.s(S.SExpr{ .value = p.callRuntime(expr.loc, "__exportDefault", export_default_args) }, expr.loc)) catch unreachable; return; @@ -12882,7 +12835,7 @@ pub fn NewParser( if (p.options.enable_bundling) { var export_default_args = p.allocator.alloc(Expr, 2) catch unreachable; - export_default_args[0] = p.e(E.Identifier{ .ref = p.exports_ref }, s2.loc); + export_default_args[0] = p.@"module.exports"(s2.loc); if (had_name) { export_default_args[1] = p.e(E.Identifier{ .ref = func.func.name.?.ref.? }, s2.loc); @@ -12910,7 +12863,7 @@ pub fn NewParser( if (p.options.enable_bundling) { var export_default_args = p.allocator.alloc(Expr, 2) catch unreachable; - export_default_args[0] = p.e(E.Identifier{ .ref = p.exports_ref }, s2.loc); + export_default_args[0] = p.@"module.exports"(s2.loc); const class_name_ref = brk: { if (class.class.class_name) |class_name_ref| { @@ -12943,7 +12896,7 @@ pub fn NewParser( .s_export_equals => |data| { if (p.options.enable_bundling) { var export_default_args = p.allocator.alloc(Expr, 2) catch unreachable; - export_default_args[0] = p.e(E.Identifier{ .ref = p.exports_ref }, stmt.loc); + export_default_args[0] = p.@"module.exports"(stmt.loc); export_default_args[1] = data.value; stmts.append(p.s(S.SExpr{ .value = p.callRuntime(stmt.loc, "__exportDefault", export_default_args) }, stmt.loc)) catch unreachable; @@ -14363,6 +14316,10 @@ pub fn NewParser( } } + pub inline fn @"module.exports"(p: *P, loc: logger.Loc) Expr { + return p.e(E.Dot{ .name = exports_string_name, .name_loc = loc, .target = p.e(E.Identifier{ .ref = p.module_ref }, loc) }, loc); + } + // This assumes that the open parenthesis has already been parsed by the caller pub fn parseParenExpr(p: *P, loc: logger.Loc, level: Level, opts: ParenExprOpts) anyerror!Expr { var items_list = List(Expr).init(p.allocator); @@ -15107,11 +15064,15 @@ pub fn NewParser( update_function_args[0] = G.Arg{ .binding = p.b(B.Identifier{ .ref = p.exports_ref }, logger.Loc.Empty) }; while (named_exports_iter.next()) |named_export| { + const named_export_value = named_export.value_ptr.*; + // Do not try to HMR export {foo} from 'bar'; - if (named_imports.get(named_export.value_ptr.ref)) |named_import| { + if (named_imports.get(named_export_value.ref)) |named_import| { if (named_import.is_exported) continue; } + const named_export_symbol: Symbol = p.symbols.items[named_export_value.ref.inner_index]; + var export_name_string = export_name_string_remainder[0 .. named_export.key_ptr.len + "$$hmr_".len]; export_name_string_remainder = export_name_string_remainder[export_name_string.len..]; std.mem.copy(u8, export_name_string, "$$hmr_"); @@ -15121,15 +15082,23 @@ pub fn NewParser( var body_stmts = export_all_function_body_stmts[named_export_i .. named_export_i + 1]; body_stmts[0] = p.s( - S.Return{ .value = p.e(E.Identifier{ - .ref = named_export.value_ptr.ref, - }, logger.Loc.Empty) }, + // was this originally a named import? + // preserve the identifier + S.Return{ .value = if (named_export_symbol.namespace_alias != null) + p.e(E.ImportIdentifier{ + .ref = named_export_value.ref, + .was_originally_identifier = true, + }, logger.Loc.Empty) + else + p.e(E.Identifier{ + .ref = named_export_value.ref, + }, logger.Loc.Empty) }, logger.Loc.Empty, ); export_clauses[named_export_i] = js_ast.ClauseItem{ .original_name = "", .alias = named_export.key_ptr.*, - .alias_loc = named_export.value_ptr.alias_loc, + .alias_loc = named_export_value.alias_loc, .name = .{ .ref = name_ref, .loc = logger.Loc.Empty }, }; diff --git a/src/js_printer.zig b/src/js_printer.zig index 5f994d5f5..259f661a4 100644 --- a/src/js_printer.zig +++ b/src/js_printer.zig @@ -1561,37 +1561,31 @@ pub fn NewPrinter( } }, .e_import_identifier => |e| { - + if (e.ref.is_source_contents_slice or e.was_from_macro) { + p.printSymbol(e.ref); + return; + } // Potentially use a property access instead of an identifier - const ref = p.symbols.follow(e.ref); var didPrint = false; - if (p.symbols.get(ref)) |symbol| { + + if (p.symbols.get(e.ref)) |symbol| { if (symbol.import_item_status == .missing) { p.printUndefined(level); didPrint = true; } else if (symbol.namespace_alias) |namespace| { - // this feels crashy var wrap = false; + didPrint = true; if (p.call_target) |target| { - wrap = e.was_originally_identifier and target.e_import_identifier.ref.eql(expr.data.e_import_identifier.ref); + wrap = e.was_originally_identifier and (target == .e_identifier and + target.e_identifier.ref.eql(expr.data.e_import_identifier.ref)); } if (wrap) { p.print("(0, "); } - p.printSymbol(namespace.namespace_ref); - const alias = namespace.alias; - if (p.canPrintIdentifier(alias)) { - p.print("."); - p.printIdentifier(alias); - } else { - p.print("["); - p.printQuotedUTF8(alias, true); - p.print("]"); - } - didPrint = true; + p.printNamespaceAlias(namespace); if (wrap) { p.print(")"); @@ -1869,6 +1863,26 @@ pub fn NewPrinter( } } + pub fn printNamespaceAlias(p: *Printer, namespace: G.NamespaceAlias) void { + const import_record = &p.import_records[namespace.import_record_index]; + + if (import_record.module_id > 0 and !import_record.contains_import_star) { + p.print("$"); + p.printModuleId(import_record.module_id); + } else { + p.printSymbol(namespace.namespace_ref); + } + + if (p.canPrintIdentifier(namespace.alias)) { + p.print("."); + p.printIdentifier(namespace.alias); + } else { + p.print("["); + p.printQuotedUTF8(namespace.alias, true); + p.print("]"); + } + } + pub fn printProperty(p: *Printer, item: G.Property) void { debugl("<printProperty>"); defer debugl("</printProperty>"); @@ -2321,16 +2335,16 @@ pub fn NewPrinter( p.printSymbol(nameRef); p.printFunc(s.func); - if (rewrite_esm_to_cjs and s.func.flags.is_export) { - p.printSemicolonAfterStatement(); - p.print("var "); - p.printSymbol(nameRef); - p.print(" = "); - p.printSymbol(nameRef); - p.printSemicolonAfterStatement(); - } else { - p.printNewline(); - } + // if (rewrite_esm_to_cjs and s.func.flags.is_export) { + // p.printSemicolonAfterStatement(); + // p.print("var "); + // p.printSymbol(nameRef); + // p.print(" = "); + // p.printSymbol(nameRef); + // p.printSemicolonAfterStatement(); + // } else { + p.printNewline(); + // } if (rewrite_esm_to_cjs and s.func.flags.is_export) { p.printIndent(); @@ -2603,10 +2617,64 @@ pub fn NewPrinter( if (!prev_stmt_tag.isExportLike()) { p.printNewline(); } + p.printIndent(); p.printSpaceBeforeIdentifier(); p.print("export"); p.printSpace(); + + // This transforms code like this: + // import {Foo, Bar} from 'bundled-module'; + // export {Foo, Bar}; + // into + // export var Foo = $$bundledModule.Foo; (where $$bundledModule is created at import time) + // This is necessary unfortunately because export statements do not allow dot expressions + // The correct approach here is to invert the logic + // instead, make the entire module behave like a CommonJS module + // and export that one instead + // This particular code segment does the transform inline by adding an extra pass over export clauses + // and then swapRemove'ing them as we go + var array = std.ArrayListUnmanaged(js_ast.ClauseItem){ .items = s.items, .capacity = s.items.len }; + { + var i: usize = 0; + while (i < array.items.len) { + const item: js_ast.ClauseItem = array.items[i]; + + if (item.original_name.len > 0) { + if (p.symbols.get(item.name.ref.?)) |symbol| { + if (symbol.namespace_alias) |namespace| { + const import_record = p.import_records[namespace.import_record_index]; + if (import_record.is_bundled) { + p.print("var "); + p.printSymbol(item.name.ref.?); + p.print("= "); + p.printNamespaceAlias(namespace); + p.printSemicolonAfterStatement(); + _ = array.swapRemove(i); + + if (i < array.items.len) { + p.printIndent(); + p.printSpaceBeforeIdentifier(); + p.print("export"); + p.printSpace(); + } + + continue; + } + } + } + } + + i += 1; + } + + if (array.items.len == 0) { + return; + } + + s.items = array.items; + } + p.print("{"); if (!s.is_single_line) { @@ -2625,6 +2693,7 @@ pub fn NewPrinter( p.printNewline(); p.printIndent(); } + const name = p.renamer.nameForSymbol(item.name.ref.?); p.printIdentifier(name); if (!strings.eql(name, item.alias)) { @@ -3056,6 +3125,7 @@ pub fn NewPrinter( for (s.items) |item, i| { p.print(item.alias); const name = p.renamer.nameForSymbol(item.name.ref.?); + if (!strings.eql(name, item.alias)) { p.print(": "); p.printSymbol(item.name.ref.?); @@ -3137,69 +3207,7 @@ pub fn NewPrinter( p.print(" = () => ({default: {}});\n"); } - if (s.items.len > 0) { - p.printIndent(); - p.printSpaceBeforeIdentifier(); - p.print("var { "); - - if (s.default_name) |default_name| { - p.print("default: "); - p.printSymbol(default_name.ref.?); - p.print(", "); - for (s.items) |item, i| { - p.print(item.alias); - const name = p.renamer.nameForSymbol(item.name.ref.?); - if (!strings.eql(name, item.alias)) { - p.print(": "); - p.printSymbol(item.name.ref.?); - } - - if (i < s.items.len - 1) { - p.print(", "); - } - } - } else { - for (s.items) |item, i| { - p.print(item.alias); - const name = p.renamer.nameForSymbol(item.name.ref.?); - if (!strings.eql(name, item.alias)) { - p.print(":"); - p.printSymbol(item.name.ref.?); - } - - if (i < s.items.len - 1) { - p.print(", "); - } - } - } - - p.print("} = "); - - p.printLoadFromBundleWithoutCall(s.import_record_index); - - p.print("()"); - p.printSemicolonAfterStatement(); - } else if (s.default_name) |default_name| { - p.printIndent(); - p.printSpaceBeforeIdentifier(); - - p.print("var {default: "); - - p.printSymbol(default_name.ref.?); - p.print("} = "); - - p.printLoadFromBundleWithoutCall(s.import_record_index); - - p.print("()"); - p.printSemicolonAfterStatement(); - } else if (record.contains_import_star) { - p.print("var "); - p.printSymbol(s.namespace_ref); - p.print(" = "); - p.printLoadFromBundleWithoutCall(s.import_record_index); - p.print("()"); - p.printSemicolonAfterStatement(); - } + p.printBundledImport(record, s, stmt); return; } @@ -3366,25 +3374,44 @@ pub fn NewPrinter( return; } + const import_record = p.import_records[s.import_record_index]; + const is_disabled = import_record.path.is_disabled; + const module_id = import_record.module_id; + switch (ImportVariant.determine(&record, p.symbols.get(s.namespace_ref).?, s)) { .path_only => { - p.printLoadFromBundle(s.import_record_index); - p.printSemicolonAfterStatement(); - }, - .import_star => { - p.print("var "); - p.printSymbol(s.namespace_ref); - p.print(" = "); - p.printLoadFromBundle(s.import_record_index); - p.printSemicolonAfterStatement(); + if (!is_disabled) { + p.printCallModuleID(module_id); + p.printSemicolonAfterStatement(); + } }, - .import_default => { - p.print("var "); - p.printSymbol(s.default_name.?.ref.?); - p.print(" = "); - p.printLoadFromBundle(s.import_record_index); - if (!bun) { - p.print(".default"); + .import_items_and_default, .import_default => { + if (!is_disabled) { + p.print("var $"); + p.printModuleId(module_id); + p.print(" = "); + p.printLoadFromBundle(s.import_record_index); + + if (s.default_name) |default_name| { + p.print(", "); + p.printSymbol(default_name.ref.?); + p.print(" = "); + + p.print("(\"default\" in $"); + p.printModuleId(module_id); + p.print(" ? $"); + p.printModuleId(module_id); + p.print(".default : $"); + p.printModuleId(module_id); + p.print(")"); + } + } else { + if (s.default_name) |default_name| { + p.print("var "); + p.printSymbol(default_name.ref.?); + p.print(" = "); + p.printDisabledImport(); + } } p.printSemicolonAfterStatement(); @@ -3394,141 +3421,64 @@ pub fn NewPrinter( p.printSymbol(s.namespace_ref); p.print(" = "); p.printLoadFromBundle(s.import_record_index); - p.print(", "); - p.printSymbol(s.default_name.?.ref.?); - p.print(" = "); - p.printSymbol(s.namespace_ref); - if (!bun) { - p.print(".default"); - } - p.printSemicolonAfterStatement(); - }, - .import_items => { - p.print("var {"); - - for (s.items) |*item, i| { - if (i != 0) { - p.print(","); - if (s.is_single_line) { - p.printSpace(); - } - } - p.printClauseAlias(item.alias); - const name = p.renamer.nameForSymbol(item.name.ref.?); - if (!strings.eql(name, item.alias)) { - p.printSpace(); - p.print(":"); - p.printSpaceBeforeIdentifier(); - p.printIdentifier(name); - } - } - - p.print("}"); - p.print(" = "); - p.printLoadFromBundle(s.import_record_index); - - p.printSemicolonAfterStatement(); - }, - .import_items_and_default => { - p.print("var {"); - - var need_comma = false; if (s.default_name) |default_name| { - if (default_name.ref) |ref| { - p.print("default: "); - p.printSymbol(ref); - need_comma = true; - } - } - - for (s.items) |*item, i| { - if (need_comma) - p.print(","); - - defer need_comma = true; + p.print(", "); + p.printSymbol(default_name.ref.?); + p.print(" = "); - p.printClauseAlias(item.alias); - const name = p.renamer.nameForSymbol(item.name.ref.?); - if (!strings.eql(name, item.alias)) { - p.printSpace(); - p.print(":"); - p.printSpaceBeforeIdentifier(); - p.printIdentifier(name); + if (!bun) { + p.print("(\"default\" in "); + p.printSymbol(s.namespace_ref); + p.print(" ? "); + p.printSymbol(s.namespace_ref); + p.print(".default : "); + p.printSymbol(s.namespace_ref); + p.print(")"); + } else { + p.printSymbol(s.namespace_ref); } } - - p.print("}"); - p.print(" = "); - p.printLoadFromBundle(s.import_record_index); - p.printSemicolonAfterStatement(); }, - .import_items_and_star => { + .import_star => { p.print("var "); - p.printSymbol(s.namespace_ref); p.print(" = "); p.printLoadFromBundle(s.import_record_index); - p.print(", {"); - - for (s.items) |*item, i| { - if (i > 0) { - p.print(","); - } - - p.printClauseAlias(item.alias); - const name = p.renamer.nameForSymbol(item.name.ref.?); - if (!strings.eql(name, item.alias)) { - p.printSpace(); - p.print(":"); - p.printSpaceBeforeIdentifier(); - p.printIdentifier(name); - } - } - - p.print("} = "); - p.printSymbol(s.namespace_ref); p.printSemicolonAfterStatement(); }, - .import_items_and_default_and_star => { - p.print("var "); - p.printSymbol(s.namespace_ref); + else => { + p.print("var $"); + p.printModuleIdAssumeEnabled(module_id); p.print(" = "); p.printLoadFromBundle(s.import_record_index); - p.print(", {"); - - const default_name = s.default_name.?.ref.?; - p.print("default: "); - p.printSymbol(default_name); - - for (s.items) |*item, i| { - p.print(","); - - p.printClauseAlias(item.alias); - const name = p.renamer.nameForSymbol(item.name.ref.?); - if (!strings.eql(name, item.alias)) { - p.printSpace(); - p.print(":"); + p.printSemicolonAfterStatement(); + }, + } - p.printIdentifier(name); - } - } + const items = s.items; + if (items.len > 0 and !is_disabled) { + for (items) |item| { + const ref = item.name.ref.?; + const symbol = p.symbols.get(ref) orelse continue; - p.print("}"); + p.printIndent(); + p.print("var "); + p.printSymbol(ref); p.print(" = "); - p.printSymbol(s.namespace_ref); + if (!import_record.contains_import_star) { + p.print("$"); + p.printModuleId(module_id); + } else { + p.printSymbol(s.namespace_ref); + } + p.print("."); + p.printSymbol(ref); p.printSemicolonAfterStatement(); - }, - // .path_only => { - // p.printLoadFromBundle(s.import_record_index); - // p.print("/* "); - // p.printSymbol(s.namespace_ref); - // p.print(" */"); - // p.printSemicolonAfterStatement(); - // }, + } } } pub fn printLoadFromBundle(p: *Printer, import_record_index: u32) void { @@ -3542,18 +3492,32 @@ pub fn NewPrinter( p.print("()"); } } + + inline fn printDisabledImport(p: *Printer) void { + p.print("(() => ({}))"); + } + pub fn printLoadFromBundleWithoutCall(p: *Printer, import_record_index: u32) void { const record = p.import_records[import_record_index]; if (record.path.is_disabled) { - p.print("(() => ({}))"); + p.printDisabledImport(); return; } @call(.{ .modifier = .always_inline }, printModuleId, .{ p, p.import_records[import_record_index].module_id }); } + pub fn printCallModuleID(p: *Printer, module_id: u32) void { + printModuleId(p, module_id); + p.print("()"); + } + inline fn printModuleId(p: *Printer, module_id: u32) void { std.debug.assert(module_id != 0); // either module_id is forgotten or it should be disabled + p.printModuleIdAssumeEnabled(module_id); + } + + inline fn printModuleIdAssumeEnabled(p: *Printer, module_id: u32) void { p.print("$"); std.fmt.formatInt(module_id, 16, .lower, .{}, p) catch unreachable; } |