Skip to content

Commit

Permalink
update readme for the "exports" feature
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Mar 9, 2021
1 parent e111ec4 commit d250d92
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 5 deletions.
36 changes: 32 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,38 @@

Previously the "resolve extensions" setting included `.mjs` and `.cjs` but this is no longer the case. This wasn't a good default because it doesn't match node's behavior and could break some packages. You now have to either explicitly specify these extensions or configure the "resolve extensions" setting yourself.
* Add support for node's `exports` field in `package.json` files

This feature was recently added to node. It allows you to rewrite what import paths inside your package map to as well as to prevent people from importing certain files in your package. Adding support for this to esbuild is a breaking change (i.e. code that was working fine before can easily stop working) so adding support for it has been delayed until this breaking change release.

One way to use this feature is to remap import paths for your package. For example, this would remap an import of `your-pkg/esm/lib.js` (the "public" import path) to `your-pkg/dist/esm/lib.js` (the "private" file system path):

```json
{
"name": "your-pkg",
"exports": {
"./esm/*": "./dist/esm/*",
"./cjs/*": "./dist/cjs/*"
}
}
```

Another way to use this feature is to have conditional imports where the same import path can mean different things in different situations. For example, this would remap `require('your-pkg')` to `your-pkg/required.cjs` and `import 'your-pkg'` to `your-pkg/imported.mjs`:

```json
{
"name": "your-pkg",
"exports": {
"import": "./imported.mjs",
"require": "./required.cjs"
}
}
```

There is built-in support for the `import` and `require` conditions depending on the kind of import and the `browser` and `node` conditions depending on the current platform. In addition, the `default` condition always applies regardless of the current configuration settings and can be used as a catch-all fallback condition.

Note that when you use conditions, _your package may end up in the bundle multiple times!_ This is a subtle issue that can cause bugs due to duplicate copies of your code's state in addition to bloating the resulting bundle. This is commonly known as the [dual package hazard](https://nodejs.org/docs/latest/api/packages.html#packages_dual_package_hazard). The primary way of avoiding this is to put all of your code in the `require` condition and have the `import` condition just be a light wrapper that calls `require` on your package and re-exports the package using ESM syntax.
## 0.8.57
* Fix overlapping chunk names when code splitting is active ([#928](https://github.com/evanw/esbuild/issues/928))
Expand Down Expand Up @@ -95,10 +127,6 @@
This is for compatibility with node which [supports this feature natively](https://nodejs.org/docs/latest/api/esm.html#esm_data_imports). Importing from a data URL is sometimes useful for injecting code to be evaluated before an external import without needing to generate a separate imported file.
* Add support for node's `exports` field in `package.json` files

This feature was recently added to node. It allows you to rewrite what import paths inside your package map to as well as to prevent people from importing certain files in your package. Adding support for this to esbuild is a breaking change (i.e. code that was working fine before can easily stop working) so adding support for it has been delayed until this breaking change release.

## 0.8.56
* Fix a discrepancy with esbuild's `tsconfig.json` implementation ([#913](https://github.com/evanw/esbuild/issues/913))
Expand Down
50 changes: 50 additions & 0 deletions internal/bundler/bundler_packagejson_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1613,3 +1613,53 @@ func TestPackageJsonExportsOrderIndependent(t *testing.T) {
},
})
}

func TestPackageJsonExportsWildcard(t *testing.T) {
packagejson_suite.expectBundled(t, bundled{
files: map[string]string{
"/Users/user/project/src/entry.js": `
import 'pkg1/foo'
import 'pkg1/foo2'
`,
"/Users/user/project/node_modules/pkg1/package.json": `
{
"exports": {
"./foo*": "./file*.js"
}
}
`,
"/Users/user/project/node_modules/pkg1/file2.js": `
console.log('SUCCESS')
`,
},
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 "pkg1/foo" (mark it as external to exclude it from the bundle)
Users/user/project/node_modules/pkg1/package.json: note: The path "./foo" is not exported by "pkg1"
`,
})
}

func TestPackageJsonExportsErrorMissingTrailingSlash(t *testing.T) {
packagejson_suite.expectBundled(t, bundled{
files: map[string]string{
"/Users/user/project/src/entry.js": `
import 'pkg1/foo/bar'
`,
"/Users/user/project/node_modules/pkg1/package.json": `
{ "exports": { "./foo/": "./test" } }
`,
},
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 "pkg1/foo/bar" (mark it as external to exclude it from the bundle)
Users/user/project/node_modules/pkg1/package.json: note: The module specifier "./test" is invalid
`,
})
}
2 changes: 1 addition & 1 deletion internal/resolver/package_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ func esmPackageTargetResolve(
// If pattern is false, subpath has non-zero length and target
// does not end with "/", throw an Invalid Module Specifier error.
if !pattern && subpath != "" && !strings.HasSuffix(target.strData, "/") {
return subpath, peStatusInvalidModuleSpecifier, target.firstToken
return target.strData, peStatusInvalidModuleSpecifier, target.firstToken
}

if !strings.HasPrefix(target.strData, "./") {
Expand Down

0 comments on commit d250d92

Please sign in to comment.