Compare commits

...

1 Commits

Author SHA1 Message Date
Jarred Sumner
406ffb9e19 wip treeshake nested namespace imports
It doesn't handle several important edgecases. I don't think this code is currently in the right place.
2023-04-29 16:28:27 -07:00
3 changed files with 123 additions and 8 deletions

View File

@@ -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.

View File

@@ -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;
}

View File

@@ -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);