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

Ensure self-name resolution uses same extension priorities as external imports #52185

Merged
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/compiler/moduleNameResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2313,7 +2313,18 @@ function loadModuleFromSelfNameReference(extensions: Extensions, moduleName: str
return undefined;
}
const trailingParts = parts.slice(nameParts.length);
return loadModuleFromExports(scope, extensions, !length(trailingParts) ? "." : `.${directorySeparator}${trailingParts.join(directorySeparator)}`, state, cache, redirectedReference);
const subpath = !length(trailingParts) ? "." : `.${directorySeparator}${trailingParts.join(directorySeparator)}`;
// Maybe TODO: splitting extensions into two priorities should be unnecessary, except
// https://github.com/microsoft/TypeScript/issues/50762 makes the behavior different.
// As long as that bug exists, we need to do two passes here in self-name loading
// in order to be consistent with (non-self) library-name loading in
// `loadModuleFromNearestNodeModulesDirectoryWorker`, which uses two passes in order
// to prioritize `@types` packages higher up the directory tree over untyped
// implementation packages.
const priorityExtensions = extensions & (Extensions.TypeScript | Extensions.Declaration);
const secondaryExtensions = extensions & ~(Extensions.TypeScript | Extensions.Declaration);
return loadModuleFromExports(scope, priorityExtensions, subpath, state, cache, redirectedReference)
|| loadModuleFromExports(scope, secondaryExtensions, subpath, state, cache, redirectedReference);
}

function loadModuleFromExports(scope: PackageJsonInfo, extensions: Extensions, subpath: string, state: ModuleResolutionState, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): SearchResult<Resolved> {
Expand Down
21 changes: 21 additions & 0 deletions tests/baselines/reference/selfNameModuleAugmentation.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
=== /node_modules/acorn-walk/dist/walk.d.ts ===
export {};
declare module 'acorn-walk' {
>'acorn-walk' : Symbol("/node_modules/acorn-walk/dist/walk", Decl(walk.d.ts, 0, 0), Decl(walk.d.ts, 0, 10))

export function simple(node: any, visitors: any, base?: any, state?: any): any;
>simple : Symbol(simple, Decl(walk.d.ts, 1, 29))
>node : Symbol(node, Decl(walk.d.ts, 2, 25))
>visitors : Symbol(visitors, Decl(walk.d.ts, 2, 35))
>base : Symbol(base, Decl(walk.d.ts, 2, 50))
>state : Symbol(state, Decl(walk.d.ts, 2, 62))
}

=== /node_modules/acorn-walk/dist/walk.mjs ===

export {};

=== /index.ts ===
import { simple } from 'acorn-walk';
>simple : Symbol(simple, Decl(index.ts, 0, 8))

39 changes: 39 additions & 0 deletions tests/baselines/reference/selfNameModuleAugmentation.trace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[
"======== Resolving module 'acorn-walk' from '/node_modules/acorn-walk/dist/walk.d.ts'. ========",
"Explicitly specified module resolution kind: 'Bundler'.",
"File '/node_modules/acorn-walk/dist/package.json' does not exist.",
"Found 'package.json' at '/node_modules/acorn-walk/package.json'.",
"Matched 'exports' condition 'import'.",
"Using 'exports' subpath '.' with target './dist/walk.mjs'.",
"File name '/node_modules/acorn-walk/dist/walk.mjs' has a '.mjs' extension - stripping it.",
"File '/node_modules/acorn-walk/dist/walk.mts' does not exist.",
"File '/node_modules/acorn-walk/dist/walk.d.mts' does not exist.",
"Saw non-matching condition 'require'.",
"Matched 'exports' condition 'default'.",
"Using 'exports' subpath '.' with target './dist/walk.js'.",
"File name '/node_modules/acorn-walk/dist/walk.js' has a '.js' extension - stripping it.",
"File '/node_modules/acorn-walk/dist/walk.ts' does not exist.",
"File '/node_modules/acorn-walk/dist/walk.tsx' does not exist.",
"File '/node_modules/acorn-walk/dist/walk.d.ts' exist - use it as a name resolution result.",
"Resolving real path for '/node_modules/acorn-walk/dist/walk.d.ts', result '/node_modules/acorn-walk/dist/walk.d.ts'.",
"======== Module name 'acorn-walk' was successfully resolved to '/node_modules/acorn-walk/dist/walk.d.ts' with Package ID 'acorn-walk/dist/walk.d.ts@8.2.0'. ========",
"======== Resolving module 'acorn-walk' from '/index.ts'. ========",
"Explicitly specified module resolution kind: 'Bundler'.",
"File '/package.json' does not exist.",
"Loading module 'acorn-walk' from 'node_modules' folder, target file types: TypeScript, JavaScript, Declaration, JSON.",
"File '/node_modules/acorn-walk/package.json' exists according to earlier cached lookups.",
"Matched 'exports' condition 'import'.",
"Using 'exports' subpath '.' with target './dist/walk.mjs'.",
"File name '/node_modules/acorn-walk/dist/walk.mjs' has a '.mjs' extension - stripping it.",
"File '/node_modules/acorn-walk/dist/walk.mts' does not exist.",
"File '/node_modules/acorn-walk/dist/walk.d.mts' does not exist.",
"Saw non-matching condition 'require'.",
"Matched 'exports' condition 'default'.",
"Using 'exports' subpath '.' with target './dist/walk.js'.",
"File name '/node_modules/acorn-walk/dist/walk.js' has a '.js' extension - stripping it.",
"File '/node_modules/acorn-walk/dist/walk.ts' does not exist.",
"File '/node_modules/acorn-walk/dist/walk.tsx' does not exist.",
"File '/node_modules/acorn-walk/dist/walk.d.ts' exist - use it as a name resolution result.",
"Resolving real path for '/node_modules/acorn-walk/dist/walk.d.ts', result '/node_modules/acorn-walk/dist/walk.d.ts'.",
"======== Module name 'acorn-walk' was successfully resolved to '/node_modules/acorn-walk/dist/walk.d.ts' with Package ID 'acorn-walk/dist/walk.d.ts@8.2.0'. ========"
]
21 changes: 21 additions & 0 deletions tests/baselines/reference/selfNameModuleAugmentation.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
=== /node_modules/acorn-walk/dist/walk.d.ts ===
export {};
declare module 'acorn-walk' {
>'acorn-walk' : typeof import("/node_modules/acorn-walk/dist/walk")

export function simple(node: any, visitors: any, base?: any, state?: any): any;
>simple : (node: any, visitors: any, base?: any, state?: any) => any
>node : any
>visitors : any
>base : any
>state : any
}

=== /node_modules/acorn-walk/dist/walk.mjs ===

export {};

=== /index.ts ===
import { simple } from 'acorn-walk';
>simple : (node: any, visitors: any, base?: any, state?: any) => any

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// @moduleResolution: bundler
// @allowJs: true
// @noEmit: true
// @traceResolution: true

// @Filename: /node_modules/acorn-walk/package.json
{
"name": "acorn-walk",
"version": "8.2.0",
"main": "dist/walk.js",
"types": "dist/walk.d.ts",
"exports": {
".": [
{
"import": "./dist/walk.mjs",
"require": "./dist/walk.js",
"default": "./dist/walk.js"
},
"./dist/walk.js"
],
"./package.json": "./package.json"
}
}

// The correct behavior for this test would be for both the module augmentation
// and the import to resolve to `walk.mjs` (triggering at least one implicit any
// error, I think?). However, https://github.com/microsoft/TypeScript/issues/50762
// causes the import to resolve through the `default` condition, replacing `.js`
// with `.d.ts` to find `walk.d.ts`. While this is incorrect, it's important that
// the module augmentation, which resolves through self-name resolution, resolves
// to the same module as the external import.

// @Filename: /node_modules/acorn-walk/dist/walk.d.ts
export {};
declare module 'acorn-walk' {
export function simple(node: any, visitors: any, base?: any, state?: any): any;
}

// @Filename: /node_modules/acorn-walk/dist/walk.mjs
export {};

// @Filename: /index.ts
import { simple } from 'acorn-walk';