Skip to content

Commit dbefad5

Browse files
committed
fix #3067: crash due to bad subpath import error
1 parent 1365a07 commit dbefad5

File tree

4 files changed

+55
-6
lines changed

4 files changed

+55
-6
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
This release fixes a bug where esbuild incorrectly identified statements of the form `export { default as x } from "y" assert { type: "json" }` as a non-default import. The bug did not affect code of the form `import { default as x } from ...` (only code that used the `export` keyword).
88

9+
* Fix a crash with an invalid subpath import ([#3067](https://github.com/evanw/esbuild/issues/3067))
10+
11+
Previously esbuild could crash when attempting to generate a friendly error message for an invalid [subpath import](https://nodejs.org/api/packages.html#subpath-imports) (i.e. an import starting with `#`). This happened because esbuild originally only supported the `exports` field and the code for that error message was not updated when esbuild later added support for the `imports` field. This crash has been fixed.
12+
913
## 0.17.17
1014

1115
* Fix CSS nesting transform for top-level `&` ([#3052](https://github.com/evanw/esbuild/issues/3052))

internal/bundler_tests/bundler_packagejson_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1463,6 +1463,46 @@ NOTE: You can mark the path "pkg2" as external to exclude it from the bundle, wh
14631463
})
14641464
}
14651465

1466+
func TestPackageJsonImportsErrorUnsupportedDirectoryImport(t *testing.T) {
1467+
packagejson_suite.expectBundled(t, bundled{
1468+
files: map[string]string{
1469+
"/Users/user/project/src/entry.js": `
1470+
import '#foo1/bar'
1471+
import '#foo2/bar'
1472+
`,
1473+
"/Users/user/project/package.json": `
1474+
{
1475+
"imports": {
1476+
"#foo1/*": "./foo1/*",
1477+
"#foo2/bar": "./foo2/bar"
1478+
}
1479+
}
1480+
`,
1481+
"/Users/user/project/foo1/bar/index.js": `
1482+
console.log(bar)
1483+
`,
1484+
"/Users/user/project/foo2/bar/index.js": `
1485+
console.log(bar)
1486+
`,
1487+
},
1488+
entryPaths: []string{"/Users/user/project/src/entry.js"},
1489+
options: config.Options{
1490+
Mode: config.ModeBundle,
1491+
AbsOutputFile: "/Users/user/project/out.js",
1492+
},
1493+
expectedScanLog: `Users/user/project/src/entry.js: ERROR: Could not resolve "#foo1/bar"
1494+
Users/user/project/package.json: NOTE: Importing the directory "./foo1/bar" is forbidden by this package:
1495+
Users/user/project/package.json: NOTE: The presence of "imports" here makes importing a directory forbidden:
1496+
Users/user/project/src/entry.js: NOTE: Import from "/index.js" to get the file "Users/user/project/foo1/bar/index.js":
1497+
NOTE: You can mark the path "#foo1/bar" as external to exclude it from the bundle, which will remove this error.
1498+
Users/user/project/src/entry.js: ERROR: Could not resolve "#foo2/bar"
1499+
Users/user/project/package.json: NOTE: Importing the directory "./foo2/bar" is forbidden by this package:
1500+
Users/user/project/package.json: NOTE: The presence of "imports" here makes importing a directory forbidden:
1501+
NOTE: You can mark the path "#foo2/bar" as external to exclude it from the bundle, which will remove this error.
1502+
`,
1503+
})
1504+
}
1505+
14661506
func TestPackageJsonExportsRequireOverImport(t *testing.T) {
14671507
packagejson_suite.expectBundled(t, bundled{
14681508
files: map[string]string{

internal/resolver/package_json.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ func (r resolverQuery) parsePackageJSON(inputPath string) *packageJSON {
443443

444444
// Read the "imports" map
445445
if importsJSON, importsLoc, ok := getProperty(json, "imports"); ok {
446-
if importsMap := parseImportsExportsMap(jsonSource, r.log, importsJSON, importsLoc); importsMap != nil {
446+
if importsMap := parseImportsExportsMap(jsonSource, r.log, importsJSON, "imports", importsLoc); importsMap != nil {
447447
if importsMap.root.kind != pjObject {
448448
r.log.AddID(logger.MsgID_PackageJSON_InvalidImportsOrExports, logger.Warning, &tracker, importsMap.root.firstToken,
449449
"The value for \"imports\" must be an object")
@@ -454,7 +454,7 @@ func (r resolverQuery) parsePackageJSON(inputPath string) *packageJSON {
454454

455455
// Read the "exports" map
456456
if exportsJSON, exportsLoc, ok := getProperty(json, "exports"); ok {
457-
if exportsMap := parseImportsExportsMap(jsonSource, r.log, exportsJSON, exportsLoc); exportsMap != nil {
457+
if exportsMap := parseImportsExportsMap(jsonSource, r.log, exportsJSON, "exports", exportsLoc); exportsMap != nil {
458458
packageJSON.exportsMap = exportsMap
459459
}
460460
}
@@ -525,6 +525,7 @@ func globstarToEscapedRegexp(glob string) (string, bool) {
525525
// Reference: https://nodejs.org/api/esm.html#esm_resolver_algorithm_specification
526526
type pjMap struct {
527527
root pjEntry
528+
propertyKey string
528529
propertyKeyLoc logger.Loc
529530
}
530531

@@ -621,7 +622,7 @@ func (entry pjEntry) valueForKey(key string) (pjEntry, bool) {
621622
return pjEntry{}, false
622623
}
623624

624-
func parseImportsExportsMap(source logger.Source, log logger.Log, json js_ast.Expr, propertyKeyLoc logger.Loc) *pjMap {
625+
func parseImportsExportsMap(source logger.Source, log logger.Log, json js_ast.Expr, propertyKey string, propertyKeyLoc logger.Loc) *pjMap {
625626
var visit func(expr js_ast.Expr) pjEntry
626627
tracker := logger.MakeLineColumnTracker(&source)
627628

@@ -730,7 +731,11 @@ func parseImportsExportsMap(source logger.Source, log logger.Log, json js_ast.Ex
730731
return nil
731732
}
732733

733-
return &pjMap{root: root, propertyKeyLoc: propertyKeyLoc}
734+
return &pjMap{
735+
root: root,
736+
propertyKey: propertyKey,
737+
propertyKeyLoc: propertyKeyLoc,
738+
}
734739
}
735740

736741
func (entry pjEntry) keysStartWithDot() bool {

internal/resolver/resolver.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2417,8 +2417,8 @@ func (r resolverQuery) finalizeImportsExportsResult(
24172417
case pjStatusUnsupportedDirectoryImport, pjStatusUnsupportedDirectoryImportMissingIndex:
24182418
r.debugMeta.notes = []logger.MsgData{
24192419
tracker.MsgData(debug.token, fmt.Sprintf("Importing the directory %q is forbidden by this package:", resolvedPath)),
2420-
tracker.MsgData(packageJSON.source.RangeOfString(packageJSON.exportsMap.propertyKeyLoc),
2421-
"The presence of \"exports\" here makes importing a directory forbidden:"),
2420+
tracker.MsgData(packageJSON.source.RangeOfString(importExportMap.propertyKeyLoc),
2421+
fmt.Sprintf("The presence of %q here makes importing a directory forbidden:", importExportMap.propertyKey)),
24222422
}
24232423

24242424
// Provide an inline suggestion message with the correct import path

0 commit comments

Comments
 (0)