Skip to content

Commit

Permalink
Ensure self-name resolution uses same extension priorities as externa…
Browse files Browse the repository at this point in the history
…l imports (#52185)
  • Loading branch information
andrewbranch committed Jan 10, 2023
1 parent 32da8a2 commit ec9f6a4
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 1 deletion.
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';

0 comments on commit ec9f6a4

Please sign in to comment.