Skip to content

Commit

Permalink
implement custom conditions for "exports"
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Mar 9, 2021
1 parent d250d92 commit 1a458e0
Show file tree
Hide file tree
Showing 10 changed files with 59 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@

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.
There is also support for custom conditions with the `--conditions=` flag. The meaning of these is entirely up to package authors. For example, you could imagine a package that requires you to configure `--conditions=test,en-US`. Node has currently only endorsed the `development` and `production` custom conditions for recommended use.
## 0.8.57
* Fix overlapping chunk names when code splitting is active ([#928](https://github.com/evanw/esbuild/issues/928))
Expand Down
28 changes: 28 additions & 0 deletions internal/bundler/bundler_packagejson_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1663,3 +1663,31 @@ Users/user/project/node_modules/pkg1/package.json: note: The module specifier ".
`,
})
}

func TestPackageJsonExportsCustomConditions(t *testing.T) {
packagejson_suite.expectBundled(t, bundled{
files: map[string]string{
"/Users/user/project/src/entry.js": `
import 'pkg1'
`,
"/Users/user/project/node_modules/pkg1/package.json": `
{
"exports": {
"custom1": "./custom1.js",
"custom2": "./custom2.js",
"default": "./default.js"
}
}
`,
"/Users/user/project/node_modules/pkg1/custom2.js": `
console.log('SUCCESS')
`,
},
entryPaths: []string{"/Users/user/project/src/entry.js"},
options: config.Options{
Mode: config.ModeBundle,
AbsOutputFile: "/Users/user/project/out.js",
Conditions: []string{"custom2"},
},
})
}
6 changes: 6 additions & 0 deletions internal/bundler/snapshots/snapshots_packagejson.txt
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,12 @@ TestPackageJsonExportsBrowser
// Users/user/project/node_modules/pkg/browser.js
console.log("SUCCESS");

================================================================================
TestPackageJsonExportsCustomConditions
---------- /Users/user/project/out.js ----------
// Users/user/project/node_modules/pkg1/custom2.js
console.log("SUCCESS");

================================================================================
TestPackageJsonExportsDefaultOverImportAndRequire
---------- /Users/user/project/out.js ----------
Expand Down
1 change: 1 addition & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ type Options struct {

ExtensionOrder []string
MainFields []string
Conditions []string
AbsNodePaths []string // The "NODE_PATH" variable from Node.js
ExternalModules ExternalModules

Expand Down
3 changes: 3 additions & 0 deletions internal/resolver/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,9 @@ func NewResolver(fs fs.FS, log logger.Log, caches *cache.CacheSet, options confi
esmConditionsDefault := map[string]bool{}
esmConditionsImport := map[string]bool{"import": true}
esmConditionsRequire := map[string]bool{"require": true}
for _, condition := range options.Conditions {
esmConditionsDefault[condition] = true
}
switch options.Platform {
case config.PlatformBrowser:
esmConditionsDefault["browser"] = true
Expand Down
10 changes: 10 additions & 0 deletions lib/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ function flagsForBuildOptions(
let resolveExtensions = getFlag(options, keys, 'resolveExtensions', mustBeArray);
let nodePathsInput = getFlag(options, keys, 'nodePaths', mustBeArray);
let mainFields = getFlag(options, keys, 'mainFields', mustBeArray);
let conditions = getFlag(options, keys, 'conditions', mustBeArray);
let external = getFlag(options, keys, 'external', mustBeArray);
let loader = getFlag(options, keys, 'loader', mustBeObject);
let outExtension = getFlag(options, keys, 'outExtension', mustBeObject);
Expand Down Expand Up @@ -235,6 +236,15 @@ function flagsForBuildOptions(
}
flags.push(`--main-fields=${values.join(',')}`);
}
if (conditions) {
let values: string[] = [];
for (let value of conditions) {
value += '';
if (value.indexOf(',') >= 0) throw new Error(`Invalid condition: ${value}`);
values.push(value);
}
flags.push(`--conditions=${values.join(',')}`);
}
if (external) for (let name of external) flags.push(`--external:${name}`);
if (banner) {
for (let type in banner) {
Expand Down
1 change: 1 addition & 0 deletions lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export interface BuildOptions extends CommonOptions {
loader?: { [ext: string]: Loader };
resolveExtensions?: string[];
mainFields?: string[];
conditions?: string[];
write?: boolean;
tsconfig?: string;
outExtension?: { [ext: string]: string };
Expand Down
1 change: 1 addition & 0 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ type BuildOptions struct {
Format Format
External []string
MainFields []string
Conditions []string // For the "exports" field in "package.json"
Loader map[string]Loader
ResolveExtensions []string
Tsconfig string
Expand Down
4 changes: 4 additions & 0 deletions pkg/api/api_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,7 @@ func rebuildImpl(
ExternalModules: validateExternals(log, realFS, buildOpts.External),
TsConfigOverride: validatePath(log, realFS, buildOpts.Tsconfig, "tsconfig path"),
MainFields: buildOpts.MainFields,
Conditions: append([]string{}, buildOpts.Conditions...),
PublicPath: buildOpts.PublicPath,
KeepNames: buildOpts.KeepNames,
InjectAbsPaths: make([]string, len(buildOpts.Inject)),
Expand All @@ -749,6 +750,9 @@ func rebuildImpl(
WatchMode: buildOpts.Watch != nil,
Plugins: plugins,
}
if options.MainFields != nil {
options.MainFields = append([]string{}, options.MainFields...)
}
for i, path := range buildOpts.Inject {
options.InjectAbsPaths[i] = validatePath(log, realFS, path, "inject path")
}
Expand Down
3 changes: 3 additions & 0 deletions pkg/cli/cli_impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@ func parseOptionsImpl(osArgs []string, buildOpts *api.BuildOptions, transformOpt
case strings.HasPrefix(arg, "--main-fields=") && buildOpts != nil:
buildOpts.MainFields = strings.Split(arg[len("--main-fields="):], ",")

case strings.HasPrefix(arg, "--conditions=") && buildOpts != nil:
buildOpts.Conditions = strings.Split(arg[len("--conditions="):], ",")

case strings.HasPrefix(arg, "--public-path=") && buildOpts != nil:
buildOpts.PublicPath = arg[len("--public-path="):]

Expand Down

0 comments on commit 1a458e0

Please sign in to comment.