diff --git a/CHANGELOG.md b/CHANGELOG.md index 84903af4e15..d94c63b49cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +* Fix Yarn PnP resolution failures due to backslashes in paths on Windows ([#2462](https://github.com/evanw/esbuild/issues/2462)) + + 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. + ## 0.15.2 * Fix Yarn PnP issue with packages containing `index.js` ([#2455](https://github.com/evanw/esbuild/issues/2455), [#2461](https://github.com/evanw/esbuild/issues/2461)) diff --git a/internal/resolver/yarnpnp.go b/internal/resolver/yarnpnp.go index c1d138028b1..9a1774e4869 100644 --- a/internal/resolver/yarnpnp.go +++ b/internal/resolver/yarnpnp.go @@ -277,6 +277,10 @@ func (r resolverQuery) findLocator(manifest *pnpData, moduleUrl string) (pnpIden relativeUrl, ok := r.fs.Rel(manifest.absDirPath, moduleUrl) if !ok { return pnpIdentAndReference{}, false + } else { + // Relative URLs on Windows will use \ instead of /, which will break + // everything we do below. Use normal slashes to keep things working. + relativeUrl = strings.ReplaceAll(relativeUrl, "\\", "/") } // The relative path must not start with ./; trim it if needed diff --git a/scripts/js-api-tests.js b/scripts/js-api-tests.js index 810586a72cc..0ad80b8f467 100644 --- a/scripts/js-api-tests.js +++ b/scripts/js-api-tests.js @@ -3171,6 +3171,84 @@ require("/assets/file.png"); // scripts/.js-api-tests/yarnPnP_indexJs/entry.js foo_default(); })(); +`) + }, + + async yarnPnP_depOfVirtual({ esbuild, testDir }) { + const entry = path.join(testDir, 'entry.js') + const pkg = path.join(testDir, '.yarn', 'cache', 'pkg', 'index.js') + const dep = path.join(testDir, '.yarn', 'cache', 'dep', 'index.js') + const manifest = path.join(testDir, '.pnp.data.json') + + await writeFileAsync(entry, `import 'pkg'`) + + await mkdirAsync(path.dirname(pkg), { recursive: true }) + await writeFileAsync(pkg, `import 'dep'`) + + await mkdirAsync(path.dirname(dep), { recursive: true }) + await writeFileAsync(dep, `success()`) + + await writeFileAsync(manifest, `{ + "packageRegistryData": [ + [null, [ + [null, { + "packageLocation": "./", + "packageDependencies": [ + ["pkg", "virtual:some-path"] + ], + "linkType": "SOFT" + }] + ]], + ["demo", [ + ["workspace:.", { + "packageLocation": "./", + "packageDependencies": [ + ["demo", "workspace:."], + ["pkg", "virtual:some-path"] + ], + "linkType": "SOFT" + }] + ]], + ["pkg", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/pkg/", + "packageDependencies": [ + ["pkg", "npm:1.0.0"] + ], + "linkType": "SOFT" + }], + ["virtual:some-path", { + "packageLocation": "./.yarn/__virtual__/pkg-virtual/0/cache/pkg/", + "packageDependencies": [ + ["pkg", "virtual:some-path"], + ["dep", "npm:1.0.0"] + ], + "linkType": "HARD" + }] + ]], + ["dep", [ + ["npm:1.0.0", { + "packageLocation": "./.yarn/cache/dep/", + "packageDependencies": [ + ["dep", "npm:1.0.0"] + ], + "linkType": "HARD" + }] + ]] + ] + }`) + + const value = await esbuild.build({ + entryPoints: [entry], + bundle: true, + write: false, + }) + + assert.strictEqual(value.outputFiles.length, 1) + assert.strictEqual(value.outputFiles[0].text, `(() => { + // scripts/.js-api-tests/yarnPnP_depOfVirtual/.yarn/cache/dep/index.js + success(); +})(); `) }, }