diff --git a/internal/bundler_tests/bundler_packagejson_test.go b/internal/bundler_tests/bundler_packagejson_test.go index 0734f23dcd..f676eb73ec 100644 --- a/internal/bundler_tests/bundler_packagejson_test.go +++ b/internal/bundler_tests/bundler_packagejson_test.go @@ -2416,27 +2416,109 @@ NOTE: You can mark the path "#" as external to exclude it from the bundle, which }) } -func TestPackageJsonImportsErrorStartsWithHashSlash(t *testing.T) { +// Tests for new Node.js behavior: https://github.com/nodejs/node/pull/60864 +// The #/ prefix is now allowed in subpath imports when there's a matching pattern + +func TestPackageJsonImportsHashSlashWithWildcard(t *testing.T) { packagejson_suite.expectBundled(t, bundled{ files: map[string]string{ "/Users/user/project/src/entry.js": ` - import '#/foo' + import '#/foo.js' + import '#/bar/baz.js' `, "/Users/user/project/src/package.json": ` { - "imports": {} + "imports": { + "#/*": "./src/*" + } } `, + "/Users/user/project/src/src/foo.js": `console.log('foo.js')`, + "/Users/user/project/src/src/bar/baz.js": `console.log('bar/baz.js')`, + }, + entryPaths: []string{"/Users/user/project/src/entry.js"}, + options: config.Options{ + Mode: config.ModeBundle, + AbsOutputFile: "/Users/user/project/out.js", + }, + }) +} + +func TestPackageJsonImportsHashSlashExactMatch(t *testing.T) { + packagejson_suite.expectBundled(t, bundled{ + files: map[string]string{ + "/Users/user/project/src/entry.js": ` + import '#/' + import '#/utils' + `, + "/Users/user/project/src/package.json": ` + { + "imports": { + "#/": "./index.js", + "#/utils": "./utils.js" + } + } + `, + "/Users/user/project/src/index.js": `console.log('index.js')`, + "/Users/user/project/src/utils.js": `console.log('utils.js')`, + }, + entryPaths: []string{"/Users/user/project/src/entry.js"}, + options: config.Options{ + Mode: config.ModeBundle, + AbsOutputFile: "/Users/user/project/out.js", + }, + }) +} + +func TestPackageJsonImportsHashSlashWithConditions(t *testing.T) { + packagejson_suite.expectBundled(t, bundled{ + files: map[string]string{ + "/Users/user/project/src/entry.js": ` + import '#/lib' + `, + "/Users/user/project/src/package.json": ` + { + "imports": { + "#/*": { + "import": "./esm/*.js", + "require": "./cjs/*.js" + } + } + } + `, + "/Users/user/project/src/esm/lib.js": `console.log('esm/lib.js')`, + "/Users/user/project/src/cjs/lib.js": `console.log('cjs/lib.js')`, + }, + entryPaths: []string{"/Users/user/project/src/entry.js"}, + options: config.Options{ + Mode: config.ModeBundle, + AbsOutputFile: "/Users/user/project/out.js", + }, + }) +} + +func TestPackageJsonImportsHashSlashSymmetricWithExports(t *testing.T) { + // Common pattern: symmetric imports/exports for root-relative imports + packagejson_suite.expectBundled(t, bundled{ + files: map[string]string{ + "/Users/user/project/src/entry.js": ` + import '#/components/button.js' + import '#/utils/format.js' + `, + "/Users/user/project/src/package.json": ` + { + "exports": { "./*": "./src/*" }, + "imports": { "#/*": "./src/*" } + } + `, + "/Users/user/project/src/src/components/button.js": `console.log('button.js')`, + "/Users/user/project/src/src/utils/format.js": `console.log('format.js')`, }, entryPaths: []string{"/Users/user/project/src/entry.js"}, options: config.Options{ Mode: config.ModeBundle, AbsOutputFile: "/Users/user/project/out.js", }, - expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "#/foo" -Users/user/project/src/package.json: NOTE: This "imports" map was ignored because the module specifier "#/foo" is invalid: -NOTE: You can mark the path "#/foo" as external to exclude it from the bundle, which will remove this error and leave the unresolved path in the bundle. -`, }) } diff --git a/internal/bundler_tests/snapshots/snapshots_packagejson.txt b/internal/bundler_tests/snapshots/snapshots_packagejson.txt index 3da1ca1ee3..5a0042e358 100644 --- a/internal/bundler_tests/snapshots/snapshots_packagejson.txt +++ b/internal/bundler_tests/snapshots/snapshots_packagejson.txt @@ -810,6 +810,39 @@ console.log("c.js"); // Users/user/project/src/node_modules/pkg/some-slash/d.js console.log("d.js"); +================================================================================ +TestPackageJsonImportsHashSlashExactMatch +---------- /Users/user/project/out.js ---------- +// Users/user/project/src/index.js +console.log("index.js"); + +// Users/user/project/src/utils.js +console.log("utils.js"); + +================================================================================ +TestPackageJsonImportsHashSlashSymmetricWithExports +---------- /Users/user/project/out.js ---------- +// Users/user/project/src/src/components/button.js +console.log("button.js"); + +// Users/user/project/src/src/utils/format.js +console.log("format.js"); + +================================================================================ +TestPackageJsonImportsHashSlashWithConditions +---------- /Users/user/project/out.js ---------- +// Users/user/project/src/esm/lib.js +console.log("esm/lib.js"); + +================================================================================ +TestPackageJsonImportsHashSlashWithWildcard +---------- /Users/user/project/out.js ---------- +// Users/user/project/src/src/foo.js +console.log("foo.js"); + +// Users/user/project/src/src/bar/baz.js +console.log("bar/baz.js"); + ================================================================================ TestPackageJsonMain ---------- /Users/user/project/out.js ---------- diff --git a/internal/resolver/resolver.go b/internal/resolver/resolver.go index 8f13043e93..530547bf98 100644 --- a/internal/resolver/resolver.go +++ b/internal/resolver/resolver.go @@ -2255,10 +2255,10 @@ func (r resolverQuery) loadPackageImports(importPath string, dirInfoPackageJSON } // Filter out invalid module specifiers now where we have more information for - // a better error message instead of later when we're inside the algorithm - if importPath == "#" || strings.HasPrefix(importPath, "#/") { + // a better error message instead of later when we're inside the algorithm. + if importPath == "#" { if r.debugLogs != nil { - r.debugLogs.addNote(fmt.Sprintf("The path %q must not equal \"#\" and must not start with \"#/\".", importPath)) + r.debugLogs.addNote(fmt.Sprintf("The path %q must not equal \"#\".", importPath)) } tracker := logger.MakeLineColumnTracker(&packageJSON.source) r.debugMeta.notes = append(r.debugMeta.notes, tracker.MsgData(packageJSON.importsMap.root.firstToken,