aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2023-04-29 16:28:27 -0700
committerGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2023-04-29 16:28:27 -0700
commit406ffb9e19846f791f1e6962cd8f6594e3a7e193 (patch)
tree6e815f5688c3ea2d447da620e83316c0fc18c1ba
parent96e113f41c0dae1ccd58c6d1e3b6dd2c54769636 (diff)
downloadbun-jarred/export-star-flat.tar.gz
bun-jarred/export-star-flat.tar.zst
bun-jarred/export-star-flat.zip
wip treeshake nested namespace importsjarred/export-star-flat
It doesn't handle several important edgecases. I don't think this code is currently in the right place.
Diffstat (limited to '')
-rw-r--r--src/bundler/bundle_v2.zig41
-rw-r--r--src/js_ast.zig19
-rw-r--r--src/js_parser.zig71
3 files changed, 123 insertions, 8 deletions
diff --git a/src/bundler/bundle_v2.zig b/src/bundler/bundle_v2.zig
index 168ef3137..9159dbb49 100644
--- a/src/bundler/bundle_v2.zig
+++ b/src/bundler/bundle_v2.zig
@@ -9045,8 +9045,11 @@ const LinkerContext = struct {
.imports = &named_imports,
};
named_imports.sort(sorter);
-
- for (named_imports.keys(), named_imports.values()) |ref, named_import| {
+ const nested_named_imports = c.graph.ast.items(.nested_named_imports);
+ const named_import_len = named_imports.count();
+ for (0..named_import_len) |named_import_i| {
+ const ref = named_imports.keys()[named_import_i];
+ const named_import = named_imports.values()[named_import_i];
// Re-use memory for the cycle detector
c.cycle_detector.clearRetainingCapacity();
@@ -9078,6 +9081,36 @@ const LinkerContext = struct {
},
},
) catch unreachable;
+
+ if (nested_named_imports[source_index].get(import_ref)) |nested| {
+ for (nested.slice()) |nested_import| {
+ if (c.graph.meta.items(.resolved_exports)[result.source_index].get(nested_import.alias)) |matching_export| {
+ var named_import_entry = named_imports.getOrPut(nested_import.ref) catch unreachable;
+ if (!named_import_entry.found_existing) {
+ named_import_entry.value_ptr.* = js_ast.NamedImport{
+ .alias = nested_import.alias,
+ .import_record_index = named_import.import_record_index,
+ .namespace_ref = import_ref,
+ };
+
+ imports_to_bind.put(
+ c.allocator,
+ nested_import.ref,
+ .{
+ .data = matching_export.data,
+ },
+ ) catch unreachable;
+ }
+ } else if (c.graph.symbols.get(nested_import.ref)) |nested_symbol| {
+ nested_symbol.namespace_alias = js_ast.G.NamespaceAlias{
+ .namespace_ref = import_ref,
+ .alias = nested_import.alias,
+ };
+ }
+
+ // }
+ }
+ }
},
.namespace => {
c.graph.symbols.get(import_ref).?.namespace_alias = js_ast.G.NamespaceAlias{
@@ -9217,6 +9250,7 @@ const LinkerContext = struct {
continue :next_export;
}
}
+
const ref = entry.value_ptr.ref;
var resolved = resolved_exports.getOrPut(this.allocator, entry.key_ptr.*) catch unreachable;
if (!resolved.found_existing) {
@@ -9384,6 +9418,9 @@ const LinkerContext = struct {
for (this.export_star_records[source_index]) |id| {
const records: []const ImportRecord = this.import_records[id].slice();
for (records) |record| {
+ // ignore external export * from
+ if (record.source_index.isInvalid()) continue;
+
// This file has dynamic exports if the exported imports are from a file
// that either has dynamic exports directly or transitively by itself
// having an export star from a file with dynamic exports.
diff --git a/src/js_ast.zig b/src/js_ast.zig
index 410539761..5f9ed06f9 100644
--- a/src/js_ast.zig
+++ b/src/js_ast.zig
@@ -5210,12 +5210,12 @@ pub const S = struct {
pub fn canBeMovedAround(self: ExportDefault) bool {
return switch (self.value) {
.expr => |e| switch (e.data) {
- .e_class => |class| class.extends == null,
+ .e_class => |class| class.canClassBeMoved(),
.e_arrow, .e_function => true,
else => e.canBeConstValue(),
},
.stmt => |s| switch (s.data) {
- .s_class => |class| class.class.extends == null,
+ .s_class => |class| class.class.canClassBeMoved(),
.s_function => true,
else => false,
},
@@ -5719,6 +5719,7 @@ pub const Ast = struct {
// since we already have to traverse the AST then anyway and the parser pass
// is conveniently fully parallelized.
named_imports: NamedImports = NamedImports.init(bun.failing_allocator),
+ nested_named_imports: NestedNamedImports = NestedNamedImports.init(bun.failing_allocator),
named_exports: NamedExports = NamedExports.init(bun.failing_allocator),
export_star_import_records: []u32 = &([_]u32{}),
@@ -5740,7 +5741,13 @@ pub const Ast = struct {
};
pub const CommonJSNamedExports = bun.StringArrayHashMapUnmanaged(CommonJSNamedExport);
- pub const NamedImports = std.ArrayHashMap(Ref, NamedImport, RefHashCtx, true);
+ pub const NestedNamedImport = struct {
+ alias: string,
+ ref: Ref,
+ };
+
+ pub const NamedImports = std.ArrayHashMap(Ref, NamedImport, RefHashCtx, false);
+ pub const NestedNamedImports = std.ArrayHashMap(Ref, BabyList(NestedNamedImport), RefHashCtx, false);
pub const NamedExports = bun.StringArrayHashMap(NamedExport);
pub const ConstValuesMap = std.ArrayHashMapUnmanaged(Ref, Expr, RefHashCtx, false);
@@ -9288,7 +9295,7 @@ pub const Macro = struct {
this.caller.loc,
this.allocator,
"cannot coerce {s} to Bun's AST. Please return a valid macro using the JSX syntax",
- .{@tagName(value.jsType())},
+ .{@tagName(value.jsTypeLoose())},
) catch unreachable;
break :brk error.MacroFailed;
},
@@ -9320,7 +9327,7 @@ pub const Macro = struct {
var blob_: ?JSC.WebCore.Blob = null;
var mime_type: ?HTTP.MimeType = null;
- if (value.jsType() == .DOMWrapper) {
+ if (value.jsTypeLoose() == .DOMWrapper) {
if (value.as(JSC.WebCore.Response)) |resp| {
mime_type = HTTP.MimeType.init(resp.mimeType(null));
blob_ = resp.body.use();
@@ -9535,7 +9542,7 @@ pub const Macro = struct {
this.caller.loc,
this.allocator,
"cannot coerce {s} to Bun's AST. Please return a valid macro using the JSX syntax",
- .{@tagName(value.jsType())},
+ .{@tagName(value.jsTypeLoose())},
) catch unreachable;
return error.MacroFailed;
}
diff --git a/src/js_parser.zig b/src/js_parser.zig
index 5933b3e9d..6afdf7253 100644
--- a/src/js_parser.zig
+++ b/src/js_parser.zig
@@ -2904,6 +2904,7 @@ pub const Parser = struct {
var before = ListManaged(js_ast.Part).init(p.allocator);
var after = ListManaged(js_ast.Part).init(p.allocator);
var parts = ListManaged(js_ast.Part).init(p.allocator);
+
defer {
after.deinit();
before.deinit();
@@ -2919,6 +2920,7 @@ pub const Parser = struct {
if (!p.options.tree_shaking) {
try p.appendPart(&parts, stmts);
} else {
+
// When tree shaking is enabled, each top-level statement is potentially a separate part.
for (stmts) |stmt| {
switch (stmt.data) {
@@ -4811,6 +4813,7 @@ fn NewParser_(
import_items_for_namespace: std.AutoHashMapUnmanaged(Ref, ImportItemForNamespaceMap) = .{},
is_import_item: RefMap = .{},
named_imports: NamedImportsType,
+ nested_named_imports: js_ast.Ast.NestedNamedImports,
named_exports: js_ast.Ast.NamedExports,
import_namespace_cc_map: Map(ImportNamespaceCallOrConstruct, bool) = .{},
@@ -17396,6 +17399,71 @@ fn NewParser_(
}
}
},
+ .e_import_identifier => |id| {
+
+ // Rewrite property accesses on explicit namespace imports as an identifier.
+ // This lets us replace them easily in the printer to rebind them to
+ // something else without paying the cost of a whole-tree traversal during
+ // module linking just to rewrite these EDot expressions.
+ if (p.options.bundle and js_lexer.isLatin1Identifier([]const u8, name)) {
+ var import_items_for_namespace_entry = p.import_items_for_namespace.getOrPut(p.allocator, id.ref) catch unreachable;
+ if (!import_items_for_namespace_entry.found_existing) {
+ import_items_for_namespace_entry.value_ptr.* = ImportItemForNamespaceMap.init(p.allocator);
+ }
+ var import_items = import_items_for_namespace_entry.value_ptr;
+ const ref = (import_items.get(name) orelse brk: {
+ const generated_name = std.fmt.allocPrint(p.allocator, "{s}_{s}", .{ p.loadNameFromRef(id.ref), name }) catch unreachable;
+ // Generate a new import item symbol in the module scope
+ const new_item = LocRef{
+ .loc = name_loc,
+ .ref = p.newSymbol(.import, generated_name) catch unreachable,
+ };
+ p.module_scope.generated.push(p.allocator, new_item.ref.?) catch unreachable;
+
+ import_items.put(name, new_item) catch unreachable;
+ p.is_import_item.put(p.allocator, new_item.ref.?, {}) catch unreachable;
+
+ var symbol = &p.symbols.items[new_item.ref.?.innerIndex()];
+
+ // Mark this as generated in case it's missing. We don't want to
+ // generate errors for missing import items that are automatically
+ // generated.
+ symbol.import_item_status = .generated;
+
+ var nested_entry = p.nested_named_imports.getOrPutValue(
+ id.ref,
+ .{},
+ ) catch unreachable;
+ nested_entry.value_ptr.push(p.allocator, .{
+ .ref = new_item.ref.?,
+ .alias = name,
+ }) catch unreachable;
+
+ break :brk new_item;
+ }).ref.?;
+
+ // Undo the usage count for the namespace itself. This is used later
+ // to detect whether the namespace symbol has ever been "captured"
+ // or whether it has just been used to read properties off of.
+ //
+ // The benefit of doing this is that if both this module and the
+ // imported module end up in the same module group and the namespace
+ // symbol has never been captured, then we don't need to generate
+ // any code for the namespace at all.
+ p.ignoreUsage(id.ref);
+
+ // Track how many times we've referenced this symbol
+ p.recordUsage(ref);
+
+ return p.handleIdentifier(
+ name_loc,
+ E.Identifier{ .ref = ref },
+ name,
+ identifier_opts,
+ );
+ }
+ },
+
.e_string => |str| {
if (p.options.features.minify_syntax) {
// minify "long-string".length to 11
@@ -21182,6 +21250,7 @@ fn NewParser_(
.approximate_newline_count = p.lexer.approximate_newline_count,
.exports_kind = exports_kind,
.named_imports = p.named_imports,
+ .nested_named_imports = p.nested_named_imports,
.named_exports = p.named_exports,
.import_keyword = p.esm_import_keyword,
.export_keyword = p.esm_export_keyword,
@@ -21254,6 +21323,7 @@ fn NewParser_(
.define = define,
.import_records = undefined,
.named_imports = undefined,
+ .nested_named_imports = undefined,
.named_exports = js_ast.Ast.NamedExports.init(allocator),
.log = log,
.allocator = allocator,
@@ -21286,6 +21356,7 @@ fn NewParser_(
if (comptime !only_scan_imports_and_do_not_visit) {
this.import_records = @TypeOf(this.import_records).init(allocator);
this.named_imports = NamedImportsType.init(allocator);
+ this.nested_named_imports = js_ast.Ast.NestedNamedImports.init(allocator);
}
this.to_expr_wrapper_namespace = Binding2ExprWrapper.Namespace.init(this);