Skip to content

Commit 44a7a61

Browse files
committed
fix #2463: change yarn pnp manifest to a singleton
1 parent 6fd8736 commit 44a7a61

File tree

3 files changed

+103
-63
lines changed

3 files changed

+103
-63
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
## Unreleased
44

5+
* Change the Yarn PnP manifest to a singleton ([#2463](https://github.com/evanw/esbuild/issues/2463))
6+
7+
Previously esbuild searched for the Yarn PnP manifest in the parent directories of each file. But with Yarn's `enableGlobalCache` setting it's possible to configure Yarn PnP's implementation to reach outside of the directory subtree containing the Yarn PnP manifest. This was causing esbuild to fail to bundle projects with the `enableGlobalCache` setting enabled.
8+
9+
To handle this case, *esbuild will now only search for the Yarn PnP manifest in the current working directory of the esbuild process*. If you're using esbuild's CLI, this means you will now have to `cd` into the appropriate directory first. If you're using esbuild's API, you can override esbuild's value for the current working directory with the `absWorkingDir` API option.
10+
511
* Fix Yarn PnP resolution failures due to backslashes in paths on Windows ([#2462](https://github.com/evanw/esbuild/issues/2462))
612

713
Previously dependencies of a Yarn PnP virtual dependency failed to resolve on Windows. This was because Windows uses `\` instead of `/` as a path separator, and the path manipulation algorithms used for Yarn PnP expected `/`. This release converts `\` into `/` in Windows paths, which fixes this issue.

internal/resolver/resolver.go

Lines changed: 74 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,9 @@ type resolver struct {
205205
// all parent directories
206206
dirCache map[string]*dirInfo
207207

208+
pnpManifestWasChecked bool
209+
pnpManifest *pnpData
210+
208211
options config.Options
209212

210213
// This mutex serves two purposes. First of all, it guards access to "dirCache"
@@ -254,6 +257,8 @@ func NewResolver(fs fs.FS, log logger.Log, caches *cache.CacheSet, options confi
254257
esmConditionsRequire[key] = true
255258
}
256259

260+
fs.Cwd()
261+
257262
return &resolver{
258263
fs: fs,
259264
log: log,
@@ -409,6 +414,30 @@ func (rr *resolver) Resolve(sourceDir string, importPath string, kind ast.Import
409414
defer r.mutex.Unlock()
410415
sourceDirInfo := r.loadModuleSuffixesForSourceDir(sourceDir)
411416

417+
// Check for the Yarn PnP manifest if it hasn't already been checked for
418+
if !r.pnpManifestWasChecked {
419+
r.pnpManifestWasChecked = true
420+
421+
// Use the current working directory to find the Yarn PnP manifest. We
422+
// can't necessarily use the entry point locations because the entry
423+
// point locations aren't necessarily file paths. For example, they could
424+
// be HTTP URLs that will be handled by a plugin.
425+
for dirInfo := r.dirInfoCached(r.fs.Cwd()); dirInfo != nil; dirInfo = dirInfo.parent {
426+
if absPath := dirInfo.pnpManifestAbsPath; absPath != "" {
427+
if strings.HasSuffix(absPath, ".json") {
428+
if json := r.extractYarnPnPDataFromJSON(absPath, pnpReportErrorsAboutMissingFiles); json.Data != nil {
429+
r.pnpManifest = compileYarnPnPData(absPath, r.fs.Dir(absPath), json)
430+
}
431+
} else {
432+
if json := r.tryToExtractYarnPnPDataFromJS(absPath, pnpReportErrorsAboutMissingFiles); json.Data != nil {
433+
r.pnpManifest = compileYarnPnPData(absPath, r.fs.Dir(absPath), json)
434+
}
435+
}
436+
break
437+
}
438+
}
439+
}
440+
412441
result := r.resolveWithoutSymlinks(sourceDir, sourceDirInfo, importPath)
413442
if result == nil {
414443
// If resolution failed, try again with the URL query and/or hash removed
@@ -692,15 +721,12 @@ func (r resolverQuery) finalizeResolve(result *ResolveResult) {
692721
}
693722

694723
func (r resolverQuery) resolveWithoutSymlinks(sourceDir string, sourceDirInfo *dirInfo, importPath string) *ResolveResult {
695-
// Find the parent directory with the Yarn PnP data
696-
for info := sourceDirInfo; info != nil; info = info.parent {
697-
if info.pnpData != nil {
698-
if result, ok := r.pnpResolve(importPath, sourceDirInfo.absPath, info.pnpData); ok {
699-
importPath = result // Continue with the module resolution algorithm from node.js
700-
} else {
701-
return nil // This is a module resolution error
702-
}
703-
break
724+
// If Yarn PnP is active, use it to rewrite the path
725+
if r.pnpManifest != nil {
726+
if result, ok := r.pnpResolve(importPath, sourceDirInfo.absPath, r.pnpManifest); ok {
727+
importPath = result // Continue with the module resolution algorithm from node.js
728+
} else {
729+
return nil // This is a module resolution error
704730
}
705731
}
706732

@@ -860,8 +886,8 @@ type dirInfo struct {
860886

861887
// All relevant information about this directory
862888
absPath string
889+
pnpManifestAbsPath string
863890
entries fs.DirEntries
864-
pnpData *pnpData
865891
packageJSON *packageJSON // Is there a "package.json" file in this directory?
866892
enclosingPackageJSON *packageJSON // Is there a "package.json" file in this directory or a parent directory?
867893
enclosingTSConfigJSON *TSConfigJSON // Is there a "tsconfig.json" file in this directory or a parent directory?
@@ -944,38 +970,45 @@ func (r resolverQuery) parseTSConfig(file string, visited map[string]bool) (*TSC
944970

945971
// Check for a Yarn PnP manifest and use that to rewrite the path
946972
if IsPackagePath(extends) {
947-
current := fileDir
948-
for {
949-
if _, _, ok := fs.ParseYarnPnPVirtualPath(current); !ok {
950-
var pnpData *pnpData
951-
absPath := r.fs.Join(current, ".pnp.data.json")
952-
if json := r.extractYarnPnPDataFromJSON(absPath, pnpIgnoreErrorsAboutMissingFiles); json.Data != nil {
953-
pnpData = compileYarnPnPData(absPath, current, json)
954-
} else {
955-
absPath := r.fs.Join(current, ".pnp.cjs")
973+
pnpData := r.pnpManifest
974+
975+
// If we haven't loaded the Yarn PnP manifest yet, try to find one
976+
if pnpData == nil {
977+
current := fileDir
978+
for {
979+
if _, _, ok := fs.ParseYarnPnPVirtualPath(current); !ok {
980+
absPath := r.fs.Join(current, ".pnp.data.json")
956981
if json := r.extractYarnPnPDataFromJSON(absPath, pnpIgnoreErrorsAboutMissingFiles); json.Data != nil {
957982
pnpData = compileYarnPnPData(absPath, current, json)
958-
} else {
959-
absPath := r.fs.Join(current, ".pnp.js")
960-
if json := r.extractYarnPnPDataFromJSON(absPath, pnpIgnoreErrorsAboutMissingFiles); json.Data != nil {
961-
pnpData = compileYarnPnPData(absPath, current, json)
962-
}
983+
break
963984
}
964-
}
965-
if pnpData != nil {
966-
if result, ok := r.pnpResolve(extends, current, pnpData); ok {
967-
extends = result // Continue with the module resolution algorithm from node.js
985+
986+
absPath = r.fs.Join(current, ".pnp.cjs")
987+
if json := r.extractYarnPnPDataFromJSON(absPath, pnpIgnoreErrorsAboutMissingFiles); json.Data != nil {
988+
pnpData = compileYarnPnPData(absPath, current, json)
989+
break
968990
}
991+
992+
absPath = r.fs.Join(current, ".pnp.js")
993+
if json := r.extractYarnPnPDataFromJSON(absPath, pnpIgnoreErrorsAboutMissingFiles); json.Data != nil {
994+
pnpData = compileYarnPnPData(absPath, current, json)
995+
break
996+
}
997+
}
998+
999+
// Go to the parent directory, stopping at the file system root
1000+
next := r.fs.Dir(current)
1001+
if current == next {
9691002
break
9701003
}
1004+
current = next
9711005
}
1006+
}
9721007

973-
// Go to the parent directory, stopping at the file system root
974-
next := r.fs.Dir(current)
975-
if current == next {
976-
break
1008+
if pnpData != nil {
1009+
if result, ok := r.pnpResolve(extends, fileDir, pnpData); ok {
1010+
extends = result // Continue with the module resolution algorithm from node.js
9771011
}
978-
current = next
9791012
}
9801013
}
9811014

@@ -1251,21 +1284,14 @@ func (r resolverQuery) dirInfoUncached(path string) *dirInfo {
12511284
//
12521285
// /project/.yarn/__virtual__/pkg/1/.yarn/__virtual__/pkg/1/bar
12531286
//
1254-
if _, _, ok := fs.ParseYarnPnPVirtualPath(path); !ok {
1255-
if pnp, _ := entries.Get(".pnp.data.json"); pnp != nil && pnp.Kind(r.fs) == fs.FileEntry {
1256-
absPath := r.fs.Join(path, ".pnp.data.json")
1257-
if json := r.extractYarnPnPDataFromJSON(absPath, pnpReportErrorsAboutMissingFiles); json.Data != nil {
1258-
info.pnpData = compileYarnPnPData(absPath, path, json)
1259-
}
1260-
} else if pnp, _ := entries.Get(".pnp.cjs"); pnp != nil && pnp.Kind(r.fs) == fs.FileEntry {
1261-
absPath := r.fs.Join(path, ".pnp.cjs")
1262-
if json := r.tryToExtractYarnPnPDataFromJS(absPath, pnpReportErrorsAboutMissingFiles); json.Data != nil {
1263-
info.pnpData = compileYarnPnPData(absPath, path, json)
1264-
}
1265-
} else if pnp, _ := entries.Get(".pnp.js"); pnp != nil && pnp.Kind(r.fs) == fs.FileEntry {
1266-
absPath := r.fs.Join(path, ".pnp.js")
1267-
if json := r.tryToExtractYarnPnPDataFromJS(absPath, pnpReportErrorsAboutMissingFiles); json.Data != nil {
1268-
info.pnpData = compileYarnPnPData(absPath, path, json)
1287+
if r.pnpManifest == nil {
1288+
if _, _, ok := fs.ParseYarnPnPVirtualPath(path); !ok {
1289+
if pnp, _ := entries.Get(".pnp.data.json"); pnp != nil && pnp.Kind(r.fs) == fs.FileEntry {
1290+
info.pnpManifestAbsPath = r.fs.Join(path, ".pnp.data.json")
1291+
} else if pnp, _ := entries.Get(".pnp.cjs"); pnp != nil && pnp.Kind(r.fs) == fs.FileEntry {
1292+
info.pnpManifestAbsPath = r.fs.Join(path, ".pnp.cjs")
1293+
} else if pnp, _ := entries.Get(".pnp.js"); pnp != nil && pnp.Kind(r.fs) == fs.FileEntry {
1294+
info.pnpManifestAbsPath = r.fs.Join(path, ".pnp.js")
12691295
}
12701296
}
12711297
}

scripts/js-api-tests.js

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2774,15 +2774,16 @@ require("/assets/file.png");
27742774
entryPoints: [entry],
27752775
bundle: true,
27762776
write: false,
2777+
absWorkingDir: testDir,
27772778
})
27782779

27792780
assert.strictEqual(value.outputFiles.length, 1)
27802781
assert.strictEqual(value.outputFiles[0].text, `(() => {
2781-
// scripts/.js-api-tests/yarnPnP_pnp_data_json/.yarn/cache/left-pad/index.js
2782+
// .yarn/cache/left-pad/index.js
27822783
function left_pad_default() {
27832784
}
27842785
2785-
// scripts/.js-api-tests/yarnPnP_pnp_data_json/entry.js
2786+
// entry.js
27862787
console.log(left_pad_default());
27872788
})();
27882789
`)
@@ -2840,15 +2841,16 @@ require("/assets/file.png");
28402841
entryPoints: [entry],
28412842
bundle: true,
28422843
write: false,
2844+
absWorkingDir: testDir,
28432845
})
28442846

28452847
assert.strictEqual(value.outputFiles.length, 1)
28462848
assert.strictEqual(value.outputFiles[0].text, `(() => {
2847-
// scripts/.js-api-tests/yarnPnP_pnp_js_object_literal/.yarn/cache/left-pad/index.js
2849+
// .yarn/cache/left-pad/index.js
28482850
function left_pad_default() {
28492851
}
28502852
2851-
// scripts/.js-api-tests/yarnPnP_pnp_js_object_literal/entry.js
2853+
// entry.js
28522854
console.log(left_pad_default());
28532855
})();
28542856
`)
@@ -2906,15 +2908,16 @@ require("/assets/file.png");
29062908
entryPoints: [entry],
29072909
bundle: true,
29082910
write: false,
2911+
absWorkingDir: testDir,
29092912
})
29102913

29112914
assert.strictEqual(value.outputFiles.length, 1)
29122915
assert.strictEqual(value.outputFiles[0].text, `(() => {
2913-
// scripts/.js-api-tests/yarnPnP_pnp_cjs_JSON_parse_string_literal/.yarn/cache/left-pad/index.js
2916+
// .yarn/cache/left-pad/index.js
29142917
function left_pad_default() {
29152918
}
29162919
2917-
// scripts/.js-api-tests/yarnPnP_pnp_cjs_JSON_parse_string_literal/entry.js
2920+
// entry.js
29182921
console.log(left_pad_default());
29192922
})();
29202923
`)
@@ -2974,15 +2977,16 @@ require("/assets/file.png");
29742977
entryPoints: [entry],
29752978
bundle: true,
29762979
write: false,
2980+
absWorkingDir: testDir,
29772981
})
29782982

29792983
assert.strictEqual(value.outputFiles.length, 1)
29802984
assert.strictEqual(value.outputFiles[0].text, `(() => {
2981-
// scripts/.js-api-tests/yarnPnP_pnp_cjs_JSON_parse_identifier/.yarn/cache/left-pad/index.js
2985+
// .yarn/cache/left-pad/index.js
29822986
function left_pad_default() {
29832987
}
29842988
2985-
// scripts/.js-api-tests/yarnPnP_pnp_cjs_JSON_parse_identifier/entry.js
2989+
// entry.js
29862990
console.log(left_pad_default());
29872991
})();
29882992
`)
@@ -3045,17 +3049,18 @@ require("/assets/file.png");
30453049
entryPoints: [entry],
30463050
bundle: true,
30473051
write: false,
3052+
absWorkingDir: testDir,
30483053
})
30493054

30503055
assert.strictEqual(value.outputFiles.length, 1)
30513056
assert.strictEqual(value.outputFiles[0].text, `(() => {
3052-
// scripts/.js-api-tests/yarnPnP_ignoreNestedManifests/__virtual__/whatever/0/bar/index.js
3057+
// __virtual__/whatever/0/bar/index.js
30533058
var bar_default = "bar";
30543059
3055-
// scripts/.js-api-tests/yarnPnP_ignoreNestedManifests/__virtual__/whatever/0/foo/index.js
3060+
// __virtual__/whatever/0/foo/index.js
30563061
var foo_default = "foo" + bar_default;
30573062
3058-
// scripts/.js-api-tests/yarnPnP_ignoreNestedManifests/entry.js
3063+
// entry.js
30593064
console.log(foo_default);
30603065
})();
30613066
`)
@@ -3107,13 +3112,14 @@ require("/assets/file.png");
31073112
entryPoints: [entry],
31083113
bundle: true,
31093114
write: false,
3115+
absWorkingDir: testDir,
31103116
})
31113117

31123118
assert.strictEqual(value.outputFiles.length, 1)
31133119
assert.strictEqual(value.outputFiles[0].text, `(() => {
31143120
var __pow = Math.pow;
31153121
3116-
// scripts/.js-api-tests/yarnPnP_tsconfig/entry.js
3122+
// entry.js
31173123
x = __pow(x, 2);
31183124
})();
31193125
`)
@@ -3161,14 +3167,15 @@ require("/assets/file.png");
31613167
entryPoints: [entry],
31623168
bundle: true,
31633169
write: false,
3170+
absWorkingDir: testDir,
31643171
})
31653172

31663173
assert.strictEqual(value.outputFiles.length, 1)
31673174
assert.strictEqual(value.outputFiles[0].text, `(() => {
3168-
// scripts/.js-api-tests/yarnPnP_indexJs/foo/index.js
3175+
// foo/index.js
31693176
var foo_default = success;
31703177
3171-
// scripts/.js-api-tests/yarnPnP_indexJs/entry.js
3178+
// entry.js
31723179
foo_default();
31733180
})();
31743181
`)
@@ -3242,11 +3249,12 @@ require("/assets/file.png");
32423249
entryPoints: [entry],
32433250
bundle: true,
32443251
write: false,
3252+
absWorkingDir: testDir,
32453253
})
32463254

32473255
assert.strictEqual(value.outputFiles.length, 1)
32483256
assert.strictEqual(value.outputFiles[0].text, `(() => {
3249-
// scripts/.js-api-tests/yarnPnP_depOfVirtual/.yarn/cache/dep/index.js
3257+
// .yarn/cache/dep/index.js
32503258
success();
32513259
})();
32523260
`)

0 commit comments

Comments
 (0)