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

refactor(pnp): normalize resolve.exports errors #3921

Merged
merged 4 commits into from
Jan 5, 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
44 changes: 25 additions & 19 deletions .pnp.cjs

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

28 changes: 28 additions & 0 deletions .yarn/versions/7c5281df.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
releases:
"@yarnpkg/cli": patch
"@yarnpkg/plugin-pnp": patch
"@yarnpkg/pnp": patch

declined:
- "@yarnpkg/esbuild-plugin-pnp"
- "@yarnpkg/plugin-compat"
- "@yarnpkg/plugin-constraints"
- "@yarnpkg/plugin-dlx"
- "@yarnpkg/plugin-essentials"
- "@yarnpkg/plugin-init"
- "@yarnpkg/plugin-interactive-tools"
- "@yarnpkg/plugin-nm"
- "@yarnpkg/plugin-npm-cli"
- "@yarnpkg/plugin-pack"
- "@yarnpkg/plugin-patch"
- "@yarnpkg/plugin-pnpm"
- "@yarnpkg/plugin-stage"
- "@yarnpkg/plugin-typescript"
- "@yarnpkg/plugin-version"
- "@yarnpkg/plugin-workspace-tools"
- "@yarnpkg/builder"
- "@yarnpkg/core"
- "@yarnpkg/doctor"
- "@yarnpkg/nm"
- "@yarnpkg/pnpify"
- "@yarnpkg/sdks"
2 changes: 1 addition & 1 deletion packages/yarnpkg-pnp/sources/hook.js

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions packages/yarnpkg-pnp/sources/loader/internalTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {Path, npath} from '@yarnpkg/fslib';
export enum ErrorCode {
API_ERROR = `API_ERROR`,
BUILTIN_NODE_RESOLUTION_FAILED = `BUILTIN_NODE_RESOLUTION_FAILED`,
EXPORTS_RESOLUTION_FAILED = `EXPORTS_RESOLUTION_FAILED`,
MISSING_DEPENDENCY = `MISSING_DEPENDENCY`,
MISSING_PEER_DEPENDENCY = `MISSING_PEER_DEPENDENCY`,
QUALIFIED_PATH_RESOLUTION_FAILED = `QUALIFIED_PATH_RESOLUTION_FAILED`,
Expand All @@ -26,8 +27,8 @@ const MODULE_NOT_FOUND_ERRORS = new Set([
* by third-parties.
*/

export function makeError(pnpCode: ErrorCode, message: string, data: Record<string, any> = {}): Error & {code: string, pnpCode: ErrorCode, data: Record<string, any>} {
const code = MODULE_NOT_FOUND_ERRORS.has(pnpCode)
export function makeError(pnpCode: ErrorCode, message: string, data: Record<string, any> = {}, code?: string): Error & {code: string, pnpCode: ErrorCode, data: Record<string, any>} {
code ??= MODULE_NOT_FOUND_ERRORS.has(pnpCode)
? `MODULE_NOT_FOUND`
: pnpCode;

Expand Down
70 changes: 41 additions & 29 deletions packages/yarnpkg-pnp/sources/loader/makeApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,13 +229,25 @@ export function makeApi(runtimeState: RuntimeState, opts: MakeApiOptions): PnpAp
if (!isRelativeRegexp.test(subpath))
subpath = `./${subpath}` as PortablePath;

const resolvedExport = resolveExport(pkgJson, ppath.normalize(subpath), {
// TODO: implement support for the --conditions flag
// Waiting on https://github.com/nodejs/node/issues/36935
// @ts-expect-error - Type should be Iterable<string>
conditions,
unsafe: true,
});
let resolvedExport;
try {
resolvedExport = resolveExport(pkgJson, ppath.normalize(subpath), {
// TODO: implement support for the --conditions flag
// Waiting on https://github.com/nodejs/node/issues/36935
// @ts-expect-error - Type should be Iterable<string>
conditions,
unsafe: true,
});
} catch (error) {
throw makeError(
ErrorCode.EXPORTS_RESOLUTION_FAILED,
error.message,
{unqualifiedPath: getPathForDisplay(unqualifiedPath), locator, pkgJson, subpath: getPathForDisplay(subpath), conditions},
// Currently, resolve.exports only throws ERR_PACKAGE_PATH_NOT_EXPORTED errors, but without assigning the error code.
// TODO: Use error.code once https://github.com/lukeed/resolve.exports/pull/6 gets merged.
`ERR_PACKAGE_PATH_NOT_EXPORTED`,
);
}

if (typeof resolvedExport === `string`)
return ppath.join(packageLocation, resolvedExport as PortablePath);
Expand Down Expand Up @@ -815,15 +827,15 @@ export function makeApi(runtimeState: RuntimeState, opts: MakeApiOptions): PnpAp
throw makeError(
ErrorCode.QUALIFIED_PATH_RESOLUTION_FAILED,
`${errorMessage}\n\nMissing package: ${containingPackage.name}@${containingPackage.reference}\nExpected package location: ${getPathForDisplay(packageLocation)}\n`,
{unqualifiedPath: unqualifiedPathForDisplay},
{unqualifiedPath: unqualifiedPathForDisplay, extensions},
);
}
}

throw makeError(
ErrorCode.QUALIFIED_PATH_RESOLUTION_FAILED,
`Qualified path resolution failed - none of those files can be found on the disk.\n\nSource path: ${unqualifiedPathForDisplay}\n${candidates.map(candidate => `Not found: ${getPathForDisplay(candidate)}\n`).join(``)}`,
{unqualifiedPath: unqualifiedPathForDisplay},
{unqualifiedPath: unqualifiedPathForDisplay, extensions},
);
}
}
Expand All @@ -837,33 +849,33 @@ export function makeApi(runtimeState: RuntimeState, opts: MakeApiOptions): PnpAp
*/

function resolveRequest(request: PortablePath, issuer: PortablePath | null, {considerBuiltins, extensions, conditions}: ResolveRequestOptions = {}): PortablePath | null {
const unqualifiedPath = resolveToUnqualified(request, issuer, {considerBuiltins});
try {
const unqualifiedPath = resolveToUnqualified(request, issuer, {considerBuiltins});

// If the request is the pnpapi, we can just return the unqualifiedPath
// without having to apply the exports resolution or the extension resolution
// (opts.pnpapiResolution is always a full path - makeManager enforces this by stat-ing it)
if (request === `pnpapi`)
return unqualifiedPath;
// If the request is the pnpapi, we can just return the unqualifiedPath
// without having to apply the exports resolution or the extension resolution
// (opts.pnpapiResolution is always a full path - makeManager enforces this by stat-ing it)
if (request === `pnpapi`)
return unqualifiedPath;

if (unqualifiedPath === null)
return null;
if (unqualifiedPath === null)
return null;

const isIssuerIgnored = () =>
issuer !== null
? isPathIgnored(issuer)
: false;
const isIssuerIgnored = () =>
issuer !== null
? isPathIgnored(issuer)
: false;

const remappedPath = (!considerBuiltins || !nodeUtils.isBuiltinModule(request)) && !isIssuerIgnored()
? resolveUnqualifiedExport(request, unqualifiedPath, conditions)
: unqualifiedPath;
const remappedPath = (!considerBuiltins || !nodeUtils.isBuiltinModule(request)) && !isIssuerIgnored()
? resolveUnqualifiedExport(request, unqualifiedPath, conditions)
: unqualifiedPath;

try {
return resolveUnqualified(remappedPath, {extensions});
} catch (resolutionError) {
if (resolutionError.pnpCode === `QUALIFIED_PATH_RESOLUTION_FAILED`)
Object.assign(resolutionError.data, {request: getPathForDisplay(request), issuer: issuer && getPathForDisplay(issuer)});
} catch (error) {
if (Object.prototype.hasOwnProperty.call(error, `pnpCode`))
Object.assign(error.data, {request: getPathForDisplay(request), issuer: issuer && getPathForDisplay(issuer)});

throw resolutionError;
throw error;
}
}

Expand Down