@nifrajs/ts-plugin 3.1.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +105 -4
- package/dist/index.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +207 -6
package/dist/index.cjs
CHANGED
|
@@ -83,6 +83,97 @@ function findRoutesDir(fromFile, exists = import_node_fs.existsSync) {
|
|
|
83
83
|
}
|
|
84
84
|
return;
|
|
85
85
|
}
|
|
86
|
+
function normalizeProgramPath(fileName) {
|
|
87
|
+
const slashed = fileName.replaceAll("\\", "/");
|
|
88
|
+
const withoutDevicePrefix = slashed.replace(/^\/\/\?\/UNC\//i, "//").replace(/^\/\/\?\//, "");
|
|
89
|
+
return process.platform === "win32" ? withoutDevicePrefix.toLowerCase() : withoutDevicePrefix;
|
|
90
|
+
}
|
|
91
|
+
function canonicalProgramPaths(tsm, fileName) {
|
|
92
|
+
const absolute = import_node_path.resolve(fileName);
|
|
93
|
+
const paths = new Set([normalizeProgramPath(absolute)]);
|
|
94
|
+
const tsmRealpath = tsm.sys.realpath;
|
|
95
|
+
const resolvers = [
|
|
96
|
+
...typeof tsmRealpath === "function" ? [tsmRealpath] : [],
|
|
97
|
+
import_node_fs.realpathSync.native,
|
|
98
|
+
import_node_fs.realpathSync
|
|
99
|
+
];
|
|
100
|
+
for (const resolver of resolvers) {
|
|
101
|
+
try {
|
|
102
|
+
paths.add(normalizeProgramPath(resolver(absolute)));
|
|
103
|
+
} catch {}
|
|
104
|
+
}
|
|
105
|
+
return [...paths];
|
|
106
|
+
}
|
|
107
|
+
function typeScriptProgramPath(tsm, program, fileName) {
|
|
108
|
+
const toPath = tsm.toPath;
|
|
109
|
+
if (typeof toPath !== "function")
|
|
110
|
+
return;
|
|
111
|
+
const getCanonicalFileName = tsm.sys.useCaseSensitiveFileNames ? (value) => value : (value) => value.toLowerCase();
|
|
112
|
+
try {
|
|
113
|
+
return toPath(import_node_path.resolve(fileName), program.getCurrentDirectory(), getCanonicalFileName);
|
|
114
|
+
} catch {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function filesystemIdentity(fileName) {
|
|
119
|
+
try {
|
|
120
|
+
const info = import_node_fs.statSync(import_node_path.resolve(fileName), { bigint: true });
|
|
121
|
+
if (info.ino === 0n)
|
|
122
|
+
return;
|
|
123
|
+
return `bigint:${info.dev}:${info.ino}`;
|
|
124
|
+
} catch {
|
|
125
|
+
try {
|
|
126
|
+
const info = import_node_fs.statSync(import_node_path.resolve(fileName));
|
|
127
|
+
if (info.ino === 0)
|
|
128
|
+
return;
|
|
129
|
+
return `number:${info.dev}:${info.ino}`;
|
|
130
|
+
} catch {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function programSourceFile(tsm, program, fileName) {
|
|
136
|
+
if (program === undefined)
|
|
137
|
+
return;
|
|
138
|
+
const direct = program.getSourceFile(fileName);
|
|
139
|
+
if (direct !== undefined)
|
|
140
|
+
return direct;
|
|
141
|
+
const programPath = typeScriptProgramPath(tsm, program, fileName);
|
|
142
|
+
if (programPath !== undefined) {
|
|
143
|
+
const byTypeScriptPath = program.getSourceFileByPath(programPath);
|
|
144
|
+
if (byTypeScriptPath !== undefined)
|
|
145
|
+
return byTypeScriptPath;
|
|
146
|
+
}
|
|
147
|
+
const wanted = new Set(canonicalProgramPaths(tsm, fileName));
|
|
148
|
+
const byPath = program.getSourceFiles().find((source) => canonicalProgramPaths(tsm, source.fileName).some((path) => wanted.has(path)));
|
|
149
|
+
if (byPath !== undefined)
|
|
150
|
+
return byPath;
|
|
151
|
+
const wantedIdentity = filesystemIdentity(fileName);
|
|
152
|
+
if (wantedIdentity === undefined)
|
|
153
|
+
return;
|
|
154
|
+
return program.getSourceFiles().find((source) => filesystemIdentity(source.fileName) === wantedIdentity);
|
|
155
|
+
}
|
|
156
|
+
function hostSourceFile(tsm, host, program, fileName) {
|
|
157
|
+
if (host === undefined)
|
|
158
|
+
return;
|
|
159
|
+
const snapshot = host.getScriptSnapshot(fileName);
|
|
160
|
+
if (snapshot === undefined)
|
|
161
|
+
return;
|
|
162
|
+
const text = snapshot.getText(0, snapshot.getLength());
|
|
163
|
+
const options = program?.getCompilerOptions();
|
|
164
|
+
const getScriptKindFromFileName = tsm.getScriptKindFromFileName;
|
|
165
|
+
return tsm.createSourceFile(fileName, text, options?.target ?? tsm.ScriptTarget.Latest, true, host.getScriptKind?.(fileName) ?? (typeof getScriptKindFromFileName === "function" ? getScriptKindFromFileName(fileName) : tsm.ScriptKind.TSX));
|
|
166
|
+
}
|
|
167
|
+
function diskSourceFile(tsm, program, fileName) {
|
|
168
|
+
let text;
|
|
169
|
+
try {
|
|
170
|
+
text = import_node_fs.readFileSync(fileName, "utf8");
|
|
171
|
+
} catch {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const getScriptKindFromFileName = tsm.getScriptKindFromFileName;
|
|
175
|
+
return tsm.createSourceFile(fileName, text, program?.getCompilerOptions().target ?? tsm.ScriptTarget.Latest, true, typeof getScriptKindFromFileName === "function" ? getScriptKindFromFileName(fileName) : tsm.ScriptKind.TSX);
|
|
176
|
+
}
|
|
86
177
|
function innermostNodeAt(tsm, source, node, position) {
|
|
87
178
|
if (position < node.getStart(source) || position > node.getEnd())
|
|
88
179
|
return;
|
|
@@ -103,8 +194,9 @@ function findRoutePathLiteral(tsm, source, position) {
|
|
|
103
194
|
const start = node.getStart(source) + 1;
|
|
104
195
|
return { text: node.text, span: { start, length: Math.max(0, node.getWidth(source) - 2) } };
|
|
105
196
|
}
|
|
106
|
-
function routeDefinitionAt(tsm, ls, fileName, position) {
|
|
107
|
-
const
|
|
197
|
+
function routeDefinitionAt(tsm, ls, host, fileName, position) {
|
|
198
|
+
const program = ls.getProgram();
|
|
199
|
+
const source = programSourceFile(tsm, program, fileName) ?? hostSourceFile(tsm, host, program, fileName) ?? diskSourceFile(tsm, program, fileName);
|
|
108
200
|
if (source === undefined)
|
|
109
201
|
return;
|
|
110
202
|
const literal = findRoutePathLiteral(tsm, source, position);
|
|
@@ -147,9 +239,18 @@ function init(modules) {
|
|
|
147
239
|
const writable = proxy;
|
|
148
240
|
for (const key of Object.keys(ls)) {
|
|
149
241
|
const member = ls[key];
|
|
150
|
-
writable[key] = typeof member === "function" ?
|
|
242
|
+
writable[key] = typeof member === "function" ? (...args) => {
|
|
243
|
+
const fileName = args[0];
|
|
244
|
+
const source = typeof fileName === "string" ? programSourceFile(tsm, ls.getProgram(), fileName) : undefined;
|
|
245
|
+
return member.apply(ls, [
|
|
246
|
+
...source === undefined ? args : [source.fileName, ...args.slice(1)]
|
|
247
|
+
]);
|
|
248
|
+
} : member;
|
|
151
249
|
}
|
|
152
|
-
proxy.getDefinitionAndBoundSpan = (fileName, position) => routeDefinitionAt(tsm, ls, fileName, position) ??
|
|
250
|
+
proxy.getDefinitionAndBoundSpan = (fileName, position) => routeDefinitionAt(tsm, ls, info.languageServiceHost, fileName, position) ?? (() => {
|
|
251
|
+
const source = programSourceFile(tsm, ls.getProgram(), fileName);
|
|
252
|
+
return source === undefined ? undefined : ls.getDefinitionAndBoundSpan(source.fileName, position);
|
|
253
|
+
})();
|
|
153
254
|
return proxy;
|
|
154
255
|
}
|
|
155
256
|
};
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,KAAK,EAAE,MAAM,YAAY,CAAA;AAGrC,sGAAsG;AACtG,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAoB,GAC1C,MAAM,GAAG,SAAS,CASpB;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,KAAK,EAAE,MAAM,YAAY,CAAA;AAGrC,sGAAsG;AACtG,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAoB,GAC1C,MAAM,GAAG,SAAS,CASpB;AAgMD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,OAAO,EAAE,EACd,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,QAAQ,EAAE,MAAM,GACf;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAA;CAAE,GAAG,SAAS,CAMjD;AA4CD,iBAAS,IAAI,CAAC,OAAO,EAAE;IAAE,UAAU,EAAE,OAAO,EAAE,CAAA;CAAE,GAAG,EAAE,CAAC,MAAM,CAAC,YAAY,CA6CxE;AAED,eAAe,IAAI,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nifrajs/ts-plugin",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "TypeScript language-service plugin for nifra - go-to-definition from a route-path string literal to the routes/ file that serves it.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"build": "bun run build.ts"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@nifrajs/core": "3.
|
|
48
|
-
"@nifrajs/web": "3.
|
|
47
|
+
"@nifrajs/core": "3.3.0",
|
|
48
|
+
"@nifrajs/web": "3.3.0"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"typescript": ">=5.5.0"
|
package/src/index.ts
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
* matches with `@nifrajs/core`'s pattern matcher (see ./resolve.ts), so a path resolves to exactly the
|
|
13
13
|
* file it would serve at runtime. The plugin only wires that into `getDefinitionAndBoundSpan`.
|
|
14
14
|
*/
|
|
15
|
-
import { existsSync } from "node:fs"
|
|
16
|
-
import { dirname, isAbsolute, join } from "node:path"
|
|
15
|
+
import { existsSync, readFileSync, realpathSync, statSync } from "node:fs"
|
|
16
|
+
import { dirname, isAbsolute, join, resolve } from "node:path"
|
|
17
17
|
import { discoverRoutes } from "@nifrajs/web/fs"
|
|
18
18
|
import type * as ts from "typescript"
|
|
19
19
|
import { resolveRouteFile } from "./resolve.ts"
|
|
@@ -33,6 +33,180 @@ export function findRoutesDir(
|
|
|
33
33
|
return undefined
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
/**
|
|
37
|
+
* TypeScript can canonicalize a file through its host while the editor hands the plugin a different
|
|
38
|
+
* spelling. Windows CI exposes this as the short `RUNNER~1` temp path versus the long path stored in
|
|
39
|
+
* the Program. Match by filesystem identity before delegating to TypeScript; a failed realpath falls
|
|
40
|
+
* back to the lexical absolute path so a missing or synthetic file is still handled normally.
|
|
41
|
+
*/
|
|
42
|
+
function normalizeProgramPath(fileName: string): string {
|
|
43
|
+
const slashed = fileName.replaceAll("\\", "/")
|
|
44
|
+
const withoutDevicePrefix = slashed.replace(/^\/\/\?\/UNC\//i, "//").replace(/^\/\/\?\//, "")
|
|
45
|
+
return process.platform === "win32" ? withoutDevicePrefix.toLowerCase() : withoutDevicePrefix
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Keep every spelling a filesystem resolver may return; native and portable realpath disagree on
|
|
49
|
+
* some Windows short-name paths, so matching only one of them is still a false negative. */
|
|
50
|
+
function canonicalProgramPaths(tsm: typeof ts, fileName: string): readonly string[] {
|
|
51
|
+
const absolute = resolve(fileName)
|
|
52
|
+
const paths = new Set<string>([normalizeProgramPath(absolute)])
|
|
53
|
+
const tsmRealpath = tsm.sys.realpath
|
|
54
|
+
const resolvers = [
|
|
55
|
+
...(typeof tsmRealpath === "function" ? [tsmRealpath] : []),
|
|
56
|
+
realpathSync.native,
|
|
57
|
+
realpathSync,
|
|
58
|
+
]
|
|
59
|
+
for (const resolver of resolvers) {
|
|
60
|
+
try {
|
|
61
|
+
paths.add(normalizeProgramPath(resolver(absolute)))
|
|
62
|
+
} catch {
|
|
63
|
+
// Synthetic editor files and a path removed during an edit have no realpath. The absolute
|
|
64
|
+
// spelling remains a valid candidate for TypeScript's virtual source overlay.
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return [...paths]
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* TypeScript keeps an internal, canonical `Path` for every source file. Prefer that lookup when it
|
|
72
|
+
* is available: unlike a direct string comparison it applies the same case/normalization rules the
|
|
73
|
+
* Program used while loading the file. This is important on Windows, where the language service can
|
|
74
|
+
* receive an 8.3 spelling (`RUNNER~1`) while the Program was populated from the long spelling.
|
|
75
|
+
*/
|
|
76
|
+
function typeScriptProgramPath(
|
|
77
|
+
tsm: typeof ts,
|
|
78
|
+
program: ts.Program,
|
|
79
|
+
fileName: string,
|
|
80
|
+
): ts.Path | undefined {
|
|
81
|
+
const toPath = (
|
|
82
|
+
tsm as unknown as {
|
|
83
|
+
toPath?: (
|
|
84
|
+
fileName: string,
|
|
85
|
+
currentDirectory: string,
|
|
86
|
+
getCanonicalFileName: (fileName: string) => string,
|
|
87
|
+
) => ts.Path
|
|
88
|
+
}
|
|
89
|
+
).toPath
|
|
90
|
+
if (typeof toPath !== "function") return undefined
|
|
91
|
+
const getCanonicalFileName = tsm.sys.useCaseSensitiveFileNames
|
|
92
|
+
? (value: string): string => value
|
|
93
|
+
: (value: string): string => value.toLowerCase()
|
|
94
|
+
try {
|
|
95
|
+
return toPath(resolve(fileName), program.getCurrentDirectory(), getCanonicalFileName)
|
|
96
|
+
} catch {
|
|
97
|
+
return undefined
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Match a real file even when Windows handed the editor an 8.3 alias for the same path. */
|
|
102
|
+
function filesystemIdentity(fileName: string): string | undefined {
|
|
103
|
+
try {
|
|
104
|
+
const info = statSync(resolve(fileName), { bigint: true })
|
|
105
|
+
if (info.ino === 0n) return undefined
|
|
106
|
+
return `bigint:${info.dev}:${info.ino}`
|
|
107
|
+
} catch {
|
|
108
|
+
try {
|
|
109
|
+
const info = statSync(resolve(fileName))
|
|
110
|
+
if (info.ino === 0) return undefined
|
|
111
|
+
return `number:${info.dev}:${info.ino}`
|
|
112
|
+
} catch {
|
|
113
|
+
// TypeScript also exposes virtual/source-overlay files. They have no filesystem identity, so
|
|
114
|
+
// the lexical/realpath checks remain the correct fallback for those inputs.
|
|
115
|
+
return undefined
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function programSourceFile(
|
|
121
|
+
tsm: typeof ts,
|
|
122
|
+
program: ts.Program | undefined,
|
|
123
|
+
fileName: string,
|
|
124
|
+
): ts.SourceFile | undefined {
|
|
125
|
+
if (program === undefined) return undefined
|
|
126
|
+
const direct = program.getSourceFile(fileName)
|
|
127
|
+
if (direct !== undefined) return direct
|
|
128
|
+
|
|
129
|
+
// `getSourceFileByPath` is the Program's own canonical lookup. It is deliberately attempted before
|
|
130
|
+
// the filesystem scans below because an editor may provide a virtual source overlay that has no
|
|
131
|
+
// stat/realpath identity at all.
|
|
132
|
+
const programPath = typeScriptProgramPath(tsm, program, fileName)
|
|
133
|
+
if (programPath !== undefined) {
|
|
134
|
+
const byTypeScriptPath = program.getSourceFileByPath(programPath)
|
|
135
|
+
if (byTypeScriptPath !== undefined) return byTypeScriptPath
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const wanted = new Set(canonicalProgramPaths(tsm, fileName))
|
|
139
|
+
const byPath = program
|
|
140
|
+
.getSourceFiles()
|
|
141
|
+
.find((source) => canonicalProgramPaths(tsm, source.fileName).some((path) => wanted.has(path)))
|
|
142
|
+
if (byPath !== undefined) return byPath
|
|
143
|
+
|
|
144
|
+
const wantedIdentity = filesystemIdentity(fileName)
|
|
145
|
+
if (wantedIdentity === undefined) return undefined
|
|
146
|
+
return program
|
|
147
|
+
.getSourceFiles()
|
|
148
|
+
.find((source) => filesystemIdentity(source.fileName) === wantedIdentity)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Parse the editor's current snapshot when the Program has canonicalized the on-disk path under a
|
|
152
|
+
* different spelling. This keeps route navigation correct for unsaved edits too; reading from disk
|
|
153
|
+
* here would make the plugin jump based on stale source while an editor buffer is open. */
|
|
154
|
+
function hostSourceFile(
|
|
155
|
+
tsm: typeof ts,
|
|
156
|
+
host: ts.LanguageServiceHost | undefined,
|
|
157
|
+
program: ts.Program | undefined,
|
|
158
|
+
fileName: string,
|
|
159
|
+
): ts.SourceFile | undefined {
|
|
160
|
+
if (host === undefined) return undefined
|
|
161
|
+
const snapshot = host.getScriptSnapshot(fileName)
|
|
162
|
+
if (snapshot === undefined) return undefined
|
|
163
|
+
const text = snapshot.getText(0, snapshot.getLength())
|
|
164
|
+
const options = program?.getCompilerOptions()
|
|
165
|
+
const getScriptKindFromFileName = (
|
|
166
|
+
tsm as unknown as {
|
|
167
|
+
getScriptKindFromFileName?: (fileName: string) => ts.ScriptKind
|
|
168
|
+
}
|
|
169
|
+
).getScriptKindFromFileName
|
|
170
|
+
return tsm.createSourceFile(
|
|
171
|
+
fileName,
|
|
172
|
+
text,
|
|
173
|
+
options?.target ?? tsm.ScriptTarget.Latest,
|
|
174
|
+
true,
|
|
175
|
+
host.getScriptKind?.(fileName) ??
|
|
176
|
+
(typeof getScriptKindFromFileName === "function"
|
|
177
|
+
? getScriptKindFromFileName(fileName)
|
|
178
|
+
: tsm.ScriptKind.TSX),
|
|
179
|
+
)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** Last-resort source overlay for embedders that omit `languageServiceHost` from PluginCreateInfo. */
|
|
183
|
+
function diskSourceFile(
|
|
184
|
+
tsm: typeof ts,
|
|
185
|
+
program: ts.Program | undefined,
|
|
186
|
+
fileName: string,
|
|
187
|
+
): ts.SourceFile | undefined {
|
|
188
|
+
let text: string
|
|
189
|
+
try {
|
|
190
|
+
text = readFileSync(fileName, "utf8")
|
|
191
|
+
} catch {
|
|
192
|
+
return undefined
|
|
193
|
+
}
|
|
194
|
+
const getScriptKindFromFileName = (
|
|
195
|
+
tsm as unknown as {
|
|
196
|
+
getScriptKindFromFileName?: (fileName: string) => ts.ScriptKind
|
|
197
|
+
}
|
|
198
|
+
).getScriptKindFromFileName
|
|
199
|
+
return tsm.createSourceFile(
|
|
200
|
+
fileName,
|
|
201
|
+
text,
|
|
202
|
+
program?.getCompilerOptions().target ?? tsm.ScriptTarget.Latest,
|
|
203
|
+
true,
|
|
204
|
+
typeof getScriptKindFromFileName === "function"
|
|
205
|
+
? getScriptKindFromFileName(fileName)
|
|
206
|
+
: tsm.ScriptKind.TSX,
|
|
207
|
+
)
|
|
208
|
+
}
|
|
209
|
+
|
|
36
210
|
/** The innermost AST node whose span contains `position`. */
|
|
37
211
|
function innermostNodeAt(
|
|
38
212
|
tsm: typeof ts,
|
|
@@ -70,10 +244,15 @@ export function findRoutePathLiteral(
|
|
|
70
244
|
function routeDefinitionAt(
|
|
71
245
|
tsm: typeof ts,
|
|
72
246
|
ls: ts.LanguageService,
|
|
247
|
+
host: ts.LanguageServiceHost | undefined,
|
|
73
248
|
fileName: string,
|
|
74
249
|
position: number,
|
|
75
250
|
): ts.DefinitionInfoAndBoundSpan | undefined {
|
|
76
|
-
const
|
|
251
|
+
const program = ls.getProgram()
|
|
252
|
+
const source =
|
|
253
|
+
programSourceFile(tsm, program, fileName) ??
|
|
254
|
+
hostSourceFile(tsm, host, program, fileName) ??
|
|
255
|
+
diskSourceFile(tsm, program, fileName)
|
|
77
256
|
if (source === undefined) return undefined
|
|
78
257
|
const literal = findRoutePathLiteral(tsm, source, position)
|
|
79
258
|
if (literal === undefined) return undefined
|
|
@@ -117,12 +296,34 @@ function init(modules: { typescript: typeof ts }): ts.server.PluginModule {
|
|
|
117
296
|
const member = ls[key]
|
|
118
297
|
writable[key] =
|
|
119
298
|
typeof member === "function"
|
|
120
|
-
? (
|
|
299
|
+
? (...args: unknown[]) => {
|
|
300
|
+
// TypeScript's public methods generally take the source file name first. Map an
|
|
301
|
+
// editor's short/long Windows spelling to the Program's own spelling before delegating;
|
|
302
|
+
// this preserves the real service (and its diagnostics) instead of swallowing a path
|
|
303
|
+
// mismatch as an empty result.
|
|
304
|
+
const fileName = args[0]
|
|
305
|
+
const source =
|
|
306
|
+
typeof fileName === "string"
|
|
307
|
+
? programSourceFile(tsm, ls.getProgram(), fileName)
|
|
308
|
+
: undefined
|
|
309
|
+
return (member as (...args: unknown[]) => unknown).apply(ls, [
|
|
310
|
+
...(source === undefined ? args : [source.fileName, ...args.slice(1)]),
|
|
311
|
+
])
|
|
312
|
+
}
|
|
121
313
|
: member
|
|
122
314
|
}
|
|
123
315
|
proxy.getDefinitionAndBoundSpan = (fileName, position) =>
|
|
124
|
-
routeDefinitionAt(tsm, ls, fileName, position) ??
|
|
125
|
-
|
|
316
|
+
routeDefinitionAt(tsm, ls, info.languageServiceHost, fileName, position) ??
|
|
317
|
+
(() => {
|
|
318
|
+
// TypeScript throws when handed an editor spelling that is not the Program's canonical file
|
|
319
|
+
// name (notably an 8.3 Windows alias). Returning no definition is safer than turning a normal
|
|
320
|
+
// editor request into an exception; every other language-service method remains delegated
|
|
321
|
+
// unchanged through the proxy above.
|
|
322
|
+
const source = programSourceFile(tsm, ls.getProgram(), fileName)
|
|
323
|
+
return source === undefined
|
|
324
|
+
? undefined
|
|
325
|
+
: ls.getDefinitionAndBoundSpan(source.fileName, position)
|
|
326
|
+
})()
|
|
126
327
|
return proxy
|
|
127
328
|
},
|
|
128
329
|
}
|