diff --git a/CHANGELOG.md b/CHANGELOG.md index 640c88d3ee1..0001c9225d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +* Fix path resolution with the `exports` field for scoped packages + + This release fixes a bug where the `exports` field in `package.json` files was not being detected for scoped packages (i.e. packages of the form `@scope/pkg-name` instead of just `pkg-name`). The `exports` field should now be respected for these kinds of packages. + * Improved error message in `exports` failure case Node's new [conditional exports feature](https://nodejs.org/docs/latest/api/packages.html#packages_conditional_exports) can be non-intuitive and hard to use. Now that esbuild supports this feature (as of version 0.9.0), you can get into a situation where it's impossible to import a package if the package's `exports` field in its `package.json` file isn't configured correctly. diff --git a/internal/resolver/package_json.go b/internal/resolver/package_json.go index a29bf179fce..424a643395d 100644 --- a/internal/resolver/package_json.go +++ b/internal/resolver/package_json.go @@ -717,7 +717,7 @@ func esmParsePackageName(packageSpecifier string) (packageName string, packageSu if slash2 == -1 { slash2 = len(packageSpecifier[slash+1:]) } - packageName = packageSpecifier[:slash] + packageName = packageSpecifier[:slash+1+slash2] } if strings.HasPrefix(packageName, ".") || strings.ContainsAny(packageName, "\\%") { diff --git a/scripts/end-to-end-tests.js b/scripts/end-to-end-tests.js index e1b2ad721bd..583c222997b 100644 --- a/scripts/end-to-end-tests.js +++ b/scripts/end-to-end-tests.js @@ -2954,6 +2954,89 @@ } }`, }), + test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), { + 'in.js': `import abc from '@scope/pkg'; if (abc !== 123) throw 'fail'`, + 'package.json': `{ "type": "module" }`, + 'node_modules/@scope/pkg/subdir/foo.js': `export default 123`, + 'node_modules/@scope/pkg/package.json': `{ + "type": "module", + "exports": { + ".": "./subdir/foo.js" + } + }`, + }), + test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), { + 'in.js': `import abc from '@scope/pkg'; if (abc !== 123) throw 'fail'`, + 'package.json': `{ "type": "module" }`, + 'node_modules/@scope/pkg/subdir/foo.js': `export default 123`, + 'node_modules/@scope/pkg/package.json': `{ + "type": "module", + "exports": { + ".": { + "default": "./subdir/foo.js" + } + } + }`, + }), + test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), { + 'in.js': `import abc from '@scope/pkg'; if (abc !== 123) throw 'fail'`, + 'package.json': `{ "type": "module" }`, + 'node_modules/@scope/pkg/subdir/foo.js': `export default 123`, + 'node_modules/@scope/pkg/package.json': `{ + "type": "module", + "exports": { + "default": "./subdir/foo.js" + } + }`, + }), + test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), { + 'in.js': `import abc from '@scope/pkg/foo.js'; if (abc !== 123) throw 'fail'`, + 'package.json': `{ "type": "module" }`, + 'node_modules/@scope/pkg/subdir/foo.js': `export default 123`, + 'node_modules/@scope/pkg/package.json': `{ + "type": "module", + "exports": { + "./": "./subdir/" + } + }`, + }), + test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), { + 'in.js': `import abc from '@scope/pkg/foo.js'; if (abc !== 123) throw 'fail'`, + 'package.json': `{ "type": "module" }`, + 'node_modules/@scope/pkg/subdir/foo.js': `export default 123`, + 'node_modules/@scope/pkg/package.json': `{ + "type": "module", + "exports": { + "./": { + "default": "./subdir/" + } + } + }`, + }), + test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), { + 'in.js': `import abc from '@scope/pkg/dir/foo.js'; if (abc !== 123) throw 'fail'`, + 'package.json': `{ "type": "module" }`, + 'node_modules/@scope/pkg/subdir/foo.js': `export default 123`, + 'node_modules/@scope/pkg/package.json': `{ + "type": "module", + "exports": { + "./dir/": "./subdir/" + } + }`, + }), + test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), { + 'in.js': `import abc from '@scope/pkg/dir/foo.js'; if (abc !== 123) throw 'fail'`, + 'package.json': `{ "type": "module" }`, + 'node_modules/@scope/pkg/subdir/foo.js': `export default 123`, + 'node_modules/@scope/pkg/package.json': `{ + "type": "module", + "exports": { + "./dir/": { + "default": "./subdir/" + } + } + }`, + }), test(['in.js', '--outfile=node.js', '--format=esm'].concat(flags), { 'in.js': `import abc from 'pkg/dirwhat'; if (abc !== 123) throw 'fail'`, 'package.json': `{ "type": "module" }`,