From c18791ccf165672df3b55f5bdd4a8655f33be26c Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 5 Oct 2022 11:54:59 -0700 Subject: [PATCH 01/12] Fix incorrect options type to WatchOptions (#51064) --- src/compiler/watchPublic.ts | 4 ++-- tests/baselines/reference/api/tsserverlibrary.d.ts | 4 ++-- tests/baselines/reference/api/typescript.d.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/compiler/watchPublic.ts b/src/compiler/watchPublic.ts index 26c6deb9a25..011a24f5b71 100644 --- a/src/compiler/watchPublic.ts +++ b/src/compiler/watchPublic.ts @@ -61,9 +61,9 @@ namespace ts { onWatchStatusChange?(diagnostic: Diagnostic, newLine: string, options: CompilerOptions, errorCount?: number): void; /** Used to watch changes in source files, missing files needed to update the program or config file */ - watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: CompilerOptions): FileWatcher; + watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: WatchOptions): FileWatcher; /** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */ - watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: CompilerOptions): FileWatcher; + watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: WatchOptions): FileWatcher; /** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */ setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any; /** If provided, will be used to reset existing delayed compilation */ diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 43681f1bd3f..4e1b115da8c 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -5399,9 +5399,9 @@ declare namespace ts { /** If provided, called with Diagnostic message that informs about change in watch status */ onWatchStatusChange?(diagnostic: Diagnostic, newLine: string, options: CompilerOptions, errorCount?: number): void; /** Used to watch changes in source files, missing files needed to update the program or config file */ - watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: CompilerOptions): FileWatcher; + watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: WatchOptions): FileWatcher; /** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */ - watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: CompilerOptions): FileWatcher; + watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: WatchOptions): FileWatcher; /** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */ setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any; /** If provided, will be used to reset existing delayed compilation */ diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index ba0f15b86dd..54e4c654e35 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -5399,9 +5399,9 @@ declare namespace ts { /** If provided, called with Diagnostic message that informs about change in watch status */ onWatchStatusChange?(diagnostic: Diagnostic, newLine: string, options: CompilerOptions, errorCount?: number): void; /** Used to watch changes in source files, missing files needed to update the program or config file */ - watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: CompilerOptions): FileWatcher; + watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number, options?: WatchOptions): FileWatcher; /** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */ - watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: CompilerOptions): FileWatcher; + watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean, options?: WatchOptions): FileWatcher; /** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */ setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any; /** If provided, will be used to reset existing delayed compilation */ From 2648f6ab09e3176c7da2c07c54066d3a3433a298 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 6 Oct 2022 13:40:21 -0700 Subject: [PATCH 02/12] Plugins in project were adding up after every config file reload (#51087) * Add test where current plugins dont get reset when reloading config file * Reset loaded plugins when reloading configured project and closing project --- src/server/project.ts | 5 +- src/testRunner/unittests/tsserver/plugins.ts | 45 ++++ .../reference/api/tsserverlibrary.d.ts | 1 - ...-external-files-with-config-file-reload.js | 195 ++++++++++++++++++ 4 files changed, 244 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/tsserver/plugins/gets-external-files-with-config-file-reload.js diff --git a/src/server/project.ts b/src/server/project.ts index e3dc2b77369..44ec0bff482 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -143,7 +143,8 @@ namespace ts.server { private missingFilesMap: ESMap | undefined; private generatedFilesMap: GeneratedFileWatcherMap | undefined; - private plugins: PluginModuleWithName[] = []; + /*@internal*/ + protected readonly plugins: PluginModuleWithName[] = []; /*@internal*/ /** @@ -845,6 +846,7 @@ namespace ts.server { this.directoryStructureHost = undefined!; this.exportMapCache = undefined; this.projectErrors = undefined; + this.plugins.length = 0; // Clean up file watchers waiting for missing files if (this.missingFilesMap) { @@ -2520,6 +2522,7 @@ namespace ts.server { /*@internal*/ enablePluginsWithOptions(options: CompilerOptions, pluginConfigOverrides: ESMap | undefined): void { + this.plugins.length = 0; if (!options.plugins?.length && !this.projectService.globalPlugins.length) return; const host = this.projectService.host; if (!host.require && !host.importPlugin) { diff --git a/src/testRunner/unittests/tsserver/plugins.ts b/src/testRunner/unittests/tsserver/plugins.ts index 5d46dd203f9..0db19f7478b 100644 --- a/src/testRunner/unittests/tsserver/plugins.ts +++ b/src/testRunner/unittests/tsserver/plugins.ts @@ -100,5 +100,50 @@ namespace ts.projectSystem { }; assert.deepEqual(resp, expectedResp); }); + + it("gets external files with config file reload", () => { + const aTs: File = { path: `${tscWatch.projectRoot}/a.ts`, content: `export const x = 10;` }; + const tsconfig: File = { + path: `${tscWatch.projectRoot}/tsconfig.json`, + content: JSON.stringify({ + compilerOptions: { + plugins: [{ name: "some-plugin" }] + } + }) + }; + + const externalFiles: MapLike = { + "some-plugin": ["someFile.txt"], + "some-other-plugin": ["someOtherFile.txt"], + }; + + const host = createServerHost([aTs, tsconfig, libFile]); + host.require = (_initialPath, moduleName) => { + session.logger.logs.push(`Require:: ${moduleName}`); + return { + module: (): server.PluginModule => { + session.logger.logs.push(`PluginFactory Invoke`); + return { + create: Harness.LanguageService.makeDefaultProxy, + getExternalFiles: () => externalFiles[moduleName] + }; + }, + error: undefined + }; + }; + const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) }); + openFilesForSession([aTs], session); + session.logger.logs.push(`ExternalFiles:: ${JSON.stringify(session.getProjectService().configuredProjects.get(tsconfig.path)!.getExternalFiles())}`); + + host.writeFile(tsconfig.path, JSON.stringify({ + compilerOptions: { + plugins: [{ name: "some-other-plugin" }] + } + })); + host.runQueuedTimeoutCallbacks(); + session.logger.logs.push(`ExternalFiles:: ${JSON.stringify(session.getProjectService().configuredProjects.get(tsconfig.path)!.getExternalFiles())}`); + + baselineTsserverLogs("plugins", "gets external files with config file reload", session); + }); }); } \ No newline at end of file diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 4e1b115da8c..76ad8dbfe7e 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -10084,7 +10084,6 @@ declare namespace ts.server { private externalFiles; private missingFilesMap; private generatedFilesMap; - private plugins; protected languageService: LanguageService; languageServiceEnabled: boolean; readonly trace?: (s: string) => void; diff --git a/tests/baselines/reference/tsserver/plugins/gets-external-files-with-config-file-reload.js b/tests/baselines/reference/tsserver/plugins/gets-external-files-with-config-file-reload.js new file mode 100644 index 00000000000..b71b682d1ea --- /dev/null +++ b/tests/baselines/reference/tsserver/plugins/gets-external-files-with-config-file-reload.js @@ -0,0 +1,195 @@ +Info 0 [00:00:21.000] Provided types map file "/a/lib/typesMap.json" doesn't exist +Info 1 [00:00:22.000] request: + { + "seq": 0, + "type": "request", + "command": "open", + "arguments": { + "file": "/user/username/projects/myproject/a.ts" + } + } +Before request +//// [/user/username/projects/myproject/a.ts] +export const x = 10; + +//// [/user/username/projects/myproject/tsconfig.json] +{"compilerOptions":{"plugins":[{"name":"some-plugin"}]}} + +//// [/a/lib/lib.d.ts] +/// +interface Boolean {} +interface Function {} +interface CallableFunction {} +interface NewableFunction {} +interface IArguments {} +interface Number { toExponential: any; } +interface Object {} +interface RegExp {} +interface String { charAt: any; } +interface Array { length: number; [n: number]: T; } + + +PolledWatches:: + +FsWatches:: + +FsWatchesRecursive:: + +Info 2 [00:00:23.000] Search path: /user/username/projects/myproject +Info 3 [00:00:24.000] For info: /user/username/projects/myproject/a.ts :: Config file name: /user/username/projects/myproject/tsconfig.json +Info 4 [00:00:25.000] Creating configuration project /user/username/projects/myproject/tsconfig.json +Info 5 [00:00:26.000] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Config file +Info 6 [00:00:27.000] Config: /user/username/projects/myproject/tsconfig.json : { + "rootNames": [ + "/user/username/projects/myproject/a.ts" + ], + "options": { + "plugins": [ + { + "name": "some-plugin" + } + ], + "configFilePath": "/user/username/projects/myproject/tsconfig.json" + } +} +Info 7 [00:00:28.000] DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 undefined Config: /user/username/projects/myproject/tsconfig.json WatchType: Wild card directory +Info 8 [00:00:29.000] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject 1 undefined Config: /user/username/projects/myproject/tsconfig.json WatchType: Wild card directory +Info 9 [00:00:30.000] Enabling plugin some-plugin from candidate paths: /a/lib/tsc.js/../../.. +Info 10 [00:00:31.000] Loading some-plugin from /a/lib/tsc.js/../../.. (resolved to /a/lib/tsc.js/../../../node_modules) +Require:: some-plugin +PluginFactory Invoke +Info 11 [00:00:32.000] Plugin validation succeeded +Info 12 [00:00:33.000] Starting updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json +Info 13 [00:00:34.000] FileWatcher:: Added:: WatchInfo: /a/lib/lib.d.ts 500 undefined WatchType: Closed Script info +Info 14 [00:00:35.000] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/somefile.txt 500 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Missing file +Info 15 [00:00:36.000] DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Type roots +Info 16 [00:00:37.000] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /user/username/projects/myproject/node_modules/@types 1 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Type roots +Info 17 [00:00:38.000] Finishing updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms +Info 18 [00:00:39.000] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info 19 [00:00:40.000] Files (2) + /a/lib/lib.d.ts + /user/username/projects/myproject/a.ts + + + ../../../../a/lib/lib.d.ts + Default library for target 'es3' + a.ts + Matched by default include pattern '**/*' + +Info 20 [00:00:41.000] ----------------------------------------------- +Info 21 [00:00:42.000] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info 21 [00:00:43.000] Files (2) + +Info 21 [00:00:44.000] ----------------------------------------------- +Info 21 [00:00:45.000] Open files: +Info 21 [00:00:46.000] FileName: /user/username/projects/myproject/a.ts ProjectRootPath: undefined +Info 21 [00:00:47.000] Projects: /user/username/projects/myproject/tsconfig.json +After request + +PolledWatches:: +/user/username/projects/myproject/somefile.txt: + {"pollingInterval":500} +/user/username/projects/myproject/node_modules/@types: + {"pollingInterval":500} + +FsWatches:: +/user/username/projects/myproject/tsconfig.json: + {} +/a/lib/lib.d.ts: + {} + +FsWatchesRecursive:: +/user/username/projects/myproject: + {} + +Info 21 [00:00:48.000] response: + { + "responseRequired": false + } +ExternalFiles:: ["someFile.txt"] +Info 22 [00:00:52.000] FileWatcher:: Triggered with /user/username/projects/myproject/tsconfig.json 1:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Config file +Info 23 [00:00:53.000] Scheduled: /user/username/projects/myproject/tsconfig.json +Info 24 [00:00:54.000] Scheduled: *ensureProjectForOpenFiles* +Info 25 [00:00:55.000] Elapsed:: *ms FileWatcher:: Triggered with /user/username/projects/myproject/tsconfig.json 1:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Config file +Before running timeout callbacks +//// [/user/username/projects/myproject/tsconfig.json] +{"compilerOptions":{"plugins":[{"name":"some-other-plugin"}]}} + + +PolledWatches:: +/user/username/projects/myproject/somefile.txt: + {"pollingInterval":500} +/user/username/projects/myproject/node_modules/@types: + {"pollingInterval":500} + +FsWatches:: +/user/username/projects/myproject/tsconfig.json: + {} +/a/lib/lib.d.ts: + {} + +FsWatchesRecursive:: +/user/username/projects/myproject: + {} + +Info 26 [00:00:56.000] Running: /user/username/projects/myproject/tsconfig.json +Info 27 [00:00:57.000] Reloading configured project /user/username/projects/myproject/tsconfig.json +Info 28 [00:00:58.000] Config: /user/username/projects/myproject/tsconfig.json : { + "rootNames": [ + "/user/username/projects/myproject/a.ts" + ], + "options": { + "plugins": [ + { + "name": "some-other-plugin" + } + ], + "configFilePath": "/user/username/projects/myproject/tsconfig.json" + } +} +Info 29 [00:00:59.000] Enabling plugin some-other-plugin from candidate paths: /a/lib/tsc.js/../../.. +Info 30 [00:01:00.000] Loading some-other-plugin from /a/lib/tsc.js/../../.. (resolved to /a/lib/tsc.js/../../../node_modules) +Require:: some-other-plugin +PluginFactory Invoke +Info 31 [00:01:01.000] Plugin validation succeeded +Info 32 [00:01:02.000] Starting updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json +Info 33 [00:01:03.000] FileWatcher:: Close:: WatchInfo: /user/username/projects/myproject/somefile.txt 500 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Missing file +Info 34 [00:01:04.000] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/someotherfile.txt 500 undefined Project: /user/username/projects/myproject/tsconfig.json WatchType: Missing file +Info 35 [00:01:05.000] Finishing updateGraphWorker: Project: /user/username/projects/myproject/tsconfig.json Version: 2 structureChanged: true structureIsReused:: Not Elapsed:: *ms +Info 36 [00:01:06.000] Different program with same set of files +Info 37 [00:01:07.000] Running: *ensureProjectForOpenFiles* +Info 38 [00:01:08.000] Before ensureProjectForOpenFiles: +Info 39 [00:01:09.000] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info 39 [00:01:10.000] Files (2) + +Info 39 [00:01:11.000] ----------------------------------------------- +Info 39 [00:01:12.000] Open files: +Info 39 [00:01:13.000] FileName: /user/username/projects/myproject/a.ts ProjectRootPath: undefined +Info 39 [00:01:14.000] Projects: /user/username/projects/myproject/tsconfig.json +Info 39 [00:01:15.000] After ensureProjectForOpenFiles: +Info 40 [00:01:16.000] Project '/user/username/projects/myproject/tsconfig.json' (Configured) +Info 40 [00:01:17.000] Files (2) + +Info 40 [00:01:18.000] ----------------------------------------------- +Info 40 [00:01:19.000] Open files: +Info 40 [00:01:20.000] FileName: /user/username/projects/myproject/a.ts ProjectRootPath: undefined +Info 40 [00:01:21.000] Projects: /user/username/projects/myproject/tsconfig.json +After running timeout callbacks + +PolledWatches:: +/user/username/projects/myproject/node_modules/@types: + {"pollingInterval":500} +/user/username/projects/myproject/someotherfile.txt: + {"pollingInterval":500} + +FsWatches:: +/user/username/projects/myproject/tsconfig.json: + {} +/a/lib/lib.d.ts: + {} + +FsWatchesRecursive:: +/user/username/projects/myproject: + {} + +ExternalFiles:: ["someOtherFile.txt"] \ No newline at end of file From 5f3e6cc4980d26af5d8a8f463e59b2c3338165c6 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 6 Oct 2022 13:45:22 -0700 Subject: [PATCH 03/12] Plugin probe location is higher priority than peer node_modules (#51079) Fixes #34616 --- src/server/project.ts | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/server/project.ts b/src/server/project.ts index 44ec0bff482..14c98583bd6 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -1606,6 +1606,16 @@ namespace ts.server { return !!this.program && this.program.isSourceOfProjectReferenceRedirect(fileName); } + /*@internal*/ + protected getGlobalPluginSearchPaths() { + // Search any globally-specified probe paths, then our peer node_modules + return [ + ...this.projectService.pluginProbeLocations, + // ../../.. to walk from X/node_modules/typescript/lib/tsserver.js to X/node_modules/ + combinePaths(this.projectService.getExecutingFilePath(), "../../.."), + ]; + } + protected enableGlobalPlugins(options: CompilerOptions, pluginConfigOverrides: Map | undefined): void { if (!this.projectService.globalPlugins.length) return; const host = this.projectService.host; @@ -1615,14 +1625,8 @@ namespace ts.server { return; } - // Search any globally-specified probe paths, then our peer node_modules - const searchPaths = [ - ...this.projectService.pluginProbeLocations, - // ../../.. to walk from X/node_modules/typescript/lib/tsserver.js to X/node_modules/ - combinePaths(this.projectService.getExecutingFilePath(), "../../.."), - ]; - // Enable global plugins with synthetic configuration entries + const searchPaths = this.getGlobalPluginSearchPaths(); for (const globalPluginName of this.projectService.globalPlugins) { // Skip empty names from odd commandline parses if (!globalPluginName) continue; @@ -2530,10 +2534,7 @@ namespace ts.server { return; } - // Search our peer node_modules, then any globally-specified probe paths - // ../../.. to walk from X/node_modules/typescript/lib/tsserver.js to X/node_modules/ - const searchPaths = [combinePaths(this.projectService.getExecutingFilePath(), "../../.."), ...this.projectService.pluginProbeLocations]; - + const searchPaths = this.getGlobalPluginSearchPaths(); if (this.projectService.allowLocalPluginLoads) { const local = getDirectoryPath(this.canonicalConfigFilePath); this.projectService.logger.info(`Local plugin loading enabled; adding ${local} to search paths`); From 42b1049aee8c655631cb4f0065de86ec1023d20a Mon Sep 17 00:00:00 2001 From: TypeScript Bot Date: Fri, 7 Oct 2022 06:09:49 +0000 Subject: [PATCH 04/12] Update package-lock.json --- package-lock.json | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0396eecec3c..a2531c11527 100644 --- a/package-lock.json +++ b/package-lock.json @@ -560,9 +560,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.8.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.2.tgz", - "integrity": "sha512-cRMwIgdDN43GO4xMWAfJAecYn8wV4JbsOGHNfNUIDiuYkUYAR5ec4Rj7IO2SAhFPEfpPtLtUTbbny/TCT7aDwA==", + "version": "18.8.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.3.tgz", + "integrity": "sha512-0os9vz6BpGwxGe9LOhgP/ncvYN5Tx1fNcd2TM3rD/aCGBkysb+ZWpXEocG24h6ZzOi13+VB8HndAQFezsSOw1w==", "dev": true }, "node_modules/@types/node-fetch": { @@ -2155,9 +2155,9 @@ } }, "node_modules/es-abstract": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.3.tgz", - "integrity": "sha512-AyrnaKVpMzljIdwjzrj+LxGmj8ik2LckwXacHqrJJ/jxz6dDDBcZ7I7nlHM0FvEW8MfbWJwOd+yT2XzYW49Frw==", + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", + "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==", "dev": true, "dependencies": { "call-bind": "^1.0.2", @@ -2170,7 +2170,7 @@ "has-property-descriptors": "^1.0.0", "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", - "is-callable": "^1.2.6", + "is-callable": "^1.2.7", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", @@ -9053,9 +9053,9 @@ "dev": true }, "@types/node": { - "version": "18.8.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.2.tgz", - "integrity": "sha512-cRMwIgdDN43GO4xMWAfJAecYn8wV4JbsOGHNfNUIDiuYkUYAR5ec4Rj7IO2SAhFPEfpPtLtUTbbny/TCT7aDwA==", + "version": "18.8.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.3.tgz", + "integrity": "sha512-0os9vz6BpGwxGe9LOhgP/ncvYN5Tx1fNcd2TM3rD/aCGBkysb+ZWpXEocG24h6ZzOi13+VB8HndAQFezsSOw1w==", "dev": true }, "@types/node-fetch": { @@ -10277,9 +10277,9 @@ } }, "es-abstract": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.3.tgz", - "integrity": "sha512-AyrnaKVpMzljIdwjzrj+LxGmj8ik2LckwXacHqrJJ/jxz6dDDBcZ7I7nlHM0FvEW8MfbWJwOd+yT2XzYW49Frw==", + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz", + "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==", "dev": true, "requires": { "call-bind": "^1.0.2", @@ -10292,7 +10292,7 @@ "has-property-descriptors": "^1.0.0", "has-symbols": "^1.0.3", "internal-slot": "^1.0.3", - "is-callable": "^1.2.6", + "is-callable": "^1.2.7", "is-negative-zero": "^2.0.2", "is-regex": "^1.1.4", "is-shared-array-buffer": "^1.0.2", From d06a592d02955822a7407b70969fb7a82bc17d59 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 7 Oct 2022 07:25:57 -0700 Subject: [PATCH 05/12] Properly defer resolution of mapped types with generic `as` clauses (#51050) * Fix isGenericMappedType, getTemplateLiteralType, getStringMappingType * Accept new baselines * Add regression tests * Fix comment --- src/compiler/checker.ts | 40 +++++++--- .../genericMappedTypeAsClause.errors.txt | 48 ++++++++++++ .../reference/genericMappedTypeAsClause.js | 35 +++++++++ .../genericMappedTypeAsClause.symbols | 75 +++++++++++++++++++ .../reference/genericMappedTypeAsClause.types | 67 +++++++++++++++++ .../baselines/reference/intrinsicTypes.types | 12 +-- .../reference/mappedTypeAsClauses.types | 2 +- .../reference/numericStringLiteralTypes.types | 2 +- ...iteralsAssignedToStringMappings.errors.txt | 8 +- ...ringLiteralsAssignedToStringMappings.types | 8 +- .../reference/templateLiteralTypes3.types | 10 +-- .../compiler/genericMappedTypeAsClause.ts | 22 ++++++ 12 files changed, 299 insertions(+), 30 deletions(-) create mode 100644 tests/baselines/reference/genericMappedTypeAsClause.errors.txt create mode 100644 tests/baselines/reference/genericMappedTypeAsClause.js create mode 100644 tests/baselines/reference/genericMappedTypeAsClause.symbols create mode 100644 tests/baselines/reference/genericMappedTypeAsClause.types create mode 100644 tests/cases/compiler/genericMappedTypeAsClause.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0e4f940238c..9ebaca719f2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12073,7 +12073,20 @@ namespace ts { } function isGenericMappedType(type: Type): type is MappedType { - return !!(getObjectFlags(type) & ObjectFlags.Mapped) && isGenericIndexType(getConstraintTypeFromMappedType(type as MappedType)); + if (getObjectFlags(type) & ObjectFlags.Mapped) { + const constraint = getConstraintTypeFromMappedType(type as MappedType); + if (isGenericIndexType(constraint)) { + return true; + } + // A mapped type is generic if the 'as' clause references generic types other than the iteration type. + // To determine this, we substitute the constraint type (that we now know isn't generic) for the iteration + // type and check whether the resulting type is generic. + const nameType = getNameTypeFromMappedType(type as MappedType); + if (nameType && isGenericIndexType(instantiateType(nameType, makeUnaryTypeMapper(getTypeParameterFromMappedType(type as MappedType), constraint)))) { + return true; + } + } + return false; } function resolveStructuredTypeMembers(type: StructuredType): ResolvedType { @@ -15638,8 +15651,14 @@ namespace ts { return getStringLiteralType(text); } newTexts.push(text); - if (every(newTexts, t => t === "") && every(newTypes, t => !!(t.flags & TypeFlags.String))) { - return stringType; + if (every(newTexts, t => t === "")) { + if (every(newTypes, t => !!(t.flags & TypeFlags.String))) { + return stringType; + } + // Normalize `${Mapping}` into Mapping + if (newTypes.length === 1 && isPatternLiteralType(newTypes[0])) { + return newTypes[0]; + } } const id = `${getTypeListId(newTypes)}|${map(newTexts, t => t.length).join(",")}|${newTexts.join("")}`; let type = templateLiteralTypes.get(id); @@ -15698,11 +15717,13 @@ namespace ts { function getStringMappingType(symbol: Symbol, type: Type): Type { return type.flags & (TypeFlags.Union | TypeFlags.Never) ? mapType(type, t => getStringMappingType(symbol, t)) : - // Mapping> === Mapping - type.flags & TypeFlags.StringMapping && symbol === type.symbol ? type : - isGenericIndexType(type) || isPatternLiteralPlaceholderType(type) ? getStringMappingTypeForGenericType(symbol, isPatternLiteralPlaceholderType(type) && !(type.flags & TypeFlags.StringMapping) ? getTemplateLiteralType(["", ""], [type]) : type) : type.flags & TypeFlags.StringLiteral ? getStringLiteralType(applyStringMapping(symbol, (type as StringLiteralType).value)) : type.flags & TypeFlags.TemplateLiteral ? getTemplateLiteralType(...applyTemplateStringMapping(symbol, (type as TemplateLiteralType).texts, (type as TemplateLiteralType).types)) : + // Mapping> === Mapping + type.flags & TypeFlags.StringMapping && symbol === type.symbol ? type : + type.flags & (TypeFlags.Any | TypeFlags.String || type.flags & TypeFlags.StringMapping) || isGenericIndexType(type) ? getStringMappingTypeForGenericType(symbol, type) : + // This handles Mapping<`${number}`> and Mapping<`${bigint}`> + isPatternLiteralPlaceholderType(type) ? getStringMappingTypeForGenericType(symbol, getTemplateLiteralType(["", ""], [type])) : type; } @@ -16000,11 +16021,12 @@ namespace ts { } function isPatternLiteralPlaceholderType(type: Type): boolean { - return !!(type.flags & (TypeFlags.Any | TypeFlags.String | TypeFlags.Number | TypeFlags.BigInt)) || !!(type.flags & TypeFlags.StringMapping && isPatternLiteralPlaceholderType((type as StringMappingType).type)); + return !!(type.flags & (TypeFlags.Any | TypeFlags.String | TypeFlags.Number | TypeFlags.BigInt)) || isPatternLiteralType(type); } function isPatternLiteralType(type: Type) { - return !!(type.flags & TypeFlags.TemplateLiteral) && every((type as TemplateLiteralType).types, isPatternLiteralPlaceholderType); + return !!(type.flags & TypeFlags.TemplateLiteral) && every((type as TemplateLiteralType).types, isPatternLiteralPlaceholderType) || + !!(type.flags & TypeFlags.StringMapping) && isPatternLiteralPlaceholderType((type as StringMappingType).type); } function isGenericType(type: Type): boolean { @@ -22559,7 +22581,7 @@ namespace ts { } function isMemberOfStringMapping(source: Type, target: Type): boolean { - if (target.flags & (TypeFlags.String | TypeFlags.AnyOrUnknown)) { + if (target.flags & (TypeFlags.String | TypeFlags.Any)) { return true; } if (target.flags & TypeFlags.TemplateLiteral) { diff --git a/tests/baselines/reference/genericMappedTypeAsClause.errors.txt b/tests/baselines/reference/genericMappedTypeAsClause.errors.txt new file mode 100644 index 00000000000..35c7395979c --- /dev/null +++ b/tests/baselines/reference/genericMappedTypeAsClause.errors.txt @@ -0,0 +1,48 @@ +tests/cases/compiler/genericMappedTypeAsClause.ts(11,36): error TS2322: Type 'string' is not assignable to type 'number'. +tests/cases/compiler/genericMappedTypeAsClause.ts(14,11): error TS2322: Type 'number' is not assignable to type 'MappedModel'. +tests/cases/compiler/genericMappedTypeAsClause.ts(15,11): error TS2322: Type 'string' is not assignable to type 'MappedModel'. +tests/cases/compiler/genericMappedTypeAsClause.ts(16,11): error TS2322: Type 'number[]' is not assignable to type 'MappedModel'. +tests/cases/compiler/genericMappedTypeAsClause.ts(17,11): error TS2322: Type 'boolean' is not assignable to type 'MappedModel'. +tests/cases/compiler/genericMappedTypeAsClause.ts(18,34): error TS2322: Type '{ a: string; b: number; }' is not assignable to type 'MappedModel'. + Object literal may only specify known properties, and 'a' does not exist in type 'MappedModel'. +tests/cases/compiler/genericMappedTypeAsClause.ts(19,11): error TS2322: Type 'undefined' is not assignable to type 'MappedModel'. + + +==== tests/cases/compiler/genericMappedTypeAsClause.ts (7 errors) ==== + type Model = { + a: string; + b: number; + }; + + type MappedModel = { + [K in keyof Model as `${K}${Suffix}`]: Model[K]; + }; + + const foo1: MappedModel<'Foo'> = { aFoo: 'test', bFoo: 42 }; + const foo2: MappedModel<'Foo'> = { bFoo: 'bar' }; // Error + ~~~~ +!!! error TS2322: Type 'string' is not assignable to type 'number'. +!!! related TS6500 tests/cases/compiler/genericMappedTypeAsClause.ts:6:43: The expected type comes from property 'bFoo' which is declared here on type 'MappedModel<"Foo">' + + function f1() { + const x1: MappedModel = 42; // Error + ~~ +!!! error TS2322: Type 'number' is not assignable to type 'MappedModel'. + const x2: MappedModel = 'test'; // Error + ~~ +!!! error TS2322: Type 'string' is not assignable to type 'MappedModel'. + const x3: MappedModel = [1, 2, 3]; // Error + ~~ +!!! error TS2322: Type 'number[]' is not assignable to type 'MappedModel'. + const x4: MappedModel = false; // Error + ~~ +!!! error TS2322: Type 'boolean' is not assignable to type 'MappedModel'. + const x5: MappedModel = { a: 'bar', b: 42 }; // Error + ~~~~~~~~ +!!! error TS2322: Type '{ a: string; b: number; }' is not assignable to type 'MappedModel'. +!!! error TS2322: Object literal may only specify known properties, and 'a' does not exist in type 'MappedModel'. + const x6: MappedModel = undefined; // Error + ~~ +!!! error TS2322: Type 'undefined' is not assignable to type 'MappedModel'. + } + \ No newline at end of file diff --git a/tests/baselines/reference/genericMappedTypeAsClause.js b/tests/baselines/reference/genericMappedTypeAsClause.js new file mode 100644 index 00000000000..0e11eb75399 --- /dev/null +++ b/tests/baselines/reference/genericMappedTypeAsClause.js @@ -0,0 +1,35 @@ +//// [genericMappedTypeAsClause.ts] +type Model = { + a: string; + b: number; +}; + +type MappedModel = { + [K in keyof Model as `${K}${Suffix}`]: Model[K]; +}; + +const foo1: MappedModel<'Foo'> = { aFoo: 'test', bFoo: 42 }; +const foo2: MappedModel<'Foo'> = { bFoo: 'bar' }; // Error + +function f1() { + const x1: MappedModel = 42; // Error + const x2: MappedModel = 'test'; // Error + const x3: MappedModel = [1, 2, 3]; // Error + const x4: MappedModel = false; // Error + const x5: MappedModel = { a: 'bar', b: 42 }; // Error + const x6: MappedModel = undefined; // Error +} + + +//// [genericMappedTypeAsClause.js] +"use strict"; +var foo1 = { aFoo: 'test', bFoo: 42 }; +var foo2 = { bFoo: 'bar' }; // Error +function f1() { + var x1 = 42; // Error + var x2 = 'test'; // Error + var x3 = [1, 2, 3]; // Error + var x4 = false; // Error + var x5 = { a: 'bar', b: 42 }; // Error + var x6 = undefined; // Error +} diff --git a/tests/baselines/reference/genericMappedTypeAsClause.symbols b/tests/baselines/reference/genericMappedTypeAsClause.symbols new file mode 100644 index 00000000000..c0d563564cd --- /dev/null +++ b/tests/baselines/reference/genericMappedTypeAsClause.symbols @@ -0,0 +1,75 @@ +=== tests/cases/compiler/genericMappedTypeAsClause.ts === +type Model = { +>Model : Symbol(Model, Decl(genericMappedTypeAsClause.ts, 0, 0)) + + a: string; +>a : Symbol(a, Decl(genericMappedTypeAsClause.ts, 0, 14)) + + b: number; +>b : Symbol(b, Decl(genericMappedTypeAsClause.ts, 1, 14)) + +}; + +type MappedModel = { +>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2)) +>Suffix : Symbol(Suffix, Decl(genericMappedTypeAsClause.ts, 5, 17)) + + [K in keyof Model as `${K}${Suffix}`]: Model[K]; +>K : Symbol(K, Decl(genericMappedTypeAsClause.ts, 6, 5)) +>Model : Symbol(Model, Decl(genericMappedTypeAsClause.ts, 0, 0)) +>K : Symbol(K, Decl(genericMappedTypeAsClause.ts, 6, 5)) +>Suffix : Symbol(Suffix, Decl(genericMappedTypeAsClause.ts, 5, 17)) +>Model : Symbol(Model, Decl(genericMappedTypeAsClause.ts, 0, 0)) +>K : Symbol(K, Decl(genericMappedTypeAsClause.ts, 6, 5)) + +}; + +const foo1: MappedModel<'Foo'> = { aFoo: 'test', bFoo: 42 }; +>foo1 : Symbol(foo1, Decl(genericMappedTypeAsClause.ts, 9, 5)) +>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2)) +>aFoo : Symbol(aFoo, Decl(genericMappedTypeAsClause.ts, 9, 34)) +>bFoo : Symbol(bFoo, Decl(genericMappedTypeAsClause.ts, 9, 48)) + +const foo2: MappedModel<'Foo'> = { bFoo: 'bar' }; // Error +>foo2 : Symbol(foo2, Decl(genericMappedTypeAsClause.ts, 10, 5)) +>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2)) +>bFoo : Symbol(bFoo, Decl(genericMappedTypeAsClause.ts, 10, 34)) + +function f1() { +>f1 : Symbol(f1, Decl(genericMappedTypeAsClause.ts, 10, 49)) +>T : Symbol(T, Decl(genericMappedTypeAsClause.ts, 12, 12)) + + const x1: MappedModel = 42; // Error +>x1 : Symbol(x1, Decl(genericMappedTypeAsClause.ts, 13, 9)) +>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2)) +>T : Symbol(T, Decl(genericMappedTypeAsClause.ts, 12, 12)) + + const x2: MappedModel = 'test'; // Error +>x2 : Symbol(x2, Decl(genericMappedTypeAsClause.ts, 14, 9)) +>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2)) +>T : Symbol(T, Decl(genericMappedTypeAsClause.ts, 12, 12)) + + const x3: MappedModel = [1, 2, 3]; // Error +>x3 : Symbol(x3, Decl(genericMappedTypeAsClause.ts, 15, 9)) +>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2)) +>T : Symbol(T, Decl(genericMappedTypeAsClause.ts, 12, 12)) + + const x4: MappedModel = false; // Error +>x4 : Symbol(x4, Decl(genericMappedTypeAsClause.ts, 16, 9)) +>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2)) +>T : Symbol(T, Decl(genericMappedTypeAsClause.ts, 12, 12)) + + const x5: MappedModel = { a: 'bar', b: 42 }; // Error +>x5 : Symbol(x5, Decl(genericMappedTypeAsClause.ts, 17, 9)) +>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2)) +>T : Symbol(T, Decl(genericMappedTypeAsClause.ts, 12, 12)) +>a : Symbol(a, Decl(genericMappedTypeAsClause.ts, 17, 32)) +>b : Symbol(b, Decl(genericMappedTypeAsClause.ts, 17, 42)) + + const x6: MappedModel = undefined; // Error +>x6 : Symbol(x6, Decl(genericMappedTypeAsClause.ts, 18, 9)) +>MappedModel : Symbol(MappedModel, Decl(genericMappedTypeAsClause.ts, 3, 2)) +>T : Symbol(T, Decl(genericMappedTypeAsClause.ts, 12, 12)) +>undefined : Symbol(undefined) +} + diff --git a/tests/baselines/reference/genericMappedTypeAsClause.types b/tests/baselines/reference/genericMappedTypeAsClause.types new file mode 100644 index 00000000000..478011afea4 --- /dev/null +++ b/tests/baselines/reference/genericMappedTypeAsClause.types @@ -0,0 +1,67 @@ +=== tests/cases/compiler/genericMappedTypeAsClause.ts === +type Model = { +>Model : { a: string; b: number; } + + a: string; +>a : string + + b: number; +>b : number + +}; + +type MappedModel = { +>MappedModel : MappedModel + + [K in keyof Model as `${K}${Suffix}`]: Model[K]; +}; + +const foo1: MappedModel<'Foo'> = { aFoo: 'test', bFoo: 42 }; +>foo1 : MappedModel<"Foo"> +>{ aFoo: 'test', bFoo: 42 } : { aFoo: string; bFoo: number; } +>aFoo : string +>'test' : "test" +>bFoo : number +>42 : 42 + +const foo2: MappedModel<'Foo'> = { bFoo: 'bar' }; // Error +>foo2 : MappedModel<"Foo"> +>{ bFoo: 'bar' } : { bFoo: string; } +>bFoo : string +>'bar' : "bar" + +function f1() { +>f1 : () => void + + const x1: MappedModel = 42; // Error +>x1 : MappedModel +>42 : 42 + + const x2: MappedModel = 'test'; // Error +>x2 : MappedModel +>'test' : "test" + + const x3: MappedModel = [1, 2, 3]; // Error +>x3 : MappedModel +>[1, 2, 3] : number[] +>1 : 1 +>2 : 2 +>3 : 3 + + const x4: MappedModel = false; // Error +>x4 : MappedModel +>false : false + + const x5: MappedModel = { a: 'bar', b: 42 }; // Error +>x5 : MappedModel +>{ a: 'bar', b: 42 } : { a: string; b: number; } +>a : string +>'bar' : "bar" +>b : number +>42 : 42 + + const x6: MappedModel = undefined; // Error +>x6 : MappedModel +>undefined : undefined +} + diff --git a/tests/baselines/reference/intrinsicTypes.types b/tests/baselines/reference/intrinsicTypes.types index d17e37284c1..b0542696834 100644 --- a/tests/baselines/reference/intrinsicTypes.types +++ b/tests/baselines/reference/intrinsicTypes.types @@ -9,7 +9,7 @@ type TU3 = Uppercase; // Uppercase >TU3 : Uppercase type TU4 = Uppercase; // Uppercase<`${any}`> ->TU4 : Uppercase<`${any}`> +>TU4 : Uppercase type TU5 = Uppercase; // never >TU5 : never @@ -27,7 +27,7 @@ type TL3 = Lowercase; // Lowercase >TL3 : Lowercase type TL4 = Lowercase; // Lowercase<`${any}`> ->TL4 : Lowercase<`${any}`> +>TL4 : Lowercase type TL5 = Lowercase; // never >TL5 : never @@ -45,7 +45,7 @@ type TC3 = Capitalize; // Capitalize >TC3 : Capitalize type TC4 = Capitalize; // Capitalize<`${any}`> ->TC4 : Capitalize<`${any}`> +>TC4 : Capitalize type TC5 = Capitalize; // never >TC5 : never @@ -63,7 +63,7 @@ type TN3 = Uncapitalize; // Uncapitalize >TN3 : Uncapitalize type TN4 = Uncapitalize; // Uncapitalize<`${any}`> ->TN4 : Uncapitalize<`${any}`> +>TN4 : Uncapitalize type TN5 = Uncapitalize; // never >TN5 : never @@ -72,13 +72,13 @@ type TN6 = Uncapitalize<42>; // Error >TN6 : 42 type TX1 = Uppercase<`aB${S}`>; ->TX1 : Uppercase<`aB${S}`> +>TX1 : `AB${Uppercase}` type TX2 = TX1<'xYz'>; // "ABXYZ" >TX2 : "ABXYZ" type TX3 = Lowercase<`aB${S}`>; ->TX3 : Lowercase<`aB${S}`> +>TX3 : `ab${Lowercase}` type TX4 = TX3<'xYz'>; // "abxyz" >TX4 : "abxyz" diff --git a/tests/baselines/reference/mappedTypeAsClauses.types b/tests/baselines/reference/mappedTypeAsClauses.types index de951157933..31ee234f7da 100644 --- a/tests/baselines/reference/mappedTypeAsClauses.types +++ b/tests/baselines/reference/mappedTypeAsClauses.types @@ -61,7 +61,7 @@ type TD1 = DoubleProp<{ a: string, b: number }>; // { a1: string, a2: string, b >b : number type TD2 = keyof TD1; // 'a1' | 'a2' | 'b1' | 'b2' ->TD2 : "a1" | "a2" | "b1" | "b2" +>TD2 : "a1" | "b1" | "a2" | "b2" type TD3 = keyof DoubleProp; // `${keyof U & string}1` | `${keyof U & string}2` >TD3 : `${keyof U & string}1` | `${keyof U & string}2` diff --git a/tests/baselines/reference/numericStringLiteralTypes.types b/tests/baselines/reference/numericStringLiteralTypes.types index 245a3a674ba..e22186758f7 100644 --- a/tests/baselines/reference/numericStringLiteralTypes.types +++ b/tests/baselines/reference/numericStringLiteralTypes.types @@ -12,7 +12,7 @@ type T3 = string & `${T}`; // `${T} >T3 : `${T}` type T4 = string & `${Capitalize<`${T}`>}`; // `${Capitalize}` ->T4 : `${Capitalize<`${T}`>}` +>T4 : `${Capitalize}` function f1(a: boolean[], x: `${number}`) { >f1 : (a: boolean[], x: `${number}`) => void diff --git a/tests/baselines/reference/stringLiteralsAssignedToStringMappings.errors.txt b/tests/baselines/reference/stringLiteralsAssignedToStringMappings.errors.txt index 63890e68912..cf74e17c863 100644 --- a/tests/baselines/reference/stringLiteralsAssignedToStringMappings.errors.txt +++ b/tests/baselines/reference/stringLiteralsAssignedToStringMappings.errors.txt @@ -1,6 +1,6 @@ tests/cases/conformance/types/literal/stringLiteralsAssignedToStringMappings.ts(7,1): error TS2322: Type 'string' is not assignable to type 'Uppercase>'. -tests/cases/conformance/types/literal/stringLiteralsAssignedToStringMappings.ts(15,1): error TS2322: Type 'string' is not assignable to type 'Uppercase<`${Lowercase<`${number}`>}`>'. -tests/cases/conformance/types/literal/stringLiteralsAssignedToStringMappings.ts(16,1): error TS2322: Type 'string' is not assignable to type 'Uppercase<`${Lowercase<`${number}`>}`>'. +tests/cases/conformance/types/literal/stringLiteralsAssignedToStringMappings.ts(15,1): error TS2322: Type 'string' is not assignable to type 'Uppercase>'. +tests/cases/conformance/types/literal/stringLiteralsAssignedToStringMappings.ts(16,1): error TS2322: Type 'string' is not assignable to type 'Uppercase>'. ==== tests/cases/conformance/types/literal/stringLiteralsAssignedToStringMappings.ts (3 errors) ==== @@ -22,7 +22,7 @@ tests/cases/conformance/types/literal/stringLiteralsAssignedToStringMappings.ts( // bad y = "a"; ~ -!!! error TS2322: Type 'string' is not assignable to type 'Uppercase<`${Lowercase<`${number}`>}`>'. +!!! error TS2322: Type 'string' is not assignable to type 'Uppercase>'. y = "A"; ~ -!!! error TS2322: Type 'string' is not assignable to type 'Uppercase<`${Lowercase<`${number}`>}`>'. \ No newline at end of file +!!! error TS2322: Type 'string' is not assignable to type 'Uppercase>'. \ No newline at end of file diff --git a/tests/baselines/reference/stringLiteralsAssignedToStringMappings.types b/tests/baselines/reference/stringLiteralsAssignedToStringMappings.types index 34e7963a8e6..852c4ca0d42 100644 --- a/tests/baselines/reference/stringLiteralsAssignedToStringMappings.types +++ b/tests/baselines/reference/stringLiteralsAssignedToStringMappings.types @@ -15,22 +15,22 @@ x = "a"; >"a" : "a" declare var y: Uppercase>; ->y : Uppercase<`${Lowercase<`${number}`>}`> +>y : Uppercase> // good y = "1"; >y = "1" : "1" ->y : Uppercase<`${Lowercase<`${number}`>}`> +>y : Uppercase> >"1" : "1" // bad y = "a"; >y = "a" : "a" ->y : Uppercase<`${Lowercase<`${number}`>}`> +>y : Uppercase> >"a" : "a" y = "A"; >y = "A" : "A" ->y : Uppercase<`${Lowercase<`${number}`>}`> +>y : Uppercase> >"A" : "A" diff --git a/tests/baselines/reference/templateLiteralTypes3.types b/tests/baselines/reference/templateLiteralTypes3.types index 66a36048f07..3fb7144050b 100644 --- a/tests/baselines/reference/templateLiteralTypes3.types +++ b/tests/baselines/reference/templateLiteralTypes3.types @@ -568,8 +568,8 @@ function ft1(t: T, u: Uppercase, u1: Uppercase<`1.${T}.3`>, >ft1 : (t: T, u: Uppercase, u1: Uppercase<`1.${T}.3`>, u2: Uppercase<`1.${T}.4`>) => void >t : T >u : Uppercase ->u1 : Uppercase<`1.${T}.3`> ->u2 : Uppercase<`1.${T}.4`> +>u1 : `1.${Uppercase}.3` +>u2 : `1.${Uppercase}.4` spread(`1.${t}.3`, `1.${t}.4`); >spread(`1.${t}.3`, `1.${t}.4`) : `1.${T}.3` | `1.${T}.4` @@ -588,9 +588,9 @@ function ft1(t: T, u: Uppercase, u1: Uppercase<`1.${T}.3`>, >u : Uppercase spread(u1, u2); ->spread(u1, u2) : Uppercase<`1.${T}.3`> | Uppercase<`1.${T}.4`> +>spread(u1, u2) : `1.${Uppercase}.3` | `1.${Uppercase}.4` >spread :

(...args: P[]) => P ->u1 : Uppercase<`1.${T}.3`> ->u2 : Uppercase<`1.${T}.4`> +>u1 : `1.${Uppercase}.3` +>u2 : `1.${Uppercase}.4` } diff --git a/tests/cases/compiler/genericMappedTypeAsClause.ts b/tests/cases/compiler/genericMappedTypeAsClause.ts new file mode 100644 index 00000000000..a5b965a02d9 --- /dev/null +++ b/tests/cases/compiler/genericMappedTypeAsClause.ts @@ -0,0 +1,22 @@ +// @strict: true + +type Model = { + a: string; + b: number; +}; + +type MappedModel = { + [K in keyof Model as `${K}${Suffix}`]: Model[K]; +}; + +const foo1: MappedModel<'Foo'> = { aFoo: 'test', bFoo: 42 }; +const foo2: MappedModel<'Foo'> = { bFoo: 'bar' }; // Error + +function f1() { + const x1: MappedModel = 42; // Error + const x2: MappedModel = 'test'; // Error + const x3: MappedModel = [1, 2, 3]; // Error + const x4: MappedModel = false; // Error + const x5: MappedModel = { a: 'bar', b: 42 }; // Error + const x6: MappedModel = undefined; // Error +} From dbeae5d943c784661862c52b8e215a2907c31a33 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Fri, 7 Oct 2022 19:13:22 +0300 Subject: [PATCH 06/12] fix(51017): Make lineText in the references response opt-out (#51081) * add option to exclude lineText from the response * add comments * update baseline --- src/server/protocol.ts | 17 ++++++++++---- src/server/session.ts | 16 +++++++++---- .../unittests/tsserver/getFileReferences.ts | 23 +++++++++++++++++++ src/testRunner/unittests/tsserver/helpers.ts | 2 +- .../reference/api/tsserverlibrary.d.ts | 16 +++++++++---- 5 files changed, 58 insertions(+), 16 deletions(-) diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 372898c4aff..e4d4a2e2d62 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -1158,12 +1158,14 @@ namespace ts.server.protocol { } export interface ReferencesResponseItem extends FileSpanWithContext { - /** Text of line containing the reference. Including this - * with the response avoids latency of editor loading files - * to show text of reference line (the server already has - * loaded the referencing files). + /** + * Text of line containing the reference. Including this + * with the response avoids latency of editor loading files + * to show text of reference line (the server already has loaded the referencing files). + * + * If {@link UserPreferences.disableLineTextInReferences} is enabled, the property won't be filled */ - lineText: string; + lineText?: string; /** * True if reference is a write location, false otherwise. @@ -3478,6 +3480,11 @@ namespace ts.server.protocol { readonly includeInlayFunctionLikeReturnTypeHints?: boolean; readonly includeInlayEnumMemberValueHints?: boolean; readonly autoImportFileExcludePatterns?: string[]; + + /** + * Indicates whether {@link ReferencesResponseItem.lineText} is supported. + */ + readonly disableLineTextInReferences?: boolean; } export interface CompilerOptions { diff --git a/src/server/session.ts b/src/server/session.ts index 302884cf080..8e63f93556d 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1807,6 +1807,7 @@ namespace ts.server { if (!simplifiedResult) return references; + const preferences = this.getPreferences(file); const defaultProject = this.getDefaultProject(args); const scriptInfo = defaultProject.getScriptInfoForNormalizedPath(file)!; const nameInfo = defaultProject.getLanguageService().getQuickInfoAtPosition(file, position); @@ -1815,7 +1816,7 @@ namespace ts.server { const symbolStartOffset = nameSpan ? scriptInfo.positionToLineOffset(nameSpan.start).offset : 0; const symbolName = nameSpan ? scriptInfo.getSnapshot().getText(nameSpan.start, textSpanEnd(nameSpan)) : ""; const refs: readonly protocol.ReferencesResponseItem[] = flatMap(references, referencedSymbol => { - return referencedSymbol.references.map(entry => referenceEntryToReferencesResponseItem(this.projectService, entry)); + return referencedSymbol.references.map(entry => referenceEntryToReferencesResponseItem(this.projectService, entry, preferences)); }); return { refs, symbolName, symbolStartOffset, symbolDisplayString }; } @@ -1823,6 +1824,7 @@ namespace ts.server { private getFileReferences(args: protocol.FileRequestArgs, simplifiedResult: boolean): protocol.FileReferencesResponseBody | readonly ReferenceEntry[] { const projects = this.getProjects(args); const fileName = args.file; + const preferences = this.getPreferences(toNormalizedPath(fileName)); const references: ReferenceEntry[] = []; const seen = createDocumentSpanSet(); @@ -1842,7 +1844,7 @@ namespace ts.server { }); if (!simplifiedResult) return references; - const refs = references.map(entry => referenceEntryToReferencesResponseItem(this.projectService, entry)); + const refs = references.map(entry => referenceEntryToReferencesResponseItem(this.projectService, entry, preferences)); return { refs, symbolName: `"${args.file}"` @@ -3516,11 +3518,10 @@ namespace ts.server { return text; } - function referenceEntryToReferencesResponseItem(projectService: ProjectService, { fileName, textSpan, contextSpan, isWriteAccess, isDefinition }: ReferencedSymbolEntry): protocol.ReferencesResponseItem { + function referenceEntryToReferencesResponseItem(projectService: ProjectService, { fileName, textSpan, contextSpan, isWriteAccess, isDefinition }: ReferencedSymbolEntry, { disableLineTextInReferences }: protocol.UserPreferences): protocol.ReferencesResponseItem { const scriptInfo = Debug.checkDefined(projectService.getScriptInfo(fileName)); const span = toProtocolTextSpanWithContext(textSpan, contextSpan, scriptInfo); - const lineSpan = scriptInfo.lineToTextSpan(span.start.line - 1); - const lineText = scriptInfo.getSnapshot().getText(lineSpan.start, textSpanEnd(lineSpan)).replace(/\r|\n/g, ""); + const lineText = disableLineTextInReferences ? undefined : getLineText(scriptInfo, span); return { file: fileName, ...span, @@ -3530,6 +3531,11 @@ namespace ts.server { }; } + function getLineText(scriptInfo: ScriptInfo, span: protocol.TextSpanWithContext) { + const lineSpan = scriptInfo.lineToTextSpan(span.start.line - 1); + return scriptInfo.getSnapshot().getText(lineSpan.start, textSpanEnd(lineSpan)).replace(/\r|\n/g, ""); + } + function isCompletionEntryData(data: any): data is CompletionEntryData { return data === undefined || data && typeof data === "object" && typeof data.exportName === "string" diff --git a/src/testRunner/unittests/tsserver/getFileReferences.ts b/src/testRunner/unittests/tsserver/getFileReferences.ts index 4244671e756..7e1f9cc8de4 100644 --- a/src/testRunner/unittests/tsserver/getFileReferences.ts +++ b/src/testRunner/unittests/tsserver/getFileReferences.ts @@ -54,5 +54,28 @@ namespace ts.projectSystem { assert.deepEqual(response, expectResponse); }); + + it("should skip lineText from file references", () => { + const session = makeSampleSession(); + session.getProjectService().setHostConfiguration({ preferences: { disableLineTextInReferences: true } }); + + const response = executeSessionRequest( + session, + protocol.CommandTypes.FileReferences, + { file: aTs.path }, + ); + + const expectResponse: protocol.FileReferencesResponseBody = { + refs: [ + makeReferenceItem({ file: bTs, text: "./a", lineText: undefined, contextText: importA, isWriteAccess: false }), + makeReferenceItem({ file: cTs, text: "./a", lineText: undefined, contextText: importCurlyFromA, isWriteAccess: false }), + makeReferenceItem({ file: dTs, text: "/project/a", lineText: undefined, contextText: importAFromA, isWriteAccess: false }), + makeReferenceItem({ file: dTs, text: "./a", lineText: undefined, contextText: typeofImportA, isWriteAccess: false }), + ], + symbolName: `"${aTs.path}"`, + }; + + assert.deepEqual(response, expectResponse); + }); }); } diff --git a/src/testRunner/unittests/tsserver/helpers.ts b/src/testRunner/unittests/tsserver/helpers.ts index 17b449b8459..a51a5587321 100644 --- a/src/testRunner/unittests/tsserver/helpers.ts +++ b/src/testRunner/unittests/tsserver/helpers.ts @@ -810,7 +810,7 @@ namespace ts.projectSystem { export interface MakeReferenceItem extends DocumentSpanFromSubstring { isDefinition?: boolean; isWriteAccess?: boolean; - lineText: string; + lineText?: string; } export function makeReferenceItem({ isDefinition, isWriteAccess, lineText, ...rest }: MakeReferenceItem): protocol.ReferencesResponseItem { diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 76ad8dbfe7e..90dd9ed780f 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -7982,12 +7982,14 @@ declare namespace ts.server.protocol { command: CommandTypes.References; } interface ReferencesResponseItem extends FileSpanWithContext { - /** Text of line containing the reference. Including this - * with the response avoids latency of editor loading files - * to show text of reference line (the server already has - * loaded the referencing files). + /** + * Text of line containing the reference. Including this + * with the response avoids latency of editor loading files + * to show text of reference line (the server already has loaded the referencing files). + * + * If {@link UserPreferences.disableLineTextInReferences} is enabled, the property won't be filled */ - lineText: string; + lineText?: string; /** * True if reference is a write location, false otherwise. */ @@ -9833,6 +9835,10 @@ declare namespace ts.server.protocol { readonly includeInlayFunctionLikeReturnTypeHints?: boolean; readonly includeInlayEnumMemberValueHints?: boolean; readonly autoImportFileExcludePatterns?: string[]; + /** + * Indicates whether {@link ReferencesResponseItem.lineText} is supported. + */ + readonly disableLineTextInReferences?: boolean; } interface CompilerOptions { allowJs?: boolean; From ad56b5ca56b763ab377e07121ecfebb457a2e810 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Fri, 7 Oct 2022 09:50:46 -0700 Subject: [PATCH 07/12] Convert scripts/Gulpfile to checked mjs/cjs so they can run without compilation (#50988) --- .dockerignore | 16 +- .eslintignore | 3 +- .eslintplugin.js | 2 +- .eslintrc.json | 39 ++- .github/workflows/ci.yml | 17 + .gitignore | 18 +- Gulpfile.js => Gulpfile.mjs | 90 +++-- package-lock.json | 310 ++++++++++-------- package.json | 7 +- ...tionTest.js => browserIntegrationTest.mjs} | 35 +- scripts/build/findUpDir.js | 21 -- scripts/build/findUpDir.mjs | 29 ++ scripts/build/{options.js => options.mjs} | 28 +- scripts/build/{prepend.js => prepend.mjs} | 23 +- scripts/build/{projects.js => projects.mjs} | 17 +- .../build/{sourcemaps.js => sourcemaps.mjs} | 4 +- scripts/build/{tests.js => tests.mjs} | 61 ++-- scripts/build/{utils.js => utils.mjs} | 168 ++-------- .../{buildProtocol.ts => buildProtocol.mjs} | 112 +++++-- ...ePrerelease.ts => configurePrerelease.mjs} | 48 ++- ...oundBuild.js => createPlaygroundBuild.mjs} | 29 +- scripts/{errorCheck.ts => errorCheck.mjs} | 14 +- .../{boolean-trivia.js => boolean-trivia.cjs} | 2 +- .../{debug-assert.js => debug-assert.cjs} | 2 +- ...no-double-space.js => no-double-space.cjs} | 2 +- .../{no-in-operator.js => no-in-operator.cjs} | 2 +- .../rules/{no-keywords.js => no-keywords.cjs} | 2 +- ...ce.js => no-type-assertion-whitespace.cjs} | 2 +- ...s => object-literal-surrounding-space.cjs} | 2 +- ...per-file.js => one-namespace-per-file.cjs} | 2 +- ...-functions.js => only-arrow-functions.cjs} | 2 +- .../{simple-indent.js => simple-indent.cjs} | 2 +- ...r-spacing.js => type-operator-spacing.cjs} | 2 +- scripts/eslint/rules/{utils.js => utils.cjs} | 0 ...trivia.test.ts => boolean-trivia.test.cjs} | 4 +- ...g-assert.test.ts => debug-assert.test.cjs} | 4 +- ...space.test.ts => no-double-space.test.cjs} | 4 +- ...erator.test.ts => no-in-operator.test.cjs} | 4 +- ...-keywords.test.ts => no-keywords.test.cjs} | 4 +- ... => no-type-assertion-whitespace.test.cjs} | 4 +- ...object-literal-surrounding-space.test.cjs} | 4 +- ....test.ts => only-arrow-functions.test.cjs} | 4 +- ...-indent.test.ts => simple-indent.test.cjs} | 4 +- scripts/eslint/tests/support/RuleTester.cjs | 6 + scripts/eslint/tests/support/RuleTester.ts | 6 - ...test.ts => type-operator-spacing.test.cjs} | 4 +- scripts/eslint/tsconfig.json | 30 -- scripts/{failed-tests.js => failed-tests.cjs} | 19 +- scripts/failed-tests.d.cts | 52 +++ scripts/failed-tests.d.ts | 22 -- scripts/find-unused-diganostic-messages.mjs | 4 +- ...> generateLocalizedDiagnosticMessages.mjs} | 60 +++- ...ests.ts => importDefinitelyTypedTests.mjs} | 56 ++-- .../importDefinitelyTypedTests/tsconfig.json | 18 - scripts/link-hooks.js | 20 -- scripts/link-hooks.mjs | 25 ++ ...rry-pick-pr.ts => open-cherry-pick-pr.mjs} | 14 +- scripts/{open-user-pr.ts => open-user-pr.mjs} | 8 +- ...rf-result-post.js => perf-result-post.mjs} | 17 +- ...ment.js => post-vsts-artifact-comment.mjs} | 13 +- ...sages.ts => processDiagnosticMessages.mjs} | 62 +++- scripts/{produceLKG.ts => produceLKG.mjs} | 36 +- ...> regenerate-unicode-identifier-parts.mjs} | 2 + ...est-pr-review.ts => request-pr-review.mjs} | 9 +- scripts/run-sequence.d.ts | 3 - scripts/{run-sequence.js => run-sequence.mjs} | 14 +- scripts/tsconfig.json | 31 +- ...es.js => update-experimental-branches.mjs} | 8 +- scripts/word.d.ts | 122 +++++++ scripts/{word2md.ts => word2md.mjs} | 203 ++++-------- src/harness/findUpDir.ts | 2 +- src/testRunner/parallel/host.ts | 4 +- 72 files changed, 1080 insertions(+), 939 deletions(-) rename Gulpfile.js => Gulpfile.mjs (87%) rename scripts/{browserIntegrationTest.js => browserIntegrationTest.mjs} (64%) delete mode 100644 scripts/build/findUpDir.js create mode 100644 scripts/build/findUpDir.mjs rename scripts/build/{options.js => options.mjs} (82%) rename scripts/build/{prepend.js => prepend.mjs} (78%) rename scripts/build/{projects.js => projects.mjs} (76%) rename scripts/build/{sourcemaps.js => sourcemaps.mjs} (87%) rename scripts/build/{tests.js => tests.mjs} (77%) rename scripts/build/{utils.js => utils.mjs} (59%) rename scripts/{buildProtocol.ts => buildProtocol.mjs} (69%) rename scripts/{configurePrerelease.ts => configurePrerelease.mjs} (77%) rename scripts/{createPlaygroundBuild.js => createPlaygroundBuild.mjs} (95%) rename scripts/{errorCheck.ts => errorCheck.mjs} (87%) rename scripts/eslint/rules/{boolean-trivia.js => boolean-trivia.cjs} (95%) rename scripts/eslint/rules/{debug-assert.js => debug-assert.cjs} (95%) rename scripts/eslint/rules/{no-double-space.js => no-double-space.cjs} (95%) rename scripts/eslint/rules/{no-in-operator.js => no-in-operator.cjs} (91%) rename scripts/eslint/rules/{no-keywords.js => no-keywords.cjs} (95%) rename scripts/eslint/rules/{no-type-assertion-whitespace.js => no-type-assertion-whitespace.cjs} (93%) rename scripts/eslint/rules/{object-literal-surrounding-space.js => object-literal-surrounding-space.cjs} (95%) rename scripts/eslint/rules/{one-namespace-per-file.js => one-namespace-per-file.cjs} (93%) rename scripts/eslint/rules/{only-arrow-functions.js => only-arrow-functions.cjs} (95%) rename scripts/eslint/rules/{simple-indent.js => simple-indent.cjs} (95%) rename scripts/eslint/rules/{type-operator-spacing.js => type-operator-spacing.cjs} (93%) rename scripts/eslint/rules/{utils.js => utils.cjs} (100%) rename scripts/eslint/tests/{boolean-trivia.test.ts => boolean-trivia.test.cjs} (89%) rename scripts/eslint/tests/{debug-assert.test.ts => debug-assert.test.cjs} (88%) rename scripts/eslint/tests/{no-double-space.test.ts => no-double-space.test.cjs} (92%) rename scripts/eslint/tests/{no-in-operator.test.ts => no-in-operator.test.cjs} (77%) rename scripts/eslint/tests/{no-keywords.test.ts => no-keywords.test.cjs} (92%) rename scripts/eslint/tests/{no-type-assertion-whitespace.test.ts => no-type-assertion-whitespace.test.cjs} (86%) rename scripts/eslint/tests/{object-literal-surrounding-space.test.ts => object-literal-surrounding-space.test.cjs} (86%) rename scripts/eslint/tests/{only-arrow-functions.test.ts => only-arrow-functions.test.cjs} (92%) rename scripts/eslint/tests/{simple-indent.test.ts => simple-indent.test.cjs} (93%) create mode 100644 scripts/eslint/tests/support/RuleTester.cjs delete mode 100644 scripts/eslint/tests/support/RuleTester.ts rename scripts/eslint/tests/{type-operator-spacing.test.ts => type-operator-spacing.test.cjs} (86%) delete mode 100644 scripts/eslint/tsconfig.json rename scripts/{failed-tests.js => failed-tests.cjs} (86%) create mode 100644 scripts/failed-tests.d.cts delete mode 100644 scripts/failed-tests.d.ts rename scripts/{generateLocalizedDiagnosticMessages.ts => generateLocalizedDiagnosticMessages.mjs} (81%) rename scripts/{importDefinitelyTypedTests/importDefinitelyTypedTests.ts => importDefinitelyTypedTests.mjs} (79%) delete mode 100644 scripts/importDefinitelyTypedTests/tsconfig.json delete mode 100644 scripts/link-hooks.js create mode 100644 scripts/link-hooks.mjs rename scripts/{open-cherry-pick-pr.ts => open-cherry-pick-pr.mjs} (92%) rename scripts/{open-user-pr.ts => open-user-pr.mjs} (86%) rename scripts/{perf-result-post.js => perf-result-post.mjs} (86%) rename scripts/{post-vsts-artifact-comment.js => post-vsts-artifact-comment.mjs} (88%) rename scripts/{processDiagnosticMessages.ts => processDiagnosticMessages.mjs} (73%) rename scripts/{produceLKG.ts => produceLKG.mjs} (80%) rename scripts/{regenerate-unicode-identifier-parts.js => regenerate-unicode-identifier-parts.mjs} (93%) rename scripts/{request-pr-review.ts => request-pr-review.mjs} (89%) delete mode 100644 scripts/run-sequence.d.ts rename scripts/{run-sequence.js => run-sequence.mjs} (67%) rename scripts/{update-experimental-branches.js => update-experimental-branches.mjs} (93%) create mode 100644 scripts/word.d.ts rename scripts/{word2md.ts => word2md.mjs} (69%) diff --git a/.dockerignore b/.dockerignore index cf23755119c..c98d74e918d 100644 --- a/.dockerignore +++ b/.dockerignore @@ -17,20 +17,8 @@ build.json *.config scripts/debug.bat scripts/run.bat -scripts/word2md.js -scripts/buildProtocol.js -scripts/ior.js -scripts/configurePrerelease.js -scripts/open-user-pr.js -scripts/open-cherry-pick-pr.js -scripts/processDiagnosticMessages.d.ts -scripts/processDiagnosticMessages.js -scripts/produceLKG.js -scripts/importDefinitelyTypedTests/importDefinitelyTypedTests.js -scripts/generateLocalizedDiagnosticMessages.js -scripts/configureLanguageServiceBuild.js -scripts/*.js.map -scripts/typings/ +scripts/**/*.js +scripts/**/*.js.map coverage/ internal/ **/.DS_Store diff --git a/.eslintignore b/.eslintignore index 8d538995500..c85649450cd 100644 --- a/.eslintignore +++ b/.eslintignore @@ -4,7 +4,8 @@ /lib/** /src/lib/*.generated.d.ts # Ignore all compiled script outputs -/scripts/*.js +/scripts/**/*.js +/scripts/**/*.d.* # But, not the ones that are hand-written. # TODO: remove once scripts are pure JS !/scripts/browserIntegrationTest.js diff --git a/.eslintplugin.js b/.eslintplugin.js index 71b2587ccda..97525acf6ff 100644 --- a/.eslintplugin.js +++ b/.eslintplugin.js @@ -2,7 +2,7 @@ const fs = require("fs"); const path = require("path"); const rulesDir = path.join(__dirname, "scripts", "eslint", "rules"); -const ext = ".js"; +const ext = ".cjs"; const ruleFiles = fs.readdirSync(rulesDir).filter((p) => p.endsWith(ext)); module.exports = { diff --git a/.eslintrc.json b/.eslintrc.json index 1711f8592ab..35901b12013 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -12,17 +12,6 @@ "plugins": [ "@typescript-eslint", "jsdoc", "no-null", "import", "eslint-plugin-local" ], - "overrides": [ - // By default, the ESLint CLI only looks at .js files. But, it will also look at - // any files which are referenced in an override config. Most users of typescript-eslint - // get this behavior by default by extending a recommended typescript-eslint config, which - // just so happens to override some core ESLint rules. We don't extend from any config, so - // explicitly reference TS files here so the CLI picks them up. - // - // ESLint in VS Code will lint any opened file (so long as it's not eslintignore'd), so - // that will work regardless of the below. - { "files": ["*.ts", "*.mts", "*.cts", "*.mjs", "*.cjs"] } - ], "rules": { "@typescript-eslint/adjacent-overload-signatures": "error", "@typescript-eslint/array-type": "error", @@ -151,5 +140,31 @@ "no-prototype-builtins": "error", "no-self-assign": "error", "no-dupe-else-if": "error" - } + }, + "overrides": [ + // By default, the ESLint CLI only looks at .js files. But, it will also look at + // any files which are referenced in an override config. Most users of typescript-eslint + // get this behavior by default by extending a recommended typescript-eslint config, which + // just so happens to override some core ESLint rules. We don't extend from any config, so + // explicitly reference TS files here so the CLI picks them up. + // + // ESLint in VS Code will lint any opened file (so long as it's not eslintignore'd), so + // that will work regardless of the below. + // + // The same applies to mjs files; ESLint appears to not scan those either. + { "files": ["*.ts", "*.mts", "*.cts", "*.mjs", "*.cjs"] }, + { + "files": ["*.mjs", "*.mts"], + "rules": { + // These globals don't exist outside of CJS files. + "no-restricted-globals": ["error", + { "name": "__filename" }, + { "name": "__dirname" }, + { "name": "require" }, + { "name": "module" }, + { "name": "exports" } + ] + } + } + ] } diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 956a4172965..bf6cb012289 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,3 +67,20 @@ jobs: - name: Validate the browser can import TypeScript run: gulp test-browser-integration + + misc: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: "*" + check-latest: true + - run: npm ci + + - name: Build scripts + run: gulp scripts + + - name: ESLint tests + run: gulp run-eslint-rules-tests diff --git a/.gitignore b/.gitignore index cdf20ba5a24..afd10c0786d 100644 --- a/.gitignore +++ b/.gitignore @@ -40,22 +40,8 @@ tests/cases/**/*.js.map scripts/eslint/built/ scripts/debug.bat scripts/run.bat -scripts/word2md.js -scripts/buildProtocol.js -scripts/ior.js -scripts/configurePrerelease.js -scripts/configureLanguageServiceBuild.js -scripts/open-user-pr.js -scripts/open-cherry-pick-pr.js -scripts/processDiagnosticMessages.d.ts -scripts/processDiagnosticMessages.js -scripts/produceLKG.js -scripts/importDefinitelyTypedTests/importDefinitelyTypedTests.js -scripts/generateLocalizedDiagnosticMessages.js -scripts/request-pr-review.js -scripts/errorCheck.js -scripts/*.js.map -scripts/typings/ +scripts/**/*.js +scripts/**/*.js.map coverage/ internal/ **/.DS_Store diff --git a/Gulpfile.js b/Gulpfile.mjs similarity index 87% rename from Gulpfile.js rename to Gulpfile.mjs index ead6442b81c..a8f32d5741b 100644 --- a/Gulpfile.js +++ b/Gulpfile.mjs @@ -1,20 +1,22 @@ // @ts-check -const path = require("path"); -const fs = require("fs"); -const log = require("fancy-log"); -const newer = require("gulp-newer"); -const sourcemaps = require("gulp-sourcemaps"); -const del = require("del"); -const rename = require("gulp-rename"); -const concat = require("gulp-concat"); -const merge2 = require("merge2"); -const { src, dest, task, parallel, series, watch } = require("gulp"); -const { append, transform } = require("gulp-insert"); -const { prependFile } = require("./scripts/build/prepend"); -const { exec, readJson, needsUpdate, getDiffTool, getDirSize, rm } = require("./scripts/build/utils"); -const { runConsoleTests, refBaseline, localBaseline, refRwcBaseline, localRwcBaseline } = require("./scripts/build/tests"); -const { buildProject, cleanProject, watchProject } = require("./scripts/build/projects"); -const cmdLineOptions = require("./scripts/build/options"); +import path from "path"; +import fs from "fs"; +import log from "fancy-log"; +import newer from "gulp-newer"; +import sourcemaps from "gulp-sourcemaps"; +import del from "del"; +import rename from "gulp-rename"; +import concat from "gulp-concat"; +import merge2 from "merge2"; +import gulp from "gulp"; +import { append, transform } from "gulp-insert"; +import { prependFile } from "./scripts/build/prepend.mjs"; +import { exec, readJson, needsUpdate, getDiffTool, getDirSize, rm } from "./scripts/build/utils.mjs"; +import { runConsoleTests, refBaseline, localBaseline, refRwcBaseline, localRwcBaseline } from "./scripts/build/tests.mjs"; +import { buildProject, cleanProject, watchProject } from "./scripts/build/projects.mjs"; +import cmdLineOptions from "./scripts/build/options.mjs"; + +const { src, dest, task, parallel, series, watch } = gulp; const copyright = "CopyrightNotice.txt"; const cleanTasks = []; @@ -23,9 +25,6 @@ const buildScripts = () => buildProject("scripts"); task("scripts", buildScripts); task("scripts").description = "Builds files in the 'scripts' folder."; -const cleanScripts = () => cleanProject("scripts"); -cleanTasks.push(cleanScripts); - /** @type {{ libs: string[]; paths: Record; }} */ const libraries = readJson("./src/lib/libs.json"); const libs = libraries.libs.map(lib => { @@ -56,10 +55,10 @@ const diagnosticMessagesJson = "src/compiler/diagnosticMessages.json"; const diagnosticMessagesGeneratedJson = "src/compiler/diagnosticMessages.generated.json"; const generateDiagnostics = async () => { if (needsUpdate(diagnosticMessagesJson, [diagnosticMessagesGeneratedJson, diagnosticInformationMapTs])) { - await exec(process.execPath, ["scripts/processDiagnosticMessages.js", diagnosticMessagesJson]); + await exec(process.execPath, ["scripts/processDiagnosticMessages.mjs", diagnosticMessagesJson]); } }; -task("generate-diagnostics", series(buildScripts, generateDiagnostics)); +task("generate-diagnostics", generateDiagnostics); task("generate-diagnostics").description = "Generates a diagnostic file in TypeScript based on an input JSON file"; const cleanDiagnostics = () => del([diagnosticInformationMapTs, diagnosticMessagesGeneratedJson]); @@ -88,7 +87,7 @@ const localizationTargets = ["cs", "de", "es", "fr", "it", "ja", "ko", "pl", "pt const localize = async () => { if (needsUpdate(diagnosticMessagesGeneratedJson, generatedLCGFile)) { - return exec(process.execPath, ["scripts/generateLocalizedDiagnosticMessages.js", "src/loc/lcl", "built/local", diagnosticMessagesGeneratedJson], { ignoreExitCode: true }); + return exec(process.execPath, ["scripts/generateLocalizedDiagnosticMessages.mjs", "src/loc/lcl", "built/local", diagnosticMessagesGeneratedJson], { ignoreExitCode: true }); } }; @@ -97,7 +96,7 @@ const cleanDebugTools = () => cleanProject("src/debug"); cleanTasks.push(cleanDebugTools); // Pre-build steps when targeting the LKG compiler -const lkgPreBuild = parallel(generateLibs, series(buildScripts, generateDiagnostics, buildDebugTools)); +const lkgPreBuild = parallel(generateLibs, series(generateDiagnostics, buildDebugTools)); const buildTsc = () => buildProject("src/tsc"); task("tsc", series(lkgPreBuild, buildTsc)); @@ -113,7 +112,7 @@ task("watch-tsc", series(lkgPreBuild, parallel(watchLib, watchDiagnostics, watch task("watch-tsc").description = "Watch for changes and rebuild the command-line compiler only."; // Pre-build steps when targeting the built/local compiler. -const localPreBuild = parallel(generateLibs, series(buildScripts, generateDiagnostics, buildDebugTools, buildTsc)); +const localPreBuild = parallel(generateLibs, series(generateDiagnostics, buildDebugTools, buildTsc)); // Pre-build steps to use based on supplied options. const preBuild = cmdLineOptions.lkg ? lkgPreBuild : localPreBuild; @@ -335,17 +334,8 @@ task("clean-tests").description = "Cleans the outputs for the test infrastructur const watchTests = () => watchProject("src/testRunner", cmdLineOptions); -const buildEslintRules = () => buildProject("scripts/eslint"); -task("build-eslint-rules", buildEslintRules); -task("build-eslint-rules").description = "Compiles eslint rules to js"; - -const cleanEslintRules = () => cleanProject("scripts/eslint"); -cleanTasks.push(cleanEslintRules); -task("clean-eslint-rules", cleanEslintRules); -task("clean-eslint-rules").description = "Cleans the outputs for the eslint rules"; - -const runEslintRulesTests = () => runConsoleTests("scripts/eslint/built/tests", "mocha-fivemat-progress-reporter", /*runInParallel*/ false, /*watchMode*/ false); -task("run-eslint-rules-tests", series(buildEslintRules, runEslintRulesTests)); +const runEslintRulesTests = () => runConsoleTests("scripts/eslint/tests", "mocha-fivemat-progress-reporter", /*runInParallel*/ false, /*watchMode*/ false); +task("run-eslint-rules-tests", runEslintRulesTests); task("run-eslint-rules-tests").description = "Runs the eslint rule tests"; /** @type { (folder: string) => { (): Promise; displayName?: string } } */ @@ -459,8 +449,8 @@ task("runtests-parallel").flags = { }; -task("test-browser-integration", () => exec(process.execPath, ["scripts/browserIntegrationTest.js"])); -task("test-browser-integration").description = "Runs scripts/browserIntegrationTest.ts which tests that typescript.js loads in a browser"; +task("test-browser-integration", () => exec(process.execPath, ["scripts/browserIntegrationTest.mjs"])); +task("test-browser-integration").description = "Runs scripts/browserIntegrationTest.mjs which tests that typescript.js loads in a browser"; task("diff", () => exec(getDiffTool(), [refBaseline, localBaseline], { ignoreExitCode: true, waitForExit: false })); @@ -493,13 +483,9 @@ const updateSublime = () => src(["built/local/tsserver.js", "built/local/tsserve task("update-sublime", updateSublime); task("update-sublime").description = "Updates the sublime plugin's tsserver"; -const buildImportDefinitelyTypedTests = () => buildProject("scripts/importDefinitelyTypedTests"); -const cleanImportDefinitelyTypedTests = () => cleanProject("scripts/importDefinitelyTypedTests"); -cleanTasks.push(cleanImportDefinitelyTypedTests); - // TODO(rbuckton): Should the path to DefinitelyTyped be configurable via an environment variable? -const importDefinitelyTypedTests = () => exec(process.execPath, ["scripts/importDefinitelyTypedTests/importDefinitelyTypedTests.js", "./", "../DefinitelyTyped"]); -task("importDefinitelyTypedTests", series(buildImportDefinitelyTypedTests, importDefinitelyTypedTests)); +const importDefinitelyTypedTests = () => exec(process.execPath, ["scripts/importDefinitelyTypedTests.mjs", "./", "../DefinitelyTyped"]); +task("importDefinitelyTypedTests", importDefinitelyTypedTests); task("importDefinitelyTypedTests").description = "Runs the importDefinitelyTypedTests script to copy DT's tests to the TS-internal RWC tests"; const buildReleaseTsc = () => buildProject("src/tsc/tsconfig.release.json"); @@ -529,7 +515,7 @@ const produceLKG = async () => { throw new Error("Cannot replace the LKG unless all built targets are present in directory 'built/local/'. The following files are missing:\n" + missingFiles.join("\n")); } const sizeBefore = getDirSize("lib"); - await exec(process.execPath, ["scripts/produceLKG.js"]); + await exec(process.execPath, ["scripts/produceLKG.mjs"]); const sizeAfter = getDirSize("lib"); if (sizeAfter > (sizeBefore * 1.10)) { throw new Error("The lib folder increased by 10% or more. This likely indicates a bug."); @@ -543,8 +529,8 @@ task("LKG").flags = { }; task("lkg", series("LKG")); -const generateSpec = () => exec("cscript", ["//nologo", "scripts/word2md.js", path.resolve("doc/TypeScript Language Specification - ARCHIVED.docx"), path.resolve("doc/spec-ARCHIVED.md")]); -task("generate-spec", series(buildScripts, generateSpec)); +const generateSpec = () => exec("cscript", ["//nologo", "scripts/word2md.mjs", path.resolve("doc/TypeScript Language Specification - ARCHIVED.docx"), path.resolve("doc/spec-ARCHIVED.md")]); +task("generate-spec", generateSpec); task("generate-spec").description = "Generates a Markdown version of the Language Specification"; task("clean", series(parallel(cleanTasks), cleanBuilt)); @@ -554,13 +540,13 @@ const configureNightly = () => exec(process.execPath, ["scripts/configurePrerele task("configure-nightly", series(buildScripts, configureNightly)); task("configure-nightly").description = "Runs scripts/configurePrerelease.ts to prepare a build for nightly publishing"; -const configureInsiders = () => exec(process.execPath, ["scripts/configurePrerelease.js", "insiders", "package.json", "src/compiler/corePublic.ts"]); -task("configure-insiders", series(buildScripts, configureInsiders)); -task("configure-insiders").description = "Runs scripts/configurePrerelease.ts to prepare a build for insiders publishing"; +const configureInsiders = () => exec(process.execPath, ["scripts/configurePrerelease.mjs", "insiders", "package.json", "src/compiler/corePublic.ts"]); +task("configure-insiders", configureInsiders); +task("configure-insiders").description = "Runs scripts/configurePrerelease.mjs to prepare a build for insiders publishing"; -const configureExperimental = () => exec(process.execPath, ["scripts/configurePrerelease.js", "experimental", "package.json", "src/compiler/corePublic.ts"]); -task("configure-experimental", series(buildScripts, configureExperimental)); -task("configure-experimental").description = "Runs scripts/configurePrerelease.ts to prepare a build for experimental publishing"; +const configureExperimental = () => exec(process.execPath, ["scripts/configurePrerelease.mjs", "experimental", "package.json", "src/compiler/corePublic.ts"]); +task("configure-experimental", configureExperimental); +task("configure-experimental").description = "Runs scripts/configurePrerelease.mjs to prepare a build for experimental publishing"; const publishNightly = () => exec("npm", ["publish", "--tag", "next"]); task("publish-nightly", series(task("clean"), task("LKG"), task("clean"), task("runtests-parallel"), publishNightly)); diff --git a/package-lock.json b/package-lock.json index a2531c11527..1d7f1fa70fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "devDependencies": { "@octokit/rest": "latest", "@types/chai": "latest", + "@types/fancy-log": "^2.0.0", "@types/fs-extra": "^9.0.13", "@types/glob": "latest", "@types/gulp": "^4.0.9", @@ -29,8 +30,8 @@ "@types/mocha": "latest", "@types/ms": "latest", "@types/node": "latest", - "@types/node-fetch": "^2.6.2", "@types/source-map-support": "latest", + "@types/which": "^2.0.1", "@types/xml2js": "^0.4.11", "@typescript-eslint/eslint-plugin": "^5.33.1", "@typescript-eslint/parser": "^5.33.1", @@ -61,7 +62,7 @@ "mocha": "latest", "mocha-fivemat-progress-reporter": "latest", "ms": "^2.1.3", - "node-fetch": "^2.6.7", + "node-fetch": "^3.2.10", "source-map-support": "latest", "typescript": "^4.8.4", "vinyl": "latest", @@ -385,6 +386,26 @@ "node": ">= 14" } }, + "node_modules/@octokit/request/node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/@octokit/rest": { "version": "19.0.4", "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.4.tgz", @@ -421,6 +442,12 @@ "integrity": "sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==", "dev": true }, + "node_modules/@types/fancy-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/fancy-log/-/fancy-log-2.0.0.tgz", + "integrity": "sha512-g39Vp8ZJ3D0gXhhkhDidVvdy4QajkF7/PV6HGn23FMaMqE/tLC1JNHUeQ7SshKLsBjucakZsXBLkWULbGLdL5g==", + "dev": true + }, "node_modules/@types/fs-extra": { "version": "9.0.13", "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", @@ -565,16 +592,6 @@ "integrity": "sha512-0os9vz6BpGwxGe9LOhgP/ncvYN5Tx1fNcd2TM3rD/aCGBkysb+ZWpXEocG24h6ZzOi13+VB8HndAQFezsSOw1w==", "dev": true }, - "node_modules/@types/node-fetch": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz", - "integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==", - "dev": true, - "dependencies": { - "@types/node": "*", - "form-data": "^3.0.0" - } - }, "node_modules/@types/source-map-support": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/@types/source-map-support/-/source-map-support-0.5.6.tgz", @@ -622,6 +639,12 @@ "@types/vinyl": "*" } }, + "node_modules/@types/which": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.1.tgz", + "integrity": "sha512-Jjakcv8Roqtio6w1gr0D7y6twbhx6gGgFGF5BLwajPpnOIOxFkakFhCq+LmyyeAz7BX6ULrjBOxdKaCDy+4+dQ==", + "dev": true + }, "node_modules/@types/xml2js": { "version": "0.4.11", "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.11.tgz", @@ -1234,12 +1257,6 @@ "node": ">= 0.10" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, "node_modules/at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", @@ -1753,18 +1770,6 @@ "color-support": "bin.js" } }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/comment-parser": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz", @@ -1879,6 +1884,15 @@ "type": "^1.0.1" } }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", + "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -2030,15 +2044,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/deprecation": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", @@ -2964,6 +2969,29 @@ "reusify": "^1.0.4" } }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -3292,18 +3320,16 @@ "node": ">=0.10.0" } }, - "node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", "dev": true, "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "fetch-blob": "^3.1.2" }, "engines": { - "node": ">= 6" + "node": ">=12.20.0" } }, "node_modules/fragment-cache": { @@ -5499,27 +5525,6 @@ "node": ">=8.6" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -5882,24 +5887,41 @@ "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", "dev": true }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.10.tgz", + "integrity": "sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==", "dev": true, "dependencies": { - "whatwg-url": "^5.0.0" + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" }, "engines": { - "node": "4.x || >=6.0.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" } }, "node_modules/normalize-package-data": { @@ -8399,6 +8421,15 @@ "node": ">= 0.10" } }, + "node_modules/web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -8868,6 +8899,17 @@ "is-plain-object": "^5.0.0", "node-fetch": "^2.6.7", "universal-user-agent": "^6.0.0" + }, + "dependencies": { + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + } + } } }, "@octokit/request-error": { @@ -8914,6 +8956,12 @@ "integrity": "sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==", "dev": true }, + "@types/fancy-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/fancy-log/-/fancy-log-2.0.0.tgz", + "integrity": "sha512-g39Vp8ZJ3D0gXhhkhDidVvdy4QajkF7/PV6HGn23FMaMqE/tLC1JNHUeQ7SshKLsBjucakZsXBLkWULbGLdL5g==", + "dev": true + }, "@types/fs-extra": { "version": "9.0.13", "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", @@ -9058,16 +9106,6 @@ "integrity": "sha512-0os9vz6BpGwxGe9LOhgP/ncvYN5Tx1fNcd2TM3rD/aCGBkysb+ZWpXEocG24h6ZzOi13+VB8HndAQFezsSOw1w==", "dev": true }, - "@types/node-fetch": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz", - "integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==", - "dev": true, - "requires": { - "@types/node": "*", - "form-data": "^3.0.0" - } - }, "@types/source-map-support": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/@types/source-map-support/-/source-map-support-0.5.6.tgz", @@ -9115,6 +9153,12 @@ "@types/vinyl": "*" } }, + "@types/which": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.1.tgz", + "integrity": "sha512-Jjakcv8Roqtio6w1gr0D7y6twbhx6gGgFGF5BLwajPpnOIOxFkakFhCq+LmyyeAz7BX6ULrjBOxdKaCDy+4+dQ==", + "dev": true + }, "@types/xml2js": { "version": "0.4.11", "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.11.tgz", @@ -9530,12 +9574,6 @@ "async-done": "^1.2.2" } }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, "at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", @@ -9943,15 +9981,6 @@ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "dev": true }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, "comment-parser": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz", @@ -10054,6 +10083,12 @@ "type": "^1.0.1" } }, + "data-uri-to-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", + "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==", + "dev": true + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -10171,12 +10206,6 @@ "slash": "^3.0.0" } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true - }, "deprecation": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", @@ -10945,6 +10974,16 @@ "reusify": "^1.0.4" } }, + "fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, + "requires": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + } + }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -11208,15 +11247,13 @@ "for-in": "^1.0.1" } }, - "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", "dev": true, "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "fetch-blob": "^3.1.2" } }, "fragment-cache": { @@ -12929,21 +12966,6 @@ "picomatch": "^2.3.1" } }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "requires": { - "mime-db": "1.52.0" - } - }, "minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -13225,13 +13247,21 @@ "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", "dev": true }, + "node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "dev": true + }, "node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.10.tgz", + "integrity": "sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==", "dev": true, "requires": { - "whatwg-url": "^5.0.0" + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" } }, "normalize-package-data": { @@ -15196,6 +15226,12 @@ } } }, + "web-streams-polyfill": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", + "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", + "dev": true + }, "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/package.json b/package.json index 4fd407ca8d0..384d29f05ce 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "devDependencies": { "@octokit/rest": "latest", "@types/chai": "latest", + "@types/fancy-log": "^2.0.0", "@types/fs-extra": "^9.0.13", "@types/glob": "latest", "@types/gulp": "^4.0.9", @@ -55,8 +56,8 @@ "@types/mocha": "latest", "@types/ms": "latest", "@types/node": "latest", - "@types/node-fetch": "^2.6.2", "@types/source-map-support": "latest", + "@types/which": "^2.0.1", "@types/xml2js": "^0.4.11", "@typescript-eslint/eslint-plugin": "^5.33.1", "@typescript-eslint/parser": "^5.33.1", @@ -87,7 +88,7 @@ "mocha": "latest", "mocha-fivemat-progress-reporter": "latest", "ms": "^2.1.3", - "node-fetch": "^2.6.7", + "node-fetch": "^3.2.10", "source-map-support": "latest", "typescript": "^4.8.4", "vinyl": "latest", @@ -107,7 +108,7 @@ "clean": "gulp clean", "gulp": "gulp", "lint": "gulp lint", - "setup-hooks": "node scripts/link-hooks.js" + "setup-hooks": "node scripts/link-hooks.mjs" }, "browser": { "fs": false, diff --git a/scripts/browserIntegrationTest.js b/scripts/browserIntegrationTest.mjs similarity index 64% rename from scripts/browserIntegrationTest.js rename to scripts/browserIntegrationTest.mjs index 622af7cb43d..6e96bd45897 100644 --- a/scripts/browserIntegrationTest.js +++ b/scripts/browserIntegrationTest.mjs @@ -1,34 +1,36 @@ -// @ts-check -const chalk = require("chalk"); -const { join } = require("path"); -const { readFileSync } = require("fs"); +import chalk from "chalk"; +import { join } from "path"; +import { readFileSync } from "fs"; + +let playwright; try { - // eslint-disable-next-line import/no-extraneous-dependencies - require("playwright"); + // @ts-ignore-error + playwright = await import("playwright"); } catch (error) { throw new Error("Playwright is expected to be installed manually before running this script"); } -// eslint-disable-next-line import/no-extraneous-dependencies -const playwright = require("playwright"); - // Turning this on will leave the Chromium browser open, giving you the // chance to open up the web inspector. const debugging = false; -(async () => { - for (const browserType of ["chromium", "firefox"]) { +/** @type {["chromium", "firefox"]} */ +const browsers = ["chromium", "firefox"]; + +for (const browserType of browsers) { const browser = await playwright[browserType].launch({ headless: !debugging }); const context = await browser.newContext(); const page = await context.newPage(); + /** @type {(err: Error) => void} */ const errorCaught = err => { console.error(chalk.red("There was an error running built/typescript.js in " + browserType)); console.log(err.toString()); process.exitCode = 1; }; + // @ts-ignore-error page.on("error", errorCaught); page.on("pageerror", errorCaught); @@ -45,13 +47,4 @@ const debugging = false; console.log("Not closing the browser, you'll need to exit the process in your terminal manually"); } console.log(`${browserType} :+1:`); - } -})(); - -process.on("unhandledRejection", (/** @type {any}*/ err) => { - if (err) { - console.error(err.stack || err.message); - } - process.exit(1); -}); - +} diff --git a/scripts/build/findUpDir.js b/scripts/build/findUpDir.js deleted file mode 100644 index fab301c6dcd..00000000000 --- a/scripts/build/findUpDir.js +++ /dev/null @@ -1,21 +0,0 @@ -const { join, resolve, dirname } = require("path"); -const { existsSync } = require("fs"); - -// search directories upward to avoid hard-wired paths based on the -// build tree (same as src/harness/findUpDir.ts) - -function findUpFile(name) { - let dir = __dirname; - while (true) { - const fullPath = join(dir, name); - if (existsSync(fullPath)) return fullPath; - const up = resolve(dir, ".."); - if (up === dir) return name; // it'll fail anyway - dir = up; - } -} -exports.findUpFile = findUpFile; - -const findUpRoot = () => - findUpRoot.cached || (findUpRoot.cached = dirname(findUpFile("Gulpfile.js"))); -exports.findUpRoot = findUpRoot; diff --git a/scripts/build/findUpDir.mjs b/scripts/build/findUpDir.mjs new file mode 100644 index 00000000000..5a03844b7a4 --- /dev/null +++ b/scripts/build/findUpDir.mjs @@ -0,0 +1,29 @@ +import { join, resolve, dirname } from "path"; +import { existsSync } from "fs"; +import url from "url"; + +const __filename = url.fileURLToPath(new URL(import.meta.url)); +const __dirname = dirname(__filename); + +// search directories upward to avoid hard-wired paths based on the +// build tree (same as src/harness/findUpDir.ts) + +/** + * @param {string} name + * @returns {string} + */ +export function findUpFile(name) { + let dir = __dirname; + while (true) { + const fullPath = join(dir, name); + if (existsSync(fullPath)) return fullPath; + const up = resolve(dir, ".."); + if (up === dir) return name; // it'll fail anyway + dir = up; + } +} + +/** @type {string | undefined} */ +let findUpRootCache; + +export const findUpRoot = () => findUpRootCache || (findUpRootCache = dirname(findUpFile("Gulpfile.mjs"))); diff --git a/scripts/build/options.js b/scripts/build/options.mjs similarity index 82% rename from scripts/build/options.js rename to scripts/build/options.mjs index fc237c58855..78240aa9aec 100644 --- a/scripts/build/options.js +++ b/scripts/build/options.mjs @@ -1,11 +1,9 @@ -// @ts-check -const minimist = require("minimist"); -const os = require("os"); +import minimist from "minimist"; +import os from "os"; -const ci = ["1", "true"].includes(process.env.CI); +const ci = ["1", "true"].includes(process.env.CI ?? ""); -/** @type {CommandLineOptions} */ -module.exports = minimist(process.argv.slice(2), { +const parsed = minimist(process.argv.slice(2), { boolean: ["dirty", "light", "colors", "lkg", "soft", "fix", "failed", "keepFailed", "force", "built", "ci"], string: ["browser", "tests", "break", "host", "reporter", "stackTraceLimit", "timeout", "shards", "shardId"], alias: { @@ -44,12 +42,19 @@ module.exports = minimist(process.argv.slice(2), { } }); -if (module.exports.built) { - module.exports.lkg = false; +/** @type {CommandLineOptions} */ +const options = /** @type {any} */ (parsed); + +if (options.built) { + options.lkg = false; } +export default options; + + + /** - * @typedef TypedOptions + * @typedef CommandLineOptions * @property {boolean} dirty * @property {boolean} light * @property {boolean} colors @@ -59,6 +64,7 @@ if (module.exports.built) { * @property {boolean} fix * @property {string} browser * @property {string} tests + * @property {string | boolean} break * @property {string | boolean} inspect * @property {string} runners * @property {string|number} workers @@ -69,7 +75,7 @@ if (module.exports.built) { * @property {boolean} failed * @property {boolean} keepFailed * @property {boolean} ci - * - * @typedef {import("minimist").ParsedArgs & TypedOptions} CommandLineOptions + * @property {string} shards + * @property {string} shardId */ void 0; diff --git a/scripts/build/prepend.js b/scripts/build/prepend.mjs similarity index 78% rename from scripts/build/prepend.js rename to scripts/build/prepend.mjs index d7571efdaab..115cd3b3d59 100644 --- a/scripts/build/prepend.js +++ b/scripts/build/prepend.mjs @@ -1,18 +1,17 @@ -// @ts-check -const stream = require("stream"); -const ts = require("../../lib/typescript"); -const fs = require("fs"); -const { base64VLQFormatEncode } = require("./sourcemaps"); +import stream from "stream"; +import ts from "../../lib/typescript.js"; +import fs from "fs"; +import { base64VLQFormatEncode } from "./sourcemaps.mjs"; /** * @param {string | ((file: import("vinyl")) => string)} data */ -function prepend(data) { +export function prepend(data) { return new stream.Transform({ objectMode: true, /** * @param {string | Buffer | import("vinyl")} input - * @param {(error: Error, data?: any) => void} cb + * @param {(error: Error | null, data?: any) => void} cb */ transform(input, _, cb) { if (typeof input === "string" || Buffer.isBuffer(input)) return cb(new Error("Only Vinyl files are supported.")); @@ -22,7 +21,7 @@ function prepend(data) { const prependContent = typeof data === "function" ? data(input) : data; output.contents = Buffer.concat([Buffer.from(prependContent, "utf8"), input.contents]); if (input.sourceMap) { - if (typeof input.sourceMap === "string") input.sourceMap = /**@type {import("./sourcemaps").RawSourceMap}*/(JSON.parse(input.sourceMap)); + if (typeof input.sourceMap === "string") input.sourceMap = /**@type {import("./sourcemaps.mjs").RawSourceMap}*/(JSON.parse(input.sourceMap)); const lineStarts = /**@type {*}*/(ts).computeLineStarts(prependContent); let prependMappings = ""; for (let i = 1; i < lineStarts.length; i++) { @@ -46,19 +45,17 @@ function prepend(data) { return cb(null, output); } catch (e) { - return cb(e); + return cb(/** @type {Error} */(e)); } } }); } -exports.prepend = prepend; /** * @param {string | ((file: import("vinyl")) => string)} file */ -function prependFile(file) { +export function prependFile(file) { const data = typeof file === "string" ? fs.readFileSync(file, "utf8") : - vinyl => fs.readFileSync(file(vinyl), "utf8"); + (/** @type {import("vinyl")} */ vinyl) => fs.readFileSync(file(vinyl), "utf8"); return prepend(data); } -exports.prependFile = prependFile; diff --git a/scripts/build/projects.js b/scripts/build/projects.mjs similarity index 76% rename from scripts/build/projects.js rename to scripts/build/projects.mjs index 7346607d3bf..1061f5521b2 100644 --- a/scripts/build/projects.js +++ b/scripts/build/projects.mjs @@ -1,7 +1,7 @@ -// @ts-check -const { exec, Debouncer } = require("./utils"); -const { resolve } = require("path"); -const { findUpRoot } = require("./findUpDir"); +import { exec, Debouncer } from "./utils.mjs"; +import { resolve } from "path"; +import { findUpRoot } from "./findUpDir.mjs"; +import assert from "assert"; class ProjectQueue { /** @@ -15,12 +15,13 @@ class ProjectQueue { /** * @param {string} project - * @param {object} options + * @param {{ lkg?: boolean; force?: boolean; }} options */ enqueue(project, { lkg = true, force = false } = {}) { let entry = this._debouncers.find(entry => entry.lkg === lkg && entry.force === force); if (!entry) { const debouncer = new Debouncer(100, async () => { + assert(entry); const projects = entry.projects; if (projects) { entry.projects = undefined; @@ -49,14 +50,14 @@ const projectBuilder = new ProjectQueue((projects, lkg, force) => execTsc(lkg, . * @param {boolean} [options.lkg=true] * @param {boolean} [options.force=false] */ -exports.buildProject = (project, { lkg, force } = {}) => projectBuilder.enqueue(project, { lkg, force }); +export const buildProject = (project, { lkg, force } = {}) => projectBuilder.enqueue(project, { lkg, force }); const projectCleaner = new ProjectQueue((projects, lkg) => execTsc(lkg, "--clean", ...projects)); /** * @param {string} project */ -exports.cleanProject = (project) => projectCleaner.enqueue(project); + export const cleanProject = (project) => projectCleaner.enqueue(project); const projectWatcher = new ProjectQueue((projects) => execTsc(/*lkg*/ true, "--watch", ...projects)); @@ -65,4 +66,4 @@ const projectWatcher = new ProjectQueue((projects) => execTsc(/*lkg*/ true, "--w * @param {object} options * @param {boolean} [options.lkg=true] */ -exports.watchProject = (project, { lkg } = {}) => projectWatcher.enqueue(project, { lkg }); +export const watchProject = (project, { lkg } = {}) => projectWatcher.enqueue(project, { lkg }); diff --git a/scripts/build/sourcemaps.js b/scripts/build/sourcemaps.mjs similarity index 87% rename from scripts/build/sourcemaps.js rename to scripts/build/sourcemaps.mjs index e572bf9cb49..6fd152c258c 100644 --- a/scripts/build/sourcemaps.js +++ b/scripts/build/sourcemaps.mjs @@ -1,4 +1,3 @@ -// @ts-check /** * @param {string} message * @returns {never} @@ -23,7 +22,7 @@ function base64FormatEncode(value) { /** * @param {number} value */ -function base64VLQFormatEncode(value) { +export function base64VLQFormatEncode(value) { if (value < 0) { value = ((-value) << 1) + 1; } @@ -45,6 +44,5 @@ function base64VLQFormatEncode(value) { return result; } -exports.base64VLQFormatEncode = base64VLQFormatEncode; /** @typedef {object} RawSourceMap */ diff --git a/scripts/build/tests.js b/scripts/build/tests.mjs similarity index 77% rename from scripts/build/tests.js rename to scripts/build/tests.mjs index 4dd76c1f62e..dce50403c0d 100644 --- a/scripts/build/tests.js +++ b/scripts/build/tests.mjs @@ -1,28 +1,27 @@ -// @ts-check -const del = require("del"); -const fs = require("fs"); -const os = require("os"); -const path = require("path"); -const mkdirP = require("mkdirp"); -const log = require("fancy-log"); -const cmdLineOptions = require("./options"); -const { exec } = require("./utils"); -const { findUpFile } = require("./findUpDir"); +import del from "del"; +import fs from "fs"; +import os from "os"; +import path from "path"; +import mkdirP from "mkdirp"; +import log from "fancy-log"; +import cmdLineOptions from "./options.mjs"; +import { exec } from "./utils.mjs"; +import { findUpFile, findUpRoot } from "./findUpDir.mjs"; -const mochaJs = require.resolve("mocha/bin/_mocha"); -exports.localBaseline = "tests/baselines/local/"; -exports.refBaseline = "tests/baselines/reference/"; -exports.localRwcBaseline = "internal/baselines/rwc/local"; -exports.refRwcBaseline = "internal/baselines/rwc/reference"; -exports.localTest262Baseline = "internal/baselines/test262/local"; +const mochaJs = path.resolve(findUpRoot(), "node_modules", "mocha", "bin", "_mocha"); +export const localBaseline = "tests/baselines/local/"; +export const refBaseline = "tests/baselines/reference/"; +export const localRwcBaseline = "internal/baselines/rwc/local"; +export const refRwcBaseline = "internal/baselines/rwc/reference"; +export const localTest262Baseline = "internal/baselines/test262/local"; /** * @param {string} runJs * @param {string} defaultReporter * @param {boolean} runInParallel - * @param {boolean} watchMode + * @param {boolean} _watchMode */ -async function runConsoleTests(runJs, defaultReporter, runInParallel, watchMode) { +export async function runConsoleTests(runJs, defaultReporter, runInParallel, _watchMode) { let testTimeout = cmdLineOptions.timeout; const tests = cmdLineOptions.tests; const inspect = cmdLineOptions.break || cmdLineOptions.inspect; @@ -74,7 +73,7 @@ async function runConsoleTests(runJs, defaultReporter, runInParallel, watchMode) // default timeout is 2sec which really should be enough, but maybe we just need a small amount longer if (!runInParallel) { args.push(mochaJs); - args.push("-R", findUpFile("scripts/failed-tests.js")); + args.push("-R", findUpFile("scripts/failed-tests.cjs")); args.push("-O", '"reporter=' + reporter + (keepFailed ? ",keepFailed=true" : "") + '"'); if (tests) { args.push("-g", `"${tests}"`); @@ -123,7 +122,7 @@ async function runConsoleTests(runJs, defaultReporter, runInParallel, watchMode) errorStatus = exitCode; error = new Error(`Process exited with status code ${errorStatus}.`); } - else if (cmdLineOptions.ci) { + else if (cmdLineOptions.ci && runJs.startsWith("built")) { // finally, do a sanity check and build the compiler with the built version of itself log.info("Starting sanity check build..."); // Cleanup everything except lint rules (we'll need those later and would rather not waste time rebuilding them) @@ -137,7 +136,7 @@ async function runConsoleTests(runJs, defaultReporter, runInParallel, watchMode) } catch (e) { errorStatus = undefined; - error = e; + error = /** @type {Error} */ (e); } finally { restoreSavedNodeEnv(); @@ -151,14 +150,12 @@ async function runConsoleTests(runJs, defaultReporter, runInParallel, watchMode) throw error; } } -exports.runConsoleTests = runConsoleTests; -async function cleanTestDirs() { - await del([exports.localBaseline, exports.localRwcBaseline]); - mkdirP.sync(exports.localRwcBaseline); - mkdirP.sync(exports.localBaseline); +export async function cleanTestDirs() { + await del([localBaseline, localRwcBaseline]); + mkdirP.sync(localRwcBaseline); + mkdirP.sync(localBaseline); } -exports.cleanTestDirs = cleanTestDirs; /** * used to pass data from gulp command line directly to run.js @@ -173,7 +170,7 @@ exports.cleanTestDirs = cleanTestDirs; * @param {number | undefined} [shards] * @param {number | undefined} [shardId] */ -function writeTestConfigFile(tests, runners, light, taskConfigsFolder, workerCount, stackTraceLimit, timeout, keepFailed, shards, shardId) { +export function writeTestConfigFile(tests, runners, light, taskConfigsFolder, workerCount, stackTraceLimit, timeout, keepFailed, shards, shardId) { const testConfigContents = JSON.stringify({ test: tests ? [tests] : undefined, runners: runners ? runners.split(",") : undefined, @@ -190,9 +187,8 @@ function writeTestConfigFile(tests, runners, light, taskConfigsFolder, workerCou log.info("Running tests with config: " + testConfigContents); fs.writeFileSync("test.config", testConfigContents); } -exports.writeTestConfigFile = writeTestConfigFile; -/** @type {string} */ +/** @type {string | undefined} */ let savedNodeEnv; function setNodeEnvToDevelopment() { savedNodeEnv = process.env.NODE_ENV; @@ -204,9 +200,12 @@ function restoreSavedNodeEnv() { } function deleteTemporaryProjectOutput() { - return del(path.join(exports.localBaseline, "projectOutput/")); + return del(path.join(localBaseline, "projectOutput/")); } +/** + * @param {string} text + */ function regExpEscape(text) { return text.replace(/[.*+?^${}()|\[\]\\]/g, "\\$&"); } diff --git a/scripts/build/utils.js b/scripts/build/utils.mjs similarity index 59% rename from scripts/build/utils.js rename to scripts/build/utils.mjs index 0e38a0b73d6..59075df3a41 100644 --- a/scripts/build/utils.js +++ b/scripts/build/utils.mjs @@ -1,20 +1,18 @@ -// @ts-check - /* eslint-disable no-restricted-globals */ // eslint-disable-next-line @typescript-eslint/triple-slash-reference /// -const fs = require("fs"); -const path = require("path"); -const log = require("fancy-log"); -const mkdirp = require("mkdirp"); -const del = require("del"); -const File = require("vinyl"); -const ts = require("../../lib/typescript"); -const chalk = require("chalk"); -const which = require("which"); -const { spawn } = require("child_process"); -const { Readable, Duplex } = require("stream"); +import fs from "fs"; +import path from "path"; +import log from "fancy-log"; +import del from "del"; +import File from "vinyl"; +import ts from "../../lib/typescript.js"; +import chalk from "chalk"; +import which from "which"; +import { spawn } from "child_process"; +import { Duplex } from "stream"; +import assert from "assert"; /** * Executes the provided command once with the supplied arguments. @@ -27,8 +25,8 @@ const { Readable, Duplex } = require("stream"); * @property {boolean} [hidePrompt] * @property {boolean} [waitForExit=true] */ -async function exec(cmd, args, options = {}) { - return /**@type {Promise<{exitCode: number}>}*/(new Promise((resolve, reject) => { +export async function exec(cmd, args, options = {}) { + return /**@type {Promise<{exitCode?: number}>}*/(new Promise((resolve, reject) => { const { ignoreExitCode, waitForExit = true } = options; if (!options.hidePrompt) log(`> ${chalk.green(cmd)} ${args.join(" ")}`); @@ -36,7 +34,7 @@ async function exec(cmd, args, options = {}) { if (waitForExit) { proc.on("exit", exitCode => { if (exitCode === 0 || ignoreExitCode) { - resolve({ exitCode }); + resolve({ exitCode: exitCode ?? undefined }); } else { reject(new Error(`Process exited with code: ${exitCode}`)); @@ -53,7 +51,6 @@ async function exec(cmd, args, options = {}) { } })); } -exports.exec = exec; /** * @param {ts.Diagnostic[]} diagnostics @@ -64,7 +61,6 @@ function formatDiagnostics(diagnostics, options) { ? ts.formatDiagnosticsWithColorAndContext(diagnostics, getFormatDiagnosticsHost(options && options.cwd)) : ts.formatDiagnostics(diagnostics, getFormatDiagnosticsHost(options && options.cwd)); } -exports.formatDiagnostics = formatDiagnostics; /** * @param {ts.Diagnostic[]} diagnostics @@ -73,7 +69,6 @@ exports.formatDiagnostics = formatDiagnostics; function reportDiagnostics(diagnostics, options) { log(formatDiagnostics(diagnostics, { cwd: options && options.cwd, pretty: process.stdout.isTTY })); } -exports.reportDiagnostics = reportDiagnostics; /** * @param {string | undefined} cwd @@ -82,17 +77,16 @@ exports.reportDiagnostics = reportDiagnostics; function getFormatDiagnosticsHost(cwd) { return { getCanonicalFileName: fileName => fileName, - getCurrentDirectory: () => cwd, + getCurrentDirectory: () => cwd ?? process.cwd(), getNewLine: () => ts.sys.newLine, }; } -exports.getFormatDiagnosticsHost = getFormatDiagnosticsHost; /** * Reads JSON data with optional comments using the LKG TypeScript compiler * @param {string} jsonPath */ -function readJson(jsonPath) { +export function readJson(jsonPath) { const jsonText = fs.readFileSync(jsonPath, "utf8"); const result = ts.parseConfigFileTextToJson(jsonPath, jsonText); if (result.error) { @@ -101,38 +95,13 @@ function readJson(jsonPath) { } return result.config; } -exports.readJson = readJson; - -/** - * @param {File} file - */ -function streamFromFile(file) { - return file.isBuffer() ? streamFromBuffer(file.contents) : - file.isStream() ? file.contents : - fs.createReadStream(file.path, { autoClose: true }); -} -exports.streamFromFile = streamFromFile; - -/** - * @param {Buffer} buffer - */ -function streamFromBuffer(buffer) { - return new Readable({ - read() { - this.push(buffer); - // eslint-disable-next-line no-null/no-null - this.push(null); - } - }); -} -exports.streamFromBuffer = streamFromBuffer; /** * @param {string | string[]} source * @param {string | string[]} dest * @returns {boolean} */ -function needsUpdate(source, dest) { +export function needsUpdate(source, dest) { if (typeof source === "string" && typeof dest === "string") { if (fs.existsSync(dest)) { const {mtime: outTime} = fs.statSync(dest); @@ -194,9 +163,8 @@ function needsUpdate(source, dest) { } return true; } -exports.needsUpdate = needsUpdate; -function getDiffTool() { +export function getDiffTool() { const program = process.env.DIFF; if (!program) { log.warn("Add the 'DIFF' environment variable to the path of the program you want to use."); @@ -204,7 +172,6 @@ function getDiffTool() { } return program; } -exports.getDiffTool = getDiffTool; /** * Find the size of a directory recursively. @@ -212,7 +179,7 @@ exports.getDiffTool = getDiffTool; * @param {string} root * @returns {number} bytes */ -function getDirSize(root) { +export function getDirSize(root) { const stats = fs.lstatSync(root); if (!stats.isDirectory()) { @@ -223,97 +190,12 @@ function getDirSize(root) { .map(file => getDirSize(path.join(root, file))) .reduce((acc, num) => acc + num, 0); } -exports.getDirSize = getDirSize; - -/** - * Flattens a project with project references into a single project. - * @param {string} projectSpec The path to a tsconfig.json file or its containing directory. - * @param {string} flattenedProjectSpec The output path for the flattened tsconfig.json file. - * @param {FlattenOptions} [options] Options used to flatten a project hierarchy. - * - * @typedef FlattenOptions - * @property {string} [cwd] The path to use for the current working directory. Defaults to `process.cwd()`. - * @property {import("../../lib/typescript").CompilerOptions} [compilerOptions] Compiler option overrides. - * @property {boolean} [force] Forces creation of the output project. - * @property {string[]} [exclude] Files to exclude (relative to `cwd`) - */ -function flatten(projectSpec, flattenedProjectSpec, options = {}) { - const cwd = normalizeSlashes(options.cwd ? path.resolve(options.cwd) : process.cwd()); - const files = []; - const resolvedOutputSpec = path.resolve(cwd, flattenedProjectSpec); - const resolvedOutputDirectory = path.dirname(resolvedOutputSpec); - const resolvedProjectSpec = resolveProjectSpec(projectSpec, cwd, /*referrer*/ undefined); - const project = readJson(resolvedProjectSpec); - const skipProjects = /**@type {Set}*/(new Set()); - const skipFiles = new Set(options && options.exclude && options.exclude.map(file => normalizeSlashes(path.resolve(cwd, file)))); - recur(resolvedProjectSpec, project); - - if (options.force || needsUpdate(files, resolvedOutputSpec)) { - const config = { - extends: normalizeSlashes(path.relative(resolvedOutputDirectory, resolvedProjectSpec)), - compilerOptions: options.compilerOptions || {}, - files: files.map(file => normalizeSlashes(path.relative(resolvedOutputDirectory, file))) - }; - mkdirp.sync(resolvedOutputDirectory); - fs.writeFileSync(resolvedOutputSpec, JSON.stringify(config, undefined, 2), "utf8"); - } - - /** - * @param {string} projectSpec - * @param {object} project - */ - function recur(projectSpec, project) { - if (skipProjects.has(projectSpec)) return; - skipProjects.add(project); - if (project.references) { - for (const ref of project.references) { - const referencedSpec = resolveProjectSpec(ref.path, cwd, projectSpec); - const referencedProject = readJson(referencedSpec); - recur(referencedSpec, referencedProject); - } - } - if (project.include) { - throw new Error("Flattened project may not have an 'include' list."); - } - if (!project.files) { - throw new Error("Flattened project must have an explicit 'files' list."); - } - const projectDirectory = path.dirname(projectSpec); - for (let file of project.files) { - file = normalizeSlashes(path.resolve(projectDirectory, file)); - if (skipFiles.has(file)) continue; - skipFiles.add(file); - files.push(file); - } - } -} -exports.flatten = flatten; - -/** - * @param {string} file - */ -function normalizeSlashes(file) { - return file.replace(/\\/g, "/"); -} - -/** - * @param {string} projectSpec - * @param {string} cwd - * @param {string | undefined} referrer - * @returns {string} - */ -function resolveProjectSpec(projectSpec, cwd, referrer) { - const projectPath = normalizeSlashes(path.resolve(cwd, referrer ? path.dirname(referrer) : "", projectSpec)); - const stats = fs.statSync(projectPath); - if (stats.isFile()) return normalizeSlashes(projectPath); - return normalizeSlashes(path.resolve(cwd, projectPath, "tsconfig.json")); -} /** * @param {string | ((file: File) => string) | { cwd?: string }} [dest] * @param {{ cwd?: string }} [opts] */ -function rm(dest, opts) { +export function rm(dest, opts) { if (dest && typeof dest === "object") { opts = dest; dest = undefined; @@ -328,7 +210,9 @@ function rm(dest, opts) { const processDeleted = () => { if (failed) return; while (pending.length && pending[0].deleted) { - const { file, cb } = pending.shift(); + const fileAndCallback = pending.shift(); + assert(fileAndCallback); + const { file, cb } = fileAndCallback; duplex.push(file); cb(); } @@ -382,7 +266,6 @@ function rm(dest, opts) { }); return duplex; } -exports.rm = rm; class Deferred { constructor() { @@ -393,7 +276,7 @@ class Deferred { } } -class Debouncer { +export class Debouncer { /** * @param {number} timeout * @param {() => Promise} action @@ -424,8 +307,8 @@ class Debouncer { } const deferred = this._deferred; + assert(deferred); this._deferred = undefined; - this._projects = undefined; try { deferred.resolve(this._action()); } @@ -434,4 +317,3 @@ class Debouncer { } } } -exports.Debouncer = Debouncer; diff --git a/scripts/buildProtocol.ts b/scripts/buildProtocol.mjs similarity index 69% rename from scripts/buildProtocol.ts rename to scripts/buildProtocol.mjs index 2cc843d4578..f7bc00c897b 100644 --- a/scripts/buildProtocol.ts +++ b/scripts/buildProtocol.mjs @@ -1,25 +1,59 @@ -/// +import ts from "../lib/typescript.js"; +import path from "path"; +import assert from "assert"; -import * as ts from "../lib/typescript"; -import * as path from "path"; - -function endsWith(s: string, suffix: string) { +/** + * + * @param {string} s + * @param {string} suffix + * @returns {boolean} + */ +function endsWith(s, suffix) { return s.lastIndexOf(suffix, s.length - suffix.length) !== -1; } -function isStringEnum(declaration: ts.EnumDeclaration) { - return declaration.members.length && declaration.members.every(m => !!m.initializer && m.initializer.kind === ts.SyntaxKind.StringLiteral); +/** + * @param {ts.EnumDeclaration} declaration + * @returns {boolean} + */ +function isStringEnum(declaration) { + return !!declaration.members.length && declaration.members.every(m => !!m.initializer && m.initializer.kind === ts.SyntaxKind.StringLiteral); } class DeclarationsWalker { - private visitedTypes: ts.Type[] = []; - private text = ""; - private removedTypes: ts.Type[] = []; + /** + * @type {ts.Type[]} + * @private + */ + visitedTypes = []; + /** + * @type {string} + * @private + */ + text = ""; + /** + * @type {ts.Type[]} + * @private + */ + removedTypes = []; - private constructor(private typeChecker: ts.TypeChecker, private protocolFile: ts.SourceFile) { + /** + * @param {ts.TypeChecker} typeChecker + * @param {ts.SourceFile} protocolFile + * @private + */ + constructor(typeChecker, protocolFile) { + this.typeChecker = typeChecker; + this.protocolFile = protocolFile; } - static getExtraDeclarations(typeChecker: ts.TypeChecker, protocolFile: ts.SourceFile): string { + /** + * + * @param {ts.TypeChecker} typeChecker + * @param {ts.SourceFile} protocolFile + * @returns {string} + */ + static getExtraDeclarations(typeChecker, protocolFile) { const walker = new DeclarationsWalker(typeChecker, protocolFile); walker.visitTypeNodes(protocolFile); let text = walker.text @@ -36,7 +70,12 @@ class DeclarationsWalker { return text; } - private processType(type: ts.Type): void { + /** + * @param {ts.Type} type + * @returns {void} + * @private + */ + processType(type) { if (this.visitedTypes.indexOf(type) >= 0) { return; } @@ -47,7 +86,7 @@ class DeclarationsWalker { } if (s.name === "Array" || s.name === "ReadOnlyArray") { // we should process type argument instead - return this.processType((type as any).typeArguments[0]); + return this.processType(/** @type {any} */(type).typeArguments[0]); } else { const declarations = s.getDeclarations(); @@ -57,7 +96,7 @@ class DeclarationsWalker { if (sourceFile === this.protocolFile || /lib(\..+)?\.d.ts/.test(path.basename(sourceFile.fileName))) { return; } - if (decl.kind === ts.SyntaxKind.EnumDeclaration && !isStringEnum(decl as ts.EnumDeclaration)) { + if (ts.isEnumDeclaration(decl) && !isStringEnum(decl)) { this.removedTypes.push(type); return; } @@ -74,7 +113,11 @@ class DeclarationsWalker { } } - private visitTypeNodes(node: ts.Node) { + /** + * @param {ts.Node} node + * @private + */ + visitTypeNodes(node) { if (node.parent) { switch (node.parent.kind) { case ts.SyntaxKind.VariableDeclaration: @@ -84,12 +127,13 @@ class DeclarationsWalker { case ts.SyntaxKind.PropertySignature: case ts.SyntaxKind.Parameter: case ts.SyntaxKind.IndexSignature: - if (((node.parent as ts.VariableDeclaration | ts.MethodDeclaration | ts.PropertyDeclaration | ts.ParameterDeclaration | ts.PropertySignature | ts.MethodSignature | ts.IndexSignatureDeclaration).type) === node) { + const parent = /** @type {ts.VariableDeclaration | ts.MethodDeclaration | ts.PropertyDeclaration | ts.ParameterDeclaration | ts.PropertySignature | ts.MethodSignature | ts.IndexSignatureDeclaration} */ (node.parent); + if (parent.type === node) { this.processTypeOfNode(node); } break; case ts.SyntaxKind.InterfaceDeclaration: - const heritageClauses = (node.parent as ts.InterfaceDeclaration).heritageClauses; + const heritageClauses = /** @type {ts.InterfaceDeclaration} */ (node.parent).heritageClauses; if (heritageClauses) { if (heritageClauses[0].token !== ts.SyntaxKind.ExtendsKeyword) { throw new Error(`Unexpected kind of heritage clause: ${ts.SyntaxKind[heritageClauses[0].kind]}`); @@ -104,9 +148,13 @@ class DeclarationsWalker { ts.forEachChild(node, n => this.visitTypeNodes(n)); } - private processTypeOfNode(node: ts.Node): void { + /** + * @param {ts.Node} node + * @private + */ + processTypeOfNode(node) { if (node.kind === ts.SyntaxKind.UnionType) { - for (const t of (node as ts.UnionTypeNode).types) { + for (const t of /** @type {ts.UnionTypeNode} */ (node).types) { this.processTypeOfNode(t); } } @@ -119,8 +167,14 @@ class DeclarationsWalker { } } -function writeProtocolFile(outputFile: string, protocolTs: string, typeScriptServicesDts: string) { - const options = { target: ts.ScriptTarget.ES5, declaration: true, noResolve: false, types: [] as string[], stripInternal: true }; +/** + * @param {string} outputFile + * @param {string} protocolTs + * @param {string} typeScriptServicesDts + */ +function writeProtocolFile(outputFile, protocolTs, typeScriptServicesDts) { + /** @type {ts.CompilerOptions} */ + const options = { target: ts.ScriptTarget.ES5, declaration: true, noResolve: false, types: [], stripInternal: true }; /** * 1st pass - generate a program from protocol.ts and typescriptservices.d.ts and emit core version of protocol.d.ts with all internal members stripped @@ -129,7 +183,8 @@ function writeProtocolFile(outputFile: string, protocolTs: string, typeScriptSer function getInitialDtsFileForProtocol() { const program = ts.createProgram([protocolTs, typeScriptServicesDts, path.join(typeScriptServicesDts, "../lib.es5.d.ts")], options); - let protocolDts: string | undefined; + /** @type {string | undefined} */ + let protocolDts; const emitResult = program.emit(program.getSourceFile(protocolTs), (file, content) => { if (endsWith(file, ".d.ts")) { protocolDts = content; @@ -137,7 +192,8 @@ function writeProtocolFile(outputFile: string, protocolTs: string, typeScriptSer }); if (protocolDts === undefined) { - const diagHost: ts.FormatDiagnosticsHost = { + /** @type {ts.FormatDiagnosticsHost} */ + const diagHost = { getCanonicalFileName(f) { return f; }, getCurrentDirectory() { return "."; }, getNewLine() { return "\r\n"; } @@ -151,12 +207,15 @@ function writeProtocolFile(outputFile: string, protocolTs: string, typeScriptSer const protocolFileName = "protocol.d.ts"; /** * Second pass - generate a program from protocol.d.ts and typescriptservices.d.ts, then augment core protocol.d.ts with extra types from typescriptservices.d.ts + * @param {string} protocolDts + * @param {boolean} includeTypeScriptServices */ - function getProgramWithProtocolText(protocolDts: string, includeTypeScriptServices: boolean) { + function getProgramWithProtocolText(protocolDts, includeTypeScriptServices) { const host = ts.createCompilerHost(options); const originalGetSourceFile = host.getSourceFile; host.getSourceFile = (fileName) => { if (fileName === protocolFileName) { + assert(options.target !== undefined); return ts.createSourceFile(fileName, protocolDts, options.target); } return originalGetSourceFile.apply(host, [fileName, ts.ScriptTarget.Latest]); @@ -168,7 +227,8 @@ function writeProtocolFile(outputFile: string, protocolTs: string, typeScriptSer let protocolDts = getInitialDtsFileForProtocol(); const program = getProgramWithProtocolText(protocolDts, /*includeTypeScriptServices*/ true); - const protocolFile = program.getSourceFile("protocol.d.ts")!; + const protocolFile = program.getSourceFile("protocol.d.ts"); + assert(protocolFile); const extraDeclarations = DeclarationsWalker.getExtraDeclarations(program.getTypeChecker(), protocolFile); if (extraDeclarations) { protocolDts += extraDeclarations; diff --git a/scripts/configurePrerelease.ts b/scripts/configurePrerelease.mjs similarity index 77% rename from scripts/configurePrerelease.ts rename to scripts/configurePrerelease.mjs index 17d8bb3a920..9c4b83ea8f5 100644 --- a/scripts/configurePrerelease.ts +++ b/scripts/configurePrerelease.mjs @@ -1,18 +1,20 @@ -/// import { normalize, relative } from "path"; -import * as assert from "assert"; +import assert from "assert"; import { readFileSync, writeFileSync } from "fs"; +import url from "url"; + +const __filename = url.fileURLToPath(new URL(import.meta.url)); /** * A minimal description for a parsed package.json object. - */ -interface PackageJson { + * @typedef {{ name: string; version: string; keywords: string[]; -} +}} PackageJson + */ -function main(): void { +function main() { const args = process.argv.slice(2); if (args.length < 3) { const thisProgramName = relative(process.cwd(), __filename); @@ -28,7 +30,8 @@ function main(): void { // Acquire the version from the package.json file and modify it appropriately. const packageJsonFilePath = normalize(args[1]); - const packageJsonValue: PackageJson = JSON.parse(readFileSync(packageJsonFilePath).toString()); + /** @type {PackageJson} */ + const packageJsonValue = JSON.parse(readFileSync(packageJsonFilePath).toString()); const { majorMinor, patch } = parsePackageJsonVersion(packageJsonValue.version); const prereleasePatch = getPrereleasePatch(tag, patch); @@ -53,17 +56,25 @@ function main(): void { } /* eslint-disable no-null/no-null */ -function updateTsFile(tsFilePath: string, tsFileContents: string, majorMinor: string, patch: string, nightlyPatch: string): string { +/** + * @param {string} tsFilePath + * @param {string} tsFileContents + * @param {string} majorMinor + * @param {string} patch + * @param {string} nightlyPatch + * @returns {string} + */ +function updateTsFile(tsFilePath, tsFileContents, majorMinor, patch, nightlyPatch) { const majorMinorRgx = /export const versionMajorMinor = "(\d+\.\d+)"/; const majorMinorMatch = majorMinorRgx.exec(tsFileContents); assert(majorMinorMatch !== null, `The file '${tsFilePath}' seems to no longer have a string matching '${majorMinorRgx}'.`); - const parsedMajorMinor = majorMinorMatch![1]; + const parsedMajorMinor = majorMinorMatch[1]; assert(parsedMajorMinor === majorMinor, `versionMajorMinor does not match. ${tsFilePath}: '${parsedMajorMinor}'; package.json: '${majorMinor}'`); const versionRgx = /export const version(?:: string)? = `\$\{versionMajorMinor\}\.(\d)(-\w+)?`;/; const patchMatch = versionRgx.exec(tsFileContents); assert(patchMatch !== null, `The file '${tsFilePath}' seems to no longer have a string matching '${versionRgx.toString()}'.`); - const parsedPatch = patchMatch![1]; + const parsedPatch = patchMatch[1]; if (parsedPatch !== patch) { throw new Error(`patch does not match. ${tsFilePath}: '${parsedPatch}; package.json: '${patch}'`); } @@ -71,16 +82,25 @@ function updateTsFile(tsFilePath: string, tsFileContents: string, majorMinor: st return tsFileContents.replace(versionRgx, `export const version: string = \`\${versionMajorMinor}.${nightlyPatch}\`;`); } -function parsePackageJsonVersion(versionString: string): { majorMinor: string, patch: string } { +/** + * @param {string} versionString + * @returns {{ majorMinor: string, patch: string }} + */ +function parsePackageJsonVersion(versionString) { const versionRgx = /(\d+\.\d+)\.(\d+)($|\-)/; const match = versionString.match(versionRgx); assert(match !== null, "package.json 'version' should match " + versionRgx.toString()); - return { majorMinor: match![1], patch: match![2] }; + return { majorMinor: match[1], patch: match[2] }; } /* eslint-enable no-null/no-null */ -/** e.g. 0-dev.20170707 */ -function getPrereleasePatch(tag: string, plainPatch: string): string { +/** + * e.g. 0-dev.20170707 + * @param {string} tag + * @param {string} plainPatch + * @returns {string} + */ +function getPrereleasePatch(tag, plainPatch) { // We're going to append a representation of the current time at the end of the current version. // String.prototype.toISOString() returns a 24-character string formatted as 'YYYY-MM-DDTHH:mm:ss.sssZ', // but we'd prefer to just remove separators and limit ourselves to YYYYMMDD. diff --git a/scripts/createPlaygroundBuild.js b/scripts/createPlaygroundBuild.mjs similarity index 95% rename from scripts/createPlaygroundBuild.js rename to scripts/createPlaygroundBuild.mjs index 31fe5af92dd..0d5a9006ae5 100644 --- a/scripts/createPlaygroundBuild.js +++ b/scripts/createPlaygroundBuild.mjs @@ -1,8 +1,7 @@ /* eslint-disable */ -// @ts-check /** Run via: - node scripts/createPlaygroundBuild.js + node scripts/createPlaygroundBuild.mjs */ // This script does two things: @@ -20,12 +19,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -const path = require('path'); -const fs = require('fs'); -const child_process = require('child_process'); -const http = require('http'); -const url = require('url'); -const nodeFetch = require("node-fetch").default +import path from 'path'; +import fs from 'fs'; +import child_process from 'child_process'; +import http from 'http'; +import url from 'url'; +import nodeFetch from "node-fetch"; +import assert from 'assert'; function updateTSDist() { // This code is a direct port of a script from monaco-typescript @@ -169,6 +169,9 @@ function updateTSDist() { })(); function importLibs() { + /** + * @param {string} name + */ function readLibFile(name) { const srcPath = path.join(TYPESCRIPT_LIB_SOURCE, name); return fs.readFileSync(srcPath).toString(); @@ -193,6 +196,7 @@ function updateTSDist() { const dtsFiles = fs.readdirSync(TYPESCRIPT_LIB_SOURCE).filter((f) => f.includes('lib.')); while (dtsFiles.length > 0) { const name = dtsFiles.shift(); + assert(name !== undefined); const output = readLibFile(name).replace(/\r\n/g, '\n'); strLibResult += `libFileMap['${name}'] = "${escapeText(output)}";\n`; strIndexResult += `libFileSet['${name}'] = true;\n`; @@ -204,6 +208,7 @@ function updateTSDist() { /** * Escape text such that it can be used in a javascript string enclosed by double quotes (") + * @param {string} text */ function escapeText(text) { // See http://www.javascriptkit.com/jsref/escapesequence.shtml @@ -265,6 +270,9 @@ function updateTSDist() { return resultPieces.join(''); } + /** + * @param {string} str + */ function stripSourceMaps(str) { return str.replace(/\/\/# sourceMappingURL[^\n]+/gm, ''); } @@ -283,14 +291,15 @@ fs.watchFile(services, () =>{ // closest version to your dev build let latestStable = "4.3.2" nodeFetch('https://typescript.azureedge.net/indexes/releases.json').then(req => req.json()).then(releases => { - latestStable = releases.versions.pop() + latestStable = /** @type {any} */ (releases).versions.pop() }); http.createServer(function (req, res) { res.setHeader("Access-Control-Allow-Origin", "*") + assert(req.url); const incoming = url.parse(req.url) - if (incoming.path.endsWith("typescriptServices.js")) { + if (incoming.path && incoming.path.endsWith("typescriptServices.js")) { // Use the built version res.writeHead(200, {"Content-Type": "text/javascript"}); const amdPath = path.join(__dirname, '../internal/lib/typescriptServices-amd.js'); diff --git a/scripts/errorCheck.ts b/scripts/errorCheck.mjs similarity index 87% rename from scripts/errorCheck.ts rename to scripts/errorCheck.mjs index 9e285516d73..c2e031e5487 100644 --- a/scripts/errorCheck.ts +++ b/scripts/errorCheck.mjs @@ -1,7 +1,7 @@ -import * as fs from "fs"; -import * as fsPromises from "fs/promises"; -import * as _glob from "glob"; -import * as util from "util"; +import fs from "fs"; +import fsPromises from "fs/promises"; +import _glob from "glob"; +import util from "util"; const glob = util.promisify(_glob); @@ -24,7 +24,7 @@ async function checkErrorBaselines() { fs.readFile(baseDir + f, "utf-8", (err, baseline) => { if (err) throw err; - let g: RegExpExecArray | null; + let g; while (g = errRegex.exec(baseline)) { const errCode = +g[1]; const msg = keys.filter(k => messages[k].code === errCode)[0]; @@ -48,8 +48,8 @@ async function checkSourceFiles() { const data = await fsPromises.readFile("src/compiler/diagnosticInformationMap.generated.ts", "utf-8"); const errorRegexp = /\s(\w+): \{ code/g; - const errorNames: string[] = []; - let errMatch: RegExpExecArray | null; + const errorNames = []; + let errMatch; while (errMatch = errorRegexp.exec(data)) { errorNames.push(errMatch[1]); } diff --git a/scripts/eslint/rules/boolean-trivia.js b/scripts/eslint/rules/boolean-trivia.cjs similarity index 95% rename from scripts/eslint/rules/boolean-trivia.js rename to scripts/eslint/rules/boolean-trivia.cjs index 2aa38ab04c2..2b823f39299 100644 --- a/scripts/eslint/rules/boolean-trivia.js +++ b/scripts/eslint/rules/boolean-trivia.cjs @@ -1,5 +1,5 @@ const { AST_NODE_TYPES, TSESTree } = require("@typescript-eslint/utils"); -const { createRule } = require("./utils"); +const { createRule } = require("./utils.cjs"); module.exports = createRule({ name: "boolean-trivia", diff --git a/scripts/eslint/rules/debug-assert.js b/scripts/eslint/rules/debug-assert.cjs similarity index 95% rename from scripts/eslint/rules/debug-assert.js rename to scripts/eslint/rules/debug-assert.cjs index 7f01a43c9c3..da4c70bf460 100644 --- a/scripts/eslint/rules/debug-assert.js +++ b/scripts/eslint/rules/debug-assert.cjs @@ -1,5 +1,5 @@ const { AST_NODE_TYPES, TSESTree } = require("@typescript-eslint/utils"); -const { createRule } = require("./utils"); +const { createRule } = require("./utils.cjs"); module.exports = createRule({ name: "debug-assert", diff --git a/scripts/eslint/rules/no-double-space.js b/scripts/eslint/rules/no-double-space.cjs similarity index 95% rename from scripts/eslint/rules/no-double-space.js rename to scripts/eslint/rules/no-double-space.cjs index 1845486edc7..1d716d084b0 100644 --- a/scripts/eslint/rules/no-double-space.js +++ b/scripts/eslint/rules/no-double-space.cjs @@ -1,5 +1,5 @@ const { TSESTree, AST_NODE_TYPES } = require("@typescript-eslint/utils"); -const { createRule } = require("./utils"); +const { createRule } = require("./utils.cjs"); module.exports = createRule({ name: "no-double-space", diff --git a/scripts/eslint/rules/no-in-operator.js b/scripts/eslint/rules/no-in-operator.cjs similarity index 91% rename from scripts/eslint/rules/no-in-operator.js rename to scripts/eslint/rules/no-in-operator.cjs index c93000e3075..d17e88be951 100644 --- a/scripts/eslint/rules/no-in-operator.js +++ b/scripts/eslint/rules/no-in-operator.cjs @@ -1,5 +1,5 @@ const { TSESTree } = require("@typescript-eslint/utils"); -const { createRule } = require("./utils"); +const { createRule } = require("./utils.cjs"); module.exports = createRule({ name: "no-in-operator", diff --git a/scripts/eslint/rules/no-keywords.js b/scripts/eslint/rules/no-keywords.cjs similarity index 95% rename from scripts/eslint/rules/no-keywords.js rename to scripts/eslint/rules/no-keywords.cjs index 00b18b598e5..673a857912c 100644 --- a/scripts/eslint/rules/no-keywords.js +++ b/scripts/eslint/rules/no-keywords.cjs @@ -1,5 +1,5 @@ const { TSESTree, AST_NODE_TYPES } = require("@typescript-eslint/utils"); -const { createRule } = require("./utils"); +const { createRule } = require("./utils.cjs"); module.exports = createRule({ name: "no-keywords", diff --git a/scripts/eslint/rules/no-type-assertion-whitespace.js b/scripts/eslint/rules/no-type-assertion-whitespace.cjs similarity index 93% rename from scripts/eslint/rules/no-type-assertion-whitespace.js rename to scripts/eslint/rules/no-type-assertion-whitespace.cjs index f107d234f6e..174de300562 100644 --- a/scripts/eslint/rules/no-type-assertion-whitespace.js +++ b/scripts/eslint/rules/no-type-assertion-whitespace.cjs @@ -1,5 +1,5 @@ const { TSESTree } = require("@typescript-eslint/utils"); -const { createRule } = require("./utils"); +const { createRule } = require("./utils.cjs"); module.exports = createRule({ name: "no-type-assertion-whitespace", diff --git a/scripts/eslint/rules/object-literal-surrounding-space.js b/scripts/eslint/rules/object-literal-surrounding-space.cjs similarity index 95% rename from scripts/eslint/rules/object-literal-surrounding-space.js rename to scripts/eslint/rules/object-literal-surrounding-space.cjs index 2a04ae20f13..2321acbce1c 100644 --- a/scripts/eslint/rules/object-literal-surrounding-space.js +++ b/scripts/eslint/rules/object-literal-surrounding-space.cjs @@ -1,5 +1,5 @@ const { TSESTree } = require("@typescript-eslint/utils"); -const { createRule } = require("./utils"); +const { createRule } = require("./utils.cjs"); module.exports = createRule({ name: "object-literal-surrounding-space", diff --git a/scripts/eslint/rules/one-namespace-per-file.js b/scripts/eslint/rules/one-namespace-per-file.cjs similarity index 93% rename from scripts/eslint/rules/one-namespace-per-file.js rename to scripts/eslint/rules/one-namespace-per-file.cjs index a91f54eeafc..2b8772005a9 100644 --- a/scripts/eslint/rules/one-namespace-per-file.js +++ b/scripts/eslint/rules/one-namespace-per-file.cjs @@ -1,5 +1,5 @@ const { AST_NODE_TYPES, TSESTree } = require("@typescript-eslint/utils"); -const { createRule } = require("./utils"); +const { createRule } = require("./utils.cjs"); module.exports = createRule({ name: "one-namespace-per-file", diff --git a/scripts/eslint/rules/only-arrow-functions.js b/scripts/eslint/rules/only-arrow-functions.cjs similarity index 95% rename from scripts/eslint/rules/only-arrow-functions.js rename to scripts/eslint/rules/only-arrow-functions.cjs index a97673b08d3..d2d553ab3b3 100644 --- a/scripts/eslint/rules/only-arrow-functions.js +++ b/scripts/eslint/rules/only-arrow-functions.cjs @@ -1,5 +1,5 @@ const { AST_NODE_TYPES, TSESTree } = require("@typescript-eslint/utils"); -const { createRule } = require("./utils"); +const { createRule } = require("./utils.cjs"); module.exports = createRule({ name: "only-arrow-functions", diff --git a/scripts/eslint/rules/simple-indent.js b/scripts/eslint/rules/simple-indent.cjs similarity index 95% rename from scripts/eslint/rules/simple-indent.js rename to scripts/eslint/rules/simple-indent.cjs index fc41e21cd19..3701da068bb 100644 --- a/scripts/eslint/rules/simple-indent.js +++ b/scripts/eslint/rules/simple-indent.cjs @@ -1,5 +1,5 @@ const { TSESTree } = require("@typescript-eslint/utils"); -const { createRule } = require("./utils"); +const { createRule } = require("./utils.cjs"); module.exports = createRule({ name: "simple-indent", diff --git a/scripts/eslint/rules/type-operator-spacing.js b/scripts/eslint/rules/type-operator-spacing.cjs similarity index 93% rename from scripts/eslint/rules/type-operator-spacing.js rename to scripts/eslint/rules/type-operator-spacing.cjs index 1df853da6a3..16a493eaf09 100644 --- a/scripts/eslint/rules/type-operator-spacing.js +++ b/scripts/eslint/rules/type-operator-spacing.cjs @@ -1,5 +1,5 @@ const { TSESTree, AST_TOKEN_TYPES } = require("@typescript-eslint/utils"); -const { createRule } = require("./utils"); +const { createRule } = require("./utils.cjs"); module.exports = createRule({ name: "type-operator-spacing", diff --git a/scripts/eslint/rules/utils.js b/scripts/eslint/rules/utils.cjs similarity index 100% rename from scripts/eslint/rules/utils.js rename to scripts/eslint/rules/utils.cjs diff --git a/scripts/eslint/tests/boolean-trivia.test.ts b/scripts/eslint/tests/boolean-trivia.test.cjs similarity index 89% rename from scripts/eslint/tests/boolean-trivia.test.ts rename to scripts/eslint/tests/boolean-trivia.test.cjs index df8ba4e34cd..717ce610f03 100644 --- a/scripts/eslint/tests/boolean-trivia.test.ts +++ b/scripts/eslint/tests/boolean-trivia.test.cjs @@ -1,5 +1,5 @@ -import { RuleTester } from "./support/RuleTester"; -import rule = require("../rules/boolean-trivia"); +const { RuleTester } = require("./support/RuleTester.cjs"); +const rule = require("../rules/boolean-trivia.cjs"); const ruleTester = new RuleTester({ parserOptions: { diff --git a/scripts/eslint/tests/debug-assert.test.ts b/scripts/eslint/tests/debug-assert.test.cjs similarity index 88% rename from scripts/eslint/tests/debug-assert.test.ts rename to scripts/eslint/tests/debug-assert.test.cjs index d0400507dc2..ae5961e41fc 100644 --- a/scripts/eslint/tests/debug-assert.test.ts +++ b/scripts/eslint/tests/debug-assert.test.cjs @@ -1,5 +1,5 @@ -import { RuleTester } from "./support/RuleTester"; -import rule = require("../rules/debug-assert"); +const { RuleTester } = require("./support/RuleTester.cjs"); +const rule = require("../rules/debug-assert.cjs"); const ruleTester = new RuleTester({ parserOptions: { diff --git a/scripts/eslint/tests/no-double-space.test.ts b/scripts/eslint/tests/no-double-space.test.cjs similarity index 92% rename from scripts/eslint/tests/no-double-space.test.ts rename to scripts/eslint/tests/no-double-space.test.cjs index b28154675b0..b411fbe6ea8 100644 --- a/scripts/eslint/tests/no-double-space.test.ts +++ b/scripts/eslint/tests/no-double-space.test.cjs @@ -1,5 +1,5 @@ -import { RuleTester } from "./support/RuleTester"; -import rule = require("../rules/no-double-space"); +const { RuleTester } = require("./support/RuleTester.cjs"); +const rule = require("../rules/no-double-space.cjs"); const ruleTester = new RuleTester({ parser: require.resolve("@typescript-eslint/parser"), diff --git a/scripts/eslint/tests/no-in-operator.test.ts b/scripts/eslint/tests/no-in-operator.test.cjs similarity index 77% rename from scripts/eslint/tests/no-in-operator.test.ts rename to scripts/eslint/tests/no-in-operator.test.cjs index aef915670fe..0df73219bc8 100644 --- a/scripts/eslint/tests/no-in-operator.test.ts +++ b/scripts/eslint/tests/no-in-operator.test.cjs @@ -1,5 +1,5 @@ -import { RuleTester } from "./support/RuleTester"; -import rule = require("../rules/no-in-operator"); +const { RuleTester } = require("./support/RuleTester.cjs"); +const rule = require("../rules/no-in-operator.cjs"); const ruleTester = new RuleTester({ parserOptions: { diff --git a/scripts/eslint/tests/no-keywords.test.ts b/scripts/eslint/tests/no-keywords.test.cjs similarity index 92% rename from scripts/eslint/tests/no-keywords.test.ts rename to scripts/eslint/tests/no-keywords.test.cjs index 18c34872059..3618329b68a 100644 --- a/scripts/eslint/tests/no-keywords.test.ts +++ b/scripts/eslint/tests/no-keywords.test.cjs @@ -1,5 +1,5 @@ -import { RuleTester } from "./support/RuleTester"; -import rule = require("../rules/no-keywords"); +const { RuleTester } = require("./support/RuleTester.cjs"); +const rule = require("../rules/no-keywords.cjs"); const ruleTester = new RuleTester({ parserOptions: { diff --git a/scripts/eslint/tests/no-type-assertion-whitespace.test.ts b/scripts/eslint/tests/no-type-assertion-whitespace.test.cjs similarity index 86% rename from scripts/eslint/tests/no-type-assertion-whitespace.test.ts rename to scripts/eslint/tests/no-type-assertion-whitespace.test.cjs index 4cc68fd3a06..aa820094a0e 100644 --- a/scripts/eslint/tests/no-type-assertion-whitespace.test.ts +++ b/scripts/eslint/tests/no-type-assertion-whitespace.test.cjs @@ -1,5 +1,5 @@ -import { RuleTester } from "./support/RuleTester"; -import rule = require("../rules/no-type-assertion-whitespace"); +const { RuleTester } = require("./support/RuleTester.cjs"); +const rule = require("../rules/no-type-assertion-whitespace.cjs"); const ruleTester = new RuleTester({ parserOptions: { diff --git a/scripts/eslint/tests/object-literal-surrounding-space.test.ts b/scripts/eslint/tests/object-literal-surrounding-space.test.cjs similarity index 86% rename from scripts/eslint/tests/object-literal-surrounding-space.test.ts rename to scripts/eslint/tests/object-literal-surrounding-space.test.cjs index 2e94a2b61f3..7c697af531f 100644 --- a/scripts/eslint/tests/object-literal-surrounding-space.test.ts +++ b/scripts/eslint/tests/object-literal-surrounding-space.test.cjs @@ -1,5 +1,5 @@ -import { RuleTester } from "./support/RuleTester"; -import rule = require("../rules/object-literal-surrounding-space"); +const { RuleTester } = require("./support/RuleTester.cjs"); +const rule = require("../rules/object-literal-surrounding-space.cjs"); const ruleTester = new RuleTester({ parserOptions: { diff --git a/scripts/eslint/tests/only-arrow-functions.test.ts b/scripts/eslint/tests/only-arrow-functions.test.cjs similarity index 92% rename from scripts/eslint/tests/only-arrow-functions.test.ts rename to scripts/eslint/tests/only-arrow-functions.test.cjs index 0909e5b5868..52945d44f39 100644 --- a/scripts/eslint/tests/only-arrow-functions.test.ts +++ b/scripts/eslint/tests/only-arrow-functions.test.cjs @@ -1,5 +1,5 @@ -import { RuleTester } from "./support/RuleTester"; -import rule = require("../rules/only-arrow-functions"); +const { RuleTester } = require("./support/RuleTester.cjs"); +const rule = require("../rules/only-arrow-functions.cjs"); const ruleTester = new RuleTester({ parserOptions: { diff --git a/scripts/eslint/tests/simple-indent.test.ts b/scripts/eslint/tests/simple-indent.test.cjs similarity index 93% rename from scripts/eslint/tests/simple-indent.test.ts rename to scripts/eslint/tests/simple-indent.test.cjs index 161755c366b..0e4d925c560 100644 --- a/scripts/eslint/tests/simple-indent.test.ts +++ b/scripts/eslint/tests/simple-indent.test.cjs @@ -1,5 +1,5 @@ -import { RuleTester } from "./support/RuleTester"; -import rule = require("../rules/simple-indent"); +const { RuleTester } = require("./support/RuleTester.cjs"); +const rule = require("../rules/simple-indent.cjs"); const ruleTester = new RuleTester({ parserOptions: { diff --git a/scripts/eslint/tests/support/RuleTester.cjs b/scripts/eslint/tests/support/RuleTester.cjs new file mode 100644 index 00000000000..44e093d48e5 --- /dev/null +++ b/scripts/eslint/tests/support/RuleTester.cjs @@ -0,0 +1,6 @@ +const path = require("path"); +const { TSESLint } = require("@typescript-eslint/utils"); + +module.exports.ROOT_DIR = path.join(process.cwd(), "scripts", "eslint", "tests", "fixtures"); +module.exports.FILENAME = path.join(module.exports.ROOT_DIR, "file.ts"); +module.exports.RuleTester = TSESLint.RuleTester; diff --git a/scripts/eslint/tests/support/RuleTester.ts b/scripts/eslint/tests/support/RuleTester.ts deleted file mode 100644 index dcb434bb860..00000000000 --- a/scripts/eslint/tests/support/RuleTester.ts +++ /dev/null @@ -1,6 +0,0 @@ -import * as path from "path"; -import { TSESLint } from "@typescript-eslint/utils"; - -export const ROOT_DIR = path.join(process.cwd(), "scripts", "eslint", "tests", "fixtures"); -export const FILENAME = path.join(ROOT_DIR, "file.ts"); -export const RuleTester = TSESLint.RuleTester; diff --git a/scripts/eslint/tests/type-operator-spacing.test.ts b/scripts/eslint/tests/type-operator-spacing.test.cjs similarity index 86% rename from scripts/eslint/tests/type-operator-spacing.test.ts rename to scripts/eslint/tests/type-operator-spacing.test.cjs index 7654dfc9c53..3341348828e 100644 --- a/scripts/eslint/tests/type-operator-spacing.test.ts +++ b/scripts/eslint/tests/type-operator-spacing.test.cjs @@ -1,5 +1,5 @@ -import { RuleTester } from "./support/RuleTester"; -import rule = require("../rules/type-operator-spacing"); +const { RuleTester } = require("./support/RuleTester.cjs"); +const rule = require("../rules/type-operator-spacing.cjs"); const ruleTester = new RuleTester({ parserOptions: { diff --git a/scripts/eslint/tsconfig.json b/scripts/eslint/tsconfig.json deleted file mode 100644 index a9994669e63..00000000000 --- a/scripts/eslint/tsconfig.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "compilerOptions": { - "lib": [ - "es6" - ], - "module": "commonjs", - "target": "es6", - "outDir": "./built", - "declaration": false, - - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "moduleResolution": "node", - "allowUnusedLabels": false, - "noImplicitOverride": true, - "noImplicitReturns": true, - "noPropertyAccessFromIndexSignature": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "allowJs": true, - "checkJs": true - }, - - "include": [ - "rules", - "tests" - ] -} diff --git a/scripts/failed-tests.js b/scripts/failed-tests.cjs similarity index 86% rename from scripts/failed-tests.js rename to scripts/failed-tests.cjs index 41536e63375..78e90fc9769 100644 --- a/scripts/failed-tests.js +++ b/scripts/failed-tests.cjs @@ -1,15 +1,19 @@ -// @ts-check +const assert = require("assert"); const Mocha = require("mocha"); const path = require("path"); const fs = require("fs"); const os = require("os"); -const failingHookRegExp = /^(.*) "(before|after) (all|each)" hook$/; +/** @typedef {{ + file?: string; + keepFailed?: boolean; + reporter?: Mocha.ReporterConstructor | keyof Mocha.reporters; + reporterOptions?: any; // TODO(jakebailey): what? +}} ReporterOptions */ /** * .failed-tests reporter * - * @typedef {Object} ReporterOptions * @property {string} [file] * @property {boolean} [keepFailed] * @property {string|Mocha.ReporterConstructor} [reporter] @@ -24,7 +28,7 @@ class FailedTestsReporter extends Mocha.reporters.Base { super(runner, options); if (!runner) return; - const reporterOptions = this.reporterOptions = options.reporterOptions || {}; + const reporterOptions = this.reporterOptions = options?.reporterOptions || {}; if (reporterOptions.file === undefined) reporterOptions.file = ".failed-tests"; if (reporterOptions.keepFailed === undefined) reporterOptions.keepFailed = false; if (reporterOptions.reporter) { @@ -67,7 +71,7 @@ class FailedTestsReporter extends Mocha.reporters.Base { * @param {ReadonlyArray} passes * @param {ReadonlyArray} failures * @param {boolean} keepFailed - * @param {(err?: NodeJS.ErrnoException) => void} done + * @param {(err?: NodeJS.ErrnoException | null) => void} done */ static writeFailures(file, passes, failures, keepFailed, done) { const failingTests = new Set(fs.existsSync(file) ? readTests() : undefined); @@ -78,6 +82,7 @@ class FailedTestsReporter extends Mocha.reporters.Base { if (failingTests.size > 0 && !keepFailed) { for (const test of passes) { failingTests.delete(test.fullTitle().trim()); + assert(test.parent); possiblyPassingSuites.add(test.parent.fullTitle().trim()); } } @@ -86,6 +91,7 @@ class FailedTestsReporter extends Mocha.reporters.Base { // containing suite as failing. If the suite for a test or hook was // possibly passing then it is now definitely failing. for (const test of failures) { + assert(test.parent); const suiteTitle = test.parent.fullTitle().trim(); if (test.type === "test") { failingTests.add(test.fullTitle().trim()); @@ -126,8 +132,11 @@ class FailedTestsReporter extends Mocha.reporters.Base { /** * @param {number} failures * @param {(failures: number) => void} [fn] + * @override */ done(failures, fn) { + assert(this.reporterOptions); + assert(this.reporterOptions.file); FailedTestsReporter.writeFailures(this.reporterOptions.file, this.passes, this.failures, this.reporterOptions.keepFailed || this.stats.tests === 0, (err) => { const reporter = this.reporter; if (reporter && reporter.done) { diff --git a/scripts/failed-tests.d.cts b/scripts/failed-tests.d.cts new file mode 100644 index 00000000000..2978fb81758 --- /dev/null +++ b/scripts/failed-tests.d.cts @@ -0,0 +1,52 @@ +export = FailedTestsReporter; +/** @typedef {{ + file?: string; + keepFailed?: boolean; + reporter?: Mocha.ReporterConstructor | keyof Mocha.reporters; + reporterOptions?: any; // TODO(jakebailey): what? +}} ReporterOptions */ +/** + * .failed-tests reporter + * + * @property {string} [file] + * @property {boolean} [keepFailed] + * @property {string|Mocha.ReporterConstructor} [reporter] + * @property {*} [reporterOptions] + */ +declare class FailedTestsReporter extends Mocha.reporters.Base { + /** + * @param {string} file + * @param {ReadonlyArray} passes + * @param {ReadonlyArray} failures + * @param {boolean} keepFailed + * @param {(err?: NodeJS.ErrnoException | null) => void} done + */ + static writeFailures(file: string, passes: ReadonlyArray, failures: ReadonlyArray, keepFailed: boolean, done: (err?: NodeJS.ErrnoException | null) => void): void; + /** + * @param {Mocha.Runner} runner + * @param {{ reporterOptions?: ReporterOptions }} [options] + */ + constructor(runner: Mocha.Runner, options?: { + reporterOptions?: ReporterOptions | undefined; + } | undefined); + reporterOptions: ReporterOptions | undefined; + reporter: Mocha.reporters.Base | undefined; + /** @type {Mocha.Test[]} */ + passes: Mocha.Test[]; + /** + * @param {number} failures + * @param {(failures: number) => void} [fn] + * @override + */ + override done(failures: number, fn?: ((failures: number) => void) | undefined): void; +} +declare namespace FailedTestsReporter { + export { ReporterOptions }; +} +import Mocha = require("mocha"); +type ReporterOptions = { + file?: string; + keepFailed?: boolean; + reporter?: Mocha.ReporterConstructor | keyof typeof Mocha.reporters; + reporterOptions?: any; +}; diff --git a/scripts/failed-tests.d.ts b/scripts/failed-tests.d.ts deleted file mode 100644 index 866ed8f6bfe..00000000000 --- a/scripts/failed-tests.d.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as Mocha from "mocha"; - -export = FailedTestsReporter; - -declare class FailedTestsReporter extends Mocha.reporters.Base { - passes: Mocha.Test[]; - failures: Mocha.Test[]; - reporterOptions: FailedTestsReporter.ReporterOptions; - reporter?: Mocha.reporters.Base; - constructor(runner: Mocha.Runner, options?: { reporterOptions?: FailedTestsReporter.ReporterOptions }); - static writeFailures(file: string, passes: readonly Mocha.Test[], failures: readonly Mocha.Test[], keepFailed: boolean, done: (err?: NodeJS.ErrnoException) => void): void; - done(failures: number, fn?: (failures: number) => void): void; -} - -declare namespace FailedTestsReporter { - interface ReporterOptions { - file?: string; - keepFailed?: boolean; - reporter?: string | Mocha.ReporterConstructor; - reporterOptions?: any; - } -} diff --git a/scripts/find-unused-diganostic-messages.mjs b/scripts/find-unused-diganostic-messages.mjs index ba529cf81bc..e9cc9bc48f2 100644 --- a/scripts/find-unused-diganostic-messages.mjs +++ b/scripts/find-unused-diganostic-messages.mjs @@ -1,14 +1,14 @@ -// @ts-check // This file requires a modern version of node 14+, and grep to be available. // node scripts/find-unused-diagnostic-messages.mjs import { readFileSync } from "fs"; -import {EOL} from "os"; +import { EOL } from "os"; import { execSync } from "child_process"; const diags = readFileSync("src/compiler/diagnosticInformationMap.generated.ts", "utf8"); const startOfDiags = diags.split("export const Diagnostics")[1]; +/** @type {string[]} */ const missingNames = []; startOfDiags.split(EOL).forEach(line => { if (!line.includes(":")) return; diff --git a/scripts/generateLocalizedDiagnosticMessages.ts b/scripts/generateLocalizedDiagnosticMessages.mjs similarity index 81% rename from scripts/generateLocalizedDiagnosticMessages.ts rename to scripts/generateLocalizedDiagnosticMessages.mjs index abfe6a3cff9..e07f6629a10 100644 --- a/scripts/generateLocalizedDiagnosticMessages.ts +++ b/scripts/generateLocalizedDiagnosticMessages.mjs @@ -1,8 +1,8 @@ -import * as fs from "fs"; -import * as path from "path"; -import * as xml2js from "xml2js"; +import fs from "fs"; +import path from "path"; +import xml2js from "xml2js"; -function main(): void { +function main() { const args = process.argv.slice(2); if (args.length !== 3) { console.log("Usage:"); @@ -25,7 +25,10 @@ function main(): void { return; - function visitDirectory(name: string) { + /** + * @param {string} name + */ + function visitDirectory(name) { const inputFilePath = path.join(inputPath, name, "diagnosticMessages", "diagnosticMessages.generated.json.lcl"); fs.readFile(inputFilePath, (err, data) => { @@ -63,8 +66,10 @@ function main(): void { * * Most of the languages we support are neutral locales, so we want to use the language name. * There are three exceptions, zh-CN, zh-TW and pt-BR. + * + * @param {string} localeName */ - function getPreferredLocaleName(localeName: string) { + function getPreferredLocaleName(localeName) { switch (localeName) { case "zh-CN": case "zh-TW": @@ -75,15 +80,22 @@ function main(): void { } } - function handleError(err: null | object) { + /** + * @param {null | object} err + */ + function handleError(err) { if (err) { console.error(err); process.exit(1); } } - function xmlObjectToString(o: any) { - const out: any = {}; + /** + * @param {any} o + */ + function xmlObjectToString(o) { + /** @type {any} */ + const out = {}; for (const item of o.LCX.Item[0].Item[0].Item) { let ItemId = item.$.ItemId; let val = item.Str[0].Tgt ? item.Str[0].Tgt[0].Val[0] : item.Str[0].Val[0]; @@ -104,7 +116,11 @@ function main(): void { } - function ensureDirectoryExists(directoryPath: string, action: () => void) { + /** + * @param {string} directoryPath + * @param {() => void} action + */ + function ensureDirectoryExists(directoryPath, action) { fs.exists(directoryPath, exists => { if (!exists) { const basePath = path.dirname(directoryPath); @@ -116,14 +132,21 @@ function main(): void { }); } - function writeFile(fileName: string, contents: string) { + /** + * @param {string} fileName + * @param {string} contents + */ + function writeFile(fileName, contents) { ensureDirectoryExists(path.dirname(fileName), () => { fs.writeFile(fileName, contents, handleError); }); } - function objectToList(o: Record) { - const list: { key: string, value: string }[] = []; + /** + * @param {Record} o + */ + function objectToList(o) { + const list = []; for (const key in o) { list.push({ key, value: o[key] }); } @@ -142,7 +165,11 @@ function main(): void { )); }); - function getItemXML(key: string, value: string) { + /** + * @param {string} key + * @param {string} value + */ + function getItemXML(key, value) { // escape entrt value value = value.replace(/]/, "]5D;"); @@ -155,7 +182,10 @@ function main(): void { `; } - function getLCGFileXML(items: string) { + /** + * @param {string} items + */ + function getLCGFileXML(items) { return ` diff --git a/scripts/importDefinitelyTypedTests/importDefinitelyTypedTests.ts b/scripts/importDefinitelyTypedTests.mjs similarity index 79% rename from scripts/importDefinitelyTypedTests/importDefinitelyTypedTests.ts rename to scripts/importDefinitelyTypedTests.mjs index bfe4886361b..1fbefc85cb6 100644 --- a/scripts/importDefinitelyTypedTests/importDefinitelyTypedTests.ts +++ b/scripts/importDefinitelyTypedTests.mjs @@ -1,17 +1,11 @@ import * as fs from "fs"; import * as path from "path"; +import * as os from "os"; import * as childProcess from "child_process"; +import url from "url"; - -interface Map { - [key: string]: T; -} - -declare let process: { - argv: string[]; - env: Map; - exit(exitCode?: number): void; -}; +const __filename = url.fileURLToPath(new URL(import.meta.url)); +const __dirname = path.dirname(__filename); main(); function main() { @@ -35,25 +29,41 @@ function main() { importDefinitelyTypedTests(tscPath, rwcTestPath, resolvedDefinitelyTypedRoot); } -function filePathEndsWith(path: string, endingString: string): boolean { +/** + * @param {string} path + * @param {string} endingString + * @returns {boolean} + */ +function filePathEndsWith(path, endingString) { const pathLen = path.length; const extLen = endingString.length; return pathLen > extLen && path.substr(pathLen - extLen, extLen).toLocaleLowerCase() === endingString.toLocaleLowerCase(); } -function copyFileSync(source: string, destination: string) { +/** + * @param {string} source + * @param {string} destination + */ +function copyFileSync(source, destination) { const text = fs.readFileSync(source); fs.writeFileSync(destination, text); } -function importDefinitelyTypedTest(tscPath: string, rwcTestPath: string, testCaseName: string, testFiles: string[], responseFile: string | undefined) { +/** + * @param {string} tscPath + * @param {string} rwcTestPath + * @param {string} testCaseName + * @param {string[]} testFiles + * @param {string | undefined} responseFile + */ +function importDefinitelyTypedTest(tscPath, rwcTestPath, testCaseName, testFiles, responseFile) { let cmd = "node " + tscPath + " --module commonjs " + testFiles.join(" "); if (responseFile) { cmd += " @" + responseFile; } const testDirectoryName = testCaseName + "_" + Math.floor((Math.random() * 10000) + 1); - const testDirectoryPath = path.join(process.env.temp, testDirectoryName); + const testDirectoryPath = path.join(os.tmpdir(), testDirectoryName); if (fs.existsSync(testDirectoryPath)) { throw new Error("Could not create test directory"); } @@ -94,13 +104,18 @@ function importDefinitelyTypedTest(tscPath: string, rwcTestPath: string, testCas } //console.log("\r\n"); - }).on("error", (error: any) => { + }).on("error", (error) => { console.log("==> error " + JSON.stringify(error)); console.log("\r\n"); }); } -function importDefinitelyTypedTests(tscPath: string, rwcTestPath: string, definitelyTypedRoot: string): void { +/** + * @param {string} tscPath + * @param {string} rwcTestPath + * @param {string} definitelyTypedRoot + */ +function importDefinitelyTypedTests(tscPath, rwcTestPath, definitelyTypedRoot) { fs.readdir(definitelyTypedRoot, (err, subDirectories) => { if (err) { throw err; @@ -120,9 +135,12 @@ function importDefinitelyTypedTests(tscPath: string, rwcTestPath: string, defini throw err; } - const tsFiles: string[] = []; - const testFiles: string[] = []; - let paramFile: string | undefined; + /** @type {string[]} */ + const tsFiles = []; + /** @type {string[]} */ + const testFiles = []; + /** @type {string | undefined} */ + let paramFile; for (const filePath of files.map(f => path.join(directoryPath, f))) { if (filePathEndsWith(filePath, ".ts")) { diff --git a/scripts/importDefinitelyTypedTests/tsconfig.json b/scripts/importDefinitelyTypedTests/tsconfig.json deleted file mode 100644 index 30d589c1199..00000000000 --- a/scripts/importDefinitelyTypedTests/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "target": "ES5", - "outDir": "./", - "rootDir": ".", - "newLine": "lf", - "noImplicitAny": true, - "strictNullChecks": true, - "sourceMap": false - }, - "files": [ - "../typings/node/node.d.ts", - "importDefinitelyTypedTests.ts" - ], - "exclude": [ - ] -} \ No newline at end of file diff --git a/scripts/link-hooks.js b/scripts/link-hooks.js deleted file mode 100644 index 33b5d4fd94f..00000000000 --- a/scripts/link-hooks.js +++ /dev/null @@ -1,20 +0,0 @@ -var fs = require("fs"); -var path = require("path"); - -var hooks = [ - "post-checkout" -]; - -hooks.forEach(function (hook) { - var hookInSourceControl = path.resolve(__dirname, "hooks", hook); - - if (fs.existsSync(hookInSourceControl)) { - var hookInHiddenDirectory = path.resolve(__dirname, "..", ".git", "hooks", hook); - - if (fs.existsSync(hookInHiddenDirectory)) { - fs.unlinkSync(hookInHiddenDirectory); - } - - fs.linkSync(hookInSourceControl, hookInHiddenDirectory); - } -}); \ No newline at end of file diff --git a/scripts/link-hooks.mjs b/scripts/link-hooks.mjs new file mode 100644 index 00000000000..b60a308918d --- /dev/null +++ b/scripts/link-hooks.mjs @@ -0,0 +1,25 @@ +import fs from "fs"; +import path from "path"; +import url from "url"; +import { findUpRoot } from "./build/findUpDir.mjs"; + +const __filename = url.fileURLToPath(new URL(import.meta.url)); +const __dirname = path.dirname(__filename); + +const hooks = [ + "post-checkout" +]; + +hooks.forEach((hook) => { + const hookInSourceControl = path.resolve(__dirname, "hooks", hook); + + if (fs.existsSync(hookInSourceControl)) { + const hookInHiddenDirectory = path.resolve(findUpRoot(), ".git", "hooks", hook); + + if (fs.existsSync(hookInHiddenDirectory)) { + fs.unlinkSync(hookInHiddenDirectory); + } + + fs.linkSync(hookInSourceControl, hookInHiddenDirectory); + } +}); diff --git a/scripts/open-cherry-pick-pr.ts b/scripts/open-cherry-pick-pr.mjs similarity index 92% rename from scripts/open-cherry-pick-pr.ts rename to scripts/open-cherry-pick-pr.mjs index 8bce97f4e06..5402663b31b 100644 --- a/scripts/open-cherry-pick-pr.ts +++ b/scripts/open-cherry-pick-pr.mjs @@ -1,11 +1,11 @@ -/// -// Must reference esnext.asynciterable lib, since octokit uses AsyncIterable internally -/// - import { Octokit } from "@octokit/rest"; -import { runSequence } from "./run-sequence"; -import * as fs from "fs"; -import * as path from "path"; +import { runSequence } from "./run-sequence.mjs"; +import fs from "fs"; +import path from "path"; +import url from "url"; + +const __filename = url.fileURLToPath(new URL(import.meta.url)); +const __dirname = path.dirname(__filename); const userName = process.env.GH_USERNAME; const reviewers = process.env.REQUESTING_USER ? [process.env.REQUESTING_USER] : ["weswigham", "RyanCavanaugh"]; diff --git a/scripts/open-user-pr.ts b/scripts/open-user-pr.mjs similarity index 86% rename from scripts/open-user-pr.ts rename to scripts/open-user-pr.mjs index 314cd737771..5594fe763ef 100644 --- a/scripts/open-user-pr.ts +++ b/scripts/open-user-pr.mjs @@ -1,12 +1,8 @@ -/// -/// -// Must reference esnext.asynciterable lib, since octokit uses AsyncIterable internally import { Octokit } from "@octokit/rest"; -import { runSequence } from "./run-sequence"; +import { runSequence } from "./run-sequence.mjs"; const userName = process.env.GH_USERNAME || "typescript-bot"; const reviewers = process.env.REQUESTING_USER ? [process.env.REQUESTING_USER] : ["weswigham", "sandersn", "RyanCavanaugh"]; -const now = new Date(); const masterBranchname = `user-baseline-updates`; const targetBranch = process.env.TARGET_BRANCH || "main"; const branchName = process.env.TARGET_FORK?.toLowerCase() === "microsoft" && (targetBranch === "main" || targetBranch === "refs/heads/main") @@ -22,7 +18,7 @@ runSequence([ ["node", ["./node_modules/gulp/bin/gulp.js", "baseline-accept"]], // accept baselines ["git", ["checkout", "-b", branchName]], // create a branch ["git", ["add", "."]], // Add all changes - ["git", ["commit", "-m", `"Update user baselines${+process.env.SOURCE_ISSUE! === 33716 ? " +cc @sandersn" : ""}"`]], // Commit all changes (ping nathan if we would post to CI thread) + ["git", ["commit", "-m", `"Update user baselines${+(process.env.SOURCE_ISSUE ?? 0) === 33716 ? " +cc @sandersn" : ""}"`]], // Commit all changes (ping nathan if we would post to CI thread) ["git", ["push", "--set-upstream", "fork", branchName, "-f"]] // push the branch ]); diff --git a/scripts/perf-result-post.js b/scripts/perf-result-post.mjs similarity index 86% rename from scripts/perf-result-post.js rename to scripts/perf-result-post.mjs index 337729128d2..e205db701fd 100644 --- a/scripts/perf-result-post.js +++ b/scripts/perf-result-post.mjs @@ -1,10 +1,8 @@ -// @ts-check -/// -// Must reference esnext.asynciterable lib, since octokit uses AsyncIterable internally -const { Octokit } = require("@octokit/rest"); -const fs = require("fs"); -const ado = require("azure-devops-node-api"); -const { default: fetch } = require("node-fetch"); +import { Octokit } from "@octokit/rest"; +import fs from "fs"; +import ado from "azure-devops-node-api"; +import fetch from "node-fetch"; +import assert from "assert"; async function main() { @@ -15,7 +13,7 @@ async function main() { if (!requester) throw new Error("REQUESTING_USER environment variable not set."); const buildId = process.env.BUILD_BUILDID; - if (!requester) throw new Error("BUILD_BUILDID environment variable not set."); + if (!buildId) throw new Error("BUILD_BUILDID environment variable not set."); const postedComment = process.env.STATUS_COMMENT; if (!postedComment) throw new Error("STATUS_COMMENT environment variable not set."); @@ -36,10 +34,11 @@ async function main() { const cli = new ado.WebApi("https://typescript.visualstudio.com/defaultcollection", ado.getHandlerFromToken("")); // Empty token, anon auth const build = await cli.getBuildApi(); const artifact = await build.getArtifact("typescript", +buildId, "benchmark"); + assert(artifact.resource?.url); const updatedUrl = new URL(artifact.resource.url); updatedUrl.search = `artifactName=benchmark&fileId=${artifact.resource.data}&fileName=manifest`; const resp = await (await fetch(`${updatedUrl}`)).json(); - for (const file of resp.items) { + for (const file of /** @type {any} */ (resp).items) { if (/[\\/]linux\.benchmark$/.test(file.path)) { const benchmarkUrl = new URL(artifact.resource.url); benchmarkUrl.search = `artifactName=benchmark&fileId=${file.blob.id}&fileName=linux.benchmark`; diff --git a/scripts/post-vsts-artifact-comment.js b/scripts/post-vsts-artifact-comment.mjs similarity index 88% rename from scripts/post-vsts-artifact-comment.js rename to scripts/post-vsts-artifact-comment.mjs index f0ffa2c4105..8c345c58427 100644 --- a/scripts/post-vsts-artifact-comment.js +++ b/scripts/post-vsts-artifact-comment.mjs @@ -1,9 +1,7 @@ -// @ts-check -/// -// Must reference esnext.asynciterable lib, since octokit uses AsyncIterable internally -const { Octokit } = require("@octokit/rest"); -const ado = require("azure-devops-node-api"); -const { default: fetch } = require("node-fetch"); +import { Octokit } from "@octokit/rest"; +import assert from "assert"; +import ado from "azure-devops-node-api"; +import fetch from "node-fetch"; async function main() { if (!process.env.SOURCE_ISSUE) { @@ -16,10 +14,11 @@ async function main() { const cli = new ado.WebApi("https://typescript.visualstudio.com/defaultcollection", ado.getHandlerFromToken("")); // Empty token, anon auth const build = await cli.getBuildApi(); const artifact = await build.getArtifact("typescript", +process.env.BUILD_BUILDID, "tgz"); + assert(artifact.resource?.url); const updatedUrl = new URL(artifact.resource.url); updatedUrl.search = `artifactName=tgz&fileId=${artifact.resource.data}&fileName=manifest`; const resp = await (await fetch(`${updatedUrl}`)).json(); - const file = resp.items[0]; + const file = /** @type {any} */ (resp).items[0]; const tgzUrl = new URL(artifact.resource.url); tgzUrl.search = `artifactName=tgz&fileId=${file.blob.id}&fileName=${file.path}`; const link = "" + tgzUrl; diff --git a/scripts/processDiagnosticMessages.ts b/scripts/processDiagnosticMessages.mjs similarity index 73% rename from scripts/processDiagnosticMessages.ts rename to scripts/processDiagnosticMessages.mjs index 0ddc27b06f1..cf6acdae02e 100644 --- a/scripts/processDiagnosticMessages.ts +++ b/scripts/processDiagnosticMessages.mjs @@ -1,25 +1,29 @@ -import * as path from "path"; -import * as fs from "fs"; +import path from "path"; +import fs from "fs"; -interface DiagnosticDetails { +/** @typedef {{ category: string; code: number; reportsUnnecessary?: {}; reportsDeprecated?: {}; isEarly?: boolean; elidedInCompatabilityPyramid?: boolean; -} +}} DiagnosticDetails */ -type InputDiagnosticMessageTable = Map; +/** @typedef {Map} InputDiagnosticMessageTable */ -function main(): void { +function main() { if (process.argv.length < 3) { console.log("Usage:"); - console.log("\tnode processDiagnosticMessages.js "); + console.log("\tnode processDiagnosticMessages.mjs "); return; } - function writeFile(fileName: string, contents: string) { + /** + * @param {string} fileName + * @param {string} contents + */ + function writeFile(fileName, contents) { fs.writeFile(path.join(path.dirname(inputFilePath), fileName), contents, { encoding: "utf-8" }, err => { if (err) throw err; }); @@ -29,9 +33,11 @@ function main(): void { console.log(`Reading diagnostics from ${inputFilePath}`); const inputStr = fs.readFileSync(inputFilePath, { encoding: "utf-8" }); - const diagnosticMessagesJson: { [key: string]: DiagnosticDetails } = JSON.parse(inputStr); + /** @type {{ [key: string]: DiagnosticDetails }} */ + const diagnosticMessagesJson = JSON.parse(inputStr); - const diagnosticMessages: InputDiagnosticMessageTable = new Map(); + /** @type {InputDiagnosticMessageTable} */ + const diagnosticMessages = new Map(); for (const key in diagnosticMessagesJson) { if (Object.hasOwnProperty.call(diagnosticMessagesJson, key)) { diagnosticMessages.set(key, diagnosticMessagesJson[key]); @@ -49,8 +55,12 @@ function main(): void { writeFile("diagnosticMessages.generated.json", messageOutput); } -function checkForUniqueCodes(diagnosticTable: InputDiagnosticMessageTable) { - const allCodes: { [key: number]: true | undefined } = []; +/** + * @param {InputDiagnosticMessageTable} diagnosticTable + */ +function checkForUniqueCodes(diagnosticTable) { + /** @type {Record} */ + const allCodes = []; diagnosticTable.forEach(({ code }) => { if (allCodes[code]) { throw new Error(`Diagnostic code ${code} appears more than once.`); @@ -59,7 +69,13 @@ function checkForUniqueCodes(diagnosticTable: InputDiagnosticMessageTable) { }); } -function buildInfoFileOutput(messageTable: InputDiagnosticMessageTable, inputFilePathRel: string, thisFilePathRel: string): string { +/** + * @param {InputDiagnosticMessageTable} messageTable + * @param {string} inputFilePathRel + * @param {string} thisFilePathRel + * @returns {string} + */ +function buildInfoFileOutput(messageTable, inputFilePathRel, thisFilePathRel) { let result = "// \r\n" + "// generated from '" + inputFilePathRel + "' in '" + thisFilePathRel.replace(/\\/g, "/") + "'\r\n" + @@ -83,7 +99,11 @@ function buildInfoFileOutput(messageTable: InputDiagnosticMessageTable, inputFil return result; } -function buildDiagnosticMessageOutput(messageTable: InputDiagnosticMessageTable): string { +/** + * @param {InputDiagnosticMessageTable} messageTable + * @returns {string} + */ +function buildDiagnosticMessageOutput(messageTable) { let result = "{"; messageTable.forEach(({ code }, name) => { const propName = convertPropertyName(name); @@ -99,11 +119,21 @@ function buildDiagnosticMessageOutput(messageTable: InputDiagnosticMessageTable) return result; } -function createKey(name: string, code: number): string { +/** + * + * @param {string} name + * @param {number} code + * @returns {string} + */ +function createKey(name, code) { return name.slice(0, 100) + "_" + code; } -function convertPropertyName(origName: string): string { +/** + * @param {string} origName + * @returns {string} + */ +function convertPropertyName(origName) { let result = origName.split("").map(char => { if (char === "*") return "_Asterisk"; if (char === "/") return "_Slash"; diff --git a/scripts/produceLKG.ts b/scripts/produceLKG.mjs similarity index 80% rename from scripts/produceLKG.ts rename to scripts/produceLKG.mjs index e4e37df028e..6861f4f7d64 100644 --- a/scripts/produceLKG.ts +++ b/scripts/produceLKG.mjs @@ -1,9 +1,11 @@ -/// +import childProcess from "child_process"; +import fs from "fs-extra"; +import path from "path"; +import glob from "glob"; +import url from "url"; -import * as childProcess from "child_process"; -import * as fs from "fs-extra"; -import * as path from "path"; -import * as glob from "glob"; +const __filename = url.fileURLToPath(new URL(import.meta.url)); +const __dirname = path.dirname(__filename); const root = path.join(__dirname, ".."); const source = path.join(root, "built/local"); @@ -45,7 +47,7 @@ async function copyTypesMap() { } async function buildProtocol() { - const protocolScript = path.join(__dirname, "buildProtocol.js"); + const protocolScript = path.join(__dirname, "buildProtocol.mjs"); if (!fs.existsSync(protocolScript)) { throw new Error(`Expected protocol script ${protocolScript} to exist`); } @@ -80,16 +82,26 @@ async function writeGitAttributes() { await fs.writeFile(path.join(dest, ".gitattributes"), `* text eol=lf`, "utf-8"); } -async function copyWithCopyright(fileName: string, destName = fileName) { +/** + * @param {string} fileName + * @param {string} destName + */ +async function copyWithCopyright(fileName, destName = fileName) { const content = await fs.readFile(path.join(source, fileName), "utf-8"); await fs.writeFile(path.join(dest, destName), copyright + "\n" + content); } -async function copyFromBuiltLocal(fileName: string) { +/** + * @param {string} fileName + */ +async function copyFromBuiltLocal(fileName) { await fs.copy(path.join(source, fileName), path.join(dest, fileName)); } -async function copyFilesWithGlob(pattern: string) { +/** + * @param {string} pattern + */ +async function copyFilesWithGlob(pattern) { const files = glob.sync(pattern, { cwd: source }).map(f => path.basename(f)); for (const f of files) { await copyFromBuiltLocal(f); @@ -97,7 +109,11 @@ async function copyFilesWithGlob(pattern: string) { console.log(`Copied ${files.length} files matching pattern ${pattern}`); } -async function exec(path: string, args: string[] = []) { +/** + * @param {string} path + * @param {string[]} args + */ +async function exec(path, args = []) { const cmdLine = ["node", path, ...args].join(" "); console.log(cmdLine); childProcess.execSync(cmdLine); diff --git a/scripts/regenerate-unicode-identifier-parts.js b/scripts/regenerate-unicode-identifier-parts.mjs similarity index 93% rename from scripts/regenerate-unicode-identifier-parts.js rename to scripts/regenerate-unicode-identifier-parts.mjs index acc25914e4d..60730b77e25 100644 --- a/scripts/regenerate-unicode-identifier-parts.js +++ b/scripts/regenerate-unicode-identifier-parts.mjs @@ -1,6 +1,8 @@ const MAX_UNICODE_CODEPOINT = 0x10FFFF; +/** @type {(c: string) => boolean} */ const isStart = c => /[\p{ID_Start}\u{2118}\u{212E}\u{309B}\u{309C}]/u.test(c); // Other_ID_Start explicitly included for back compat - see http://www.unicode.org/reports/tr31/#Introduction +/** @type {(c: string) => boolean} */ const isPart = c => /[\p{ID_Continue}\u{00B7}\u{0387}\u{19DA}\u{1369}\u{136A}\u{136B}\u{136C}\u{136D}\u{136E}\u{136F}\u{1370}\u{1371}]/u.test(c) || isStart(c); // Likewise for Other_ID_Continue const parts = []; let partsActive = false; diff --git a/scripts/request-pr-review.ts b/scripts/request-pr-review.mjs similarity index 89% rename from scripts/request-pr-review.ts rename to scripts/request-pr-review.mjs index f8c993f19e7..51c9e90e786 100644 --- a/scripts/request-pr-review.ts +++ b/scripts/request-pr-review.mjs @@ -1,7 +1,5 @@ -/// -/// import { Octokit } from "@octokit/rest"; -import * as minimist from "minimist"; +import minimist from "minimist"; const options = minimist(process.argv.slice(2), { boolean: ["help"], @@ -55,7 +53,10 @@ async function main() { } } -function printHelpAndExit(exitCode: number) { +/** + * @param {number} exitCode + */ +function printHelpAndExit(exitCode) { console.log(` usage: request-pr-review.js [options] diff --git a/scripts/run-sequence.d.ts b/scripts/run-sequence.d.ts deleted file mode 100644 index fde899dde0d..00000000000 --- a/scripts/run-sequence.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { SpawnSyncOptions } from "child_process"; - -export function runSequence(tasks: [string, string[]][], opts?: SpawnSyncOptions): string; diff --git a/scripts/run-sequence.js b/scripts/run-sequence.mjs similarity index 67% rename from scripts/run-sequence.js rename to scripts/run-sequence.mjs index 6f5828f0b79..910c9e5e518 100644 --- a/scripts/run-sequence.js +++ b/scripts/run-sequence.mjs @@ -1,10 +1,12 @@ -// @ts-check -const cp = require("child_process"); +import assert from "assert"; +import cp from "child_process"; + /** * @param {[string, string[]][]} tasks * @param {cp.SpawnSyncOptions} opts + * @returns {string} */ -function runSequence(tasks, opts = { timeout: 100000, shell: true }) { +export function runSequence(tasks, opts = { timeout: 100000, shell: true }) { let lastResult; for (const task of tasks) { console.log(`${task[0]} ${task[1].join(" ")}`); @@ -13,7 +15,7 @@ function runSequence(tasks, opts = { timeout: 100000, shell: true }) { console.log(result.stdout && result.stdout.toString()); lastResult = result; } - return lastResult && lastResult.stdout && lastResult.stdout.toString(); + const out = lastResult?.stdout?.toString(); + assert(out !== undefined); + return out; } - -exports.runSequence = runSequence; diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json index 54d4a435014..dbda203c4fd 100644 --- a/scripts/tsconfig.json +++ b/scripts/tsconfig.json @@ -1,15 +1,28 @@ { "compilerOptions": { + "lib": [ + "es2018" + ], + "module": "Node16", + "moduleResolution": "Node16", + "target": "es2018", + "noEmit": true, + // "declaration": true, + // "emitDeclarationOnly": true, + "strict": true, - "removeComments": false, - "declaration": false, - "sourceMap": true, - "newLine": "lf", - "target": "es6", - "module": "commonjs", - "types": ["node"], - "lib": ["es6", "scripthost"], + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "allowUnusedLabels": false, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "allowJs": true, + "checkJs": true }, - "include": ["*.ts"] + "include": [ + "**/*.mjs", "**/*.cjs" + ] } diff --git a/scripts/update-experimental-branches.js b/scripts/update-experimental-branches.mjs similarity index 93% rename from scripts/update-experimental-branches.js rename to scripts/update-experimental-branches.mjs index 2dfea45e893..77ea19706b8 100644 --- a/scripts/update-experimental-branches.js +++ b/scripts/update-experimental-branches.mjs @@ -1,7 +1,5 @@ -// @ts-check -/// -const { Octokit } = require("@octokit/rest"); -const { runSequence } = require("./run-sequence"); +import { Octokit } from "@octokit/rest"; +import { runSequence } from "./run-sequence.mjs"; // The first is used by bot-based kickoffs, the second by automatic triggers const triggeredPR = process.env.SOURCE_ISSUE || process.env.SYSTEM_PULLREQUEST_PULLREQUESTNUMBER; @@ -46,7 +44,7 @@ async function main() { const inputPR = await gh.pulls.get({ owner: "Microsoft", repo: "TypeScript", pull_number: num }); // GH calculates the rebaseable-ness of a PR into its target, so we can just use that here if (!inputPR.data.rebaseable) { - if (+triggeredPR === num) { + if (+(triggeredPR ?? 0) === num) { await gh.issues.createComment({ owner: "Microsoft", repo: "TypeScript", diff --git a/scripts/word.d.ts b/scripts/word.d.ts new file mode 100644 index 00000000000..c9e7a56fabc --- /dev/null +++ b/scripts/word.d.ts @@ -0,0 +1,122 @@ +declare namespace Word { + export interface Collection { + count: number; + item(index: number): T; + } + + export interface Font { + bold: boolean; + italic: boolean; + subscript: boolean; + superscript: boolean; + } + + export interface Find { + font: Font; + format: boolean; + replacement: Replacement; + style: any; + text: string; + clearFormatting(): void; + execute( + findText: string, + matchCase: boolean, + matchWholeWord: boolean, + matchWildcards: boolean, + matchSoundsLike: boolean, + matchAllWordForms: boolean, + forward: boolean, + wrap: number, + format: boolean, + replaceWith: string, + replace: number): boolean; + } + + export interface Replacement { + font: Font; + style: any; + text: string; + clearFormatting(): void; + } + + export interface ListFormat { + listLevelNumber: number; + listString: string; + } + + export interface Column { + } + + export interface Columns extends Collection { + } + + export interface Table { + columns: Columns; + } + + export interface Tables extends Collection { + } + + export interface Range { + find: Find; + listFormat: ListFormat; + tables: Tables; + text: string; + textRetrievalMode: { + includeHiddenText: boolean; + } + words: Ranges; + } + + export interface Ranges extends Collection { + } + + export interface Style { + nameLocal: string; + } + + export interface Paragraph { + alignment: number; + range: Range; + style: Style; + next(): Paragraph; + } + + export interface Paragraphs extends Collection { + first: Paragraph; + } + + export interface Field { + } + + export interface Fields extends Collection { + toggleShowCodes(): void; + } + + export interface Hyperlink { + address: string; + textToDisplay: string; + range: Range; + } + + export interface Hyperlinks extends Collection { + } + + export interface Document { + fields: Fields; + paragraphs: Paragraphs; + hyperlinks: Hyperlinks; + builtInDocumentProperties: Collection; + close(saveChanges: boolean): void; + range(): Range; + } + + export interface Documents extends Collection { + open(filename: string): Document; + } + + export interface Application { + documents: Documents; + quit(): void; + } +} diff --git a/scripts/word2md.ts b/scripts/word2md.mjs similarity index 69% rename from scripts/word2md.ts rename to scripts/word2md.mjs index 472699156bb..785bcf93f14 100644 --- a/scripts/word2md.ts +++ b/scripts/word2md.mjs @@ -7,145 +7,32 @@ // as a command line argument and the resulting Markdown is written to standard output. The // tool recognizes the specific Word styles used in the TypeScript Language Specification. -namespace Word { - export interface Collection { - count: number; - item(index: number): T; - } - - export interface Font { - bold: boolean; - italic: boolean; - subscript: boolean; - superscript: boolean; - } - - export interface Find { - font: Font; - format: boolean; - replacement: Replacement; - style: any; - text: string; - clearFormatting(): void; - execute( - findText: string, - matchCase: boolean, - matchWholeWord: boolean, - matchWildcards: boolean, - matchSoundsLike: boolean, - matchAllWordForms: boolean, - forward: boolean, - wrap: number, - format: boolean, - replaceWith: string, - replace: number): boolean; - } - - export interface Replacement { - font: Font; - style: any; - text: string; - clearFormatting(): void; - } - - export interface ListFormat { - listLevelNumber: number; - listString: string; - } - - export interface Column { - } - - export interface Columns extends Collection { - } - - export interface Table { - columns: Columns; - } - - export interface Tables extends Collection
{ - } - - export interface Range { - find: Find; - listFormat: ListFormat; - tables: Tables; - text: string; - textRetrievalMode: { - includeHiddenText: boolean; - } - words: Ranges; - } - - export interface Ranges extends Collection { - } - - export interface Style { - nameLocal: string; - } - - export interface Paragraph { - alignment: number; - range: Range; - style: Style; - next(): Paragraph; - } - - export interface Paragraphs extends Collection { - first: Paragraph; - } - - export interface Field { - } - - export interface Fields extends Collection { - toggleShowCodes(): void; - } - - export interface Hyperlink { - address: string; - textToDisplay: string; - range: Range; - } - - export interface Hyperlinks extends Collection { - } - - export interface Document { - fields: Fields; - paragraphs: Paragraphs; - hyperlinks: Hyperlinks; - builtInDocumentProperties: Collection; - close(saveChanges: boolean): void; - range(): Range; - } - - export interface Documents extends Collection { - open(filename: string): Document; - } - - export interface Application { - documents: Documents; - quit(): void; - } -} +/// +// eslint-disable-next-line @typescript-eslint/triple-slash-reference +/// +/** @type {{ + args: string[]; + createObject: (typeName: string) => any; + write(s: string): void; + writeFile: (fileName: string, data: string) => void; +}} */ const sys = (() => { const fileStream = new ActiveXObject("ADODB.Stream"); fileStream.Type = 2 /* text */; const binaryStream = new ActiveXObject("ADODB.Stream"); binaryStream.Type = 1 /* binary */; - const args: string[] = []; + const args = []; for (let i = 0; i < WScript.Arguments.length; i++) { args[i] = WScript.Arguments.Item(i); } return { args, - createObject: (typeName: string) => new ActiveXObject(typeName), - write(s: string): void { + createObject: (typeName) => new ActiveXObject(typeName), + write(s) { WScript.StdOut.Write(s); }, - writeFile: (fileName: string, data: string): void => { + writeFile: (fileName, data) => { fileStream.Open(); binaryStream.Open(); try { @@ -165,25 +52,37 @@ const sys = (() => { }; })(); -interface FindReplaceOptions { +/** @typedef {{ style?: any; font?: { bold?: boolean; italic?: boolean; subscript?: boolean; }; -} +}} FindReplaceOptions */ -function convertDocumentToMarkdown(doc: Word.Document): string { - - const columnAlignment: number[] = []; - let tableColumnCount: number; - let tableCellIndex: number; - let lastInTable: boolean; - let lastStyle: string; +/** + * @param {Word.Document} doc + * @returns {string} + */ +function convertDocumentToMarkdown(doc) { + /** @type {number[]} */ + const columnAlignment = []; + /** @type {number} */ + let tableColumnCount; + /** @type {number} */ + let tableCellIndex; + /** @type {boolean} */ + let lastInTable; + /** @type {string} */ + let lastStyle; let result = ""; - function setProperties(target: any, properties: any) { + /** + * @param {any} target + * @param {any} properties + */ + function setProperties(target, properties) { for (const name in properties) { if (Object.prototype.hasOwnProperty.call(properties, name)) { const value = properties[name]; @@ -197,7 +96,13 @@ function convertDocumentToMarkdown(doc: Word.Document): string { } } - function findReplace(findText: string, findOptions: FindReplaceOptions, replaceText: string, replaceOptions: FindReplaceOptions) { + /** + * @param {string} findText + * @param {FindReplaceOptions} findOptions + * @param {string} replaceText + * @param {FindReplaceOptions} replaceOptions + */ + function findReplace(findText, findOptions, replaceText, replaceOptions) { const find = doc.range().find; find.clearFormatting(); setProperties(find, findOptions); @@ -230,7 +135,10 @@ function convertDocumentToMarkdown(doc: Word.Document): string { } } - function write(s: string) { + /** + * @param {string} s + */ + function write(s) { result += s; } @@ -250,7 +158,10 @@ function convertDocumentToMarkdown(doc: Word.Document): string { write("|\n"); } - function trimEndFormattingMarks(text: string) { + /** + * @param {string} text + */ + function trimEndFormattingMarks(text) { let i = text.length; while (i > 0 && text.charCodeAt(i - 1) < 0x20) i--; return text.substr(0, i); @@ -269,7 +180,10 @@ function convertDocumentToMarkdown(doc: Word.Document): string { } } - function writeParagraph(p: Word.Paragraph) { + /** + * @param {Word.Paragraph} p + */ + function writeParagraph(p) { const range = p.range; const inTable = range.tables.count > 0; @@ -408,12 +322,17 @@ function convertDocumentToMarkdown(doc: Word.Document): string { return result; } -function main(args: string[]) { +/** + * @param {string[]} args + */ +function main(args) { if (args.length !== 2) { sys.write("Syntax: word2md \n"); return; } - const app: Word.Application = sys.createObject("Word.Application"); + + /** @type {Word.Application} */ + const app = sys.createObject("Word.Application"); const doc = app.documents.open(args[0]); sys.writeFile(args[1], convertDocumentToMarkdown(doc)); doc.close(/* saveChanges */ false); diff --git a/src/harness/findUpDir.ts b/src/harness/findUpDir.ts index 3078554f61b..013ac51ef0a 100644 --- a/src/harness/findUpDir.ts +++ b/src/harness/findUpDir.ts @@ -17,5 +17,5 @@ namespace Utils { } export const findUpRoot: { (): string; cached?: string; } = () => - findUpRoot.cached ||= dirname(findUpFile("Gulpfile.js")); + findUpRoot.cached ||= dirname(findUpFile("Gulpfile.mjs")); } diff --git a/src/testRunner/parallel/host.ts b/src/testRunner/parallel/host.ts index d9c49e7ca21..6c11b5a2899 100644 --- a/src/testRunner/parallel/host.ts +++ b/src/testRunner/parallel/host.ts @@ -14,7 +14,7 @@ namespace Harness.Parallel.Host { const { statSync } = require("fs") as typeof import("fs"); // NOTE: paths for module and types for FailedTestReporter _do not_ line up due to our use of --outFile for run.js - const FailedTestReporter = require(Utils.findUpFile("scripts/failed-tests.js")) as typeof import("../../../scripts/failed-tests"); + const FailedTestReporter = require(Utils.findUpFile("scripts/failed-tests.cjs")) as typeof import("../../../scripts/failed-tests.cjs"); const perfdataFileNameFragment = ".parallelperf"; const perfData = readSavedPerfData(configOption); @@ -534,7 +534,7 @@ namespace Harness.Parallel.Host { patchStats(consoleReporter.stats); let xunitReporter: import("mocha").reporters.XUnit | undefined; - let failedTestReporter: import("../../../scripts/failed-tests") | undefined; + let failedTestReporter: import("../../../scripts/failed-tests.cjs") | undefined; if (process.env.CI === "true") { xunitReporter = new Mocha.reporters.XUnit(replayRunner, { reporterOptions: { From d0bfd8caed521bfd24fc44960d9936a891744bb7 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Fri, 7 Oct 2022 20:33:20 +0300 Subject: [PATCH 08/12] fix(51072): ts.preProcessFile finds import in template string after conditional expression with template strings (#51082) --- src/services/preProcess.ts | 3 +-- .../unittests/services/preProcessFile.ts | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/services/preProcess.ts b/src/services/preProcess.ts index d5c384e9161..f891891deb7 100644 --- a/src/services/preProcess.ts +++ b/src/services/preProcess.ts @@ -347,8 +347,8 @@ namespace ts { if (scanner.getToken() === SyntaxKind.TemplateHead) { const stack = [scanner.getToken()]; - let token = scanner.scan(); loop: while (length(stack)) { + const token = scanner.scan(); switch (token) { case SyntaxKind.EndOfFileToken: break loop; @@ -376,7 +376,6 @@ namespace ts { } break; } - token = scanner.scan(); } nextToken(); } diff --git a/src/testRunner/unittests/services/preProcessFile.ts b/src/testRunner/unittests/services/preProcessFile.ts index a6369d6e4d4..a0ff91a26b1 100644 --- a/src/testRunner/unittests/services/preProcessFile.ts +++ b/src/testRunner/unittests/services/preProcessFile.ts @@ -205,6 +205,22 @@ describe("unittests:: services:: PreProcessFile:", () => { /* eslint-enable no-template-curly-in-string */ }); + it("Ignores imports in template strings", () => { + /* eslint-disable no-template-curly-in-string */ + test("a ? `&${a}` : `#${b}`;\n\n `import(\"${moduleSpecifier}\").${id}`;", + /*readImportFile*/ true, + /*detectJavaScriptImports*/ true, + { + referencedFiles: [], + typeReferenceDirectives: [], + libReferenceDirectives: [], + importedFiles: [], + ambientExternalModules: undefined, + isLibFile: false + }); + /* eslint-enable no-template-curly-in-string */ + }); + it("Correctly returns imports after a template expression", () => { /* eslint-disable no-template-curly-in-string */ test("`${foo}`; import \"./foo\";", From a7d10f15bbd28166b869ae00482214e360891613 Mon Sep 17 00:00:00 2001 From: TypeScript Bot Date: Sat, 8 Oct 2022 06:08:40 +0000 Subject: [PATCH 09/12] Update package-lock.json --- package-lock.json | 46 ++++++++++++++-------------------------------- 1 file changed, 14 insertions(+), 32 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1d7f1fa70fa..66238c0178e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -88,9 +88,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.2.tgz", - "integrity": "sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", + "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -187,16 +187,6 @@ "node": ">=10.10.0" } }, - "node_modules/@humanwhocodes/gitignore-to-minimatch": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", - "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -2295,14 +2285,13 @@ } }, "node_modules/eslint": { - "version": "8.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.24.0.tgz", - "integrity": "sha512-dWFaPhGhTAiPcCgm3f6LI2MBWbogMnTJzFBbhXVRQDJPkr9pGZvVjlVfXd+vyDcWPA2Ic9L2AXPIQM0+vk/cSQ==", + "version": "8.25.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.25.0.tgz", + "integrity": "sha512-DVlJOZ4Pn50zcKW5bYH7GQK/9MsoQG2d5eDH0ebEkE8PbgzTTmtt/VTH9GGJ4BfeZCpBLqFfvsjX35UacUL83A==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.3.2", + "@eslint/eslintrc": "^1.3.3", "@humanwhocodes/config-array": "^0.10.5", - "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", "@humanwhocodes/module-importer": "^1.0.1", "ajv": "^6.10.0", "chalk": "^4.0.0", @@ -8686,9 +8675,9 @@ } }, "@eslint/eslintrc": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.2.tgz", - "integrity": "sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", + "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", "dev": true, "requires": { "ajv": "^6.12.4", @@ -8765,12 +8754,6 @@ "minimatch": "^3.0.4" } }, - "@humanwhocodes/gitignore-to-minimatch": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", - "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", - "dev": true - }, "@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -10422,14 +10405,13 @@ "dev": true }, "eslint": { - "version": "8.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.24.0.tgz", - "integrity": "sha512-dWFaPhGhTAiPcCgm3f6LI2MBWbogMnTJzFBbhXVRQDJPkr9pGZvVjlVfXd+vyDcWPA2Ic9L2AXPIQM0+vk/cSQ==", + "version": "8.25.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.25.0.tgz", + "integrity": "sha512-DVlJOZ4Pn50zcKW5bYH7GQK/9MsoQG2d5eDH0ebEkE8PbgzTTmtt/VTH9GGJ4BfeZCpBLqFfvsjX35UacUL83A==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.3.2", + "@eslint/eslintrc": "^1.3.3", "@humanwhocodes/config-array": "^0.10.5", - "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", "@humanwhocodes/module-importer": "^1.0.1", "ajv": "^6.10.0", "chalk": "^4.0.0", From c01ae01fac37268bac3362fb6e6d26db730f7ed5 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 10 Oct 2022 09:33:45 -0700 Subject: [PATCH 10/12] Fix nightly publish oops in Gulpfile (#51131) --- Gulpfile.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gulpfile.mjs b/Gulpfile.mjs index a8f32d5741b..46c58277988 100644 --- a/Gulpfile.mjs +++ b/Gulpfile.mjs @@ -536,7 +536,7 @@ task("generate-spec").description = "Generates a Markdown version of the Languag task("clean", series(parallel(cleanTasks), cleanBuilt)); task("clean").description = "Cleans build outputs"; -const configureNightly = () => exec(process.execPath, ["scripts/configurePrerelease.js", "dev", "package.json", "src/compiler/corePublic.ts"]); +const configureNightly = () => exec(process.execPath, ["scripts/configurePrerelease.mjs", "dev", "package.json", "src/compiler/corePublic.ts"]); task("configure-nightly", series(buildScripts, configureNightly)); task("configure-nightly").description = "Runs scripts/configurePrerelease.ts to prepare a build for nightly publishing"; From 9c87ded2b3fc4ba4a9a7656e9be39d5e404e6ab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Ch=C5=82odnicki?= Date: Mon, 10 Oct 2022 21:03:15 +0200 Subject: [PATCH 11/12] fix(51100): ensure tsserver shuts down when parent process is killed (#51107) * fix(51100): ensure tsserver shuts down when parent process is killed When using IPC channel (`--useNodeIpc`) for communicating with tsserver, the child tsserver process did not shut down on parent process disconnecting (for example due to it being killed). Call exit() on IPC disconnect, same as stdio-based communication did when pipe to parent process was destroyed. * don't duplicate inherited method --- src/tsserver/nodeServer.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tsserver/nodeServer.ts b/src/tsserver/nodeServer.ts index 0bfc5f58719..47e3ff6bfb1 100644 --- a/src/tsserver/nodeServer.ts +++ b/src/tsserver/nodeServer.ts @@ -783,6 +783,10 @@ namespace ts.server { process.on("message", (e: any) => { this.onMessage(e); }); + + process.on("disconnect", () => { + this.exit(); + }); } } From 67256e50c41aa9178a60c52de8416477f070b190 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 10 Oct 2022 13:57:10 -0700 Subject: [PATCH 12/12] Remove unused declarations array in extractSymbol's TargetRange (#51091) --- src/services/refactors/extractSymbol.ts | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 67435b47354..51db8802532 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -239,11 +239,6 @@ namespace ts.refactor.extractSymbol { interface TargetRange { readonly range: Expression | Statement[]; readonly facts: RangeFacts; - /** - * A list of symbols that are declared in the selected range which are visible in the containing lexical scope - * Used to ensure we don't turn something used outside the range free (or worse, resolve to a different entity). - */ - readonly declarations: Symbol[]; /** * If `this` is referring to a function instead of class, we need to retrieve its type. */ @@ -294,8 +289,6 @@ namespace ts.refactor.extractSymbol { // Do the same for the ending position const end = cursorRequest ? start : getParentNodeInSpan(endToken, sourceFile, adjustedSpan); - const declarations: Symbol[] = []; - // We'll modify these flags as we walk the tree to collect data // about what things need to be done as part of the extraction. let rangeFacts = RangeFacts.None; @@ -344,7 +337,7 @@ namespace ts.refactor.extractSymbol { return { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.cannotExtractRange)] }; } - return { targetRange: { range: statements, facts: rangeFacts, declarations, thisNode } }; + return { targetRange: { range: statements, facts: rangeFacts, thisNode } }; } if (isReturnStatement(start) && !start.expression) { @@ -359,7 +352,7 @@ namespace ts.refactor.extractSymbol { if (errors) { return { errors }; } - return { targetRange: { range: getStatementOrExpressionRange(node)!, facts: rangeFacts, declarations, thisNode } }; // TODO: GH#18217 + return { targetRange: { range: getStatementOrExpressionRange(node)!, facts: rangeFacts, thisNode } }; // TODO: GH#18217 /** * Attempt to refine the extraction node (generally, by shrinking it) to produce better results. @@ -489,7 +482,6 @@ namespace ts.refactor.extractSymbol { (errors ||= []).push(createDiagnosticForNode(node, Messages.cannotExtractExportedEntity)); return true; } - declarations.push(node.symbol); } // Some things can't be extracted in certain situations