aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--src/fallback.version2
-rw-r--r--src/import_record.zig57
-rw-r--r--src/js_ast.zig3
-rw-r--r--src/js_parser/js_parser.zig309
-rw-r--r--src/js_printer.zig404
6 files changed, 359 insertions, 418 deletions
diff --git a/Makefile b/Makefile
index 0b88a673e..d7cdda4a6 100644
--- a/Makefile
+++ b/Makefile
@@ -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;
}