Skip to content

Commit 38e22ed

Browse files
committed
add a warning/debug log message for #3867
1 parent a15bb51 commit 38e22ed

File tree

4 files changed

+165
-0
lines changed

4 files changed

+165
-0
lines changed

internal/bundler_tests/bundler_packagejson_test.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2908,3 +2908,97 @@ func TestPackageJsonSubpathImportNodeBuiltinIssue3485(t *testing.T) {
29082908
},
29092909
})
29102910
}
2911+
2912+
// See: https://github.com/evanw/esbuild/issues/3867
2913+
func TestPackageJsonBadExportsImportAndRequireWarningIssue3867(t *testing.T) {
2914+
packagejson_suite.expectBundled(t, bundled{
2915+
files: map[string]string{
2916+
"/entry.js": `
2917+
import "foo"
2918+
`,
2919+
"/node_modules/foo/package.json": `
2920+
{
2921+
"exports": {
2922+
".": {
2923+
"import": "./dist/node/index.js",
2924+
"require": "./dist/node/index.cjs",
2925+
"node": {
2926+
"import": "./dist/node/index.js",
2927+
"require": "./dist/node/index.cjs"
2928+
},
2929+
"browser": {
2930+
"import": "./dist/browser/index.js",
2931+
"require": "./dist/browser/index.cjs"
2932+
},
2933+
"worker": {
2934+
"import": "./dist/browser/index.js",
2935+
"require": "./dist/browser/index.cjs"
2936+
}
2937+
}
2938+
}
2939+
}
2940+
`,
2941+
"/node_modules/foo/dist/node/index.js": ``,
2942+
"/node_modules/foo/dist/node/index.cjs": ``,
2943+
"/node_modules/foo/dist/browser/index.js": ``,
2944+
"/node_modules/foo/dist/browser/index.cjs": ``,
2945+
},
2946+
entryPaths: []string{"/entry.js"},
2947+
options: config.Options{
2948+
Mode: config.ModeBundle,
2949+
Platform: config.PlatformNode,
2950+
AbsOutputFile: "/out.js",
2951+
},
2952+
debugLogs: true,
2953+
expectedScanLog: `node_modules/foo/package.json: DEBUG: The conditions "node" and "browser" and "worker" here will never be used as they come after both "import" and "require"
2954+
node_modules/foo/package.json: NOTE: The "import" condition comes earlier and will be used for all "import" statements:
2955+
node_modules/foo/package.json: NOTE: The "require" condition comes earlier and will be used for all "require" calls:
2956+
`,
2957+
})
2958+
}
2959+
2960+
// See: https://github.com/evanw/esbuild/issues/3867
2961+
func TestPackageJsonBadExportsDefaultWarningIssue3867(t *testing.T) {
2962+
packagejson_suite.expectBundled(t, bundled{
2963+
files: map[string]string{
2964+
"/entry.js": `
2965+
import "foo"
2966+
`,
2967+
"/node_modules/foo/package.json": `
2968+
{
2969+
"exports": {
2970+
".": {
2971+
"default": "./dist/node/index.js",
2972+
"node": {
2973+
"import": "./dist/node/index.js",
2974+
"require": "./dist/node/index.cjs"
2975+
},
2976+
"browser": {
2977+
"import": "./dist/browser/index.js",
2978+
"require": "./dist/browser/index.cjs"
2979+
},
2980+
"worker": {
2981+
"import": "./dist/browser/index.js",
2982+
"require": "./dist/browser/index.cjs"
2983+
}
2984+
}
2985+
}
2986+
}
2987+
`,
2988+
"/node_modules/foo/dist/node/index.js": ``,
2989+
"/node_modules/foo/dist/node/index.cjs": ``,
2990+
"/node_modules/foo/dist/browser/index.js": ``,
2991+
"/node_modules/foo/dist/browser/index.cjs": ``,
2992+
},
2993+
entryPaths: []string{"/entry.js"},
2994+
options: config.Options{
2995+
Mode: config.ModeBundle,
2996+
Platform: config.PlatformNode,
2997+
AbsOutputFile: "/out.js",
2998+
},
2999+
debugLogs: true,
3000+
expectedScanLog: `node_modules/foo/package.json: DEBUG: The conditions "node" and "browser" and "worker" here will never be used as they come after "default"
3001+
node_modules/foo/package.json: NOTE: The "default" condition comes earlier and will always be chosen:
3002+
`,
3003+
})
3004+
}

internal/bundler_tests/snapshots/snapshots_packagejson.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ TestCommonJSVariableInESMTypeModule
33
// entry.js
44
module.exports = null;
55

6+
================================================================================
7+
TestPackageJsonBadExportsDefaultWarningIssue3867
8+
---------- /out.js ----------
9+
10+
================================================================================
11+
TestPackageJsonBadExportsImportAndRequireWarningIssue3867
12+
---------- /out.js ----------
13+
614
================================================================================
715
TestPackageJsonBadMain
816
---------- /Users/user/project/out.js ----------

internal/logger/msg_ids.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ const (
7474

7575
// package.json
7676
MsgID_PackageJSON_FIRST // Keep this first
77+
MsgID_PackageJSON_DeadCondition
7778
MsgID_PackageJSON_InvalidBrowser
7879
MsgID_PackageJSON_InvalidImportsOrExports
7980
MsgID_PackageJSON_InvalidSideEffects

internal/resolver/package_json.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,16 @@ func parseImportsExportsMap(source logger.Source, log logger.Log, json js_ast.Ex
675675
firstToken := logger.Range{Loc: expr.Loc, Len: 1}
676676
isConditionalSugar := false
677677

678+
type DeadCondition struct {
679+
reason string
680+
ranges []logger.Range
681+
notes []logger.MsgData
682+
}
683+
var foundDefault logger.Range
684+
var foundImport logger.Range
685+
var foundRequire logger.Range
686+
var deadCondition DeadCondition
687+
678688
for i, property := range e.Properties {
679689
keyStr, _ := property.Key.Data.(*js_ast.EString)
680690
key := helpers.UTF16ToString(keyStr.Value)
@@ -697,6 +707,34 @@ func parseImportsExportsMap(source logger.Source, log logger.Log, json js_ast.Ex
697707
}
698708
}
699709

710+
// Track "dead" conditional branches that can never be reached
711+
if foundDefault.Len != 0 || (foundImport.Len != 0 && foundRequire.Len != 0) {
712+
deadCondition.ranges = append(deadCondition.ranges, keyRange)
713+
if deadCondition.reason == "" {
714+
if foundDefault.Len != 0 {
715+
deadCondition.reason = "\"default\""
716+
deadCondition.notes = []logger.MsgData{
717+
tracker.MsgData(foundDefault, "The \"default\" condition comes earlier and will always be chosen:"),
718+
}
719+
} else {
720+
deadCondition.reason = "both \"import\" and \"require\""
721+
deadCondition.notes = []logger.MsgData{
722+
tracker.MsgData(foundImport, "The \"import\" condition comes earlier and will be used for all \"import\" statements:"),
723+
tracker.MsgData(foundRequire, "The \"require\" condition comes earlier and will be used for all \"require\" calls:"),
724+
}
725+
}
726+
}
727+
} else {
728+
switch key {
729+
case "default":
730+
foundDefault = keyRange
731+
case "import":
732+
foundImport = keyRange
733+
case "require":
734+
foundRequire = keyRange
735+
}
736+
}
737+
700738
entry := pjMapEntry{
701739
key: key,
702740
keyRange: keyRange,
@@ -715,6 +753,30 @@ func parseImportsExportsMap(source logger.Source, log logger.Log, json js_ast.Ex
715753
// PATTERN_KEY_COMPARE which orders in descending order of specificity.
716754
sort.Stable(expansionKeys)
717755

756+
// Warn about "dead" conditional branches that can never be reached
757+
if deadCondition.reason != "" {
758+
kind := logger.Warning
759+
if helpers.IsInsideNodeModules(source.KeyPath.Text) {
760+
kind = logger.Debug
761+
}
762+
var conditions string
763+
conditionWord := "condition"
764+
itComesWord := "it comes"
765+
if len(deadCondition.ranges) > 1 {
766+
conditionWord = "conditions"
767+
itComesWord = "they come"
768+
}
769+
for i, r := range deadCondition.ranges {
770+
if i > 0 {
771+
conditions += " and "
772+
}
773+
conditions += source.TextForRange(r)
774+
}
775+
log.AddIDWithNotes(logger.MsgID_PackageJSON_DeadCondition, kind, &tracker, deadCondition.ranges[0],
776+
fmt.Sprintf("The %s %s here will never be used as %s after %s", conditionWord, conditions, itComesWord, deadCondition.reason),
777+
deadCondition.notes)
778+
}
779+
718780
return pjEntry{
719781
kind: pjObject,
720782
firstToken: firstToken,

0 commit comments

Comments
 (0)