Skip to content

Commit

Permalink
fix #3377: improve resolution error due to null
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Sep 13, 2023
1 parent 79ac17a commit 5ecf535
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 3 deletions.
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,28 @@

Please never actually write code like this.

* Improve the error message for `null` entries in `exports` ([#3377](https://github.com/evanw/esbuild/issues/3377))

Package authors can disable package export paths with the `exports` map in `package.json`. With this release, esbuild now has a clearer error message that points to the `null` token in `package.json` itself instead of to the surrounding context. Here is an example of the new error message:

```
✘ [ERROR] Could not resolve "msw/browser"

lib/msw-config.ts:2:28:
2 │ import { setupWorker } from 'msw/browser';
~~~~~~~~~~~~~

The path "./browser" cannot be imported from package "msw" because it was explicitly disabled by
the package author here:

node_modules/msw/package.json:17:14:
17 │ "node": null,
~~~~

You can mark the path "msw/browser" as external to exclude it from the bundle, which will remove
this error and leave the unresolved path in the bundle.
```

## 0.19.2

* Update how CSS nesting is parsed again
Expand Down
38 changes: 38 additions & 0 deletions internal/bundler_tests/bundler_packagejson_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2803,3 +2803,41 @@ func TestPackageJsonNodePathsIssue2752(t *testing.T) {
},
})
}

// See: https://github.com/evanw/esbuild/issues/3377
func TestPackageJsonReversePackageExportsIssue3377(t *testing.T) {
packagejson_suite.expectBundled(t, bundled{
files: map[string]string{
"/lib/msw-config.ts": `
import { setupWorker, type SetupWorker } from 'msw/browser'
setupWorker();
`,
"/node_modules/msw/package.json": `{
"exports": {
"./browser": {
"node": null,
"require": "./lib/browser/index.js",
"import": "./lib/browser/index.mjs",
"default": "./lib/browser/index.js"
}
}
}`,
"/node_modules/msw/browser/package.json": `{
"main": "../lib/browser/index.js",
"module": "../lib/browser/index.mjs"
}`,
"/node_modules/msw/lib/browser/index.js": `TEST FAILURE`,
"/node_modules/msw/lib/browser/index.mjs": `TEST FAILURE`,
},
entryPaths: []string{"/lib/msw-config.ts"},
options: config.Options{
Mode: config.ModeBundle,
AbsOutputFile: "/out.js",
Platform: config.PlatformNode,
},
expectedScanLog: `lib/msw-config.ts: ERROR: Could not resolve "msw/browser"
node_modules/msw/package.json: NOTE: The path "./browser" cannot be imported from package "msw" because it was explicitly disabled by the package author here:
NOTE: You can mark the path "msw/browser" as external to exclude it from the bundle, which will remove this error and leave the unresolved path in the bundle.
`,
})
}
12 changes: 10 additions & 2 deletions internal/resolver/package_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,9 @@ type pjDebug struct {

// This is the range of the token to use for error messages
token logger.Range

// If true, the token is a "null" literal
isBecauseOfNullLiteral bool
}

func (r resolverQuery) esmHandlePostConditions(
Expand Down Expand Up @@ -892,6 +895,7 @@ func (r resolverQuery) esmPackageExportsResolve(
return "", pjStatusInvalidPackageConfiguration, pjDebug{token: exports.firstToken}
}

debugToReturn := pjDebug{token: exports.firstToken}
if subpath == "." {
mainExport := pjEntry{kind: pjNull}
if exports.kind == pjString || exports.kind == pjArray || (exports.kind == pjObject && !exports.keysStartWithDot()) {
Expand All @@ -908,19 +912,23 @@ func (r resolverQuery) esmPackageExportsResolve(
resolved, status, debug := r.esmPackageTargetResolve(packageURL, mainExport, "", false, false, conditions)
if status != pjStatusNull && status != pjStatusUndefined {
return resolved, status, debug
} else {
debugToReturn = debug
}
}
} else if exports.kind == pjObject && exports.keysStartWithDot() {
resolved, status, debug := r.esmPackageImportsExportsResolve(subpath, exports, packageURL, false, conditions)
if status != pjStatusNull && status != pjStatusUndefined {
return resolved, status, debug
} else {
debugToReturn = debug
}
}

if r.debugLogs != nil {
r.debugLogs.addNote(fmt.Sprintf("The path %q is not exported", subpath))
}
return "", pjStatusPackagePathNotExported, pjDebug{token: exports.firstToken}
return "", pjStatusPackagePathNotExported, debugToReturn
}

func (r resolverQuery) esmPackageImportsExportsResolve(
Expand Down Expand Up @@ -1237,7 +1245,7 @@ func (r resolverQuery) esmPackageTargetResolve(
if r.debugLogs != nil {
r.debugLogs.addNote(fmt.Sprintf("The path %q is set to null", subpath))
}
return "", pjStatusNull, pjDebug{token: target.firstToken}
return "", pjStatusNull, pjDebug{token: target.firstToken, isBecauseOfNullLiteral: true}
}

if r.debugLogs != nil {
Expand Down
11 changes: 10 additions & 1 deletion internal/resolver/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -2609,6 +2609,12 @@ func (r resolverQuery) finalizeImportsExportsResult(
r.debugMeta.notes = []logger.MsgData{tracker.MsgData(debug.token, why)}

case pjStatusPackagePathNotExported:
if debug.isBecauseOfNullLiteral {
r.debugMeta.notes = []logger.MsgData{tracker.MsgData(debug.token,
fmt.Sprintf("The path %q cannot be imported from package %q because it was explicitly disabled by the package author here:", esmPackageSubpath, esmPackageName))}
break
}

r.debugMeta.notes = []logger.MsgData{tracker.MsgData(debug.token,
fmt.Sprintf("The path %q is not exported by package %q:", esmPackageSubpath, esmPackageName))}

Expand Down Expand Up @@ -2706,7 +2712,10 @@ func (r resolverQuery) finalizeImportsExportsResult(
}

default:
if !didSuggestEnablingCondition {
// Note: Don't suggest the adding the "types" condition because
// TypeScript uses that for type definitions, which are not
// intended to be included in a bundle as executable code
if !didSuggestEnablingCondition && key.Text != "types" {
var how string
switch logger.API {
case logger.CLIAPI:
Expand Down

0 comments on commit 5ecf535

Please sign in to comment.