Skip to content

Commit 9b40267

Browse files
committed
fix #2455: strip / to fix index.js edge case
1 parent 4e68c27 commit 9b40267

File tree

4 files changed

+67
-4
lines changed

4 files changed

+67
-4
lines changed

CHANGELOG.md

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

33
## Unreleased
44

5+
* 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))
6+
7+
Yarn PnP's tests require the resolved paths to end in `/`. That's not how the rest of esbuild's internals work, however, and doing this messed up esbuild's node module path resolution regarding automatically-detected `index.js` files. Previously packages that relied on implicit `index.js` resolution rules didn't work with esbuild under Yarn PnP. Removing this slash has fixed esbuild's path resolution behavior regarding `index.js`, which should now the same both with and without Yarn PnP.
8+
59
* Fix Yarn PnP support for `extends` in `tsconfig.json` ([#2456](https://github.com/evanw/esbuild/issues/2456))
610

711
Previously using `extends` in `tsconfig.json` with a path in a Yarn PnP package didn't work. This is because the process of setting up package path resolution rules requires parsing `tsconfig.json` files (due to the `baseUrl` and `paths` features) and resolving `extends` to a package path requires package path resolution rules to already be set up, which is a circular dependency. This cycle is broken by using special rules for `extends` in `tsconfig.json` that bypasses esbuild's normal package path resolution process. This is why using `extends` with a Yarn PnP package didn't automatically work. With this release, these special rules have been modified to check for a Yarn PnP manifest so this case should work now.

internal/resolver/yarnpnp.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -266,10 +266,6 @@ func (r resolverQuery) resolveToUnqualified(specifier string, parentURL string,
266266

267267
// Return path.resolve(manifest.dirPath, dependencyPkg.packageLocation, modulePath)
268268
result := r.fs.Join(manifest.absDirPath, dependencyPkg.packageLocation, modulePath)
269-
if !strings.HasSuffix(result, "/") && ((modulePath != "" && strings.HasSuffix(modulePath, "/")) ||
270-
(modulePath == "" && strings.HasSuffix(dependencyPkg.packageLocation, "/"))) {
271-
result += "/" // This is important for matching Yarn PnP's expectations in tests
272-
}
273269
if r.debugLogs != nil {
274270
r.debugLogs.addNote(fmt.Sprintf(" Resolved %q via Yarn PnP to %q", specifier, result))
275271
}

internal/resolver/yarnpnp_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/json"
55
"fmt"
66
"io/ioutil"
7+
"strings"
78
"testing"
89

910
"github.com/evanw/esbuild/internal/config"
@@ -80,6 +81,13 @@ func TestYarnPnP(t *testing.T) {
8081
// incorrect. So we change the expected value of the test instead.
8182
if current.It == `shouldn't go through PnP when trying to resolve dependencies from packages covered by ignorePatternData` {
8283
expected = current.Imported
84+
} else if result != "error!" && !strings.HasSuffix(result, "/") {
85+
// This is important for matching Yarn PnP's expectations in tests,
86+
// but it's important for esbuild that the slash isn't present.
87+
// Otherwise esbuild's implementation of node module resolution
88+
// (which runs after Yarn PnP resolution) will fail. Specifically
89+
// "foo/" will look for "foo/foo.js" instead of "foo/index.js".
90+
result += "/"
8391
}
8492

8593
test.AssertEqualWithDiff(t, result, expected)

scripts/js-api-tests.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3116,6 +3116,61 @@ require("/assets/file.png");
31163116
// scripts/.js-api-tests/yarnPnP_tsconfig/entry.js
31173117
x = __pow(x, 2);
31183118
})();
3119+
`)
3120+
},
3121+
3122+
async yarnPnP_indexJs({ esbuild, testDir }) {
3123+
const entry = path.join(testDir, 'entry.js')
3124+
const fooIndex = path.join(testDir, 'foo', 'index.js')
3125+
const fooFoo = path.join(testDir, 'foo', 'foo.js')
3126+
const manifest = path.join(testDir, '.pnp.data.json')
3127+
3128+
await writeFileAsync(entry, `
3129+
import x from '@some/pkg'
3130+
x()
3131+
`)
3132+
3133+
await mkdirAsync(path.dirname(fooIndex), { recursive: true })
3134+
await writeFileAsync(fooIndex, `export default success`)
3135+
3136+
await mkdirAsync(path.dirname(fooFoo), { recursive: true })
3137+
await writeFileAsync(fooFoo, `failure!`)
3138+
3139+
await writeFileAsync(manifest, `{
3140+
"packageRegistryData": [
3141+
[null, [
3142+
[null, {
3143+
"packageLocation": "./",
3144+
"packageDependencies": [
3145+
["@some/pkg", "npm:1.0.0"]
3146+
],
3147+
"linkType": "SOFT"
3148+
}]
3149+
]],
3150+
["@some/pkg", [
3151+
["npm:1.0.0", {
3152+
"packageLocation": "./foo/",
3153+
"packageDependencies": [],
3154+
"linkType": "HARD"
3155+
}]
3156+
]]
3157+
]
3158+
}`)
3159+
3160+
const value = await esbuild.build({
3161+
entryPoints: [entry],
3162+
bundle: true,
3163+
write: false,
3164+
})
3165+
3166+
assert.strictEqual(value.outputFiles.length, 1)
3167+
assert.strictEqual(value.outputFiles[0].text, `(() => {
3168+
// scripts/.js-api-tests/yarnPnP_indexJs/foo/index.js
3169+
var foo_default = success;
3170+
3171+
// scripts/.js-api-tests/yarnPnP_indexJs/entry.js
3172+
foo_default();
3173+
})();
31193174
`)
31203175
},
31213176
}

0 commit comments

Comments
 (0)