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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy