diff options
| author | 2021-09-12 00:39:01 -0700 | |
|---|---|---|
| committer | 2021-09-12 00:39:01 -0700 | |
| commit | 092f9ac766ff532cb34587b5d93c401070cc79cf (patch) | |
| tree | 92660628d243cbcde0c02ca48f3a2eba52b5ab74 /src | |
| 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
Diffstat (limited to 'src')
| -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,              ); | 
