diff options
author | 2021-09-12 00:39:01 -0700 | |
---|---|---|
committer | 2021-09-12 00:39:01 -0700 | |
commit | 092f9ac766ff532cb34587b5d93c401070cc79cf (patch) | |
tree | 92660628d243cbcde0c02ca48f3a2eba52b5ab74 | |
parent | dfb65ef1ca0406e9679f21ae316b6a0878b56271 (diff) | |
download | bun-092f9ac766ff532cb34587b5d93c401070cc79cf.tar.gz bun-092f9ac766ff532cb34587b5d93c401070cc79cf.tar.zst bun-092f9ac766ff532cb34587b5d93c401070cc79cf.zip |
Fix CommonJS interop issue when, while linking, we find out the import is possibly CommonJS
-rw-r--r-- | src/js_ast.zig | 19 | ||||
-rw-r--r-- | src/js_printer.zig | 53 | ||||
-rw-r--r-- | src/linker.zig | 105 |
3 files changed, 131 insertions, 46 deletions
diff --git a/src/js_ast.zig b/src/js_ast.zig index ef92fb377..fe20cdfcf 100644 --- a/src/js_ast.zig +++ b/src/js_ast.zig @@ -759,7 +759,10 @@ pub const Symbol = struct { } pub inline fn isKindHoisted(kind: Symbol.Kind) bool { - return @enumToInt(kind) == @enumToInt(Symbol.Kind.hoisted) or @enumToInt(kind) == @enumToInt(Symbol.Kind.hoisted_function); + return switch (kind) { + .hoisted, .hoisted_function => true, + else => false, + }; } pub inline fn isHoisted(self: *const Symbol) bool { @@ -767,11 +770,17 @@ pub const Symbol = struct { } pub inline fn isKindHoistedOrFunction(kind: Symbol.Kind) bool { - return isKindHoisted(kind) or kind == Symbol.Kind.generator_or_async_function; + return switch (kind) { + .hoisted, .hoisted_function, .generator_or_async_function => true, + else => false, + }; } pub inline fn isKindFunction(kind: Symbol.Kind) bool { - return kind == Symbol.Kind.hoisted_function or kind == Symbol.Kind.generator_or_async_function; + return switch (kind) { + .hoisted_function, .generator_or_async_function => true, + else => false, + }; } pub fn isReactComponentishName(symbol: *const Symbol) bool { @@ -3619,6 +3628,7 @@ pub const Ast = struct { has_top_level_return: bool = false, uses_exports_ref: bool = false, uses_module_ref: bool = false, + uses_require_ref: bool = false, exports_kind: ExportsKind = ExportsKind.none, bundle_export_ref: ?Ref = null, @@ -3643,9 +3653,10 @@ pub const Ast = struct { exports_ref: ?Ref = null, module_ref: ?Ref = null, wrapper_ref: ?Ref = null, - require_ref: ?Ref = null, + require_ref: Ref = Ref.None, bundle_namespace_ref: ?Ref = null, + prepend_part: ?Part = null, // These are used when bundling. They are filled in during the parser pass // since we already have to traverse the AST then anyway and the parser pass diff --git a/src/js_printer.zig b/src/js_printer.zig index c71964c45..7e59a9bfa 100644 --- a/src/js_printer.zig +++ b/src/js_printer.zig @@ -768,18 +768,10 @@ pub fn NewPrinter( p.printSpaceBeforeIdentifier(); - if (bun) { - p.print("module.require("); - } else { - p.print("require("); - } - // if (p.options.platform == .node) { - + p.printSymbol(p.options.require_ref.?); + p.print("("); p.printQuotedUTF8(record.path.text, true); p.print(")"); - // } else { - // p.options.platform - // } return; } @@ -2948,14 +2940,10 @@ pub fn NewPrinter( } if (record.wrap_with_to_module) { - const require_ref = p.options.require_ref orelse { - - }; - - const module_id = @truncate( - u32, - std.hash.Wyhash.hash(2, record.path.pretty), - ); + const require_ref = p.options.require_ref.?; + + const module_id = record.module_id; + p.print("import * as "); p.printModuleId(module_id); @@ -4063,6 +4051,16 @@ pub fn printAst( opts, linker, ); + + if (tree.prepend_part) |part| { + for (part.stmts) |stmt| { + try printer.printStmt(stmt); + if (printer.writer.getError()) {} else |err| { + return err; + } + } + } + for (tree.parts) |part| { for (part.stmts) |stmt| { try printer.printStmt(stmt); @@ -4098,6 +4096,15 @@ pub fn printCommonJS( opts, linker, ); + + if (tree.prepend_part) |part| { + for (part.stmts) |stmt| { + try printer.printStmt(stmt); + if (printer.writer.getError()) {} else |err| { + return err; + } + } + } for (tree.parts) |part| { for (part.stmts) |stmt| { try printer.printStmt(stmt); @@ -4146,6 +4153,16 @@ pub fn printCommonJSThreaded( opts, linker, ); + + if (tree.prepend_part) |part| { + for (part.stmts) |stmt| { + try printer.printStmt(stmt); + if (printer.writer.getError()) {} else |err| { + return err; + } + } + } + for (tree.parts) |part| { for (part.stmts) |stmt| { try printer.printStmt(stmt); diff --git a/src/linker.zig b/src/linker.zig index 5f02adc5a..5787573b1 100644 --- a/src/linker.zig +++ b/src/linker.zig @@ -184,6 +184,11 @@ pub fn NewLinker(comptime BundlerType: type) type { // This modifies the Ast in-place! // But more importantly, this does the following: // - Wrap CommonJS files + threadlocal var require_part: js_ast.Part = undefined; + threadlocal var require_part_stmts: [1]js_ast.Stmt = undefined; + threadlocal var require_part_import_statement: js_ast.S.Import = undefined; + threadlocal var require_part_import_clauses: [1]js_ast.ClauseItem = undefined; + const require_alias: string = "__require"; pub fn link( linker: *ThisLinker, file_path: Fs.Path, @@ -200,6 +205,7 @@ pub fn NewLinker(comptime BundlerType: type) type { var needs_bundle = false; var first_bundled_index: ?u32 = null; var had_resolve_errors = false; + var needs_require = false; // Step 1. Resolve imports & requires switch (result.loader) { @@ -219,6 +225,7 @@ pub fn NewLinker(comptime BundlerType: type) type { linker.runtime_source_path, Runtime.version(), false, + "bun", import_path_format, ); result.ast.runtime_import_record_id = record_index; @@ -348,9 +355,15 @@ pub fn NewLinker(comptime BundlerType: type) type { // If it's a namespace import, assume it's safe. // We can do this in the printer instead of creating a bunch of AST nodes here. // But we need to at least tell the printer that this needs to happen. - if (result.ast.exports_kind != .cjs and (import_record.kind == .require or (import_record.kind == .stmt and resolved_import.shouldAssumeCommonJS(import_record)))) { + if (result.ast.exports_kind != .cjs and + (import_record.kind == .require or + (import_record.kind == .stmt and resolved_import.shouldAssumeCommonJS(import_record)))) + { import_record.wrap_with_to_module = true; + import_record.module_id = @truncate(u32, std.hash.Wyhash.hash(0, path.pretty)); + result.ast.needs_runtime = true; + needs_require = true; } } else |err| { had_resolve_errors = true; @@ -413,7 +426,7 @@ pub fn NewLinker(comptime BundlerType: type) type { }, else => {}, } - if (had_resolve_errors) return error.LinkError; + if (had_resolve_errors) return error.ResolveError; result.ast.externals = externals.toOwnedSlice(); if (result.ast.needs_runtime and result.ast.runtime_import_record_id == null) { @@ -426,11 +439,42 @@ pub fn NewLinker(comptime BundlerType: type) type { linker.runtime_source_path, Runtime.version(), false, + "bun", import_path_format, ), .range = logger.Range{ .loc = logger.Loc{ .start = 0 }, .len = 0 }, }; result.ast.runtime_import_record_id = @truncate(u32, import_records.len - 1); + result.ast.import_records = import_records; + } + + // We _assume_ you're importing ESM. + // But, that assumption can be wrong without parsing code of the imports. + // That's where in here, we inject + // > import {require} from 'bun:runtime'; + // Since they definitely aren't using require, we don't have to worry about the symbol being renamed. + if (needs_require and !result.ast.uses_require_ref) { + result.ast.uses_require_ref = true; + require_part_import_clauses[0] = js_ast.ClauseItem{ + .alias = require_alias, + .original_name = "", + .alias_loc = logger.Loc.Empty, + .name = js_ast.LocRef{ + .loc = logger.Loc.Empty, + .ref = result.ast.require_ref, + }, + }; + + require_part_import_statement = js_ast.S.Import{ + .namespace_ref = Ref.None, + .items = std.mem.span(&require_part_import_clauses), + .import_record_index = result.ast.runtime_import_record_id.?, + }; + require_part_stmts[0] = js_ast.Stmt{ + .data = .{ .s_import = &require_part_import_statement }, + .loc = logger.Loc.Empty, + }; + result.ast.prepend_part = js_ast.Part{ .stmts = std.mem.span(&require_part_stmts) }; } // This is a bad idea @@ -495,6 +539,7 @@ pub fn NewLinker(comptime BundlerType: type) type { source_path: string, package_version: ?string, use_hashed_name: bool, + namespace: string, comptime import_path_format: Options.BundleOptions.ImportPathFormat, ) !Fs.Path { switch (import_path_format) { @@ -553,35 +598,46 @@ pub fn NewLinker(comptime BundlerType: type) type { }, .absolute_url => { - var absolute_pathname = Fs.PathName.init(source_path); + if (strings.eqlComptime(namespace, "node")) { + return Fs.Path.init(try std.fmt.allocPrint( + linker.allocator, + "{s}/node:{s}", + .{ + linker.options.origin.origin, + source_path, + }, + )); + } else { + var absolute_pathname = Fs.PathName.init(source_path); - if (!linker.options.preserve_extensions) { - if (linker.options.out_extensions.get(absolute_pathname.ext)) |ext| { - absolute_pathname.ext = ext; + if (!linker.options.preserve_extensions) { + if (linker.options.out_extensions.get(absolute_pathname.ext)) |ext| { + absolute_pathname.ext = ext; + } } - } - var base = linker.fs.relativeTo(source_path); - if (strings.lastIndexOfChar(base, '.')) |dot| { - base = base[0..dot]; - } + var base = linker.fs.relativeTo(source_path); + if (strings.lastIndexOfChar(base, '.')) |dot| { + base = base[0..dot]; + } - var dirname = std.fs.path.dirname(base) orelse ""; + var dirname = std.fs.path.dirname(base) orelse ""; - var basename = std.fs.path.basename(base); + var basename = std.fs.path.basename(base); - if (use_hashed_name) { - var basepath = Fs.Path.init(source_path); - basename = try linker.getHashedFilename(basepath, null); - } + if (use_hashed_name) { + var basepath = Fs.Path.init(source_path); + basename = try linker.getHashedFilename(basepath, null); + } - return Fs.Path.init(try linker.options.origin.joinAlloc( - linker.allocator, - linker.options.routes.asset_prefix_path, - dirname, - basename, - absolute_pathname.ext, - )); + return Fs.Path.init(try linker.options.origin.joinAlloc( + linker.allocator, + linker.options.routes.asset_prefix_path, + dirname, + basename, + absolute_pathname.ext, + )); + } }, else => unreachable, @@ -610,6 +666,7 @@ pub fn NewLinker(comptime BundlerType: type) type { if (path.is_symlink and import_path_format == .absolute_url and linker.options.platform != .bun) path.pretty else path.text, if (resolve_result.package_json) |package_json| package_json.version else "", BundlerType.isCacheEnabled and loader == .file, + path.namespace, import_path_format, ); |