Skip to content

Commit 3dd7b84

Browse files
authored
Merge pull request #33567 from microsoft/resolution
Sort the paths for module specifier by closeness to importing file path
2 parents 250d5a8 + 91c66a0 commit 3dd7b84

8 files changed

+253
-62
lines changed

src/compiler/moduleSpecifiers.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,18 @@ namespace ts.moduleSpecifiers {
186186
return result;
187187
}
188188

189+
function numberOfDirectorySeparators(str: string) {
190+
const match = str.match(/\//g);
191+
return match ? match.length : 0;
192+
}
193+
194+
function comparePathsByNumberOfDirectrorySeparators(a: string, b: string) {
195+
return compareValues(
196+
numberOfDirectorySeparators(a),
197+
numberOfDirectorySeparators(b)
198+
);
199+
}
200+
189201
/**
190202
* Looks for existing imports that use symlinks to this module.
191203
* Symlinks will be returned first so they are preferred over the real path.
@@ -214,7 +226,32 @@ namespace ts.moduleSpecifiers {
214226
}
215227
});
216228
result.push(...targets);
217-
return result;
229+
if (result.length < 2) return result;
230+
231+
// Sort by paths closest to importing file Name directory
232+
const allFileNames = arrayToMap(result, identity, getCanonicalFileName);
233+
const sortedPaths: string[] = [];
234+
for (
235+
let directory = getDirectoryPath(toPath(importingFileName, cwd, getCanonicalFileName));
236+
allFileNames.size !== 0;
237+
directory = getDirectoryPath(directory)
238+
) {
239+
const directoryStart = ensureTrailingDirectorySeparator(directory);
240+
let pathsInDirectory: string[] | undefined;
241+
allFileNames.forEach((canonicalFileName, fileName) => {
242+
if (startsWith(canonicalFileName, directoryStart)) {
243+
(pathsInDirectory || (pathsInDirectory = [])).push(fileName);
244+
allFileNames.delete(fileName);
245+
}
246+
});
247+
if (pathsInDirectory) {
248+
if (pathsInDirectory.length > 1) {
249+
pathsInDirectory.sort(comparePathsByNumberOfDirectrorySeparators);
250+
}
251+
sortedPaths.push(...pathsInDirectory);
252+
}
253+
}
254+
return sortedPaths;
218255
}
219256

220257
function tryGetModuleNameFromAmbientModule(moduleSymbol: Symbol): string | undefined {

src/compiler/watch.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,11 @@ namespace ts {
131131
export function listFiles(program: ProgramToEmitFilesAndReportErrors, writeFileName: (s: string) => void) {
132132
if (program.getCompilerOptions().listFiles) {
133133
forEach(program.getSourceFiles(), file => {
134-
writeFileName(file.fileName);
134+
writeFileName(
135+
!file.redirectInfo ?
136+
file.fileName :
137+
`${file.fileName} -> ${file.redirectInfo.redirectTarget.fileName}`
138+
);
135139
});
136140
}
137141
}

src/testRunner/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@
109109
"unittests/tsbuild/transitiveReferences.ts",
110110
"unittests/tsbuild/watchEnvironment.ts",
111111
"unittests/tsbuild/watchMode.ts",
112+
"unittests/tsc/declarationEmit.ts",
112113
"unittests/tscWatch/consoleClearing.ts",
113114
"unittests/tscWatch/emit.ts",
114115
"unittests/tscWatch/emitAndErrorUpdates.ts",
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
namespace ts {
2+
describe("unittests:: tsc:: declarationEmit::", () => {
3+
verifyTsc({
4+
scenario: "declarationEmit",
5+
subScenario: "when same version is referenced through source and another symlinked package",
6+
fs: () => {
7+
const fsaPackageJson = utils.dedent`
8+
{
9+
"name": "typescript-fsa",
10+
"version": "3.0.0-beta-2"
11+
}`;
12+
const fsaIndex = utils.dedent`
13+
export interface Action<Payload> {
14+
type: string;
15+
payload: Payload;
16+
}
17+
export declare type ActionCreator<Payload> = {
18+
type: string;
19+
(payload: Payload): Action<Payload>;
20+
}
21+
export interface ActionCreatorFactory {
22+
<Payload = void>(type: string): ActionCreator<Payload>;
23+
}
24+
export declare function actionCreatorFactory(prefix?: string | null): ActionCreatorFactory;
25+
export default actionCreatorFactory;`;
26+
return loadProjectFromFiles({
27+
"/src/plugin-two/index.d.ts": utils.dedent`
28+
declare const _default: {
29+
features: {
30+
featureOne: {
31+
actions: {
32+
featureOne: {
33+
(payload: {
34+
name: string;
35+
order: number;
36+
}, meta?: {
37+
[key: string]: any;
38+
}): import("typescript-fsa").Action<{
39+
name: string;
40+
order: number;
41+
}>;
42+
};
43+
};
44+
path: string;
45+
};
46+
};
47+
};
48+
export default _default;`,
49+
"/src/plugin-two/node_modules/typescript-fsa/package.json": fsaPackageJson,
50+
"/src/plugin-two/node_modules/typescript-fsa/index.d.ts": fsaIndex,
51+
"/src/plugin-one/tsconfig.json": utils.dedent`
52+
{
53+
"compilerOptions": {
54+
"target": "es5",
55+
"declaration": true,
56+
},
57+
}`,
58+
"/src/plugin-one/index.ts": utils.dedent`
59+
import pluginTwo from "plugin-two"; // include this to add reference to symlink`,
60+
"/src/plugin-one/action.ts": utils.dedent`
61+
import { actionCreatorFactory } from "typescript-fsa"; // Include version of shared lib
62+
const action = actionCreatorFactory("somekey");
63+
const featureOne = action<{ route: string }>("feature-one");
64+
export const actions = { featureOne };`,
65+
"/src/plugin-one/node_modules/typescript-fsa/package.json": fsaPackageJson,
66+
"/src/plugin-one/node_modules/typescript-fsa/index.d.ts": fsaIndex,
67+
"/src/plugin-one/node_modules/plugin-two": new vfs.Symlink("/src/plugin-two"),
68+
});
69+
},
70+
commandLineArgs: ["-p", "src/plugin-one", "--listFiles"]
71+
});
72+
73+
verifyTsc({
74+
scenario: "declarationEmit",
75+
subScenario: "when pkg references sibling package through indirect symlink",
76+
fs: () => loadProjectFromFiles({
77+
"/src/pkg1/dist/index.d.ts": utils.dedent`
78+
export * from './types';`,
79+
"/src/pkg1/dist/types.d.ts": utils.dedent`
80+
export declare type A = {
81+
id: string;
82+
};
83+
export declare type B = {
84+
id: number;
85+
};
86+
export declare type IdType = A | B;
87+
export declare class MetadataAccessor<T, D extends IdType = IdType> {
88+
readonly key: string;
89+
private constructor();
90+
toString(): string;
91+
static create<T, D extends IdType = IdType>(key: string): MetadataAccessor<T, D>;
92+
}`,
93+
"/src/pkg1/package.json": utils.dedent`
94+
{
95+
"name": "@raymondfeng/pkg1",
96+
"version": "1.0.0",
97+
"description": "",
98+
"main": "dist/index.js",
99+
"typings": "dist/index.d.ts"
100+
}`,
101+
"/src/pkg2/dist/index.d.ts": utils.dedent`
102+
export * from './types';`,
103+
"/src/pkg2/dist/types.d.ts": utils.dedent`
104+
export {MetadataAccessor} from '@raymondfeng/pkg1';`,
105+
"/src/pkg2/package.json": utils.dedent`
106+
{
107+
"name": "@raymondfeng/pkg2",
108+
"version": "1.0.0",
109+
"description": "",
110+
"main": "dist/index.js",
111+
"typings": "dist/index.d.ts"
112+
}`,
113+
"/src/pkg3/src/index.ts": utils.dedent`
114+
export * from './keys';`,
115+
"/src/pkg3/src/keys.ts": utils.dedent`
116+
import {MetadataAccessor} from "@raymondfeng/pkg2";
117+
export const ADMIN = MetadataAccessor.create<boolean>('1');`,
118+
"/src/pkg3/tsconfig.json": utils.dedent`
119+
{
120+
"compilerOptions": {
121+
"outDir": "dist",
122+
"rootDir": "src",
123+
"target": "es5",
124+
"module": "commonjs",
125+
"strict": true,
126+
"esModuleInterop": true,
127+
"declaration": true
128+
}
129+
}`,
130+
"/src/pkg2/node_modules/@raymondfeng/pkg1": new vfs.Symlink("/src/pkg1"),
131+
"/src/pkg3/node_modules/@raymondfeng/pkg2": new vfs.Symlink("/src/pkg2"),
132+
}),
133+
commandLineArgs: ["-p", "src/pkg3", "--listFiles"]
134+
});
135+
});
136+
}

tests/baselines/reference/declarationEmitReexportedSymlinkReference3.errors.txt

Lines changed: 0 additions & 60 deletions
This file was deleted.

tests/baselines/reference/declarationEmitReexportedSymlinkReference3.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
5757
__export(require("./keys"));
5858

5959

60+
//// [keys.d.ts]
61+
import { MetadataAccessor } from "@raymondfeng/pkg2";
62+
export declare const ADMIN: MetadataAccessor<boolean, import("../../pkg1/dist").IdType>;
6063
//// [index.d.ts]
6164
export * from './keys';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//// [/lib/initial-buildOutput.txt]
2+
/lib/tsc -p src/pkg3 --listFiles
3+
src/pkg3/src/keys.ts(2,14): error TS2742: The inferred type of 'ADMIN' cannot be named without a reference to '@raymondfeng/pkg2/node_modules/@raymondfeng/pkg1'. This is likely not portable. A type annotation is necessary.
4+
/lib/lib.d.ts
5+
/src/pkg3/node_modules/@raymondfeng/pkg2/node_modules/@raymondfeng/pkg1/dist/types.d.ts
6+
/src/pkg3/node_modules/@raymondfeng/pkg2/node_modules/@raymondfeng/pkg1/dist/index.d.ts
7+
/src/pkg3/node_modules/@raymondfeng/pkg2/dist/types.d.ts
8+
/src/pkg3/node_modules/@raymondfeng/pkg2/dist/index.d.ts
9+
/src/pkg3/src/keys.ts
10+
/src/pkg3/src/index.ts
11+
exitCode:: 1
12+
13+
14+
//// [/src/pkg3/dist/index.d.ts]
15+
export * from './keys';
16+
17+
18+
//// [/src/pkg3/dist/index.js]
19+
"use strict";
20+
function __export(m) {
21+
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
22+
}
23+
Object.defineProperty(exports, "__esModule", { value: true });
24+
__export(require("./keys"));
25+
26+
27+
//// [/src/pkg3/dist/keys.js]
28+
"use strict";
29+
Object.defineProperty(exports, "__esModule", { value: true });
30+
var pkg2_1 = require("@raymondfeng/pkg2");
31+
exports.ADMIN = pkg2_1.MetadataAccessor.create('1');
32+
33+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//// [/lib/initial-buildOutput.txt]
2+
/lib/tsc -p src/plugin-one --listFiles
3+
/lib/lib.d.ts
4+
/src/plugin-one/node_modules/typescript-fsa/index.d.ts
5+
/src/plugin-one/action.ts
6+
/src/plugin-one/node_modules/plugin-two/node_modules/typescript-fsa/index.d.ts -> /src/plugin-one/node_modules/typescript-fsa/index.d.ts
7+
/src/plugin-one/node_modules/plugin-two/index.d.ts
8+
/src/plugin-one/index.ts
9+
exitCode:: 0
10+
11+
12+
//// [/src/plugin-one/action.d.ts]
13+
export declare const actions: {
14+
featureOne: import("typescript-fsa").ActionCreator<{
15+
route: string;
16+
}>;
17+
};
18+
19+
20+
//// [/src/plugin-one/action.js]
21+
"use strict";
22+
Object.defineProperty(exports, "__esModule", { value: true });
23+
var typescript_fsa_1 = require("typescript-fsa"); // Include version of shared lib
24+
var action = typescript_fsa_1.actionCreatorFactory("somekey");
25+
var featureOne = action("feature-one");
26+
exports.actions = { featureOne: featureOne };
27+
28+
29+
//// [/src/plugin-one/index.d.ts]
30+
export {};
31+
32+
33+
//// [/src/plugin-one/index.js]
34+
"use strict";
35+
Object.defineProperty(exports, "__esModule", { value: true });
36+
37+

0 commit comments

Comments
 (0)