aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-09-12 00:39:01 -0700
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-09-12 00:39:01 -0700
commit092f9ac766ff532cb34587b5d93c401070cc79cf (patch)
tree92660628d243cbcde0c02ca48f3a2eba52b5ab74
parentdfb65ef1ca0406e9679f21ae316b6a0878b56271 (diff)
downloadbun-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.zig19
-rw-r--r--src/js_printer.zig53
-rw-r--r--src/linker.zig105
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,
);