Skip to content

Commit ed48072

Browse files
authored
Strongly type file paths (#64159)
1 parent 7e5d1c1 commit ed48072

483 files changed

Lines changed: 18732 additions & 10120 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎packages/typescript/package.json‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@
5656
"@typescript/source": "./src/api/fs.ts",
5757
"default": "./dist/api/fs.js"
5858
},
59+
"./unstable/path": {
60+
"@typescript/source": "./src/api/typedPaths.ts",
61+
"default": "./dist/api/typedPaths.js"
62+
},
5963
"./unstable/proto": {
6064
"@typescript/source": "./src/api/proto.ts",
6165
"default": "./dist/api/proto.js"

‎packages/typescript/src/api/async/api.ts‎

Lines changed: 86 additions & 45 deletions
Large diffs are not rendered by default.

‎packages/typescript/src/api/async/client.ts‎

Lines changed: 70 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ import {
99
} from "#vscode-jsonrpc/node";
1010
import type { ChildProcess } from "node:child_process";
1111
import type { Socket } from "node:net";
12+
import type {
13+
RootedDirectoryPath,
14+
RootedFilePath,
15+
RootedPath,
16+
} from "../../ast/index.ts";
1217
import type { FileSystemCallbacks } from "../fs.ts";
1318
import {
1419
configureFileSystemCallbacks,
@@ -142,24 +147,72 @@ export class Client {
142147
): void {
143148
if (!fs) return;
144149
for (const name of configuration.callbackNames) {
145-
if (name === "writeFile") {
146-
const callback = fs.writeFile;
147-
if (typeof callback !== "function") throw new Error("Invalid writeFile callback configuration");
148-
149-
const requestType = new RequestType<{ path: string; data: string; }, unknown, void>(name);
150-
connection.onRequest(requestType, (arg: { path: string; data: string; }) => {
151-
return encodeFileSystemCallbackResult(name, callback(arg.path, arg.data));
152-
});
153-
154-
continue;
150+
switch (name) {
151+
case "readFile": {
152+
const callback = fs.readFile;
153+
if (typeof callback !== "function") throw new Error("Invalid readFile callback configuration");
154+
connection.onRequest(new RequestType<RootedFilePath, unknown, void>(name), fileName => {
155+
return encodeFileSystemCallbackResult(name, callback(fileName));
156+
});
157+
break;
158+
}
159+
case "fileExists": {
160+
const callback = fs.fileExists;
161+
if (typeof callback !== "function") throw new Error("Invalid fileExists callback configuration");
162+
connection.onRequest(new RequestType<RootedFilePath, unknown, void>(name), fileName => {
163+
return encodeFileSystemCallbackResult(name, callback(fileName));
164+
});
165+
break;
166+
}
167+
case "directoryExists": {
168+
const callback = fs.directoryExists;
169+
if (typeof callback !== "function") throw new Error("Invalid directoryExists callback configuration");
170+
connection.onRequest(new RequestType<RootedDirectoryPath, unknown, void>(name), directoryName => {
171+
return encodeFileSystemCallbackResult(name, callback(directoryName));
172+
});
173+
break;
174+
}
175+
case "getAccessibleEntries": {
176+
const callback = fs.getAccessibleEntries;
177+
if (typeof callback !== "function") throw new Error("Invalid getAccessibleEntries callback configuration");
178+
connection.onRequest(new RequestType<RootedDirectoryPath, unknown, void>(name), directoryName => {
179+
return encodeFileSystemCallbackResult(name, callback(directoryName));
180+
});
181+
break;
182+
}
183+
case "realpath": {
184+
const callback = fs.realpath;
185+
if (typeof callback !== "function") throw new Error("Invalid realpath callback configuration");
186+
connection.onRequest(new RequestType<RootedPath, unknown, void>(name), path => {
187+
return encodeFileSystemCallbackResult(name, callback(path));
188+
});
189+
break;
190+
}
191+
case "stat": {
192+
const callback = fs.stat;
193+
if (typeof callback !== "function") throw new Error("Invalid stat callback configuration");
194+
connection.onRequest(new RequestType<RootedPath, unknown, void>(name), path => {
195+
return encodeFileSystemCallbackResult(name, callback(path));
196+
});
197+
break;
198+
}
199+
case "writeFile": {
200+
const callback = fs.writeFile;
201+
if (typeof callback !== "function") throw new Error("Invalid writeFile callback configuration");
202+
connection.onRequest(new RequestType<{ path: RootedFilePath; data: string; }, unknown, void>(name), arg => {
203+
return encodeFileSystemCallbackResult(name, callback(arg.path, arg.data));
204+
});
205+
break;
206+
}
207+
case "removeFile": {
208+
const callback = fs.removeFile;
209+
if (typeof callback !== "function") throw new Error("Invalid removeFile callback configuration");
210+
connection.onRequest(new RequestType<RootedPath, unknown, void>(name), path => {
211+
return encodeFileSystemCallbackResult(name, callback(path));
212+
});
213+
break;
214+
}
155215
}
156-
157-
const callback = fs[name];
158-
if (typeof callback !== "function") throw new Error(`Invalid ${name} callback configuration`);
159-
const requestType = new RequestType<unknown, unknown, void>(name);
160-
connection.onRequest(requestType, (arg: unknown) => {
161-
return encodeFileSystemCallbackResult(name, callback(arg as string));
162-
});
163216
}
164217
}
165218

‎packages/typescript/src/api/async/types.ts‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type {
88
NamedTupleMember,
99
ParameterDeclaration,
1010
} from "../../ast/ast.ts";
11+
import type { RootedFilePath } from "../../ast/index.ts";
1112
import type {
1213
Diagnostic,
1314
RequestFileSystem,
@@ -417,21 +418,21 @@ export interface FormatDiagnosticsHost {
417418

418419
export interface EmitOutputFile {
419420
readonly text: string;
420-
readonly sourceFileName?: string | undefined;
421+
readonly sourceFileName?: RootedFilePath | undefined;
421422
}
422423

423424
export interface EmitResult {
424425
readonly emitSkipped: boolean;
425426
readonly diagnostics: readonly Diagnostic[];
426-
readonly emittedFiles: readonly string[];
427+
readonly emittedFiles: readonly RootedFilePath[];
427428
/** Emitted files captured as a filesystem layer suitable for {@link Snapshot.update}. */
428429
readonly fileSystem?: RequestFileSystem | undefined;
429430
}
430431

431432
export interface EmitOutput {
432433
readonly emitSkipped: boolean;
433434
readonly diagnostics: readonly Diagnostic[];
434-
readonly outputFiles: ReadonlyMap<string, EmitOutputFile>;
435+
readonly outputFiles: ReadonlyMap<RootedFilePath, EmitOutputFile>;
435436
}
436437

437438
export interface ImportSymbolAction {

‎packages/typescript/src/api/compilerOptions.generated.ts‎

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ import type { ModuleKind } from "#enums/moduleKind";
66
import type { ModuleResolutionKind } from "#enums/moduleResolutionKind";
77
import type { NewLineKind } from "#enums/newLineKind";
88
import type { ScriptTarget } from "#enums/scriptTarget";
9+
import type {
10+
RootedDirectoryPath,
11+
RootedFilePath,
12+
RootedPath,
13+
} from "../ast/index.ts";
914

1015
export { JsxEmit } from "#enums/jsxEmit";
1116
export { ModuleDetectionKind } from "#enums/moduleDetectionKind";
@@ -31,7 +36,7 @@ export interface CompilerOptions {
3136
emitBOM?: boolean | undefined;
3237
emitDecoratorMetadata?: boolean | undefined;
3338
declaration?: boolean | undefined;
34-
declarationDir?: string | undefined;
39+
declarationDir?: RootedDirectoryPath | undefined;
3540
declarationMap?: boolean | undefined;
3641
deduplicatePackages?: boolean | undefined;
3742
disableSizeLimit?: boolean | undefined;
@@ -81,21 +86,21 @@ export interface CompilerOptions {
8186
noResolve?: boolean | undefined;
8287
noImplicitOverride?: boolean | undefined;
8388
noUncheckedSideEffectImports?: boolean | undefined;
84-
outDir?: string | undefined;
89+
outDir?: RootedDirectoryPath | undefined;
8590
paths?: Record<string, string[]> | undefined;
8691
/** Plugins are parsed only so tools can report that native TypeScript does not support them. */
8792
plugins?: PluginImport[] | undefined;
8893
preserveConstEnums?: boolean | undefined;
8994
preserveSymlinks?: boolean | undefined;
90-
project?: string | undefined;
95+
project?: RootedPath | undefined;
9196
resolveJsonModule?: boolean | undefined;
9297
resolvePackageJsonExports?: boolean | undefined;
9398
resolvePackageJsonImports?: boolean | undefined;
9499
removeComments?: boolean | undefined;
95100
rewriteRelativeImportExtensions?: boolean | undefined;
96101
reactNamespace?: string | undefined;
97-
rootDir?: string | undefined;
98-
rootDirs?: string[] | undefined;
102+
rootDir?: RootedDirectoryPath | undefined;
103+
rootDirs?: RootedDirectoryPath[] | undefined;
99104
skipLibCheck?: boolean | undefined;
100105
stableTypeOrdering?: boolean | undefined;
101106
strict?: boolean | undefined;
@@ -111,8 +116,8 @@ export interface CompilerOptions {
111116
suppressOutputPathCheck?: boolean | undefined;
112117
target?: ScriptTarget | undefined;
113118
traceResolution?: boolean | undefined;
114-
tsBuildInfoFile?: string | undefined;
115-
typeRoots?: string[] | undefined;
119+
tsBuildInfoFile?: RootedFilePath | undefined;
120+
typeRoots?: RootedDirectoryPath[] | undefined;
116121
types?: string[] | undefined;
117122
useDefineForClassFields?: boolean | undefined;
118123
useUnknownInCatchVariables?: boolean | undefined;
@@ -121,7 +126,7 @@ export interface CompilerOptions {
121126

122127
// Internal fields
123128

124-
configFilePath?: string | undefined;
129+
configFilePath?: RootedFilePath | undefined;
125130
}
126131

127132
export interface PluginImport {

‎packages/typescript/src/api/diagnosticFormatter.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { RootedFilePath } from "../ast/index.ts";
12
import { convertToRelativePath } from "./path.ts";
23
import type { DiagnosticResponse as Diagnostic } from "./proto.generated.ts";
34

@@ -70,7 +71,7 @@ function flattenDiagnosticMessage(diagnostic: Diagnostic, newLine: string, inden
7071
return result;
7172
}
7273

73-
function relativeFileName(fileName: string, host: FormatDiagnosticsHost): string {
74+
function relativeFileName(fileName: RootedFilePath, host: FormatDiagnosticsHost): string {
7475
return convertToRelativePath(
7576
fileName,
7677
host.getCurrentDirectory(),

‎packages/typescript/src/api/fs.ts‎

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import getExePath from "#getExePath";
22
import { dirname } from "node:path";
3+
import type {
4+
RootedDirectoryPath,
5+
RootedFilePath,
6+
RootedPath,
7+
} from "../ast/index.ts";
38
import { normalizePath } from "./path.ts";
49
import type {
510
RequestDirectoryEntries,
@@ -54,15 +59,15 @@ export const serverFS: {
5459

5560
export interface FileSystemCallbacks {
5661
directoryExists:
57-
| ((directoryName: string) => boolean | typeof serverFS.useOS | typeof serverFS.error)
62+
| ((directoryName: RootedDirectoryPath) => boolean | typeof serverFS.useOS | typeof serverFS.error)
5863
| typeof serverFS.useOS
5964
| typeof serverFS.error;
6065
fileExists:
61-
| ((fileName: string) => boolean | typeof serverFS.useOS | typeof serverFS.error)
66+
| ((fileName: RootedFilePath) => boolean | typeof serverFS.useOS | typeof serverFS.error)
6267
| typeof serverFS.useOS
6368
| typeof serverFS.error;
6469
getAccessibleEntries:
65-
| ((directoryName: string) => FileSystemEntries | typeof serverFS.useOS | typeof serverFS.error)
70+
| ((directoryName: RootedDirectoryPath) => FileSystemEntries | typeof serverFS.useOS | typeof serverFS.error)
6671
| typeof serverFS.useOS
6772
| typeof serverFS.error;
6873
/**
@@ -72,26 +77,27 @@ export interface FileSystemCallbacks {
7277
* - Return {@link serverFS.useOS} to fall back to the server's operating-system filesystem.
7378
*/
7479
readFile:
75-
| ((fileName: string) => string | undefined | typeof serverFS.useOS | typeof serverFS.error)
80+
| ((fileName: RootedFilePath) => string | undefined | typeof serverFS.useOS | typeof serverFS.error)
7681
| typeof serverFS.useOS
7782
| typeof serverFS.error;
83+
/** Relative results are resolved against the queried path's directory by the server. */
7884
realpath:
79-
| ((path: string) => string | typeof serverFS.useOS | typeof serverFS.identity | typeof serverFS.error)
85+
| ((path: RootedPath) => string | typeof serverFS.useOS | typeof serverFS.identity | typeof serverFS.error)
8086
| typeof serverFS.useOS
8187
| typeof serverFS.identity
8288
| typeof serverFS.error;
8389
stat:
84-
| ((path: string) => FileSystemStat | undefined | typeof serverFS.useOS | typeof serverFS.fakeStat | typeof serverFS.error)
90+
| ((path: RootedPath) => FileSystemStat | undefined | typeof serverFS.useOS | typeof serverFS.fakeStat | typeof serverFS.error)
8591
| typeof serverFS.useOS
8692
| typeof serverFS.fakeStat
8793
| typeof serverFS.error;
8894
writeFile:
89-
| ((path: string, content: string) => void | typeof serverFS.useOS | typeof serverFS.noop | typeof serverFS.error)
95+
| ((path: RootedFilePath, content: string) => void | typeof serverFS.useOS | typeof serverFS.noop | typeof serverFS.error)
9096
| typeof serverFS.useOS
9197
| typeof serverFS.noop
9298
| typeof serverFS.error;
9399
removeFile:
94-
| ((path: string) => void | typeof serverFS.useOS | typeof serverFS.noop | typeof serverFS.error)
100+
| ((path: RootedPath) => void | typeof serverFS.useOS | typeof serverFS.noop | typeof serverFS.error)
95101
| typeof serverFS.useOS
96102
| typeof serverFS.noop
97103
| typeof serverFS.error;

‎packages/typescript/src/api/node/node.infrastructure.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
type FileReference,
33
type Node,
4+
type PathKey,
45
SyntaxKind,
56
} from "../../ast/index.ts";
67
export { modifierToFlag } from "../../ast/modifiers.ts";

0 commit comments

Comments
 (0)