Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: prefer exports when resolving #10371

Merged
merged 3 commits into from
Oct 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 29 additions & 5 deletions packages/vite/src/node/plugins/resolve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -648,13 +648,36 @@ export function tryNodeResolve(
basedir = nestedResolveFrom(nestedRoot, basedir, preserveSymlinks)
}

// nearest package.json
let nearestPkg: PackageData | undefined
// nearest package.json that may have the `exports` field
let pkg: PackageData | undefined
const pkgId = possiblePkgIds.reverse().find((pkgId) => {
pkg = resolvePackageData(pkgId, basedir, preserveSymlinks, packageCache)!
return pkg

let pkgId = possiblePkgIds.reverse().find((pkgId) => {
nearestPkg = resolvePackageData(
pkgId,
basedir,
preserveSymlinks,
packageCache
)!
return nearestPkg
})!

if (!pkg) {
const rootPkgId = possiblePkgIds[0]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is actually the root package ID, unless you mean something different than "the @foo/bar part of @foo/bar/x/y/z".

Reason being, the possiblePkgIds array is reversed in the statement before this one.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, you're probably right about that

const rootPkg = resolvePackageData(
rootPkgId,
basedir,
preserveSymlinks,
packageCache
)!
if (rootPkg?.data?.exports) {
pkg = rootPkg
pkgId = rootPkgId
} else {
pkg = nearestPkg
}

if (!pkg || !nearestPkg) {
// if import can't be found, check if it's an optional peer dep.
// if so, we can resolve to a special id that errors only when imported.
if (
Expand Down Expand Up @@ -753,7 +776,8 @@ export function tryNodeResolve(
}

const ext = path.extname(resolved)
const isCJS = ext === '.cjs' || (ext === '.js' && pkg.data.type !== 'module')
const isCJS =
ext === '.cjs' || (ext === '.js' && nearestPkg.data.type !== 'module')

if (
!options.ssrOptimizeCheck &&
Expand Down
2 changes: 1 addition & 1 deletion packages/vite/src/node/ssr/ssrModuleLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ async function instantiateModule(
preserveSymlinks,
isBuild: true,
isProduction,
isRequire: true,
isRequire: false,
root
}

Expand Down
7 changes: 7 additions & 0 deletions playground/resolve/__tests__/resolve.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ test('deep import with exports field + mapped dir', async () => {
)
})

// this is how Svelte 3 is packaged
test('deep import with exports and legacy fallback', async () => {
expect(await page.textContent('.exports-legacy-fallback')).toMatch(
'[success]'
)
})

test('Respect exports field env key priority', async () => {
expect(await page.textContent('.exports-env')).toMatch('[success]')
})
Expand Down
1 change: 1 addition & 0 deletions playground/resolve/exports-legacy-fallback/dir/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const msg = '[fail] mapped js file'
1 change: 1 addition & 0 deletions playground/resolve/exports-legacy-fallback/dir/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const msg = '[success] mapped mjs file'
4 changes: 4 additions & 0 deletions playground/resolve/exports-legacy-fallback/dir/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"main": "index.js",
"module": "index.mjs"
}
1 change: 1 addition & 0 deletions playground/resolve/exports-legacy-fallback/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default 5
12 changes: 12 additions & 0 deletions playground/resolve/exports-legacy-fallback/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "resolve-exports-legacy-fallback",
"private": true,
"version": "1.0.0",
"exports": {
"./dir": {
"import": "./dir/index.mjs",
"require": "./dir/index.js"
},
".": "index.js"
}
}
6 changes: 6 additions & 0 deletions playground/resolve/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ <h2>Deep import with exports field + mapped directory</h2>
<h2>Exports field env priority</h2>
<p class="exports-env">fail</p>

<h2>Exports with legacy fallback</h2>
<p class="exports-legacy-fallback">fail</p>

<h2>Resolve /index.*</h2>
<p class="index">fail</p>

Expand Down Expand Up @@ -163,6 +166,9 @@ <h2>resolve package that contains # in path</h2>
import { msg as exportsEnvMsg } from 'resolve-exports-env'
text('.exports-env', exportsEnvMsg)

import { msg as exportsLegacyFallbackMsg } from 'resolve-exports-legacy-fallback/dir'
text('.exports-legacy-fallback', exportsLegacyFallbackMsg)

// implicit index resolving
import { foo } from './util'
text('.index', foo())
Expand Down
1 change: 1 addition & 0 deletions playground/resolve/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"resolve-custom-condition": "link:./custom-condition",
"resolve-custom-main-field": "link:./custom-main-field",
"resolve-exports-env": "link:./exports-env",
"resolve-exports-legacy-fallback": "link:./exports-legacy-fallback",
"resolve-exports-path": "link:./exports-path",
"resolve-linked": "workspace:*"
}
Expand Down
8 changes: 8 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.