diff options
Diffstat (limited to 'src/resolver/resolver.zig')
-rw-r--r-- | src/resolver/resolver.zig | 191 |
1 files changed, 121 insertions, 70 deletions
diff --git a/src/resolver/resolver.zig b/src/resolver/resolver.zig index 93cdec82e..6496ce118 100644 --- a/src/resolver/resolver.zig +++ b/src/resolver/resolver.zig @@ -1007,7 +1007,7 @@ pub const Resolver = struct { if (remapped.len == 0) { // "browser": {"module": false} // does the module exist in the filesystem? - if (r.loadNodeModules(import_path, kind, source_dir_info)) |node_module| { + if (r.loadNodeModules(import_path, kind, source_dir_info, false)) |node_module| { var pair = node_module.path_pair; pair.primary.is_disabled = true; if (pair.secondary != null) { @@ -1183,7 +1183,13 @@ pub const Resolver = struct { threadlocal var esm_subpath_buf: [512]u8 = undefined; threadlocal var esm_absolute_package_path: [bun.MAX_PATH_BYTES]u8 = undefined; threadlocal var esm_absolute_package_path_joined: [bun.MAX_PATH_BYTES]u8 = undefined; - pub fn loadNodeModules(r: *ThisResolver, import_path: string, kind: ast.ImportKind, _dir_info: *DirInfo) ?MatchResult { + pub fn loadNodeModules( + r: *ThisResolver, + import_path: string, + kind: ast.ImportKind, + _dir_info: *DirInfo, + forbid_imports: bool, + ) ?MatchResult { var dir_info = _dir_info; if (r.debug_logs) |*debug| { debug.addNoteFmt("Searching for {s} in \"node_modules\" directories starting from \"{s}\"", .{ import_path, dir_info.abs_path }) catch {}; @@ -1221,6 +1227,44 @@ pub const Resolver = struct { const esm_ = ESModule.Package.parse(import_path, &esm_subpath_buf); + if (import_path[0] == '#' and !forbid_imports) { + if (esm_ != null) { + if (dir_info.package_json) |package_json| { + load_from_imports_map: { + const imports_map = package_json.imports orelse break :load_from_imports_map; + + if (import_path.len == 1 or strings.hasPrefix(import_path, "#/")) { + if (r.debug_logs) |*debug| { + debug.addNoteFmt("The path \"{s}\" must not equal \"#\" and must not start with \"#/\"", .{import_path}) catch {}; + } + return null; + } + + const esmodule = ESModule{ + .conditions = switch (kind) { + ast.ImportKind.require, ast.ImportKind.require_resolve => r.opts.conditions.require, + else => r.opts.conditions.import, + }, + .allocator = r.allocator, + .debug_logs = if (r.debug_logs) |*debug| debug else null, + }; + + const esm_resolution = esmodule.resolveImports(import_path, imports_map.root); + + if (esm_resolution.status == .PackageResolve) + return r.loadNodeModules( + esm_resolution.path, + kind, + dir_info, + true, + ); + + return r.handleESMResolution(esm_resolution, dir_info.abs_path, kind, package_json); + } + } + } + } + // Then check for the package in any enclosing "node_modules" directories while (true) { // Skip directories that are themselves called "node_modules", since we @@ -1259,73 +1303,10 @@ pub const Resolver = struct { // want problems due to Windows paths, which are very unlike URL // paths. We also want to avoid any "%" characters in the absolute // directory path accidentally being interpreted as URL escapes. - var esm_resolution = esmodule.resolve("/", esm.subpath, exports_map.root); - - if ((esm_resolution.status == .Inexact or esm_resolution.status == .Exact) and - esm_resolution.path.len > 0 and esm_resolution.path[0] == '/') - { - const abs_esm_path: string = brk: { - var parts = [_]string{ - abs_package_path, - esm_resolution.path[1..], - }; - break :brk r.fs.absBuf(&parts, &esm_absolute_package_path_joined); - }; - - switch (esm_resolution.status) { - .Exact => { - const resolved_dir_info = (r.dirInfoCached(std.fs.path.dirname(abs_esm_path).?) catch null) orelse { - esm_resolution.status = .ModuleNotFound; - return null; - }; - const entries = resolved_dir_info.getEntries() orelse { - esm_resolution.status = .ModuleNotFound; - return null; - }; - const entry_query = entries.get(std.fs.path.basename(abs_esm_path)) orelse { - esm_resolution.status = .ModuleNotFound; - return null; - }; - - if (entry_query.entry.kind(&r.fs.fs) == .dir) { - esm_resolution.status = .UnsupportedDirectoryImport; - return null; - } - - const absolute_out_path = brk: { - if (entry_query.entry.abs_path.isEmpty()) { - entry_query.entry.abs_path = - PathString.init(r.fs.dirname_store.append(@TypeOf(abs_esm_path), abs_esm_path) catch unreachable); - } - break :brk entry_query.entry.abs_path.slice(); - }; - - return MatchResult{ - .path_pair = PathPair{ - .primary = Path.initWithNamespace(absolute_out_path, "file"), - }, - .dirname_fd = entries.fd, - .file_fd = entry_query.entry.cache.fd, - .dir_info = resolved_dir_info, - .diff_case = entry_query.diff_case, - .is_node_module = true, - .package_json = resolved_dir_info.package_json orelse package_json, - }; - }, - .Inexact => { - // If this was resolved against an expansion key ending in a "/" - // instead of a "*", we need to try CommonJS-style implicit - // extension and/or directory detection. - if (r.loadAsFileOrDirectory(abs_esm_path, kind)) |*res| { - res.is_node_module = true; - res.package_json = res.package_json orelse package_json; - return res.*; - } - esm_resolution.status = .ModuleNotFound; - return null; - }, - else => unreachable, - } + const esm_resolution = esmodule.resolve("/", esm.subpath, exports_map.root); + + if (r.handleESMResolution(esm_resolution, abs_package_path, kind, package_json)) |res| { + return res; } } } @@ -1347,9 +1328,79 @@ pub const Resolver = struct { return null; } + fn handleESMResolution(r: *ThisResolver, esm_resolution_: ESModule.Resolution, abs_package_path: string, kind: ast.ImportKind, package_json: *PackageJSON) ?MatchResult { + var esm_resolution = esm_resolution_; + if (!((esm_resolution.status == .Inexact or esm_resolution.status == .Exact) and + esm_resolution.path.len > 0 and esm_resolution.path[0] == '/')) + return null; + + const abs_esm_path: string = brk: { + var parts = [_]string{ + abs_package_path, + esm_resolution.path[1..], + }; + break :brk r.fs.absBuf(&parts, &esm_absolute_package_path_joined); + }; + + switch (esm_resolution.status) { + .Exact => { + const resolved_dir_info = (r.dirInfoCached(std.fs.path.dirname(abs_esm_path).?) catch null) orelse { + esm_resolution.status = .ModuleNotFound; + return null; + }; + const entries = resolved_dir_info.getEntries() orelse { + esm_resolution.status = .ModuleNotFound; + return null; + }; + const entry_query = entries.get(std.fs.path.basename(abs_esm_path)) orelse { + esm_resolution.status = .ModuleNotFound; + return null; + }; + + if (entry_query.entry.kind(&r.fs.fs) == .dir) { + esm_resolution.status = .UnsupportedDirectoryImport; + return null; + } + + const absolute_out_path = brk: { + if (entry_query.entry.abs_path.isEmpty()) { + entry_query.entry.abs_path = + PathString.init(r.fs.dirname_store.append(@TypeOf(abs_esm_path), abs_esm_path) catch unreachable); + } + break :brk entry_query.entry.abs_path.slice(); + }; + + return MatchResult{ + .path_pair = PathPair{ + .primary = Path.initWithNamespace(absolute_out_path, "file"), + }, + .dirname_fd = entries.fd, + .file_fd = entry_query.entry.cache.fd, + .dir_info = resolved_dir_info, + .diff_case = entry_query.diff_case, + .is_node_module = true, + .package_json = resolved_dir_info.package_json orelse package_json, + }; + }, + .Inexact => { + // If this was resolved against an expansion key ending in a "/" + // instead of a "*", we need to try CommonJS-style implicit + // extension and/or directory detection. + if (r.loadAsFileOrDirectory(abs_esm_path, kind)) |*res| { + res.is_node_module = true; + res.package_json = res.package_json orelse package_json; + return res.*; + } + esm_resolution.status = .ModuleNotFound; + return null; + }, + else => unreachable, + } + } + pub fn resolveWithoutRemapping(r: *ThisResolver, source_dir_info: *DirInfo, import_path: string, kind: ast.ImportKind) ?MatchResult { if (isPackagePath(import_path)) { - return r.loadNodeModules(import_path, kind, source_dir_info); + return r.loadNodeModules(import_path, kind, source_dir_info, false); } else { const paths = [_]string{ source_dir_info.abs_path, import_path }; var resolved = r.fs.absBuf(&paths, &resolve_without_remapping_buf); |