diff --git a/.dprint.jsonc b/.dprint.jsonc index 15ae55f237c05..3bc207bf626fe 100644 --- a/.dprint.jsonc +++ b/.dprint.jsonc @@ -1,71 +1,75 @@ -{ - // If updating this, also update the config in dtsBundler.mjs. - "indentWidth": 4, - "lineWidth": 1000, - "newLineKind": "auto", - "useTabs": false, - "typescript": { - "newLineKind": "crlf", - "semiColons": "always", - "quoteStyle": "preferDouble", - "quoteProps": "consistent", - "useBraces": "whenNotSingleLine", - "bracePosition": "sameLineUnlessHanging", - "singleBodyPosition": "sameLine", - "nextControlFlowPosition": "nextLine", // Stroustrup style braces. - "trailingCommas": "onlyMultiLine", - "preferHanging": false, - "operatorPosition": "maintain", - - "arrowFunction.useParentheses": "preferNone", - "conditionalExpression.linePerExpression": false, // Keep our "match/case"-ish conditionals. - "functionExpression.spaceAfterFunctionKeyword": true, - "importDeclaration.forceMultiLine": "whenMultiple", - "constructorType.spaceAfterNewKeyword": true, - "constructSignature.spaceAfterNewKeyword": true, - - "module.sortImportDeclarations": "caseInsensitive", - "module.sortExportDeclarations": "caseInsensitive", - "exportDeclaration.sortNamedExports": "caseInsensitive", - "importDeclaration.sortNamedImports": "caseInsensitive" - }, - "yaml": { - "indentWidth": 2, - "quotes": "preferSingle" - }, - "json": { - // This would be good to do in known-JSONC files, but VS Code warns on trailing commas. - "trailingCommas": "never" - }, - "gofumpt": { - "langVersion": "go1.26", - "modulePath": "github.com/microsoft/TypeScript/tsc" - }, - "excludes": [ - "**/.git", - "**/node_modules", - "**/*-lock.json", - "**/testdata", - "packages/vscode-typescript/l10n/**", - "tsc/internal/bundled/libs/**", - "tsc/internal/lsp/lsproto/_generate/*.json", - "tsc/internal/lsp/lsproto/_generate/metaModelSchema.mts", - // Needs to be LF to have a working shebang. - "packages/typescript/bin/tsc", - "tsc/internal/bundled/source/**", - "tsc/internal/locale/lcl/**", - "packages/typescript/dist/**", - "packages/typescript/tsconfig.tsbuildinfo", - "packages/vscode-typescript/dist/**", - "packages/vscode-typescript-nightly/dist/**", - "packages/typescript/vendor/**" - ], - // Note: if adding new languages, make sure settings.template.json is updated too. - // Also, if updating typescript, update the one in package.json. - "plugins": [ - "npm:@dprint/typescript@0.96.1", - "npm:@dprint/json@0.22.0", - "npm:dprint-plugin-yaml@0.6.0", - "npm:@jakebailey/dprint-plugin-gofumpt@0.0.13" - ] -} +{ + // If updating this, also update the config in dtsBundler.mjs. + "indentWidth": 4, + "lineWidth": 1000, + "newLineKind": "lf", + "useTabs": false, + "typescript": { + "newLineKind": "lf", + "semiColons": "always", + "quoteStyle": "preferDouble", + "quoteProps": "consistent", + "useBraces": "whenNotSingleLine", + "bracePosition": "sameLineUnlessHanging", + "singleBodyPosition": "sameLine", + "nextControlFlowPosition": "nextLine", // Stroustrup style braces. + "trailingCommas": "onlyMultiLine", + "preferHanging": false, + "operatorPosition": "maintain", + + "arrowFunction.useParentheses": "preferNone", + "conditionalExpression.linePerExpression": false, // Keep our "match/case"-ish conditionals. + "functionExpression.spaceAfterFunctionKeyword": true, + "importDeclaration.forceMultiLine": "whenMultiple", + "constructorType.spaceAfterNewKeyword": true, + "constructSignature.spaceAfterNewKeyword": true, + + "module.sortImportDeclarations": "caseInsensitive", + "module.sortExportDeclarations": "caseInsensitive", + "exportDeclaration.sortNamedExports": "caseInsensitive", + "importDeclaration.sortNamedImports": "caseInsensitive" + }, + "yaml": { + "indentWidth": 2, + "quotes": "preferSingle" + }, + "json": { + // This would be good to do in known-JSONC files, but VS Code warns on trailing commas. + "trailingCommas": "never" + }, + "gofumpt": { + "langVersion": "go1.26", + "modulePath": "github.com/microsoft/TypeScript/tsc" + }, + "excludes": [ + "**/.git", + "**/node_modules", + "**/*-lock.json", + "**/testdata", + "packages/vscode-typescript/l10n/**", + "tsc/internal/bundled/libs/lib.dom.d.ts", + "tsc/internal/bundled/libs/lib.dom.asynciterable.d.ts", + "tsc/internal/bundled/libs/lib.dom.iterable.d.ts", + "tsc/internal/bundled/libs/lib.webworker.d.ts", + "tsc/internal/bundled/libs/lib.webworker.asynciterable.d.ts", + "tsc/internal/bundled/libs/lib.webworker.iterable.d.ts", + "tsc/internal/lsp/lsproto/_generate/*.json", + "tsc/internal/lsp/lsproto/_generate/metaModelSchema.mts", + // Needs to be LF to have a working shebang. + "packages/typescript/bin/tsc", + "tsc/internal/locale/lcl/**", + "packages/typescript/dist/**", + "packages/typescript/tsconfig.tsbuildinfo", + "packages/vscode-typescript/dist/**", + "packages/vscode-typescript-nightly/dist/**", + "packages/typescript/vendor/**" + ], + // Note: if adding new languages, make sure settings.template.json is updated too. + // Also, if updating typescript, update the one in package.json. + "plugins": [ + "npm:@dprint/typescript@0.96.1", + "npm:@dprint/json@0.22.0", + "npm:dprint-plugin-yaml@0.6.0", + "npm:@jakebailey/dprint-plugin-gofumpt@0.0.13" + ] +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000000..9597e5823ff9a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +root = true + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[tsc/testdata/**] +end_of_line = unset +charset = unset +trim_trailing_whitespace = unset +insert_final_newline = unset + +[tsc/internal/locale/lcl/**] +end_of_line = unset +charset = unset +trim_trailing_whitespace = unset +insert_final_newline = unset diff --git a/.gitattributes b/.gitattributes index 6ecc3203dc4f8..5ab17c3b3f92f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,10 @@ *.js linguist-language=TypeScript **/*.json linguist-language=jsonc -* -text -*.go text eol=lf + +* text=auto eol=lf + +# Preserve line-ending-sensitive fixtures exactly. +tsc/testdata/** -text + +# TODO: Remove this exception when this directory is converted or removed. +tsc/internal/locale/lcl/** -text diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index f57eb0a941c88..5ce1098e58375 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,19 +1,19 @@ - - -Fixes # + + +Fixes # diff --git a/.gitignore b/.gitignore index 6baeed8f5f4f2..156c85166cad1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,136 +1,136 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* -.pnpm-debug.log* - -# Diagnostic reports -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Profiling and coverage -*.prof -*.pb.gz -coverage -*.lcov -.nyc_output - -# Dependency and package-manager directories -node_modules/ -jspm_packages/ -web_modules/ -.npm -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.yarn/install-state.gz -.pnp.* - -# Build and package outputs -*.tsbuildinfo -*.tgz -dist -.eslintcache -.cache -.parcel-cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ -.vscode-test -/built -# https://github.com/golang/go/issues/63901 -/go.work.sum -/tsc/cmd/tsc/tsc -/tsc/tsc - -# Native compiler outputs -*.exe -*.exe~ -*.dll -*.so -*.dylib -*.wasm -*.test -__debug_bin* -*.out - -# Environment and editor files -.env -.env.development.local -.env.test.local -.env.production.local -.env.local -.node_repl_history -**/.vscode/* -!**/.vscode/tasks.json -!**/.vscode/settings.template.json -!**/.vscode/launch.template.json -!**/.vscode/extensions.json -.DS_Store -.idea - -# Local compiler baselines and test output -tsc/testdata/baselines/local -tsc/testdata/baselines/tmp - -# Legacy Strada build and test output -/.node_modules/ -/test-args.txt -/rwc-report.html -/build.json -*.actual -/.parallelperf.* -/.failed-tests -/TEST-results.xml -/*v8.log -/lib/ -/internal/ -/scripts/eslint/built/ -/scripts/debug.bat -/scripts/run.bat -/scripts/**/*.js -/scripts/**/*.js.map -/src/harness/*.js -/src/compiler/diagnosticInformationMap.generated.ts -/src/compiler/diagnosticMessages.generated.json -/src/parser/diagnosticInformationMap.generated.ts -/src/parser/diagnosticMessages.generated.json -/tests/cases/rwc/ -/tests/cases/perf/ -/tests/cases/**/*.js -/tests/cases/**/*.js.map -/tests/baselines/local/ -/tests/baselines/local.old/ -/tests/baselines/prototyping/local/ -/tests/baselines/rwc/ -/tests/baselines/symlinks/ -/tests/baselines/reference/projectOutput/ -/tests/baselines/reference/testresults.tap -/tests/baselines/reference/dt -/tests/services/baselines/local/ -/tests/services/baselines/prototyping/local/ -/tests/services/browser/typescriptServices.js -/tests/webTestServer.js -/tests/webTestServer.js.map -/tests/webhost/*.d.ts -/tests/webhost/webtsc.js - -# Generated development tools -tools/custom-gcl -tools/custom-gcl.exe -tools/custom-gcl.hash - -# Keep tracked text assets that overlap broad output patterns -!tsc/testdata/baselines/reference/**/*.txt -!packages/typescript/vendor/**/*.txt -!tsc/testdata/submoduleAccepted.txt -!tsc/testdata/submoduleTriaged.txt +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Profiling and coverage +*.prof +*.pb.gz +coverage +*.lcov +.nyc_output + +# Dependency and package-manager directories +node_modules/ +jspm_packages/ +web_modules/ +.npm +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# Build and package outputs +*.tsbuildinfo +*.tgz +dist +.eslintcache +.cache +.parcel-cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ +.vscode-test +/built +# https://github.com/golang/go/issues/63901 +/go.work.sum +/tsc/cmd/tsc/tsc +/tsc/tsc + +# Native compiler outputs +*.exe +*.exe~ +*.dll +*.so +*.dylib +*.wasm +*.test +__debug_bin* +*.out + +# Environment and editor files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local +.node_repl_history +**/.vscode/* +!**/.vscode/tasks.json +!**/.vscode/settings.template.json +!**/.vscode/launch.template.json +!**/.vscode/extensions.json +.DS_Store +.idea + +# Local compiler baselines and test output +tsc/testdata/baselines/local +tsc/testdata/baselines/tmp + +# Legacy Strada build and test output +/.node_modules/ +/test-args.txt +/rwc-report.html +/build.json +*.actual +/.parallelperf.* +/.failed-tests +/TEST-results.xml +/*v8.log +/lib/ +/internal/ +/scripts/eslint/built/ +/scripts/debug.bat +/scripts/run.bat +/scripts/**/*.js +/scripts/**/*.js.map +/src/harness/*.js +/src/compiler/diagnosticInformationMap.generated.ts +/src/compiler/diagnosticMessages.generated.json +/src/parser/diagnosticInformationMap.generated.ts +/src/parser/diagnosticMessages.generated.json +/tests/cases/rwc/ +/tests/cases/perf/ +/tests/cases/**/*.js +/tests/cases/**/*.js.map +/tests/baselines/local/ +/tests/baselines/local.old/ +/tests/baselines/prototyping/local/ +/tests/baselines/rwc/ +/tests/baselines/symlinks/ +/tests/baselines/reference/projectOutput/ +/tests/baselines/reference/testresults.tap +/tests/baselines/reference/dt +/tests/services/baselines/local/ +/tests/services/baselines/prototyping/local/ +/tests/services/browser/typescriptServices.js +/tests/webTestServer.js +/tests/webTestServer.js.map +/tests/webhost/*.d.ts +/tests/webhost/webtsc.js + +# Generated development tools +tools/custom-gcl +tools/custom-gcl.exe +tools/custom-gcl.hash + +# Keep tracked text assets that overlap broad output patterns +!tsc/testdata/baselines/reference/**/*.txt +!packages/typescript/vendor/**/*.txt +!tsc/testdata/submoduleAccepted.txt +!tsc/testdata/submoduleTriaged.txt diff --git a/.vscode/settings.template.json b/.vscode/settings.template.json index dd37e6f9e2ba7..517d357c09c59 100644 --- a/.vscode/settings.template.json +++ b/.vscode/settings.template.json @@ -1,12 +1,12 @@ // Rename this file 'settings.json' or merge its // contents into your existing settings. { + "files.eol": "\n", "[typescript][typescriptreact][javascript][javascriptreact][json][jsonc][yaml][github-actions-workflow]": { "editor.defaultFormatter": "dprint.dprint", "editor.formatOnSave": true }, "[go][go.mod][go.sum][go.work]": { - "files.eol": "\n", "files.insertFinalNewline": true, "editor.defaultFormatter": "golang.go", "editor.formatOnSave": true, diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 835019b61a4c5..c4057910f7aca 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,129 +1,129 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "label": "Build compiler", - "type": "npm", - "script": "build", - "group": { - "kind": "build", - "isDefault": true - }, - "problemMatcher": ["$go"] - }, - { - "label": "Watch compiler", - "type": "npm", - "script": "build:watch:debug", - "group": "build", - "isBackground": true, - "problemMatcher": { - "owner": "hereby", - "fileLocation": "autoDetect", - "source": "hereby", - "applyTo": "closedDocuments", - "pattern": { "regexp": "" }, - "background": { - "activeOnStart": true, - "beginsPattern": "\\[build:watch\\] changed due to", - "endsPattern": "\\[build:watch\\] run complete" - } - } - }, - { - "label": "Test", - "type": "shell", - "command": "npx", - "args": ["hereby", "test"], - "group": "test", - "problemMatcher": ["$go"] - }, - { - "label": "Lint", - "type": "shell", - "command": "npx", - "args": ["hereby", "lint"], - "problemMatcher": ["$go"] - }, - { - "label": "Generate", - "type": "shell", - "command": "npx", - "args": ["hereby", "generate"], - "problemMatcher": ["$go"] - }, - { - "label": "Build extension", - "type": "npm", - "script": "extension:build", - "group": "build", - "problemMatcher": [ - "$tsc", - // Copy-paste from https://github.com/connor4312/esbuild-problem-matchers to avoid the extension dependency - { - "owner": "esbuild", - "severity": "error", - "applyTo": "closedDocuments", - "source": "esbuild", - "fileLocation": "relative", - "pattern": [ - { - "regexp": "^[✘▲] \\[([A-Z]+)\\] (.+)", - "severity": 1, - "message": 2 - }, - { - "regexp": "^(?:\\t| {4})(?!\\s)([^:]+)(?::([0-9]+))?(?::([0-9]+))?:$", - "file": 1, - "line": 2, - "column": 3 - } - ] - } - ] - }, - { - "label": "Watch extension", - "type": "npm", - "script": "extension:watch", - "group": "build", - "isBackground": true, - "problemMatcher": [ - // Copy-paste from https://github.com/connor4312/esbuild-problem-matchers to avoid the extension dependency - { - "owner": "esbuild-watch", - "severity": "error", - "applyTo": "closedDocuments", - "source": "esbuild", - "fileLocation": "relative", - "pattern": [ - { - "regexp": "^[✘▲] \\[([A-Z]+)\\] (.+)", - "severity": 1, - "message": 2 - }, - { - "regexp": "^(?:\\t| {4})(?!\\s)([^:]+)(?::([0-9]+))?(?::([0-9]+))?:$", - "file": 1, - "line": 2, - "column": 3 - } - ], - "background": { - "activeOnStart": true, - "beginsPattern": { - "regexp": "\\[watch\\] build started" - }, - "endsPattern": { - "regexp": "\\[watch\\] build finished" - } - } - } - ] - }, - { - "label": "Watch for extension run", - "dependsOn": ["Watch extension", "Watch compiler"] - } - ] -} +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Build compiler", + "type": "npm", + "script": "build", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": ["$go"] + }, + { + "label": "Watch compiler", + "type": "npm", + "script": "build:watch:debug", + "group": "build", + "isBackground": true, + "problemMatcher": { + "owner": "hereby", + "fileLocation": "autoDetect", + "source": "hereby", + "applyTo": "closedDocuments", + "pattern": { "regexp": "" }, + "background": { + "activeOnStart": true, + "beginsPattern": "\\[build:watch\\] changed due to", + "endsPattern": "\\[build:watch\\] run complete" + } + } + }, + { + "label": "Test", + "type": "shell", + "command": "npx", + "args": ["hereby", "test"], + "group": "test", + "problemMatcher": ["$go"] + }, + { + "label": "Lint", + "type": "shell", + "command": "npx", + "args": ["hereby", "lint"], + "problemMatcher": ["$go"] + }, + { + "label": "Generate", + "type": "shell", + "command": "npx", + "args": ["hereby", "generate"], + "problemMatcher": ["$go"] + }, + { + "label": "Build extension", + "type": "npm", + "script": "extension:build", + "group": "build", + "problemMatcher": [ + "$tsc", + // Copy-paste from https://github.com/connor4312/esbuild-problem-matchers to avoid the extension dependency + { + "owner": "esbuild", + "severity": "error", + "applyTo": "closedDocuments", + "source": "esbuild", + "fileLocation": "relative", + "pattern": [ + { + "regexp": "^[✘▲] \\[([A-Z]+)\\] (.+)", + "severity": 1, + "message": 2 + }, + { + "regexp": "^(?:\\t| {4})(?!\\s)([^:]+)(?::([0-9]+))?(?::([0-9]+))?:$", + "file": 1, + "line": 2, + "column": 3 + } + ] + } + ] + }, + { + "label": "Watch extension", + "type": "npm", + "script": "extension:watch", + "group": "build", + "isBackground": true, + "problemMatcher": [ + // Copy-paste from https://github.com/connor4312/esbuild-problem-matchers to avoid the extension dependency + { + "owner": "esbuild-watch", + "severity": "error", + "applyTo": "closedDocuments", + "source": "esbuild", + "fileLocation": "relative", + "pattern": [ + { + "regexp": "^[✘▲] \\[([A-Z]+)\\] (.+)", + "severity": 1, + "message": 2 + }, + { + "regexp": "^(?:\\t| {4})(?!\\s)([^:]+)(?::([0-9]+))?(?::([0-9]+))?:$", + "file": 1, + "line": 2, + "column": 3 + } + ], + "background": { + "activeOnStart": true, + "beginsPattern": { + "regexp": "\\[watch\\] build started" + }, + "endsPattern": { + "regexp": "\\[watch\\] build finished" + } + } + } + ] + }, + { + "label": "Watch for extension run", + "dependsOn": ["Watch extension", "Watch compiler"] + } + ] +} diff --git a/Herebyfile.mjs b/Herebyfile.mjs index 361b819ba9240..14ed15602c9fc 100644 --- a/Herebyfile.mjs +++ b/Herebyfile.mjs @@ -1,2446 +1,2498 @@ -// @ts-check - -import AdmZip from "adm-zip"; -import chokidar from "chokidar"; -import { $ as _$ } from "execa"; -import { glob } from "glob"; -import { task } from "hereby"; -import assert from "node:assert"; -import crypto from "node:crypto"; -import fs from "node:fs"; -import os from "node:os"; -import path from "node:path"; -import url from "node:url"; -import { parseArgs } from "node:util"; -import pLimit from "p-limit"; -import pc from "picocolors"; -import * as tar from "tar"; -import tmp from "tmp"; -import which from "which"; - -if (process.platform === "win32") { - process.chdir(fs.realpathSync.native(process.cwd())); -} - -const __filename = url.fileURLToPath(new URL(import.meta.url)); -const __dirname = path.dirname(__filename); - -const isCI = !!process.env.CI || !!process.env.TF_BUILD; - -const $pipe = _$({ verbose: "short" }); -const $ = _$({ verbose: "short", stdio: "inherit" }); - -/** - * @param {string} name - * @param {boolean} defaultValue - * @returns {boolean} - */ -function parseEnvBoolean(name, defaultValue = false) { - name = "TSGO_HEREBY_" + name.toUpperCase(); - - const value = process.env[name]; - if (!value) { - return defaultValue; - } - switch (value.toUpperCase()) { - case "1": - case "TRUE": - case "YES": - case "ON": - return true; - case "0": - case "FALSE": - case "NO": - case "OFF": - return false; - } - throw new Error(`Invalid value for ${name}: ${value}`); -} - -const { values: rawOptions } = parseArgs({ - args: process.argv.slice(2), - options: { - tests: { type: "string", short: "t" }, - fix: { type: "boolean" }, - debug: { type: "boolean" }, - dirty: { type: "boolean" }, - release: { type: "boolean" }, - - setPrerelease: { type: "string" }, - forRelease: { type: "boolean" }, - - race: { type: "boolean", default: parseEnvBoolean("RACE") }, - noembed: { type: "boolean", default: parseEnvBoolean("NOEMBED") }, - concurrentTestPrograms: { type: "boolean", default: parseEnvBoolean("CONCURRENT_TEST_PROGRAMS") }, - coverage: { type: "boolean", default: parseEnvBoolean("COVERAGE") }, - }, - strict: false, - allowPositionals: true, - allowNegative: true, -}); - -// We can't use parseArgs' strict mode as it errors on hereby's --tasks flag. -/** - * @typedef {{ [K in keyof typeof rawOptions as {} extends Record ? never : K]: typeof rawOptions[K] }} Options - */ -const options = /** @type {Options} */ (rawOptions); - -// Native release branches can edit these constants to publish a fixed stable version. -// Main publishes prerelease builds of the TypeScript package. -const nativePreviewReleaseProfile = /** @type {"native-preview" | "typescript"} */ ("typescript"); -const nativePreviewReleaseVersion = /** @type {string | undefined} */ (undefined); -const produceNativePreviewVsix = /** @type {boolean} */ (false); -const produceTypeScriptNightlyVsix = /** @type {boolean} */ (true); -const usePublishedPlatformPackagesForVsix = /** @type {boolean} */ (false); -const produceAnyVsix = produceNativePreviewVsix || produceTypeScriptNightlyVsix; -const publishAsTypescript = nativePreviewReleaseProfile === "typescript"; - -if (options.forRelease && !options.setPrerelease && (!nativePreviewReleaseVersion || produceAnyVsix)) { - throw new Error("forRelease requires setPrerelease unless nativePreviewReleaseVersion is hardcoded and VSIX production is disabled"); -} -if (usePublishedPlatformPackagesForVsix && !publishAsTypescript) { - throw new Error("usePublishedPlatformPackagesForVsix requires nativePreviewReleaseProfile to be 'typescript'"); -} - -const defaultGoBuildTags = [ - ...(options.noembed ? ["noembed"] : []), -]; - -/** - * @param {...string} extra - * @returns {string[]} - */ -function goBuildTags(...extra) { - const tags = new Set(defaultGoBuildTags.concat(extra)); - return tags.size ? [`-tags=${[...tags].join(",")}`] : []; -} - -const goBuildFlags = [ - ...(options.race ? ["-race"] : []), - // https://github.com/go-delve/delve/blob/62cd2d423c6a85991e49d6a70cc5cb3e97d6ceef/Documentation/usage/dlv_exec.md?plain=1#L12 - ...(options.debug ? ["-gcflags=all=-N -l"] : []), -]; - -const goBuildEnv = { - ...(options.race ? {} : { CGO_ENABLED: "0" }), -}; - -/** - * @template T - * @param {() => T} fn - * @returns {() => T} - */ -function memoize(fn) { - /** @type {T} */ - let value; - return () => { - if (fn !== undefined) { - value = fn(); - fn = /** @type {any} */ (undefined); - } - return value; - }; -} - -const tools = new Map([ - ["gotest.tools/gotestsum", "latest"], -]); - -/** - * @param {string} tool - */ -function isInstalled(tool) { - return !!which.sync(tool, { nothrow: true }); -} - -const builtLocal = "./built/local"; - -const libsDir = "./tsc/internal/bundled/libs"; -const libsRegexp = /(?:^|[\\/])internal[\\/]bundled[\\/]libs[\\/]/; - -/** - * @param {string} out - */ -async function generateLibs(out) { - await fs.promises.mkdir(out, { recursive: true }); - - const libs = await fs.promises.readdir(libsDir); - - await Promise.all(libs.map(async lib => { - fs.promises.copyFile(path.join(libsDir, lib), path.join(out, lib)); - })); -} - -export const lib = task({ - name: "lib", - description: "Copies the libs to built/local.", - run: () => generateLibs(builtLocal), -}); - -/** - * Gets the release build flags for stripping debug info. - * @param {string} [versionOverride] Optional version to embed in the binary. - * @returns {string[]} - */ -function getReleaseBuildFlags(versionOverride) { - let ldflags = "-ldflags=-s -w"; - if (versionOverride) { - ldflags += ` -X github.com/microsoft/TypeScript/tsc/internal/core.version=${versionOverride}`; - } - return ["-trimpath", ldflags]; -} - -/** - * @param {object} [opts] - * @param {string} [opts.out] - * @param {AbortSignal} [opts.abortSignal] - * @param {Record} [opts.env] - * @param {string[]} [opts.extraFlags] - */ -function buildTsc(opts) { - opts ||= {}; - const out = opts.out ?? path.resolve("./built/local/tsc" + (process.platform === "win32" ? ".exe" : "")); - const env = { ...goBuildEnv, ...opts.env }; - return $({ cancelSignal: opts.abortSignal, env, cwd: "./tsc" })`go build ${goBuildFlags} ${opts.extraFlags ?? []} ${goBuildTags("noembed")} -o ${out} ./cmd/tsc`; -} - -export const tscBuild = task({ - name: "tsc:build", - description: "Builds the tsc binary.", - run: async () => { - await buildTsc({ extraFlags: options.release ? getReleaseBuildFlags() : [] }); - }, -}); - -export const tsgo = task({ - name: "tsgo", - dependencies: [lib, tscBuild], -}); - -export const local = task({ - name: "local", - dependencies: [tsgo], -}); - -export const build = task({ - name: "build", - dependencies: [local], -}); - -export const buildWatch = task({ - name: "build:watch", - description: "Builds the tsc binary and watches for changes.", - run: async () => { - await watchDebounced("build:watch", async (paths, abortSignal) => { - let libsChanged = false; - let goChanged = false; - - if (paths) { - for (const p of paths) { - if (libsRegexp.test(p)) { - libsChanged = true; - } - else if (p.endsWith(".go")) { - goChanged = true; - } - if (libsChanged && goChanged) { - break; - } - } - } - else { - libsChanged = true; - goChanged = true; - } - - if (libsChanged) { - console.log("Generating libs..."); - await generateLibs(builtLocal); - } - - if (goChanged) { - console.log("Building tsgo..."); - await buildTsc({ abortSignal }); - } - }, { - paths: ["tsc/cmd", "tsc/internal"], - ignored: path => /[\\/]testdata[\\/]/.test(path), - }); - }, -}); - -export const cleanBuilt = task({ - name: "clean:built", - hiddenFromTaskList: true, - run: () => rimraf("built"), -}); - -export const generate = task({ - name: "generate", - description: "Runs go generate on the project.", - run: async () => { - await $({ cwd: "./tsc" })`go generate -v ./...`; - }, -}); - -export const generateExtension = task({ - name: "generate:extension", - description: "Generates files in the extension", - run: async () => { - await $`npm run -w native-preview generateLocBundle`; - }, -}); - -// ── Enum generation from Go source ────────────────────────────── - -/** - * @typedef {{ - * name: string; - * goPrefix: string; - * goFile: string; - * outDir: string; - * stringEnum?: boolean; - * excludeMembers?: readonly string[]; - * valueReplacements?: Record; - * }} EnumDef - */ - -/** @type {EnumDef[]} */ -const enumDefs = [ - { name: "SymbolFlags", goPrefix: "SymbolFlags", goFile: "tsc/internal/ast/symbolflags.go", outDir: "packages/typescript/src/enums" }, - { name: "CheckFlags", goPrefix: "CheckFlags", goFile: "tsc/internal/ast/checkflags.go", outDir: "packages/typescript/src/enums" }, - { name: "TypeFlags", goPrefix: "TypeFlags", goFile: "tsc/internal/checker/types.go", outDir: "packages/typescript/src/enums" }, - { name: "ObjectFlags", goPrefix: "ObjectFlags", goFile: "tsc/internal/checker/types.go", outDir: "packages/typescript/src/enums" }, - { name: "SignatureFlags", goPrefix: "SignatureFlags", goFile: "tsc/internal/checker/types.go", outDir: "packages/typescript/src/enums" }, - { name: "SignatureKind", goPrefix: "SignatureKind", goFile: "tsc/internal/checker/types.go", outDir: "packages/typescript/src/enums" }, - { name: "ElementFlags", goPrefix: "ElementFlags", goFile: "tsc/internal/checker/types.go", outDir: "packages/typescript/src/enums" }, - { name: "TypePredicateKind", goPrefix: "TypePredicateKind", goFile: "tsc/internal/checker/types.go", outDir: "packages/typescript/src/enums" }, - { name: "TypeFormatFlags", goPrefix: "TypeFormatFlags", goFile: "tsc/internal/checker/types.go", outDir: "packages/typescript/src/enums" }, - { name: "DiagnosticCategory", goPrefix: "Category", goFile: "tsc/internal/diagnostics/diagnostics.go", outDir: "packages/typescript/src/enums" }, - { name: "SyntaxKind", goPrefix: "Kind", goFile: "tsc/internal/ast/kind_generated.go", outDir: "packages/typescript/src/enums" }, - { name: "NodeFlags", goPrefix: "NodeFlags", goFile: "tsc/internal/ast/nodeflags.go", outDir: "packages/typescript/src/enums" }, - { name: "OuterExpressionKinds", goPrefix: "OEK", goFile: "tsc/internal/ast/utilities.go", outDir: "packages/typescript/src/enums" }, - { name: "ModifierFlags", goPrefix: "ModifierFlags", goFile: "tsc/internal/ast/modifierflags.go", outDir: "packages/typescript/src/enums" }, - { name: "ModuleKind", goPrefix: "ModuleKind", goFile: "tsc/internal/core/compileroptions.go", outDir: "packages/typescript/src/enums" }, - { name: "ModuleResolutionKind", goPrefix: "ModuleResolutionKind", goFile: "tsc/internal/core/compileroptions.go", outDir: "packages/typescript/src/enums" }, - { name: "ModuleDetectionKind", goPrefix: "ModuleDetectionKind", goFile: "tsc/internal/core/compileroptions.go", outDir: "packages/typescript/src/enums" }, - { name: "NewLineKind", goPrefix: "NewLineKind", goFile: "tsc/internal/core/compileroptions.go", outDir: "packages/typescript/src/enums" }, - { name: "JsxEmit", goPrefix: "JsxEmit", goFile: "tsc/internal/core/compileroptions.go", outDir: "packages/typescript/src/enums" }, - { name: "ScriptKind", goPrefix: "ScriptKind", goFile: "tsc/internal/core/scriptkind.go", outDir: "packages/typescript/src/enums" }, - { name: "TokenFlags", goPrefix: "TokenFlags", goFile: "tsc/internal/ast/tokenflags.go", outDir: "packages/typescript/src/enums" }, - { name: "DiagnosticDirectivePolicy", goPrefix: "MappedDiagnosticDirectivePolicy", goFile: "tsc/internal/ast/ast.go", outDir: "packages/typescript/src/enums" }, - { name: "SpanMapKind", goPrefix: "Kind", goFile: "tsc/internal/spanmap/spanmap.go", outDir: "packages/typescript/src/enums" }, - { name: "SpanMapFidelity", goPrefix: "Fidelity", goFile: "tsc/internal/spanmap/spanmap.go", outDir: "packages/typescript/src/enums" }, - { name: "SpanMapFeature", goPrefix: "Feature", goFile: "tsc/internal/spanmap/spanmap.go", outDir: "packages/typescript/src/enums" }, - { name: "NodeBuilderFlags", goPrefix: "Flags", goFile: "tsc/internal/nodebuilder/types.go", outDir: "packages/typescript/src/enums" }, - { name: "CompletionItemKind", goPrefix: "CompletionItemKind", goFile: "tsc/internal/lsp/lsproto/lsp_generated.go", outDir: "packages/typescript/src/enums" }, - { name: "EmitOnly", goPrefix: "Emit", goFile: "tsc/internal/compiler/emitter.go", outDir: "packages/typescript/src/enums", excludeMembers: ["OnlyBuilderSignature"] }, - // String enum: Go stores internal names with a "\xFE" sentinel prefix, but the escaped - // form sent over the wire uses "__" (see EscapeSymbolName), so map the sentinel accordingly. - { name: "InternalSymbolName", goPrefix: "InternalSymbolName", goFile: "tsc/internal/ast/symbol.go", outDir: "packages/typescript/src/enums", stringEnum: true, valueReplacements: { InternalSymbolNamePrefix: "__" } }, -]; - -/** - * @param {string} block - * @param {EnumDef} def - * @returns {{ name: string, value: string }[]} - */ -function parseGoConstBlock(block, def) { - const prefix = def.goPrefix; - const members = []; - let iotaCounter = 0; - let iotaExpression; - - const lines = block.split("\n"); - let i = 0; - - while (i < lines.length) { - const rawLine = lines[i]; - const line = rawLine.replace(/\/\/.*$/, "").trim(); - - if (!line) { - i++; - continue; - } - - // Match: PrefixName Type = value or PrefixName = value - const fullMatch = line.match(new RegExp(`^(${prefix}\\w+)\\s+(?:\\S+\\s*)?=\\s*(.+)$`)); - // Match bare iota continuation: just PrefixName - const bareMatch = !fullMatch && iotaExpression !== undefined - ? line.match(new RegExp(`^(${prefix}\\w+)$`)) - : null; - - if (!fullMatch && !bareMatch) { - i++; - continue; - } - - const goName = fullMatch ? fullMatch[1] : /** @type {RegExpMatchArray} */ (bareMatch)[1]; - let goValue = fullMatch ? fullMatch[2].trim() : ""; - const memberName = goName.slice(prefix.length); - - // Accumulate continuation lines ending with | - i++; - while (i < lines.length && goValue.endsWith("|")) { - const nextRaw = lines[i]; - const nextLine = nextRaw.replace(/\/\/.*$/, "").trim(); - if (!nextLine) { - i++; - continue; - } - goValue += " " + nextLine; - i++; - } - - let tsValue; - if (def.stringEnum) { - tsValue = parseGoStringValue(goValue, def.valueReplacements ?? {}); - } - else if (goValue.includes("iota")) { - iotaExpression = goValue; - tsValue = goValue.replace(/\biota\b/g, String(iotaCounter)); - } - else if (iotaExpression !== undefined && goValue === "") { - tsValue = iotaExpression.replace(/\biota\b/g, String(iotaCounter)); - } - else { - // Replace Go bitwise NOT (^) with TypeScript (~) - tsValue = goValue.replace(/\^/g, "~"); - // Strip enum prefix from member references - tsValue = tsValue.replace(new RegExp(`${prefix}(\\w+)`, "g"), "$1"); - } - - members.push({ name: memberName, value: tsValue }); - iotaCounter++; - } - - return members; -} - -/** - * Resolve a Go string-constant expression (e.g. `Prefix + "call"` or `"export="`) - * into a quoted, JS-escaped TypeScript string literal. `replacements` maps bare - * Go identifiers (such as a sentinel-prefix constant) to their literal value. - * @param {string} goValue - * @param {Record} replacements - * @returns {string} - */ -function parseGoStringValue(goValue, replacements) { - let result = ""; - for (const part of goValue.split("+").map(p => p.trim())) { - if (Object.prototype.hasOwnProperty.call(replacements, part)) { - result += replacements[part]; - continue; - } - const stringMatch = part.match(/^"((?:[^"\\]|\\.)*)"$/); - if (stringMatch === null) { - throw new Error(`Cannot parse string enum value: ${goValue}`); - } - // Interpret Go escape sequences via JSON, then re-stringify below. - result += JSON.parse(`"${stringMatch[1]}"`); - } - return JSON.stringify(result); -} - -/** - * @param {EnumDef} def - * @returns {{ name: string, value: string }[]} - */ -function parseGoEnum(def) { - const source = fs.readFileSync(def.goFile, "utf-8"); - const constBlockRegex = /const\s*\(([\s\S]*?)\n\)/g; - - for (const match of source.matchAll(constBlockRegex)) { - const members = parseGoConstBlock(match[1], def).filter(member => !def.excludeMembers?.includes(member.name)); - if (members.length > 0) return topoSortMembers(members); - } - - throw new Error(`No members found for enum ${def.name} in ${def.goFile}`); -} - -/** - * Topologically sort enum members so composite members appear after - * all members they reference (Go allows forward references, TS does not). - * @param {{ name: string, value: string }[]} members - * @returns {{ name: string, value: string }[]} - */ -function topoSortMembers(members) { - const nameSet = new Set(members.map(m => m.name)); - /** @type {Map>} */ - const deps = new Map(); - for (const m of members) { - /** @type {Set} */ - const refs = new Set(); - // Find all identifier references in the value that are other member names - for (const [ref] of m.value.matchAll(/\b([A-Za-z_]\w*)\b/g)) { - if (ref !== m.name && nameSet.has(ref)) refs.add(ref); - } - deps.set(m.name, refs); - } - - const sorted = /** @type {{ name: string, value: string }[]} */ ([]); - const visited = new Set(); - const visiting = new Set(); - - /** @param {string} name */ - function visit(name) { - if (visited.has(name)) return; - if (visiting.has(name)) return; // cycle — keep original order - visiting.add(name); - for (const dep of deps.get(name) ?? []) { - visit(dep); - } - visiting.delete(name); - visited.add(name); - sorted.push(/** @type {{ name: string, value: string }} */ (members.find(m => m.name === name))); - } - - for (const m of members) { - visit(m.name); - } - return sorted; -} - -/** - * @param {EnumDef} def - * @param {{ name: string, value: string }[]} members - * @returns {string} - */ -function renderEnumTS(def, members) { - const header = `// Code generated by Herebyfile.mjs generate:enums from ${def.goFile}. DO NOT EDIT.\n\n`; - - const lines = members.map(m => ` ${m.name} = ${m.value},`); - return `${header}export enum ${def.name} {\n${lines.join("\n")}\n}\n`; -} - -async function runGenerateEnums() { - const ts = /** @type {typeof import("typescript")} */ (await import("typescript")); - - /** - * @param {string} enumSource - * @param {string} enumName - * @returns {string} - */ - function transpile(enumSource, enumName) { - const result = ts.transpileModule(enumSource, { - compilerOptions: { - module: ts.ModuleKind.ESNext, - target: ts.ScriptTarget.ESNext, - }, - }); - return result.outputText.replace( - `export var ${enumName};`, - `export var ${enumName}: any;`, - ); - } - - console.log("Generating enums from Go source..."); - /** @type {string[]} */ - const generatedFiles = []; - - for (const def of enumDefs) { - const members = parseGoEnum(def); - const camelName = def.name.charAt(0).toLowerCase() + def.name.slice(1); - - fs.mkdirSync(def.outDir, { recursive: true }); - - // Generate .enum.ts (TypeScript enum — used for types) - const enumTS = renderEnumTS(def, members); - const enumPath = path.join(def.outDir, `${camelName}.enum.ts`); - fs.writeFileSync(enumPath, enumTS); - generatedFiles.push(enumPath); - - // Generate .ts (IIFE — used at runtime) - const iifeSource = transpile(enumTS, def.name); - const iifePath = path.join(def.outDir, `${camelName}.ts`); - fs.writeFileSync(iifePath, iifeSource); - generatedFiles.push(iifePath); - - console.log(` ${def.name}: ${members.length} members → ${camelName}.enum.ts, ${camelName}.ts`); - } - - await $`dprint fmt ${generatedFiles}`; - console.log("Done."); -} - -export const generateEnums = task({ - name: "generate:enums", - description: "Generates TypeScript enum files from Go source.", - run: runGenerateEnums, -}); - -export const generateAST = task({ - name: "generate:ast", - description: "Generates AST and encoder files from ast.json.", - run: () => $`node --experimental-strip-types --no-warnings ./tools/scripts/tsc/generate.ts`, -}); - -export const generateAPI = task({ - name: "generate:api", - description: "Generates API files from internal/api/proto.go and internal/api/session.go.", - run: async () => { - await $`go -C ./tools run ./gen-proto ../tsc/internal/api/proto.go ../packages/typescript/src/api/proto.generated.ts`; - await $`npx dprint fmt packages/typescript/src/api/proto.generated.ts`; - }, -}); - -// ── Vendored npm dependencies ─────────────────────────────────── - -const vendorJsonrpcDir = "packages/typescript/vendor/vscode-jsonrpc"; -const vendorJsonrpcSrc = "node_modules/vscode-jsonrpc"; -// Files copied verbatim from the installed vscode-jsonrpc package into the -// vendored copy. Only the runtime files needed by the `#vscode-jsonrpc/node` -// import (lib + typings + package.json) plus license/readme are vendored. -const vendorJsonrpcFiles = ["package.json", "README.md", "License.txt", "lib", "typings"]; - -async function runGenerateVendor() { - const src = path.join(__dirname, vendorJsonrpcSrc); - const dest = path.join(__dirname, vendorJsonrpcDir); - if (!fs.existsSync(src)) { - throw new Error(`${vendorJsonrpcSrc} is not installed; run \`npm ci\` first.`); - } - await rimraf(dest); - await fs.promises.mkdir(dest, { recursive: true }); - for (const file of vendorJsonrpcFiles) { - await cpRecursive(path.join(src, file), path.join(dest, file)); - } -} - -export const generateVendor = task({ - name: "generate:vendor", - description: "Updates the vendored copy of vscode-jsonrpc from node_modules.", - run: runGenerateVendor, -}); - -const coverageDir = path.join(__dirname, "coverage"); - -const ensureCoverageDirExists = memoize(() => { - if (options.coverage) { - fs.mkdirSync(coverageDir, { recursive: true }); - } -}); - -/** - * @param {string} taskName - */ -function goTestFlags(taskName) { - ensureCoverageDirExists(); - return [ - ...goBuildFlags, - ...goBuildTags(), - ...(options.tests ? [`-run=${options.tests}`] : []), - ...(options.coverage ? [`-coverprofile=${path.join(coverageDir, "coverage." + taskName + ".out")}`, "-coverpkg=./..."] : []), - ]; -} - -function getGODEBUG() { - const key = "tracebackancestors"; - const setting = `${key}=10`; - const existing = process.env.GODEBUG ?? ""; - if (!existing) return setting; - if (existing.includes(`${key}=`)) return existing; - return `${existing},${setting}`; -} - -const goTestEnv = { - GODEBUG: getGODEBUG(), - ...(options.concurrentTestPrograms ? { TS_TEST_PROGRAM_SINGLE_THREADED: "false" } : {}), - // Go test caching takes a long time on Windows. - // https://github.com/golang/go/issues/72992 - ...(process.platform === "win32" ? { GOFLAGS: "-count=1" } : {}), -}; - -const baselineTrackingEnabled = false && ![ - options.tests, - options.noembed, - options.concurrentTestPrograms, - options.race, - options.dirty, -].some(Boolean); - -const goTestSumFlags = [ - "--format-hide-empty-pkg", - "--hide-summary", - "skipped", -]; - -/** - * Collects all baseline files that were used during the test run. - * @param {string} trackingDir - * @returns {Promise>} - */ -async function collectUsedBaselines(trackingDir) { - /** @type {Set} */ - const usedBaselines = new Set(); - if (!fs.existsSync(trackingDir)) { - return usedBaselines; - } - - const trackingFiles = await fs.promises.readdir(trackingDir); - for (const file of trackingFiles) { - const content = await fs.promises.readFile(path.join(trackingDir, file), "utf-8"); - for (const line of content.split("\n")) { - const trimmed = line.trim(); - if (trimmed) { - usedBaselines.add(trimmed); - } - } - } - return usedBaselines; -} - -/** - * Checks for unused baseline files and reports them. - * @param {string} trackingDir - * @returns {Promise} List of unused baseline file paths. - */ -async function checkUnusedBaselines(trackingDir) { - const usedBaselines = await collectUsedBaselines(trackingDir); - if (usedBaselines.size === 0) { - // No baselines recorded - either no tests ran or tracking wasn't set up properly - return []; - } - - const allBaselines = await glob(`${refBaseline}/**`, { nodir: true }); - const unusedBaselines = allBaselines - .map(p => path.relative(refBaseline, p)) - .filter(p => !usedBaselines.has(p)); - - return unusedBaselines; -} - -const $test = $({ env: goTestEnv, cwd: "./tsc" }); - -/** - * @param {string} taskName - */ -function gotestsum(taskName) { - const args = isInstalled("gotestsum") ? ["gotestsum", ...goTestSumFlags, "--"] : ["go", "test"]; - return args.concat(goTestFlags(taskName)); -} - -/** - * @param {string} taskName - */ -function goTest(taskName) { - return ["go", "test"].concat(goTestFlags(taskName)); -} - -async function runTests() { - if (!options.dirty) { - await rimraf(localBaseline); - await fs.promises.mkdir(localBaseline, { recursive: true }); - } - - // Create a tmp directory for baseline tracking if enabled - /** @type {string | undefined} */ - let trackingDir; - /** @type {(() => void) | undefined} */ - let cleanupTracking; - - if (baselineTrackingEnabled) { - const tmpDir = tmp.dirSync({ prefix: "tsgo-baseline-tracking-", unsafeCleanup: true }); - trackingDir = tmpDir.name; - cleanupTracking = tmpDir.removeCallback; - } - - try { - const testEnv = { - ...goTestEnv, - ...(trackingDir ? { TSGO_BASELINE_TRACKING_DIR: trackingDir } : {}), - }; - const $testWithTracking = $({ env: testEnv, cwd: "./tsc" }); - await $testWithTracking`${gotestsum("tests")} ./... ${isCI ? ["--timeout=45m"] : []}`; - - // Check for unused baselines after tests complete - if (trackingDir) { - const unusedBaselines = await checkUnusedBaselines(trackingDir); - if (unusedBaselines.length > 0) { - console.error(pc.red(`\nFound ${unusedBaselines.length} unused baseline file(s):`)); - for (const baseline of unusedBaselines.slice(0, 20)) { - console.error(pc.red(` ${baseline}`)); - } - if (unusedBaselines.length > 20) { - console.error(pc.red(` ... and ${unusedBaselines.length - 20} more`)); - } - - // Create .delete files for each unused baseline so baseline-accept can remove them - for (const baseline of unusedBaselines) { - const deleteFilePath = path.join(localBaseline, baseline + ".delete"); - await fs.promises.mkdir(path.dirname(deleteFilePath), { recursive: true }); - await fs.promises.writeFile(deleteFilePath, ""); - } - console.error(pc.red(`\nRun 'hereby baseline-accept' to delete them.`)); - - throw new Error(`Found ${unusedBaselines.length} unused baseline file(s). Run 'hereby baseline-accept' to delete them.`); - } - } - } - finally { - if (cleanupTracking) { - cleanupTracking(); - } - } -} - -async function runTestExtension() { - await $`npm test -w native-preview`; -} - -export const testTsc = task({ - name: "test:tsc", - description: "Runs all tests in the tsc module.", - run: runTests, -}); - -export const test = task({ - name: "test", - description: "Runs all tests. This is the most typical test task to need.", - run: async () => { - await runTests(); - await runTestExtension(); - }, -}); - -async function runTestBenchmarks() { - // Run the benchmarks once to ensure they compile and run without errors. - await $test`${goTest("benchmarks")} -run=- -bench=. -benchtime=1x ./...`; -} - -export const testBenchmarks = task({ - name: "test:benchmarks", - description: "Runs all benchmarks.", - run: runTestBenchmarks, -}); - -async function runTestTools() { - await $test({ cwd: path.join(__dirname, "tools") })`${gotestsum("tools")} ./...`; -} - -async function runTestAPI() { - // await $`npm run -w @typescript/typescript test:only`; // doesn't work on windows - some path escaping isn't done correctly, test runner runs no tests - await _$({ verbose: "short", stdio: "inherit", cwd: "./packages/typescript" })`node --experimental-strip-types --no-warnings --conditions @typescript/source --test ./test/**/*.test.ts`; -} - -export const testTools = task({ - name: "test:tools", - description: "Runs all tests in the tools module.", - run: runTestTools, -}); - -export const testExtension = task({ - name: "test:extension", - description: "Runs the VS Code extension tests.", - run: runTestExtension, -}); - -export const buildAPI = task({ - name: "build:api", - description: "Builds @typescript/typescript JS API.", - run: async () => { - await $`npm run -w @typescript/typescript build`; - }, -}); - -export const buildAPITests = task({ - name: "build:api:test", - description: "Builds the @typescript/typescript JS API tests.", - dependencies: [generateEnums, generateAPI], - run: async () => { - await $`npm run -w @typescript/typescript build:test`; - }, -}); - -export const testAPI = task({ - name: "test:api", - description: "Runs the @typescript/typescript JS API tests.", - dependencies: [tsgo, buildAPITests], - run: runTestAPI, -}); - -export const testAll = task({ - name: "test:all", - description: "Runs ALL tests in the repo, including benchmarks, tools, and the API tests.", - dependencies: [tsgo, buildAPITests], - run: async () => { - // Prevent interleaving by running these directly instead of in parallel. - await runTests(); - await runTestExtension(); - await runTestBenchmarks(); - await runTestTools(); - await runTestAPI(); - }, -}); - -const customLinterPath = "./tools/custom-gcl"; -const customLinterHashPath = customLinterPath + ".hash"; - -const golangciLintPackage = memoize(() => { - const golangciLintYml = fs.readFileSync(".custom-gcl.yml", "utf8"); - const pattern = /^version:\s*(v\d+\.\d+\.\d+).*$/m; - const match = pattern.exec(golangciLintYml); - if (!match) { - throw new Error("Expected version in .custom-gcl.yml"); - } - const version = match[1]; - const major = version.split(".")[0]; - const versionSuffix = ["v0", "v1"].includes(major) ? "" : "/" + major; - - return `github.com/golangci/golangci-lint${versionSuffix}/cmd/golangci-lint@${version}`; -}); - -const customlintHash = memoize(() => { - const files = glob.sync([ - "./tools/go.mod", - "./tools/customlint/**/*", - "./.custom-gcl.yml", - ], { - ignore: "**/testdata/**", - nodir: true, - absolute: true, - }); - files.sort(); - - const hash = crypto.createHash("sha256"); - - for (const file of files) { - hash.update(file); - hash.update(fs.readFileSync(file)); - } - - return hash.digest("hex") + "\n"; -}); - -const buildCustomLinter = memoize(async () => { - const hash = customlintHash(); - if ( - isInstalled(customLinterPath) - && fs.existsSync(customLinterHashPath) - && fs.readFileSync(customLinterHashPath, "utf8") === hash - ) { - return; - } - - await $`go run ${golangciLintPackage()} custom`; - await $`${customLinterPath} cache clean`; - - fs.writeFileSync(customLinterHashPath, hash); -}); - -export const lint = task({ - name: "lint", - description: "Runs golangci-lint.", - run: runLint, -}); - -async function runLint() { - await buildCustomLinter(); - - const lintArgs = ["run"]; - if (defaultGoBuildTags.length) { - lintArgs.push("--build-tags", defaultGoBuildTags.join(",")); - } - if (options.fix) { - lintArgs.push("--fix"); - } - - const resolvedCustomLinterPath = path.resolve(customLinterPath); - await $({ cwd: "./tsc" })`${resolvedCustomLinterPath} ${lintArgs} --config ../.golangci.yml`; - console.log("Linting tools"); - await $({ cwd: "./tools" })`${resolvedCustomLinterPath} ${lintArgs} --config ../.golangci.yml`; -} - -export const installTools = task({ - name: "install-tools", - description: "Installs optional tools for developing within the repo.", - run: async () => { - await Promise.all([ - ...[...tools].map(([tool, version]) => $`go install ${tool}${version ? `@${version}` : ""}`), - buildCustomLinter(), - ]); - }, -}); - -export const format = task({ - name: "format", - description: "Formats the repo.", - run: runFormat, -}); - -async function runFormat() { - await $`dprint fmt`; -} - -export const checkFormat = task({ - name: "check:format", - description: "Checks that the repo is formatted.", - run: async () => { - await $`dprint check`; - }, -}); - -const scriptTsconfigs = [ - "./tools/scripts/tsc/tsconfig.json", - "./tsc/internal/lsp/lsproto/_generate/tsconfig.json", -]; - -export const checkScripts = task({ - name: "check:scripts", - description: "Type-checks TypeScript scripts.", - run: async () => { - for (const tsconfig of scriptTsconfigs) { - console.log(`Type-checking ${tsconfig}`); - await $`tsc -p ${tsconfig}`; - } - }, -}); - -/** - * @param {string} localBaseline Path to the local copy of the baselines - * @param {string} refBaseline Path to the reference copy of the baselines - */ -function baselineAcceptTask(localBaseline, refBaseline) { - /** - * @param {string} p - */ - function localPathToRefPath(p) { - const relative = path.relative(localBaseline, p); - return path.join(refBaseline, relative); - } - - return async () => { - const toCopy = await glob(`${localBaseline}/**`, { nodir: true, ignore: `${localBaseline}/**/*.delete` }); - for (const p of toCopy) { - const out = localPathToRefPath(p); - await fs.promises.mkdir(path.dirname(out), { recursive: true }); - await fs.promises.copyFile(p, out); - } - const toDelete = await glob(`${localBaseline}/**/*.delete`, { nodir: true }); - for (const p of toDelete) { - const out = localPathToRefPath(p).replace(/\.delete$/, ""); - await rimraf(out); - await rimraf(p); // also delete the .delete file so that it no longer shows up in a diff tool. - } - }; -} - -const localBaseline = "tsc/testdata/baselines/local/"; -const refBaseline = "tsc/testdata/baselines/reference/"; - -export const baselineAccept = task({ - name: "baseline-accept", - description: "Makes the most recent test results the new baseline, overwriting the old baseline.", - run: baselineAcceptTask(localBaseline, refBaseline), -}); - -function getDiffTool() { - const program = process.env.DIFF; - if (!program) { - console.warn("Add the 'DIFF' environment variable to the path of the program you want to use."); - process.exit(1); - } - return program; -} - -export const diff = task({ - name: "diff", - description: "Diffs baselines using the diff tool specified by the 'DIFF' environment variable", - run: () => $`${getDiffTool()} ${refBaseline} ${localBaseline}`, -}); - -/** - * @param {fs.PathLike} p - */ -function rimraf(p) { - // The rimraf package uses maxRetries=10 on Windows, but Node's fs.rm does not have that special case. - return fs.promises.rm(p, { recursive: true, force: true, maxRetries: process.platform === "win32" ? 10 : 0 }); -} - -/** @typedef {{ - * name: string; - * paths: string | string[]; - * ignored?: (path: string) => boolean; - * run: (paths: Set, abortSignal: AbortSignal) => void | Promise; - * }} WatchTask */ -void 0; - -/** - * @param {string} name - * @param {(paths: Set | undefined, abortSignal: AbortSignal) => void | Promise} run - * @param {object} options - * @param {string | string[]} options.paths - * @param {(path: string) => boolean} [options.ignored] - * @param {string} [options.name] - */ -async function watchDebounced(name, run, options) { - let watching = true; - let running = true; - let lastChangeTimeMs = Date.now(); - let changedDeferred = /** @type {Deferred} */ (new Deferred()); - let abortController = new AbortController(); - - const debouncer = new Debouncer(1_000, endRun); - const watcher = chokidar.watch(options.paths, { - ignored: options.ignored, - ignorePermissionErrors: true, - alwaysStat: true, - }); - // The paths that have changed since the last run. - /** @type {Set | undefined} */ - let paths; - - process.on("SIGINT", endWatchMode); - process.on("beforeExit", endWatchMode); - watcher.on("all", onChange); - - while (watching) { - const promise = changedDeferred.promise; - const token = abortController.signal; - if (!token.aborted) { - running = true; - try { - const thePaths = paths; - paths = new Set(); - await run(thePaths, token); - } - catch { - // ignore - } - running = false; - } - if (watching) { - console.log(pc.yellowBright(`[${name}] run complete, waiting for changes...`)); - await promise; - } - } - - console.log("end"); - - /** - * @param {'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir' | 'all' | 'ready' | 'raw' | 'error'} eventName - * @param {string} path - * @param {fs.Stats | undefined} stats - */ - function onChange(eventName, path, stats) { - switch (eventName) { - case "change": - case "unlink": - case "unlinkDir": - break; - case "add": - case "addDir": - // skip files that are detected as 'add' but haven't actually changed since the last time we ran. - if (stats && stats.mtimeMs <= lastChangeTimeMs) { - return; - } - break; - } - beginRun(path); - } - - /** - * @param {string} path - */ - function beginRun(path) { - if (debouncer.empty) { - console.log(pc.yellowBright(`[${name}] changed due to '${path}', restarting...`)); - if (running) { - console.log(pc.yellowBright(`[${name}] aborting in-progress run...`)); - } - abortController.abort(); - abortController = new AbortController(); - } - - debouncer.enqueue(); - paths ??= new Set(); - paths.add(path); - } - - function endRun() { - lastChangeTimeMs = Date.now(); - changedDeferred.resolve(); - changedDeferred = /** @type {Deferred} */ (new Deferred()); - } - - function endWatchMode() { - if (watching) { - watching = false; - console.log(pc.yellowBright(`[${name}] exiting watch mode...`)); - abortController.abort(); - watcher.close(); - } - } -} - -/** - * @template T - */ -export class Deferred { - constructor() { - /** @type {Promise} */ - this.promise = new Promise((resolve, reject) => { - this.resolve = resolve; - this.reject = reject; - }); - } -} - -export class Debouncer { - /** - * @param {number} timeout - * @param {() => Promise | void} action - */ - constructor(timeout, action) { - this._timeout = timeout; - this._action = action; - /** @type {ReturnType | undefined} */ - this._timer = undefined; - /** @type {Deferred | undefined} */ - this._deferred = undefined; - } - - get empty() { - return !this._deferred; - } - - enqueue() { - if (this._timer) { - clearTimeout(this._timer); - this._timer = undefined; - } - - if (!this._deferred) { - this._deferred = new Deferred(); - } - - this._timer = setTimeout(() => this.run(), 100); - return this._deferred.promise; - } - - run() { - if (this._timer) { - clearTimeout(this._timer); - this._timer = undefined; - } - - const deferred = this._deferred; - assert(deferred); - this._deferred = undefined; - try { - deferred.resolve(this._action()); - } - catch (e) { - deferred.reject(e); - } - } -} - -const getVersion = memoize(() => { - if (nativePreviewReleaseVersion) { - return nativePreviewReleaseVersion; - } - - const f = fs.readFileSync("./tsc/internal/core/version.go", "utf8"); - - const match = f.match(/var version\s*=\s*"(\d+\.\d+\.\d+)(-[^"]+)?"/); - if (!match) { - throw new Error("Failed to extract version from version.go"); - } - - let version = match[1]; - if (options.setPrerelease) { - version += `-${options.setPrerelease}`; - } - else if (match[2]) { - version += match[2]; - } - - return version; -}); - -function getPublishTag() { - if (publishAsTypescript) { - const version = getVersion(); - if (!version) { - throw new Error("Publishing as 'typescript' requires a version before selecting an npm tag."); - } - const match = version.match(/-(dev|beta|rc)(?:[.-]|$)/); - if (match?.[1]) return match[1] === "dev" ? "next" : match[1]; - if (version === nativePreviewReleaseVersion) return "latest"; - throw new Error(`Refusing to publish 'typescript' with the latest tag from non-release version ${version}.`); - } - return "latest"; -} - -const extensionDir = path.resolve("./packages/vscode-typescript"); -const nightlyExtensionDir = path.resolve("./packages/vscode-typescript-nightly"); -const builtNpm = path.resolve("./built/npm"); -const builtVsix = path.resolve("./built/vsix"); -const builtPublishedPlatformPackages = path.resolve("./built/published-platform-packages"); -const builtSignTmp = path.resolve("./built/sign-tmp"); -const publishedTypeScriptAliasPackageName = "@typescript/bundled-typescript"; -const releasePackageEnv = { COREPACK_ENABLE_STRICT: "0" }; - -const getSignTempDir = memoize(async () => { - const dir = path.resolve(builtSignTmp); - await rimraf(dir); - await fs.promises.mkdir(dir, { recursive: true }); - return dir; -}); - -const cleanSignTempDirectory = task({ - name: "clean:sign-tmp", - hiddenFromTaskList: true, - run: runCleanSignTempDirectory, -}); - -function runCleanSignTempDirectory() { - return rimraf(builtSignTmp); -} - -let signCount = 0; - -/** - * @typedef {{ - * SignFileRecordList: { - * SignFileList: { SrcPath: string; DstPath: string | null }[]; - * Certs: Cert; - * MacAppName: string | undefined - * }[] - * }} DDSignFileList - * - * @param {DDSignFileList} filelist - */ -async function sign(filelist, unchangedOutputOkay = false) { - let data = JSON.stringify(filelist, undefined, 4); - console.log("filelist:", data); - - if (!process.env.MBSIGN_APPFOLDER) { - console.log(pc.yellow("Faking signing because MBSIGN_APPFOLDER is not set.")); - - // Fake signing for testing. - - for (const record of filelist.SignFileRecordList) { - for (const file of record.SignFileList) { - const src = file.SrcPath; - const dst = file.DstPath ?? src; - - if (!fs.existsSync(src)) { - throw new Error(`Source file does not exist: ${src}`); - } - - const dstDir = path.dirname(dst); - if (!fs.existsSync(dstDir)) { - throw new Error(`Destination directory does not exist: ${dstDir}`); - } - - if (dst.endsWith(".sig")) { - console.log(`Faking signature for ${src} -> ${dst}`); - // No great way to fake a signature. - await fs.promises.writeFile(dst, "fake signature"); - } - else { - if (src === dst) { - console.log(`Faking signing ${src}`); - } - else { - console.log(`Faking signing ${src} -> ${dst}`); - } - const contents = await fs.promises.readFile(src); - await fs.promises.writeFile(dst, contents); - } - } - } - - return; - } - - const signingWorkaround = true; - - /** @type {{ source: string; target: string }[]} */ - const signingWorkaroundFiles = []; - - if (signingWorkaround) { - // DstPath is currently broken in the signing tool. - // Copy all of the files to a new tempdir and then leave DstPath unset - // so that it's overwritten, then move the file to the destination. - console.log("Working around DstPath bug"); - - /** @type {DDSignFileList} */ - const newFileList = { - SignFileRecordList: filelist.SignFileRecordList.map(list => { - return { - Certs: list.Certs, - SignFileList: list.SignFileList.map(file => { - const dstPath = file.DstPath; - if (dstPath === null) { - return file; - } - - const src = file.SrcPath; - // File extensions must be preserved; use a prefix. - const dstPathTemp = `${path.dirname(src)}/signing-temp-${path.basename(src)}`; - - console.log(`Copying: ${src} -> ${dstPathTemp}`); - fs.cpSync(src, dstPathTemp); - - signingWorkaroundFiles.push({ source: dstPathTemp, target: dstPath }); - - return { - SrcPath: dstPathTemp, - DstPath: null, - }; - }), - MacAppName: list.MacAppName, - }; - }), - }; - - data = JSON.stringify(newFileList, undefined, 4); - console.log("new filelist:", data); - } - - /** @type {Map} */ - const srcHashes = new Map(); - - for (const record of filelist.SignFileRecordList) { - for (const file of record.SignFileList) { - const src = file.SrcPath; - const dst = file.DstPath ?? src; - - if (!fs.existsSync(src)) { - throw new Error(`Source file does not exist: ${src}`); - } - - const hash = crypto.createHash("sha256").update(fs.readFileSync(src)).digest("hex"); - srcHashes.set(src, hash); - - console.log(`Will sign ${src} -> ${dst}`); - console.log(` sha256: ${hash}`); - } - } - - const tmp = await getSignTempDir(); - const filelistPath = path.resolve(tmp, `signing-filelist-${signCount++}.json`); - await fs.promises.writeFile(filelistPath, data); - - try { - const dll = path.join(process.env.MBSIGN_APPFOLDER, "DDSignFiles.dll"); - const filelistFlag = `/filelist:${filelistPath}`; - await $`dotnet ${dll} -- ${filelistFlag}`; - } - finally { - await fs.promises.unlink(filelistPath); - } - - if (signingWorkaround) { - // Now, copy the files back. - for (const { source, target } of signingWorkaroundFiles) { - console.log(`Moving signed file: ${source} -> ${target}`); - await fs.promises.rename(source, target); - } - } - - /** @type {string[]} */ - let failures = []; - - for (const record of filelist.SignFileRecordList) { - for (const file of record.SignFileList) { - const src = file.SrcPath; - const dst = file.DstPath ?? src; - - if (!fs.existsSync(dst)) { - failures.push(`Signed file does not exist: ${dst}`); - const newSrcHash = crypto.createHash("sha256").update(fs.readFileSync(src)).digest("hex"); - const oldSrcHash = srcHashes.get(src); - assert(oldSrcHash); - if (oldSrcHash !== newSrcHash) { - failures.push(` Source file changed during signing: ${src}\n before: ${oldSrcHash}\n after: ${newSrcHash}`); - } - continue; - } - - const srcHash = srcHashes.get(src); - assert(srcHash); - const dstHash = crypto.createHash("sha256").update(fs.readFileSync(dst)).digest("hex"); - if (srcHash === dstHash) { - const message = `Signed file is identical to source file (not signed?): ${src} -> ${dst}\n sha256: ${dstHash}`; - if (unchangedOutputOkay) { - console.log(message); - } - else { - failures.push(message); - continue; - } - } - - if (src === dst) { - console.log(`Signed ${src}`); - } - else { - console.log(`Signed ${src} -> ${dst}`); - } - console.log(` sha256: ${dstHash}`); - } - } - - if (failures.length) { - throw new Error("Some files failed to sign:\n" + failures.map(f => " - " + f).join("\n")); - } -} - -/** - * @param {string} src - * @param {string} dest - * @param {(p: string) => boolean} [filter] - */ -function cpRecursive(src, dest, filter) { - return fs.promises.cp(src, dest, { - recursive: true, - filter: filter ? src => filter(src.replace(/\\/g, "/")) : undefined, - }); -} - -/** - * @param {string} src - * @param {string} dest - */ -function cpWithoutNodeModulesOrTsconfig(src, dest) { - return cpRecursive(src, dest, p => !p.endsWith("/node_modules") && !p.endsWith("/tsconfig.json")); -} - -const mainNativePreviewPackage = { - npmPackageName: publishAsTypescript ? "typescript" : "@typescript/typescript", - npmDir: path.join(builtNpm, publishAsTypescript ? "typescript" : "native-preview"), - npmTarball: path.join(builtNpm, publishAsTypescript ? "typescript.tgz" : "native-preview.tgz"), -}; - -const typescriptMacEntitlements = [ - "com.apple.security.cs.allow-dyld-environment-variables", - "com.apple.security.cs.disable-library-validation", -]; - -function createTypeScriptMacEntitlementsPlist() { - const entries = typescriptMacEntitlements.map(entitlement => ` ${entitlement}\n `).join("\n"); - return ` - - - -${entries} - - -`; -} - -/** - * @typedef {"win32" | "linux" | "darwin" | "aix" | "android" | "freebsd" | "netbsd" | "openbsd" | "sunos"} OS - * @typedef {"x64" | "arm" | "arm64" | "ia32" | "ppc64" | "loong64" | "mips64el" | "riscv64" | "s390x"} Arch - * @typedef {"Microsoft400" | "LinuxSign" | "MacDeveloperHarden" | "8020" | "VSCodePublisher"} Cert - * @typedef {`${OS | "alpine"}-${Exclude | "armhf"}`} VSCodeTarget - * @typedef {{ name: string; sourceDir: string }} VsixExtensionPackage - * @typedef {{ nodeOs: string; vscodeTarget: string; sourceDir: string; extensionDir: string; vsixPath: string; vsixManifestPath: string; vsixSignaturePath: string }} VsixExtension - * @typedef {{ GOOS: string; GOARCH: string }} GoDistTarget - * @typedef {{ os: OS; arch: Arch; cert?: Cert; vsix?: boolean; alpine?: boolean }} Platform - */ -void 0; - -/** @type {VsixExtensionPackage[]} */ -const vsixExtensionPackages = [ - ...(produceNativePreviewVsix ? [{ name: "native-preview", sourceDir: extensionDir }] : []), - ...(produceTypeScriptNightlyVsix ? [{ name: "vscode-typescript-nightly", sourceDir: nightlyExtensionDir }] : []), -]; - -/** - * npm package platforms supported by the native release. - * The native-preview package publishes only the entries with vsix: true; - * the typescript package publishes the full list. - * BSD targets that are not in Node's supported-platforms table are best-effort - * and limited to mainstream 64-bit x64/arm64 architectures. - * alpine is set only for the subset that also produces Alpine VSIXes. - * cert defaults to LinuxSign. - * @type {Platform[]} - */ -const platforms = [ - { os: "win32", arch: "x64", vsix: true, cert: "Microsoft400" }, - { os: "win32", arch: "arm64", vsix: true, cert: "Microsoft400" }, - { os: "linux", arch: "x64", vsix: true, alpine: true }, - { os: "linux", arch: "arm", vsix: true }, - { os: "linux", arch: "arm64", vsix: true, alpine: true }, - { os: "darwin", arch: "x64", vsix: true, cert: "MacDeveloperHarden" }, - { os: "darwin", arch: "arm64", vsix: true, cert: "MacDeveloperHarden" }, - { os: "aix", arch: "ppc64" }, - { os: "android", arch: "arm64" }, - { os: "freebsd", arch: "arm64" }, - { os: "freebsd", arch: "x64" }, - { os: "linux", arch: "loong64" }, - { os: "linux", arch: "mips64el" }, - { os: "linux", arch: "ppc64" }, - { os: "linux", arch: "riscv64" }, - { os: "linux", arch: "s390x" }, - { os: "netbsd", arch: "arm64" }, - { os: "netbsd", arch: "x64" }, - { os: "openbsd", arch: "arm64" }, - { os: "openbsd", arch: "x64" }, - { os: "sunos", arch: "x64" }, - // Wasm? -]; - -const ignoredGoTargets = new Map([ - ["android/386", "Android is not a Node runtime target TypeScript supports"], - ["android/amd64", "Android is not a Node runtime target TypeScript supports"], - ["android/arm", "Android is not a Node runtime target TypeScript supports"], - ["freebsd/386", "FreeBSD is experimental in Node and limited here to mainstream 64-bit x64/arm64"], - ["freebsd/arm", "FreeBSD is experimental in Node and limited here to mainstream 64-bit x64/arm64"], - ["linux/386", "ia32 means 32-bit x86, which TypeScript does not support for native packages"], - ["linux/ppc64", "Node supports Linux ppc64le; npm's ppc64 CPU name cannot select big-endian ppc64 separately"], - ["netbsd/386", "NetBSD is not in Node's supported-platforms table and is limited here to mainstream 64-bit x64/arm64"], - ["netbsd/arm", "NetBSD is not in Node's supported-platforms table and is limited here to mainstream 64-bit x64/arm64"], - ["openbsd/386", "OpenBSD is not in Node's supported-platforms table and is limited here to mainstream 64-bit x64/arm64"], - ["openbsd/arm", "OpenBSD is not in Node's supported-platforms table and is limited here to mainstream 64-bit x64/arm64"], - ["openbsd/ppc64", "OpenBSD is not in Node's supported-platforms table and is limited here to mainstream 64-bit x64/arm64"], - ["openbsd/riscv64", "OpenBSD is not in Node's supported-platforms table and is limited here to mainstream 64-bit x64/arm64"], - ["solaris/amd64", "Node documents SmartOS/sunos rather than Oracle Solaris; sunos-x64 publishes illumos/amd64 for that runtime family"], - ["windows/386", "ia32 means 32-bit x86, which TypeScript does not support for native packages"], -]); - -/** - * @param {string} os - * @returns {"windows" | "illumos" | "darwin" | "linux" | "aix" | "android" | "freebsd" | "netbsd" | "openbsd"} - */ -function nodeToGOOS(os) { - switch (os) { - case "win32": - return "windows"; - case "sunos": - return "illumos"; - case "darwin": - case "linux": - case "aix": - case "android": - case "freebsd": - case "netbsd": - case "openbsd": - return os; - default: - throw new Error(`Unsupported OS: ${os}`); - } -} - -/** - * @param {string} arch - * @param {string} os - * @returns {"amd64" | "386" | "mips64le" | "ppc64" | "ppc64le" | "arm" | "arm64" | "loong64" | "riscv64" | "s390x"} - */ -function nodeToGOARCH(arch, os) { - switch (arch) { - case "x64": - return "amd64"; - case "ia32": - return "386"; - case "mips64el": - return "mips64le"; - case "ppc64": - return os === "aix" ? "ppc64" : "ppc64le"; - case "arm": - case "arm64": - case "loong64": - case "riscv64": - case "s390x": - return arch; - default: - throw new Error(`Unsupported ARCH: ${arch}`); - } -} - -const getPlatforms = memoize(() => { - const publishTag = getPublishTag(); - let supportedPlatforms = publishAsTypescript && publishTag !== "next" - ? platforms - : platforms.filter(({ vsix }) => vsix); - - if (!options.forRelease) { - supportedPlatforms = supportedPlatforms.filter(({ os, arch }) => os === process.platform && arch === process.arch); - assert.equal(supportedPlatforms.length, 1, "No supported platforms found"); - } - - return supportedPlatforms.map(({ os, arch, cert = "LinuxSign", vsix, alpine }) => { - const packageBaseName = publishAsTypescript ? "typescript" : "native-preview"; - const npmDirName = `${packageBaseName}-${os}-${arch}`; - const npmDir = path.join(builtNpm, npmDirName); - const npmTarball = `${npmDir}.tgz`; - const npmPackageName = `@typescript/${npmDirName}`; - - /** @type {VsixExtension[]} */ - let extensions = []; - if (produceAnyVsix && vsix) { - /** @type {string[]} */ - const vscodeTargets = [`${os}-${arch === "arm" ? "armhf" : arch}`]; - if (alpine) { - vscodeTargets.push(`alpine-${arch === "arm" ? "armhf" : arch}`); - } - - extensions = vscodeTargets.flatMap(vscodeTarget => - vsixExtensionPackages.map(({ name: packageName, sourceDir }) => { - const extensionDir = path.join(builtVsix, `${packageName}-${vscodeTarget}`); - const vsixPath = extensionDir + ".vsix"; - const vsixManifestPath = extensionDir + ".manifest"; - const vsixSignaturePath = extensionDir + ".signature.p7s"; - return { - nodeOs: os, - vscodeTarget, - sourceDir, - extensionDir, - vsixPath, - vsixManifestPath, - vsixSignaturePath, - }; - }) - ); - } - - return { - nodeOs: os, - nodeArch: arch, - goos: nodeToGOOS(os), - goarch: nodeToGOARCH(arch, os), - npmPackageName, - npmDirName, - npmDir, - npmTarball, - extensions, - cert, - }; - }); -}); - -export const checkPlatforms = task({ - name: "typescript:check-platforms", - hiddenFromTaskList: true, - run: runCheckPlatforms, -}); - -/** - * @param {GoDistTarget} target - */ -function goDistTargetToPlatform(target) { - const goTarget = `${target.GOOS}/${target.GOARCH}`; - if (ignoredGoTargets.has(goTarget)) { - return undefined; - } - - /** @type {OS | undefined} */ - let nodeOs; - switch (target.GOOS) { - case "windows": - nodeOs = "win32"; - break; - case "illumos": - nodeOs = "sunos"; - break; - case "aix": - case "android": - case "darwin": - case "freebsd": - case "linux": - case "netbsd": - case "openbsd": - nodeOs = target.GOOS; - break; - default: - return undefined; - } - - /** @type {Arch | undefined} */ - let nodeArch; - switch (target.GOARCH) { - case "386": - nodeArch = "ia32"; - break; - case "amd64": - nodeArch = "x64"; - break; - case "mips64le": - nodeArch = "mips64el"; - break; - case "ppc64": - nodeArch = "ppc64"; - break; - case "ppc64le": - nodeArch = "ppc64"; - break; - case "arm": - case "arm64": - case "loong64": - case "riscv64": - case "s390x": - nodeArch = target.GOARCH; - break; - default: - return undefined; - } - - return `${nodeOs}-${nodeArch}`; -} - -async function runCheckPlatforms() { - const { stdout } = await $pipe`go tool dist list -json`; - /** @type {GoDistTarget[]} */ - const goTargets = JSON.parse(stdout); - const goTargetSet = new Set(goTargets.map(({ GOOS, GOARCH }) => `${GOOS}/${GOARCH}`)); - - /** @type {[os: OS, arch: Arch][]} */ - const packagePlatforms = platforms.map(({ os, arch }) => /** @type {[OS, Arch]} */ ([os, arch])); - const actual = new Set(packagePlatforms.map(([os, arch]) => `${os}-${arch}`)); - const expected = new Set(goTargets.map(goDistTargetToPlatform).filter(platform => platform !== undefined)); - - const errors = []; - for (const [os, arch] of packagePlatforms) { - const goTarget = `${nodeToGOOS(os)}/${nodeToGOARCH(arch, os)}`; - if (!goTargetSet.has(goTarget)) { - errors.push(`Configured package platform ${os}-${arch} maps to unsupported Go target ${goTarget}.`); - } - } - - const missing = [...expected].filter(platform => !actual.has(platform)); - if (missing.length) { - errors.push(`Missing package platform(s) for the current Go toolchain: ${missing.join(", ")}.`); - } - - const extra = [...actual].filter(platform => !expected.has(platform)); - if (extra.length) { - errors.push(`Unexpected package platform(s), or missing exclusion policy: ${extra.join(", ")}.`); - } - - if (errors.length) { - throw new Error(`native-preview platform list is out of sync with 'go tool dist list':\n${errors.map(e => ` - ${e}`).join("\n")}`); - } -} - -/** - * Recursively strips `@typescript/source` export conditions from a package.json object. - * Processes `exports` and `imports` fields, skipping past subpath keys (starting with "." - * or "#") and recursing into condition objects. After removal, simplifies objects that have - * only a single `default` key down to their bare value. - * @param {Record} packageJson - */ -function stripSourceConditions(packageJson) { - for (const field of ["exports", "imports"]) { - if (packageJson[field] != null && typeof packageJson[field] === "object") { - packageJson[field] = stripConditionsFromValue(packageJson[field]); - } - } -} - -/** - * @param {any} value - * @returns {any} - */ -function stripConditionsFromValue(value) { - if (value == null || typeof value !== "object") { - return value; - } - delete value["@typescript/source"]; - for (const key of Object.keys(value)) { - value[key] = stripConditionsFromValue(value[key]); - } - // Simplify: if only "default" remains, collapse to its value. - const keys = Object.keys(value); - if (keys.length === 1 && keys[0] === "default") { - return value["default"]; - } - return value; -} - -export const buildNativePreviewPackages = task({ - name: "typescript:build", - hiddenFromTaskList: true, - run: runBuildNativePreviewPackages, -}); - -async function runBuildNativePreviewPackages() { - if (usePublishedPlatformPackagesForVsix) { - checkPublishedPlatformPackagesForVsix(); - await rimraf(builtNpm); - console.log("Skipping npm package builds; VSIX packaging will use published platform packages."); - return; - } - - await rimraf(builtNpm); - - const platforms = getPlatforms(); - - const inputDir = "./packages/typescript"; - - const inputPackageJson = JSON.parse(fs.readFileSync(path.join(inputDir, "package.json"), "utf8")); - inputPackageJson.version = getVersion(); - delete inputPackageJson.private; - inputPackageJson.files = [...new Set([...(inputPackageJson.files ?? []), "NOTICE.txt"])]; - if (publishAsTypescript) { - inputPackageJson.bin = { - tsc: "./bin/tsc", - }; - inputPackageJson.description = "TypeScript is a language for application scale JavaScript development"; - inputPackageJson.homepage = "https://www.typescriptlang.org/"; - inputPackageJson.keywords = [ - "TypeScript", - "Microsoft", - "compiler", - "language", - "javascript", - ]; - inputPackageJson.bugs = { - url: "https://github.com/microsoft/TypeScript/issues", - }; - inputPackageJson.repository = { - type: "git", - url: "https://github.com/microsoft/TypeScript.git", - }; - delete inputPackageJson.scripts; - delete inputPackageJson.devDependencies; - } - stripSourceConditions(inputPackageJson); - - const { stdout: gitHead } = await $pipe`git rev-parse HEAD`; - inputPackageJson.gitHead = gitHead; - inputPackageJson.publishConfig = { - access: "public", - tag: getPublishTag(), - }; - - const mainPackage = { - ...inputPackageJson, - name: mainNativePreviewPackage.npmPackageName, - optionalDependencies: Object.fromEntries(platforms.map(p => [p.npmPackageName, getVersion()])), - }; - - const mainPackageDir = mainNativePreviewPackage.npmDir; - - await fs.promises.mkdir(mainPackageDir, { recursive: true }); - - // Copy package contents excluding node_modules and dist (dist is copied separately after build). - // The package.json "files" field controls what npm pack actually includes. - await cpRecursive(inputDir, mainPackageDir, p => !p.endsWith("/node_modules") && !p.includes("/dist")); - if (publishAsTypescript) { - await fs.promises.writeFile(path.join(mainPackageDir, "bin", "tsc"), '#!/usr/bin/env node\nimport "../lib/tsc.js";\n'); - await fs.promises.chmod(path.join(mainPackageDir, "bin", "tsc"), 0o755); - await fs.promises.copyFile(path.join(inputDir, "typescript-package-readme.md"), path.join(mainPackageDir, "README.md")); - } - - await fs.promises.writeFile(path.join(mainPackageDir, "package.json"), JSON.stringify(mainPackage, undefined, 4)); - await fs.promises.copyFile("LICENSE.txt", path.join(mainPackageDir, "LICENSE")); - await fs.promises.copyFile("NOTICE.txt", path.join(mainPackageDir, "NOTICE.txt")); - - // Build JS API and copy dist into the package. - await $`npm run -w @typescript/typescript build`; - await cpRecursive(path.join(inputDir, "dist"), path.join(mainPackageDir, "dist")); - - // Validate that .d.ts files contain no external imports (all imports must start with "." or "#"). - const dtsFiles = await glob(`${mainPackageDir}/dist/**/*.d.ts`); - const importErrors = []; - for (const dtsFile of dtsFiles) { - const content = await fs.promises.readFile(dtsFile, "utf-8"); - const relPath = path.relative(mainPackageDir, dtsFile); - for (const [i, line] of content.split("\n").entries()) { - // Match: import ... from "specifier" / export ... from "specifier" - const fromMatch = line.match(/(?:import|export)\s.*?\sfrom\s+["']([^"']+)["']/); - if (fromMatch && !fromMatch[1].startsWith(".") && !fromMatch[1].startsWith("#")) { - importErrors.push(`${relPath}:${i + 1}: external import declaration "${fromMatch[1]}"`); - } - // Match: import("specifier") - for (const m of line.matchAll(/import\(["']([^"']+)["']\)/g)) { - if (!m[1].startsWith(".") && !m[1].startsWith("#")) { - importErrors.push(`${relPath}:${i + 1}: external dynamic import "${m[1]}"`); - } - } - } - } - if (importErrors.length) { - throw new Error(`Found external imports in .d.ts files:\n${importErrors.map(e => " " + e).join("\n")}`); - } - - const extraFlags = getReleaseBuildFlags(options.setPrerelease || nativePreviewReleaseVersion ? getVersion() : undefined); - - const platformBuilders = platforms.map(({ npmDir, npmPackageName, nodeOs, nodeArch, goos, goarch }) => async () => { - const packageJson = { - ...inputPackageJson, - bin: undefined, - files: ["lib", "NOTICE.txt"], - imports: undefined, - dependencies: undefined, - name: npmPackageName, - os: [nodeOs], - cpu: [nodeArch], - exports: { - "./package.json": "./package.json", - }, - }; - - const out = path.join(npmDir, "lib"); - await fs.promises.mkdir(out, { recursive: true }); - await fs.promises.writeFile(path.join(npmDir, "package.json"), JSON.stringify(packageJson, undefined, 4)); - await fs.promises.copyFile("LICENSE.txt", path.join(npmDir, "LICENSE")); - await fs.promises.copyFile("NOTICE.txt", path.join(npmDir, "NOTICE.txt")); - - const readme = [ - `# \`${npmPackageName}\``, - "", - `This package provides ${nodeOs}-${nodeArch} support for [${mainNativePreviewPackage.npmPackageName}](https://www.npmjs.com/package/${mainNativePreviewPackage.npmPackageName}).`, - ]; - - await fs.promises.writeFile(path.join(npmDir, "README.md"), readme.join("\n") + "\n"); - - await generateLibs(out); - - const exeName = nativePreviewExeName(nodeOs); - await buildTsc({ - out: publishAsTypescript ? path.join(out, exeName) : out, - env: { GOOS: goos, GOARCH: goarch, GOARM: "6", CGO_ENABLED: "0" }, - extraFlags, - }); - }); - - if (isCI) { - for (const build of platformBuilders) { - await build(); - // Build machines have too little space. - // Clear the Go build cache between platforms. - await $`go clean -cache`; - } - } - else { - const buildLimit = pLimit(os.availableParallelism()); - await Promise.all(platformBuilders.map(f => buildLimit(f))); - } -} - -export const signNativePreviewPackages = task({ - name: "typescript:sign", - hiddenFromTaskList: true, - run: runSignNativePreviewPackages, -}); - -/** - * @param {string} nodeOs - */ -function nativePreviewExeName(nodeOs) { - const baseName = publishAsTypescript ? "tsc" : "tsgo"; - return nodeOs === "win32" ? `${baseName}.exe` : baseName; -} - -async function runSignNativePreviewPackages() { - if (!options.forRelease) { - throw new Error("This task should not be run in non-release builds."); - } - if (usePublishedPlatformPackagesForVsix) { - checkPublishedPlatformPackagesForVsix(); - console.log("Skipping npm package signing; VSIX packaging will use published platform packages."); - return; - } - - const platforms = getPlatforms(); - - /** @type {Map} */ - const filelistByCert = new Map(); - for (const { npmDir, nodeOs, cert, npmDirName } of platforms) { - let certFilelist = filelistByCert.get(cert); - if (!certFilelist) { - filelistByCert.set(cert, certFilelist = []); - } - certFilelist.push({ - tmpName: npmDirName, - path: path.join(npmDir, "lib", nativePreviewExeName(nodeOs)), - }); - } - - const tmp = await getSignTempDir(); - const typescriptMacEntitlementsPath = path.join(tmp, "typescript-macos-entitlements.plist"); - await fs.promises.writeFile(typescriptMacEntitlementsPath, createTypeScriptMacEntitlementsPlist()); - - /** @type {DDSignFileList} */ - const filelist = { - SignFileRecordList: [], - }; - - /** @type {{ path: string; unsignedZipPath: string; signedZipPath: string; notarizedZipPath: string; }[]} */ - const macZips = []; - - // First, sign the files. - - for (const [cert, filelistPaths] of filelistByCert) { - switch (cert) { - case "Microsoft400": - filelist.SignFileRecordList.push({ - SignFileList: filelistPaths.map(p => ({ SrcPath: p.path, DstPath: null })), - Certs: cert, - MacAppName: undefined, - }); - break; - case "LinuxSign": - filelist.SignFileRecordList.push({ - SignFileList: filelistPaths.map(p => ({ SrcPath: p.path, DstPath: p.path + ".sig" })), - Certs: cert, - MacAppName: undefined, - }); - break; - case "MacDeveloperHarden": - // Mac signing requires putting files into zips and then signing those, - // along with a notarization step. - for (const p of filelistPaths) { - // ESRP preserves entitlements from an existing ad-hoc signature. - await $pipe`go -C ./tools run ./cmd/machotool sign ${typescriptMacEntitlementsPath} ${p.path}`; - - const unsignedZipPath = path.join(tmp, `${p.tmpName}.unsigned.zip`); - const signedZipPath = path.join(tmp, `${p.tmpName}.signed.zip`); - const notarizedZipPath = path.join(tmp, `${p.tmpName}.notarized.zip`); - - const zip = new AdmZip(); - zip.addLocalFile(p.path); - zip.writeZip(unsignedZipPath); - - macZips.push({ - path: p.path, - unsignedZipPath, - signedZipPath, - notarizedZipPath, - }); - } - filelist.SignFileRecordList.push({ - SignFileList: macZips.map(p => ({ SrcPath: p.unsignedZipPath, DstPath: p.signedZipPath })), - Certs: cert, - MacAppName: undefined, // MacAppName is only for notarization - }); - break; - default: - throw new Error(`Unknown cert: ${cert}`); - } - } - - await sign(filelist); - - // All of the files have been signed in place / had signatures added. - - if (macZips.length) { - // Now, notarize the Mac files. - - /** @type {DDSignFileList} */ - const notarizeFilelist = { - SignFileRecordList: [ - { - SignFileList: macZips.map(p => ({ SrcPath: p.signedZipPath, DstPath: p.notarizedZipPath })), - Certs: "8020", // "MacNotarize" (friendly name not supported by the tooling) - MacAppName: "MicrosoftTypeScript", - }, - ], - }; - - // Notarizing does not change the file, it just sends it to Apple, so ignore the case - // where the input files are the same as the output files. - await sign(notarizeFilelist, /*unchangedOutputOkay*/ true); - - // Finally, unzip the notarized files and move them back to their original locations. - - for (const p of macZips) { - const zip = new AdmZip(p.notarizedZipPath); - zip.extractEntryTo(path.basename(p.path), path.dirname(p.path), false, true); - } - - // chmod +x the unzipped files. - - for (const p of macZips) { - await fs.promises.chmod(p.path, 0o755); - await $pipe`go -C ./tools run ./cmd/machotool verify ${typescriptMacEntitlementsPath} ${p.path}`; - } - } -} - -export const packNativePreviewPackages = task({ - name: "typescript:pack", - hiddenFromTaskList: true, - dependencies: options.forRelease ? undefined : [buildNativePreviewPackages, cleanSignTempDirectory], - run: runPackNativePreviewPackages, -}); - -async function runPackNativePreviewPackages() { - if (usePublishedPlatformPackagesForVsix) { - checkPublishedPlatformPackagesForVsix(); - await rimraf(builtNpm); - console.log("Skipping npm package packing; VSIX packaging will use published platform packages."); - return; - } - - const platforms = getPlatforms(); - await Promise.all([mainNativePreviewPackage, ...platforms].map(async ({ npmDir, npmTarball }) => { - const { stdout } = await $pipe`npm pack --json ${npmDir}`; - const filename = JSON.parse(stdout)[0].filename.replace("@", "").replace("/", "-"); - await fs.promises.rename(filename, npmTarball); - })); - - // npm packages need to be published in dependency order: platform packages - // first, then the main package that references them as optionalDependencies. - const publishManifest = { - stages: [ - platforms.map(p => ({ - filename: path.basename(p.npmTarball), - })), - [ - { - filename: path.basename(mainNativePreviewPackage.npmTarball), - }, - ], - ], - }; - - const publishManifestPath = path.join(builtNpm, "publish-manifest.json"); - await fs.promises.writeFile(publishManifestPath, JSON.stringify(publishManifest, undefined, 4) + "\n"); -} - -export const packVsixExtensions = task({ - name: "vscode-typescript:pack", - hiddenFromTaskList: true, - dependencies: options.forRelease || usePublishedPlatformPackagesForVsix ? undefined : [buildNativePreviewPackages, cleanSignTempDirectory], - run: runPackVsixExtensions, -}); - -/** @type {Map>} */ -const publishedPlatformPackageLibDirs = new Map(); - -const getPublishedTypeScriptPackageJson = memoize(() => { - const candidates = [ - path.join(extensionDir, "node_modules", publishedTypeScriptAliasPackageName, "package.json"), - path.join(__dirname, "node_modules", publishedTypeScriptAliasPackageName, "package.json"), - ]; - - for (const candidate of candidates) { - if (fs.existsSync(candidate)) { - const packageJson = JSON.parse(fs.readFileSync(candidate, "utf8")); - if (packageJson.name !== "typescript") { - throw new Error(`${publishedTypeScriptAliasPackageName} should alias the typescript package, but found ${packageJson.name}.`); - } - if (!packageJson.version || typeof packageJson.version !== "string") { - throw new Error(`${publishedTypeScriptAliasPackageName} package.json did not contain a version.`); - } - if (!packageJson.optionalDependencies || typeof packageJson.optionalDependencies !== "object") { - throw new Error(`${publishedTypeScriptAliasPackageName} package.json did not contain platform optionalDependencies.`); - } - return packageJson; - } - } - - throw new Error(`Could not find ${publishedTypeScriptAliasPackageName}; run npm install first.`); -}); - -function getPublishedTypeScriptVersion() { - const version = getPublishedTypeScriptPackageJson().version; - const expectedVersion = getVersion(); - if (usePublishedPlatformPackagesForVsix && version !== expectedVersion) { - throw new Error(`usePublishedPlatformPackagesForVsix requires ${publishedTypeScriptAliasPackageName}'s installed version (${version}) to match release version ${expectedVersion}.`); - } - return version; -} - -function checkPublishedPlatformPackagesForVsix() { - if (!options.forRelease) { - throw new Error("usePublishedPlatformPackagesForVsix requires forRelease"); - } - getPublishedTypeScriptVersion(); -} - -const getPackageLock = memoize(() => JSON.parse(fs.readFileSync(path.join(__dirname, "package-lock.json"), "utf8"))); - -/** - * @param {string} npmPackageName - */ -async function getPublishedPlatformPackageLibDir(npmPackageName) { - let promise = publishedPlatformPackageLibDirs.get(npmPackageName); - if (!promise) { - promise = getPublishedPlatformPackageLibDirWorker(npmPackageName); - publishedPlatformPackageLibDirs.set(npmPackageName, promise); - } - return promise; -} - -/** - * @param {string} npmPackageName - */ -async function getPublishedPlatformPackageLibDirWorker(npmPackageName) { - const dest = path.join(builtPublishedPlatformPackages, "node_modules", ...npmPackageName.split("/")); - const lib = path.join(dest, "lib"); - if (fs.existsSync(lib)) { - return lib; - } - - await fs.promises.mkdir(dest, { recursive: true }); - - const tarballDestination = path.join(builtPublishedPlatformPackages, "tarballs"); - await fs.promises.mkdir(tarballDestination, { recursive: true }); - - const version = getPublishedTypeScriptPackageJson().optionalDependencies[npmPackageName]; - if (!version || typeof version !== "string") { - throw new Error(`${publishedTypeScriptAliasPackageName} does not depend on ${npmPackageName}.`); - } - - const lockEntry = getPackageLock().packages[`node_modules/${npmPackageName}`]; - if (!lockEntry) { - throw new Error(`package-lock.json does not contain ${npmPackageName}; run npm install.`); - } - if (lockEntry.version !== version) { - throw new Error(`package-lock.json has ${npmPackageName}@${lockEntry.version}, but ${publishedTypeScriptAliasPackageName} depends on ${version}.`); - } - if (!lockEntry.resolved || typeof lockEntry.resolved !== "string") { - throw new Error(`package-lock.json entry for ${npmPackageName}@${version} does not contain a tarball URL.`); - } - - console.log(`Fetching ${npmPackageName}@${version} with npm.`); - const { stdout } = await $pipe({ cwd: tarballDestination, env: releasePackageEnv })`npm pack --json ${npmPackageName}@${version}`; - const [packed] = JSON.parse(stdout); - if (!packed.filename || typeof packed.filename !== "string") { - throw new Error(`npm pack ${npmPackageName}@${version} did not return a filename.`); - } - await tar.x({ file: path.join(tarballDestination, packed.filename), cwd: dest, strip: 1 }); - - if (!fs.existsSync(lib)) { - throw new Error(`Published platform package ${npmPackageName}@${version} did not contain a lib directory.`); - } - - return lib; -} - -async function runPackVsixExtensions() { - await rimraf(builtVsix); - await fs.promises.mkdir(builtVsix, { recursive: true }); - if (usePublishedPlatformPackagesForVsix) { - checkPublishedPlatformPackagesForVsix(); - publishedPlatformPackageLibDirs.clear(); - await rimraf(builtPublishedPlatformPackages); - } - - const platforms = getPlatforms(); - const extensions = platforms.flatMap(({ npmDir, npmPackageName, extensions }) => extensions.map(e => ({ npmDir, npmPackageName, ...e }))); - if (!extensions.length) { - console.log("No VSIX targets configured; skipping extension packaging."); - return; - } - - // We don't use vscode:prepublish, as that would run the build for each package below. - await $({ cwd: extensionDir, env: releasePackageEnv })`npm run bundle:release`; - - let version = "0.0.0"; - if (options.forRelease) { - // No real semver prerelease versioning. - // https://code.visualstudio.com/api/working-with-extensions/publishing-extension#prerelease-extensions - assert(options.setPrerelease, "forRelease is true but setPrerelease is not set"); - const prerelease = options.setPrerelease; - assert(typeof prerelease === "string", "setPrerelease is not a string"); - // parse `dev..`. - const match = prerelease.match(/dev\.(\d+)\.(\d+)/); - if (!match) { - throw new Error(`Prerelease version should be in the form of dev.., but got ${prerelease}`); - } - // Set version to `0..`. - version = `0.${match[1]}.${match[2]}`; - } - - console.log("Version:", version); - - await Promise.all(extensions.map(async ({ npmDir, npmPackageName, nodeOs, vscodeTarget, sourceDir, extensionDir: thisExtensionDir, vsixPath, vsixManifestPath, vsixSignaturePath }) => { - const npmLibDir = usePublishedPlatformPackagesForVsix - ? await getPublishedPlatformPackageLibDir(npmPackageName) - : path.join(npmDir, "lib"); - const extensionLibDir = path.join(thisExtensionDir, "lib"); - await fs.promises.mkdir(extensionLibDir, { recursive: true }); - - await cpWithoutNodeModulesOrTsconfig(sourceDir, thisExtensionDir); - await cpWithoutNodeModulesOrTsconfig(npmLibDir, extensionLibDir); - await fs.promises.chmod(path.join(extensionLibDir, nativePreviewExeName(nodeOs)), 0o755); - - const packageJsonPath = path.join(thisExtensionDir, "package.json"); - const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")); - packageJson.version = version; - packageJson.bundledTypeScriptVersion = usePublishedPlatformPackagesForVsix ? getPublishedTypeScriptVersion() : getVersion(); - fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, undefined, 4)); - - await fs.promises.copyFile("NOTICE.txt", path.join(thisExtensionDir, "NOTICE.txt")); - - await $({ cwd: thisExtensionDir, env: releasePackageEnv })`vsce package ${version} --no-update-package-json --no-dependencies --out ${vsixPath} --target ${vscodeTarget}`; - - if (options.forRelease) { - await $({ cwd: thisExtensionDir, env: releasePackageEnv })`vsce generate-manifest --packagePath ${vsixPath} --out ${vsixManifestPath}`; - await fs.promises.cp(vsixManifestPath, vsixSignaturePath); - } - })); -} - -export const signVsixExtensions = task({ - name: "vscode-typescript:sign", - hiddenFromTaskList: true, - run: runSignVsixExtensions, -}); - -async function runSignVsixExtensions() { - if (!options.forRelease) { - throw new Error("This task should not be run in non-release builds."); - } - - const platforms = getPlatforms(); - const extensions = platforms.flatMap(({ npmDir, extensions }) => extensions.map(e => ({ npmDir, ...e }))); - if (!extensions.length) { - console.log("No VSIX targets configured; skipping extension signing."); - return; - } - - await sign({ - SignFileRecordList: [ - { - SignFileList: extensions.map(({ vsixSignaturePath }) => ({ SrcPath: vsixSignaturePath, DstPath: null })), - Certs: "VSCodePublisher", - MacAppName: undefined, - }, - ], - }); -} - -export const nativePreviewRelease = task({ - name: "typescript:release", - hiddenFromTaskList: true, - run: async () => { - if (!options.forRelease || !options.setPrerelease && (!nativePreviewReleaseVersion || produceAnyVsix)) { - throw new Error("typescript:release requires --forRelease and --setPrerelease flags, unless nativePreviewReleaseVersion is hardcoded and VSIX production is disabled. Example: npx hereby typescript:release --forRelease --setPrerelease=dev.1.0"); - } - await runBuildNativePreviewPackages(); - await runSignNativePreviewPackages(); - await runPackNativePreviewPackages(); - await runPackVsixExtensions(); - await runSignVsixExtensions(); - await runCleanSignTempDirectory(); - }, -}); - -export const nativePreview = task({ - name: "native-preview", - hiddenFromTaskList: true, - dependencies: options.forRelease ? undefined : [packNativePreviewPackages, packVsixExtensions], - run: options.forRelease ? async () => { - throw new Error("This task should not be run in release builds."); - } : undefined, -}); - -export const allChecks = task({ - name: "all-checks", - description: "Runs all checks for the Go code (fourslash, lint, tests, etc.)", - run: async () => { - await runTests(); - await runFormat(); - await runLint(); - await runTests(); - }, -}); - -export const tidy = task({ - name: "tidy", - description: "Tidies both Go modules and synchronizes the workspace.", - run: async () => { - await $({ cwd: "./tsc" })`go mod tidy`; - await $({ cwd: "./tools" })`go mod tidy`; - await $`go work sync`; - }, -}); +// @ts-check + +import AdmZip from "adm-zip"; +import chokidar from "chokidar"; +import { $ as _$ } from "execa"; +import { glob } from "glob"; +import { task } from "hereby"; +import assert from "node:assert"; +import crypto from "node:crypto"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import url from "node:url"; +import { parseArgs } from "node:util"; +import pLimit from "p-limit"; +import pc from "picocolors"; +import * as tar from "tar"; +import tmp from "tmp"; +import which from "which"; + +if (process.platform === "win32") { + process.chdir(fs.realpathSync.native(process.cwd())); +} + +const __filename = url.fileURLToPath(new URL(import.meta.url)); +const __dirname = path.dirname(__filename); + +const isCI = !!process.env.CI || !!process.env.TF_BUILD; + +const $pipe = _$({ verbose: "short" }); +const $ = _$({ verbose: "short", stdio: "inherit" }); + +/** + * @param {string} name + * @param {boolean} defaultValue + * @returns {boolean} + */ +function parseEnvBoolean(name, defaultValue = false) { + name = "TSGO_HEREBY_" + name.toUpperCase(); + + const value = process.env[name]; + if (!value) { + return defaultValue; + } + switch (value.toUpperCase()) { + case "1": + case "TRUE": + case "YES": + case "ON": + return true; + case "0": + case "FALSE": + case "NO": + case "OFF": + return false; + } + throw new Error(`Invalid value for ${name}: ${value}`); +} + +const { values: rawOptions } = parseArgs({ + args: process.argv.slice(2), + options: { + tests: { type: "string", short: "t" }, + fix: { type: "boolean" }, + debug: { type: "boolean" }, + dirty: { type: "boolean" }, + release: { type: "boolean" }, + + setPrerelease: { type: "string" }, + forRelease: { type: "boolean" }, + + race: { type: "boolean", default: parseEnvBoolean("RACE") }, + noembed: { type: "boolean", default: parseEnvBoolean("NOEMBED") }, + concurrentTestPrograms: { type: "boolean", default: parseEnvBoolean("CONCURRENT_TEST_PROGRAMS") }, + coverage: { type: "boolean", default: parseEnvBoolean("COVERAGE") }, + }, + strict: false, + allowPositionals: true, + allowNegative: true, +}); + +// We can't use parseArgs' strict mode as it errors on hereby's --tasks flag. +/** + * @typedef {{ [K in keyof typeof rawOptions as {} extends Record ? never : K]: typeof rawOptions[K] }} Options + */ +const options = /** @type {Options} */ (rawOptions); + +// Native release branches can edit these constants to publish a fixed stable version. +// Main publishes prerelease builds of the TypeScript package. +const nativePreviewReleaseProfile = /** @type {"native-preview" | "typescript"} */ ("typescript"); +const nativePreviewReleaseVersion = /** @type {string | undefined} */ (undefined); +const produceNativePreviewVsix = /** @type {boolean} */ (false); +const produceTypeScriptNightlyVsix = /** @type {boolean} */ (true); +const usePublishedPlatformPackagesForVsix = /** @type {boolean} */ (false); +const produceAnyVsix = produceNativePreviewVsix || produceTypeScriptNightlyVsix; +const publishAsTypescript = nativePreviewReleaseProfile === "typescript"; + +if (options.forRelease && !options.setPrerelease && (!nativePreviewReleaseVersion || produceAnyVsix)) { + throw new Error("forRelease requires setPrerelease unless nativePreviewReleaseVersion is hardcoded and VSIX production is disabled"); +} +if (usePublishedPlatformPackagesForVsix && !publishAsTypescript) { + throw new Error("usePublishedPlatformPackagesForVsix requires nativePreviewReleaseProfile to be 'typescript'"); +} + +const defaultGoBuildTags = [ + ...(options.noembed ? ["noembed"] : []), +]; + +/** + * @param {...string} extra + * @returns {string[]} + */ +function goBuildTags(...extra) { + const tags = new Set(defaultGoBuildTags.concat(extra)); + return tags.size ? [`-tags=${[...tags].join(",")}`] : []; +} + +const goBuildFlags = [ + ...(options.race ? ["-race"] : []), + // https://github.com/go-delve/delve/blob/62cd2d423c6a85991e49d6a70cc5cb3e97d6ceef/Documentation/usage/dlv_exec.md?plain=1#L12 + ...(options.debug ? ["-gcflags=all=-N -l"] : []), +]; + +const goBuildEnv = { + ...(options.race ? {} : { CGO_ENABLED: "0" }), +}; + +/** + * @template T + * @param {() => T} fn + * @returns {() => T} + */ +function memoize(fn) { + /** @type {T} */ + let value; + return () => { + if (fn !== undefined) { + value = fn(); + fn = /** @type {any} */ (undefined); + } + return value; + }; +} + +const tools = new Map([ + ["gotest.tools/gotestsum", "latest"], +]); + +/** + * @param {string} tool + */ +function isInstalled(tool) { + return !!which.sync(tool, { nothrow: true }); +} + +const builtLocal = "./built/local"; + +const libsDir = "./tsc/internal/bundled/libs"; +const libsRegexp = /(?:^|[\\/])internal[\\/]bundled[\\/]libs[\\/]/; + +/** + * @param {string} out + */ +async function generateLibs(out) { + await fs.promises.mkdir(out, { recursive: true }); + + const libs = await fs.promises.readdir(libsDir); + + await Promise.all(libs.map(async lib => { + fs.promises.copyFile(path.join(libsDir, lib), path.join(out, lib)); + })); +} + +export const lib = task({ + name: "lib", + description: "Copies the libs to built/local.", + run: () => generateLibs(builtLocal), +}); + +/** + * Gets the release build flags for stripping debug info. + * @param {string} [versionOverride] Optional version to embed in the binary. + * @returns {string[]} + */ +function getReleaseBuildFlags(versionOverride) { + let ldflags = "-ldflags=-s -w"; + if (versionOverride) { + ldflags += ` -X github.com/microsoft/TypeScript/tsc/internal/core.version=${versionOverride}`; + } + return ["-trimpath", ldflags]; +} + +/** + * @param {object} [opts] + * @param {string} [opts.out] + * @param {AbortSignal} [opts.abortSignal] + * @param {Record} [opts.env] + * @param {string[]} [opts.extraFlags] + */ +function buildTsc(opts) { + opts ||= {}; + const out = opts.out ?? path.resolve("./built/local/tsc" + (process.platform === "win32" ? ".exe" : "")); + const env = { ...goBuildEnv, ...opts.env }; + return $({ cancelSignal: opts.abortSignal, env, cwd: "./tsc" })`go build ${goBuildFlags} ${opts.extraFlags ?? []} ${goBuildTags("noembed")} -o ${out} ./cmd/tsc`; +} + +export const tscBuild = task({ + name: "tsc:build", + description: "Builds the tsc binary.", + run: async () => { + await buildTsc({ extraFlags: options.release ? getReleaseBuildFlags() : [] }); + }, +}); + +export const tsgo = task({ + name: "tsgo", + dependencies: [lib, tscBuild], +}); + +export const local = task({ + name: "local", + dependencies: [tsgo], +}); + +export const build = task({ + name: "build", + dependencies: [local], +}); + +export const buildWatch = task({ + name: "build:watch", + description: "Builds the tsc binary and watches for changes.", + run: async () => { + await watchDebounced("build:watch", async (paths, abortSignal) => { + let libsChanged = false; + let goChanged = false; + + if (paths) { + for (const p of paths) { + if (libsRegexp.test(p)) { + libsChanged = true; + } + else if (p.endsWith(".go")) { + goChanged = true; + } + if (libsChanged && goChanged) { + break; + } + } + } + else { + libsChanged = true; + goChanged = true; + } + + if (libsChanged) { + console.log("Generating libs..."); + await generateLibs(builtLocal); + } + + if (goChanged) { + console.log("Building tsgo..."); + await buildTsc({ abortSignal }); + } + }, { + paths: ["tsc/cmd", "tsc/internal"], + ignored: path => /[\\/]testdata[\\/]/.test(path), + }); + }, +}); + +export const cleanBuilt = task({ + name: "clean:built", + hiddenFromTaskList: true, + run: () => rimraf("built"), +}); + +export const generate = task({ + name: "generate", + description: "Runs go generate on the project.", + run: async () => { + await $({ cwd: "./tsc" })`go generate -v ./...`; + }, +}); + +export const generateExtension = task({ + name: "generate:extension", + description: "Generates files in the extension", + run: async () => { + await $`npm run -w native-preview generateLocBundle`; + }, +}); + +// ── Enum generation from Go source ────────────────────────────── + +/** + * @typedef {{ + * name: string; + * goPrefix: string; + * goFile: string; + * outDir: string; + * stringEnum?: boolean; + * excludeMembers?: readonly string[]; + * valueReplacements?: Record; + * }} EnumDef + */ + +/** @type {EnumDef[]} */ +const enumDefs = [ + { name: "SymbolFlags", goPrefix: "SymbolFlags", goFile: "tsc/internal/ast/symbolflags.go", outDir: "packages/typescript/src/enums" }, + { name: "CheckFlags", goPrefix: "CheckFlags", goFile: "tsc/internal/ast/checkflags.go", outDir: "packages/typescript/src/enums" }, + { name: "TypeFlags", goPrefix: "TypeFlags", goFile: "tsc/internal/checker/types.go", outDir: "packages/typescript/src/enums" }, + { name: "ObjectFlags", goPrefix: "ObjectFlags", goFile: "tsc/internal/checker/types.go", outDir: "packages/typescript/src/enums" }, + { name: "SignatureFlags", goPrefix: "SignatureFlags", goFile: "tsc/internal/checker/types.go", outDir: "packages/typescript/src/enums" }, + { name: "SignatureKind", goPrefix: "SignatureKind", goFile: "tsc/internal/checker/types.go", outDir: "packages/typescript/src/enums" }, + { name: "ElementFlags", goPrefix: "ElementFlags", goFile: "tsc/internal/checker/types.go", outDir: "packages/typescript/src/enums" }, + { name: "TypePredicateKind", goPrefix: "TypePredicateKind", goFile: "tsc/internal/checker/types.go", outDir: "packages/typescript/src/enums" }, + { name: "TypeFormatFlags", goPrefix: "TypeFormatFlags", goFile: "tsc/internal/checker/types.go", outDir: "packages/typescript/src/enums" }, + { name: "DiagnosticCategory", goPrefix: "Category", goFile: "tsc/internal/diagnostics/diagnostics.go", outDir: "packages/typescript/src/enums" }, + { name: "SyntaxKind", goPrefix: "Kind", goFile: "tsc/internal/ast/kind_generated.go", outDir: "packages/typescript/src/enums" }, + { name: "NodeFlags", goPrefix: "NodeFlags", goFile: "tsc/internal/ast/nodeflags.go", outDir: "packages/typescript/src/enums" }, + { name: "OuterExpressionKinds", goPrefix: "OEK", goFile: "tsc/internal/ast/utilities.go", outDir: "packages/typescript/src/enums" }, + { name: "ModifierFlags", goPrefix: "ModifierFlags", goFile: "tsc/internal/ast/modifierflags.go", outDir: "packages/typescript/src/enums" }, + { name: "ModuleKind", goPrefix: "ModuleKind", goFile: "tsc/internal/core/compileroptions.go", outDir: "packages/typescript/src/enums" }, + { name: "ModuleResolutionKind", goPrefix: "ModuleResolutionKind", goFile: "tsc/internal/core/compileroptions.go", outDir: "packages/typescript/src/enums" }, + { name: "ModuleDetectionKind", goPrefix: "ModuleDetectionKind", goFile: "tsc/internal/core/compileroptions.go", outDir: "packages/typescript/src/enums" }, + { name: "NewLineKind", goPrefix: "NewLineKind", goFile: "tsc/internal/core/compileroptions.go", outDir: "packages/typescript/src/enums" }, + { name: "JsxEmit", goPrefix: "JsxEmit", goFile: "tsc/internal/core/compileroptions.go", outDir: "packages/typescript/src/enums" }, + { name: "ScriptKind", goPrefix: "ScriptKind", goFile: "tsc/internal/core/scriptkind.go", outDir: "packages/typescript/src/enums" }, + { name: "TokenFlags", goPrefix: "TokenFlags", goFile: "tsc/internal/ast/tokenflags.go", outDir: "packages/typescript/src/enums" }, + { name: "DiagnosticDirectivePolicy", goPrefix: "MappedDiagnosticDirectivePolicy", goFile: "tsc/internal/ast/ast.go", outDir: "packages/typescript/src/enums" }, + { name: "SpanMapKind", goPrefix: "Kind", goFile: "tsc/internal/spanmap/spanmap.go", outDir: "packages/typescript/src/enums" }, + { name: "SpanMapFidelity", goPrefix: "Fidelity", goFile: "tsc/internal/spanmap/spanmap.go", outDir: "packages/typescript/src/enums" }, + { name: "SpanMapFeature", goPrefix: "Feature", goFile: "tsc/internal/spanmap/spanmap.go", outDir: "packages/typescript/src/enums" }, + { name: "NodeBuilderFlags", goPrefix: "Flags", goFile: "tsc/internal/nodebuilder/types.go", outDir: "packages/typescript/src/enums" }, + { name: "CompletionItemKind", goPrefix: "CompletionItemKind", goFile: "tsc/internal/lsp/lsproto/lsp_generated.go", outDir: "packages/typescript/src/enums" }, + { name: "EmitOnly", goPrefix: "Emit", goFile: "tsc/internal/compiler/emitter.go", outDir: "packages/typescript/src/enums", excludeMembers: ["OnlyBuilderSignature"] }, + // String enum: Go stores internal names with a "\xFE" sentinel prefix, but the escaped + // form sent over the wire uses "__" (see EscapeSymbolName), so map the sentinel accordingly. + { name: "InternalSymbolName", goPrefix: "InternalSymbolName", goFile: "tsc/internal/ast/symbol.go", outDir: "packages/typescript/src/enums", stringEnum: true, valueReplacements: { InternalSymbolNamePrefix: "__" } }, +]; + +/** + * @param {string} block + * @param {EnumDef} def + * @returns {{ name: string, value: string }[]} + */ +function parseGoConstBlock(block, def) { + const prefix = def.goPrefix; + const members = []; + let iotaCounter = 0; + let iotaExpression; + + const lines = block.split("\n"); + let i = 0; + + while (i < lines.length) { + const rawLine = lines[i]; + const line = rawLine.replace(/\/\/.*$/, "").trim(); + + if (!line) { + i++; + continue; + } + + // Match: PrefixName Type = value or PrefixName = value + const fullMatch = line.match(new RegExp(`^(${prefix}\\w+)\\s+(?:\\S+\\s*)?=\\s*(.+)$`)); + // Match bare iota continuation: just PrefixName + const bareMatch = !fullMatch && iotaExpression !== undefined + ? line.match(new RegExp(`^(${prefix}\\w+)$`)) + : null; + + if (!fullMatch && !bareMatch) { + i++; + continue; + } + + const goName = fullMatch ? fullMatch[1] : /** @type {RegExpMatchArray} */ (bareMatch)[1]; + let goValue = fullMatch ? fullMatch[2].trim() : ""; + const memberName = goName.slice(prefix.length); + + // Accumulate continuation lines ending with | + i++; + while (i < lines.length && goValue.endsWith("|")) { + const nextRaw = lines[i]; + const nextLine = nextRaw.replace(/\/\/.*$/, "").trim(); + if (!nextLine) { + i++; + continue; + } + goValue += " " + nextLine; + i++; + } + + let tsValue; + if (def.stringEnum) { + tsValue = parseGoStringValue(goValue, def.valueReplacements ?? {}); + } + else if (goValue.includes("iota")) { + iotaExpression = goValue; + tsValue = goValue.replace(/\biota\b/g, String(iotaCounter)); + } + else if (iotaExpression !== undefined && goValue === "") { + tsValue = iotaExpression.replace(/\biota\b/g, String(iotaCounter)); + } + else { + // Replace Go bitwise NOT (^) with TypeScript (~) + tsValue = goValue.replace(/\^/g, "~"); + // Strip enum prefix from member references + tsValue = tsValue.replace(new RegExp(`${prefix}(\\w+)`, "g"), "$1"); + } + + members.push({ name: memberName, value: tsValue }); + iotaCounter++; + } + + return members; +} + +/** + * Resolve a Go string-constant expression (e.g. `Prefix + "call"` or `"export="`) + * into a quoted, JS-escaped TypeScript string literal. `replacements` maps bare + * Go identifiers (such as a sentinel-prefix constant) to their literal value. + * @param {string} goValue + * @param {Record} replacements + * @returns {string} + */ +function parseGoStringValue(goValue, replacements) { + let result = ""; + for (const part of goValue.split("+").map(p => p.trim())) { + if (Object.prototype.hasOwnProperty.call(replacements, part)) { + result += replacements[part]; + continue; + } + const stringMatch = part.match(/^"((?:[^"\\]|\\.)*)"$/); + if (stringMatch === null) { + throw new Error(`Cannot parse string enum value: ${goValue}`); + } + // Interpret Go escape sequences via JSON, then re-stringify below. + result += JSON.parse(`"${stringMatch[1]}"`); + } + return JSON.stringify(result); +} + +/** + * @param {EnumDef} def + * @returns {{ name: string, value: string }[]} + */ +function parseGoEnum(def) { + const source = fs.readFileSync(def.goFile, "utf-8"); + const constBlockRegex = /const\s*\(([\s\S]*?)\n\)/g; + + for (const match of source.matchAll(constBlockRegex)) { + const members = parseGoConstBlock(match[1], def).filter(member => !def.excludeMembers?.includes(member.name)); + if (members.length > 0) return topoSortMembers(members); + } + + throw new Error(`No members found for enum ${def.name} in ${def.goFile}`); +} + +/** + * Topologically sort enum members so composite members appear after + * all members they reference (Go allows forward references, TS does not). + * @param {{ name: string, value: string }[]} members + * @returns {{ name: string, value: string }[]} + */ +function topoSortMembers(members) { + const nameSet = new Set(members.map(m => m.name)); + /** @type {Map>} */ + const deps = new Map(); + for (const m of members) { + /** @type {Set} */ + const refs = new Set(); + // Find all identifier references in the value that are other member names + for (const [ref] of m.value.matchAll(/\b([A-Za-z_]\w*)\b/g)) { + if (ref !== m.name && nameSet.has(ref)) refs.add(ref); + } + deps.set(m.name, refs); + } + + const sorted = /** @type {{ name: string, value: string }[]} */ ([]); + const visited = new Set(); + const visiting = new Set(); + + /** @param {string} name */ + function visit(name) { + if (visited.has(name)) return; + if (visiting.has(name)) return; // cycle — keep original order + visiting.add(name); + for (const dep of deps.get(name) ?? []) { + visit(dep); + } + visiting.delete(name); + visited.add(name); + sorted.push(/** @type {{ name: string, value: string }} */ (members.find(m => m.name === name))); + } + + for (const m of members) { + visit(m.name); + } + return sorted; +} + +/** + * @param {EnumDef} def + * @param {{ name: string, value: string }[]} members + * @returns {string} + */ +function renderEnumTS(def, members) { + const header = `// Code generated by Herebyfile.mjs generate:enums from ${def.goFile}. DO NOT EDIT.\n\n`; + + const lines = members.map(m => ` ${m.name} = ${m.value},`); + return `${header}export enum ${def.name} {\n${lines.join("\n")}\n}\n`; +} + +async function runGenerateEnums() { + const ts = /** @type {typeof import("typescript")} */ (await import("typescript")); + + /** + * @param {string} enumSource + * @param {string} enumName + * @returns {string} + */ + function transpile(enumSource, enumName) { + const result = ts.transpileModule(enumSource, { + compilerOptions: { + module: ts.ModuleKind.ESNext, + target: ts.ScriptTarget.ESNext, + }, + }); + return result.outputText.replace( + `export var ${enumName};`, + `export var ${enumName}: any;`, + ); + } + + console.log("Generating enums from Go source..."); + /** @type {string[]} */ + const generatedFiles = []; + + for (const def of enumDefs) { + const members = parseGoEnum(def); + const camelName = def.name.charAt(0).toLowerCase() + def.name.slice(1); + + fs.mkdirSync(def.outDir, { recursive: true }); + + // Generate .enum.ts (TypeScript enum — used for types) + const enumTS = renderEnumTS(def, members); + const enumPath = path.join(def.outDir, `${camelName}.enum.ts`); + fs.writeFileSync(enumPath, enumTS); + generatedFiles.push(enumPath); + + // Generate .ts (IIFE — used at runtime) + const iifeSource = transpile(enumTS, def.name); + const iifePath = path.join(def.outDir, `${camelName}.ts`); + fs.writeFileSync(iifePath, iifeSource); + generatedFiles.push(iifePath); + + console.log(` ${def.name}: ${members.length} members → ${camelName}.enum.ts, ${camelName}.ts`); + } + + await $`dprint fmt ${generatedFiles}`; + console.log("Done."); +} + +export const generateEnums = task({ + name: "generate:enums", + description: "Generates TypeScript enum files from Go source.", + run: runGenerateEnums, +}); + +export const generateAST = task({ + name: "generate:ast", + description: "Generates AST and encoder files from ast.json.", + run: () => $`node --experimental-strip-types --no-warnings ./tools/scripts/tsc/generate.ts`, +}); + +export const generateAPI = task({ + name: "generate:api", + description: "Generates API files from internal/api/proto.go and internal/api/session.go.", + run: async () => { + await $`go -C ./tools run ./gen-proto ../tsc/internal/api/proto.go ../packages/typescript/src/api/proto.generated.ts`; + await $`npx dprint fmt packages/typescript/src/api/proto.generated.ts`; + }, +}); + +// ── Vendored npm dependencies ─────────────────────────────────── + +const vendorJsonrpcDir = "packages/typescript/vendor/vscode-jsonrpc"; +const vendorJsonrpcSrc = "node_modules/vscode-jsonrpc"; +// Files copied verbatim from the installed vscode-jsonrpc package into the +// vendored copy. Only the runtime files needed by the `#vscode-jsonrpc/node` +// import (lib + typings + package.json) plus license/readme are vendored. +const vendorJsonrpcFiles = ["package.json", "README.md", "License.txt", "lib", "typings"]; + +async function runGenerateVendor() { + const src = path.join(__dirname, vendorJsonrpcSrc); + const dest = path.join(__dirname, vendorJsonrpcDir); + if (!fs.existsSync(src)) { + throw new Error(`${vendorJsonrpcSrc} is not installed; run \`npm ci\` first.`); + } + await rimraf(dest); + await fs.promises.mkdir(dest, { recursive: true }); + for (const file of vendorJsonrpcFiles) { + await cpRecursive(path.join(src, file), path.join(dest, file)); + } +} + +export const generateVendor = task({ + name: "generate:vendor", + description: "Updates the vendored copy of vscode-jsonrpc from node_modules.", + run: runGenerateVendor, +}); + +const coverageDir = path.join(__dirname, "coverage"); + +const ensureCoverageDirExists = memoize(() => { + if (options.coverage) { + fs.mkdirSync(coverageDir, { recursive: true }); + } +}); + +/** + * @param {string} taskName + */ +function goTestFlags(taskName) { + ensureCoverageDirExists(); + return [ + ...goBuildFlags, + ...goBuildTags(), + ...(options.tests ? [`-run=${options.tests}`] : []), + ...(options.coverage ? [`-coverprofile=${path.join(coverageDir, "coverage." + taskName + ".out")}`, "-coverpkg=./..."] : []), + ]; +} + +function getGODEBUG() { + const key = "tracebackancestors"; + const setting = `${key}=10`; + const existing = process.env.GODEBUG ?? ""; + if (!existing) return setting; + if (existing.includes(`${key}=`)) return existing; + return `${existing},${setting}`; +} + +const goTestEnv = { + GODEBUG: getGODEBUG(), + ...(options.concurrentTestPrograms ? { TS_TEST_PROGRAM_SINGLE_THREADED: "false" } : {}), + // Go test caching takes a long time on Windows. + // https://github.com/golang/go/issues/72992 + ...(process.platform === "win32" ? { GOFLAGS: "-count=1" } : {}), +}; + +const baselineTrackingEnabled = false && ![ + options.tests, + options.noembed, + options.concurrentTestPrograms, + options.race, + options.dirty, +].some(Boolean); + +const goTestSumFlags = [ + "--format-hide-empty-pkg", + "--hide-summary", + "skipped", +]; + +/** + * Collects all baseline files that were used during the test run. + * @param {string} trackingDir + * @returns {Promise>} + */ +async function collectUsedBaselines(trackingDir) { + /** @type {Set} */ + const usedBaselines = new Set(); + if (!fs.existsSync(trackingDir)) { + return usedBaselines; + } + + const trackingFiles = await fs.promises.readdir(trackingDir); + for (const file of trackingFiles) { + const content = await fs.promises.readFile(path.join(trackingDir, file), "utf-8"); + for (const line of content.split("\n")) { + const trimmed = line.trim(); + if (trimmed) { + usedBaselines.add(trimmed); + } + } + } + return usedBaselines; +} + +/** + * Checks for unused baseline files and reports them. + * @param {string} trackingDir + * @returns {Promise} List of unused baseline file paths. + */ +async function checkUnusedBaselines(trackingDir) { + const usedBaselines = await collectUsedBaselines(trackingDir); + if (usedBaselines.size === 0) { + // No baselines recorded - either no tests ran or tracking wasn't set up properly + return []; + } + + const allBaselines = await glob(`${refBaseline}/**`, { nodir: true }); + const unusedBaselines = allBaselines + .map(p => path.relative(refBaseline, p)) + .filter(p => !usedBaselines.has(p)); + + return unusedBaselines; +} + +const $test = $({ env: goTestEnv, cwd: "./tsc" }); + +/** + * @param {string} taskName + */ +function gotestsum(taskName) { + const args = isInstalled("gotestsum") ? ["gotestsum", ...goTestSumFlags, "--"] : ["go", "test"]; + return args.concat(goTestFlags(taskName)); +} + +/** + * @param {string} taskName + */ +function goTest(taskName) { + return ["go", "test"].concat(goTestFlags(taskName)); +} + +async function runTests() { + if (!options.dirty) { + await rimraf(localBaseline); + await fs.promises.mkdir(localBaseline, { recursive: true }); + } + + // Create a tmp directory for baseline tracking if enabled + /** @type {string | undefined} */ + let trackingDir; + /** @type {(() => void) | undefined} */ + let cleanupTracking; + + if (baselineTrackingEnabled) { + const tmpDir = tmp.dirSync({ prefix: "tsgo-baseline-tracking-", unsafeCleanup: true }); + trackingDir = tmpDir.name; + cleanupTracking = tmpDir.removeCallback; + } + + try { + const testEnv = { + ...goTestEnv, + ...(trackingDir ? { TSGO_BASELINE_TRACKING_DIR: trackingDir } : {}), + }; + const $testWithTracking = $({ env: testEnv, cwd: "./tsc" }); + await $testWithTracking`${gotestsum("tests")} ./... ${isCI ? ["--timeout=45m"] : []}`; + + // Check for unused baselines after tests complete + if (trackingDir) { + const unusedBaselines = await checkUnusedBaselines(trackingDir); + if (unusedBaselines.length > 0) { + console.error(pc.red(`\nFound ${unusedBaselines.length} unused baseline file(s):`)); + for (const baseline of unusedBaselines.slice(0, 20)) { + console.error(pc.red(` ${baseline}`)); + } + if (unusedBaselines.length > 20) { + console.error(pc.red(` ... and ${unusedBaselines.length - 20} more`)); + } + + // Create .delete files for each unused baseline so baseline-accept can remove them + for (const baseline of unusedBaselines) { + const deleteFilePath = path.join(localBaseline, baseline + ".delete"); + await fs.promises.mkdir(path.dirname(deleteFilePath), { recursive: true }); + await fs.promises.writeFile(deleteFilePath, ""); + } + console.error(pc.red(`\nRun 'hereby baseline-accept' to delete them.`)); + + throw new Error(`Found ${unusedBaselines.length} unused baseline file(s). Run 'hereby baseline-accept' to delete them.`); + } + } + } + finally { + if (cleanupTracking) { + cleanupTracking(); + } + } +} + +async function runTestExtension() { + await $`npm test -w native-preview`; +} + +export const testTsc = task({ + name: "test:tsc", + description: "Runs all tests in the tsc module.", + run: runTests, +}); + +export const test = task({ + name: "test", + description: "Runs all tests. This is the most typical test task to need.", + run: async () => { + await runTests(); + await runTestExtension(); + }, +}); + +async function runTestBenchmarks() { + // Run the benchmarks once to ensure they compile and run without errors. + await $test`${goTest("benchmarks")} -run=- -bench=. -benchtime=1x ./...`; +} + +export const testBenchmarks = task({ + name: "test:benchmarks", + description: "Runs all benchmarks.", + run: runTestBenchmarks, +}); + +async function runTestTools() { + await $test({ cwd: path.join(__dirname, "tools") })`${gotestsum("tools")} ./...`; +} + +async function runTestAPI() { + // await $`npm run -w @typescript/typescript test:only`; // doesn't work on windows - some path escaping isn't done correctly, test runner runs no tests + await _$({ verbose: "short", stdio: "inherit", cwd: "./packages/typescript" })`node --experimental-strip-types --no-warnings --conditions @typescript/source --test ./test/**/*.test.ts`; +} + +export const testTools = task({ + name: "test:tools", + description: "Runs all tests in the tools module.", + run: runTestTools, +}); + +export const testExtension = task({ + name: "test:extension", + description: "Runs the VS Code extension tests.", + run: runTestExtension, +}); + +export const buildAPI = task({ + name: "build:api", + description: "Builds @typescript/typescript JS API.", + run: async () => { + await $`npm run -w @typescript/typescript build`; + }, +}); + +export const buildAPITests = task({ + name: "build:api:test", + description: "Builds the @typescript/typescript JS API tests.", + dependencies: [generateEnums, generateAPI], + run: async () => { + await $`npm run -w @typescript/typescript build:test`; + }, +}); + +export const testAPI = task({ + name: "test:api", + description: "Runs the @typescript/typescript JS API tests.", + dependencies: [tsgo, buildAPITests], + run: runTestAPI, +}); + +export const testAll = task({ + name: "test:all", + description: "Runs ALL tests in the repo, including benchmarks, tools, and the API tests.", + dependencies: [tsgo, buildAPITests], + run: async () => { + // Prevent interleaving by running these directly instead of in parallel. + await runTests(); + await runTestExtension(); + await runTestBenchmarks(); + await runTestTools(); + await runTestAPI(); + }, +}); + +const customLinterPath = "./tools/custom-gcl"; +const customLinterHashPath = customLinterPath + ".hash"; + +const golangciLintPackage = memoize(() => { + const golangciLintYml = fs.readFileSync(".custom-gcl.yml", "utf8"); + const pattern = /^version:\s*(v\d+\.\d+\.\d+).*$/m; + const match = pattern.exec(golangciLintYml); + if (!match) { + throw new Error("Expected version in .custom-gcl.yml"); + } + const version = match[1]; + const major = version.split(".")[0]; + const versionSuffix = ["v0", "v1"].includes(major) ? "" : "/" + major; + + return `github.com/golangci/golangci-lint${versionSuffix}/cmd/golangci-lint@${version}`; +}); + +const customlintHash = memoize(() => { + const files = glob.sync([ + "./tools/go.mod", + "./tools/customlint/**/*", + "./.custom-gcl.yml", + ], { + ignore: "**/testdata/**", + nodir: true, + absolute: true, + }); + files.sort(); + + const hash = crypto.createHash("sha256"); + + for (const file of files) { + hash.update(file); + hash.update(fs.readFileSync(file)); + } + + return hash.digest("hex") + "\n"; +}); + +const buildCustomLinter = memoize(async () => { + const hash = customlintHash(); + if ( + isInstalled(customLinterPath) + && fs.existsSync(customLinterHashPath) + && fs.readFileSync(customLinterHashPath, "utf8") === hash + ) { + return; + } + + await $`go run ${golangciLintPackage()} custom`; + await $`${customLinterPath} cache clean`; + + fs.writeFileSync(customLinterHashPath, hash); +}); + +export const lint = task({ + name: "lint", + description: "Runs golangci-lint.", + run: runLint, +}); + +async function runLint() { + await buildCustomLinter(); + + const lintArgs = ["run"]; + if (defaultGoBuildTags.length) { + lintArgs.push("--build-tags", defaultGoBuildTags.join(",")); + } + if (options.fix) { + lintArgs.push("--fix"); + } + + const resolvedCustomLinterPath = path.resolve(customLinterPath); + await $({ cwd: "./tsc" })`${resolvedCustomLinterPath} ${lintArgs} --config ../.golangci.yml`; + console.log("Linting tools"); + await $({ cwd: "./tools" })`${resolvedCustomLinterPath} ${lintArgs} --config ../.golangci.yml`; +} + +export const installTools = task({ + name: "install-tools", + description: "Installs optional tools for developing within the repo.", + run: async () => { + await Promise.all([ + ...[...tools].map(([tool, version]) => $`go install ${tool}${version ? `@${version}` : ""}`), + buildCustomLinter(), + ]); + }, +}); + +export const format = task({ + name: "format", + description: "Formats the repo.", + run: runFormat, +}); + +async function runFormat() { + await $`dprint fmt`; + await fixLineEndings(); +} + +const lineEndingExclusions = [ + ":(exclude)tsc/testdata/**", + // TODO: Remove this exclusion when this directory is converted or removed. + ":(exclude)tsc/internal/locale/lcl/**", +]; + +export const fixLineEndingsTask = task({ + name: "fix:line-endings", + description: "Converts tracked files that Git expects to use LF.", + run: fixLineEndings, +}); + +async function fixLineEndings() { + const result = await _$`git ls-files --eol -z -- . ${lineEndingExclusions}`; + const files = []; + for (const record of result.stdout.split("\0")) { + if (!record) { + continue; + } + const separator = record.indexOf("\t"); + assert(separator !== -1); + const eolInfo = record.slice(0, separator); + if (!/\bw\/(?:crlf|mixed)\b/.test(eolInfo) || !eolInfo.includes("eol=lf")) { + continue; + } + files.push(record.slice(separator + 1)); + } + + await Promise.all(files.map(async file => { + const contents = await fs.promises.readFile(file); + const normalized = Buffer.from(contents.toString("latin1").replaceAll("\r\n", "\n"), "latin1"); + await fs.promises.writeFile(file, normalized); + })); + + if (files.length) { + console.log(`Converted ${files.length} file(s) to LF.`); + } +} + +export const checkFormat = task({ + name: "check:format", + description: "Checks that the repo is formatted.", + run: async () => { + await $`dprint check`; + await checkLineEndings(); + }, +}); + +async function checkLineEndings() { + const result = await _$({ reject: false })`git grep --cached -Il ${"\r"} -- . ${lineEndingExclusions}`; + if (result.exitCode === 1) { + return; + } + if (result.exitCode !== 0) { + throw new Error(`Failed to check line endings:\n${result.stderr}`); + } + throw new Error(`Files must use LF line endings:\n${result.stdout}`); +} + +const scriptTsconfigs = [ + "./tools/scripts/tsc/tsconfig.json", + "./tsc/internal/lsp/lsproto/_generate/tsconfig.json", +]; + +export const checkScripts = task({ + name: "check:scripts", + description: "Type-checks TypeScript scripts.", + run: async () => { + for (const tsconfig of scriptTsconfigs) { + console.log(`Type-checking ${tsconfig}`); + await $`tsc -p ${tsconfig}`; + } + }, +}); + +/** + * @param {string} localBaseline Path to the local copy of the baselines + * @param {string} refBaseline Path to the reference copy of the baselines + */ +function baselineAcceptTask(localBaseline, refBaseline) { + /** + * @param {string} p + */ + function localPathToRefPath(p) { + const relative = path.relative(localBaseline, p); + return path.join(refBaseline, relative); + } + + return async () => { + const toCopy = await glob(`${localBaseline}/**`, { nodir: true, ignore: `${localBaseline}/**/*.delete` }); + for (const p of toCopy) { + const out = localPathToRefPath(p); + await fs.promises.mkdir(path.dirname(out), { recursive: true }); + await fs.promises.copyFile(p, out); + } + const toDelete = await glob(`${localBaseline}/**/*.delete`, { nodir: true }); + for (const p of toDelete) { + const out = localPathToRefPath(p).replace(/\.delete$/, ""); + await rimraf(out); + await rimraf(p); // also delete the .delete file so that it no longer shows up in a diff tool. + } + }; +} + +const localBaseline = "tsc/testdata/baselines/local/"; +const refBaseline = "tsc/testdata/baselines/reference/"; + +export const baselineAccept = task({ + name: "baseline-accept", + description: "Makes the most recent test results the new baseline, overwriting the old baseline.", + run: baselineAcceptTask(localBaseline, refBaseline), +}); + +function getDiffTool() { + const program = process.env.DIFF; + if (!program) { + console.warn("Add the 'DIFF' environment variable to the path of the program you want to use."); + process.exit(1); + } + return program; +} + +export const diff = task({ + name: "diff", + description: "Diffs baselines using the diff tool specified by the 'DIFF' environment variable", + run: () => $`${getDiffTool()} ${refBaseline} ${localBaseline}`, +}); + +/** + * @param {fs.PathLike} p + */ +function rimraf(p) { + // The rimraf package uses maxRetries=10 on Windows, but Node's fs.rm does not have that special case. + return fs.promises.rm(p, { recursive: true, force: true, maxRetries: process.platform === "win32" ? 10 : 0 }); +} + +/** @typedef {{ + * name: string; + * paths: string | string[]; + * ignored?: (path: string) => boolean; + * run: (paths: Set, abortSignal: AbortSignal) => void | Promise; + * }} WatchTask */ +void 0; + +/** + * @param {string} name + * @param {(paths: Set | undefined, abortSignal: AbortSignal) => void | Promise} run + * @param {object} options + * @param {string | string[]} options.paths + * @param {(path: string) => boolean} [options.ignored] + * @param {string} [options.name] + */ +async function watchDebounced(name, run, options) { + let watching = true; + let running = true; + let lastChangeTimeMs = Date.now(); + let changedDeferred = /** @type {Deferred} */ (new Deferred()); + let abortController = new AbortController(); + + const debouncer = new Debouncer(1_000, endRun); + const watcher = chokidar.watch(options.paths, { + ignored: options.ignored, + ignorePermissionErrors: true, + alwaysStat: true, + }); + // The paths that have changed since the last run. + /** @type {Set | undefined} */ + let paths; + + process.on("SIGINT", endWatchMode); + process.on("beforeExit", endWatchMode); + watcher.on("all", onChange); + + while (watching) { + const promise = changedDeferred.promise; + const token = abortController.signal; + if (!token.aborted) { + running = true; + try { + const thePaths = paths; + paths = new Set(); + await run(thePaths, token); + } + catch { + // ignore + } + running = false; + } + if (watching) { + console.log(pc.yellowBright(`[${name}] run complete, waiting for changes...`)); + await promise; + } + } + + console.log("end"); + + /** + * @param {'add' | 'addDir' | 'change' | 'unlink' | 'unlinkDir' | 'all' | 'ready' | 'raw' | 'error'} eventName + * @param {string} path + * @param {fs.Stats | undefined} stats + */ + function onChange(eventName, path, stats) { + switch (eventName) { + case "change": + case "unlink": + case "unlinkDir": + break; + case "add": + case "addDir": + // skip files that are detected as 'add' but haven't actually changed since the last time we ran. + if (stats && stats.mtimeMs <= lastChangeTimeMs) { + return; + } + break; + } + beginRun(path); + } + + /** + * @param {string} path + */ + function beginRun(path) { + if (debouncer.empty) { + console.log(pc.yellowBright(`[${name}] changed due to '${path}', restarting...`)); + if (running) { + console.log(pc.yellowBright(`[${name}] aborting in-progress run...`)); + } + abortController.abort(); + abortController = new AbortController(); + } + + debouncer.enqueue(); + paths ??= new Set(); + paths.add(path); + } + + function endRun() { + lastChangeTimeMs = Date.now(); + changedDeferred.resolve(); + changedDeferred = /** @type {Deferred} */ (new Deferred()); + } + + function endWatchMode() { + if (watching) { + watching = false; + console.log(pc.yellowBright(`[${name}] exiting watch mode...`)); + abortController.abort(); + watcher.close(); + } + } +} + +/** + * @template T + */ +export class Deferred { + constructor() { + /** @type {Promise} */ + this.promise = new Promise((resolve, reject) => { + this.resolve = resolve; + this.reject = reject; + }); + } +} + +export class Debouncer { + /** + * @param {number} timeout + * @param {() => Promise | void} action + */ + constructor(timeout, action) { + this._timeout = timeout; + this._action = action; + /** @type {ReturnType | undefined} */ + this._timer = undefined; + /** @type {Deferred | undefined} */ + this._deferred = undefined; + } + + get empty() { + return !this._deferred; + } + + enqueue() { + if (this._timer) { + clearTimeout(this._timer); + this._timer = undefined; + } + + if (!this._deferred) { + this._deferred = new Deferred(); + } + + this._timer = setTimeout(() => this.run(), 100); + return this._deferred.promise; + } + + run() { + if (this._timer) { + clearTimeout(this._timer); + this._timer = undefined; + } + + const deferred = this._deferred; + assert(deferred); + this._deferred = undefined; + try { + deferred.resolve(this._action()); + } + catch (e) { + deferred.reject(e); + } + } +} + +const getVersion = memoize(() => { + if (nativePreviewReleaseVersion) { + return nativePreviewReleaseVersion; + } + + const f = fs.readFileSync("./tsc/internal/core/version.go", "utf8"); + + const match = f.match(/var version\s*=\s*"(\d+\.\d+\.\d+)(-[^"]+)?"/); + if (!match) { + throw new Error("Failed to extract version from version.go"); + } + + let version = match[1]; + if (options.setPrerelease) { + version += `-${options.setPrerelease}`; + } + else if (match[2]) { + version += match[2]; + } + + return version; +}); + +function getPublishTag() { + if (publishAsTypescript) { + const version = getVersion(); + if (!version) { + throw new Error("Publishing as 'typescript' requires a version before selecting an npm tag."); + } + const match = version.match(/-(dev|beta|rc)(?:[.-]|$)/); + if (match?.[1]) return match[1] === "dev" ? "next" : match[1]; + if (version === nativePreviewReleaseVersion) return "latest"; + throw new Error(`Refusing to publish 'typescript' with the latest tag from non-release version ${version}.`); + } + return "latest"; +} + +const extensionDir = path.resolve("./packages/vscode-typescript"); +const nightlyExtensionDir = path.resolve("./packages/vscode-typescript-nightly"); +const builtNpm = path.resolve("./built/npm"); +const builtVsix = path.resolve("./built/vsix"); +const builtPublishedPlatformPackages = path.resolve("./built/published-platform-packages"); +const builtSignTmp = path.resolve("./built/sign-tmp"); +const publishedTypeScriptAliasPackageName = "@typescript/bundled-typescript"; +const releasePackageEnv = { COREPACK_ENABLE_STRICT: "0" }; + +const getSignTempDir = memoize(async () => { + const dir = path.resolve(builtSignTmp); + await rimraf(dir); + await fs.promises.mkdir(dir, { recursive: true }); + return dir; +}); + +const cleanSignTempDirectory = task({ + name: "clean:sign-tmp", + hiddenFromTaskList: true, + run: runCleanSignTempDirectory, +}); + +function runCleanSignTempDirectory() { + return rimraf(builtSignTmp); +} + +let signCount = 0; + +/** + * @typedef {{ + * SignFileRecordList: { + * SignFileList: { SrcPath: string; DstPath: string | null }[]; + * Certs: Cert; + * MacAppName: string | undefined + * }[] + * }} DDSignFileList + * + * @param {DDSignFileList} filelist + */ +async function sign(filelist, unchangedOutputOkay = false) { + let data = JSON.stringify(filelist, undefined, 4); + console.log("filelist:", data); + + if (!process.env.MBSIGN_APPFOLDER) { + console.log(pc.yellow("Faking signing because MBSIGN_APPFOLDER is not set.")); + + // Fake signing for testing. + + for (const record of filelist.SignFileRecordList) { + for (const file of record.SignFileList) { + const src = file.SrcPath; + const dst = file.DstPath ?? src; + + if (!fs.existsSync(src)) { + throw new Error(`Source file does not exist: ${src}`); + } + + const dstDir = path.dirname(dst); + if (!fs.existsSync(dstDir)) { + throw new Error(`Destination directory does not exist: ${dstDir}`); + } + + if (dst.endsWith(".sig")) { + console.log(`Faking signature for ${src} -> ${dst}`); + // No great way to fake a signature. + await fs.promises.writeFile(dst, "fake signature"); + } + else { + if (src === dst) { + console.log(`Faking signing ${src}`); + } + else { + console.log(`Faking signing ${src} -> ${dst}`); + } + const contents = await fs.promises.readFile(src); + await fs.promises.writeFile(dst, contents); + } + } + } + + return; + } + + const signingWorkaround = true; + + /** @type {{ source: string; target: string }[]} */ + const signingWorkaroundFiles = []; + + if (signingWorkaround) { + // DstPath is currently broken in the signing tool. + // Copy all of the files to a new tempdir and then leave DstPath unset + // so that it's overwritten, then move the file to the destination. + console.log("Working around DstPath bug"); + + /** @type {DDSignFileList} */ + const newFileList = { + SignFileRecordList: filelist.SignFileRecordList.map(list => { + return { + Certs: list.Certs, + SignFileList: list.SignFileList.map(file => { + const dstPath = file.DstPath; + if (dstPath === null) { + return file; + } + + const src = file.SrcPath; + // File extensions must be preserved; use a prefix. + const dstPathTemp = `${path.dirname(src)}/signing-temp-${path.basename(src)}`; + + console.log(`Copying: ${src} -> ${dstPathTemp}`); + fs.cpSync(src, dstPathTemp); + + signingWorkaroundFiles.push({ source: dstPathTemp, target: dstPath }); + + return { + SrcPath: dstPathTemp, + DstPath: null, + }; + }), + MacAppName: list.MacAppName, + }; + }), + }; + + data = JSON.stringify(newFileList, undefined, 4); + console.log("new filelist:", data); + } + + /** @type {Map} */ + const srcHashes = new Map(); + + for (const record of filelist.SignFileRecordList) { + for (const file of record.SignFileList) { + const src = file.SrcPath; + const dst = file.DstPath ?? src; + + if (!fs.existsSync(src)) { + throw new Error(`Source file does not exist: ${src}`); + } + + const hash = crypto.createHash("sha256").update(fs.readFileSync(src)).digest("hex"); + srcHashes.set(src, hash); + + console.log(`Will sign ${src} -> ${dst}`); + console.log(` sha256: ${hash}`); + } + } + + const tmp = await getSignTempDir(); + const filelistPath = path.resolve(tmp, `signing-filelist-${signCount++}.json`); + await fs.promises.writeFile(filelistPath, data); + + try { + const dll = path.join(process.env.MBSIGN_APPFOLDER, "DDSignFiles.dll"); + const filelistFlag = `/filelist:${filelistPath}`; + await $`dotnet ${dll} -- ${filelistFlag}`; + } + finally { + await fs.promises.unlink(filelistPath); + } + + if (signingWorkaround) { + // Now, copy the files back. + for (const { source, target } of signingWorkaroundFiles) { + console.log(`Moving signed file: ${source} -> ${target}`); + await fs.promises.rename(source, target); + } + } + + /** @type {string[]} */ + let failures = []; + + for (const record of filelist.SignFileRecordList) { + for (const file of record.SignFileList) { + const src = file.SrcPath; + const dst = file.DstPath ?? src; + + if (!fs.existsSync(dst)) { + failures.push(`Signed file does not exist: ${dst}`); + const newSrcHash = crypto.createHash("sha256").update(fs.readFileSync(src)).digest("hex"); + const oldSrcHash = srcHashes.get(src); + assert(oldSrcHash); + if (oldSrcHash !== newSrcHash) { + failures.push(` Source file changed during signing: ${src}\n before: ${oldSrcHash}\n after: ${newSrcHash}`); + } + continue; + } + + const srcHash = srcHashes.get(src); + assert(srcHash); + const dstHash = crypto.createHash("sha256").update(fs.readFileSync(dst)).digest("hex"); + if (srcHash === dstHash) { + const message = `Signed file is identical to source file (not signed?): ${src} -> ${dst}\n sha256: ${dstHash}`; + if (unchangedOutputOkay) { + console.log(message); + } + else { + failures.push(message); + continue; + } + } + + if (src === dst) { + console.log(`Signed ${src}`); + } + else { + console.log(`Signed ${src} -> ${dst}`); + } + console.log(` sha256: ${dstHash}`); + } + } + + if (failures.length) { + throw new Error("Some files failed to sign:\n" + failures.map(f => " - " + f).join("\n")); + } +} + +/** + * @param {string} src + * @param {string} dest + * @param {(p: string) => boolean} [filter] + */ +function cpRecursive(src, dest, filter) { + return fs.promises.cp(src, dest, { + recursive: true, + filter: filter ? src => filter(src.replace(/\\/g, "/")) : undefined, + }); +} + +/** + * @param {string} src + * @param {string} dest + */ +function cpWithoutNodeModulesOrTsconfig(src, dest) { + return cpRecursive(src, dest, p => !p.endsWith("/node_modules") && !p.endsWith("/tsconfig.json")); +} + +const mainNativePreviewPackage = { + npmPackageName: publishAsTypescript ? "typescript" : "@typescript/typescript", + npmDir: path.join(builtNpm, publishAsTypescript ? "typescript" : "native-preview"), + npmTarball: path.join(builtNpm, publishAsTypescript ? "typescript.tgz" : "native-preview.tgz"), +}; + +const typescriptMacEntitlements = [ + "com.apple.security.cs.allow-dyld-environment-variables", + "com.apple.security.cs.disable-library-validation", +]; + +function createTypeScriptMacEntitlementsPlist() { + const entries = typescriptMacEntitlements.map(entitlement => ` ${entitlement}\n `).join("\n"); + return ` + + + +${entries} + + +`; +} + +/** + * @typedef {"win32" | "linux" | "darwin" | "aix" | "android" | "freebsd" | "netbsd" | "openbsd" | "sunos"} OS + * @typedef {"x64" | "arm" | "arm64" | "ia32" | "ppc64" | "loong64" | "mips64el" | "riscv64" | "s390x"} Arch + * @typedef {"Microsoft400" | "LinuxSign" | "MacDeveloperHarden" | "8020" | "VSCodePublisher"} Cert + * @typedef {`${OS | "alpine"}-${Exclude | "armhf"}`} VSCodeTarget + * @typedef {{ name: string; sourceDir: string }} VsixExtensionPackage + * @typedef {{ nodeOs: string; vscodeTarget: string; sourceDir: string; extensionDir: string; vsixPath: string; vsixManifestPath: string; vsixSignaturePath: string }} VsixExtension + * @typedef {{ GOOS: string; GOARCH: string }} GoDistTarget + * @typedef {{ os: OS; arch: Arch; cert?: Cert; vsix?: boolean; alpine?: boolean }} Platform + */ +void 0; + +/** @type {VsixExtensionPackage[]} */ +const vsixExtensionPackages = [ + ...(produceNativePreviewVsix ? [{ name: "native-preview", sourceDir: extensionDir }] : []), + ...(produceTypeScriptNightlyVsix ? [{ name: "vscode-typescript-nightly", sourceDir: nightlyExtensionDir }] : []), +]; + +/** + * npm package platforms supported by the native release. + * The native-preview package publishes only the entries with vsix: true; + * the typescript package publishes the full list. + * BSD targets that are not in Node's supported-platforms table are best-effort + * and limited to mainstream 64-bit x64/arm64 architectures. + * alpine is set only for the subset that also produces Alpine VSIXes. + * cert defaults to LinuxSign. + * @type {Platform[]} + */ +const platforms = [ + { os: "win32", arch: "x64", vsix: true, cert: "Microsoft400" }, + { os: "win32", arch: "arm64", vsix: true, cert: "Microsoft400" }, + { os: "linux", arch: "x64", vsix: true, alpine: true }, + { os: "linux", arch: "arm", vsix: true }, + { os: "linux", arch: "arm64", vsix: true, alpine: true }, + { os: "darwin", arch: "x64", vsix: true, cert: "MacDeveloperHarden" }, + { os: "darwin", arch: "arm64", vsix: true, cert: "MacDeveloperHarden" }, + { os: "aix", arch: "ppc64" }, + { os: "android", arch: "arm64" }, + { os: "freebsd", arch: "arm64" }, + { os: "freebsd", arch: "x64" }, + { os: "linux", arch: "loong64" }, + { os: "linux", arch: "mips64el" }, + { os: "linux", arch: "ppc64" }, + { os: "linux", arch: "riscv64" }, + { os: "linux", arch: "s390x" }, + { os: "netbsd", arch: "arm64" }, + { os: "netbsd", arch: "x64" }, + { os: "openbsd", arch: "arm64" }, + { os: "openbsd", arch: "x64" }, + { os: "sunos", arch: "x64" }, + // Wasm? +]; + +const ignoredGoTargets = new Map([ + ["android/386", "Android is not a Node runtime target TypeScript supports"], + ["android/amd64", "Android is not a Node runtime target TypeScript supports"], + ["android/arm", "Android is not a Node runtime target TypeScript supports"], + ["freebsd/386", "FreeBSD is experimental in Node and limited here to mainstream 64-bit x64/arm64"], + ["freebsd/arm", "FreeBSD is experimental in Node and limited here to mainstream 64-bit x64/arm64"], + ["linux/386", "ia32 means 32-bit x86, which TypeScript does not support for native packages"], + ["linux/ppc64", "Node supports Linux ppc64le; npm's ppc64 CPU name cannot select big-endian ppc64 separately"], + ["netbsd/386", "NetBSD is not in Node's supported-platforms table and is limited here to mainstream 64-bit x64/arm64"], + ["netbsd/arm", "NetBSD is not in Node's supported-platforms table and is limited here to mainstream 64-bit x64/arm64"], + ["openbsd/386", "OpenBSD is not in Node's supported-platforms table and is limited here to mainstream 64-bit x64/arm64"], + ["openbsd/arm", "OpenBSD is not in Node's supported-platforms table and is limited here to mainstream 64-bit x64/arm64"], + ["openbsd/ppc64", "OpenBSD is not in Node's supported-platforms table and is limited here to mainstream 64-bit x64/arm64"], + ["openbsd/riscv64", "OpenBSD is not in Node's supported-platforms table and is limited here to mainstream 64-bit x64/arm64"], + ["solaris/amd64", "Node documents SmartOS/sunos rather than Oracle Solaris; sunos-x64 publishes illumos/amd64 for that runtime family"], + ["windows/386", "ia32 means 32-bit x86, which TypeScript does not support for native packages"], +]); + +/** + * @param {string} os + * @returns {"windows" | "illumos" | "darwin" | "linux" | "aix" | "android" | "freebsd" | "netbsd" | "openbsd"} + */ +function nodeToGOOS(os) { + switch (os) { + case "win32": + return "windows"; + case "sunos": + return "illumos"; + case "darwin": + case "linux": + case "aix": + case "android": + case "freebsd": + case "netbsd": + case "openbsd": + return os; + default: + throw new Error(`Unsupported OS: ${os}`); + } +} + +/** + * @param {string} arch + * @param {string} os + * @returns {"amd64" | "386" | "mips64le" | "ppc64" | "ppc64le" | "arm" | "arm64" | "loong64" | "riscv64" | "s390x"} + */ +function nodeToGOARCH(arch, os) { + switch (arch) { + case "x64": + return "amd64"; + case "ia32": + return "386"; + case "mips64el": + return "mips64le"; + case "ppc64": + return os === "aix" ? "ppc64" : "ppc64le"; + case "arm": + case "arm64": + case "loong64": + case "riscv64": + case "s390x": + return arch; + default: + throw new Error(`Unsupported ARCH: ${arch}`); + } +} + +const getPlatforms = memoize(() => { + const publishTag = getPublishTag(); + let supportedPlatforms = publishAsTypescript && publishTag !== "next" + ? platforms + : platforms.filter(({ vsix }) => vsix); + + if (!options.forRelease) { + supportedPlatforms = supportedPlatforms.filter(({ os, arch }) => os === process.platform && arch === process.arch); + assert.equal(supportedPlatforms.length, 1, "No supported platforms found"); + } + + return supportedPlatforms.map(({ os, arch, cert = "LinuxSign", vsix, alpine }) => { + const packageBaseName = publishAsTypescript ? "typescript" : "native-preview"; + const npmDirName = `${packageBaseName}-${os}-${arch}`; + const npmDir = path.join(builtNpm, npmDirName); + const npmTarball = `${npmDir}.tgz`; + const npmPackageName = `@typescript/${npmDirName}`; + + /** @type {VsixExtension[]} */ + let extensions = []; + if (produceAnyVsix && vsix) { + /** @type {string[]} */ + const vscodeTargets = [`${os}-${arch === "arm" ? "armhf" : arch}`]; + if (alpine) { + vscodeTargets.push(`alpine-${arch === "arm" ? "armhf" : arch}`); + } + + extensions = vscodeTargets.flatMap(vscodeTarget => + vsixExtensionPackages.map(({ name: packageName, sourceDir }) => { + const extensionDir = path.join(builtVsix, `${packageName}-${vscodeTarget}`); + const vsixPath = extensionDir + ".vsix"; + const vsixManifestPath = extensionDir + ".manifest"; + const vsixSignaturePath = extensionDir + ".signature.p7s"; + return { + nodeOs: os, + vscodeTarget, + sourceDir, + extensionDir, + vsixPath, + vsixManifestPath, + vsixSignaturePath, + }; + }) + ); + } + + return { + nodeOs: os, + nodeArch: arch, + goos: nodeToGOOS(os), + goarch: nodeToGOARCH(arch, os), + npmPackageName, + npmDirName, + npmDir, + npmTarball, + extensions, + cert, + }; + }); +}); + +export const checkPlatforms = task({ + name: "typescript:check-platforms", + hiddenFromTaskList: true, + run: runCheckPlatforms, +}); + +/** + * @param {GoDistTarget} target + */ +function goDistTargetToPlatform(target) { + const goTarget = `${target.GOOS}/${target.GOARCH}`; + if (ignoredGoTargets.has(goTarget)) { + return undefined; + } + + /** @type {OS | undefined} */ + let nodeOs; + switch (target.GOOS) { + case "windows": + nodeOs = "win32"; + break; + case "illumos": + nodeOs = "sunos"; + break; + case "aix": + case "android": + case "darwin": + case "freebsd": + case "linux": + case "netbsd": + case "openbsd": + nodeOs = target.GOOS; + break; + default: + return undefined; + } + + /** @type {Arch | undefined} */ + let nodeArch; + switch (target.GOARCH) { + case "386": + nodeArch = "ia32"; + break; + case "amd64": + nodeArch = "x64"; + break; + case "mips64le": + nodeArch = "mips64el"; + break; + case "ppc64": + nodeArch = "ppc64"; + break; + case "ppc64le": + nodeArch = "ppc64"; + break; + case "arm": + case "arm64": + case "loong64": + case "riscv64": + case "s390x": + nodeArch = target.GOARCH; + break; + default: + return undefined; + } + + return `${nodeOs}-${nodeArch}`; +} + +async function runCheckPlatforms() { + const { stdout } = await $pipe`go tool dist list -json`; + /** @type {GoDistTarget[]} */ + const goTargets = JSON.parse(stdout); + const goTargetSet = new Set(goTargets.map(({ GOOS, GOARCH }) => `${GOOS}/${GOARCH}`)); + + /** @type {[os: OS, arch: Arch][]} */ + const packagePlatforms = platforms.map(({ os, arch }) => /** @type {[OS, Arch]} */ ([os, arch])); + const actual = new Set(packagePlatforms.map(([os, arch]) => `${os}-${arch}`)); + const expected = new Set(goTargets.map(goDistTargetToPlatform).filter(platform => platform !== undefined)); + + const errors = []; + for (const [os, arch] of packagePlatforms) { + const goTarget = `${nodeToGOOS(os)}/${nodeToGOARCH(arch, os)}`; + if (!goTargetSet.has(goTarget)) { + errors.push(`Configured package platform ${os}-${arch} maps to unsupported Go target ${goTarget}.`); + } + } + + const missing = [...expected].filter(platform => !actual.has(platform)); + if (missing.length) { + errors.push(`Missing package platform(s) for the current Go toolchain: ${missing.join(", ")}.`); + } + + const extra = [...actual].filter(platform => !expected.has(platform)); + if (extra.length) { + errors.push(`Unexpected package platform(s), or missing exclusion policy: ${extra.join(", ")}.`); + } + + if (errors.length) { + throw new Error(`native-preview platform list is out of sync with 'go tool dist list':\n${errors.map(e => ` - ${e}`).join("\n")}`); + } +} + +/** + * Recursively strips `@typescript/source` export conditions from a package.json object. + * Processes `exports` and `imports` fields, skipping past subpath keys (starting with "." + * or "#") and recursing into condition objects. After removal, simplifies objects that have + * only a single `default` key down to their bare value. + * @param {Record} packageJson + */ +function stripSourceConditions(packageJson) { + for (const field of ["exports", "imports"]) { + if (packageJson[field] != null && typeof packageJson[field] === "object") { + packageJson[field] = stripConditionsFromValue(packageJson[field]); + } + } +} + +/** + * @param {any} value + * @returns {any} + */ +function stripConditionsFromValue(value) { + if (value == null || typeof value !== "object") { + return value; + } + delete value["@typescript/source"]; + for (const key of Object.keys(value)) { + value[key] = stripConditionsFromValue(value[key]); + } + // Simplify: if only "default" remains, collapse to its value. + const keys = Object.keys(value); + if (keys.length === 1 && keys[0] === "default") { + return value["default"]; + } + return value; +} + +export const buildNativePreviewPackages = task({ + name: "typescript:build", + hiddenFromTaskList: true, + run: runBuildNativePreviewPackages, +}); + +async function runBuildNativePreviewPackages() { + if (usePublishedPlatformPackagesForVsix) { + checkPublishedPlatformPackagesForVsix(); + await rimraf(builtNpm); + console.log("Skipping npm package builds; VSIX packaging will use published platform packages."); + return; + } + + await rimraf(builtNpm); + + const platforms = getPlatforms(); + + const inputDir = "./packages/typescript"; + + const inputPackageJson = JSON.parse(fs.readFileSync(path.join(inputDir, "package.json"), "utf8")); + inputPackageJson.version = getVersion(); + delete inputPackageJson.private; + inputPackageJson.files = [...new Set([...(inputPackageJson.files ?? []), "NOTICE.txt"])]; + if (publishAsTypescript) { + inputPackageJson.bin = { + tsc: "./bin/tsc", + }; + inputPackageJson.description = "TypeScript is a language for application scale JavaScript development"; + inputPackageJson.homepage = "https://www.typescriptlang.org/"; + inputPackageJson.keywords = [ + "TypeScript", + "Microsoft", + "compiler", + "language", + "javascript", + ]; + inputPackageJson.bugs = { + url: "https://github.com/microsoft/TypeScript/issues", + }; + inputPackageJson.repository = { + type: "git", + url: "https://github.com/microsoft/TypeScript.git", + }; + delete inputPackageJson.scripts; + delete inputPackageJson.devDependencies; + } + stripSourceConditions(inputPackageJson); + + const { stdout: gitHead } = await $pipe`git rev-parse HEAD`; + inputPackageJson.gitHead = gitHead; + inputPackageJson.publishConfig = { + access: "public", + tag: getPublishTag(), + }; + + const mainPackage = { + ...inputPackageJson, + name: mainNativePreviewPackage.npmPackageName, + optionalDependencies: Object.fromEntries(platforms.map(p => [p.npmPackageName, getVersion()])), + }; + + const mainPackageDir = mainNativePreviewPackage.npmDir; + + await fs.promises.mkdir(mainPackageDir, { recursive: true }); + + // Copy package contents excluding node_modules and dist (dist is copied separately after build). + // The package.json "files" field controls what npm pack actually includes. + await cpRecursive(inputDir, mainPackageDir, p => !p.endsWith("/node_modules") && !p.includes("/dist")); + if (publishAsTypescript) { + await fs.promises.writeFile(path.join(mainPackageDir, "bin", "tsc"), '#!/usr/bin/env node\nimport "../lib/tsc.js";\n'); + await fs.promises.chmod(path.join(mainPackageDir, "bin", "tsc"), 0o755); + await fs.promises.copyFile(path.join(inputDir, "typescript-package-readme.md"), path.join(mainPackageDir, "README.md")); + } + + await fs.promises.writeFile(path.join(mainPackageDir, "package.json"), JSON.stringify(mainPackage, undefined, 4)); + await fs.promises.copyFile("LICENSE.txt", path.join(mainPackageDir, "LICENSE")); + await fs.promises.copyFile("NOTICE.txt", path.join(mainPackageDir, "NOTICE.txt")); + + // Build JS API and copy dist into the package. + await $`npm run -w @typescript/typescript build`; + await cpRecursive(path.join(inputDir, "dist"), path.join(mainPackageDir, "dist")); + + // Validate that .d.ts files contain no external imports (all imports must start with "." or "#"). + const dtsFiles = await glob(`${mainPackageDir}/dist/**/*.d.ts`); + const importErrors = []; + for (const dtsFile of dtsFiles) { + const content = await fs.promises.readFile(dtsFile, "utf-8"); + const relPath = path.relative(mainPackageDir, dtsFile); + for (const [i, line] of content.split("\n").entries()) { + // Match: import ... from "specifier" / export ... from "specifier" + const fromMatch = line.match(/(?:import|export)\s.*?\sfrom\s+["']([^"']+)["']/); + if (fromMatch && !fromMatch[1].startsWith(".") && !fromMatch[1].startsWith("#")) { + importErrors.push(`${relPath}:${i + 1}: external import declaration "${fromMatch[1]}"`); + } + // Match: import("specifier") + for (const m of line.matchAll(/import\(["']([^"']+)["']\)/g)) { + if (!m[1].startsWith(".") && !m[1].startsWith("#")) { + importErrors.push(`${relPath}:${i + 1}: external dynamic import "${m[1]}"`); + } + } + } + } + if (importErrors.length) { + throw new Error(`Found external imports in .d.ts files:\n${importErrors.map(e => " " + e).join("\n")}`); + } + + const extraFlags = getReleaseBuildFlags(options.setPrerelease || nativePreviewReleaseVersion ? getVersion() : undefined); + + const platformBuilders = platforms.map(({ npmDir, npmPackageName, nodeOs, nodeArch, goos, goarch }) => async () => { + const packageJson = { + ...inputPackageJson, + bin: undefined, + files: ["lib", "NOTICE.txt"], + imports: undefined, + dependencies: undefined, + name: npmPackageName, + os: [nodeOs], + cpu: [nodeArch], + exports: { + "./package.json": "./package.json", + }, + }; + + const out = path.join(npmDir, "lib"); + await fs.promises.mkdir(out, { recursive: true }); + await fs.promises.writeFile(path.join(npmDir, "package.json"), JSON.stringify(packageJson, undefined, 4)); + await fs.promises.copyFile("LICENSE.txt", path.join(npmDir, "LICENSE")); + await fs.promises.copyFile("NOTICE.txt", path.join(npmDir, "NOTICE.txt")); + + const readme = [ + `# \`${npmPackageName}\``, + "", + `This package provides ${nodeOs}-${nodeArch} support for [${mainNativePreviewPackage.npmPackageName}](https://www.npmjs.com/package/${mainNativePreviewPackage.npmPackageName}).`, + ]; + + await fs.promises.writeFile(path.join(npmDir, "README.md"), readme.join("\n") + "\n"); + + await generateLibs(out); + + const exeName = nativePreviewExeName(nodeOs); + await buildTsc({ + out: publishAsTypescript ? path.join(out, exeName) : out, + env: { GOOS: goos, GOARCH: goarch, GOARM: "6", CGO_ENABLED: "0" }, + extraFlags, + }); + }); + + if (isCI) { + for (const build of platformBuilders) { + await build(); + // Build machines have too little space. + // Clear the Go build cache between platforms. + await $`go clean -cache`; + } + } + else { + const buildLimit = pLimit(os.availableParallelism()); + await Promise.all(platformBuilders.map(f => buildLimit(f))); + } +} + +export const signNativePreviewPackages = task({ + name: "typescript:sign", + hiddenFromTaskList: true, + run: runSignNativePreviewPackages, +}); + +/** + * @param {string} nodeOs + */ +function nativePreviewExeName(nodeOs) { + const baseName = publishAsTypescript ? "tsc" : "tsgo"; + return nodeOs === "win32" ? `${baseName}.exe` : baseName; +} + +async function runSignNativePreviewPackages() { + if (!options.forRelease) { + throw new Error("This task should not be run in non-release builds."); + } + if (usePublishedPlatformPackagesForVsix) { + checkPublishedPlatformPackagesForVsix(); + console.log("Skipping npm package signing; VSIX packaging will use published platform packages."); + return; + } + + const platforms = getPlatforms(); + + /** @type {Map} */ + const filelistByCert = new Map(); + for (const { npmDir, nodeOs, cert, npmDirName } of platforms) { + let certFilelist = filelistByCert.get(cert); + if (!certFilelist) { + filelistByCert.set(cert, certFilelist = []); + } + certFilelist.push({ + tmpName: npmDirName, + path: path.join(npmDir, "lib", nativePreviewExeName(nodeOs)), + }); + } + + const tmp = await getSignTempDir(); + const typescriptMacEntitlementsPath = path.join(tmp, "typescript-macos-entitlements.plist"); + await fs.promises.writeFile(typescriptMacEntitlementsPath, createTypeScriptMacEntitlementsPlist()); + + /** @type {DDSignFileList} */ + const filelist = { + SignFileRecordList: [], + }; + + /** @type {{ path: string; unsignedZipPath: string; signedZipPath: string; notarizedZipPath: string; }[]} */ + const macZips = []; + + // First, sign the files. + + for (const [cert, filelistPaths] of filelistByCert) { + switch (cert) { + case "Microsoft400": + filelist.SignFileRecordList.push({ + SignFileList: filelistPaths.map(p => ({ SrcPath: p.path, DstPath: null })), + Certs: cert, + MacAppName: undefined, + }); + break; + case "LinuxSign": + filelist.SignFileRecordList.push({ + SignFileList: filelistPaths.map(p => ({ SrcPath: p.path, DstPath: p.path + ".sig" })), + Certs: cert, + MacAppName: undefined, + }); + break; + case "MacDeveloperHarden": + // Mac signing requires putting files into zips and then signing those, + // along with a notarization step. + for (const p of filelistPaths) { + // ESRP preserves entitlements from an existing ad-hoc signature. + await $pipe`go -C ./tools run ./cmd/machotool sign ${typescriptMacEntitlementsPath} ${p.path}`; + + const unsignedZipPath = path.join(tmp, `${p.tmpName}.unsigned.zip`); + const signedZipPath = path.join(tmp, `${p.tmpName}.signed.zip`); + const notarizedZipPath = path.join(tmp, `${p.tmpName}.notarized.zip`); + + const zip = new AdmZip(); + zip.addLocalFile(p.path); + zip.writeZip(unsignedZipPath); + + macZips.push({ + path: p.path, + unsignedZipPath, + signedZipPath, + notarizedZipPath, + }); + } + filelist.SignFileRecordList.push({ + SignFileList: macZips.map(p => ({ SrcPath: p.unsignedZipPath, DstPath: p.signedZipPath })), + Certs: cert, + MacAppName: undefined, // MacAppName is only for notarization + }); + break; + default: + throw new Error(`Unknown cert: ${cert}`); + } + } + + await sign(filelist); + + // All of the files have been signed in place / had signatures added. + + if (macZips.length) { + // Now, notarize the Mac files. + + /** @type {DDSignFileList} */ + const notarizeFilelist = { + SignFileRecordList: [ + { + SignFileList: macZips.map(p => ({ SrcPath: p.signedZipPath, DstPath: p.notarizedZipPath })), + Certs: "8020", // "MacNotarize" (friendly name not supported by the tooling) + MacAppName: "MicrosoftTypeScript", + }, + ], + }; + + // Notarizing does not change the file, it just sends it to Apple, so ignore the case + // where the input files are the same as the output files. + await sign(notarizeFilelist, /*unchangedOutputOkay*/ true); + + // Finally, unzip the notarized files and move them back to their original locations. + + for (const p of macZips) { + const zip = new AdmZip(p.notarizedZipPath); + zip.extractEntryTo(path.basename(p.path), path.dirname(p.path), false, true); + } + + // chmod +x the unzipped files. + + for (const p of macZips) { + await fs.promises.chmod(p.path, 0o755); + await $pipe`go -C ./tools run ./cmd/machotool verify ${typescriptMacEntitlementsPath} ${p.path}`; + } + } +} + +export const packNativePreviewPackages = task({ + name: "typescript:pack", + hiddenFromTaskList: true, + dependencies: options.forRelease ? undefined : [buildNativePreviewPackages, cleanSignTempDirectory], + run: runPackNativePreviewPackages, +}); + +async function runPackNativePreviewPackages() { + if (usePublishedPlatformPackagesForVsix) { + checkPublishedPlatformPackagesForVsix(); + await rimraf(builtNpm); + console.log("Skipping npm package packing; VSIX packaging will use published platform packages."); + return; + } + + const platforms = getPlatforms(); + await Promise.all([mainNativePreviewPackage, ...platforms].map(async ({ npmDir, npmTarball }) => { + const { stdout } = await $pipe`npm pack --json ${npmDir}`; + const filename = JSON.parse(stdout)[0].filename.replace("@", "").replace("/", "-"); + await fs.promises.rename(filename, npmTarball); + })); + + // npm packages need to be published in dependency order: platform packages + // first, then the main package that references them as optionalDependencies. + const publishManifest = { + stages: [ + platforms.map(p => ({ + filename: path.basename(p.npmTarball), + })), + [ + { + filename: path.basename(mainNativePreviewPackage.npmTarball), + }, + ], + ], + }; + + const publishManifestPath = path.join(builtNpm, "publish-manifest.json"); + await fs.promises.writeFile(publishManifestPath, JSON.stringify(publishManifest, undefined, 4) + "\n"); +} + +export const packVsixExtensions = task({ + name: "vscode-typescript:pack", + hiddenFromTaskList: true, + dependencies: options.forRelease || usePublishedPlatformPackagesForVsix ? undefined : [buildNativePreviewPackages, cleanSignTempDirectory], + run: runPackVsixExtensions, +}); + +/** @type {Map>} */ +const publishedPlatformPackageLibDirs = new Map(); + +const getPublishedTypeScriptPackageJson = memoize(() => { + const candidates = [ + path.join(extensionDir, "node_modules", publishedTypeScriptAliasPackageName, "package.json"), + path.join(__dirname, "node_modules", publishedTypeScriptAliasPackageName, "package.json"), + ]; + + for (const candidate of candidates) { + if (fs.existsSync(candidate)) { + const packageJson = JSON.parse(fs.readFileSync(candidate, "utf8")); + if (packageJson.name !== "typescript") { + throw new Error(`${publishedTypeScriptAliasPackageName} should alias the typescript package, but found ${packageJson.name}.`); + } + if (!packageJson.version || typeof packageJson.version !== "string") { + throw new Error(`${publishedTypeScriptAliasPackageName} package.json did not contain a version.`); + } + if (!packageJson.optionalDependencies || typeof packageJson.optionalDependencies !== "object") { + throw new Error(`${publishedTypeScriptAliasPackageName} package.json did not contain platform optionalDependencies.`); + } + return packageJson; + } + } + + throw new Error(`Could not find ${publishedTypeScriptAliasPackageName}; run npm install first.`); +}); + +function getPublishedTypeScriptVersion() { + const version = getPublishedTypeScriptPackageJson().version; + const expectedVersion = getVersion(); + if (usePublishedPlatformPackagesForVsix && version !== expectedVersion) { + throw new Error(`usePublishedPlatformPackagesForVsix requires ${publishedTypeScriptAliasPackageName}'s installed version (${version}) to match release version ${expectedVersion}.`); + } + return version; +} + +function checkPublishedPlatformPackagesForVsix() { + if (!options.forRelease) { + throw new Error("usePublishedPlatformPackagesForVsix requires forRelease"); + } + getPublishedTypeScriptVersion(); +} + +const getPackageLock = memoize(() => JSON.parse(fs.readFileSync(path.join(__dirname, "package-lock.json"), "utf8"))); + +/** + * @param {string} npmPackageName + */ +async function getPublishedPlatformPackageLibDir(npmPackageName) { + let promise = publishedPlatformPackageLibDirs.get(npmPackageName); + if (!promise) { + promise = getPublishedPlatformPackageLibDirWorker(npmPackageName); + publishedPlatformPackageLibDirs.set(npmPackageName, promise); + } + return promise; +} + +/** + * @param {string} npmPackageName + */ +async function getPublishedPlatformPackageLibDirWorker(npmPackageName) { + const dest = path.join(builtPublishedPlatformPackages, "node_modules", ...npmPackageName.split("/")); + const lib = path.join(dest, "lib"); + if (fs.existsSync(lib)) { + return lib; + } + + await fs.promises.mkdir(dest, { recursive: true }); + + const tarballDestination = path.join(builtPublishedPlatformPackages, "tarballs"); + await fs.promises.mkdir(tarballDestination, { recursive: true }); + + const version = getPublishedTypeScriptPackageJson().optionalDependencies[npmPackageName]; + if (!version || typeof version !== "string") { + throw new Error(`${publishedTypeScriptAliasPackageName} does not depend on ${npmPackageName}.`); + } + + const lockEntry = getPackageLock().packages[`node_modules/${npmPackageName}`]; + if (!lockEntry) { + throw new Error(`package-lock.json does not contain ${npmPackageName}; run npm install.`); + } + if (lockEntry.version !== version) { + throw new Error(`package-lock.json has ${npmPackageName}@${lockEntry.version}, but ${publishedTypeScriptAliasPackageName} depends on ${version}.`); + } + if (!lockEntry.resolved || typeof lockEntry.resolved !== "string") { + throw new Error(`package-lock.json entry for ${npmPackageName}@${version} does not contain a tarball URL.`); + } + + console.log(`Fetching ${npmPackageName}@${version} with npm.`); + const { stdout } = await $pipe({ cwd: tarballDestination, env: releasePackageEnv })`npm pack --json ${npmPackageName}@${version}`; + const [packed] = JSON.parse(stdout); + if (!packed.filename || typeof packed.filename !== "string") { + throw new Error(`npm pack ${npmPackageName}@${version} did not return a filename.`); + } + await tar.x({ file: path.join(tarballDestination, packed.filename), cwd: dest, strip: 1 }); + + if (!fs.existsSync(lib)) { + throw new Error(`Published platform package ${npmPackageName}@${version} did not contain a lib directory.`); + } + + return lib; +} + +async function runPackVsixExtensions() { + await rimraf(builtVsix); + await fs.promises.mkdir(builtVsix, { recursive: true }); + if (usePublishedPlatformPackagesForVsix) { + checkPublishedPlatformPackagesForVsix(); + publishedPlatformPackageLibDirs.clear(); + await rimraf(builtPublishedPlatformPackages); + } + + const platforms = getPlatforms(); + const extensions = platforms.flatMap(({ npmDir, npmPackageName, extensions }) => extensions.map(e => ({ npmDir, npmPackageName, ...e }))); + if (!extensions.length) { + console.log("No VSIX targets configured; skipping extension packaging."); + return; + } + + // We don't use vscode:prepublish, as that would run the build for each package below. + await $({ cwd: extensionDir, env: releasePackageEnv })`npm run bundle:release`; + + let version = "0.0.0"; + if (options.forRelease) { + // No real semver prerelease versioning. + // https://code.visualstudio.com/api/working-with-extensions/publishing-extension#prerelease-extensions + assert(options.setPrerelease, "forRelease is true but setPrerelease is not set"); + const prerelease = options.setPrerelease; + assert(typeof prerelease === "string", "setPrerelease is not a string"); + // parse `dev..`. + const match = prerelease.match(/dev\.(\d+)\.(\d+)/); + if (!match) { + throw new Error(`Prerelease version should be in the form of dev.., but got ${prerelease}`); + } + // Set version to `0..`. + version = `0.${match[1]}.${match[2]}`; + } + + console.log("Version:", version); + + await Promise.all(extensions.map(async ({ npmDir, npmPackageName, nodeOs, vscodeTarget, sourceDir, extensionDir: thisExtensionDir, vsixPath, vsixManifestPath, vsixSignaturePath }) => { + const npmLibDir = usePublishedPlatformPackagesForVsix + ? await getPublishedPlatformPackageLibDir(npmPackageName) + : path.join(npmDir, "lib"); + const extensionLibDir = path.join(thisExtensionDir, "lib"); + await fs.promises.mkdir(extensionLibDir, { recursive: true }); + + await cpWithoutNodeModulesOrTsconfig(sourceDir, thisExtensionDir); + await cpWithoutNodeModulesOrTsconfig(npmLibDir, extensionLibDir); + await fs.promises.chmod(path.join(extensionLibDir, nativePreviewExeName(nodeOs)), 0o755); + + const packageJsonPath = path.join(thisExtensionDir, "package.json"); + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")); + packageJson.version = version; + packageJson.bundledTypeScriptVersion = usePublishedPlatformPackagesForVsix ? getPublishedTypeScriptVersion() : getVersion(); + fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, undefined, 4)); + + await fs.promises.copyFile("NOTICE.txt", path.join(thisExtensionDir, "NOTICE.txt")); + + await $({ cwd: thisExtensionDir, env: releasePackageEnv })`vsce package ${version} --no-update-package-json --no-dependencies --out ${vsixPath} --target ${vscodeTarget}`; + + if (options.forRelease) { + await $({ cwd: thisExtensionDir, env: releasePackageEnv })`vsce generate-manifest --packagePath ${vsixPath} --out ${vsixManifestPath}`; + await fs.promises.cp(vsixManifestPath, vsixSignaturePath); + } + })); +} + +export const signVsixExtensions = task({ + name: "vscode-typescript:sign", + hiddenFromTaskList: true, + run: runSignVsixExtensions, +}); + +async function runSignVsixExtensions() { + if (!options.forRelease) { + throw new Error("This task should not be run in non-release builds."); + } + + const platforms = getPlatforms(); + const extensions = platforms.flatMap(({ npmDir, extensions }) => extensions.map(e => ({ npmDir, ...e }))); + if (!extensions.length) { + console.log("No VSIX targets configured; skipping extension signing."); + return; + } + + await sign({ + SignFileRecordList: [ + { + SignFileList: extensions.map(({ vsixSignaturePath }) => ({ SrcPath: vsixSignaturePath, DstPath: null })), + Certs: "VSCodePublisher", + MacAppName: undefined, + }, + ], + }); +} + +export const nativePreviewRelease = task({ + name: "typescript:release", + hiddenFromTaskList: true, + run: async () => { + if (!options.forRelease || !options.setPrerelease && (!nativePreviewReleaseVersion || produceAnyVsix)) { + throw new Error("typescript:release requires --forRelease and --setPrerelease flags, unless nativePreviewReleaseVersion is hardcoded and VSIX production is disabled. Example: npx hereby typescript:release --forRelease --setPrerelease=dev.1.0"); + } + await runBuildNativePreviewPackages(); + await runSignNativePreviewPackages(); + await runPackNativePreviewPackages(); + await runPackVsixExtensions(); + await runSignVsixExtensions(); + await runCleanSignTempDirectory(); + }, +}); + +export const nativePreview = task({ + name: "native-preview", + hiddenFromTaskList: true, + dependencies: options.forRelease ? undefined : [packNativePreviewPackages, packVsixExtensions], + run: options.forRelease ? async () => { + throw new Error("This task should not be run in release builds."); + } : undefined, +}); + +export const allChecks = task({ + name: "all-checks", + description: "Runs all checks for the Go code (fourslash, lint, tests, etc.)", + run: async () => { + await runTests(); + await runFormat(); + await runLint(); + await runTests(); + }, +}); + +export const tidy = task({ + name: "tidy", + description: "Tidies both Go modules and synchronizes the workspace.", + run: async () => { + await $({ cwd: "./tsc" })`go mod tidy`; + await $({ cwd: "./tools" })`go mod tidy`; + await $`go work sync`; + }, +}); diff --git a/LICENSE.txt b/LICENSE.txt index 8746124b27791..edc24fd6e1409 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,55 +1,55 @@ -Apache License - -Version 2.0, January 2004 - -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of this License; and - -You must cause any modified files to carry prominent notices stating that You changed the files; and - -You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - -If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and + +You must cause any modified files to carry prominent notices stating that You changed the files; and + +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/NOTICE.txt b/NOTICE.txt index 0c40b1e6165ff..e301bd192c59c 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -1,436 +1,436 @@ -NOTICES AND INFORMATION -Do Not Translate or Localize - -This software incorporates material from third parties. -Microsoft makes certain open source code available at https://3rdpartysource.microsoft.com, -or you may send a check or money order for US $5.00, including the product name, -the open source component name, platform, and version number, to: - -Source Code Compliance Team -Microsoft Corporation -One Microsoft Way -Redmond, WA 98052 -USA - -Notwithstanding any other terms, you may reverse engineer this software to the extent -required to debug changes to any libraries licensed under the GNU Lesser General Public License. - - -------------------- DefinitelyTyped -------------------- -This file is based on or incorporates material from the projects listed below (collectively "Third Party Code"). Microsoft is not the original author of the Third Party Code. The original copyright notice and the license, under which Microsoft received such Third Party Code, are set forth below. Such licenses and notices are provided for informational purposes only. Microsoft, not the third party, licenses the Third Party Code to you under the terms set forth in the EULA for the Microsoft Product. Microsoft reserves all other rights not expressly granted under this agreement, whether by implication, estoppel or otherwise. -DefinitelyTyped -This project is licensed under the MIT license. Copyrights are respective of each contributor listed at the beginning of each definition file. Provided for Informational Purposes Only - -MIT License -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------------------------------------- - -------------------- Unicode -------------------- -UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE - -Unicode Data Files include all data files under the directories -http://www.unicode.org/Public/, http://www.unicode.org/reports/, -http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and -http://www.unicode.org/utility/trac/browser/. - -Unicode Data Files do not include PDF online code charts under the -directory http://www.unicode.org/Public/. - -Software includes any source code published in the Unicode Standard -or under the directories -http://www.unicode.org/Public/, http://www.unicode.org/reports/, -http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and -http://www.unicode.org/utility/trac/browser/. - -NOTICE TO USER: Carefully read the following legal agreement. -BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S -DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), -YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE -TERMS AND CONDITIONS OF THIS AGREEMENT. -IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE -THE DATA FILES OR SOFTWARE. - -COPYRIGHT AND PERMISSION NOTICE - -Copyright (c) 1991-2017 Unicode, Inc. All rights reserved. -Distributed under the Terms of Use in http://www.unicode.org/copyright.html. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Unicode data files and any associated documentation -(the "Data Files") or Unicode software and any associated documentation -(the "Software") to deal in the Data Files or Software -without restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, and/or sell copies of -the Data Files or Software, and to permit persons to whom the Data Files -or Software are furnished to do so, provided that either -(a) this copyright and permission notice appear with all copies -of the Data Files or Software, or -(b) this copyright and permission notice appear in associated -Documentation. - -THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT OF THIRD PARTY RIGHTS. -IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS -NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL -DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, -DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER -TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THE DATA FILES OR SOFTWARE. - -Except as contained in this notice, the name of a copyright holder -shall not be used in advertising or otherwise to promote the sale, -use or other dealings in these Data Files or Software without prior -written authorization of the copyright holder. -------------------------------------------------------------------------------------- - --------------------Document Object Model----------------------------- -DOM - -W3C License -This work is being provided by the copyright holders under the following license. -By obtaining and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions. -Permission to copy, modify, and distribute this work, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following -on ALL copies of the work or portions thereof, including modifications: -* The full text of this NOTICE in a location viewable to users of the redistributed or derivative work. -* Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, the W3C Software and Document Short Notice should be included. -* Notice of any changes or modifications, through a copyright statement on the new code or document such as "This software or document includes material copied from or derived -from [title and URI of the W3C document]. Copyright © [YEAR] W3C® (MIT, ERCIM, Keio, Beihang)." -Disclaimers -THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR -FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. -COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT. -The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the work without specific, written prior permission. -Title to copyright in this work will at all times remain with copyright holders. - ---------- - -DOM -Copyright © 2018 WHATWG (Apple, Google, Mozilla, Microsoft). This work is licensed under a Creative Commons Attribution 4.0 International License: Attribution 4.0 International -======================================================================= -Creative Commons Corporation ("Creative Commons") is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an "as-is" basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. Using Creative Commons Public Licenses Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC- licensed material, or material used under an exception or limitation to copyright. More considerations for licensors: - -wiki.creativecommons.org/Considerations_for_licensors Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor's permission is not necessary for any reason--for example, because of any applicable exception or limitation to copyright--then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More_considerations for the public: wiki.creativecommons.org/Considerations_for_licensees ======================================================================= -Creative Commons Attribution 4.0 International Public License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. Section 1 -- Definitions. a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. c. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. d. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. e. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. f. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. g. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. h. Licensor means the individual(s) or entity(ies) granting rights under this Public License. i. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. j. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. k. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. Section 2 -- Scope. a. License grant. 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: a. reproduce and Share the Licensed Material, in whole or in part; and b. produce, reproduce, and Share Adapted Material. 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 3. Term. The term of this Public License is specified in Section 6(a). 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a) (4) never produces Adapted Material. 5. Downstream recipients. a. Offer from the Licensor -- Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. b. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). b. Other rights. 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 2. Patent and trademark rights are not licensed under this Public License. 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. Section 3 -- License Conditions. Your exercise of the Licensed Rights is expressly made subject to the following conditions. a. Attribution. 1. If You Share the Licensed Material (including in modified form), You must: a. retain the following if it is supplied by the Licensor with the Licensed Material: i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); ii. a copyright notice; iii. a notice that refers to this Public License; iv. a notice that refers to the disclaimer of warranties; v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; b. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and c. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. 4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License. Section 4 -- Sui Generis Database Rights. Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. Section 5 -- Disclaimer of Warranties and Limitation of Liability. a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. Section 6 -- Term and Termination. a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 2. upon express reinstatement by the Licensor. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. Section 7 -- Other Terms and Conditions. a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. Section 8 -- Interpretation. a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. ======================================================================= Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the "Licensor." Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark "Creative Commons" or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. Creative Commons may be contacted at creativecommons.org. - --------------------------------------------------------------------------------- - -----------------------Web Background Synchronization------------------------------ - -Web Background Synchronization Specification -Portions of spec © by W3C - -W3C Community Final Specification Agreement -To secure commitments from participants for the full text of a Community or Business Group Report, the group may call for voluntary commitments to the following terms; a "summary" is -available. See also the related "W3C Community Contributor License Agreement". -1. The Purpose of this Agreement. -This Agreement sets forth the terms under which I make certain copyright and patent rights available to you for your implementation of the Specification. -Any other capitalized terms not specifically defined herein have the same meaning as those terms have in the "W3C Patent Policy", and if not defined there, in the "W3C Process Document". -2. Copyrights. -2.1. Copyright Grant. I grant to you a perpetual (for the duration of the applicable copyright), worldwide, non-exclusive, no-charge, royalty-free, copyright license, without any obligation for accounting to me, to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, distribute, and implement the Specification to the full extent of my copyright interest in the Specification. -2.2. Attribution. As a condition of the copyright grant, you must include an attribution to the Specification in any derivative work you make based on the Specification. That attribution must include, at minimum, the Specification name and version number. -3. Patents. -3.1. Patent Licensing Commitment. I agree to license my Essential Claims under the W3C Community RF Licensing Requirements. This requirement includes Essential Claims that I own and any that I have the right to license without obligation of payment or other consideration to an unrelated third party. W3C Community RF Licensing Requirements obligations made concerning the Specification and described in this policy are binding on me for the life of the patents in question and encumber the patents containing Essential Claims, regardless of changes in participation status or W3C Membership. I also agree to license my Essential Claims under the W3C Community RF Licensing Requirements in derivative works of the Specification so long as all normative portions of the Specification are maintained and that this licensing commitment does not extend to any portion of the derivative work that was not included in the Specification. -3.2. Optional, Additional Patent Grant. In addition to the provisions of Section 3.1, I may also, at my option, make certain intellectual property rights infringed by implementations of the Specification, including Essential Claims, available by providing those terms via the W3C Web site. -4. No Other Rights. Except as specifically set forth in this Agreement, no other express or implied patent, trademark, copyright, or other property rights are granted under this Agreement, including by implication, waiver, or estoppel. -5. Antitrust Compliance. I acknowledge that I may compete with other participants, that I am under no obligation to implement the Specification, that each participant is free to develop competing technologies and standards, and that each party is free to license its patent rights to third parties, including for the purpose of enabling competing technologies and standards. -6. Non-Circumvention. I agree that I will not intentionally take or willfully assist any third party to take any action for the purpose of circumventing my obligations under this Agreement. -7. Transition to W3C Recommendation Track. The Specification developed by the Project may transition to the W3C Recommendation Track. The W3C Team is responsible for notifying me that a Corresponding Working Group has been chartered. I have no obligation to join the Corresponding Working Group. If the Specification developed by the Project transitions to the W3C Recommendation Track, the following terms apply: -7.1. If I join the Corresponding Working Group. If I join the Corresponding Working Group, I will be subject to all W3C rules, obligations, licensing commitments, and policies that govern that Corresponding Working Group. -7.2. If I Do Not Join the Corresponding Working Group. -7.2.1. Licensing Obligations to Resulting Specification. If I do not join the Corresponding Working Group, I agree to offer patent licenses according to the W3C Royalty-Free licensing requirements described in Section 5 of the W3C Patent Policy for the portions of the Specification included in the resulting Recommendation. This licensing commitment does not extend to any portion of an implementation of the Recommendation that was not included in the Specification. This licensing commitment may not be revoked but may be modified through the exclusion process defined in Section 4 of the W3C Patent Policy. I am not required to join the Corresponding Working Group to exclude patents from the W3C Royalty-Free licensing commitment, but must otherwise follow the normal exclusion procedures defined by the W3C Patent Policy. The W3C Team will notify me of any Call for Exclusion in the Corresponding Working Group as set forth in Section 4.5 of the W3C Patent Policy. -7.2.2. No Disclosure Obligation. If I do not join the Corresponding Working Group, I have no patent disclosure obligations outside of those set forth in Section 6 of the W3C Patent Policy. -8. Conflict of Interest. I will disclose significant relationships when those relationships might reasonably be perceived as creating a conflict of interest with my role. I will notify W3C of any change in my affiliation using W3C-provided mechanisms. -9. Representations, Warranties and Disclaimers. I represent and warrant that I am legally entitled to grant the rights and promises set forth in this Agreement. IN ALL OTHER RESPECTS THE SPECIFICATION IS PROVIDED “AS IS.” The entire risk as to implementing or otherwise using the Specification is assumed by the implementer and user. Except as stated herein, I expressly disclaim any warranties (express, implied, or otherwise), including implied warranties of merchantability, non-infringement, fitness for a particular purpose, or title, related to the Specification. IN NO EVENT WILL ANY PARTY BE LIABLE TO ANY OTHER PARTY FOR LOST PROFITS OR ANY FORM OF INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER FROM ANY CAUSES OF ACTION OF ANY KIND WITH RESPECT TO THIS AGREEMENT, WHETHER BASED ON BREACH OF CONTRACT, TORT (INCLUDING NEGLIGENCE), OR OTHERWISE, AND WHETHER OR NOT THE OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. All of my obligations under Section 3 regarding the transfer, successors in interest, or assignment of Granted Claims will be satisfied if I notify the transferee or assignee of any patent that I know contains Granted Claims of the obligations under Section 3. Nothing in this Agreement requires me to undertake a patent search. -10. Definitions. -10.1. Agreement. “Agreement” means this W3C Community Final Specification Agreement. -10.2. Corresponding Working Group. “Corresponding Working Group” is a W3C Working Group that is chartered to develop a Recommendation, as defined in the W3C Process Document, that takes the Specification as an input. -10.3. Essential Claims. “Essential Claims” shall mean all claims in any patent or patent application in any jurisdiction in the world that would necessarily be infringed by implementation of the Specification. A claim is necessarily infringed hereunder only when it is not possible to avoid infringing it because there is no non-infringing alternative for implementing the normative portions of the Specification. Existence of a non-infringing alternative shall be judged based on the state of the art at the time of the publication of the Specification. The following are expressly excluded from and shall not be deemed to constitute Essential Claims: -10.3.1. any claims other than as set forth above even if contained in the same patent as Essential Claims; and -10.3.2. claims which would be infringed only by: -portions of an implementation that are not specified in the normative portions of the Specification, or -enabling technologies that may be necessary to make or use any product or portion thereof that complies with the Specification and are not themselves expressly set forth in the Specification (e.g., semiconductor manufacturing technology, compiler technology, object-oriented technology, basic operating system technology, and the like); or -the implementation of technology developed elsewhere and merely incorporated by reference in the body of the Specification. -10.3.3. design patents and design registrations. -For purposes of this definition, the normative portions of the Specification shall be deemed to include only architectural and interoperability requirements. Optional features in the RFC 2119 sense are considered normative unless they are specifically identified as informative. Implementation examples or any other material that merely illustrate the requirements of the Specification are informative, rather than normative. -10.4. I, Me, or My. “I,” “me,” or “my” refers to the signatory. -10.5 Project. “Project” means the W3C Community Group or Business Group for which I executed this Agreement. -10.6. Specification. “Specification” means the Specification identified by the Project as the target of this agreement in a call for Final Specification Commitments. W3C shall provide the authoritative mechanisms for the identification of this Specification. -10.7. W3C Community RF Licensing Requirements. “W3C Community RF Licensing Requirements” license shall mean a non-assignable, non-sublicensable license to make, have made, use, sell, have sold, offer to sell, import, and distribute and dispose of implementations of the Specification that: -10.7.1. shall be available to all, worldwide, whether or not they are W3C Members; -10.7.2. shall extend to all Essential Claims owned or controlled by me; -10.7.3. may be limited to implementations of the Specification, and to what is required by the Specification; -10.7.4. may be conditioned on a grant of a reciprocal RF license (as defined in this policy) to all Essential Claims owned or controlled by the licensee. A reciprocal license may be required to be available to all, and a reciprocal license may itself be conditioned on a further reciprocal license from all. -10.7.5. may not be conditioned on payment of royalties, fees or other consideration; -10.7.6. may be suspended with respect to any licensee when licensor issued by licensee for infringement of claims essential to implement the Specification or any W3C Recommendation; -10.7.7. may not impose any further conditions or restrictions on the use of any technology, intellectual property rights, or other restrictions on behavior of the licensee, but may include reasonable, customary terms relating to operation or maintenance of the license relationship such as the following: choice of law and dispute resolution; -10.7.8. shall not be considered accepted by an implementer who manifests an intent not to accept the terms of the W3C Community RF Licensing Requirements license as offered by the licensor. -10.7.9. The RF license conforming to the requirements in this policy shall be made available by the licensor as long as the Specification is in effect. The term of such license shall be for the life of the patents in question. -I am encouraged to provide a contact from which licensing information can be obtained and other relevant licensing information. Any such information will be made publicly available. -10.8. You or Your. “You,” “you,” or “your” means any person or entity who exercises copyright or patent rights granted under this Agreement, and any person that person or entity controls. - -------------------------------------------------------------------------------------- - -------------------- WebGL ----------------------------- -Copyright (c) 2018 The Khronos Group Inc. - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and/or associated documentation files (the -"Materials"), to deal in the Materials without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Materials, and to -permit persons to whom the Materials are furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Materials. - -THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. ------------------------------------------------------- - ---------------------------------------------------------- - -github.com/zeebo/xxh3 v1.1.0 - BSD-2-Clause - - -Copyright (c) 2019, Jeff Wendling -Copyright (c) 2012-2014, Yann Collet - -Copyright (c) . All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------- - ---------------------------------------------------------- - -github.com/go-json-experiment/json v0.0.0-20260623181947-01eb4420fa68 - BSD-3-Clause - - -Copyright 2010 The Go Authors -Copyright 2011 The Go Authors -Copyright 2016 The Go Authors -Copyright 2018 The Go Authors -Copyright 2020 The Go Authors -Copyright 2021 The Go Authors -Copyright 2022 The Go Authors -Copyright 2023 The Go Authors -Copyright 2024 The Go Authors -Copyright (c) 2020 The Go Authors - -Copyright (c) . All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - - 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ---------------------------------------------------------- - ---------------------------------------------------------- - -golang.org/x/sync v0.21.0 - BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang - - -Copyright 2009 The Go Authors -Copyright 2013 The Go Authors -Copyright 2016 The Go Authors -Copyright 2017 The Go Authors -Copyright 2019 The Go Authors - -BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang - ---------------------------------------------------------- - ---------------------------------------------------------- - -golang.org/x/sys v0.46.0 - BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang - - -Copyright 2009 The Go Authors -Copyright 2010 The Go Authors -Copyright 2011 The Go Authors -Copyright 2012 The Go Authors -Copyright 2013 The Go Authors -Copyright 2014 The Go Authors -Copyright 2015 The Go Authors -Copyright 2016 The Go Authors -Copyright 2017 The Go Authors -Copyright 2018 The Go Authors -Copyright 2019 The Go Authors -Copyright 2020 The Go Authors -Copyright 2021 The Go Authors -Copyright 2022 The Go Authors -Copyright 2023 The Go Authors -Copyright 2024 The Go Authors -Copyright 2025 The Go Authors -Copyright 2009,2010 The Go Authors - -BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang - ---------------------------------------------------------- - ---------------------------------------------------------- - -golang.org/x/term v0.44.0 - BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang - - -Copyright 2009 The Go Authors -Copyright 2011 The Go Authors -Copyright 2013 The Go Authors -Copyright 2019 The Go Authors -Copyright 2021 The Go Authors - -BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang - ---------------------------------------------------------- - ---------------------------------------------------------- - -golang.org/x/text v0.38.0 - BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang - - -(c) AeHa (c) -(c) EeAoq (c) -(c) EAiE (c) A -(c) lEEe (c) AE -(c) (c) AAEE (c) -(c) oav!A (c) AY -(c) aA"AE (c) 1AE (c) -Copyright 2009 The Go Authors -Copyright 2011 The Go Authors -Copyright 2012 The Go Authors -Copyright 2013 The Go Authors -Copyright 2014 The Go Authors -Copyright 2015 The Go Authors -Copyright 2016 The Go Authors -Copyright 2017 The Go Authors -Copyright 2018 The Go Authors -Copyright 2019 The Go Authors -Copyright 2021 The Go Authors -Copyright 2025 The Go Authors - -BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang - ---------------------------------------------------------- - ---------------------------------------------------------- - -github.com/klauspost/cpuid/v2 v2.2.10 - MIT - - -Copyright (c) 2015 Klaus Post -Copyright (c) 2020 Klaus Post -Copyright (c) 2021 Klaus Post -Copyright 2018 The Go Authors -Copyright (c) 2015- Klaus Post & Contributors - -MIT License - -Copyright (c) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ---------------------------------------------------------- - ---------------------------------------------------------- - -github.com/Microsoft/go-winio v0.6.2 - MIT - - -Copyright (c) 2015 Microsoft -Copyright 2013 The Go Authors - -MIT License - -Copyright (c) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - ---------------------------------------------------------- - ---------------------------------------------------------- - -github.com/mackerelio/go-osstat v0.2.7 - Apache-2.0 - - -Copyright 2017-2019 Hatena Co., Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - ---------------------------------------------------------- - ---------------------------------------------------------- - -parcel-bundler/watcher 8926bb8b281733bbfcaf69bb4e62ab7a1431c42a - MIT - - -Copyright Node.js contributors -Copyright 2012-2020 Facebook, Inc. -Copyright (c) 2017-present Devon Govett - -MIT License - -Copyright (c) 2017-present Devon Govett - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - ---------------------------------------------------------- - +NOTICES AND INFORMATION +Do Not Translate or Localize + +This software incorporates material from third parties. +Microsoft makes certain open source code available at https://3rdpartysource.microsoft.com, +or you may send a check or money order for US $5.00, including the product name, +the open source component name, platform, and version number, to: + +Source Code Compliance Team +Microsoft Corporation +One Microsoft Way +Redmond, WA 98052 +USA + +Notwithstanding any other terms, you may reverse engineer this software to the extent +required to debug changes to any libraries licensed under the GNU Lesser General Public License. + + +------------------- DefinitelyTyped -------------------- +This file is based on or incorporates material from the projects listed below (collectively "Third Party Code"). Microsoft is not the original author of the Third Party Code. The original copyright notice and the license, under which Microsoft received such Third Party Code, are set forth below. Such licenses and notices are provided for informational purposes only. Microsoft, not the third party, licenses the Third Party Code to you under the terms set forth in the EULA for the Microsoft Product. Microsoft reserves all other rights not expressly granted under this agreement, whether by implication, estoppel or otherwise. +DefinitelyTyped +This project is licensed under the MIT license. Copyrights are respective of each contributor listed at the beginning of each definition file. Provided for Informational Purposes Only + +MIT License +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-------------------------------------------------------------------------------------- + +------------------- Unicode -------------------- +UNICODE, INC. LICENSE AGREEMENT - DATA FILES AND SOFTWARE + +Unicode Data Files include all data files under the directories +http://www.unicode.org/Public/, http://www.unicode.org/reports/, +http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and +http://www.unicode.org/utility/trac/browser/. + +Unicode Data Files do not include PDF online code charts under the +directory http://www.unicode.org/Public/. + +Software includes any source code published in the Unicode Standard +or under the directories +http://www.unicode.org/Public/, http://www.unicode.org/reports/, +http://www.unicode.org/cldr/data/, http://source.icu-project.org/repos/icu/, and +http://www.unicode.org/utility/trac/browser/. + +NOTICE TO USER: Carefully read the following legal agreement. +BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING UNICODE INC.'S +DATA FILES ("DATA FILES"), AND/OR SOFTWARE ("SOFTWARE"), +YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. +IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE +THE DATA FILES OR SOFTWARE. + +COPYRIGHT AND PERMISSION NOTICE + +Copyright (c) 1991-2017 Unicode, Inc. All rights reserved. +Distributed under the Terms of Use in http://www.unicode.org/copyright.html. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Unicode data files and any associated documentation +(the "Data Files") or Unicode software and any associated documentation +(the "Software") to deal in the Data Files or Software +without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, and/or sell copies of +the Data Files or Software, and to permit persons to whom the Data Files +or Software are furnished to do so, provided that either +(a) this copyright and permission notice appear with all copies +of the Data Files or Software, or +(b) this copyright and permission notice appear in associated +Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT OF THIRD PARTY RIGHTS. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS +NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL +DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THE DATA FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, +use or other dealings in these Data Files or Software without prior +written authorization of the copyright holder. +------------------------------------------------------------------------------------- + +-------------------Document Object Model----------------------------- +DOM + +W3C License +This work is being provided by the copyright holders under the following license. +By obtaining and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions. +Permission to copy, modify, and distribute this work, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following +on ALL copies of the work or portions thereof, including modifications: +* The full text of this NOTICE in a location viewable to users of the redistributed or derivative work. +* Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, the W3C Software and Document Short Notice should be included. +* Notice of any changes or modifications, through a copyright statement on the new code or document such as "This software or document includes material copied from or derived +from [title and URI of the W3C document]. Copyright © [YEAR] W3C® (MIT, ERCIM, Keio, Beihang)." +Disclaimers +THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR +FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. +COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT. +The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the work without specific, written prior permission. +Title to copyright in this work will at all times remain with copyright holders. + +--------- + +DOM +Copyright © 2018 WHATWG (Apple, Google, Mozilla, Microsoft). This work is licensed under a Creative Commons Attribution 4.0 International License: Attribution 4.0 International +======================================================================= +Creative Commons Corporation ("Creative Commons") is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an "as-is" basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. Using Creative Commons Public Licenses Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC- licensed material, or material used under an exception or limitation to copyright. More considerations for licensors: + +wiki.creativecommons.org/Considerations_for_licensors Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor's permission is not necessary for any reason--for example, because of any applicable exception or limitation to copyright--then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More_considerations for the public: wiki.creativecommons.org/Considerations_for_licensees ======================================================================= +Creative Commons Attribution 4.0 International Public License By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. Section 1 -- Definitions. a. Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. b. Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. c. Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. d. Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. e. Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. f. Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. g. Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. h. Licensor means the individual(s) or entity(ies) granting rights under this Public License. i. Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. j. Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. k. You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. Section 2 -- Scope. a. License grant. 1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: a. reproduce and Share the Licensed Material, in whole or in part; and b. produce, reproduce, and Share Adapted Material. 2. Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 3. Term. The term of this Public License is specified in Section 6(a). 4. Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a) (4) never produces Adapted Material. 5. Downstream recipients. a. Offer from the Licensor -- Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. b. No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 6. No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). b. Other rights. 1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 2. Patent and trademark rights are not licensed under this Public License. 3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. Section 3 -- License Conditions. Your exercise of the Licensed Rights is expressly made subject to the following conditions. a. Attribution. 1. If You Share the Licensed Material (including in modified form), You must: a. retain the following if it is supplied by the Licensor with the Licensed Material: i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); ii. a copyright notice; iii. a notice that refers to this Public License; iv. a notice that refers to the disclaimer of warranties; v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable; b. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and c. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. 4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License. Section 4 -- Sui Generis Database Rights. Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: a. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; b. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and c. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. Section 5 -- Disclaimer of Warranties and Limitation of Liability. a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. c. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. Section 6 -- Term and Termination. a. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. b. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 2. upon express reinstatement by the Licensor. For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. c. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. d. Sections 1, 5, 6, 7, and 8 survive termination of this Public License. Section 7 -- Other Terms and Conditions. a. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. b. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. Section 8 -- Interpretation. a. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. b. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. c. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. d. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. ======================================================================= Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the "Licensor." Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark "Creative Commons" or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. Creative Commons may be contacted at creativecommons.org. + +-------------------------------------------------------------------------------- + +----------------------Web Background Synchronization------------------------------ + +Web Background Synchronization Specification +Portions of spec © by W3C + +W3C Community Final Specification Agreement +To secure commitments from participants for the full text of a Community or Business Group Report, the group may call for voluntary commitments to the following terms; a "summary" is +available. See also the related "W3C Community Contributor License Agreement". +1. The Purpose of this Agreement. +This Agreement sets forth the terms under which I make certain copyright and patent rights available to you for your implementation of the Specification. +Any other capitalized terms not specifically defined herein have the same meaning as those terms have in the "W3C Patent Policy", and if not defined there, in the "W3C Process Document". +2. Copyrights. +2.1. Copyright Grant. I grant to you a perpetual (for the duration of the applicable copyright), worldwide, non-exclusive, no-charge, royalty-free, copyright license, without any obligation for accounting to me, to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, distribute, and implement the Specification to the full extent of my copyright interest in the Specification. +2.2. Attribution. As a condition of the copyright grant, you must include an attribution to the Specification in any derivative work you make based on the Specification. That attribution must include, at minimum, the Specification name and version number. +3. Patents. +3.1. Patent Licensing Commitment. I agree to license my Essential Claims under the W3C Community RF Licensing Requirements. This requirement includes Essential Claims that I own and any that I have the right to license without obligation of payment or other consideration to an unrelated third party. W3C Community RF Licensing Requirements obligations made concerning the Specification and described in this policy are binding on me for the life of the patents in question and encumber the patents containing Essential Claims, regardless of changes in participation status or W3C Membership. I also agree to license my Essential Claims under the W3C Community RF Licensing Requirements in derivative works of the Specification so long as all normative portions of the Specification are maintained and that this licensing commitment does not extend to any portion of the derivative work that was not included in the Specification. +3.2. Optional, Additional Patent Grant. In addition to the provisions of Section 3.1, I may also, at my option, make certain intellectual property rights infringed by implementations of the Specification, including Essential Claims, available by providing those terms via the W3C Web site. +4. No Other Rights. Except as specifically set forth in this Agreement, no other express or implied patent, trademark, copyright, or other property rights are granted under this Agreement, including by implication, waiver, or estoppel. +5. Antitrust Compliance. I acknowledge that I may compete with other participants, that I am under no obligation to implement the Specification, that each participant is free to develop competing technologies and standards, and that each party is free to license its patent rights to third parties, including for the purpose of enabling competing technologies and standards. +6. Non-Circumvention. I agree that I will not intentionally take or willfully assist any third party to take any action for the purpose of circumventing my obligations under this Agreement. +7. Transition to W3C Recommendation Track. The Specification developed by the Project may transition to the W3C Recommendation Track. The W3C Team is responsible for notifying me that a Corresponding Working Group has been chartered. I have no obligation to join the Corresponding Working Group. If the Specification developed by the Project transitions to the W3C Recommendation Track, the following terms apply: +7.1. If I join the Corresponding Working Group. If I join the Corresponding Working Group, I will be subject to all W3C rules, obligations, licensing commitments, and policies that govern that Corresponding Working Group. +7.2. If I Do Not Join the Corresponding Working Group. +7.2.1. Licensing Obligations to Resulting Specification. If I do not join the Corresponding Working Group, I agree to offer patent licenses according to the W3C Royalty-Free licensing requirements described in Section 5 of the W3C Patent Policy for the portions of the Specification included in the resulting Recommendation. This licensing commitment does not extend to any portion of an implementation of the Recommendation that was not included in the Specification. This licensing commitment may not be revoked but may be modified through the exclusion process defined in Section 4 of the W3C Patent Policy. I am not required to join the Corresponding Working Group to exclude patents from the W3C Royalty-Free licensing commitment, but must otherwise follow the normal exclusion procedures defined by the W3C Patent Policy. The W3C Team will notify me of any Call for Exclusion in the Corresponding Working Group as set forth in Section 4.5 of the W3C Patent Policy. +7.2.2. No Disclosure Obligation. If I do not join the Corresponding Working Group, I have no patent disclosure obligations outside of those set forth in Section 6 of the W3C Patent Policy. +8. Conflict of Interest. I will disclose significant relationships when those relationships might reasonably be perceived as creating a conflict of interest with my role. I will notify W3C of any change in my affiliation using W3C-provided mechanisms. +9. Representations, Warranties and Disclaimers. I represent and warrant that I am legally entitled to grant the rights and promises set forth in this Agreement. IN ALL OTHER RESPECTS THE SPECIFICATION IS PROVIDED “AS IS.” The entire risk as to implementing or otherwise using the Specification is assumed by the implementer and user. Except as stated herein, I expressly disclaim any warranties (express, implied, or otherwise), including implied warranties of merchantability, non-infringement, fitness for a particular purpose, or title, related to the Specification. IN NO EVENT WILL ANY PARTY BE LIABLE TO ANY OTHER PARTY FOR LOST PROFITS OR ANY FORM OF INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER FROM ANY CAUSES OF ACTION OF ANY KIND WITH RESPECT TO THIS AGREEMENT, WHETHER BASED ON BREACH OF CONTRACT, TORT (INCLUDING NEGLIGENCE), OR OTHERWISE, AND WHETHER OR NOT THE OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. All of my obligations under Section 3 regarding the transfer, successors in interest, or assignment of Granted Claims will be satisfied if I notify the transferee or assignee of any patent that I know contains Granted Claims of the obligations under Section 3. Nothing in this Agreement requires me to undertake a patent search. +10. Definitions. +10.1. Agreement. “Agreement” means this W3C Community Final Specification Agreement. +10.2. Corresponding Working Group. “Corresponding Working Group” is a W3C Working Group that is chartered to develop a Recommendation, as defined in the W3C Process Document, that takes the Specification as an input. +10.3. Essential Claims. “Essential Claims” shall mean all claims in any patent or patent application in any jurisdiction in the world that would necessarily be infringed by implementation of the Specification. A claim is necessarily infringed hereunder only when it is not possible to avoid infringing it because there is no non-infringing alternative for implementing the normative portions of the Specification. Existence of a non-infringing alternative shall be judged based on the state of the art at the time of the publication of the Specification. The following are expressly excluded from and shall not be deemed to constitute Essential Claims: +10.3.1. any claims other than as set forth above even if contained in the same patent as Essential Claims; and +10.3.2. claims which would be infringed only by: +portions of an implementation that are not specified in the normative portions of the Specification, or +enabling technologies that may be necessary to make or use any product or portion thereof that complies with the Specification and are not themselves expressly set forth in the Specification (e.g., semiconductor manufacturing technology, compiler technology, object-oriented technology, basic operating system technology, and the like); or +the implementation of technology developed elsewhere and merely incorporated by reference in the body of the Specification. +10.3.3. design patents and design registrations. +For purposes of this definition, the normative portions of the Specification shall be deemed to include only architectural and interoperability requirements. Optional features in the RFC 2119 sense are considered normative unless they are specifically identified as informative. Implementation examples or any other material that merely illustrate the requirements of the Specification are informative, rather than normative. +10.4. I, Me, or My. “I,” “me,” or “my” refers to the signatory. +10.5 Project. “Project” means the W3C Community Group or Business Group for which I executed this Agreement. +10.6. Specification. “Specification” means the Specification identified by the Project as the target of this agreement in a call for Final Specification Commitments. W3C shall provide the authoritative mechanisms for the identification of this Specification. +10.7. W3C Community RF Licensing Requirements. “W3C Community RF Licensing Requirements” license shall mean a non-assignable, non-sublicensable license to make, have made, use, sell, have sold, offer to sell, import, and distribute and dispose of implementations of the Specification that: +10.7.1. shall be available to all, worldwide, whether or not they are W3C Members; +10.7.2. shall extend to all Essential Claims owned or controlled by me; +10.7.3. may be limited to implementations of the Specification, and to what is required by the Specification; +10.7.4. may be conditioned on a grant of a reciprocal RF license (as defined in this policy) to all Essential Claims owned or controlled by the licensee. A reciprocal license may be required to be available to all, and a reciprocal license may itself be conditioned on a further reciprocal license from all. +10.7.5. may not be conditioned on payment of royalties, fees or other consideration; +10.7.6. may be suspended with respect to any licensee when licensor issued by licensee for infringement of claims essential to implement the Specification or any W3C Recommendation; +10.7.7. may not impose any further conditions or restrictions on the use of any technology, intellectual property rights, or other restrictions on behavior of the licensee, but may include reasonable, customary terms relating to operation or maintenance of the license relationship such as the following: choice of law and dispute resolution; +10.7.8. shall not be considered accepted by an implementer who manifests an intent not to accept the terms of the W3C Community RF Licensing Requirements license as offered by the licensor. +10.7.9. The RF license conforming to the requirements in this policy shall be made available by the licensor as long as the Specification is in effect. The term of such license shall be for the life of the patents in question. +I am encouraged to provide a contact from which licensing information can be obtained and other relevant licensing information. Any such information will be made publicly available. +10.8. You or Your. “You,” “you,” or “your” means any person or entity who exercises copyright or patent rights granted under this Agreement, and any person that person or entity controls. + +------------------------------------------------------------------------------------- + +------------------- WebGL ----------------------------- +Copyright (c) 2018 The Khronos Group Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and/or associated documentation files (the +"Materials"), to deal in the Materials without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Materials, and to +permit persons to whom the Materials are furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Materials. + +THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +------------------------------------------------------ + +--------------------------------------------------------- + +github.com/zeebo/xxh3 v1.1.0 - BSD-2-Clause + + +Copyright (c) 2019, Jeff Wendling +Copyright (c) 2012-2014, Yann Collet + +Copyright (c) . All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +github.com/go-json-experiment/json v0.0.0-20260623181947-01eb4420fa68 - BSD-3-Clause + + +Copyright 2010 The Go Authors +Copyright 2011 The Go Authors +Copyright 2016 The Go Authors +Copyright 2018 The Go Authors +Copyright 2020 The Go Authors +Copyright 2021 The Go Authors +Copyright 2022 The Go Authors +Copyright 2023 The Go Authors +Copyright 2024 The Go Authors +Copyright (c) 2020 The Go Authors + +Copyright (c) . All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +golang.org/x/sync v0.21.0 - BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang + + +Copyright 2009 The Go Authors +Copyright 2013 The Go Authors +Copyright 2016 The Go Authors +Copyright 2017 The Go Authors +Copyright 2019 The Go Authors + +BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang + +--------------------------------------------------------- + +--------------------------------------------------------- + +golang.org/x/sys v0.46.0 - BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang + + +Copyright 2009 The Go Authors +Copyright 2010 The Go Authors +Copyright 2011 The Go Authors +Copyright 2012 The Go Authors +Copyright 2013 The Go Authors +Copyright 2014 The Go Authors +Copyright 2015 The Go Authors +Copyright 2016 The Go Authors +Copyright 2017 The Go Authors +Copyright 2018 The Go Authors +Copyright 2019 The Go Authors +Copyright 2020 The Go Authors +Copyright 2021 The Go Authors +Copyright 2022 The Go Authors +Copyright 2023 The Go Authors +Copyright 2024 The Go Authors +Copyright 2025 The Go Authors +Copyright 2009,2010 The Go Authors + +BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang + +--------------------------------------------------------- + +--------------------------------------------------------- + +golang.org/x/term v0.44.0 - BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang + + +Copyright 2009 The Go Authors +Copyright 2011 The Go Authors +Copyright 2013 The Go Authors +Copyright 2019 The Go Authors +Copyright 2021 The Go Authors + +BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang + +--------------------------------------------------------- + +--------------------------------------------------------- + +golang.org/x/text v0.38.0 - BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang + + +(c) AeHa (c) +(c) EeAoq (c) +(c) EAiE (c) A +(c) lEEe (c) AE +(c) (c) AAEE (c) +(c) oav!A (c) AY +(c) aA"AE (c) 1AE (c) +Copyright 2009 The Go Authors +Copyright 2011 The Go Authors +Copyright 2012 The Go Authors +Copyright 2013 The Go Authors +Copyright 2014 The Go Authors +Copyright 2015 The Go Authors +Copyright 2016 The Go Authors +Copyright 2017 The Go Authors +Copyright 2018 The Go Authors +Copyright 2019 The Go Authors +Copyright 2021 The Go Authors +Copyright 2025 The Go Authors + +BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang + +--------------------------------------------------------- + +--------------------------------------------------------- + +github.com/klauspost/cpuid/v2 v2.2.10 - MIT + + +Copyright (c) 2015 Klaus Post +Copyright (c) 2020 Klaus Post +Copyright (c) 2021 Klaus Post +Copyright 2018 The Go Authors +Copyright (c) 2015- Klaus Post & Contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +github.com/Microsoft/go-winio v0.6.2 - MIT + + +Copyright (c) 2015 Microsoft +Copyright 2013 The Go Authors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +github.com/mackerelio/go-osstat v0.2.7 - Apache-2.0 + + +Copyright 2017-2019 Hatena Co., Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +--------------------------------------------------------- + +--------------------------------------------------------- + +parcel-bundler/watcher 8926bb8b281733bbfcaf69bb4e62ab7a1431c42a - MIT + + +Copyright Node.js contributors +Copyright 2012-2020 Facebook, Inc. +Copyright (c) 2017-present Devon Govett + +MIT License + +Copyright (c) 2017-present Devon Govett + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + diff --git a/README.md b/README.md index 79adc1b165e39..6747f24816639 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,50 @@ - -# TypeScript - -[![CI](https://github.com/microsoft/TypeScript/actions/workflows/ci.yml/badge.svg)](https://github.com/microsoft/TypeScript/actions/workflows/ci.yml) -[![npm version](https://badge.fury.io/js/typescript.svg)](https://www.npmjs.com/package/typescript) -[![Downloads](https://img.shields.io/npm/dm/typescript.svg)](https://www.npmjs.com/package/typescript) -[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/microsoft/TypeScript/badge)](https://securityscorecards.dev/viewer/?uri=github.com/microsoft/TypeScript) - - -[TypeScript](https://www.typescriptlang.org/) is a language for application-scale JavaScript. TypeScript adds optional types to JavaScript that support tools for large-scale JavaScript applications for any browser, for any host, on any OS. TypeScript compiles to readable, standards-based JavaScript. Try it out at the [playground](https://www.typescriptlang.org/play/), and stay up to date via [our blog](https://devblogs.microsoft.com/typescript/) and [Twitter account](https://twitter.com/typescript). - -Find others who are using TypeScript at [our community page](https://www.typescriptlang.org/community/). - -## Installing - -For the latest stable version: - -```bash -npm install -D typescript -``` - -For our nightly builds: - -```bash -npm install -D typescript@next -``` - -## Contribute - -There are many ways to [contribute](CONTRIBUTING.md) to TypeScript. -* [Submit bugs](https://github.com/microsoft/TypeScript/issues) and help us verify fixes as they are checked in. -* Review the [source code changes](https://github.com/microsoft/TypeScript/pulls). -* Engage with other TypeScript users and developers on [StackOverflow](https://stackoverflow.com/questions/tagged/typescript). -* Help each other in the [TypeScript Community Discord](https://discord.gg/typescript). -* Join the [#typescript](https://twitter.com/search?q=%23TypeScript) discussion on Twitter. -* [Contribute bug fixes](CONTRIBUTING.md). - -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see -the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) -with any additional questions or comments. - -## Documentation - -* [TypeScript in 5 minutes](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html) -* [Programming handbook](https://www.typescriptlang.org/docs/handbook/intro.html) -* [Homepage](https://www.typescriptlang.org/) - -## Roadmap - -For details on our planned features and future direction, please refer to our [roadmap](https://github.com/microsoft/TypeScript/wiki/Roadmap). + +# TypeScript + +[![CI](https://github.com/microsoft/TypeScript/actions/workflows/ci.yml/badge.svg)](https://github.com/microsoft/TypeScript/actions/workflows/ci.yml) +[![npm version](https://badge.fury.io/js/typescript.svg)](https://www.npmjs.com/package/typescript) +[![Downloads](https://img.shields.io/npm/dm/typescript.svg)](https://www.npmjs.com/package/typescript) +[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/microsoft/TypeScript/badge)](https://securityscorecards.dev/viewer/?uri=github.com/microsoft/TypeScript) + + +[TypeScript](https://www.typescriptlang.org/) is a language for application-scale JavaScript. TypeScript adds optional types to JavaScript that support tools for large-scale JavaScript applications for any browser, for any host, on any OS. TypeScript compiles to readable, standards-based JavaScript. Try it out at the [playground](https://www.typescriptlang.org/play/), and stay up to date via [our blog](https://devblogs.microsoft.com/typescript/) and [Twitter account](https://twitter.com/typescript). + +Find others who are using TypeScript at [our community page](https://www.typescriptlang.org/community/). + +## Installing + +For the latest stable version: + +```bash +npm install -D typescript +``` + +For our nightly builds: + +```bash +npm install -D typescript@next +``` + +## Contribute + +There are many ways to [contribute](CONTRIBUTING.md) to TypeScript. +* [Submit bugs](https://github.com/microsoft/TypeScript/issues) and help us verify fixes as they are checked in. +* Review the [source code changes](https://github.com/microsoft/TypeScript/pulls). +* Engage with other TypeScript users and developers on [StackOverflow](https://stackoverflow.com/questions/tagged/typescript). +* Help each other in the [TypeScript Community Discord](https://discord.gg/typescript). +* Join the [#typescript](https://twitter.com/search?q=%23TypeScript) discussion on Twitter. +* [Contribute bug fixes](CONTRIBUTING.md). + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see +the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) +with any additional questions or comments. + +## Documentation + +* [TypeScript in 5 minutes](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes.html) +* [Programming handbook](https://www.typescriptlang.org/docs/handbook/intro.html) +* [Homepage](https://www.typescriptlang.org/) + +## Roadmap + +For details on our planned features and future direction, please refer to our [roadmap](https://github.com/microsoft/TypeScript/wiki/Roadmap). diff --git a/packages/typescript/lib/getExePath.d.ts b/packages/typescript/lib/getExePath.d.ts index bf33fb7cf6882..b78ebd282ab9f 100644 --- a/packages/typescript/lib/getExePath.d.ts +++ b/packages/typescript/lib/getExePath.d.ts @@ -1,2 +1,2 @@ -declare function getExePath(): string; -export default getExePath; +declare function getExePath(): string; +export default getExePath; diff --git a/packages/typescript/lib/getExePath.js b/packages/typescript/lib/getExePath.js index 0bb4ab39e13e7..2de01d4c005a9 100644 --- a/packages/typescript/lib/getExePath.js +++ b/packages/typescript/lib/getExePath.js @@ -1,70 +1,70 @@ -import fs from "node:fs"; -import module from "node:module"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; - -// NOTE: Keep VS Code extension's resolveTsdkPathToExe in sync with this function. -export default function getExePath() { - const __dirname = path.dirname(fileURLToPath(import.meta.url)); - const normalizedDirname = __dirname.replace(/\\/g, "/"); - - const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf8")); - const pkgName = pkg.name; - const baseName = pkgName.startsWith("@") ? pkgName.split("/")[1] : pkgName; - const expectedBinName = baseName === "typescript" ? "tsc" : "tsgo"; - const binNames = pkg.bin && typeof pkg.bin === "object" ? Object.keys(pkg.bin) : []; - if (binNames.length !== 1 || binNames[0] !== expectedBinName) { - throw new Error(`Expected ${pkgName} to declare exactly one bin entry named ${expectedBinName}.`); - } - let binName = expectedBinName; - let exeDir; - - const expectedPackage = baseName + "-" + process.platform + "-" + process.arch; - - if (normalizedDirname.endsWith("/packages/" + baseName + "/lib")) { - // We're running directly from source in the repo. - // The local repo build (`hereby build`) always produces `tsgo`, regardless - // of the published `bin` name, so don't use binName here. - exeDir = path.resolve(__dirname, "..", "..", "..", "built", "local"); - binName = "tsc"; - } - else if (normalizedDirname.endsWith("/built/npm/" + baseName + "/lib")) { - // We're running from the built output. - exeDir = path.resolve(__dirname, "..", "..", expectedPackage, "lib"); - } - else { - // We're actually running from an installed package. - const platformPackageName = "@typescript/" + expectedPackage; - try { - if (typeof import.meta.resolve === "undefined") { - // v16.20.1 - const require = module.createRequire(import.meta.url); - const packageJson = require.resolve(platformPackageName + "/package.json"); - exeDir = path.join(path.dirname(packageJson), "lib"); - } - else { - // v20.6.0, v18.19.0 - const packageJson = import.meta.resolve(platformPackageName + "/package.json"); - const packageJsonPath = fileURLToPath(packageJson); - exeDir = path.join(path.dirname(packageJsonPath), "lib"); - } - } - catch (e) { - throw new Error("Unable to resolve " + platformPackageName + ". Either your platform is unsupported, or you are missing the package on disk."); - } - } - - let exe = path.join(exeDir, binName); - if (process.platform === "win32") { - exe += ".exe"; - if (exe.length >= 248) { - exe = "\\\\?\\" + exe; - } - } - - if (!fs.existsSync(exe)) { - throw new Error("Executable not found: " + exe); - } - - return exe; -} +import fs from "node:fs"; +import module from "node:module"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +// NOTE: Keep VS Code extension's resolveTsdkPathToExe in sync with this function. +export default function getExePath() { + const __dirname = path.dirname(fileURLToPath(import.meta.url)); + const normalizedDirname = __dirname.replace(/\\/g, "/"); + + const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf8")); + const pkgName = pkg.name; + const baseName = pkgName.startsWith("@") ? pkgName.split("/")[1] : pkgName; + const expectedBinName = baseName === "typescript" ? "tsc" : "tsgo"; + const binNames = pkg.bin && typeof pkg.bin === "object" ? Object.keys(pkg.bin) : []; + if (binNames.length !== 1 || binNames[0] !== expectedBinName) { + throw new Error(`Expected ${pkgName} to declare exactly one bin entry named ${expectedBinName}.`); + } + let binName = expectedBinName; + let exeDir; + + const expectedPackage = baseName + "-" + process.platform + "-" + process.arch; + + if (normalizedDirname.endsWith("/packages/" + baseName + "/lib")) { + // We're running directly from source in the repo. + // The local repo build (`hereby build`) always produces `tsgo`, regardless + // of the published `bin` name, so don't use binName here. + exeDir = path.resolve(__dirname, "..", "..", "..", "built", "local"); + binName = "tsc"; + } + else if (normalizedDirname.endsWith("/built/npm/" + baseName + "/lib")) { + // We're running from the built output. + exeDir = path.resolve(__dirname, "..", "..", expectedPackage, "lib"); + } + else { + // We're actually running from an installed package. + const platformPackageName = "@typescript/" + expectedPackage; + try { + if (typeof import.meta.resolve === "undefined") { + // v16.20.1 + const require = module.createRequire(import.meta.url); + const packageJson = require.resolve(platformPackageName + "/package.json"); + exeDir = path.join(path.dirname(packageJson), "lib"); + } + else { + // v20.6.0, v18.19.0 + const packageJson = import.meta.resolve(platformPackageName + "/package.json"); + const packageJsonPath = fileURLToPath(packageJson); + exeDir = path.join(path.dirname(packageJsonPath), "lib"); + } + } + catch (e) { + throw new Error("Unable to resolve " + platformPackageName + ". Either your platform is unsupported, or you are missing the package on disk."); + } + } + + let exe = path.join(exeDir, binName); + if (process.platform === "win32") { + exe += ".exe"; + if (exe.length >= 248) { + exe = "\\\\?\\" + exe; + } + } + + if (!fs.existsSync(exe)) { + throw new Error("Executable not found: " + exe); + } + + return exe; +} diff --git a/packages/typescript/lib/tsc.js b/packages/typescript/lib/tsc.js index eb869b715737e..721fde8fb13a8 100644 --- a/packages/typescript/lib/tsc.js +++ b/packages/typescript/lib/tsc.js @@ -1,28 +1,28 @@ -#!/usr/bin/env node - -import getExePath from "#getExePath"; -import { execFileSync } from "node:child_process"; - -const exe = getExePath(); - -if (process.platform !== "win32" && typeof process.execve === "function") { - // > v22.15.0 - try { - process.execve(exe, [exe, ...process.argv.slice(2)]); - } - catch { - // may not be available, ignore the error and fallback - } -} - -try { - execFileSync(exe, process.argv.slice(2), { stdio: "inherit" }); -} -catch (e) { - if (e.status) { - process.exitCode = e.status; - } - else { - throw e; - } -} +#!/usr/bin/env node + +import getExePath from "#getExePath"; +import { execFileSync } from "node:child_process"; + +const exe = getExePath(); + +if (process.platform !== "win32" && typeof process.execve === "function") { + // > v22.15.0 + try { + process.execve(exe, [exe, ...process.argv.slice(2)]); + } + catch { + // may not be available, ignore the error and fallback + } +} + +try { + execFileSync(exe, process.argv.slice(2), { stdio: "inherit" }); +} +catch (e) { + if (e.status) { + process.exitCode = e.status; + } + else { + throw e; + } +} diff --git a/packages/typescript/lib/version.cjs b/packages/typescript/lib/version.cjs index fde8484c9078a..fa8ef64d86786 100644 --- a/packages/typescript/lib/version.cjs +++ b/packages/typescript/lib/version.cjs @@ -1,3 +1,3 @@ -const { version } = require("../package.json"); -exports.version = version; -exports.versionMajorMinor = "7.1"; +const { version } = require("../package.json"); +exports.version = version; +exports.versionMajorMinor = "7.1"; diff --git a/packages/typescript/lib/version.d.cts b/packages/typescript/lib/version.d.cts index 1939756106417..3f8198b773b9a 100644 --- a/packages/typescript/lib/version.d.cts +++ b/packages/typescript/lib/version.d.cts @@ -1,2 +1,2 @@ -export declare const version: string; -export declare const versionMajorMinor = "7.1"; +export declare const version: string; +export declare const versionMajorMinor = "7.1"; diff --git a/packages/typescript/scripts/generateSync.ts b/packages/typescript/scripts/generateSync.ts index d0e61d3e54e3e..af292c5497826 100755 --- a/packages/typescript/scripts/generateSync.ts +++ b/packages/typescript/scripts/generateSync.ts @@ -1,629 +1,629 @@ -#!/usr/bin/env -S node --experimental-strip-types --no-warnings - -/** - * Generates sync API from async API source files. - * - * Reads async/types.ts and async/api.ts, applies directive-based and - * AST-based transforms, and writes sync/types.ts and sync/api.ts. - * - * Directives (placed in comments in the async source): - * // @sync-skip — omit this line in sync output - * // @sync-skip-block-start/end — omit all lines between (inclusive) - * // @sync-only-start/end — uncomment lines between (strip "// " prefix) - * // @sync: — replace this line with (preserving indent) - * - * AST-based transforms (applied after directives using the TypeScript compiler API): - * - Generate synchronous implementations from async methods - * - Attach generator implementations as each synchronous method's `.gen` property - * - Unwrap `Promise` → `T` in synchronous type references - * - * Usage: - * node --experimental-strip-types --no-warnings generateSync.ts - */ - -import { execaSync } from "execa"; -import { - mkdirSync, - readFileSync, - writeFileSync, -} from "node:fs"; -import { - dirname, - join, - relative, -} from "node:path"; -import ts from "typescript"; - -function generatedHeader(asyncSourceRelPath: string): string { - return [ - "//", - "// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", - "// !!! THIS FILE IS AUTO-GENERATED - DO NOT EDIT !!!", - "// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", - "//", - `// Source: ${asyncSourceRelPath}`, - "// Regenerate: npm run generate (from packages/typescript)", - "//", - "", - ].join("\n"); -} - -const ROOT = join(import.meta.dirname!, ".."); -const SRC = join(ROOT, "src", "api"); -const TEST = join(ROOT, "test"); - -type SourceTransform = (source: string, fileName: string) => string; - -function generateSyncFile( - srcPath: string, - destPath: string, - transform: SourceTransform, -): string { - const source = readFileSync(srcPath, "utf-8"); - - // Normalize line endings to LF - const normalized = source.replace(/\r/g, ""); - - // Phase 1: Process sync directives (text-based, operates on comments/lines) - const afterDirectives = processDirectives(normalized.split("\n")).join("\n"); - - // Phase 2: AST-based async→sync transforms - const fileName = destPath.split("/").pop()!; - let result = transform(afterDirectives, fileName); - - // Prepend generated header pointing to async source - const srcRelPath = relative(ROOT, srcPath).replaceAll("\\", "/"); - result = generatedHeader(srcRelPath) + result; - - mkdirSync(dirname(destPath), { recursive: true }); - writeFileSync(destPath, result); - const label = relative(ROOT, srcPath).replaceAll("\\", "/"); - const destLabel = relative(ROOT, destPath).replaceAll("\\", "/"); - console.log(` ${label} → ${destLabel}`); - return destPath; -} - -// ── Directive processing ───────────────────────────────────────── - -function processDirectives(lines: string[]): string[] { - const output: string[] = []; - let skipBlock = false; - let syncOnlyBlock = false; - - for (const line of lines) { - const trimmed = line.trim(); - - // Block-skip markers - if (trimmed === "// @sync-skip-block-start") { - skipBlock = true; - continue; - } - if (trimmed === "// @sync-skip-block-end") { - skipBlock = false; - continue; - } - if (skipBlock) continue; - - // Sync-only markers (uncomment block) - if (trimmed === "// @sync-only-start") { - syncOnlyBlock = true; - continue; - } - if (trimmed === "// @sync-only-end") { - syncOnlyBlock = false; - continue; - } - - if (syncOnlyBlock) { - const indent = line.match(/^(\s*)/)![1]; - const rest = line.slice(indent.length); - if (rest.startsWith("// ")) { - output.push(indent + rest.slice(3)); - } - else if (rest === "//") { - output.push(indent); - } - else { - output.push(line); - } - continue; - } - - // Single-line skip - if (line.includes("// @sync-skip")) { - continue; - } - - // Single-line replacement: // @sync: - const syncReplaceMatch = line.match(/\/\/ @sync: (.+)$/); - if (syncReplaceMatch) { - const indent = line.match(/^(\s*)/)![1]; - output.push(indent + syncReplaceMatch[1]); - continue; - } - - output.push(line); - } - - return output; -} - -// ── AST-based transforms ──────────────────────────────────────── - -interface Edit { - start: number; - end: number; - newText: string; -} - -function applyEdits(source: string, edits: Edit[]): string { - edits.sort((a, b) => b.start - a.start); - let result = source; - for (const edit of edits) { - result = result.slice(0, edit.start) + edit.newText + result.slice(edit.end); - } - return result; -} - -type TransformMode = "sync" | "generator"; - -function transformAsyncSource(source: string, fileName: string, attachGenerators: boolean): string { - const sourceFile = ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, true); - const edits: Edit[] = []; - const asyncMethodNames = new Set(); - const synchronousMethodNames = new Set(); - - for (const classDeclaration of getNamedClasses(sourceFile).values()) { - for (const group of getMethodGroups(classDeclaration).values()) { - const method = group.implementation; - if (isGeneratorMethod(method)) asyncMethodNames.add((method.name as ts.Identifier).text); - else synchronousMethodNames.add((method.name as ts.Identifier).text); - } - } - - visit(sourceFile); - return applyEdits(source, edits); - - function visit(node: ts.Node): void { - if (attachGenerators && ts.isClassDeclaration(node)) { - const replacedMethods = new Set(); - for (const group of getMethodGroups(node).values()) { - if (!isGeneratorMethod(group.implementation)) continue; - for (const declaration of group.declarations) replacedMethods.add(declaration); - edits.push({ - start: group.declarations[0].getStart(sourceFile), - end: group.implementation.end, - newText: createCombinedMethodFromAsync(group, source, sourceFile), - }); - } - for (const member of node.members) { - if (!ts.isMethodDeclaration(member) || !replacedMethods.has(member)) visit(member); - } - return; - } - - if (attachGenerators && ts.isInterfaceDeclaration(node)) { - const replacedSignatures = new Set(); - for (const [name, declarations] of getSignatureGroups(node)) { - if (!declarations.every(declaration => isPromiseType(declaration.type))) continue; - for (const declaration of declarations) replacedSignatures.add(declaration); - const indent = getIndent(source, declarations[0].getStart(sourceFile)); - const syncSignatures = declarations.map(declaration => `${indent} ${createAsyncCallSignature(declaration, "sync")}`).join("\n"); - const generatorSignatures = declarations.map(declaration => `${indent} gen${createAsyncCallSignature(declaration, "generator")}`).join("\n"); - edits.push({ - start: declarations[0].getStart(sourceFile), - end: declarations.at(-1)!.end, - newText: `${name}: {\n${syncSignatures}\n${generatorSignatures}\n${indent}};`, - }); - } - for (const member of node.members) { - if (!ts.isMethodSignature(member) || !replacedSignatures.has(member)) visit(member); - } - return; - } - - addSyncEdit(node, source, sourceFile, edits); - ts.forEachChild(node, visit); - } - - function createCombinedMethodFromAsync(group: MethodGroup, source: string, sourceFile: ts.SourceFile): string { - const method = group.implementation; - const name = (method.name as ts.Identifier).text; - const indent = getIndent(source, group.declarations[0].getStart(sourceFile)); - const modifiers = method.modifiers - ?.filter(modifier => modifier.kind !== ts.SyntaxKind.AsyncKeyword) - .map(modifier => source.slice(modifier.getStart(sourceFile), modifier.end)) - .join(" "); - const declarations = getPublicDeclarations(group); - const syncSignatures = declarations.map(declaration => `${indent} ${createAsyncCallSignature(declaration, "sync")}`).join("\n"); - const generatorSignatures = declarations.map(declaration => `${indent} gen${createAsyncCallSignature(declaration, "generator")}`).join("\n"); - const body = declarations.length > 1 - ? createAsyncOverloadedGetterBody(name, group, indent) - : `{\n${indent} const owner = this;\n${indent} return cacheGeneratorMethod(\n${indent} owner,\n${indent} "${name}",\n${indent} ${createAsyncFunction(method, "sync", false)},\n${indent} ${createAsyncFunction(method, "generator", true)},\n${indent} );\n${indent}}`; - return `${modifiers ? `${modifiers} ` : ""}get ${name}(): {\n${syncSignatures}\n${generatorSignatures}\n${indent}} ${body}`; - } - - function createAsyncOverloadedGetterBody(name: string, group: MethodGroup, indent: string): string { - const declarations = getPublicDeclarations(group); - const syncOverloads = declarations.map(declaration => `function ${name}${createAsyncCallSignature(declaration, "sync")}`).join("\n"); - const generatorOverloads = declarations.map(declaration => `function gen${createAsyncCallSignature(declaration, "generator")}`).join("\n"); - const syncImplementation = createAsyncNamedFunction(name, group.implementation, "sync", false); - const generatorImplementation = createAsyncNamedFunction("gen", group.implementation, "generator", true); - return `{\n${indent} const owner = this;\n${indent}${indentLines(syncOverloads, `${indent} `)}\n${indent}${indentLines(syncImplementation, `${indent} `)}\n${indent}${indentLines(generatorOverloads, `${indent} `)}\n${indent}${indentLines(generatorImplementation, `${indent} `)}\n${indent} return cacheGeneratorMethod(owner, "${name}", ${name}, gen);\n${indent}}`; - } - - function createAsyncCallSignature(node: ts.SignatureDeclarationBase, mode: TransformMode): string { - return `${getTypeParametersText(node, source, sourceFile)}(${getAsyncSignatureParameters(node, mode)}): ${getAsyncReturnType(node, mode)};`; - } - - function createAsyncFunction(node: ts.MethodDeclaration, mode: TransformMode, generator: boolean): string { - const parameters = node.parameters.map(parameter => transformNodeText(parameter, mode, true)).join(", "); - const body = transformNodeText(node.body!, mode, true); - return `function${generator ? "*" : ""} ${getTypeParametersText(node, source, sourceFile)}(${parameters}): ${getAsyncReturnType(node, mode)} ${body}`; - } - - function createAsyncNamedFunction(name: string, node: ts.MethodDeclaration, mode: TransformMode, generator: boolean): string { - const parameters = node.parameters.map(parameter => transformNodeText(parameter, mode, true)).join(", "); - const body = transformNodeText(node.body!, mode, true); - return `function${generator ? "*" : ""} ${name}${getTypeParametersText(node, source, sourceFile)}(${parameters}): ${getAsyncReturnType(node, mode)} ${body}`; - } - - function getAsyncSignatureParameters(node: ts.SignatureDeclarationBase, mode: TransformMode): string { - return node.parameters.map(parameter => { - const rest = parameter.dotDotDotToken ? "..." : ""; - const name = source.slice(parameter.name.getStart(sourceFile), parameter.name.end); - const optional = parameter.questionToken || parameter.initializer ? "?" : ""; - const type = parameter.type ? transformNodeText(parameter.type, mode, false) : "unknown"; - return `${rest}${name}${optional}: ${type}`; - }).join(", "); - } - - function getAsyncReturnType(node: ts.SignatureDeclarationBase, mode: TransformMode): string { - if (!node.type) return "void"; - if (mode === "generator" && isPromiseType(node.type)) { - const returnType = getUnwrappedPromiseText(node.type, source, sourceFile); - return `Generator`; - } - return transformNodeText(node.type, mode, false); - } - - function transformNodeText(node: ts.Node, mode: TransformMode, replaceThis: boolean): string { - const start = node.getStart(sourceFile); - const localEdits: Edit[] = []; - - function visitNode(current: ts.Node): void { - if (replaceThis && current.kind === ts.SyntaxKind.ThisKeyword) { - localEdits.push({ start: current.getStart(sourceFile) - start, end: current.end - start, newText: "owner" }); - return; - } - if (replaceThis && ts.isBlock(node) && current !== node && ts.isFunctionLike(current) && !ts.isArrowFunction(current)) return; - - if (mode === "sync") { - if (addSyncEdit(current, source, sourceFile, localEdits, start)) return; - } - else if (addGeneratorEdit(current, start, localEdits)) { - return; - } - ts.forEachChild(current, visitNode); - } - - visitNode(node); - return applyEdits(source.slice(start, node.end), localEdits); - } - - function addGeneratorEdit(node: ts.Node, offset: number, targetEdits: Edit[]): boolean { - if (ts.isAwaitExpression(node)) { - const replacement = getGeneratorAwaitText(node); - editsForNode(node, replacement, offset); - return true; - } - if (ts.isCallExpression(node) && isAPIRequestCall(node) && !ts.isAwaitExpression(node.parent)) { - const receiver = node.expression.expression; - if (ts.isPropertyAccessExpression(receiver) && receiver.name.text === "client") { - editsForNode(node, getRequestYieldText(node), offset); - return true; - } - } - if ( - ts.isCallExpression(node) && - !ts.isAwaitExpression(node.parent) && - isGeneratorCall(node) - ) { - const generatorCall = `yield* ${getGeneratorCallText(node)}`; - editsForNode(node, ts.isReturnStatement(node.parent) ? generatorCall : `(${generatorCall})`, offset); - return true; - } - if (ts.isTypeReferenceNode(node) && isPromiseType(node)) { - const replacement = isFunctionReturnType(node) - ? `Generator` - : getUnwrappedPromiseText(node, source, sourceFile); - editsForNode(node, replacement, offset); - return true; - } - return false; - - function editsForNode(current: ts.Node, replacement: string, base: number): void { - targetEdits.push({ start: current.getStart(sourceFile) - base, end: current.end - base, newText: replacement }); - } - } - - function getGeneratorAwaitText(node: ts.AwaitExpression): string { - if (isAPIRequestCall(node.expression)) { - const receiver = node.expression.expression.expression; - if (ts.isPropertyAccessExpression(receiver) && receiver.name.text === "client") return getRequestYieldText(node.expression); - return `yield ${getTextWithOwner(node.expression)}`; - } - if ( - ts.isCallExpression(node.expression) && - ts.isPropertyAccessExpression(node.expression.expression) && - ts.isPropertyAccessExpression(node.expression.expression.expression) && - node.expression.expression.expression.name.text === "client" - ) { - return getTextWithOwner(node.expression); - } - if (ts.isCallExpression(node.expression) && ts.isIdentifier(node.expression.expression) && node.expression.expression.text === "cb") { - return `yield* (${getTextWithOwner(node.expression)} ?? [])`; - } - const expression = ts.isCallExpression(node.expression) && ts.isPropertyAccessExpression(node.expression.expression) - ? getGeneratorCallText(node.expression) - : getTextWithOwner(node.expression); - return `(yield* ${expression})`; - } - - function getRequestYieldText(call: ts.CallExpression): string { - const [method, params] = call.arguments; - const methodText = getTextWithOwner(method); - const paramsText = params ? getTextWithOwner(params) : "undefined"; - return `yield* apiRequest(${methodText}, ${paramsText})`; - } - - function getGeneratorCallText(call: ts.CallExpression): string { - const expression = getTextWithOwner(call.expression); - const callText = getTextWithOwner(call); - return `${expression}.gen${callText.slice(expression.length)}`; - } - - function getTextWithOwner(node: ts.Node): string { - const start = node.getStart(sourceFile); - const ownerEdits: Edit[] = []; - - function visitNode(current: ts.Node): void { - if (current.kind === ts.SyntaxKind.ThisKeyword) { - ownerEdits.push({ start: current.getStart(sourceFile) - start, end: current.end - start, newText: "owner" }); - return; - } - if (current !== node && ts.isFunctionLike(current) && !ts.isArrowFunction(current)) return; - ts.forEachChild(current, visitNode); - } - - visitNode(node); - return applyEdits(source.slice(start, node.end), ownerEdits); - } - - function isGeneratorCall(node: ts.Expression): boolean { - if (ts.isParenthesizedExpression(node) || ts.isAsExpression(node)) return isGeneratorCall(node.expression); - if (ts.isConditionalExpression(node)) return isGeneratorCall(node.whenTrue) && isGeneratorCall(node.whenFalse); - const call = getCallExpression(node); - return !!call && - ts.isPropertyAccessExpression(call.expression) && - !(ts.isPropertyAccessExpression(call.expression.expression) && call.expression.expression.name.text === "client") && - !synchronousMethodNames.has(call.expression.name.text) && - asyncMethodNames.has(call.expression.name.text); - } - - function isGeneratorMethod(node: ts.MethodDeclaration): boolean { - return !!node.body && (node.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.AsyncKeyword) || isPromiseType(node.type)); - } - - function isPromiseType(node: ts.TypeNode | undefined): node is ts.TypeReferenceNode { - return !!node && - ts.isTypeReferenceNode(node) && - ts.isIdentifier(node.typeName) && - node.typeName.text === "Promise" && - node.typeArguments?.length === 1; - } - - function isAPIRequestCall(node: ts.Expression): node is ts.CallExpression & { expression: ts.PropertyAccessExpression; } { - return ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && node.expression.name.text === "apiRequest"; - } - - function getCallExpression(node: ts.Expression): ts.CallExpression | undefined { - while (ts.isAsExpression(node) || ts.isParenthesizedExpression(node)) node = node.expression; - return ts.isCallExpression(node) ? node : undefined; - } - - function isFunctionReturnType(node: ts.TypeNode): boolean { - let current: ts.Node = node; - while (ts.isTypeNode(current.parent)) { - if ((ts.isFunctionTypeNode(current.parent) || ts.isConstructorTypeNode(current.parent)) && current.parent.type === current) return true; - current = current.parent; - } - return ts.isFunctionLike(current.parent) && current.parent.type === current; - } -} - -function addSyncEdit(node: ts.Node, source: string, sourceFile: ts.SourceFile, edits: Edit[], offset: number = 0): boolean { - if ( - (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node) || ts.isArrowFunction(node) || ts.isFunctionExpression(node)) && - node.modifiers - ) { - for (const modifier of node.modifiers) { - if (modifier.kind !== ts.SyntaxKind.AsyncKeyword) continue; - let end = modifier.end; - while (end < source.length && source[end] === " ") end++; - edits.push({ start: modifier.getStart(sourceFile) - offset, end: end - offset, newText: "" }); - } - } - if (ts.isAwaitExpression(node)) { - edits.push({ - start: node.getStart(sourceFile) - offset, - end: node.expression.getStart(sourceFile) - offset, - newText: "", - }); - } - if (isPromiseTypeReference(node)) { - const innerText = getUnwrappedPromiseText(node, source, sourceFile); - if (ts.isUnionTypeNode(node.parent)) { - const unionTypes = node.parent.types; - const currentIndex = unionTypes.indexOf(node); - const representativeIndex = unionTypes.findIndex(type => getUnwrappedTypeText(type, source, sourceFile) === innerText); - if (representativeIndex !== currentIndex) { - if (currentIndex < representativeIndex) { - edits.push({ - start: node.getStart(sourceFile) - offset, - end: unionTypes[currentIndex + 1].getStart(sourceFile) - offset, - newText: "", - }); - } - else { - edits.push({ - start: unionTypes[currentIndex - 1].end - offset, - end: node.end - offset, - newText: "", - }); - } - return true; - } - } - edits.push({ start: node.getStart(sourceFile) - offset, end: node.end - offset, newText: innerText }); - return true; - } - return false; -} - -function isPromiseTypeReference(node: ts.Node | undefined): node is ts.TypeReferenceNode { - return !!node && - ts.isTypeReferenceNode(node) && - ts.isIdentifier(node.typeName) && - node.typeName.text === "Promise" && - node.typeArguments?.length === 1; -} - -function getUnwrappedPromiseText(node: ts.TypeNode, source: string, sourceFile: ts.SourceFile): string { - let current = node; - while (isPromiseTypeReference(current)) current = current.typeArguments![0]; - return source.slice(current.getStart(sourceFile), current.end); -} - -function getUnwrappedTypeText(node: ts.TypeNode, source: string, sourceFile: ts.SourceFile): string { - return isPromiseTypeReference(node) ? getUnwrappedPromiseText(node, source, sourceFile) : source.slice(node.getStart(sourceFile), node.end); -} - -interface MethodGroup { - declarations: readonly ts.MethodDeclaration[]; - implementation: ts.MethodDeclaration; -} - -function getNamedClasses(sourceFile: ts.SourceFile): Map { - const classes = new Map(); - for (const statement of sourceFile.statements) { - if (ts.isClassDeclaration(statement) && statement.name) classes.set(statement.name.text, statement); - } - return classes; -} - -function getMethodGroups(node: ts.ClassDeclaration): Map { - const groups = new Map(); - for (let index = 0; index < node.members.length; index++) { - const member = node.members[index]; - if (!ts.isMethodDeclaration(member) || !ts.isIdentifier(member.name)) continue; - - const declarations = [member]; - while (index + 1 < node.members.length) { - const nextMember = node.members[index + 1]; - if (!ts.isMethodDeclaration(nextMember) || !ts.isIdentifier(nextMember.name) || nextMember.name.text !== member.name.text) break; - declarations.push(nextMember); - index++; - } - const implementation = declarations.findLast(declaration => declaration.body); - if (!implementation) continue; - groups.set(getMethodKey(implementation), { declarations, implementation }); - } - return groups; -} - -function getSignatureGroups(node: ts.InterfaceDeclaration): Map { - const groups = new Map(); - for (const member of node.members) { - if (!ts.isMethodSignature(member) || !ts.isIdentifier(member.name)) continue; - const declarations = groups.get(member.name.text) ?? []; - declarations.push(member); - groups.set(member.name.text, declarations); - } - return groups; -} - -function getMethodKey(node: ts.MethodDeclaration): string { - return `${node.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.StaticKeyword) ? "static:" : ""}${(node.name as ts.Identifier).text}`; -} - -function getPublicDeclarations(group: MethodGroup): readonly ts.MethodDeclaration[] { - const overloads = group.declarations.filter(declaration => !declaration.body); - return overloads.length ? overloads : [group.implementation]; -} - -function indentLines(text: string, indent: string): string { - return text.replaceAll("\n", `\n${indent}`); -} - -function getTypeParametersText(node: ts.SignatureDeclarationBase, source: string, sourceFile: ts.SourceFile): string { - if (!node.typeParameters?.length) return ""; - return `<${node.typeParameters.map(typeParameter => source.slice(typeParameter.getStart(sourceFile), typeParameter.end)).join(", ")}>`; -} - -function getIndent(source: string, position: number): string { - const lineStart = source.lastIndexOf("\n", position - 1) + 1; - return source.slice(lineStart, position); -} - -// ── Formatting ─────────────────────────────────────────────────── - -function formatFiles(paths: string[]): void { - execaSync("dprint", ["fmt", ...paths]); -} - -// ── Main ───────────────────────────────────────────────────────── - -export function generateSync(): void { - console.log("Generating sync API from async source..."); - const generatedFiles: string[] = []; - - // Source files - for (const relPath of ["types.ts", "api.ts"]) { - generatedFiles.push(generateSyncFile( - join(SRC, "async", relPath), - join(SRC, "sync", relPath), - relPath === "api.ts" - ? (source, fileName) => - [ - 'import type { APIRequest as ProtocolRequest, APIResponse as ProtocolResponse } from "../proto.ts";', - 'import { apiRequest, cacheGeneratorMethod } from "./generatorSupport.ts";', - "", - transformAsyncSource(source, fileName, true), - ].join("\n") - : (source, fileName) => - [ - 'import type { APIRequest as ProtocolRequest, APIResponse as ProtocolResponse } from "../proto.ts";', - "", - transformAsyncSource(source, fileName, true), - ].join("\n"), - )); - } - - // Test files - for (const relPath of ["api.test.ts", "api.bench.ts", "astnav.test.ts"]) { - generatedFiles.push(generateSyncFile( - join(TEST, "async", relPath), - join(TEST, "sync", relPath), - (source, fileName) => transformAsyncSource(source, fileName, false), - )); - } - - console.log("Formatting..."); - formatFiles(generatedFiles); - console.log("Done."); -} - -if (process.argv[1] === import.meta.filename) { - generateSync(); -} +#!/usr/bin/env -S node --experimental-strip-types --no-warnings + +/** + * Generates sync API from async API source files. + * + * Reads async/types.ts and async/api.ts, applies directive-based and + * AST-based transforms, and writes sync/types.ts and sync/api.ts. + * + * Directives (placed in comments in the async source): + * // @sync-skip — omit this line in sync output + * // @sync-skip-block-start/end — omit all lines between (inclusive) + * // @sync-only-start/end — uncomment lines between (strip "// " prefix) + * // @sync: — replace this line with (preserving indent) + * + * AST-based transforms (applied after directives using the TypeScript compiler API): + * - Generate synchronous implementations from async methods + * - Attach generator implementations as each synchronous method's `.gen` property + * - Unwrap `Promise` → `T` in synchronous type references + * + * Usage: + * node --experimental-strip-types --no-warnings generateSync.ts + */ + +import { execaSync } from "execa"; +import { + mkdirSync, + readFileSync, + writeFileSync, +} from "node:fs"; +import { + dirname, + join, + relative, +} from "node:path"; +import ts from "typescript"; + +function generatedHeader(asyncSourceRelPath: string): string { + return [ + "//", + "// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", + "// !!! THIS FILE IS AUTO-GENERATED - DO NOT EDIT !!!", + "// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", + "//", + `// Source: ${asyncSourceRelPath}`, + "// Regenerate: npm run generate (from packages/typescript)", + "//", + "", + ].join("\n"); +} + +const ROOT = join(import.meta.dirname!, ".."); +const SRC = join(ROOT, "src", "api"); +const TEST = join(ROOT, "test"); + +type SourceTransform = (source: string, fileName: string) => string; + +function generateSyncFile( + srcPath: string, + destPath: string, + transform: SourceTransform, +): string { + const source = readFileSync(srcPath, "utf-8"); + + // Normalize line endings to LF + const normalized = source.replace(/\r/g, ""); + + // Phase 1: Process sync directives (text-based, operates on comments/lines) + const afterDirectives = processDirectives(normalized.split("\n")).join("\n"); + + // Phase 2: AST-based async→sync transforms + const fileName = destPath.split("/").pop()!; + let result = transform(afterDirectives, fileName); + + // Prepend generated header pointing to async source + const srcRelPath = relative(ROOT, srcPath).replaceAll("\\", "/"); + result = generatedHeader(srcRelPath) + result; + + mkdirSync(dirname(destPath), { recursive: true }); + writeFileSync(destPath, result); + const label = relative(ROOT, srcPath).replaceAll("\\", "/"); + const destLabel = relative(ROOT, destPath).replaceAll("\\", "/"); + console.log(` ${label} → ${destLabel}`); + return destPath; +} + +// ── Directive processing ───────────────────────────────────────── + +function processDirectives(lines: string[]): string[] { + const output: string[] = []; + let skipBlock = false; + let syncOnlyBlock = false; + + for (const line of lines) { + const trimmed = line.trim(); + + // Block-skip markers + if (trimmed === "// @sync-skip-block-start") { + skipBlock = true; + continue; + } + if (trimmed === "// @sync-skip-block-end") { + skipBlock = false; + continue; + } + if (skipBlock) continue; + + // Sync-only markers (uncomment block) + if (trimmed === "// @sync-only-start") { + syncOnlyBlock = true; + continue; + } + if (trimmed === "// @sync-only-end") { + syncOnlyBlock = false; + continue; + } + + if (syncOnlyBlock) { + const indent = line.match(/^(\s*)/)![1]; + const rest = line.slice(indent.length); + if (rest.startsWith("// ")) { + output.push(indent + rest.slice(3)); + } + else if (rest === "//") { + output.push(indent); + } + else { + output.push(line); + } + continue; + } + + // Single-line skip + if (line.includes("// @sync-skip")) { + continue; + } + + // Single-line replacement: // @sync: + const syncReplaceMatch = line.match(/\/\/ @sync: (.+)$/); + if (syncReplaceMatch) { + const indent = line.match(/^(\s*)/)![1]; + output.push(indent + syncReplaceMatch[1]); + continue; + } + + output.push(line); + } + + return output; +} + +// ── AST-based transforms ──────────────────────────────────────── + +interface Edit { + start: number; + end: number; + newText: string; +} + +function applyEdits(source: string, edits: Edit[]): string { + edits.sort((a, b) => b.start - a.start); + let result = source; + for (const edit of edits) { + result = result.slice(0, edit.start) + edit.newText + result.slice(edit.end); + } + return result; +} + +type TransformMode = "sync" | "generator"; + +function transformAsyncSource(source: string, fileName: string, attachGenerators: boolean): string { + const sourceFile = ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, true); + const edits: Edit[] = []; + const asyncMethodNames = new Set(); + const synchronousMethodNames = new Set(); + + for (const classDeclaration of getNamedClasses(sourceFile).values()) { + for (const group of getMethodGroups(classDeclaration).values()) { + const method = group.implementation; + if (isGeneratorMethod(method)) asyncMethodNames.add((method.name as ts.Identifier).text); + else synchronousMethodNames.add((method.name as ts.Identifier).text); + } + } + + visit(sourceFile); + return applyEdits(source, edits); + + function visit(node: ts.Node): void { + if (attachGenerators && ts.isClassDeclaration(node)) { + const replacedMethods = new Set(); + for (const group of getMethodGroups(node).values()) { + if (!isGeneratorMethod(group.implementation)) continue; + for (const declaration of group.declarations) replacedMethods.add(declaration); + edits.push({ + start: group.declarations[0].getStart(sourceFile), + end: group.implementation.end, + newText: createCombinedMethodFromAsync(group, source, sourceFile), + }); + } + for (const member of node.members) { + if (!ts.isMethodDeclaration(member) || !replacedMethods.has(member)) visit(member); + } + return; + } + + if (attachGenerators && ts.isInterfaceDeclaration(node)) { + const replacedSignatures = new Set(); + for (const [name, declarations] of getSignatureGroups(node)) { + if (!declarations.every(declaration => isPromiseType(declaration.type))) continue; + for (const declaration of declarations) replacedSignatures.add(declaration); + const indent = getIndent(source, declarations[0].getStart(sourceFile)); + const syncSignatures = declarations.map(declaration => `${indent} ${createAsyncCallSignature(declaration, "sync")}`).join("\n"); + const generatorSignatures = declarations.map(declaration => `${indent} gen${createAsyncCallSignature(declaration, "generator")}`).join("\n"); + edits.push({ + start: declarations[0].getStart(sourceFile), + end: declarations.at(-1)!.end, + newText: `${name}: {\n${syncSignatures}\n${generatorSignatures}\n${indent}};`, + }); + } + for (const member of node.members) { + if (!ts.isMethodSignature(member) || !replacedSignatures.has(member)) visit(member); + } + return; + } + + addSyncEdit(node, source, sourceFile, edits); + ts.forEachChild(node, visit); + } + + function createCombinedMethodFromAsync(group: MethodGroup, source: string, sourceFile: ts.SourceFile): string { + const method = group.implementation; + const name = (method.name as ts.Identifier).text; + const indent = getIndent(source, group.declarations[0].getStart(sourceFile)); + const modifiers = method.modifiers + ?.filter(modifier => modifier.kind !== ts.SyntaxKind.AsyncKeyword) + .map(modifier => source.slice(modifier.getStart(sourceFile), modifier.end)) + .join(" "); + const declarations = getPublicDeclarations(group); + const syncSignatures = declarations.map(declaration => `${indent} ${createAsyncCallSignature(declaration, "sync")}`).join("\n"); + const generatorSignatures = declarations.map(declaration => `${indent} gen${createAsyncCallSignature(declaration, "generator")}`).join("\n"); + const body = declarations.length > 1 + ? createAsyncOverloadedGetterBody(name, group, indent) + : `{\n${indent} const owner = this;\n${indent} return cacheGeneratorMethod(\n${indent} owner,\n${indent} "${name}",\n${indent} ${createAsyncFunction(method, "sync", false)},\n${indent} ${createAsyncFunction(method, "generator", true)},\n${indent} );\n${indent}}`; + return `${modifiers ? `${modifiers} ` : ""}get ${name}(): {\n${syncSignatures}\n${generatorSignatures}\n${indent}} ${body}`; + } + + function createAsyncOverloadedGetterBody(name: string, group: MethodGroup, indent: string): string { + const declarations = getPublicDeclarations(group); + const syncOverloads = declarations.map(declaration => `function ${name}${createAsyncCallSignature(declaration, "sync")}`).join("\n"); + const generatorOverloads = declarations.map(declaration => `function gen${createAsyncCallSignature(declaration, "generator")}`).join("\n"); + const syncImplementation = createAsyncNamedFunction(name, group.implementation, "sync", false); + const generatorImplementation = createAsyncNamedFunction("gen", group.implementation, "generator", true); + return `{\n${indent} const owner = this;\n${indent}${indentLines(syncOverloads, `${indent} `)}\n${indent}${indentLines(syncImplementation, `${indent} `)}\n${indent}${indentLines(generatorOverloads, `${indent} `)}\n${indent}${indentLines(generatorImplementation, `${indent} `)}\n${indent} return cacheGeneratorMethod(owner, "${name}", ${name}, gen);\n${indent}}`; + } + + function createAsyncCallSignature(node: ts.SignatureDeclarationBase, mode: TransformMode): string { + return `${getTypeParametersText(node, source, sourceFile)}(${getAsyncSignatureParameters(node, mode)}): ${getAsyncReturnType(node, mode)};`; + } + + function createAsyncFunction(node: ts.MethodDeclaration, mode: TransformMode, generator: boolean): string { + const parameters = node.parameters.map(parameter => transformNodeText(parameter, mode, true)).join(", "); + const body = transformNodeText(node.body!, mode, true); + return `function${generator ? "*" : ""} ${getTypeParametersText(node, source, sourceFile)}(${parameters}): ${getAsyncReturnType(node, mode)} ${body}`; + } + + function createAsyncNamedFunction(name: string, node: ts.MethodDeclaration, mode: TransformMode, generator: boolean): string { + const parameters = node.parameters.map(parameter => transformNodeText(parameter, mode, true)).join(", "); + const body = transformNodeText(node.body!, mode, true); + return `function${generator ? "*" : ""} ${name}${getTypeParametersText(node, source, sourceFile)}(${parameters}): ${getAsyncReturnType(node, mode)} ${body}`; + } + + function getAsyncSignatureParameters(node: ts.SignatureDeclarationBase, mode: TransformMode): string { + return node.parameters.map(parameter => { + const rest = parameter.dotDotDotToken ? "..." : ""; + const name = source.slice(parameter.name.getStart(sourceFile), parameter.name.end); + const optional = parameter.questionToken || parameter.initializer ? "?" : ""; + const type = parameter.type ? transformNodeText(parameter.type, mode, false) : "unknown"; + return `${rest}${name}${optional}: ${type}`; + }).join(", "); + } + + function getAsyncReturnType(node: ts.SignatureDeclarationBase, mode: TransformMode): string { + if (!node.type) return "void"; + if (mode === "generator" && isPromiseType(node.type)) { + const returnType = getUnwrappedPromiseText(node.type, source, sourceFile); + return `Generator`; + } + return transformNodeText(node.type, mode, false); + } + + function transformNodeText(node: ts.Node, mode: TransformMode, replaceThis: boolean): string { + const start = node.getStart(sourceFile); + const localEdits: Edit[] = []; + + function visitNode(current: ts.Node): void { + if (replaceThis && current.kind === ts.SyntaxKind.ThisKeyword) { + localEdits.push({ start: current.getStart(sourceFile) - start, end: current.end - start, newText: "owner" }); + return; + } + if (replaceThis && ts.isBlock(node) && current !== node && ts.isFunctionLike(current) && !ts.isArrowFunction(current)) return; + + if (mode === "sync") { + if (addSyncEdit(current, source, sourceFile, localEdits, start)) return; + } + else if (addGeneratorEdit(current, start, localEdits)) { + return; + } + ts.forEachChild(current, visitNode); + } + + visitNode(node); + return applyEdits(source.slice(start, node.end), localEdits); + } + + function addGeneratorEdit(node: ts.Node, offset: number, targetEdits: Edit[]): boolean { + if (ts.isAwaitExpression(node)) { + const replacement = getGeneratorAwaitText(node); + editsForNode(node, replacement, offset); + return true; + } + if (ts.isCallExpression(node) && isAPIRequestCall(node) && !ts.isAwaitExpression(node.parent)) { + const receiver = node.expression.expression; + if (ts.isPropertyAccessExpression(receiver) && receiver.name.text === "client") { + editsForNode(node, getRequestYieldText(node), offset); + return true; + } + } + if ( + ts.isCallExpression(node) && + !ts.isAwaitExpression(node.parent) && + isGeneratorCall(node) + ) { + const generatorCall = `yield* ${getGeneratorCallText(node)}`; + editsForNode(node, ts.isReturnStatement(node.parent) ? generatorCall : `(${generatorCall})`, offset); + return true; + } + if (ts.isTypeReferenceNode(node) && isPromiseType(node)) { + const replacement = isFunctionReturnType(node) + ? `Generator` + : getUnwrappedPromiseText(node, source, sourceFile); + editsForNode(node, replacement, offset); + return true; + } + return false; + + function editsForNode(current: ts.Node, replacement: string, base: number): void { + targetEdits.push({ start: current.getStart(sourceFile) - base, end: current.end - base, newText: replacement }); + } + } + + function getGeneratorAwaitText(node: ts.AwaitExpression): string { + if (isAPIRequestCall(node.expression)) { + const receiver = node.expression.expression.expression; + if (ts.isPropertyAccessExpression(receiver) && receiver.name.text === "client") return getRequestYieldText(node.expression); + return `yield ${getTextWithOwner(node.expression)}`; + } + if ( + ts.isCallExpression(node.expression) && + ts.isPropertyAccessExpression(node.expression.expression) && + ts.isPropertyAccessExpression(node.expression.expression.expression) && + node.expression.expression.expression.name.text === "client" + ) { + return getTextWithOwner(node.expression); + } + if (ts.isCallExpression(node.expression) && ts.isIdentifier(node.expression.expression) && node.expression.expression.text === "cb") { + return `yield* (${getTextWithOwner(node.expression)} ?? [])`; + } + const expression = ts.isCallExpression(node.expression) && ts.isPropertyAccessExpression(node.expression.expression) + ? getGeneratorCallText(node.expression) + : getTextWithOwner(node.expression); + return `(yield* ${expression})`; + } + + function getRequestYieldText(call: ts.CallExpression): string { + const [method, params] = call.arguments; + const methodText = getTextWithOwner(method); + const paramsText = params ? getTextWithOwner(params) : "undefined"; + return `yield* apiRequest(${methodText}, ${paramsText})`; + } + + function getGeneratorCallText(call: ts.CallExpression): string { + const expression = getTextWithOwner(call.expression); + const callText = getTextWithOwner(call); + return `${expression}.gen${callText.slice(expression.length)}`; + } + + function getTextWithOwner(node: ts.Node): string { + const start = node.getStart(sourceFile); + const ownerEdits: Edit[] = []; + + function visitNode(current: ts.Node): void { + if (current.kind === ts.SyntaxKind.ThisKeyword) { + ownerEdits.push({ start: current.getStart(sourceFile) - start, end: current.end - start, newText: "owner" }); + return; + } + if (current !== node && ts.isFunctionLike(current) && !ts.isArrowFunction(current)) return; + ts.forEachChild(current, visitNode); + } + + visitNode(node); + return applyEdits(source.slice(start, node.end), ownerEdits); + } + + function isGeneratorCall(node: ts.Expression): boolean { + if (ts.isParenthesizedExpression(node) || ts.isAsExpression(node)) return isGeneratorCall(node.expression); + if (ts.isConditionalExpression(node)) return isGeneratorCall(node.whenTrue) && isGeneratorCall(node.whenFalse); + const call = getCallExpression(node); + return !!call && + ts.isPropertyAccessExpression(call.expression) && + !(ts.isPropertyAccessExpression(call.expression.expression) && call.expression.expression.name.text === "client") && + !synchronousMethodNames.has(call.expression.name.text) && + asyncMethodNames.has(call.expression.name.text); + } + + function isGeneratorMethod(node: ts.MethodDeclaration): boolean { + return !!node.body && (node.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.AsyncKeyword) || isPromiseType(node.type)); + } + + function isPromiseType(node: ts.TypeNode | undefined): node is ts.TypeReferenceNode { + return !!node && + ts.isTypeReferenceNode(node) && + ts.isIdentifier(node.typeName) && + node.typeName.text === "Promise" && + node.typeArguments?.length === 1; + } + + function isAPIRequestCall(node: ts.Expression): node is ts.CallExpression & { expression: ts.PropertyAccessExpression; } { + return ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && node.expression.name.text === "apiRequest"; + } + + function getCallExpression(node: ts.Expression): ts.CallExpression | undefined { + while (ts.isAsExpression(node) || ts.isParenthesizedExpression(node)) node = node.expression; + return ts.isCallExpression(node) ? node : undefined; + } + + function isFunctionReturnType(node: ts.TypeNode): boolean { + let current: ts.Node = node; + while (ts.isTypeNode(current.parent)) { + if ((ts.isFunctionTypeNode(current.parent) || ts.isConstructorTypeNode(current.parent)) && current.parent.type === current) return true; + current = current.parent; + } + return ts.isFunctionLike(current.parent) && current.parent.type === current; + } +} + +function addSyncEdit(node: ts.Node, source: string, sourceFile: ts.SourceFile, edits: Edit[], offset: number = 0): boolean { + if ( + (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node) || ts.isArrowFunction(node) || ts.isFunctionExpression(node)) && + node.modifiers + ) { + for (const modifier of node.modifiers) { + if (modifier.kind !== ts.SyntaxKind.AsyncKeyword) continue; + let end = modifier.end; + while (end < source.length && source[end] === " ") end++; + edits.push({ start: modifier.getStart(sourceFile) - offset, end: end - offset, newText: "" }); + } + } + if (ts.isAwaitExpression(node)) { + edits.push({ + start: node.getStart(sourceFile) - offset, + end: node.expression.getStart(sourceFile) - offset, + newText: "", + }); + } + if (isPromiseTypeReference(node)) { + const innerText = getUnwrappedPromiseText(node, source, sourceFile); + if (ts.isUnionTypeNode(node.parent)) { + const unionTypes = node.parent.types; + const currentIndex = unionTypes.indexOf(node); + const representativeIndex = unionTypes.findIndex(type => getUnwrappedTypeText(type, source, sourceFile) === innerText); + if (representativeIndex !== currentIndex) { + if (currentIndex < representativeIndex) { + edits.push({ + start: node.getStart(sourceFile) - offset, + end: unionTypes[currentIndex + 1].getStart(sourceFile) - offset, + newText: "", + }); + } + else { + edits.push({ + start: unionTypes[currentIndex - 1].end - offset, + end: node.end - offset, + newText: "", + }); + } + return true; + } + } + edits.push({ start: node.getStart(sourceFile) - offset, end: node.end - offset, newText: innerText }); + return true; + } + return false; +} + +function isPromiseTypeReference(node: ts.Node | undefined): node is ts.TypeReferenceNode { + return !!node && + ts.isTypeReferenceNode(node) && + ts.isIdentifier(node.typeName) && + node.typeName.text === "Promise" && + node.typeArguments?.length === 1; +} + +function getUnwrappedPromiseText(node: ts.TypeNode, source: string, sourceFile: ts.SourceFile): string { + let current = node; + while (isPromiseTypeReference(current)) current = current.typeArguments![0]; + return source.slice(current.getStart(sourceFile), current.end); +} + +function getUnwrappedTypeText(node: ts.TypeNode, source: string, sourceFile: ts.SourceFile): string { + return isPromiseTypeReference(node) ? getUnwrappedPromiseText(node, source, sourceFile) : source.slice(node.getStart(sourceFile), node.end); +} + +interface MethodGroup { + declarations: readonly ts.MethodDeclaration[]; + implementation: ts.MethodDeclaration; +} + +function getNamedClasses(sourceFile: ts.SourceFile): Map { + const classes = new Map(); + for (const statement of sourceFile.statements) { + if (ts.isClassDeclaration(statement) && statement.name) classes.set(statement.name.text, statement); + } + return classes; +} + +function getMethodGroups(node: ts.ClassDeclaration): Map { + const groups = new Map(); + for (let index = 0; index < node.members.length; index++) { + const member = node.members[index]; + if (!ts.isMethodDeclaration(member) || !ts.isIdentifier(member.name)) continue; + + const declarations = [member]; + while (index + 1 < node.members.length) { + const nextMember = node.members[index + 1]; + if (!ts.isMethodDeclaration(nextMember) || !ts.isIdentifier(nextMember.name) || nextMember.name.text !== member.name.text) break; + declarations.push(nextMember); + index++; + } + const implementation = declarations.findLast(declaration => declaration.body); + if (!implementation) continue; + groups.set(getMethodKey(implementation), { declarations, implementation }); + } + return groups; +} + +function getSignatureGroups(node: ts.InterfaceDeclaration): Map { + const groups = new Map(); + for (const member of node.members) { + if (!ts.isMethodSignature(member) || !ts.isIdentifier(member.name)) continue; + const declarations = groups.get(member.name.text) ?? []; + declarations.push(member); + groups.set(member.name.text, declarations); + } + return groups; +} + +function getMethodKey(node: ts.MethodDeclaration): string { + return `${node.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.StaticKeyword) ? "static:" : ""}${(node.name as ts.Identifier).text}`; +} + +function getPublicDeclarations(group: MethodGroup): readonly ts.MethodDeclaration[] { + const overloads = group.declarations.filter(declaration => !declaration.body); + return overloads.length ? overloads : [group.implementation]; +} + +function indentLines(text: string, indent: string): string { + return text.replaceAll("\n", `\n${indent}`); +} + +function getTypeParametersText(node: ts.SignatureDeclarationBase, source: string, sourceFile: ts.SourceFile): string { + if (!node.typeParameters?.length) return ""; + return `<${node.typeParameters.map(typeParameter => source.slice(typeParameter.getStart(sourceFile), typeParameter.end)).join(", ")}>`; +} + +function getIndent(source: string, position: number): string { + const lineStart = source.lastIndexOf("\n", position - 1) + 1; + return source.slice(lineStart, position); +} + +// ── Formatting ─────────────────────────────────────────────────── + +function formatFiles(paths: string[]): void { + execaSync("dprint", ["fmt", ...paths]); +} + +// ── Main ───────────────────────────────────────────────────────── + +export function generateSync(): void { + console.log("Generating sync API from async source..."); + const generatedFiles: string[] = []; + + // Source files + for (const relPath of ["types.ts", "api.ts"]) { + generatedFiles.push(generateSyncFile( + join(SRC, "async", relPath), + join(SRC, "sync", relPath), + relPath === "api.ts" + ? (source, fileName) => + [ + 'import type { APIRequest as ProtocolRequest, APIResponse as ProtocolResponse } from "../proto.ts";', + 'import { apiRequest, cacheGeneratorMethod } from "./generatorSupport.ts";', + "", + transformAsyncSource(source, fileName, true), + ].join("\n") + : (source, fileName) => + [ + 'import type { APIRequest as ProtocolRequest, APIResponse as ProtocolResponse } from "../proto.ts";', + "", + transformAsyncSource(source, fileName, true), + ].join("\n"), + )); + } + + // Test files + for (const relPath of ["api.test.ts", "api.bench.ts", "astnav.test.ts"]) { + generatedFiles.push(generateSyncFile( + join(TEST, "async", relPath), + join(TEST, "sync", relPath), + (source, fileName) => transformAsyncSource(source, fileName, false), + )); + } + + console.log("Formatting..."); + formatFiles(generatedFiles); + console.log("Done."); +} + +if (process.argv[1] === import.meta.filename) { + generateSync(); +} diff --git a/packages/typescript/src/api/async/api.ts b/packages/typescript/src/api/async/api.ts index ef6d7fb7ef10b..81669ed11206a 100644 --- a/packages/typescript/src/api/async/api.ts +++ b/packages/typescript/src/api/async/api.ts @@ -1,2843 +1,2843 @@ -import { CheckFlags } from "#enums/checkFlags"; -import { CompletionItemKind } from "#enums/completionItemKind"; -import { DiagnosticCategory } from "#enums/diagnosticCategory"; -import { ElementFlags } from "#enums/elementFlags"; -import { EmitOnly } from "#enums/emitOnly"; -import { ModuleKind } from "#enums/moduleKind"; -import { NewLineKind } from "#enums/newLineKind"; -import { NodeBuilderFlags } from "#enums/nodeBuilderFlags"; -import { ObjectFlags } from "#enums/objectFlags"; -import { SignatureFlags } from "#enums/signatureFlags"; -import { SignatureKind } from "#enums/signatureKind"; -import { SymbolFlags } from "#enums/symbolFlags"; -import { TypeFlags } from "#enums/typeFlags"; -import { TypeFormatFlags } from "#enums/typeFormatFlags"; -import { TypePredicateKind } from "#enums/typePredicateKind"; -import { - type __String, - type Declaration, - type Expression, - type Identifier, - type IndexSignatureDeclaration, - ModifierFlags, - type Node, - type Path, - type SourceFile, - type SyntaxKind, - type TypeNode, - unescapeLeadingUnderscores, -} from "../../ast/index.ts"; -import { assertNever } from "../../internal/utils.ts"; -import { - encodeNode, - uint8ArrayToBase64, -} from "../node/encoder.ts"; -import { - decodeNode, - getNodeId, - parseNodeHandle, - readParseOptionsKey, - readSourceFileHash, - RemoteSourceFile, -} from "../node/node.ts"; -import { Wtf8Decoder } from "../node/wtf8.ts"; -import type { - APIOptions, - LSPConnectionOptions, -} from "../options.ts"; -import { - createGetCanonicalFileName, - toPath, -} from "../path.ts"; -import type { - CompilerOptions, - Diagnostic, - DocumentIdentifier, - DocumentPosition, - EmitOutputResponse as ProtocolEmitOutputResponse, - ImportAdderAction, - IntrinsicTypeMethod, - LSPUpdateSnapshotParams, - ParsedCommandLine, - ProjectReference, - ProjectResponse, - ReadConfigFileResponse, - SignaturePropertyMethod, - SignatureResponse, - SourceFileMetadata, - SymbolPropertyMethod, - SymbolResponse, - SymbolsPropertyMethod, - TextEdit, - TypeAcquisition, - TypePropertyMethod, - TypeResponse, - TypesPropertyMethod, - UpdateSnapshotParams, - UpdateSnapshotResponse, -} from "../proto.ts"; -import { - resolveFileName, - toUpdateSnapshotRequest, -} from "../proto.ts"; -import { SourceFileCache } from "../sourceFileCache.ts"; -import type { - RequestTiming, - TimingAccumulators, - TimingInfo, -} from "../timing.ts"; -import { - Client, - type ClientSocketOptions, - type ClientSpawnOptions, -} from "./client.ts"; -import type { - AssertsIdentifierTypePredicate, - AssertsThisTypePredicate, - BigIntLiteralType, - BooleanLiteralType, - CompletionEntry, - CompletionInfo, - CompletionOptions, - ConditionalType, - EmitOutput, - EmitOutputFile, - EmitResult, - FormatDiagnosticsHost, - FreshableType, - GetImportEditsForSymbolsOptions, - IdentifierTypePredicate, - ImportAdderAction as APIImportAdderAction, - IndexedAccessType, - IndexInfo, - IndexType, - InterfaceType, - IntersectionType, - IntrinsicType, - JSDocTagInfo, - LiteralType, - NumberLiteralType, - ObjectType, - StringLiteralType, - StringMappingType, - StructuredType, - SubstitutionType, - TemplateLiteralType, - ThisTypePredicate, - TupleType, - Type, - TypeParameter, - TypePredicate, - TypePredicateBase, - TypeReference, - UnionOrIntersectionType, - UnionType, -} from "./types.ts"; - -export { formatDiagnostics, formatDiagnosticsWithColorAndContext } from "../diagnosticFormatter.ts"; -export { documentURIToFileName, fileNameToDocumentURI } from "../path.ts"; -export { CheckFlags, CompletionItemKind, DiagnosticCategory, ElementFlags, EmitOnly, ModifierFlags, ModuleKind, NodeBuilderFlags, ObjectFlags, SignatureFlags, SignatureKind, SymbolFlags, TypeFlags, TypeFormatFlags, TypePredicateKind }; -export type { - APIImportAdderAction as ImportAdderAction, - APIOptions, - AssertsIdentifierTypePredicate, - AssertsThisTypePredicate, - BigIntLiteralType, - BooleanLiteralType, - ClientSocketOptions, - ClientSpawnOptions, - CompilerOptions, - CompletionEntry, - CompletionInfo, - CompletionOptions, - ConditionalType, - Diagnostic, - DocumentIdentifier, - DocumentPosition, - EmitOutput, - EmitOutputFile, - EmitResult, - FormatDiagnosticsHost, - FreshableType, - GetImportEditsForSymbolsOptions, - IdentifierTypePredicate, - IndexedAccessType, - IndexInfo, - IndexType, - InterfaceType, - IntersectionType, - IntrinsicType, - JSDocTagInfo, - LiteralType, - LSPConnectionOptions, - NumberLiteralType, - ObjectType, - ParsedCommandLine, - ProjectReference, - ReadConfigFileResponse, - RequestTiming, - SourceFileMetadata, - StringLiteralType, - StringMappingType, - StructuredType, - SubstitutionType, - TemplateLiteralType, - TextEdit, - ThisTypePredicate, - TimingAccumulators, - TimingInfo, - TupleType, - Type, - TypeAcquisition, - TypeParameter, - TypePredicate, - TypePredicateBase, - TypeReference, - UnionOrIntersectionType, - UnionType, -}; - -export interface TranspileOptions { - compilerOptions?: CompilerOptions; - fileName?: string; - reportDiagnostics?: boolean; -} - -export interface TranspileOutput { - outputText: string; - diagnostics?: readonly Diagnostic[]; - sourceMapText?: string; -} - -// @sync-only-start -// export { all } from "./generatorSupport.ts"; -// import {all, type ExecutedGeneratorsResults, type APIRequestGenerator} from "./generatorSupport.ts"; -// @sync-only-end - -export class API implements FormatDiagnosticsHost { - private client: Client; - private sourceFileCache: SourceFileCache; - private toPath: ((fileName: string) => Path) | undefined; - private currentDirectory: string | undefined; - private getCanonicalFileNameWorker: ((fileName: string) => string) | undefined; - private initialized: boolean = false; - private activeSnapshots: Set = new Set(); - private latestSnapshot: Snapshot | undefined; - readonly internal: InternalAPI; - - constructor(options: APIOptions | LSPConnectionOptions = {}) { - this.client = new Client(options); - this.sourceFileCache = new SourceFileCache(); - this.internal = new InternalAPI(this.client, () => this.ensureInitialized()); // @sync: this.internal = new InternalAPI(this.client, this.ensureInitialized); - } - - /** - * Create an API instance from an existing LSP connection's API session. - * Use this when connecting to an API pipe provided by an LSP server via custom/initializeAPISession. - */ - static async fromLSPConnection(options: LSPConnectionOptions): Promise> { - const api = new API(options); - await api.ensureInitialized(); - return api; - } - - // @sync-skip-block-start - batchContext(): { [globalThis.Symbol.dispose](): void; } { - return this.client.batchContext(); - } - // @sync-skip-block-end - // @sync-only-start - // batch(...requestGenerators: T): ExecutedGeneratorsResults { - // const batches = all(...requestGenerators); - // let state = batches.next(); - // while (!state.done) { - // state = batches.next(this.client.batchRequests(state.value).responses); - // } - // return state.value; - // } - // @sync-only-end - - private async ensureInitialized(): Promise { - if (!this.initialized) { - const response = await this.client.apiRequest("initialize", null); - const getCanonicalFileName = createGetCanonicalFileName(response.useCaseSensitiveFileNames); - const currentDirectory = response.currentDirectory; - this.getCanonicalFileNameWorker = getCanonicalFileName; - this.currentDirectory = currentDirectory; - this.toPath = (fileName: string) => toPath(fileName, currentDirectory, getCanonicalFileName) as Path; - this.initialized = true; - } - } - - getCurrentDirectory(): string { - if (this.currentDirectory === undefined) { - throw new Error("API has not been initialized"); - } - return this.currentDirectory; - } - - getCanonicalFileName(fileName: string): string { - if (this.getCanonicalFileNameWorker === undefined) { - throw new Error("API has not been initialized"); - } - return this.getCanonicalFileNameWorker(fileName); - } - - getNewLine(): string { - return "\n"; - } - - async parseConfigFile(file: DocumentIdentifier): Promise { - await this.ensureInitialized(); - return this.client.apiRequest("parseConfigFile", { file }); - } - - async parseCommandLine(commandLine: readonly string[]): Promise { - await this.ensureInitialized(); - return this.client.apiRequest("parseCommandLine", { commandLine }); - } - - async readConfigFile(file: DocumentIdentifier): Promise { - await this.ensureInitialized(); - return this.client.apiRequest("readConfigFile", { file }); - } - - async parseJsonConfigFileContent( - json: any, - options: - | { configDirectory: string; configFileName?: never; } - | { configFileName: DocumentIdentifier; configDirectory?: never; }, - ): Promise { - await this.ensureInitialized(); - return this.client.apiRequest("parseJsonConfigFileContent", { json, ...options }); - } - - async transpileModule(input: string, options: TranspileOptions = {}): Promise { - await this.ensureInitialized(); - return this.client.apiRequest("transpileModule", { input, options }); - } - - async transpileModuleFromFile(fileName: string, options: TranspileOptions = {}): Promise { - await this.ensureInitialized(); - return this.client.apiRequest("transpileModuleFromFile", { fileName, options }); - } - - async transpileDeclaration(input: string, options: TranspileOptions = {}): Promise { - await this.ensureInitialized(); - return this.client.apiRequest("transpileDeclaration", { input, options }); - } - - async transpileDeclarationFromFile(fileName: string, options: TranspileOptions = {}): Promise { - await this.ensureInitialized(); - return this.client.apiRequest("transpileDeclarationFromFile", { fileName, options }); - } - - async updateSnapshot(params?: FromLSP extends true ? LSPUpdateSnapshotParams : UpdateSnapshotParams): Promise { - await this.ensureInitialized(); - - const requestParams = toUpdateSnapshotRequest(params); - const data = await this.client.apiRequest("updateSnapshot", requestParams); - - // Retain cached source files from previous snapshot for unchanged files - if (this.latestSnapshot) { - this.sourceFileCache.retainForSnapshot(data.snapshot, this.latestSnapshot.id, data.changes); - if (this.latestSnapshot.isDisposed()) { - this.sourceFileCache.releaseSnapshot(this.latestSnapshot.id); - } - } - - const snapshot = new Snapshot( - data, - this.client, - this.sourceFileCache, - this.toPath!, - this, - () => { - this.activeSnapshots.delete(snapshot); - if (snapshot !== this.latestSnapshot) { - this.sourceFileCache.releaseSnapshot(snapshot.id); - } - }, - ); - this.latestSnapshot = snapshot; - this.activeSnapshots.add(snapshot); - - return snapshot; - } - - async close(): Promise { - // Dispose all active snapshots - for (const snapshot of [...this.activeSnapshots]) { - await snapshot.dispose(); - } - // Release the latest snapshot's cache refs if still held - if (this.latestSnapshot) { - this.sourceFileCache.releaseSnapshot(this.latestSnapshot.id); - this.latestSnapshot = undefined; - } - await this.client.close(); - this.sourceFileCache.clear(); - } - - clearSourceFileCache(): void { - this.sourceFileCache.clear(); - } - - async runWithTemporaryFileUpdate(baseSnapshot: Snapshot, file: DocumentIdentifier, newText: string, cb: (newSnapshot: Snapshot) => void | Promise): Promise { - await this.ensureInitialized(); - - if (!this.activeSnapshots.has(baseSnapshot) || baseSnapshot.isDisposed()) { - throw new Error("Cannot run a temporary file update on an inactive snapshot"); - } - const data = await this.client.apiRequest("updateTemporarySnapshot", { snapshot: baseSnapshot.id, file, newText }); - - // Retain cached source files from the base snapshot for files unchanged by - // the temporary update. The temporary snapshot is not the latest snapshot, so - // we never release the latest snapshot's cache here. - this.sourceFileCache.retainForSnapshot(data.snapshot, baseSnapshot.id, data.changes); - - const snapshot = new Snapshot( - data, - this.client, - this.sourceFileCache, - this.toPath!, - this, - () => { - this.activeSnapshots.delete(snapshot); - this.sourceFileCache.releaseSnapshot(snapshot.id); - }, - ); - this.activeSnapshots.add(snapshot); - - try { - await cb(snapshot); - } - finally { - await snapshot.dispose(); - } - } - - /** - * Returns a snapshot of collected timing information for requests made - * through this API instance: client-measured round-trip latency and bytes - * transferred, folded together with the server's own per-request processing - * time and an estimated transport overhead (round-trip minus server time). - * - * Fetching the snapshot issues a lightweight request to the server to - * retrieve its timing collection. Collection must be enabled via the - * `collectTiming` option; when it is not, the returned snapshot has - * `enabled: false` and zeroed totals. - */ - getTimingInfo(): Promise { - return this.client.getTimingInfo(); - } - - /** Clears all accumulated timing totals and recent-request history, on both the client and the server. */ - resetTimingInfo(): Promise { - return this.client.resetTimingInfo(); - } -} - -type EnsureInitialized = () => Promise; // @sync: type EnsureInitialized = (() => void) & { gen(): Generator; }; - -export class InternalAPI { - private client: Client; - private ensureInitialized: EnsureInitialized; - - /** @internal */ - constructor(client: Client, ensureInitialized: EnsureInitialized) { - this.client = client; - this.ensureInitialized = ensureInitialized; - } - - async startCPUProfile(dir: string): Promise { - await this.ensureInitialized(); - await this.client.apiRequest("startCPUProfile", { dir }); - } - - async stopCPUProfile(): Promise { - await this.ensureInitialized(); - const result = await this.client.apiRequest("stopCPUProfile", null); - return result.file; - } - - async saveHeapProfile(dir: string): Promise { - await this.ensureInitialized(); - const result = await this.client.apiRequest("saveHeapProfile", { dir }); - return result.file; - } -} - -export class Snapshot { - readonly id: number; - private projectMap: Map; - private toPath: (fileName: string) => Path; - private client: Client; - private disposed: boolean = false; - private onDispose: () => void; - private snapshotRegistry: SnapshotObjectRegistry; - readonly internal: SnapshotInternalAPI; - - constructor( - data: UpdateSnapshotResponse, - client: Client, - sourceFileCache: SourceFileCache, - toPath: (fileName: string) => Path, - formatDiagnosticsHost: FormatDiagnosticsHost, - onDispose: () => void, - ) { - this.id = data.snapshot; - this.client = client; - this.toPath = toPath; - this.onDispose = onDispose; - this.projectMap = new Map(); - this.snapshotRegistry = new SnapshotObjectRegistry(client, this.id, projectId => this.projectMap.get(projectId)); - - for (const projData of data.projects) { - const project = new Project(projData, this.id, client, sourceFileCache, toPath, formatDiagnosticsHost, this.snapshotRegistry); - this.projectMap.set(toPath(projData.configFileName), project); - } - - this.internal = new SnapshotInternalAPI(this.id, client); - } - - getProjects(): readonly Project[] { - this.ensureNotDisposed(); - return [...this.projectMap.values()]; - } - - getProject(configFileName: string): Project | undefined { - this.ensureNotDisposed(); - return this.projectMap.get(this.toPath(configFileName)); - } - - async getDefaultProjectForFile(file: DocumentIdentifier): Promise { - this.ensureNotDisposed(); - const data = await this.client.apiRequest("getDefaultProjectForFile", { - snapshot: this.id, - file, - }); - if (!data) return undefined; - return this.projectMap.get(this.toPath(data.configFileName)); - } - - [globalThis.Symbol.dispose](): void { - this.dispose(); - } - - async dispose(): Promise { - if (this.disposed) return; - this.disposed = true; - for (const project of this.projectMap.values()) { - project.dispose(); - } - this.projectMap.clear(); - this.snapshotRegistry.clear(); - this.onDispose(); - await this.client.apiRequest("release", { snapshot: this.id }); - } - - isDisposed(): boolean { - return this.disposed; - } - - private ensureNotDisposed(): void { - if (this.disposed) { - throw new Error("Snapshot is disposed"); - } - } -} - -class SnapshotObjectRegistry { - private readonly symbols: Map = new Map(); - private readonly client: Client; - private readonly snapshotId: number; - private readonly resolveProject: (projectId: Path) => Project | undefined; - - constructor(client: Client, snapshotId: number, resolveProject: (projectId: Path) => Project | undefined) { - this.client = client; - this.snapshotId = snapshotId; - this.resolveProject = resolveProject; - } - - /** Resolve a project id (a config file path) to its Project within this snapshot. */ - getProject(projectId: Path): Project | undefined { - return this.resolveProject(projectId); - } - - getOrCreateSymbol(data: SymbolResponse): Symbol { - let symbol = this.symbols.get(data.id); - if (!symbol) { - symbol = new Symbol(data, this); - this.symbols.set(data.id, symbol); - } - return symbol; - } - - getSymbol(id: number): Symbol | undefined { - return this.symbols.get(id); - } - - clear(): void { - this.symbols.clear(); - } - - async fetchSymbol(source: Symbol | Signature | Type, method: SymbolPropertyMethod, handle: number | undefined, projectId: Path): Promise { - if (!handle) return undefined as unknown as Symbol; - const cached = this.getSymbol(handle); - if (cached) return cached; - - const data = await this.client.apiRequest(method, { - snapshot: this.snapshotId, - project: projectId, - objectId: source.id, - }); - if (!data) throw new Error(`${method} returned null symbol for ${source.constructor.name} ${source.id}`); - return this.getOrCreateSymbol(data); - } - - async fetchSymbols(source: Symbol | Signature | Type, method: SymbolsPropertyMethod, handles: readonly number[] | undefined, projectId: Path): Promise { - if (handles) { - const result = new Array(handles.length); - let allCached = true; - for (let i = 0; i < handles.length; i++) { - const cached = this.getSymbol(handles[i]); - if (!cached) { - allCached = false; - break; - } - result[i] = cached; - } - if (allCached) return result; - } - const symbolData = await this.client.apiRequest(method, { - snapshot: this.snapshotId, - project: projectId, - objectId: source.id, - }); - if (symbolData == null) return []; - else return symbolData.map(data => this.getOrCreateSymbol(data)); - } -} - -class ProjectObjectRegistry { - private client: Client; - private snapshotId: number; - private project: Project; - private snapshotRegistry: SnapshotObjectRegistry; - private types: Map = new Map(); - private signatures: Map = new Map(); - - constructor( - client: Client, - snapshotId: number, - project: Project, - snapshotRegistry: SnapshotObjectRegistry, - ) { - this.client = client; - this.snapshotId = snapshotId; - this.project = project; - this.snapshotRegistry = snapshotRegistry; - } - - getOrCreateSymbol(data: SymbolResponse): Symbol { - return this.snapshotRegistry.getOrCreateSymbol(data); - } - - getSymbol(id: number): Symbol | undefined { - return this.snapshotRegistry.getSymbol(id); - } - - getOrCreateType(data: TypeResponse): TypeObject { - let type = this.types.get(data.id); - if (!type) { - type = new TypeObject(data, this); - this.types.set(data.id, type); - } - return type; - } - - getType(id: number): TypeObject | undefined { - return this.types.get(id); - } - - getOrCreateSignature(data: SignatureResponse): Signature { - let sig = this.signatures.get(data.id); - if (!sig) { - sig = new Signature(data, this.project, this); - this.signatures.set(data.id, sig); - } - return sig; - } - - getSignature(id: number): Signature | undefined { - return this.signatures.get(id); - } - - clear(): void { - this.types.clear(); - this.signatures.clear(); - } - - async fetchOptionalType(source: Symbol | Signature | Type, method: TypePropertyMethod, handle: number | false | undefined): Promise { - if (handle !== false) { - if (!handle) return undefined; - const cached = this.getType(handle); - if (cached) return cached as unknown as T; - } - - const data = await this.client.apiRequest(method, { - snapshot: this.snapshotId, - project: this.project.id, - objectId: source.id, - }); - if (!data) return undefined; - return this.getOrCreateType(data) as unknown as T; - } - - async fetchType(source: Symbol | Signature | Type, method: TypePropertyMethod, handle: number | false | undefined): Promise { - const result = await this.fetchOptionalType(source, method, handle); - if (result === undefined) throw new Error(`${method} returned no type for ${source.constructor.name} ${source.id}`); - return result; - } - - async fetchSymbol(source: Symbol | Signature | Type, method: SymbolPropertyMethod, handle: number | undefined): Promise { - return this.snapshotRegistry.fetchSymbol(source, method, handle, this.project.id); - } - - async fetchSignature(source: Symbol | Signature | Type, method: SignaturePropertyMethod, handle: number | undefined): Promise { - if (!handle) return undefined as unknown as Signature; - const cached = this.getSignature(handle); - if (cached) return cached; - - const data = await this.client.apiRequest(method, { - snapshot: this.snapshotId, - project: this.project.id, - objectId: source.id, - }); - if (!data) throw new Error(`${method} returned null signature for ${source.constructor.name} ${source.id}`); - return this.getOrCreateSignature(data); - } - - async fetchTypes(source: Symbol | Signature | Type, method: TypesPropertyMethod, handles?: readonly number[]): Promise { - if (handles) { - const result = new Array(handles.length); - let allCached = true; - for (let i = 0; i < handles.length; i++) { - const cached = this.getType(handles[i]); - if (!cached) { - allCached = false; - break; - } - result[i] = cached; - } - if (allCached) return result; - } - const typesData = await this.client.apiRequest(method, { - snapshot: this.snapshotId, - project: this.project.id, - objectId: source.id, - }); - if (typesData == null) return []; - else return typesData.map(data => this.getOrCreateType(data)); - } - - async fetchSymbols(source: Symbol | Signature | Type, method: SymbolsPropertyMethod, handles?: readonly number[]): Promise { - return this.snapshotRegistry.fetchSymbols(source, method, handles, this.project.id); - } - - // getBaseTypes is a checker-level endpoint keyed by `type` (not `objectId`), - // so it cannot go through fetchTypes. This helper reuses that server method. - async fetchBaseTypes(source: Type): Promise { - const typesData = await this.client.apiRequest("getBaseTypes", { - snapshot: this.snapshotId, - project: this.project.id, - type: source.id, - }); - if (typesData == null) return []; - return typesData.map(data => this.getOrCreateType(data)); - } - - async fetchPropertiesOfType(source: Type): Promise { - const data = await this.client.apiRequest("getPropertiesOfType", { - snapshot: this.snapshotId, - project: this.project.id, - type: source.id, - }); - return data ? data.map(symbol => this.getOrCreateSymbol(symbol)) : []; - } - - async fetchApparentPropertiesOfType(source: Type): Promise { - const data = await this.client.apiRequest("getApparentPropertiesOfType", { - snapshot: this.snapshotId, - project: this.project.id, - objectId: source.id, - }); - return data ? data.map(symbol => this.getOrCreateSymbol(symbol)) : []; - } - - async fetchPropertyOfType(source: Type, name: string): Promise { - const data = await this.client.apiRequest("getPropertyOfType", { - snapshot: this.snapshotId, - project: this.project.id, - type: source.id, - name, - }); - return data ? this.getOrCreateSymbol(data) : undefined; - } - - async fetchSignaturesOfType(source: Type, kind: SignatureKind): Promise { - const data = await this.client.apiRequest("getSignaturesOfType", { - snapshot: this.snapshotId, - project: this.project.id, - type: source.id, - kind, - }); - return data.map(signature => this.getOrCreateSignature(signature)); - } - - async fetchIndexInfosOfType(source: Type): Promise { - const data = await this.client.apiRequest("getIndexInfosOfType", { - snapshot: this.snapshotId, - project: this.project.id, - type: source.id, - }); - if (!data) return []; - return data.map(info => ({ - keyType: this.getOrCreateType(info.keyType), - valueType: this.getOrCreateType(info.valueType), - isReadonly: info.isReadonly ?? false, - declaration: info.declaration ? new NodeHandle(info.declaration, this.project) : undefined, - })); - } - - async fetchTypeParameterAtPosition(source: Signature, pos: number): Promise { - const data = await this.client.apiRequest("getTypeParameterAtPosition", { - snapshot: this.snapshotId, - project: this.project.id, - signature: source.id, - index: pos, - }); - return this.getOrCreateType(data); - } -} - -export class Project { - readonly id: Path; - readonly configFileName: string; - readonly currentDirectory: string; - readonly parsedCommandLine: ParsedCommandLine; - /** @deprecated Use `parsedCommandLine.options`. */ - readonly compilerOptions: CompilerOptions; - /** @deprecated Use `parsedCommandLine.fileNames`. */ - readonly rootFiles: readonly string[]; - - readonly program: Program; - readonly checker: Checker; - readonly emitter: Emitter; - readonly languageService: LanguageService; - private client: Client; - private snapshotId: number; - - constructor( - data: ProjectResponse, - snapshotId: number, - client: Client, - sourceFileCache: SourceFileCache, - toPath: (fileName: string) => Path, - formatDiagnosticsHost: FormatDiagnosticsHost, - snapshotRegistry: SnapshotObjectRegistry, - ) { - this.id = data.id as Path; - this.configFileName = data.configFileName; - this.currentDirectory = data.currentDirectory; - if (!data.parsedCommandLine?.options) { - throw new Error(`Project '${data.configFileName}' has no parsed command line`); - } - this.parsedCommandLine = data.parsedCommandLine; - this.compilerOptions = this.parsedCommandLine.options; - this.rootFiles = this.parsedCommandLine.fileNames; - this.client = client; - this.snapshotId = snapshotId; - this.program = new Program( - snapshotId, - this, - client, - sourceFileCache, - toPath, - formatDiagnosticsHost, - ); - const objectRegistry = new ProjectObjectRegistry(client, snapshotId, this, snapshotRegistry); - this.checker = new Checker( - snapshotId, - this, - client, - objectRegistry, - ); - this.emitter = new Emitter(client); - this.languageService = new LanguageService(snapshotId, this, client, objectRegistry); - } - - /** @deprecated Use `languageService.getImportAdderEdits`. */ - getImportAdderEdits(file: DocumentIdentifier, actions: readonly APIImportAdderAction[]): Promise { - return this.languageService.getImportAdderEdits(file, actions); - } - - /** @deprecated Use `languageService.getImportEditsForSymbols`. */ - getImportEditsForSymbols(file: DocumentIdentifier, symbols: readonly Symbol[], options: GetImportEditsForSymbolsOptions = {}): Promise { - return this.languageService.getImportEditsForSymbols(file, symbols, options); - } - - dispose(): void { - this.checker.dispose(); - } -} - -export class LanguageService { - private snapshotId: number; - private project: Project; - private client: Client; - private objectRegistry: ProjectObjectRegistry; - - constructor( - snapshotId: number, - project: Project, - client: Client, - objectRegistry: ProjectObjectRegistry, - ) { - this.snapshotId = snapshotId; - this.project = project; - this.client = client; - this.objectRegistry = objectRegistry; - } - - async getImportAdderEdits(file: DocumentIdentifier, actions: readonly APIImportAdderAction[]): Promise { - const requestActions: ImportAdderAction[] = actions.map(action => { - switch (action.kind) { - case "importSymbol": - const importSymbolAction: ImportAdderAction = { - kind: "importSymbol", - symbol: action.symbol.id, - }; - if (action.isValidTypeOnlyUseSite !== undefined) { - importSymbolAction.isValidTypeOnlyUseSite = action.isValidTypeOnlyUseSite; - } - return importSymbolAction; - default: - return assertNever(action.kind); - } - }); - - const data = await this.client.apiRequest("getImportAdderEdits", { - snapshot: this.snapshotId, - project: this.project.id, - file, - actions: requestActions, - }); - return data ?? []; - } - - async getImportEditsForSymbols(file: DocumentIdentifier, symbols: readonly Symbol[], options: GetImportEditsForSymbolsOptions = {}): Promise { - return this.getImportAdderEdits( - file, - symbols.map((symbol): APIImportAdderAction => { - if (options.isValidTypeOnlyUseSite !== undefined) { - return { - kind: "importSymbol", - symbol, - isValidTypeOnlyUseSite: options.isValidTypeOnlyUseSite, - }; - } - return { - kind: "importSymbol", - symbol, - }; - }), - ); - } - - async getReferencedSymbolsForNode(node: Node, position: number): Promise { - const data = await this.client.apiRequest("getReferencedSymbolsForNode", { - snapshot: this.snapshotId, - project: this.project.id, - node: getNodeId(node), - position, - }); - return (data ?? []).map(entry => ({ - definition: new NodeHandle(entry.definition, this.project), - symbol: entry.symbol ? this.objectRegistry.getOrCreateSymbol(entry.symbol) : undefined, - references: (entry.references ?? []).map(h => new NodeHandle(h, this.project)), - })); - } - - async getSignatureUsage(signatureDecl: Node): Promise { - const data = await this.client.apiRequest("getSignatureUsages", { - snapshot: this.snapshotId, - project: this.project.id, - signatureDecl: getNodeId(signatureDecl), - }); - return (data ?? []).map(entry => ({ - name: new NodeHandle(entry.name, this.project), - call: entry.call ? new NodeHandle(entry.call, this.project) : undefined, - })); - } - - async getCompletionsAtPosition(document: string, position: number, options?: CompletionOptions): Promise { - const data = await this.client.apiRequest("getCompletionsAtPosition", { - snapshot: this.snapshotId, - project: this.project.id, - file: document, - position, - ...(options?.triggerCharacter !== undefined ? { triggerCharacter: options.triggerCharacter } : {}), - ...(options?.includeSymbol !== undefined ? { includeSymbol: options.includeSymbol } : {}), - }); - if (!data) return undefined; - return { - isIncomplete: data.isIncomplete, - entries: data.entries.map(e => ({ - ...e, - symbol: e.symbol ? this.objectRegistry.getOrCreateSymbol(e.symbol) : undefined, - })), - }; - } -} - -export class Program implements FormatDiagnosticsHost { - private snapshotId: number; - private project: Project; - private client: Client; - private sourceFileCache: SourceFileCache; - private toPath: (fileName: string) => Path; - private formatDiagnosticsHost: FormatDiagnosticsHost; - private decoder = new Wtf8Decoder(); - private sourceFileMetadataCache = new Map>(); - - constructor( - snapshotId: number, - project: Project, - client: Client, - sourceFileCache: SourceFileCache, - toPath: (fileName: string) => Path, - formatDiagnosticsHost: FormatDiagnosticsHost, - ) { - this.snapshotId = snapshotId; - this.project = project; - this.client = client; - this.sourceFileCache = sourceFileCache; - this.toPath = toPath; - this.formatDiagnosticsHost = formatDiagnosticsHost; - } - - getCurrentDirectory(): string { - return this.project.currentDirectory; - } - - getCanonicalFileName(fileName: string): string { - return this.formatDiagnosticsHost.getCanonicalFileName(fileName); - } - - getNewLine(): string { - return this.project.compilerOptions.newLine === NewLineKind.CRLF ? "\r\n" : "\n"; - } - - getCompilerOptions(): CompilerOptions { - return this.project.compilerOptions; - } - - async getSourceFile(file: DocumentIdentifier): Promise { - const fileName = resolveFileName(file); - const path = this.toPath(fileName); - - // Check if we already have a retained cache entry for this (snapshot, project) pair - const retained = this.sourceFileCache.getRetained(path, this.snapshotId, this.project.id); - if (retained) { - return retained; - } - - // Fetch from server - const binaryData = await this.client.apiRequestBinary("getSourceFile", { - snapshot: this.snapshotId, - project: this.project.id, - file, - }); - if (!binaryData) { - return undefined; - } - - const view = new DataView(binaryData.buffer, binaryData.byteOffset, binaryData.byteLength); - const contentHash = readSourceFileHash(view); - const parseOptionsKey = readParseOptionsKey(view); - - // Create a new RemoteSourceFile and cache it (set returns existing if hash matches) - const sourceFile = new RemoteSourceFile(binaryData, this.decoder, this.client.getTimingCollector()) as unknown as SourceFile; - return this.sourceFileCache.set(path, sourceFile, parseOptionsKey, contentHash, this.snapshotId, this.project.id); - } - - async getSourceFileNames(): Promise { - const data = await this.client.apiRequest("getSourceFileNames", { - snapshot: this.snapshotId, - project: this.project.id, - }); - return data ?? []; - } - - /** - * Returns program-stored metadata for the given source file, or `undefined` if the file - * is not part of the program. Metadata is fetched lazily per file and cached on this - * `Program` instance. - */ - getSourceFileMetadata(fileName: string): Promise { - return this.getSourceFileMetadataByPath(this.toPath(fileName)); - } - - /** - * Returns program-stored metadata for the source file at the given path, or `undefined` - * if the file is not part of the program. Like {@link getSourceFileMetadata}, but skips - * the file name to path conversion. Metadata is fetched lazily per file and cached on - * this `Program` instance. - */ - getSourceFileMetadataByPath(path: Path): Promise { - let metadata = this.sourceFileMetadataCache.get(path); - if (metadata === undefined) { - metadata = this.fetchSourceFileMetadata(path); - this.sourceFileMetadataCache.set(path, metadata); - } - return metadata; - } - - private async fetchSourceFileMetadata(path: Path): Promise { - const data = await this.client.apiRequest("getSourceFileMetadata", { - snapshot: this.snapshotId, - project: this.project.id, - file: path, - }); - return data ?? undefined; - } - - /** - * Returns whether the given source file was loaded as part of an external library - * (e.g. a dependency resolved from `node_modules`). The underlying program metadata is - * fetched lazily per file and cached on this `Program` instance. - */ - async isSourceFileFromExternalLibrary(file: SourceFile): Promise { - const metadata = await this.getSourceFileMetadataByPath(file.path); - return metadata?.isFromExternalLibrary ?? false; - } - - /** - * Returns whether the given source file is a default library file (e.g. `lib.d.ts`). - * The underlying program metadata is fetched lazily per file and cached on this - * `Program` instance. - */ - async isSourceFileDefaultLibrary(file: SourceFile): Promise { - const metadata = await this.getSourceFileMetadataByPath(file.path); - return metadata?.isDefaultLibrary ?? false; - } - - /** - * Get all config source file names associated with this program's project config. - * Includes the root config file and any extended config files. - */ - async getConfigFileNames(): Promise { - const data = await this.client.apiRequest("getConfigFileNames", { - snapshot: this.snapshotId, - project: this.project.id, - }); - return data ?? []; - } - - /** - * Get a config source file by file name/URI. - * This can return the project's root tsconfig file or one of its extended config files. - */ - async getConfigSourceFile(file: DocumentIdentifier): Promise { - const binaryData = await this.client.apiRequestBinary("getConfigSourceFile", { - snapshot: this.snapshotId, - project: this.project.id, - file, - }); - if (!binaryData) { - return undefined; - } - - return new RemoteSourceFile(binaryData, this.decoder) as unknown as SourceFile; - } - - /** - * Get syntactic (parse) diagnostics for specific files or all files. - * @param file - Optional file(s) to get diagnostics for. If omitted, returns diagnostics for all files. - */ - async getSyntacticDiagnostics(file?: DocumentIdentifier | readonly DocumentIdentifier[]): Promise { - const files = file === undefined ? undefined - : Array.isArray(file) ? file - : [file]; - const data = await this.client.apiRequest("getSyntacticDiagnostics", { - snapshot: this.snapshotId, - project: this.project.id, - ...(files !== undefined ? { files } : {}), - }); - return data ?? []; - } - - /** - * Get binder diagnostics for specific files or all files. - * @param file - Optional file(s) to get diagnostics for. If omitted, returns diagnostics for all files. - */ - async getBindDiagnostics(file?: DocumentIdentifier | readonly DocumentIdentifier[]): Promise { - const files = file === undefined ? undefined - : Array.isArray(file) ? file - : [file]; - const data = await this.client.apiRequest("getBindDiagnostics", { - snapshot: this.snapshotId, - project: this.project.id, - ...(files !== undefined ? { files } : {}), - }); - return data ?? []; - } - - /** - * Get semantic (type-check) diagnostics for specific files or all files. - * @param file - Optional file(s) to get diagnostics for. If omitted, returns diagnostics for all files. - */ - async getSemanticDiagnostics(file?: DocumentIdentifier | readonly DocumentIdentifier[]): Promise { - const files = file === undefined ? undefined - : Array.isArray(file) ? file - : [file]; - const data = await this.client.apiRequest("getSemanticDiagnostics", { - snapshot: this.snapshotId, - project: this.project.id, - ...(files !== undefined ? { files } : {}), - }); - return data ?? []; - } - - /** - * Get suggestion diagnostics for specific files or all files. - * @param file - Optional file(s) to get diagnostics for. If omitted, returns diagnostics for all files. - */ - async getSuggestionDiagnostics(file?: DocumentIdentifier | readonly DocumentIdentifier[]): Promise { - const files = file === undefined ? undefined - : Array.isArray(file) ? file - : [file]; - const data = await this.client.apiRequest("getSuggestionDiagnostics", { - snapshot: this.snapshotId, - project: this.project.id, - ...(files !== undefined ? { files } : {}), - }); - return data ?? []; - } - - /** - * Get declaration emit diagnostics for specific files or all files. - * @param file - Optional file(s) to get diagnostics for. If omitted, returns diagnostics for all files. - */ - async getDeclarationDiagnostics(file?: DocumentIdentifier | readonly DocumentIdentifier[]): Promise { - const files = file === undefined ? undefined - : Array.isArray(file) ? file - : [file]; - const data = await this.client.apiRequest("getDeclarationDiagnostics", { - snapshot: this.snapshotId, - project: this.project.id, - ...(files !== undefined ? { files } : {}), - }); - return data ?? []; - } - - /** - * Get program-wide diagnostics for the project, including compiler options diagnostics. - */ - async getProgramDiagnostics(): Promise { - const data = await this.client.apiRequest("getProgramDiagnostics", { - snapshot: this.snapshotId, - project: this.project.id, - }); - return data ?? []; - } - - /** - * Get global (non-file-specific) semantic diagnostics for the project. - */ - async getGlobalDiagnostics(): Promise { - const data = await this.client.apiRequest("getGlobalDiagnostics", { - snapshot: this.snapshotId, - project: this.project.id, - }); - return data ?? []; - } - - /** - * Get config file parsing diagnostics for the project. - */ - async getConfigFileParsingDiagnostics(): Promise { - const data = await this.client.apiRequest("getConfigFileParsingDiagnostics", { - snapshot: this.snapshotId, - project: this.project.id, - }); - return data ?? []; - } - - /** - * Emits files to the configured filesystem. - * - * When the API has a virtual filesystem with a `writeFile` callback, output - * is written there. Otherwise, the server writes directly to the host filesystem. - */ - async emit(emitOnly?: EmitOnly): Promise { - const response = await this.client.apiRequest("emit", { - snapshot: this.snapshotId, - project: this.project.id, - ...(emitOnly !== undefined ? { emitOnly } : {}), - }); - return { - emitSkipped: response.emitSkipped, - diagnostics: response.diagnostics, - emittedFiles: response.emittedFiles, - }; - } - - /** - * Emits files and returns their contents without writing to the filesystem. - */ - async emitToString(emitOnly?: EmitOnly): Promise { - const response = await this.client.apiRequest("emitToString", { - snapshot: this.snapshotId, - project: this.project.id, - ...(emitOnly !== undefined ? { emitOnly } : {}), - }); - return toEmitOutput(response); - } - - /** - * Gets JavaScript output for selected files regardless of project `noEmit`, `emitDeclarationOnly`, and `noEmitOnError` settings. - */ - async getJavaScriptEmit(files: readonly DocumentIdentifier[]): Promise { - const response = await this.client.apiRequest("getJavaScriptEmit", { - snapshot: this.snapshotId, - project: this.project.id, - files, - }); - return toEmitOutput(response); - } - - /** - * Gets declaration output for selected files regardless of project `noEmit`, `declaration`, `emitDeclarationOnly`, and `noEmitOnError` settings. - */ - async getDeclarationEmit(files: readonly DocumentIdentifier[]): Promise { - const response = await this.client.apiRequest("getDeclarationEmit", { - snapshot: this.snapshotId, - project: this.project.id, - files, - }); - return toEmitOutput(response); - } -} - -function toEmitOutput(response: ProtocolEmitOutputResponse): EmitOutput { - const outputFiles = new Map(); - for (const { fileName, ...outputFile } of response.outputFiles) { - outputFiles.set(fileName, outputFile); - } - return { - emitSkipped: response.emitSkipped, - diagnostics: response.diagnostics, - outputFiles, - }; -} - -export class Checker { - private snapshotId: number; - private project: Project; - private client: Client; - private objectRegistry: ProjectObjectRegistry; - private wellKnownSymbols: Promise<{ unknown: number; undefined: number; arguments: number; }> | undefined; - private wellKnownSignatures: Promise<{ unknown: number; }> | undefined; - - constructor( - snapshotId: number, - project: Project, - client: Client, - objectRegistry: ProjectObjectRegistry, - ) { - this.snapshotId = snapshotId; - this.project = project; - this.client = client; - this.objectRegistry = objectRegistry; - } - - dispose(): void { - this.objectRegistry.clear(); - } - - getSymbolAtLocation(node: Node): Promise; - getSymbolAtLocation(nodes: readonly Node[]): Promise<(Symbol | undefined)[]>; - async getSymbolAtLocation(nodeOrNodes: Node | readonly Node[]): Promise { - if (Array.isArray(nodeOrNodes)) { - const data = await this.client.apiRequest("getSymbolsAtLocations", { - snapshot: this.snapshotId, - project: this.project.id, - locations: nodeOrNodes.map(node => getNodeId(node)), - }); - return data.map(d => d ? this.objectRegistry.getOrCreateSymbol(d) : undefined); - } - const data = await this.client.apiRequest("getSymbolAtLocation", { - snapshot: this.snapshotId, - project: this.project.id, - location: getNodeId(nodeOrNodes as Node), - }); - return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined; - } - - getSymbolAtPosition(file: DocumentIdentifier, position: number): Promise; - getSymbolAtPosition(file: DocumentIdentifier, positions: readonly number[]): Promise<(Symbol | undefined)[]>; - async getSymbolAtPosition(file: DocumentIdentifier, positionOrPositions: number | readonly number[]): Promise { - if (typeof positionOrPositions === "number") { - const data = await this.client.apiRequest("getSymbolAtPosition", { - snapshot: this.snapshotId, - project: this.project.id, - file, - position: positionOrPositions, - }); - return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined; - } - const data = await this.client.apiRequest("getSymbolsAtPositions", { - snapshot: this.snapshotId, - project: this.project.id, - file, - positions: positionOrPositions, - }); - return data.map(d => d ? this.objectRegistry.getOrCreateSymbol(d) : undefined); - } - - getSymbolOfSourceFile(file: DocumentIdentifier): Promise; - getSymbolOfSourceFile(files: readonly DocumentIdentifier[]): Promise<(Symbol | undefined)[]>; - async getSymbolOfSourceFile(fileOrFiles: DocumentIdentifier | readonly DocumentIdentifier[]): Promise { - if (Array.isArray(fileOrFiles)) { - const data = await this.client.apiRequest("getSymbolsOfSourceFiles", { - snapshot: this.snapshotId, - project: this.project.id, - files: fileOrFiles, - }); - return data.map(d => d ? this.objectRegistry.getOrCreateSymbol(d) : undefined); - } - const data = await this.client.apiRequest("getSymbolOfSourceFile", { - snapshot: this.snapshotId, - project: this.project.id, - file: fileOrFiles as DocumentIdentifier, - }); - return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined; - } - - /** - * Get the type of a symbol. Always returns a type; for symbols whose type - * cannot be determined the checker yields the error type (use - * {@link Type.isErrorType} to detect it). - */ - getTypeOfSymbol(symbol: Symbol): Promise; - getTypeOfSymbol(symbols: readonly Symbol[]): Promise; - async getTypeOfSymbol(symbolOrSymbols: Symbol | readonly Symbol[]): Promise { - if (Array.isArray(symbolOrSymbols)) { - const data = await this.client.apiRequest("getTypesOfSymbols", { - snapshot: this.snapshotId, - project: this.project.id, - symbols: symbolOrSymbols.map(s => s.id), - }); - return data.map(d => this.objectRegistry.getOrCreateType(d)); - } - const data = await this.client.apiRequest("getTypeOfSymbol", { - snapshot: this.snapshotId, - project: this.project.id, - symbol: (symbolOrSymbols as Symbol).id, - }); - return this.objectRegistry.getOrCreateType(data); - } - - /** - * Get the declared type of a symbol. Always returns a type; for symbols whose - * declared type cannot be determined the checker yields the error type (use - * {@link Type.isErrorType} to detect it). - */ - async getDeclaredTypeOfSymbol(symbol: Symbol): Promise { - const data = await this.client.apiRequest("getDeclaredTypeOfSymbol", { - snapshot: this.snapshotId, - project: this.project.id, - symbol: symbol.id, - }); - return this.objectRegistry.getOrCreateType(data); - } - - /** - * Get the type of a symbol, excluding the missing type when - * `exactOptionalPropertyTypes: true` is set; for symbols whose - * type cannot be determined the checker yields the error type - * (use {@link Type.isErrorType} to detect it). - */ - async getNonMissingTypeOfSymbol(symbol: Symbol): Promise { - const data = await this.client.apiRequest("getNonMissingTypeOfSymbol", { - snapshot: this.snapshotId, - project: this.project.id, - symbol: symbol.id, - }); - return this.objectRegistry.getOrCreateType(data); - } - - async getReferencesToSymbolInFile(file: DocumentIdentifier, symbol: Symbol): Promise { - const data = await this.client.apiRequest("getReferencesToSymbolInFile", { - snapshot: this.snapshotId, - project: this.project.id, - file, - symbol: symbol.id, - }); - return (data ?? []).map(h => new NodeHandle(h, this.project)); - } - - /** @deprecated Use `project.languageService.getReferencedSymbolsForNode`. */ - getReferencedSymbolsForNode(node: Node, position: number): Promise { - return this.project.languageService.getReferencedSymbolsForNode(node, position); - } - - /** @deprecated Use `project.languageService.getSignatureUsage`. */ - getSignatureUsage(signatureDecl: Node): Promise { - return this.project.languageService.getSignatureUsage(signatureDecl); - } - - /** @deprecated Use `project.languageService.getCompletionsAtPosition`. */ - getCompletionsAtPosition(document: string, position: number, options?: CompletionOptions): Promise { - return this.project.languageService.getCompletionsAtPosition(document, position, options); - } - - /** - * Get the type at a node location. Always returns a type; for nodes whose - * type cannot be determined the checker yields the error type (use - * {@link Type.isErrorType} to detect it). - */ - getTypeAtLocation(node: Node): Promise; - getTypeAtLocation(nodes: readonly Node[]): Promise; - async getTypeAtLocation(nodeOrNodes: Node | readonly Node[]): Promise { - if (Array.isArray(nodeOrNodes)) { - const data = await this.client.apiRequest("getTypeAtLocations", { - snapshot: this.snapshotId, - project: this.project.id, - locations: nodeOrNodes.map(node => getNodeId(node)), - }); - return data.map(d => this.objectRegistry.getOrCreateType(d)); - } - const data = await this.client.apiRequest("getTypeAtLocation", { - snapshot: this.snapshotId, - project: this.project.id, - location: getNodeId(nodeOrNodes as Node), - }); - return this.objectRegistry.getOrCreateType(data); - } - - async getSignaturesOfType(type: Type, kind: SignatureKind): Promise { - return kind === SignatureKind.Call ? type.getCallSignatures() : type.getConstructSignatures(); - } - - /** - * Get the resolved signature of a call-like expression. Always returns a - * signature; when a call cannot be resolved the checker yields the unknown - * signature (use {@link Checker.isUnknownSignature} to detect it). - */ - async getResolvedSignature(node: Node): Promise { - const data = await this.client.apiRequest("getResolvedSignature", { - snapshot: this.snapshotId, - project: this.project.id, - location: getNodeId(node), - }); - return this.objectRegistry.getOrCreateSignature(data); - } - - getTypeAtPosition(file: DocumentIdentifier, position: number): Promise; - getTypeAtPosition(file: DocumentIdentifier, positions: readonly number[]): Promise<(Type | undefined)[]>; - async getTypeAtPosition(file: DocumentIdentifier, positionOrPositions: number | readonly number[]): Promise { - if (typeof positionOrPositions === "number") { - const data = await this.client.apiRequest("getTypeAtPosition", { - snapshot: this.snapshotId, - project: this.project.id, - file, - position: positionOrPositions, - }); - return data ? this.objectRegistry.getOrCreateType(data) : undefined; - } - const data = await this.client.apiRequest("getTypesAtPositions", { - snapshot: this.snapshotId, - project: this.project.id, - file, - positions: positionOrPositions, - }); - return data.map(d => d ? this.objectRegistry.getOrCreateType(d) : undefined); - } - - async resolveName( - name: string, - meaning: SymbolFlags, - location?: Node | DocumentPosition, - excludeGlobals?: boolean, - ): Promise { - // Distinguish Node (has `kind`) from DocumentPosition (has `document` and `position`) - const isNode = location && "kind" in location; - const data = await this.client.apiRequest("resolveName", { - snapshot: this.snapshotId, - project: this.project.id, - name, - meaning, - ...(isNode ? { location: getNodeId(location as Node) } : {}), - ...(!isNode && location - ? { - file: (location as DocumentPosition).document, - position: (location as DocumentPosition).position, - } - : {}), - ...(excludeGlobals !== undefined ? { excludeGlobals } : {}), - }); - return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined; - } - - /** - * Returns all symbols with the given meaning that are visible at `location`. - */ - async getSymbolsInScope(location: Node | DocumentPosition, meaning: SymbolFlags): Promise { - // Distinguish Node (has `kind`) from DocumentPosition (has `document` and `position`) - const isNode = "kind" in location; - const data = await this.client.apiRequest("getSymbolsInScope", { - snapshot: this.snapshotId, - project: this.project.id, - meaning, - ...(isNode - ? { location: getNodeId(location as Node) } - : { - file: (location as DocumentPosition).document, - position: (location as DocumentPosition).position, - }), - }); - return data ? data.map(d => this.objectRegistry.getOrCreateSymbol(d)) : []; - } - - async getResolvedSymbol(node: Identifier): Promise { - const text = node.text; - if (!text) return undefined; - return this.resolveName(text, SymbolFlags.Value | SymbolFlags.ExportValue, node); - } - - async getContextualType(node: Expression): Promise { - const data = await this.client.apiRequest("getContextualType", { - snapshot: this.snapshotId, - project: this.project.id, - location: getNodeId(node), - }); - return data ? this.objectRegistry.getOrCreateType(data) : undefined; - } - - /** Get the base type of a literal type (e.g. `number` for `42`). Always returns a type. */ - async getBaseTypeOfLiteralType(type: Type): Promise { - const data = await this.client.apiRequest("getBaseTypeOfLiteralType", { - snapshot: this.snapshotId, - project: this.project.id, - type: type.id, - }); - return this.objectRegistry.getOrCreateType(data); - } - - /** Get the type with `null` and `undefined` removed. Always returns a type. */ - async getNonNullableType(type: Type): Promise { - return type.getNonNullableType(); - } - - /** - * Get the type for a type node. Always returns a type; for type nodes whose - * type cannot be determined the checker yields the error type (use - * {@link Type.isErrorType} to detect it). - */ - async getTypeFromTypeNode(node: TypeNode): Promise { - const data = await this.client.apiRequest("getTypeFromTypeNode", { - snapshot: this.snapshotId, - project: this.project.id, - location: getNodeId(node), - }); - return this.objectRegistry.getOrCreateType(data); - } - - /** Get the widened type. Always returns a type. */ - async getWidenedType(type: Type): Promise { - const data = await this.client.apiRequest("getWidenedType", { - snapshot: this.snapshotId, - project: this.project.id, - type: type.id, - }); - return this.objectRegistry.getOrCreateType(data); - } - - /** - * Get the type of the parameter at the given index in a signature. Always - * returns a type; an out-of-range index yields the `any` type. - */ - async getParameterType(signature: Signature, index: number): Promise { - const data = await this.client.apiRequest("getParameterType", { - snapshot: this.snapshotId, - project: this.project.id, - signature: signature.id, - index, - }); - return this.objectRegistry.getOrCreateType(data); - } - - async isArrayLikeType(type: Type): Promise { - return this.client.apiRequest("isArrayLikeType", { - snapshot: this.snapshotId, - project: this.project.id, - type: type.id, - }); - } - - async isTypeAssignableTo(source: Type, target: Type): Promise { - return this.client.apiRequest("isTypeAssignableTo", { - snapshot: this.snapshotId, - project: this.project.id, - source: source.id, - target: target.id, - }); - } - - async getShorthandAssignmentValueSymbol(node: Node): Promise { - const data = await this.client.apiRequest("getShorthandAssignmentValueSymbol", { - snapshot: this.snapshotId, - project: this.project.id, - location: getNodeId(node), - }); - return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined; - } - - /** - * Get the type of a symbol as narrowed at a specific location. Always returns - * a type; for symbols whose type cannot be determined the checker yields the - * error type (use {@link Type.isErrorType} to detect it). - */ - async getTypeOfSymbolAtLocation(symbol: Symbol, location: Node): Promise { - const data = await this.client.apiRequest("getTypeOfSymbolAtLocation", { - snapshot: this.snapshotId, - project: this.project.id, - symbol: symbol.id, - location: getNodeId(location), - }); - return this.objectRegistry.getOrCreateType(data); - } - - private async getIntrinsicType(method: IntrinsicTypeMethod): Promise { - const data = await this.client.apiRequest(method, { - snapshot: this.snapshotId, - project: this.project.id, - }); - return this.objectRegistry.getOrCreateType(data); - } - - async getAnyType(): Promise { - return this.getIntrinsicType("getAnyType"); - } - async getStringType(): Promise { - return this.getIntrinsicType("getStringType"); - } - async getNumberType(): Promise { - return this.getIntrinsicType("getNumberType"); - } - async getBooleanType(): Promise { - return this.getIntrinsicType("getBooleanType"); - } - async getVoidType(): Promise { - return this.getIntrinsicType("getVoidType"); - } - async getUndefinedType(): Promise { - return this.getIntrinsicType("getUndefinedType"); - } - async getNullType(): Promise { - return this.getIntrinsicType("getNullType"); - } - async getNeverType(): Promise { - return this.getIntrinsicType("getNeverType"); - } - async getUnknownType(): Promise { - return this.getIntrinsicType("getUnknownType"); - } - async getBigIntType(): Promise { - return this.getIntrinsicType("getBigIntType"); - } - async getESSymbolType(): Promise { - return this.getIntrinsicType("getESSymbolType"); - } - async getNonPrimitiveType(): Promise { - return this.getIntrinsicType("getNonPrimitiveType"); - } - - async typeToTypeNode(type: Type, enclosingDeclaration?: Node, flags?: number): Promise { - const binaryData = await this.client.apiRequestBinary("typeToTypeNode", { - snapshot: this.snapshotId, - project: this.project.id, - type: type.id, - ...(enclosingDeclaration ? { location: getNodeId(enclosingDeclaration) } : {}), - ...(flags !== undefined ? { flags } : {}), - }); - if (!binaryData) return undefined; - return decodeNode(binaryData) as TypeNode; - } - - async signatureToSignatureDeclaration(signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): Promise { - const binaryData = await this.client.apiRequestBinary("signatureToSignatureDeclaration", { - snapshot: this.snapshotId, - project: this.project.id, - signature: signature.id, - kind, - ...(enclosingDeclaration ? { location: getNodeId(enclosingDeclaration) } : {}), - ...(flags !== undefined ? { flags } : {}), - }); - if (!binaryData) return undefined; - return decodeNode(binaryData) as Node; - } - - async typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): Promise { - const result = await this.client.apiRequest("typeToString", { - snapshot: this.snapshotId, - project: this.project.id, - type: type.id, - ...(enclosingDeclaration ? { location: getNodeId(enclosingDeclaration) } : {}), - ...(flags !== undefined ? { flags } : {}), - }); - if (typeof result !== "string") throw new TypeError("typeToString returned a non-string result"); - return result; - } - - async isContextSensitive(node: Node): Promise { - return this.client.apiRequest("isContextSensitive", { - snapshot: this.snapshotId, - project: this.project.id, - location: getNodeId(node), - }); - } - - async isArrayType(type: Type): Promise { - return this.client.apiRequest("isArrayType", { - snapshot: this.snapshotId, - project: this.project.id, - type: type.id, - }); - } - - async isTupleType(type: Type): Promise { - return this.client.apiRequest("isTupleType", { - snapshot: this.snapshotId, - project: this.project.id, - type: type.id, - }); - } - - /** - * The following symbols are considered read-only: - * - Properties with a `readonly` modifier - * - Variables declared with `const` - * - Get accessors without matching set accessors - * - Enum members - * - `Object.defineProperty` assignments with `writable: false` or no setter - * - Unions and intersections of the above - */ - async isReadonlySymbol(symbol: Symbol): Promise { - return this.client.apiRequest("isReadonlySymbol", { - snapshot: this.snapshotId, - project: this.project.id, - symbol: symbol.id, - }); - } - - /** Get the return type of a signature. Always returns a type. */ - async getReturnTypeOfSignature(signature: Signature): Promise { - return signature.getReturnType(); - } - - /** - * Get the rest type of a signature. Always returns a type; a signature with - * no rest parameter yields the `any` type. - */ - async getRestTypeOfSignature(signature: Signature): Promise { - const data = await this.client.apiRequest("getRestTypeOfSignature", { - snapshot: this.snapshotId, - project: this.project.id, - signature: signature.id, - }); - return this.objectRegistry.getOrCreateType(data); - } - - async getTypePredicateOfSignature(signature: Signature): Promise { - const data = await this.client.apiRequest("getTypePredicateOfSignature", { - snapshot: this.snapshotId, - project: this.project.id, - signature: signature.id, - }); - if (!data) return undefined; - return { - kind: data.kind, - parameterIndex: data.parameterIndex, - parameterName: data.parameterName, - type: data.type ? this.objectRegistry.getOrCreateType(data.type) : undefined, - } as TypePredicate; - } - - /** - * Get the base types of a class or interface type. A type with no base types - * yields an empty array. - */ - async getBaseTypes(type: InterfaceType): Promise { - return await type.getBaseTypes() ?? []; - } - - /** Get the apparent type of a type. Always returns a type. */ - async getApparentType(type: Type): Promise { - return type.getApparentType(); - } - - /** Get the reduced type of a type. Always returns a type. */ - async getReducedType(type: Type): Promise { - return type.getReducedType(); - } - - async getPropertiesOfType(type: Type): Promise { - return type.getProperties(); - } - - async getIndexInfosOfType(type: Type): Promise { - return type.getIndexInfos(); - } - - /** - * Get the constraint of a type parameter (the `T` in ``), or - * undefined if it has none. - */ - async getConstraintOfTypeParameter(type: TypeParameter): Promise { - return type.getConstraint(); - } - - async getDefaultFromTypeParameter(type: TypeParameter): Promise { - return type.getDefault(); - } - - async getBaseConstraintOfType(type: Type): Promise { - const data = await this.client.apiRequest("getBaseConstraintOfType", { - snapshot: this.snapshotId, - project: this.project.id, - type: type.id, - }); - return data ? this.objectRegistry.getOrCreateType(data) : undefined; - } - - async getPropertyOfType(type: Type, name: string): Promise { - const data = await this.client.apiRequest("getPropertyOfType", { - snapshot: this.snapshotId, - project: this.project.id, - type: type.id, - name, - }); - return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined; - } - - async getConstantValue(node: Node): Promise { - const data = await this.client.apiRequest("getConstantValue", { - snapshot: this.snapshotId, - project: this.project.id, - location: getNodeId(node), - }); - return typeof data === "string" || typeof data === "number" ? data : undefined; - } - - /** Get the signature of a function-like declaration. Always returns a signature. */ - async getSignatureFromDeclaration(node: Node): Promise { - const data = await this.client.apiRequest("getSignatureFromDeclaration", { - snapshot: this.snapshotId, - project: this.project.id, - location: getNodeId(node), - }); - return this.objectRegistry.getOrCreateSignature(data); - } - - async getExportSpecifierLocalTargetSymbol(node: Node): Promise { - const data = await this.client.apiRequest("getExportSpecifierLocalTargetSymbol", { - snapshot: this.snapshotId, - project: this.project.id, - location: getNodeId(node), - }); - return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined; - } - - /** - * Follow all aliases to get the original symbol. Always returns a symbol; for - * an unresolved alias the checker yields the unknown symbol (use - * {@link Checker.isUnknownSymbol} to detect it). - */ - async getAliasedSymbol(symbol: Symbol): Promise { - const data = await this.client.apiRequest("getAliasedSymbol", { - snapshot: this.snapshotId, - project: this.project.id, - symbol: symbol.id, - }); - return this.objectRegistry.getOrCreateSymbol(data); - } - - /** - * Get the fully qualified name of a symbol, walking up its parent chain - * (e.g. `"/path/to/module".Namespace.Name`). - */ - async getFullyQualifiedName(symbol: Symbol): Promise { - return this.client.apiRequest("getFullyQualifiedName", { - snapshot: this.snapshotId, - project: this.project.id, - symbol: symbol.id, - }); - } - - async getImmediateAliasedSymbol(symbol: Symbol): Promise { - const data = await this.client.apiRequest("getImmediateAliasedSymbol", { - snapshot: this.snapshotId, - project: this.project.id, - symbol: symbol.id, - }); - return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined; - } - - /** - * Get the target symbol if instantiated, or the provided symbol otherwise. - */ - async getTargetSymbol(symbol: Symbol): Promise { - if (symbol.checkFlags & CheckFlags.Instantiated) { - const data = await this.client.apiRequest("getTargetSymbol", { - snapshot: this.snapshotId, - project: this.project.id, - symbol: symbol.id, - }); - return this.objectRegistry.getOrCreateSymbol(data); - } - return symbol; - } - - /** - * Fetch (once, then cache) the handle ids of the per-checker singleton - * symbols (unknown, undefined, arguments). These ids are stable for the life - * of the project's checker, so identity checks against them are local after - * the first call. - */ - private getWellKnownSymbols(): Promise<{ unknown: number; undefined: number; arguments: number; }> { - return this.wellKnownSymbols ??= this.client.apiRequest("getWellKnownSymbols", { - snapshot: this.snapshotId, - project: this.project.id, - }); - } - - /** - * Returns `true` if the symbol is the checker's "unknown" symbol (e.g. the - * result of {@link Checker.getAliasedSymbol} on an unresolved alias). - */ - async isUnknownSymbol(symbol: Symbol): Promise { - return symbol.id === (await this.getWellKnownSymbols()).unknown; - } - - /** - * Returns `true` if the symbol is the checker's "undefined" symbol. - */ - async isUndefinedSymbol(symbol: Symbol): Promise { - return symbol.id === (await this.getWellKnownSymbols()).undefined; - } - - /** - * Returns `true` if the symbol is the checker's "arguments" symbol. - */ - async isArgumentsSymbol(symbol: Symbol): Promise { - return symbol.id === (await this.getWellKnownSymbols()).arguments; - } - - /** - * Fetch (once, then cache) the handle id of the per-checker unknown - * signature. This id is stable for the life of the project's checker, so - * identity checks against it are local after the first call. - */ - private getWellKnownSignatures(): Promise<{ unknown: number; }> { - return this.wellKnownSignatures ??= this.client.apiRequest("getWellKnownSignatures", { - snapshot: this.snapshotId, - project: this.project.id, - }); - } - - /** - * Returns `true` if the signature is the checker's "unknown" signature (e.g. - * the result of {@link Checker.getResolvedSignature} on a call that cannot be - * resolved). - */ - async isUnknownSignature(signature: Signature): Promise { - return signature.id === (await this.getWellKnownSignatures()).unknown; - } - - async getExportsOfModule(symbol: Symbol): Promise { - const data = await this.client.apiRequest("getExportsOfModule", { - snapshot: this.snapshotId, - project: this.project.id, - symbol: symbol.id, - }); - return data ? data.map(d => this.objectRegistry.getOrCreateSymbol(d)) : []; - } - - async getMemberInModuleExports(symbol: Symbol, name: string): Promise { - const data = await this.client.apiRequest("getMemberInModuleExports", { - snapshot: this.snapshotId, - project: this.project.id, - symbol: symbol.id, - name, - }); - return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined; - } - - async getJsDocTagsOfSymbol(symbol: Symbol): Promise { - const data = await this.client.apiRequest("getJsDocTags", { - snapshot: this.snapshotId, - project: this.project.id, - symbol: symbol.id, - }); - return data ?? []; - } - - async getDocumentationCommentOfSymbol(symbol: Symbol): Promise { - return this.client.apiRequest("getDocumentationComment", { - snapshot: this.snapshotId, - project: this.project.id, - symbol: symbol.id, - }); - } - - /** - * Get the type arguments of a type reference (e.g. the `string` in `Array`). - */ - async getTypeArguments(type: TypeReference): Promise { - const data = await this.client.apiRequest("getTypeArguments", { - snapshot: this.snapshotId, - project: this.project.id, - type: type.id, - }); - return data ? data.map(d => this.objectRegistry.getOrCreateType(d)) : []; - } -} - -export interface PrintNodeOptions { - preserveSourceNewlines?: boolean | undefined; - neverAsciiEscape?: boolean | undefined; - terminateUnterminatedLiterals?: boolean | undefined; -} - -export class Emitter { - private client: Client; - - constructor(client: Client) { - this.client = client; - } - - async printNode(node: Node, options: PrintNodeOptions = {}): Promise { - const encoded = encodeNode(node); - const base64 = uint8ArrayToBase64(encoded); - return this.client.apiRequest("printNode", { - data: base64, - ...(options.preserveSourceNewlines !== undefined ? { preserveSourceNewlines: options.preserveSourceNewlines } : {}), - ...(options.neverAsciiEscape !== undefined ? { neverAsciiEscape: options.neverAsciiEscape } : {}), - ...(options.terminateUnterminatedLiterals !== undefined ? { terminateUnterminatedLiterals: options.terminateUnterminatedLiterals } : {}), - }); - } -} - -export class SnapshotInternalAPI { - private snapshotId: number; - private client: Client; - - constructor(snapshotId: number, client: Client) { - this.snapshotId = snapshotId; - this.client = client; - } - - /** - * Format a synthesized node with the correct indentation for insertion at a - * specific position in an existing source file. - * - * @param node The synthesized AST node to format. - * @param file The target file where the node will be inserted. - * @param position The UTF-16 code-unit offset in the target file for insertion. - * @returns The formatted text of the node, indented for the insertion position. - */ - async formatNodeForInsertion(node: Node, file: DocumentIdentifier, position: number): Promise { - const data = await this.client.apiRequest("getDefaultProjectForFile", { - snapshot: this.snapshotId, - file, - }); - if (!data) { - throw new Error(`No project found for file: ${typeof file === "string" ? file : file.uri}`); - } - - const encoded = encodeNode(node); - const base64 = uint8ArrayToBase64(encoded); - return this.client.apiRequest("formatNodeForInsertion", { - snapshot: this.snapshotId, - project: data.id, - file, - position, - data: base64, - }); - } -} - -export class NodeHandle { - /** - * The project this handle was produced in, used as the default for {@link resolve}. - * Node handles are only meaningful within a project's program, so the producing project - * is remembered so callers don't have to pass it explicitly. - */ - private readonly canonicalProject: Project; - readonly index: number; - readonly kind: SyntaxKind; - readonly path: Path; - - constructor(handle: string, canonicalProject: Project) { - const parsed = parseNodeHandle(handle); - this.index = parsed.index; - this.kind = parsed.kind; - this.path = parsed.path; - this.canonicalProject = canonicalProject; - } - - /** - * Resolve this handle to the actual AST node by fetching the source file from a project - * and looking up the node by index. If no project is passed, the project that produced - * the handle is used. - */ - async resolve(project: Project = this.canonicalProject): Promise { - const sourceFile = await project.program.getSourceFile(this.path); - if (!sourceFile) { - return undefined; - } - return (sourceFile as unknown as RemoteSourceFile).getOrCreateNodeAtIndex(this.index) as T | undefined; - } -} - -/** A symbol definition paired with all of its reference nodes. */ -export interface ReferencedSymbolEntry { - /** The node handle for the symbol's definition. */ - definition: NodeHandle; - /** The resolved symbol for the definition, if available. */ - symbol?: Symbol | undefined; - /** The node handles for each reference to the symbol. */ - references: NodeHandle[]; -} - -/** A single usage of a signature, pairing the reference name with its call expression (if any). */ -export interface SignatureUsage { - /** The node handle for the name reference. */ - name: NodeHandle; - /** The node handle for the call expression, if the reference is invoked. */ - call?: NodeHandle | undefined; -} - -export class Symbol { - private objectRegistry: SnapshotObjectRegistry; - /** - * The project this symbol was first observed in, used as the default project for - * lookups that need a project context (members/exports/parent). Symbols are shared - * snapshot-wide, so these lookups can otherwise be ambiguous about which project to use. - */ - private readonly canonicalProject: Project; - - readonly id: number; - /** The escaped (`__String`) name, used as the key in member/export tables. */ - readonly escapedName: __String; - /** The display name (escaped underscores removed). */ - readonly name: string; - readonly flags: SymbolFlags; - readonly checkFlags: CheckFlags; - readonly declarations: readonly NodeHandle[]; - readonly valueDeclaration: NodeHandle | undefined; - private readonly parent!: number; - private readonly exportSymbol!: number; - private membersCache: Promise> | undefined; - private exportsCache: Promise> | undefined; - - constructor(data: SymbolResponse, objectRegistry: SnapshotObjectRegistry) { - this.objectRegistry = objectRegistry; - - this.id = data.id; - this.escapedName = data.name as __String; - this.name = unescapeLeadingUnderscores(data.name as __String); - this.flags = data.flags; - this.checkFlags = data.checkFlags; - const canonicalProject = objectRegistry.getProject(data.project as Path); - if (!canonicalProject) { - throw new Error(`Symbol ${data.id} references unknown canonical project '${data.project}'`); - } - this.canonicalProject = canonicalProject; - this.declarations = (data.declarations ?? []).map(d => new NodeHandle(d, canonicalProject)); - this.valueDeclaration = data.valueDeclaration ? new NodeHandle(data.valueDeclaration, canonicalProject) : undefined; - - if (data.parent !== undefined) this.parent = data.parent; - if (data.exportSymbol !== undefined) this.exportSymbol = data.exportSymbol; - } - - async getParent(): Promise { - return this.objectRegistry.fetchSymbol(this, "getParentOfSymbol", this.parent, this.canonicalProject.id); - } - - /** - * Get this symbol's members keyed by escaped name. The result is cached on - * the symbol, so repeated calls do not round-trip to the server. - */ - getMembers(): Promise> { - return this.membersCache ??= this.fetchSymbolTable("getMembersOfSymbol"); - } - - /** - * Get this symbol's exports keyed by escaped name. The result is cached on - * the symbol, so repeated calls do not round-trip to the server. - */ - getExports(): Promise> { - return this.exportsCache ??= this.fetchSymbolTable("getExportsOfSymbol"); - } - - private async fetchSymbolTable(method: SymbolsPropertyMethod): Promise> { - const symbols = await this.objectRegistry.fetchSymbols(this, method, undefined, this.canonicalProject.id); - const table = new Map<__String, Symbol>(); - for (const symbol of symbols) { - table.set(symbol.escapedName, symbol); - } - return table; - } - - async getExportSymbol(): Promise { - if (!this.exportSymbol) return this; - return this.objectRegistry.fetchSymbol(this, "getExportSymbolOfSymbol", this.exportSymbol, this.canonicalProject.id); - } - - async getJsDocTags(checker: Checker): Promise { - return checker.getJsDocTagsOfSymbol(this); - } - - async getDocumentationComment(checker: Checker): Promise { - return checker.getDocumentationCommentOfSymbol(this); - } -} - -class TypeObject implements Type { - private objectRegistry: ProjectObjectRegistry; - - // Fields included in TypeResponse. References to other objects are stored as IDs - // and resolved lazily via the object registry. - readonly id: number; - readonly flags: TypeFlags; - readonly objectFlags!: ObjectFlags; - readonly symbol!: number; - readonly value!: string | number | boolean | bigint; - readonly intrinsicName!: string; - readonly isThisType!: boolean; - readonly freshType!: number; - readonly regularType!: number; - readonly target!: number; - readonly typeParameters!: readonly number[]; - readonly outerTypeParameters!: readonly number[]; - readonly localTypeParameters!: readonly number[]; - readonly aliasTypeArguments!: readonly number[]; - readonly aliasSymbol!: number; - readonly elementFlags!: readonly ElementFlags[]; - readonly fixedLength!: number; - readonly readonly!: boolean; - readonly texts!: readonly string[]; - readonly objectType!: number; - readonly indexType!: number; - readonly checkType!: number; - readonly extendsType!: number; - readonly baseType!: number; - readonly substConstraint!: number; - - // Cached results of lazy fetches, not included in TypeResponse - // (typically because they require some amount of computation or - // could cause an arbitrarily large number of types to be cached - // on the server for ID-based lookup). `false` is a sentinel value - // indicating a fetch has not yet occurred. - private trueType: number | false; - private falseType: number | false; - private constraint: number | false; - private default: number | false; - private nonNullableType: number | false; - private apparentType: number | false; - private reducedType: number | false; - private properties: readonly Symbol[] | false; - private apparentProperties: readonly Symbol[] | false; - private callSignatures: readonly Signature[] | false; - private constructSignatures: readonly Signature[] | false; - private indexInfos: readonly IndexInfo[] | false; - private baseTypes: readonly Type[] | false; - private stringIndexType: Type | undefined | false; - private numberIndexType: Type | undefined | false; - - constructor(data: TypeResponse, objectRegistry: ProjectObjectRegistry) { - this.objectRegistry = objectRegistry; - - this.id = data.id; - this.flags = data.flags; - if (data.objectFlags !== undefined) this.objectFlags = data.objectFlags; - if (data.symbol !== undefined) this.symbol = data.symbol; - if (data.value != null) { - // BigInt literal values are serialized as decimal strings (e.g. "-123") because - // JSON cannot represent bigint. Decode them back into a real bigint here. - const value = data.value as string | number | boolean; - this.value = (data.flags & TypeFlags.BigIntLiteral) ? BigInt(value as string) : value; - } - if (data.intrinsicName !== undefined) this.intrinsicName = data.intrinsicName; - if (data.isThisType !== undefined) this.isThisType = data.isThisType; - if (data.freshType !== undefined) this.freshType = data.freshType; - if (data.regularType !== undefined) this.regularType = data.regularType; - if (data.target !== undefined) this.target = data.target; - this.typeParameters = data.typeParameters ?? []; - this.outerTypeParameters = data.outerTypeParameters ?? []; - this.localTypeParameters = data.localTypeParameters ?? []; - this.aliasTypeArguments = data.aliasTypeArguments ?? []; - if (data.aliasSymbol !== undefined) this.aliasSymbol = data.aliasSymbol; - if (data.elementFlags !== undefined) this.elementFlags = data.elementFlags; - if (data.fixedLength !== undefined) this.fixedLength = data.fixedLength; - if (data.readonly !== undefined) this.readonly = data.readonly; - if (data.texts !== undefined) this.texts = data.texts; - if (data.objectType !== undefined) this.objectType = data.objectType; - if (data.indexType !== undefined) this.indexType = data.indexType; - if (data.checkType !== undefined) this.checkType = data.checkType; - if (data.extendsType !== undefined) this.extendsType = data.extendsType; - if (data.baseType !== undefined) this.baseType = data.baseType; - if (data.substConstraint !== undefined) this.substConstraint = data.substConstraint; - - this.trueType = false; - this.falseType = false; - this.constraint = false; - this.default = false; - this.nonNullableType = false; - this.apparentType = false; - this.reducedType = false; - this.properties = false; - this.apparentProperties = false; - this.callSignatures = false; - this.constructSignatures = false; - this.indexInfos = false; - this.baseTypes = false; - this.stringIndexType = false; - this.numberIndexType = false; - } - - async getSymbol(): Promise { - return this.objectRegistry.fetchSymbol(this, "getSymbolOfType", this.symbol); - } - - async getProperties(): Promise { - if (this.properties === false) { - this.properties = await this.objectRegistry.fetchPropertiesOfType(this); - } - return this.properties; - } - - getProperty(propertyName: string): Promise { - return this.objectRegistry.fetchPropertyOfType(this, propertyName); - } - - async getApparentProperties(): Promise { - if (this.apparentProperties === false) { - this.apparentProperties = await this.objectRegistry.fetchApparentPropertiesOfType(this); - } - return this.apparentProperties; - } - - async getCallSignatures(): Promise { - if (this.callSignatures === false) { - this.callSignatures = await this.objectRegistry.fetchSignaturesOfType(this, SignatureKind.Call); - } - return this.callSignatures; - } - - async getConstructSignatures(): Promise { - if (this.constructSignatures === false) { - this.constructSignatures = await this.objectRegistry.fetchSignaturesOfType(this, SignatureKind.Construct); - } - return this.constructSignatures; - } - - async getNonNullableType(): Promise { - const result = await this.objectRegistry.fetchType(this, "getNonNullableType", this.nonNullableType); - this.nonNullableType = result.id; - return result; - } - - async getStringIndexType(): Promise { - if (this.stringIndexType === false) { - this.stringIndexType = await this.getStringIndexTypeWorker(); - } - return this.stringIndexType; - } - - private async getStringIndexTypeWorker(): Promise { - const infos = await this.getIndexInfos(); - return infos.find(info => (info.keyType.flags & TypeFlags.String) !== 0)?.valueType; - } - - async getNumberIndexType(): Promise { - if (this.numberIndexType === false) { - this.numberIndexType = await this.getNumberIndexTypeWorker(); - } - return this.numberIndexType; - } - - private async getNumberIndexTypeWorker(): Promise { - const infos = await this.getIndexInfos(); - return infos.find(info => (info.keyType.flags & TypeFlags.Number) !== 0)?.valueType; - } - - async getApparentType(): Promise { - const result = await this.objectRegistry.fetchType(this, "getApparentType", this.apparentType); - this.apparentType = result.id; - return result; - } - - async getReducedType(): Promise { - const result = await this.objectRegistry.fetchType(this, "getReducedType", this.reducedType); - this.reducedType = result.id; - return result; - } - - async getIndexInfos(): Promise { - if (this.indexInfos === false) { - this.indexInfos = await this.objectRegistry.fetchIndexInfosOfType(this); - } - return this.indexInfos; - } - - async getAliasSymbol(): Promise { - return this.objectRegistry.fetchSymbol(this, "getAliasSymbolOfType", this.aliasSymbol); - } - - async getTarget(): Promise { - return this.objectRegistry.fetchType(this, "getTargetOfType", this.target); - } - - async getFreshType(): Promise { - return this.objectRegistry.fetchOptionalType(this, "getFreshTypeOfType", this.freshType); - } - - async getRegularType(): Promise { - return this.objectRegistry.fetchOptionalType(this, "getRegularTypeOfType", this.regularType); - } - - async getTypes(): Promise { - // Only union, intersection, and template literal types have constituent - // types; any other kind has none, so return undefined rather than sending - // a request the server cannot satisfy. - if (!(this.flags & (TypeFlags.UnionOrIntersection | TypeFlags.TemplateLiteral))) { - return undefined; - } - return this.objectRegistry.fetchTypes(this, "getTypesOfType"); - } - - async getTypeParameters(): Promise { - return this.objectRegistry.fetchTypes(this, "getTypeParametersOfType", this.typeParameters) as Promise; - } - - async getOuterTypeParameters(): Promise { - return this.objectRegistry.fetchTypes(this, "getOuterTypeParametersOfType", this.outerTypeParameters) as Promise; - } - - async getLocalTypeParameters(): Promise { - return this.objectRegistry.fetchTypes(this, "getLocalTypeParametersOfType", this.localTypeParameters) as Promise; - } - - async getAliasTypeArguments(): Promise { - return this.objectRegistry.fetchTypes(this, "getAliasTypeArgumentsOfType", this.aliasTypeArguments); - } - - async getObjectType(): Promise { - return this.objectRegistry.fetchType(this, "getObjectTypeOfType", this.objectType); - } - - async getIndexType(): Promise { - return this.objectRegistry.fetchType(this, "getIndexTypeOfType", this.indexType); - } - - async getCheckType(): Promise { - return this.objectRegistry.fetchType(this, "getCheckTypeOfType", this.checkType); - } - - async getExtendsType(): Promise { - return this.objectRegistry.fetchType(this, "getExtendsTypeOfType", this.extendsType); - } - - async getBaseType(): Promise { - return this.objectRegistry.fetchType(this, "getBaseTypeOfType", this.baseType); - } - - async getConstraint(): Promise { - // Type parameters resolve their constraint lazily through the checker, - // whereas substitution types carry a preloaded constraint handle. - if (this.flags & TypeFlags.TypeParameter) { - const result = await this.objectRegistry.fetchOptionalType(this, "getConstraintOfTypeParameter", this.constraint); - this.constraint = result ? result.id : 0; - return result; - } - return this.objectRegistry.fetchType(this, "getConstraintOfType", this.substConstraint); - } - - async getDefault(): Promise { - const result = await this.objectRegistry.fetchOptionalType(this, "getDefaultFromTypeParameter", this.default); - this.default = result ? result.id : 0; - return result; - } - - async getTrueType(): Promise { - const result = await this.objectRegistry.fetchType(this, "getTrueTypeOfConditionalType", this.trueType); - this.trueType = result.id; - return result; - } - - async getFalseType(): Promise { - const result = await this.objectRegistry.fetchType(this, "getFalseTypeOfConditionalType", this.falseType); - this.falseType = result.id; - return result; - } - - /** - * Get the base types of this type. Returns `undefined` for any type that is - * not a class or interface. - */ - async getBaseTypes(): Promise { - if (!this.isClassOrInterface()) { - return undefined; - } - if (this.baseTypes === false) { - this.baseTypes = await this.objectRegistry.fetchBaseTypes(this); - } - return this.baseTypes; - } - - isClassOrInterface(): this is InterfaceType { - return isClassOrInterfaceType(this); - } - - isUnionType(): this is UnionType { - return isUnionType(this); - } - - isIntersectionType(): this is IntersectionType { - return isIntersectionType(this); - } - - isObjectType(): this is ObjectType { - return isObjectType(this); - } - - isIntrinsicType(): this is IntrinsicType { - return isIntrinsicType(this); - } - - isErrorType(): boolean { - return isErrorType(this); - } - - isLiteralType(): this is LiteralType { - return isLiteralType(this); - } - - isStringLiteralType(): this is StringLiteralType { - return isStringLiteralType(this); - } - - isNumberLiteralType(): this is NumberLiteralType { - return isNumberLiteralType(this); - } - - isBigIntLiteralType(): this is BigIntLiteralType { - return isBigIntLiteralType(this); - } - - isBooleanLiteralType(): this is BooleanLiteralType { - return isBooleanLiteralType(this); - } - - isTypeReference(): this is TypeReference { - return isTypeReference(this); - } - - isTupleType(): this is TupleType { - return isTupleType(this); - } - - isIndexType(): this is IndexType { - return isIndexType(this); - } - - isIndexedAccessType(): this is IndexedAccessType { - return isIndexedAccessType(this); - } - - isConditionalType(): this is ConditionalType { - return isConditionalType(this); - } - - isSubstitutionType(): this is SubstitutionType { - return isSubstitutionType(this); - } - - isTemplateLiteralType(): this is TemplateLiteralType { - return isTemplateLiteralType(this); - } - - isStringMappingType(): this is StringMappingType { - return isStringMappingType(this); - } - - isTypeParameter(): this is TypeParameter { - return isTypeParameter(this); - } -} - -export function isUnionType(type: Type): type is UnionType { - return (type.flags & TypeFlags.Union) !== 0; -} - -export function isIntersectionType(type: Type): type is IntersectionType { - return (type.flags & TypeFlags.Intersection) !== 0; -} - -export function isObjectType(type: Type): type is ObjectType { - return (type.flags & TypeFlags.Object) !== 0; -} - -export function isClassOrInterfaceType(type: Type): type is InterfaceType { - return isObjectType(type) && (type.objectFlags & ObjectFlags.ClassOrInterface) !== 0; -} - -export function isIntrinsicType(type: Type): type is IntrinsicType { - return (type.flags & TypeFlags.Intrinsic) !== 0; -} - -/** - * Whether this is the error type — the placeholder the checker produces when a - * type cannot be determined (e.g. an unresolved reference). It is an intrinsic - * type named `"error"` (this covers both the singleton error type and the - * per-alias error types manufactured for unresolved type alias references). - */ -export function isErrorType(type: Type): boolean { - return isIntrinsicType(type) && type.intrinsicName === "error"; -} - -export function isLiteralType(type: Type): type is LiteralType { - return (type.flags & TypeFlags.Literal) !== 0; -} - -export function isStringLiteralType(type: Type): type is StringLiteralType { - return (type.flags & TypeFlags.StringLiteral) !== 0; -} - -export function isNumberLiteralType(type: Type): type is NumberLiteralType { - return (type.flags & TypeFlags.NumberLiteral) !== 0; -} - -export function isBigIntLiteralType(type: Type): type is BigIntLiteralType { - return (type.flags & TypeFlags.BigIntLiteral) !== 0; -} - -export function isBooleanLiteralType(type: Type): type is BooleanLiteralType { - return (type.flags & TypeFlags.BooleanLiteral) !== 0; -} - -export function isTypeReference(type: Type): type is TypeReference { - return isObjectType(type) && (type.objectFlags & ObjectFlags.Reference) !== 0; -} - -export function isTupleType(type: Type): type is TupleType { - return isObjectType(type) && (type.objectFlags & ObjectFlags.Tuple) !== 0; -} - -export function isIndexType(type: Type): type is IndexType { - return (type.flags & TypeFlags.Index) !== 0; -} - -export function isIndexedAccessType(type: Type): type is IndexedAccessType { - return (type.flags & TypeFlags.IndexedAccess) !== 0; -} - -export function isConditionalType(type: Type): type is ConditionalType { - return (type.flags & TypeFlags.Conditional) !== 0; -} - -export function isSubstitutionType(type: Type): type is SubstitutionType { - return (type.flags & TypeFlags.Substitution) !== 0; -} - -export function isTemplateLiteralType(type: Type): type is TemplateLiteralType { - return (type.flags & TypeFlags.TemplateLiteral) !== 0; -} - -export function isStringMappingType(type: Type): type is StringMappingType { - return (type.flags & TypeFlags.StringMapping) !== 0; -} - -export function isTypeParameter(type: Type): type is TypeParameter { - return (type.flags & TypeFlags.TypeParameter) !== 0; -} - -export class Signature { - private flags: number; - private objectRegistry: ProjectObjectRegistry; - - readonly id: number; - readonly declaration?: NodeHandle | undefined; - readonly typeParameters?: readonly number[] | undefined; - readonly parameters: readonly number[]; - readonly thisParameter?: number | undefined; - readonly target?: number | undefined; - private returnType: number | false; - - constructor(data: SignatureResponse, project: Project, objectRegistry: ProjectObjectRegistry) { - this.id = data.id; - this.flags = data.flags; - this.objectRegistry = objectRegistry; - this.declaration = data.declaration ? new NodeHandle(data.declaration, project) : undefined; - this.typeParameters = data.typeParameters ?? []; - this.parameters = data.parameters ?? []; - this.thisParameter = data.thisParameter; - this.target = data.target; - this.returnType = false; - } - - async getTypeParameters(): Promise { - return this.objectRegistry.fetchTypes(this, "getTypeParametersOfSignature", this.typeParameters) as Promise; - } - - async getParameters(): Promise { - return this.objectRegistry.fetchSymbols(this, "getParametersOfSignature", this.parameters); - } - - async getThisParameter(): Promise { - return this.objectRegistry.fetchSymbol(this, "getThisParameterOfSignature", this.thisParameter); - } - - async getTarget(): Promise { - return this.objectRegistry.fetchSignature(this, "getTargetOfSignature", this.target); - } - - async getReturnType(): Promise { - const result = await this.objectRegistry.fetchType(this, "getReturnTypeOfSignature", this.returnType); - this.returnType = result.id; - return result; - } - - getTypeParameterAtPosition(pos: number): Promise { - return this.objectRegistry.fetchTypeParameterAtPosition(this, pos); - } - - get hasRestParameter(): boolean { - return (this.flags & SignatureFlags.HasRestParameter) !== 0; - } - - get isConstruct(): boolean { - return (this.flags & SignatureFlags.Construct) !== 0; - } - - get isAbstract(): boolean { - return (this.flags & SignatureFlags.Abstract) !== 0; - } -} +import { CheckFlags } from "#enums/checkFlags"; +import { CompletionItemKind } from "#enums/completionItemKind"; +import { DiagnosticCategory } from "#enums/diagnosticCategory"; +import { ElementFlags } from "#enums/elementFlags"; +import { EmitOnly } from "#enums/emitOnly"; +import { ModuleKind } from "#enums/moduleKind"; +import { NewLineKind } from "#enums/newLineKind"; +import { NodeBuilderFlags } from "#enums/nodeBuilderFlags"; +import { ObjectFlags } from "#enums/objectFlags"; +import { SignatureFlags } from "#enums/signatureFlags"; +import { SignatureKind } from "#enums/signatureKind"; +import { SymbolFlags } from "#enums/symbolFlags"; +import { TypeFlags } from "#enums/typeFlags"; +import { TypeFormatFlags } from "#enums/typeFormatFlags"; +import { TypePredicateKind } from "#enums/typePredicateKind"; +import { + type __String, + type Declaration, + type Expression, + type Identifier, + type IndexSignatureDeclaration, + ModifierFlags, + type Node, + type Path, + type SourceFile, + type SyntaxKind, + type TypeNode, + unescapeLeadingUnderscores, +} from "../../ast/index.ts"; +import { assertNever } from "../../internal/utils.ts"; +import { + encodeNode, + uint8ArrayToBase64, +} from "../node/encoder.ts"; +import { + decodeNode, + getNodeId, + parseNodeHandle, + readParseOptionsKey, + readSourceFileHash, + RemoteSourceFile, +} from "../node/node.ts"; +import { Wtf8Decoder } from "../node/wtf8.ts"; +import type { + APIOptions, + LSPConnectionOptions, +} from "../options.ts"; +import { + createGetCanonicalFileName, + toPath, +} from "../path.ts"; +import type { + CompilerOptions, + Diagnostic, + DocumentIdentifier, + DocumentPosition, + EmitOutputResponse as ProtocolEmitOutputResponse, + ImportAdderAction, + IntrinsicTypeMethod, + LSPUpdateSnapshotParams, + ParsedCommandLine, + ProjectReference, + ProjectResponse, + ReadConfigFileResponse, + SignaturePropertyMethod, + SignatureResponse, + SourceFileMetadata, + SymbolPropertyMethod, + SymbolResponse, + SymbolsPropertyMethod, + TextEdit, + TypeAcquisition, + TypePropertyMethod, + TypeResponse, + TypesPropertyMethod, + UpdateSnapshotParams, + UpdateSnapshotResponse, +} from "../proto.ts"; +import { + resolveFileName, + toUpdateSnapshotRequest, +} from "../proto.ts"; +import { SourceFileCache } from "../sourceFileCache.ts"; +import type { + RequestTiming, + TimingAccumulators, + TimingInfo, +} from "../timing.ts"; +import { + Client, + type ClientSocketOptions, + type ClientSpawnOptions, +} from "./client.ts"; +import type { + AssertsIdentifierTypePredicate, + AssertsThisTypePredicate, + BigIntLiteralType, + BooleanLiteralType, + CompletionEntry, + CompletionInfo, + CompletionOptions, + ConditionalType, + EmitOutput, + EmitOutputFile, + EmitResult, + FormatDiagnosticsHost, + FreshableType, + GetImportEditsForSymbolsOptions, + IdentifierTypePredicate, + ImportAdderAction as APIImportAdderAction, + IndexedAccessType, + IndexInfo, + IndexType, + InterfaceType, + IntersectionType, + IntrinsicType, + JSDocTagInfo, + LiteralType, + NumberLiteralType, + ObjectType, + StringLiteralType, + StringMappingType, + StructuredType, + SubstitutionType, + TemplateLiteralType, + ThisTypePredicate, + TupleType, + Type, + TypeParameter, + TypePredicate, + TypePredicateBase, + TypeReference, + UnionOrIntersectionType, + UnionType, +} from "./types.ts"; + +export { formatDiagnostics, formatDiagnosticsWithColorAndContext } from "../diagnosticFormatter.ts"; +export { documentURIToFileName, fileNameToDocumentURI } from "../path.ts"; +export { CheckFlags, CompletionItemKind, DiagnosticCategory, ElementFlags, EmitOnly, ModifierFlags, ModuleKind, NodeBuilderFlags, ObjectFlags, SignatureFlags, SignatureKind, SymbolFlags, TypeFlags, TypeFormatFlags, TypePredicateKind }; +export type { + APIImportAdderAction as ImportAdderAction, + APIOptions, + AssertsIdentifierTypePredicate, + AssertsThisTypePredicate, + BigIntLiteralType, + BooleanLiteralType, + ClientSocketOptions, + ClientSpawnOptions, + CompilerOptions, + CompletionEntry, + CompletionInfo, + CompletionOptions, + ConditionalType, + Diagnostic, + DocumentIdentifier, + DocumentPosition, + EmitOutput, + EmitOutputFile, + EmitResult, + FormatDiagnosticsHost, + FreshableType, + GetImportEditsForSymbolsOptions, + IdentifierTypePredicate, + IndexedAccessType, + IndexInfo, + IndexType, + InterfaceType, + IntersectionType, + IntrinsicType, + JSDocTagInfo, + LiteralType, + LSPConnectionOptions, + NumberLiteralType, + ObjectType, + ParsedCommandLine, + ProjectReference, + ReadConfigFileResponse, + RequestTiming, + SourceFileMetadata, + StringLiteralType, + StringMappingType, + StructuredType, + SubstitutionType, + TemplateLiteralType, + TextEdit, + ThisTypePredicate, + TimingAccumulators, + TimingInfo, + TupleType, + Type, + TypeAcquisition, + TypeParameter, + TypePredicate, + TypePredicateBase, + TypeReference, + UnionOrIntersectionType, + UnionType, +}; + +export interface TranspileOptions { + compilerOptions?: CompilerOptions; + fileName?: string; + reportDiagnostics?: boolean; +} + +export interface TranspileOutput { + outputText: string; + diagnostics?: readonly Diagnostic[]; + sourceMapText?: string; +} + +// @sync-only-start +// export { all } from "./generatorSupport.ts"; +// import {all, type ExecutedGeneratorsResults, type APIRequestGenerator} from "./generatorSupport.ts"; +// @sync-only-end + +export class API implements FormatDiagnosticsHost { + private client: Client; + private sourceFileCache: SourceFileCache; + private toPath: ((fileName: string) => Path) | undefined; + private currentDirectory: string | undefined; + private getCanonicalFileNameWorker: ((fileName: string) => string) | undefined; + private initialized: boolean = false; + private activeSnapshots: Set = new Set(); + private latestSnapshot: Snapshot | undefined; + readonly internal: InternalAPI; + + constructor(options: APIOptions | LSPConnectionOptions = {}) { + this.client = new Client(options); + this.sourceFileCache = new SourceFileCache(); + this.internal = new InternalAPI(this.client, () => this.ensureInitialized()); // @sync: this.internal = new InternalAPI(this.client, this.ensureInitialized); + } + + /** + * Create an API instance from an existing LSP connection's API session. + * Use this when connecting to an API pipe provided by an LSP server via custom/initializeAPISession. + */ + static async fromLSPConnection(options: LSPConnectionOptions): Promise> { + const api = new API(options); + await api.ensureInitialized(); + return api; + } + + // @sync-skip-block-start + batchContext(): { [globalThis.Symbol.dispose](): void; } { + return this.client.batchContext(); + } + // @sync-skip-block-end + // @sync-only-start + // batch(...requestGenerators: T): ExecutedGeneratorsResults { + // const batches = all(...requestGenerators); + // let state = batches.next(); + // while (!state.done) { + // state = batches.next(this.client.batchRequests(state.value).responses); + // } + // return state.value; + // } + // @sync-only-end + + private async ensureInitialized(): Promise { + if (!this.initialized) { + const response = await this.client.apiRequest("initialize", null); + const getCanonicalFileName = createGetCanonicalFileName(response.useCaseSensitiveFileNames); + const currentDirectory = response.currentDirectory; + this.getCanonicalFileNameWorker = getCanonicalFileName; + this.currentDirectory = currentDirectory; + this.toPath = (fileName: string) => toPath(fileName, currentDirectory, getCanonicalFileName) as Path; + this.initialized = true; + } + } + + getCurrentDirectory(): string { + if (this.currentDirectory === undefined) { + throw new Error("API has not been initialized"); + } + return this.currentDirectory; + } + + getCanonicalFileName(fileName: string): string { + if (this.getCanonicalFileNameWorker === undefined) { + throw new Error("API has not been initialized"); + } + return this.getCanonicalFileNameWorker(fileName); + } + + getNewLine(): string { + return "\n"; + } + + async parseConfigFile(file: DocumentIdentifier): Promise { + await this.ensureInitialized(); + return this.client.apiRequest("parseConfigFile", { file }); + } + + async parseCommandLine(commandLine: readonly string[]): Promise { + await this.ensureInitialized(); + return this.client.apiRequest("parseCommandLine", { commandLine }); + } + + async readConfigFile(file: DocumentIdentifier): Promise { + await this.ensureInitialized(); + return this.client.apiRequest("readConfigFile", { file }); + } + + async parseJsonConfigFileContent( + json: any, + options: + | { configDirectory: string; configFileName?: never; } + | { configFileName: DocumentIdentifier; configDirectory?: never; }, + ): Promise { + await this.ensureInitialized(); + return this.client.apiRequest("parseJsonConfigFileContent", { json, ...options }); + } + + async transpileModule(input: string, options: TranspileOptions = {}): Promise { + await this.ensureInitialized(); + return this.client.apiRequest("transpileModule", { input, options }); + } + + async transpileModuleFromFile(fileName: string, options: TranspileOptions = {}): Promise { + await this.ensureInitialized(); + return this.client.apiRequest("transpileModuleFromFile", { fileName, options }); + } + + async transpileDeclaration(input: string, options: TranspileOptions = {}): Promise { + await this.ensureInitialized(); + return this.client.apiRequest("transpileDeclaration", { input, options }); + } + + async transpileDeclarationFromFile(fileName: string, options: TranspileOptions = {}): Promise { + await this.ensureInitialized(); + return this.client.apiRequest("transpileDeclarationFromFile", { fileName, options }); + } + + async updateSnapshot(params?: FromLSP extends true ? LSPUpdateSnapshotParams : UpdateSnapshotParams): Promise { + await this.ensureInitialized(); + + const requestParams = toUpdateSnapshotRequest(params); + const data = await this.client.apiRequest("updateSnapshot", requestParams); + + // Retain cached source files from previous snapshot for unchanged files + if (this.latestSnapshot) { + this.sourceFileCache.retainForSnapshot(data.snapshot, this.latestSnapshot.id, data.changes); + if (this.latestSnapshot.isDisposed()) { + this.sourceFileCache.releaseSnapshot(this.latestSnapshot.id); + } + } + + const snapshot = new Snapshot( + data, + this.client, + this.sourceFileCache, + this.toPath!, + this, + () => { + this.activeSnapshots.delete(snapshot); + if (snapshot !== this.latestSnapshot) { + this.sourceFileCache.releaseSnapshot(snapshot.id); + } + }, + ); + this.latestSnapshot = snapshot; + this.activeSnapshots.add(snapshot); + + return snapshot; + } + + async close(): Promise { + // Dispose all active snapshots + for (const snapshot of [...this.activeSnapshots]) { + await snapshot.dispose(); + } + // Release the latest snapshot's cache refs if still held + if (this.latestSnapshot) { + this.sourceFileCache.releaseSnapshot(this.latestSnapshot.id); + this.latestSnapshot = undefined; + } + await this.client.close(); + this.sourceFileCache.clear(); + } + + clearSourceFileCache(): void { + this.sourceFileCache.clear(); + } + + async runWithTemporaryFileUpdate(baseSnapshot: Snapshot, file: DocumentIdentifier, newText: string, cb: (newSnapshot: Snapshot) => void | Promise): Promise { + await this.ensureInitialized(); + + if (!this.activeSnapshots.has(baseSnapshot) || baseSnapshot.isDisposed()) { + throw new Error("Cannot run a temporary file update on an inactive snapshot"); + } + const data = await this.client.apiRequest("updateTemporarySnapshot", { snapshot: baseSnapshot.id, file, newText }); + + // Retain cached source files from the base snapshot for files unchanged by + // the temporary update. The temporary snapshot is not the latest snapshot, so + // we never release the latest snapshot's cache here. + this.sourceFileCache.retainForSnapshot(data.snapshot, baseSnapshot.id, data.changes); + + const snapshot = new Snapshot( + data, + this.client, + this.sourceFileCache, + this.toPath!, + this, + () => { + this.activeSnapshots.delete(snapshot); + this.sourceFileCache.releaseSnapshot(snapshot.id); + }, + ); + this.activeSnapshots.add(snapshot); + + try { + await cb(snapshot); + } + finally { + await snapshot.dispose(); + } + } + + /** + * Returns a snapshot of collected timing information for requests made + * through this API instance: client-measured round-trip latency and bytes + * transferred, folded together with the server's own per-request processing + * time and an estimated transport overhead (round-trip minus server time). + * + * Fetching the snapshot issues a lightweight request to the server to + * retrieve its timing collection. Collection must be enabled via the + * `collectTiming` option; when it is not, the returned snapshot has + * `enabled: false` and zeroed totals. + */ + getTimingInfo(): Promise { + return this.client.getTimingInfo(); + } + + /** Clears all accumulated timing totals and recent-request history, on both the client and the server. */ + resetTimingInfo(): Promise { + return this.client.resetTimingInfo(); + } +} + +type EnsureInitialized = () => Promise; // @sync: type EnsureInitialized = (() => void) & { gen(): Generator; }; + +export class InternalAPI { + private client: Client; + private ensureInitialized: EnsureInitialized; + + /** @internal */ + constructor(client: Client, ensureInitialized: EnsureInitialized) { + this.client = client; + this.ensureInitialized = ensureInitialized; + } + + async startCPUProfile(dir: string): Promise { + await this.ensureInitialized(); + await this.client.apiRequest("startCPUProfile", { dir }); + } + + async stopCPUProfile(): Promise { + await this.ensureInitialized(); + const result = await this.client.apiRequest("stopCPUProfile", null); + return result.file; + } + + async saveHeapProfile(dir: string): Promise { + await this.ensureInitialized(); + const result = await this.client.apiRequest("saveHeapProfile", { dir }); + return result.file; + } +} + +export class Snapshot { + readonly id: number; + private projectMap: Map; + private toPath: (fileName: string) => Path; + private client: Client; + private disposed: boolean = false; + private onDispose: () => void; + private snapshotRegistry: SnapshotObjectRegistry; + readonly internal: SnapshotInternalAPI; + + constructor( + data: UpdateSnapshotResponse, + client: Client, + sourceFileCache: SourceFileCache, + toPath: (fileName: string) => Path, + formatDiagnosticsHost: FormatDiagnosticsHost, + onDispose: () => void, + ) { + this.id = data.snapshot; + this.client = client; + this.toPath = toPath; + this.onDispose = onDispose; + this.projectMap = new Map(); + this.snapshotRegistry = new SnapshotObjectRegistry(client, this.id, projectId => this.projectMap.get(projectId)); + + for (const projData of data.projects) { + const project = new Project(projData, this.id, client, sourceFileCache, toPath, formatDiagnosticsHost, this.snapshotRegistry); + this.projectMap.set(toPath(projData.configFileName), project); + } + + this.internal = new SnapshotInternalAPI(this.id, client); + } + + getProjects(): readonly Project[] { + this.ensureNotDisposed(); + return [...this.projectMap.values()]; + } + + getProject(configFileName: string): Project | undefined { + this.ensureNotDisposed(); + return this.projectMap.get(this.toPath(configFileName)); + } + + async getDefaultProjectForFile(file: DocumentIdentifier): Promise { + this.ensureNotDisposed(); + const data = await this.client.apiRequest("getDefaultProjectForFile", { + snapshot: this.id, + file, + }); + if (!data) return undefined; + return this.projectMap.get(this.toPath(data.configFileName)); + } + + [globalThis.Symbol.dispose](): void { + this.dispose(); + } + + async dispose(): Promise { + if (this.disposed) return; + this.disposed = true; + for (const project of this.projectMap.values()) { + project.dispose(); + } + this.projectMap.clear(); + this.snapshotRegistry.clear(); + this.onDispose(); + await this.client.apiRequest("release", { snapshot: this.id }); + } + + isDisposed(): boolean { + return this.disposed; + } + + private ensureNotDisposed(): void { + if (this.disposed) { + throw new Error("Snapshot is disposed"); + } + } +} + +class SnapshotObjectRegistry { + private readonly symbols: Map = new Map(); + private readonly client: Client; + private readonly snapshotId: number; + private readonly resolveProject: (projectId: Path) => Project | undefined; + + constructor(client: Client, snapshotId: number, resolveProject: (projectId: Path) => Project | undefined) { + this.client = client; + this.snapshotId = snapshotId; + this.resolveProject = resolveProject; + } + + /** Resolve a project id (a config file path) to its Project within this snapshot. */ + getProject(projectId: Path): Project | undefined { + return this.resolveProject(projectId); + } + + getOrCreateSymbol(data: SymbolResponse): Symbol { + let symbol = this.symbols.get(data.id); + if (!symbol) { + symbol = new Symbol(data, this); + this.symbols.set(data.id, symbol); + } + return symbol; + } + + getSymbol(id: number): Symbol | undefined { + return this.symbols.get(id); + } + + clear(): void { + this.symbols.clear(); + } + + async fetchSymbol(source: Symbol | Signature | Type, method: SymbolPropertyMethod, handle: number | undefined, projectId: Path): Promise { + if (!handle) return undefined as unknown as Symbol; + const cached = this.getSymbol(handle); + if (cached) return cached; + + const data = await this.client.apiRequest(method, { + snapshot: this.snapshotId, + project: projectId, + objectId: source.id, + }); + if (!data) throw new Error(`${method} returned null symbol for ${source.constructor.name} ${source.id}`); + return this.getOrCreateSymbol(data); + } + + async fetchSymbols(source: Symbol | Signature | Type, method: SymbolsPropertyMethod, handles: readonly number[] | undefined, projectId: Path): Promise { + if (handles) { + const result = new Array(handles.length); + let allCached = true; + for (let i = 0; i < handles.length; i++) { + const cached = this.getSymbol(handles[i]); + if (!cached) { + allCached = false; + break; + } + result[i] = cached; + } + if (allCached) return result; + } + const symbolData = await this.client.apiRequest(method, { + snapshot: this.snapshotId, + project: projectId, + objectId: source.id, + }); + if (symbolData == null) return []; + else return symbolData.map(data => this.getOrCreateSymbol(data)); + } +} + +class ProjectObjectRegistry { + private client: Client; + private snapshotId: number; + private project: Project; + private snapshotRegistry: SnapshotObjectRegistry; + private types: Map = new Map(); + private signatures: Map = new Map(); + + constructor( + client: Client, + snapshotId: number, + project: Project, + snapshotRegistry: SnapshotObjectRegistry, + ) { + this.client = client; + this.snapshotId = snapshotId; + this.project = project; + this.snapshotRegistry = snapshotRegistry; + } + + getOrCreateSymbol(data: SymbolResponse): Symbol { + return this.snapshotRegistry.getOrCreateSymbol(data); + } + + getSymbol(id: number): Symbol | undefined { + return this.snapshotRegistry.getSymbol(id); + } + + getOrCreateType(data: TypeResponse): TypeObject { + let type = this.types.get(data.id); + if (!type) { + type = new TypeObject(data, this); + this.types.set(data.id, type); + } + return type; + } + + getType(id: number): TypeObject | undefined { + return this.types.get(id); + } + + getOrCreateSignature(data: SignatureResponse): Signature { + let sig = this.signatures.get(data.id); + if (!sig) { + sig = new Signature(data, this.project, this); + this.signatures.set(data.id, sig); + } + return sig; + } + + getSignature(id: number): Signature | undefined { + return this.signatures.get(id); + } + + clear(): void { + this.types.clear(); + this.signatures.clear(); + } + + async fetchOptionalType(source: Symbol | Signature | Type, method: TypePropertyMethod, handle: number | false | undefined): Promise { + if (handle !== false) { + if (!handle) return undefined; + const cached = this.getType(handle); + if (cached) return cached as unknown as T; + } + + const data = await this.client.apiRequest(method, { + snapshot: this.snapshotId, + project: this.project.id, + objectId: source.id, + }); + if (!data) return undefined; + return this.getOrCreateType(data) as unknown as T; + } + + async fetchType(source: Symbol | Signature | Type, method: TypePropertyMethod, handle: number | false | undefined): Promise { + const result = await this.fetchOptionalType(source, method, handle); + if (result === undefined) throw new Error(`${method} returned no type for ${source.constructor.name} ${source.id}`); + return result; + } + + async fetchSymbol(source: Symbol | Signature | Type, method: SymbolPropertyMethod, handle: number | undefined): Promise { + return this.snapshotRegistry.fetchSymbol(source, method, handle, this.project.id); + } + + async fetchSignature(source: Symbol | Signature | Type, method: SignaturePropertyMethod, handle: number | undefined): Promise { + if (!handle) return undefined as unknown as Signature; + const cached = this.getSignature(handle); + if (cached) return cached; + + const data = await this.client.apiRequest(method, { + snapshot: this.snapshotId, + project: this.project.id, + objectId: source.id, + }); + if (!data) throw new Error(`${method} returned null signature for ${source.constructor.name} ${source.id}`); + return this.getOrCreateSignature(data); + } + + async fetchTypes(source: Symbol | Signature | Type, method: TypesPropertyMethod, handles?: readonly number[]): Promise { + if (handles) { + const result = new Array(handles.length); + let allCached = true; + for (let i = 0; i < handles.length; i++) { + const cached = this.getType(handles[i]); + if (!cached) { + allCached = false; + break; + } + result[i] = cached; + } + if (allCached) return result; + } + const typesData = await this.client.apiRequest(method, { + snapshot: this.snapshotId, + project: this.project.id, + objectId: source.id, + }); + if (typesData == null) return []; + else return typesData.map(data => this.getOrCreateType(data)); + } + + async fetchSymbols(source: Symbol | Signature | Type, method: SymbolsPropertyMethod, handles?: readonly number[]): Promise { + return this.snapshotRegistry.fetchSymbols(source, method, handles, this.project.id); + } + + // getBaseTypes is a checker-level endpoint keyed by `type` (not `objectId`), + // so it cannot go through fetchTypes. This helper reuses that server method. + async fetchBaseTypes(source: Type): Promise { + const typesData = await this.client.apiRequest("getBaseTypes", { + snapshot: this.snapshotId, + project: this.project.id, + type: source.id, + }); + if (typesData == null) return []; + return typesData.map(data => this.getOrCreateType(data)); + } + + async fetchPropertiesOfType(source: Type): Promise { + const data = await this.client.apiRequest("getPropertiesOfType", { + snapshot: this.snapshotId, + project: this.project.id, + type: source.id, + }); + return data ? data.map(symbol => this.getOrCreateSymbol(symbol)) : []; + } + + async fetchApparentPropertiesOfType(source: Type): Promise { + const data = await this.client.apiRequest("getApparentPropertiesOfType", { + snapshot: this.snapshotId, + project: this.project.id, + objectId: source.id, + }); + return data ? data.map(symbol => this.getOrCreateSymbol(symbol)) : []; + } + + async fetchPropertyOfType(source: Type, name: string): Promise { + const data = await this.client.apiRequest("getPropertyOfType", { + snapshot: this.snapshotId, + project: this.project.id, + type: source.id, + name, + }); + return data ? this.getOrCreateSymbol(data) : undefined; + } + + async fetchSignaturesOfType(source: Type, kind: SignatureKind): Promise { + const data = await this.client.apiRequest("getSignaturesOfType", { + snapshot: this.snapshotId, + project: this.project.id, + type: source.id, + kind, + }); + return data.map(signature => this.getOrCreateSignature(signature)); + } + + async fetchIndexInfosOfType(source: Type): Promise { + const data = await this.client.apiRequest("getIndexInfosOfType", { + snapshot: this.snapshotId, + project: this.project.id, + type: source.id, + }); + if (!data) return []; + return data.map(info => ({ + keyType: this.getOrCreateType(info.keyType), + valueType: this.getOrCreateType(info.valueType), + isReadonly: info.isReadonly ?? false, + declaration: info.declaration ? new NodeHandle(info.declaration, this.project) : undefined, + })); + } + + async fetchTypeParameterAtPosition(source: Signature, pos: number): Promise { + const data = await this.client.apiRequest("getTypeParameterAtPosition", { + snapshot: this.snapshotId, + project: this.project.id, + signature: source.id, + index: pos, + }); + return this.getOrCreateType(data); + } +} + +export class Project { + readonly id: Path; + readonly configFileName: string; + readonly currentDirectory: string; + readonly parsedCommandLine: ParsedCommandLine; + /** @deprecated Use `parsedCommandLine.options`. */ + readonly compilerOptions: CompilerOptions; + /** @deprecated Use `parsedCommandLine.fileNames`. */ + readonly rootFiles: readonly string[]; + + readonly program: Program; + readonly checker: Checker; + readonly emitter: Emitter; + readonly languageService: LanguageService; + private client: Client; + private snapshotId: number; + + constructor( + data: ProjectResponse, + snapshotId: number, + client: Client, + sourceFileCache: SourceFileCache, + toPath: (fileName: string) => Path, + formatDiagnosticsHost: FormatDiagnosticsHost, + snapshotRegistry: SnapshotObjectRegistry, + ) { + this.id = data.id as Path; + this.configFileName = data.configFileName; + this.currentDirectory = data.currentDirectory; + if (!data.parsedCommandLine?.options) { + throw new Error(`Project '${data.configFileName}' has no parsed command line`); + } + this.parsedCommandLine = data.parsedCommandLine; + this.compilerOptions = this.parsedCommandLine.options; + this.rootFiles = this.parsedCommandLine.fileNames; + this.client = client; + this.snapshotId = snapshotId; + this.program = new Program( + snapshotId, + this, + client, + sourceFileCache, + toPath, + formatDiagnosticsHost, + ); + const objectRegistry = new ProjectObjectRegistry(client, snapshotId, this, snapshotRegistry); + this.checker = new Checker( + snapshotId, + this, + client, + objectRegistry, + ); + this.emitter = new Emitter(client); + this.languageService = new LanguageService(snapshotId, this, client, objectRegistry); + } + + /** @deprecated Use `languageService.getImportAdderEdits`. */ + getImportAdderEdits(file: DocumentIdentifier, actions: readonly APIImportAdderAction[]): Promise { + return this.languageService.getImportAdderEdits(file, actions); + } + + /** @deprecated Use `languageService.getImportEditsForSymbols`. */ + getImportEditsForSymbols(file: DocumentIdentifier, symbols: readonly Symbol[], options: GetImportEditsForSymbolsOptions = {}): Promise { + return this.languageService.getImportEditsForSymbols(file, symbols, options); + } + + dispose(): void { + this.checker.dispose(); + } +} + +export class LanguageService { + private snapshotId: number; + private project: Project; + private client: Client; + private objectRegistry: ProjectObjectRegistry; + + constructor( + snapshotId: number, + project: Project, + client: Client, + objectRegistry: ProjectObjectRegistry, + ) { + this.snapshotId = snapshotId; + this.project = project; + this.client = client; + this.objectRegistry = objectRegistry; + } + + async getImportAdderEdits(file: DocumentIdentifier, actions: readonly APIImportAdderAction[]): Promise { + const requestActions: ImportAdderAction[] = actions.map(action => { + switch (action.kind) { + case "importSymbol": + const importSymbolAction: ImportAdderAction = { + kind: "importSymbol", + symbol: action.symbol.id, + }; + if (action.isValidTypeOnlyUseSite !== undefined) { + importSymbolAction.isValidTypeOnlyUseSite = action.isValidTypeOnlyUseSite; + } + return importSymbolAction; + default: + return assertNever(action.kind); + } + }); + + const data = await this.client.apiRequest("getImportAdderEdits", { + snapshot: this.snapshotId, + project: this.project.id, + file, + actions: requestActions, + }); + return data ?? []; + } + + async getImportEditsForSymbols(file: DocumentIdentifier, symbols: readonly Symbol[], options: GetImportEditsForSymbolsOptions = {}): Promise { + return this.getImportAdderEdits( + file, + symbols.map((symbol): APIImportAdderAction => { + if (options.isValidTypeOnlyUseSite !== undefined) { + return { + kind: "importSymbol", + symbol, + isValidTypeOnlyUseSite: options.isValidTypeOnlyUseSite, + }; + } + return { + kind: "importSymbol", + symbol, + }; + }), + ); + } + + async getReferencedSymbolsForNode(node: Node, position: number): Promise { + const data = await this.client.apiRequest("getReferencedSymbolsForNode", { + snapshot: this.snapshotId, + project: this.project.id, + node: getNodeId(node), + position, + }); + return (data ?? []).map(entry => ({ + definition: new NodeHandle(entry.definition, this.project), + symbol: entry.symbol ? this.objectRegistry.getOrCreateSymbol(entry.symbol) : undefined, + references: (entry.references ?? []).map(h => new NodeHandle(h, this.project)), + })); + } + + async getSignatureUsage(signatureDecl: Node): Promise { + const data = await this.client.apiRequest("getSignatureUsages", { + snapshot: this.snapshotId, + project: this.project.id, + signatureDecl: getNodeId(signatureDecl), + }); + return (data ?? []).map(entry => ({ + name: new NodeHandle(entry.name, this.project), + call: entry.call ? new NodeHandle(entry.call, this.project) : undefined, + })); + } + + async getCompletionsAtPosition(document: string, position: number, options?: CompletionOptions): Promise { + const data = await this.client.apiRequest("getCompletionsAtPosition", { + snapshot: this.snapshotId, + project: this.project.id, + file: document, + position, + ...(options?.triggerCharacter !== undefined ? { triggerCharacter: options.triggerCharacter } : {}), + ...(options?.includeSymbol !== undefined ? { includeSymbol: options.includeSymbol } : {}), + }); + if (!data) return undefined; + return { + isIncomplete: data.isIncomplete, + entries: data.entries.map(e => ({ + ...e, + symbol: e.symbol ? this.objectRegistry.getOrCreateSymbol(e.symbol) : undefined, + })), + }; + } +} + +export class Program implements FormatDiagnosticsHost { + private snapshotId: number; + private project: Project; + private client: Client; + private sourceFileCache: SourceFileCache; + private toPath: (fileName: string) => Path; + private formatDiagnosticsHost: FormatDiagnosticsHost; + private decoder = new Wtf8Decoder(); + private sourceFileMetadataCache = new Map>(); + + constructor( + snapshotId: number, + project: Project, + client: Client, + sourceFileCache: SourceFileCache, + toPath: (fileName: string) => Path, + formatDiagnosticsHost: FormatDiagnosticsHost, + ) { + this.snapshotId = snapshotId; + this.project = project; + this.client = client; + this.sourceFileCache = sourceFileCache; + this.toPath = toPath; + this.formatDiagnosticsHost = formatDiagnosticsHost; + } + + getCurrentDirectory(): string { + return this.project.currentDirectory; + } + + getCanonicalFileName(fileName: string): string { + return this.formatDiagnosticsHost.getCanonicalFileName(fileName); + } + + getNewLine(): string { + return this.project.compilerOptions.newLine === NewLineKind.CRLF ? "\r\n" : "\n"; + } + + getCompilerOptions(): CompilerOptions { + return this.project.compilerOptions; + } + + async getSourceFile(file: DocumentIdentifier): Promise { + const fileName = resolveFileName(file); + const path = this.toPath(fileName); + + // Check if we already have a retained cache entry for this (snapshot, project) pair + const retained = this.sourceFileCache.getRetained(path, this.snapshotId, this.project.id); + if (retained) { + return retained; + } + + // Fetch from server + const binaryData = await this.client.apiRequestBinary("getSourceFile", { + snapshot: this.snapshotId, + project: this.project.id, + file, + }); + if (!binaryData) { + return undefined; + } + + const view = new DataView(binaryData.buffer, binaryData.byteOffset, binaryData.byteLength); + const contentHash = readSourceFileHash(view); + const parseOptionsKey = readParseOptionsKey(view); + + // Create a new RemoteSourceFile and cache it (set returns existing if hash matches) + const sourceFile = new RemoteSourceFile(binaryData, this.decoder, this.client.getTimingCollector()) as unknown as SourceFile; + return this.sourceFileCache.set(path, sourceFile, parseOptionsKey, contentHash, this.snapshotId, this.project.id); + } + + async getSourceFileNames(): Promise { + const data = await this.client.apiRequest("getSourceFileNames", { + snapshot: this.snapshotId, + project: this.project.id, + }); + return data ?? []; + } + + /** + * Returns program-stored metadata for the given source file, or `undefined` if the file + * is not part of the program. Metadata is fetched lazily per file and cached on this + * `Program` instance. + */ + getSourceFileMetadata(fileName: string): Promise { + return this.getSourceFileMetadataByPath(this.toPath(fileName)); + } + + /** + * Returns program-stored metadata for the source file at the given path, or `undefined` + * if the file is not part of the program. Like {@link getSourceFileMetadata}, but skips + * the file name to path conversion. Metadata is fetched lazily per file and cached on + * this `Program` instance. + */ + getSourceFileMetadataByPath(path: Path): Promise { + let metadata = this.sourceFileMetadataCache.get(path); + if (metadata === undefined) { + metadata = this.fetchSourceFileMetadata(path); + this.sourceFileMetadataCache.set(path, metadata); + } + return metadata; + } + + private async fetchSourceFileMetadata(path: Path): Promise { + const data = await this.client.apiRequest("getSourceFileMetadata", { + snapshot: this.snapshotId, + project: this.project.id, + file: path, + }); + return data ?? undefined; + } + + /** + * Returns whether the given source file was loaded as part of an external library + * (e.g. a dependency resolved from `node_modules`). The underlying program metadata is + * fetched lazily per file and cached on this `Program` instance. + */ + async isSourceFileFromExternalLibrary(file: SourceFile): Promise { + const metadata = await this.getSourceFileMetadataByPath(file.path); + return metadata?.isFromExternalLibrary ?? false; + } + + /** + * Returns whether the given source file is a default library file (e.g. `lib.d.ts`). + * The underlying program metadata is fetched lazily per file and cached on this + * `Program` instance. + */ + async isSourceFileDefaultLibrary(file: SourceFile): Promise { + const metadata = await this.getSourceFileMetadataByPath(file.path); + return metadata?.isDefaultLibrary ?? false; + } + + /** + * Get all config source file names associated with this program's project config. + * Includes the root config file and any extended config files. + */ + async getConfigFileNames(): Promise { + const data = await this.client.apiRequest("getConfigFileNames", { + snapshot: this.snapshotId, + project: this.project.id, + }); + return data ?? []; + } + + /** + * Get a config source file by file name/URI. + * This can return the project's root tsconfig file or one of its extended config files. + */ + async getConfigSourceFile(file: DocumentIdentifier): Promise { + const binaryData = await this.client.apiRequestBinary("getConfigSourceFile", { + snapshot: this.snapshotId, + project: this.project.id, + file, + }); + if (!binaryData) { + return undefined; + } + + return new RemoteSourceFile(binaryData, this.decoder) as unknown as SourceFile; + } + + /** + * Get syntactic (parse) diagnostics for specific files or all files. + * @param file - Optional file(s) to get diagnostics for. If omitted, returns diagnostics for all files. + */ + async getSyntacticDiagnostics(file?: DocumentIdentifier | readonly DocumentIdentifier[]): Promise { + const files = file === undefined ? undefined + : Array.isArray(file) ? file + : [file]; + const data = await this.client.apiRequest("getSyntacticDiagnostics", { + snapshot: this.snapshotId, + project: this.project.id, + ...(files !== undefined ? { files } : {}), + }); + return data ?? []; + } + + /** + * Get binder diagnostics for specific files or all files. + * @param file - Optional file(s) to get diagnostics for. If omitted, returns diagnostics for all files. + */ + async getBindDiagnostics(file?: DocumentIdentifier | readonly DocumentIdentifier[]): Promise { + const files = file === undefined ? undefined + : Array.isArray(file) ? file + : [file]; + const data = await this.client.apiRequest("getBindDiagnostics", { + snapshot: this.snapshotId, + project: this.project.id, + ...(files !== undefined ? { files } : {}), + }); + return data ?? []; + } + + /** + * Get semantic (type-check) diagnostics for specific files or all files. + * @param file - Optional file(s) to get diagnostics for. If omitted, returns diagnostics for all files. + */ + async getSemanticDiagnostics(file?: DocumentIdentifier | readonly DocumentIdentifier[]): Promise { + const files = file === undefined ? undefined + : Array.isArray(file) ? file + : [file]; + const data = await this.client.apiRequest("getSemanticDiagnostics", { + snapshot: this.snapshotId, + project: this.project.id, + ...(files !== undefined ? { files } : {}), + }); + return data ?? []; + } + + /** + * Get suggestion diagnostics for specific files or all files. + * @param file - Optional file(s) to get diagnostics for. If omitted, returns diagnostics for all files. + */ + async getSuggestionDiagnostics(file?: DocumentIdentifier | readonly DocumentIdentifier[]): Promise { + const files = file === undefined ? undefined + : Array.isArray(file) ? file + : [file]; + const data = await this.client.apiRequest("getSuggestionDiagnostics", { + snapshot: this.snapshotId, + project: this.project.id, + ...(files !== undefined ? { files } : {}), + }); + return data ?? []; + } + + /** + * Get declaration emit diagnostics for specific files or all files. + * @param file - Optional file(s) to get diagnostics for. If omitted, returns diagnostics for all files. + */ + async getDeclarationDiagnostics(file?: DocumentIdentifier | readonly DocumentIdentifier[]): Promise { + const files = file === undefined ? undefined + : Array.isArray(file) ? file + : [file]; + const data = await this.client.apiRequest("getDeclarationDiagnostics", { + snapshot: this.snapshotId, + project: this.project.id, + ...(files !== undefined ? { files } : {}), + }); + return data ?? []; + } + + /** + * Get program-wide diagnostics for the project, including compiler options diagnostics. + */ + async getProgramDiagnostics(): Promise { + const data = await this.client.apiRequest("getProgramDiagnostics", { + snapshot: this.snapshotId, + project: this.project.id, + }); + return data ?? []; + } + + /** + * Get global (non-file-specific) semantic diagnostics for the project. + */ + async getGlobalDiagnostics(): Promise { + const data = await this.client.apiRequest("getGlobalDiagnostics", { + snapshot: this.snapshotId, + project: this.project.id, + }); + return data ?? []; + } + + /** + * Get config file parsing diagnostics for the project. + */ + async getConfigFileParsingDiagnostics(): Promise { + const data = await this.client.apiRequest("getConfigFileParsingDiagnostics", { + snapshot: this.snapshotId, + project: this.project.id, + }); + return data ?? []; + } + + /** + * Emits files to the configured filesystem. + * + * When the API has a virtual filesystem with a `writeFile` callback, output + * is written there. Otherwise, the server writes directly to the host filesystem. + */ + async emit(emitOnly?: EmitOnly): Promise { + const response = await this.client.apiRequest("emit", { + snapshot: this.snapshotId, + project: this.project.id, + ...(emitOnly !== undefined ? { emitOnly } : {}), + }); + return { + emitSkipped: response.emitSkipped, + diagnostics: response.diagnostics, + emittedFiles: response.emittedFiles, + }; + } + + /** + * Emits files and returns their contents without writing to the filesystem. + */ + async emitToString(emitOnly?: EmitOnly): Promise { + const response = await this.client.apiRequest("emitToString", { + snapshot: this.snapshotId, + project: this.project.id, + ...(emitOnly !== undefined ? { emitOnly } : {}), + }); + return toEmitOutput(response); + } + + /** + * Gets JavaScript output for selected files regardless of project `noEmit`, `emitDeclarationOnly`, and `noEmitOnError` settings. + */ + async getJavaScriptEmit(files: readonly DocumentIdentifier[]): Promise { + const response = await this.client.apiRequest("getJavaScriptEmit", { + snapshot: this.snapshotId, + project: this.project.id, + files, + }); + return toEmitOutput(response); + } + + /** + * Gets declaration output for selected files regardless of project `noEmit`, `declaration`, `emitDeclarationOnly`, and `noEmitOnError` settings. + */ + async getDeclarationEmit(files: readonly DocumentIdentifier[]): Promise { + const response = await this.client.apiRequest("getDeclarationEmit", { + snapshot: this.snapshotId, + project: this.project.id, + files, + }); + return toEmitOutput(response); + } +} + +function toEmitOutput(response: ProtocolEmitOutputResponse): EmitOutput { + const outputFiles = new Map(); + for (const { fileName, ...outputFile } of response.outputFiles) { + outputFiles.set(fileName, outputFile); + } + return { + emitSkipped: response.emitSkipped, + diagnostics: response.diagnostics, + outputFiles, + }; +} + +export class Checker { + private snapshotId: number; + private project: Project; + private client: Client; + private objectRegistry: ProjectObjectRegistry; + private wellKnownSymbols: Promise<{ unknown: number; undefined: number; arguments: number; }> | undefined; + private wellKnownSignatures: Promise<{ unknown: number; }> | undefined; + + constructor( + snapshotId: number, + project: Project, + client: Client, + objectRegistry: ProjectObjectRegistry, + ) { + this.snapshotId = snapshotId; + this.project = project; + this.client = client; + this.objectRegistry = objectRegistry; + } + + dispose(): void { + this.objectRegistry.clear(); + } + + getSymbolAtLocation(node: Node): Promise; + getSymbolAtLocation(nodes: readonly Node[]): Promise<(Symbol | undefined)[]>; + async getSymbolAtLocation(nodeOrNodes: Node | readonly Node[]): Promise { + if (Array.isArray(nodeOrNodes)) { + const data = await this.client.apiRequest("getSymbolsAtLocations", { + snapshot: this.snapshotId, + project: this.project.id, + locations: nodeOrNodes.map(node => getNodeId(node)), + }); + return data.map(d => d ? this.objectRegistry.getOrCreateSymbol(d) : undefined); + } + const data = await this.client.apiRequest("getSymbolAtLocation", { + snapshot: this.snapshotId, + project: this.project.id, + location: getNodeId(nodeOrNodes as Node), + }); + return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined; + } + + getSymbolAtPosition(file: DocumentIdentifier, position: number): Promise; + getSymbolAtPosition(file: DocumentIdentifier, positions: readonly number[]): Promise<(Symbol | undefined)[]>; + async getSymbolAtPosition(file: DocumentIdentifier, positionOrPositions: number | readonly number[]): Promise { + if (typeof positionOrPositions === "number") { + const data = await this.client.apiRequest("getSymbolAtPosition", { + snapshot: this.snapshotId, + project: this.project.id, + file, + position: positionOrPositions, + }); + return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined; + } + const data = await this.client.apiRequest("getSymbolsAtPositions", { + snapshot: this.snapshotId, + project: this.project.id, + file, + positions: positionOrPositions, + }); + return data.map(d => d ? this.objectRegistry.getOrCreateSymbol(d) : undefined); + } + + getSymbolOfSourceFile(file: DocumentIdentifier): Promise; + getSymbolOfSourceFile(files: readonly DocumentIdentifier[]): Promise<(Symbol | undefined)[]>; + async getSymbolOfSourceFile(fileOrFiles: DocumentIdentifier | readonly DocumentIdentifier[]): Promise { + if (Array.isArray(fileOrFiles)) { + const data = await this.client.apiRequest("getSymbolsOfSourceFiles", { + snapshot: this.snapshotId, + project: this.project.id, + files: fileOrFiles, + }); + return data.map(d => d ? this.objectRegistry.getOrCreateSymbol(d) : undefined); + } + const data = await this.client.apiRequest("getSymbolOfSourceFile", { + snapshot: this.snapshotId, + project: this.project.id, + file: fileOrFiles as DocumentIdentifier, + }); + return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined; + } + + /** + * Get the type of a symbol. Always returns a type; for symbols whose type + * cannot be determined the checker yields the error type (use + * {@link Type.isErrorType} to detect it). + */ + getTypeOfSymbol(symbol: Symbol): Promise; + getTypeOfSymbol(symbols: readonly Symbol[]): Promise; + async getTypeOfSymbol(symbolOrSymbols: Symbol | readonly Symbol[]): Promise { + if (Array.isArray(symbolOrSymbols)) { + const data = await this.client.apiRequest("getTypesOfSymbols", { + snapshot: this.snapshotId, + project: this.project.id, + symbols: symbolOrSymbols.map(s => s.id), + }); + return data.map(d => this.objectRegistry.getOrCreateType(d)); + } + const data = await this.client.apiRequest("getTypeOfSymbol", { + snapshot: this.snapshotId, + project: this.project.id, + symbol: (symbolOrSymbols as Symbol).id, + }); + return this.objectRegistry.getOrCreateType(data); + } + + /** + * Get the declared type of a symbol. Always returns a type; for symbols whose + * declared type cannot be determined the checker yields the error type (use + * {@link Type.isErrorType} to detect it). + */ + async getDeclaredTypeOfSymbol(symbol: Symbol): Promise { + const data = await this.client.apiRequest("getDeclaredTypeOfSymbol", { + snapshot: this.snapshotId, + project: this.project.id, + symbol: symbol.id, + }); + return this.objectRegistry.getOrCreateType(data); + } + + /** + * Get the type of a symbol, excluding the missing type when + * `exactOptionalPropertyTypes: true` is set; for symbols whose + * type cannot be determined the checker yields the error type + * (use {@link Type.isErrorType} to detect it). + */ + async getNonMissingTypeOfSymbol(symbol: Symbol): Promise { + const data = await this.client.apiRequest("getNonMissingTypeOfSymbol", { + snapshot: this.snapshotId, + project: this.project.id, + symbol: symbol.id, + }); + return this.objectRegistry.getOrCreateType(data); + } + + async getReferencesToSymbolInFile(file: DocumentIdentifier, symbol: Symbol): Promise { + const data = await this.client.apiRequest("getReferencesToSymbolInFile", { + snapshot: this.snapshotId, + project: this.project.id, + file, + symbol: symbol.id, + }); + return (data ?? []).map(h => new NodeHandle(h, this.project)); + } + + /** @deprecated Use `project.languageService.getReferencedSymbolsForNode`. */ + getReferencedSymbolsForNode(node: Node, position: number): Promise { + return this.project.languageService.getReferencedSymbolsForNode(node, position); + } + + /** @deprecated Use `project.languageService.getSignatureUsage`. */ + getSignatureUsage(signatureDecl: Node): Promise { + return this.project.languageService.getSignatureUsage(signatureDecl); + } + + /** @deprecated Use `project.languageService.getCompletionsAtPosition`. */ + getCompletionsAtPosition(document: string, position: number, options?: CompletionOptions): Promise { + return this.project.languageService.getCompletionsAtPosition(document, position, options); + } + + /** + * Get the type at a node location. Always returns a type; for nodes whose + * type cannot be determined the checker yields the error type (use + * {@link Type.isErrorType} to detect it). + */ + getTypeAtLocation(node: Node): Promise; + getTypeAtLocation(nodes: readonly Node[]): Promise; + async getTypeAtLocation(nodeOrNodes: Node | readonly Node[]): Promise { + if (Array.isArray(nodeOrNodes)) { + const data = await this.client.apiRequest("getTypeAtLocations", { + snapshot: this.snapshotId, + project: this.project.id, + locations: nodeOrNodes.map(node => getNodeId(node)), + }); + return data.map(d => this.objectRegistry.getOrCreateType(d)); + } + const data = await this.client.apiRequest("getTypeAtLocation", { + snapshot: this.snapshotId, + project: this.project.id, + location: getNodeId(nodeOrNodes as Node), + }); + return this.objectRegistry.getOrCreateType(data); + } + + async getSignaturesOfType(type: Type, kind: SignatureKind): Promise { + return kind === SignatureKind.Call ? type.getCallSignatures() : type.getConstructSignatures(); + } + + /** + * Get the resolved signature of a call-like expression. Always returns a + * signature; when a call cannot be resolved the checker yields the unknown + * signature (use {@link Checker.isUnknownSignature} to detect it). + */ + async getResolvedSignature(node: Node): Promise { + const data = await this.client.apiRequest("getResolvedSignature", { + snapshot: this.snapshotId, + project: this.project.id, + location: getNodeId(node), + }); + return this.objectRegistry.getOrCreateSignature(data); + } + + getTypeAtPosition(file: DocumentIdentifier, position: number): Promise; + getTypeAtPosition(file: DocumentIdentifier, positions: readonly number[]): Promise<(Type | undefined)[]>; + async getTypeAtPosition(file: DocumentIdentifier, positionOrPositions: number | readonly number[]): Promise { + if (typeof positionOrPositions === "number") { + const data = await this.client.apiRequest("getTypeAtPosition", { + snapshot: this.snapshotId, + project: this.project.id, + file, + position: positionOrPositions, + }); + return data ? this.objectRegistry.getOrCreateType(data) : undefined; + } + const data = await this.client.apiRequest("getTypesAtPositions", { + snapshot: this.snapshotId, + project: this.project.id, + file, + positions: positionOrPositions, + }); + return data.map(d => d ? this.objectRegistry.getOrCreateType(d) : undefined); + } + + async resolveName( + name: string, + meaning: SymbolFlags, + location?: Node | DocumentPosition, + excludeGlobals?: boolean, + ): Promise { + // Distinguish Node (has `kind`) from DocumentPosition (has `document` and `position`) + const isNode = location && "kind" in location; + const data = await this.client.apiRequest("resolveName", { + snapshot: this.snapshotId, + project: this.project.id, + name, + meaning, + ...(isNode ? { location: getNodeId(location as Node) } : {}), + ...(!isNode && location + ? { + file: (location as DocumentPosition).document, + position: (location as DocumentPosition).position, + } + : {}), + ...(excludeGlobals !== undefined ? { excludeGlobals } : {}), + }); + return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined; + } + + /** + * Returns all symbols with the given meaning that are visible at `location`. + */ + async getSymbolsInScope(location: Node | DocumentPosition, meaning: SymbolFlags): Promise { + // Distinguish Node (has `kind`) from DocumentPosition (has `document` and `position`) + const isNode = "kind" in location; + const data = await this.client.apiRequest("getSymbolsInScope", { + snapshot: this.snapshotId, + project: this.project.id, + meaning, + ...(isNode + ? { location: getNodeId(location as Node) } + : { + file: (location as DocumentPosition).document, + position: (location as DocumentPosition).position, + }), + }); + return data ? data.map(d => this.objectRegistry.getOrCreateSymbol(d)) : []; + } + + async getResolvedSymbol(node: Identifier): Promise { + const text = node.text; + if (!text) return undefined; + return this.resolveName(text, SymbolFlags.Value | SymbolFlags.ExportValue, node); + } + + async getContextualType(node: Expression): Promise { + const data = await this.client.apiRequest("getContextualType", { + snapshot: this.snapshotId, + project: this.project.id, + location: getNodeId(node), + }); + return data ? this.objectRegistry.getOrCreateType(data) : undefined; + } + + /** Get the base type of a literal type (e.g. `number` for `42`). Always returns a type. */ + async getBaseTypeOfLiteralType(type: Type): Promise { + const data = await this.client.apiRequest("getBaseTypeOfLiteralType", { + snapshot: this.snapshotId, + project: this.project.id, + type: type.id, + }); + return this.objectRegistry.getOrCreateType(data); + } + + /** Get the type with `null` and `undefined` removed. Always returns a type. */ + async getNonNullableType(type: Type): Promise { + return type.getNonNullableType(); + } + + /** + * Get the type for a type node. Always returns a type; for type nodes whose + * type cannot be determined the checker yields the error type (use + * {@link Type.isErrorType} to detect it). + */ + async getTypeFromTypeNode(node: TypeNode): Promise { + const data = await this.client.apiRequest("getTypeFromTypeNode", { + snapshot: this.snapshotId, + project: this.project.id, + location: getNodeId(node), + }); + return this.objectRegistry.getOrCreateType(data); + } + + /** Get the widened type. Always returns a type. */ + async getWidenedType(type: Type): Promise { + const data = await this.client.apiRequest("getWidenedType", { + snapshot: this.snapshotId, + project: this.project.id, + type: type.id, + }); + return this.objectRegistry.getOrCreateType(data); + } + + /** + * Get the type of the parameter at the given index in a signature. Always + * returns a type; an out-of-range index yields the `any` type. + */ + async getParameterType(signature: Signature, index: number): Promise { + const data = await this.client.apiRequest("getParameterType", { + snapshot: this.snapshotId, + project: this.project.id, + signature: signature.id, + index, + }); + return this.objectRegistry.getOrCreateType(data); + } + + async isArrayLikeType(type: Type): Promise { + return this.client.apiRequest("isArrayLikeType", { + snapshot: this.snapshotId, + project: this.project.id, + type: type.id, + }); + } + + async isTypeAssignableTo(source: Type, target: Type): Promise { + return this.client.apiRequest("isTypeAssignableTo", { + snapshot: this.snapshotId, + project: this.project.id, + source: source.id, + target: target.id, + }); + } + + async getShorthandAssignmentValueSymbol(node: Node): Promise { + const data = await this.client.apiRequest("getShorthandAssignmentValueSymbol", { + snapshot: this.snapshotId, + project: this.project.id, + location: getNodeId(node), + }); + return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined; + } + + /** + * Get the type of a symbol as narrowed at a specific location. Always returns + * a type; for symbols whose type cannot be determined the checker yields the + * error type (use {@link Type.isErrorType} to detect it). + */ + async getTypeOfSymbolAtLocation(symbol: Symbol, location: Node): Promise { + const data = await this.client.apiRequest("getTypeOfSymbolAtLocation", { + snapshot: this.snapshotId, + project: this.project.id, + symbol: symbol.id, + location: getNodeId(location), + }); + return this.objectRegistry.getOrCreateType(data); + } + + private async getIntrinsicType(method: IntrinsicTypeMethod): Promise { + const data = await this.client.apiRequest(method, { + snapshot: this.snapshotId, + project: this.project.id, + }); + return this.objectRegistry.getOrCreateType(data); + } + + async getAnyType(): Promise { + return this.getIntrinsicType("getAnyType"); + } + async getStringType(): Promise { + return this.getIntrinsicType("getStringType"); + } + async getNumberType(): Promise { + return this.getIntrinsicType("getNumberType"); + } + async getBooleanType(): Promise { + return this.getIntrinsicType("getBooleanType"); + } + async getVoidType(): Promise { + return this.getIntrinsicType("getVoidType"); + } + async getUndefinedType(): Promise { + return this.getIntrinsicType("getUndefinedType"); + } + async getNullType(): Promise { + return this.getIntrinsicType("getNullType"); + } + async getNeverType(): Promise { + return this.getIntrinsicType("getNeverType"); + } + async getUnknownType(): Promise { + return this.getIntrinsicType("getUnknownType"); + } + async getBigIntType(): Promise { + return this.getIntrinsicType("getBigIntType"); + } + async getESSymbolType(): Promise { + return this.getIntrinsicType("getESSymbolType"); + } + async getNonPrimitiveType(): Promise { + return this.getIntrinsicType("getNonPrimitiveType"); + } + + async typeToTypeNode(type: Type, enclosingDeclaration?: Node, flags?: number): Promise { + const binaryData = await this.client.apiRequestBinary("typeToTypeNode", { + snapshot: this.snapshotId, + project: this.project.id, + type: type.id, + ...(enclosingDeclaration ? { location: getNodeId(enclosingDeclaration) } : {}), + ...(flags !== undefined ? { flags } : {}), + }); + if (!binaryData) return undefined; + return decodeNode(binaryData) as TypeNode; + } + + async signatureToSignatureDeclaration(signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): Promise { + const binaryData = await this.client.apiRequestBinary("signatureToSignatureDeclaration", { + snapshot: this.snapshotId, + project: this.project.id, + signature: signature.id, + kind, + ...(enclosingDeclaration ? { location: getNodeId(enclosingDeclaration) } : {}), + ...(flags !== undefined ? { flags } : {}), + }); + if (!binaryData) return undefined; + return decodeNode(binaryData) as Node; + } + + async typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): Promise { + const result = await this.client.apiRequest("typeToString", { + snapshot: this.snapshotId, + project: this.project.id, + type: type.id, + ...(enclosingDeclaration ? { location: getNodeId(enclosingDeclaration) } : {}), + ...(flags !== undefined ? { flags } : {}), + }); + if (typeof result !== "string") throw new TypeError("typeToString returned a non-string result"); + return result; + } + + async isContextSensitive(node: Node): Promise { + return this.client.apiRequest("isContextSensitive", { + snapshot: this.snapshotId, + project: this.project.id, + location: getNodeId(node), + }); + } + + async isArrayType(type: Type): Promise { + return this.client.apiRequest("isArrayType", { + snapshot: this.snapshotId, + project: this.project.id, + type: type.id, + }); + } + + async isTupleType(type: Type): Promise { + return this.client.apiRequest("isTupleType", { + snapshot: this.snapshotId, + project: this.project.id, + type: type.id, + }); + } + + /** + * The following symbols are considered read-only: + * - Properties with a `readonly` modifier + * - Variables declared with `const` + * - Get accessors without matching set accessors + * - Enum members + * - `Object.defineProperty` assignments with `writable: false` or no setter + * - Unions and intersections of the above + */ + async isReadonlySymbol(symbol: Symbol): Promise { + return this.client.apiRequest("isReadonlySymbol", { + snapshot: this.snapshotId, + project: this.project.id, + symbol: symbol.id, + }); + } + + /** Get the return type of a signature. Always returns a type. */ + async getReturnTypeOfSignature(signature: Signature): Promise { + return signature.getReturnType(); + } + + /** + * Get the rest type of a signature. Always returns a type; a signature with + * no rest parameter yields the `any` type. + */ + async getRestTypeOfSignature(signature: Signature): Promise { + const data = await this.client.apiRequest("getRestTypeOfSignature", { + snapshot: this.snapshotId, + project: this.project.id, + signature: signature.id, + }); + return this.objectRegistry.getOrCreateType(data); + } + + async getTypePredicateOfSignature(signature: Signature): Promise { + const data = await this.client.apiRequest("getTypePredicateOfSignature", { + snapshot: this.snapshotId, + project: this.project.id, + signature: signature.id, + }); + if (!data) return undefined; + return { + kind: data.kind, + parameterIndex: data.parameterIndex, + parameterName: data.parameterName, + type: data.type ? this.objectRegistry.getOrCreateType(data.type) : undefined, + } as TypePredicate; + } + + /** + * Get the base types of a class or interface type. A type with no base types + * yields an empty array. + */ + async getBaseTypes(type: InterfaceType): Promise { + return await type.getBaseTypes() ?? []; + } + + /** Get the apparent type of a type. Always returns a type. */ + async getApparentType(type: Type): Promise { + return type.getApparentType(); + } + + /** Get the reduced type of a type. Always returns a type. */ + async getReducedType(type: Type): Promise { + return type.getReducedType(); + } + + async getPropertiesOfType(type: Type): Promise { + return type.getProperties(); + } + + async getIndexInfosOfType(type: Type): Promise { + return type.getIndexInfos(); + } + + /** + * Get the constraint of a type parameter (the `T` in ``), or + * undefined if it has none. + */ + async getConstraintOfTypeParameter(type: TypeParameter): Promise { + return type.getConstraint(); + } + + async getDefaultFromTypeParameter(type: TypeParameter): Promise { + return type.getDefault(); + } + + async getBaseConstraintOfType(type: Type): Promise { + const data = await this.client.apiRequest("getBaseConstraintOfType", { + snapshot: this.snapshotId, + project: this.project.id, + type: type.id, + }); + return data ? this.objectRegistry.getOrCreateType(data) : undefined; + } + + async getPropertyOfType(type: Type, name: string): Promise { + const data = await this.client.apiRequest("getPropertyOfType", { + snapshot: this.snapshotId, + project: this.project.id, + type: type.id, + name, + }); + return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined; + } + + async getConstantValue(node: Node): Promise { + const data = await this.client.apiRequest("getConstantValue", { + snapshot: this.snapshotId, + project: this.project.id, + location: getNodeId(node), + }); + return typeof data === "string" || typeof data === "number" ? data : undefined; + } + + /** Get the signature of a function-like declaration. Always returns a signature. */ + async getSignatureFromDeclaration(node: Node): Promise { + const data = await this.client.apiRequest("getSignatureFromDeclaration", { + snapshot: this.snapshotId, + project: this.project.id, + location: getNodeId(node), + }); + return this.objectRegistry.getOrCreateSignature(data); + } + + async getExportSpecifierLocalTargetSymbol(node: Node): Promise { + const data = await this.client.apiRequest("getExportSpecifierLocalTargetSymbol", { + snapshot: this.snapshotId, + project: this.project.id, + location: getNodeId(node), + }); + return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined; + } + + /** + * Follow all aliases to get the original symbol. Always returns a symbol; for + * an unresolved alias the checker yields the unknown symbol (use + * {@link Checker.isUnknownSymbol} to detect it). + */ + async getAliasedSymbol(symbol: Symbol): Promise { + const data = await this.client.apiRequest("getAliasedSymbol", { + snapshot: this.snapshotId, + project: this.project.id, + symbol: symbol.id, + }); + return this.objectRegistry.getOrCreateSymbol(data); + } + + /** + * Get the fully qualified name of a symbol, walking up its parent chain + * (e.g. `"/path/to/module".Namespace.Name`). + */ + async getFullyQualifiedName(symbol: Symbol): Promise { + return this.client.apiRequest("getFullyQualifiedName", { + snapshot: this.snapshotId, + project: this.project.id, + symbol: symbol.id, + }); + } + + async getImmediateAliasedSymbol(symbol: Symbol): Promise { + const data = await this.client.apiRequest("getImmediateAliasedSymbol", { + snapshot: this.snapshotId, + project: this.project.id, + symbol: symbol.id, + }); + return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined; + } + + /** + * Get the target symbol if instantiated, or the provided symbol otherwise. + */ + async getTargetSymbol(symbol: Symbol): Promise { + if (symbol.checkFlags & CheckFlags.Instantiated) { + const data = await this.client.apiRequest("getTargetSymbol", { + snapshot: this.snapshotId, + project: this.project.id, + symbol: symbol.id, + }); + return this.objectRegistry.getOrCreateSymbol(data); + } + return symbol; + } + + /** + * Fetch (once, then cache) the handle ids of the per-checker singleton + * symbols (unknown, undefined, arguments). These ids are stable for the life + * of the project's checker, so identity checks against them are local after + * the first call. + */ + private getWellKnownSymbols(): Promise<{ unknown: number; undefined: number; arguments: number; }> { + return this.wellKnownSymbols ??= this.client.apiRequest("getWellKnownSymbols", { + snapshot: this.snapshotId, + project: this.project.id, + }); + } + + /** + * Returns `true` if the symbol is the checker's "unknown" symbol (e.g. the + * result of {@link Checker.getAliasedSymbol} on an unresolved alias). + */ + async isUnknownSymbol(symbol: Symbol): Promise { + return symbol.id === (await this.getWellKnownSymbols()).unknown; + } + + /** + * Returns `true` if the symbol is the checker's "undefined" symbol. + */ + async isUndefinedSymbol(symbol: Symbol): Promise { + return symbol.id === (await this.getWellKnownSymbols()).undefined; + } + + /** + * Returns `true` if the symbol is the checker's "arguments" symbol. + */ + async isArgumentsSymbol(symbol: Symbol): Promise { + return symbol.id === (await this.getWellKnownSymbols()).arguments; + } + + /** + * Fetch (once, then cache) the handle id of the per-checker unknown + * signature. This id is stable for the life of the project's checker, so + * identity checks against it are local after the first call. + */ + private getWellKnownSignatures(): Promise<{ unknown: number; }> { + return this.wellKnownSignatures ??= this.client.apiRequest("getWellKnownSignatures", { + snapshot: this.snapshotId, + project: this.project.id, + }); + } + + /** + * Returns `true` if the signature is the checker's "unknown" signature (e.g. + * the result of {@link Checker.getResolvedSignature} on a call that cannot be + * resolved). + */ + async isUnknownSignature(signature: Signature): Promise { + return signature.id === (await this.getWellKnownSignatures()).unknown; + } + + async getExportsOfModule(symbol: Symbol): Promise { + const data = await this.client.apiRequest("getExportsOfModule", { + snapshot: this.snapshotId, + project: this.project.id, + symbol: symbol.id, + }); + return data ? data.map(d => this.objectRegistry.getOrCreateSymbol(d)) : []; + } + + async getMemberInModuleExports(symbol: Symbol, name: string): Promise { + const data = await this.client.apiRequest("getMemberInModuleExports", { + snapshot: this.snapshotId, + project: this.project.id, + symbol: symbol.id, + name, + }); + return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined; + } + + async getJsDocTagsOfSymbol(symbol: Symbol): Promise { + const data = await this.client.apiRequest("getJsDocTags", { + snapshot: this.snapshotId, + project: this.project.id, + symbol: symbol.id, + }); + return data ?? []; + } + + async getDocumentationCommentOfSymbol(symbol: Symbol): Promise { + return this.client.apiRequest("getDocumentationComment", { + snapshot: this.snapshotId, + project: this.project.id, + symbol: symbol.id, + }); + } + + /** + * Get the type arguments of a type reference (e.g. the `string` in `Array`). + */ + async getTypeArguments(type: TypeReference): Promise { + const data = await this.client.apiRequest("getTypeArguments", { + snapshot: this.snapshotId, + project: this.project.id, + type: type.id, + }); + return data ? data.map(d => this.objectRegistry.getOrCreateType(d)) : []; + } +} + +export interface PrintNodeOptions { + preserveSourceNewlines?: boolean | undefined; + neverAsciiEscape?: boolean | undefined; + terminateUnterminatedLiterals?: boolean | undefined; +} + +export class Emitter { + private client: Client; + + constructor(client: Client) { + this.client = client; + } + + async printNode(node: Node, options: PrintNodeOptions = {}): Promise { + const encoded = encodeNode(node); + const base64 = uint8ArrayToBase64(encoded); + return this.client.apiRequest("printNode", { + data: base64, + ...(options.preserveSourceNewlines !== undefined ? { preserveSourceNewlines: options.preserveSourceNewlines } : {}), + ...(options.neverAsciiEscape !== undefined ? { neverAsciiEscape: options.neverAsciiEscape } : {}), + ...(options.terminateUnterminatedLiterals !== undefined ? { terminateUnterminatedLiterals: options.terminateUnterminatedLiterals } : {}), + }); + } +} + +export class SnapshotInternalAPI { + private snapshotId: number; + private client: Client; + + constructor(snapshotId: number, client: Client) { + this.snapshotId = snapshotId; + this.client = client; + } + + /** + * Format a synthesized node with the correct indentation for insertion at a + * specific position in an existing source file. + * + * @param node The synthesized AST node to format. + * @param file The target file where the node will be inserted. + * @param position The UTF-16 code-unit offset in the target file for insertion. + * @returns The formatted text of the node, indented for the insertion position. + */ + async formatNodeForInsertion(node: Node, file: DocumentIdentifier, position: number): Promise { + const data = await this.client.apiRequest("getDefaultProjectForFile", { + snapshot: this.snapshotId, + file, + }); + if (!data) { + throw new Error(`No project found for file: ${typeof file === "string" ? file : file.uri}`); + } + + const encoded = encodeNode(node); + const base64 = uint8ArrayToBase64(encoded); + return this.client.apiRequest("formatNodeForInsertion", { + snapshot: this.snapshotId, + project: data.id, + file, + position, + data: base64, + }); + } +} + +export class NodeHandle { + /** + * The project this handle was produced in, used as the default for {@link resolve}. + * Node handles are only meaningful within a project's program, so the producing project + * is remembered so callers don't have to pass it explicitly. + */ + private readonly canonicalProject: Project; + readonly index: number; + readonly kind: SyntaxKind; + readonly path: Path; + + constructor(handle: string, canonicalProject: Project) { + const parsed = parseNodeHandle(handle); + this.index = parsed.index; + this.kind = parsed.kind; + this.path = parsed.path; + this.canonicalProject = canonicalProject; + } + + /** + * Resolve this handle to the actual AST node by fetching the source file from a project + * and looking up the node by index. If no project is passed, the project that produced + * the handle is used. + */ + async resolve(project: Project = this.canonicalProject): Promise { + const sourceFile = await project.program.getSourceFile(this.path); + if (!sourceFile) { + return undefined; + } + return (sourceFile as unknown as RemoteSourceFile).getOrCreateNodeAtIndex(this.index) as T | undefined; + } +} + +/** A symbol definition paired with all of its reference nodes. */ +export interface ReferencedSymbolEntry { + /** The node handle for the symbol's definition. */ + definition: NodeHandle; + /** The resolved symbol for the definition, if available. */ + symbol?: Symbol | undefined; + /** The node handles for each reference to the symbol. */ + references: NodeHandle[]; +} + +/** A single usage of a signature, pairing the reference name with its call expression (if any). */ +export interface SignatureUsage { + /** The node handle for the name reference. */ + name: NodeHandle; + /** The node handle for the call expression, if the reference is invoked. */ + call?: NodeHandle | undefined; +} + +export class Symbol { + private objectRegistry: SnapshotObjectRegistry; + /** + * The project this symbol was first observed in, used as the default project for + * lookups that need a project context (members/exports/parent). Symbols are shared + * snapshot-wide, so these lookups can otherwise be ambiguous about which project to use. + */ + private readonly canonicalProject: Project; + + readonly id: number; + /** The escaped (`__String`) name, used as the key in member/export tables. */ + readonly escapedName: __String; + /** The display name (escaped underscores removed). */ + readonly name: string; + readonly flags: SymbolFlags; + readonly checkFlags: CheckFlags; + readonly declarations: readonly NodeHandle[]; + readonly valueDeclaration: NodeHandle | undefined; + private readonly parent!: number; + private readonly exportSymbol!: number; + private membersCache: Promise> | undefined; + private exportsCache: Promise> | undefined; + + constructor(data: SymbolResponse, objectRegistry: SnapshotObjectRegistry) { + this.objectRegistry = objectRegistry; + + this.id = data.id; + this.escapedName = data.name as __String; + this.name = unescapeLeadingUnderscores(data.name as __String); + this.flags = data.flags; + this.checkFlags = data.checkFlags; + const canonicalProject = objectRegistry.getProject(data.project as Path); + if (!canonicalProject) { + throw new Error(`Symbol ${data.id} references unknown canonical project '${data.project}'`); + } + this.canonicalProject = canonicalProject; + this.declarations = (data.declarations ?? []).map(d => new NodeHandle(d, canonicalProject)); + this.valueDeclaration = data.valueDeclaration ? new NodeHandle(data.valueDeclaration, canonicalProject) : undefined; + + if (data.parent !== undefined) this.parent = data.parent; + if (data.exportSymbol !== undefined) this.exportSymbol = data.exportSymbol; + } + + async getParent(): Promise { + return this.objectRegistry.fetchSymbol(this, "getParentOfSymbol", this.parent, this.canonicalProject.id); + } + + /** + * Get this symbol's members keyed by escaped name. The result is cached on + * the symbol, so repeated calls do not round-trip to the server. + */ + getMembers(): Promise> { + return this.membersCache ??= this.fetchSymbolTable("getMembersOfSymbol"); + } + + /** + * Get this symbol's exports keyed by escaped name. The result is cached on + * the symbol, so repeated calls do not round-trip to the server. + */ + getExports(): Promise> { + return this.exportsCache ??= this.fetchSymbolTable("getExportsOfSymbol"); + } + + private async fetchSymbolTable(method: SymbolsPropertyMethod): Promise> { + const symbols = await this.objectRegistry.fetchSymbols(this, method, undefined, this.canonicalProject.id); + const table = new Map<__String, Symbol>(); + for (const symbol of symbols) { + table.set(symbol.escapedName, symbol); + } + return table; + } + + async getExportSymbol(): Promise { + if (!this.exportSymbol) return this; + return this.objectRegistry.fetchSymbol(this, "getExportSymbolOfSymbol", this.exportSymbol, this.canonicalProject.id); + } + + async getJsDocTags(checker: Checker): Promise { + return checker.getJsDocTagsOfSymbol(this); + } + + async getDocumentationComment(checker: Checker): Promise { + return checker.getDocumentationCommentOfSymbol(this); + } +} + +class TypeObject implements Type { + private objectRegistry: ProjectObjectRegistry; + + // Fields included in TypeResponse. References to other objects are stored as IDs + // and resolved lazily via the object registry. + readonly id: number; + readonly flags: TypeFlags; + readonly objectFlags!: ObjectFlags; + readonly symbol!: number; + readonly value!: string | number | boolean | bigint; + readonly intrinsicName!: string; + readonly isThisType!: boolean; + readonly freshType!: number; + readonly regularType!: number; + readonly target!: number; + readonly typeParameters!: readonly number[]; + readonly outerTypeParameters!: readonly number[]; + readonly localTypeParameters!: readonly number[]; + readonly aliasTypeArguments!: readonly number[]; + readonly aliasSymbol!: number; + readonly elementFlags!: readonly ElementFlags[]; + readonly fixedLength!: number; + readonly readonly!: boolean; + readonly texts!: readonly string[]; + readonly objectType!: number; + readonly indexType!: number; + readonly checkType!: number; + readonly extendsType!: number; + readonly baseType!: number; + readonly substConstraint!: number; + + // Cached results of lazy fetches, not included in TypeResponse + // (typically because they require some amount of computation or + // could cause an arbitrarily large number of types to be cached + // on the server for ID-based lookup). `false` is a sentinel value + // indicating a fetch has not yet occurred. + private trueType: number | false; + private falseType: number | false; + private constraint: number | false; + private default: number | false; + private nonNullableType: number | false; + private apparentType: number | false; + private reducedType: number | false; + private properties: readonly Symbol[] | false; + private apparentProperties: readonly Symbol[] | false; + private callSignatures: readonly Signature[] | false; + private constructSignatures: readonly Signature[] | false; + private indexInfos: readonly IndexInfo[] | false; + private baseTypes: readonly Type[] | false; + private stringIndexType: Type | undefined | false; + private numberIndexType: Type | undefined | false; + + constructor(data: TypeResponse, objectRegistry: ProjectObjectRegistry) { + this.objectRegistry = objectRegistry; + + this.id = data.id; + this.flags = data.flags; + if (data.objectFlags !== undefined) this.objectFlags = data.objectFlags; + if (data.symbol !== undefined) this.symbol = data.symbol; + if (data.value != null) { + // BigInt literal values are serialized as decimal strings (e.g. "-123") because + // JSON cannot represent bigint. Decode them back into a real bigint here. + const value = data.value as string | number | boolean; + this.value = (data.flags & TypeFlags.BigIntLiteral) ? BigInt(value as string) : value; + } + if (data.intrinsicName !== undefined) this.intrinsicName = data.intrinsicName; + if (data.isThisType !== undefined) this.isThisType = data.isThisType; + if (data.freshType !== undefined) this.freshType = data.freshType; + if (data.regularType !== undefined) this.regularType = data.regularType; + if (data.target !== undefined) this.target = data.target; + this.typeParameters = data.typeParameters ?? []; + this.outerTypeParameters = data.outerTypeParameters ?? []; + this.localTypeParameters = data.localTypeParameters ?? []; + this.aliasTypeArguments = data.aliasTypeArguments ?? []; + if (data.aliasSymbol !== undefined) this.aliasSymbol = data.aliasSymbol; + if (data.elementFlags !== undefined) this.elementFlags = data.elementFlags; + if (data.fixedLength !== undefined) this.fixedLength = data.fixedLength; + if (data.readonly !== undefined) this.readonly = data.readonly; + if (data.texts !== undefined) this.texts = data.texts; + if (data.objectType !== undefined) this.objectType = data.objectType; + if (data.indexType !== undefined) this.indexType = data.indexType; + if (data.checkType !== undefined) this.checkType = data.checkType; + if (data.extendsType !== undefined) this.extendsType = data.extendsType; + if (data.baseType !== undefined) this.baseType = data.baseType; + if (data.substConstraint !== undefined) this.substConstraint = data.substConstraint; + + this.trueType = false; + this.falseType = false; + this.constraint = false; + this.default = false; + this.nonNullableType = false; + this.apparentType = false; + this.reducedType = false; + this.properties = false; + this.apparentProperties = false; + this.callSignatures = false; + this.constructSignatures = false; + this.indexInfos = false; + this.baseTypes = false; + this.stringIndexType = false; + this.numberIndexType = false; + } + + async getSymbol(): Promise { + return this.objectRegistry.fetchSymbol(this, "getSymbolOfType", this.symbol); + } + + async getProperties(): Promise { + if (this.properties === false) { + this.properties = await this.objectRegistry.fetchPropertiesOfType(this); + } + return this.properties; + } + + getProperty(propertyName: string): Promise { + return this.objectRegistry.fetchPropertyOfType(this, propertyName); + } + + async getApparentProperties(): Promise { + if (this.apparentProperties === false) { + this.apparentProperties = await this.objectRegistry.fetchApparentPropertiesOfType(this); + } + return this.apparentProperties; + } + + async getCallSignatures(): Promise { + if (this.callSignatures === false) { + this.callSignatures = await this.objectRegistry.fetchSignaturesOfType(this, SignatureKind.Call); + } + return this.callSignatures; + } + + async getConstructSignatures(): Promise { + if (this.constructSignatures === false) { + this.constructSignatures = await this.objectRegistry.fetchSignaturesOfType(this, SignatureKind.Construct); + } + return this.constructSignatures; + } + + async getNonNullableType(): Promise { + const result = await this.objectRegistry.fetchType(this, "getNonNullableType", this.nonNullableType); + this.nonNullableType = result.id; + return result; + } + + async getStringIndexType(): Promise { + if (this.stringIndexType === false) { + this.stringIndexType = await this.getStringIndexTypeWorker(); + } + return this.stringIndexType; + } + + private async getStringIndexTypeWorker(): Promise { + const infos = await this.getIndexInfos(); + return infos.find(info => (info.keyType.flags & TypeFlags.String) !== 0)?.valueType; + } + + async getNumberIndexType(): Promise { + if (this.numberIndexType === false) { + this.numberIndexType = await this.getNumberIndexTypeWorker(); + } + return this.numberIndexType; + } + + private async getNumberIndexTypeWorker(): Promise { + const infos = await this.getIndexInfos(); + return infos.find(info => (info.keyType.flags & TypeFlags.Number) !== 0)?.valueType; + } + + async getApparentType(): Promise { + const result = await this.objectRegistry.fetchType(this, "getApparentType", this.apparentType); + this.apparentType = result.id; + return result; + } + + async getReducedType(): Promise { + const result = await this.objectRegistry.fetchType(this, "getReducedType", this.reducedType); + this.reducedType = result.id; + return result; + } + + async getIndexInfos(): Promise { + if (this.indexInfos === false) { + this.indexInfos = await this.objectRegistry.fetchIndexInfosOfType(this); + } + return this.indexInfos; + } + + async getAliasSymbol(): Promise { + return this.objectRegistry.fetchSymbol(this, "getAliasSymbolOfType", this.aliasSymbol); + } + + async getTarget(): Promise { + return this.objectRegistry.fetchType(this, "getTargetOfType", this.target); + } + + async getFreshType(): Promise { + return this.objectRegistry.fetchOptionalType(this, "getFreshTypeOfType", this.freshType); + } + + async getRegularType(): Promise { + return this.objectRegistry.fetchOptionalType(this, "getRegularTypeOfType", this.regularType); + } + + async getTypes(): Promise { + // Only union, intersection, and template literal types have constituent + // types; any other kind has none, so return undefined rather than sending + // a request the server cannot satisfy. + if (!(this.flags & (TypeFlags.UnionOrIntersection | TypeFlags.TemplateLiteral))) { + return undefined; + } + return this.objectRegistry.fetchTypes(this, "getTypesOfType"); + } + + async getTypeParameters(): Promise { + return this.objectRegistry.fetchTypes(this, "getTypeParametersOfType", this.typeParameters) as Promise; + } + + async getOuterTypeParameters(): Promise { + return this.objectRegistry.fetchTypes(this, "getOuterTypeParametersOfType", this.outerTypeParameters) as Promise; + } + + async getLocalTypeParameters(): Promise { + return this.objectRegistry.fetchTypes(this, "getLocalTypeParametersOfType", this.localTypeParameters) as Promise; + } + + async getAliasTypeArguments(): Promise { + return this.objectRegistry.fetchTypes(this, "getAliasTypeArgumentsOfType", this.aliasTypeArguments); + } + + async getObjectType(): Promise { + return this.objectRegistry.fetchType(this, "getObjectTypeOfType", this.objectType); + } + + async getIndexType(): Promise { + return this.objectRegistry.fetchType(this, "getIndexTypeOfType", this.indexType); + } + + async getCheckType(): Promise { + return this.objectRegistry.fetchType(this, "getCheckTypeOfType", this.checkType); + } + + async getExtendsType(): Promise { + return this.objectRegistry.fetchType(this, "getExtendsTypeOfType", this.extendsType); + } + + async getBaseType(): Promise { + return this.objectRegistry.fetchType(this, "getBaseTypeOfType", this.baseType); + } + + async getConstraint(): Promise { + // Type parameters resolve their constraint lazily through the checker, + // whereas substitution types carry a preloaded constraint handle. + if (this.flags & TypeFlags.TypeParameter) { + const result = await this.objectRegistry.fetchOptionalType(this, "getConstraintOfTypeParameter", this.constraint); + this.constraint = result ? result.id : 0; + return result; + } + return this.objectRegistry.fetchType(this, "getConstraintOfType", this.substConstraint); + } + + async getDefault(): Promise { + const result = await this.objectRegistry.fetchOptionalType(this, "getDefaultFromTypeParameter", this.default); + this.default = result ? result.id : 0; + return result; + } + + async getTrueType(): Promise { + const result = await this.objectRegistry.fetchType(this, "getTrueTypeOfConditionalType", this.trueType); + this.trueType = result.id; + return result; + } + + async getFalseType(): Promise { + const result = await this.objectRegistry.fetchType(this, "getFalseTypeOfConditionalType", this.falseType); + this.falseType = result.id; + return result; + } + + /** + * Get the base types of this type. Returns `undefined` for any type that is + * not a class or interface. + */ + async getBaseTypes(): Promise { + if (!this.isClassOrInterface()) { + return undefined; + } + if (this.baseTypes === false) { + this.baseTypes = await this.objectRegistry.fetchBaseTypes(this); + } + return this.baseTypes; + } + + isClassOrInterface(): this is InterfaceType { + return isClassOrInterfaceType(this); + } + + isUnionType(): this is UnionType { + return isUnionType(this); + } + + isIntersectionType(): this is IntersectionType { + return isIntersectionType(this); + } + + isObjectType(): this is ObjectType { + return isObjectType(this); + } + + isIntrinsicType(): this is IntrinsicType { + return isIntrinsicType(this); + } + + isErrorType(): boolean { + return isErrorType(this); + } + + isLiteralType(): this is LiteralType { + return isLiteralType(this); + } + + isStringLiteralType(): this is StringLiteralType { + return isStringLiteralType(this); + } + + isNumberLiteralType(): this is NumberLiteralType { + return isNumberLiteralType(this); + } + + isBigIntLiteralType(): this is BigIntLiteralType { + return isBigIntLiteralType(this); + } + + isBooleanLiteralType(): this is BooleanLiteralType { + return isBooleanLiteralType(this); + } + + isTypeReference(): this is TypeReference { + return isTypeReference(this); + } + + isTupleType(): this is TupleType { + return isTupleType(this); + } + + isIndexType(): this is IndexType { + return isIndexType(this); + } + + isIndexedAccessType(): this is IndexedAccessType { + return isIndexedAccessType(this); + } + + isConditionalType(): this is ConditionalType { + return isConditionalType(this); + } + + isSubstitutionType(): this is SubstitutionType { + return isSubstitutionType(this); + } + + isTemplateLiteralType(): this is TemplateLiteralType { + return isTemplateLiteralType(this); + } + + isStringMappingType(): this is StringMappingType { + return isStringMappingType(this); + } + + isTypeParameter(): this is TypeParameter { + return isTypeParameter(this); + } +} + +export function isUnionType(type: Type): type is UnionType { + return (type.flags & TypeFlags.Union) !== 0; +} + +export function isIntersectionType(type: Type): type is IntersectionType { + return (type.flags & TypeFlags.Intersection) !== 0; +} + +export function isObjectType(type: Type): type is ObjectType { + return (type.flags & TypeFlags.Object) !== 0; +} + +export function isClassOrInterfaceType(type: Type): type is InterfaceType { + return isObjectType(type) && (type.objectFlags & ObjectFlags.ClassOrInterface) !== 0; +} + +export function isIntrinsicType(type: Type): type is IntrinsicType { + return (type.flags & TypeFlags.Intrinsic) !== 0; +} + +/** + * Whether this is the error type — the placeholder the checker produces when a + * type cannot be determined (e.g. an unresolved reference). It is an intrinsic + * type named `"error"` (this covers both the singleton error type and the + * per-alias error types manufactured for unresolved type alias references). + */ +export function isErrorType(type: Type): boolean { + return isIntrinsicType(type) && type.intrinsicName === "error"; +} + +export function isLiteralType(type: Type): type is LiteralType { + return (type.flags & TypeFlags.Literal) !== 0; +} + +export function isStringLiteralType(type: Type): type is StringLiteralType { + return (type.flags & TypeFlags.StringLiteral) !== 0; +} + +export function isNumberLiteralType(type: Type): type is NumberLiteralType { + return (type.flags & TypeFlags.NumberLiteral) !== 0; +} + +export function isBigIntLiteralType(type: Type): type is BigIntLiteralType { + return (type.flags & TypeFlags.BigIntLiteral) !== 0; +} + +export function isBooleanLiteralType(type: Type): type is BooleanLiteralType { + return (type.flags & TypeFlags.BooleanLiteral) !== 0; +} + +export function isTypeReference(type: Type): type is TypeReference { + return isObjectType(type) && (type.objectFlags & ObjectFlags.Reference) !== 0; +} + +export function isTupleType(type: Type): type is TupleType { + return isObjectType(type) && (type.objectFlags & ObjectFlags.Tuple) !== 0; +} + +export function isIndexType(type: Type): type is IndexType { + return (type.flags & TypeFlags.Index) !== 0; +} + +export function isIndexedAccessType(type: Type): type is IndexedAccessType { + return (type.flags & TypeFlags.IndexedAccess) !== 0; +} + +export function isConditionalType(type: Type): type is ConditionalType { + return (type.flags & TypeFlags.Conditional) !== 0; +} + +export function isSubstitutionType(type: Type): type is SubstitutionType { + return (type.flags & TypeFlags.Substitution) !== 0; +} + +export function isTemplateLiteralType(type: Type): type is TemplateLiteralType { + return (type.flags & TypeFlags.TemplateLiteral) !== 0; +} + +export function isStringMappingType(type: Type): type is StringMappingType { + return (type.flags & TypeFlags.StringMapping) !== 0; +} + +export function isTypeParameter(type: Type): type is TypeParameter { + return (type.flags & TypeFlags.TypeParameter) !== 0; +} + +export class Signature { + private flags: number; + private objectRegistry: ProjectObjectRegistry; + + readonly id: number; + readonly declaration?: NodeHandle | undefined; + readonly typeParameters?: readonly number[] | undefined; + readonly parameters: readonly number[]; + readonly thisParameter?: number | undefined; + readonly target?: number | undefined; + private returnType: number | false; + + constructor(data: SignatureResponse, project: Project, objectRegistry: ProjectObjectRegistry) { + this.id = data.id; + this.flags = data.flags; + this.objectRegistry = objectRegistry; + this.declaration = data.declaration ? new NodeHandle(data.declaration, project) : undefined; + this.typeParameters = data.typeParameters ?? []; + this.parameters = data.parameters ?? []; + this.thisParameter = data.thisParameter; + this.target = data.target; + this.returnType = false; + } + + async getTypeParameters(): Promise { + return this.objectRegistry.fetchTypes(this, "getTypeParametersOfSignature", this.typeParameters) as Promise; + } + + async getParameters(): Promise { + return this.objectRegistry.fetchSymbols(this, "getParametersOfSignature", this.parameters); + } + + async getThisParameter(): Promise { + return this.objectRegistry.fetchSymbol(this, "getThisParameterOfSignature", this.thisParameter); + } + + async getTarget(): Promise { + return this.objectRegistry.fetchSignature(this, "getTargetOfSignature", this.target); + } + + async getReturnType(): Promise { + const result = await this.objectRegistry.fetchType(this, "getReturnTypeOfSignature", this.returnType); + this.returnType = result.id; + return result; + } + + getTypeParameterAtPosition(pos: number): Promise { + return this.objectRegistry.fetchTypeParameterAtPosition(this, pos); + } + + get hasRestParameter(): boolean { + return (this.flags & SignatureFlags.HasRestParameter) !== 0; + } + + get isConstruct(): boolean { + return (this.flags & SignatureFlags.Construct) !== 0; + } + + get isAbstract(): boolean { + return (this.flags & SignatureFlags.Abstract) !== 0; + } +} diff --git a/packages/typescript/src/api/async/client.ts b/packages/typescript/src/api/async/client.ts index 5ed5397cf55cd..2e0d79d93fdc6 100644 --- a/packages/typescript/src/api/async/client.ts +++ b/packages/typescript/src/api/async/client.ts @@ -1,345 +1,345 @@ -import { - createMessageConnection, - type MessageConnection, - RequestType, - SocketMessageReader, - SocketMessageWriter, - StreamMessageReader, - StreamMessageWriter, -} from "#vscode-jsonrpc/node"; -import type { ChildProcess } from "node:child_process"; -import type { Socket } from "node:net"; -import { - type FileSystem, - fsCallbackNames, -} from "../fs.ts"; -import { - type ClientOptions, - type ClientSocketOptions, - type ClientSpawnOptions, - getAPIProcessArgs, - isSpawnOptions, - resolveExePath, -} from "../options.ts"; -import type { - APIMethodInfo, - APIRequest, - BatchRequestsParams, - BatchRequestsResponse, - SourceFileResponseMethod, -} from "../proto.ts"; -import { - combineTimingInfo, - disabledServerTimingInfo, - disabledTimingInfo, - type ServerTimingInfo, - TimingCollector, - type TimingInfo, -} from "../timing.ts"; - -export type { ClientOptions, ClientSocketOptions, ClientSpawnOptions }; - -/** - * Client handles communication with the TypeScript API server - * over STDIO (spawned process) or a Unix domain socket using JSON-RPC. - */ -export class Client { - private socket: Socket | undefined; - private process: ChildProcess | undefined; - private connection: MessageConnection | undefined; - private options: ClientOptions; - private connected = false; - private timing: TimingCollector | undefined; - private batchedRequests: { method: APIRequest["method"]; params: APIRequest["params"]; resolve: (value: unknown) => void; reject: (reason?: any) => void; }[] = []; - private nextBatch: NodeJS.Immediate | "manual" | undefined; - - constructor(options: ClientOptions) { - this.options = options; - if (isSpawnOptions(options) && options.collectTiming) { - this.timing = new TimingCollector(); - } - } - - async connect(): Promise { - if (this.connected) return; - - if (isSpawnOptions(this.options)) { - await this.connectViaSpawn(this.options); - } - else { - await this.connectViaSocket(this.options); - } - } - - private async connectViaSpawn(options: ClientSpawnOptions): Promise { - const { spawn } = await import("node:child_process"); - - return new Promise((resolve, reject) => { - const args = getAPIProcessArgs(options, true); - - // Enable virtual FS callbacks for each provided FS function - const enabledCallbacks: string[] = []; - if (options.fs) { - for (const name of fsCallbackNames) { - if (options.fs[name]) { - enabledCallbacks.push(name); - } - } - } - if (enabledCallbacks.length > 0) { - args.push(`--callbacks=${enabledCallbacks.join(",")}`); - } - - this.process = spawn(resolveExePath(options), args, { - stdio: ["pipe", "pipe", "inherit"], - }); - - this.process.once("error", error => { - reject(new Error(`Failed to start tsgo process: ${error.message}`)); - }); - - this.process.once("spawn", () => { - this.connected = true; - resolve(); - }); - - const reader = new StreamMessageReader(this.process.stdout!); - const writer = new StreamMessageWriter(this.process.stdin!); - this.connection = createMessageConnection(reader, writer); - this.registerFSCallbacks(this.connection, options.fs); - this.connection.listen(); - }); - } - - private async connectViaSocket(options: ClientSocketOptions): Promise { - const { createConnection } = await import("node:net"); - - return new Promise((resolve, reject) => { - this.socket = createConnection(options.pipe, () => { - const reader = new SocketMessageReader(this.socket!); - const writer = new SocketMessageWriter(this.socket!); - this.connection = createMessageConnection(reader, writer); - this.connection.listen(); - this.connected = true; - resolve(); - }); - - this.socket.once("error", error => { - reject(new Error(`Socket error: ${error.message}`)); - }); - }); - } - - private registerFSCallbacks(connection: MessageConnection, fs: FileSystem | undefined): void { - if (!fs) return; - for (const name of fsCallbackNames) { - if (name === "writeFile") { - if (!fs.writeFile) continue; - const callback = fs.writeFile; - - const requestType = new RequestType<{ path: string; data: string; }, unknown, void>(name); - connection.onRequest(requestType, (arg: { path: string; data: string; }) => { - callback(arg.path, arg.data); - return null; - }); - - continue; - } - - const callback = fs[name]; - if (callback) { - const requestType = new RequestType(name); - connection.onRequest(requestType, (arg: unknown) => { - const result = callback(arg as any); - if (name === "readFile") { - // readFile has 3 returns: string (content), null (not found), undefined (fall back). - // JSON-RPC can't distinguish null from undefined, so wrap in object. - if (result === undefined) return null; - return { content: result }; - } - return result ?? null; - }); - } - } - } - - private async sendRequestWithTiming(requestType: RequestType, params: unknown): Promise { - if (!this.connection) { - throw new Error("Connection not established"); - } - - if (!this.timing) { - return this.connection.sendRequest(requestType, params); - } - - // Round-trip latency is measured here; byte counts approximate the wire - // payload via the serialized JSON. Server-side processing time is not - // carried on the response; it is retrieved separately (via a - // getServerTiming request) and folded in by getTimingInfo(). - const bytesSent = params === undefined ? 0 : Buffer.byteLength(JSON.stringify(params), "utf-8"); - const start = performance.now(); - const result = await this.connection.sendRequest(requestType, params); - const roundTripMs = performance.now() - start; - this.timing.record({ - method: requestType.method, - roundTripMs, - bytesSent, - bytesReceived: result === undefined || result === null - ? 0 - : Buffer.byteLength(JSON.stringify(result), "utf-8"), - }); - return result; - } - - private async doBatch(): Promise { - this.nextBatch = undefined; - if (!this.batchedRequests.length) return; - const requests = this.batchedRequests; - this.batchedRequests = []; - try { - if (!this.connected) { - await this.connect(); - } - if (!this.connection) { - throw new Error("Connection not established"); - } - - if (requests.length === 1) { - // send single queued requests directly instead of as a batched request - const requestType = new RequestType(requests[0].method); - const response = await this.sendRequestWithTiming(requestType, requests[0].params); - requests[0].resolve(response); - return; - } - - const requestType = new RequestType("batchRequests"); - const params: BatchRequestsParams = { requests: requests.map(request => ({ method: request.method, params: request.params })) }; - const response = await this.sendRequestWithTiming(requestType, params); - for (let i = 0; i < requests.length; i++) { - const { resolve, reject } = requests[i]; - const item = response.responses[i]; - if (item.error !== undefined) { - reject(new Error(item.error)); - } - else { - resolve(item.result); - } - } - } - catch (error) { - for (const { reject } of requests) reject(error); - } - } - - private scheduleImmediateBatch(): void { - if (this.nextBatch) return; - this.nextBatch = setImmediate(this.doBatch.bind(this)); - } - - batchContext(): { [Symbol.dispose](): void; } { - if (this.nextBatch === "manual") { - throw new Error("Already in a manual batch context"); - } - if (this.nextBatch) { - clearImmediate(this.nextBatch); - this.doBatch(); // empty the queue before entering a manual batch context - } - this.nextBatch = "manual"; - return { - [Symbol.dispose]: () => { - this.nextBatch = undefined; - this.scheduleImmediateBatch(); - }, - }; - } - - async apiRequest(method: K, params: APIMethodInfo[K]["params"]): Promise { - if (!this.connected) { - await this.connect(); - } - if (!this.connection) { - throw new Error("Connection not established"); - } - - const resultPromise = new Promise((resolve, reject) => { - this.batchedRequests.push({ method, params, resolve, reject }); - this.scheduleImmediateBatch(); - }); - return resultPromise; - } - - async apiRequestBinary(method: K, params: APIMethodInfo[K]["params"]): Promise { - const response = await this.apiRequest(method, params); - if (!response) return undefined; - const buffer = Buffer.from(response.data, "base64"); - return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength); - } - - /** - * Returns the timing collector that per-node materialization is reported - * into, or undefined when timing collection is disabled. The returned - * collector is the same one folded into {@link getTimingInfo}, so - * materialization totals surface alongside request timings. - */ - getTimingCollector(): TimingCollector | undefined { - return this.timing; - } - - /** - * Returns a combined timing snapshot: client-measured round-trip and byte - * counts folded together with the server's own per-request processing time - * (fetched via a getServerTiming request) and estimated transport overhead. - */ - async getTimingInfo(): Promise { - if (!this.timing) { - return disabledTimingInfo(); - } - const local = this.timing.getInfo(); - // No requests have been sent yet: nothing to fetch from the server. - if (!this.connected || !this.connection) { - return local; - } - return combineTimingInfo(local, await this.fetchServerTiming()); - } - - async resetTimingInfo(): Promise { - if (!this.timing) return; - this.timing.reset(); - if (this.connected && this.connection) { - // Keep the server's collection in sync so combined totals stay meaningful. - const requestType = new RequestType("resetServerTiming"); - await this.connection.sendRequest(requestType, undefined); - } - } - - private async fetchServerTiming(): Promise { - if (!this.connection) { - return disabledServerTimingInfo(); - } - // Fetch the server's own timing collection via a dedicated request. This - // bypasses the client-side collector so the query does not pollute it. - const requestType = new RequestType("getServerTiming"); - return this.connection.sendRequest(requestType, undefined); - } - - async close(): Promise { - if (this.connection) { - this.connection.dispose(); - this.connection = undefined; - } - if (this.socket) { - this.socket.destroy(); - this.socket = undefined; - } - if (this.process) { - // Close stdin to unblock the server's read loop, allowing it to exit cleanly. - // The server is blocked on stdin.Read(), so just sending SIGTERM would deadlock: - // - Node won't exit while child is alive - // - Child can't process SIGTERM while blocked on read - // - Read won't error until stdin is closed - this.process.stdin?.end(); - this.process = undefined; - } - this.connected = false; - } -} +import { + createMessageConnection, + type MessageConnection, + RequestType, + SocketMessageReader, + SocketMessageWriter, + StreamMessageReader, + StreamMessageWriter, +} from "#vscode-jsonrpc/node"; +import type { ChildProcess } from "node:child_process"; +import type { Socket } from "node:net"; +import { + type FileSystem, + fsCallbackNames, +} from "../fs.ts"; +import { + type ClientOptions, + type ClientSocketOptions, + type ClientSpawnOptions, + getAPIProcessArgs, + isSpawnOptions, + resolveExePath, +} from "../options.ts"; +import type { + APIMethodInfo, + APIRequest, + BatchRequestsParams, + BatchRequestsResponse, + SourceFileResponseMethod, +} from "../proto.ts"; +import { + combineTimingInfo, + disabledServerTimingInfo, + disabledTimingInfo, + type ServerTimingInfo, + TimingCollector, + type TimingInfo, +} from "../timing.ts"; + +export type { ClientOptions, ClientSocketOptions, ClientSpawnOptions }; + +/** + * Client handles communication with the TypeScript API server + * over STDIO (spawned process) or a Unix domain socket using JSON-RPC. + */ +export class Client { + private socket: Socket | undefined; + private process: ChildProcess | undefined; + private connection: MessageConnection | undefined; + private options: ClientOptions; + private connected = false; + private timing: TimingCollector | undefined; + private batchedRequests: { method: APIRequest["method"]; params: APIRequest["params"]; resolve: (value: unknown) => void; reject: (reason?: any) => void; }[] = []; + private nextBatch: NodeJS.Immediate | "manual" | undefined; + + constructor(options: ClientOptions) { + this.options = options; + if (isSpawnOptions(options) && options.collectTiming) { + this.timing = new TimingCollector(); + } + } + + async connect(): Promise { + if (this.connected) return; + + if (isSpawnOptions(this.options)) { + await this.connectViaSpawn(this.options); + } + else { + await this.connectViaSocket(this.options); + } + } + + private async connectViaSpawn(options: ClientSpawnOptions): Promise { + const { spawn } = await import("node:child_process"); + + return new Promise((resolve, reject) => { + const args = getAPIProcessArgs(options, true); + + // Enable virtual FS callbacks for each provided FS function + const enabledCallbacks: string[] = []; + if (options.fs) { + for (const name of fsCallbackNames) { + if (options.fs[name]) { + enabledCallbacks.push(name); + } + } + } + if (enabledCallbacks.length > 0) { + args.push(`--callbacks=${enabledCallbacks.join(",")}`); + } + + this.process = spawn(resolveExePath(options), args, { + stdio: ["pipe", "pipe", "inherit"], + }); + + this.process.once("error", error => { + reject(new Error(`Failed to start tsgo process: ${error.message}`)); + }); + + this.process.once("spawn", () => { + this.connected = true; + resolve(); + }); + + const reader = new StreamMessageReader(this.process.stdout!); + const writer = new StreamMessageWriter(this.process.stdin!); + this.connection = createMessageConnection(reader, writer); + this.registerFSCallbacks(this.connection, options.fs); + this.connection.listen(); + }); + } + + private async connectViaSocket(options: ClientSocketOptions): Promise { + const { createConnection } = await import("node:net"); + + return new Promise((resolve, reject) => { + this.socket = createConnection(options.pipe, () => { + const reader = new SocketMessageReader(this.socket!); + const writer = new SocketMessageWriter(this.socket!); + this.connection = createMessageConnection(reader, writer); + this.connection.listen(); + this.connected = true; + resolve(); + }); + + this.socket.once("error", error => { + reject(new Error(`Socket error: ${error.message}`)); + }); + }); + } + + private registerFSCallbacks(connection: MessageConnection, fs: FileSystem | undefined): void { + if (!fs) return; + for (const name of fsCallbackNames) { + if (name === "writeFile") { + if (!fs.writeFile) continue; + const callback = fs.writeFile; + + const requestType = new RequestType<{ path: string; data: string; }, unknown, void>(name); + connection.onRequest(requestType, (arg: { path: string; data: string; }) => { + callback(arg.path, arg.data); + return null; + }); + + continue; + } + + const callback = fs[name]; + if (callback) { + const requestType = new RequestType(name); + connection.onRequest(requestType, (arg: unknown) => { + const result = callback(arg as any); + if (name === "readFile") { + // readFile has 3 returns: string (content), null (not found), undefined (fall back). + // JSON-RPC can't distinguish null from undefined, so wrap in object. + if (result === undefined) return null; + return { content: result }; + } + return result ?? null; + }); + } + } + } + + private async sendRequestWithTiming(requestType: RequestType, params: unknown): Promise { + if (!this.connection) { + throw new Error("Connection not established"); + } + + if (!this.timing) { + return this.connection.sendRequest(requestType, params); + } + + // Round-trip latency is measured here; byte counts approximate the wire + // payload via the serialized JSON. Server-side processing time is not + // carried on the response; it is retrieved separately (via a + // getServerTiming request) and folded in by getTimingInfo(). + const bytesSent = params === undefined ? 0 : Buffer.byteLength(JSON.stringify(params), "utf-8"); + const start = performance.now(); + const result = await this.connection.sendRequest(requestType, params); + const roundTripMs = performance.now() - start; + this.timing.record({ + method: requestType.method, + roundTripMs, + bytesSent, + bytesReceived: result === undefined || result === null + ? 0 + : Buffer.byteLength(JSON.stringify(result), "utf-8"), + }); + return result; + } + + private async doBatch(): Promise { + this.nextBatch = undefined; + if (!this.batchedRequests.length) return; + const requests = this.batchedRequests; + this.batchedRequests = []; + try { + if (!this.connected) { + await this.connect(); + } + if (!this.connection) { + throw new Error("Connection not established"); + } + + if (requests.length === 1) { + // send single queued requests directly instead of as a batched request + const requestType = new RequestType(requests[0].method); + const response = await this.sendRequestWithTiming(requestType, requests[0].params); + requests[0].resolve(response); + return; + } + + const requestType = new RequestType("batchRequests"); + const params: BatchRequestsParams = { requests: requests.map(request => ({ method: request.method, params: request.params })) }; + const response = await this.sendRequestWithTiming(requestType, params); + for (let i = 0; i < requests.length; i++) { + const { resolve, reject } = requests[i]; + const item = response.responses[i]; + if (item.error !== undefined) { + reject(new Error(item.error)); + } + else { + resolve(item.result); + } + } + } + catch (error) { + for (const { reject } of requests) reject(error); + } + } + + private scheduleImmediateBatch(): void { + if (this.nextBatch) return; + this.nextBatch = setImmediate(this.doBatch.bind(this)); + } + + batchContext(): { [Symbol.dispose](): void; } { + if (this.nextBatch === "manual") { + throw new Error("Already in a manual batch context"); + } + if (this.nextBatch) { + clearImmediate(this.nextBatch); + this.doBatch(); // empty the queue before entering a manual batch context + } + this.nextBatch = "manual"; + return { + [Symbol.dispose]: () => { + this.nextBatch = undefined; + this.scheduleImmediateBatch(); + }, + }; + } + + async apiRequest(method: K, params: APIMethodInfo[K]["params"]): Promise { + if (!this.connected) { + await this.connect(); + } + if (!this.connection) { + throw new Error("Connection not established"); + } + + const resultPromise = new Promise((resolve, reject) => { + this.batchedRequests.push({ method, params, resolve, reject }); + this.scheduleImmediateBatch(); + }); + return resultPromise; + } + + async apiRequestBinary(method: K, params: APIMethodInfo[K]["params"]): Promise { + const response = await this.apiRequest(method, params); + if (!response) return undefined; + const buffer = Buffer.from(response.data, "base64"); + return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength); + } + + /** + * Returns the timing collector that per-node materialization is reported + * into, or undefined when timing collection is disabled. The returned + * collector is the same one folded into {@link getTimingInfo}, so + * materialization totals surface alongside request timings. + */ + getTimingCollector(): TimingCollector | undefined { + return this.timing; + } + + /** + * Returns a combined timing snapshot: client-measured round-trip and byte + * counts folded together with the server's own per-request processing time + * (fetched via a getServerTiming request) and estimated transport overhead. + */ + async getTimingInfo(): Promise { + if (!this.timing) { + return disabledTimingInfo(); + } + const local = this.timing.getInfo(); + // No requests have been sent yet: nothing to fetch from the server. + if (!this.connected || !this.connection) { + return local; + } + return combineTimingInfo(local, await this.fetchServerTiming()); + } + + async resetTimingInfo(): Promise { + if (!this.timing) return; + this.timing.reset(); + if (this.connected && this.connection) { + // Keep the server's collection in sync so combined totals stay meaningful. + const requestType = new RequestType("resetServerTiming"); + await this.connection.sendRequest(requestType, undefined); + } + } + + private async fetchServerTiming(): Promise { + if (!this.connection) { + return disabledServerTimingInfo(); + } + // Fetch the server's own timing collection via a dedicated request. This + // bypasses the client-side collector so the query does not pollute it. + const requestType = new RequestType("getServerTiming"); + return this.connection.sendRequest(requestType, undefined); + } + + async close(): Promise { + if (this.connection) { + this.connection.dispose(); + this.connection = undefined; + } + if (this.socket) { + this.socket.destroy(); + this.socket = undefined; + } + if (this.process) { + // Close stdin to unblock the server's read loop, allowing it to exit cleanly. + // The server is blocked on stdin.Read(), so just sending SIGTERM would deadlock: + // - Node won't exit while child is alive + // - Child can't process SIGTERM while blocked on read + // - Read won't error until stdin is closed + this.process.stdin?.end(); + this.process = undefined; + } + this.connected = false; + } +} diff --git a/packages/typescript/src/api/async/types.ts b/packages/typescript/src/api/async/types.ts index 79c392aed6b63..be1ccf98d3ebb 100644 --- a/packages/typescript/src/api/async/types.ts +++ b/packages/typescript/src/api/async/types.ts @@ -1,406 +1,406 @@ -import type { CompletionItemKind } from "#enums/completionItemKind"; -import type { ElementFlags } from "#enums/elementFlags"; -import type { ObjectFlags } from "#enums/objectFlags"; -import type { TypeFlags } from "#enums/typeFlags"; -import type { TypePredicateKind } from "#enums/typePredicateKind"; -import type { IndexSignatureDeclaration } from "../../ast/ast.ts"; -import type { Diagnostic } from "../proto.ts"; -import type { - NodeHandle, - Signature, - Symbol, -} from "./api.ts"; - -export type { Diagnostic } from "../proto.ts"; - -/** - * A TypeScript type. - * - * Use TypeFlags to determine the specific kind of type and access - * kind-specific properties. For example: - * - * ```ts - * if (type.flags & TypeFlags.StringLiteral) { - * console.log((type as StringLiteralType).value); // string - * } - * ``` - */ -export interface Type { - /** Type flags — use to determine the specific kind of type. */ - readonly flags: TypeFlags; - - /** Unique identifier for this type */ - readonly id: number; - - /** Get the symbol associated with this type, if any */ - getSymbol(): Promise; - - /** Get the properties of this type. */ - getProperties(): Promise; - - /** Get a named property of this type, if present. */ - getProperty(propertyName: string): Promise; - - /** Get the properties of the apparent type of this type. */ - getApparentProperties(): Promise; - - /** Get the apparent type of this type. */ - getApparentType(): Promise; - - /** Get the reduced type of this type. */ - getReducedType(): Promise; - - /** Get the call signatures of this type. */ - getCallSignatures(): Promise; - - /** Get the construct signatures of this type. */ - getConstructSignatures(): Promise; - - /** Get this type with `null` and `undefined` removed. */ - getNonNullableType(): Promise; - - /** Get this type's string index value type, if present. */ - getStringIndexType(): Promise; - - /** Get this type's number index value type, if present. */ - getNumberIndexType(): Promise; - - /** Get all index information for this type. */ - getIndexInfos(): Promise; - - /** Get the type arguments of the type alias this type was instantiated from, if any */ - getAliasTypeArguments(): Promise; - - /** Get the symbol of the type alias this type was instantiated from, if any */ - getAliasSymbol(): Promise; - - /** - * Get the base types of this type, or `undefined` if it is not a class or - * interface type. - */ - getBaseTypes(): Promise; - - /** Whether this type is a class or interface type */ - isClassOrInterface(): this is InterfaceType; - /** Whether this type is a union type */ - isUnionType(): this is UnionType; - /** Whether this type is an intersection type */ - isIntersectionType(): this is IntersectionType; - /** Whether this type is an object type */ - isObjectType(): this is ObjectType; - /** Whether this type is an intrinsic primitive type */ - isIntrinsicType(): this is IntrinsicType; - /** - * Whether this is the error type — the placeholder produced when a type - * cannot be determined (e.g. an unresolved reference). - */ - isErrorType(): boolean; - /** Whether this type is a literal type */ - isLiteralType(): this is LiteralType; - /** Whether this type is a string literal type */ - isStringLiteralType(): this is StringLiteralType; - /** Whether this type is a number literal type */ - isNumberLiteralType(): this is NumberLiteralType; - /** Whether this type is a bigint literal type */ - isBigIntLiteralType(): this is BigIntLiteralType; - /** Whether this type is a boolean literal type */ - isBooleanLiteralType(): this is BooleanLiteralType; - /** Whether this type is a type reference */ - isTypeReference(): this is TypeReference; - /** Whether this type is a tuple type */ - isTupleType(): this is TupleType; - /** Whether this type is an index type (`keyof T`) */ - isIndexType(): this is IndexType; - /** Whether this type is an indexed access type (`T[K]`) */ - isIndexedAccessType(): this is IndexedAccessType; - /** Whether this type is a conditional type */ - isConditionalType(): this is ConditionalType; - /** Whether this type is a substitution type */ - isSubstitutionType(): this is SubstitutionType; - /** Whether this type is a template literal type */ - isTemplateLiteralType(): this is TemplateLiteralType; - /** Whether this type is a string mapping type */ - isStringMappingType(): this is StringMappingType; - /** Whether this type is a type parameter */ - isTypeParameter(): this is TypeParameter; -} - -/** - * Freshable types (TypeFlags.Freshable) - literal types (TypeFlags.Literal) and computed enum types (TypeFlags.Enum). - */ -export interface FreshableType extends Type { - /** Get the fresh version of this type, if any */ - getFreshType(): Promise; - /** Get the regular (non-fresh) version of this type, if any */ - getRegularType(): Promise; -} - -/** Literal types: StringLiteral, NumberLiteral, BigIntLiteral, BooleanLiteral */ -export interface LiteralType extends FreshableType { - /** The literal value. Use TypeFlags to narrow to a specific literal subtype with a concrete value type. */ - readonly value: string | number | boolean | bigint; -} - -/** String literal types (TypeFlags.StringLiteral) */ -export interface StringLiteralType extends LiteralType { - /** The string value of the literal */ - readonly value: string; -} - -/** Numeric literal types (TypeFlags.NumberLiteral) */ -export interface NumberLiteralType extends LiteralType { - /** The numeric value of the literal */ - readonly value: number; -} - -/** BigInt literal types (TypeFlags.BigIntLiteral) */ -export interface BigIntLiteralType extends LiteralType { - /** The bigint value of the literal */ - readonly value: bigint; -} - -/** Boolean literal types (TypeFlags.BooleanLiteral) */ -export interface BooleanLiteralType extends LiteralType { - /** The boolean value of the literal */ - readonly value: boolean; -} - -/** Object types (TypeFlags.Object) */ -export interface ObjectType extends Type { - /** Object flags — use to determine the specific kind of object type. */ - readonly objectFlags: ObjectFlags; -} - -/** Type references (ObjectFlags.Reference) — e.g. Array, Map */ -export interface TypeReference extends ObjectType { - /** Get the generic target type (e.g. Array for Array) */ - getTarget(): Promise; -} - -/** Interface types — classes and interfaces (ObjectFlags.ClassOrInterface) */ -export interface InterfaceType extends TypeReference { - /** Get all type parameters (outer + local, excluding thisType) */ - getTypeParameters(): Promise; - /** Get outer type parameters from enclosing declarations */ - getOuterTypeParameters(): Promise; - /** Get local type parameters declared on this interface/class */ - getLocalTypeParameters(): Promise; -} - -/** Tuple types (ObjectFlags.Tuple) */ -export interface TupleType extends InterfaceType { - /** Per-element flags (Required, Optional, Rest, Variadic) */ - readonly elementFlags: readonly ElementFlags[]; - /** Number of initial required or optional elements */ - readonly fixedLength: number; - /** Whether the tuple is readonly */ - readonly readonly: boolean; -} - -/** Union or intersection types (TypeFlags.Union | TypeFlags.Intersection) */ -export interface UnionOrIntersectionType extends Type { - /** Get the constituent types */ - getTypes(): Promise; -} - -/** Union types (TypeFlags.Union) */ -export interface UnionType extends UnionOrIntersectionType { -} - -/** Intersection types (TypeFlags.Intersection) */ -export interface IntersectionType extends UnionOrIntersectionType { -} - -/** Structured types (TypeFlags.StructuredType) */ -export type StructuredType = ObjectType | UnionType | IntersectionType; - -/** Type parameters (TypeFlags.TypeParameter) */ -export interface TypeParameter extends Type { - /** True if this is the synthetic `this` type of an interface, class, or tuple */ - readonly isThisType?: boolean | undefined; - /** Get the constraint (the `T` in ``), or undefined if it has none */ - getConstraint(): Promise; - /** Get the default type (the `T` in ``), or undefined if it has none */ - getDefault(): Promise; -} - -/** Index types — keyof T (TypeFlags.Index) */ -export interface IndexType extends Type { - /** Get the target type T in `keyof T` */ - getTarget(): Promise; -} - -/** Indexed access types — T[K] (TypeFlags.IndexedAccess) */ -export interface IndexedAccessType extends Type { - /** Get the object type T in `T[K]` */ - getObjectType(): Promise; - /** Get the index type K in `T[K]` */ - getIndexType(): Promise; -} - -/** Conditional types — T extends U ? X : Y (TypeFlags.Conditional) */ -export interface ConditionalType extends Type { - /** Get the check type T in `T extends U ? X : Y` */ - getCheckType(): Promise; - /** Get the extends type U in `T extends U ? X : Y` */ - getExtendsType(): Promise; - /** Get the true type X in `T extends U ? X : Y` */ - getTrueType(): Promise; - /** Get the false type Y in `T extends U ? X : Y` */ - getFalseType(): Promise; -} - -/** Substitution types (TypeFlags.Substitution) */ -export interface SubstitutionType extends Type { - getBaseType(): Promise; - getConstraint(): Promise; -} - -/** Template literal types (TypeFlags.TemplateLiteral) */ -export interface TemplateLiteralType extends Type { - /** Text segments (always one more than the number of type spans) */ - readonly texts: readonly string[]; - /** Get the types interspersed between text segments */ - getTypes(): Promise; -} - -/** String mapping types — Uppercase, Lowercase, etc. (TypeFlags.StringMapping) */ -export interface StringMappingType extends Type { - /** Get the mapped type */ - getTarget(): Promise; -} - -/** Intrinsic types — any, unknown, string, number, bigint, symbol, void, undefined, null, never, object (TypeFlags.Intrinsic) */ -export interface IntrinsicType extends Type { - /** The intrinsic type name (e.g. "any", "string", "never") */ - readonly intrinsicName: string; -} - -/** Base for all type predicates */ -export interface TypePredicateBase { - readonly kind: TypePredicateKind; - readonly type: Type | undefined; -} - -/** `this is T` */ -export interface ThisTypePredicate extends TypePredicateBase { - readonly kind: TypePredicateKind.This; - readonly parameterName: undefined; - readonly parameterIndex: undefined; - readonly type: Type; -} - -/** `x is T` */ -export interface IdentifierTypePredicate extends TypePredicateBase { - readonly kind: TypePredicateKind.Identifier; - readonly parameterName: string; - readonly parameterIndex: number; - readonly type: Type; -} - -/** `asserts this is T` */ -export interface AssertsThisTypePredicate extends TypePredicateBase { - readonly kind: TypePredicateKind.AssertsThis; - readonly parameterName: undefined; - readonly parameterIndex: undefined; - readonly type: Type | undefined; -} - -/** `asserts x is T` */ -export interface AssertsIdentifierTypePredicate extends TypePredicateBase { - readonly kind: TypePredicateKind.AssertsIdentifier; - readonly parameterName: string; - readonly parameterIndex: number; - readonly type: Type | undefined; -} - -/** A type predicate — e.g. `x is T` or `asserts x is T` */ -export type TypePredicate = ThisTypePredicate | IdentifierTypePredicate | AssertsThisTypePredicate | AssertsIdentifierTypePredicate; - -/** An index signature — e.g. `[key: string]: T` */ -export interface IndexInfo { - /** The index key type (e.g. string or number) */ - readonly keyType: Type; - /** The index value type */ - readonly valueType: Type; - /** Whether the index signature is readonly */ - readonly isReadonly: boolean; - /** The index signature declaration, if any */ - readonly declaration?: NodeHandle | undefined; -} - -/** - * A single JSDoc tag attached to a symbol — e.g. `@param`, `@returns`. - */ -export interface JSDocTagInfo { - /** The tag name, without the leading `@` — e.g. `"param"`. */ - readonly name: string; - /** The rendered tag text, if any — e.g. `"a the first number"` for `@param a the first number`. */ - readonly text?: string | undefined; -} - -export interface CompletionEntryLabelDetails { - detail?: string | undefined; - description?: string | undefined; -} - -/** Options for {@link LanguageService.getCompletionsAtPosition}. */ -export interface CompletionOptions { - triggerCharacter?: string | undefined; - /** Include a `symbol` property on each completion entry. Only populated for symbol-based completions (not keywords or literals). */ - includeSymbol?: boolean | undefined; -} - -/** A single completion item returned by {@link LanguageService.getCompletionsAtPosition}. */ -export interface CompletionEntry { - readonly name: string; - readonly kind?: CompletionItemKind | undefined; - readonly sortText?: string | undefined; - readonly insertText?: string | undefined; - readonly filterText?: string | undefined; - readonly detail?: string | undefined; - readonly labelDetails?: CompletionEntryLabelDetails | undefined; - /** The symbol associated with this completion entry. Only set when `includeSymbol: true` is passed and a symbol is available. */ - readonly symbol?: Symbol | undefined; -} - -/** The result of {@link LanguageService.getCompletionsAtPosition}. */ -export interface CompletionInfo { - readonly isIncomplete: boolean; - readonly entries: readonly CompletionEntry[]; -} - -export interface FormatDiagnosticsHost { - getCurrentDirectory(): string; - getCanonicalFileName(fileName: string): string; - getNewLine(): string; -} - -export interface EmitOutputFile { - readonly text: string; - readonly sourceFileName?: string | undefined; -} - -export interface EmitResult { - readonly emitSkipped: boolean; - readonly diagnostics: readonly Diagnostic[]; - readonly emittedFiles: readonly string[]; -} - -export interface EmitOutput { - readonly emitSkipped: boolean; - readonly diagnostics: readonly Diagnostic[]; - readonly outputFiles: ReadonlyMap; -} - -export interface ImportSymbolAction { - readonly kind: "importSymbol"; - readonly symbol: Symbol; - readonly isValidTypeOnlyUseSite?: boolean; -} - -export type ImportAdderAction = ImportSymbolAction; - -export interface GetImportEditsForSymbolsOptions { - readonly isValidTypeOnlyUseSite?: boolean; -} +import type { CompletionItemKind } from "#enums/completionItemKind"; +import type { ElementFlags } from "#enums/elementFlags"; +import type { ObjectFlags } from "#enums/objectFlags"; +import type { TypeFlags } from "#enums/typeFlags"; +import type { TypePredicateKind } from "#enums/typePredicateKind"; +import type { IndexSignatureDeclaration } from "../../ast/ast.ts"; +import type { Diagnostic } from "../proto.ts"; +import type { + NodeHandle, + Signature, + Symbol, +} from "./api.ts"; + +export type { Diagnostic } from "../proto.ts"; + +/** + * A TypeScript type. + * + * Use TypeFlags to determine the specific kind of type and access + * kind-specific properties. For example: + * + * ```ts + * if (type.flags & TypeFlags.StringLiteral) { + * console.log((type as StringLiteralType).value); // string + * } + * ``` + */ +export interface Type { + /** Type flags — use to determine the specific kind of type. */ + readonly flags: TypeFlags; + + /** Unique identifier for this type */ + readonly id: number; + + /** Get the symbol associated with this type, if any */ + getSymbol(): Promise; + + /** Get the properties of this type. */ + getProperties(): Promise; + + /** Get a named property of this type, if present. */ + getProperty(propertyName: string): Promise; + + /** Get the properties of the apparent type of this type. */ + getApparentProperties(): Promise; + + /** Get the apparent type of this type. */ + getApparentType(): Promise; + + /** Get the reduced type of this type. */ + getReducedType(): Promise; + + /** Get the call signatures of this type. */ + getCallSignatures(): Promise; + + /** Get the construct signatures of this type. */ + getConstructSignatures(): Promise; + + /** Get this type with `null` and `undefined` removed. */ + getNonNullableType(): Promise; + + /** Get this type's string index value type, if present. */ + getStringIndexType(): Promise; + + /** Get this type's number index value type, if present. */ + getNumberIndexType(): Promise; + + /** Get all index information for this type. */ + getIndexInfos(): Promise; + + /** Get the type arguments of the type alias this type was instantiated from, if any */ + getAliasTypeArguments(): Promise; + + /** Get the symbol of the type alias this type was instantiated from, if any */ + getAliasSymbol(): Promise; + + /** + * Get the base types of this type, or `undefined` if it is not a class or + * interface type. + */ + getBaseTypes(): Promise; + + /** Whether this type is a class or interface type */ + isClassOrInterface(): this is InterfaceType; + /** Whether this type is a union type */ + isUnionType(): this is UnionType; + /** Whether this type is an intersection type */ + isIntersectionType(): this is IntersectionType; + /** Whether this type is an object type */ + isObjectType(): this is ObjectType; + /** Whether this type is an intrinsic primitive type */ + isIntrinsicType(): this is IntrinsicType; + /** + * Whether this is the error type — the placeholder produced when a type + * cannot be determined (e.g. an unresolved reference). + */ + isErrorType(): boolean; + /** Whether this type is a literal type */ + isLiteralType(): this is LiteralType; + /** Whether this type is a string literal type */ + isStringLiteralType(): this is StringLiteralType; + /** Whether this type is a number literal type */ + isNumberLiteralType(): this is NumberLiteralType; + /** Whether this type is a bigint literal type */ + isBigIntLiteralType(): this is BigIntLiteralType; + /** Whether this type is a boolean literal type */ + isBooleanLiteralType(): this is BooleanLiteralType; + /** Whether this type is a type reference */ + isTypeReference(): this is TypeReference; + /** Whether this type is a tuple type */ + isTupleType(): this is TupleType; + /** Whether this type is an index type (`keyof T`) */ + isIndexType(): this is IndexType; + /** Whether this type is an indexed access type (`T[K]`) */ + isIndexedAccessType(): this is IndexedAccessType; + /** Whether this type is a conditional type */ + isConditionalType(): this is ConditionalType; + /** Whether this type is a substitution type */ + isSubstitutionType(): this is SubstitutionType; + /** Whether this type is a template literal type */ + isTemplateLiteralType(): this is TemplateLiteralType; + /** Whether this type is a string mapping type */ + isStringMappingType(): this is StringMappingType; + /** Whether this type is a type parameter */ + isTypeParameter(): this is TypeParameter; +} + +/** + * Freshable types (TypeFlags.Freshable) - literal types (TypeFlags.Literal) and computed enum types (TypeFlags.Enum). + */ +export interface FreshableType extends Type { + /** Get the fresh version of this type, if any */ + getFreshType(): Promise; + /** Get the regular (non-fresh) version of this type, if any */ + getRegularType(): Promise; +} + +/** Literal types: StringLiteral, NumberLiteral, BigIntLiteral, BooleanLiteral */ +export interface LiteralType extends FreshableType { + /** The literal value. Use TypeFlags to narrow to a specific literal subtype with a concrete value type. */ + readonly value: string | number | boolean | bigint; +} + +/** String literal types (TypeFlags.StringLiteral) */ +export interface StringLiteralType extends LiteralType { + /** The string value of the literal */ + readonly value: string; +} + +/** Numeric literal types (TypeFlags.NumberLiteral) */ +export interface NumberLiteralType extends LiteralType { + /** The numeric value of the literal */ + readonly value: number; +} + +/** BigInt literal types (TypeFlags.BigIntLiteral) */ +export interface BigIntLiteralType extends LiteralType { + /** The bigint value of the literal */ + readonly value: bigint; +} + +/** Boolean literal types (TypeFlags.BooleanLiteral) */ +export interface BooleanLiteralType extends LiteralType { + /** The boolean value of the literal */ + readonly value: boolean; +} + +/** Object types (TypeFlags.Object) */ +export interface ObjectType extends Type { + /** Object flags — use to determine the specific kind of object type. */ + readonly objectFlags: ObjectFlags; +} + +/** Type references (ObjectFlags.Reference) — e.g. Array, Map */ +export interface TypeReference extends ObjectType { + /** Get the generic target type (e.g. Array for Array) */ + getTarget(): Promise; +} + +/** Interface types — classes and interfaces (ObjectFlags.ClassOrInterface) */ +export interface InterfaceType extends TypeReference { + /** Get all type parameters (outer + local, excluding thisType) */ + getTypeParameters(): Promise; + /** Get outer type parameters from enclosing declarations */ + getOuterTypeParameters(): Promise; + /** Get local type parameters declared on this interface/class */ + getLocalTypeParameters(): Promise; +} + +/** Tuple types (ObjectFlags.Tuple) */ +export interface TupleType extends InterfaceType { + /** Per-element flags (Required, Optional, Rest, Variadic) */ + readonly elementFlags: readonly ElementFlags[]; + /** Number of initial required or optional elements */ + readonly fixedLength: number; + /** Whether the tuple is readonly */ + readonly readonly: boolean; +} + +/** Union or intersection types (TypeFlags.Union | TypeFlags.Intersection) */ +export interface UnionOrIntersectionType extends Type { + /** Get the constituent types */ + getTypes(): Promise; +} + +/** Union types (TypeFlags.Union) */ +export interface UnionType extends UnionOrIntersectionType { +} + +/** Intersection types (TypeFlags.Intersection) */ +export interface IntersectionType extends UnionOrIntersectionType { +} + +/** Structured types (TypeFlags.StructuredType) */ +export type StructuredType = ObjectType | UnionType | IntersectionType; + +/** Type parameters (TypeFlags.TypeParameter) */ +export interface TypeParameter extends Type { + /** True if this is the synthetic `this` type of an interface, class, or tuple */ + readonly isThisType?: boolean | undefined; + /** Get the constraint (the `T` in ``), or undefined if it has none */ + getConstraint(): Promise; + /** Get the default type (the `T` in ``), or undefined if it has none */ + getDefault(): Promise; +} + +/** Index types — keyof T (TypeFlags.Index) */ +export interface IndexType extends Type { + /** Get the target type T in `keyof T` */ + getTarget(): Promise; +} + +/** Indexed access types — T[K] (TypeFlags.IndexedAccess) */ +export interface IndexedAccessType extends Type { + /** Get the object type T in `T[K]` */ + getObjectType(): Promise; + /** Get the index type K in `T[K]` */ + getIndexType(): Promise; +} + +/** Conditional types — T extends U ? X : Y (TypeFlags.Conditional) */ +export interface ConditionalType extends Type { + /** Get the check type T in `T extends U ? X : Y` */ + getCheckType(): Promise; + /** Get the extends type U in `T extends U ? X : Y` */ + getExtendsType(): Promise; + /** Get the true type X in `T extends U ? X : Y` */ + getTrueType(): Promise; + /** Get the false type Y in `T extends U ? X : Y` */ + getFalseType(): Promise; +} + +/** Substitution types (TypeFlags.Substitution) */ +export interface SubstitutionType extends Type { + getBaseType(): Promise; + getConstraint(): Promise; +} + +/** Template literal types (TypeFlags.TemplateLiteral) */ +export interface TemplateLiteralType extends Type { + /** Text segments (always one more than the number of type spans) */ + readonly texts: readonly string[]; + /** Get the types interspersed between text segments */ + getTypes(): Promise; +} + +/** String mapping types — Uppercase, Lowercase, etc. (TypeFlags.StringMapping) */ +export interface StringMappingType extends Type { + /** Get the mapped type */ + getTarget(): Promise; +} + +/** Intrinsic types — any, unknown, string, number, bigint, symbol, void, undefined, null, never, object (TypeFlags.Intrinsic) */ +export interface IntrinsicType extends Type { + /** The intrinsic type name (e.g. "any", "string", "never") */ + readonly intrinsicName: string; +} + +/** Base for all type predicates */ +export interface TypePredicateBase { + readonly kind: TypePredicateKind; + readonly type: Type | undefined; +} + +/** `this is T` */ +export interface ThisTypePredicate extends TypePredicateBase { + readonly kind: TypePredicateKind.This; + readonly parameterName: undefined; + readonly parameterIndex: undefined; + readonly type: Type; +} + +/** `x is T` */ +export interface IdentifierTypePredicate extends TypePredicateBase { + readonly kind: TypePredicateKind.Identifier; + readonly parameterName: string; + readonly parameterIndex: number; + readonly type: Type; +} + +/** `asserts this is T` */ +export interface AssertsThisTypePredicate extends TypePredicateBase { + readonly kind: TypePredicateKind.AssertsThis; + readonly parameterName: undefined; + readonly parameterIndex: undefined; + readonly type: Type | undefined; +} + +/** `asserts x is T` */ +export interface AssertsIdentifierTypePredicate extends TypePredicateBase { + readonly kind: TypePredicateKind.AssertsIdentifier; + readonly parameterName: string; + readonly parameterIndex: number; + readonly type: Type | undefined; +} + +/** A type predicate — e.g. `x is T` or `asserts x is T` */ +export type TypePredicate = ThisTypePredicate | IdentifierTypePredicate | AssertsThisTypePredicate | AssertsIdentifierTypePredicate; + +/** An index signature — e.g. `[key: string]: T` */ +export interface IndexInfo { + /** The index key type (e.g. string or number) */ + readonly keyType: Type; + /** The index value type */ + readonly valueType: Type; + /** Whether the index signature is readonly */ + readonly isReadonly: boolean; + /** The index signature declaration, if any */ + readonly declaration?: NodeHandle | undefined; +} + +/** + * A single JSDoc tag attached to a symbol — e.g. `@param`, `@returns`. + */ +export interface JSDocTagInfo { + /** The tag name, without the leading `@` — e.g. `"param"`. */ + readonly name: string; + /** The rendered tag text, if any — e.g. `"a the first number"` for `@param a the first number`. */ + readonly text?: string | undefined; +} + +export interface CompletionEntryLabelDetails { + detail?: string | undefined; + description?: string | undefined; +} + +/** Options for {@link LanguageService.getCompletionsAtPosition}. */ +export interface CompletionOptions { + triggerCharacter?: string | undefined; + /** Include a `symbol` property on each completion entry. Only populated for symbol-based completions (not keywords or literals). */ + includeSymbol?: boolean | undefined; +} + +/** A single completion item returned by {@link LanguageService.getCompletionsAtPosition}. */ +export interface CompletionEntry { + readonly name: string; + readonly kind?: CompletionItemKind | undefined; + readonly sortText?: string | undefined; + readonly insertText?: string | undefined; + readonly filterText?: string | undefined; + readonly detail?: string | undefined; + readonly labelDetails?: CompletionEntryLabelDetails | undefined; + /** The symbol associated with this completion entry. Only set when `includeSymbol: true` is passed and a symbol is available. */ + readonly symbol?: Symbol | undefined; +} + +/** The result of {@link LanguageService.getCompletionsAtPosition}. */ +export interface CompletionInfo { + readonly isIncomplete: boolean; + readonly entries: readonly CompletionEntry[]; +} + +export interface FormatDiagnosticsHost { + getCurrentDirectory(): string; + getCanonicalFileName(fileName: string): string; + getNewLine(): string; +} + +export interface EmitOutputFile { + readonly text: string; + readonly sourceFileName?: string | undefined; +} + +export interface EmitResult { + readonly emitSkipped: boolean; + readonly diagnostics: readonly Diagnostic[]; + readonly emittedFiles: readonly string[]; +} + +export interface EmitOutput { + readonly emitSkipped: boolean; + readonly diagnostics: readonly Diagnostic[]; + readonly outputFiles: ReadonlyMap; +} + +export interface ImportSymbolAction { + readonly kind: "importSymbol"; + readonly symbol: Symbol; + readonly isValidTypeOnlyUseSite?: boolean; +} + +export type ImportAdderAction = ImportSymbolAction; + +export interface GetImportEditsForSymbolsOptions { + readonly isValidTypeOnlyUseSite?: boolean; +} diff --git a/packages/typescript/src/api/diagnosticFormatter.ts b/packages/typescript/src/api/diagnosticFormatter.ts index 32bfbb57c8a6c..03e2c2665d7c2 100644 --- a/packages/typescript/src/api/diagnosticFormatter.ts +++ b/packages/typescript/src/api/diagnosticFormatter.ts @@ -1,203 +1,203 @@ -import { convertToRelativePath } from "./path.ts"; -import type { DiagnosticResponse as Diagnostic } from "./proto.generated.ts"; - -export interface FormatDiagnosticsHost { - getCurrentDirectory(): string; - getCanonicalFileName(fileName: string): string; - getNewLine(): string; -} - -const foregroundColorEscapeGrey = "\x1b[90m"; -const foregroundColorEscapeRed = "\x1b[91m"; -const foregroundColorEscapeYellow = "\x1b[93m"; -const foregroundColorEscapeBlue = "\x1b[94m"; -const foregroundColorEscapeCyan = "\x1b[96m"; -const gutterStyleSequence = "\x1b[7m"; -const gutterSeparator = " "; -const resetEscapeSequence = "\x1b[0m"; -const ellipsis = "..."; -const halfIndent = " "; -const indent = " "; -const fileAppearsToBeBinaryCode = 1490; - -function diagnosticCategoryName(category: number): string { - switch (category) { - case 0: - return "warning"; - case 1: - return "error"; - case 2: - return "suggestion"; - case 3: - return "message"; - default: - throw new Error(`Unknown diagnostic category: ${category}`); - } -} - -function getCategoryFormat(category: number): string { - switch (category) { - case 0: - return foregroundColorEscapeYellow; - case 1: - return foregroundColorEscapeRed; - case 2: - return foregroundColorEscapeGrey; - case 3: - return foregroundColorEscapeBlue; - default: - throw new Error(`Unknown diagnostic category: ${category}`); - } -} - -function formatColorAndReset(text: string, formatStyle: string): string { - return formatStyle + text + resetEscapeSequence; -} - -function diagnosticPrefix(diagnostic: Diagnostic): string { - return diagnostic.source || "TS"; -} - -function flattenDiagnosticMessage(diagnostic: Diagnostic, newLine: string, indentLevel = 0): string { - let result = ""; - if (indentLevel) { - result += newLine + " ".repeat(indentLevel); - } - result += diagnostic.text; - for (const child of diagnostic.messageChain ?? []) { - result += flattenDiagnosticMessage(child, newLine, indentLevel + 1); - } - return result; -} - -function relativeFileName(fileName: string, host: FormatDiagnosticsHost): string { - return convertToRelativePath( - fileName, - host.getCurrentDirectory(), - name => host.getCanonicalFileName(name), - ); -} - -function formatLocation(diagnostic: Diagnostic, host: FormatDiagnosticsHost): string { - if (!diagnostic.fileName || !diagnostic.startPosition) return ""; - const fileName = relativeFileName(diagnostic.fileName, host); - const { line, character } = diagnostic.startPosition; - return formatColorAndReset(fileName, foregroundColorEscapeCyan) + - ":" + - formatColorAndReset(`${line + 1}`, foregroundColorEscapeYellow) + - ":" + - formatColorAndReset(`${character + 1}`, foregroundColorEscapeYellow); -} - -function formatCodeSpan( - diagnostic: Diagnostic, - lineIndent: string, - squiggleColor: string, - host: FormatDiagnosticsHost, -): string { - const { startPosition, endPosition, sourceLines } = diagnostic; - if (!startPosition || !endPosition || !sourceLines?.length) return ""; - - const endCharacter = startPosition.line === endPosition.line && - startPosition.character === endPosition.character - ? endPosition.character + 1 - : endPosition.character; - const hasMoreThanFiveLines = endPosition.line - startPosition.line >= 4; - const gutterWidth = hasMoreThanFiveLines - ? Math.max(ellipsis.length, `${endPosition.line + 1}`.length) - : `${endPosition.line + 1}`.length; - let context = ""; - let previousLine: number | undefined; - - for (const sourceLine of sourceLines) { - if (previousLine !== undefined && sourceLine.line > previousLine + 1) { - context += host.getNewLine(); - context += lineIndent + - formatColorAndReset(ellipsis.padStart(gutterWidth), gutterStyleSequence) + - gutterSeparator; - } - - const lineContent = sourceLine.text.trimEnd().replace(/\t/g, " "); - context += host.getNewLine(); - context += lineIndent + - formatColorAndReset(`${sourceLine.line + 1}`.padStart(gutterWidth), gutterStyleSequence) + - gutterSeparator + - lineContent + - host.getNewLine(); - context += lineIndent + - formatColorAndReset("".padStart(gutterWidth), gutterStyleSequence) + - gutterSeparator + - squiggleColor; - - if (sourceLine.line === startPosition.line) { - const lastCharacter = sourceLine.line === endPosition.line - ? endCharacter - : lineContent.length; - context += " ".repeat(startPosition.character); - context += "~".repeat(Math.max(0, lastCharacter - startPosition.character)); - } - else if (sourceLine.line === endPosition.line) { - context += "~".repeat(endCharacter); - } - else { - context += "~".repeat(lineContent.length); - } - context += resetEscapeSequence; - previousLine = sourceLine.line; - } - - return context; -} - -export function formatDiagnostics(diagnostics: readonly Diagnostic[], host: FormatDiagnosticsHost): string { - let output = ""; - for (const diagnostic of diagnostics) { - const errorMessage = `${diagnosticCategoryName(diagnostic.category)} ${diagnosticPrefix(diagnostic)}${diagnostic.code}: ${flattenDiagnosticMessage(diagnostic, host.getNewLine())}${host.getNewLine()}`; - if (diagnostic.fileName && diagnostic.startPosition) { - const { line, character } = diagnostic.startPosition; - output += `${relativeFileName(diagnostic.fileName, host)}(${line + 1},${character + 1}): ${errorMessage}`; - } - else { - output += errorMessage; - } - } - return output; -} - -export function formatDiagnosticsWithColorAndContext( - diagnostics: readonly Diagnostic[], - host: FormatDiagnosticsHost, -): string { - let output = ""; - for (let i = 0; i < diagnostics.length; i++) { - if (i > 0) { - output += host.getNewLine(); - } - const diagnostic = diagnostics[i]; - if (diagnostic.fileName && diagnostic.startPosition) { - output += formatLocation(diagnostic, host) + " - "; - } - output += formatColorAndReset(diagnosticCategoryName(diagnostic.category), getCategoryFormat(diagnostic.category)); - output += formatColorAndReset(` ${diagnosticPrefix(diagnostic)}${diagnostic.code}: `, foregroundColorEscapeGrey); - output += flattenDiagnosticMessage(diagnostic, host.getNewLine()); - - if (diagnostic.fileName && diagnostic.code !== fileAppearsToBeBinaryCode) { - output += host.getNewLine(); - output += formatCodeSpan(diagnostic, "", getCategoryFormat(diagnostic.category), host); - output += host.getNewLine(); - } - - if (diagnostic.relatedInformation?.length) { - for (const related of diagnostic.relatedInformation) { - if (related.fileName && related.startPosition) { - output += host.getNewLine(); - output += halfIndent + formatLocation(related, host); - output += " - " + flattenDiagnosticMessage(related, host.getNewLine()); - output += formatCodeSpan(related, indent, foregroundColorEscapeCyan, host); - } - output += host.getNewLine(); - } - } - } - return output; -} +import { convertToRelativePath } from "./path.ts"; +import type { DiagnosticResponse as Diagnostic } from "./proto.generated.ts"; + +export interface FormatDiagnosticsHost { + getCurrentDirectory(): string; + getCanonicalFileName(fileName: string): string; + getNewLine(): string; +} + +const foregroundColorEscapeGrey = "\x1b[90m"; +const foregroundColorEscapeRed = "\x1b[91m"; +const foregroundColorEscapeYellow = "\x1b[93m"; +const foregroundColorEscapeBlue = "\x1b[94m"; +const foregroundColorEscapeCyan = "\x1b[96m"; +const gutterStyleSequence = "\x1b[7m"; +const gutterSeparator = " "; +const resetEscapeSequence = "\x1b[0m"; +const ellipsis = "..."; +const halfIndent = " "; +const indent = " "; +const fileAppearsToBeBinaryCode = 1490; + +function diagnosticCategoryName(category: number): string { + switch (category) { + case 0: + return "warning"; + case 1: + return "error"; + case 2: + return "suggestion"; + case 3: + return "message"; + default: + throw new Error(`Unknown diagnostic category: ${category}`); + } +} + +function getCategoryFormat(category: number): string { + switch (category) { + case 0: + return foregroundColorEscapeYellow; + case 1: + return foregroundColorEscapeRed; + case 2: + return foregroundColorEscapeGrey; + case 3: + return foregroundColorEscapeBlue; + default: + throw new Error(`Unknown diagnostic category: ${category}`); + } +} + +function formatColorAndReset(text: string, formatStyle: string): string { + return formatStyle + text + resetEscapeSequence; +} + +function diagnosticPrefix(diagnostic: Diagnostic): string { + return diagnostic.source || "TS"; +} + +function flattenDiagnosticMessage(diagnostic: Diagnostic, newLine: string, indentLevel = 0): string { + let result = ""; + if (indentLevel) { + result += newLine + " ".repeat(indentLevel); + } + result += diagnostic.text; + for (const child of diagnostic.messageChain ?? []) { + result += flattenDiagnosticMessage(child, newLine, indentLevel + 1); + } + return result; +} + +function relativeFileName(fileName: string, host: FormatDiagnosticsHost): string { + return convertToRelativePath( + fileName, + host.getCurrentDirectory(), + name => host.getCanonicalFileName(name), + ); +} + +function formatLocation(diagnostic: Diagnostic, host: FormatDiagnosticsHost): string { + if (!diagnostic.fileName || !diagnostic.startPosition) return ""; + const fileName = relativeFileName(diagnostic.fileName, host); + const { line, character } = diagnostic.startPosition; + return formatColorAndReset(fileName, foregroundColorEscapeCyan) + + ":" + + formatColorAndReset(`${line + 1}`, foregroundColorEscapeYellow) + + ":" + + formatColorAndReset(`${character + 1}`, foregroundColorEscapeYellow); +} + +function formatCodeSpan( + diagnostic: Diagnostic, + lineIndent: string, + squiggleColor: string, + host: FormatDiagnosticsHost, +): string { + const { startPosition, endPosition, sourceLines } = diagnostic; + if (!startPosition || !endPosition || !sourceLines?.length) return ""; + + const endCharacter = startPosition.line === endPosition.line && + startPosition.character === endPosition.character + ? endPosition.character + 1 + : endPosition.character; + const hasMoreThanFiveLines = endPosition.line - startPosition.line >= 4; + const gutterWidth = hasMoreThanFiveLines + ? Math.max(ellipsis.length, `${endPosition.line + 1}`.length) + : `${endPosition.line + 1}`.length; + let context = ""; + let previousLine: number | undefined; + + for (const sourceLine of sourceLines) { + if (previousLine !== undefined && sourceLine.line > previousLine + 1) { + context += host.getNewLine(); + context += lineIndent + + formatColorAndReset(ellipsis.padStart(gutterWidth), gutterStyleSequence) + + gutterSeparator; + } + + const lineContent = sourceLine.text.trimEnd().replace(/\t/g, " "); + context += host.getNewLine(); + context += lineIndent + + formatColorAndReset(`${sourceLine.line + 1}`.padStart(gutterWidth), gutterStyleSequence) + + gutterSeparator + + lineContent + + host.getNewLine(); + context += lineIndent + + formatColorAndReset("".padStart(gutterWidth), gutterStyleSequence) + + gutterSeparator + + squiggleColor; + + if (sourceLine.line === startPosition.line) { + const lastCharacter = sourceLine.line === endPosition.line + ? endCharacter + : lineContent.length; + context += " ".repeat(startPosition.character); + context += "~".repeat(Math.max(0, lastCharacter - startPosition.character)); + } + else if (sourceLine.line === endPosition.line) { + context += "~".repeat(endCharacter); + } + else { + context += "~".repeat(lineContent.length); + } + context += resetEscapeSequence; + previousLine = sourceLine.line; + } + + return context; +} + +export function formatDiagnostics(diagnostics: readonly Diagnostic[], host: FormatDiagnosticsHost): string { + let output = ""; + for (const diagnostic of diagnostics) { + const errorMessage = `${diagnosticCategoryName(diagnostic.category)} ${diagnosticPrefix(diagnostic)}${diagnostic.code}: ${flattenDiagnosticMessage(diagnostic, host.getNewLine())}${host.getNewLine()}`; + if (diagnostic.fileName && diagnostic.startPosition) { + const { line, character } = diagnostic.startPosition; + output += `${relativeFileName(diagnostic.fileName, host)}(${line + 1},${character + 1}): ${errorMessage}`; + } + else { + output += errorMessage; + } + } + return output; +} + +export function formatDiagnosticsWithColorAndContext( + diagnostics: readonly Diagnostic[], + host: FormatDiagnosticsHost, +): string { + let output = ""; + for (let i = 0; i < diagnostics.length; i++) { + if (i > 0) { + output += host.getNewLine(); + } + const diagnostic = diagnostics[i]; + if (diagnostic.fileName && diagnostic.startPosition) { + output += formatLocation(diagnostic, host) + " - "; + } + output += formatColorAndReset(diagnosticCategoryName(diagnostic.category), getCategoryFormat(diagnostic.category)); + output += formatColorAndReset(` ${diagnosticPrefix(diagnostic)}${diagnostic.code}: `, foregroundColorEscapeGrey); + output += flattenDiagnosticMessage(diagnostic, host.getNewLine()); + + if (diagnostic.fileName && diagnostic.code !== fileAppearsToBeBinaryCode) { + output += host.getNewLine(); + output += formatCodeSpan(diagnostic, "", getCategoryFormat(diagnostic.category), host); + output += host.getNewLine(); + } + + if (diagnostic.relatedInformation?.length) { + for (const related of diagnostic.relatedInformation) { + if (related.fileName && related.startPosition) { + output += host.getNewLine(); + output += halfIndent + formatLocation(related, host); + output += " - " + flattenDiagnosticMessage(related, host.getNewLine()); + output += formatCodeSpan(related, indent, foregroundColorEscapeCyan, host); + } + output += host.getNewLine(); + } + } + } + return output; +} diff --git a/packages/typescript/src/api/fs.ts b/packages/typescript/src/api/fs.ts index 0deb7b6a668e2..7e1af000bff62 100644 --- a/packages/typescript/src/api/fs.ts +++ b/packages/typescript/src/api/fs.ts @@ -1,152 +1,152 @@ -import { getPathComponents } from "./path.ts"; - -export interface FileSystemEntries { - files: string[]; - directories: string[]; -} - -export interface FileSystem { - directoryExists?: (directoryName: string) => boolean | undefined; - fileExists?: (fileName: string) => boolean | undefined; - getAccessibleEntries?: (directoryName: string) => FileSystemEntries | undefined; - /** - * Read a file's content. - * - Return the file content as a `string` (including `""` for empty files). - * - Return `null` to indicate the file does not exist (without falling back to the real FS). - * - Return `undefined` to fall back to the real filesystem. - */ - readFile?: (fileName: string) => string | null | undefined; - realpath?: (path: string) => string | undefined; - writeFile?: (path: string, content: string) => void; - removeFile?: (path: string) => void; -} - -/** The callback names supported by the Go server for virtual FS delegation. */ -export const fsCallbackNames = ["readFile", "fileExists", "directoryExists", "getAccessibleEntries", "realpath", "writeFile"] as const; - -interface VDirectory { - type: "directory"; - children: Record; -} - -interface VFile { - type: "file"; -} - -type VNode = VDirectory | VFile; - -export function createVirtualFileSystem(files: Record): FileSystem { - const root: VDirectory = { - type: "directory", - children: {}, - }; - const content: Record = {}; - - for (const filePath of Object.keys(files)) { - content[filePath] = files[filePath]; - addToTree(filePath); - } - - return { - directoryExists, - fileExists, - getAccessibleEntries, - readFile, - realpath: path => path, - writeFile, - removeFile, - }; - - function getNodeFromPath(path: string): VNode | undefined { - if (!path || path === "/") { - return root; - } - const segments = getPathComponents(path).slice(1); - let current: VNode = root; - for (const segment of segments) { - if (current.type !== "directory") { - return undefined; - } - const child: VNode = current.children[segment]; - if (!child) { - return undefined; - } - current = child; - } - return current; - } - - function ensureDirectory(segments: string[]): VDirectory { - let current: VDirectory = root; - for (const segment of segments) { - if (!current.children[segment]) { - current.children[segment] = { type: "directory", children: {} }; - } - else if (current.children[segment].type !== "directory") { - throw new Error(`Cannot create directory: a file already exists at "/${segments.join("/")}"`); - } - current = current.children[segment] as VDirectory; - } - return current; - } - - function addToTree(path: string): void { - const segments = getPathComponents(path).slice(1); - if (segments.length === 0) { - throw new Error(`Invalid file path: "${path}"`); - } - const filename = segments.pop()!; - const dirNode = ensureDirectory(segments); - dirNode.children[filename] = { type: "file" }; - } - - function writeFile(path: string, data: string): void { - content[path] = data; - addToTree(path); - } - - function removeFile(path: string): void { - delete content[path]; - const segments = getPathComponents(path).slice(1); - if (segments.length === 0) return; - const filename = segments.pop()!; - const dirNode = getNodeFromPath("/" + segments.join("/")); - if (dirNode && dirNode.type === "directory") { - delete dirNode.children[filename]; - } - } - - function directoryExists(directoryName: string): boolean { - const node = getNodeFromPath(directoryName); - return !!node && node.type === "directory"; - } - - function fileExists(fileName: string): boolean { - return fileName in content; - } - - function getAccessibleEntries(directoryName: string): FileSystemEntries | undefined { - const node = getNodeFromPath(directoryName); - if (!node || node.type !== "directory") { - return undefined; - } - const fileEntries: string[] = []; - const directories: string[] = []; - for (const [name, child] of Object.entries(node.children)) { - if (child.type === "file") { - fileEntries.push(name); - } - else { - directories.push(name); - } - } - return { files: fileEntries, directories }; - } - - function readFile(fileName: string): string | undefined { - if (fileName in content) { - return content[fileName]; - } - return undefined; - } -} +import { getPathComponents } from "./path.ts"; + +export interface FileSystemEntries { + files: string[]; + directories: string[]; +} + +export interface FileSystem { + directoryExists?: (directoryName: string) => boolean | undefined; + fileExists?: (fileName: string) => boolean | undefined; + getAccessibleEntries?: (directoryName: string) => FileSystemEntries | undefined; + /** + * Read a file's content. + * - Return the file content as a `string` (including `""` for empty files). + * - Return `null` to indicate the file does not exist (without falling back to the real FS). + * - Return `undefined` to fall back to the real filesystem. + */ + readFile?: (fileName: string) => string | null | undefined; + realpath?: (path: string) => string | undefined; + writeFile?: (path: string, content: string) => void; + removeFile?: (path: string) => void; +} + +/** The callback names supported by the Go server for virtual FS delegation. */ +export const fsCallbackNames = ["readFile", "fileExists", "directoryExists", "getAccessibleEntries", "realpath", "writeFile"] as const; + +interface VDirectory { + type: "directory"; + children: Record; +} + +interface VFile { + type: "file"; +} + +type VNode = VDirectory | VFile; + +export function createVirtualFileSystem(files: Record): FileSystem { + const root: VDirectory = { + type: "directory", + children: {}, + }; + const content: Record = {}; + + for (const filePath of Object.keys(files)) { + content[filePath] = files[filePath]; + addToTree(filePath); + } + + return { + directoryExists, + fileExists, + getAccessibleEntries, + readFile, + realpath: path => path, + writeFile, + removeFile, + }; + + function getNodeFromPath(path: string): VNode | undefined { + if (!path || path === "/") { + return root; + } + const segments = getPathComponents(path).slice(1); + let current: VNode = root; + for (const segment of segments) { + if (current.type !== "directory") { + return undefined; + } + const child: VNode = current.children[segment]; + if (!child) { + return undefined; + } + current = child; + } + return current; + } + + function ensureDirectory(segments: string[]): VDirectory { + let current: VDirectory = root; + for (const segment of segments) { + if (!current.children[segment]) { + current.children[segment] = { type: "directory", children: {} }; + } + else if (current.children[segment].type !== "directory") { + throw new Error(`Cannot create directory: a file already exists at "/${segments.join("/")}"`); + } + current = current.children[segment] as VDirectory; + } + return current; + } + + function addToTree(path: string): void { + const segments = getPathComponents(path).slice(1); + if (segments.length === 0) { + throw new Error(`Invalid file path: "${path}"`); + } + const filename = segments.pop()!; + const dirNode = ensureDirectory(segments); + dirNode.children[filename] = { type: "file" }; + } + + function writeFile(path: string, data: string): void { + content[path] = data; + addToTree(path); + } + + function removeFile(path: string): void { + delete content[path]; + const segments = getPathComponents(path).slice(1); + if (segments.length === 0) return; + const filename = segments.pop()!; + const dirNode = getNodeFromPath("/" + segments.join("/")); + if (dirNode && dirNode.type === "directory") { + delete dirNode.children[filename]; + } + } + + function directoryExists(directoryName: string): boolean { + const node = getNodeFromPath(directoryName); + return !!node && node.type === "directory"; + } + + function fileExists(fileName: string): boolean { + return fileName in content; + } + + function getAccessibleEntries(directoryName: string): FileSystemEntries | undefined { + const node = getNodeFromPath(directoryName); + if (!node || node.type !== "directory") { + return undefined; + } + const fileEntries: string[] = []; + const directories: string[] = []; + for (const [name, child] of Object.entries(node.children)) { + if (child.type === "file") { + fileEntries.push(name); + } + else { + directories.push(name); + } + } + return { files: fileEntries, directories }; + } + + function readFile(fileName: string): string | undefined { + if (fileName in content) { + return content[fileName]; + } + return undefined; + } +} diff --git a/packages/typescript/src/api/node/encoder.generated.ts b/packages/typescript/src/api/node/encoder.generated.ts index df5572d9690aa..be92cadab26a4 100644 --- a/packages/typescript/src/api/node/encoder.generated.ts +++ b/packages/typescript/src/api/node/encoder.generated.ts @@ -1,104 +1,104 @@ -// Code generated by tools/scripts/tsc/generate-encoder.ts. DO NOT EDIT. - -import type { - ArrayLiteralExpression, - Block, - ExportAssignment, - ExportDeclaration, - ExportSpecifier, - HeritageClause, - ImportAttributes, - ImportClause, - ImportEqualsDeclaration, - ImportSpecifier, - ImportTypeNode, - JSDocParameterOrPropertyTag, - JSDocTypeLiteral, - JsxText, - MetaProperty, - ModuleDeclaration, - Node, - ObjectLiteralExpression, - PostfixUnaryExpression, - PrefixUnaryExpression, - SyntheticExpression, - TypeOperatorNode, -} from "../../ast/index.ts"; -import { SyntaxKind } from "../../ast/index.ts"; -import { - NODE_DATA_TYPE_CHILDREN, - NODE_DATA_TYPE_EXTENDED, - NODE_DATA_TYPE_STRING, -} from "./protocol.ts"; - -export function getNodeDataType(kind: SyntaxKind): number { - switch (kind) { - case SyntaxKind.Identifier: - case SyntaxKind.PrivateIdentifier: - case SyntaxKind.JsxText: - case SyntaxKind.JSDocText: - case SyntaxKind.JSDocLink: - case SyntaxKind.JSDocLinkPlain: - case SyntaxKind.JSDocLinkCode: - return NODE_DATA_TYPE_STRING; - case SyntaxKind.StringLiteral: - case SyntaxKind.NumericLiteral: - case SyntaxKind.BigIntLiteral: - case SyntaxKind.RegularExpressionLiteral: - case SyntaxKind.NoSubstitutionTemplateLiteral: - case SyntaxKind.TemplateHead: - case SyntaxKind.TemplateMiddle: - case SyntaxKind.TemplateTail: - case SyntaxKind.SourceFile: - return NODE_DATA_TYPE_EXTENDED; - default: - return NODE_DATA_TYPE_CHILDREN; - } -} - -export function getNodeCommonData(node: Node): number { - switch (node.kind) { - case SyntaxKind.Block: - return ((node as Block).multiLine ? 1 : 0) << 24; - case SyntaxKind.HeritageClause: - return ((node as HeritageClause).token === SyntaxKind.ImplementsKeyword ? 1 : 0) << 24; - case SyntaxKind.ExportAssignment: - return ((node as ExportAssignment).isExportEquals ? 1 : 0) << 24; - case SyntaxKind.ExportSpecifier: - return ((node as ExportSpecifier).isTypeOnly ? 1 : 0) << 24; - case SyntaxKind.PrefixUnaryExpression: - return ((node as PrefixUnaryExpression).operator === SyntaxKind.MinusToken ? 1 : (node as PrefixUnaryExpression).operator === SyntaxKind.TildeToken ? 2 : (node as PrefixUnaryExpression).operator === SyntaxKind.ExclamationToken ? 3 : (node as PrefixUnaryExpression).operator === SyntaxKind.PlusPlusToken ? 4 : (node as PrefixUnaryExpression).operator === SyntaxKind.MinusMinusToken ? 5 : 0) << 24; - case SyntaxKind.PostfixUnaryExpression: - return ((node as PostfixUnaryExpression).operator === SyntaxKind.MinusMinusToken ? 1 : 0) << 24; - case SyntaxKind.MetaProperty: - return ((node as MetaProperty).keywordToken === SyntaxKind.NewKeyword ? 1 : 0) << 24; - case SyntaxKind.ArrayLiteralExpression: - return ((node as ArrayLiteralExpression).multiLine ? 1 : 0) << 24; - case SyntaxKind.ObjectLiteralExpression: - return ((node as ObjectLiteralExpression).multiLine ? 1 : 0) << 24; - case SyntaxKind.TypeOperator: - return ((node as TypeOperatorNode).operator === SyntaxKind.ReadonlyKeyword ? 1 : (node as TypeOperatorNode).operator === SyntaxKind.UniqueKeyword ? 2 : 0) << 24; - case SyntaxKind.ImportAttributes: - return ((node as ImportAttributes).multiLine ? 1 : 0) << 24 | ((node as ImportAttributes).token === SyntaxKind.AssertKeyword ? 1 : 0) << 25; - case SyntaxKind.JsxText: - return ((node as JsxText).containsOnlyTriviaWhiteSpaces ? 1 : 0) << 24; - case SyntaxKind.ModuleDeclaration: - return ((node as ModuleDeclaration).keyword === SyntaxKind.NamespaceKeyword ? 1 : 0) << 24; - case SyntaxKind.ImportEqualsDeclaration: - return ((node as ImportEqualsDeclaration).isTypeOnly ? 1 : 0) << 24; - case SyntaxKind.ExportDeclaration: - return ((node as ExportDeclaration).isTypeOnly ? 1 : 0) << 24; - case SyntaxKind.ImportType: - return ((node as ImportTypeNode).isTypeOf ? 1 : 0) << 24; - case SyntaxKind.ImportClause: - return ((node as ImportClause).phaseModifier === SyntaxKind.TypeKeyword ? 1 : (node as ImportClause).phaseModifier === SyntaxKind.DeferKeyword ? 2 : 0) << 24; - case SyntaxKind.ImportSpecifier: - return ((node as ImportSpecifier).isTypeOnly ? 1 : 0) << 24; - case SyntaxKind.JSDocTypeLiteral: - return ((node as JSDocTypeLiteral).isArrayType ? 1 : 0) << 24; - case SyntaxKind.JSDocParameterTag: - case SyntaxKind.JSDocPropertyTag: - return ((node as JSDocParameterOrPropertyTag).isBracketed ? 1 : 0) << 24 | ((node as JSDocParameterOrPropertyTag).isNameFirst ? 1 : 0) << 25; - } - return 0; -} +// Code generated by tools/scripts/tsc/generate-encoder.ts. DO NOT EDIT. + +import type { + ArrayLiteralExpression, + Block, + ExportAssignment, + ExportDeclaration, + ExportSpecifier, + HeritageClause, + ImportAttributes, + ImportClause, + ImportEqualsDeclaration, + ImportSpecifier, + ImportTypeNode, + JSDocParameterOrPropertyTag, + JSDocTypeLiteral, + JsxText, + MetaProperty, + ModuleDeclaration, + Node, + ObjectLiteralExpression, + PostfixUnaryExpression, + PrefixUnaryExpression, + SyntheticExpression, + TypeOperatorNode, +} from "../../ast/index.ts"; +import { SyntaxKind } from "../../ast/index.ts"; +import { + NODE_DATA_TYPE_CHILDREN, + NODE_DATA_TYPE_EXTENDED, + NODE_DATA_TYPE_STRING, +} from "./protocol.ts"; + +export function getNodeDataType(kind: SyntaxKind): number { + switch (kind) { + case SyntaxKind.Identifier: + case SyntaxKind.PrivateIdentifier: + case SyntaxKind.JsxText: + case SyntaxKind.JSDocText: + case SyntaxKind.JSDocLink: + case SyntaxKind.JSDocLinkPlain: + case SyntaxKind.JSDocLinkCode: + return NODE_DATA_TYPE_STRING; + case SyntaxKind.StringLiteral: + case SyntaxKind.NumericLiteral: + case SyntaxKind.BigIntLiteral: + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.NoSubstitutionTemplateLiteral: + case SyntaxKind.TemplateHead: + case SyntaxKind.TemplateMiddle: + case SyntaxKind.TemplateTail: + case SyntaxKind.SourceFile: + return NODE_DATA_TYPE_EXTENDED; + default: + return NODE_DATA_TYPE_CHILDREN; + } +} + +export function getNodeCommonData(node: Node): number { + switch (node.kind) { + case SyntaxKind.Block: + return ((node as Block).multiLine ? 1 : 0) << 24; + case SyntaxKind.HeritageClause: + return ((node as HeritageClause).token === SyntaxKind.ImplementsKeyword ? 1 : 0) << 24; + case SyntaxKind.ExportAssignment: + return ((node as ExportAssignment).isExportEquals ? 1 : 0) << 24; + case SyntaxKind.ExportSpecifier: + return ((node as ExportSpecifier).isTypeOnly ? 1 : 0) << 24; + case SyntaxKind.PrefixUnaryExpression: + return ((node as PrefixUnaryExpression).operator === SyntaxKind.MinusToken ? 1 : (node as PrefixUnaryExpression).operator === SyntaxKind.TildeToken ? 2 : (node as PrefixUnaryExpression).operator === SyntaxKind.ExclamationToken ? 3 : (node as PrefixUnaryExpression).operator === SyntaxKind.PlusPlusToken ? 4 : (node as PrefixUnaryExpression).operator === SyntaxKind.MinusMinusToken ? 5 : 0) << 24; + case SyntaxKind.PostfixUnaryExpression: + return ((node as PostfixUnaryExpression).operator === SyntaxKind.MinusMinusToken ? 1 : 0) << 24; + case SyntaxKind.MetaProperty: + return ((node as MetaProperty).keywordToken === SyntaxKind.NewKeyword ? 1 : 0) << 24; + case SyntaxKind.ArrayLiteralExpression: + return ((node as ArrayLiteralExpression).multiLine ? 1 : 0) << 24; + case SyntaxKind.ObjectLiteralExpression: + return ((node as ObjectLiteralExpression).multiLine ? 1 : 0) << 24; + case SyntaxKind.TypeOperator: + return ((node as TypeOperatorNode).operator === SyntaxKind.ReadonlyKeyword ? 1 : (node as TypeOperatorNode).operator === SyntaxKind.UniqueKeyword ? 2 : 0) << 24; + case SyntaxKind.ImportAttributes: + return ((node as ImportAttributes).multiLine ? 1 : 0) << 24 | ((node as ImportAttributes).token === SyntaxKind.AssertKeyword ? 1 : 0) << 25; + case SyntaxKind.JsxText: + return ((node as JsxText).containsOnlyTriviaWhiteSpaces ? 1 : 0) << 24; + case SyntaxKind.ModuleDeclaration: + return ((node as ModuleDeclaration).keyword === SyntaxKind.NamespaceKeyword ? 1 : 0) << 24; + case SyntaxKind.ImportEqualsDeclaration: + return ((node as ImportEqualsDeclaration).isTypeOnly ? 1 : 0) << 24; + case SyntaxKind.ExportDeclaration: + return ((node as ExportDeclaration).isTypeOnly ? 1 : 0) << 24; + case SyntaxKind.ImportType: + return ((node as ImportTypeNode).isTypeOf ? 1 : 0) << 24; + case SyntaxKind.ImportClause: + return ((node as ImportClause).phaseModifier === SyntaxKind.TypeKeyword ? 1 : (node as ImportClause).phaseModifier === SyntaxKind.DeferKeyword ? 2 : 0) << 24; + case SyntaxKind.ImportSpecifier: + return ((node as ImportSpecifier).isTypeOnly ? 1 : 0) << 24; + case SyntaxKind.JSDocTypeLiteral: + return ((node as JSDocTypeLiteral).isArrayType ? 1 : 0) << 24; + case SyntaxKind.JSDocParameterTag: + case SyntaxKind.JSDocPropertyTag: + return ((node as JSDocParameterOrPropertyTag).isBracketed ? 1 : 0) << 24 | ((node as JSDocParameterOrPropertyTag).isNameFirst ? 1 : 0) << 25; + } + return 0; +} diff --git a/packages/typescript/src/api/node/encoder.ts b/packages/typescript/src/api/node/encoder.ts index 0071f7970a579..83d624de169ea 100644 --- a/packages/typescript/src/api/node/encoder.ts +++ b/packages/typescript/src/api/node/encoder.ts @@ -1,379 +1,379 @@ -import type { - FileReference, - LiteralLikeNode, - Node, - NodeArray, - SourceFile, - TemplateLiteralLikeNode, -} from "../../ast/index.ts"; -import { SyntaxKind } from "../../ast/index.ts"; -import { - getNodeCommonData, - getNodeDataType, -} from "./encoder.generated.ts"; -import { MsgpackWriter } from "./msgpack.ts"; -import { - childProperties, - HEADER_OFFSET_EXTENDED_DATA, - HEADER_OFFSET_METADATA, - HEADER_OFFSET_NODES, - HEADER_OFFSET_STRING_TABLE, - HEADER_OFFSET_STRING_TABLE_OFFSETS, - HEADER_OFFSET_STRUCTURED_DATA, - HEADER_SIZE, - KIND_NODE_LIST, - NODE_DATA_TYPE_CHILDREN, - NODE_DATA_TYPE_EXTENDED, - NODE_DATA_TYPE_STRING, - NODE_LEN, - PROTOCOL_VERSION, -} from "./protocol.ts"; - -const NODE_FIELDS = NODE_LEN / 4; -const NODE_FIELD_NEXT = 3; -const NO_STRUCTURED_DATA = 0xFFFFFFFF; - -// String table that accumulates strings into a flat byte pool. -class StringTable { - private parts: string[]; - private byteLen: number; - private offsets: number[]; - - constructor() { - this.parts = []; - this.byteLen = 0; - this.offsets = []; - } - - add(text: string): number { - const index = this.offsets.length; - const encoder = cachedEncoder(); - const encodedLength = encoder.encode(text).length; - const offset = this.byteLen; - this.parts.push(text); - this.byteLen += encodedLength; - this.offsets.push(offset, offset + encodedLength); - return index; - } - - encode(): Uint8Array { - const encoder = cachedEncoder(); - const dataBytes = encoder.encode(this.parts.join("")); - const offsetBytes = new Uint8Array(this.offsets.length * 4); - const view = new DataView(offsetBytes.buffer); - for (let i = 0; i < this.offsets.length; i++) { - view.setUint32(i * 4, this.offsets[i], true); - } - const result = new Uint8Array(offsetBytes.length + dataBytes.length); - result.set(offsetBytes, 0); - result.set(dataBytes, offsetBytes.length); - return result; - } - - stringByteLength(): number { - return this.byteLen; - } - - offsetsCount(): number { - return this.offsets.length; - } -} - -let _encoder: TextEncoder | undefined; -function cachedEncoder(): TextEncoder { - return _encoder ??= new TextEncoder(); -} - -function getChildrenPropertyMask(node: Node): number { - const kind = node.kind; - - const props = childProperties[kind]; - if (!props) { - return 0; - } - const n = node as unknown as Record; - let mask = 0; - for (let i = 0; i < props.length; i++) { - const prop = props[i]; - if (prop !== undefined && isChildPresent(n[prop])) { - mask |= 1 << i; - } - } - return mask; -} - -// A child is "present" if it's non-null/non-undefined. -// This matches the Go encoder's behavior where non-nil NodeLists (even empty) -// are treated as present, and only nil NodeLists are absent. -function isChildPresent(v: unknown): boolean { - if (v === undefined || v === null) return false; - return true; -} - -function recordNodeStrings(node: Node, strs: StringTable): number { - return strs.add((node as LiteralLikeNode).text ?? ""); -} - -function encodeFileReferences(refs: readonly FileReference[] | undefined, writer: MsgpackWriter): number { - if (!refs || refs.length === 0) return NO_STRUCTURED_DATA; - const offset = writer.finish().length; - writer.writeArrayHeader(refs.length); - for (const ref of refs) { - writer.writeArrayHeader(5); - writer.writeUint(ref.pos); - writer.writeUint(ref.end); - writer.writeString(ref.fileName); - writer.writeUint(ref.resolutionMode ?? 0); - writer.writeBool(ref.preserve ?? false); - } - return offset; -} - -function recordExtendedData(node: Node, strs: StringTable, extendedData: number[], structuredWriter: MsgpackWriter): number { - const offset = extendedData.length * 4; - if (node.kind === SyntaxKind.SourceFile) { - const sf = node as SourceFile; - const textIndex = strs.add(sf.text); - const fileNameIndex = strs.add(sf.fileName); - const pathIndex = strs.add(sf.path); - const referencedFilesOffset = encodeFileReferences(sf.referencedFiles, structuredWriter); - const typeRefDirectivesOffset = encodeFileReferences(sf.typeReferenceDirectives, structuredWriter); - const libRefDirectivesOffset = encodeFileReferences(sf.libReferenceDirectives, structuredWriter); - // Content-mapper metadata is program-owned and supplied only by the Go encoder. - extendedData.push(textIndex, fileNameIndex, pathIndex, sf.languageVariant, sf.scriptKind, referencedFilesOffset, typeRefDirectivesOffset, libRefDirectivesOffset, NO_STRUCTURED_DATA, NO_STRUCTURED_DATA, NO_STRUCTURED_DATA, 0, textIndex, NO_STRUCTURED_DATA, NO_STRUCTURED_DATA, NO_STRUCTURED_DATA, NO_STRUCTURED_DATA, NO_STRUCTURED_DATA, NO_STRUCTURED_DATA); - } - else if ( - node.kind === SyntaxKind.TemplateHead || - node.kind === SyntaxKind.TemplateMiddle || - node.kind === SyntaxKind.TemplateTail - ) { - const tmpl = node as TemplateLiteralLikeNode; - const text: string = tmpl.text ?? ""; - const rawText: string = tmpl.rawText ?? ""; - const templateFlags: number = tmpl.templateFlags ?? 0; - const textIndex = strs.add(text); - const rawTextIndex = strs.add(rawText); - extendedData.push(textIndex, rawTextIndex, templateFlags); - } - else { - // StringLiteral, NumericLiteral, BigIntLiteral, RegularExpressionLiteral, - // NoSubstitutionTemplateLiteral — format: [textIndex, tokenFlags] - const n = node as any; - const text: string = n.text ?? ""; - const tokenFlags: number = n.tokenFlags ?? 0; - const textIndex = strs.add(text); - extendedData.push(textIndex, tokenFlags); - } - return offset; -} - -function getNodeData(node: Node, strs: StringTable, extendedData: number[], structuredWriter: MsgpackWriter): number { - const t = getNodeDataType(node.kind); - const common = getNodeCommonData(node); - switch (t) { - case NODE_DATA_TYPE_CHILDREN: - return t | common | getChildrenPropertyMask(node); - case NODE_DATA_TYPE_STRING: - return t | common | recordNodeStrings(node, strs); - case NODE_DATA_TYPE_EXTENDED: - return t | common | recordExtendedData(node, strs, extendedData, structuredWriter); - default: - throw new Error("unreachable"); - } -} - -function getChildPropertiesForNode(node: Node): readonly (string | undefined)[] | undefined { - return childProperties[node.kind]; -} - -// Returns whether a value is a NodeArray (array-like with pos and end). -function isNodeArray(value: any): value is NodeArray { - return Array.isArray(value) && typeof (value as any).pos === "number" && typeof (value as any).end === "number"; -} - -/** - * Encode a SourceFile AST node into the binary format. - */ -export function encodeSourceFile(sourceFile: SourceFile): Uint8Array { - return encodeNode(sourceFile); -} - -/** - * Encode an arbitrary AST node into the binary format. - * When encoding a non-SourceFile node, the header hash and parse options fields will be zero. - */ -export function encodeNode(node: Node): Uint8Array { - const strs = new StringTable(); - const extendedDataValues: number[] = []; - const structuredWriter = new MsgpackWriter(); - - // We'll build an array of uint32 values for the nodes section, 7 per node - const nodeValues: number[] = []; - - // Nil node (index 0) - nodeValues.push(0, 0, 0, 0, 0, 0, 0); - - let nodeCount = 0; - let parentIndex = 0; - let prevIndex = 0; - - function visitNode(node: Node): void { - nodeCount++; - const currentIndex = nodeCount; - - if (prevIndex !== 0) { - // Set next pointer on previous sibling - nodeValues[prevIndex * NODE_FIELDS + NODE_FIELD_NEXT] = currentIndex; - } - - const data = getNodeData(node, strs, extendedDataValues, structuredWriter); - nodeValues.push( - node.kind, - node.pos >= 0 ? node.pos : 0, - node.end >= 0 ? node.end : 0, - 0, // next (filled in later) - parentIndex, - data, - node.flags, - ); - - const saveParentIndex = parentIndex; - const savePrevIndex = prevIndex; - parentIndex = currentIndex; - prevIndex = 0; - - visitChildren(node); - - prevIndex = currentIndex; - parentIndex = saveParentIndex; - } - - function visitNodeList(list: NodeArray): void { - if (!list) { - return; - } - - nodeCount++; - const currentIndex = nodeCount; - - if (prevIndex !== 0) { - nodeValues[prevIndex * NODE_FIELDS + NODE_FIELD_NEXT] = currentIndex; - } - - nodeValues.push( - KIND_NODE_LIST, - list.pos >= 0 ? list.pos : 0, - list.end >= 0 ? list.end : 0, - 0, // next - parentIndex, - list.length, // data for NodeList is its length - list.hasTrailingComma ? 1 : 0, // NodeLists have no AST flags; this slot is reused for hasTrailingComma - ); - - const saveParentIndex = parentIndex; - parentIndex = currentIndex; - prevIndex = 0; - - for (const child of list) { - visitNode(child); - } - - prevIndex = currentIndex; - parentIndex = saveParentIndex; - } - - function visitChildren(node: Node): void { - const props = getChildPropertiesForNode(node); - const n = node as any; - - if (props) { - for (const propName of props) { - if (propName === undefined) continue; - const child = n[propName]; - if (child === undefined || child === null) continue; - if (isNodeArray(child)) { - visitNodeList(child); - } - else { - visitNode(child); - } - } - } - } - - // Encode root node - nodeCount++; - parentIndex++; - const rootData = getNodeData(node, strs, extendedDataValues, structuredWriter); - nodeValues.push( - node.kind, - node.pos >= 0 ? node.pos : 0, - node.end >= 0 ? node.end : 0, - 0, - 0, - rootData, - node.flags, - ); - - const saveParent = parentIndex; - prevIndex = 0; - parentIndex = 1; // root is at index 1 - visitChildren(node); - parentIndex = saveParent; - - // Encode extended data section - const extendedDataBytes = new Uint8Array(extendedDataValues.length * 4); - const extView = new DataView(extendedDataBytes.buffer); - for (let i = 0; i < extendedDataValues.length; i++) { - extView.setUint32(i * 4, extendedDataValues[i], true); - } - - // Encode structured data section - const structuredDataBytes = structuredWriter.finish(); - - // Encode string table - const strsBytes = strs.encode(); - - // Encode nodes section - const nodesBytes = new Uint8Array(nodeValues.length * 4); - const nodesView = new DataView(nodesBytes.buffer); - for (let i = 0; i < nodeValues.length; i++) { - nodesView.setUint32(i * 4, nodeValues[i] >>> 0, true); - } - - // Calculate section offsets - const offsetStringTableOffsets = HEADER_SIZE; - const offsetStringTableData = HEADER_SIZE + strs.offsetsCount() * 4; - const offsetExtendedData = offsetStringTableData + strs.stringByteLength(); - const offsetStructuredData = offsetExtendedData + extendedDataBytes.length; - const offsetNodes = offsetStructuredData + structuredDataBytes.length; - - // Build header - const header = new Uint8Array(HEADER_SIZE); - const headerView = new DataView(header.buffer); - const metadata = PROTOCOL_VERSION << 24; - headerView.setUint32(HEADER_OFFSET_METADATA, metadata, true); - // bytes 4-19: hash (zero for non-SourceFile, we don't have access to xxh3 here) - // byte 20-23: parse options (zero for non-SourceFile) - headerView.setUint32(HEADER_OFFSET_STRING_TABLE_OFFSETS, offsetStringTableOffsets, true); - headerView.setUint32(HEADER_OFFSET_STRING_TABLE, offsetStringTableData, true); - headerView.setUint32(HEADER_OFFSET_EXTENDED_DATA, offsetExtendedData, true); - headerView.setUint32(HEADER_OFFSET_STRUCTURED_DATA, offsetStructuredData, true); - headerView.setUint32(HEADER_OFFSET_NODES, offsetNodes, true); - - // Concatenate all sections - const result = new Uint8Array(header.length + strsBytes.length + extendedDataBytes.length + structuredDataBytes.length + nodesBytes.length); - result.set(header, 0); - result.set(strsBytes, HEADER_SIZE); - result.set(extendedDataBytes, offsetExtendedData); - result.set(structuredDataBytes, offsetStructuredData); - result.set(nodesBytes, offsetNodes); - return result; -} - -/** - * Encode a Uint8Array to a base64 string. - */ -export function uint8ArrayToBase64(data: Uint8Array): string { - return Buffer.from(data).toString("base64"); -} +import type { + FileReference, + LiteralLikeNode, + Node, + NodeArray, + SourceFile, + TemplateLiteralLikeNode, +} from "../../ast/index.ts"; +import { SyntaxKind } from "../../ast/index.ts"; +import { + getNodeCommonData, + getNodeDataType, +} from "./encoder.generated.ts"; +import { MsgpackWriter } from "./msgpack.ts"; +import { + childProperties, + HEADER_OFFSET_EXTENDED_DATA, + HEADER_OFFSET_METADATA, + HEADER_OFFSET_NODES, + HEADER_OFFSET_STRING_TABLE, + HEADER_OFFSET_STRING_TABLE_OFFSETS, + HEADER_OFFSET_STRUCTURED_DATA, + HEADER_SIZE, + KIND_NODE_LIST, + NODE_DATA_TYPE_CHILDREN, + NODE_DATA_TYPE_EXTENDED, + NODE_DATA_TYPE_STRING, + NODE_LEN, + PROTOCOL_VERSION, +} from "./protocol.ts"; + +const NODE_FIELDS = NODE_LEN / 4; +const NODE_FIELD_NEXT = 3; +const NO_STRUCTURED_DATA = 0xFFFFFFFF; + +// String table that accumulates strings into a flat byte pool. +class StringTable { + private parts: string[]; + private byteLen: number; + private offsets: number[]; + + constructor() { + this.parts = []; + this.byteLen = 0; + this.offsets = []; + } + + add(text: string): number { + const index = this.offsets.length; + const encoder = cachedEncoder(); + const encodedLength = encoder.encode(text).length; + const offset = this.byteLen; + this.parts.push(text); + this.byteLen += encodedLength; + this.offsets.push(offset, offset + encodedLength); + return index; + } + + encode(): Uint8Array { + const encoder = cachedEncoder(); + const dataBytes = encoder.encode(this.parts.join("")); + const offsetBytes = new Uint8Array(this.offsets.length * 4); + const view = new DataView(offsetBytes.buffer); + for (let i = 0; i < this.offsets.length; i++) { + view.setUint32(i * 4, this.offsets[i], true); + } + const result = new Uint8Array(offsetBytes.length + dataBytes.length); + result.set(offsetBytes, 0); + result.set(dataBytes, offsetBytes.length); + return result; + } + + stringByteLength(): number { + return this.byteLen; + } + + offsetsCount(): number { + return this.offsets.length; + } +} + +let _encoder: TextEncoder | undefined; +function cachedEncoder(): TextEncoder { + return _encoder ??= new TextEncoder(); +} + +function getChildrenPropertyMask(node: Node): number { + const kind = node.kind; + + const props = childProperties[kind]; + if (!props) { + return 0; + } + const n = node as unknown as Record; + let mask = 0; + for (let i = 0; i < props.length; i++) { + const prop = props[i]; + if (prop !== undefined && isChildPresent(n[prop])) { + mask |= 1 << i; + } + } + return mask; +} + +// A child is "present" if it's non-null/non-undefined. +// This matches the Go encoder's behavior where non-nil NodeLists (even empty) +// are treated as present, and only nil NodeLists are absent. +function isChildPresent(v: unknown): boolean { + if (v === undefined || v === null) return false; + return true; +} + +function recordNodeStrings(node: Node, strs: StringTable): number { + return strs.add((node as LiteralLikeNode).text ?? ""); +} + +function encodeFileReferences(refs: readonly FileReference[] | undefined, writer: MsgpackWriter): number { + if (!refs || refs.length === 0) return NO_STRUCTURED_DATA; + const offset = writer.finish().length; + writer.writeArrayHeader(refs.length); + for (const ref of refs) { + writer.writeArrayHeader(5); + writer.writeUint(ref.pos); + writer.writeUint(ref.end); + writer.writeString(ref.fileName); + writer.writeUint(ref.resolutionMode ?? 0); + writer.writeBool(ref.preserve ?? false); + } + return offset; +} + +function recordExtendedData(node: Node, strs: StringTable, extendedData: number[], structuredWriter: MsgpackWriter): number { + const offset = extendedData.length * 4; + if (node.kind === SyntaxKind.SourceFile) { + const sf = node as SourceFile; + const textIndex = strs.add(sf.text); + const fileNameIndex = strs.add(sf.fileName); + const pathIndex = strs.add(sf.path); + const referencedFilesOffset = encodeFileReferences(sf.referencedFiles, structuredWriter); + const typeRefDirectivesOffset = encodeFileReferences(sf.typeReferenceDirectives, structuredWriter); + const libRefDirectivesOffset = encodeFileReferences(sf.libReferenceDirectives, structuredWriter); + // Content-mapper metadata is program-owned and supplied only by the Go encoder. + extendedData.push(textIndex, fileNameIndex, pathIndex, sf.languageVariant, sf.scriptKind, referencedFilesOffset, typeRefDirectivesOffset, libRefDirectivesOffset, NO_STRUCTURED_DATA, NO_STRUCTURED_DATA, NO_STRUCTURED_DATA, 0, textIndex, NO_STRUCTURED_DATA, NO_STRUCTURED_DATA, NO_STRUCTURED_DATA, NO_STRUCTURED_DATA, NO_STRUCTURED_DATA, NO_STRUCTURED_DATA); + } + else if ( + node.kind === SyntaxKind.TemplateHead || + node.kind === SyntaxKind.TemplateMiddle || + node.kind === SyntaxKind.TemplateTail + ) { + const tmpl = node as TemplateLiteralLikeNode; + const text: string = tmpl.text ?? ""; + const rawText: string = tmpl.rawText ?? ""; + const templateFlags: number = tmpl.templateFlags ?? 0; + const textIndex = strs.add(text); + const rawTextIndex = strs.add(rawText); + extendedData.push(textIndex, rawTextIndex, templateFlags); + } + else { + // StringLiteral, NumericLiteral, BigIntLiteral, RegularExpressionLiteral, + // NoSubstitutionTemplateLiteral — format: [textIndex, tokenFlags] + const n = node as any; + const text: string = n.text ?? ""; + const tokenFlags: number = n.tokenFlags ?? 0; + const textIndex = strs.add(text); + extendedData.push(textIndex, tokenFlags); + } + return offset; +} + +function getNodeData(node: Node, strs: StringTable, extendedData: number[], structuredWriter: MsgpackWriter): number { + const t = getNodeDataType(node.kind); + const common = getNodeCommonData(node); + switch (t) { + case NODE_DATA_TYPE_CHILDREN: + return t | common | getChildrenPropertyMask(node); + case NODE_DATA_TYPE_STRING: + return t | common | recordNodeStrings(node, strs); + case NODE_DATA_TYPE_EXTENDED: + return t | common | recordExtendedData(node, strs, extendedData, structuredWriter); + default: + throw new Error("unreachable"); + } +} + +function getChildPropertiesForNode(node: Node): readonly (string | undefined)[] | undefined { + return childProperties[node.kind]; +} + +// Returns whether a value is a NodeArray (array-like with pos and end). +function isNodeArray(value: any): value is NodeArray { + return Array.isArray(value) && typeof (value as any).pos === "number" && typeof (value as any).end === "number"; +} + +/** + * Encode a SourceFile AST node into the binary format. + */ +export function encodeSourceFile(sourceFile: SourceFile): Uint8Array { + return encodeNode(sourceFile); +} + +/** + * Encode an arbitrary AST node into the binary format. + * When encoding a non-SourceFile node, the header hash and parse options fields will be zero. + */ +export function encodeNode(node: Node): Uint8Array { + const strs = new StringTable(); + const extendedDataValues: number[] = []; + const structuredWriter = new MsgpackWriter(); + + // We'll build an array of uint32 values for the nodes section, 7 per node + const nodeValues: number[] = []; + + // Nil node (index 0) + nodeValues.push(0, 0, 0, 0, 0, 0, 0); + + let nodeCount = 0; + let parentIndex = 0; + let prevIndex = 0; + + function visitNode(node: Node): void { + nodeCount++; + const currentIndex = nodeCount; + + if (prevIndex !== 0) { + // Set next pointer on previous sibling + nodeValues[prevIndex * NODE_FIELDS + NODE_FIELD_NEXT] = currentIndex; + } + + const data = getNodeData(node, strs, extendedDataValues, structuredWriter); + nodeValues.push( + node.kind, + node.pos >= 0 ? node.pos : 0, + node.end >= 0 ? node.end : 0, + 0, // next (filled in later) + parentIndex, + data, + node.flags, + ); + + const saveParentIndex = parentIndex; + const savePrevIndex = prevIndex; + parentIndex = currentIndex; + prevIndex = 0; + + visitChildren(node); + + prevIndex = currentIndex; + parentIndex = saveParentIndex; + } + + function visitNodeList(list: NodeArray): void { + if (!list) { + return; + } + + nodeCount++; + const currentIndex = nodeCount; + + if (prevIndex !== 0) { + nodeValues[prevIndex * NODE_FIELDS + NODE_FIELD_NEXT] = currentIndex; + } + + nodeValues.push( + KIND_NODE_LIST, + list.pos >= 0 ? list.pos : 0, + list.end >= 0 ? list.end : 0, + 0, // next + parentIndex, + list.length, // data for NodeList is its length + list.hasTrailingComma ? 1 : 0, // NodeLists have no AST flags; this slot is reused for hasTrailingComma + ); + + const saveParentIndex = parentIndex; + parentIndex = currentIndex; + prevIndex = 0; + + for (const child of list) { + visitNode(child); + } + + prevIndex = currentIndex; + parentIndex = saveParentIndex; + } + + function visitChildren(node: Node): void { + const props = getChildPropertiesForNode(node); + const n = node as any; + + if (props) { + for (const propName of props) { + if (propName === undefined) continue; + const child = n[propName]; + if (child === undefined || child === null) continue; + if (isNodeArray(child)) { + visitNodeList(child); + } + else { + visitNode(child); + } + } + } + } + + // Encode root node + nodeCount++; + parentIndex++; + const rootData = getNodeData(node, strs, extendedDataValues, structuredWriter); + nodeValues.push( + node.kind, + node.pos >= 0 ? node.pos : 0, + node.end >= 0 ? node.end : 0, + 0, + 0, + rootData, + node.flags, + ); + + const saveParent = parentIndex; + prevIndex = 0; + parentIndex = 1; // root is at index 1 + visitChildren(node); + parentIndex = saveParent; + + // Encode extended data section + const extendedDataBytes = new Uint8Array(extendedDataValues.length * 4); + const extView = new DataView(extendedDataBytes.buffer); + for (let i = 0; i < extendedDataValues.length; i++) { + extView.setUint32(i * 4, extendedDataValues[i], true); + } + + // Encode structured data section + const structuredDataBytes = structuredWriter.finish(); + + // Encode string table + const strsBytes = strs.encode(); + + // Encode nodes section + const nodesBytes = new Uint8Array(nodeValues.length * 4); + const nodesView = new DataView(nodesBytes.buffer); + for (let i = 0; i < nodeValues.length; i++) { + nodesView.setUint32(i * 4, nodeValues[i] >>> 0, true); + } + + // Calculate section offsets + const offsetStringTableOffsets = HEADER_SIZE; + const offsetStringTableData = HEADER_SIZE + strs.offsetsCount() * 4; + const offsetExtendedData = offsetStringTableData + strs.stringByteLength(); + const offsetStructuredData = offsetExtendedData + extendedDataBytes.length; + const offsetNodes = offsetStructuredData + structuredDataBytes.length; + + // Build header + const header = new Uint8Array(HEADER_SIZE); + const headerView = new DataView(header.buffer); + const metadata = PROTOCOL_VERSION << 24; + headerView.setUint32(HEADER_OFFSET_METADATA, metadata, true); + // bytes 4-19: hash (zero for non-SourceFile, we don't have access to xxh3 here) + // byte 20-23: parse options (zero for non-SourceFile) + headerView.setUint32(HEADER_OFFSET_STRING_TABLE_OFFSETS, offsetStringTableOffsets, true); + headerView.setUint32(HEADER_OFFSET_STRING_TABLE, offsetStringTableData, true); + headerView.setUint32(HEADER_OFFSET_EXTENDED_DATA, offsetExtendedData, true); + headerView.setUint32(HEADER_OFFSET_STRUCTURED_DATA, offsetStructuredData, true); + headerView.setUint32(HEADER_OFFSET_NODES, offsetNodes, true); + + // Concatenate all sections + const result = new Uint8Array(header.length + strsBytes.length + extendedDataBytes.length + structuredDataBytes.length + nodesBytes.length); + result.set(header, 0); + result.set(strsBytes, HEADER_SIZE); + result.set(extendedDataBytes, offsetExtendedData); + result.set(structuredDataBytes, offsetStructuredData); + result.set(nodesBytes, offsetNodes); + return result; +} + +/** + * Encode a Uint8Array to a base64 string. + */ +export function uint8ArrayToBase64(data: Uint8Array): string { + return Buffer.from(data).toString("base64"); +} diff --git a/packages/typescript/src/api/node/msgpack.ts b/packages/typescript/src/api/node/msgpack.ts index 50248d27bda31..dc9c80da1ee22 100644 --- a/packages/typescript/src/api/node/msgpack.ts +++ b/packages/typescript/src/api/node/msgpack.ts @@ -1,224 +1,224 @@ -// Minimal msgpack encoder/decoder. -// Supports: arrays, unsigned integers, strings, booleans, binary data. - -import { Wtf8Decoder } from "./wtf8.ts"; - -// ── MessagePack format constants ──────────────────────────────────── -export const MSGPACK_FIXARRAY3 = 0x93; // 3-element fixarray -export const MSGPACK_BIN8 = 0xc4; -export const MSGPACK_BIN16 = 0xc5; -export const MSGPACK_BIN32 = 0xc6; -export const MSGPACK_UINT8 = 0xcc; - -// ── Bin header helpers ────────────────────────────────────────────── - -/** Compute the MessagePack bin header size for a given data length. */ -export function binHeaderSize(len: number): number { - if (len < 0x100) return 2; // BIN8: marker + 1-byte size - if (len < 0x10000) return 3; // BIN16: marker + 2-byte size - return 5; // BIN32: marker + 4-byte size -} - -/** Write a MessagePack bin header into `buf` at `off`, return new offset. */ -export function writeBinHeader(buf: Uint8Array, off: number, len: number): number { - if (len < 0x100) { - buf[off++] = MSGPACK_BIN8; - buf[off++] = len; - } - else if (len < 0x10000) { - buf[off++] = MSGPACK_BIN16; - buf[off++] = (len >>> 8) & 0xff; - buf[off++] = len & 0xff; - } - else { - buf[off++] = MSGPACK_BIN32; - buf[off++] = (len >>> 24) & 0xff; - buf[off++] = (len >>> 16) & 0xff; - buf[off++] = (len >>> 8) & 0xff; - buf[off++] = len & 0xff; - } - return off; -} - -const encoder = new TextEncoder(); -const decoder = new Wtf8Decoder(); - -export class MsgpackWriter { - private buf: Uint8Array; - private view: DataView; - private pos: number; - - constructor(initialSize = 256) { - this.buf = new Uint8Array(initialSize); - this.view = new DataView(this.buf.buffer); - this.pos = 0; - } - - private ensure(n: number): void { - if (this.pos + n > this.buf.length) { - let newSize = this.buf.length * 2; - while (newSize < this.pos + n) newSize *= 2; - const next = new Uint8Array(newSize); - next.set(this.buf); - this.buf = next; - this.view = new DataView(this.buf.buffer); - } - } - - writeArrayHeader(length: number): void { - if (length <= 0x0f) { - this.ensure(1); - this.buf[this.pos++] = 0x90 | length; - } - else if (length <= 0xffff) { - this.ensure(3); - this.buf[this.pos++] = 0xdc; - this.view.setUint16(this.pos, length, false); - this.pos += 2; - } - else { - this.ensure(5); - this.buf[this.pos++] = 0xdd; - this.view.setUint32(this.pos, length, false); - this.pos += 4; - } - } - - writeUint(value: number): void { - if (value <= 0x7f) { - this.ensure(1); - this.buf[this.pos++] = value; - } - else if (value <= 0xff) { - this.ensure(2); - this.buf[this.pos++] = 0xcc; - this.buf[this.pos++] = value; - } - else if (value <= 0xffff) { - this.ensure(3); - this.buf[this.pos++] = 0xcd; - this.view.setUint16(this.pos, value, false); - this.pos += 2; - } - else { - this.ensure(5); - this.buf[this.pos++] = 0xce; - this.view.setUint32(this.pos, value, false); - this.pos += 4; - } - } - - writeString(str: string): void { - const encoded = encoder.encode(str); - const len = encoded.length; - if (len <= 0x1f) { - this.ensure(1 + len); - this.buf[this.pos++] = 0xa0 | len; - } - else if (len <= 0xff) { - this.ensure(2 + len); - this.buf[this.pos++] = 0xd9; - this.buf[this.pos++] = len; - } - else if (len <= 0xffff) { - this.ensure(3 + len); - this.buf[this.pos++] = 0xda; - this.view.setUint16(this.pos, len, false); - this.pos += 2; - } - else { - this.ensure(5 + len); - this.buf[this.pos++] = 0xdb; - this.view.setUint32(this.pos, len, false); - this.pos += 4; - } - this.buf.set(encoded, this.pos); - this.pos += len; - } - - writeBool(value: boolean): void { - this.ensure(1); - this.buf[this.pos++] = value ? 0xc3 : 0xc2; - } - - finish(): Uint8Array { - return this.buf.subarray(0, this.pos); - } -} - -export class MsgpackReader { - private buf: Uint8Array; - private view: DataView; - private pos: number; - - constructor(data: Uint8Array, offset = 0) { - this.buf = data; - this.view = new DataView(data.buffer, data.byteOffset, data.byteLength); - this.pos = offset; - } - - readArrayHeader(): number { - const byte = this.buf[this.pos++]; - if ((byte & 0xf0) === 0x90) return byte & 0x0f; - if (byte === 0xdc) { - const len = this.view.getUint16(this.pos, false); - this.pos += 2; - return len; - } - if (byte === 0xdd) { - const len = this.view.getUint32(this.pos, false); - this.pos += 4; - return len; - } - throw new Error(`Expected array header, got 0x${byte.toString(16)}`); - } - - readUint(): number { - const byte = this.buf[this.pos++]; - if (byte <= 0x7f) return byte; - if (byte === 0xcc) return this.buf[this.pos++]; - if (byte === 0xcd) { - const val = this.view.getUint16(this.pos, false); - this.pos += 2; - return val; - } - if (byte === 0xce) { - const val = this.view.getUint32(this.pos, false); - this.pos += 4; - return val; - } - throw new Error(`Expected uint, got 0x${byte.toString(16)}`); - } - - readString(): string { - const byte = this.buf[this.pos++]; - let len: number; - if ((byte & 0xe0) === 0xa0) { - len = byte & 0x1f; - } - else if (byte === 0xd9) { - len = this.buf[this.pos++]; - } - else if (byte === 0xda) { - len = this.view.getUint16(this.pos, false); - this.pos += 2; - } - else if (byte === 0xdb) { - len = this.view.getUint32(this.pos, false); - this.pos += 4; - } - else { - throw new Error(`Expected string, got 0x${byte.toString(16)}`); - } - const str = decoder.decode(this.buf.subarray(this.pos, this.pos + len)); - this.pos += len; - return str; - } - - readBool(): boolean { - const byte = this.buf[this.pos++]; - if (byte === 0xc3) return true; - if (byte === 0xc2) return false; - throw new Error(`Expected bool, got 0x${byte.toString(16)}`); - } -} +// Minimal msgpack encoder/decoder. +// Supports: arrays, unsigned integers, strings, booleans, binary data. + +import { Wtf8Decoder } from "./wtf8.ts"; + +// ── MessagePack format constants ──────────────────────────────────── +export const MSGPACK_FIXARRAY3 = 0x93; // 3-element fixarray +export const MSGPACK_BIN8 = 0xc4; +export const MSGPACK_BIN16 = 0xc5; +export const MSGPACK_BIN32 = 0xc6; +export const MSGPACK_UINT8 = 0xcc; + +// ── Bin header helpers ────────────────────────────────────────────── + +/** Compute the MessagePack bin header size for a given data length. */ +export function binHeaderSize(len: number): number { + if (len < 0x100) return 2; // BIN8: marker + 1-byte size + if (len < 0x10000) return 3; // BIN16: marker + 2-byte size + return 5; // BIN32: marker + 4-byte size +} + +/** Write a MessagePack bin header into `buf` at `off`, return new offset. */ +export function writeBinHeader(buf: Uint8Array, off: number, len: number): number { + if (len < 0x100) { + buf[off++] = MSGPACK_BIN8; + buf[off++] = len; + } + else if (len < 0x10000) { + buf[off++] = MSGPACK_BIN16; + buf[off++] = (len >>> 8) & 0xff; + buf[off++] = len & 0xff; + } + else { + buf[off++] = MSGPACK_BIN32; + buf[off++] = (len >>> 24) & 0xff; + buf[off++] = (len >>> 16) & 0xff; + buf[off++] = (len >>> 8) & 0xff; + buf[off++] = len & 0xff; + } + return off; +} + +const encoder = new TextEncoder(); +const decoder = new Wtf8Decoder(); + +export class MsgpackWriter { + private buf: Uint8Array; + private view: DataView; + private pos: number; + + constructor(initialSize = 256) { + this.buf = new Uint8Array(initialSize); + this.view = new DataView(this.buf.buffer); + this.pos = 0; + } + + private ensure(n: number): void { + if (this.pos + n > this.buf.length) { + let newSize = this.buf.length * 2; + while (newSize < this.pos + n) newSize *= 2; + const next = new Uint8Array(newSize); + next.set(this.buf); + this.buf = next; + this.view = new DataView(this.buf.buffer); + } + } + + writeArrayHeader(length: number): void { + if (length <= 0x0f) { + this.ensure(1); + this.buf[this.pos++] = 0x90 | length; + } + else if (length <= 0xffff) { + this.ensure(3); + this.buf[this.pos++] = 0xdc; + this.view.setUint16(this.pos, length, false); + this.pos += 2; + } + else { + this.ensure(5); + this.buf[this.pos++] = 0xdd; + this.view.setUint32(this.pos, length, false); + this.pos += 4; + } + } + + writeUint(value: number): void { + if (value <= 0x7f) { + this.ensure(1); + this.buf[this.pos++] = value; + } + else if (value <= 0xff) { + this.ensure(2); + this.buf[this.pos++] = 0xcc; + this.buf[this.pos++] = value; + } + else if (value <= 0xffff) { + this.ensure(3); + this.buf[this.pos++] = 0xcd; + this.view.setUint16(this.pos, value, false); + this.pos += 2; + } + else { + this.ensure(5); + this.buf[this.pos++] = 0xce; + this.view.setUint32(this.pos, value, false); + this.pos += 4; + } + } + + writeString(str: string): void { + const encoded = encoder.encode(str); + const len = encoded.length; + if (len <= 0x1f) { + this.ensure(1 + len); + this.buf[this.pos++] = 0xa0 | len; + } + else if (len <= 0xff) { + this.ensure(2 + len); + this.buf[this.pos++] = 0xd9; + this.buf[this.pos++] = len; + } + else if (len <= 0xffff) { + this.ensure(3 + len); + this.buf[this.pos++] = 0xda; + this.view.setUint16(this.pos, len, false); + this.pos += 2; + } + else { + this.ensure(5 + len); + this.buf[this.pos++] = 0xdb; + this.view.setUint32(this.pos, len, false); + this.pos += 4; + } + this.buf.set(encoded, this.pos); + this.pos += len; + } + + writeBool(value: boolean): void { + this.ensure(1); + this.buf[this.pos++] = value ? 0xc3 : 0xc2; + } + + finish(): Uint8Array { + return this.buf.subarray(0, this.pos); + } +} + +export class MsgpackReader { + private buf: Uint8Array; + private view: DataView; + private pos: number; + + constructor(data: Uint8Array, offset = 0) { + this.buf = data; + this.view = new DataView(data.buffer, data.byteOffset, data.byteLength); + this.pos = offset; + } + + readArrayHeader(): number { + const byte = this.buf[this.pos++]; + if ((byte & 0xf0) === 0x90) return byte & 0x0f; + if (byte === 0xdc) { + const len = this.view.getUint16(this.pos, false); + this.pos += 2; + return len; + } + if (byte === 0xdd) { + const len = this.view.getUint32(this.pos, false); + this.pos += 4; + return len; + } + throw new Error(`Expected array header, got 0x${byte.toString(16)}`); + } + + readUint(): number { + const byte = this.buf[this.pos++]; + if (byte <= 0x7f) return byte; + if (byte === 0xcc) return this.buf[this.pos++]; + if (byte === 0xcd) { + const val = this.view.getUint16(this.pos, false); + this.pos += 2; + return val; + } + if (byte === 0xce) { + const val = this.view.getUint32(this.pos, false); + this.pos += 4; + return val; + } + throw new Error(`Expected uint, got 0x${byte.toString(16)}`); + } + + readString(): string { + const byte = this.buf[this.pos++]; + let len: number; + if ((byte & 0xe0) === 0xa0) { + len = byte & 0x1f; + } + else if (byte === 0xd9) { + len = this.buf[this.pos++]; + } + else if (byte === 0xda) { + len = this.view.getUint16(this.pos, false); + this.pos += 2; + } + else if (byte === 0xdb) { + len = this.view.getUint32(this.pos, false); + this.pos += 4; + } + else { + throw new Error(`Expected string, got 0x${byte.toString(16)}`); + } + const str = decoder.decode(this.buf.subarray(this.pos, this.pos + len)); + this.pos += len; + return str; + } + + readBool(): boolean { + const byte = this.buf[this.pos++]; + if (byte === 0xc3) return true; + if (byte === 0xc2) return false; + throw new Error(`Expected bool, got 0x${byte.toString(16)}`); + } +} diff --git a/packages/typescript/src/api/node/node.generated.ts b/packages/typescript/src/api/node/node.generated.ts index a601319e3d9b0..ef140442d0ea0 100644 --- a/packages/typescript/src/api/node/node.generated.ts +++ b/packages/typescript/src/api/node/node.generated.ts @@ -1,922 +1,922 @@ -// Code generated by tools/scripts/tsc/generate-encoder.ts. DO NOT EDIT. - -import { - getTokenPosOfNode, - ModifierFlags, - type Node, - type NodeArray, - type SourceFile, - SyntaxKind, -} from "../../ast/index.ts"; -import { - modifierToFlag, - NODE_CHILD_MASK, - NODE_DATA_TYPE_MASK, - NODE_EXTENDED_DATA_MASK, - NODE_STRING_INDEX_MASK, - type NodeDataType, - popcount8, - RemoteNodeBase, - type SourceFileInfo, -} from "./node.infrastructure.ts"; -import { - childProperties, - KIND_NODE_LIST, - NODE_DATA_TYPE_CHILDREN, - NODE_DATA_TYPE_EXTENDED, - NODE_DATA_TYPE_STRING, - NODE_LEN, - NODE_OFFSET_DATA, - NODE_OFFSET_END, - NODE_OFFSET_FLAGS, - NODE_OFFSET_KIND, - NODE_OFFSET_NEXT, - NODE_OFFSET_PARENT, - NODE_OFFSET_POS, -} from "./protocol.ts"; - -export class RemoteNodeList extends Array implements NodeArray { - // Inherited Array methods like filter/map/slice use ArraySpeciesCreate, which would - // otherwise call `new RemoteNodeList(length)` and fail. Produce a plain Array instead. - static get [Symbol.species](): ArrayConstructor { - return Array; - } - - parent: RemoteNode; - transformFlags: number = 0; - protected view: DataView; - protected index: number; - private _byteIndex: number; - // Cursor memoizing the last resolved (logical index -> node index) so that - // sequential forward access (index loops and list[i], plus forEach/map/ - // reduce/filter) resumes instead of re-walking from the head, turning an - // O(n) pass over the whole list from O(n^2) into O(n). - private _cursorIndex: number = 0; - private _cursorNodeIndex: number = 0; - - get pos(): number { - return this.view.getUint32(this._byteIndex + NODE_OFFSET_POS, true); - } - - get end(): number { - return this.view.getUint32(this._byteIndex + NODE_OFFSET_END, true); - } - - get next(): number { - return this.view.getUint32(this._byteIndex + NODE_OFFSET_NEXT, true); - } - - private get data(): number { - return this.view.getUint32(this._byteIndex + NODE_OFFSET_DATA, true); - } - - get hasTrailingComma(): boolean { - return (this.view.getUint32(this._byteIndex + NODE_OFFSET_FLAGS, true) & 1) !== 0; - } - - private sourceFile: SourceFileInfo; - - constructor(view: DataView, index: number, parent: RemoteNode, sourceFile: SourceFileInfo, offsetNodes: number) { - super(view.getUint32(offsetNodes + index * NODE_LEN + NODE_OFFSET_DATA, true)); - this.view = view; - this.index = index; - this.parent = parent; - this.sourceFile = sourceFile; - this._byteIndex = offsetNodes + index * NODE_LEN; - this._cursorNodeIndex = index + 1; - - const length = this.length; - for (let i = 16; i < length; i++) { - Object.defineProperty(this, i, { - get() { - return this.at(i); - }, - }); - } - } - get 0(): RemoteNode { - return this.at(0); - } - get 1(): RemoteNode { - return this.at(1); - } - get 2(): RemoteNode { - return this.at(2); - } - get 3(): RemoteNode { - return this.at(3); - } - get 4(): RemoteNode { - return this.at(4); - } - get 5(): RemoteNode { - return this.at(5); - } - get 6(): RemoteNode { - return this.at(6); - } - get 7(): RemoteNode { - return this.at(7); - } - get 8(): RemoteNode { - return this.at(8); - } - get 9(): RemoteNode { - return this.at(9); - } - get 10(): RemoteNode { - return this.at(10); - } - get 11(): RemoteNode { - return this.at(11); - } - get 12(): RemoteNode { - return this.at(12); - } - get 13(): RemoteNode { - return this.at(13); - } - get 14(): RemoteNode { - return this.at(14); - } - get 15(): RemoteNode { - return this.at(15); - } - - *[Symbol.iterator](): ArrayIterator { - if (!this.length) return; - let next = this.index + 1; - while (next) { - const child = this.getOrCreateChildAtNodeIndex(next); - next = child.next; - yield child as RemoteNode; - } - } - - forEachNode(visitNode: (node: RemoteNode) => T | undefined): T | undefined { - if (!this.length) return; - let next = this.index + 1; - while (next) { - const child = this.getOrCreateChildAtNodeIndex(next); - next = child.next; - const result = visitNode(child as RemoteNode); - if (result) return result; - } - } - - at(index: number): RemoteNode { - if (!Number.isInteger(index)) { - return undefined!; - } - if (index >= this.data || (index < 0 && -index > this.data)) { - return undefined!; - } - if (index < 0) { - index = this.length + index; - } - // Walk the raw buffer following each node's `next` pointer instead of - // materializing every intermediate RemoteNode just to read it. Resume from - // the memoized cursor when possible so sequential forward access is O(1) - // amortized (a full in-order pass is O(n) rather than O(n^2)). - const offsetNodes = this.sourceFile._offsetNodes; - let i: number; - let next: number; - if (index >= this._cursorIndex) { - i = this._cursorIndex; - next = this._cursorNodeIndex; - } - else { - i = 0; - next = this.index + 1; - } - for (; i < index; i++) { - next = this.view.getUint32(offsetNodes + next * NODE_LEN + NODE_OFFSET_NEXT, true); - } - this._cursorIndex = index; - this._cursorNodeIndex = next; - return this.getOrCreateChildAtNodeIndex(next) as RemoteNode; - } - - private getOrCreateChildAtNodeIndex(index: number): RemoteNode | RemoteNodeList { - let child = this.sourceFile.nodes[index]; - if (!child) { - const kind = this.view.getUint32(this.sourceFile._offsetNodes + index * NODE_LEN + NODE_OFFSET_KIND, true); - if (kind === KIND_NODE_LIST) { - throw new Error("NodeList cannot directly contain another NodeList"); - } - const sf = this.sourceFile; - child = new RemoteNode(this.view, index, this.parent, sf, sf._offsetNodes); - sf.nodes[index] = child; - sf._timing?.recordMaterialization(); - } - return child; - } - - __print(): string { - const result = []; - result.push(`kind: NodeList`); - result.push(`index: ${this.index}`); - result.push(`byteIndex: ${this._byteIndex}`); - result.push(`length: ${this.length}`); - return result.join("\n"); - } -} - -export class RemoteNode extends RemoteNodeBase implements Node { - protected static NODE_LEN: number = NODE_LEN; - protected override get sourceFile(): SourceFileInfo { - return this._sourceFile; - } - protected _sourceFile: SourceFileInfo; - get id(): string { - return `${this.index}.${this.kind}.${this.sourceFile.path}`; - } - - constructor(view: DataView, index: number, parent: RemoteNode, sourceFile: SourceFileInfo, offsetNodes: number) { - super(view, index, parent, offsetNodes + index * NODE_LEN); - this._sourceFile = sourceFile; - } - - forEachChild(visitNode: (node: Node) => T, visitList?: (list: NodeArray) => T): T | undefined { - if (this.hasChildren()) { - let next = this.index + 1; - do { - const child = this.getOrCreateChildAtNodeIndex(next); - if (child instanceof RemoteNodeList) { - if (visitList) { - const result = visitList(child); - if (result) { - return result; - } - } - else { - const result = child.forEachNode(visitNode); - if (result) { - return result; - } - } - } - else if (child.kind !== SyntaxKind.JSDoc) { - const result = visitNode(child); - if (result) { - return result; - } - } - next = child.next; - } - while (next); - } - } - - get jsDoc(): readonly Node[] | undefined { - if (!this.hasChildren()) { - return undefined; - } - let result: Node[] | undefined; - let next = this.index + 1; - do { - const child = this.getOrCreateChildAtNodeIndex(next); - if (!(child instanceof RemoteNodeList) && child.kind === SyntaxKind.JSDoc) { - (result ??= []).push(child); - } - next = child.next; - } - while (next); - return result; - } - - getSourceFile(): SourceFile { - return this.sourceFile as unknown as SourceFile; - } - - getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number { - return getTokenPosOfNode(this as unknown as Node, sourceFile ?? this.getSourceFile(), includeJsDocComment); - } - - getFullStart(): number { - return this.pos; - } - - getEnd(): number { - return this.end; - } - - getWidth(sourceFile?: SourceFile): number { - return this.getEnd() - this.getStart(sourceFile); - } - - getFullWidth(): number { - return this.end - this.pos; - } - - getLeadingTriviaWidth(sourceFile?: SourceFile): number { - return this.getStart(sourceFile) - this.pos; - } - - getFullText(sourceFile?: SourceFile): string { - return (sourceFile ?? this.getSourceFile()).text.substring(this.pos, this.end); - } - - getText(sourceFile?: SourceFile): string { - sourceFile ??= this.getSourceFile(); - return sourceFile.text.substring(this.getStart(sourceFile), this.end); - } - - protected getString(index: number): string { - const offsetStringTableOffsets = this.sourceFile._offsetStringTableOffsets; - const start = this.view.getUint32(offsetStringTableOffsets + index * 4, true); - const end = this.view.getUint32(offsetStringTableOffsets + (index + 1) * 4, true); - const offsetStringTable = this.sourceFile._offsetStringTable; - const text = new Uint8Array(this.view.buffer, this.view.byteOffset + offsetStringTable + start, end - start); - return this.sourceFile._decoder.decode(text); - } - - private getOrCreateChildAtNodeIndex(index: number): RemoteNode | RemoteNodeList { - let child = this.sourceFile.nodes[index]; - if (!child) { - const sf = this.sourceFile; - const offsetNodes = sf._offsetNodes; - const kind = this.view.getUint32(offsetNodes + index * NODE_LEN + NODE_OFFSET_KIND, true); - child = kind === KIND_NODE_LIST - ? new RemoteNodeList(this.view, index, this, sf, offsetNodes) - : new RemoteNode(this.view, index, this, sf, offsetNodes); - sf.nodes[index] = child; - sf._timing?.recordMaterialization(); - } - return child; - } - - private hasChildren(): boolean { - if (this._byteIndex >= this.view.byteLength - NODE_LEN) { - return false; - } - const nextNodeParent = this.view.getUint32(this.sourceFile._offsetNodes + (this.index + 1) * NODE_LEN + NODE_OFFSET_PARENT, true); - return nextNodeParent === this.index; - } - - private getNamedChild(propertyName: string): RemoteNode | RemoteNodeList | undefined { - const kind = this.kind; - const propertyNames = childProperties[kind]; - if (!propertyNames) { - return undefined; - } - - const order = propertyNames.indexOf(propertyName); - if (order === -1) { - return undefined; - } - return this.getChildAtOrder(order); - } - - private getChildAtOrder(order: number): RemoteNode | RemoteNodeList | undefined { - if (!this.hasChildren()) return undefined; - const mask = this.childMask; - if (!(mask & (1 << order))) { - // Property is not present - return undefined; - } - - // The property index is `order`, minus the number of zeros in the mask that are in bit positions less - // than the `order`th bit. Example: - // - // This is a MethodDeclaration with mask 0b01110101. The possible properties are - // ["modifiers", "asteriskToken", "name", "postfixToken", "typeParameters", "parameters", "type", "body"] - // (it has modifiers, name, typeParameters, parameters, and type). - // - // | Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | - // | ----- | ---- | ---- | ---------- | -------------- | ------------ | ---- | ------------- | --------- | - // | Value | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 1 | - // | Name | body | type | parameters | typeParameters | postfixToken | name | asteriskToken | modifiers | - // - // We are trying to get the index of "parameters" (bit = 5). - // First, set all the more significant bits to 1: - // - // | Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | - // | ----- | ---- | ---- | ---------- | -------------- | ------------ | ---- | ------------- | --------- | - // | Value | 1 | 1 | 1 | 1 | 0 | 1 | 0 | 1 | - // - // Then, flip the bits: - // - // | Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | - // | ----- | ---- | ---- | ---------- | -------------- | ------------ | ---- | ------------- | --------- | - // | Value | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | - // - // Counting the 1s gives us the number of *missing properties* before the `order`th property. If every property - // were present, we would have `parameters = children[5]`, but since `postfixToken` and `astersiskToken` are - // missing, we have `parameters = children[5 - 2]`. - const propertyIndex = order - popcount8[~(mask | ((0xff << order) & 0xff)) & 0xff]; - let childIndex = this.index + 1; - for (let i = 0; i < propertyIndex; i++) { - // Walk through children via their `next` pointer until we get to the right property index - childIndex = this.view.getUint32(this.sourceFile._offsetNodes + childIndex * NODE_LEN + NODE_OFFSET_NEXT, true); - } - return this.getOrCreateChildAtNodeIndex(childIndex); - } - - __print(): string { - const result = []; - result.push(`index: ${this.index}`); - result.push(`byteIndex: ${this._byteIndex}`); - result.push(`kind: ${SyntaxKind[this.kind]}`); - result.push(`pos: ${this.pos}`); - result.push(`end: ${this.end}`); - result.push(`next: ${this.next}`); - result.push(`parent: ${this.parentIndex}`); - result.push(`data: ${this.data.toString(2).padStart(32, "0")}`); - const dataType = this.dataType === NODE_DATA_TYPE_CHILDREN ? "children" : - this.dataType === NODE_DATA_TYPE_STRING ? "string" : - "extended"; - result.push(`dataType: ${dataType}`); - if (this.dataType === NODE_DATA_TYPE_CHILDREN) { - result.push(`childMask: ${this.childMask.toString(2).padStart(8, "0")}`); - result.push(`childProperties: ${childProperties[this.kind]?.join(", ")}`); - } - return result.join("\n"); - } - - __printChildren(): string { - const result = []; - let next = this.index + 1; - while (next) { - const child = this.getOrCreateChildAtNodeIndex(next); - next = child.next; - result.push(child.__print()); - } - return result.join("\n\n"); - } - - __printSubtree(): string { - const result = [this.__print()]; - this.forEachChild(function visitNode(node) { - result.push((node as RemoteNode).__print()); - node.forEachChild(visitNode); - }, visitList => { - result.push((visitList as RemoteNodeList).__print()); - }); - return result.join("\n\n"); - } - - // ═══ Generated boolean property getters ═══ - - get containsOnlyTriviaWhiteSpaces(): boolean { - return (this.data & (1 << 24)) !== 0; - } - - get isArrayType(): boolean { - return (this.data & (1 << 24)) !== 0; - } - - get isBracketed(): boolean { - return (this.data & (1 << 24)) !== 0; - } - - get isExportEquals(): boolean { - return (this.data & (1 << 24)) !== 0; - } - - get isNameFirst(): boolean { - return (this.data & (1 << 25)) !== 0; - } - - get isTypeOf(): boolean { - return (this.data & (1 << 24)) !== 0; - } - - get isTypeOnly(): boolean { - return (this.data & (1 << 24)) !== 0; - } - - get multiLine(): boolean { - return (this.data & (1 << 24)) !== 0; - } - - // ═══ Generated SyntaxKind union property getters ═══ - - get keyword(): SyntaxKind | undefined { - switch (this.kind) { - case SyntaxKind.ModuleDeclaration: - return (this.data >> 24) & 0x1 ? SyntaxKind.NamespaceKeyword : SyntaxKind.ModuleKeyword; - } - } - - get keywordToken(): SyntaxKind | undefined { - switch (this.kind) { - case SyntaxKind.MetaProperty: - return (this.data >> 24) & 0x1 ? SyntaxKind.NewKeyword : SyntaxKind.ImportKeyword; - } - } - - get operator(): SyntaxKind | undefined { - switch (this.kind) { - case SyntaxKind.PrefixUnaryExpression: { - const idx = (this.data >> 24) & 0x7; - if (idx === 1) return SyntaxKind.MinusToken; - if (idx === 2) return SyntaxKind.TildeToken; - if (idx === 3) return SyntaxKind.ExclamationToken; - if (idx === 4) return SyntaxKind.PlusPlusToken; - if (idx === 5) return SyntaxKind.MinusMinusToken; - return SyntaxKind.PlusToken; - } - case SyntaxKind.PostfixUnaryExpression: - return (this.data >> 24) & 0x1 ? SyntaxKind.MinusMinusToken : SyntaxKind.PlusPlusToken; - case SyntaxKind.TypeOperator: { - const idx = (this.data >> 24) & 0x3; - if (idx === 1) return SyntaxKind.ReadonlyKeyword; - if (idx === 2) return SyntaxKind.UniqueKeyword; - return SyntaxKind.KeyOfKeyword; - } - } - } - - get phaseModifier(): SyntaxKind | undefined { - switch (this.kind) { - case SyntaxKind.ImportClause: { - const idx = (this.data >> 24) & 0x3; - if (idx === 0) return undefined; - return idx === 1 ? SyntaxKind.TypeKeyword : idx === 2 ? SyntaxKind.DeferKeyword : undefined; - } - } - } - - get token(): SyntaxKind | undefined { - switch (this.kind) { - case SyntaxKind.HeritageClause: - return (this.data >> 24) & 0x1 ? SyntaxKind.ImplementsKeyword : SyntaxKind.ExtendsKeyword; - case SyntaxKind.ImportAttributes: - return (this.data >> 25) & 0x1 ? SyntaxKind.AssertKeyword : SyntaxKind.WithKeyword; - } - } - - get templateFlags(): number | undefined { - switch (this.kind) { - case SyntaxKind.TemplateHead: - case SyntaxKind.TemplateMiddle: - case SyntaxKind.TemplateTail: - const extendedDataOffset = this.sourceFile._offsetExtendedData + (this.data & NODE_EXTENDED_DATA_MASK); - return this.view.getUint32(extendedDataOffset + 8, true); - } - } - - get tokenFlags(): number { - switch (this.kind) { - case SyntaxKind.StringLiteral: - case SyntaxKind.NumericLiteral: - case SyntaxKind.BigIntLiteral: - case SyntaxKind.RegularExpressionLiteral: - const extendedDataOffset = this.sourceFile._offsetExtendedData + (this.data & NODE_EXTENDED_DATA_MASK); - return this.view.getUint32(extendedDataOffset + 4, true); - default: - return 0; - } - } - - // ═══ Generated child property getters ═══ - - get argument(): RemoteNode | undefined { - return this.getNamedChild("argument") as RemoteNode; - } - get argumentExpression(): RemoteNode | undefined { - return this.getNamedChild("argumentExpression") as RemoteNode; - } - get arguments(): RemoteNodeList | undefined { - return this.getNamedChild("arguments") as RemoteNodeList; - } - get assertsModifier(): RemoteNode | undefined { - return this.getNamedChild("assertsModifier") as RemoteNode; - } - get asteriskToken(): RemoteNode | undefined { - return this.getNamedChild("asteriskToken") as RemoteNode; - } - get attributes(): RemoteNode | RemoteNodeList | undefined { - return this.getNamedChild("attributes") as RemoteNode | RemoteNodeList; - } - get awaitModifier(): RemoteNode | undefined { - return this.getNamedChild("awaitModifier") as RemoteNode; - } - get block(): RemoteNode | undefined { - return this.getNamedChild("block") as RemoteNode; - } - get body(): RemoteNode | undefined { - return this.getNamedChild("body") as RemoteNode; - } - get caseBlock(): RemoteNode | undefined { - return this.getNamedChild("caseBlock") as RemoteNode; - } - get catchClause(): RemoteNode | undefined { - return this.getNamedChild("catchClause") as RemoteNode; - } - get checkType(): RemoteNode | undefined { - return this.getNamedChild("checkType") as RemoteNode; - } - get children(): RemoteNode | RemoteNodeList | undefined { - return this.getNamedChild("children") as RemoteNode | RemoteNodeList; - } - get className(): RemoteNode | undefined { - return this.getNamedChild("className") as RemoteNode; - } - get clauses(): RemoteNodeList | undefined { - return this.getNamedChild("clauses") as RemoteNodeList; - } - get closingElement(): RemoteNode | undefined { - return this.getNamedChild("closingElement") as RemoteNode; - } - get closingFragment(): RemoteNode | undefined { - return this.getNamedChild("closingFragment") as RemoteNode; - } - get colonToken(): RemoteNode | undefined { - return this.getNamedChild("colonToken") as RemoteNode; - } - get comment(): RemoteNodeList | undefined { - return this.getNamedChild("comment") as RemoteNodeList; - } - get condition(): RemoteNode | undefined { - return this.getNamedChild("condition") as RemoteNode; - } - get constraint(): RemoteNode | undefined { - return this.getNamedChild("constraint") as RemoteNode; - } - get declarationList(): RemoteNode | undefined { - return this.getNamedChild("declarationList") as RemoteNode; - } - get declarations(): RemoteNodeList | undefined { - return this.getNamedChild("declarations") as RemoteNodeList; - } - get defaultType(): RemoteNode | undefined { - return this.getNamedChild("defaultType") as RemoteNode; - } - get dotDotDotToken(): RemoteNode | undefined { - return this.getNamedChild("dotDotDotToken") as RemoteNode; - } - get elements(): RemoteNodeList | undefined { - return this.getNamedChild("elements") as RemoteNodeList; - } - get elementType(): RemoteNode | undefined { - return this.getNamedChild("elementType") as RemoteNode; - } - get elseStatement(): RemoteNode | undefined { - return this.getNamedChild("elseStatement") as RemoteNode; - } - get endOfFileToken(): RemoteNode | undefined { - return this.getNamedChild("endOfFileToken") as RemoteNode; - } - get equalsGreaterThanToken(): RemoteNode | undefined { - return this.getNamedChild("equalsGreaterThanToken") as RemoteNode; - } - get equalsToken(): RemoteNode | undefined { - return this.getNamedChild("equalsToken") as RemoteNode; - } - get exclamationToken(): RemoteNode | undefined { - return this.getNamedChild("exclamationToken") as RemoteNode; - } - get exportClause(): RemoteNode | undefined { - return this.getNamedChild("exportClause") as RemoteNode; - } - get expression(): RemoteNode | undefined { - return this.getNamedChild("expression") as RemoteNode; - } - get exprName(): RemoteNode | undefined { - return this.getNamedChild("exprName") as RemoteNode; - } - get extendsType(): RemoteNode | undefined { - return this.getNamedChild("extendsType") as RemoteNode; - } - get falseType(): RemoteNode | undefined { - return this.getNamedChild("falseType") as RemoteNode; - } - get finallyBlock(): RemoteNode | undefined { - return this.getNamedChild("finallyBlock") as RemoteNode; - } - get head(): RemoteNode | undefined { - return this.getNamedChild("head") as RemoteNode; - } - get heritageClauses(): RemoteNodeList | undefined { - return this.getNamedChild("heritageClauses") as RemoteNodeList; - } - get importClause(): RemoteNode | undefined { - return this.getNamedChild("importClause") as RemoteNode; - } - get incrementor(): RemoteNode | undefined { - return this.getNamedChild("incrementor") as RemoteNode; - } - get indexType(): RemoteNode | undefined { - return this.getNamedChild("indexType") as RemoteNode; - } - get initializer(): RemoteNode | undefined { - return this.getNamedChild("initializer") as RemoteNode; - } - get jsdocPropertyTags(): RemoteNode | undefined { - return this.getNamedChild("jsdocPropertyTags") as RemoteNode; - } - get label(): RemoteNode | undefined { - return this.getNamedChild("label") as RemoteNode; - } - get left(): RemoteNode | undefined { - return this.getNamedChild("left") as RemoteNode; - } - get literal(): RemoteNode | undefined { - return this.getNamedChild("literal") as RemoteNode; - } - get members(): RemoteNodeList | undefined { - return this.getNamedChild("members") as RemoteNodeList; - } - get modifiers(): RemoteNodeList | undefined { - return this.getNamedChild("modifiers") as RemoteNodeList; - } - get moduleReference(): RemoteNode | undefined { - return this.getNamedChild("moduleReference") as RemoteNode; - } - get moduleSpecifier(): RemoteNode | undefined { - return this.getNamedChild("moduleSpecifier") as RemoteNode; - } - get name(): RemoteNode | undefined { - return this.getNamedChild("name") as RemoteNode; - } - get namedBindings(): RemoteNode | undefined { - return this.getNamedChild("namedBindings") as RemoteNode; - } - get nameExpression(): RemoteNode | undefined { - return this.getNamedChild("nameExpression") as RemoteNode; - } - get namespace(): RemoteNode | undefined { - return this.getNamedChild("namespace") as RemoteNode; - } - get nameType(): RemoteNode | undefined { - return this.getNamedChild("nameType") as RemoteNode; - } - get objectAssignmentInitializer(): RemoteNode | undefined { - return this.getNamedChild("objectAssignmentInitializer") as RemoteNode; - } - get objectType(): RemoteNode | undefined { - return this.getNamedChild("objectType") as RemoteNode; - } - get openingElement(): RemoteNode | undefined { - return this.getNamedChild("openingElement") as RemoteNode; - } - get openingFragment(): RemoteNode | undefined { - return this.getNamedChild("openingFragment") as RemoteNode; - } - get operand(): RemoteNode | undefined { - return this.getNamedChild("operand") as RemoteNode; - } - get operatorToken(): RemoteNode | undefined { - return this.getNamedChild("operatorToken") as RemoteNode; - } - get parameterName(): RemoteNode | undefined { - return this.getNamedChild("parameterName") as RemoteNode; - } - get parameters(): RemoteNodeList | undefined { - return this.getNamedChild("parameters") as RemoteNodeList; - } - get postfixToken(): RemoteNode | undefined { - return this.getNamedChild("postfixToken") as RemoteNode; - } - get properties(): RemoteNodeList | undefined { - return this.getNamedChild("properties") as RemoteNodeList; - } - get propertyName(): RemoteNode | undefined { - return this.getNamedChild("propertyName") as RemoteNode; - } - get qualifier(): RemoteNode | undefined { - return this.getNamedChild("qualifier") as RemoteNode; - } - get questionDotToken(): RemoteNode | undefined { - return this.getNamedChild("questionDotToken") as RemoteNode; - } - get questionToken(): RemoteNode | undefined { - return this.getNamedChild("questionToken") as RemoteNode; - } - get readonlyToken(): RemoteNode | undefined { - return this.getNamedChild("readonlyToken") as RemoteNode; - } - get right(): RemoteNode | undefined { - return this.getNamedChild("right") as RemoteNode; - } - get statement(): RemoteNode | undefined { - return this.getNamedChild("statement") as RemoteNode; - } - get statements(): RemoteNodeList | undefined { - return this.getNamedChild("statements") as RemoteNodeList; - } - get tag(): RemoteNode | undefined { - return this.getNamedChild("tag") as RemoteNode; - } - get tagName(): RemoteNode | undefined { - return this.getNamedChild("tagName") as RemoteNode; - } - get tags(): RemoteNodeList | undefined { - return this.getNamedChild("tags") as RemoteNodeList; - } - get template(): RemoteNode | undefined { - return this.getNamedChild("template") as RemoteNode; - } - get templateSpans(): RemoteNodeList | undefined { - return this.getNamedChild("templateSpans") as RemoteNodeList; - } - get thenStatement(): RemoteNode | undefined { - return this.getNamedChild("thenStatement") as RemoteNode; - } - get thisArg(): RemoteNode | undefined { - return this.getNamedChild("thisArg") as RemoteNode; - } - get trueType(): RemoteNode | undefined { - return this.getNamedChild("trueType") as RemoteNode; - } - get tryBlock(): RemoteNode | undefined { - return this.getNamedChild("tryBlock") as RemoteNode; - } - get tupleNameSource(): RemoteNode | undefined { - return this.getNamedChild("tupleNameSource") as RemoteNode; - } - get type(): RemoteNode | undefined { - return this.getNamedChild("type") as RemoteNode; - } - get typeArguments(): RemoteNodeList | undefined { - return this.getNamedChild("typeArguments") as RemoteNodeList; - } - get typeExpression(): RemoteNode | undefined { - return this.getNamedChild("typeExpression") as RemoteNode; - } - get typeName(): RemoteNode | undefined { - return this.getNamedChild("typeName") as RemoteNode; - } - get typeParameter(): RemoteNode | undefined { - return this.getNamedChild("typeParameter") as RemoteNode; - } - get typeParameters(): RemoteNodeList | undefined { - return this.getNamedChild("typeParameters") as RemoteNodeList; - } - get types(): RemoteNodeList | undefined { - return this.getNamedChild("types") as RemoteNodeList; - } - get value(): RemoteNode | undefined { - return this.getNamedChild("value") as RemoteNode; - } - get variableDeclaration(): RemoteNode | undefined { - return this.getNamedChild("variableDeclaration") as RemoteNode; - } - get whenFalse(): RemoteNode | undefined { - return this.getNamedChild("whenFalse") as RemoteNode; - } - get whenTrue(): RemoteNode | undefined { - return this.getNamedChild("whenTrue") as RemoteNode; - } - - // ═══ Generated string property getters ═══ - - get text(): string | undefined { - switch (this.kind) { - case SyntaxKind.Identifier: - case SyntaxKind.PrivateIdentifier: - case SyntaxKind.JsxText: - case SyntaxKind.JSDocText: - case SyntaxKind.JSDocLink: - case SyntaxKind.JSDocLinkPlain: - case SyntaxKind.JSDocLinkCode: { - const stringIndex = this.data & NODE_STRING_INDEX_MASK; - return this.getString(stringIndex); - } - case SyntaxKind.StringLiteral: - case SyntaxKind.NumericLiteral: - case SyntaxKind.BigIntLiteral: - case SyntaxKind.RegularExpressionLiteral: - case SyntaxKind.NoSubstitutionTemplateLiteral: - case SyntaxKind.TemplateHead: - case SyntaxKind.TemplateMiddle: - case SyntaxKind.TemplateTail: - case SyntaxKind.SourceFile: { - const extendedDataOffset = this.sourceFile._offsetExtendedData + (this.data & NODE_EXTENDED_DATA_MASK); - const stringIndex = this.view.getUint32(extendedDataOffset, true); - return this.getString(stringIndex); - } - } - } - - get rawText(): string | undefined { - switch (this.kind) { - case SyntaxKind.TemplateHead: - case SyntaxKind.TemplateMiddle: - case SyntaxKind.TemplateTail: - const extendedDataOffset = this.sourceFile._offsetExtendedData + (this.data & NODE_EXTENDED_DATA_MASK); - const stringIndex = this.view.getUint32(extendedDataOffset + 4, true); - return this.getString(stringIndex); - } - } - - // ═══ Generated extended data property getters ═══ - - // ═══ Other property getters ═══ - - get flags(): number { - return this.view.getUint32(this._byteIndex + NODE_OFFSET_FLAGS, true); - } - - get modifierFlags(): ModifierFlags { - const mods = this.modifiers; - if (!mods) return ModifierFlags.None; - let flags: ModifierFlags = ModifierFlags.None; - for (const mod of mods) { - flags |= modifierToFlag(mod.kind); - } - return flags; - } -} +// Code generated by tools/scripts/tsc/generate-encoder.ts. DO NOT EDIT. + +import { + getTokenPosOfNode, + ModifierFlags, + type Node, + type NodeArray, + type SourceFile, + SyntaxKind, +} from "../../ast/index.ts"; +import { + modifierToFlag, + NODE_CHILD_MASK, + NODE_DATA_TYPE_MASK, + NODE_EXTENDED_DATA_MASK, + NODE_STRING_INDEX_MASK, + type NodeDataType, + popcount8, + RemoteNodeBase, + type SourceFileInfo, +} from "./node.infrastructure.ts"; +import { + childProperties, + KIND_NODE_LIST, + NODE_DATA_TYPE_CHILDREN, + NODE_DATA_TYPE_EXTENDED, + NODE_DATA_TYPE_STRING, + NODE_LEN, + NODE_OFFSET_DATA, + NODE_OFFSET_END, + NODE_OFFSET_FLAGS, + NODE_OFFSET_KIND, + NODE_OFFSET_NEXT, + NODE_OFFSET_PARENT, + NODE_OFFSET_POS, +} from "./protocol.ts"; + +export class RemoteNodeList extends Array implements NodeArray { + // Inherited Array methods like filter/map/slice use ArraySpeciesCreate, which would + // otherwise call `new RemoteNodeList(length)` and fail. Produce a plain Array instead. + static get [Symbol.species](): ArrayConstructor { + return Array; + } + + parent: RemoteNode; + transformFlags: number = 0; + protected view: DataView; + protected index: number; + private _byteIndex: number; + // Cursor memoizing the last resolved (logical index -> node index) so that + // sequential forward access (index loops and list[i], plus forEach/map/ + // reduce/filter) resumes instead of re-walking from the head, turning an + // O(n) pass over the whole list from O(n^2) into O(n). + private _cursorIndex: number = 0; + private _cursorNodeIndex: number = 0; + + get pos(): number { + return this.view.getUint32(this._byteIndex + NODE_OFFSET_POS, true); + } + + get end(): number { + return this.view.getUint32(this._byteIndex + NODE_OFFSET_END, true); + } + + get next(): number { + return this.view.getUint32(this._byteIndex + NODE_OFFSET_NEXT, true); + } + + private get data(): number { + return this.view.getUint32(this._byteIndex + NODE_OFFSET_DATA, true); + } + + get hasTrailingComma(): boolean { + return (this.view.getUint32(this._byteIndex + NODE_OFFSET_FLAGS, true) & 1) !== 0; + } + + private sourceFile: SourceFileInfo; + + constructor(view: DataView, index: number, parent: RemoteNode, sourceFile: SourceFileInfo, offsetNodes: number) { + super(view.getUint32(offsetNodes + index * NODE_LEN + NODE_OFFSET_DATA, true)); + this.view = view; + this.index = index; + this.parent = parent; + this.sourceFile = sourceFile; + this._byteIndex = offsetNodes + index * NODE_LEN; + this._cursorNodeIndex = index + 1; + + const length = this.length; + for (let i = 16; i < length; i++) { + Object.defineProperty(this, i, { + get() { + return this.at(i); + }, + }); + } + } + get 0(): RemoteNode { + return this.at(0); + } + get 1(): RemoteNode { + return this.at(1); + } + get 2(): RemoteNode { + return this.at(2); + } + get 3(): RemoteNode { + return this.at(3); + } + get 4(): RemoteNode { + return this.at(4); + } + get 5(): RemoteNode { + return this.at(5); + } + get 6(): RemoteNode { + return this.at(6); + } + get 7(): RemoteNode { + return this.at(7); + } + get 8(): RemoteNode { + return this.at(8); + } + get 9(): RemoteNode { + return this.at(9); + } + get 10(): RemoteNode { + return this.at(10); + } + get 11(): RemoteNode { + return this.at(11); + } + get 12(): RemoteNode { + return this.at(12); + } + get 13(): RemoteNode { + return this.at(13); + } + get 14(): RemoteNode { + return this.at(14); + } + get 15(): RemoteNode { + return this.at(15); + } + + *[Symbol.iterator](): ArrayIterator { + if (!this.length) return; + let next = this.index + 1; + while (next) { + const child = this.getOrCreateChildAtNodeIndex(next); + next = child.next; + yield child as RemoteNode; + } + } + + forEachNode(visitNode: (node: RemoteNode) => T | undefined): T | undefined { + if (!this.length) return; + let next = this.index + 1; + while (next) { + const child = this.getOrCreateChildAtNodeIndex(next); + next = child.next; + const result = visitNode(child as RemoteNode); + if (result) return result; + } + } + + at(index: number): RemoteNode { + if (!Number.isInteger(index)) { + return undefined!; + } + if (index >= this.data || (index < 0 && -index > this.data)) { + return undefined!; + } + if (index < 0) { + index = this.length + index; + } + // Walk the raw buffer following each node's `next` pointer instead of + // materializing every intermediate RemoteNode just to read it. Resume from + // the memoized cursor when possible so sequential forward access is O(1) + // amortized (a full in-order pass is O(n) rather than O(n^2)). + const offsetNodes = this.sourceFile._offsetNodes; + let i: number; + let next: number; + if (index >= this._cursorIndex) { + i = this._cursorIndex; + next = this._cursorNodeIndex; + } + else { + i = 0; + next = this.index + 1; + } + for (; i < index; i++) { + next = this.view.getUint32(offsetNodes + next * NODE_LEN + NODE_OFFSET_NEXT, true); + } + this._cursorIndex = index; + this._cursorNodeIndex = next; + return this.getOrCreateChildAtNodeIndex(next) as RemoteNode; + } + + private getOrCreateChildAtNodeIndex(index: number): RemoteNode | RemoteNodeList { + let child = this.sourceFile.nodes[index]; + if (!child) { + const kind = this.view.getUint32(this.sourceFile._offsetNodes + index * NODE_LEN + NODE_OFFSET_KIND, true); + if (kind === KIND_NODE_LIST) { + throw new Error("NodeList cannot directly contain another NodeList"); + } + const sf = this.sourceFile; + child = new RemoteNode(this.view, index, this.parent, sf, sf._offsetNodes); + sf.nodes[index] = child; + sf._timing?.recordMaterialization(); + } + return child; + } + + __print(): string { + const result = []; + result.push(`kind: NodeList`); + result.push(`index: ${this.index}`); + result.push(`byteIndex: ${this._byteIndex}`); + result.push(`length: ${this.length}`); + return result.join("\n"); + } +} + +export class RemoteNode extends RemoteNodeBase implements Node { + protected static NODE_LEN: number = NODE_LEN; + protected override get sourceFile(): SourceFileInfo { + return this._sourceFile; + } + protected _sourceFile: SourceFileInfo; + get id(): string { + return `${this.index}.${this.kind}.${this.sourceFile.path}`; + } + + constructor(view: DataView, index: number, parent: RemoteNode, sourceFile: SourceFileInfo, offsetNodes: number) { + super(view, index, parent, offsetNodes + index * NODE_LEN); + this._sourceFile = sourceFile; + } + + forEachChild(visitNode: (node: Node) => T, visitList?: (list: NodeArray) => T): T | undefined { + if (this.hasChildren()) { + let next = this.index + 1; + do { + const child = this.getOrCreateChildAtNodeIndex(next); + if (child instanceof RemoteNodeList) { + if (visitList) { + const result = visitList(child); + if (result) { + return result; + } + } + else { + const result = child.forEachNode(visitNode); + if (result) { + return result; + } + } + } + else if (child.kind !== SyntaxKind.JSDoc) { + const result = visitNode(child); + if (result) { + return result; + } + } + next = child.next; + } + while (next); + } + } + + get jsDoc(): readonly Node[] | undefined { + if (!this.hasChildren()) { + return undefined; + } + let result: Node[] | undefined; + let next = this.index + 1; + do { + const child = this.getOrCreateChildAtNodeIndex(next); + if (!(child instanceof RemoteNodeList) && child.kind === SyntaxKind.JSDoc) { + (result ??= []).push(child); + } + next = child.next; + } + while (next); + return result; + } + + getSourceFile(): SourceFile { + return this.sourceFile as unknown as SourceFile; + } + + getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number { + return getTokenPosOfNode(this as unknown as Node, sourceFile ?? this.getSourceFile(), includeJsDocComment); + } + + getFullStart(): number { + return this.pos; + } + + getEnd(): number { + return this.end; + } + + getWidth(sourceFile?: SourceFile): number { + return this.getEnd() - this.getStart(sourceFile); + } + + getFullWidth(): number { + return this.end - this.pos; + } + + getLeadingTriviaWidth(sourceFile?: SourceFile): number { + return this.getStart(sourceFile) - this.pos; + } + + getFullText(sourceFile?: SourceFile): string { + return (sourceFile ?? this.getSourceFile()).text.substring(this.pos, this.end); + } + + getText(sourceFile?: SourceFile): string { + sourceFile ??= this.getSourceFile(); + return sourceFile.text.substring(this.getStart(sourceFile), this.end); + } + + protected getString(index: number): string { + const offsetStringTableOffsets = this.sourceFile._offsetStringTableOffsets; + const start = this.view.getUint32(offsetStringTableOffsets + index * 4, true); + const end = this.view.getUint32(offsetStringTableOffsets + (index + 1) * 4, true); + const offsetStringTable = this.sourceFile._offsetStringTable; + const text = new Uint8Array(this.view.buffer, this.view.byteOffset + offsetStringTable + start, end - start); + return this.sourceFile._decoder.decode(text); + } + + private getOrCreateChildAtNodeIndex(index: number): RemoteNode | RemoteNodeList { + let child = this.sourceFile.nodes[index]; + if (!child) { + const sf = this.sourceFile; + const offsetNodes = sf._offsetNodes; + const kind = this.view.getUint32(offsetNodes + index * NODE_LEN + NODE_OFFSET_KIND, true); + child = kind === KIND_NODE_LIST + ? new RemoteNodeList(this.view, index, this, sf, offsetNodes) + : new RemoteNode(this.view, index, this, sf, offsetNodes); + sf.nodes[index] = child; + sf._timing?.recordMaterialization(); + } + return child; + } + + private hasChildren(): boolean { + if (this._byteIndex >= this.view.byteLength - NODE_LEN) { + return false; + } + const nextNodeParent = this.view.getUint32(this.sourceFile._offsetNodes + (this.index + 1) * NODE_LEN + NODE_OFFSET_PARENT, true); + return nextNodeParent === this.index; + } + + private getNamedChild(propertyName: string): RemoteNode | RemoteNodeList | undefined { + const kind = this.kind; + const propertyNames = childProperties[kind]; + if (!propertyNames) { + return undefined; + } + + const order = propertyNames.indexOf(propertyName); + if (order === -1) { + return undefined; + } + return this.getChildAtOrder(order); + } + + private getChildAtOrder(order: number): RemoteNode | RemoteNodeList | undefined { + if (!this.hasChildren()) return undefined; + const mask = this.childMask; + if (!(mask & (1 << order))) { + // Property is not present + return undefined; + } + + // The property index is `order`, minus the number of zeros in the mask that are in bit positions less + // than the `order`th bit. Example: + // + // This is a MethodDeclaration with mask 0b01110101. The possible properties are + // ["modifiers", "asteriskToken", "name", "postfixToken", "typeParameters", "parameters", "type", "body"] + // (it has modifiers, name, typeParameters, parameters, and type). + // + // | Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + // | ----- | ---- | ---- | ---------- | -------------- | ------------ | ---- | ------------- | --------- | + // | Value | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 1 | + // | Name | body | type | parameters | typeParameters | postfixToken | name | asteriskToken | modifiers | + // + // We are trying to get the index of "parameters" (bit = 5). + // First, set all the more significant bits to 1: + // + // | Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + // | ----- | ---- | ---- | ---------- | -------------- | ------------ | ---- | ------------- | --------- | + // | Value | 1 | 1 | 1 | 1 | 0 | 1 | 0 | 1 | + // + // Then, flip the bits: + // + // | Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + // | ----- | ---- | ---- | ---------- | -------------- | ------------ | ---- | ------------- | --------- | + // | Value | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | + // + // Counting the 1s gives us the number of *missing properties* before the `order`th property. If every property + // were present, we would have `parameters = children[5]`, but since `postfixToken` and `astersiskToken` are + // missing, we have `parameters = children[5 - 2]`. + const propertyIndex = order - popcount8[~(mask | ((0xff << order) & 0xff)) & 0xff]; + let childIndex = this.index + 1; + for (let i = 0; i < propertyIndex; i++) { + // Walk through children via their `next` pointer until we get to the right property index + childIndex = this.view.getUint32(this.sourceFile._offsetNodes + childIndex * NODE_LEN + NODE_OFFSET_NEXT, true); + } + return this.getOrCreateChildAtNodeIndex(childIndex); + } + + __print(): string { + const result = []; + result.push(`index: ${this.index}`); + result.push(`byteIndex: ${this._byteIndex}`); + result.push(`kind: ${SyntaxKind[this.kind]}`); + result.push(`pos: ${this.pos}`); + result.push(`end: ${this.end}`); + result.push(`next: ${this.next}`); + result.push(`parent: ${this.parentIndex}`); + result.push(`data: ${this.data.toString(2).padStart(32, "0")}`); + const dataType = this.dataType === NODE_DATA_TYPE_CHILDREN ? "children" : + this.dataType === NODE_DATA_TYPE_STRING ? "string" : + "extended"; + result.push(`dataType: ${dataType}`); + if (this.dataType === NODE_DATA_TYPE_CHILDREN) { + result.push(`childMask: ${this.childMask.toString(2).padStart(8, "0")}`); + result.push(`childProperties: ${childProperties[this.kind]?.join(", ")}`); + } + return result.join("\n"); + } + + __printChildren(): string { + const result = []; + let next = this.index + 1; + while (next) { + const child = this.getOrCreateChildAtNodeIndex(next); + next = child.next; + result.push(child.__print()); + } + return result.join("\n\n"); + } + + __printSubtree(): string { + const result = [this.__print()]; + this.forEachChild(function visitNode(node) { + result.push((node as RemoteNode).__print()); + node.forEachChild(visitNode); + }, visitList => { + result.push((visitList as RemoteNodeList).__print()); + }); + return result.join("\n\n"); + } + + // ═══ Generated boolean property getters ═══ + + get containsOnlyTriviaWhiteSpaces(): boolean { + return (this.data & (1 << 24)) !== 0; + } + + get isArrayType(): boolean { + return (this.data & (1 << 24)) !== 0; + } + + get isBracketed(): boolean { + return (this.data & (1 << 24)) !== 0; + } + + get isExportEquals(): boolean { + return (this.data & (1 << 24)) !== 0; + } + + get isNameFirst(): boolean { + return (this.data & (1 << 25)) !== 0; + } + + get isTypeOf(): boolean { + return (this.data & (1 << 24)) !== 0; + } + + get isTypeOnly(): boolean { + return (this.data & (1 << 24)) !== 0; + } + + get multiLine(): boolean { + return (this.data & (1 << 24)) !== 0; + } + + // ═══ Generated SyntaxKind union property getters ═══ + + get keyword(): SyntaxKind | undefined { + switch (this.kind) { + case SyntaxKind.ModuleDeclaration: + return (this.data >> 24) & 0x1 ? SyntaxKind.NamespaceKeyword : SyntaxKind.ModuleKeyword; + } + } + + get keywordToken(): SyntaxKind | undefined { + switch (this.kind) { + case SyntaxKind.MetaProperty: + return (this.data >> 24) & 0x1 ? SyntaxKind.NewKeyword : SyntaxKind.ImportKeyword; + } + } + + get operator(): SyntaxKind | undefined { + switch (this.kind) { + case SyntaxKind.PrefixUnaryExpression: { + const idx = (this.data >> 24) & 0x7; + if (idx === 1) return SyntaxKind.MinusToken; + if (idx === 2) return SyntaxKind.TildeToken; + if (idx === 3) return SyntaxKind.ExclamationToken; + if (idx === 4) return SyntaxKind.PlusPlusToken; + if (idx === 5) return SyntaxKind.MinusMinusToken; + return SyntaxKind.PlusToken; + } + case SyntaxKind.PostfixUnaryExpression: + return (this.data >> 24) & 0x1 ? SyntaxKind.MinusMinusToken : SyntaxKind.PlusPlusToken; + case SyntaxKind.TypeOperator: { + const idx = (this.data >> 24) & 0x3; + if (idx === 1) return SyntaxKind.ReadonlyKeyword; + if (idx === 2) return SyntaxKind.UniqueKeyword; + return SyntaxKind.KeyOfKeyword; + } + } + } + + get phaseModifier(): SyntaxKind | undefined { + switch (this.kind) { + case SyntaxKind.ImportClause: { + const idx = (this.data >> 24) & 0x3; + if (idx === 0) return undefined; + return idx === 1 ? SyntaxKind.TypeKeyword : idx === 2 ? SyntaxKind.DeferKeyword : undefined; + } + } + } + + get token(): SyntaxKind | undefined { + switch (this.kind) { + case SyntaxKind.HeritageClause: + return (this.data >> 24) & 0x1 ? SyntaxKind.ImplementsKeyword : SyntaxKind.ExtendsKeyword; + case SyntaxKind.ImportAttributes: + return (this.data >> 25) & 0x1 ? SyntaxKind.AssertKeyword : SyntaxKind.WithKeyword; + } + } + + get templateFlags(): number | undefined { + switch (this.kind) { + case SyntaxKind.TemplateHead: + case SyntaxKind.TemplateMiddle: + case SyntaxKind.TemplateTail: + const extendedDataOffset = this.sourceFile._offsetExtendedData + (this.data & NODE_EXTENDED_DATA_MASK); + return this.view.getUint32(extendedDataOffset + 8, true); + } + } + + get tokenFlags(): number { + switch (this.kind) { + case SyntaxKind.StringLiteral: + case SyntaxKind.NumericLiteral: + case SyntaxKind.BigIntLiteral: + case SyntaxKind.RegularExpressionLiteral: + const extendedDataOffset = this.sourceFile._offsetExtendedData + (this.data & NODE_EXTENDED_DATA_MASK); + return this.view.getUint32(extendedDataOffset + 4, true); + default: + return 0; + } + } + + // ═══ Generated child property getters ═══ + + get argument(): RemoteNode | undefined { + return this.getNamedChild("argument") as RemoteNode; + } + get argumentExpression(): RemoteNode | undefined { + return this.getNamedChild("argumentExpression") as RemoteNode; + } + get arguments(): RemoteNodeList | undefined { + return this.getNamedChild("arguments") as RemoteNodeList; + } + get assertsModifier(): RemoteNode | undefined { + return this.getNamedChild("assertsModifier") as RemoteNode; + } + get asteriskToken(): RemoteNode | undefined { + return this.getNamedChild("asteriskToken") as RemoteNode; + } + get attributes(): RemoteNode | RemoteNodeList | undefined { + return this.getNamedChild("attributes") as RemoteNode | RemoteNodeList; + } + get awaitModifier(): RemoteNode | undefined { + return this.getNamedChild("awaitModifier") as RemoteNode; + } + get block(): RemoteNode | undefined { + return this.getNamedChild("block") as RemoteNode; + } + get body(): RemoteNode | undefined { + return this.getNamedChild("body") as RemoteNode; + } + get caseBlock(): RemoteNode | undefined { + return this.getNamedChild("caseBlock") as RemoteNode; + } + get catchClause(): RemoteNode | undefined { + return this.getNamedChild("catchClause") as RemoteNode; + } + get checkType(): RemoteNode | undefined { + return this.getNamedChild("checkType") as RemoteNode; + } + get children(): RemoteNode | RemoteNodeList | undefined { + return this.getNamedChild("children") as RemoteNode | RemoteNodeList; + } + get className(): RemoteNode | undefined { + return this.getNamedChild("className") as RemoteNode; + } + get clauses(): RemoteNodeList | undefined { + return this.getNamedChild("clauses") as RemoteNodeList; + } + get closingElement(): RemoteNode | undefined { + return this.getNamedChild("closingElement") as RemoteNode; + } + get closingFragment(): RemoteNode | undefined { + return this.getNamedChild("closingFragment") as RemoteNode; + } + get colonToken(): RemoteNode | undefined { + return this.getNamedChild("colonToken") as RemoteNode; + } + get comment(): RemoteNodeList | undefined { + return this.getNamedChild("comment") as RemoteNodeList; + } + get condition(): RemoteNode | undefined { + return this.getNamedChild("condition") as RemoteNode; + } + get constraint(): RemoteNode | undefined { + return this.getNamedChild("constraint") as RemoteNode; + } + get declarationList(): RemoteNode | undefined { + return this.getNamedChild("declarationList") as RemoteNode; + } + get declarations(): RemoteNodeList | undefined { + return this.getNamedChild("declarations") as RemoteNodeList; + } + get defaultType(): RemoteNode | undefined { + return this.getNamedChild("defaultType") as RemoteNode; + } + get dotDotDotToken(): RemoteNode | undefined { + return this.getNamedChild("dotDotDotToken") as RemoteNode; + } + get elements(): RemoteNodeList | undefined { + return this.getNamedChild("elements") as RemoteNodeList; + } + get elementType(): RemoteNode | undefined { + return this.getNamedChild("elementType") as RemoteNode; + } + get elseStatement(): RemoteNode | undefined { + return this.getNamedChild("elseStatement") as RemoteNode; + } + get endOfFileToken(): RemoteNode | undefined { + return this.getNamedChild("endOfFileToken") as RemoteNode; + } + get equalsGreaterThanToken(): RemoteNode | undefined { + return this.getNamedChild("equalsGreaterThanToken") as RemoteNode; + } + get equalsToken(): RemoteNode | undefined { + return this.getNamedChild("equalsToken") as RemoteNode; + } + get exclamationToken(): RemoteNode | undefined { + return this.getNamedChild("exclamationToken") as RemoteNode; + } + get exportClause(): RemoteNode | undefined { + return this.getNamedChild("exportClause") as RemoteNode; + } + get expression(): RemoteNode | undefined { + return this.getNamedChild("expression") as RemoteNode; + } + get exprName(): RemoteNode | undefined { + return this.getNamedChild("exprName") as RemoteNode; + } + get extendsType(): RemoteNode | undefined { + return this.getNamedChild("extendsType") as RemoteNode; + } + get falseType(): RemoteNode | undefined { + return this.getNamedChild("falseType") as RemoteNode; + } + get finallyBlock(): RemoteNode | undefined { + return this.getNamedChild("finallyBlock") as RemoteNode; + } + get head(): RemoteNode | undefined { + return this.getNamedChild("head") as RemoteNode; + } + get heritageClauses(): RemoteNodeList | undefined { + return this.getNamedChild("heritageClauses") as RemoteNodeList; + } + get importClause(): RemoteNode | undefined { + return this.getNamedChild("importClause") as RemoteNode; + } + get incrementor(): RemoteNode | undefined { + return this.getNamedChild("incrementor") as RemoteNode; + } + get indexType(): RemoteNode | undefined { + return this.getNamedChild("indexType") as RemoteNode; + } + get initializer(): RemoteNode | undefined { + return this.getNamedChild("initializer") as RemoteNode; + } + get jsdocPropertyTags(): RemoteNode | undefined { + return this.getNamedChild("jsdocPropertyTags") as RemoteNode; + } + get label(): RemoteNode | undefined { + return this.getNamedChild("label") as RemoteNode; + } + get left(): RemoteNode | undefined { + return this.getNamedChild("left") as RemoteNode; + } + get literal(): RemoteNode | undefined { + return this.getNamedChild("literal") as RemoteNode; + } + get members(): RemoteNodeList | undefined { + return this.getNamedChild("members") as RemoteNodeList; + } + get modifiers(): RemoteNodeList | undefined { + return this.getNamedChild("modifiers") as RemoteNodeList; + } + get moduleReference(): RemoteNode | undefined { + return this.getNamedChild("moduleReference") as RemoteNode; + } + get moduleSpecifier(): RemoteNode | undefined { + return this.getNamedChild("moduleSpecifier") as RemoteNode; + } + get name(): RemoteNode | undefined { + return this.getNamedChild("name") as RemoteNode; + } + get namedBindings(): RemoteNode | undefined { + return this.getNamedChild("namedBindings") as RemoteNode; + } + get nameExpression(): RemoteNode | undefined { + return this.getNamedChild("nameExpression") as RemoteNode; + } + get namespace(): RemoteNode | undefined { + return this.getNamedChild("namespace") as RemoteNode; + } + get nameType(): RemoteNode | undefined { + return this.getNamedChild("nameType") as RemoteNode; + } + get objectAssignmentInitializer(): RemoteNode | undefined { + return this.getNamedChild("objectAssignmentInitializer") as RemoteNode; + } + get objectType(): RemoteNode | undefined { + return this.getNamedChild("objectType") as RemoteNode; + } + get openingElement(): RemoteNode | undefined { + return this.getNamedChild("openingElement") as RemoteNode; + } + get openingFragment(): RemoteNode | undefined { + return this.getNamedChild("openingFragment") as RemoteNode; + } + get operand(): RemoteNode | undefined { + return this.getNamedChild("operand") as RemoteNode; + } + get operatorToken(): RemoteNode | undefined { + return this.getNamedChild("operatorToken") as RemoteNode; + } + get parameterName(): RemoteNode | undefined { + return this.getNamedChild("parameterName") as RemoteNode; + } + get parameters(): RemoteNodeList | undefined { + return this.getNamedChild("parameters") as RemoteNodeList; + } + get postfixToken(): RemoteNode | undefined { + return this.getNamedChild("postfixToken") as RemoteNode; + } + get properties(): RemoteNodeList | undefined { + return this.getNamedChild("properties") as RemoteNodeList; + } + get propertyName(): RemoteNode | undefined { + return this.getNamedChild("propertyName") as RemoteNode; + } + get qualifier(): RemoteNode | undefined { + return this.getNamedChild("qualifier") as RemoteNode; + } + get questionDotToken(): RemoteNode | undefined { + return this.getNamedChild("questionDotToken") as RemoteNode; + } + get questionToken(): RemoteNode | undefined { + return this.getNamedChild("questionToken") as RemoteNode; + } + get readonlyToken(): RemoteNode | undefined { + return this.getNamedChild("readonlyToken") as RemoteNode; + } + get right(): RemoteNode | undefined { + return this.getNamedChild("right") as RemoteNode; + } + get statement(): RemoteNode | undefined { + return this.getNamedChild("statement") as RemoteNode; + } + get statements(): RemoteNodeList | undefined { + return this.getNamedChild("statements") as RemoteNodeList; + } + get tag(): RemoteNode | undefined { + return this.getNamedChild("tag") as RemoteNode; + } + get tagName(): RemoteNode | undefined { + return this.getNamedChild("tagName") as RemoteNode; + } + get tags(): RemoteNodeList | undefined { + return this.getNamedChild("tags") as RemoteNodeList; + } + get template(): RemoteNode | undefined { + return this.getNamedChild("template") as RemoteNode; + } + get templateSpans(): RemoteNodeList | undefined { + return this.getNamedChild("templateSpans") as RemoteNodeList; + } + get thenStatement(): RemoteNode | undefined { + return this.getNamedChild("thenStatement") as RemoteNode; + } + get thisArg(): RemoteNode | undefined { + return this.getNamedChild("thisArg") as RemoteNode; + } + get trueType(): RemoteNode | undefined { + return this.getNamedChild("trueType") as RemoteNode; + } + get tryBlock(): RemoteNode | undefined { + return this.getNamedChild("tryBlock") as RemoteNode; + } + get tupleNameSource(): RemoteNode | undefined { + return this.getNamedChild("tupleNameSource") as RemoteNode; + } + get type(): RemoteNode | undefined { + return this.getNamedChild("type") as RemoteNode; + } + get typeArguments(): RemoteNodeList | undefined { + return this.getNamedChild("typeArguments") as RemoteNodeList; + } + get typeExpression(): RemoteNode | undefined { + return this.getNamedChild("typeExpression") as RemoteNode; + } + get typeName(): RemoteNode | undefined { + return this.getNamedChild("typeName") as RemoteNode; + } + get typeParameter(): RemoteNode | undefined { + return this.getNamedChild("typeParameter") as RemoteNode; + } + get typeParameters(): RemoteNodeList | undefined { + return this.getNamedChild("typeParameters") as RemoteNodeList; + } + get types(): RemoteNodeList | undefined { + return this.getNamedChild("types") as RemoteNodeList; + } + get value(): RemoteNode | undefined { + return this.getNamedChild("value") as RemoteNode; + } + get variableDeclaration(): RemoteNode | undefined { + return this.getNamedChild("variableDeclaration") as RemoteNode; + } + get whenFalse(): RemoteNode | undefined { + return this.getNamedChild("whenFalse") as RemoteNode; + } + get whenTrue(): RemoteNode | undefined { + return this.getNamedChild("whenTrue") as RemoteNode; + } + + // ═══ Generated string property getters ═══ + + get text(): string | undefined { + switch (this.kind) { + case SyntaxKind.Identifier: + case SyntaxKind.PrivateIdentifier: + case SyntaxKind.JsxText: + case SyntaxKind.JSDocText: + case SyntaxKind.JSDocLink: + case SyntaxKind.JSDocLinkPlain: + case SyntaxKind.JSDocLinkCode: { + const stringIndex = this.data & NODE_STRING_INDEX_MASK; + return this.getString(stringIndex); + } + case SyntaxKind.StringLiteral: + case SyntaxKind.NumericLiteral: + case SyntaxKind.BigIntLiteral: + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.NoSubstitutionTemplateLiteral: + case SyntaxKind.TemplateHead: + case SyntaxKind.TemplateMiddle: + case SyntaxKind.TemplateTail: + case SyntaxKind.SourceFile: { + const extendedDataOffset = this.sourceFile._offsetExtendedData + (this.data & NODE_EXTENDED_DATA_MASK); + const stringIndex = this.view.getUint32(extendedDataOffset, true); + return this.getString(stringIndex); + } + } + } + + get rawText(): string | undefined { + switch (this.kind) { + case SyntaxKind.TemplateHead: + case SyntaxKind.TemplateMiddle: + case SyntaxKind.TemplateTail: + const extendedDataOffset = this.sourceFile._offsetExtendedData + (this.data & NODE_EXTENDED_DATA_MASK); + const stringIndex = this.view.getUint32(extendedDataOffset + 4, true); + return this.getString(stringIndex); + } + } + + // ═══ Generated extended data property getters ═══ + + // ═══ Other property getters ═══ + + get flags(): number { + return this.view.getUint32(this._byteIndex + NODE_OFFSET_FLAGS, true); + } + + get modifierFlags(): ModifierFlags { + const mods = this.modifiers; + if (!mods) return ModifierFlags.None; + let flags: ModifierFlags = ModifierFlags.None; + for (const mod of mods) { + flags |= modifierToFlag(mod.kind); + } + return flags; + } +} diff --git a/packages/typescript/src/api/node/node.infrastructure.ts b/packages/typescript/src/api/node/node.infrastructure.ts index 92b2922877e2a..7792b66e9e181 100644 --- a/packages/typescript/src/api/node/node.infrastructure.ts +++ b/packages/typescript/src/api/node/node.infrastructure.ts @@ -1,196 +1,196 @@ -import { - type FileReference, - ModifierFlags, - type Node, - SyntaxKind, -} from "../../ast/index.ts"; -import type { TimingCollector } from "../timing.ts"; -import { - HEADER_OFFSET_HASH_HI0, - HEADER_OFFSET_HASH_HI1, - HEADER_OFFSET_HASH_LO0, - HEADER_OFFSET_HASH_LO1, - HEADER_OFFSET_PARSE_OPTIONS, - NODE_DATA_TYPE_CHILDREN, - NODE_DATA_TYPE_EXTENDED, - NODE_DATA_TYPE_STRING, - NODE_OFFSET_DATA, - NODE_OFFSET_END, - NODE_OFFSET_KIND, - NODE_OFFSET_NEXT, - NODE_OFFSET_PARENT, - NODE_OFFSET_POS, -} from "./protocol.ts"; - -// ═══════════════════════════════════════════════════════════════════════════ -// Constants -// ═══════════════════════════════════════════════════════════════════════════ - -export const popcount8: number[] = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8]; - -export type NodeDataType = typeof NODE_DATA_TYPE_CHILDREN | typeof NODE_DATA_TYPE_STRING | typeof NODE_DATA_TYPE_EXTENDED; -export const NODE_DATA_TYPE_MASK = 0xc0_00_00_00; -export const NODE_CHILD_MASK = 0x00_00_00_ff; -export const NODE_STRING_INDEX_MASK = 0x00_ff_ff_ff; -export const NODE_EXTENDED_DATA_MASK = 0x00_ff_ff_ff; - -// ═══════════════════════════════════════════════════════════════════════════ -// SourceFileInfo — the interface RemoteNode/RemoteNodeList need from the -// source file, avoiding a direct dependency on RemoteSourceFile. -// ═══════════════════════════════════════════════════════════════════════════ - -// The global type is not available in earlier @types/node versions -export interface TextDecoder { - decode(input?: ArrayBufferView | ArrayBufferLike): string; -} - -export interface SourceFileInfo { - readonly _offsetNodes: number; - readonly _offsetStringTableOffsets: number; - readonly _offsetStringTable: number; - readonly _offsetExtendedData: number; - readonly _offsetStructuredData: number; - readonly _decoder: TextDecoder; - nodes: any[]; - readonly path?: string; - /** - * The timing collector that per-node materialization is reported into, and - * that this source file registered itself with when fetched. Present only - * when timing collection is enabled; when undefined, materialization is not - * timed and no clock is read. - */ - readonly _timing?: TimingCollector | undefined; - readFileReferences(offset: number): readonly FileReference[]; - readNodeIndexArray(offset: number): readonly Node[]; - readStringArray(offset: number): readonly string[]; - getOrCreateNodeAtIndex(index: number): Node; -} - -// ═══════════════════════════════════════════════════════════════════════════ -// Free functions -// ═══════════════════════════════════════════════════════════════════════════ - -/** - * Read the 128-bit content hash from a source file binary response as a hex string. - */ -export function readSourceFileHash(data: DataView): string { - const lo0 = data.getUint32(HEADER_OFFSET_HASH_LO0, true); - const lo1 = data.getUint32(HEADER_OFFSET_HASH_LO1, true); - const hi0 = data.getUint32(HEADER_OFFSET_HASH_HI0, true); - const hi1 = data.getUint32(HEADER_OFFSET_HASH_HI1, true); - return hex8(hi1) + hex8(hi0) + hex8(lo1) + hex8(lo0); -} - -/** - * Read the per-file parse options key from a source file binary response. - * This encodes the ExternalModuleIndicatorOptions bitmask as a string, - * allowing the client to distinguish files parsed with different options. - */ -export function readParseOptionsKey(data: DataView): string { - return data.getUint32(HEADER_OFFSET_PARSE_OPTIONS, true).toString(); -} - -function hex8(n: number): string { - return (n >>> 0).toString(16).padStart(8, "0"); -} - -export function modifierToFlag(kind: SyntaxKind): ModifierFlags { - switch (kind) { - case SyntaxKind.StaticKeyword: - return ModifierFlags.Static; - case SyntaxKind.PublicKeyword: - return ModifierFlags.Public; - case SyntaxKind.ProtectedKeyword: - return ModifierFlags.Protected; - case SyntaxKind.PrivateKeyword: - return ModifierFlags.Private; - case SyntaxKind.AbstractKeyword: - return ModifierFlags.Abstract; - case SyntaxKind.AccessorKeyword: - return ModifierFlags.Accessor; - case SyntaxKind.ExportKeyword: - return ModifierFlags.Export; - case SyntaxKind.DeclareKeyword: - return ModifierFlags.Ambient; - case SyntaxKind.ConstKeyword: - return ModifierFlags.Const; - case SyntaxKind.DefaultKeyword: - return ModifierFlags.Default; - case SyntaxKind.AsyncKeyword: - return ModifierFlags.Async; - case SyntaxKind.ReadonlyKeyword: - return ModifierFlags.Readonly; - case SyntaxKind.OverrideKeyword: - return ModifierFlags.Override; - case SyntaxKind.InKeyword: - return ModifierFlags.In; - case SyntaxKind.OutKeyword: - return ModifierFlags.Out; - case SyntaxKind.Decorator: - return ModifierFlags.Decorator; - default: - return ModifierFlags.None; - } -} - -// ═══════════════════════════════════════════════════════════════════════════ -// RemoteNodeBase -// ═══════════════════════════════════════════════════════════════════════════ - -export class RemoteNodeBase { - parent: any; // RemoteNode at runtime - view: DataView; - protected index: number; - protected _byteIndex: number; - - constructor(view: DataView, index: number, parent: any, byteIndex: number) { - this.view = view; - this.index = index; - this.parent = parent; - this._byteIndex = byteIndex; - } - - get kind(): SyntaxKind { - return this.view.getUint32(this._byteIndex + NODE_OFFSET_KIND, true); - } - - get pos(): number { - return this.view.getInt32(this._byteIndex + NODE_OFFSET_POS, true); - } - - get end(): number { - return this.view.getInt32(this._byteIndex + NODE_OFFSET_END, true); - } - - get next(): number { - return this.view.getUint32(this._byteIndex + NODE_OFFSET_NEXT, true); - } - - protected get parentIndex(): number { - return this.view.getUint32(this._byteIndex + NODE_OFFSET_PARENT, true); - } - - protected get data(): number { - return this.view.getUint32(this._byteIndex + NODE_OFFSET_DATA, true); - } - - protected get dataType(): NodeDataType { - return (this.data & NODE_DATA_TYPE_MASK) as NodeDataType; - } - - protected get childMask(): number { - if (this.dataType !== NODE_DATA_TYPE_CHILDREN) { - return -1; - } - return this.data & NODE_CHILD_MASK; - } - - protected getFileText(start: number, end: number): string { - return this.sourceFile._decoder.decode(new Uint8Array(this.view.buffer, this.view.byteOffset + this.sourceFile._offsetStringTable + start, end - start)); - } - - protected get sourceFile(): SourceFileInfo { - // Overridden in RemoteNode; exists here for getFileText access - throw new Error("sourceFile not available on base"); - } -} +import { + type FileReference, + ModifierFlags, + type Node, + SyntaxKind, +} from "../../ast/index.ts"; +import type { TimingCollector } from "../timing.ts"; +import { + HEADER_OFFSET_HASH_HI0, + HEADER_OFFSET_HASH_HI1, + HEADER_OFFSET_HASH_LO0, + HEADER_OFFSET_HASH_LO1, + HEADER_OFFSET_PARSE_OPTIONS, + NODE_DATA_TYPE_CHILDREN, + NODE_DATA_TYPE_EXTENDED, + NODE_DATA_TYPE_STRING, + NODE_OFFSET_DATA, + NODE_OFFSET_END, + NODE_OFFSET_KIND, + NODE_OFFSET_NEXT, + NODE_OFFSET_PARENT, + NODE_OFFSET_POS, +} from "./protocol.ts"; + +// ═══════════════════════════════════════════════════════════════════════════ +// Constants +// ═══════════════════════════════════════════════════════════════════════════ + +export const popcount8: number[] = [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8]; + +export type NodeDataType = typeof NODE_DATA_TYPE_CHILDREN | typeof NODE_DATA_TYPE_STRING | typeof NODE_DATA_TYPE_EXTENDED; +export const NODE_DATA_TYPE_MASK = 0xc0_00_00_00; +export const NODE_CHILD_MASK = 0x00_00_00_ff; +export const NODE_STRING_INDEX_MASK = 0x00_ff_ff_ff; +export const NODE_EXTENDED_DATA_MASK = 0x00_ff_ff_ff; + +// ═══════════════════════════════════════════════════════════════════════════ +// SourceFileInfo — the interface RemoteNode/RemoteNodeList need from the +// source file, avoiding a direct dependency on RemoteSourceFile. +// ═══════════════════════════════════════════════════════════════════════════ + +// The global type is not available in earlier @types/node versions +export interface TextDecoder { + decode(input?: ArrayBufferView | ArrayBufferLike): string; +} + +export interface SourceFileInfo { + readonly _offsetNodes: number; + readonly _offsetStringTableOffsets: number; + readonly _offsetStringTable: number; + readonly _offsetExtendedData: number; + readonly _offsetStructuredData: number; + readonly _decoder: TextDecoder; + nodes: any[]; + readonly path?: string; + /** + * The timing collector that per-node materialization is reported into, and + * that this source file registered itself with when fetched. Present only + * when timing collection is enabled; when undefined, materialization is not + * timed and no clock is read. + */ + readonly _timing?: TimingCollector | undefined; + readFileReferences(offset: number): readonly FileReference[]; + readNodeIndexArray(offset: number): readonly Node[]; + readStringArray(offset: number): readonly string[]; + getOrCreateNodeAtIndex(index: number): Node; +} + +// ═══════════════════════════════════════════════════════════════════════════ +// Free functions +// ═══════════════════════════════════════════════════════════════════════════ + +/** + * Read the 128-bit content hash from a source file binary response as a hex string. + */ +export function readSourceFileHash(data: DataView): string { + const lo0 = data.getUint32(HEADER_OFFSET_HASH_LO0, true); + const lo1 = data.getUint32(HEADER_OFFSET_HASH_LO1, true); + const hi0 = data.getUint32(HEADER_OFFSET_HASH_HI0, true); + const hi1 = data.getUint32(HEADER_OFFSET_HASH_HI1, true); + return hex8(hi1) + hex8(hi0) + hex8(lo1) + hex8(lo0); +} + +/** + * Read the per-file parse options key from a source file binary response. + * This encodes the ExternalModuleIndicatorOptions bitmask as a string, + * allowing the client to distinguish files parsed with different options. + */ +export function readParseOptionsKey(data: DataView): string { + return data.getUint32(HEADER_OFFSET_PARSE_OPTIONS, true).toString(); +} + +function hex8(n: number): string { + return (n >>> 0).toString(16).padStart(8, "0"); +} + +export function modifierToFlag(kind: SyntaxKind): ModifierFlags { + switch (kind) { + case SyntaxKind.StaticKeyword: + return ModifierFlags.Static; + case SyntaxKind.PublicKeyword: + return ModifierFlags.Public; + case SyntaxKind.ProtectedKeyword: + return ModifierFlags.Protected; + case SyntaxKind.PrivateKeyword: + return ModifierFlags.Private; + case SyntaxKind.AbstractKeyword: + return ModifierFlags.Abstract; + case SyntaxKind.AccessorKeyword: + return ModifierFlags.Accessor; + case SyntaxKind.ExportKeyword: + return ModifierFlags.Export; + case SyntaxKind.DeclareKeyword: + return ModifierFlags.Ambient; + case SyntaxKind.ConstKeyword: + return ModifierFlags.Const; + case SyntaxKind.DefaultKeyword: + return ModifierFlags.Default; + case SyntaxKind.AsyncKeyword: + return ModifierFlags.Async; + case SyntaxKind.ReadonlyKeyword: + return ModifierFlags.Readonly; + case SyntaxKind.OverrideKeyword: + return ModifierFlags.Override; + case SyntaxKind.InKeyword: + return ModifierFlags.In; + case SyntaxKind.OutKeyword: + return ModifierFlags.Out; + case SyntaxKind.Decorator: + return ModifierFlags.Decorator; + default: + return ModifierFlags.None; + } +} + +// ═══════════════════════════════════════════════════════════════════════════ +// RemoteNodeBase +// ═══════════════════════════════════════════════════════════════════════════ + +export class RemoteNodeBase { + parent: any; // RemoteNode at runtime + view: DataView; + protected index: number; + protected _byteIndex: number; + + constructor(view: DataView, index: number, parent: any, byteIndex: number) { + this.view = view; + this.index = index; + this.parent = parent; + this._byteIndex = byteIndex; + } + + get kind(): SyntaxKind { + return this.view.getUint32(this._byteIndex + NODE_OFFSET_KIND, true); + } + + get pos(): number { + return this.view.getInt32(this._byteIndex + NODE_OFFSET_POS, true); + } + + get end(): number { + return this.view.getInt32(this._byteIndex + NODE_OFFSET_END, true); + } + + get next(): number { + return this.view.getUint32(this._byteIndex + NODE_OFFSET_NEXT, true); + } + + protected get parentIndex(): number { + return this.view.getUint32(this._byteIndex + NODE_OFFSET_PARENT, true); + } + + protected get data(): number { + return this.view.getUint32(this._byteIndex + NODE_OFFSET_DATA, true); + } + + protected get dataType(): NodeDataType { + return (this.data & NODE_DATA_TYPE_MASK) as NodeDataType; + } + + protected get childMask(): number { + if (this.dataType !== NODE_DATA_TYPE_CHILDREN) { + return -1; + } + return this.data & NODE_CHILD_MASK; + } + + protected getFileText(start: number, end: number): string { + return this.sourceFile._decoder.decode(new Uint8Array(this.view.buffer, this.view.byteOffset + this.sourceFile._offsetStringTable + start, end - start)); + } + + protected get sourceFile(): SourceFileInfo { + // Overridden in RemoteNode; exists here for getFileText access + throw new Error("sourceFile not available on base"); + } +} diff --git a/packages/typescript/src/api/node/node.ts b/packages/typescript/src/api/node/node.ts index 65247c6d267bd..d0728ef9cf0cd 100644 --- a/packages/typescript/src/api/node/node.ts +++ b/packages/typescript/src/api/node/node.ts @@ -1,481 +1,481 @@ -import { - computeLineStarts, - type FileReference, - type LineAndCharacter, - type MappedDiagnosticDirective, - type Node, - NodeFlags, - type Path, - SpanMap, - SpanMapFeature, - SpanMapKind, - SyntaxKind, - TokenFlags, -} from "../../ast/index.ts"; -import type { TimingCollector } from "../timing.ts"; -import { MsgpackReader } from "./msgpack.ts"; -import { - RemoteNode, - RemoteNodeList, -} from "./node.generated.ts"; -import { - NODE_EXTENDED_DATA_MASK, - type SourceFileInfo, - type TextDecoder, -} from "./node.infrastructure.ts"; -import { - HEADER_OFFSET_EXTENDED_DATA, - HEADER_OFFSET_NODES, - HEADER_OFFSET_STRING_TABLE, - HEADER_OFFSET_STRING_TABLE_OFFSETS, - HEADER_OFFSET_STRUCTURED_DATA, - KIND_NODE_LIST, - NODE_LEN, - NODE_OFFSET_KIND, - NODE_OFFSET_PARENT, -} from "./protocol.ts"; -import { Wtf8Decoder } from "./wtf8.ts"; - -// Re-export everything consumers need from the other two files. -export { RemoteNode, RemoteNodeList } from "./node.generated.ts"; -export { readParseOptionsKey, readSourceFileHash, RemoteNodeBase } from "./node.infrastructure.ts"; - -const sourceFileExtendedDataOffsets = { - Text: 0, - FileName: 4, - Path: 8, - LanguageVariant: 12, - ScriptKind: 16, - ReferencedFiles: 20, - TypeReferenceDirectives: 24, - LibReferenceDirectives: 28, - Imports: 32, - ModuleAugmentations: 36, - AmbientModuleNames: 40, - ExternalModuleIndicator: 44, - OriginalText: 48, - SpanMap: 52, - SupplementalSourceFileNames: 56, - CanonicalSourceFileName: 60, - ContentMapper: 64, - VirtualFileName: 68, - DiagnosticDirectives: 72, -} as const; - -for (const [index, offset] of Object.values(sourceFileExtendedDataOffsets).entries()) { - if (offset !== index * Uint32Array.BYTES_PER_ELEMENT) { - throw new Error(`Invalid SourceFile extended data offset ${offset} at index ${index}`); - } -} - -// ═══════════════════════════════════════════════════════════════════════════ -// RemoteSourceFile -// ═══════════════════════════════════════════════════════════════════════════ - -const NO_STRUCTURED_DATA = 0xFFFFFFFF; - -export class RemoteSourceFile extends RemoteNode implements SourceFileInfo { - readonly nodes: (RemoteNode | RemoteNodeList)[]; - readonly _offsetNodes: number; - readonly _offsetStringTableOffsets: number; - readonly _offsetStringTable: number; - readonly _offsetExtendedData: number; - readonly _offsetStructuredData: number; - readonly _decoder: TextDecoder; - readonly _timing: TimingCollector | undefined; - private _lineStarts: readonly number[] | undefined; - private _cachedText: string | undefined; - private _cachedReferencedFiles: readonly FileReference[] | undefined; - private _cachedTypeReferenceDirectives: readonly FileReference[] | undefined; - private _cachedLibReferenceDirectives: readonly FileReference[] | undefined; - private _cachedImports: readonly Node[] | undefined; - private _cachedModuleAugmentations: readonly Node[] | undefined; - private _cachedAmbientModuleNames: readonly string[] | undefined; - private _cachedSpanMap: SpanMap | undefined; - private _spanMapRead = false; - private _cachedSupplementalSourceFileNames: readonly string[] | undefined; - private _cachedDiagnosticDirectives: readonly MappedDiagnosticDirective[] | undefined; - private _diagnosticDirectivesRead = false; - - constructor(data: Uint8Array, decoder: TextDecoder, timing?: TimingCollector) { - const view = new DataView(data.buffer, data.byteOffset, data.byteLength); - const offsetNodes = view.getUint32(HEADER_OFFSET_NODES, true); - super(view, 1, undefined!, undefined!, offsetNodes); - this._sourceFile = this; - this._offsetNodes = offsetNodes; - this._offsetStringTableOffsets = view.getUint32(HEADER_OFFSET_STRING_TABLE_OFFSETS, true); - this._offsetStringTable = view.getUint32(HEADER_OFFSET_STRING_TABLE, true); - this._offsetExtendedData = view.getUint32(HEADER_OFFSET_EXTENDED_DATA, true); - this._offsetStructuredData = view.getUint32(HEADER_OFFSET_STRUCTURED_DATA, true); - this._decoder = decoder; - this._timing = timing; - this.nodes = Array((view.byteLength - offsetNodes) / NODE_LEN); - this.nodes[1] = this; - // Every node slot is materializable on demand except the nil sentinel at - // index 0 and the source-file node at index 1, which is pre-materialized. - timing?.recordSourceFileFetched(Math.max(0, this.nodes.length - 2)); - } - - readFileReferences(structuredDataOffset: number): readonly FileReference[] { - if (structuredDataOffset === NO_STRUCTURED_DATA) { - return []; - } - const buf = new Uint8Array(this.view.buffer, this.view.byteOffset, this.view.byteLength); - const reader = new MsgpackReader(buf, this._offsetStructuredData + structuredDataOffset); - const count = reader.readArrayHeader(); - const result: FileReference[] = []; - for (let i = 0; i < count; i++) { - reader.readArrayHeader(); // 5-element tuple - const pos = reader.readUint(); - const end = reader.readUint(); - const fileName = reader.readString(); - const resolutionMode = reader.readUint(); - const preserve = reader.readBool(); - result.push({ pos, end, fileName, resolutionMode, preserve }); - } - return result; - } - - readNodeIndexArray(structuredDataOffset: number): readonly Node[] { - if (structuredDataOffset === NO_STRUCTURED_DATA) { - return []; - } - const buf = new Uint8Array(this.view.buffer, this.view.byteOffset, this.view.byteLength); - const reader = new MsgpackReader(buf, this._offsetStructuredData + structuredDataOffset); - const count = reader.readArrayHeader(); - const result: Node[] = []; - for (let i = 0; i < count; i++) { - const nodeIndex = reader.readUint(); - result.push(this.getOrCreateNodeAtIndex(nodeIndex)); - } - return result; - } - - readStringArray(structuredDataOffset: number): readonly string[] { - if (structuredDataOffset === NO_STRUCTURED_DATA) { - return []; - } - const buf = new Uint8Array(this.view.buffer, this.view.byteOffset, this.view.byteLength); - const reader = new MsgpackReader(buf, this._offsetStructuredData + structuredDataOffset); - const count = reader.readArrayHeader(); - const result: string[] = []; - for (let i = 0; i < count; i++) { - result.push(reader.readString()); - } - return result; - } - - /** @internal */ - getOrCreateNodeAtIndex(index: number): Node { - let node = this.nodes[index]; - if (!node) { - // Resolve the real parent so that nodes looked up directly by index (e.g. via - // NodeHandle.resolve) report the correct `parent`, rather than always pointing at - // the source file. The stored parent index can refer to a synthetic NodeList - // container; skip those to mirror normal traversal, where list elements take the - // list's parent. The walk terminates at the source file, which occupies index 1 - // and is already cached. - let parentIndex = this.view.getUint32(this._offsetNodes + index * NODE_LEN + NODE_OFFSET_PARENT, true); - while ( - parentIndex !== index && - this.view.getUint32(this._offsetNodes + parentIndex * NODE_LEN + NODE_OFFSET_KIND, true) === KIND_NODE_LIST - ) { - parentIndex = this.view.getUint32(this._offsetNodes + parentIndex * NODE_LEN + NODE_OFFSET_PARENT, true); - } - const parent = parentIndex === index ? this : this.getOrCreateNodeAtIndex(parentIndex) as RemoteNode; - node = new RemoteNode(this.view, index, parent, this, this._offsetNodes); - this.nodes[index] = node; - this._timing?.recordMaterialization(); - } - return node as Node; - } - - // ═══ SourceFile-specific extended data getters ═══ - - private get extendedDataOffset(): number { - return this._offsetExtendedData + (this.data & NODE_EXTENDED_DATA_MASK); - } - - get fileName(): string { - const stringIndex = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.FileName, true); - return this.getString(stringIndex); - } - - get path(): string { - const stringIndex = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.Path, true); - return this.getString(stringIndex); - } - - get languageVariant(): number { - return this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.LanguageVariant, true); - } - - get scriptKind(): number { - return this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.ScriptKind, true); - } - - get referencedFiles(): readonly FileReference[] { - if (this._cachedReferencedFiles !== undefined) return this._cachedReferencedFiles; - const offset = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.ReferencedFiles, true); - const files = this.readFileReferences(offset); - this._cachedReferencedFiles = files; - return files; - } - - get typeReferenceDirectives(): readonly FileReference[] { - if (this._cachedTypeReferenceDirectives !== undefined) return this._cachedTypeReferenceDirectives; - const offset = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.TypeReferenceDirectives, true); - const directives = this.readFileReferences(offset); - this._cachedTypeReferenceDirectives = directives; - return directives; - } - - get libReferenceDirectives(): readonly FileReference[] { - if (this._cachedLibReferenceDirectives !== undefined) return this._cachedLibReferenceDirectives; - const offset = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.LibReferenceDirectives, true); - const directives = this.readFileReferences(offset); - this._cachedLibReferenceDirectives = directives; - return directives; - } - - get imports(): readonly Node[] { - if (this._cachedImports !== undefined) return this._cachedImports; - const offset = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.Imports, true); - const imports = this.readNodeIndexArray(offset); - this._cachedImports = imports; - return imports; - } - - get moduleAugmentations(): readonly Node[] { - if (this._cachedModuleAugmentations !== undefined) return this._cachedModuleAugmentations; - const offset = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.ModuleAugmentations, true); - const moduleAugmentations = this.readNodeIndexArray(offset); - this._cachedModuleAugmentations = moduleAugmentations; - return moduleAugmentations; - } - - get ambientModuleNames(): readonly string[] { - if (this._cachedAmbientModuleNames !== undefined) return this._cachedAmbientModuleNames; - const offset = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.AmbientModuleNames, true); - const names = this.readStringArray(offset); - this._cachedAmbientModuleNames = names; - return names; - } - - get externalModuleIndicator(): Node | true | undefined { - const nodeIndex = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.ExternalModuleIndicator, true); - if (nodeIndex === 0) return undefined; - if (nodeIndex === this.index) return true; - return this.getOrCreateNodeAtIndex(nodeIndex) as Node; - } - - get originalText(): string { - const stringIndex = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.OriginalText, true); - return this.getString(stringIndex); - } - - get spanMap(): SpanMap | undefined { - if (this._spanMapRead) return this._cachedSpanMap; - this._spanMapRead = true; - const offset = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.SpanMap, true); - if (offset === NO_STRUCTURED_DATA) return undefined; - const buf = new Uint8Array(this.view.buffer, this.view.byteOffset, this.view.byteLength); - const reader = new MsgpackReader(buf, this._offsetStructuredData + offset); - const count = reader.readArrayHeader(); - const segments = Array(count); - for (let i = 0; i < count; i++) { - const tupleLength = reader.readArrayHeader(); - if (tupleLength !== 5 && tupleLength !== 6) throw new Error("Invalid span map segment"); - const virtualStart = reader.readUint(); - const virtualLength = reader.readUint(); - const originalStart = reader.readUint(); - const originalLength = reader.readUint(); - const kind = reader.readUint(); - const features = tupleLength === 6 ? reader.readUint() as SpanMapFeature : SpanMapFeature.All; - if (kind !== SpanMapKind.Verbatim && kind !== SpanMapKind.Atom && kind !== SpanMapKind.Alias) throw new Error(`Invalid span map kind: ${kind}`); - segments[i] = { - virtualStart, - virtualEnd: virtualStart + virtualLength, - originalStart, - originalEnd: originalStart + originalLength, - kind, - features, - }; - } - return this._cachedSpanMap = new SpanMap(segments); - } - - get supplementalSourceFileNames(): readonly string[] | undefined { - if (this._cachedSupplementalSourceFileNames !== undefined) return this._cachedSupplementalSourceFileNames; - const offset = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.SupplementalSourceFileNames, true); - if (offset === NO_STRUCTURED_DATA) return undefined; - return this._cachedSupplementalSourceFileNames = this.readStringArray(offset); - } - - get canonicalSourceFileName(): string | undefined { - const stringIndex = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.CanonicalSourceFileName, true); - return stringIndex === NO_STRUCTURED_DATA ? undefined : this.getString(stringIndex); - } - - get contentMapper(): string | undefined { - const stringIndex = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.ContentMapper, true); - return stringIndex === NO_STRUCTURED_DATA ? undefined : this.getString(stringIndex); - } - - get virtualFileName(): string | undefined { - const stringIndex = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.VirtualFileName, true); - return stringIndex === NO_STRUCTURED_DATA ? undefined : this.getString(stringIndex); - } - - get diagnosticDirectives(): readonly MappedDiagnosticDirective[] | undefined { - if (this._diagnosticDirectivesRead) return this._cachedDiagnosticDirectives; - this._diagnosticDirectivesRead = true; - const offset = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.DiagnosticDirectives, true); - if (offset === NO_STRUCTURED_DATA) return undefined; - const buf = new Uint8Array(this.view.buffer, this.view.byteOffset, this.view.byteLength); - const reader = new MsgpackReader(buf, this._offsetStructuredData + offset); - const count = reader.readArrayHeader(); - const directives = Array(count); - for (let i = 0; i < count; i++) { - if (reader.readArrayHeader() !== 6) throw new Error("Invalid diagnostic directive"); - const originalStart = reader.readUint(); - const originalLength = reader.readUint(); - const virtualStart = reader.readUint(); - const virtualLength = reader.readUint(); - directives[i] = { - originalRange: { pos: originalStart, end: originalStart + originalLength }, - virtualRange: { pos: virtualStart, end: virtualStart + virtualLength }, - policy: reader.readUint(), - unusedCode: reader.readUint(), - }; - } - return this._cachedDiagnosticDirectives = directives; - } - - get isDeclarationFile(): boolean { - return (this.flags & NodeFlags.Ambient) !== 0; - } - - get text(): string { - if (this._cachedText !== undefined) return this._cachedText; - const text = super.text!; - this._cachedText = text; - return text; - } - - // ═══ Line/character position mapping ═══ - - getLineStarts(): readonly number[] { - return this._lineStarts ??= computeLineStarts(this.text ?? ""); - } - - getLineAndCharacterOfPosition(position: number): LineAndCharacter { - const lineStarts = this.getLineStarts(); - const line = computeLineOfPosition(lineStarts, position); - return { line, character: position - lineStarts[line] }; - } - - getPositionOfLineAndCharacter(line: number, character: number): number { - const lineStarts = this.getLineStarts(); - if (line < 0 || line >= lineStarts.length) { - throw new Error(`Bad line number. Line: ${line}, lineStarts.length: ${lineStarts.length}`); - } - return lineStarts[line] + character; - } -} - -/** - * Find the 0-based line number containing the given position via binary search. - * Assumes the first line starts at position 0 and `position` is non-negative. - */ -function computeLineOfPosition(lineStarts: readonly number[], position: number): number { - let low = 0; - let high = lineStarts.length - 1; - while (low <= high) { - const middle = low + ((high - low) >> 1); - const value = lineStarts[middle]; - if (value < position) { - low = middle + 1; - } - else if (value > position) { - high = middle - 1; - } - else { - return middle; - } - } - return low - 1; -} - -/** - * Find a descendant node at a specific position with matching kind and end position. - */ -export function findDescendant(root: Node, pos: number, end: number, kind: SyntaxKind): Node | undefined { - if (root.pos === pos && root.end === end && root.kind === kind) { - return root; - } - - // Search children - let result: Node | undefined; - root.forEachChild(child => { - if (result) return result; // Already found - // Only search in children that could contain our target - if (child.pos <= pos && child.end >= end) { - result = findDescendant(child, pos, end, kind); - } - return undefined; - }); - - return result; -} - -/** - * Parsed components of a node handle. - */ -export interface ParsedNodeHandle { - index: number; - kind: SyntaxKind; - path: Path; -} - -/** - * Parse a node handle string into its components. - * Handle format: "index.kind.path" where path may contain dots. - */ -export function parseNodeHandle(handle: string): ParsedNodeHandle { - const firstDot = handle.indexOf("."); - if (firstDot === -1) { - throw new Error(`Invalid node handle: ${handle}`); - } - const secondDot = handle.indexOf(".", firstDot + 1); - if (secondDot === -1) { - throw new Error(`Invalid node handle: ${handle}`); - } - - return { - index: parseInt(handle.slice(0, firstDot), 10), - kind: parseInt(handle.slice(firstDot + 1, secondDot), 10) as SyntaxKind, - path: handle.slice(secondDot + 1) as Path, - }; -} - -/** - * Decode binary-encoded AST data into a Node. - * Works for any binary-encoded node, including synthetic nodes - * (e.g. from typeToTypeNode) that don't have a source file. - */ -export function decodeNode(data: Uint8Array): Node { - const sf = new RemoteSourceFile(data, new Wtf8Decoder()); - return sf as unknown as Node; -} - -/** - * Get the unique ID string for a remote node. - * Throws if the node is not a RemoteNode (i.e. not decoded from binary data). - */ -export function getNodeId(node: Node): string { - if (!(node instanceof RemoteNode)) { - throw new Error("getNodeId requires a RemoteNode"); - } - return node.id; -} +import { + computeLineStarts, + type FileReference, + type LineAndCharacter, + type MappedDiagnosticDirective, + type Node, + NodeFlags, + type Path, + SpanMap, + SpanMapFeature, + SpanMapKind, + SyntaxKind, + TokenFlags, +} from "../../ast/index.ts"; +import type { TimingCollector } from "../timing.ts"; +import { MsgpackReader } from "./msgpack.ts"; +import { + RemoteNode, + RemoteNodeList, +} from "./node.generated.ts"; +import { + NODE_EXTENDED_DATA_MASK, + type SourceFileInfo, + type TextDecoder, +} from "./node.infrastructure.ts"; +import { + HEADER_OFFSET_EXTENDED_DATA, + HEADER_OFFSET_NODES, + HEADER_OFFSET_STRING_TABLE, + HEADER_OFFSET_STRING_TABLE_OFFSETS, + HEADER_OFFSET_STRUCTURED_DATA, + KIND_NODE_LIST, + NODE_LEN, + NODE_OFFSET_KIND, + NODE_OFFSET_PARENT, +} from "./protocol.ts"; +import { Wtf8Decoder } from "./wtf8.ts"; + +// Re-export everything consumers need from the other two files. +export { RemoteNode, RemoteNodeList } from "./node.generated.ts"; +export { readParseOptionsKey, readSourceFileHash, RemoteNodeBase } from "./node.infrastructure.ts"; + +const sourceFileExtendedDataOffsets = { + Text: 0, + FileName: 4, + Path: 8, + LanguageVariant: 12, + ScriptKind: 16, + ReferencedFiles: 20, + TypeReferenceDirectives: 24, + LibReferenceDirectives: 28, + Imports: 32, + ModuleAugmentations: 36, + AmbientModuleNames: 40, + ExternalModuleIndicator: 44, + OriginalText: 48, + SpanMap: 52, + SupplementalSourceFileNames: 56, + CanonicalSourceFileName: 60, + ContentMapper: 64, + VirtualFileName: 68, + DiagnosticDirectives: 72, +} as const; + +for (const [index, offset] of Object.values(sourceFileExtendedDataOffsets).entries()) { + if (offset !== index * Uint32Array.BYTES_PER_ELEMENT) { + throw new Error(`Invalid SourceFile extended data offset ${offset} at index ${index}`); + } +} + +// ═══════════════════════════════════════════════════════════════════════════ +// RemoteSourceFile +// ═══════════════════════════════════════════════════════════════════════════ + +const NO_STRUCTURED_DATA = 0xFFFFFFFF; + +export class RemoteSourceFile extends RemoteNode implements SourceFileInfo { + readonly nodes: (RemoteNode | RemoteNodeList)[]; + readonly _offsetNodes: number; + readonly _offsetStringTableOffsets: number; + readonly _offsetStringTable: number; + readonly _offsetExtendedData: number; + readonly _offsetStructuredData: number; + readonly _decoder: TextDecoder; + readonly _timing: TimingCollector | undefined; + private _lineStarts: readonly number[] | undefined; + private _cachedText: string | undefined; + private _cachedReferencedFiles: readonly FileReference[] | undefined; + private _cachedTypeReferenceDirectives: readonly FileReference[] | undefined; + private _cachedLibReferenceDirectives: readonly FileReference[] | undefined; + private _cachedImports: readonly Node[] | undefined; + private _cachedModuleAugmentations: readonly Node[] | undefined; + private _cachedAmbientModuleNames: readonly string[] | undefined; + private _cachedSpanMap: SpanMap | undefined; + private _spanMapRead = false; + private _cachedSupplementalSourceFileNames: readonly string[] | undefined; + private _cachedDiagnosticDirectives: readonly MappedDiagnosticDirective[] | undefined; + private _diagnosticDirectivesRead = false; + + constructor(data: Uint8Array, decoder: TextDecoder, timing?: TimingCollector) { + const view = new DataView(data.buffer, data.byteOffset, data.byteLength); + const offsetNodes = view.getUint32(HEADER_OFFSET_NODES, true); + super(view, 1, undefined!, undefined!, offsetNodes); + this._sourceFile = this; + this._offsetNodes = offsetNodes; + this._offsetStringTableOffsets = view.getUint32(HEADER_OFFSET_STRING_TABLE_OFFSETS, true); + this._offsetStringTable = view.getUint32(HEADER_OFFSET_STRING_TABLE, true); + this._offsetExtendedData = view.getUint32(HEADER_OFFSET_EXTENDED_DATA, true); + this._offsetStructuredData = view.getUint32(HEADER_OFFSET_STRUCTURED_DATA, true); + this._decoder = decoder; + this._timing = timing; + this.nodes = Array((view.byteLength - offsetNodes) / NODE_LEN); + this.nodes[1] = this; + // Every node slot is materializable on demand except the nil sentinel at + // index 0 and the source-file node at index 1, which is pre-materialized. + timing?.recordSourceFileFetched(Math.max(0, this.nodes.length - 2)); + } + + readFileReferences(structuredDataOffset: number): readonly FileReference[] { + if (structuredDataOffset === NO_STRUCTURED_DATA) { + return []; + } + const buf = new Uint8Array(this.view.buffer, this.view.byteOffset, this.view.byteLength); + const reader = new MsgpackReader(buf, this._offsetStructuredData + structuredDataOffset); + const count = reader.readArrayHeader(); + const result: FileReference[] = []; + for (let i = 0; i < count; i++) { + reader.readArrayHeader(); // 5-element tuple + const pos = reader.readUint(); + const end = reader.readUint(); + const fileName = reader.readString(); + const resolutionMode = reader.readUint(); + const preserve = reader.readBool(); + result.push({ pos, end, fileName, resolutionMode, preserve }); + } + return result; + } + + readNodeIndexArray(structuredDataOffset: number): readonly Node[] { + if (structuredDataOffset === NO_STRUCTURED_DATA) { + return []; + } + const buf = new Uint8Array(this.view.buffer, this.view.byteOffset, this.view.byteLength); + const reader = new MsgpackReader(buf, this._offsetStructuredData + structuredDataOffset); + const count = reader.readArrayHeader(); + const result: Node[] = []; + for (let i = 0; i < count; i++) { + const nodeIndex = reader.readUint(); + result.push(this.getOrCreateNodeAtIndex(nodeIndex)); + } + return result; + } + + readStringArray(structuredDataOffset: number): readonly string[] { + if (structuredDataOffset === NO_STRUCTURED_DATA) { + return []; + } + const buf = new Uint8Array(this.view.buffer, this.view.byteOffset, this.view.byteLength); + const reader = new MsgpackReader(buf, this._offsetStructuredData + structuredDataOffset); + const count = reader.readArrayHeader(); + const result: string[] = []; + for (let i = 0; i < count; i++) { + result.push(reader.readString()); + } + return result; + } + + /** @internal */ + getOrCreateNodeAtIndex(index: number): Node { + let node = this.nodes[index]; + if (!node) { + // Resolve the real parent so that nodes looked up directly by index (e.g. via + // NodeHandle.resolve) report the correct `parent`, rather than always pointing at + // the source file. The stored parent index can refer to a synthetic NodeList + // container; skip those to mirror normal traversal, where list elements take the + // list's parent. The walk terminates at the source file, which occupies index 1 + // and is already cached. + let parentIndex = this.view.getUint32(this._offsetNodes + index * NODE_LEN + NODE_OFFSET_PARENT, true); + while ( + parentIndex !== index && + this.view.getUint32(this._offsetNodes + parentIndex * NODE_LEN + NODE_OFFSET_KIND, true) === KIND_NODE_LIST + ) { + parentIndex = this.view.getUint32(this._offsetNodes + parentIndex * NODE_LEN + NODE_OFFSET_PARENT, true); + } + const parent = parentIndex === index ? this : this.getOrCreateNodeAtIndex(parentIndex) as RemoteNode; + node = new RemoteNode(this.view, index, parent, this, this._offsetNodes); + this.nodes[index] = node; + this._timing?.recordMaterialization(); + } + return node as Node; + } + + // ═══ SourceFile-specific extended data getters ═══ + + private get extendedDataOffset(): number { + return this._offsetExtendedData + (this.data & NODE_EXTENDED_DATA_MASK); + } + + get fileName(): string { + const stringIndex = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.FileName, true); + return this.getString(stringIndex); + } + + get path(): string { + const stringIndex = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.Path, true); + return this.getString(stringIndex); + } + + get languageVariant(): number { + return this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.LanguageVariant, true); + } + + get scriptKind(): number { + return this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.ScriptKind, true); + } + + get referencedFiles(): readonly FileReference[] { + if (this._cachedReferencedFiles !== undefined) return this._cachedReferencedFiles; + const offset = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.ReferencedFiles, true); + const files = this.readFileReferences(offset); + this._cachedReferencedFiles = files; + return files; + } + + get typeReferenceDirectives(): readonly FileReference[] { + if (this._cachedTypeReferenceDirectives !== undefined) return this._cachedTypeReferenceDirectives; + const offset = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.TypeReferenceDirectives, true); + const directives = this.readFileReferences(offset); + this._cachedTypeReferenceDirectives = directives; + return directives; + } + + get libReferenceDirectives(): readonly FileReference[] { + if (this._cachedLibReferenceDirectives !== undefined) return this._cachedLibReferenceDirectives; + const offset = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.LibReferenceDirectives, true); + const directives = this.readFileReferences(offset); + this._cachedLibReferenceDirectives = directives; + return directives; + } + + get imports(): readonly Node[] { + if (this._cachedImports !== undefined) return this._cachedImports; + const offset = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.Imports, true); + const imports = this.readNodeIndexArray(offset); + this._cachedImports = imports; + return imports; + } + + get moduleAugmentations(): readonly Node[] { + if (this._cachedModuleAugmentations !== undefined) return this._cachedModuleAugmentations; + const offset = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.ModuleAugmentations, true); + const moduleAugmentations = this.readNodeIndexArray(offset); + this._cachedModuleAugmentations = moduleAugmentations; + return moduleAugmentations; + } + + get ambientModuleNames(): readonly string[] { + if (this._cachedAmbientModuleNames !== undefined) return this._cachedAmbientModuleNames; + const offset = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.AmbientModuleNames, true); + const names = this.readStringArray(offset); + this._cachedAmbientModuleNames = names; + return names; + } + + get externalModuleIndicator(): Node | true | undefined { + const nodeIndex = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.ExternalModuleIndicator, true); + if (nodeIndex === 0) return undefined; + if (nodeIndex === this.index) return true; + return this.getOrCreateNodeAtIndex(nodeIndex) as Node; + } + + get originalText(): string { + const stringIndex = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.OriginalText, true); + return this.getString(stringIndex); + } + + get spanMap(): SpanMap | undefined { + if (this._spanMapRead) return this._cachedSpanMap; + this._spanMapRead = true; + const offset = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.SpanMap, true); + if (offset === NO_STRUCTURED_DATA) return undefined; + const buf = new Uint8Array(this.view.buffer, this.view.byteOffset, this.view.byteLength); + const reader = new MsgpackReader(buf, this._offsetStructuredData + offset); + const count = reader.readArrayHeader(); + const segments = Array(count); + for (let i = 0; i < count; i++) { + const tupleLength = reader.readArrayHeader(); + if (tupleLength !== 5 && tupleLength !== 6) throw new Error("Invalid span map segment"); + const virtualStart = reader.readUint(); + const virtualLength = reader.readUint(); + const originalStart = reader.readUint(); + const originalLength = reader.readUint(); + const kind = reader.readUint(); + const features = tupleLength === 6 ? reader.readUint() as SpanMapFeature : SpanMapFeature.All; + if (kind !== SpanMapKind.Verbatim && kind !== SpanMapKind.Atom && kind !== SpanMapKind.Alias) throw new Error(`Invalid span map kind: ${kind}`); + segments[i] = { + virtualStart, + virtualEnd: virtualStart + virtualLength, + originalStart, + originalEnd: originalStart + originalLength, + kind, + features, + }; + } + return this._cachedSpanMap = new SpanMap(segments); + } + + get supplementalSourceFileNames(): readonly string[] | undefined { + if (this._cachedSupplementalSourceFileNames !== undefined) return this._cachedSupplementalSourceFileNames; + const offset = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.SupplementalSourceFileNames, true); + if (offset === NO_STRUCTURED_DATA) return undefined; + return this._cachedSupplementalSourceFileNames = this.readStringArray(offset); + } + + get canonicalSourceFileName(): string | undefined { + const stringIndex = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.CanonicalSourceFileName, true); + return stringIndex === NO_STRUCTURED_DATA ? undefined : this.getString(stringIndex); + } + + get contentMapper(): string | undefined { + const stringIndex = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.ContentMapper, true); + return stringIndex === NO_STRUCTURED_DATA ? undefined : this.getString(stringIndex); + } + + get virtualFileName(): string | undefined { + const stringIndex = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.VirtualFileName, true); + return stringIndex === NO_STRUCTURED_DATA ? undefined : this.getString(stringIndex); + } + + get diagnosticDirectives(): readonly MappedDiagnosticDirective[] | undefined { + if (this._diagnosticDirectivesRead) return this._cachedDiagnosticDirectives; + this._diagnosticDirectivesRead = true; + const offset = this.view.getUint32(this.extendedDataOffset + sourceFileExtendedDataOffsets.DiagnosticDirectives, true); + if (offset === NO_STRUCTURED_DATA) return undefined; + const buf = new Uint8Array(this.view.buffer, this.view.byteOffset, this.view.byteLength); + const reader = new MsgpackReader(buf, this._offsetStructuredData + offset); + const count = reader.readArrayHeader(); + const directives = Array(count); + for (let i = 0; i < count; i++) { + if (reader.readArrayHeader() !== 6) throw new Error("Invalid diagnostic directive"); + const originalStart = reader.readUint(); + const originalLength = reader.readUint(); + const virtualStart = reader.readUint(); + const virtualLength = reader.readUint(); + directives[i] = { + originalRange: { pos: originalStart, end: originalStart + originalLength }, + virtualRange: { pos: virtualStart, end: virtualStart + virtualLength }, + policy: reader.readUint(), + unusedCode: reader.readUint(), + }; + } + return this._cachedDiagnosticDirectives = directives; + } + + get isDeclarationFile(): boolean { + return (this.flags & NodeFlags.Ambient) !== 0; + } + + get text(): string { + if (this._cachedText !== undefined) return this._cachedText; + const text = super.text!; + this._cachedText = text; + return text; + } + + // ═══ Line/character position mapping ═══ + + getLineStarts(): readonly number[] { + return this._lineStarts ??= computeLineStarts(this.text ?? ""); + } + + getLineAndCharacterOfPosition(position: number): LineAndCharacter { + const lineStarts = this.getLineStarts(); + const line = computeLineOfPosition(lineStarts, position); + return { line, character: position - lineStarts[line] }; + } + + getPositionOfLineAndCharacter(line: number, character: number): number { + const lineStarts = this.getLineStarts(); + if (line < 0 || line >= lineStarts.length) { + throw new Error(`Bad line number. Line: ${line}, lineStarts.length: ${lineStarts.length}`); + } + return lineStarts[line] + character; + } +} + +/** + * Find the 0-based line number containing the given position via binary search. + * Assumes the first line starts at position 0 and `position` is non-negative. + */ +function computeLineOfPosition(lineStarts: readonly number[], position: number): number { + let low = 0; + let high = lineStarts.length - 1; + while (low <= high) { + const middle = low + ((high - low) >> 1); + const value = lineStarts[middle]; + if (value < position) { + low = middle + 1; + } + else if (value > position) { + high = middle - 1; + } + else { + return middle; + } + } + return low - 1; +} + +/** + * Find a descendant node at a specific position with matching kind and end position. + */ +export function findDescendant(root: Node, pos: number, end: number, kind: SyntaxKind): Node | undefined { + if (root.pos === pos && root.end === end && root.kind === kind) { + return root; + } + + // Search children + let result: Node | undefined; + root.forEachChild(child => { + if (result) return result; // Already found + // Only search in children that could contain our target + if (child.pos <= pos && child.end >= end) { + result = findDescendant(child, pos, end, kind); + } + return undefined; + }); + + return result; +} + +/** + * Parsed components of a node handle. + */ +export interface ParsedNodeHandle { + index: number; + kind: SyntaxKind; + path: Path; +} + +/** + * Parse a node handle string into its components. + * Handle format: "index.kind.path" where path may contain dots. + */ +export function parseNodeHandle(handle: string): ParsedNodeHandle { + const firstDot = handle.indexOf("."); + if (firstDot === -1) { + throw new Error(`Invalid node handle: ${handle}`); + } + const secondDot = handle.indexOf(".", firstDot + 1); + if (secondDot === -1) { + throw new Error(`Invalid node handle: ${handle}`); + } + + return { + index: parseInt(handle.slice(0, firstDot), 10), + kind: parseInt(handle.slice(firstDot + 1, secondDot), 10) as SyntaxKind, + path: handle.slice(secondDot + 1) as Path, + }; +} + +/** + * Decode binary-encoded AST data into a Node. + * Works for any binary-encoded node, including synthetic nodes + * (e.g. from typeToTypeNode) that don't have a source file. + */ +export function decodeNode(data: Uint8Array): Node { + const sf = new RemoteSourceFile(data, new Wtf8Decoder()); + return sf as unknown as Node; +} + +/** + * Get the unique ID string for a remote node. + * Throws if the node is not a RemoteNode (i.e. not decoded from binary data). + */ +export function getNodeId(node: Node): string { + if (!(node instanceof RemoteNode)) { + throw new Error("getNodeId requires a RemoteNode"); + } + return node.id; +} diff --git a/packages/typescript/src/api/node/protocol.generated.ts b/packages/typescript/src/api/node/protocol.generated.ts index 648eabeb2494b..6150be07f3f37 100644 --- a/packages/typescript/src/api/node/protocol.generated.ts +++ b/packages/typescript/src/api/node/protocol.generated.ts @@ -1,243 +1,243 @@ -// Code generated by tools/scripts/tsc/generate-encoder.ts. DO NOT EDIT. - -import { SyntaxKind } from "../../ast/index.ts"; - -export const childProperties: Readonly>> = { - [SyntaxKind.QualifiedName]: ["left", "right"], - [SyntaxKind.ComputedPropertyName]: ["expression"], - [SyntaxKind.Decorator]: ["expression"], - [SyntaxKind.IfStatement]: ["expression", "thenStatement", "elseStatement"], - [SyntaxKind.DoStatement]: ["statement", "expression"], - [SyntaxKind.WhileStatement]: ["expression", "statement"], - [SyntaxKind.ForStatement]: ["initializer", "condition", "incrementor", "statement"], - [SyntaxKind.ForInStatement]: ["awaitModifier", "initializer", "expression", "statement"], - [SyntaxKind.ForOfStatement]: ["awaitModifier", "initializer", "expression", "statement"], - [SyntaxKind.BreakStatement]: ["label"], - [SyntaxKind.ContinueStatement]: ["label"], - [SyntaxKind.ReturnStatement]: ["expression"], - [SyntaxKind.WithStatement]: ["expression", "statement"], - [SyntaxKind.SwitchStatement]: ["expression", "caseBlock"], - [SyntaxKind.CaseBlock]: ["clauses"], - [SyntaxKind.CaseClause]: ["expression", "statements"], - [SyntaxKind.DefaultClause]: ["expression", "statements"], - [SyntaxKind.ThrowStatement]: ["expression"], - [SyntaxKind.TryStatement]: ["tryBlock", "catchClause", "finallyBlock"], - [SyntaxKind.CatchClause]: ["variableDeclaration", "block"], - [SyntaxKind.LabeledStatement]: ["label", "statement"], - [SyntaxKind.ExpressionStatement]: ["expression"], - [SyntaxKind.Block]: ["statements"], - [SyntaxKind.VariableStatement]: ["modifiers", "declarationList"], - [SyntaxKind.VariableDeclaration]: ["name", "exclamationToken", "type", "initializer"], - [SyntaxKind.VariableDeclarationList]: ["declarations"], - [SyntaxKind.ObjectBindingPattern]: ["elements"], - [SyntaxKind.ArrayBindingPattern]: ["elements"], - [SyntaxKind.Parameter]: ["modifiers", "dotDotDotToken", "name", "questionToken", "type", "initializer"], - [SyntaxKind.BindingElement]: ["dotDotDotToken", "propertyName", "name", "initializer"], - [SyntaxKind.MissingDeclaration]: ["modifiers"], - [SyntaxKind.FunctionDeclaration]: ["modifiers", "asteriskToken", "name", "typeParameters", "parameters", "type", "body"], - [SyntaxKind.ClassDeclaration]: ["modifiers", "name", "typeParameters", "heritageClauses", "members"], - [SyntaxKind.ClassExpression]: ["modifiers", "name", "typeParameters", "heritageClauses", "members"], - [SyntaxKind.HeritageClause]: ["types"], - [SyntaxKind.InterfaceDeclaration]: ["modifiers", "name", "typeParameters", "heritageClauses", "members"], - [SyntaxKind.TypeAliasDeclaration]: ["modifiers", "name", "typeParameters", "type"], - [SyntaxKind.JSTypeAliasDeclaration]: ["modifiers", "name", "typeParameters", "type"], - [SyntaxKind.EnumMember]: ["name", "initializer"], - [SyntaxKind.EnumDeclaration]: ["modifiers", "name", "members"], - [SyntaxKind.ModuleBlock]: ["statements"], - [SyntaxKind.ImportDeclaration]: ["modifiers", "importClause", "moduleSpecifier", "attributes"], - [SyntaxKind.JSImportDeclaration]: ["modifiers", "importClause", "moduleSpecifier", "attributes"], - [SyntaxKind.ExternalModuleReference]: ["expression"], - [SyntaxKind.NamespaceImport]: ["name"], - [SyntaxKind.NamedImports]: ["elements"], - [SyntaxKind.ExportAssignment]: ["modifiers", "type", "expression"], - [SyntaxKind.NamespaceExportDeclaration]: ["modifiers", "name"], - [SyntaxKind.NamespaceExport]: ["name"], - [SyntaxKind.NamedExports]: ["elements"], - [SyntaxKind.ExportSpecifier]: ["propertyName", "name"], - [SyntaxKind.CallSignature]: ["typeParameters", "parameters", "type"], - [SyntaxKind.ConstructSignature]: ["typeParameters", "parameters", "type"], - [SyntaxKind.Constructor]: ["modifiers", "typeParameters", "parameters", "type", "body"], - [SyntaxKind.GetAccessor]: ["modifiers", "name", "typeParameters", "parameters", "type", "body"], - [SyntaxKind.SetAccessor]: ["modifiers", "name", "typeParameters", "parameters", "type", "body"], - [SyntaxKind.IndexSignature]: ["modifiers", "parameters", "type"], - [SyntaxKind.MethodSignature]: ["modifiers", "name", "postfixToken", "typeParameters", "parameters", "type"], - [SyntaxKind.MethodDeclaration]: ["modifiers", "asteriskToken", "name", "postfixToken", "typeParameters", "parameters", "type", "body"], - [SyntaxKind.PropertySignature]: ["modifiers", "name", "postfixToken", "type", "initializer"], - [SyntaxKind.PropertyDeclaration]: ["modifiers", "name", "postfixToken", "type", "initializer"], - [SyntaxKind.ClassStaticBlockDeclaration]: ["modifiers", "body"], - [SyntaxKind.BinaryExpression]: ["modifiers", "left", "type", "operatorToken", "right"], - [SyntaxKind.PrefixUnaryExpression]: ["operand"], - [SyntaxKind.PostfixUnaryExpression]: ["operand"], - [SyntaxKind.YieldExpression]: ["asteriskToken", "expression"], - [SyntaxKind.ArrowFunction]: ["modifiers", "typeParameters", "parameters", "type", "equalsGreaterThanToken", "body"], - [SyntaxKind.FunctionExpression]: ["modifiers", "asteriskToken", "name", "typeParameters", "parameters", "type", "body"], - [SyntaxKind.AsExpression]: ["expression", "type"], - [SyntaxKind.SatisfiesExpression]: ["expression", "type"], - [SyntaxKind.ConditionalExpression]: ["condition", "questionToken", "whenTrue", "colonToken", "whenFalse"], - [SyntaxKind.PropertyAccessExpression]: ["expression", "questionDotToken", "name"], - [SyntaxKind.ElementAccessExpression]: ["expression", "questionDotToken", "argumentExpression"], - [SyntaxKind.CallExpression]: ["expression", "questionDotToken", "typeArguments", "arguments"], - [SyntaxKind.NewExpression]: ["expression", "typeArguments", "arguments"], - [SyntaxKind.MetaProperty]: ["name"], - [SyntaxKind.NonNullExpression]: ["expression"], - [SyntaxKind.SpreadElement]: ["expression"], - [SyntaxKind.TemplateExpression]: ["head", "templateSpans"], - [SyntaxKind.TemplateSpan]: ["expression", "literal"], - [SyntaxKind.TaggedTemplateExpression]: ["tag", "questionDotToken", "typeArguments", "template"], - [SyntaxKind.ParenthesizedExpression]: ["expression"], - [SyntaxKind.ArrayLiteralExpression]: ["elements"], - [SyntaxKind.ObjectLiteralExpression]: ["properties"], - [SyntaxKind.SpreadAssignment]: ["expression"], - [SyntaxKind.PropertyAssignment]: ["modifiers", "name", "postfixToken", "type", "initializer"], - [SyntaxKind.ShorthandPropertyAssignment]: ["modifiers", "name", "postfixToken", "type", "equalsToken", "objectAssignmentInitializer"], - [SyntaxKind.DeleteExpression]: ["expression"], - [SyntaxKind.TypeOfExpression]: ["expression"], - [SyntaxKind.VoidExpression]: ["expression"], - [SyntaxKind.AwaitExpression]: ["expression"], - [SyntaxKind.TypeAssertionExpression]: ["type", "expression"], - [SyntaxKind.UnionType]: ["types"], - [SyntaxKind.IntersectionType]: ["types"], - [SyntaxKind.ConditionalType]: ["checkType", "extendsType", "trueType", "falseType"], - [SyntaxKind.TypeOperator]: ["type"], - [SyntaxKind.InferType]: ["typeParameter"], - [SyntaxKind.ArrayType]: ["elementType"], - [SyntaxKind.IndexedAccessType]: ["objectType", "indexType"], - [SyntaxKind.TypeReference]: ["typeName", "typeArguments"], - [SyntaxKind.ExpressionWithTypeArguments]: ["expression", "typeArguments"], - [SyntaxKind.LiteralType]: ["literal"], - [SyntaxKind.TypePredicate]: ["assertsModifier", "parameterName", "type"], - [SyntaxKind.ImportAttribute]: ["name", "value"], - [SyntaxKind.ImportAttributes]: ["attributes"], - [SyntaxKind.TypeQuery]: ["exprName", "typeArguments"], - [SyntaxKind.MappedType]: ["readonlyToken", "typeParameter", "nameType", "questionToken", "type", "members"], - [SyntaxKind.TypeLiteral]: ["members"], - [SyntaxKind.TupleType]: ["elements"], - [SyntaxKind.NamedTupleMember]: ["dotDotDotToken", "name", "questionToken", "type"], - [SyntaxKind.OptionalType]: ["type"], - [SyntaxKind.RestType]: ["type"], - [SyntaxKind.ParenthesizedType]: ["type"], - [SyntaxKind.FunctionType]: ["typeParameters", "parameters", "type"], - [SyntaxKind.ConstructorType]: ["modifiers", "typeParameters", "parameters", "type"], - [SyntaxKind.TemplateLiteralType]: ["head", "templateSpans"], - [SyntaxKind.TemplateLiteralTypeSpan]: ["type", "literal"], - [SyntaxKind.SyntheticExpression]: ["tupleNameSource"], - [SyntaxKind.PartiallyEmittedExpression]: ["expression"], - [SyntaxKind.JsxElement]: ["openingElement", "children", "closingElement"], - [SyntaxKind.JsxAttributes]: ["properties"], - [SyntaxKind.JsxNamespacedName]: ["namespace", "name"], - [SyntaxKind.JsxOpeningElement]: ["tagName", "typeArguments", "attributes"], - [SyntaxKind.JsxSelfClosingElement]: ["tagName", "typeArguments", "attributes"], - [SyntaxKind.JsxFragment]: ["openingFragment", "children", "closingFragment"], - [SyntaxKind.JsxAttribute]: ["name", "initializer"], - [SyntaxKind.JsxSpreadAttribute]: ["expression"], - [SyntaxKind.JsxClosingElement]: ["tagName"], - [SyntaxKind.JsxExpression]: ["dotDotDotToken", "expression"], - [SyntaxKind.SyntaxList]: ["children"], - [SyntaxKind.JSDoc]: ["comment", "tags"], - [SyntaxKind.JSDocTypeExpression]: ["type"], - [SyntaxKind.JSDocNonNullableType]: ["type"], - [SyntaxKind.JSDocNullableType]: ["type"], - [SyntaxKind.JSDocVariadicType]: ["type"], - [SyntaxKind.JSDocOptionalType]: ["type"], - [SyntaxKind.JSDocTypeTag]: ["tagName", "typeExpression", "comment"], - [SyntaxKind.JSDocUnknownTag]: ["tagName", "comment"], - [SyntaxKind.JSDocTemplateTag]: ["tagName", "constraint", "typeParameters", "comment"], - [SyntaxKind.JSDocReturnTag]: ["tagName", "typeExpression", "comment"], - [SyntaxKind.JSDocPublicTag]: ["tagName", "comment"], - [SyntaxKind.JSDocPrivateTag]: ["tagName", "comment"], - [SyntaxKind.JSDocProtectedTag]: ["tagName", "comment"], - [SyntaxKind.JSDocReadonlyTag]: ["tagName", "comment"], - [SyntaxKind.JSDocOverrideTag]: ["tagName", "comment"], - [SyntaxKind.JSDocDeprecatedTag]: ["tagName", "comment"], - [SyntaxKind.JSDocSeeTag]: ["tagName", "nameExpression", "comment"], - [SyntaxKind.JSDocImplementsTag]: ["tagName", "className", "comment"], - [SyntaxKind.JSDocAugmentsTag]: ["tagName", "className", "comment"], - [SyntaxKind.JSDocSatisfiesTag]: ["tagName", "typeExpression", "comment"], - [SyntaxKind.JSDocThrowsTag]: ["tagName", "typeExpression", "comment"], - [SyntaxKind.JSDocThisTag]: ["tagName", "typeExpression", "comment"], - [SyntaxKind.JSDocImportTag]: ["tagName", "importClause", "moduleSpecifier", "attributes", "comment"], - [SyntaxKind.JSDocCallbackTag]: ["tagName", "typeExpression", "name", "comment"], - [SyntaxKind.JSDocOverloadTag]: ["tagName", "typeExpression", "comment"], - [SyntaxKind.JSDocTypedefTag]: ["tagName", "typeExpression", "name", "comment"], - [SyntaxKind.JSDocSignature]: ["typeParameters", "parameters", "type"], - [SyntaxKind.JSDocNameReference]: ["name"], - [SyntaxKind.SourceFile]: ["statements", "endOfFileToken"], - [SyntaxKind.ModuleDeclaration]: ["modifiers", "name", "body"], - [SyntaxKind.ImportEqualsDeclaration]: ["modifiers", "name", "moduleReference"], - [SyntaxKind.ExportDeclaration]: ["modifiers", "exportClause", "moduleSpecifier", "attributes"], - [SyntaxKind.ImportType]: ["argument", "attributes", "qualifier", "typeArguments"], - [SyntaxKind.ImportClause]: ["name", "namedBindings"], - [SyntaxKind.ImportSpecifier]: ["propertyName", "name"], - [SyntaxKind.JSDocLink]: ["name"], - [SyntaxKind.JSDocLinkPlain]: ["name"], - [SyntaxKind.JSDocLinkCode]: ["name"], - [SyntaxKind.TypeParameter]: ["modifiers", "name", "constraint", "expression", "defaultType"], - [SyntaxKind.SyntheticReferenceExpression]: ["expression", "thisArg"], - [SyntaxKind.JSDocTypeLiteral]: ["jsdocPropertyTags"], - [SyntaxKind.JSDocParameterTag]: ["tagName", "name", "typeExpression", "comment"], - [SyntaxKind.JSDocPropertyTag]: ["tagName", "name", "typeExpression", "comment"], -}; - -export const singleChildNodePropertyNames: Readonly>> = { - [SyntaxKind.ComputedPropertyName]: "expression", - [SyntaxKind.Decorator]: "expression", - [SyntaxKind.BreakStatement]: "label", - [SyntaxKind.ContinueStatement]: "label", - [SyntaxKind.ReturnStatement]: "expression", - [SyntaxKind.CaseBlock]: "clauses", - [SyntaxKind.ThrowStatement]: "expression", - [SyntaxKind.ExpressionStatement]: "expression", - [SyntaxKind.Block]: "statements", - [SyntaxKind.VariableDeclarationList]: "declarations", - [SyntaxKind.ObjectBindingPattern]: "elements", - [SyntaxKind.ArrayBindingPattern]: "elements", - [SyntaxKind.MissingDeclaration]: "modifiers", - [SyntaxKind.HeritageClause]: "types", - [SyntaxKind.ModuleBlock]: "statements", - [SyntaxKind.ExternalModuleReference]: "expression", - [SyntaxKind.NamespaceImport]: "name", - [SyntaxKind.NamedImports]: "elements", - [SyntaxKind.NamespaceExport]: "name", - [SyntaxKind.NamedExports]: "elements", - [SyntaxKind.PrefixUnaryExpression]: "operand", - [SyntaxKind.PostfixUnaryExpression]: "operand", - [SyntaxKind.MetaProperty]: "name", - [SyntaxKind.NonNullExpression]: "expression", - [SyntaxKind.SpreadElement]: "expression", - [SyntaxKind.ParenthesizedExpression]: "expression", - [SyntaxKind.ArrayLiteralExpression]: "elements", - [SyntaxKind.ObjectLiteralExpression]: "properties", - [SyntaxKind.SpreadAssignment]: "expression", - [SyntaxKind.DeleteExpression]: "expression", - [SyntaxKind.TypeOfExpression]: "expression", - [SyntaxKind.VoidExpression]: "expression", - [SyntaxKind.AwaitExpression]: "expression", - [SyntaxKind.UnionType]: "types", - [SyntaxKind.IntersectionType]: "types", - [SyntaxKind.TypeOperator]: "type", - [SyntaxKind.InferType]: "typeParameter", - [SyntaxKind.ArrayType]: "elementType", - [SyntaxKind.LiteralType]: "literal", - [SyntaxKind.ImportAttributes]: "attributes", - [SyntaxKind.TypeLiteral]: "members", - [SyntaxKind.TupleType]: "elements", - [SyntaxKind.OptionalType]: "type", - [SyntaxKind.RestType]: "type", - [SyntaxKind.ParenthesizedType]: "type", - [SyntaxKind.SyntheticExpression]: "tupleNameSource", - [SyntaxKind.PartiallyEmittedExpression]: "expression", - [SyntaxKind.JsxAttributes]: "properties", - [SyntaxKind.JsxSpreadAttribute]: "expression", - [SyntaxKind.JsxClosingElement]: "tagName", - [SyntaxKind.SyntaxList]: "children", - [SyntaxKind.JSDocTypeExpression]: "type", - [SyntaxKind.JSDocNonNullableType]: "type", - [SyntaxKind.JSDocNullableType]: "type", - [SyntaxKind.JSDocVariadicType]: "type", - [SyntaxKind.JSDocOptionalType]: "type", - [SyntaxKind.JSDocNameReference]: "name", - [SyntaxKind.JSDocLink]: "name", - [SyntaxKind.JSDocLinkPlain]: "name", - [SyntaxKind.JSDocLinkCode]: "name", - [SyntaxKind.JSDocTypeLiteral]: "jsdocPropertyTags", -}; +// Code generated by tools/scripts/tsc/generate-encoder.ts. DO NOT EDIT. + +import { SyntaxKind } from "../../ast/index.ts"; + +export const childProperties: Readonly>> = { + [SyntaxKind.QualifiedName]: ["left", "right"], + [SyntaxKind.ComputedPropertyName]: ["expression"], + [SyntaxKind.Decorator]: ["expression"], + [SyntaxKind.IfStatement]: ["expression", "thenStatement", "elseStatement"], + [SyntaxKind.DoStatement]: ["statement", "expression"], + [SyntaxKind.WhileStatement]: ["expression", "statement"], + [SyntaxKind.ForStatement]: ["initializer", "condition", "incrementor", "statement"], + [SyntaxKind.ForInStatement]: ["awaitModifier", "initializer", "expression", "statement"], + [SyntaxKind.ForOfStatement]: ["awaitModifier", "initializer", "expression", "statement"], + [SyntaxKind.BreakStatement]: ["label"], + [SyntaxKind.ContinueStatement]: ["label"], + [SyntaxKind.ReturnStatement]: ["expression"], + [SyntaxKind.WithStatement]: ["expression", "statement"], + [SyntaxKind.SwitchStatement]: ["expression", "caseBlock"], + [SyntaxKind.CaseBlock]: ["clauses"], + [SyntaxKind.CaseClause]: ["expression", "statements"], + [SyntaxKind.DefaultClause]: ["expression", "statements"], + [SyntaxKind.ThrowStatement]: ["expression"], + [SyntaxKind.TryStatement]: ["tryBlock", "catchClause", "finallyBlock"], + [SyntaxKind.CatchClause]: ["variableDeclaration", "block"], + [SyntaxKind.LabeledStatement]: ["label", "statement"], + [SyntaxKind.ExpressionStatement]: ["expression"], + [SyntaxKind.Block]: ["statements"], + [SyntaxKind.VariableStatement]: ["modifiers", "declarationList"], + [SyntaxKind.VariableDeclaration]: ["name", "exclamationToken", "type", "initializer"], + [SyntaxKind.VariableDeclarationList]: ["declarations"], + [SyntaxKind.ObjectBindingPattern]: ["elements"], + [SyntaxKind.ArrayBindingPattern]: ["elements"], + [SyntaxKind.Parameter]: ["modifiers", "dotDotDotToken", "name", "questionToken", "type", "initializer"], + [SyntaxKind.BindingElement]: ["dotDotDotToken", "propertyName", "name", "initializer"], + [SyntaxKind.MissingDeclaration]: ["modifiers"], + [SyntaxKind.FunctionDeclaration]: ["modifiers", "asteriskToken", "name", "typeParameters", "parameters", "type", "body"], + [SyntaxKind.ClassDeclaration]: ["modifiers", "name", "typeParameters", "heritageClauses", "members"], + [SyntaxKind.ClassExpression]: ["modifiers", "name", "typeParameters", "heritageClauses", "members"], + [SyntaxKind.HeritageClause]: ["types"], + [SyntaxKind.InterfaceDeclaration]: ["modifiers", "name", "typeParameters", "heritageClauses", "members"], + [SyntaxKind.TypeAliasDeclaration]: ["modifiers", "name", "typeParameters", "type"], + [SyntaxKind.JSTypeAliasDeclaration]: ["modifiers", "name", "typeParameters", "type"], + [SyntaxKind.EnumMember]: ["name", "initializer"], + [SyntaxKind.EnumDeclaration]: ["modifiers", "name", "members"], + [SyntaxKind.ModuleBlock]: ["statements"], + [SyntaxKind.ImportDeclaration]: ["modifiers", "importClause", "moduleSpecifier", "attributes"], + [SyntaxKind.JSImportDeclaration]: ["modifiers", "importClause", "moduleSpecifier", "attributes"], + [SyntaxKind.ExternalModuleReference]: ["expression"], + [SyntaxKind.NamespaceImport]: ["name"], + [SyntaxKind.NamedImports]: ["elements"], + [SyntaxKind.ExportAssignment]: ["modifiers", "type", "expression"], + [SyntaxKind.NamespaceExportDeclaration]: ["modifiers", "name"], + [SyntaxKind.NamespaceExport]: ["name"], + [SyntaxKind.NamedExports]: ["elements"], + [SyntaxKind.ExportSpecifier]: ["propertyName", "name"], + [SyntaxKind.CallSignature]: ["typeParameters", "parameters", "type"], + [SyntaxKind.ConstructSignature]: ["typeParameters", "parameters", "type"], + [SyntaxKind.Constructor]: ["modifiers", "typeParameters", "parameters", "type", "body"], + [SyntaxKind.GetAccessor]: ["modifiers", "name", "typeParameters", "parameters", "type", "body"], + [SyntaxKind.SetAccessor]: ["modifiers", "name", "typeParameters", "parameters", "type", "body"], + [SyntaxKind.IndexSignature]: ["modifiers", "parameters", "type"], + [SyntaxKind.MethodSignature]: ["modifiers", "name", "postfixToken", "typeParameters", "parameters", "type"], + [SyntaxKind.MethodDeclaration]: ["modifiers", "asteriskToken", "name", "postfixToken", "typeParameters", "parameters", "type", "body"], + [SyntaxKind.PropertySignature]: ["modifiers", "name", "postfixToken", "type", "initializer"], + [SyntaxKind.PropertyDeclaration]: ["modifiers", "name", "postfixToken", "type", "initializer"], + [SyntaxKind.ClassStaticBlockDeclaration]: ["modifiers", "body"], + [SyntaxKind.BinaryExpression]: ["modifiers", "left", "type", "operatorToken", "right"], + [SyntaxKind.PrefixUnaryExpression]: ["operand"], + [SyntaxKind.PostfixUnaryExpression]: ["operand"], + [SyntaxKind.YieldExpression]: ["asteriskToken", "expression"], + [SyntaxKind.ArrowFunction]: ["modifiers", "typeParameters", "parameters", "type", "equalsGreaterThanToken", "body"], + [SyntaxKind.FunctionExpression]: ["modifiers", "asteriskToken", "name", "typeParameters", "parameters", "type", "body"], + [SyntaxKind.AsExpression]: ["expression", "type"], + [SyntaxKind.SatisfiesExpression]: ["expression", "type"], + [SyntaxKind.ConditionalExpression]: ["condition", "questionToken", "whenTrue", "colonToken", "whenFalse"], + [SyntaxKind.PropertyAccessExpression]: ["expression", "questionDotToken", "name"], + [SyntaxKind.ElementAccessExpression]: ["expression", "questionDotToken", "argumentExpression"], + [SyntaxKind.CallExpression]: ["expression", "questionDotToken", "typeArguments", "arguments"], + [SyntaxKind.NewExpression]: ["expression", "typeArguments", "arguments"], + [SyntaxKind.MetaProperty]: ["name"], + [SyntaxKind.NonNullExpression]: ["expression"], + [SyntaxKind.SpreadElement]: ["expression"], + [SyntaxKind.TemplateExpression]: ["head", "templateSpans"], + [SyntaxKind.TemplateSpan]: ["expression", "literal"], + [SyntaxKind.TaggedTemplateExpression]: ["tag", "questionDotToken", "typeArguments", "template"], + [SyntaxKind.ParenthesizedExpression]: ["expression"], + [SyntaxKind.ArrayLiteralExpression]: ["elements"], + [SyntaxKind.ObjectLiteralExpression]: ["properties"], + [SyntaxKind.SpreadAssignment]: ["expression"], + [SyntaxKind.PropertyAssignment]: ["modifiers", "name", "postfixToken", "type", "initializer"], + [SyntaxKind.ShorthandPropertyAssignment]: ["modifiers", "name", "postfixToken", "type", "equalsToken", "objectAssignmentInitializer"], + [SyntaxKind.DeleteExpression]: ["expression"], + [SyntaxKind.TypeOfExpression]: ["expression"], + [SyntaxKind.VoidExpression]: ["expression"], + [SyntaxKind.AwaitExpression]: ["expression"], + [SyntaxKind.TypeAssertionExpression]: ["type", "expression"], + [SyntaxKind.UnionType]: ["types"], + [SyntaxKind.IntersectionType]: ["types"], + [SyntaxKind.ConditionalType]: ["checkType", "extendsType", "trueType", "falseType"], + [SyntaxKind.TypeOperator]: ["type"], + [SyntaxKind.InferType]: ["typeParameter"], + [SyntaxKind.ArrayType]: ["elementType"], + [SyntaxKind.IndexedAccessType]: ["objectType", "indexType"], + [SyntaxKind.TypeReference]: ["typeName", "typeArguments"], + [SyntaxKind.ExpressionWithTypeArguments]: ["expression", "typeArguments"], + [SyntaxKind.LiteralType]: ["literal"], + [SyntaxKind.TypePredicate]: ["assertsModifier", "parameterName", "type"], + [SyntaxKind.ImportAttribute]: ["name", "value"], + [SyntaxKind.ImportAttributes]: ["attributes"], + [SyntaxKind.TypeQuery]: ["exprName", "typeArguments"], + [SyntaxKind.MappedType]: ["readonlyToken", "typeParameter", "nameType", "questionToken", "type", "members"], + [SyntaxKind.TypeLiteral]: ["members"], + [SyntaxKind.TupleType]: ["elements"], + [SyntaxKind.NamedTupleMember]: ["dotDotDotToken", "name", "questionToken", "type"], + [SyntaxKind.OptionalType]: ["type"], + [SyntaxKind.RestType]: ["type"], + [SyntaxKind.ParenthesizedType]: ["type"], + [SyntaxKind.FunctionType]: ["typeParameters", "parameters", "type"], + [SyntaxKind.ConstructorType]: ["modifiers", "typeParameters", "parameters", "type"], + [SyntaxKind.TemplateLiteralType]: ["head", "templateSpans"], + [SyntaxKind.TemplateLiteralTypeSpan]: ["type", "literal"], + [SyntaxKind.SyntheticExpression]: ["tupleNameSource"], + [SyntaxKind.PartiallyEmittedExpression]: ["expression"], + [SyntaxKind.JsxElement]: ["openingElement", "children", "closingElement"], + [SyntaxKind.JsxAttributes]: ["properties"], + [SyntaxKind.JsxNamespacedName]: ["namespace", "name"], + [SyntaxKind.JsxOpeningElement]: ["tagName", "typeArguments", "attributes"], + [SyntaxKind.JsxSelfClosingElement]: ["tagName", "typeArguments", "attributes"], + [SyntaxKind.JsxFragment]: ["openingFragment", "children", "closingFragment"], + [SyntaxKind.JsxAttribute]: ["name", "initializer"], + [SyntaxKind.JsxSpreadAttribute]: ["expression"], + [SyntaxKind.JsxClosingElement]: ["tagName"], + [SyntaxKind.JsxExpression]: ["dotDotDotToken", "expression"], + [SyntaxKind.SyntaxList]: ["children"], + [SyntaxKind.JSDoc]: ["comment", "tags"], + [SyntaxKind.JSDocTypeExpression]: ["type"], + [SyntaxKind.JSDocNonNullableType]: ["type"], + [SyntaxKind.JSDocNullableType]: ["type"], + [SyntaxKind.JSDocVariadicType]: ["type"], + [SyntaxKind.JSDocOptionalType]: ["type"], + [SyntaxKind.JSDocTypeTag]: ["tagName", "typeExpression", "comment"], + [SyntaxKind.JSDocUnknownTag]: ["tagName", "comment"], + [SyntaxKind.JSDocTemplateTag]: ["tagName", "constraint", "typeParameters", "comment"], + [SyntaxKind.JSDocReturnTag]: ["tagName", "typeExpression", "comment"], + [SyntaxKind.JSDocPublicTag]: ["tagName", "comment"], + [SyntaxKind.JSDocPrivateTag]: ["tagName", "comment"], + [SyntaxKind.JSDocProtectedTag]: ["tagName", "comment"], + [SyntaxKind.JSDocReadonlyTag]: ["tagName", "comment"], + [SyntaxKind.JSDocOverrideTag]: ["tagName", "comment"], + [SyntaxKind.JSDocDeprecatedTag]: ["tagName", "comment"], + [SyntaxKind.JSDocSeeTag]: ["tagName", "nameExpression", "comment"], + [SyntaxKind.JSDocImplementsTag]: ["tagName", "className", "comment"], + [SyntaxKind.JSDocAugmentsTag]: ["tagName", "className", "comment"], + [SyntaxKind.JSDocSatisfiesTag]: ["tagName", "typeExpression", "comment"], + [SyntaxKind.JSDocThrowsTag]: ["tagName", "typeExpression", "comment"], + [SyntaxKind.JSDocThisTag]: ["tagName", "typeExpression", "comment"], + [SyntaxKind.JSDocImportTag]: ["tagName", "importClause", "moduleSpecifier", "attributes", "comment"], + [SyntaxKind.JSDocCallbackTag]: ["tagName", "typeExpression", "name", "comment"], + [SyntaxKind.JSDocOverloadTag]: ["tagName", "typeExpression", "comment"], + [SyntaxKind.JSDocTypedefTag]: ["tagName", "typeExpression", "name", "comment"], + [SyntaxKind.JSDocSignature]: ["typeParameters", "parameters", "type"], + [SyntaxKind.JSDocNameReference]: ["name"], + [SyntaxKind.SourceFile]: ["statements", "endOfFileToken"], + [SyntaxKind.ModuleDeclaration]: ["modifiers", "name", "body"], + [SyntaxKind.ImportEqualsDeclaration]: ["modifiers", "name", "moduleReference"], + [SyntaxKind.ExportDeclaration]: ["modifiers", "exportClause", "moduleSpecifier", "attributes"], + [SyntaxKind.ImportType]: ["argument", "attributes", "qualifier", "typeArguments"], + [SyntaxKind.ImportClause]: ["name", "namedBindings"], + [SyntaxKind.ImportSpecifier]: ["propertyName", "name"], + [SyntaxKind.JSDocLink]: ["name"], + [SyntaxKind.JSDocLinkPlain]: ["name"], + [SyntaxKind.JSDocLinkCode]: ["name"], + [SyntaxKind.TypeParameter]: ["modifiers", "name", "constraint", "expression", "defaultType"], + [SyntaxKind.SyntheticReferenceExpression]: ["expression", "thisArg"], + [SyntaxKind.JSDocTypeLiteral]: ["jsdocPropertyTags"], + [SyntaxKind.JSDocParameterTag]: ["tagName", "name", "typeExpression", "comment"], + [SyntaxKind.JSDocPropertyTag]: ["tagName", "name", "typeExpression", "comment"], +}; + +export const singleChildNodePropertyNames: Readonly>> = { + [SyntaxKind.ComputedPropertyName]: "expression", + [SyntaxKind.Decorator]: "expression", + [SyntaxKind.BreakStatement]: "label", + [SyntaxKind.ContinueStatement]: "label", + [SyntaxKind.ReturnStatement]: "expression", + [SyntaxKind.CaseBlock]: "clauses", + [SyntaxKind.ThrowStatement]: "expression", + [SyntaxKind.ExpressionStatement]: "expression", + [SyntaxKind.Block]: "statements", + [SyntaxKind.VariableDeclarationList]: "declarations", + [SyntaxKind.ObjectBindingPattern]: "elements", + [SyntaxKind.ArrayBindingPattern]: "elements", + [SyntaxKind.MissingDeclaration]: "modifiers", + [SyntaxKind.HeritageClause]: "types", + [SyntaxKind.ModuleBlock]: "statements", + [SyntaxKind.ExternalModuleReference]: "expression", + [SyntaxKind.NamespaceImport]: "name", + [SyntaxKind.NamedImports]: "elements", + [SyntaxKind.NamespaceExport]: "name", + [SyntaxKind.NamedExports]: "elements", + [SyntaxKind.PrefixUnaryExpression]: "operand", + [SyntaxKind.PostfixUnaryExpression]: "operand", + [SyntaxKind.MetaProperty]: "name", + [SyntaxKind.NonNullExpression]: "expression", + [SyntaxKind.SpreadElement]: "expression", + [SyntaxKind.ParenthesizedExpression]: "expression", + [SyntaxKind.ArrayLiteralExpression]: "elements", + [SyntaxKind.ObjectLiteralExpression]: "properties", + [SyntaxKind.SpreadAssignment]: "expression", + [SyntaxKind.DeleteExpression]: "expression", + [SyntaxKind.TypeOfExpression]: "expression", + [SyntaxKind.VoidExpression]: "expression", + [SyntaxKind.AwaitExpression]: "expression", + [SyntaxKind.UnionType]: "types", + [SyntaxKind.IntersectionType]: "types", + [SyntaxKind.TypeOperator]: "type", + [SyntaxKind.InferType]: "typeParameter", + [SyntaxKind.ArrayType]: "elementType", + [SyntaxKind.LiteralType]: "literal", + [SyntaxKind.ImportAttributes]: "attributes", + [SyntaxKind.TypeLiteral]: "members", + [SyntaxKind.TupleType]: "elements", + [SyntaxKind.OptionalType]: "type", + [SyntaxKind.RestType]: "type", + [SyntaxKind.ParenthesizedType]: "type", + [SyntaxKind.SyntheticExpression]: "tupleNameSource", + [SyntaxKind.PartiallyEmittedExpression]: "expression", + [SyntaxKind.JsxAttributes]: "properties", + [SyntaxKind.JsxSpreadAttribute]: "expression", + [SyntaxKind.JsxClosingElement]: "tagName", + [SyntaxKind.SyntaxList]: "children", + [SyntaxKind.JSDocTypeExpression]: "type", + [SyntaxKind.JSDocNonNullableType]: "type", + [SyntaxKind.JSDocNullableType]: "type", + [SyntaxKind.JSDocVariadicType]: "type", + [SyntaxKind.JSDocOptionalType]: "type", + [SyntaxKind.JSDocNameReference]: "name", + [SyntaxKind.JSDocLink]: "name", + [SyntaxKind.JSDocLinkPlain]: "name", + [SyntaxKind.JSDocLinkCode]: "name", + [SyntaxKind.JSDocTypeLiteral]: "jsdocPropertyTags", +}; diff --git a/packages/typescript/src/api/node/protocol.ts b/packages/typescript/src/api/node/protocol.ts index 52ff7e831124f..f0a51d7f602d7 100644 --- a/packages/typescript/src/api/node/protocol.ts +++ b/packages/typescript/src/api/node/protocol.ts @@ -1,36 +1,36 @@ -export const PROTOCOL_VERSION = 8; - -export const HEADER_OFFSET_METADATA = 0; -export const HEADER_OFFSET_HASH_LO0 = 4; -export const HEADER_OFFSET_HASH_LO1 = 8; -export const HEADER_OFFSET_HASH_HI0 = 12; -export const HEADER_OFFSET_HASH_HI1 = 16; -export const HEADER_OFFSET_PARSE_OPTIONS = 20; -export const HEADER_OFFSET_STRING_TABLE_OFFSETS = 24; -export const HEADER_OFFSET_STRING_TABLE = 28; -export const HEADER_OFFSET_EXTENDED_DATA = 32; -export const HEADER_OFFSET_STRUCTURED_DATA = 36; -export const HEADER_OFFSET_NODES = 40; -export const HEADER_SIZE = 44; - -export const NODE_LEN = 28; - -export const NODE_OFFSET_KIND = 0; -export const NODE_OFFSET_POS = 4; -export const NODE_OFFSET_END = 8; -export const NODE_OFFSET_NEXT = 12; -export const NODE_OFFSET_PARENT = 16; -export const NODE_OFFSET_DATA = 20; -export const NODE_OFFSET_FLAGS = 24; - -export const KIND_NODE_LIST = 0xFFFFFFFF; - -export const NODE_DATA_TYPE_CHILDREN = 0x00000000; -export const NODE_DATA_TYPE_STRING = 0x40000000; -export const NODE_DATA_TYPE_EXTENDED = 0x80000000; - -export const NODE_STRING_INDEX_MASK = 0x00FFFFFF; -export const NODE_EXTENDED_DATA_MASK = 0x00FFFFFF; - -// Re-export generated child properties -export { childProperties, singleChildNodePropertyNames } from "./protocol.generated.ts"; +export const PROTOCOL_VERSION = 8; + +export const HEADER_OFFSET_METADATA = 0; +export const HEADER_OFFSET_HASH_LO0 = 4; +export const HEADER_OFFSET_HASH_LO1 = 8; +export const HEADER_OFFSET_HASH_HI0 = 12; +export const HEADER_OFFSET_HASH_HI1 = 16; +export const HEADER_OFFSET_PARSE_OPTIONS = 20; +export const HEADER_OFFSET_STRING_TABLE_OFFSETS = 24; +export const HEADER_OFFSET_STRING_TABLE = 28; +export const HEADER_OFFSET_EXTENDED_DATA = 32; +export const HEADER_OFFSET_STRUCTURED_DATA = 36; +export const HEADER_OFFSET_NODES = 40; +export const HEADER_SIZE = 44; + +export const NODE_LEN = 28; + +export const NODE_OFFSET_KIND = 0; +export const NODE_OFFSET_POS = 4; +export const NODE_OFFSET_END = 8; +export const NODE_OFFSET_NEXT = 12; +export const NODE_OFFSET_PARENT = 16; +export const NODE_OFFSET_DATA = 20; +export const NODE_OFFSET_FLAGS = 24; + +export const KIND_NODE_LIST = 0xFFFFFFFF; + +export const NODE_DATA_TYPE_CHILDREN = 0x00000000; +export const NODE_DATA_TYPE_STRING = 0x40000000; +export const NODE_DATA_TYPE_EXTENDED = 0x80000000; + +export const NODE_STRING_INDEX_MASK = 0x00FFFFFF; +export const NODE_EXTENDED_DATA_MASK = 0x00FFFFFF; + +// Re-export generated child properties +export { childProperties, singleChildNodePropertyNames } from "./protocol.generated.ts"; diff --git a/packages/typescript/src/api/node/wtf8.ts b/packages/typescript/src/api/node/wtf8.ts index 45bba64a91081..2d05dc9a12c56 100644 --- a/packages/typescript/src/api/node/wtf8.ts +++ b/packages/typescript/src/api/node/wtf8.ts @@ -1,72 +1,72 @@ -import { Buffer } from "node:buffer"; - -const surrogateLeadByte = 0xED; -const surrogateSecondByteMin = 0xA0; -const surrogateSecondByteMax = 0xBF; -const continuationByteMin = 0x80; -const continuationByteMax = 0xBF; -type DecodeOptions = Parameters[1]; - -function isWtf8Surrogate(bytes: Uint8Array, index: number): boolean { - return index + 2 < bytes.length - && bytes[index] === surrogateLeadByte - && bytes[index + 1] >= surrogateSecondByteMin - && bytes[index + 1] <= surrogateSecondByteMax - && bytes[index + 2] >= continuationByteMin - && bytes[index + 2] <= continuationByteMax; -} - -function getSurrogateCodeUnit(bytes: Uint8Array, index: number): number { - return 0xD000 | ((bytes[index + 1] & 0x3F) << 6) | (bytes[index + 2] & 0x3F); -} - -function hasSurrogateLeadByte(bytes: Uint8Array): boolean { - return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).indexOf(surrogateLeadByte) >= 0; -} - -function toUint8Array(input: NodeJS.AllowSharedBufferSource): Uint8Array { - if (input instanceof Uint8Array) { - return input; - } - if (ArrayBuffer.isView(input)) { - return new Uint8Array(input.buffer, input.byteOffset, input.byteLength); - } - return new Uint8Array(input); -} - -export class Wtf8Decoder extends TextDecoder { - override decode(input?: NodeJS.AllowSharedBufferSource, options?: DecodeOptions): string { - if (input === undefined) { - return super.decode(input, options); - } - - const bytes = toUint8Array(input); - if (!hasSurrogateLeadByte(bytes)) { - return super.decode(bytes, options); - } - - const parts: string[] = []; - let segmentStart = 0; - - for (let i = 0; i < bytes.length; i++) { - if (!isWtf8Surrogate(bytes, i)) { - continue; - } - - if (segmentStart < i) { - parts.push(super.decode(bytes.subarray(segmentStart, i), options)); - } - parts.push(String.fromCharCode(getSurrogateCodeUnit(bytes, i))); - i += 2; - segmentStart = i + 1; - } - - if (segmentStart === 0) { - return super.decode(bytes, options); - } - if (segmentStart < bytes.length) { - parts.push(super.decode(bytes.subarray(segmentStart), options)); - } - return parts.join(""); - } -} +import { Buffer } from "node:buffer"; + +const surrogateLeadByte = 0xED; +const surrogateSecondByteMin = 0xA0; +const surrogateSecondByteMax = 0xBF; +const continuationByteMin = 0x80; +const continuationByteMax = 0xBF; +type DecodeOptions = Parameters[1]; + +function isWtf8Surrogate(bytes: Uint8Array, index: number): boolean { + return index + 2 < bytes.length + && bytes[index] === surrogateLeadByte + && bytes[index + 1] >= surrogateSecondByteMin + && bytes[index + 1] <= surrogateSecondByteMax + && bytes[index + 2] >= continuationByteMin + && bytes[index + 2] <= continuationByteMax; +} + +function getSurrogateCodeUnit(bytes: Uint8Array, index: number): number { + return 0xD000 | ((bytes[index + 1] & 0x3F) << 6) | (bytes[index + 2] & 0x3F); +} + +function hasSurrogateLeadByte(bytes: Uint8Array): boolean { + return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).indexOf(surrogateLeadByte) >= 0; +} + +function toUint8Array(input: NodeJS.AllowSharedBufferSource): Uint8Array { + if (input instanceof Uint8Array) { + return input; + } + if (ArrayBuffer.isView(input)) { + return new Uint8Array(input.buffer, input.byteOffset, input.byteLength); + } + return new Uint8Array(input); +} + +export class Wtf8Decoder extends TextDecoder { + override decode(input?: NodeJS.AllowSharedBufferSource, options?: DecodeOptions): string { + if (input === undefined) { + return super.decode(input, options); + } + + const bytes = toUint8Array(input); + if (!hasSurrogateLeadByte(bytes)) { + return super.decode(bytes, options); + } + + const parts: string[] = []; + let segmentStart = 0; + + for (let i = 0; i < bytes.length; i++) { + if (!isWtf8Surrogate(bytes, i)) { + continue; + } + + if (segmentStart < i) { + parts.push(super.decode(bytes.subarray(segmentStart, i), options)); + } + parts.push(String.fromCharCode(getSurrogateCodeUnit(bytes, i))); + i += 2; + segmentStart = i + 1; + } + + if (segmentStart === 0) { + return super.decode(bytes, options); + } + if (segmentStart < bytes.length) { + parts.push(super.decode(bytes.subarray(segmentStart), options)); + } + return parts.join(""); + } +} diff --git a/packages/typescript/src/api/options.ts b/packages/typescript/src/api/options.ts index 759523393d0f2..ab58c3662f48f 100644 --- a/packages/typescript/src/api/options.ts +++ b/packages/typescript/src/api/options.ts @@ -1,55 +1,55 @@ -/** - * Shared utilities for the TypeScript API client. - */ - -import getExePath from "#getExePath"; -import type { FileSystem } from "./fs.ts"; - -export interface ClientSocketOptions { - /** Path to the Unix domain socket or Windows named pipe for API communication */ - pipe: string; -} - -export interface ClientSpawnOptions { - /** Path to the tsc executable. Defaults to the bundled tsc binary. */ - tsserverPath?: string; - /** Current working directory */ - cwd?: string; - /** Virtual filesystem callbacks */ - fs?: FileSystem; - /** Allow trusted projects to execute configured external content mapper processes. */ - runExternalCode?: boolean; - /** - * When true, collect timing information for each request. The client - * measures round-trip latency and bytes sent/received, and the server - * measures its own per-request processing time; both are combined (along - * with an estimated transport overhead) in the snapshot returned by - * {@link API.getTimingInfo}. - */ - collectTiming?: boolean; -} - -export type ClientOptions = ClientSocketOptions | ClientSpawnOptions; - -export function isSpawnOptions(options: ClientOptions): options is ClientSpawnOptions { - return !("pipe" in options); -} - -export function resolveExePath(options: ClientSpawnOptions): string { - return options.tsserverPath ?? getExePath(); -} - -export function getAPIProcessArgs(options: ClientSpawnOptions, async: boolean): string[] { - const args = ["--api"]; - if (async) args.push("--async"); - args.push("--cwd", options.cwd ?? process.cwd()); - if (options.runExternalCode) args.push("--runExternalCode"); - if (options.collectTiming) args.push("--timing"); - return args; -} - -export interface LSPConnectionOptions extends ClientSocketOptions { -} - -export interface APIOptions extends ClientSpawnOptions { -} +/** + * Shared utilities for the TypeScript API client. + */ + +import getExePath from "#getExePath"; +import type { FileSystem } from "./fs.ts"; + +export interface ClientSocketOptions { + /** Path to the Unix domain socket or Windows named pipe for API communication */ + pipe: string; +} + +export interface ClientSpawnOptions { + /** Path to the tsc executable. Defaults to the bundled tsc binary. */ + tsserverPath?: string; + /** Current working directory */ + cwd?: string; + /** Virtual filesystem callbacks */ + fs?: FileSystem; + /** Allow trusted projects to execute configured external content mapper processes. */ + runExternalCode?: boolean; + /** + * When true, collect timing information for each request. The client + * measures round-trip latency and bytes sent/received, and the server + * measures its own per-request processing time; both are combined (along + * with an estimated transport overhead) in the snapshot returned by + * {@link API.getTimingInfo}. + */ + collectTiming?: boolean; +} + +export type ClientOptions = ClientSocketOptions | ClientSpawnOptions; + +export function isSpawnOptions(options: ClientOptions): options is ClientSpawnOptions { + return !("pipe" in options); +} + +export function resolveExePath(options: ClientSpawnOptions): string { + return options.tsserverPath ?? getExePath(); +} + +export function getAPIProcessArgs(options: ClientSpawnOptions, async: boolean): string[] { + const args = ["--api"]; + if (async) args.push("--async"); + args.push("--cwd", options.cwd ?? process.cwd()); + if (options.runExternalCode) args.push("--runExternalCode"); + if (options.collectTiming) args.push("--timing"); + return args; +} + +export interface LSPConnectionOptions extends ClientSocketOptions { +} + +export interface APIOptions extends ClientSpawnOptions { +} diff --git a/packages/typescript/src/api/path.ts b/packages/typescript/src/api/path.ts index 2d30b2a4a49e5..0fc47774787d5 100644 --- a/packages/typescript/src/api/path.ts +++ b/packages/typescript/src/api/path.ts @@ -1,590 +1,590 @@ -const CharacterCodesSlash = "/".charCodeAt(0); -const CharacterCodesBackslash = "\\".charCodeAt(0); -const CharacterCodesColon = ":".charCodeAt(0); -const CharacterCodesPercent = "%".charCodeAt(0); -const CharacterCodes3 = "3".charCodeAt(0); -const CharacterCodesa = "a".charCodeAt(0); -const CharacterCodesz = "z".charCodeAt(0); -const CharacterCodesA = "A".charCodeAt(0); -const CharacterCodesZ = "Z".charCodeAt(0); -const CharacterCodesDot = ".".charCodeAt(0); -const directorySeparator = "/"; -const altDirectorySeparator = "\\"; -const urlSchemeSeparator = "://"; -const backslashRegExp = /\\/g; -// check path for these segments: '', '.'. '..' -const relativePathSegmentRegExp = /\/\/|(?:^|\/)\.\.?(?:$|\/)/; - -/** - * Determines whether a charCode corresponds to `/` or `\\`. - */ -function isAnyDirectorySeparator(charCode: number): boolean { - return charCode === CharacterCodesSlash || charCode === CharacterCodesBackslash; -} - -function isVolumeCharacter(charCode: number) { - return (charCode >= CharacterCodesa && charCode <= CharacterCodesz) || - (charCode >= CharacterCodesA && charCode <= CharacterCodesZ); -} - -function getFileUrlVolumeSeparatorEnd(url: string, start: number) { - const ch0 = url.charCodeAt(start); - if (ch0 === CharacterCodesColon) return start + 1; - if (ch0 === CharacterCodesPercent && url.charCodeAt(start + 1) === CharacterCodes3) { - const ch2 = url.charCodeAt(start + 2); - if (ch2 === CharacterCodesa || ch2 === CharacterCodesA) return start + 3; - } - return -1; -} - -/** - * Returns length of the root part of a path or URL (i.e. length of "/", "x:/", "//server/share/, file:///user/files"). - * - * For example: - * ```ts - * getRootLength("a") === 0 // "" - * getRootLength("/") === 1 // "/" - * getRootLength("c:") === 2 // "c:" - * getRootLength("c:d") === 0 // "" - * getRootLength("c:/") === 3 // "c:/" - * getRootLength("c:\\") === 3 // "c:\\" - * getRootLength("//server") === 7 // "//server" - * getRootLength("//server/share") === 8 // "//server/" - * getRootLength("\\\\server") === 7 // "\\\\server" - * getRootLength("\\\\server\\share") === 8 // "\\\\server\\" - * getRootLength("file:///path") === 8 // "file:///" - * getRootLength("file:///c:") === 10 // "file:///c:" - * getRootLength("file:///c:d") === 8 // "file:///" - * getRootLength("file:///c:/path") === 11 // "file:///c:/" - * getRootLength("file://server") === 13 // "file://server" - * getRootLength("file://server/path") === 14 // "file://server/" - * getRootLength("http://server") === 13 // "http://server" - * getRootLength("http://server/path") === 14 // "http://server/" - * ``` - * - * @internal - */ -export function getRootLength(path: string): number { - const rootLength = getEncodedRootLength(path); - return rootLength < 0 ? ~rootLength : rootLength; -} - -/** - * Returns length of the root part of a path or URL (i.e. length of "/", "x:/", "//server/share/, file:///user/files"). - * If the root is part of a URL, the twos-complement of the root length is returned. - */ -function getEncodedRootLength(path: string): number { - if (!path) return 0; - const ch0 = path.charCodeAt(0); - - // POSIX or UNC - if (ch0 === CharacterCodesSlash || ch0 === CharacterCodesBackslash) { - if (path.charCodeAt(1) !== ch0) return 1; // POSIX: "/" (or non-normalized "\") - - const p1 = path.indexOf(ch0 === CharacterCodesSlash ? directorySeparator : altDirectorySeparator, 2); - if (p1 < 0) return path.length; // UNC: "//server" or "\\server" - - return p1 + 1; // UNC: "//server/" or "\\server\" - } - - // DOS - if (isVolumeCharacter(ch0) && path.charCodeAt(1) === CharacterCodesColon) { - const ch2 = path.charCodeAt(2); - if (ch2 === CharacterCodesSlash || ch2 === CharacterCodesBackslash) return 3; // DOS: "c:/" or "c:\" - if (path.length === 2) return 2; // DOS: "c:" (but not "c:d") - } - - // URL - const schemeEnd = path.indexOf(urlSchemeSeparator); - if (schemeEnd !== -1) { - const authorityStart = schemeEnd + urlSchemeSeparator.length; - const authorityEnd = path.indexOf(directorySeparator, authorityStart); - if (authorityEnd !== -1) { // URL: "file:///", "file://server/", "file://server/path" - // For local "file" URLs, include the leading DOS volume (if present). - // Per https://www.ietf.org/rfc/rfc1738.txt, a host of "" or "localhost" is a - // special case interpreted as "the machine from which the URL is being interpreted". - const scheme = path.slice(0, schemeEnd); - const authority = path.slice(authorityStart, authorityEnd); - if ( - scheme === "file" && (authority === "" || authority === "localhost") && - isVolumeCharacter(path.charCodeAt(authorityEnd + 1)) - ) { - const volumeSeparatorEnd = getFileUrlVolumeSeparatorEnd(path, authorityEnd + 2); - if (volumeSeparatorEnd !== -1) { - if (path.charCodeAt(volumeSeparatorEnd) === CharacterCodesSlash) { - // URL: "file:///c:/", "file://localhost/c:/", "file:///c%3a/", "file://localhost/c%3a/" - return ~(volumeSeparatorEnd + 1); - } - if (volumeSeparatorEnd === path.length) { - // URL: "file:///c:", "file://localhost/c:", "file:///c$3a", "file://localhost/c%3a" - // but not "file:///c:d" or "file:///c%3ad" - return ~volumeSeparatorEnd; - } - } - } - return ~(authorityEnd + 1); // URL: "file://server/", "http://server/" - } - return ~path.length; // URL: "file://server", "http://server" - } - - // relative - return 0; -} - -export function getPathComponents(path: string): string[] { - return pathComponents(path, getRootLength(path)); -} - -function pathComponents(path: string, rootLength: number) { - const root = path.substring(0, rootLength); - const rest = path.substring(rootLength).split("/"); - if (rest.length && !lastOrUndefined(rest)) rest.pop(); - return [root, ...rest]; -} - -function lastOrUndefined(array: T[]): T | undefined { - return array.length ? array[array.length - 1] : undefined; -} - -/** - * Determines whether a path has a trailing separator (`/` or `\\`). - */ -export function hasTrailingDirectorySeparator(path: string): boolean { - return path.length > 0 && isAnyDirectorySeparator(path.charCodeAt(path.length - 1)); -} - -/** - * Removes a trailing directory separator from a path, if it does not already have one. - */ -export function removeTrailingDirectorySeparator(path: string): string { - if (hasTrailingDirectorySeparator(path)) { - return path.substr(0, path.length - 1); - } - return path; -} - -/** - * Adds a trailing directory separator to a path, if it does not already have one. - */ -export function ensureTrailingDirectorySeparator(path: string): string { - if (!hasTrailingDirectorySeparator(path)) { - return path + directorySeparator; - } - return path; -} - -/** - * Normalize path separators, converting `\\` into `/`. - */ -export function normalizeSlashes(path: string): string { - return path.includes("\\") - ? path.replace(backslashRegExp, directorySeparator) - : path; -} - -/** - * Combines paths. If a path is absolute, it replaces any previous path. Relative paths are not simplified. - */ -export function combinePaths(path: string, ...paths: (string | undefined)[]): string { - if (path) path = normalizeSlashes(path); - for (let relativePath of paths) { - if (!relativePath) continue; - relativePath = normalizeSlashes(relativePath); - if (!path || getRootLength(relativePath) !== 0) { - path = relativePath; - } - else { - path = ensureTrailingDirectorySeparator(path) + relativePath; - } - } - return path; -} - -function simpleNormalizePath(path: string): string | undefined { - // Most paths don't require normalization - if (!relativePathSegmentRegExp.test(path)) { - return path; - } - // Some paths only require cleanup of `/./` or leading `./` - let simplified = path.replace(/\/\.\//g, "/"); - if (simplified.startsWith("./")) { - simplified = simplified.slice(2); - } - if (simplified !== path) { - path = simplified; - if (!relativePathSegmentRegExp.test(path)) { - return path; - } - } - return undefined; -} - -/** - * Returns the normalized absolute path, resolving `.` and `..` segments. - */ -export function getNormalizedAbsolutePath(path: string, currentDirectory: string | undefined): string { - let rootLength = getRootLength(path); - if (rootLength === 0 && currentDirectory) { - path = combinePaths(currentDirectory, path); - rootLength = getRootLength(path); - } - else { - // combinePaths normalizes slashes, so not necessary in the other branch - path = normalizeSlashes(path); - } - - const simpleNormalized = simpleNormalizePath(path); - if (simpleNormalized !== undefined) { - return simpleNormalized.length > rootLength ? removeTrailingDirectorySeparator(simpleNormalized) : simpleNormalized; - } - - const length = path.length; - const root = path.substring(0, rootLength); - // `normalized` is only initialized once `path` is determined to be non-normalized - let normalized: string | undefined; - let index = rootLength; - let segmentStart = index; - let normalizedUpTo = index; - let seenNonDotDotSegment = rootLength !== 0; - while (index < length) { - // At beginning of segment - segmentStart = index; - let ch = path.charCodeAt(index); - while (ch === CharacterCodesSlash && index + 1 < length) { - index++; - ch = path.charCodeAt(index); - } - if (index > segmentStart) { - // Seen superfluous separator - normalized ??= path.substring(0, segmentStart - 1); - segmentStart = index; - } - // Past any superfluous separators - let segmentEnd = path.indexOf(directorySeparator, index + 1); - if (segmentEnd === -1) { - segmentEnd = length; - } - const segmentLength = segmentEnd - segmentStart; - if (segmentLength === 1 && path.charCodeAt(index) === CharacterCodesDot) { - // "." segment (skip) - normalized ??= path.substring(0, normalizedUpTo); - } - else if (segmentLength === 2 && path.charCodeAt(index) === CharacterCodesDot && path.charCodeAt(index + 1) === CharacterCodesDot) { - // ".." segment - if (!seenNonDotDotSegment) { - if (normalized !== undefined) { - normalized += normalized.length === rootLength ? ".." : "/.."; - } - else { - normalizedUpTo = index + 2; - } - } - else if (normalized === undefined) { - if (normalizedUpTo - 2 >= 0) { - normalized = path.substring(0, Math.max(rootLength, path.lastIndexOf(directorySeparator, normalizedUpTo - 2))); - } - else { - normalized = path.substring(0, normalizedUpTo); - } - } - else { - const lastSlash = normalized.lastIndexOf(directorySeparator); - if (lastSlash !== -1) { - normalized = normalized.substring(0, Math.max(rootLength, lastSlash)); - } - else { - normalized = root; - } - if (normalized.length === rootLength) { - seenNonDotDotSegment = rootLength !== 0; - } - } - } - else if (normalized !== undefined) { - if (normalized.length !== rootLength) { - normalized += directorySeparator; - } - seenNonDotDotSegment = true; - normalized += path.substring(segmentStart, segmentEnd); - } - else { - seenNonDotDotSegment = true; - normalizedUpTo = segmentEnd; - } - index = segmentEnd + 1; - } - return normalized ?? (length > rootLength ? removeTrailingDirectorySeparator(path) : path); -} - -/** - * Normalizes a path, resolving `.` and `..` segments and converting backslashes to forward slashes. - */ -export function normalizePath(path: string): string { - path = normalizeSlashes(path); - let normalized = simpleNormalizePath(path); - if (normalized !== undefined) { - return normalized; - } - normalized = getNormalizedAbsolutePath(path, ""); - return normalized && hasTrailingDirectorySeparator(path) ? ensureTrailingDirectorySeparator(normalized) : normalized; -} - -/** - * Determines whether a path is an absolute disk path (e.g. starts with `/`, or a DOS path - * like `c:`, `c:\\` or `c:/`). - */ -export function isRootedDiskPath(path: string): boolean { - return getEncodedRootLength(path) > 0; -} - -export function convertToRelativePath( - absoluteOrRelativePath: string, - basePath: string, - getCanonicalFileName: (path: string) => string, -): string { - if (!isRootedDiskPath(absoluteOrRelativePath)) { - return absoluteOrRelativePath; - } - - const fromComponents = getPathComponents(getNormalizedAbsolutePath(basePath, "")); - const toComponents = getPathComponents(getNormalizedAbsolutePath(absoluteOrRelativePath, "")); - let start = 0; - for (; start < fromComponents.length && start < toComponents.length; start++) { - const fromComponent = getCanonicalFileName(fromComponents[start]); - const toComponent = getCanonicalFileName(toComponents[start]); - const equal = start === 0 - ? fromComponent.toLowerCase() === toComponent.toLowerCase() - : fromComponent === toComponent; - if (!equal) { - break; - } - } - - if (start === 0) { - return pathFromComponents(toComponents); - } - - const relative = Array(fromComponents.length - start).fill(".."); - return pathFromComponents(["", ...relative, ...toComponents.slice(start)]); -} - -function pathFromComponents(components: readonly string[]): string { - if (components.length === 0) { - return ""; - } - const root = components[0] && ensureTrailingDirectorySeparator(components[0]); - return root + components.slice(1).join(directorySeparator); -} - -/** - * Converts a file name to a normalized path. - * - * @param fileName The file name to convert - * @param basePath The base path to use for relative file names - * @param getCanonicalFileName A function to get the canonical file name (e.g., toLowerCase for case-insensitive systems) - * @returns The normalized path - */ -export function toPath(fileName: string, basePath: string | undefined, getCanonicalFileName: (path: string) => string): string { - const nonCanonicalizedPath = isRootedDiskPath(fileName) - ? normalizePath(fileName) - : getNormalizedAbsolutePath(fileName, basePath); - return getCanonicalFileName(nonCanonicalizedPath); -} - -/** - * Creates a getCanonicalFileName function based on case sensitivity. - */ -export function createGetCanonicalFileName(useCaseSensitiveFileNames: boolean): (fileName: string) => string { - return useCaseSensitiveFileNames ? identity : toLowerCase; -} - -function identity(x: T): T { - return x; -} - -function toLowerCase(s: string): string { - return s.toLowerCase(); -} - -const bundledScheme = "bundled:///"; - -/** - * Returns true if the path refers to a bundled library file. - */ -export function isBundled(path: string): boolean { - return path.startsWith(bundledScheme); -} - -/** - * Returns true if the file name represents a dynamic/virtual file - * that doesn't exist on disk (e.g., untitled files with paths like "^/untitled/..."). - */ -export function isDynamicFileName(fileName: string): boolean { - return fileName.startsWith("^/"); -} - -/** - * Splits a Windows volume (e.g., "c:") from the rest of the path. - * Returns [volume, rest, ok] where ok is true if a volume was found. - */ -export function splitVolumePath(path: string): [volume: string, rest: string, ok: boolean] { - if (path.length >= 2 && isVolumeCharacter(path.charCodeAt(0)) && path.charCodeAt(1) === CharacterCodesColon) { - return [path.substring(0, 2).toLowerCase(), path.substring(2), true]; - } - return ["", path, false]; -} - -// Characters that need extra escaping in URI path segments -// https://github.com/microsoft/vscode-uri/blob/edfdccd976efaf4bb8fdeca87e97c47257721729/src/uri.ts#L455 -const extraEscapeChars: Record = { - ":": "%3A", - "/": "%2F", - "?": "%3F", - "#": "%23", - "[": "%5B", - "]": "%5D", - "@": "%40", - "!": "%21", - "$": "%24", - "&": "%26", - "'": "%27", - "(": "%28", - ")": "%29", - "*": "%2A", - "+": "%2B", - ",": "%2C", - ";": "%3B", - "=": "%3D", - " ": "%20", -}; - -function extraEscape(s: string): string { - let result = s; - for (const [char, escape] of Object.entries(extraEscapeChars)) { - result = result.replaceAll(char, escape); - } - return result; -} - -/** - * Converts a file name to a document URI. - * - * @example - * fileNameToDocumentURI("/path/to/file.ts") === "file:///path/to/file.ts" - * fileNameToDocumentURI("c:/path/to/file.ts") === "file:///c%3A/path/to/file.ts" - * fileNameToDocumentURI("^/untitled/ts-nul-authority/Untitled-1") === "untitled:Untitled-1" - * fileNameToDocumentURI("^/vscode-vfs/github/microsoft/TypeScript/file.ts") === "vscode-vfs://github/microsoft/TypeScript/file.ts" - */ -export function fileNameToDocumentURI(fileName: string): string { - // Bundled files are returned as-is - if (isBundled(fileName)) { - return fileName; - } - - // Dynamic/virtual files (untitled, vscode-vfs, etc.) need special handling - if (isDynamicFileName(fileName)) { - // Format: ^/scheme/authority/path - const withoutPrefix = fileName.substring(2); // Remove "^/" - const firstSlash = withoutPrefix.indexOf("/"); - if (firstSlash === -1) { - throw new Error("invalid file name: " + fileName); - } - const scheme = withoutPrefix.substring(0, firstSlash); - const rest = withoutPrefix.substring(firstSlash + 1); - - const secondSlash = rest.indexOf("/"); - if (secondSlash === -1) { - throw new Error("invalid file name: " + fileName); - } - const authority = rest.substring(0, secondSlash); - const path = rest.substring(secondSlash + 1); - - // ts-nul-authority is a placeholder for URIs without an authority - if (authority === "ts-nul-authority") { - return scheme + ":" + path; - } - return scheme + "://" + authority + "/" + path; - } - - // Regular file path - convert to file:// URI - let [volume, rest] = splitVolumePath(fileName); - if (volume !== "") { - volume = "/" + extraEscape(volume); - } - - // Remove leading // for UNC paths (already handled by file://) - if (rest.startsWith("//")) { - rest = rest.substring(2); - } - - const parts = rest.split("/"); - const encodedParts = parts.map(part => extraEscape(encodeURIComponent(part))); - - return "file://" + volume + encodedParts.join("/"); -} - -/** - * Converts a document URI to a file name. - * - * @example - * documentURIToFileName("file:///path/to/file.ts") === "/path/to/file.ts" - * documentURIToFileName("file:///c%3A/path/to/file.ts") === "c:/path/to/file.ts" - * documentURIToFileName("untitled:Untitled-1") === "^/untitled/ts-nul-authority/Untitled-1" - * documentURIToFileName("vscode-vfs://github/microsoft/TypeScript/file.ts") === "^/vscode-vfs/github/microsoft/TypeScript/file.ts" - */ -export function documentURIToFileName(uri: string): string { - // Bundled files are returned as-is - if (isBundled(uri)) { - return uri; - } - - // Handle file:// URIs - if (uri.startsWith("file://")) { - let parsed: URL; - try { - parsed = new URL(uri); - } - catch { - throw new Error("invalid file URI: " + uri); - } - - // UNC path: file://server/share/... - if (parsed.host !== "") { - return "//" + parsed.host + parsed.pathname; - } - - // Local file - fix Windows path by removing leading slash before volume - const path = decodeURIComponent(parsed.pathname); - if (path.length >= 3 && path.charCodeAt(0) === CharacterCodesSlash) { - const [volume, rest, ok] = splitVolumePath(path.substring(1)); - if (ok) { - return volume + rest; - } - } - return path; - } - - // Leave all other URIs escaped so we can round-trip them. - // Convert to dynamic file name format: ^/scheme/authority/path - - const colonIndex = uri.indexOf(":"); - if (colonIndex === -1) { - throw new Error("invalid URI: " + uri); - } - - const scheme = uri.substring(0, colonIndex); - let path = uri.substring(colonIndex + 1); - - let authority = "ts-nul-authority"; - if (path.startsWith("//")) { - const rest = path.substring(2); - const slashIndex = rest.indexOf("/"); - if (slashIndex === -1) { - throw new Error("invalid URI: " + uri); - } - authority = rest.substring(0, slashIndex); - path = rest.substring(slashIndex + 1); - } - - return "^/" + scheme + "/" + authority + "/" + path; -} +const CharacterCodesSlash = "/".charCodeAt(0); +const CharacterCodesBackslash = "\\".charCodeAt(0); +const CharacterCodesColon = ":".charCodeAt(0); +const CharacterCodesPercent = "%".charCodeAt(0); +const CharacterCodes3 = "3".charCodeAt(0); +const CharacterCodesa = "a".charCodeAt(0); +const CharacterCodesz = "z".charCodeAt(0); +const CharacterCodesA = "A".charCodeAt(0); +const CharacterCodesZ = "Z".charCodeAt(0); +const CharacterCodesDot = ".".charCodeAt(0); +const directorySeparator = "/"; +const altDirectorySeparator = "\\"; +const urlSchemeSeparator = "://"; +const backslashRegExp = /\\/g; +// check path for these segments: '', '.'. '..' +const relativePathSegmentRegExp = /\/\/|(?:^|\/)\.\.?(?:$|\/)/; + +/** + * Determines whether a charCode corresponds to `/` or `\\`. + */ +function isAnyDirectorySeparator(charCode: number): boolean { + return charCode === CharacterCodesSlash || charCode === CharacterCodesBackslash; +} + +function isVolumeCharacter(charCode: number) { + return (charCode >= CharacterCodesa && charCode <= CharacterCodesz) || + (charCode >= CharacterCodesA && charCode <= CharacterCodesZ); +} + +function getFileUrlVolumeSeparatorEnd(url: string, start: number) { + const ch0 = url.charCodeAt(start); + if (ch0 === CharacterCodesColon) return start + 1; + if (ch0 === CharacterCodesPercent && url.charCodeAt(start + 1) === CharacterCodes3) { + const ch2 = url.charCodeAt(start + 2); + if (ch2 === CharacterCodesa || ch2 === CharacterCodesA) return start + 3; + } + return -1; +} + +/** + * Returns length of the root part of a path or URL (i.e. length of "/", "x:/", "//server/share/, file:///user/files"). + * + * For example: + * ```ts + * getRootLength("a") === 0 // "" + * getRootLength("/") === 1 // "/" + * getRootLength("c:") === 2 // "c:" + * getRootLength("c:d") === 0 // "" + * getRootLength("c:/") === 3 // "c:/" + * getRootLength("c:\\") === 3 // "c:\\" + * getRootLength("//server") === 7 // "//server" + * getRootLength("//server/share") === 8 // "//server/" + * getRootLength("\\\\server") === 7 // "\\\\server" + * getRootLength("\\\\server\\share") === 8 // "\\\\server\\" + * getRootLength("file:///path") === 8 // "file:///" + * getRootLength("file:///c:") === 10 // "file:///c:" + * getRootLength("file:///c:d") === 8 // "file:///" + * getRootLength("file:///c:/path") === 11 // "file:///c:/" + * getRootLength("file://server") === 13 // "file://server" + * getRootLength("file://server/path") === 14 // "file://server/" + * getRootLength("http://server") === 13 // "http://server" + * getRootLength("http://server/path") === 14 // "http://server/" + * ``` + * + * @internal + */ +export function getRootLength(path: string): number { + const rootLength = getEncodedRootLength(path); + return rootLength < 0 ? ~rootLength : rootLength; +} + +/** + * Returns length of the root part of a path or URL (i.e. length of "/", "x:/", "//server/share/, file:///user/files"). + * If the root is part of a URL, the twos-complement of the root length is returned. + */ +function getEncodedRootLength(path: string): number { + if (!path) return 0; + const ch0 = path.charCodeAt(0); + + // POSIX or UNC + if (ch0 === CharacterCodesSlash || ch0 === CharacterCodesBackslash) { + if (path.charCodeAt(1) !== ch0) return 1; // POSIX: "/" (or non-normalized "\") + + const p1 = path.indexOf(ch0 === CharacterCodesSlash ? directorySeparator : altDirectorySeparator, 2); + if (p1 < 0) return path.length; // UNC: "//server" or "\\server" + + return p1 + 1; // UNC: "//server/" or "\\server\" + } + + // DOS + if (isVolumeCharacter(ch0) && path.charCodeAt(1) === CharacterCodesColon) { + const ch2 = path.charCodeAt(2); + if (ch2 === CharacterCodesSlash || ch2 === CharacterCodesBackslash) return 3; // DOS: "c:/" or "c:\" + if (path.length === 2) return 2; // DOS: "c:" (but not "c:d") + } + + // URL + const schemeEnd = path.indexOf(urlSchemeSeparator); + if (schemeEnd !== -1) { + const authorityStart = schemeEnd + urlSchemeSeparator.length; + const authorityEnd = path.indexOf(directorySeparator, authorityStart); + if (authorityEnd !== -1) { // URL: "file:///", "file://server/", "file://server/path" + // For local "file" URLs, include the leading DOS volume (if present). + // Per https://www.ietf.org/rfc/rfc1738.txt, a host of "" or "localhost" is a + // special case interpreted as "the machine from which the URL is being interpreted". + const scheme = path.slice(0, schemeEnd); + const authority = path.slice(authorityStart, authorityEnd); + if ( + scheme === "file" && (authority === "" || authority === "localhost") && + isVolumeCharacter(path.charCodeAt(authorityEnd + 1)) + ) { + const volumeSeparatorEnd = getFileUrlVolumeSeparatorEnd(path, authorityEnd + 2); + if (volumeSeparatorEnd !== -1) { + if (path.charCodeAt(volumeSeparatorEnd) === CharacterCodesSlash) { + // URL: "file:///c:/", "file://localhost/c:/", "file:///c%3a/", "file://localhost/c%3a/" + return ~(volumeSeparatorEnd + 1); + } + if (volumeSeparatorEnd === path.length) { + // URL: "file:///c:", "file://localhost/c:", "file:///c$3a", "file://localhost/c%3a" + // but not "file:///c:d" or "file:///c%3ad" + return ~volumeSeparatorEnd; + } + } + } + return ~(authorityEnd + 1); // URL: "file://server/", "http://server/" + } + return ~path.length; // URL: "file://server", "http://server" + } + + // relative + return 0; +} + +export function getPathComponents(path: string): string[] { + return pathComponents(path, getRootLength(path)); +} + +function pathComponents(path: string, rootLength: number) { + const root = path.substring(0, rootLength); + const rest = path.substring(rootLength).split("/"); + if (rest.length && !lastOrUndefined(rest)) rest.pop(); + return [root, ...rest]; +} + +function lastOrUndefined(array: T[]): T | undefined { + return array.length ? array[array.length - 1] : undefined; +} + +/** + * Determines whether a path has a trailing separator (`/` or `\\`). + */ +export function hasTrailingDirectorySeparator(path: string): boolean { + return path.length > 0 && isAnyDirectorySeparator(path.charCodeAt(path.length - 1)); +} + +/** + * Removes a trailing directory separator from a path, if it does not already have one. + */ +export function removeTrailingDirectorySeparator(path: string): string { + if (hasTrailingDirectorySeparator(path)) { + return path.substr(0, path.length - 1); + } + return path; +} + +/** + * Adds a trailing directory separator to a path, if it does not already have one. + */ +export function ensureTrailingDirectorySeparator(path: string): string { + if (!hasTrailingDirectorySeparator(path)) { + return path + directorySeparator; + } + return path; +} + +/** + * Normalize path separators, converting `\\` into `/`. + */ +export function normalizeSlashes(path: string): string { + return path.includes("\\") + ? path.replace(backslashRegExp, directorySeparator) + : path; +} + +/** + * Combines paths. If a path is absolute, it replaces any previous path. Relative paths are not simplified. + */ +export function combinePaths(path: string, ...paths: (string | undefined)[]): string { + if (path) path = normalizeSlashes(path); + for (let relativePath of paths) { + if (!relativePath) continue; + relativePath = normalizeSlashes(relativePath); + if (!path || getRootLength(relativePath) !== 0) { + path = relativePath; + } + else { + path = ensureTrailingDirectorySeparator(path) + relativePath; + } + } + return path; +} + +function simpleNormalizePath(path: string): string | undefined { + // Most paths don't require normalization + if (!relativePathSegmentRegExp.test(path)) { + return path; + } + // Some paths only require cleanup of `/./` or leading `./` + let simplified = path.replace(/\/\.\//g, "/"); + if (simplified.startsWith("./")) { + simplified = simplified.slice(2); + } + if (simplified !== path) { + path = simplified; + if (!relativePathSegmentRegExp.test(path)) { + return path; + } + } + return undefined; +} + +/** + * Returns the normalized absolute path, resolving `.` and `..` segments. + */ +export function getNormalizedAbsolutePath(path: string, currentDirectory: string | undefined): string { + let rootLength = getRootLength(path); + if (rootLength === 0 && currentDirectory) { + path = combinePaths(currentDirectory, path); + rootLength = getRootLength(path); + } + else { + // combinePaths normalizes slashes, so not necessary in the other branch + path = normalizeSlashes(path); + } + + const simpleNormalized = simpleNormalizePath(path); + if (simpleNormalized !== undefined) { + return simpleNormalized.length > rootLength ? removeTrailingDirectorySeparator(simpleNormalized) : simpleNormalized; + } + + const length = path.length; + const root = path.substring(0, rootLength); + // `normalized` is only initialized once `path` is determined to be non-normalized + let normalized: string | undefined; + let index = rootLength; + let segmentStart = index; + let normalizedUpTo = index; + let seenNonDotDotSegment = rootLength !== 0; + while (index < length) { + // At beginning of segment + segmentStart = index; + let ch = path.charCodeAt(index); + while (ch === CharacterCodesSlash && index + 1 < length) { + index++; + ch = path.charCodeAt(index); + } + if (index > segmentStart) { + // Seen superfluous separator + normalized ??= path.substring(0, segmentStart - 1); + segmentStart = index; + } + // Past any superfluous separators + let segmentEnd = path.indexOf(directorySeparator, index + 1); + if (segmentEnd === -1) { + segmentEnd = length; + } + const segmentLength = segmentEnd - segmentStart; + if (segmentLength === 1 && path.charCodeAt(index) === CharacterCodesDot) { + // "." segment (skip) + normalized ??= path.substring(0, normalizedUpTo); + } + else if (segmentLength === 2 && path.charCodeAt(index) === CharacterCodesDot && path.charCodeAt(index + 1) === CharacterCodesDot) { + // ".." segment + if (!seenNonDotDotSegment) { + if (normalized !== undefined) { + normalized += normalized.length === rootLength ? ".." : "/.."; + } + else { + normalizedUpTo = index + 2; + } + } + else if (normalized === undefined) { + if (normalizedUpTo - 2 >= 0) { + normalized = path.substring(0, Math.max(rootLength, path.lastIndexOf(directorySeparator, normalizedUpTo - 2))); + } + else { + normalized = path.substring(0, normalizedUpTo); + } + } + else { + const lastSlash = normalized.lastIndexOf(directorySeparator); + if (lastSlash !== -1) { + normalized = normalized.substring(0, Math.max(rootLength, lastSlash)); + } + else { + normalized = root; + } + if (normalized.length === rootLength) { + seenNonDotDotSegment = rootLength !== 0; + } + } + } + else if (normalized !== undefined) { + if (normalized.length !== rootLength) { + normalized += directorySeparator; + } + seenNonDotDotSegment = true; + normalized += path.substring(segmentStart, segmentEnd); + } + else { + seenNonDotDotSegment = true; + normalizedUpTo = segmentEnd; + } + index = segmentEnd + 1; + } + return normalized ?? (length > rootLength ? removeTrailingDirectorySeparator(path) : path); +} + +/** + * Normalizes a path, resolving `.` and `..` segments and converting backslashes to forward slashes. + */ +export function normalizePath(path: string): string { + path = normalizeSlashes(path); + let normalized = simpleNormalizePath(path); + if (normalized !== undefined) { + return normalized; + } + normalized = getNormalizedAbsolutePath(path, ""); + return normalized && hasTrailingDirectorySeparator(path) ? ensureTrailingDirectorySeparator(normalized) : normalized; +} + +/** + * Determines whether a path is an absolute disk path (e.g. starts with `/`, or a DOS path + * like `c:`, `c:\\` or `c:/`). + */ +export function isRootedDiskPath(path: string): boolean { + return getEncodedRootLength(path) > 0; +} + +export function convertToRelativePath( + absoluteOrRelativePath: string, + basePath: string, + getCanonicalFileName: (path: string) => string, +): string { + if (!isRootedDiskPath(absoluteOrRelativePath)) { + return absoluteOrRelativePath; + } + + const fromComponents = getPathComponents(getNormalizedAbsolutePath(basePath, "")); + const toComponents = getPathComponents(getNormalizedAbsolutePath(absoluteOrRelativePath, "")); + let start = 0; + for (; start < fromComponents.length && start < toComponents.length; start++) { + const fromComponent = getCanonicalFileName(fromComponents[start]); + const toComponent = getCanonicalFileName(toComponents[start]); + const equal = start === 0 + ? fromComponent.toLowerCase() === toComponent.toLowerCase() + : fromComponent === toComponent; + if (!equal) { + break; + } + } + + if (start === 0) { + return pathFromComponents(toComponents); + } + + const relative = Array(fromComponents.length - start).fill(".."); + return pathFromComponents(["", ...relative, ...toComponents.slice(start)]); +} + +function pathFromComponents(components: readonly string[]): string { + if (components.length === 0) { + return ""; + } + const root = components[0] && ensureTrailingDirectorySeparator(components[0]); + return root + components.slice(1).join(directorySeparator); +} + +/** + * Converts a file name to a normalized path. + * + * @param fileName The file name to convert + * @param basePath The base path to use for relative file names + * @param getCanonicalFileName A function to get the canonical file name (e.g., toLowerCase for case-insensitive systems) + * @returns The normalized path + */ +export function toPath(fileName: string, basePath: string | undefined, getCanonicalFileName: (path: string) => string): string { + const nonCanonicalizedPath = isRootedDiskPath(fileName) + ? normalizePath(fileName) + : getNormalizedAbsolutePath(fileName, basePath); + return getCanonicalFileName(nonCanonicalizedPath); +} + +/** + * Creates a getCanonicalFileName function based on case sensitivity. + */ +export function createGetCanonicalFileName(useCaseSensitiveFileNames: boolean): (fileName: string) => string { + return useCaseSensitiveFileNames ? identity : toLowerCase; +} + +function identity(x: T): T { + return x; +} + +function toLowerCase(s: string): string { + return s.toLowerCase(); +} + +const bundledScheme = "bundled:///"; + +/** + * Returns true if the path refers to a bundled library file. + */ +export function isBundled(path: string): boolean { + return path.startsWith(bundledScheme); +} + +/** + * Returns true if the file name represents a dynamic/virtual file + * that doesn't exist on disk (e.g., untitled files with paths like "^/untitled/..."). + */ +export function isDynamicFileName(fileName: string): boolean { + return fileName.startsWith("^/"); +} + +/** + * Splits a Windows volume (e.g., "c:") from the rest of the path. + * Returns [volume, rest, ok] where ok is true if a volume was found. + */ +export function splitVolumePath(path: string): [volume: string, rest: string, ok: boolean] { + if (path.length >= 2 && isVolumeCharacter(path.charCodeAt(0)) && path.charCodeAt(1) === CharacterCodesColon) { + return [path.substring(0, 2).toLowerCase(), path.substring(2), true]; + } + return ["", path, false]; +} + +// Characters that need extra escaping in URI path segments +// https://github.com/microsoft/vscode-uri/blob/edfdccd976efaf4bb8fdeca87e97c47257721729/src/uri.ts#L455 +const extraEscapeChars: Record = { + ":": "%3A", + "/": "%2F", + "?": "%3F", + "#": "%23", + "[": "%5B", + "]": "%5D", + "@": "%40", + "!": "%21", + "$": "%24", + "&": "%26", + "'": "%27", + "(": "%28", + ")": "%29", + "*": "%2A", + "+": "%2B", + ",": "%2C", + ";": "%3B", + "=": "%3D", + " ": "%20", +}; + +function extraEscape(s: string): string { + let result = s; + for (const [char, escape] of Object.entries(extraEscapeChars)) { + result = result.replaceAll(char, escape); + } + return result; +} + +/** + * Converts a file name to a document URI. + * + * @example + * fileNameToDocumentURI("/path/to/file.ts") === "file:///path/to/file.ts" + * fileNameToDocumentURI("c:/path/to/file.ts") === "file:///c%3A/path/to/file.ts" + * fileNameToDocumentURI("^/untitled/ts-nul-authority/Untitled-1") === "untitled:Untitled-1" + * fileNameToDocumentURI("^/vscode-vfs/github/microsoft/TypeScript/file.ts") === "vscode-vfs://github/microsoft/TypeScript/file.ts" + */ +export function fileNameToDocumentURI(fileName: string): string { + // Bundled files are returned as-is + if (isBundled(fileName)) { + return fileName; + } + + // Dynamic/virtual files (untitled, vscode-vfs, etc.) need special handling + if (isDynamicFileName(fileName)) { + // Format: ^/scheme/authority/path + const withoutPrefix = fileName.substring(2); // Remove "^/" + const firstSlash = withoutPrefix.indexOf("/"); + if (firstSlash === -1) { + throw new Error("invalid file name: " + fileName); + } + const scheme = withoutPrefix.substring(0, firstSlash); + const rest = withoutPrefix.substring(firstSlash + 1); + + const secondSlash = rest.indexOf("/"); + if (secondSlash === -1) { + throw new Error("invalid file name: " + fileName); + } + const authority = rest.substring(0, secondSlash); + const path = rest.substring(secondSlash + 1); + + // ts-nul-authority is a placeholder for URIs without an authority + if (authority === "ts-nul-authority") { + return scheme + ":" + path; + } + return scheme + "://" + authority + "/" + path; + } + + // Regular file path - convert to file:// URI + let [volume, rest] = splitVolumePath(fileName); + if (volume !== "") { + volume = "/" + extraEscape(volume); + } + + // Remove leading // for UNC paths (already handled by file://) + if (rest.startsWith("//")) { + rest = rest.substring(2); + } + + const parts = rest.split("/"); + const encodedParts = parts.map(part => extraEscape(encodeURIComponent(part))); + + return "file://" + volume + encodedParts.join("/"); +} + +/** + * Converts a document URI to a file name. + * + * @example + * documentURIToFileName("file:///path/to/file.ts") === "/path/to/file.ts" + * documentURIToFileName("file:///c%3A/path/to/file.ts") === "c:/path/to/file.ts" + * documentURIToFileName("untitled:Untitled-1") === "^/untitled/ts-nul-authority/Untitled-1" + * documentURIToFileName("vscode-vfs://github/microsoft/TypeScript/file.ts") === "^/vscode-vfs/github/microsoft/TypeScript/file.ts" + */ +export function documentURIToFileName(uri: string): string { + // Bundled files are returned as-is + if (isBundled(uri)) { + return uri; + } + + // Handle file:// URIs + if (uri.startsWith("file://")) { + let parsed: URL; + try { + parsed = new URL(uri); + } + catch { + throw new Error("invalid file URI: " + uri); + } + + // UNC path: file://server/share/... + if (parsed.host !== "") { + return "//" + parsed.host + parsed.pathname; + } + + // Local file - fix Windows path by removing leading slash before volume + const path = decodeURIComponent(parsed.pathname); + if (path.length >= 3 && path.charCodeAt(0) === CharacterCodesSlash) { + const [volume, rest, ok] = splitVolumePath(path.substring(1)); + if (ok) { + return volume + rest; + } + } + return path; + } + + // Leave all other URIs escaped so we can round-trip them. + // Convert to dynamic file name format: ^/scheme/authority/path + + const colonIndex = uri.indexOf(":"); + if (colonIndex === -1) { + throw new Error("invalid URI: " + uri); + } + + const scheme = uri.substring(0, colonIndex); + let path = uri.substring(colonIndex + 1); + + let authority = "ts-nul-authority"; + if (path.startsWith("//")) { + const rest = path.substring(2); + const slashIndex = rest.indexOf("/"); + if (slashIndex === -1) { + throw new Error("invalid URI: " + uri); + } + authority = rest.substring(0, slashIndex); + path = rest.substring(slashIndex + 1); + } + + return "^/" + scheme + "/" + authority + "/" + path; +} diff --git a/packages/typescript/src/api/proto.generated.ts b/packages/typescript/src/api/proto.generated.ts index 6528b59b0f3f9..e42dcefa653e8 100644 --- a/packages/typescript/src/api/proto.generated.ts +++ b/packages/typescript/src/api/proto.generated.ts @@ -1,1381 +1,1381 @@ -// Code generated by gen-proto; DO NOT EDIT. - -import type { JsxEmit } from "#enums/jsxEmit"; -import type { ModuleDetectionKind } from "#enums/moduleDetectionKind"; -import type { ModuleKind } from "#enums/moduleKind"; -import type { ModuleResolutionKind } from "#enums/moduleResolutionKind"; -import type { NewLineKind } from "#enums/newLineKind"; -import type { ScriptTarget } from "#enums/scriptTarget"; - -export type APIMethod = { params: TParams; result: TResult; }; - -export interface APIMethodInfo { - release: APIMethod; - batchRequests: APIMethod; - initialize: APIMethod; - updateSnapshot: APIMethod; - updateTemporarySnapshot: APIMethod; - parseCommandLine: APIMethod; - readConfigFile: APIMethod; - parseJsonConfigFileContent: APIMethod; - parseConfigFile: APIMethod; - transpileModule: APIMethod; - transpileModuleFromFile: APIMethod; - transpileDeclaration: APIMethod; - transpileDeclarationFromFile: APIMethod; - getDefaultProjectForFile: APIMethod; - getSymbolAtPosition: APIMethod; - getSymbolsAtPositions: APIMethod; - getSymbolAtLocation: APIMethod; - getSymbolsAtLocations: APIMethod; - getSymbolOfSourceFile: APIMethod; - getSymbolsOfSourceFiles: APIMethod; - getTypeOfSymbol: APIMethod; - getTypesOfSymbols: APIMethod; - getDeclaredTypeOfSymbol: APIMethod; - getNonMissingTypeOfSymbol: APIMethod; - getSourceFile: APIMethod; - getSourceFileNames: APIMethod; - getSourceFileMetadata: APIMethod; - getConfigFileNames: APIMethod; - getConfigSourceFile: APIMethod; - resolveName: APIMethod; - getSymbolsInScope: APIMethod; - getSignaturesOfType: APIMethod; - getResolvedSignature: APIMethod; - getTypeAtLocation: APIMethod; - getTypeAtLocations: APIMethod; - getTypeAtPosition: APIMethod; - getTypesAtPositions: APIMethod; - getParentOfSymbol: APIMethod; - getMembersOfSymbol: APIMethod; - getExportsOfSymbol: APIMethod; - getExportSymbolOfSymbol: APIMethod; - getSymbolOfType: APIMethod; - getTargetOfType: APIMethod; - getFreshTypeOfType: APIMethod; - getRegularTypeOfType: APIMethod; - getTypesOfType: APIMethod; - getTypeParametersOfType: APIMethod; - getOuterTypeParametersOfType: APIMethod; - getLocalTypeParametersOfType: APIMethod; - getAliasTypeArgumentsOfType: APIMethod; - getAliasSymbolOfType: APIMethod; - getObjectTypeOfType: APIMethod; - getIndexTypeOfType: APIMethod; - getCheckTypeOfType: APIMethod; - getExtendsTypeOfType: APIMethod; - getBaseTypeOfType: APIMethod; - getConstraintOfType: APIMethod; - getTypeParametersOfSignature: APIMethod; - getParametersOfSignature: APIMethod; - getThisParameterOfSignature: APIMethod; - getTargetOfSignature: APIMethod; - getContextualType: APIMethod; - getBaseTypeOfLiteralType: APIMethod; - getNonNullableType: APIMethod; - getTypeFromTypeNode: APIMethod; - getWidenedType: APIMethod; - getParameterType: APIMethod; - getTypeParameterAtPosition: APIMethod; - isArrayLikeType: APIMethod; - isTypeAssignableTo: APIMethod; - getShorthandAssignmentValueSymbol: APIMethod; - getTypeOfSymbolAtLocation: APIMethod; - typeToTypeNode: APIMethod; - signatureToSignatureDeclaration: APIMethod; - typeToString: APIMethod; - isContextSensitive: APIMethod; - getReturnTypeOfSignature: APIMethod; - getRestTypeOfSignature: APIMethod; - getTypePredicateOfSignature: APIMethod; - getBaseTypes: APIMethod; - getPropertiesOfType: APIMethod; - getApparentPropertiesOfType: APIMethod; - getApparentType: APIMethod; - getReducedType: APIMethod; - getPropertyOfType: APIMethod; - getIndexInfosOfType: APIMethod; - getConstraintOfTypeParameter: APIMethod; - getDefaultFromTypeParameter: APIMethod; - getBaseConstraintOfType: APIMethod; - getTypeArguments: APIMethod; - getImportAdderEdits: APIMethod; - getTrueTypeOfConditionalType: APIMethod; - getFalseTypeOfConditionalType: APIMethod; - getConstantValue: APIMethod; - getSignatureFromDeclaration: APIMethod; - getExportSpecifierLocalTargetSymbol: APIMethod; - getAliasedSymbol: APIMethod; - getImmediateAliasedSymbol: APIMethod; - getTargetSymbol: APIMethod; - getFullyQualifiedName: APIMethod; - getExportsOfModule: APIMethod; - getMemberInModuleExports: APIMethod; - getJsDocTags: APIMethod; - getDocumentationComment: APIMethod; - isArrayType: APIMethod; - isTupleType: APIMethod; - isReadonlySymbol: APIMethod; - getReferencesToSymbolInFile: APIMethod; - getReferencedSymbolsForNode: APIMethod; - getSignatureUsages: APIMethod; - getCompletionsAtPosition: APIMethod; - getSyntacticDiagnostics: APIMethod; - getBindDiagnostics: APIMethod; - getSemanticDiagnostics: APIMethod; - getSuggestionDiagnostics: APIMethod; - getDeclarationDiagnostics: APIMethod; - getProgramDiagnostics: APIMethod; - getGlobalDiagnostics: APIMethod; - getConfigFileParsingDiagnostics: APIMethod; - printNode: APIMethod; - formatNodeForInsertion: APIMethod; - emit: APIMethod; - emitToString: APIMethod; - getJavaScriptEmit: APIMethod; - getDeclarationEmit: APIMethod; - getAnyType: APIMethod; - getStringType: APIMethod; - getNumberType: APIMethod; - getBooleanType: APIMethod; - getVoidType: APIMethod; - getUndefinedType: APIMethod; - getNullType: APIMethod; - getNeverType: APIMethod; - getUnknownType: APIMethod; - getBigIntType: APIMethod; - getESSymbolType: APIMethod; - getNonPrimitiveType: APIMethod; - getWellKnownSymbols: APIMethod; - getWellKnownSignatures: APIMethod; - startCPUProfile: APIMethod; - stopCPUProfile: APIMethod; - saveHeapProfile: APIMethod; -} - -export type DocumentIdentifier = string | { uri: string; }; - -/** ReleaseParams are the parameters for the release method. */ -export interface ReleaseParams { - snapshot: number; -} - -export interface BatchRequestsParams { - requests: readonly BatchRequest[] | null; -} - -export interface BatchRequestsResponse { - responses: BatchResponse[]; -} - -/** InitializeResponse is returned by the initialize method. */ -export interface InitializeResponse { - /** UseCaseSensitiveFileNames indicates whether the host file system is case-sensitive. */ - useCaseSensitiveFileNames: boolean; - /** CurrentDirectory is the server's current working directory. */ - currentDirectory: string; -} - -/** - * UpdateSnapshotParams are the parameters for creating a new snapshot. - * All fields are optional. With no fields set, the server adopts the latest LSP state. - */ -export interface UpdateSnapshotParams { - /** - * OpenProjects lists tsconfig.json files to open/load in the new snapshot. - * Opens are ref-counted and persist across snapshots until closed. - */ - openProjects?: readonly DocumentIdentifier[]; - /** - * CloseProjects lists tsconfig.json files to release in the new snapshot. - * A project is only unloaded once every API client that opened it closes it. - */ - closeProjects?: readonly DocumentIdentifier[]; - /** FileChanges describes file system changes since the last snapshot. */ - fileChanges?: APIFileChanges; - /** - * OpenFiles lists files to keep open for the API client, mirroring LSP's - * textDocument/didOpen. For each file, ancestor directories are searched for a - * tsconfig that contains it; if found, that configured project is loaded and - * becomes the file's default project. Otherwise the file is loaded into the - * inferred project (e.g. a node_modules d.ts not in any project's import graph). - * Opens persist across snapshots until the file is closed. - */ - openFiles?: readonly DocumentIdentifier[]; - /** - * CloseFiles lists files to release in the new snapshot. A file is only fully - * closed once every API client that opened it closes it. - */ - closeFiles?: readonly DocumentIdentifier[]; -} - -/** UpdateSnapshotResponse is returned by updateSnapshot. */ -export interface UpdateSnapshotResponse { - /** Snapshot is the handle for the newly created snapshot. */ - snapshot: number; - /** Projects is the list of projects in the snapshot. */ - projects: ProjectResponse[]; - /** - * Changes describes source file differences from the previous snapshot. - * Nil for the first snapshot in a session. - */ - changes?: SnapshotChanges; -} - -/** - * UpdateTemporarySnapshotParams are the parameters for creating a temporary - * snapshot that overrides a single file's content. - */ -export interface UpdateTemporarySnapshotParams { - /** Snapshot is the current client snapshot on which to layer the temporary update. */ - snapshot: number; - /** File identifies the file whose content is temporarily overridden. */ - file: DocumentIdentifier; - /** NewText is the temporary content for the file. */ - newText: string; -} - -export interface ParseCommandLineParams { - commandLine: readonly string[] | null; -} - -export interface ConfigFileResponse { - fileNames: string[]; - options: CompilerOptions; - projectReferences?: ProjectReference[]; - typeAcquisition?: TypeAcquisition; - compileOnSave?: boolean; - raw?: unknown; - errors: DiagnosticResponse[]; -} - -export interface ReadConfigFileParams { - file: DocumentIdentifier; -} - -export interface ReadConfigFileResponse { - config: unknown; - error?: DiagnosticResponse; -} - -export interface ParseJsonConfigFileContentParams { - json: unknown; - configDirectory?: string; - configFileName?: DocumentIdentifier; -} - -export interface ParseConfigFileParams { - file: DocumentIdentifier; -} - -export interface TranspileParams { - input: string; - options: TranspileOptions; -} - -export interface TranspileOutputResponse { - outputText: string; - diagnostics?: DiagnosticResponse[]; - sourceMapText?: string; -} - -export interface TranspileFromFileParams { - fileName: string; - options: TranspileOptions; -} - -export interface GetDefaultProjectForFileParams { - snapshot: number; - file: DocumentIdentifier; -} - -export interface ProjectResponse { - id: string; - configFileName: string; - currentDirectory: string; - parsedCommandLine: ConfigFileResponse; - /** @deprecated Use parsedCommandLine.fileNames. */ - rootFiles: string[]; - /** @deprecated Use parsedCommandLine.options. */ - compilerOptions: CompilerOptions; -} - -export interface GetSymbolAtPositionParams { - snapshot: number; - project: string; - file: DocumentIdentifier; - position: number; -} - -export interface SymbolResponse { - id: number; - /** - * Project is the project in which the symbol was first observed. It is the - * default project for follow-up lookups whose results can vary by project. - */ - project: string; - name: string; - flags: number; - checkFlags: number; - declarations?: string[]; - valueDeclaration?: string; - parent?: number; - exportSymbol?: number; -} - -export interface GetSymbolsAtPositionsParams { - snapshot: number; - project: string; - file: DocumentIdentifier; - positions: readonly number[] | null; -} - -export interface GetSymbolAtLocationParams { - snapshot: number; - project: string; - location: string; -} - -export interface GetSymbolsAtLocationsParams { - snapshot: number; - project: string; - locations: readonly string[] | null; -} - -export interface GetSymbolOfSourceFileParams { - snapshot: number; - project: string; - file: DocumentIdentifier; -} - -export interface GetSymbolsOfSourceFilesParams { - snapshot: number; - project: string; - files: readonly DocumentIdentifier[] | null; -} - -export interface GetTypeOfSymbolParams { - snapshot: number; - project: string; - symbol: number; -} - -export interface TypeResponse { - id: number; - flags: number; - objectFlags?: number; - /** - * Value is literal type data. BigInt literals are encoded as signed decimal - * strings because JSON cannot represent bigint; absent values are null. - */ - value: unknown; - /** ObjectType / TypeReference / StringMappingType / IndexType target */ - target?: number; - /** InterfaceType type parameters */ - typeParameters?: number[]; - outerTypeParameters?: number[]; - localTypeParameters?: number[]; - /** TupleType data */ - elementFlags?: number[]; - fixedLength?: number; - readonly?: boolean; - /** IndexedAccessType data */ - objectType?: number; - indexType?: number; - /** ConditionalType data */ - checkType?: number; - extendsType?: number; - /** SubstitutionType data */ - baseType?: number; - substConstraint?: number; - /** TemplateLiteralType text segments */ - texts?: string[]; - /** FreshableType data (LiteralType and computed enum types) */ - freshType?: number; - regularType?: number; - /** TypeParameter data */ - isThisType?: boolean; - /** IntrinsicType data */ - intrinsicName?: string; - /** TypeAlias data */ - aliasTypeArguments?: number[]; - aliasSymbol?: number; - /** Symbol associated with structured types */ - symbol?: number; -} - -export interface GetTypesOfSymbolsParams { - snapshot: number; - project: string; - symbols: readonly number[] | null; -} - -export interface GetSourceFileParams { - snapshot: number; - project: string; - file: DocumentIdentifier; -} - -/** - * SourceFileResponse contains the binary-encoded AST data for a source file. - * The Data field is base64-encoded binary data in the encoder's format. - */ -export interface SourceFileResponse { - /** Data is the base64-encoded binary AST data in the encoder's format. */ - data: string; -} - -export interface GetSourceFileNamesParams { - snapshot: number; - project: string; -} - -/** SourceFileMetadata carries program-stored metadata about a single source file. */ -export interface SourceFileMetadata { - isDefaultLibrary: boolean; - isFromExternalLibrary: boolean; - packageJsonType: string; - packageJsonDirectory: string; - impliedNodeFormat: ModuleKind; -} - -/** GetProjectDiagnosticsParams are parameters for project-wide diagnostic methods. */ -export interface GetProjectDiagnosticsParams { - snapshot: number; - project: string; -} - -export interface ResolveNameParams { - snapshot: number; - project: string; - name: string; - /** Optional: node handle for location context */ - location?: string; - /** Optional: file for location context (alternative to Location) */ - file?: DocumentIdentifier; - /** Optional: position in file for location context (with File) */ - position?: number; - /** SymbolFlags for what kind of symbol to find */ - meaning: number; - /** Whether to exclude global symbols */ - excludeGlobals?: boolean; -} - -/** - * GetSymbolsInScopeParams are parameters for getSymbolsInScope, which returns - * all symbols visible at a given location. - */ -export interface GetSymbolsInScopeParams { - snapshot: number; - project: string; - /** Optional: node handle for location context */ - location?: string; - /** Optional: file for location context (alternative to Location) */ - file?: DocumentIdentifier; - /** Optional: position in file for location context (with File) */ - position?: number; - /** SymbolFlags for what kind of symbols to find */ - meaning: number; -} - -export interface GetSignaturesOfTypeParams { - snapshot: number; - project: string; - type: number; - kind: number; -} - -export interface SignatureResponse { - id: number; - flags: number; - declaration?: string; - typeParameters?: number[]; - parameters?: number[]; - thisParameter?: number; - target?: number; -} - -export interface GetResolvedSignatureParams { - snapshot: number; - project: string; - location: string; -} - -export interface GetTypeAtLocationParams { - snapshot: number; - project: string; - location: string; -} - -export interface GetTypeAtLocationsParams { - snapshot: number; - project: string; - locations: readonly string[] | null; -} - -export interface GetTypeAtPositionParams { - snapshot: number; - project: string; - file: DocumentIdentifier; - position: number; -} - -export interface GetTypesAtPositionsParams { - snapshot: number; - project: string; - file: DocumentIdentifier; - positions: readonly number[] | null; -} - -/** GetSymbolPropertyParams is used for all symbol sub-property endpoints. */ -export interface GetSymbolPropertyParams { - snapshot: number; - project: string; - objectId: number; -} - -/** GetTypePropertyParams is used for all type sub-property endpoints. */ -export interface GetTypePropertyParams { - snapshot: number; - project: string; - objectId: number; -} - -/** GetSignaturePropertyParams is used for all signature sub-property endpoints. */ -export interface GetSignaturePropertyParams { - snapshot: number; - project: string; - objectId: number; -} - -/** GetContextualTypeParams returns the contextual type for a node. */ -export interface GetContextualTypeParams { - snapshot: number; - project: string; - location: string; -} - -/** GetBaseTypeOfLiteralTypeParams returns the base type of a literal type. */ -export interface GetBaseTypeOfLiteralTypeParams { - snapshot: number; - project: string; - type: number; -} - -/** GetTypeFromTypeNodeParams are the parameters for the getTypeFromTypeNode method. */ -export interface GetTypeFromTypeNodeParams { - snapshot: number; - project: string; - location: string; -} - -/** GetWidenedTypeParams are the parameters for the getWidenedType method. */ -export interface GetWidenedTypeParams { - snapshot: number; - project: string; - type: number; -} - -/** GetParameterTypeParams are the parameters for the getParameterType method. */ -export interface GetParameterTypeParams { - snapshot: number; - project: string; - signature: number; - index: number; -} - -/** IsArrayLikeTypeParams checks whether a type is array-like. */ -export interface IsArrayLikeTypeParams { - snapshot: number; - project: string; - type: number; -} - -/** IsTypeAssignableToParams checks assignability between two types. */ -export interface IsTypeAssignableToParams { - snapshot: number; - project: string; - source: number; - target: number; -} - -/** GetTypeOfSymbolAtLocationParams returns the narrowed type of a symbol at a specific location. */ -export interface GetTypeOfSymbolAtLocationParams { - snapshot: number; - project: string; - symbol: number; - location: string; -} - -/** TypeToTypeNodeParams are the parameters for the typeToTypeNode method. */ -export interface TypeToTypeNodeParams { - snapshot: number; - project: string; - type: number; - location?: string; - flags?: number; -} - -/** SignatureToSignatureDeclarationParams are the parameters for the signatureToSignatureDeclaration method. */ -export interface SignatureToSignatureDeclarationParams { - snapshot: number; - project: string; - signature: number; - kind: number; - location?: string; - flags?: number; -} - -/** CheckerSignatureParams are parameters for checker methods that operate on a signature. */ -export interface CheckerSignatureParams { - snapshot: number; - project: string; - signature: number; -} - -/** TypePredicateResponse is the response for getTypePredicateOfSignature. */ -export interface TypePredicateResponse { - kind: number; - parameterIndex: number; - parameterName?: string; - type?: TypeResponse; -} - -/** CheckerTypeParams are parameters for checker methods that operate on a type. */ -export interface CheckerTypeParams { - snapshot: number; - project: string; - type: number; -} - -/** GetPropertyOfTypeParams are parameters for getPropertyOfType (a named property of a type). */ -export interface GetPropertyOfTypeParams { - snapshot: number; - project: string; - type: number; - name: string; -} - -/** IndexInfoResponse represents a single index signature. */ -export interface IndexInfoResponse { - keyType: TypeResponse; - valueType: TypeResponse; - isReadonly?: boolean; - declaration?: string; -} - -export interface GetImportAdderEditsParams { - snapshot: number; - project: string; - file: DocumentIdentifier; - actions: readonly ImportAdderAction[] | null; -} - -export interface TextEdit { - pos: number; - end: number; - newText: string; -} - -/** CheckerNodeParams are parameters for checker methods that operate on a node location. */ -export interface CheckerNodeParams { - snapshot: number; - project: string; - location: string; -} - -/** CheckerSymbolParams are parameters for checker methods that operate on a symbol. */ -export interface CheckerSymbolParams { - snapshot: number; - project: string; - symbol: number; -} - -/** GetMemberInModuleExportsParams are parameters for getMemberInModuleExports. */ -export interface GetMemberInModuleExportsParams { - snapshot: number; - project: string; - symbol: number; - name: string; -} - -/** - * JSDocTagInfo is a single JSDoc tag, mirroring Strada's JSDocTagInfo but with the tag text - * rendered as a plain string rather than SymbolDisplayPart[]. - */ -export interface JSDocTagInfo { - name: string; - text?: string; -} - -/** GetReferencesToSymbolInFileParams are the parameters for the getReferencesToSymbolInFile method. */ -export interface GetReferencesToSymbolInFileParams { - snapshot: number; - project: string; - file: DocumentIdentifier; - symbol: number; -} - -/** GetReferencedSymbolsForNodeParams are the parameters for the getReferencedSymbolsForNode method. */ -export interface GetReferencedSymbolsForNodeParams { - snapshot: number; - project: string; - node: string; - position: number; -} - -/** ReferencedSymbolEntry represents a symbol definition and its references. */ -export interface ReferencedSymbolEntry { - definition: string; - symbol?: SymbolResponse; - references: string[]; -} - -/** GetSignatureUsagesParams are the parameters for the getSignatureUsages method. */ -export interface GetSignatureUsagesParams { - snapshot: number; - project: string; - signatureDecl: string; -} - -/** SignatureUsageResponse represents a single usage of a signature as a name-call pair. */ -export interface SignatureUsageResponse { - name: string; - call?: string; -} - -/** GetCompletionsAtPositionParams are the parameters for the getCompletionsAtPosition method. */ -export interface GetCompletionsAtPositionParams { - snapshot: number; - project: string; - file: DocumentIdentifier; - position: number; - triggerCharacter?: string; - includeSymbol?: boolean; -} - -/** CompletionInfoResponse wraps a list of completion entries. */ -export interface CompletionInfoResponse { - isIncomplete: boolean; - entries: CompletionEntryResponse[]; -} - -/** GetDiagnosticsParams are parameters for per-file diagnostic methods. */ -export interface GetDiagnosticsParams { - snapshot: number; - project: string; - files?: readonly DocumentIdentifier[]; -} - -/** DiagnosticResponse is the API response for a single diagnostic. */ -export interface DiagnosticResponse { - /** FileName is the path of the file this diagnostic belongs to, if any. */ - fileName?: string; - /** Pos is the start position of the diagnostic in the source file. */ - pos: number; - /** End is the end position of the diagnostic in the source file. */ - end: number; - /** StartPosition is the zero-based line and UTF-16 character position of Pos. */ - startPosition?: DiagnosticPositionResponse; - /** EndPosition is the zero-based line and UTF-16 character position of End. */ - endPosition?: DiagnosticPositionResponse; - /** SourceLines contains the source lines needed to render this diagnostic with context. */ - sourceLines?: DiagnosticSourceLineResponse[]; - /** Code is the diagnostic error code. */ - code: number; - /** Category is the diagnostic category (error, warning, suggestion, message). */ - category: number; - /** Source is a custom diagnostic-code prefix. An empty value uses the default "TS". */ - source?: string; - /** Text is the localized diagnostic message text. */ - text: string; - /** ReportsUnnecessary indicates this diagnostic highlights unnecessary code. */ - reportsUnnecessary?: boolean; - /** ReportsDeprecated indicates this diagnostic highlights deprecated code. */ - reportsDeprecated?: boolean; - /** MessageChain contains chained diagnostic messages, if any. */ - messageChain?: DiagnosticResponse[]; - /** RelatedInformation contains related diagnostic information, if any. */ - relatedInformation?: DiagnosticResponse[]; -} - -/** PrintNodeParams are the parameters for the printNode method. */ -export interface PrintNodeParams { - /** base64-encoded binary AST data */ - data: string; - preserveSourceNewlines?: boolean; - neverAsciiEscape?: boolean; - terminateUnterminatedLiterals?: boolean; -} - -/** FormatNodeForInsertionParams are the parameters for the formatNodeForInsertion method. */ -export interface FormatNodeForInsertionParams { - snapshot: number; - project: string; - /** target file where the node will be inserted */ - file: DocumentIdentifier; - /** UTF-16 code-unit offset of the insertion position in the target file */ - position: number; - /** base64-encoded binary AST data for the synthesized node */ - data: string; -} - -export interface EmitParams { - snapshot: number; - project: string; - emitOnly?: number; -} - -export interface EmitResponse { - emitSkipped: boolean; - diagnostics: DiagnosticResponse[]; - emittedFiles: string[]; -} - -export interface EmitOutputResponse { - emitSkipped: boolean; - diagnostics: DiagnosticResponse[]; - outputFiles: EmitOutputFile[]; -} - -export interface SelectedFilesEmitParams { - snapshot: number; - project: string; - files: readonly DocumentIdentifier[] | null; -} - -/** GetIntrinsicTypeParams is used for intrinsic type getters (anyType, stringType, etc.). */ -export interface GetIntrinsicTypeParams { - snapshot: number; - project: string; -} - -/** - * WellKnownSymbolsResponse carries the handle ids of the per-checker singleton - * symbols (unknown, undefined, arguments) so the client can identify them by id - * without a round-trip on every check. - */ -export interface WellKnownSymbolsResponse { - unknown: number; - undefined: number; - arguments: number; -} - -/** - * WellKnownSignaturesResponse carries the handle id of the per-checker singleton - * unknown signature (the signature the checker yields when a call cannot be - * resolved) so the client can identify it by id without a round-trip on every check. - */ -export interface WellKnownSignaturesResponse { - unknown: number; -} - -export interface ProfileParams { - dir: string; -} - -export interface ProfileResult { - file: string; -} - -export interface BatchRequest { - method: - | "batchRequests" - | "emit" - | "emitToString" - | "formatNodeForInsertion" - | "getAliasSymbolOfType" - | "getAliasTypeArgumentsOfType" - | "getAliasedSymbol" - | "getAnyType" - | "getApparentPropertiesOfType" - | "getApparentType" - | "getBaseConstraintOfType" - | "getBaseTypeOfLiteralType" - | "getBaseTypeOfType" - | "getBaseTypes" - | "getBigIntType" - | "getBindDiagnostics" - | "getBooleanType" - | "getCheckTypeOfType" - | "getCompletionsAtPosition" - | "getConfigFileNames" - | "getConfigFileParsingDiagnostics" - | "getConfigSourceFile" - | "getConstantValue" - | "getConstraintOfType" - | "getConstraintOfTypeParameter" - | "getContextualType" - | "getDeclarationDiagnostics" - | "getDeclarationEmit" - | "getDeclaredTypeOfSymbol" - | "getDefaultFromTypeParameter" - | "getDefaultProjectForFile" - | "getDocumentationComment" - | "getESSymbolType" - | "getExportSpecifierLocalTargetSymbol" - | "getExportSymbolOfSymbol" - | "getExportsOfModule" - | "getExportsOfSymbol" - | "getExtendsTypeOfType" - | "getFalseTypeOfConditionalType" - | "getFreshTypeOfType" - | "getFullyQualifiedName" - | "getGlobalDiagnostics" - | "getImmediateAliasedSymbol" - | "getImportAdderEdits" - | "getIndexInfosOfType" - | "getIndexTypeOfType" - | "getJavaScriptEmit" - | "getJsDocTags" - | "getLocalTypeParametersOfType" - | "getMemberInModuleExports" - | "getMembersOfSymbol" - | "getNeverType" - | "getNonMissingTypeOfSymbol" - | "getNonNullableType" - | "getNonPrimitiveType" - | "getNullType" - | "getNumberType" - | "getObjectTypeOfType" - | "getOuterTypeParametersOfType" - | "getParameterType" - | "getParametersOfSignature" - | "getParentOfSymbol" - | "getProgramDiagnostics" - | "getPropertiesOfType" - | "getPropertyOfType" - | "getReducedType" - | "getReferencedSymbolsForNode" - | "getReferencesToSymbolInFile" - | "getRegularTypeOfType" - | "getResolvedSignature" - | "getRestTypeOfSignature" - | "getReturnTypeOfSignature" - | "getSemanticDiagnostics" - | "getShorthandAssignmentValueSymbol" - | "getSignatureFromDeclaration" - | "getSignatureUsages" - | "getSignaturesOfType" - | "getSourceFile" - | "getSourceFileMetadata" - | "getSourceFileNames" - | "getStringType" - | "getSuggestionDiagnostics" - | "getSymbolAtLocation" - | "getSymbolAtPosition" - | "getSymbolOfSourceFile" - | "getSymbolOfType" - | "getSymbolsAtLocations" - | "getSymbolsAtPositions" - | "getSymbolsInScope" - | "getSymbolsOfSourceFiles" - | "getSyntacticDiagnostics" - | "getTargetOfSignature" - | "getTargetOfType" - | "getTargetSymbol" - | "getThisParameterOfSignature" - | "getTrueTypeOfConditionalType" - | "getTypeArguments" - | "getTypeAtLocation" - | "getTypeAtLocations" - | "getTypeAtPosition" - | "getTypeFromTypeNode" - | "getTypeOfSymbol" - | "getTypeOfSymbolAtLocation" - | "getTypeParameterAtPosition" - | "getTypeParametersOfSignature" - | "getTypeParametersOfType" - | "getTypePredicateOfSignature" - | "getTypesAtPositions" - | "getTypesOfSymbols" - | "getTypesOfType" - | "getUndefinedType" - | "getUnknownType" - | "getVoidType" - | "getWellKnownSignatures" - | "getWellKnownSymbols" - | "getWidenedType" - | "initialize" - | "isArrayLikeType" - | "isArrayType" - | "isContextSensitive" - | "isReadonlySymbol" - | "isTupleType" - | "isTypeAssignableTo" - | "parseCommandLine" - | "parseConfigFile" - | "parseJsonConfigFileContent" - | "printNode" - | "readConfigFile" - | "release" - | "resolveName" - | "saveHeapProfile" - | "signatureToSignatureDeclaration" - | "startCPUProfile" - | "stopCPUProfile" - | "transpileDeclaration" - | "transpileDeclarationFromFile" - | "transpileModule" - | "transpileModuleFromFile" - | "typeToString" - | "typeToTypeNode" - | "updateSnapshot" - | "updateTemporarySnapshot"; - params?: unknown; -} - -export interface BatchResponse { - method: - | "batchRequests" - | "emit" - | "emitToString" - | "formatNodeForInsertion" - | "getAliasSymbolOfType" - | "getAliasTypeArgumentsOfType" - | "getAliasedSymbol" - | "getAnyType" - | "getApparentPropertiesOfType" - | "getApparentType" - | "getBaseConstraintOfType" - | "getBaseTypeOfLiteralType" - | "getBaseTypeOfType" - | "getBaseTypes" - | "getBigIntType" - | "getBindDiagnostics" - | "getBooleanType" - | "getCheckTypeOfType" - | "getCompletionsAtPosition" - | "getConfigFileNames" - | "getConfigFileParsingDiagnostics" - | "getConfigSourceFile" - | "getConstantValue" - | "getConstraintOfType" - | "getConstraintOfTypeParameter" - | "getContextualType" - | "getDeclarationDiagnostics" - | "getDeclarationEmit" - | "getDeclaredTypeOfSymbol" - | "getDefaultFromTypeParameter" - | "getDefaultProjectForFile" - | "getDocumentationComment" - | "getESSymbolType" - | "getExportSpecifierLocalTargetSymbol" - | "getExportSymbolOfSymbol" - | "getExportsOfModule" - | "getExportsOfSymbol" - | "getExtendsTypeOfType" - | "getFalseTypeOfConditionalType" - | "getFreshTypeOfType" - | "getFullyQualifiedName" - | "getGlobalDiagnostics" - | "getImmediateAliasedSymbol" - | "getImportAdderEdits" - | "getIndexInfosOfType" - | "getIndexTypeOfType" - | "getJavaScriptEmit" - | "getJsDocTags" - | "getLocalTypeParametersOfType" - | "getMemberInModuleExports" - | "getMembersOfSymbol" - | "getNeverType" - | "getNonMissingTypeOfSymbol" - | "getNonNullableType" - | "getNonPrimitiveType" - | "getNullType" - | "getNumberType" - | "getObjectTypeOfType" - | "getOuterTypeParametersOfType" - | "getParameterType" - | "getParametersOfSignature" - | "getParentOfSymbol" - | "getProgramDiagnostics" - | "getPropertiesOfType" - | "getPropertyOfType" - | "getReducedType" - | "getReferencedSymbolsForNode" - | "getReferencesToSymbolInFile" - | "getRegularTypeOfType" - | "getResolvedSignature" - | "getRestTypeOfSignature" - | "getReturnTypeOfSignature" - | "getSemanticDiagnostics" - | "getShorthandAssignmentValueSymbol" - | "getSignatureFromDeclaration" - | "getSignatureUsages" - | "getSignaturesOfType" - | "getSourceFile" - | "getSourceFileMetadata" - | "getSourceFileNames" - | "getStringType" - | "getSuggestionDiagnostics" - | "getSymbolAtLocation" - | "getSymbolAtPosition" - | "getSymbolOfSourceFile" - | "getSymbolOfType" - | "getSymbolsAtLocations" - | "getSymbolsAtPositions" - | "getSymbolsInScope" - | "getSymbolsOfSourceFiles" - | "getSyntacticDiagnostics" - | "getTargetOfSignature" - | "getTargetOfType" - | "getTargetSymbol" - | "getThisParameterOfSignature" - | "getTrueTypeOfConditionalType" - | "getTypeArguments" - | "getTypeAtLocation" - | "getTypeAtLocations" - | "getTypeAtPosition" - | "getTypeFromTypeNode" - | "getTypeOfSymbol" - | "getTypeOfSymbolAtLocation" - | "getTypeParameterAtPosition" - | "getTypeParametersOfSignature" - | "getTypeParametersOfType" - | "getTypePredicateOfSignature" - | "getTypesAtPositions" - | "getTypesOfSymbols" - | "getTypesOfType" - | "getUndefinedType" - | "getUnknownType" - | "getVoidType" - | "getWellKnownSignatures" - | "getWellKnownSymbols" - | "getWidenedType" - | "initialize" - | "isArrayLikeType" - | "isArrayType" - | "isContextSensitive" - | "isReadonlySymbol" - | "isTupleType" - | "isTypeAssignableTo" - | "parseCommandLine" - | "parseConfigFile" - | "parseJsonConfigFileContent" - | "printNode" - | "readConfigFile" - | "release" - | "resolveName" - | "saveHeapProfile" - | "signatureToSignatureDeclaration" - | "startCPUProfile" - | "stopCPUProfile" - | "transpileDeclaration" - | "transpileDeclarationFromFile" - | "transpileModule" - | "transpileModuleFromFile" - | "typeToString" - | "typeToTypeNode" - | "updateSnapshot" - | "updateTemporarySnapshot"; - result: unknown; - error?: string; -} - -/** - * APIFileChanges describes file changes to apply when updating a snapshot. - * Either InvalidateAll is true (discard all caches) or Changed/Created/Deleted - * list individual documents. - */ -export interface APIFileChanges { - invalidateAll?: boolean; - changed?: DocumentIdentifier[]; - created?: DocumentIdentifier[]; - deleted?: DocumentIdentifier[]; -} - -/** - * SnapshotChanges describes what changed between the previous latest snapshot - * and the newly created snapshot. Changes are reported per-project so clients - * can track cache refs at the (snapshot, project) level. - */ -export interface SnapshotChanges { - /** - * ChangedProjects maps project handles to the file changes within that project. - * Projects not listed here (and not in RemovedProjects) are unchanged. - */ - changedProjects?: Record; - /** - * RemovedProjects lists project handles that were present in the previous - * snapshot but absent from the new one. - */ - removedProjects?: string[]; -} - -/** CompilerOptions contains the compiler options exposed by the API. */ -export interface CompilerOptions { - allowJs?: boolean; - allowArbitraryExtensions?: boolean; - allowImportingTsExtensions?: boolean; - allowNonTsExtensions?: boolean; - allowUmdGlobalAccess?: boolean; - allowUnreachableCode?: boolean; - allowUnusedLabels?: boolean; - assumeChangesOnlyAffectDirectDependencies?: boolean; - checkJs?: boolean; - customConditions?: string[]; - composite?: boolean; - emitDeclarationOnly?: boolean; - emitBOM?: boolean; - emitDecoratorMetadata?: boolean; - declaration?: boolean; - declarationDir?: string; - declarationMap?: boolean; - deduplicatePackages?: boolean; - disableSizeLimit?: boolean; - disableSourceOfProjectReferenceRedirect?: boolean; - disableSolutionSearching?: boolean; - disableReferencedProjectLoad?: boolean; - erasableSyntaxOnly?: boolean; - exactOptionalPropertyTypes?: boolean; - experimentalDecorators?: boolean; - forceConsistentCasingInFileNames?: boolean; - isolatedModules?: boolean; - isolatedDeclarations?: boolean; - ignoreConfig?: boolean; - ignoreDeprecations?: string; - importHelpers?: boolean; - inlineSourceMap?: boolean; - inlineSources?: boolean; - init?: boolean; - incremental?: boolean; - jsx?: JsxEmit; - jsxFactory?: string; - jsxFragmentFactory?: string; - jsxImportSource?: string; - lib?: string[]; - libReplacement?: boolean; - locale?: string; - mapRoot?: string; - module?: ModuleKind; - moduleResolution?: ModuleResolutionKind; - moduleSuffixes?: string[]; - moduleDetection?: ModuleDetectionKind; - newLine?: NewLineKind; - noEmit?: boolean; - noCheck?: boolean; - noErrorTruncation?: boolean; - noFallthroughCasesInSwitch?: boolean; - noImplicitAny?: boolean; - noImplicitThis?: boolean; - noImplicitReturns?: boolean; - noEmitHelpers?: boolean; - noLib?: boolean; - noPropertyAccessFromIndexSignature?: boolean; - noUncheckedIndexedAccess?: boolean; - noEmitOnError?: boolean; - noUnusedLocals?: boolean; - noUnusedParameters?: boolean; - noResolve?: boolean; - noImplicitOverride?: boolean; - noUncheckedSideEffectImports?: boolean; - outDir?: string; - paths?: Record; - preserveConstEnums?: boolean; - preserveSymlinks?: boolean; - project?: string; - resolveJsonModule?: boolean; - resolvePackageJsonExports?: boolean; - resolvePackageJsonImports?: boolean; - removeComments?: boolean; - rewriteRelativeImportExtensions?: boolean; - reactNamespace?: string; - rootDir?: string; - rootDirs?: string[]; - skipLibCheck?: boolean; - stableTypeOrdering?: boolean; - strict?: boolean; - strictBindCallApply?: boolean; - strictBuiltinIteratorReturn?: boolean; - strictFunctionTypes?: boolean; - strictNullChecks?: boolean; - strictPropertyInitialization?: boolean; - stripInternal?: boolean; - skipDefaultLibCheck?: boolean; - sourceMap?: boolean; - sourceRoot?: string; - suppressOutputPathCheck?: boolean; - target?: ScriptTarget; - traceResolution?: boolean; - tsBuildInfoFile?: string; - typeRoots?: string[]; - types?: string[]; - useDefineForClassFields?: boolean; - useUnknownInCatchVariables?: boolean; - verbatimModuleSyntax?: boolean; - maxNodeModuleJsDepth?: number; - /** Internal fields */ - configFilePath?: string; -} - -export interface ProjectReference { - /** Path is a normalized path on disk. */ - path: string; - /** OriginalPath is the path as it was originally written. */ - originalPath: string; - /** Circular indicates that this reference is intended to form a circularity. */ - circular: boolean; -} - -export interface TypeAcquisition { - enable?: boolean; - include?: string[]; - exclude?: string[]; - disableFilenameBasedTypeAcquisition?: boolean; -} - -export interface TranspileOptions { - compilerOptions?: CompilerOptions; - fileName?: string; - reportDiagnostics?: boolean; -} - -export interface ImportAdderAction { - kind: "importSymbol"; - symbol?: number; - isValidTypeOnlyUseSite?: boolean; -} - -/** CompletionEntryResponse represents a single completion item. */ -export interface CompletionEntryResponse { - name: string; - kind?: number; - sortText?: string; - insertText?: string; - filterText?: string; - detail?: string; - labelDetails?: CompletionEntryLabelDetailsResponse; - symbol?: SymbolResponse; -} - -export interface DiagnosticPositionResponse { - line: number; - character: number; -} - -export interface DiagnosticSourceLineResponse { - line: number; - text: string; -} - -export interface EmitOutputFile { - fileName: string; - text: string; - sourceFileName?: string; -} - -/** ProjectFileChanges describes what source files changed within a single project. */ -export interface ProjectFileChanges { - /** ChangedFiles lists source file paths whose content differs. */ - changedFiles?: string[]; - /** DeletedFiles lists source file paths removed from the project's program. */ - deletedFiles?: string[]; -} - -/** CompletionEntryLabelDetailsResponse holds additional label display text for a completion entry. */ -export interface CompletionEntryLabelDetailsResponse { - detail?: string; - description?: string; -} +// Code generated by gen-proto; DO NOT EDIT. + +import type { JsxEmit } from "#enums/jsxEmit"; +import type { ModuleDetectionKind } from "#enums/moduleDetectionKind"; +import type { ModuleKind } from "#enums/moduleKind"; +import type { ModuleResolutionKind } from "#enums/moduleResolutionKind"; +import type { NewLineKind } from "#enums/newLineKind"; +import type { ScriptTarget } from "#enums/scriptTarget"; + +export type APIMethod = { params: TParams; result: TResult; }; + +export interface APIMethodInfo { + release: APIMethod; + batchRequests: APIMethod; + initialize: APIMethod; + updateSnapshot: APIMethod; + updateTemporarySnapshot: APIMethod; + parseCommandLine: APIMethod; + readConfigFile: APIMethod; + parseJsonConfigFileContent: APIMethod; + parseConfigFile: APIMethod; + transpileModule: APIMethod; + transpileModuleFromFile: APIMethod; + transpileDeclaration: APIMethod; + transpileDeclarationFromFile: APIMethod; + getDefaultProjectForFile: APIMethod; + getSymbolAtPosition: APIMethod; + getSymbolsAtPositions: APIMethod; + getSymbolAtLocation: APIMethod; + getSymbolsAtLocations: APIMethod; + getSymbolOfSourceFile: APIMethod; + getSymbolsOfSourceFiles: APIMethod; + getTypeOfSymbol: APIMethod; + getTypesOfSymbols: APIMethod; + getDeclaredTypeOfSymbol: APIMethod; + getNonMissingTypeOfSymbol: APIMethod; + getSourceFile: APIMethod; + getSourceFileNames: APIMethod; + getSourceFileMetadata: APIMethod; + getConfigFileNames: APIMethod; + getConfigSourceFile: APIMethod; + resolveName: APIMethod; + getSymbolsInScope: APIMethod; + getSignaturesOfType: APIMethod; + getResolvedSignature: APIMethod; + getTypeAtLocation: APIMethod; + getTypeAtLocations: APIMethod; + getTypeAtPosition: APIMethod; + getTypesAtPositions: APIMethod; + getParentOfSymbol: APIMethod; + getMembersOfSymbol: APIMethod; + getExportsOfSymbol: APIMethod; + getExportSymbolOfSymbol: APIMethod; + getSymbolOfType: APIMethod; + getTargetOfType: APIMethod; + getFreshTypeOfType: APIMethod; + getRegularTypeOfType: APIMethod; + getTypesOfType: APIMethod; + getTypeParametersOfType: APIMethod; + getOuterTypeParametersOfType: APIMethod; + getLocalTypeParametersOfType: APIMethod; + getAliasTypeArgumentsOfType: APIMethod; + getAliasSymbolOfType: APIMethod; + getObjectTypeOfType: APIMethod; + getIndexTypeOfType: APIMethod; + getCheckTypeOfType: APIMethod; + getExtendsTypeOfType: APIMethod; + getBaseTypeOfType: APIMethod; + getConstraintOfType: APIMethod; + getTypeParametersOfSignature: APIMethod; + getParametersOfSignature: APIMethod; + getThisParameterOfSignature: APIMethod; + getTargetOfSignature: APIMethod; + getContextualType: APIMethod; + getBaseTypeOfLiteralType: APIMethod; + getNonNullableType: APIMethod; + getTypeFromTypeNode: APIMethod; + getWidenedType: APIMethod; + getParameterType: APIMethod; + getTypeParameterAtPosition: APIMethod; + isArrayLikeType: APIMethod; + isTypeAssignableTo: APIMethod; + getShorthandAssignmentValueSymbol: APIMethod; + getTypeOfSymbolAtLocation: APIMethod; + typeToTypeNode: APIMethod; + signatureToSignatureDeclaration: APIMethod; + typeToString: APIMethod; + isContextSensitive: APIMethod; + getReturnTypeOfSignature: APIMethod; + getRestTypeOfSignature: APIMethod; + getTypePredicateOfSignature: APIMethod; + getBaseTypes: APIMethod; + getPropertiesOfType: APIMethod; + getApparentPropertiesOfType: APIMethod; + getApparentType: APIMethod; + getReducedType: APIMethod; + getPropertyOfType: APIMethod; + getIndexInfosOfType: APIMethod; + getConstraintOfTypeParameter: APIMethod; + getDefaultFromTypeParameter: APIMethod; + getBaseConstraintOfType: APIMethod; + getTypeArguments: APIMethod; + getImportAdderEdits: APIMethod; + getTrueTypeOfConditionalType: APIMethod; + getFalseTypeOfConditionalType: APIMethod; + getConstantValue: APIMethod; + getSignatureFromDeclaration: APIMethod; + getExportSpecifierLocalTargetSymbol: APIMethod; + getAliasedSymbol: APIMethod; + getImmediateAliasedSymbol: APIMethod; + getTargetSymbol: APIMethod; + getFullyQualifiedName: APIMethod; + getExportsOfModule: APIMethod; + getMemberInModuleExports: APIMethod; + getJsDocTags: APIMethod; + getDocumentationComment: APIMethod; + isArrayType: APIMethod; + isTupleType: APIMethod; + isReadonlySymbol: APIMethod; + getReferencesToSymbolInFile: APIMethod; + getReferencedSymbolsForNode: APIMethod; + getSignatureUsages: APIMethod; + getCompletionsAtPosition: APIMethod; + getSyntacticDiagnostics: APIMethod; + getBindDiagnostics: APIMethod; + getSemanticDiagnostics: APIMethod; + getSuggestionDiagnostics: APIMethod; + getDeclarationDiagnostics: APIMethod; + getProgramDiagnostics: APIMethod; + getGlobalDiagnostics: APIMethod; + getConfigFileParsingDiagnostics: APIMethod; + printNode: APIMethod; + formatNodeForInsertion: APIMethod; + emit: APIMethod; + emitToString: APIMethod; + getJavaScriptEmit: APIMethod; + getDeclarationEmit: APIMethod; + getAnyType: APIMethod; + getStringType: APIMethod; + getNumberType: APIMethod; + getBooleanType: APIMethod; + getVoidType: APIMethod; + getUndefinedType: APIMethod; + getNullType: APIMethod; + getNeverType: APIMethod; + getUnknownType: APIMethod; + getBigIntType: APIMethod; + getESSymbolType: APIMethod; + getNonPrimitiveType: APIMethod; + getWellKnownSymbols: APIMethod; + getWellKnownSignatures: APIMethod; + startCPUProfile: APIMethod; + stopCPUProfile: APIMethod; + saveHeapProfile: APIMethod; +} + +export type DocumentIdentifier = string | { uri: string; }; + +/** ReleaseParams are the parameters for the release method. */ +export interface ReleaseParams { + snapshot: number; +} + +export interface BatchRequestsParams { + requests: readonly BatchRequest[] | null; +} + +export interface BatchRequestsResponse { + responses: BatchResponse[]; +} + +/** InitializeResponse is returned by the initialize method. */ +export interface InitializeResponse { + /** UseCaseSensitiveFileNames indicates whether the host file system is case-sensitive. */ + useCaseSensitiveFileNames: boolean; + /** CurrentDirectory is the server's current working directory. */ + currentDirectory: string; +} + +/** + * UpdateSnapshotParams are the parameters for creating a new snapshot. + * All fields are optional. With no fields set, the server adopts the latest LSP state. + */ +export interface UpdateSnapshotParams { + /** + * OpenProjects lists tsconfig.json files to open/load in the new snapshot. + * Opens are ref-counted and persist across snapshots until closed. + */ + openProjects?: readonly DocumentIdentifier[]; + /** + * CloseProjects lists tsconfig.json files to release in the new snapshot. + * A project is only unloaded once every API client that opened it closes it. + */ + closeProjects?: readonly DocumentIdentifier[]; + /** FileChanges describes file system changes since the last snapshot. */ + fileChanges?: APIFileChanges; + /** + * OpenFiles lists files to keep open for the API client, mirroring LSP's + * textDocument/didOpen. For each file, ancestor directories are searched for a + * tsconfig that contains it; if found, that configured project is loaded and + * becomes the file's default project. Otherwise the file is loaded into the + * inferred project (e.g. a node_modules d.ts not in any project's import graph). + * Opens persist across snapshots until the file is closed. + */ + openFiles?: readonly DocumentIdentifier[]; + /** + * CloseFiles lists files to release in the new snapshot. A file is only fully + * closed once every API client that opened it closes it. + */ + closeFiles?: readonly DocumentIdentifier[]; +} + +/** UpdateSnapshotResponse is returned by updateSnapshot. */ +export interface UpdateSnapshotResponse { + /** Snapshot is the handle for the newly created snapshot. */ + snapshot: number; + /** Projects is the list of projects in the snapshot. */ + projects: ProjectResponse[]; + /** + * Changes describes source file differences from the previous snapshot. + * Nil for the first snapshot in a session. + */ + changes?: SnapshotChanges; +} + +/** + * UpdateTemporarySnapshotParams are the parameters for creating a temporary + * snapshot that overrides a single file's content. + */ +export interface UpdateTemporarySnapshotParams { + /** Snapshot is the current client snapshot on which to layer the temporary update. */ + snapshot: number; + /** File identifies the file whose content is temporarily overridden. */ + file: DocumentIdentifier; + /** NewText is the temporary content for the file. */ + newText: string; +} + +export interface ParseCommandLineParams { + commandLine: readonly string[] | null; +} + +export interface ConfigFileResponse { + fileNames: string[]; + options: CompilerOptions; + projectReferences?: ProjectReference[]; + typeAcquisition?: TypeAcquisition; + compileOnSave?: boolean; + raw?: unknown; + errors: DiagnosticResponse[]; +} + +export interface ReadConfigFileParams { + file: DocumentIdentifier; +} + +export interface ReadConfigFileResponse { + config: unknown; + error?: DiagnosticResponse; +} + +export interface ParseJsonConfigFileContentParams { + json: unknown; + configDirectory?: string; + configFileName?: DocumentIdentifier; +} + +export interface ParseConfigFileParams { + file: DocumentIdentifier; +} + +export interface TranspileParams { + input: string; + options: TranspileOptions; +} + +export interface TranspileOutputResponse { + outputText: string; + diagnostics?: DiagnosticResponse[]; + sourceMapText?: string; +} + +export interface TranspileFromFileParams { + fileName: string; + options: TranspileOptions; +} + +export interface GetDefaultProjectForFileParams { + snapshot: number; + file: DocumentIdentifier; +} + +export interface ProjectResponse { + id: string; + configFileName: string; + currentDirectory: string; + parsedCommandLine: ConfigFileResponse; + /** @deprecated Use parsedCommandLine.fileNames. */ + rootFiles: string[]; + /** @deprecated Use parsedCommandLine.options. */ + compilerOptions: CompilerOptions; +} + +export interface GetSymbolAtPositionParams { + snapshot: number; + project: string; + file: DocumentIdentifier; + position: number; +} + +export interface SymbolResponse { + id: number; + /** + * Project is the project in which the symbol was first observed. It is the + * default project for follow-up lookups whose results can vary by project. + */ + project: string; + name: string; + flags: number; + checkFlags: number; + declarations?: string[]; + valueDeclaration?: string; + parent?: number; + exportSymbol?: number; +} + +export interface GetSymbolsAtPositionsParams { + snapshot: number; + project: string; + file: DocumentIdentifier; + positions: readonly number[] | null; +} + +export interface GetSymbolAtLocationParams { + snapshot: number; + project: string; + location: string; +} + +export interface GetSymbolsAtLocationsParams { + snapshot: number; + project: string; + locations: readonly string[] | null; +} + +export interface GetSymbolOfSourceFileParams { + snapshot: number; + project: string; + file: DocumentIdentifier; +} + +export interface GetSymbolsOfSourceFilesParams { + snapshot: number; + project: string; + files: readonly DocumentIdentifier[] | null; +} + +export interface GetTypeOfSymbolParams { + snapshot: number; + project: string; + symbol: number; +} + +export interface TypeResponse { + id: number; + flags: number; + objectFlags?: number; + /** + * Value is literal type data. BigInt literals are encoded as signed decimal + * strings because JSON cannot represent bigint; absent values are null. + */ + value: unknown; + /** ObjectType / TypeReference / StringMappingType / IndexType target */ + target?: number; + /** InterfaceType type parameters */ + typeParameters?: number[]; + outerTypeParameters?: number[]; + localTypeParameters?: number[]; + /** TupleType data */ + elementFlags?: number[]; + fixedLength?: number; + readonly?: boolean; + /** IndexedAccessType data */ + objectType?: number; + indexType?: number; + /** ConditionalType data */ + checkType?: number; + extendsType?: number; + /** SubstitutionType data */ + baseType?: number; + substConstraint?: number; + /** TemplateLiteralType text segments */ + texts?: string[]; + /** FreshableType data (LiteralType and computed enum types) */ + freshType?: number; + regularType?: number; + /** TypeParameter data */ + isThisType?: boolean; + /** IntrinsicType data */ + intrinsicName?: string; + /** TypeAlias data */ + aliasTypeArguments?: number[]; + aliasSymbol?: number; + /** Symbol associated with structured types */ + symbol?: number; +} + +export interface GetTypesOfSymbolsParams { + snapshot: number; + project: string; + symbols: readonly number[] | null; +} + +export interface GetSourceFileParams { + snapshot: number; + project: string; + file: DocumentIdentifier; +} + +/** + * SourceFileResponse contains the binary-encoded AST data for a source file. + * The Data field is base64-encoded binary data in the encoder's format. + */ +export interface SourceFileResponse { + /** Data is the base64-encoded binary AST data in the encoder's format. */ + data: string; +} + +export interface GetSourceFileNamesParams { + snapshot: number; + project: string; +} + +/** SourceFileMetadata carries program-stored metadata about a single source file. */ +export interface SourceFileMetadata { + isDefaultLibrary: boolean; + isFromExternalLibrary: boolean; + packageJsonType: string; + packageJsonDirectory: string; + impliedNodeFormat: ModuleKind; +} + +/** GetProjectDiagnosticsParams are parameters for project-wide diagnostic methods. */ +export interface GetProjectDiagnosticsParams { + snapshot: number; + project: string; +} + +export interface ResolveNameParams { + snapshot: number; + project: string; + name: string; + /** Optional: node handle for location context */ + location?: string; + /** Optional: file for location context (alternative to Location) */ + file?: DocumentIdentifier; + /** Optional: position in file for location context (with File) */ + position?: number; + /** SymbolFlags for what kind of symbol to find */ + meaning: number; + /** Whether to exclude global symbols */ + excludeGlobals?: boolean; +} + +/** + * GetSymbolsInScopeParams are parameters for getSymbolsInScope, which returns + * all symbols visible at a given location. + */ +export interface GetSymbolsInScopeParams { + snapshot: number; + project: string; + /** Optional: node handle for location context */ + location?: string; + /** Optional: file for location context (alternative to Location) */ + file?: DocumentIdentifier; + /** Optional: position in file for location context (with File) */ + position?: number; + /** SymbolFlags for what kind of symbols to find */ + meaning: number; +} + +export interface GetSignaturesOfTypeParams { + snapshot: number; + project: string; + type: number; + kind: number; +} + +export interface SignatureResponse { + id: number; + flags: number; + declaration?: string; + typeParameters?: number[]; + parameters?: number[]; + thisParameter?: number; + target?: number; +} + +export interface GetResolvedSignatureParams { + snapshot: number; + project: string; + location: string; +} + +export interface GetTypeAtLocationParams { + snapshot: number; + project: string; + location: string; +} + +export interface GetTypeAtLocationsParams { + snapshot: number; + project: string; + locations: readonly string[] | null; +} + +export interface GetTypeAtPositionParams { + snapshot: number; + project: string; + file: DocumentIdentifier; + position: number; +} + +export interface GetTypesAtPositionsParams { + snapshot: number; + project: string; + file: DocumentIdentifier; + positions: readonly number[] | null; +} + +/** GetSymbolPropertyParams is used for all symbol sub-property endpoints. */ +export interface GetSymbolPropertyParams { + snapshot: number; + project: string; + objectId: number; +} + +/** GetTypePropertyParams is used for all type sub-property endpoints. */ +export interface GetTypePropertyParams { + snapshot: number; + project: string; + objectId: number; +} + +/** GetSignaturePropertyParams is used for all signature sub-property endpoints. */ +export interface GetSignaturePropertyParams { + snapshot: number; + project: string; + objectId: number; +} + +/** GetContextualTypeParams returns the contextual type for a node. */ +export interface GetContextualTypeParams { + snapshot: number; + project: string; + location: string; +} + +/** GetBaseTypeOfLiteralTypeParams returns the base type of a literal type. */ +export interface GetBaseTypeOfLiteralTypeParams { + snapshot: number; + project: string; + type: number; +} + +/** GetTypeFromTypeNodeParams are the parameters for the getTypeFromTypeNode method. */ +export interface GetTypeFromTypeNodeParams { + snapshot: number; + project: string; + location: string; +} + +/** GetWidenedTypeParams are the parameters for the getWidenedType method. */ +export interface GetWidenedTypeParams { + snapshot: number; + project: string; + type: number; +} + +/** GetParameterTypeParams are the parameters for the getParameterType method. */ +export interface GetParameterTypeParams { + snapshot: number; + project: string; + signature: number; + index: number; +} + +/** IsArrayLikeTypeParams checks whether a type is array-like. */ +export interface IsArrayLikeTypeParams { + snapshot: number; + project: string; + type: number; +} + +/** IsTypeAssignableToParams checks assignability between two types. */ +export interface IsTypeAssignableToParams { + snapshot: number; + project: string; + source: number; + target: number; +} + +/** GetTypeOfSymbolAtLocationParams returns the narrowed type of a symbol at a specific location. */ +export interface GetTypeOfSymbolAtLocationParams { + snapshot: number; + project: string; + symbol: number; + location: string; +} + +/** TypeToTypeNodeParams are the parameters for the typeToTypeNode method. */ +export interface TypeToTypeNodeParams { + snapshot: number; + project: string; + type: number; + location?: string; + flags?: number; +} + +/** SignatureToSignatureDeclarationParams are the parameters for the signatureToSignatureDeclaration method. */ +export interface SignatureToSignatureDeclarationParams { + snapshot: number; + project: string; + signature: number; + kind: number; + location?: string; + flags?: number; +} + +/** CheckerSignatureParams are parameters for checker methods that operate on a signature. */ +export interface CheckerSignatureParams { + snapshot: number; + project: string; + signature: number; +} + +/** TypePredicateResponse is the response for getTypePredicateOfSignature. */ +export interface TypePredicateResponse { + kind: number; + parameterIndex: number; + parameterName?: string; + type?: TypeResponse; +} + +/** CheckerTypeParams are parameters for checker methods that operate on a type. */ +export interface CheckerTypeParams { + snapshot: number; + project: string; + type: number; +} + +/** GetPropertyOfTypeParams are parameters for getPropertyOfType (a named property of a type). */ +export interface GetPropertyOfTypeParams { + snapshot: number; + project: string; + type: number; + name: string; +} + +/** IndexInfoResponse represents a single index signature. */ +export interface IndexInfoResponse { + keyType: TypeResponse; + valueType: TypeResponse; + isReadonly?: boolean; + declaration?: string; +} + +export interface GetImportAdderEditsParams { + snapshot: number; + project: string; + file: DocumentIdentifier; + actions: readonly ImportAdderAction[] | null; +} + +export interface TextEdit { + pos: number; + end: number; + newText: string; +} + +/** CheckerNodeParams are parameters for checker methods that operate on a node location. */ +export interface CheckerNodeParams { + snapshot: number; + project: string; + location: string; +} + +/** CheckerSymbolParams are parameters for checker methods that operate on a symbol. */ +export interface CheckerSymbolParams { + snapshot: number; + project: string; + symbol: number; +} + +/** GetMemberInModuleExportsParams are parameters for getMemberInModuleExports. */ +export interface GetMemberInModuleExportsParams { + snapshot: number; + project: string; + symbol: number; + name: string; +} + +/** + * JSDocTagInfo is a single JSDoc tag, mirroring Strada's JSDocTagInfo but with the tag text + * rendered as a plain string rather than SymbolDisplayPart[]. + */ +export interface JSDocTagInfo { + name: string; + text?: string; +} + +/** GetReferencesToSymbolInFileParams are the parameters for the getReferencesToSymbolInFile method. */ +export interface GetReferencesToSymbolInFileParams { + snapshot: number; + project: string; + file: DocumentIdentifier; + symbol: number; +} + +/** GetReferencedSymbolsForNodeParams are the parameters for the getReferencedSymbolsForNode method. */ +export interface GetReferencedSymbolsForNodeParams { + snapshot: number; + project: string; + node: string; + position: number; +} + +/** ReferencedSymbolEntry represents a symbol definition and its references. */ +export interface ReferencedSymbolEntry { + definition: string; + symbol?: SymbolResponse; + references: string[]; +} + +/** GetSignatureUsagesParams are the parameters for the getSignatureUsages method. */ +export interface GetSignatureUsagesParams { + snapshot: number; + project: string; + signatureDecl: string; +} + +/** SignatureUsageResponse represents a single usage of a signature as a name-call pair. */ +export interface SignatureUsageResponse { + name: string; + call?: string; +} + +/** GetCompletionsAtPositionParams are the parameters for the getCompletionsAtPosition method. */ +export interface GetCompletionsAtPositionParams { + snapshot: number; + project: string; + file: DocumentIdentifier; + position: number; + triggerCharacter?: string; + includeSymbol?: boolean; +} + +/** CompletionInfoResponse wraps a list of completion entries. */ +export interface CompletionInfoResponse { + isIncomplete: boolean; + entries: CompletionEntryResponse[]; +} + +/** GetDiagnosticsParams are parameters for per-file diagnostic methods. */ +export interface GetDiagnosticsParams { + snapshot: number; + project: string; + files?: readonly DocumentIdentifier[]; +} + +/** DiagnosticResponse is the API response for a single diagnostic. */ +export interface DiagnosticResponse { + /** FileName is the path of the file this diagnostic belongs to, if any. */ + fileName?: string; + /** Pos is the start position of the diagnostic in the source file. */ + pos: number; + /** End is the end position of the diagnostic in the source file. */ + end: number; + /** StartPosition is the zero-based line and UTF-16 character position of Pos. */ + startPosition?: DiagnosticPositionResponse; + /** EndPosition is the zero-based line and UTF-16 character position of End. */ + endPosition?: DiagnosticPositionResponse; + /** SourceLines contains the source lines needed to render this diagnostic with context. */ + sourceLines?: DiagnosticSourceLineResponse[]; + /** Code is the diagnostic error code. */ + code: number; + /** Category is the diagnostic category (error, warning, suggestion, message). */ + category: number; + /** Source is a custom diagnostic-code prefix. An empty value uses the default "TS". */ + source?: string; + /** Text is the localized diagnostic message text. */ + text: string; + /** ReportsUnnecessary indicates this diagnostic highlights unnecessary code. */ + reportsUnnecessary?: boolean; + /** ReportsDeprecated indicates this diagnostic highlights deprecated code. */ + reportsDeprecated?: boolean; + /** MessageChain contains chained diagnostic messages, if any. */ + messageChain?: DiagnosticResponse[]; + /** RelatedInformation contains related diagnostic information, if any. */ + relatedInformation?: DiagnosticResponse[]; +} + +/** PrintNodeParams are the parameters for the printNode method. */ +export interface PrintNodeParams { + /** base64-encoded binary AST data */ + data: string; + preserveSourceNewlines?: boolean; + neverAsciiEscape?: boolean; + terminateUnterminatedLiterals?: boolean; +} + +/** FormatNodeForInsertionParams are the parameters for the formatNodeForInsertion method. */ +export interface FormatNodeForInsertionParams { + snapshot: number; + project: string; + /** target file where the node will be inserted */ + file: DocumentIdentifier; + /** UTF-16 code-unit offset of the insertion position in the target file */ + position: number; + /** base64-encoded binary AST data for the synthesized node */ + data: string; +} + +export interface EmitParams { + snapshot: number; + project: string; + emitOnly?: number; +} + +export interface EmitResponse { + emitSkipped: boolean; + diagnostics: DiagnosticResponse[]; + emittedFiles: string[]; +} + +export interface EmitOutputResponse { + emitSkipped: boolean; + diagnostics: DiagnosticResponse[]; + outputFiles: EmitOutputFile[]; +} + +export interface SelectedFilesEmitParams { + snapshot: number; + project: string; + files: readonly DocumentIdentifier[] | null; +} + +/** GetIntrinsicTypeParams is used for intrinsic type getters (anyType, stringType, etc.). */ +export interface GetIntrinsicTypeParams { + snapshot: number; + project: string; +} + +/** + * WellKnownSymbolsResponse carries the handle ids of the per-checker singleton + * symbols (unknown, undefined, arguments) so the client can identify them by id + * without a round-trip on every check. + */ +export interface WellKnownSymbolsResponse { + unknown: number; + undefined: number; + arguments: number; +} + +/** + * WellKnownSignaturesResponse carries the handle id of the per-checker singleton + * unknown signature (the signature the checker yields when a call cannot be + * resolved) so the client can identify it by id without a round-trip on every check. + */ +export interface WellKnownSignaturesResponse { + unknown: number; +} + +export interface ProfileParams { + dir: string; +} + +export interface ProfileResult { + file: string; +} + +export interface BatchRequest { + method: + | "batchRequests" + | "emit" + | "emitToString" + | "formatNodeForInsertion" + | "getAliasSymbolOfType" + | "getAliasTypeArgumentsOfType" + | "getAliasedSymbol" + | "getAnyType" + | "getApparentPropertiesOfType" + | "getApparentType" + | "getBaseConstraintOfType" + | "getBaseTypeOfLiteralType" + | "getBaseTypeOfType" + | "getBaseTypes" + | "getBigIntType" + | "getBindDiagnostics" + | "getBooleanType" + | "getCheckTypeOfType" + | "getCompletionsAtPosition" + | "getConfigFileNames" + | "getConfigFileParsingDiagnostics" + | "getConfigSourceFile" + | "getConstantValue" + | "getConstraintOfType" + | "getConstraintOfTypeParameter" + | "getContextualType" + | "getDeclarationDiagnostics" + | "getDeclarationEmit" + | "getDeclaredTypeOfSymbol" + | "getDefaultFromTypeParameter" + | "getDefaultProjectForFile" + | "getDocumentationComment" + | "getESSymbolType" + | "getExportSpecifierLocalTargetSymbol" + | "getExportSymbolOfSymbol" + | "getExportsOfModule" + | "getExportsOfSymbol" + | "getExtendsTypeOfType" + | "getFalseTypeOfConditionalType" + | "getFreshTypeOfType" + | "getFullyQualifiedName" + | "getGlobalDiagnostics" + | "getImmediateAliasedSymbol" + | "getImportAdderEdits" + | "getIndexInfosOfType" + | "getIndexTypeOfType" + | "getJavaScriptEmit" + | "getJsDocTags" + | "getLocalTypeParametersOfType" + | "getMemberInModuleExports" + | "getMembersOfSymbol" + | "getNeverType" + | "getNonMissingTypeOfSymbol" + | "getNonNullableType" + | "getNonPrimitiveType" + | "getNullType" + | "getNumberType" + | "getObjectTypeOfType" + | "getOuterTypeParametersOfType" + | "getParameterType" + | "getParametersOfSignature" + | "getParentOfSymbol" + | "getProgramDiagnostics" + | "getPropertiesOfType" + | "getPropertyOfType" + | "getReducedType" + | "getReferencedSymbolsForNode" + | "getReferencesToSymbolInFile" + | "getRegularTypeOfType" + | "getResolvedSignature" + | "getRestTypeOfSignature" + | "getReturnTypeOfSignature" + | "getSemanticDiagnostics" + | "getShorthandAssignmentValueSymbol" + | "getSignatureFromDeclaration" + | "getSignatureUsages" + | "getSignaturesOfType" + | "getSourceFile" + | "getSourceFileMetadata" + | "getSourceFileNames" + | "getStringType" + | "getSuggestionDiagnostics" + | "getSymbolAtLocation" + | "getSymbolAtPosition" + | "getSymbolOfSourceFile" + | "getSymbolOfType" + | "getSymbolsAtLocations" + | "getSymbolsAtPositions" + | "getSymbolsInScope" + | "getSymbolsOfSourceFiles" + | "getSyntacticDiagnostics" + | "getTargetOfSignature" + | "getTargetOfType" + | "getTargetSymbol" + | "getThisParameterOfSignature" + | "getTrueTypeOfConditionalType" + | "getTypeArguments" + | "getTypeAtLocation" + | "getTypeAtLocations" + | "getTypeAtPosition" + | "getTypeFromTypeNode" + | "getTypeOfSymbol" + | "getTypeOfSymbolAtLocation" + | "getTypeParameterAtPosition" + | "getTypeParametersOfSignature" + | "getTypeParametersOfType" + | "getTypePredicateOfSignature" + | "getTypesAtPositions" + | "getTypesOfSymbols" + | "getTypesOfType" + | "getUndefinedType" + | "getUnknownType" + | "getVoidType" + | "getWellKnownSignatures" + | "getWellKnownSymbols" + | "getWidenedType" + | "initialize" + | "isArrayLikeType" + | "isArrayType" + | "isContextSensitive" + | "isReadonlySymbol" + | "isTupleType" + | "isTypeAssignableTo" + | "parseCommandLine" + | "parseConfigFile" + | "parseJsonConfigFileContent" + | "printNode" + | "readConfigFile" + | "release" + | "resolveName" + | "saveHeapProfile" + | "signatureToSignatureDeclaration" + | "startCPUProfile" + | "stopCPUProfile" + | "transpileDeclaration" + | "transpileDeclarationFromFile" + | "transpileModule" + | "transpileModuleFromFile" + | "typeToString" + | "typeToTypeNode" + | "updateSnapshot" + | "updateTemporarySnapshot"; + params?: unknown; +} + +export interface BatchResponse { + method: + | "batchRequests" + | "emit" + | "emitToString" + | "formatNodeForInsertion" + | "getAliasSymbolOfType" + | "getAliasTypeArgumentsOfType" + | "getAliasedSymbol" + | "getAnyType" + | "getApparentPropertiesOfType" + | "getApparentType" + | "getBaseConstraintOfType" + | "getBaseTypeOfLiteralType" + | "getBaseTypeOfType" + | "getBaseTypes" + | "getBigIntType" + | "getBindDiagnostics" + | "getBooleanType" + | "getCheckTypeOfType" + | "getCompletionsAtPosition" + | "getConfigFileNames" + | "getConfigFileParsingDiagnostics" + | "getConfigSourceFile" + | "getConstantValue" + | "getConstraintOfType" + | "getConstraintOfTypeParameter" + | "getContextualType" + | "getDeclarationDiagnostics" + | "getDeclarationEmit" + | "getDeclaredTypeOfSymbol" + | "getDefaultFromTypeParameter" + | "getDefaultProjectForFile" + | "getDocumentationComment" + | "getESSymbolType" + | "getExportSpecifierLocalTargetSymbol" + | "getExportSymbolOfSymbol" + | "getExportsOfModule" + | "getExportsOfSymbol" + | "getExtendsTypeOfType" + | "getFalseTypeOfConditionalType" + | "getFreshTypeOfType" + | "getFullyQualifiedName" + | "getGlobalDiagnostics" + | "getImmediateAliasedSymbol" + | "getImportAdderEdits" + | "getIndexInfosOfType" + | "getIndexTypeOfType" + | "getJavaScriptEmit" + | "getJsDocTags" + | "getLocalTypeParametersOfType" + | "getMemberInModuleExports" + | "getMembersOfSymbol" + | "getNeverType" + | "getNonMissingTypeOfSymbol" + | "getNonNullableType" + | "getNonPrimitiveType" + | "getNullType" + | "getNumberType" + | "getObjectTypeOfType" + | "getOuterTypeParametersOfType" + | "getParameterType" + | "getParametersOfSignature" + | "getParentOfSymbol" + | "getProgramDiagnostics" + | "getPropertiesOfType" + | "getPropertyOfType" + | "getReducedType" + | "getReferencedSymbolsForNode" + | "getReferencesToSymbolInFile" + | "getRegularTypeOfType" + | "getResolvedSignature" + | "getRestTypeOfSignature" + | "getReturnTypeOfSignature" + | "getSemanticDiagnostics" + | "getShorthandAssignmentValueSymbol" + | "getSignatureFromDeclaration" + | "getSignatureUsages" + | "getSignaturesOfType" + | "getSourceFile" + | "getSourceFileMetadata" + | "getSourceFileNames" + | "getStringType" + | "getSuggestionDiagnostics" + | "getSymbolAtLocation" + | "getSymbolAtPosition" + | "getSymbolOfSourceFile" + | "getSymbolOfType" + | "getSymbolsAtLocations" + | "getSymbolsAtPositions" + | "getSymbolsInScope" + | "getSymbolsOfSourceFiles" + | "getSyntacticDiagnostics" + | "getTargetOfSignature" + | "getTargetOfType" + | "getTargetSymbol" + | "getThisParameterOfSignature" + | "getTrueTypeOfConditionalType" + | "getTypeArguments" + | "getTypeAtLocation" + | "getTypeAtLocations" + | "getTypeAtPosition" + | "getTypeFromTypeNode" + | "getTypeOfSymbol" + | "getTypeOfSymbolAtLocation" + | "getTypeParameterAtPosition" + | "getTypeParametersOfSignature" + | "getTypeParametersOfType" + | "getTypePredicateOfSignature" + | "getTypesAtPositions" + | "getTypesOfSymbols" + | "getTypesOfType" + | "getUndefinedType" + | "getUnknownType" + | "getVoidType" + | "getWellKnownSignatures" + | "getWellKnownSymbols" + | "getWidenedType" + | "initialize" + | "isArrayLikeType" + | "isArrayType" + | "isContextSensitive" + | "isReadonlySymbol" + | "isTupleType" + | "isTypeAssignableTo" + | "parseCommandLine" + | "parseConfigFile" + | "parseJsonConfigFileContent" + | "printNode" + | "readConfigFile" + | "release" + | "resolveName" + | "saveHeapProfile" + | "signatureToSignatureDeclaration" + | "startCPUProfile" + | "stopCPUProfile" + | "transpileDeclaration" + | "transpileDeclarationFromFile" + | "transpileModule" + | "transpileModuleFromFile" + | "typeToString" + | "typeToTypeNode" + | "updateSnapshot" + | "updateTemporarySnapshot"; + result: unknown; + error?: string; +} + +/** + * APIFileChanges describes file changes to apply when updating a snapshot. + * Either InvalidateAll is true (discard all caches) or Changed/Created/Deleted + * list individual documents. + */ +export interface APIFileChanges { + invalidateAll?: boolean; + changed?: DocumentIdentifier[]; + created?: DocumentIdentifier[]; + deleted?: DocumentIdentifier[]; +} + +/** + * SnapshotChanges describes what changed between the previous latest snapshot + * and the newly created snapshot. Changes are reported per-project so clients + * can track cache refs at the (snapshot, project) level. + */ +export interface SnapshotChanges { + /** + * ChangedProjects maps project handles to the file changes within that project. + * Projects not listed here (and not in RemovedProjects) are unchanged. + */ + changedProjects?: Record; + /** + * RemovedProjects lists project handles that were present in the previous + * snapshot but absent from the new one. + */ + removedProjects?: string[]; +} + +/** CompilerOptions contains the compiler options exposed by the API. */ +export interface CompilerOptions { + allowJs?: boolean; + allowArbitraryExtensions?: boolean; + allowImportingTsExtensions?: boolean; + allowNonTsExtensions?: boolean; + allowUmdGlobalAccess?: boolean; + allowUnreachableCode?: boolean; + allowUnusedLabels?: boolean; + assumeChangesOnlyAffectDirectDependencies?: boolean; + checkJs?: boolean; + customConditions?: string[]; + composite?: boolean; + emitDeclarationOnly?: boolean; + emitBOM?: boolean; + emitDecoratorMetadata?: boolean; + declaration?: boolean; + declarationDir?: string; + declarationMap?: boolean; + deduplicatePackages?: boolean; + disableSizeLimit?: boolean; + disableSourceOfProjectReferenceRedirect?: boolean; + disableSolutionSearching?: boolean; + disableReferencedProjectLoad?: boolean; + erasableSyntaxOnly?: boolean; + exactOptionalPropertyTypes?: boolean; + experimentalDecorators?: boolean; + forceConsistentCasingInFileNames?: boolean; + isolatedModules?: boolean; + isolatedDeclarations?: boolean; + ignoreConfig?: boolean; + ignoreDeprecations?: string; + importHelpers?: boolean; + inlineSourceMap?: boolean; + inlineSources?: boolean; + init?: boolean; + incremental?: boolean; + jsx?: JsxEmit; + jsxFactory?: string; + jsxFragmentFactory?: string; + jsxImportSource?: string; + lib?: string[]; + libReplacement?: boolean; + locale?: string; + mapRoot?: string; + module?: ModuleKind; + moduleResolution?: ModuleResolutionKind; + moduleSuffixes?: string[]; + moduleDetection?: ModuleDetectionKind; + newLine?: NewLineKind; + noEmit?: boolean; + noCheck?: boolean; + noErrorTruncation?: boolean; + noFallthroughCasesInSwitch?: boolean; + noImplicitAny?: boolean; + noImplicitThis?: boolean; + noImplicitReturns?: boolean; + noEmitHelpers?: boolean; + noLib?: boolean; + noPropertyAccessFromIndexSignature?: boolean; + noUncheckedIndexedAccess?: boolean; + noEmitOnError?: boolean; + noUnusedLocals?: boolean; + noUnusedParameters?: boolean; + noResolve?: boolean; + noImplicitOverride?: boolean; + noUncheckedSideEffectImports?: boolean; + outDir?: string; + paths?: Record; + preserveConstEnums?: boolean; + preserveSymlinks?: boolean; + project?: string; + resolveJsonModule?: boolean; + resolvePackageJsonExports?: boolean; + resolvePackageJsonImports?: boolean; + removeComments?: boolean; + rewriteRelativeImportExtensions?: boolean; + reactNamespace?: string; + rootDir?: string; + rootDirs?: string[]; + skipLibCheck?: boolean; + stableTypeOrdering?: boolean; + strict?: boolean; + strictBindCallApply?: boolean; + strictBuiltinIteratorReturn?: boolean; + strictFunctionTypes?: boolean; + strictNullChecks?: boolean; + strictPropertyInitialization?: boolean; + stripInternal?: boolean; + skipDefaultLibCheck?: boolean; + sourceMap?: boolean; + sourceRoot?: string; + suppressOutputPathCheck?: boolean; + target?: ScriptTarget; + traceResolution?: boolean; + tsBuildInfoFile?: string; + typeRoots?: string[]; + types?: string[]; + useDefineForClassFields?: boolean; + useUnknownInCatchVariables?: boolean; + verbatimModuleSyntax?: boolean; + maxNodeModuleJsDepth?: number; + /** Internal fields */ + configFilePath?: string; +} + +export interface ProjectReference { + /** Path is a normalized path on disk. */ + path: string; + /** OriginalPath is the path as it was originally written. */ + originalPath: string; + /** Circular indicates that this reference is intended to form a circularity. */ + circular: boolean; +} + +export interface TypeAcquisition { + enable?: boolean; + include?: string[]; + exclude?: string[]; + disableFilenameBasedTypeAcquisition?: boolean; +} + +export interface TranspileOptions { + compilerOptions?: CompilerOptions; + fileName?: string; + reportDiagnostics?: boolean; +} + +export interface ImportAdderAction { + kind: "importSymbol"; + symbol?: number; + isValidTypeOnlyUseSite?: boolean; +} + +/** CompletionEntryResponse represents a single completion item. */ +export interface CompletionEntryResponse { + name: string; + kind?: number; + sortText?: string; + insertText?: string; + filterText?: string; + detail?: string; + labelDetails?: CompletionEntryLabelDetailsResponse; + symbol?: SymbolResponse; +} + +export interface DiagnosticPositionResponse { + line: number; + character: number; +} + +export interface DiagnosticSourceLineResponse { + line: number; + text: string; +} + +export interface EmitOutputFile { + fileName: string; + text: string; + sourceFileName?: string; +} + +/** ProjectFileChanges describes what source files changed within a single project. */ +export interface ProjectFileChanges { + /** ChangedFiles lists source file paths whose content differs. */ + changedFiles?: string[]; + /** DeletedFiles lists source file paths removed from the project's program. */ + deletedFiles?: string[]; +} + +/** CompletionEntryLabelDetailsResponse holds additional label display text for a completion entry. */ +export interface CompletionEntryLabelDetailsResponse { + detail?: string; + description?: string; +} diff --git a/packages/typescript/src/api/proto.ts b/packages/typescript/src/api/proto.ts index 6c29b4c3a4a13..86d84d0ead47f 100644 --- a/packages/typescript/src/api/proto.ts +++ b/packages/typescript/src/api/proto.ts @@ -1,113 +1,113 @@ -import { - documentURIToFileName, - fileNameToDocumentURI, -} from "./path.ts"; -import type { - APIMethodInfo, - DocumentIdentifier, - SignatureResponse, - SourceFileResponse, - SymbolResponse, - TypeResponse, - UpdateSnapshotParams as CoreUpdateSnapshotParams, -} from "./proto.generated.ts"; -export type { ConfigFileResponse as ParsedCommandLine, DiagnosticResponse as Diagnostic } from "./proto.generated.ts"; - -export * from "./proto.generated.ts"; - -export type APIMethodsReturning = { [K in keyof APIMethodInfo]: [T] extends [NonNullable] ? [NonNullable] extends [T] ? K : never : never; }[keyof APIMethodInfo]; - -export type SourceFileResponseMethod = APIMethodsReturning; -export type SymbolPropertyMethod = APIMethodsReturning; -export type SymbolsPropertyMethod = APIMethodsReturning; -export type SignaturePropertyMethod = APIMethodsReturning; -export type TypePropertyMethod = Exclude, IntrinsicTypeMethod>; -export type TypesPropertyMethod = APIMethodsReturning; -export type IntrinsicTypeMethod = "getAnyType" | "getBigIntType" | "getBooleanType" | "getESSymbolType" | "getNeverType" | "getNonPrimitiveType" | "getNullType" | "getNumberType" | "getStringType" | "getUndefinedType" | "getUnknownType" | "getVoidType"; - -export type APIRequest = { [K in keyof APIMethodInfo]: { method: K; params: APIMethodInfo[K]["params"]; }; }[keyof APIMethodInfo]; -export type APIResponse = Request extends APIRequest ? - & { - method: Request["method"]; - } - & ({ - result: APIMethodInfo[Request["method"]]["result"]; - error?: undefined; - } | { - result: null; - error: string; - }) : - never; -export type APIResponseTuple = { - [Index in keyof Requests]: APIResponse; -}; - -/** - * A position within a document, combining a document identifier with an offset. - */ -export interface DocumentPosition { - /** The document containing the position */ - document: DocumentIdentifier; - /** The character offset within the document */ - position: number; -} - -/** - * Resolves a DocumentIdentifier to a file name. - * If the identifier contains a URI, it is converted to a file name. - */ -export function resolveFileName(identifier: DocumentIdentifier): string { - if (typeof identifier === "string") { - return identifier; - } - return documentURIToFileName(identifier.uri); -} - -/** - * Resolves a DocumentIdentifier to a document URI. - * If the identifier contains a file name, it is converted to a URI. - */ -export function resolveDocumentURI(identifier: DocumentIdentifier): string { - if (typeof identifier === "string") { - return fileNameToDocumentURI(identifier); - } - return identifier.uri; -} - -export interface LSPUpdateSnapshotParams extends CoreUpdateSnapshotParams { - /** - * @deprecated Use {@link openProjects} instead. - * Path to a tsconfig.json file to open in the new snapshot. - */ - openProject?: string; - - /** FileChanges are not supplied by the LSP */ - fileChanges?: never; -} - -/** - * Parameters for updateSnapshot, including deprecated members handled by `toUpdateSnapshotRequest` - */ -export interface UpdateSnapshotParams extends CoreUpdateSnapshotParams { - /** - * @deprecated Use {@link openProjects} instead. - * Path to a tsconfig.json file to open in the new snapshot. - */ - openProject?: string; -} - -/** - * Builds the wire request for updateSnapshot, applying the deprecated `openProject` - * compatibility shim: a single `openProject` is folded into `openProjects` and is - * never sent on the wire. - */ -export function toUpdateSnapshotRequest(params?: UpdateSnapshotParams): UpdateSnapshotParams { - const { openProject, openProjects, ...rest } = params ?? {}; - const mergedOpenProjects = openProject !== undefined - ? [resolveFileName(openProject), ...(openProjects ?? [])] - : openProjects; - return { - ...rest, - ...(mergedOpenProjects !== undefined ? { openProjects: mergedOpenProjects } : {}), - }; -} +import { + documentURIToFileName, + fileNameToDocumentURI, +} from "./path.ts"; +import type { + APIMethodInfo, + DocumentIdentifier, + SignatureResponse, + SourceFileResponse, + SymbolResponse, + TypeResponse, + UpdateSnapshotParams as CoreUpdateSnapshotParams, +} from "./proto.generated.ts"; +export type { ConfigFileResponse as ParsedCommandLine, DiagnosticResponse as Diagnostic } from "./proto.generated.ts"; + +export * from "./proto.generated.ts"; + +export type APIMethodsReturning = { [K in keyof APIMethodInfo]: [T] extends [NonNullable] ? [NonNullable] extends [T] ? K : never : never; }[keyof APIMethodInfo]; + +export type SourceFileResponseMethod = APIMethodsReturning; +export type SymbolPropertyMethod = APIMethodsReturning; +export type SymbolsPropertyMethod = APIMethodsReturning; +export type SignaturePropertyMethod = APIMethodsReturning; +export type TypePropertyMethod = Exclude, IntrinsicTypeMethod>; +export type TypesPropertyMethod = APIMethodsReturning; +export type IntrinsicTypeMethod = "getAnyType" | "getBigIntType" | "getBooleanType" | "getESSymbolType" | "getNeverType" | "getNonPrimitiveType" | "getNullType" | "getNumberType" | "getStringType" | "getUndefinedType" | "getUnknownType" | "getVoidType"; + +export type APIRequest = { [K in keyof APIMethodInfo]: { method: K; params: APIMethodInfo[K]["params"]; }; }[keyof APIMethodInfo]; +export type APIResponse = Request extends APIRequest ? + & { + method: Request["method"]; + } + & ({ + result: APIMethodInfo[Request["method"]]["result"]; + error?: undefined; + } | { + result: null; + error: string; + }) : + never; +export type APIResponseTuple = { + [Index in keyof Requests]: APIResponse; +}; + +/** + * A position within a document, combining a document identifier with an offset. + */ +export interface DocumentPosition { + /** The document containing the position */ + document: DocumentIdentifier; + /** The character offset within the document */ + position: number; +} + +/** + * Resolves a DocumentIdentifier to a file name. + * If the identifier contains a URI, it is converted to a file name. + */ +export function resolveFileName(identifier: DocumentIdentifier): string { + if (typeof identifier === "string") { + return identifier; + } + return documentURIToFileName(identifier.uri); +} + +/** + * Resolves a DocumentIdentifier to a document URI. + * If the identifier contains a file name, it is converted to a URI. + */ +export function resolveDocumentURI(identifier: DocumentIdentifier): string { + if (typeof identifier === "string") { + return fileNameToDocumentURI(identifier); + } + return identifier.uri; +} + +export interface LSPUpdateSnapshotParams extends CoreUpdateSnapshotParams { + /** + * @deprecated Use {@link openProjects} instead. + * Path to a tsconfig.json file to open in the new snapshot. + */ + openProject?: string; + + /** FileChanges are not supplied by the LSP */ + fileChanges?: never; +} + +/** + * Parameters for updateSnapshot, including deprecated members handled by `toUpdateSnapshotRequest` + */ +export interface UpdateSnapshotParams extends CoreUpdateSnapshotParams { + /** + * @deprecated Use {@link openProjects} instead. + * Path to a tsconfig.json file to open in the new snapshot. + */ + openProject?: string; +} + +/** + * Builds the wire request for updateSnapshot, applying the deprecated `openProject` + * compatibility shim: a single `openProject` is folded into `openProjects` and is + * never sent on the wire. + */ +export function toUpdateSnapshotRequest(params?: UpdateSnapshotParams): UpdateSnapshotParams { + const { openProject, openProjects, ...rest } = params ?? {}; + const mergedOpenProjects = openProject !== undefined + ? [resolveFileName(openProject), ...(openProjects ?? [])] + : openProjects; + return { + ...rest, + ...(mergedOpenProjects !== undefined ? { openProjects: mergedOpenProjects } : {}), + }; +} diff --git a/packages/typescript/src/api/sourceFileCache.ts b/packages/typescript/src/api/sourceFileCache.ts index b4ee651c5e1e8..de7934ec07340 100644 --- a/packages/typescript/src/api/sourceFileCache.ts +++ b/packages/typescript/src/api/sourceFileCache.ts @@ -1,193 +1,193 @@ -import type { - Path, - SourceFile, -} from "../ast/index.ts"; -import type { SnapshotChanges } from "./proto.ts"; - -/** - * Builds a composite ref key from a snapshot ID and project ID. - */ -function refKey(snapshotId: number, projectId: string): string { - return `${snapshotId}:${projectId}`; -} - -/** - * A cached source file entry, identified by content hash. - */ -export interface CachedSourceFile { - /** The cached source file object */ - file: SourceFile; - /** The content hash from the server */ - contentHash: string; - /** The parse options key that was used to create this file */ - parseOptionsKey: string; - /** Set of (snapshot, project) ref keys that reference this entry */ - refs: Set; -} - -/** - * Client-side cache for source files keyed by (path, parseOptionsKey, contentHash). - * - * Supports multiple versions of the same file at the same path (e.g., from - * different snapshots with different file contents). Each version is identified - * by its content hash and parse options key. - * - * Entries are ref-counted by (snapshot, project) pairs. When a snapshot is - * disposed, all refs for that snapshot across all projects are released, - * and entries with no remaining references are evicted. - * - * When a new snapshot is created, unchanged cache entries from the previous - * snapshot are retained per-project. Only files within changed or removed - * projects are invalidated. - */ -export class SourceFileCache { - /** Map from path to all cached versions of that file */ - private cache: Map = new Map(); - /** Map from snapshotId to (projectId → Set of paths fetched through that project) */ - private snapshotProjectPaths: Map>> = new Map(); - - /** - * Get a cached source file already retained for the given (snapshot, project) pair. - * This does not require a content hash or parse options key — it returns the entry - * if one exists with a matching ref. Used to skip the server request entirely when - * retainForSnapshot has already carried over the ref. - * - * A given (snapshot, project) pair always parses a file the same way, so there is - * at most one matching entry per ref. - */ - getRetained(path: Path, snapshotId: number, projectId: string): SourceFile | undefined { - const entries = this.cache.get(path); - if (!entries) return undefined; - const key = refKey(snapshotId, projectId); - const entry = entries.find(e => e.refs.has(key)); - return entry?.file; - } - - /** - * Store a source file in the cache and retain it for the given (snapshot, project) pair. - * Returns the cached file — which may be an existing entry if the hash matches. - */ - set(path: Path, file: SourceFile, parseOptionsKey: string, contentHash: string, snapshotId: number, projectId: string): SourceFile { - let entries = this.cache.get(path); - if (!entries) { - entries = []; - this.cache.set(path, entries); - } - const ref = refKey(snapshotId, projectId); - // Check if we already have this exact version - const existing = entries.find(e => e.parseOptionsKey === parseOptionsKey && e.contentHash === contentHash); - if (existing) { - existing.refs.add(ref); - this.trackPath(snapshotId, projectId, path); - return existing.file; - } - entries.push({ file, contentHash, parseOptionsKey, refs: new Set([ref]) }); - this.trackPath(snapshotId, projectId, path); - return file; - } - - /** - * Retain cache entries from a previous snapshot for a new snapshot. - * For each project in the previous snapshot: - * - Removed projects: skip (don't retain any refs). - * - Changed projects: retain refs for files not listed in changedFiles/deletedFiles. - * - Unchanged projects: retain all refs. - */ - retainForSnapshot(newSnapshotId: number, previousSnapshotId: number, changes: SnapshotChanges | undefined): void { - const prevProjectMap = this.snapshotProjectPaths.get(previousSnapshotId); - if (!prevProjectMap) return; - - const removedProjects = new Set(changes?.removedProjects ?? []); - const changedProjects = changes?.changedProjects ?? {}; - - for (const [projectId, paths] of prevProjectMap) { - if (removedProjects.has(projectId)) continue; - - const projectChanges = changedProjects[projectId]; - let invalidPaths: Set | undefined; - if (projectChanges) { - invalidPaths = new Set(); - for (const p of projectChanges.changedFiles ?? []) invalidPaths.add(p); - for (const p of projectChanges.deletedFiles ?? []) invalidPaths.add(p); - } - - const prevRef = refKey(previousSnapshotId, projectId); - const newRef = refKey(newSnapshotId, projectId); - - for (const path of paths) { - if (invalidPaths?.has(path)) continue; - const entries = this.cache.get(path); - if (!entries) continue; - for (const entry of entries) { - if (entry.refs.has(prevRef)) { - entry.refs.add(newRef); - this.trackPath(newSnapshotId, projectId, path); - } - } - } - } - } - - /** - * Release all entries retained by the given snapshot across all projects. - * Only visits paths that the snapshot actually referenced. - * Entries with no remaining refs are evicted. - */ - releaseSnapshot(snapshotId: number): void { - const projectMap = this.snapshotProjectPaths.get(snapshotId); - if (!projectMap) return; - for (const [projectId, paths] of projectMap) { - const key = refKey(snapshotId, projectId); - for (const path of paths) { - const entries = this.cache.get(path); - if (!entries) continue; - for (let i = entries.length - 1; i >= 0; i--) { - entries[i].refs.delete(key); - if (entries[i].refs.size === 0) { - entries.splice(i, 1); - } - } - if (entries.length === 0) { - this.cache.delete(path); - } - } - } - this.snapshotProjectPaths.delete(snapshotId); - } - - private trackPath(snapshotId: number, projectId: string, path: Path): void { - let projectMap = this.snapshotProjectPaths.get(snapshotId); - if (!projectMap) { - projectMap = new Map(); - this.snapshotProjectPaths.set(snapshotId, projectMap); - } - let paths = projectMap.get(projectId); - if (!paths) { - paths = new Set(); - projectMap.set(projectId, paths); - } - paths.add(path); - } - - /** - * Clear all entries from the cache. - */ - clear(): void { - this.cache.clear(); - this.snapshotProjectPaths.clear(); - } - - /** - * Get the number of unique paths in the cache. - */ - get size(): number { - return this.cache.size; - } - - /** - * Check if a path is in the cache. - */ - has(path: Path): boolean { - return this.cache.has(path); - } -} +import type { + Path, + SourceFile, +} from "../ast/index.ts"; +import type { SnapshotChanges } from "./proto.ts"; + +/** + * Builds a composite ref key from a snapshot ID and project ID. + */ +function refKey(snapshotId: number, projectId: string): string { + return `${snapshotId}:${projectId}`; +} + +/** + * A cached source file entry, identified by content hash. + */ +export interface CachedSourceFile { + /** The cached source file object */ + file: SourceFile; + /** The content hash from the server */ + contentHash: string; + /** The parse options key that was used to create this file */ + parseOptionsKey: string; + /** Set of (snapshot, project) ref keys that reference this entry */ + refs: Set; +} + +/** + * Client-side cache for source files keyed by (path, parseOptionsKey, contentHash). + * + * Supports multiple versions of the same file at the same path (e.g., from + * different snapshots with different file contents). Each version is identified + * by its content hash and parse options key. + * + * Entries are ref-counted by (snapshot, project) pairs. When a snapshot is + * disposed, all refs for that snapshot across all projects are released, + * and entries with no remaining references are evicted. + * + * When a new snapshot is created, unchanged cache entries from the previous + * snapshot are retained per-project. Only files within changed or removed + * projects are invalidated. + */ +export class SourceFileCache { + /** Map from path to all cached versions of that file */ + private cache: Map = new Map(); + /** Map from snapshotId to (projectId → Set of paths fetched through that project) */ + private snapshotProjectPaths: Map>> = new Map(); + + /** + * Get a cached source file already retained for the given (snapshot, project) pair. + * This does not require a content hash or parse options key — it returns the entry + * if one exists with a matching ref. Used to skip the server request entirely when + * retainForSnapshot has already carried over the ref. + * + * A given (snapshot, project) pair always parses a file the same way, so there is + * at most one matching entry per ref. + */ + getRetained(path: Path, snapshotId: number, projectId: string): SourceFile | undefined { + const entries = this.cache.get(path); + if (!entries) return undefined; + const key = refKey(snapshotId, projectId); + const entry = entries.find(e => e.refs.has(key)); + return entry?.file; + } + + /** + * Store a source file in the cache and retain it for the given (snapshot, project) pair. + * Returns the cached file — which may be an existing entry if the hash matches. + */ + set(path: Path, file: SourceFile, parseOptionsKey: string, contentHash: string, snapshotId: number, projectId: string): SourceFile { + let entries = this.cache.get(path); + if (!entries) { + entries = []; + this.cache.set(path, entries); + } + const ref = refKey(snapshotId, projectId); + // Check if we already have this exact version + const existing = entries.find(e => e.parseOptionsKey === parseOptionsKey && e.contentHash === contentHash); + if (existing) { + existing.refs.add(ref); + this.trackPath(snapshotId, projectId, path); + return existing.file; + } + entries.push({ file, contentHash, parseOptionsKey, refs: new Set([ref]) }); + this.trackPath(snapshotId, projectId, path); + return file; + } + + /** + * Retain cache entries from a previous snapshot for a new snapshot. + * For each project in the previous snapshot: + * - Removed projects: skip (don't retain any refs). + * - Changed projects: retain refs for files not listed in changedFiles/deletedFiles. + * - Unchanged projects: retain all refs. + */ + retainForSnapshot(newSnapshotId: number, previousSnapshotId: number, changes: SnapshotChanges | undefined): void { + const prevProjectMap = this.snapshotProjectPaths.get(previousSnapshotId); + if (!prevProjectMap) return; + + const removedProjects = new Set(changes?.removedProjects ?? []); + const changedProjects = changes?.changedProjects ?? {}; + + for (const [projectId, paths] of prevProjectMap) { + if (removedProjects.has(projectId)) continue; + + const projectChanges = changedProjects[projectId]; + let invalidPaths: Set | undefined; + if (projectChanges) { + invalidPaths = new Set(); + for (const p of projectChanges.changedFiles ?? []) invalidPaths.add(p); + for (const p of projectChanges.deletedFiles ?? []) invalidPaths.add(p); + } + + const prevRef = refKey(previousSnapshotId, projectId); + const newRef = refKey(newSnapshotId, projectId); + + for (const path of paths) { + if (invalidPaths?.has(path)) continue; + const entries = this.cache.get(path); + if (!entries) continue; + for (const entry of entries) { + if (entry.refs.has(prevRef)) { + entry.refs.add(newRef); + this.trackPath(newSnapshotId, projectId, path); + } + } + } + } + } + + /** + * Release all entries retained by the given snapshot across all projects. + * Only visits paths that the snapshot actually referenced. + * Entries with no remaining refs are evicted. + */ + releaseSnapshot(snapshotId: number): void { + const projectMap = this.snapshotProjectPaths.get(snapshotId); + if (!projectMap) return; + for (const [projectId, paths] of projectMap) { + const key = refKey(snapshotId, projectId); + for (const path of paths) { + const entries = this.cache.get(path); + if (!entries) continue; + for (let i = entries.length - 1; i >= 0; i--) { + entries[i].refs.delete(key); + if (entries[i].refs.size === 0) { + entries.splice(i, 1); + } + } + if (entries.length === 0) { + this.cache.delete(path); + } + } + } + this.snapshotProjectPaths.delete(snapshotId); + } + + private trackPath(snapshotId: number, projectId: string, path: Path): void { + let projectMap = this.snapshotProjectPaths.get(snapshotId); + if (!projectMap) { + projectMap = new Map(); + this.snapshotProjectPaths.set(snapshotId, projectMap); + } + let paths = projectMap.get(projectId); + if (!paths) { + paths = new Set(); + projectMap.set(projectId, paths); + } + paths.add(path); + } + + /** + * Clear all entries from the cache. + */ + clear(): void { + this.cache.clear(); + this.snapshotProjectPaths.clear(); + } + + /** + * Get the number of unique paths in the cache. + */ + get size(): number { + return this.cache.size; + } + + /** + * Check if a path is in the cache. + */ + has(path: Path): boolean { + return this.cache.has(path); + } +} diff --git a/packages/typescript/src/api/sync/api.ts b/packages/typescript/src/api/sync/api.ts index b92a3fa26fb72..93563559d0284 100644 --- a/packages/typescript/src/api/sync/api.ts +++ b/packages/typescript/src/api/sync/api.ts @@ -1,6160 +1,6160 @@ -// -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// !!! THIS FILE IS AUTO-GENERATED - DO NOT EDIT !!! -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// -// Source: src/api/async/api.ts -// Regenerate: npm run generate (from packages/typescript) -// -import type { - APIRequest as ProtocolRequest, - APIResponse as ProtocolResponse, -} from "../proto.ts"; -import { - apiRequest, - cacheGeneratorMethod, -} from "./generatorSupport.ts"; - -import { CheckFlags } from "#enums/checkFlags"; -import { CompletionItemKind } from "#enums/completionItemKind"; -import { DiagnosticCategory } from "#enums/diagnosticCategory"; -import { ElementFlags } from "#enums/elementFlags"; -import { EmitOnly } from "#enums/emitOnly"; -import { ModuleKind } from "#enums/moduleKind"; -import { NewLineKind } from "#enums/newLineKind"; -import { NodeBuilderFlags } from "#enums/nodeBuilderFlags"; -import { ObjectFlags } from "#enums/objectFlags"; -import { SignatureFlags } from "#enums/signatureFlags"; -import { SignatureKind } from "#enums/signatureKind"; -import { SymbolFlags } from "#enums/symbolFlags"; -import { TypeFlags } from "#enums/typeFlags"; -import { TypeFormatFlags } from "#enums/typeFormatFlags"; -import { TypePredicateKind } from "#enums/typePredicateKind"; -import { - type __String, - type Declaration, - type Expression, - type Identifier, - type IndexSignatureDeclaration, - ModifierFlags, - type Node, - type Path, - type SourceFile, - type SyntaxKind, - type TypeNode, - unescapeLeadingUnderscores, -} from "../../ast/index.ts"; -import { assertNever } from "../../internal/utils.ts"; -import { - encodeNode, - uint8ArrayToBase64, -} from "../node/encoder.ts"; -import { - decodeNode, - getNodeId, - parseNodeHandle, - readParseOptionsKey, - readSourceFileHash, - RemoteSourceFile, -} from "../node/node.ts"; -import { Wtf8Decoder } from "../node/wtf8.ts"; -import type { - APIOptions, - LSPConnectionOptions, -} from "../options.ts"; -import { - createGetCanonicalFileName, - toPath, -} from "../path.ts"; -import type { - CompilerOptions, - Diagnostic, - DocumentIdentifier, - DocumentPosition, - EmitOutputResponse as ProtocolEmitOutputResponse, - ImportAdderAction, - IntrinsicTypeMethod, - LSPUpdateSnapshotParams, - ParsedCommandLine, - ProjectReference, - ProjectResponse, - ReadConfigFileResponse, - SignaturePropertyMethod, - SignatureResponse, - SourceFileMetadata, - SymbolPropertyMethod, - SymbolResponse, - SymbolsPropertyMethod, - TextEdit, - TypeAcquisition, - TypePropertyMethod, - TypeResponse, - TypesPropertyMethod, - UpdateSnapshotParams, - UpdateSnapshotResponse, -} from "../proto.ts"; -import { - resolveFileName, - toUpdateSnapshotRequest, -} from "../proto.ts"; -import { SourceFileCache } from "../sourceFileCache.ts"; -import type { - RequestTiming, - TimingAccumulators, - TimingInfo, -} from "../timing.ts"; -import { - Client, - type ClientSocketOptions, - type ClientSpawnOptions, -} from "./client.ts"; -import type { - AssertsIdentifierTypePredicate, - AssertsThisTypePredicate, - BigIntLiteralType, - BooleanLiteralType, - CompletionEntry, - CompletionInfo, - CompletionOptions, - ConditionalType, - EmitOutput, - EmitOutputFile, - EmitResult, - FormatDiagnosticsHost, - FreshableType, - GetImportEditsForSymbolsOptions, - IdentifierTypePredicate, - ImportAdderAction as APIImportAdderAction, - IndexedAccessType, - IndexInfo, - IndexType, - InterfaceType, - IntersectionType, - IntrinsicType, - JSDocTagInfo, - LiteralType, - NumberLiteralType, - ObjectType, - StringLiteralType, - StringMappingType, - StructuredType, - SubstitutionType, - TemplateLiteralType, - ThisTypePredicate, - TupleType, - Type, - TypeParameter, - TypePredicate, - TypePredicateBase, - TypeReference, - UnionOrIntersectionType, - UnionType, -} from "./types.ts"; - -export { formatDiagnostics, formatDiagnosticsWithColorAndContext } from "../diagnosticFormatter.ts"; -export { documentURIToFileName, fileNameToDocumentURI } from "../path.ts"; -export { CheckFlags, CompletionItemKind, DiagnosticCategory, ElementFlags, EmitOnly, ModifierFlags, ModuleKind, NodeBuilderFlags, ObjectFlags, SignatureFlags, SignatureKind, SymbolFlags, TypeFlags, TypeFormatFlags, TypePredicateKind }; -export type { - APIImportAdderAction as ImportAdderAction, - APIOptions, - AssertsIdentifierTypePredicate, - AssertsThisTypePredicate, - BigIntLiteralType, - BooleanLiteralType, - ClientSocketOptions, - ClientSpawnOptions, - CompilerOptions, - CompletionEntry, - CompletionInfo, - CompletionOptions, - ConditionalType, - Diagnostic, - DocumentIdentifier, - DocumentPosition, - EmitOutput, - EmitOutputFile, - EmitResult, - FormatDiagnosticsHost, - FreshableType, - GetImportEditsForSymbolsOptions, - IdentifierTypePredicate, - IndexedAccessType, - IndexInfo, - IndexType, - InterfaceType, - IntersectionType, - IntrinsicType, - JSDocTagInfo, - LiteralType, - LSPConnectionOptions, - NumberLiteralType, - ObjectType, - ParsedCommandLine, - ProjectReference, - ReadConfigFileResponse, - RequestTiming, - SourceFileMetadata, - StringLiteralType, - StringMappingType, - StructuredType, - SubstitutionType, - TemplateLiteralType, - TextEdit, - ThisTypePredicate, - TimingAccumulators, - TimingInfo, - TupleType, - Type, - TypeAcquisition, - TypeParameter, - TypePredicate, - TypePredicateBase, - TypeReference, - UnionOrIntersectionType, - UnionType, -}; - -export interface TranspileOptions { - compilerOptions?: CompilerOptions; - fileName?: string; - reportDiagnostics?: boolean; -} - -export interface TranspileOutput { - outputText: string; - diagnostics?: readonly Diagnostic[]; - sourceMapText?: string; -} - -export { all } from "./generatorSupport.ts"; -import { - all, - type APIRequestGenerator, - type ExecutedGeneratorsResults, -} from "./generatorSupport.ts"; - -export class API implements FormatDiagnosticsHost { - private client: Client; - private sourceFileCache: SourceFileCache; - private toPath: ((fileName: string) => Path) | undefined; - private currentDirectory: string | undefined; - private getCanonicalFileNameWorker: ((fileName: string) => string) | undefined; - private initialized: boolean = false; - private activeSnapshots: Set = new Set(); - private latestSnapshot: Snapshot | undefined; - readonly internal: InternalAPI; - - constructor(options: APIOptions | LSPConnectionOptions = {}) { - this.client = new Client(options); - this.sourceFileCache = new SourceFileCache(); - this.internal = new InternalAPI(this.client, this.ensureInitialized); - } - - /** - * Create an API instance from an existing LSP connection's API session. - * Use this when connecting to an API pipe provided by an LSP server via custom/initializeAPISession. - */ - static get fromLSPConnection(): { - (options: LSPConnectionOptions): API; - gen(options: LSPConnectionOptions): Generator, ProtocolResponse["result"]>; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "fromLSPConnection", - function (options: LSPConnectionOptions): API { - const api = new API(options); - api.ensureInitialized(); - return api; - }, - function* (options: LSPConnectionOptions): Generator, ProtocolResponse["result"]> { - const api = new API(options); - yield* api.ensureInitialized.gen(); - return api; - }, - ); - } - - batch(...requestGenerators: T): ExecutedGeneratorsResults { - const batches = all(...requestGenerators); - let state = batches.next(); - while (!state.done) { - state = batches.next(this.client.batchRequests(state.value).responses); - } - return state.value; - } - - private get ensureInitialized(): { - (): void; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "ensureInitialized", - function (): void { - if (!owner.initialized) { - const response = owner.client.apiRequest("initialize", null); - const getCanonicalFileName = createGetCanonicalFileName(response.useCaseSensitiveFileNames); - const currentDirectory = response.currentDirectory; - owner.getCanonicalFileNameWorker = getCanonicalFileName; - owner.currentDirectory = currentDirectory; - owner.toPath = (fileName: string) => toPath(fileName, currentDirectory, getCanonicalFileName) as Path; - owner.initialized = true; - } - }, - function* (): Generator { - if (!owner.initialized) { - const response = yield* apiRequest("initialize", null); - const getCanonicalFileName = createGetCanonicalFileName(response.useCaseSensitiveFileNames); - const currentDirectory = response.currentDirectory; - owner.getCanonicalFileNameWorker = getCanonicalFileName; - owner.currentDirectory = currentDirectory; - owner.toPath = (fileName: string) => toPath(fileName, currentDirectory, getCanonicalFileName) as Path; - owner.initialized = true; - } - }, - ); - } - - getCurrentDirectory(): string { - if (this.currentDirectory === undefined) { - throw new Error("API has not been initialized"); - } - return this.currentDirectory; - } - - getCanonicalFileName(fileName: string): string { - if (this.getCanonicalFileNameWorker === undefined) { - throw new Error("API has not been initialized"); - } - return this.getCanonicalFileNameWorker(fileName); - } - - getNewLine(): string { - return "\n"; - } - - get parseConfigFile(): { - (file: DocumentIdentifier): ParsedCommandLine; - gen(file: DocumentIdentifier): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "parseConfigFile", - function (file: DocumentIdentifier): ParsedCommandLine { - owner.ensureInitialized(); - return owner.client.apiRequest("parseConfigFile", { file }); - }, - function* (file: DocumentIdentifier): Generator { - yield* owner.ensureInitialized.gen(); - return yield* apiRequest("parseConfigFile", { file }); - }, - ); - } - - get parseCommandLine(): { - (commandLine: readonly string[]): ParsedCommandLine; - gen(commandLine: readonly string[]): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "parseCommandLine", - function (commandLine: readonly string[]): ParsedCommandLine { - owner.ensureInitialized(); - return owner.client.apiRequest("parseCommandLine", { commandLine }); - }, - function* (commandLine: readonly string[]): Generator { - yield* owner.ensureInitialized.gen(); - return yield* apiRequest("parseCommandLine", { commandLine }); - }, - ); - } - - get readConfigFile(): { - (file: DocumentIdentifier): ReadConfigFileResponse; - gen(file: DocumentIdentifier): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "readConfigFile", - function (file: DocumentIdentifier): ReadConfigFileResponse { - owner.ensureInitialized(); - return owner.client.apiRequest("readConfigFile", { file }); - }, - function* (file: DocumentIdentifier): Generator { - yield* owner.ensureInitialized.gen(); - return yield* apiRequest("readConfigFile", { file }); - }, - ); - } - - get parseJsonConfigFileContent(): { - ( - json: any, - options: - | { configDirectory: string; configFileName?: never; } - | { configFileName: DocumentIdentifier; configDirectory?: never; }, - ): ParsedCommandLine; - gen( - json: any, - options: - | { configDirectory: string; configFileName?: never; } - | { configFileName: DocumentIdentifier; configDirectory?: never; }, - ): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "parseJsonConfigFileContent", - function ( - json: any, - options: - | { configDirectory: string; configFileName?: never; } - | { configFileName: DocumentIdentifier; configDirectory?: never; }, - ): ParsedCommandLine { - owner.ensureInitialized(); - return owner.client.apiRequest("parseJsonConfigFileContent", { json, ...options }); - }, - function* ( - json: any, - options: - | { configDirectory: string; configFileName?: never; } - | { configFileName: DocumentIdentifier; configDirectory?: never; }, - ): Generator { - yield* owner.ensureInitialized.gen(); - return yield* apiRequest("parseJsonConfigFileContent", { json, ...options }); - }, - ); - } - - get transpileModule(): { - (input: string, options?: TranspileOptions): TranspileOutput; - gen(input: string, options?: TranspileOptions): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "transpileModule", - function (input: string, options: TranspileOptions = {}): TranspileOutput { - owner.ensureInitialized(); - return owner.client.apiRequest("transpileModule", { input, options }); - }, - function* (input: string, options: TranspileOptions = {}): Generator { - yield* owner.ensureInitialized.gen(); - return yield* apiRequest("transpileModule", { input, options }); - }, - ); - } - - get transpileModuleFromFile(): { - (fileName: string, options?: TranspileOptions): TranspileOutput; - gen(fileName: string, options?: TranspileOptions): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "transpileModuleFromFile", - function (fileName: string, options: TranspileOptions = {}): TranspileOutput { - owner.ensureInitialized(); - return owner.client.apiRequest("transpileModuleFromFile", { fileName, options }); - }, - function* (fileName: string, options: TranspileOptions = {}): Generator { - yield* owner.ensureInitialized.gen(); - return yield* apiRequest("transpileModuleFromFile", { fileName, options }); - }, - ); - } - - get transpileDeclaration(): { - (input: string, options?: TranspileOptions): TranspileOutput; - gen(input: string, options?: TranspileOptions): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "transpileDeclaration", - function (input: string, options: TranspileOptions = {}): TranspileOutput { - owner.ensureInitialized(); - return owner.client.apiRequest("transpileDeclaration", { input, options }); - }, - function* (input: string, options: TranspileOptions = {}): Generator { - yield* owner.ensureInitialized.gen(); - return yield* apiRequest("transpileDeclaration", { input, options }); - }, - ); - } - - get transpileDeclarationFromFile(): { - (fileName: string, options?: TranspileOptions): TranspileOutput; - gen(fileName: string, options?: TranspileOptions): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "transpileDeclarationFromFile", - function (fileName: string, options: TranspileOptions = {}): TranspileOutput { - owner.ensureInitialized(); - return owner.client.apiRequest("transpileDeclarationFromFile", { fileName, options }); - }, - function* (fileName: string, options: TranspileOptions = {}): Generator { - yield* owner.ensureInitialized.gen(); - return yield* apiRequest("transpileDeclarationFromFile", { fileName, options }); - }, - ); - } - - get updateSnapshot(): { - (params?: FromLSP extends true ? LSPUpdateSnapshotParams : UpdateSnapshotParams): Snapshot; - gen(params?: FromLSP extends true ? LSPUpdateSnapshotParams : UpdateSnapshotParams): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "updateSnapshot", - function (params?: FromLSP extends true ? LSPUpdateSnapshotParams : UpdateSnapshotParams): Snapshot { - owner.ensureInitialized(); - - const requestParams = toUpdateSnapshotRequest(params); - const data = owner.client.apiRequest("updateSnapshot", requestParams); - - // Retain cached source files from previous snapshot for unchanged files - if (owner.latestSnapshot) { - owner.sourceFileCache.retainForSnapshot(data.snapshot, owner.latestSnapshot.id, data.changes); - if (owner.latestSnapshot.isDisposed()) { - owner.sourceFileCache.releaseSnapshot(owner.latestSnapshot.id); - } - } - - const snapshot = new Snapshot( - data, - owner.client, - owner.sourceFileCache, - owner.toPath!, - owner, - () => { - owner.activeSnapshots.delete(snapshot); - if (snapshot !== owner.latestSnapshot) { - owner.sourceFileCache.releaseSnapshot(snapshot.id); - } - }, - ); - owner.latestSnapshot = snapshot; - owner.activeSnapshots.add(snapshot); - - return snapshot; - }, - function* (params?: FromLSP extends true ? LSPUpdateSnapshotParams : UpdateSnapshotParams): Generator { - yield* owner.ensureInitialized.gen(); - - const requestParams = toUpdateSnapshotRequest(params); - const data = yield* apiRequest("updateSnapshot", requestParams); - - // Retain cached source files from previous snapshot for unchanged files - if (owner.latestSnapshot) { - owner.sourceFileCache.retainForSnapshot(data.snapshot, owner.latestSnapshot.id, data.changes); - if (owner.latestSnapshot.isDisposed()) { - owner.sourceFileCache.releaseSnapshot(owner.latestSnapshot.id); - } - } - - const snapshot = new Snapshot( - data, - owner.client, - owner.sourceFileCache, - owner.toPath!, - owner, - () => { - owner.activeSnapshots.delete(snapshot); - if (snapshot !== owner.latestSnapshot) { - owner.sourceFileCache.releaseSnapshot(snapshot.id); - } - }, - ); - owner.latestSnapshot = snapshot; - owner.activeSnapshots.add(snapshot); - - return snapshot; - }, - ); - } - - get close(): { - (): void; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "close", - function (): void { - // Dispose all active snapshots - for (const snapshot of [...owner.activeSnapshots]) { - snapshot.dispose(); - } - // Release the latest snapshot's cache refs if still held - if (owner.latestSnapshot) { - owner.sourceFileCache.releaseSnapshot(owner.latestSnapshot.id); - owner.latestSnapshot = undefined; - } - owner.client.close(); - owner.sourceFileCache.clear(); - }, - function* (): Generator { - // Dispose all active snapshots - for (const snapshot of [...owner.activeSnapshots]) { - yield* snapshot.dispose.gen(); - } - // Release the latest snapshot's cache refs if still held - if (owner.latestSnapshot) { - owner.sourceFileCache.releaseSnapshot(owner.latestSnapshot.id); - owner.latestSnapshot = undefined; - } - owner.client.close(); - owner.sourceFileCache.clear(); - }, - ); - } - - clearSourceFileCache(): void { - this.sourceFileCache.clear(); - } - - get runWithTemporaryFileUpdate(): { - (baseSnapshot: Snapshot, file: DocumentIdentifier, newText: string, cb: (newSnapshot: Snapshot) => void): void; - gen(baseSnapshot: Snapshot, file: DocumentIdentifier, newText: string, cb: (newSnapshot: Snapshot) => void | Generator): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "runWithTemporaryFileUpdate", - function (baseSnapshot: Snapshot, file: DocumentIdentifier, newText: string, cb: (newSnapshot: Snapshot) => void): void { - owner.ensureInitialized(); - - if (!owner.activeSnapshots.has(baseSnapshot) || baseSnapshot.isDisposed()) { - throw new Error("Cannot run a temporary file update on an inactive snapshot"); - } - const data = owner.client.apiRequest("updateTemporarySnapshot", { snapshot: baseSnapshot.id, file, newText }); - - // Retain cached source files from the base snapshot for files unchanged by - // the temporary update. The temporary snapshot is not the latest snapshot, so - // we never release the latest snapshot's cache here. - owner.sourceFileCache.retainForSnapshot(data.snapshot, baseSnapshot.id, data.changes); - - const snapshot = new Snapshot( - data, - owner.client, - owner.sourceFileCache, - owner.toPath!, - owner, - () => { - owner.activeSnapshots.delete(snapshot); - owner.sourceFileCache.releaseSnapshot(snapshot.id); - }, - ); - owner.activeSnapshots.add(snapshot); - - try { - cb(snapshot); - } - finally { - snapshot.dispose(); - } - }, - function* (baseSnapshot: Snapshot, file: DocumentIdentifier, newText: string, cb: (newSnapshot: Snapshot) => void | Generator): Generator { - yield* owner.ensureInitialized.gen(); - - if (!owner.activeSnapshots.has(baseSnapshot) || baseSnapshot.isDisposed()) { - throw new Error("Cannot run a temporary file update on an inactive snapshot"); - } - const data = yield* apiRequest("updateTemporarySnapshot", { snapshot: baseSnapshot.id, file, newText }); - - // Retain cached source files from the base snapshot for files unchanged by - // the temporary update. The temporary snapshot is not the latest snapshot, so - // we never release the latest snapshot's cache here. - owner.sourceFileCache.retainForSnapshot(data.snapshot, baseSnapshot.id, data.changes); - - const snapshot = new Snapshot( - data, - owner.client, - owner.sourceFileCache, - owner.toPath!, - owner, - () => { - owner.activeSnapshots.delete(snapshot); - owner.sourceFileCache.releaseSnapshot(snapshot.id); - }, - ); - owner.activeSnapshots.add(snapshot); - - try { - yield* (cb(snapshot) ?? []); - } - finally { - yield* snapshot.dispose.gen(); - } - }, - ); - } - - /** - * Returns a snapshot of collected timing information for requests made - * through this API instance: client-measured round-trip latency and bytes - * transferred, folded together with the server's own per-request processing - * time and an estimated transport overhead (round-trip minus server time). - * - * Fetching the snapshot issues a lightweight request to the server to - * retrieve its timing collection. Collection must be enabled via the - * `collectTiming` option; when it is not, the returned snapshot has - * `enabled: false` and zeroed totals. - */ - get getTimingInfo(): { - (): TimingInfo; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getTimingInfo", - function (): TimingInfo { - return owner.client.getTimingInfo(); - }, - function* (): Generator { - return owner.client.getTimingInfo(); - }, - ); - } - - /** Clears all accumulated timing totals and recent-request history, on both the client and the server. */ - get resetTimingInfo(): { - (): void; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "resetTimingInfo", - function (): void { - return owner.client.resetTimingInfo(); - }, - function* (): Generator { - return owner.client.resetTimingInfo(); - }, - ); - } -} - -type EnsureInitialized = (() => void) & { gen(): Generator; }; - -export class InternalAPI { - private client: Client; - private ensureInitialized: EnsureInitialized; - - /** @internal */ - constructor(client: Client, ensureInitialized: EnsureInitialized) { - this.client = client; - this.ensureInitialized = ensureInitialized; - } - - get startCPUProfile(): { - (dir: string): void; - gen(dir: string): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "startCPUProfile", - function (dir: string): void { - owner.ensureInitialized(); - owner.client.apiRequest("startCPUProfile", { dir }); - }, - function* (dir: string): Generator { - yield* owner.ensureInitialized.gen(); - yield* apiRequest("startCPUProfile", { dir }); - }, - ); - } - - get stopCPUProfile(): { - (): string; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "stopCPUProfile", - function (): string { - owner.ensureInitialized(); - const result = owner.client.apiRequest("stopCPUProfile", null); - return result.file; - }, - function* (): Generator { - yield* owner.ensureInitialized.gen(); - const result = yield* apiRequest("stopCPUProfile", null); - return result.file; - }, - ); - } - - get saveHeapProfile(): { - (dir: string): string; - gen(dir: string): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "saveHeapProfile", - function (dir: string): string { - owner.ensureInitialized(); - const result = owner.client.apiRequest("saveHeapProfile", { dir }); - return result.file; - }, - function* (dir: string): Generator { - yield* owner.ensureInitialized.gen(); - const result = yield* apiRequest("saveHeapProfile", { dir }); - return result.file; - }, - ); - } -} - -export class Snapshot { - readonly id: number; - private projectMap: Map; - private toPath: (fileName: string) => Path; - private client: Client; - private disposed: boolean = false; - private onDispose: () => void; - private snapshotRegistry: SnapshotObjectRegistry; - readonly internal: SnapshotInternalAPI; - - constructor( - data: UpdateSnapshotResponse, - client: Client, - sourceFileCache: SourceFileCache, - toPath: (fileName: string) => Path, - formatDiagnosticsHost: FormatDiagnosticsHost, - onDispose: () => void, - ) { - this.id = data.snapshot; - this.client = client; - this.toPath = toPath; - this.onDispose = onDispose; - this.projectMap = new Map(); - this.snapshotRegistry = new SnapshotObjectRegistry(client, this.id, projectId => this.projectMap.get(projectId)); - - for (const projData of data.projects) { - const project = new Project(projData, this.id, client, sourceFileCache, toPath, formatDiagnosticsHost, this.snapshotRegistry); - this.projectMap.set(toPath(projData.configFileName), project); - } - - this.internal = new SnapshotInternalAPI(this.id, client); - } - - getProjects(): readonly Project[] { - this.ensureNotDisposed(); - return [...this.projectMap.values()]; - } - - getProject(configFileName: string): Project | undefined { - this.ensureNotDisposed(); - return this.projectMap.get(this.toPath(configFileName)); - } - - get getDefaultProjectForFile(): { - (file: DocumentIdentifier): Project | undefined; - gen(file: DocumentIdentifier): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getDefaultProjectForFile", - function (file: DocumentIdentifier): Project | undefined { - owner.ensureNotDisposed(); - const data = owner.client.apiRequest("getDefaultProjectForFile", { - snapshot: owner.id, - file, - }); - if (!data) return undefined; - return owner.projectMap.get(owner.toPath(data.configFileName)); - }, - function* (file: DocumentIdentifier): Generator { - owner.ensureNotDisposed(); - const data = yield* apiRequest("getDefaultProjectForFile", { - snapshot: owner.id, - file, - }); - if (!data) return undefined; - return owner.projectMap.get(owner.toPath(data.configFileName)); - }, - ); - } - - [globalThis.Symbol.dispose](): void { - this.dispose(); - } - - get dispose(): { - (): void; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "dispose", - function (): void { - if (owner.disposed) return; - owner.disposed = true; - for (const project of owner.projectMap.values()) { - project.dispose(); - } - owner.projectMap.clear(); - owner.snapshotRegistry.clear(); - owner.onDispose(); - owner.client.apiRequest("release", { snapshot: owner.id }); - }, - function* (): Generator { - if (owner.disposed) return; - owner.disposed = true; - for (const project of owner.projectMap.values()) { - project.dispose(); - } - owner.projectMap.clear(); - owner.snapshotRegistry.clear(); - owner.onDispose(); - yield* apiRequest("release", { snapshot: owner.id }); - }, - ); - } - - isDisposed(): boolean { - return this.disposed; - } - - private ensureNotDisposed(): void { - if (this.disposed) { - throw new Error("Snapshot is disposed"); - } - } -} - -class SnapshotObjectRegistry { - private readonly symbols: Map = new Map(); - private readonly client: Client; - private readonly snapshotId: number; - private readonly resolveProject: (projectId: Path) => Project | undefined; - - constructor(client: Client, snapshotId: number, resolveProject: (projectId: Path) => Project | undefined) { - this.client = client; - this.snapshotId = snapshotId; - this.resolveProject = resolveProject; - } - - /** Resolve a project id (a config file path) to its Project within this snapshot. */ - getProject(projectId: Path): Project | undefined { - return this.resolveProject(projectId); - } - - getOrCreateSymbol(data: SymbolResponse): Symbol { - let symbol = this.symbols.get(data.id); - if (!symbol) { - symbol = new Symbol(data, this); - this.symbols.set(data.id, symbol); - } - return symbol; - } - - getSymbol(id: number): Symbol | undefined { - return this.symbols.get(id); - } - - clear(): void { - this.symbols.clear(); - } - - get fetchSymbol(): { - (source: Symbol | Signature | Type, method: SymbolPropertyMethod, handle: number | undefined, projectId: Path): Symbol; - gen(source: Symbol | Signature | Type, method: SymbolPropertyMethod, handle: number | undefined, projectId: Path): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "fetchSymbol", - function (source: Symbol | Signature | Type, method: SymbolPropertyMethod, handle: number | undefined, projectId: Path): Symbol { - if (!handle) return undefined as unknown as Symbol; - const cached = owner.getSymbol(handle); - if (cached) return cached; - - const data = owner.client.apiRequest(method, { - snapshot: owner.snapshotId, - project: projectId, - objectId: source.id, - }); - if (!data) throw new Error(`${method} returned null symbol for ${source.constructor.name} ${source.id}`); - return owner.getOrCreateSymbol(data); - }, - function* (source: Symbol | Signature | Type, method: SymbolPropertyMethod, handle: number | undefined, projectId: Path): Generator { - if (!handle) return undefined as unknown as Symbol; - const cached = owner.getSymbol(handle); - if (cached) return cached; - - const data = yield* apiRequest(method, { - snapshot: owner.snapshotId, - project: projectId, - objectId: source.id, - }); - if (!data) throw new Error(`${method} returned null symbol for ${source.constructor.name} ${source.id}`); - return owner.getOrCreateSymbol(data); - }, - ); - } - - get fetchSymbols(): { - (source: Symbol | Signature | Type, method: SymbolsPropertyMethod, handles: readonly number[] | undefined, projectId: Path): readonly Symbol[]; - gen(source: Symbol | Signature | Type, method: SymbolsPropertyMethod, handles: readonly number[] | undefined, projectId: Path): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "fetchSymbols", - function (source: Symbol | Signature | Type, method: SymbolsPropertyMethod, handles: readonly number[] | undefined, projectId: Path): readonly Symbol[] { - if (handles) { - const result = new Array(handles.length); - let allCached = true; - for (let i = 0; i < handles.length; i++) { - const cached = owner.getSymbol(handles[i]); - if (!cached) { - allCached = false; - break; - } - result[i] = cached; - } - if (allCached) return result; - } - const symbolData = owner.client.apiRequest(method, { - snapshot: owner.snapshotId, - project: projectId, - objectId: source.id, - }); - if (symbolData == null) return []; - else return symbolData.map(data => owner.getOrCreateSymbol(data)); - }, - function* (source: Symbol | Signature | Type, method: SymbolsPropertyMethod, handles: readonly number[] | undefined, projectId: Path): Generator { - if (handles) { - const result = new Array(handles.length); - let allCached = true; - for (let i = 0; i < handles.length; i++) { - const cached = owner.getSymbol(handles[i]); - if (!cached) { - allCached = false; - break; - } - result[i] = cached; - } - if (allCached) return result; - } - const symbolData = yield* apiRequest(method, { - snapshot: owner.snapshotId, - project: projectId, - objectId: source.id, - }); - if (symbolData == null) return []; - else return symbolData.map(data => owner.getOrCreateSymbol(data)); - }, - ); - } -} - -class ProjectObjectRegistry { - private client: Client; - private snapshotId: number; - private project: Project; - private snapshotRegistry: SnapshotObjectRegistry; - private types: Map = new Map(); - private signatures: Map = new Map(); - - constructor( - client: Client, - snapshotId: number, - project: Project, - snapshotRegistry: SnapshotObjectRegistry, - ) { - this.client = client; - this.snapshotId = snapshotId; - this.project = project; - this.snapshotRegistry = snapshotRegistry; - } - - getOrCreateSymbol(data: SymbolResponse): Symbol { - return this.snapshotRegistry.getOrCreateSymbol(data); - } - - getSymbol(id: number): Symbol | undefined { - return this.snapshotRegistry.getSymbol(id); - } - - getOrCreateType(data: TypeResponse): TypeObject { - let type = this.types.get(data.id); - if (!type) { - type = new TypeObject(data, this); - this.types.set(data.id, type); - } - return type; - } - - getType(id: number): TypeObject | undefined { - return this.types.get(id); - } - - getOrCreateSignature(data: SignatureResponse): Signature { - let sig = this.signatures.get(data.id); - if (!sig) { - sig = new Signature(data, this.project, this); - this.signatures.set(data.id, sig); - } - return sig; - } - - getSignature(id: number): Signature | undefined { - return this.signatures.get(id); - } - - clear(): void { - this.types.clear(); - this.signatures.clear(); - } - - get fetchOptionalType(): { - (source: Symbol | Signature | Type, method: TypePropertyMethod, handle: number | false | undefined): T | undefined; - gen(source: Symbol | Signature | Type, method: TypePropertyMethod, handle: number | false | undefined): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "fetchOptionalType", - function (source: Symbol | Signature | Type, method: TypePropertyMethod, handle: number | false | undefined): T | undefined { - if (handle !== false) { - if (!handle) return undefined; - const cached = owner.getType(handle); - if (cached) return cached as unknown as T; - } - - const data = owner.client.apiRequest(method, { - snapshot: owner.snapshotId, - project: owner.project.id, - objectId: source.id, - }); - if (!data) return undefined; - return owner.getOrCreateType(data) as unknown as T; - }, - function* (source: Symbol | Signature | Type, method: TypePropertyMethod, handle: number | false | undefined): Generator { - if (handle !== false) { - if (!handle) return undefined; - const cached = owner.getType(handle); - if (cached) return cached as unknown as T; - } - - const data = yield* apiRequest(method, { - snapshot: owner.snapshotId, - project: owner.project.id, - objectId: source.id, - }); - if (!data) return undefined; - return owner.getOrCreateType(data) as unknown as T; - }, - ); - } - - get fetchType(): { - (source: Symbol | Signature | Type, method: TypePropertyMethod, handle: number | false | undefined): T; - gen(source: Symbol | Signature | Type, method: TypePropertyMethod, handle: number | false | undefined): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "fetchType", - function (source: Symbol | Signature | Type, method: TypePropertyMethod, handle: number | false | undefined): T { - const result = owner.fetchOptionalType(source, method, handle); - if (result === undefined) throw new Error(`${method} returned no type for ${source.constructor.name} ${source.id}`); - return result; - }, - function* (source: Symbol | Signature | Type, method: TypePropertyMethod, handle: number | false | undefined): Generator { - const result = yield* owner.fetchOptionalType.gen(source, method, handle); - if (result === undefined) throw new Error(`${method} returned no type for ${source.constructor.name} ${source.id}`); - return result; - }, - ); - } - - get fetchSymbol(): { - (source: Symbol | Signature | Type, method: SymbolPropertyMethod, handle: number | undefined): Symbol; - gen(source: Symbol | Signature | Type, method: SymbolPropertyMethod, handle: number | undefined): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "fetchSymbol", - function (source: Symbol | Signature | Type, method: SymbolPropertyMethod, handle: number | undefined): Symbol { - return owner.snapshotRegistry.fetchSymbol(source, method, handle, owner.project.id); - }, - function* (source: Symbol | Signature | Type, method: SymbolPropertyMethod, handle: number | undefined): Generator { - return yield* owner.snapshotRegistry.fetchSymbol.gen(source, method, handle, owner.project.id); - }, - ); - } - - get fetchSignature(): { - (source: Symbol | Signature | Type, method: SignaturePropertyMethod, handle: number | undefined): Signature; - gen(source: Symbol | Signature | Type, method: SignaturePropertyMethod, handle: number | undefined): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "fetchSignature", - function (source: Symbol | Signature | Type, method: SignaturePropertyMethod, handle: number | undefined): Signature { - if (!handle) return undefined as unknown as Signature; - const cached = owner.getSignature(handle); - if (cached) return cached; - - const data = owner.client.apiRequest(method, { - snapshot: owner.snapshotId, - project: owner.project.id, - objectId: source.id, - }); - if (!data) throw new Error(`${method} returned null signature for ${source.constructor.name} ${source.id}`); - return owner.getOrCreateSignature(data); - }, - function* (source: Symbol | Signature | Type, method: SignaturePropertyMethod, handle: number | undefined): Generator { - if (!handle) return undefined as unknown as Signature; - const cached = owner.getSignature(handle); - if (cached) return cached; - - const data = yield* apiRequest(method, { - snapshot: owner.snapshotId, - project: owner.project.id, - objectId: source.id, - }); - if (!data) throw new Error(`${method} returned null signature for ${source.constructor.name} ${source.id}`); - return owner.getOrCreateSignature(data); - }, - ); - } - - get fetchTypes(): { - (source: Symbol | Signature | Type, method: TypesPropertyMethod, handles?: readonly number[]): readonly Type[]; - gen(source: Symbol | Signature | Type, method: TypesPropertyMethod, handles?: readonly number[]): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "fetchTypes", - function (source: Symbol | Signature | Type, method: TypesPropertyMethod, handles?: readonly number[]): readonly Type[] { - if (handles) { - const result = new Array(handles.length); - let allCached = true; - for (let i = 0; i < handles.length; i++) { - const cached = owner.getType(handles[i]); - if (!cached) { - allCached = false; - break; - } - result[i] = cached; - } - if (allCached) return result; - } - const typesData = owner.client.apiRequest(method, { - snapshot: owner.snapshotId, - project: owner.project.id, - objectId: source.id, - }); - if (typesData == null) return []; - else return typesData.map(data => owner.getOrCreateType(data)); - }, - function* (source: Symbol | Signature | Type, method: TypesPropertyMethod, handles?: readonly number[]): Generator { - if (handles) { - const result = new Array(handles.length); - let allCached = true; - for (let i = 0; i < handles.length; i++) { - const cached = owner.getType(handles[i]); - if (!cached) { - allCached = false; - break; - } - result[i] = cached; - } - if (allCached) return result; - } - const typesData = yield* apiRequest(method, { - snapshot: owner.snapshotId, - project: owner.project.id, - objectId: source.id, - }); - if (typesData == null) return []; - else return typesData.map(data => owner.getOrCreateType(data)); - }, - ); - } - - get fetchSymbols(): { - (source: Symbol | Signature | Type, method: SymbolsPropertyMethod, handles?: readonly number[]): readonly Symbol[]; - gen(source: Symbol | Signature | Type, method: SymbolsPropertyMethod, handles?: readonly number[]): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "fetchSymbols", - function (source: Symbol | Signature | Type, method: SymbolsPropertyMethod, handles?: readonly number[]): readonly Symbol[] { - return owner.snapshotRegistry.fetchSymbols(source, method, handles, owner.project.id); - }, - function* (source: Symbol | Signature | Type, method: SymbolsPropertyMethod, handles?: readonly number[]): Generator { - return yield* owner.snapshotRegistry.fetchSymbols.gen(source, method, handles, owner.project.id); - }, - ); - } - - // getBaseTypes is a checker-level endpoint keyed by `type` (not `objectId`), - // so it cannot go through fetchTypes. This helper reuses that server method. - get fetchBaseTypes(): { - (source: Type): readonly Type[]; - gen(source: Type): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "fetchBaseTypes", - function (source: Type): readonly Type[] { - const typesData = owner.client.apiRequest("getBaseTypes", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: source.id, - }); - if (typesData == null) return []; - return typesData.map(data => owner.getOrCreateType(data)); - }, - function* (source: Type): Generator { - const typesData = yield* apiRequest("getBaseTypes", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: source.id, - }); - if (typesData == null) return []; - return typesData.map(data => owner.getOrCreateType(data)); - }, - ); - } - - get fetchPropertiesOfType(): { - (source: Type): readonly Symbol[]; - gen(source: Type): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "fetchPropertiesOfType", - function (source: Type): readonly Symbol[] { - const data = owner.client.apiRequest("getPropertiesOfType", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: source.id, - }); - return data ? data.map(symbol => owner.getOrCreateSymbol(symbol)) : []; - }, - function* (source: Type): Generator { - const data = yield* apiRequest("getPropertiesOfType", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: source.id, - }); - return data ? data.map(symbol => owner.getOrCreateSymbol(symbol)) : []; - }, - ); - } - - get fetchApparentPropertiesOfType(): { - (source: Type): readonly Symbol[]; - gen(source: Type): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "fetchApparentPropertiesOfType", - function (source: Type): readonly Symbol[] { - const data = owner.client.apiRequest("getApparentPropertiesOfType", { - snapshot: owner.snapshotId, - project: owner.project.id, - objectId: source.id, - }); - return data ? data.map(symbol => owner.getOrCreateSymbol(symbol)) : []; - }, - function* (source: Type): Generator { - const data = yield* apiRequest("getApparentPropertiesOfType", { - snapshot: owner.snapshotId, - project: owner.project.id, - objectId: source.id, - }); - return data ? data.map(symbol => owner.getOrCreateSymbol(symbol)) : []; - }, - ); - } - - get fetchPropertyOfType(): { - (source: Type, name: string): Symbol | undefined; - gen(source: Type, name: string): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "fetchPropertyOfType", - function (source: Type, name: string): Symbol | undefined { - const data = owner.client.apiRequest("getPropertyOfType", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: source.id, - name, - }); - return data ? owner.getOrCreateSymbol(data) : undefined; - }, - function* (source: Type, name: string): Generator { - const data = yield* apiRequest("getPropertyOfType", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: source.id, - name, - }); - return data ? owner.getOrCreateSymbol(data) : undefined; - }, - ); - } - - get fetchSignaturesOfType(): { - (source: Type, kind: SignatureKind): readonly Signature[]; - gen(source: Type, kind: SignatureKind): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "fetchSignaturesOfType", - function (source: Type, kind: SignatureKind): readonly Signature[] { - const data = owner.client.apiRequest("getSignaturesOfType", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: source.id, - kind, - }); - return data.map(signature => owner.getOrCreateSignature(signature)); - }, - function* (source: Type, kind: SignatureKind): Generator { - const data = yield* apiRequest("getSignaturesOfType", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: source.id, - kind, - }); - return data.map(signature => owner.getOrCreateSignature(signature)); - }, - ); - } - - get fetchIndexInfosOfType(): { - (source: Type): readonly IndexInfo[]; - gen(source: Type): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "fetchIndexInfosOfType", - function (source: Type): readonly IndexInfo[] { - const data = owner.client.apiRequest("getIndexInfosOfType", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: source.id, - }); - if (!data) return []; - return data.map(info => ({ - keyType: owner.getOrCreateType(info.keyType), - valueType: owner.getOrCreateType(info.valueType), - isReadonly: info.isReadonly ?? false, - declaration: info.declaration ? new NodeHandle(info.declaration, owner.project) : undefined, - })); - }, - function* (source: Type): Generator { - const data = yield* apiRequest("getIndexInfosOfType", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: source.id, - }); - if (!data) return []; - return data.map(info => ({ - keyType: owner.getOrCreateType(info.keyType), - valueType: owner.getOrCreateType(info.valueType), - isReadonly: info.isReadonly ?? false, - declaration: info.declaration ? new NodeHandle(info.declaration, owner.project) : undefined, - })); - }, - ); - } - - get fetchTypeParameterAtPosition(): { - (source: Signature, pos: number): Type; - gen(source: Signature, pos: number): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "fetchTypeParameterAtPosition", - function (source: Signature, pos: number): Type { - const data = owner.client.apiRequest("getTypeParameterAtPosition", { - snapshot: owner.snapshotId, - project: owner.project.id, - signature: source.id, - index: pos, - }); - return owner.getOrCreateType(data); - }, - function* (source: Signature, pos: number): Generator { - const data = yield* apiRequest("getTypeParameterAtPosition", { - snapshot: owner.snapshotId, - project: owner.project.id, - signature: source.id, - index: pos, - }); - return owner.getOrCreateType(data); - }, - ); - } -} - -export class Project { - readonly id: Path; - readonly configFileName: string; - readonly currentDirectory: string; - readonly parsedCommandLine: ParsedCommandLine; - /** @deprecated Use `parsedCommandLine.options`. */ - readonly compilerOptions: CompilerOptions; - /** @deprecated Use `parsedCommandLine.fileNames`. */ - readonly rootFiles: readonly string[]; - - readonly program: Program; - readonly checker: Checker; - readonly emitter: Emitter; - readonly languageService: LanguageService; - private client: Client; - private snapshotId: number; - - constructor( - data: ProjectResponse, - snapshotId: number, - client: Client, - sourceFileCache: SourceFileCache, - toPath: (fileName: string) => Path, - formatDiagnosticsHost: FormatDiagnosticsHost, - snapshotRegistry: SnapshotObjectRegistry, - ) { - this.id = data.id as Path; - this.configFileName = data.configFileName; - this.currentDirectory = data.currentDirectory; - if (!data.parsedCommandLine?.options) { - throw new Error(`Project '${data.configFileName}' has no parsed command line`); - } - this.parsedCommandLine = data.parsedCommandLine; - this.compilerOptions = this.parsedCommandLine.options; - this.rootFiles = this.parsedCommandLine.fileNames; - this.client = client; - this.snapshotId = snapshotId; - this.program = new Program( - snapshotId, - this, - client, - sourceFileCache, - toPath, - formatDiagnosticsHost, - ); - const objectRegistry = new ProjectObjectRegistry(client, snapshotId, this, snapshotRegistry); - this.checker = new Checker( - snapshotId, - this, - client, - objectRegistry, - ); - this.emitter = new Emitter(client); - this.languageService = new LanguageService(snapshotId, this, client, objectRegistry); - } - - /** @deprecated Use `languageService.getImportAdderEdits`. */ - get getImportAdderEdits(): { - (file: DocumentIdentifier, actions: readonly APIImportAdderAction[]): readonly TextEdit[]; - gen(file: DocumentIdentifier, actions: readonly APIImportAdderAction[]): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getImportAdderEdits", - function (file: DocumentIdentifier, actions: readonly APIImportAdderAction[]): readonly TextEdit[] { - return owner.languageService.getImportAdderEdits(file, actions); - }, - function* (file: DocumentIdentifier, actions: readonly APIImportAdderAction[]): Generator { - return yield* owner.languageService.getImportAdderEdits.gen(file, actions); - }, - ); - } - - /** @deprecated Use `languageService.getImportEditsForSymbols`. */ - get getImportEditsForSymbols(): { - (file: DocumentIdentifier, symbols: readonly Symbol[], options?: GetImportEditsForSymbolsOptions): readonly TextEdit[]; - gen(file: DocumentIdentifier, symbols: readonly Symbol[], options?: GetImportEditsForSymbolsOptions): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getImportEditsForSymbols", - function (file: DocumentIdentifier, symbols: readonly Symbol[], options: GetImportEditsForSymbolsOptions = {}): readonly TextEdit[] { - return owner.languageService.getImportEditsForSymbols(file, symbols, options); - }, - function* (file: DocumentIdentifier, symbols: readonly Symbol[], options: GetImportEditsForSymbolsOptions = {}): Generator { - return yield* owner.languageService.getImportEditsForSymbols.gen(file, symbols, options); - }, - ); - } - - dispose(): void { - this.checker.dispose(); - } -} - -export class LanguageService { - private snapshotId: number; - private project: Project; - private client: Client; - private objectRegistry: ProjectObjectRegistry; - - constructor( - snapshotId: number, - project: Project, - client: Client, - objectRegistry: ProjectObjectRegistry, - ) { - this.snapshotId = snapshotId; - this.project = project; - this.client = client; - this.objectRegistry = objectRegistry; - } - - get getImportAdderEdits(): { - (file: DocumentIdentifier, actions: readonly APIImportAdderAction[]): readonly TextEdit[]; - gen(file: DocumentIdentifier, actions: readonly APIImportAdderAction[]): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getImportAdderEdits", - function (file: DocumentIdentifier, actions: readonly APIImportAdderAction[]): readonly TextEdit[] { - const requestActions: ImportAdderAction[] = actions.map(action => { - switch (action.kind) { - case "importSymbol": - const importSymbolAction: ImportAdderAction = { - kind: "importSymbol", - symbol: action.symbol.id, - }; - if (action.isValidTypeOnlyUseSite !== undefined) { - importSymbolAction.isValidTypeOnlyUseSite = action.isValidTypeOnlyUseSite; - } - return importSymbolAction; - default: - return assertNever(action.kind); - } - }); - - const data = owner.client.apiRequest("getImportAdderEdits", { - snapshot: owner.snapshotId, - project: owner.project.id, - file, - actions: requestActions, - }); - return data ?? []; - }, - function* (file: DocumentIdentifier, actions: readonly APIImportAdderAction[]): Generator { - const requestActions: ImportAdderAction[] = actions.map(action => { - switch (action.kind) { - case "importSymbol": - const importSymbolAction: ImportAdderAction = { - kind: "importSymbol", - symbol: action.symbol.id, - }; - if (action.isValidTypeOnlyUseSite !== undefined) { - importSymbolAction.isValidTypeOnlyUseSite = action.isValidTypeOnlyUseSite; - } - return importSymbolAction; - default: - return assertNever(action.kind); - } - }); - - const data = yield* apiRequest("getImportAdderEdits", { - snapshot: owner.snapshotId, - project: owner.project.id, - file, - actions: requestActions, - }); - return data ?? []; - }, - ); - } - - get getImportEditsForSymbols(): { - (file: DocumentIdentifier, symbols: readonly Symbol[], options?: GetImportEditsForSymbolsOptions): readonly TextEdit[]; - gen(file: DocumentIdentifier, symbols: readonly Symbol[], options?: GetImportEditsForSymbolsOptions): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getImportEditsForSymbols", - function (file: DocumentIdentifier, symbols: readonly Symbol[], options: GetImportEditsForSymbolsOptions = {}): readonly TextEdit[] { - return owner.getImportAdderEdits( - file, - symbols.map((symbol): APIImportAdderAction => { - if (options.isValidTypeOnlyUseSite !== undefined) { - return { - kind: "importSymbol", - symbol, - isValidTypeOnlyUseSite: options.isValidTypeOnlyUseSite, - }; - } - return { - kind: "importSymbol", - symbol, - }; - }), - ); - }, - function* (file: DocumentIdentifier, symbols: readonly Symbol[], options: GetImportEditsForSymbolsOptions = {}): Generator { - return yield* owner.getImportAdderEdits.gen( - file, - symbols.map((symbol): APIImportAdderAction => { - if (options.isValidTypeOnlyUseSite !== undefined) { - return { - kind: "importSymbol", - symbol, - isValidTypeOnlyUseSite: options.isValidTypeOnlyUseSite, - }; - } - return { - kind: "importSymbol", - symbol, - }; - }), - ); - }, - ); - } - - get getReferencedSymbolsForNode(): { - (node: Node, position: number): ReferencedSymbolEntry[]; - gen(node: Node, position: number): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getReferencedSymbolsForNode", - function (node: Node, position: number): ReferencedSymbolEntry[] { - const data = owner.client.apiRequest("getReferencedSymbolsForNode", { - snapshot: owner.snapshotId, - project: owner.project.id, - node: getNodeId(node), - position, - }); - return (data ?? []).map(entry => ({ - definition: new NodeHandle(entry.definition, owner.project), - symbol: entry.symbol ? owner.objectRegistry.getOrCreateSymbol(entry.symbol) : undefined, - references: (entry.references ?? []).map(h => new NodeHandle(h, owner.project)), - })); - }, - function* (node: Node, position: number): Generator { - const data = yield* apiRequest("getReferencedSymbolsForNode", { - snapshot: owner.snapshotId, - project: owner.project.id, - node: getNodeId(node), - position, - }); - return (data ?? []).map(entry => ({ - definition: new NodeHandle(entry.definition, owner.project), - symbol: entry.symbol ? owner.objectRegistry.getOrCreateSymbol(entry.symbol) : undefined, - references: (entry.references ?? []).map(h => new NodeHandle(h, owner.project)), - })); - }, - ); - } - - get getSignatureUsage(): { - (signatureDecl: Node): SignatureUsage[]; - gen(signatureDecl: Node): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getSignatureUsage", - function (signatureDecl: Node): SignatureUsage[] { - const data = owner.client.apiRequest("getSignatureUsages", { - snapshot: owner.snapshotId, - project: owner.project.id, - signatureDecl: getNodeId(signatureDecl), - }); - return (data ?? []).map(entry => ({ - name: new NodeHandle(entry.name, owner.project), - call: entry.call ? new NodeHandle(entry.call, owner.project) : undefined, - })); - }, - function* (signatureDecl: Node): Generator { - const data = yield* apiRequest("getSignatureUsages", { - snapshot: owner.snapshotId, - project: owner.project.id, - signatureDecl: getNodeId(signatureDecl), - }); - return (data ?? []).map(entry => ({ - name: new NodeHandle(entry.name, owner.project), - call: entry.call ? new NodeHandle(entry.call, owner.project) : undefined, - })); - }, - ); - } - - get getCompletionsAtPosition(): { - (document: string, position: number, options?: CompletionOptions): CompletionInfo | undefined; - gen(document: string, position: number, options?: CompletionOptions): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getCompletionsAtPosition", - function (document: string, position: number, options?: CompletionOptions): CompletionInfo | undefined { - const data = owner.client.apiRequest("getCompletionsAtPosition", { - snapshot: owner.snapshotId, - project: owner.project.id, - file: document, - position, - ...(options?.triggerCharacter !== undefined ? { triggerCharacter: options.triggerCharacter } : {}), - ...(options?.includeSymbol !== undefined ? { includeSymbol: options.includeSymbol } : {}), - }); - if (!data) return undefined; - return { - isIncomplete: data.isIncomplete, - entries: data.entries.map(e => ({ - ...e, - symbol: e.symbol ? owner.objectRegistry.getOrCreateSymbol(e.symbol) : undefined, - })), - }; - }, - function* (document: string, position: number, options?: CompletionOptions): Generator { - const data = yield* apiRequest("getCompletionsAtPosition", { - snapshot: owner.snapshotId, - project: owner.project.id, - file: document, - position, - ...(options?.triggerCharacter !== undefined ? { triggerCharacter: options.triggerCharacter } : {}), - ...(options?.includeSymbol !== undefined ? { includeSymbol: options.includeSymbol } : {}), - }); - if (!data) return undefined; - return { - isIncomplete: data.isIncomplete, - entries: data.entries.map(e => ({ - ...e, - symbol: e.symbol ? owner.objectRegistry.getOrCreateSymbol(e.symbol) : undefined, - })), - }; - }, - ); - } -} - -export class Program implements FormatDiagnosticsHost { - private snapshotId: number; - private project: Project; - private client: Client; - private sourceFileCache: SourceFileCache; - private toPath: (fileName: string) => Path; - private formatDiagnosticsHost: FormatDiagnosticsHost; - private decoder = new Wtf8Decoder(); - private sourceFileMetadataCache = new Map(); - - constructor( - snapshotId: number, - project: Project, - client: Client, - sourceFileCache: SourceFileCache, - toPath: (fileName: string) => Path, - formatDiagnosticsHost: FormatDiagnosticsHost, - ) { - this.snapshotId = snapshotId; - this.project = project; - this.client = client; - this.sourceFileCache = sourceFileCache; - this.toPath = toPath; - this.formatDiagnosticsHost = formatDiagnosticsHost; - } - - getCurrentDirectory(): string { - return this.project.currentDirectory; - } - - getCanonicalFileName(fileName: string): string { - return this.formatDiagnosticsHost.getCanonicalFileName(fileName); - } - - getNewLine(): string { - return this.project.compilerOptions.newLine === NewLineKind.CRLF ? "\r\n" : "\n"; - } - - getCompilerOptions(): CompilerOptions { - return this.project.compilerOptions; - } - - get getSourceFile(): { - (file: DocumentIdentifier): SourceFile | undefined; - gen(file: DocumentIdentifier): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getSourceFile", - function (file: DocumentIdentifier): SourceFile | undefined { - const fileName = resolveFileName(file); - const path = owner.toPath(fileName); - - // Check if we already have a retained cache entry for this (snapshot, project) pair - const retained = owner.sourceFileCache.getRetained(path, owner.snapshotId, owner.project.id); - if (retained) { - return retained; - } - - // Fetch from server - const binaryData = owner.client.apiRequestBinary("getSourceFile", { - snapshot: owner.snapshotId, - project: owner.project.id, - file, - }); - if (!binaryData) { - return undefined; - } - - const view = new DataView(binaryData.buffer, binaryData.byteOffset, binaryData.byteLength); - const contentHash = readSourceFileHash(view); - const parseOptionsKey = readParseOptionsKey(view); - - // Create a new RemoteSourceFile and cache it (set returns existing if hash matches) - const sourceFile = new RemoteSourceFile(binaryData, owner.decoder, owner.client.getTimingCollector()) as unknown as SourceFile; - return owner.sourceFileCache.set(path, sourceFile, parseOptionsKey, contentHash, owner.snapshotId, owner.project.id); - }, - function* (file: DocumentIdentifier): Generator { - const fileName = resolveFileName(file); - const path = owner.toPath(fileName); - - // Check if we already have a retained cache entry for this (snapshot, project) pair - const retained = owner.sourceFileCache.getRetained(path, owner.snapshotId, owner.project.id); - if (retained) { - return retained; - } - - // Fetch from server - const binaryData = owner.client.apiRequestBinary("getSourceFile", { - snapshot: owner.snapshotId, - project: owner.project.id, - file, - }); - if (!binaryData) { - return undefined; - } - - const view = new DataView(binaryData.buffer, binaryData.byteOffset, binaryData.byteLength); - const contentHash = readSourceFileHash(view); - const parseOptionsKey = readParseOptionsKey(view); - - // Create a new RemoteSourceFile and cache it (set returns existing if hash matches) - const sourceFile = new RemoteSourceFile(binaryData, owner.decoder, owner.client.getTimingCollector()) as unknown as SourceFile; - return owner.sourceFileCache.set(path, sourceFile, parseOptionsKey, contentHash, owner.snapshotId, owner.project.id); - }, - ); - } - - get getSourceFileNames(): { - (): readonly string[]; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getSourceFileNames", - function (): readonly string[] { - const data = owner.client.apiRequest("getSourceFileNames", { - snapshot: owner.snapshotId, - project: owner.project.id, - }); - return data ?? []; - }, - function* (): Generator { - const data = yield* apiRequest("getSourceFileNames", { - snapshot: owner.snapshotId, - project: owner.project.id, - }); - return data ?? []; - }, - ); - } - - /** - * Returns program-stored metadata for the given source file, or `undefined` if the file - * is not part of the program. Metadata is fetched lazily per file and cached on this - * `Program` instance. - */ - get getSourceFileMetadata(): { - (fileName: string): SourceFileMetadata | undefined; - gen(fileName: string): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getSourceFileMetadata", - function (fileName: string): SourceFileMetadata | undefined { - return owner.getSourceFileMetadataByPath(owner.toPath(fileName)); - }, - function* (fileName: string): Generator { - return yield* owner.getSourceFileMetadataByPath.gen(owner.toPath(fileName)); - }, - ); - } - - /** - * Returns program-stored metadata for the source file at the given path, or `undefined` - * if the file is not part of the program. Like {@link getSourceFileMetadata}, but skips - * the file name to path conversion. Metadata is fetched lazily per file and cached on - * this `Program` instance. - */ - get getSourceFileMetadataByPath(): { - (path: Path): SourceFileMetadata | undefined; - gen(path: Path): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getSourceFileMetadataByPath", - function (path: Path): SourceFileMetadata | undefined { - let metadata = owner.sourceFileMetadataCache.get(path); - if (metadata === undefined) { - metadata = owner.fetchSourceFileMetadata(path); - owner.sourceFileMetadataCache.set(path, metadata); - } - return metadata; - }, - function* (path: Path): Generator { - let metadata = owner.sourceFileMetadataCache.get(path); - if (metadata === undefined) { - metadata = yield* owner.fetchSourceFileMetadata.gen(path); - owner.sourceFileMetadataCache.set(path, metadata); - } - return metadata; - }, - ); - } - - private get fetchSourceFileMetadata(): { - (path: Path): SourceFileMetadata | undefined; - gen(path: Path): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "fetchSourceFileMetadata", - function (path: Path): SourceFileMetadata | undefined { - const data = owner.client.apiRequest("getSourceFileMetadata", { - snapshot: owner.snapshotId, - project: owner.project.id, - file: path, - }); - return data ?? undefined; - }, - function* (path: Path): Generator { - const data = yield* apiRequest("getSourceFileMetadata", { - snapshot: owner.snapshotId, - project: owner.project.id, - file: path, - }); - return data ?? undefined; - }, - ); - } - - /** - * Returns whether the given source file was loaded as part of an external library - * (e.g. a dependency resolved from `node_modules`). The underlying program metadata is - * fetched lazily per file and cached on this `Program` instance. - */ - get isSourceFileFromExternalLibrary(): { - (file: SourceFile): boolean; - gen(file: SourceFile): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "isSourceFileFromExternalLibrary", - function (file: SourceFile): boolean { - const metadata = owner.getSourceFileMetadataByPath(file.path); - return metadata?.isFromExternalLibrary ?? false; - }, - function* (file: SourceFile): Generator { - const metadata = yield* owner.getSourceFileMetadataByPath.gen(file.path); - return metadata?.isFromExternalLibrary ?? false; - }, - ); - } - - /** - * Returns whether the given source file is a default library file (e.g. `lib.d.ts`). - * The underlying program metadata is fetched lazily per file and cached on this - * `Program` instance. - */ - get isSourceFileDefaultLibrary(): { - (file: SourceFile): boolean; - gen(file: SourceFile): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "isSourceFileDefaultLibrary", - function (file: SourceFile): boolean { - const metadata = owner.getSourceFileMetadataByPath(file.path); - return metadata?.isDefaultLibrary ?? false; - }, - function* (file: SourceFile): Generator { - const metadata = yield* owner.getSourceFileMetadataByPath.gen(file.path); - return metadata?.isDefaultLibrary ?? false; - }, - ); - } - - /** - * Get all config source file names associated with this program's project config. - * Includes the root config file and any extended config files. - */ - get getConfigFileNames(): { - (): readonly string[]; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getConfigFileNames", - function (): readonly string[] { - const data = owner.client.apiRequest("getConfigFileNames", { - snapshot: owner.snapshotId, - project: owner.project.id, - }); - return data ?? []; - }, - function* (): Generator { - const data = yield* apiRequest("getConfigFileNames", { - snapshot: owner.snapshotId, - project: owner.project.id, - }); - return data ?? []; - }, - ); - } - - /** - * Get a config source file by file name/URI. - * This can return the project's root tsconfig file or one of its extended config files. - */ - get getConfigSourceFile(): { - (file: DocumentIdentifier): SourceFile | undefined; - gen(file: DocumentIdentifier): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getConfigSourceFile", - function (file: DocumentIdentifier): SourceFile | undefined { - const binaryData = owner.client.apiRequestBinary("getConfigSourceFile", { - snapshot: owner.snapshotId, - project: owner.project.id, - file, - }); - if (!binaryData) { - return undefined; - } - - return new RemoteSourceFile(binaryData, owner.decoder) as unknown as SourceFile; - }, - function* (file: DocumentIdentifier): Generator { - const binaryData = owner.client.apiRequestBinary("getConfigSourceFile", { - snapshot: owner.snapshotId, - project: owner.project.id, - file, - }); - if (!binaryData) { - return undefined; - } - - return new RemoteSourceFile(binaryData, owner.decoder) as unknown as SourceFile; - }, - ); - } - - /** - * Get syntactic (parse) diagnostics for specific files or all files. - * @param file - Optional file(s) to get diagnostics for. If omitted, returns diagnostics for all files. - */ - get getSyntacticDiagnostics(): { - (file?: DocumentIdentifier | readonly DocumentIdentifier[]): readonly Diagnostic[]; - gen(file?: DocumentIdentifier | readonly DocumentIdentifier[]): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getSyntacticDiagnostics", - function (file?: DocumentIdentifier | readonly DocumentIdentifier[]): readonly Diagnostic[] { - const files = file === undefined ? undefined - : Array.isArray(file) ? file - : [file]; - const data = owner.client.apiRequest("getSyntacticDiagnostics", { - snapshot: owner.snapshotId, - project: owner.project.id, - ...(files !== undefined ? { files } : {}), - }); - return data ?? []; - }, - function* (file?: DocumentIdentifier | readonly DocumentIdentifier[]): Generator { - const files = file === undefined ? undefined - : Array.isArray(file) ? file - : [file]; - const data = yield* apiRequest("getSyntacticDiagnostics", { - snapshot: owner.snapshotId, - project: owner.project.id, - ...(files !== undefined ? { files } : {}), - }); - return data ?? []; - }, - ); - } - - /** - * Get binder diagnostics for specific files or all files. - * @param file - Optional file(s) to get diagnostics for. If omitted, returns diagnostics for all files. - */ - get getBindDiagnostics(): { - (file?: DocumentIdentifier | readonly DocumentIdentifier[]): readonly Diagnostic[]; - gen(file?: DocumentIdentifier | readonly DocumentIdentifier[]): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getBindDiagnostics", - function (file?: DocumentIdentifier | readonly DocumentIdentifier[]): readonly Diagnostic[] { - const files = file === undefined ? undefined - : Array.isArray(file) ? file - : [file]; - const data = owner.client.apiRequest("getBindDiagnostics", { - snapshot: owner.snapshotId, - project: owner.project.id, - ...(files !== undefined ? { files } : {}), - }); - return data ?? []; - }, - function* (file?: DocumentIdentifier | readonly DocumentIdentifier[]): Generator { - const files = file === undefined ? undefined - : Array.isArray(file) ? file - : [file]; - const data = yield* apiRequest("getBindDiagnostics", { - snapshot: owner.snapshotId, - project: owner.project.id, - ...(files !== undefined ? { files } : {}), - }); - return data ?? []; - }, - ); - } - - /** - * Get semantic (type-check) diagnostics for specific files or all files. - * @param file - Optional file(s) to get diagnostics for. If omitted, returns diagnostics for all files. - */ - get getSemanticDiagnostics(): { - (file?: DocumentIdentifier | readonly DocumentIdentifier[]): readonly Diagnostic[]; - gen(file?: DocumentIdentifier | readonly DocumentIdentifier[]): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getSemanticDiagnostics", - function (file?: DocumentIdentifier | readonly DocumentIdentifier[]): readonly Diagnostic[] { - const files = file === undefined ? undefined - : Array.isArray(file) ? file - : [file]; - const data = owner.client.apiRequest("getSemanticDiagnostics", { - snapshot: owner.snapshotId, - project: owner.project.id, - ...(files !== undefined ? { files } : {}), - }); - return data ?? []; - }, - function* (file?: DocumentIdentifier | readonly DocumentIdentifier[]): Generator { - const files = file === undefined ? undefined - : Array.isArray(file) ? file - : [file]; - const data = yield* apiRequest("getSemanticDiagnostics", { - snapshot: owner.snapshotId, - project: owner.project.id, - ...(files !== undefined ? { files } : {}), - }); - return data ?? []; - }, - ); - } - - /** - * Get suggestion diagnostics for specific files or all files. - * @param file - Optional file(s) to get diagnostics for. If omitted, returns diagnostics for all files. - */ - get getSuggestionDiagnostics(): { - (file?: DocumentIdentifier | readonly DocumentIdentifier[]): readonly Diagnostic[]; - gen(file?: DocumentIdentifier | readonly DocumentIdentifier[]): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getSuggestionDiagnostics", - function (file?: DocumentIdentifier | readonly DocumentIdentifier[]): readonly Diagnostic[] { - const files = file === undefined ? undefined - : Array.isArray(file) ? file - : [file]; - const data = owner.client.apiRequest("getSuggestionDiagnostics", { - snapshot: owner.snapshotId, - project: owner.project.id, - ...(files !== undefined ? { files } : {}), - }); - return data ?? []; - }, - function* (file?: DocumentIdentifier | readonly DocumentIdentifier[]): Generator { - const files = file === undefined ? undefined - : Array.isArray(file) ? file - : [file]; - const data = yield* apiRequest("getSuggestionDiagnostics", { - snapshot: owner.snapshotId, - project: owner.project.id, - ...(files !== undefined ? { files } : {}), - }); - return data ?? []; - }, - ); - } - - /** - * Get declaration emit diagnostics for specific files or all files. - * @param file - Optional file(s) to get diagnostics for. If omitted, returns diagnostics for all files. - */ - get getDeclarationDiagnostics(): { - (file?: DocumentIdentifier | readonly DocumentIdentifier[]): readonly Diagnostic[]; - gen(file?: DocumentIdentifier | readonly DocumentIdentifier[]): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getDeclarationDiagnostics", - function (file?: DocumentIdentifier | readonly DocumentIdentifier[]): readonly Diagnostic[] { - const files = file === undefined ? undefined - : Array.isArray(file) ? file - : [file]; - const data = owner.client.apiRequest("getDeclarationDiagnostics", { - snapshot: owner.snapshotId, - project: owner.project.id, - ...(files !== undefined ? { files } : {}), - }); - return data ?? []; - }, - function* (file?: DocumentIdentifier | readonly DocumentIdentifier[]): Generator { - const files = file === undefined ? undefined - : Array.isArray(file) ? file - : [file]; - const data = yield* apiRequest("getDeclarationDiagnostics", { - snapshot: owner.snapshotId, - project: owner.project.id, - ...(files !== undefined ? { files } : {}), - }); - return data ?? []; - }, - ); - } - - /** - * Get program-wide diagnostics for the project, including compiler options diagnostics. - */ - get getProgramDiagnostics(): { - (): readonly Diagnostic[]; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getProgramDiagnostics", - function (): readonly Diagnostic[] { - const data = owner.client.apiRequest("getProgramDiagnostics", { - snapshot: owner.snapshotId, - project: owner.project.id, - }); - return data ?? []; - }, - function* (): Generator { - const data = yield* apiRequest("getProgramDiagnostics", { - snapshot: owner.snapshotId, - project: owner.project.id, - }); - return data ?? []; - }, - ); - } - - /** - * Get global (non-file-specific) semantic diagnostics for the project. - */ - get getGlobalDiagnostics(): { - (): readonly Diagnostic[]; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getGlobalDiagnostics", - function (): readonly Diagnostic[] { - const data = owner.client.apiRequest("getGlobalDiagnostics", { - snapshot: owner.snapshotId, - project: owner.project.id, - }); - return data ?? []; - }, - function* (): Generator { - const data = yield* apiRequest("getGlobalDiagnostics", { - snapshot: owner.snapshotId, - project: owner.project.id, - }); - return data ?? []; - }, - ); - } - - /** - * Get config file parsing diagnostics for the project. - */ - get getConfigFileParsingDiagnostics(): { - (): readonly Diagnostic[]; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getConfigFileParsingDiagnostics", - function (): readonly Diagnostic[] { - const data = owner.client.apiRequest("getConfigFileParsingDiagnostics", { - snapshot: owner.snapshotId, - project: owner.project.id, - }); - return data ?? []; - }, - function* (): Generator { - const data = yield* apiRequest("getConfigFileParsingDiagnostics", { - snapshot: owner.snapshotId, - project: owner.project.id, - }); - return data ?? []; - }, - ); - } - - /** - * Emits files to the configured filesystem. - * - * When the API has a virtual filesystem with a `writeFile` callback, output - * is written there. Otherwise, the server writes directly to the host filesystem. - */ - get emit(): { - (emitOnly?: EmitOnly): EmitResult; - gen(emitOnly?: EmitOnly): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "emit", - function (emitOnly?: EmitOnly): EmitResult { - const response = owner.client.apiRequest("emit", { - snapshot: owner.snapshotId, - project: owner.project.id, - ...(emitOnly !== undefined ? { emitOnly } : {}), - }); - return { - emitSkipped: response.emitSkipped, - diagnostics: response.diagnostics, - emittedFiles: response.emittedFiles, - }; - }, - function* (emitOnly?: EmitOnly): Generator { - const response = yield* apiRequest("emit", { - snapshot: owner.snapshotId, - project: owner.project.id, - ...(emitOnly !== undefined ? { emitOnly } : {}), - }); - return { - emitSkipped: response.emitSkipped, - diagnostics: response.diagnostics, - emittedFiles: response.emittedFiles, - }; - }, - ); - } - - /** - * Emits files and returns their contents without writing to the filesystem. - */ - get emitToString(): { - (emitOnly?: EmitOnly): EmitOutput; - gen(emitOnly?: EmitOnly): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "emitToString", - function (emitOnly?: EmitOnly): EmitOutput { - const response = owner.client.apiRequest("emitToString", { - snapshot: owner.snapshotId, - project: owner.project.id, - ...(emitOnly !== undefined ? { emitOnly } : {}), - }); - return toEmitOutput(response); - }, - function* (emitOnly?: EmitOnly): Generator { - const response = yield* apiRequest("emitToString", { - snapshot: owner.snapshotId, - project: owner.project.id, - ...(emitOnly !== undefined ? { emitOnly } : {}), - }); - return toEmitOutput(response); - }, - ); - } - - /** - * Gets JavaScript output for selected files regardless of project `noEmit`, `emitDeclarationOnly`, and `noEmitOnError` settings. - */ - get getJavaScriptEmit(): { - (files: readonly DocumentIdentifier[]): EmitOutput; - gen(files: readonly DocumentIdentifier[]): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getJavaScriptEmit", - function (files: readonly DocumentIdentifier[]): EmitOutput { - const response = owner.client.apiRequest("getJavaScriptEmit", { - snapshot: owner.snapshotId, - project: owner.project.id, - files, - }); - return toEmitOutput(response); - }, - function* (files: readonly DocumentIdentifier[]): Generator { - const response = yield* apiRequest("getJavaScriptEmit", { - snapshot: owner.snapshotId, - project: owner.project.id, - files, - }); - return toEmitOutput(response); - }, - ); - } - - /** - * Gets declaration output for selected files regardless of project `noEmit`, `declaration`, `emitDeclarationOnly`, and `noEmitOnError` settings. - */ - get getDeclarationEmit(): { - (files: readonly DocumentIdentifier[]): EmitOutput; - gen(files: readonly DocumentIdentifier[]): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getDeclarationEmit", - function (files: readonly DocumentIdentifier[]): EmitOutput { - const response = owner.client.apiRequest("getDeclarationEmit", { - snapshot: owner.snapshotId, - project: owner.project.id, - files, - }); - return toEmitOutput(response); - }, - function* (files: readonly DocumentIdentifier[]): Generator { - const response = yield* apiRequest("getDeclarationEmit", { - snapshot: owner.snapshotId, - project: owner.project.id, - files, - }); - return toEmitOutput(response); - }, - ); - } -} - -function toEmitOutput(response: ProtocolEmitOutputResponse): EmitOutput { - const outputFiles = new Map(); - for (const { fileName, ...outputFile } of response.outputFiles) { - outputFiles.set(fileName, outputFile); - } - return { - emitSkipped: response.emitSkipped, - diagnostics: response.diagnostics, - outputFiles, - }; -} - -export class Checker { - private snapshotId: number; - private project: Project; - private client: Client; - private objectRegistry: ProjectObjectRegistry; - private wellKnownSymbols: { unknown: number; undefined: number; arguments: number; } | undefined; - private wellKnownSignatures: { unknown: number; } | undefined; - - constructor( - snapshotId: number, - project: Project, - client: Client, - objectRegistry: ProjectObjectRegistry, - ) { - this.snapshotId = snapshotId; - this.project = project; - this.client = client; - this.objectRegistry = objectRegistry; - } - - dispose(): void { - this.objectRegistry.clear(); - } - - get getSymbolAtLocation(): { - (node: Node): Symbol | undefined; - (nodes: readonly Node[]): (Symbol | undefined)[]; - gen(node: Node): Generator; - gen(nodes: readonly Node[]): Generator; - } { - const owner = this; - function getSymbolAtLocation(node: Node): Symbol | undefined; - function getSymbolAtLocation(nodes: readonly Node[]): (Symbol | undefined)[]; - function getSymbolAtLocation(nodeOrNodes: Node | readonly Node[]): Symbol | (Symbol | undefined)[] | undefined { - if (Array.isArray(nodeOrNodes)) { - const data = owner.client.apiRequest("getSymbolsAtLocations", { - snapshot: owner.snapshotId, - project: owner.project.id, - locations: nodeOrNodes.map(node => getNodeId(node)), - }); - return data.map(d => d ? owner.objectRegistry.getOrCreateSymbol(d) : undefined); - } - const data = owner.client.apiRequest("getSymbolAtLocation", { - snapshot: owner.snapshotId, - project: owner.project.id, - location: getNodeId(nodeOrNodes as Node), - }); - return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; - } - function gen(node: Node): Generator; - function gen(nodes: readonly Node[]): Generator; - function* gen(nodeOrNodes: Node | readonly Node[]): Generator { - if (Array.isArray(nodeOrNodes)) { - const data = yield* apiRequest("getSymbolsAtLocations", { - snapshot: owner.snapshotId, - project: owner.project.id, - locations: nodeOrNodes.map(node => getNodeId(node)), - }); - return data.map(d => d ? owner.objectRegistry.getOrCreateSymbol(d) : undefined); - } - const data = yield* apiRequest("getSymbolAtLocation", { - snapshot: owner.snapshotId, - project: owner.project.id, - location: getNodeId(nodeOrNodes as Node), - }); - return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; - } - return cacheGeneratorMethod(owner, "getSymbolAtLocation", getSymbolAtLocation, gen); - } - - get getSymbolAtPosition(): { - (file: DocumentIdentifier, position: number): Symbol | undefined; - (file: DocumentIdentifier, positions: readonly number[]): (Symbol | undefined)[]; - gen(file: DocumentIdentifier, position: number): Generator; - gen(file: DocumentIdentifier, positions: readonly number[]): Generator; - } { - const owner = this; - function getSymbolAtPosition(file: DocumentIdentifier, position: number): Symbol | undefined; - function getSymbolAtPosition(file: DocumentIdentifier, positions: readonly number[]): (Symbol | undefined)[]; - function getSymbolAtPosition(file: DocumentIdentifier, positionOrPositions: number | readonly number[]): Symbol | (Symbol | undefined)[] | undefined { - if (typeof positionOrPositions === "number") { - const data = owner.client.apiRequest("getSymbolAtPosition", { - snapshot: owner.snapshotId, - project: owner.project.id, - file, - position: positionOrPositions, - }); - return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; - } - const data = owner.client.apiRequest("getSymbolsAtPositions", { - snapshot: owner.snapshotId, - project: owner.project.id, - file, - positions: positionOrPositions, - }); - return data.map(d => d ? owner.objectRegistry.getOrCreateSymbol(d) : undefined); - } - function gen(file: DocumentIdentifier, position: number): Generator; - function gen(file: DocumentIdentifier, positions: readonly number[]): Generator; - function* gen(file: DocumentIdentifier, positionOrPositions: number | readonly number[]): Generator { - if (typeof positionOrPositions === "number") { - const data = yield* apiRequest("getSymbolAtPosition", { - snapshot: owner.snapshotId, - project: owner.project.id, - file, - position: positionOrPositions, - }); - return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; - } - const data = yield* apiRequest("getSymbolsAtPositions", { - snapshot: owner.snapshotId, - project: owner.project.id, - file, - positions: positionOrPositions, - }); - return data.map(d => d ? owner.objectRegistry.getOrCreateSymbol(d) : undefined); - } - return cacheGeneratorMethod(owner, "getSymbolAtPosition", getSymbolAtPosition, gen); - } - - get getSymbolOfSourceFile(): { - (file: DocumentIdentifier): Symbol | undefined; - (files: readonly DocumentIdentifier[]): (Symbol | undefined)[]; - gen(file: DocumentIdentifier): Generator; - gen(files: readonly DocumentIdentifier[]): Generator; - } { - const owner = this; - function getSymbolOfSourceFile(file: DocumentIdentifier): Symbol | undefined; - function getSymbolOfSourceFile(files: readonly DocumentIdentifier[]): (Symbol | undefined)[]; - function getSymbolOfSourceFile(fileOrFiles: DocumentIdentifier | readonly DocumentIdentifier[]): Symbol | (Symbol | undefined)[] | undefined { - if (Array.isArray(fileOrFiles)) { - const data = owner.client.apiRequest("getSymbolsOfSourceFiles", { - snapshot: owner.snapshotId, - project: owner.project.id, - files: fileOrFiles, - }); - return data.map(d => d ? owner.objectRegistry.getOrCreateSymbol(d) : undefined); - } - const data = owner.client.apiRequest("getSymbolOfSourceFile", { - snapshot: owner.snapshotId, - project: owner.project.id, - file: fileOrFiles as DocumentIdentifier, - }); - return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; - } - function gen(file: DocumentIdentifier): Generator; - function gen(files: readonly DocumentIdentifier[]): Generator; - function* gen(fileOrFiles: DocumentIdentifier | readonly DocumentIdentifier[]): Generator { - if (Array.isArray(fileOrFiles)) { - const data = yield* apiRequest("getSymbolsOfSourceFiles", { - snapshot: owner.snapshotId, - project: owner.project.id, - files: fileOrFiles, - }); - return data.map(d => d ? owner.objectRegistry.getOrCreateSymbol(d) : undefined); - } - const data = yield* apiRequest("getSymbolOfSourceFile", { - snapshot: owner.snapshotId, - project: owner.project.id, - file: fileOrFiles as DocumentIdentifier, - }); - return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; - } - return cacheGeneratorMethod(owner, "getSymbolOfSourceFile", getSymbolOfSourceFile, gen); - } - - /** - * Get the type of a symbol. Always returns a type; for symbols whose type - * cannot be determined the checker yields the error type (use - * {@link Type.isErrorType} to detect it). - */ - get getTypeOfSymbol(): { - (symbol: Symbol): Type; - (symbols: readonly Symbol[]): Type[]; - gen(symbol: Symbol): Generator; - gen(symbols: readonly Symbol[]): Generator; - } { - const owner = this; - function getTypeOfSymbol(symbol: Symbol): Type; - function getTypeOfSymbol(symbols: readonly Symbol[]): Type[]; - function getTypeOfSymbol(symbolOrSymbols: Symbol | readonly Symbol[]): Type | Type[] { - if (Array.isArray(symbolOrSymbols)) { - const data = owner.client.apiRequest("getTypesOfSymbols", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbols: symbolOrSymbols.map(s => s.id), - }); - return data.map(d => owner.objectRegistry.getOrCreateType(d)); - } - const data = owner.client.apiRequest("getTypeOfSymbol", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: (symbolOrSymbols as Symbol).id, - }); - return owner.objectRegistry.getOrCreateType(data); - } - function gen(symbol: Symbol): Generator; - function gen(symbols: readonly Symbol[]): Generator; - function* gen(symbolOrSymbols: Symbol | readonly Symbol[]): Generator { - if (Array.isArray(symbolOrSymbols)) { - const data = yield* apiRequest("getTypesOfSymbols", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbols: symbolOrSymbols.map(s => s.id), - }); - return data.map(d => owner.objectRegistry.getOrCreateType(d)); - } - const data = yield* apiRequest("getTypeOfSymbol", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: (symbolOrSymbols as Symbol).id, - }); - return owner.objectRegistry.getOrCreateType(data); - } - return cacheGeneratorMethod(owner, "getTypeOfSymbol", getTypeOfSymbol, gen); - } - - /** - * Get the declared type of a symbol. Always returns a type; for symbols whose - * declared type cannot be determined the checker yields the error type (use - * {@link Type.isErrorType} to detect it). - */ - get getDeclaredTypeOfSymbol(): { - (symbol: Symbol): Type; - gen(symbol: Symbol): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getDeclaredTypeOfSymbol", - function (symbol: Symbol): Type { - const data = owner.client.apiRequest("getDeclaredTypeOfSymbol", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: symbol.id, - }); - return owner.objectRegistry.getOrCreateType(data); - }, - function* (symbol: Symbol): Generator { - const data = yield* apiRequest("getDeclaredTypeOfSymbol", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: symbol.id, - }); - return owner.objectRegistry.getOrCreateType(data); - }, - ); - } - - /** - * Get the type of a symbol, excluding the missing type when - * `exactOptionalPropertyTypes: true` is set; for symbols whose - * type cannot be determined the checker yields the error type - * (use {@link Type.isErrorType} to detect it). - */ - get getNonMissingTypeOfSymbol(): { - (symbol: Symbol): Type; - gen(symbol: Symbol): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getNonMissingTypeOfSymbol", - function (symbol: Symbol): Type { - const data = owner.client.apiRequest("getNonMissingTypeOfSymbol", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: symbol.id, - }); - return owner.objectRegistry.getOrCreateType(data); - }, - function* (symbol: Symbol): Generator { - const data = yield* apiRequest("getNonMissingTypeOfSymbol", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: symbol.id, - }); - return owner.objectRegistry.getOrCreateType(data); - }, - ); - } - - get getReferencesToSymbolInFile(): { - (file: DocumentIdentifier, symbol: Symbol): NodeHandle[]; - gen(file: DocumentIdentifier, symbol: Symbol): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getReferencesToSymbolInFile", - function (file: DocumentIdentifier, symbol: Symbol): NodeHandle[] { - const data = owner.client.apiRequest("getReferencesToSymbolInFile", { - snapshot: owner.snapshotId, - project: owner.project.id, - file, - symbol: symbol.id, - }); - return (data ?? []).map(h => new NodeHandle(h, owner.project)); - }, - function* (file: DocumentIdentifier, symbol: Symbol): Generator { - const data = yield* apiRequest("getReferencesToSymbolInFile", { - snapshot: owner.snapshotId, - project: owner.project.id, - file, - symbol: symbol.id, - }); - return (data ?? []).map(h => new NodeHandle(h, owner.project)); - }, - ); - } - - /** @deprecated Use `project.languageService.getReferencedSymbolsForNode`. */ - get getReferencedSymbolsForNode(): { - (node: Node, position: number): ReferencedSymbolEntry[]; - gen(node: Node, position: number): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getReferencedSymbolsForNode", - function (node: Node, position: number): ReferencedSymbolEntry[] { - return owner.project.languageService.getReferencedSymbolsForNode(node, position); - }, - function* (node: Node, position: number): Generator { - return yield* owner.project.languageService.getReferencedSymbolsForNode.gen(node, position); - }, - ); - } - - /** @deprecated Use `project.languageService.getSignatureUsage`. */ - get getSignatureUsage(): { - (signatureDecl: Node): SignatureUsage[]; - gen(signatureDecl: Node): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getSignatureUsage", - function (signatureDecl: Node): SignatureUsage[] { - return owner.project.languageService.getSignatureUsage(signatureDecl); - }, - function* (signatureDecl: Node): Generator { - return yield* owner.project.languageService.getSignatureUsage.gen(signatureDecl); - }, - ); - } - - /** @deprecated Use `project.languageService.getCompletionsAtPosition`. */ - get getCompletionsAtPosition(): { - (document: string, position: number, options?: CompletionOptions): CompletionInfo | undefined; - gen(document: string, position: number, options?: CompletionOptions): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getCompletionsAtPosition", - function (document: string, position: number, options?: CompletionOptions): CompletionInfo | undefined { - return owner.project.languageService.getCompletionsAtPosition(document, position, options); - }, - function* (document: string, position: number, options?: CompletionOptions): Generator { - return yield* owner.project.languageService.getCompletionsAtPosition.gen(document, position, options); - }, - ); - } - - /** - * Get the type at a node location. Always returns a type; for nodes whose - * type cannot be determined the checker yields the error type (use - * {@link Type.isErrorType} to detect it). - */ - get getTypeAtLocation(): { - (node: Node): Type; - (nodes: readonly Node[]): Type[]; - gen(node: Node): Generator; - gen(nodes: readonly Node[]): Generator; - } { - const owner = this; - function getTypeAtLocation(node: Node): Type; - function getTypeAtLocation(nodes: readonly Node[]): Type[]; - function getTypeAtLocation(nodeOrNodes: Node | readonly Node[]): Type | Type[] { - if (Array.isArray(nodeOrNodes)) { - const data = owner.client.apiRequest("getTypeAtLocations", { - snapshot: owner.snapshotId, - project: owner.project.id, - locations: nodeOrNodes.map(node => getNodeId(node)), - }); - return data.map(d => owner.objectRegistry.getOrCreateType(d)); - } - const data = owner.client.apiRequest("getTypeAtLocation", { - snapshot: owner.snapshotId, - project: owner.project.id, - location: getNodeId(nodeOrNodes as Node), - }); - return owner.objectRegistry.getOrCreateType(data); - } - function gen(node: Node): Generator; - function gen(nodes: readonly Node[]): Generator; - function* gen(nodeOrNodes: Node | readonly Node[]): Generator { - if (Array.isArray(nodeOrNodes)) { - const data = yield* apiRequest("getTypeAtLocations", { - snapshot: owner.snapshotId, - project: owner.project.id, - locations: nodeOrNodes.map(node => getNodeId(node)), - }); - return data.map(d => owner.objectRegistry.getOrCreateType(d)); - } - const data = yield* apiRequest("getTypeAtLocation", { - snapshot: owner.snapshotId, - project: owner.project.id, - location: getNodeId(nodeOrNodes as Node), - }); - return owner.objectRegistry.getOrCreateType(data); - } - return cacheGeneratorMethod(owner, "getTypeAtLocation", getTypeAtLocation, gen); - } - - get getSignaturesOfType(): { - (type: Type, kind: SignatureKind): readonly Signature[]; - gen(type: Type, kind: SignatureKind): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getSignaturesOfType", - function (type: Type, kind: SignatureKind): readonly Signature[] { - return kind === SignatureKind.Call ? type.getCallSignatures() : type.getConstructSignatures(); - }, - function* (type: Type, kind: SignatureKind): Generator { - return kind === SignatureKind.Call ? (yield* type.getCallSignatures.gen()) : (yield* type.getConstructSignatures.gen()); - }, - ); - } - - /** - * Get the resolved signature of a call-like expression. Always returns a - * signature; when a call cannot be resolved the checker yields the unknown - * signature (use {@link Checker.isUnknownSignature} to detect it). - */ - get getResolvedSignature(): { - (node: Node): Signature; - gen(node: Node): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getResolvedSignature", - function (node: Node): Signature { - const data = owner.client.apiRequest("getResolvedSignature", { - snapshot: owner.snapshotId, - project: owner.project.id, - location: getNodeId(node), - }); - return owner.objectRegistry.getOrCreateSignature(data); - }, - function* (node: Node): Generator { - const data = yield* apiRequest("getResolvedSignature", { - snapshot: owner.snapshotId, - project: owner.project.id, - location: getNodeId(node), - }); - return owner.objectRegistry.getOrCreateSignature(data); - }, - ); - } - - get getTypeAtPosition(): { - (file: DocumentIdentifier, position: number): Type | undefined; - (file: DocumentIdentifier, positions: readonly number[]): (Type | undefined)[]; - gen(file: DocumentIdentifier, position: number): Generator; - gen(file: DocumentIdentifier, positions: readonly number[]): Generator; - } { - const owner = this; - function getTypeAtPosition(file: DocumentIdentifier, position: number): Type | undefined; - function getTypeAtPosition(file: DocumentIdentifier, positions: readonly number[]): (Type | undefined)[]; - function getTypeAtPosition(file: DocumentIdentifier, positionOrPositions: number | readonly number[]): Type | (Type | undefined)[] | undefined { - if (typeof positionOrPositions === "number") { - const data = owner.client.apiRequest("getTypeAtPosition", { - snapshot: owner.snapshotId, - project: owner.project.id, - file, - position: positionOrPositions, - }); - return data ? owner.objectRegistry.getOrCreateType(data) : undefined; - } - const data = owner.client.apiRequest("getTypesAtPositions", { - snapshot: owner.snapshotId, - project: owner.project.id, - file, - positions: positionOrPositions, - }); - return data.map(d => d ? owner.objectRegistry.getOrCreateType(d) : undefined); - } - function gen(file: DocumentIdentifier, position: number): Generator; - function gen(file: DocumentIdentifier, positions: readonly number[]): Generator; - function* gen(file: DocumentIdentifier, positionOrPositions: number | readonly number[]): Generator { - if (typeof positionOrPositions === "number") { - const data = yield* apiRequest("getTypeAtPosition", { - snapshot: owner.snapshotId, - project: owner.project.id, - file, - position: positionOrPositions, - }); - return data ? owner.objectRegistry.getOrCreateType(data) : undefined; - } - const data = yield* apiRequest("getTypesAtPositions", { - snapshot: owner.snapshotId, - project: owner.project.id, - file, - positions: positionOrPositions, - }); - return data.map(d => d ? owner.objectRegistry.getOrCreateType(d) : undefined); - } - return cacheGeneratorMethod(owner, "getTypeAtPosition", getTypeAtPosition, gen); - } - - get resolveName(): { - (name: string, meaning: SymbolFlags, location?: Node | DocumentPosition, excludeGlobals?: boolean): Symbol | undefined; - gen(name: string, meaning: SymbolFlags, location?: Node | DocumentPosition, excludeGlobals?: boolean): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "resolveName", - function (name: string, meaning: SymbolFlags, location?: Node | DocumentPosition, excludeGlobals?: boolean): Symbol | undefined { - // Distinguish Node (has `kind`) from DocumentPosition (has `document` and `position`) - const isNode = location && "kind" in location; - const data = owner.client.apiRequest("resolveName", { - snapshot: owner.snapshotId, - project: owner.project.id, - name, - meaning, - ...(isNode ? { location: getNodeId(location as Node) } : {}), - ...(!isNode && location - ? { - file: (location as DocumentPosition).document, - position: (location as DocumentPosition).position, - } - : {}), - ...(excludeGlobals !== undefined ? { excludeGlobals } : {}), - }); - return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; - }, - function* (name: string, meaning: SymbolFlags, location?: Node | DocumentPosition, excludeGlobals?: boolean): Generator { - // Distinguish Node (has `kind`) from DocumentPosition (has `document` and `position`) - const isNode = location && "kind" in location; - const data = yield* apiRequest("resolveName", { - snapshot: owner.snapshotId, - project: owner.project.id, - name, - meaning, - ...(isNode ? { location: getNodeId(location as Node) } : {}), - ...(!isNode && location - ? { - file: (location as DocumentPosition).document, - position: (location as DocumentPosition).position, - } - : {}), - ...(excludeGlobals !== undefined ? { excludeGlobals } : {}), - }); - return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; - }, - ); - } - - /** - * Returns all symbols with the given meaning that are visible at `location`. - */ - get getSymbolsInScope(): { - (location: Node | DocumentPosition, meaning: SymbolFlags): readonly Symbol[]; - gen(location: Node | DocumentPosition, meaning: SymbolFlags): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getSymbolsInScope", - function (location: Node | DocumentPosition, meaning: SymbolFlags): readonly Symbol[] { - // Distinguish Node (has `kind`) from DocumentPosition (has `document` and `position`) - const isNode = "kind" in location; - const data = owner.client.apiRequest("getSymbolsInScope", { - snapshot: owner.snapshotId, - project: owner.project.id, - meaning, - ...(isNode - ? { location: getNodeId(location as Node) } - : { - file: (location as DocumentPosition).document, - position: (location as DocumentPosition).position, - }), - }); - return data ? data.map(d => owner.objectRegistry.getOrCreateSymbol(d)) : []; - }, - function* (location: Node | DocumentPosition, meaning: SymbolFlags): Generator { - // Distinguish Node (has `kind`) from DocumentPosition (has `document` and `position`) - const isNode = "kind" in location; - const data = yield* apiRequest("getSymbolsInScope", { - snapshot: owner.snapshotId, - project: owner.project.id, - meaning, - ...(isNode - ? { location: getNodeId(location as Node) } - : { - file: (location as DocumentPosition).document, - position: (location as DocumentPosition).position, - }), - }); - return data ? data.map(d => owner.objectRegistry.getOrCreateSymbol(d)) : []; - }, - ); - } - - get getResolvedSymbol(): { - (node: Identifier): Symbol | undefined; - gen(node: Identifier): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getResolvedSymbol", - function (node: Identifier): Symbol | undefined { - const text = node.text; - if (!text) return undefined; - return owner.resolveName(text, SymbolFlags.Value | SymbolFlags.ExportValue, node); - }, - function* (node: Identifier): Generator { - const text = node.text; - if (!text) return undefined; - return yield* owner.resolveName.gen(text, SymbolFlags.Value | SymbolFlags.ExportValue, node); - }, - ); - } - - get getContextualType(): { - (node: Expression): Type | undefined; - gen(node: Expression): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getContextualType", - function (node: Expression): Type | undefined { - const data = owner.client.apiRequest("getContextualType", { - snapshot: owner.snapshotId, - project: owner.project.id, - location: getNodeId(node), - }); - return data ? owner.objectRegistry.getOrCreateType(data) : undefined; - }, - function* (node: Expression): Generator { - const data = yield* apiRequest("getContextualType", { - snapshot: owner.snapshotId, - project: owner.project.id, - location: getNodeId(node), - }); - return data ? owner.objectRegistry.getOrCreateType(data) : undefined; - }, - ); - } - - /** Get the base type of a literal type (e.g. `number` for `42`). Always returns a type. */ - get getBaseTypeOfLiteralType(): { - (type: Type): Type; - gen(type: Type): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getBaseTypeOfLiteralType", - function (type: Type): Type { - const data = owner.client.apiRequest("getBaseTypeOfLiteralType", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: type.id, - }); - return owner.objectRegistry.getOrCreateType(data); - }, - function* (type: Type): Generator { - const data = yield* apiRequest("getBaseTypeOfLiteralType", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: type.id, - }); - return owner.objectRegistry.getOrCreateType(data); - }, - ); - } - - /** Get the type with `null` and `undefined` removed. Always returns a type. */ - get getNonNullableType(): { - (type: Type): Type; - gen(type: Type): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getNonNullableType", - function (type: Type): Type { - return type.getNonNullableType(); - }, - function* (type: Type): Generator { - return yield* type.getNonNullableType.gen(); - }, - ); - } - - /** - * Get the type for a type node. Always returns a type; for type nodes whose - * type cannot be determined the checker yields the error type (use - * {@link Type.isErrorType} to detect it). - */ - get getTypeFromTypeNode(): { - (node: TypeNode): Type; - gen(node: TypeNode): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getTypeFromTypeNode", - function (node: TypeNode): Type { - const data = owner.client.apiRequest("getTypeFromTypeNode", { - snapshot: owner.snapshotId, - project: owner.project.id, - location: getNodeId(node), - }); - return owner.objectRegistry.getOrCreateType(data); - }, - function* (node: TypeNode): Generator { - const data = yield* apiRequest("getTypeFromTypeNode", { - snapshot: owner.snapshotId, - project: owner.project.id, - location: getNodeId(node), - }); - return owner.objectRegistry.getOrCreateType(data); - }, - ); - } - - /** Get the widened type. Always returns a type. */ - get getWidenedType(): { - (type: Type): Type; - gen(type: Type): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getWidenedType", - function (type: Type): Type { - const data = owner.client.apiRequest("getWidenedType", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: type.id, - }); - return owner.objectRegistry.getOrCreateType(data); - }, - function* (type: Type): Generator { - const data = yield* apiRequest("getWidenedType", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: type.id, - }); - return owner.objectRegistry.getOrCreateType(data); - }, - ); - } - - /** - * Get the type of the parameter at the given index in a signature. Always - * returns a type; an out-of-range index yields the `any` type. - */ - get getParameterType(): { - (signature: Signature, index: number): Type; - gen(signature: Signature, index: number): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getParameterType", - function (signature: Signature, index: number): Type { - const data = owner.client.apiRequest("getParameterType", { - snapshot: owner.snapshotId, - project: owner.project.id, - signature: signature.id, - index, - }); - return owner.objectRegistry.getOrCreateType(data); - }, - function* (signature: Signature, index: number): Generator { - const data = yield* apiRequest("getParameterType", { - snapshot: owner.snapshotId, - project: owner.project.id, - signature: signature.id, - index, - }); - return owner.objectRegistry.getOrCreateType(data); - }, - ); - } - - get isArrayLikeType(): { - (type: Type): boolean; - gen(type: Type): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "isArrayLikeType", - function (type: Type): boolean { - return owner.client.apiRequest("isArrayLikeType", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: type.id, - }); - }, - function* (type: Type): Generator { - return yield* apiRequest("isArrayLikeType", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: type.id, - }); - }, - ); - } - - get isTypeAssignableTo(): { - (source: Type, target: Type): boolean; - gen(source: Type, target: Type): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "isTypeAssignableTo", - function (source: Type, target: Type): boolean { - return owner.client.apiRequest("isTypeAssignableTo", { - snapshot: owner.snapshotId, - project: owner.project.id, - source: source.id, - target: target.id, - }); - }, - function* (source: Type, target: Type): Generator { - return yield* apiRequest("isTypeAssignableTo", { - snapshot: owner.snapshotId, - project: owner.project.id, - source: source.id, - target: target.id, - }); - }, - ); - } - - get getShorthandAssignmentValueSymbol(): { - (node: Node): Symbol | undefined; - gen(node: Node): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getShorthandAssignmentValueSymbol", - function (node: Node): Symbol | undefined { - const data = owner.client.apiRequest("getShorthandAssignmentValueSymbol", { - snapshot: owner.snapshotId, - project: owner.project.id, - location: getNodeId(node), - }); - return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; - }, - function* (node: Node): Generator { - const data = yield* apiRequest("getShorthandAssignmentValueSymbol", { - snapshot: owner.snapshotId, - project: owner.project.id, - location: getNodeId(node), - }); - return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; - }, - ); - } - - /** - * Get the type of a symbol as narrowed at a specific location. Always returns - * a type; for symbols whose type cannot be determined the checker yields the - * error type (use {@link Type.isErrorType} to detect it). - */ - get getTypeOfSymbolAtLocation(): { - (symbol: Symbol, location: Node): Type; - gen(symbol: Symbol, location: Node): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getTypeOfSymbolAtLocation", - function (symbol: Symbol, location: Node): Type { - const data = owner.client.apiRequest("getTypeOfSymbolAtLocation", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: symbol.id, - location: getNodeId(location), - }); - return owner.objectRegistry.getOrCreateType(data); - }, - function* (symbol: Symbol, location: Node): Generator { - const data = yield* apiRequest("getTypeOfSymbolAtLocation", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: symbol.id, - location: getNodeId(location), - }); - return owner.objectRegistry.getOrCreateType(data); - }, - ); - } - - private get getIntrinsicType(): { - (method: IntrinsicTypeMethod): Type; - gen(method: IntrinsicTypeMethod): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getIntrinsicType", - function (method: IntrinsicTypeMethod): Type { - const data = owner.client.apiRequest(method, { - snapshot: owner.snapshotId, - project: owner.project.id, - }); - return owner.objectRegistry.getOrCreateType(data); - }, - function* (method: IntrinsicTypeMethod): Generator { - const data = yield* apiRequest(method, { - snapshot: owner.snapshotId, - project: owner.project.id, - }); - return owner.objectRegistry.getOrCreateType(data); - }, - ); - } - - get getAnyType(): { - (): Type; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getAnyType", - function (): Type { - return owner.getIntrinsicType("getAnyType"); - }, - function* (): Generator { - return yield* owner.getIntrinsicType.gen("getAnyType"); - }, - ); - } - get getStringType(): { - (): Type; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getStringType", - function (): Type { - return owner.getIntrinsicType("getStringType"); - }, - function* (): Generator { - return yield* owner.getIntrinsicType.gen("getStringType"); - }, - ); - } - get getNumberType(): { - (): Type; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getNumberType", - function (): Type { - return owner.getIntrinsicType("getNumberType"); - }, - function* (): Generator { - return yield* owner.getIntrinsicType.gen("getNumberType"); - }, - ); - } - get getBooleanType(): { - (): Type; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getBooleanType", - function (): Type { - return owner.getIntrinsicType("getBooleanType"); - }, - function* (): Generator { - return yield* owner.getIntrinsicType.gen("getBooleanType"); - }, - ); - } - get getVoidType(): { - (): Type; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getVoidType", - function (): Type { - return owner.getIntrinsicType("getVoidType"); - }, - function* (): Generator { - return yield* owner.getIntrinsicType.gen("getVoidType"); - }, - ); - } - get getUndefinedType(): { - (): Type; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getUndefinedType", - function (): Type { - return owner.getIntrinsicType("getUndefinedType"); - }, - function* (): Generator { - return yield* owner.getIntrinsicType.gen("getUndefinedType"); - }, - ); - } - get getNullType(): { - (): Type; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getNullType", - function (): Type { - return owner.getIntrinsicType("getNullType"); - }, - function* (): Generator { - return yield* owner.getIntrinsicType.gen("getNullType"); - }, - ); - } - get getNeverType(): { - (): Type; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getNeverType", - function (): Type { - return owner.getIntrinsicType("getNeverType"); - }, - function* (): Generator { - return yield* owner.getIntrinsicType.gen("getNeverType"); - }, - ); - } - get getUnknownType(): { - (): Type; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getUnknownType", - function (): Type { - return owner.getIntrinsicType("getUnknownType"); - }, - function* (): Generator { - return yield* owner.getIntrinsicType.gen("getUnknownType"); - }, - ); - } - get getBigIntType(): { - (): Type; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getBigIntType", - function (): Type { - return owner.getIntrinsicType("getBigIntType"); - }, - function* (): Generator { - return yield* owner.getIntrinsicType.gen("getBigIntType"); - }, - ); - } - get getESSymbolType(): { - (): Type; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getESSymbolType", - function (): Type { - return owner.getIntrinsicType("getESSymbolType"); - }, - function* (): Generator { - return yield* owner.getIntrinsicType.gen("getESSymbolType"); - }, - ); - } - get getNonPrimitiveType(): { - (): Type; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getNonPrimitiveType", - function (): Type { - return owner.getIntrinsicType("getNonPrimitiveType"); - }, - function* (): Generator { - return yield* owner.getIntrinsicType.gen("getNonPrimitiveType"); - }, - ); - } - - get typeToTypeNode(): { - (type: Type, enclosingDeclaration?: Node, flags?: number): TypeNode | undefined; - gen(type: Type, enclosingDeclaration?: Node, flags?: number): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "typeToTypeNode", - function (type: Type, enclosingDeclaration?: Node, flags?: number): TypeNode | undefined { - const binaryData = owner.client.apiRequestBinary("typeToTypeNode", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: type.id, - ...(enclosingDeclaration ? { location: getNodeId(enclosingDeclaration) } : {}), - ...(flags !== undefined ? { flags } : {}), - }); - if (!binaryData) return undefined; - return decodeNode(binaryData) as TypeNode; - }, - function* (type: Type, enclosingDeclaration?: Node, flags?: number): Generator { - const binaryData = owner.client.apiRequestBinary("typeToTypeNode", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: type.id, - ...(enclosingDeclaration ? { location: getNodeId(enclosingDeclaration) } : {}), - ...(flags !== undefined ? { flags } : {}), - }); - if (!binaryData) return undefined; - return decodeNode(binaryData) as TypeNode; - }, - ); - } - - get signatureToSignatureDeclaration(): { - (signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): Node | undefined; - gen(signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "signatureToSignatureDeclaration", - function (signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): Node | undefined { - const binaryData = owner.client.apiRequestBinary("signatureToSignatureDeclaration", { - snapshot: owner.snapshotId, - project: owner.project.id, - signature: signature.id, - kind, - ...(enclosingDeclaration ? { location: getNodeId(enclosingDeclaration) } : {}), - ...(flags !== undefined ? { flags } : {}), - }); - if (!binaryData) return undefined; - return decodeNode(binaryData) as Node; - }, - function* (signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): Generator { - const binaryData = owner.client.apiRequestBinary("signatureToSignatureDeclaration", { - snapshot: owner.snapshotId, - project: owner.project.id, - signature: signature.id, - kind, - ...(enclosingDeclaration ? { location: getNodeId(enclosingDeclaration) } : {}), - ...(flags !== undefined ? { flags } : {}), - }); - if (!binaryData) return undefined; - return decodeNode(binaryData) as Node; - }, - ); - } - - get typeToString(): { - (type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string; - gen(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "typeToString", - function (type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string { - const result = owner.client.apiRequest("typeToString", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: type.id, - ...(enclosingDeclaration ? { location: getNodeId(enclosingDeclaration) } : {}), - ...(flags !== undefined ? { flags } : {}), - }); - if (typeof result !== "string") throw new TypeError("typeToString returned a non-string result"); - return result; - }, - function* (type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): Generator { - const result = yield* apiRequest("typeToString", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: type.id, - ...(enclosingDeclaration ? { location: getNodeId(enclosingDeclaration) } : {}), - ...(flags !== undefined ? { flags } : {}), - }); - if (typeof result !== "string") throw new TypeError("typeToString returned a non-string result"); - return result; - }, - ); - } - - get isContextSensitive(): { - (node: Node): boolean; - gen(node: Node): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "isContextSensitive", - function (node: Node): boolean { - return owner.client.apiRequest("isContextSensitive", { - snapshot: owner.snapshotId, - project: owner.project.id, - location: getNodeId(node), - }); - }, - function* (node: Node): Generator { - return yield* apiRequest("isContextSensitive", { - snapshot: owner.snapshotId, - project: owner.project.id, - location: getNodeId(node), - }); - }, - ); - } - - get isArrayType(): { - (type: Type): boolean; - gen(type: Type): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "isArrayType", - function (type: Type): boolean { - return owner.client.apiRequest("isArrayType", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: type.id, - }); - }, - function* (type: Type): Generator { - return yield* apiRequest("isArrayType", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: type.id, - }); - }, - ); - } - - get isTupleType(): { - (type: Type): boolean; - gen(type: Type): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "isTupleType", - function (type: Type): boolean { - return owner.client.apiRequest("isTupleType", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: type.id, - }); - }, - function* (type: Type): Generator { - return yield* apiRequest("isTupleType", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: type.id, - }); - }, - ); - } - - /** - * The following symbols are considered read-only: - * - Properties with a `readonly` modifier - * - Variables declared with `const` - * - Get accessors without matching set accessors - * - Enum members - * - `Object.defineProperty` assignments with `writable: false` or no setter - * - Unions and intersections of the above - */ - get isReadonlySymbol(): { - (symbol: Symbol): boolean; - gen(symbol: Symbol): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "isReadonlySymbol", - function (symbol: Symbol): boolean { - return owner.client.apiRequest("isReadonlySymbol", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: symbol.id, - }); - }, - function* (symbol: Symbol): Generator { - return yield* apiRequest("isReadonlySymbol", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: symbol.id, - }); - }, - ); - } - - /** Get the return type of a signature. Always returns a type. */ - get getReturnTypeOfSignature(): { - (signature: Signature): Type; - gen(signature: Signature): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getReturnTypeOfSignature", - function (signature: Signature): Type { - return signature.getReturnType(); - }, - function* (signature: Signature): Generator { - return yield* signature.getReturnType.gen(); - }, - ); - } - - /** - * Get the rest type of a signature. Always returns a type; a signature with - * no rest parameter yields the `any` type. - */ - get getRestTypeOfSignature(): { - (signature: Signature): Type; - gen(signature: Signature): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getRestTypeOfSignature", - function (signature: Signature): Type { - const data = owner.client.apiRequest("getRestTypeOfSignature", { - snapshot: owner.snapshotId, - project: owner.project.id, - signature: signature.id, - }); - return owner.objectRegistry.getOrCreateType(data); - }, - function* (signature: Signature): Generator { - const data = yield* apiRequest("getRestTypeOfSignature", { - snapshot: owner.snapshotId, - project: owner.project.id, - signature: signature.id, - }); - return owner.objectRegistry.getOrCreateType(data); - }, - ); - } - - get getTypePredicateOfSignature(): { - (signature: Signature): TypePredicate | undefined; - gen(signature: Signature): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getTypePredicateOfSignature", - function (signature: Signature): TypePredicate | undefined { - const data = owner.client.apiRequest("getTypePredicateOfSignature", { - snapshot: owner.snapshotId, - project: owner.project.id, - signature: signature.id, - }); - if (!data) return undefined; - return { - kind: data.kind, - parameterIndex: data.parameterIndex, - parameterName: data.parameterName, - type: data.type ? owner.objectRegistry.getOrCreateType(data.type) : undefined, - } as TypePredicate; - }, - function* (signature: Signature): Generator { - const data = yield* apiRequest("getTypePredicateOfSignature", { - snapshot: owner.snapshotId, - project: owner.project.id, - signature: signature.id, - }); - if (!data) return undefined; - return { - kind: data.kind, - parameterIndex: data.parameterIndex, - parameterName: data.parameterName, - type: data.type ? owner.objectRegistry.getOrCreateType(data.type) : undefined, - } as TypePredicate; - }, - ); - } - - /** - * Get the base types of a class or interface type. A type with no base types - * yields an empty array. - */ - get getBaseTypes(): { - (type: InterfaceType): readonly Type[]; - gen(type: InterfaceType): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getBaseTypes", - function (type: InterfaceType): readonly Type[] { - return type.getBaseTypes() ?? []; - }, - function* (type: InterfaceType): Generator { - return (yield* type.getBaseTypes.gen()) ?? []; - }, - ); - } - - /** Get the apparent type of a type. Always returns a type. */ - get getApparentType(): { - (type: Type): Type; - gen(type: Type): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getApparentType", - function (type: Type): Type { - return type.getApparentType(); - }, - function* (type: Type): Generator { - return yield* type.getApparentType.gen(); - }, - ); - } - - /** Get the reduced type of a type. Always returns a type. */ - get getReducedType(): { - (type: Type): Type; - gen(type: Type): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getReducedType", - function (type: Type): Type { - return type.getReducedType(); - }, - function* (type: Type): Generator { - return yield* type.getReducedType.gen(); - }, - ); - } - - get getPropertiesOfType(): { - (type: Type): readonly Symbol[]; - gen(type: Type): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getPropertiesOfType", - function (type: Type): readonly Symbol[] { - return type.getProperties(); - }, - function* (type: Type): Generator { - return yield* type.getProperties.gen(); - }, - ); - } - - get getIndexInfosOfType(): { - (type: Type): readonly IndexInfo[]; - gen(type: Type): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getIndexInfosOfType", - function (type: Type): readonly IndexInfo[] { - return type.getIndexInfos(); - }, - function* (type: Type): Generator { - return yield* type.getIndexInfos.gen(); - }, - ); - } - - /** - * Get the constraint of a type parameter (the `T` in ``), or - * undefined if it has none. - */ - get getConstraintOfTypeParameter(): { - (type: TypeParameter): Type | undefined; - gen(type: TypeParameter): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getConstraintOfTypeParameter", - function (type: TypeParameter): Type | undefined { - return type.getConstraint(); - }, - function* (type: TypeParameter): Generator { - return yield* type.getConstraint.gen(); - }, - ); - } - - get getDefaultFromTypeParameter(): { - (type: TypeParameter): Type | undefined; - gen(type: TypeParameter): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getDefaultFromTypeParameter", - function (type: TypeParameter): Type | undefined { - return type.getDefault(); - }, - function* (type: TypeParameter): Generator { - return yield* type.getDefault.gen(); - }, - ); - } - - get getBaseConstraintOfType(): { - (type: Type): Type | undefined; - gen(type: Type): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getBaseConstraintOfType", - function (type: Type): Type | undefined { - const data = owner.client.apiRequest("getBaseConstraintOfType", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: type.id, - }); - return data ? owner.objectRegistry.getOrCreateType(data) : undefined; - }, - function* (type: Type): Generator { - const data = yield* apiRequest("getBaseConstraintOfType", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: type.id, - }); - return data ? owner.objectRegistry.getOrCreateType(data) : undefined; - }, - ); - } - - get getPropertyOfType(): { - (type: Type, name: string): Symbol | undefined; - gen(type: Type, name: string): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getPropertyOfType", - function (type: Type, name: string): Symbol | undefined { - const data = owner.client.apiRequest("getPropertyOfType", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: type.id, - name, - }); - return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; - }, - function* (type: Type, name: string): Generator { - const data = yield* apiRequest("getPropertyOfType", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: type.id, - name, - }); - return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; - }, - ); - } - - get getConstantValue(): { - (node: Node): string | number | undefined; - gen(node: Node): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getConstantValue", - function (node: Node): string | number | undefined { - const data = owner.client.apiRequest("getConstantValue", { - snapshot: owner.snapshotId, - project: owner.project.id, - location: getNodeId(node), - }); - return typeof data === "string" || typeof data === "number" ? data : undefined; - }, - function* (node: Node): Generator { - const data = yield* apiRequest("getConstantValue", { - snapshot: owner.snapshotId, - project: owner.project.id, - location: getNodeId(node), - }); - return typeof data === "string" || typeof data === "number" ? data : undefined; - }, - ); - } - - /** Get the signature of a function-like declaration. Always returns a signature. */ - get getSignatureFromDeclaration(): { - (node: Node): Signature; - gen(node: Node): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getSignatureFromDeclaration", - function (node: Node): Signature { - const data = owner.client.apiRequest("getSignatureFromDeclaration", { - snapshot: owner.snapshotId, - project: owner.project.id, - location: getNodeId(node), - }); - return owner.objectRegistry.getOrCreateSignature(data); - }, - function* (node: Node): Generator { - const data = yield* apiRequest("getSignatureFromDeclaration", { - snapshot: owner.snapshotId, - project: owner.project.id, - location: getNodeId(node), - }); - return owner.objectRegistry.getOrCreateSignature(data); - }, - ); - } - - get getExportSpecifierLocalTargetSymbol(): { - (node: Node): Symbol | undefined; - gen(node: Node): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getExportSpecifierLocalTargetSymbol", - function (node: Node): Symbol | undefined { - const data = owner.client.apiRequest("getExportSpecifierLocalTargetSymbol", { - snapshot: owner.snapshotId, - project: owner.project.id, - location: getNodeId(node), - }); - return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; - }, - function* (node: Node): Generator { - const data = yield* apiRequest("getExportSpecifierLocalTargetSymbol", { - snapshot: owner.snapshotId, - project: owner.project.id, - location: getNodeId(node), - }); - return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; - }, - ); - } - - /** - * Follow all aliases to get the original symbol. Always returns a symbol; for - * an unresolved alias the checker yields the unknown symbol (use - * {@link Checker.isUnknownSymbol} to detect it). - */ - get getAliasedSymbol(): { - (symbol: Symbol): Symbol; - gen(symbol: Symbol): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getAliasedSymbol", - function (symbol: Symbol): Symbol { - const data = owner.client.apiRequest("getAliasedSymbol", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: symbol.id, - }); - return owner.objectRegistry.getOrCreateSymbol(data); - }, - function* (symbol: Symbol): Generator { - const data = yield* apiRequest("getAliasedSymbol", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: symbol.id, - }); - return owner.objectRegistry.getOrCreateSymbol(data); - }, - ); - } - - /** - * Get the fully qualified name of a symbol, walking up its parent chain - * (e.g. `"/path/to/module".Namespace.Name`). - */ - get getFullyQualifiedName(): { - (symbol: Symbol): string; - gen(symbol: Symbol): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getFullyQualifiedName", - function (symbol: Symbol): string { - return owner.client.apiRequest("getFullyQualifiedName", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: symbol.id, - }); - }, - function* (symbol: Symbol): Generator { - return yield* apiRequest("getFullyQualifiedName", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: symbol.id, - }); - }, - ); - } - - get getImmediateAliasedSymbol(): { - (symbol: Symbol): Symbol | undefined; - gen(symbol: Symbol): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getImmediateAliasedSymbol", - function (symbol: Symbol): Symbol | undefined { - const data = owner.client.apiRequest("getImmediateAliasedSymbol", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: symbol.id, - }); - return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; - }, - function* (symbol: Symbol): Generator { - const data = yield* apiRequest("getImmediateAliasedSymbol", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: symbol.id, - }); - return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; - }, - ); - } - - /** - * Get the target symbol if instantiated, or the provided symbol otherwise. - */ - get getTargetSymbol(): { - (symbol: Symbol): Symbol; - gen(symbol: Symbol): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getTargetSymbol", - function (symbol: Symbol): Symbol { - if (symbol.checkFlags & CheckFlags.Instantiated) { - const data = owner.client.apiRequest("getTargetSymbol", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: symbol.id, - }); - return owner.objectRegistry.getOrCreateSymbol(data); - } - return symbol; - }, - function* (symbol: Symbol): Generator { - if (symbol.checkFlags & CheckFlags.Instantiated) { - const data = yield* apiRequest("getTargetSymbol", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: symbol.id, - }); - return owner.objectRegistry.getOrCreateSymbol(data); - } - return symbol; - }, - ); - } - - /** - * Fetch (once, then cache) the handle ids of the per-checker singleton - * symbols (unknown, undefined, arguments). These ids are stable for the life - * of the project's checker, so identity checks against them are local after - * the first call. - */ - private get getWellKnownSymbols(): { - (): { unknown: number; undefined: number; arguments: number; }; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getWellKnownSymbols", - function (): { unknown: number; undefined: number; arguments: number; } { - return owner.wellKnownSymbols ??= owner.client.apiRequest("getWellKnownSymbols", { - snapshot: owner.snapshotId, - project: owner.project.id, - }); - }, - function* (): Generator { - return owner.wellKnownSymbols ??= yield* apiRequest("getWellKnownSymbols", { - snapshot: owner.snapshotId, - project: owner.project.id, - }); - }, - ); - } - - /** - * Returns `true` if the symbol is the checker's "unknown" symbol (e.g. the - * result of {@link Checker.getAliasedSymbol} on an unresolved alias). - */ - get isUnknownSymbol(): { - (symbol: Symbol): boolean; - gen(symbol: Symbol): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "isUnknownSymbol", - function (symbol: Symbol): boolean { - return symbol.id === (owner.getWellKnownSymbols()).unknown; - }, - function* (symbol: Symbol): Generator { - return symbol.id === (yield* owner.getWellKnownSymbols.gen()).unknown; - }, - ); - } - - /** - * Returns `true` if the symbol is the checker's "undefined" symbol. - */ - get isUndefinedSymbol(): { - (symbol: Symbol): boolean; - gen(symbol: Symbol): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "isUndefinedSymbol", - function (symbol: Symbol): boolean { - return symbol.id === (owner.getWellKnownSymbols()).undefined; - }, - function* (symbol: Symbol): Generator { - return symbol.id === (yield* owner.getWellKnownSymbols.gen()).undefined; - }, - ); - } - - /** - * Returns `true` if the symbol is the checker's "arguments" symbol. - */ - get isArgumentsSymbol(): { - (symbol: Symbol): boolean; - gen(symbol: Symbol): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "isArgumentsSymbol", - function (symbol: Symbol): boolean { - return symbol.id === (owner.getWellKnownSymbols()).arguments; - }, - function* (symbol: Symbol): Generator { - return symbol.id === (yield* owner.getWellKnownSymbols.gen()).arguments; - }, - ); - } - - /** - * Fetch (once, then cache) the handle id of the per-checker unknown - * signature. This id is stable for the life of the project's checker, so - * identity checks against it are local after the first call. - */ - private get getWellKnownSignatures(): { - (): { unknown: number; }; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getWellKnownSignatures", - function (): { unknown: number; } { - return owner.wellKnownSignatures ??= owner.client.apiRequest("getWellKnownSignatures", { - snapshot: owner.snapshotId, - project: owner.project.id, - }); - }, - function* (): Generator { - return owner.wellKnownSignatures ??= yield* apiRequest("getWellKnownSignatures", { - snapshot: owner.snapshotId, - project: owner.project.id, - }); - }, - ); - } - - /** - * Returns `true` if the signature is the checker's "unknown" signature (e.g. - * the result of {@link Checker.getResolvedSignature} on a call that cannot be - * resolved). - */ - get isUnknownSignature(): { - (signature: Signature): boolean; - gen(signature: Signature): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "isUnknownSignature", - function (signature: Signature): boolean { - return signature.id === (owner.getWellKnownSignatures()).unknown; - }, - function* (signature: Signature): Generator { - return signature.id === (yield* owner.getWellKnownSignatures.gen()).unknown; - }, - ); - } - - get getExportsOfModule(): { - (symbol: Symbol): readonly Symbol[]; - gen(symbol: Symbol): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getExportsOfModule", - function (symbol: Symbol): readonly Symbol[] { - const data = owner.client.apiRequest("getExportsOfModule", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: symbol.id, - }); - return data ? data.map(d => owner.objectRegistry.getOrCreateSymbol(d)) : []; - }, - function* (symbol: Symbol): Generator { - const data = yield* apiRequest("getExportsOfModule", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: symbol.id, - }); - return data ? data.map(d => owner.objectRegistry.getOrCreateSymbol(d)) : []; - }, - ); - } - - get getMemberInModuleExports(): { - (symbol: Symbol, name: string): Symbol | undefined; - gen(symbol: Symbol, name: string): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getMemberInModuleExports", - function (symbol: Symbol, name: string): Symbol | undefined { - const data = owner.client.apiRequest("getMemberInModuleExports", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: symbol.id, - name, - }); - return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; - }, - function* (symbol: Symbol, name: string): Generator { - const data = yield* apiRequest("getMemberInModuleExports", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: symbol.id, - name, - }); - return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; - }, - ); - } - - get getJsDocTagsOfSymbol(): { - (symbol: Symbol): readonly JSDocTagInfo[]; - gen(symbol: Symbol): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getJsDocTagsOfSymbol", - function (symbol: Symbol): readonly JSDocTagInfo[] { - const data = owner.client.apiRequest("getJsDocTags", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: symbol.id, - }); - return data ?? []; - }, - function* (symbol: Symbol): Generator { - const data = yield* apiRequest("getJsDocTags", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: symbol.id, - }); - return data ?? []; - }, - ); - } - - get getDocumentationCommentOfSymbol(): { - (symbol: Symbol): string; - gen(symbol: Symbol): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getDocumentationCommentOfSymbol", - function (symbol: Symbol): string { - return owner.client.apiRequest("getDocumentationComment", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: symbol.id, - }); - }, - function* (symbol: Symbol): Generator { - return yield* apiRequest("getDocumentationComment", { - snapshot: owner.snapshotId, - project: owner.project.id, - symbol: symbol.id, - }); - }, - ); - } - - /** - * Get the type arguments of a type reference (e.g. the `string` in `Array`). - */ - get getTypeArguments(): { - (type: TypeReference): readonly Type[]; - gen(type: TypeReference): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getTypeArguments", - function (type: TypeReference): readonly Type[] { - const data = owner.client.apiRequest("getTypeArguments", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: type.id, - }); - return data ? data.map(d => owner.objectRegistry.getOrCreateType(d)) : []; - }, - function* (type: TypeReference): Generator { - const data = yield* apiRequest("getTypeArguments", { - snapshot: owner.snapshotId, - project: owner.project.id, - type: type.id, - }); - return data ? data.map(d => owner.objectRegistry.getOrCreateType(d)) : []; - }, - ); - } -} - -export interface PrintNodeOptions { - preserveSourceNewlines?: boolean | undefined; - neverAsciiEscape?: boolean | undefined; - terminateUnterminatedLiterals?: boolean | undefined; -} - -export class Emitter { - private client: Client; - - constructor(client: Client) { - this.client = client; - } - - get printNode(): { - (node: Node, options?: PrintNodeOptions): string; - gen(node: Node, options?: PrintNodeOptions): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "printNode", - function (node: Node, options: PrintNodeOptions = {}): string { - const encoded = encodeNode(node); - const base64 = uint8ArrayToBase64(encoded); - return owner.client.apiRequest("printNode", { - data: base64, - ...(options.preserveSourceNewlines !== undefined ? { preserveSourceNewlines: options.preserveSourceNewlines } : {}), - ...(options.neverAsciiEscape !== undefined ? { neverAsciiEscape: options.neverAsciiEscape } : {}), - ...(options.terminateUnterminatedLiterals !== undefined ? { terminateUnterminatedLiterals: options.terminateUnterminatedLiterals } : {}), - }); - }, - function* (node: Node, options: PrintNodeOptions = {}): Generator { - const encoded = encodeNode(node); - const base64 = uint8ArrayToBase64(encoded); - return yield* apiRequest("printNode", { - data: base64, - ...(options.preserveSourceNewlines !== undefined ? { preserveSourceNewlines: options.preserveSourceNewlines } : {}), - ...(options.neverAsciiEscape !== undefined ? { neverAsciiEscape: options.neverAsciiEscape } : {}), - ...(options.terminateUnterminatedLiterals !== undefined ? { terminateUnterminatedLiterals: options.terminateUnterminatedLiterals } : {}), - }); - }, - ); - } -} - -export class SnapshotInternalAPI { - private snapshotId: number; - private client: Client; - - constructor(snapshotId: number, client: Client) { - this.snapshotId = snapshotId; - this.client = client; - } - - /** - * Format a synthesized node with the correct indentation for insertion at a - * specific position in an existing source file. - * - * @param node The synthesized AST node to format. - * @param file The target file where the node will be inserted. - * @param position The UTF-16 code-unit offset in the target file for insertion. - * @returns The formatted text of the node, indented for the insertion position. - */ - get formatNodeForInsertion(): { - (node: Node, file: DocumentIdentifier, position: number): string; - gen(node: Node, file: DocumentIdentifier, position: number): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "formatNodeForInsertion", - function (node: Node, file: DocumentIdentifier, position: number): string { - const data = owner.client.apiRequest("getDefaultProjectForFile", { - snapshot: owner.snapshotId, - file, - }); - if (!data) { - throw new Error(`No project found for file: ${typeof file === "string" ? file : file.uri}`); - } - - const encoded = encodeNode(node); - const base64 = uint8ArrayToBase64(encoded); - return owner.client.apiRequest("formatNodeForInsertion", { - snapshot: owner.snapshotId, - project: data.id, - file, - position, - data: base64, - }); - }, - function* (node: Node, file: DocumentIdentifier, position: number): Generator { - const data = yield* apiRequest("getDefaultProjectForFile", { - snapshot: owner.snapshotId, - file, - }); - if (!data) { - throw new Error(`No project found for file: ${typeof file === "string" ? file : file.uri}`); - } - - const encoded = encodeNode(node); - const base64 = uint8ArrayToBase64(encoded); - return yield* apiRequest("formatNodeForInsertion", { - snapshot: owner.snapshotId, - project: data.id, - file, - position, - data: base64, - }); - }, - ); - } -} - -export class NodeHandle { - /** - * The project this handle was produced in, used as the default for {@link resolve}. - * Node handles are only meaningful within a project's program, so the producing project - * is remembered so callers don't have to pass it explicitly. - */ - private readonly canonicalProject: Project; - readonly index: number; - readonly kind: SyntaxKind; - readonly path: Path; - - constructor(handle: string, canonicalProject: Project) { - const parsed = parseNodeHandle(handle); - this.index = parsed.index; - this.kind = parsed.kind; - this.path = parsed.path; - this.canonicalProject = canonicalProject; - } - - /** - * Resolve this handle to the actual AST node by fetching the source file from a project - * and looking up the node by index. If no project is passed, the project that produced - * the handle is used. - */ - get resolve(): { - (project?: Project): T | undefined; - gen(project?: Project): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "resolve", - function (project: Project = owner.canonicalProject): T | undefined { - const sourceFile = project.program.getSourceFile(owner.path); - if (!sourceFile) { - return undefined; - } - return (sourceFile as unknown as RemoteSourceFile).getOrCreateNodeAtIndex(owner.index) as T | undefined; - }, - function* (project: Project = owner.canonicalProject): Generator { - const sourceFile = yield* project.program.getSourceFile.gen(owner.path); - if (!sourceFile) { - return undefined; - } - return (sourceFile as unknown as RemoteSourceFile).getOrCreateNodeAtIndex(owner.index) as T | undefined; - }, - ); - } -} - -/** A symbol definition paired with all of its reference nodes. */ -export interface ReferencedSymbolEntry { - /** The node handle for the symbol's definition. */ - definition: NodeHandle; - /** The resolved symbol for the definition, if available. */ - symbol?: Symbol | undefined; - /** The node handles for each reference to the symbol. */ - references: NodeHandle[]; -} - -/** A single usage of a signature, pairing the reference name with its call expression (if any). */ -export interface SignatureUsage { - /** The node handle for the name reference. */ - name: NodeHandle; - /** The node handle for the call expression, if the reference is invoked. */ - call?: NodeHandle | undefined; -} - -export class Symbol { - private objectRegistry: SnapshotObjectRegistry; - /** - * The project this symbol was first observed in, used as the default project for - * lookups that need a project context (members/exports/parent). Symbols are shared - * snapshot-wide, so these lookups can otherwise be ambiguous about which project to use. - */ - private readonly canonicalProject: Project; - - readonly id: number; - /** The escaped (`__String`) name, used as the key in member/export tables. */ - readonly escapedName: __String; - /** The display name (escaped underscores removed). */ - readonly name: string; - readonly flags: SymbolFlags; - readonly checkFlags: CheckFlags; - readonly declarations: readonly NodeHandle[]; - readonly valueDeclaration: NodeHandle | undefined; - private readonly parent!: number; - private readonly exportSymbol!: number; - private membersCache: ReadonlyMap<__String, Symbol> | undefined; - private exportsCache: ReadonlyMap<__String, Symbol> | undefined; - - constructor(data: SymbolResponse, objectRegistry: SnapshotObjectRegistry) { - this.objectRegistry = objectRegistry; - - this.id = data.id; - this.escapedName = data.name as __String; - this.name = unescapeLeadingUnderscores(data.name as __String); - this.flags = data.flags; - this.checkFlags = data.checkFlags; - const canonicalProject = objectRegistry.getProject(data.project as Path); - if (!canonicalProject) { - throw new Error(`Symbol ${data.id} references unknown canonical project '${data.project}'`); - } - this.canonicalProject = canonicalProject; - this.declarations = (data.declarations ?? []).map(d => new NodeHandle(d, canonicalProject)); - this.valueDeclaration = data.valueDeclaration ? new NodeHandle(data.valueDeclaration, canonicalProject) : undefined; - - if (data.parent !== undefined) this.parent = data.parent; - if (data.exportSymbol !== undefined) this.exportSymbol = data.exportSymbol; - } - - get getParent(): { - (): Symbol | undefined; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getParent", - function (): Symbol | undefined { - return owner.objectRegistry.fetchSymbol(owner, "getParentOfSymbol", owner.parent, owner.canonicalProject.id); - }, - function* (): Generator { - return yield* owner.objectRegistry.fetchSymbol.gen(owner, "getParentOfSymbol", owner.parent, owner.canonicalProject.id); - }, - ); - } - - /** - * Get this symbol's members keyed by escaped name. The result is cached on - * the symbol, so repeated calls do not round-trip to the server. - */ - get getMembers(): { - (): ReadonlyMap<__String, Symbol>; - gen(): Generator, ProtocolResponse["result"]>; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getMembers", - function (): ReadonlyMap<__String, Symbol> { - return owner.membersCache ??= owner.fetchSymbolTable("getMembersOfSymbol"); - }, - function* (): Generator, ProtocolResponse["result"]> { - return owner.membersCache ??= yield* owner.fetchSymbolTable.gen("getMembersOfSymbol"); - }, - ); - } - - /** - * Get this symbol's exports keyed by escaped name. The result is cached on - * the symbol, so repeated calls do not round-trip to the server. - */ - get getExports(): { - (): ReadonlyMap<__String, Symbol>; - gen(): Generator, ProtocolResponse["result"]>; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getExports", - function (): ReadonlyMap<__String, Symbol> { - return owner.exportsCache ??= owner.fetchSymbolTable("getExportsOfSymbol"); - }, - function* (): Generator, ProtocolResponse["result"]> { - return owner.exportsCache ??= yield* owner.fetchSymbolTable.gen("getExportsOfSymbol"); - }, - ); - } - - private get fetchSymbolTable(): { - (method: SymbolsPropertyMethod): ReadonlyMap<__String, Symbol>; - gen(method: SymbolsPropertyMethod): Generator, ProtocolResponse["result"]>; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "fetchSymbolTable", - function (method: SymbolsPropertyMethod): ReadonlyMap<__String, Symbol> { - const symbols = owner.objectRegistry.fetchSymbols(owner, method, undefined, owner.canonicalProject.id); - const table = new Map<__String, Symbol>(); - for (const symbol of symbols) { - table.set(symbol.escapedName, symbol); - } - return table; - }, - function* (method: SymbolsPropertyMethod): Generator, ProtocolResponse["result"]> { - const symbols = yield* owner.objectRegistry.fetchSymbols.gen(owner, method, undefined, owner.canonicalProject.id); - const table = new Map<__String, Symbol>(); - for (const symbol of symbols) { - table.set(symbol.escapedName, symbol); - } - return table; - }, - ); - } - - get getExportSymbol(): { - (): Symbol; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getExportSymbol", - function (): Symbol { - if (!owner.exportSymbol) return owner; - return owner.objectRegistry.fetchSymbol(owner, "getExportSymbolOfSymbol", owner.exportSymbol, owner.canonicalProject.id); - }, - function* (): Generator { - if (!owner.exportSymbol) return owner; - return yield* owner.objectRegistry.fetchSymbol.gen(owner, "getExportSymbolOfSymbol", owner.exportSymbol, owner.canonicalProject.id); - }, - ); - } - - get getJsDocTags(): { - (checker: Checker): readonly JSDocTagInfo[]; - gen(checker: Checker): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getJsDocTags", - function (checker: Checker): readonly JSDocTagInfo[] { - return checker.getJsDocTagsOfSymbol(owner); - }, - function* (checker: Checker): Generator { - return yield* checker.getJsDocTagsOfSymbol.gen(owner); - }, - ); - } - - get getDocumentationComment(): { - (checker: Checker): string; - gen(checker: Checker): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getDocumentationComment", - function (checker: Checker): string { - return checker.getDocumentationCommentOfSymbol(owner); - }, - function* (checker: Checker): Generator { - return yield* checker.getDocumentationCommentOfSymbol.gen(owner); - }, - ); - } -} - -class TypeObject implements Type { - private objectRegistry: ProjectObjectRegistry; - - // Fields included in TypeResponse. References to other objects are stored as IDs - // and resolved lazily via the object registry. - readonly id: number; - readonly flags: TypeFlags; - readonly objectFlags!: ObjectFlags; - readonly symbol!: number; - readonly value!: string | number | boolean | bigint; - readonly intrinsicName!: string; - readonly isThisType!: boolean; - readonly freshType!: number; - readonly regularType!: number; - readonly target!: number; - readonly typeParameters!: readonly number[]; - readonly outerTypeParameters!: readonly number[]; - readonly localTypeParameters!: readonly number[]; - readonly aliasTypeArguments!: readonly number[]; - readonly aliasSymbol!: number; - readonly elementFlags!: readonly ElementFlags[]; - readonly fixedLength!: number; - readonly readonly!: boolean; - readonly texts!: readonly string[]; - readonly objectType!: number; - readonly indexType!: number; - readonly checkType!: number; - readonly extendsType!: number; - readonly baseType!: number; - readonly substConstraint!: number; - - // Cached results of lazy fetches, not included in TypeResponse - // (typically because they require some amount of computation or - // could cause an arbitrarily large number of types to be cached - // on the server for ID-based lookup). `false` is a sentinel value - // indicating a fetch has not yet occurred. - private trueType: number | false; - private falseType: number | false; - private constraint: number | false; - private default: number | false; - private nonNullableType: number | false; - private apparentType: number | false; - private reducedType: number | false; - private properties: readonly Symbol[] | false; - private apparentProperties: readonly Symbol[] | false; - private callSignatures: readonly Signature[] | false; - private constructSignatures: readonly Signature[] | false; - private indexInfos: readonly IndexInfo[] | false; - private baseTypes: readonly Type[] | false; - private stringIndexType: Type | undefined | false; - private numberIndexType: Type | undefined | false; - - constructor(data: TypeResponse, objectRegistry: ProjectObjectRegistry) { - this.objectRegistry = objectRegistry; - - this.id = data.id; - this.flags = data.flags; - if (data.objectFlags !== undefined) this.objectFlags = data.objectFlags; - if (data.symbol !== undefined) this.symbol = data.symbol; - if (data.value != null) { - // BigInt literal values are serialized as decimal strings (e.g. "-123") because - // JSON cannot represent bigint. Decode them back into a real bigint here. - const value = data.value as string | number | boolean; - this.value = (data.flags & TypeFlags.BigIntLiteral) ? BigInt(value as string) : value; - } - if (data.intrinsicName !== undefined) this.intrinsicName = data.intrinsicName; - if (data.isThisType !== undefined) this.isThisType = data.isThisType; - if (data.freshType !== undefined) this.freshType = data.freshType; - if (data.regularType !== undefined) this.regularType = data.regularType; - if (data.target !== undefined) this.target = data.target; - this.typeParameters = data.typeParameters ?? []; - this.outerTypeParameters = data.outerTypeParameters ?? []; - this.localTypeParameters = data.localTypeParameters ?? []; - this.aliasTypeArguments = data.aliasTypeArguments ?? []; - if (data.aliasSymbol !== undefined) this.aliasSymbol = data.aliasSymbol; - if (data.elementFlags !== undefined) this.elementFlags = data.elementFlags; - if (data.fixedLength !== undefined) this.fixedLength = data.fixedLength; - if (data.readonly !== undefined) this.readonly = data.readonly; - if (data.texts !== undefined) this.texts = data.texts; - if (data.objectType !== undefined) this.objectType = data.objectType; - if (data.indexType !== undefined) this.indexType = data.indexType; - if (data.checkType !== undefined) this.checkType = data.checkType; - if (data.extendsType !== undefined) this.extendsType = data.extendsType; - if (data.baseType !== undefined) this.baseType = data.baseType; - if (data.substConstraint !== undefined) this.substConstraint = data.substConstraint; - - this.trueType = false; - this.falseType = false; - this.constraint = false; - this.default = false; - this.nonNullableType = false; - this.apparentType = false; - this.reducedType = false; - this.properties = false; - this.apparentProperties = false; - this.callSignatures = false; - this.constructSignatures = false; - this.indexInfos = false; - this.baseTypes = false; - this.stringIndexType = false; - this.numberIndexType = false; - } - - get getSymbol(): { - (): Symbol | undefined; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getSymbol", - function (): Symbol | undefined { - return owner.objectRegistry.fetchSymbol(owner, "getSymbolOfType", owner.symbol); - }, - function* (): Generator { - return yield* owner.objectRegistry.fetchSymbol.gen(owner, "getSymbolOfType", owner.symbol); - }, - ); - } - - get getProperties(): { - (): readonly Symbol[]; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getProperties", - function (): readonly Symbol[] { - if (owner.properties === false) { - owner.properties = owner.objectRegistry.fetchPropertiesOfType(owner); - } - return owner.properties; - }, - function* (): Generator { - if (owner.properties === false) { - owner.properties = yield* owner.objectRegistry.fetchPropertiesOfType.gen(owner); - } - return owner.properties; - }, - ); - } - - get getProperty(): { - (propertyName: string): Symbol | undefined; - gen(propertyName: string): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getProperty", - function (propertyName: string): Symbol | undefined { - return owner.objectRegistry.fetchPropertyOfType(owner, propertyName); - }, - function* (propertyName: string): Generator { - return yield* owner.objectRegistry.fetchPropertyOfType.gen(owner, propertyName); - }, - ); - } - - get getApparentProperties(): { - (): readonly Symbol[]; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getApparentProperties", - function (): readonly Symbol[] { - if (owner.apparentProperties === false) { - owner.apparentProperties = owner.objectRegistry.fetchApparentPropertiesOfType(owner); - } - return owner.apparentProperties; - }, - function* (): Generator { - if (owner.apparentProperties === false) { - owner.apparentProperties = yield* owner.objectRegistry.fetchApparentPropertiesOfType.gen(owner); - } - return owner.apparentProperties; - }, - ); - } - - get getCallSignatures(): { - (): readonly Signature[]; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getCallSignatures", - function (): readonly Signature[] { - if (owner.callSignatures === false) { - owner.callSignatures = owner.objectRegistry.fetchSignaturesOfType(owner, SignatureKind.Call); - } - return owner.callSignatures; - }, - function* (): Generator { - if (owner.callSignatures === false) { - owner.callSignatures = yield* owner.objectRegistry.fetchSignaturesOfType.gen(owner, SignatureKind.Call); - } - return owner.callSignatures; - }, - ); - } - - get getConstructSignatures(): { - (): readonly Signature[]; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getConstructSignatures", - function (): readonly Signature[] { - if (owner.constructSignatures === false) { - owner.constructSignatures = owner.objectRegistry.fetchSignaturesOfType(owner, SignatureKind.Construct); - } - return owner.constructSignatures; - }, - function* (): Generator { - if (owner.constructSignatures === false) { - owner.constructSignatures = yield* owner.objectRegistry.fetchSignaturesOfType.gen(owner, SignatureKind.Construct); - } - return owner.constructSignatures; - }, - ); - } - - get getNonNullableType(): { - (): Type; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getNonNullableType", - function (): Type { - const result = owner.objectRegistry.fetchType(owner, "getNonNullableType", owner.nonNullableType); - owner.nonNullableType = result.id; - return result; - }, - function* (): Generator { - const result = yield* owner.objectRegistry.fetchType.gen(owner, "getNonNullableType", owner.nonNullableType); - owner.nonNullableType = result.id; - return result; - }, - ); - } - - get getStringIndexType(): { - (): Type | undefined; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getStringIndexType", - function (): Type | undefined { - if (owner.stringIndexType === false) { - owner.stringIndexType = owner.getStringIndexTypeWorker(); - } - return owner.stringIndexType; - }, - function* (): Generator { - if (owner.stringIndexType === false) { - owner.stringIndexType = yield* owner.getStringIndexTypeWorker.gen(); - } - return owner.stringIndexType; - }, - ); - } - - private get getStringIndexTypeWorker(): { - (): Type | undefined; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getStringIndexTypeWorker", - function (): Type | undefined { - const infos = owner.getIndexInfos(); - return infos.find(info => (info.keyType.flags & TypeFlags.String) !== 0)?.valueType; - }, - function* (): Generator { - const infos = yield* owner.getIndexInfos.gen(); - return infos.find(info => (info.keyType.flags & TypeFlags.String) !== 0)?.valueType; - }, - ); - } - - get getNumberIndexType(): { - (): Type | undefined; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getNumberIndexType", - function (): Type | undefined { - if (owner.numberIndexType === false) { - owner.numberIndexType = owner.getNumberIndexTypeWorker(); - } - return owner.numberIndexType; - }, - function* (): Generator { - if (owner.numberIndexType === false) { - owner.numberIndexType = yield* owner.getNumberIndexTypeWorker.gen(); - } - return owner.numberIndexType; - }, - ); - } - - private get getNumberIndexTypeWorker(): { - (): Type | undefined; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getNumberIndexTypeWorker", - function (): Type | undefined { - const infos = owner.getIndexInfos(); - return infos.find(info => (info.keyType.flags & TypeFlags.Number) !== 0)?.valueType; - }, - function* (): Generator { - const infos = yield* owner.getIndexInfos.gen(); - return infos.find(info => (info.keyType.flags & TypeFlags.Number) !== 0)?.valueType; - }, - ); - } - - get getApparentType(): { - (): Type; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getApparentType", - function (): Type { - const result = owner.objectRegistry.fetchType(owner, "getApparentType", owner.apparentType); - owner.apparentType = result.id; - return result; - }, - function* (): Generator { - const result = yield* owner.objectRegistry.fetchType.gen(owner, "getApparentType", owner.apparentType); - owner.apparentType = result.id; - return result; - }, - ); - } - - get getReducedType(): { - (): Type; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getReducedType", - function (): Type { - const result = owner.objectRegistry.fetchType(owner, "getReducedType", owner.reducedType); - owner.reducedType = result.id; - return result; - }, - function* (): Generator { - const result = yield* owner.objectRegistry.fetchType.gen(owner, "getReducedType", owner.reducedType); - owner.reducedType = result.id; - return result; - }, - ); - } - - get getIndexInfos(): { - (): readonly IndexInfo[]; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getIndexInfos", - function (): readonly IndexInfo[] { - if (owner.indexInfos === false) { - owner.indexInfos = owner.objectRegistry.fetchIndexInfosOfType(owner); - } - return owner.indexInfos; - }, - function* (): Generator { - if (owner.indexInfos === false) { - owner.indexInfos = yield* owner.objectRegistry.fetchIndexInfosOfType.gen(owner); - } - return owner.indexInfos; - }, - ); - } - - get getAliasSymbol(): { - (): Symbol | undefined; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getAliasSymbol", - function (): Symbol | undefined { - return owner.objectRegistry.fetchSymbol(owner, "getAliasSymbolOfType", owner.aliasSymbol); - }, - function* (): Generator { - return yield* owner.objectRegistry.fetchSymbol.gen(owner, "getAliasSymbolOfType", owner.aliasSymbol); - }, - ); - } - - get getTarget(): { - (): Type; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getTarget", - function (): Type { - return owner.objectRegistry.fetchType(owner, "getTargetOfType", owner.target); - }, - function* (): Generator { - return yield* owner.objectRegistry.fetchType.gen(owner, "getTargetOfType", owner.target); - }, - ); - } - - get getFreshType(): { - (): FreshableType | undefined; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getFreshType", - function (): FreshableType | undefined { - return owner.objectRegistry.fetchOptionalType(owner, "getFreshTypeOfType", owner.freshType); - }, - function* (): Generator { - return yield* owner.objectRegistry.fetchOptionalType.gen(owner, "getFreshTypeOfType", owner.freshType); - }, - ); - } - - get getRegularType(): { - (): FreshableType | undefined; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getRegularType", - function (): FreshableType | undefined { - return owner.objectRegistry.fetchOptionalType(owner, "getRegularTypeOfType", owner.regularType); - }, - function* (): Generator { - return yield* owner.objectRegistry.fetchOptionalType.gen(owner, "getRegularTypeOfType", owner.regularType); - }, - ); - } - - get getTypes(): { - (): readonly Type[] | undefined; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getTypes", - function (): readonly Type[] | undefined { - // Only union, intersection, and template literal types have constituent - // types; any other kind has none, so return undefined rather than sending - // a request the server cannot satisfy. - if (!(owner.flags & (TypeFlags.UnionOrIntersection | TypeFlags.TemplateLiteral))) { - return undefined; - } - return owner.objectRegistry.fetchTypes(owner, "getTypesOfType"); - }, - function* (): Generator { - // Only union, intersection, and template literal types have constituent - // types; any other kind has none, so return undefined rather than sending - // a request the server cannot satisfy. - if (!(owner.flags & (TypeFlags.UnionOrIntersection | TypeFlags.TemplateLiteral))) { - return undefined; - } - return yield* owner.objectRegistry.fetchTypes.gen(owner, "getTypesOfType"); - }, - ); - } - - get getTypeParameters(): { - (): readonly TypeParameter[]; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getTypeParameters", - function (): readonly TypeParameter[] { - return owner.objectRegistry.fetchTypes(owner, "getTypeParametersOfType", owner.typeParameters) as readonly TypeParameter[]; - }, - function* (): Generator { - return (yield* owner.objectRegistry.fetchTypes.gen(owner, "getTypeParametersOfType", owner.typeParameters)) as readonly TypeParameter[]; - }, - ); - } - - get getOuterTypeParameters(): { - (): readonly TypeParameter[]; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getOuterTypeParameters", - function (): readonly TypeParameter[] { - return owner.objectRegistry.fetchTypes(owner, "getOuterTypeParametersOfType", owner.outerTypeParameters) as readonly TypeParameter[]; - }, - function* (): Generator { - return (yield* owner.objectRegistry.fetchTypes.gen(owner, "getOuterTypeParametersOfType", owner.outerTypeParameters)) as readonly TypeParameter[]; - }, - ); - } - - get getLocalTypeParameters(): { - (): readonly TypeParameter[]; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getLocalTypeParameters", - function (): readonly TypeParameter[] { - return owner.objectRegistry.fetchTypes(owner, "getLocalTypeParametersOfType", owner.localTypeParameters) as readonly TypeParameter[]; - }, - function* (): Generator { - return (yield* owner.objectRegistry.fetchTypes.gen(owner, "getLocalTypeParametersOfType", owner.localTypeParameters)) as readonly TypeParameter[]; - }, - ); - } - - get getAliasTypeArguments(): { - (): readonly Type[]; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getAliasTypeArguments", - function (): readonly Type[] { - return owner.objectRegistry.fetchTypes(owner, "getAliasTypeArgumentsOfType", owner.aliasTypeArguments); - }, - function* (): Generator { - return yield* owner.objectRegistry.fetchTypes.gen(owner, "getAliasTypeArgumentsOfType", owner.aliasTypeArguments); - }, - ); - } - - get getObjectType(): { - (): Type; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getObjectType", - function (): Type { - return owner.objectRegistry.fetchType(owner, "getObjectTypeOfType", owner.objectType); - }, - function* (): Generator { - return yield* owner.objectRegistry.fetchType.gen(owner, "getObjectTypeOfType", owner.objectType); - }, - ); - } - - get getIndexType(): { - (): Type; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getIndexType", - function (): Type { - return owner.objectRegistry.fetchType(owner, "getIndexTypeOfType", owner.indexType); - }, - function* (): Generator { - return yield* owner.objectRegistry.fetchType.gen(owner, "getIndexTypeOfType", owner.indexType); - }, - ); - } - - get getCheckType(): { - (): Type; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getCheckType", - function (): Type { - return owner.objectRegistry.fetchType(owner, "getCheckTypeOfType", owner.checkType); - }, - function* (): Generator { - return yield* owner.objectRegistry.fetchType.gen(owner, "getCheckTypeOfType", owner.checkType); - }, - ); - } - - get getExtendsType(): { - (): Type; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getExtendsType", - function (): Type { - return owner.objectRegistry.fetchType(owner, "getExtendsTypeOfType", owner.extendsType); - }, - function* (): Generator { - return yield* owner.objectRegistry.fetchType.gen(owner, "getExtendsTypeOfType", owner.extendsType); - }, - ); - } - - get getBaseType(): { - (): Type; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getBaseType", - function (): Type { - return owner.objectRegistry.fetchType(owner, "getBaseTypeOfType", owner.baseType); - }, - function* (): Generator { - return yield* owner.objectRegistry.fetchType.gen(owner, "getBaseTypeOfType", owner.baseType); - }, - ); - } - - get getConstraint(): { - (): Type | undefined; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getConstraint", - function (): Type | undefined { - // Type parameters resolve their constraint lazily through the checker, - // whereas substitution types carry a preloaded constraint handle. - if (owner.flags & TypeFlags.TypeParameter) { - const result = owner.objectRegistry.fetchOptionalType(owner, "getConstraintOfTypeParameter", owner.constraint); - owner.constraint = result ? result.id : 0; - return result; - } - return owner.objectRegistry.fetchType(owner, "getConstraintOfType", owner.substConstraint); - }, - function* (): Generator { - // Type parameters resolve their constraint lazily through the checker, - // whereas substitution types carry a preloaded constraint handle. - if (owner.flags & TypeFlags.TypeParameter) { - const result = yield* owner.objectRegistry.fetchOptionalType.gen(owner, "getConstraintOfTypeParameter", owner.constraint); - owner.constraint = result ? result.id : 0; - return result; - } - return yield* owner.objectRegistry.fetchType.gen(owner, "getConstraintOfType", owner.substConstraint); - }, - ); - } - - get getDefault(): { - (): Type | undefined; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getDefault", - function (): Type | undefined { - const result = owner.objectRegistry.fetchOptionalType(owner, "getDefaultFromTypeParameter", owner.default); - owner.default = result ? result.id : 0; - return result; - }, - function* (): Generator { - const result = yield* owner.objectRegistry.fetchOptionalType.gen(owner, "getDefaultFromTypeParameter", owner.default); - owner.default = result ? result.id : 0; - return result; - }, - ); - } - - get getTrueType(): { - (): Type; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getTrueType", - function (): Type { - const result = owner.objectRegistry.fetchType(owner, "getTrueTypeOfConditionalType", owner.trueType); - owner.trueType = result.id; - return result; - }, - function* (): Generator { - const result = yield* owner.objectRegistry.fetchType.gen(owner, "getTrueTypeOfConditionalType", owner.trueType); - owner.trueType = result.id; - return result; - }, - ); - } - - get getFalseType(): { - (): Type; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getFalseType", - function (): Type { - const result = owner.objectRegistry.fetchType(owner, "getFalseTypeOfConditionalType", owner.falseType); - owner.falseType = result.id; - return result; - }, - function* (): Generator { - const result = yield* owner.objectRegistry.fetchType.gen(owner, "getFalseTypeOfConditionalType", owner.falseType); - owner.falseType = result.id; - return result; - }, - ); - } - - /** - * Get the base types of this type. Returns `undefined` for any type that is - * not a class or interface. - */ - get getBaseTypes(): { - (): readonly Type[] | undefined; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getBaseTypes", - function (): readonly Type[] | undefined { - if (!owner.isClassOrInterface()) { - return undefined; - } - if (owner.baseTypes === false) { - owner.baseTypes = owner.objectRegistry.fetchBaseTypes(owner); - } - return owner.baseTypes; - }, - function* (): Generator { - if (!owner.isClassOrInterface()) { - return undefined; - } - if (owner.baseTypes === false) { - owner.baseTypes = yield* owner.objectRegistry.fetchBaseTypes.gen(owner); - } - return owner.baseTypes; - }, - ); - } - - isClassOrInterface(): this is InterfaceType { - return isClassOrInterfaceType(this); - } - - isUnionType(): this is UnionType { - return isUnionType(this); - } - - isIntersectionType(): this is IntersectionType { - return isIntersectionType(this); - } - - isObjectType(): this is ObjectType { - return isObjectType(this); - } - - isIntrinsicType(): this is IntrinsicType { - return isIntrinsicType(this); - } - - isErrorType(): boolean { - return isErrorType(this); - } - - isLiteralType(): this is LiteralType { - return isLiteralType(this); - } - - isStringLiteralType(): this is StringLiteralType { - return isStringLiteralType(this); - } - - isNumberLiteralType(): this is NumberLiteralType { - return isNumberLiteralType(this); - } - - isBigIntLiteralType(): this is BigIntLiteralType { - return isBigIntLiteralType(this); - } - - isBooleanLiteralType(): this is BooleanLiteralType { - return isBooleanLiteralType(this); - } - - isTypeReference(): this is TypeReference { - return isTypeReference(this); - } - - isTupleType(): this is TupleType { - return isTupleType(this); - } - - isIndexType(): this is IndexType { - return isIndexType(this); - } - - isIndexedAccessType(): this is IndexedAccessType { - return isIndexedAccessType(this); - } - - isConditionalType(): this is ConditionalType { - return isConditionalType(this); - } - - isSubstitutionType(): this is SubstitutionType { - return isSubstitutionType(this); - } - - isTemplateLiteralType(): this is TemplateLiteralType { - return isTemplateLiteralType(this); - } - - isStringMappingType(): this is StringMappingType { - return isStringMappingType(this); - } - - isTypeParameter(): this is TypeParameter { - return isTypeParameter(this); - } -} - -export function isUnionType(type: Type): type is UnionType { - return (type.flags & TypeFlags.Union) !== 0; -} - -export function isIntersectionType(type: Type): type is IntersectionType { - return (type.flags & TypeFlags.Intersection) !== 0; -} - -export function isObjectType(type: Type): type is ObjectType { - return (type.flags & TypeFlags.Object) !== 0; -} - -export function isClassOrInterfaceType(type: Type): type is InterfaceType { - return isObjectType(type) && (type.objectFlags & ObjectFlags.ClassOrInterface) !== 0; -} - -export function isIntrinsicType(type: Type): type is IntrinsicType { - return (type.flags & TypeFlags.Intrinsic) !== 0; -} - -/** - * Whether this is the error type — the placeholder the checker produces when a - * type cannot be determined (e.g. an unresolved reference). It is an intrinsic - * type named `"error"` (this covers both the singleton error type and the - * per-alias error types manufactured for unresolved type alias references). - */ -export function isErrorType(type: Type): boolean { - return isIntrinsicType(type) && type.intrinsicName === "error"; -} - -export function isLiteralType(type: Type): type is LiteralType { - return (type.flags & TypeFlags.Literal) !== 0; -} - -export function isStringLiteralType(type: Type): type is StringLiteralType { - return (type.flags & TypeFlags.StringLiteral) !== 0; -} - -export function isNumberLiteralType(type: Type): type is NumberLiteralType { - return (type.flags & TypeFlags.NumberLiteral) !== 0; -} - -export function isBigIntLiteralType(type: Type): type is BigIntLiteralType { - return (type.flags & TypeFlags.BigIntLiteral) !== 0; -} - -export function isBooleanLiteralType(type: Type): type is BooleanLiteralType { - return (type.flags & TypeFlags.BooleanLiteral) !== 0; -} - -export function isTypeReference(type: Type): type is TypeReference { - return isObjectType(type) && (type.objectFlags & ObjectFlags.Reference) !== 0; -} - -export function isTupleType(type: Type): type is TupleType { - return isObjectType(type) && (type.objectFlags & ObjectFlags.Tuple) !== 0; -} - -export function isIndexType(type: Type): type is IndexType { - return (type.flags & TypeFlags.Index) !== 0; -} - -export function isIndexedAccessType(type: Type): type is IndexedAccessType { - return (type.flags & TypeFlags.IndexedAccess) !== 0; -} - -export function isConditionalType(type: Type): type is ConditionalType { - return (type.flags & TypeFlags.Conditional) !== 0; -} - -export function isSubstitutionType(type: Type): type is SubstitutionType { - return (type.flags & TypeFlags.Substitution) !== 0; -} - -export function isTemplateLiteralType(type: Type): type is TemplateLiteralType { - return (type.flags & TypeFlags.TemplateLiteral) !== 0; -} - -export function isStringMappingType(type: Type): type is StringMappingType { - return (type.flags & TypeFlags.StringMapping) !== 0; -} - -export function isTypeParameter(type: Type): type is TypeParameter { - return (type.flags & TypeFlags.TypeParameter) !== 0; -} - -export class Signature { - private flags: number; - private objectRegistry: ProjectObjectRegistry; - - readonly id: number; - readonly declaration?: NodeHandle | undefined; - readonly typeParameters?: readonly number[] | undefined; - readonly parameters: readonly number[]; - readonly thisParameter?: number | undefined; - readonly target?: number | undefined; - private returnType: number | false; - - constructor(data: SignatureResponse, project: Project, objectRegistry: ProjectObjectRegistry) { - this.id = data.id; - this.flags = data.flags; - this.objectRegistry = objectRegistry; - this.declaration = data.declaration ? new NodeHandle(data.declaration, project) : undefined; - this.typeParameters = data.typeParameters ?? []; - this.parameters = data.parameters ?? []; - this.thisParameter = data.thisParameter; - this.target = data.target; - this.returnType = false; - } - - get getTypeParameters(): { - (): readonly TypeParameter[]; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getTypeParameters", - function (): readonly TypeParameter[] { - return owner.objectRegistry.fetchTypes(owner, "getTypeParametersOfSignature", owner.typeParameters) as readonly TypeParameter[]; - }, - function* (): Generator { - return (yield* owner.objectRegistry.fetchTypes.gen(owner, "getTypeParametersOfSignature", owner.typeParameters)) as readonly TypeParameter[]; - }, - ); - } - - get getParameters(): { - (): readonly Symbol[]; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getParameters", - function (): readonly Symbol[] { - return owner.objectRegistry.fetchSymbols(owner, "getParametersOfSignature", owner.parameters); - }, - function* (): Generator { - return yield* owner.objectRegistry.fetchSymbols.gen(owner, "getParametersOfSignature", owner.parameters); - }, - ); - } - - get getThisParameter(): { - (): Symbol | undefined; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getThisParameter", - function (): Symbol | undefined { - return owner.objectRegistry.fetchSymbol(owner, "getThisParameterOfSignature", owner.thisParameter); - }, - function* (): Generator { - return yield* owner.objectRegistry.fetchSymbol.gen(owner, "getThisParameterOfSignature", owner.thisParameter); - }, - ); - } - - get getTarget(): { - (): Signature | undefined; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getTarget", - function (): Signature | undefined { - return owner.objectRegistry.fetchSignature(owner, "getTargetOfSignature", owner.target); - }, - function* (): Generator { - return yield* owner.objectRegistry.fetchSignature.gen(owner, "getTargetOfSignature", owner.target); - }, - ); - } - - get getReturnType(): { - (): Type; - gen(): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getReturnType", - function (): Type { - const result = owner.objectRegistry.fetchType(owner, "getReturnTypeOfSignature", owner.returnType); - owner.returnType = result.id; - return result; - }, - function* (): Generator { - const result = yield* owner.objectRegistry.fetchType.gen(owner, "getReturnTypeOfSignature", owner.returnType); - owner.returnType = result.id; - return result; - }, - ); - } - - get getTypeParameterAtPosition(): { - (pos: number): Type; - gen(pos: number): Generator; - } { - const owner = this; - return cacheGeneratorMethod( - owner, - "getTypeParameterAtPosition", - function (pos: number): Type { - return owner.objectRegistry.fetchTypeParameterAtPosition(owner, pos); - }, - function* (pos: number): Generator { - return yield* owner.objectRegistry.fetchTypeParameterAtPosition.gen(owner, pos); - }, - ); - } - - get hasRestParameter(): boolean { - return (this.flags & SignatureFlags.HasRestParameter) !== 0; - } - - get isConstruct(): boolean { - return (this.flags & SignatureFlags.Construct) !== 0; - } - - get isAbstract(): boolean { - return (this.flags & SignatureFlags.Abstract) !== 0; - } -} +// +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// !!! THIS FILE IS AUTO-GENERATED - DO NOT EDIT !!! +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// +// Source: src/api/async/api.ts +// Regenerate: npm run generate (from packages/typescript) +// +import type { + APIRequest as ProtocolRequest, + APIResponse as ProtocolResponse, +} from "../proto.ts"; +import { + apiRequest, + cacheGeneratorMethod, +} from "./generatorSupport.ts"; + +import { CheckFlags } from "#enums/checkFlags"; +import { CompletionItemKind } from "#enums/completionItemKind"; +import { DiagnosticCategory } from "#enums/diagnosticCategory"; +import { ElementFlags } from "#enums/elementFlags"; +import { EmitOnly } from "#enums/emitOnly"; +import { ModuleKind } from "#enums/moduleKind"; +import { NewLineKind } from "#enums/newLineKind"; +import { NodeBuilderFlags } from "#enums/nodeBuilderFlags"; +import { ObjectFlags } from "#enums/objectFlags"; +import { SignatureFlags } from "#enums/signatureFlags"; +import { SignatureKind } from "#enums/signatureKind"; +import { SymbolFlags } from "#enums/symbolFlags"; +import { TypeFlags } from "#enums/typeFlags"; +import { TypeFormatFlags } from "#enums/typeFormatFlags"; +import { TypePredicateKind } from "#enums/typePredicateKind"; +import { + type __String, + type Declaration, + type Expression, + type Identifier, + type IndexSignatureDeclaration, + ModifierFlags, + type Node, + type Path, + type SourceFile, + type SyntaxKind, + type TypeNode, + unescapeLeadingUnderscores, +} from "../../ast/index.ts"; +import { assertNever } from "../../internal/utils.ts"; +import { + encodeNode, + uint8ArrayToBase64, +} from "../node/encoder.ts"; +import { + decodeNode, + getNodeId, + parseNodeHandle, + readParseOptionsKey, + readSourceFileHash, + RemoteSourceFile, +} from "../node/node.ts"; +import { Wtf8Decoder } from "../node/wtf8.ts"; +import type { + APIOptions, + LSPConnectionOptions, +} from "../options.ts"; +import { + createGetCanonicalFileName, + toPath, +} from "../path.ts"; +import type { + CompilerOptions, + Diagnostic, + DocumentIdentifier, + DocumentPosition, + EmitOutputResponse as ProtocolEmitOutputResponse, + ImportAdderAction, + IntrinsicTypeMethod, + LSPUpdateSnapshotParams, + ParsedCommandLine, + ProjectReference, + ProjectResponse, + ReadConfigFileResponse, + SignaturePropertyMethod, + SignatureResponse, + SourceFileMetadata, + SymbolPropertyMethod, + SymbolResponse, + SymbolsPropertyMethod, + TextEdit, + TypeAcquisition, + TypePropertyMethod, + TypeResponse, + TypesPropertyMethod, + UpdateSnapshotParams, + UpdateSnapshotResponse, +} from "../proto.ts"; +import { + resolveFileName, + toUpdateSnapshotRequest, +} from "../proto.ts"; +import { SourceFileCache } from "../sourceFileCache.ts"; +import type { + RequestTiming, + TimingAccumulators, + TimingInfo, +} from "../timing.ts"; +import { + Client, + type ClientSocketOptions, + type ClientSpawnOptions, +} from "./client.ts"; +import type { + AssertsIdentifierTypePredicate, + AssertsThisTypePredicate, + BigIntLiteralType, + BooleanLiteralType, + CompletionEntry, + CompletionInfo, + CompletionOptions, + ConditionalType, + EmitOutput, + EmitOutputFile, + EmitResult, + FormatDiagnosticsHost, + FreshableType, + GetImportEditsForSymbolsOptions, + IdentifierTypePredicate, + ImportAdderAction as APIImportAdderAction, + IndexedAccessType, + IndexInfo, + IndexType, + InterfaceType, + IntersectionType, + IntrinsicType, + JSDocTagInfo, + LiteralType, + NumberLiteralType, + ObjectType, + StringLiteralType, + StringMappingType, + StructuredType, + SubstitutionType, + TemplateLiteralType, + ThisTypePredicate, + TupleType, + Type, + TypeParameter, + TypePredicate, + TypePredicateBase, + TypeReference, + UnionOrIntersectionType, + UnionType, +} from "./types.ts"; + +export { formatDiagnostics, formatDiagnosticsWithColorAndContext } from "../diagnosticFormatter.ts"; +export { documentURIToFileName, fileNameToDocumentURI } from "../path.ts"; +export { CheckFlags, CompletionItemKind, DiagnosticCategory, ElementFlags, EmitOnly, ModifierFlags, ModuleKind, NodeBuilderFlags, ObjectFlags, SignatureFlags, SignatureKind, SymbolFlags, TypeFlags, TypeFormatFlags, TypePredicateKind }; +export type { + APIImportAdderAction as ImportAdderAction, + APIOptions, + AssertsIdentifierTypePredicate, + AssertsThisTypePredicate, + BigIntLiteralType, + BooleanLiteralType, + ClientSocketOptions, + ClientSpawnOptions, + CompilerOptions, + CompletionEntry, + CompletionInfo, + CompletionOptions, + ConditionalType, + Diagnostic, + DocumentIdentifier, + DocumentPosition, + EmitOutput, + EmitOutputFile, + EmitResult, + FormatDiagnosticsHost, + FreshableType, + GetImportEditsForSymbolsOptions, + IdentifierTypePredicate, + IndexedAccessType, + IndexInfo, + IndexType, + InterfaceType, + IntersectionType, + IntrinsicType, + JSDocTagInfo, + LiteralType, + LSPConnectionOptions, + NumberLiteralType, + ObjectType, + ParsedCommandLine, + ProjectReference, + ReadConfigFileResponse, + RequestTiming, + SourceFileMetadata, + StringLiteralType, + StringMappingType, + StructuredType, + SubstitutionType, + TemplateLiteralType, + TextEdit, + ThisTypePredicate, + TimingAccumulators, + TimingInfo, + TupleType, + Type, + TypeAcquisition, + TypeParameter, + TypePredicate, + TypePredicateBase, + TypeReference, + UnionOrIntersectionType, + UnionType, +}; + +export interface TranspileOptions { + compilerOptions?: CompilerOptions; + fileName?: string; + reportDiagnostics?: boolean; +} + +export interface TranspileOutput { + outputText: string; + diagnostics?: readonly Diagnostic[]; + sourceMapText?: string; +} + +export { all } from "./generatorSupport.ts"; +import { + all, + type APIRequestGenerator, + type ExecutedGeneratorsResults, +} from "./generatorSupport.ts"; + +export class API implements FormatDiagnosticsHost { + private client: Client; + private sourceFileCache: SourceFileCache; + private toPath: ((fileName: string) => Path) | undefined; + private currentDirectory: string | undefined; + private getCanonicalFileNameWorker: ((fileName: string) => string) | undefined; + private initialized: boolean = false; + private activeSnapshots: Set = new Set(); + private latestSnapshot: Snapshot | undefined; + readonly internal: InternalAPI; + + constructor(options: APIOptions | LSPConnectionOptions = {}) { + this.client = new Client(options); + this.sourceFileCache = new SourceFileCache(); + this.internal = new InternalAPI(this.client, this.ensureInitialized); + } + + /** + * Create an API instance from an existing LSP connection's API session. + * Use this when connecting to an API pipe provided by an LSP server via custom/initializeAPISession. + */ + static get fromLSPConnection(): { + (options: LSPConnectionOptions): API; + gen(options: LSPConnectionOptions): Generator, ProtocolResponse["result"]>; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "fromLSPConnection", + function (options: LSPConnectionOptions): API { + const api = new API(options); + api.ensureInitialized(); + return api; + }, + function* (options: LSPConnectionOptions): Generator, ProtocolResponse["result"]> { + const api = new API(options); + yield* api.ensureInitialized.gen(); + return api; + }, + ); + } + + batch(...requestGenerators: T): ExecutedGeneratorsResults { + const batches = all(...requestGenerators); + let state = batches.next(); + while (!state.done) { + state = batches.next(this.client.batchRequests(state.value).responses); + } + return state.value; + } + + private get ensureInitialized(): { + (): void; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "ensureInitialized", + function (): void { + if (!owner.initialized) { + const response = owner.client.apiRequest("initialize", null); + const getCanonicalFileName = createGetCanonicalFileName(response.useCaseSensitiveFileNames); + const currentDirectory = response.currentDirectory; + owner.getCanonicalFileNameWorker = getCanonicalFileName; + owner.currentDirectory = currentDirectory; + owner.toPath = (fileName: string) => toPath(fileName, currentDirectory, getCanonicalFileName) as Path; + owner.initialized = true; + } + }, + function* (): Generator { + if (!owner.initialized) { + const response = yield* apiRequest("initialize", null); + const getCanonicalFileName = createGetCanonicalFileName(response.useCaseSensitiveFileNames); + const currentDirectory = response.currentDirectory; + owner.getCanonicalFileNameWorker = getCanonicalFileName; + owner.currentDirectory = currentDirectory; + owner.toPath = (fileName: string) => toPath(fileName, currentDirectory, getCanonicalFileName) as Path; + owner.initialized = true; + } + }, + ); + } + + getCurrentDirectory(): string { + if (this.currentDirectory === undefined) { + throw new Error("API has not been initialized"); + } + return this.currentDirectory; + } + + getCanonicalFileName(fileName: string): string { + if (this.getCanonicalFileNameWorker === undefined) { + throw new Error("API has not been initialized"); + } + return this.getCanonicalFileNameWorker(fileName); + } + + getNewLine(): string { + return "\n"; + } + + get parseConfigFile(): { + (file: DocumentIdentifier): ParsedCommandLine; + gen(file: DocumentIdentifier): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "parseConfigFile", + function (file: DocumentIdentifier): ParsedCommandLine { + owner.ensureInitialized(); + return owner.client.apiRequest("parseConfigFile", { file }); + }, + function* (file: DocumentIdentifier): Generator { + yield* owner.ensureInitialized.gen(); + return yield* apiRequest("parseConfigFile", { file }); + }, + ); + } + + get parseCommandLine(): { + (commandLine: readonly string[]): ParsedCommandLine; + gen(commandLine: readonly string[]): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "parseCommandLine", + function (commandLine: readonly string[]): ParsedCommandLine { + owner.ensureInitialized(); + return owner.client.apiRequest("parseCommandLine", { commandLine }); + }, + function* (commandLine: readonly string[]): Generator { + yield* owner.ensureInitialized.gen(); + return yield* apiRequest("parseCommandLine", { commandLine }); + }, + ); + } + + get readConfigFile(): { + (file: DocumentIdentifier): ReadConfigFileResponse; + gen(file: DocumentIdentifier): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "readConfigFile", + function (file: DocumentIdentifier): ReadConfigFileResponse { + owner.ensureInitialized(); + return owner.client.apiRequest("readConfigFile", { file }); + }, + function* (file: DocumentIdentifier): Generator { + yield* owner.ensureInitialized.gen(); + return yield* apiRequest("readConfigFile", { file }); + }, + ); + } + + get parseJsonConfigFileContent(): { + ( + json: any, + options: + | { configDirectory: string; configFileName?: never; } + | { configFileName: DocumentIdentifier; configDirectory?: never; }, + ): ParsedCommandLine; + gen( + json: any, + options: + | { configDirectory: string; configFileName?: never; } + | { configFileName: DocumentIdentifier; configDirectory?: never; }, + ): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "parseJsonConfigFileContent", + function ( + json: any, + options: + | { configDirectory: string; configFileName?: never; } + | { configFileName: DocumentIdentifier; configDirectory?: never; }, + ): ParsedCommandLine { + owner.ensureInitialized(); + return owner.client.apiRequest("parseJsonConfigFileContent", { json, ...options }); + }, + function* ( + json: any, + options: + | { configDirectory: string; configFileName?: never; } + | { configFileName: DocumentIdentifier; configDirectory?: never; }, + ): Generator { + yield* owner.ensureInitialized.gen(); + return yield* apiRequest("parseJsonConfigFileContent", { json, ...options }); + }, + ); + } + + get transpileModule(): { + (input: string, options?: TranspileOptions): TranspileOutput; + gen(input: string, options?: TranspileOptions): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "transpileModule", + function (input: string, options: TranspileOptions = {}): TranspileOutput { + owner.ensureInitialized(); + return owner.client.apiRequest("transpileModule", { input, options }); + }, + function* (input: string, options: TranspileOptions = {}): Generator { + yield* owner.ensureInitialized.gen(); + return yield* apiRequest("transpileModule", { input, options }); + }, + ); + } + + get transpileModuleFromFile(): { + (fileName: string, options?: TranspileOptions): TranspileOutput; + gen(fileName: string, options?: TranspileOptions): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "transpileModuleFromFile", + function (fileName: string, options: TranspileOptions = {}): TranspileOutput { + owner.ensureInitialized(); + return owner.client.apiRequest("transpileModuleFromFile", { fileName, options }); + }, + function* (fileName: string, options: TranspileOptions = {}): Generator { + yield* owner.ensureInitialized.gen(); + return yield* apiRequest("transpileModuleFromFile", { fileName, options }); + }, + ); + } + + get transpileDeclaration(): { + (input: string, options?: TranspileOptions): TranspileOutput; + gen(input: string, options?: TranspileOptions): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "transpileDeclaration", + function (input: string, options: TranspileOptions = {}): TranspileOutput { + owner.ensureInitialized(); + return owner.client.apiRequest("transpileDeclaration", { input, options }); + }, + function* (input: string, options: TranspileOptions = {}): Generator { + yield* owner.ensureInitialized.gen(); + return yield* apiRequest("transpileDeclaration", { input, options }); + }, + ); + } + + get transpileDeclarationFromFile(): { + (fileName: string, options?: TranspileOptions): TranspileOutput; + gen(fileName: string, options?: TranspileOptions): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "transpileDeclarationFromFile", + function (fileName: string, options: TranspileOptions = {}): TranspileOutput { + owner.ensureInitialized(); + return owner.client.apiRequest("transpileDeclarationFromFile", { fileName, options }); + }, + function* (fileName: string, options: TranspileOptions = {}): Generator { + yield* owner.ensureInitialized.gen(); + return yield* apiRequest("transpileDeclarationFromFile", { fileName, options }); + }, + ); + } + + get updateSnapshot(): { + (params?: FromLSP extends true ? LSPUpdateSnapshotParams : UpdateSnapshotParams): Snapshot; + gen(params?: FromLSP extends true ? LSPUpdateSnapshotParams : UpdateSnapshotParams): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "updateSnapshot", + function (params?: FromLSP extends true ? LSPUpdateSnapshotParams : UpdateSnapshotParams): Snapshot { + owner.ensureInitialized(); + + const requestParams = toUpdateSnapshotRequest(params); + const data = owner.client.apiRequest("updateSnapshot", requestParams); + + // Retain cached source files from previous snapshot for unchanged files + if (owner.latestSnapshot) { + owner.sourceFileCache.retainForSnapshot(data.snapshot, owner.latestSnapshot.id, data.changes); + if (owner.latestSnapshot.isDisposed()) { + owner.sourceFileCache.releaseSnapshot(owner.latestSnapshot.id); + } + } + + const snapshot = new Snapshot( + data, + owner.client, + owner.sourceFileCache, + owner.toPath!, + owner, + () => { + owner.activeSnapshots.delete(snapshot); + if (snapshot !== owner.latestSnapshot) { + owner.sourceFileCache.releaseSnapshot(snapshot.id); + } + }, + ); + owner.latestSnapshot = snapshot; + owner.activeSnapshots.add(snapshot); + + return snapshot; + }, + function* (params?: FromLSP extends true ? LSPUpdateSnapshotParams : UpdateSnapshotParams): Generator { + yield* owner.ensureInitialized.gen(); + + const requestParams = toUpdateSnapshotRequest(params); + const data = yield* apiRequest("updateSnapshot", requestParams); + + // Retain cached source files from previous snapshot for unchanged files + if (owner.latestSnapshot) { + owner.sourceFileCache.retainForSnapshot(data.snapshot, owner.latestSnapshot.id, data.changes); + if (owner.latestSnapshot.isDisposed()) { + owner.sourceFileCache.releaseSnapshot(owner.latestSnapshot.id); + } + } + + const snapshot = new Snapshot( + data, + owner.client, + owner.sourceFileCache, + owner.toPath!, + owner, + () => { + owner.activeSnapshots.delete(snapshot); + if (snapshot !== owner.latestSnapshot) { + owner.sourceFileCache.releaseSnapshot(snapshot.id); + } + }, + ); + owner.latestSnapshot = snapshot; + owner.activeSnapshots.add(snapshot); + + return snapshot; + }, + ); + } + + get close(): { + (): void; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "close", + function (): void { + // Dispose all active snapshots + for (const snapshot of [...owner.activeSnapshots]) { + snapshot.dispose(); + } + // Release the latest snapshot's cache refs if still held + if (owner.latestSnapshot) { + owner.sourceFileCache.releaseSnapshot(owner.latestSnapshot.id); + owner.latestSnapshot = undefined; + } + owner.client.close(); + owner.sourceFileCache.clear(); + }, + function* (): Generator { + // Dispose all active snapshots + for (const snapshot of [...owner.activeSnapshots]) { + yield* snapshot.dispose.gen(); + } + // Release the latest snapshot's cache refs if still held + if (owner.latestSnapshot) { + owner.sourceFileCache.releaseSnapshot(owner.latestSnapshot.id); + owner.latestSnapshot = undefined; + } + owner.client.close(); + owner.sourceFileCache.clear(); + }, + ); + } + + clearSourceFileCache(): void { + this.sourceFileCache.clear(); + } + + get runWithTemporaryFileUpdate(): { + (baseSnapshot: Snapshot, file: DocumentIdentifier, newText: string, cb: (newSnapshot: Snapshot) => void): void; + gen(baseSnapshot: Snapshot, file: DocumentIdentifier, newText: string, cb: (newSnapshot: Snapshot) => void | Generator): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "runWithTemporaryFileUpdate", + function (baseSnapshot: Snapshot, file: DocumentIdentifier, newText: string, cb: (newSnapshot: Snapshot) => void): void { + owner.ensureInitialized(); + + if (!owner.activeSnapshots.has(baseSnapshot) || baseSnapshot.isDisposed()) { + throw new Error("Cannot run a temporary file update on an inactive snapshot"); + } + const data = owner.client.apiRequest("updateTemporarySnapshot", { snapshot: baseSnapshot.id, file, newText }); + + // Retain cached source files from the base snapshot for files unchanged by + // the temporary update. The temporary snapshot is not the latest snapshot, so + // we never release the latest snapshot's cache here. + owner.sourceFileCache.retainForSnapshot(data.snapshot, baseSnapshot.id, data.changes); + + const snapshot = new Snapshot( + data, + owner.client, + owner.sourceFileCache, + owner.toPath!, + owner, + () => { + owner.activeSnapshots.delete(snapshot); + owner.sourceFileCache.releaseSnapshot(snapshot.id); + }, + ); + owner.activeSnapshots.add(snapshot); + + try { + cb(snapshot); + } + finally { + snapshot.dispose(); + } + }, + function* (baseSnapshot: Snapshot, file: DocumentIdentifier, newText: string, cb: (newSnapshot: Snapshot) => void | Generator): Generator { + yield* owner.ensureInitialized.gen(); + + if (!owner.activeSnapshots.has(baseSnapshot) || baseSnapshot.isDisposed()) { + throw new Error("Cannot run a temporary file update on an inactive snapshot"); + } + const data = yield* apiRequest("updateTemporarySnapshot", { snapshot: baseSnapshot.id, file, newText }); + + // Retain cached source files from the base snapshot for files unchanged by + // the temporary update. The temporary snapshot is not the latest snapshot, so + // we never release the latest snapshot's cache here. + owner.sourceFileCache.retainForSnapshot(data.snapshot, baseSnapshot.id, data.changes); + + const snapshot = new Snapshot( + data, + owner.client, + owner.sourceFileCache, + owner.toPath!, + owner, + () => { + owner.activeSnapshots.delete(snapshot); + owner.sourceFileCache.releaseSnapshot(snapshot.id); + }, + ); + owner.activeSnapshots.add(snapshot); + + try { + yield* (cb(snapshot) ?? []); + } + finally { + yield* snapshot.dispose.gen(); + } + }, + ); + } + + /** + * Returns a snapshot of collected timing information for requests made + * through this API instance: client-measured round-trip latency and bytes + * transferred, folded together with the server's own per-request processing + * time and an estimated transport overhead (round-trip minus server time). + * + * Fetching the snapshot issues a lightweight request to the server to + * retrieve its timing collection. Collection must be enabled via the + * `collectTiming` option; when it is not, the returned snapshot has + * `enabled: false` and zeroed totals. + */ + get getTimingInfo(): { + (): TimingInfo; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getTimingInfo", + function (): TimingInfo { + return owner.client.getTimingInfo(); + }, + function* (): Generator { + return owner.client.getTimingInfo(); + }, + ); + } + + /** Clears all accumulated timing totals and recent-request history, on both the client and the server. */ + get resetTimingInfo(): { + (): void; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "resetTimingInfo", + function (): void { + return owner.client.resetTimingInfo(); + }, + function* (): Generator { + return owner.client.resetTimingInfo(); + }, + ); + } +} + +type EnsureInitialized = (() => void) & { gen(): Generator; }; + +export class InternalAPI { + private client: Client; + private ensureInitialized: EnsureInitialized; + + /** @internal */ + constructor(client: Client, ensureInitialized: EnsureInitialized) { + this.client = client; + this.ensureInitialized = ensureInitialized; + } + + get startCPUProfile(): { + (dir: string): void; + gen(dir: string): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "startCPUProfile", + function (dir: string): void { + owner.ensureInitialized(); + owner.client.apiRequest("startCPUProfile", { dir }); + }, + function* (dir: string): Generator { + yield* owner.ensureInitialized.gen(); + yield* apiRequest("startCPUProfile", { dir }); + }, + ); + } + + get stopCPUProfile(): { + (): string; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "stopCPUProfile", + function (): string { + owner.ensureInitialized(); + const result = owner.client.apiRequest("stopCPUProfile", null); + return result.file; + }, + function* (): Generator { + yield* owner.ensureInitialized.gen(); + const result = yield* apiRequest("stopCPUProfile", null); + return result.file; + }, + ); + } + + get saveHeapProfile(): { + (dir: string): string; + gen(dir: string): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "saveHeapProfile", + function (dir: string): string { + owner.ensureInitialized(); + const result = owner.client.apiRequest("saveHeapProfile", { dir }); + return result.file; + }, + function* (dir: string): Generator { + yield* owner.ensureInitialized.gen(); + const result = yield* apiRequest("saveHeapProfile", { dir }); + return result.file; + }, + ); + } +} + +export class Snapshot { + readonly id: number; + private projectMap: Map; + private toPath: (fileName: string) => Path; + private client: Client; + private disposed: boolean = false; + private onDispose: () => void; + private snapshotRegistry: SnapshotObjectRegistry; + readonly internal: SnapshotInternalAPI; + + constructor( + data: UpdateSnapshotResponse, + client: Client, + sourceFileCache: SourceFileCache, + toPath: (fileName: string) => Path, + formatDiagnosticsHost: FormatDiagnosticsHost, + onDispose: () => void, + ) { + this.id = data.snapshot; + this.client = client; + this.toPath = toPath; + this.onDispose = onDispose; + this.projectMap = new Map(); + this.snapshotRegistry = new SnapshotObjectRegistry(client, this.id, projectId => this.projectMap.get(projectId)); + + for (const projData of data.projects) { + const project = new Project(projData, this.id, client, sourceFileCache, toPath, formatDiagnosticsHost, this.snapshotRegistry); + this.projectMap.set(toPath(projData.configFileName), project); + } + + this.internal = new SnapshotInternalAPI(this.id, client); + } + + getProjects(): readonly Project[] { + this.ensureNotDisposed(); + return [...this.projectMap.values()]; + } + + getProject(configFileName: string): Project | undefined { + this.ensureNotDisposed(); + return this.projectMap.get(this.toPath(configFileName)); + } + + get getDefaultProjectForFile(): { + (file: DocumentIdentifier): Project | undefined; + gen(file: DocumentIdentifier): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getDefaultProjectForFile", + function (file: DocumentIdentifier): Project | undefined { + owner.ensureNotDisposed(); + const data = owner.client.apiRequest("getDefaultProjectForFile", { + snapshot: owner.id, + file, + }); + if (!data) return undefined; + return owner.projectMap.get(owner.toPath(data.configFileName)); + }, + function* (file: DocumentIdentifier): Generator { + owner.ensureNotDisposed(); + const data = yield* apiRequest("getDefaultProjectForFile", { + snapshot: owner.id, + file, + }); + if (!data) return undefined; + return owner.projectMap.get(owner.toPath(data.configFileName)); + }, + ); + } + + [globalThis.Symbol.dispose](): void { + this.dispose(); + } + + get dispose(): { + (): void; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "dispose", + function (): void { + if (owner.disposed) return; + owner.disposed = true; + for (const project of owner.projectMap.values()) { + project.dispose(); + } + owner.projectMap.clear(); + owner.snapshotRegistry.clear(); + owner.onDispose(); + owner.client.apiRequest("release", { snapshot: owner.id }); + }, + function* (): Generator { + if (owner.disposed) return; + owner.disposed = true; + for (const project of owner.projectMap.values()) { + project.dispose(); + } + owner.projectMap.clear(); + owner.snapshotRegistry.clear(); + owner.onDispose(); + yield* apiRequest("release", { snapshot: owner.id }); + }, + ); + } + + isDisposed(): boolean { + return this.disposed; + } + + private ensureNotDisposed(): void { + if (this.disposed) { + throw new Error("Snapshot is disposed"); + } + } +} + +class SnapshotObjectRegistry { + private readonly symbols: Map = new Map(); + private readonly client: Client; + private readonly snapshotId: number; + private readonly resolveProject: (projectId: Path) => Project | undefined; + + constructor(client: Client, snapshotId: number, resolveProject: (projectId: Path) => Project | undefined) { + this.client = client; + this.snapshotId = snapshotId; + this.resolveProject = resolveProject; + } + + /** Resolve a project id (a config file path) to its Project within this snapshot. */ + getProject(projectId: Path): Project | undefined { + return this.resolveProject(projectId); + } + + getOrCreateSymbol(data: SymbolResponse): Symbol { + let symbol = this.symbols.get(data.id); + if (!symbol) { + symbol = new Symbol(data, this); + this.symbols.set(data.id, symbol); + } + return symbol; + } + + getSymbol(id: number): Symbol | undefined { + return this.symbols.get(id); + } + + clear(): void { + this.symbols.clear(); + } + + get fetchSymbol(): { + (source: Symbol | Signature | Type, method: SymbolPropertyMethod, handle: number | undefined, projectId: Path): Symbol; + gen(source: Symbol | Signature | Type, method: SymbolPropertyMethod, handle: number | undefined, projectId: Path): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "fetchSymbol", + function (source: Symbol | Signature | Type, method: SymbolPropertyMethod, handle: number | undefined, projectId: Path): Symbol { + if (!handle) return undefined as unknown as Symbol; + const cached = owner.getSymbol(handle); + if (cached) return cached; + + const data = owner.client.apiRequest(method, { + snapshot: owner.snapshotId, + project: projectId, + objectId: source.id, + }); + if (!data) throw new Error(`${method} returned null symbol for ${source.constructor.name} ${source.id}`); + return owner.getOrCreateSymbol(data); + }, + function* (source: Symbol | Signature | Type, method: SymbolPropertyMethod, handle: number | undefined, projectId: Path): Generator { + if (!handle) return undefined as unknown as Symbol; + const cached = owner.getSymbol(handle); + if (cached) return cached; + + const data = yield* apiRequest(method, { + snapshot: owner.snapshotId, + project: projectId, + objectId: source.id, + }); + if (!data) throw new Error(`${method} returned null symbol for ${source.constructor.name} ${source.id}`); + return owner.getOrCreateSymbol(data); + }, + ); + } + + get fetchSymbols(): { + (source: Symbol | Signature | Type, method: SymbolsPropertyMethod, handles: readonly number[] | undefined, projectId: Path): readonly Symbol[]; + gen(source: Symbol | Signature | Type, method: SymbolsPropertyMethod, handles: readonly number[] | undefined, projectId: Path): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "fetchSymbols", + function (source: Symbol | Signature | Type, method: SymbolsPropertyMethod, handles: readonly number[] | undefined, projectId: Path): readonly Symbol[] { + if (handles) { + const result = new Array(handles.length); + let allCached = true; + for (let i = 0; i < handles.length; i++) { + const cached = owner.getSymbol(handles[i]); + if (!cached) { + allCached = false; + break; + } + result[i] = cached; + } + if (allCached) return result; + } + const symbolData = owner.client.apiRequest(method, { + snapshot: owner.snapshotId, + project: projectId, + objectId: source.id, + }); + if (symbolData == null) return []; + else return symbolData.map(data => owner.getOrCreateSymbol(data)); + }, + function* (source: Symbol | Signature | Type, method: SymbolsPropertyMethod, handles: readonly number[] | undefined, projectId: Path): Generator { + if (handles) { + const result = new Array(handles.length); + let allCached = true; + for (let i = 0; i < handles.length; i++) { + const cached = owner.getSymbol(handles[i]); + if (!cached) { + allCached = false; + break; + } + result[i] = cached; + } + if (allCached) return result; + } + const symbolData = yield* apiRequest(method, { + snapshot: owner.snapshotId, + project: projectId, + objectId: source.id, + }); + if (symbolData == null) return []; + else return symbolData.map(data => owner.getOrCreateSymbol(data)); + }, + ); + } +} + +class ProjectObjectRegistry { + private client: Client; + private snapshotId: number; + private project: Project; + private snapshotRegistry: SnapshotObjectRegistry; + private types: Map = new Map(); + private signatures: Map = new Map(); + + constructor( + client: Client, + snapshotId: number, + project: Project, + snapshotRegistry: SnapshotObjectRegistry, + ) { + this.client = client; + this.snapshotId = snapshotId; + this.project = project; + this.snapshotRegistry = snapshotRegistry; + } + + getOrCreateSymbol(data: SymbolResponse): Symbol { + return this.snapshotRegistry.getOrCreateSymbol(data); + } + + getSymbol(id: number): Symbol | undefined { + return this.snapshotRegistry.getSymbol(id); + } + + getOrCreateType(data: TypeResponse): TypeObject { + let type = this.types.get(data.id); + if (!type) { + type = new TypeObject(data, this); + this.types.set(data.id, type); + } + return type; + } + + getType(id: number): TypeObject | undefined { + return this.types.get(id); + } + + getOrCreateSignature(data: SignatureResponse): Signature { + let sig = this.signatures.get(data.id); + if (!sig) { + sig = new Signature(data, this.project, this); + this.signatures.set(data.id, sig); + } + return sig; + } + + getSignature(id: number): Signature | undefined { + return this.signatures.get(id); + } + + clear(): void { + this.types.clear(); + this.signatures.clear(); + } + + get fetchOptionalType(): { + (source: Symbol | Signature | Type, method: TypePropertyMethod, handle: number | false | undefined): T | undefined; + gen(source: Symbol | Signature | Type, method: TypePropertyMethod, handle: number | false | undefined): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "fetchOptionalType", + function (source: Symbol | Signature | Type, method: TypePropertyMethod, handle: number | false | undefined): T | undefined { + if (handle !== false) { + if (!handle) return undefined; + const cached = owner.getType(handle); + if (cached) return cached as unknown as T; + } + + const data = owner.client.apiRequest(method, { + snapshot: owner.snapshotId, + project: owner.project.id, + objectId: source.id, + }); + if (!data) return undefined; + return owner.getOrCreateType(data) as unknown as T; + }, + function* (source: Symbol | Signature | Type, method: TypePropertyMethod, handle: number | false | undefined): Generator { + if (handle !== false) { + if (!handle) return undefined; + const cached = owner.getType(handle); + if (cached) return cached as unknown as T; + } + + const data = yield* apiRequest(method, { + snapshot: owner.snapshotId, + project: owner.project.id, + objectId: source.id, + }); + if (!data) return undefined; + return owner.getOrCreateType(data) as unknown as T; + }, + ); + } + + get fetchType(): { + (source: Symbol | Signature | Type, method: TypePropertyMethod, handle: number | false | undefined): T; + gen(source: Symbol | Signature | Type, method: TypePropertyMethod, handle: number | false | undefined): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "fetchType", + function (source: Symbol | Signature | Type, method: TypePropertyMethod, handle: number | false | undefined): T { + const result = owner.fetchOptionalType(source, method, handle); + if (result === undefined) throw new Error(`${method} returned no type for ${source.constructor.name} ${source.id}`); + return result; + }, + function* (source: Symbol | Signature | Type, method: TypePropertyMethod, handle: number | false | undefined): Generator { + const result = yield* owner.fetchOptionalType.gen(source, method, handle); + if (result === undefined) throw new Error(`${method} returned no type for ${source.constructor.name} ${source.id}`); + return result; + }, + ); + } + + get fetchSymbol(): { + (source: Symbol | Signature | Type, method: SymbolPropertyMethod, handle: number | undefined): Symbol; + gen(source: Symbol | Signature | Type, method: SymbolPropertyMethod, handle: number | undefined): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "fetchSymbol", + function (source: Symbol | Signature | Type, method: SymbolPropertyMethod, handle: number | undefined): Symbol { + return owner.snapshotRegistry.fetchSymbol(source, method, handle, owner.project.id); + }, + function* (source: Symbol | Signature | Type, method: SymbolPropertyMethod, handle: number | undefined): Generator { + return yield* owner.snapshotRegistry.fetchSymbol.gen(source, method, handle, owner.project.id); + }, + ); + } + + get fetchSignature(): { + (source: Symbol | Signature | Type, method: SignaturePropertyMethod, handle: number | undefined): Signature; + gen(source: Symbol | Signature | Type, method: SignaturePropertyMethod, handle: number | undefined): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "fetchSignature", + function (source: Symbol | Signature | Type, method: SignaturePropertyMethod, handle: number | undefined): Signature { + if (!handle) return undefined as unknown as Signature; + const cached = owner.getSignature(handle); + if (cached) return cached; + + const data = owner.client.apiRequest(method, { + snapshot: owner.snapshotId, + project: owner.project.id, + objectId: source.id, + }); + if (!data) throw new Error(`${method} returned null signature for ${source.constructor.name} ${source.id}`); + return owner.getOrCreateSignature(data); + }, + function* (source: Symbol | Signature | Type, method: SignaturePropertyMethod, handle: number | undefined): Generator { + if (!handle) return undefined as unknown as Signature; + const cached = owner.getSignature(handle); + if (cached) return cached; + + const data = yield* apiRequest(method, { + snapshot: owner.snapshotId, + project: owner.project.id, + objectId: source.id, + }); + if (!data) throw new Error(`${method} returned null signature for ${source.constructor.name} ${source.id}`); + return owner.getOrCreateSignature(data); + }, + ); + } + + get fetchTypes(): { + (source: Symbol | Signature | Type, method: TypesPropertyMethod, handles?: readonly number[]): readonly Type[]; + gen(source: Symbol | Signature | Type, method: TypesPropertyMethod, handles?: readonly number[]): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "fetchTypes", + function (source: Symbol | Signature | Type, method: TypesPropertyMethod, handles?: readonly number[]): readonly Type[] { + if (handles) { + const result = new Array(handles.length); + let allCached = true; + for (let i = 0; i < handles.length; i++) { + const cached = owner.getType(handles[i]); + if (!cached) { + allCached = false; + break; + } + result[i] = cached; + } + if (allCached) return result; + } + const typesData = owner.client.apiRequest(method, { + snapshot: owner.snapshotId, + project: owner.project.id, + objectId: source.id, + }); + if (typesData == null) return []; + else return typesData.map(data => owner.getOrCreateType(data)); + }, + function* (source: Symbol | Signature | Type, method: TypesPropertyMethod, handles?: readonly number[]): Generator { + if (handles) { + const result = new Array(handles.length); + let allCached = true; + for (let i = 0; i < handles.length; i++) { + const cached = owner.getType(handles[i]); + if (!cached) { + allCached = false; + break; + } + result[i] = cached; + } + if (allCached) return result; + } + const typesData = yield* apiRequest(method, { + snapshot: owner.snapshotId, + project: owner.project.id, + objectId: source.id, + }); + if (typesData == null) return []; + else return typesData.map(data => owner.getOrCreateType(data)); + }, + ); + } + + get fetchSymbols(): { + (source: Symbol | Signature | Type, method: SymbolsPropertyMethod, handles?: readonly number[]): readonly Symbol[]; + gen(source: Symbol | Signature | Type, method: SymbolsPropertyMethod, handles?: readonly number[]): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "fetchSymbols", + function (source: Symbol | Signature | Type, method: SymbolsPropertyMethod, handles?: readonly number[]): readonly Symbol[] { + return owner.snapshotRegistry.fetchSymbols(source, method, handles, owner.project.id); + }, + function* (source: Symbol | Signature | Type, method: SymbolsPropertyMethod, handles?: readonly number[]): Generator { + return yield* owner.snapshotRegistry.fetchSymbols.gen(source, method, handles, owner.project.id); + }, + ); + } + + // getBaseTypes is a checker-level endpoint keyed by `type` (not `objectId`), + // so it cannot go through fetchTypes. This helper reuses that server method. + get fetchBaseTypes(): { + (source: Type): readonly Type[]; + gen(source: Type): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "fetchBaseTypes", + function (source: Type): readonly Type[] { + const typesData = owner.client.apiRequest("getBaseTypes", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: source.id, + }); + if (typesData == null) return []; + return typesData.map(data => owner.getOrCreateType(data)); + }, + function* (source: Type): Generator { + const typesData = yield* apiRequest("getBaseTypes", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: source.id, + }); + if (typesData == null) return []; + return typesData.map(data => owner.getOrCreateType(data)); + }, + ); + } + + get fetchPropertiesOfType(): { + (source: Type): readonly Symbol[]; + gen(source: Type): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "fetchPropertiesOfType", + function (source: Type): readonly Symbol[] { + const data = owner.client.apiRequest("getPropertiesOfType", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: source.id, + }); + return data ? data.map(symbol => owner.getOrCreateSymbol(symbol)) : []; + }, + function* (source: Type): Generator { + const data = yield* apiRequest("getPropertiesOfType", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: source.id, + }); + return data ? data.map(symbol => owner.getOrCreateSymbol(symbol)) : []; + }, + ); + } + + get fetchApparentPropertiesOfType(): { + (source: Type): readonly Symbol[]; + gen(source: Type): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "fetchApparentPropertiesOfType", + function (source: Type): readonly Symbol[] { + const data = owner.client.apiRequest("getApparentPropertiesOfType", { + snapshot: owner.snapshotId, + project: owner.project.id, + objectId: source.id, + }); + return data ? data.map(symbol => owner.getOrCreateSymbol(symbol)) : []; + }, + function* (source: Type): Generator { + const data = yield* apiRequest("getApparentPropertiesOfType", { + snapshot: owner.snapshotId, + project: owner.project.id, + objectId: source.id, + }); + return data ? data.map(symbol => owner.getOrCreateSymbol(symbol)) : []; + }, + ); + } + + get fetchPropertyOfType(): { + (source: Type, name: string): Symbol | undefined; + gen(source: Type, name: string): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "fetchPropertyOfType", + function (source: Type, name: string): Symbol | undefined { + const data = owner.client.apiRequest("getPropertyOfType", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: source.id, + name, + }); + return data ? owner.getOrCreateSymbol(data) : undefined; + }, + function* (source: Type, name: string): Generator { + const data = yield* apiRequest("getPropertyOfType", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: source.id, + name, + }); + return data ? owner.getOrCreateSymbol(data) : undefined; + }, + ); + } + + get fetchSignaturesOfType(): { + (source: Type, kind: SignatureKind): readonly Signature[]; + gen(source: Type, kind: SignatureKind): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "fetchSignaturesOfType", + function (source: Type, kind: SignatureKind): readonly Signature[] { + const data = owner.client.apiRequest("getSignaturesOfType", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: source.id, + kind, + }); + return data.map(signature => owner.getOrCreateSignature(signature)); + }, + function* (source: Type, kind: SignatureKind): Generator { + const data = yield* apiRequest("getSignaturesOfType", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: source.id, + kind, + }); + return data.map(signature => owner.getOrCreateSignature(signature)); + }, + ); + } + + get fetchIndexInfosOfType(): { + (source: Type): readonly IndexInfo[]; + gen(source: Type): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "fetchIndexInfosOfType", + function (source: Type): readonly IndexInfo[] { + const data = owner.client.apiRequest("getIndexInfosOfType", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: source.id, + }); + if (!data) return []; + return data.map(info => ({ + keyType: owner.getOrCreateType(info.keyType), + valueType: owner.getOrCreateType(info.valueType), + isReadonly: info.isReadonly ?? false, + declaration: info.declaration ? new NodeHandle(info.declaration, owner.project) : undefined, + })); + }, + function* (source: Type): Generator { + const data = yield* apiRequest("getIndexInfosOfType", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: source.id, + }); + if (!data) return []; + return data.map(info => ({ + keyType: owner.getOrCreateType(info.keyType), + valueType: owner.getOrCreateType(info.valueType), + isReadonly: info.isReadonly ?? false, + declaration: info.declaration ? new NodeHandle(info.declaration, owner.project) : undefined, + })); + }, + ); + } + + get fetchTypeParameterAtPosition(): { + (source: Signature, pos: number): Type; + gen(source: Signature, pos: number): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "fetchTypeParameterAtPosition", + function (source: Signature, pos: number): Type { + const data = owner.client.apiRequest("getTypeParameterAtPosition", { + snapshot: owner.snapshotId, + project: owner.project.id, + signature: source.id, + index: pos, + }); + return owner.getOrCreateType(data); + }, + function* (source: Signature, pos: number): Generator { + const data = yield* apiRequest("getTypeParameterAtPosition", { + snapshot: owner.snapshotId, + project: owner.project.id, + signature: source.id, + index: pos, + }); + return owner.getOrCreateType(data); + }, + ); + } +} + +export class Project { + readonly id: Path; + readonly configFileName: string; + readonly currentDirectory: string; + readonly parsedCommandLine: ParsedCommandLine; + /** @deprecated Use `parsedCommandLine.options`. */ + readonly compilerOptions: CompilerOptions; + /** @deprecated Use `parsedCommandLine.fileNames`. */ + readonly rootFiles: readonly string[]; + + readonly program: Program; + readonly checker: Checker; + readonly emitter: Emitter; + readonly languageService: LanguageService; + private client: Client; + private snapshotId: number; + + constructor( + data: ProjectResponse, + snapshotId: number, + client: Client, + sourceFileCache: SourceFileCache, + toPath: (fileName: string) => Path, + formatDiagnosticsHost: FormatDiagnosticsHost, + snapshotRegistry: SnapshotObjectRegistry, + ) { + this.id = data.id as Path; + this.configFileName = data.configFileName; + this.currentDirectory = data.currentDirectory; + if (!data.parsedCommandLine?.options) { + throw new Error(`Project '${data.configFileName}' has no parsed command line`); + } + this.parsedCommandLine = data.parsedCommandLine; + this.compilerOptions = this.parsedCommandLine.options; + this.rootFiles = this.parsedCommandLine.fileNames; + this.client = client; + this.snapshotId = snapshotId; + this.program = new Program( + snapshotId, + this, + client, + sourceFileCache, + toPath, + formatDiagnosticsHost, + ); + const objectRegistry = new ProjectObjectRegistry(client, snapshotId, this, snapshotRegistry); + this.checker = new Checker( + snapshotId, + this, + client, + objectRegistry, + ); + this.emitter = new Emitter(client); + this.languageService = new LanguageService(snapshotId, this, client, objectRegistry); + } + + /** @deprecated Use `languageService.getImportAdderEdits`. */ + get getImportAdderEdits(): { + (file: DocumentIdentifier, actions: readonly APIImportAdderAction[]): readonly TextEdit[]; + gen(file: DocumentIdentifier, actions: readonly APIImportAdderAction[]): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getImportAdderEdits", + function (file: DocumentIdentifier, actions: readonly APIImportAdderAction[]): readonly TextEdit[] { + return owner.languageService.getImportAdderEdits(file, actions); + }, + function* (file: DocumentIdentifier, actions: readonly APIImportAdderAction[]): Generator { + return yield* owner.languageService.getImportAdderEdits.gen(file, actions); + }, + ); + } + + /** @deprecated Use `languageService.getImportEditsForSymbols`. */ + get getImportEditsForSymbols(): { + (file: DocumentIdentifier, symbols: readonly Symbol[], options?: GetImportEditsForSymbolsOptions): readonly TextEdit[]; + gen(file: DocumentIdentifier, symbols: readonly Symbol[], options?: GetImportEditsForSymbolsOptions): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getImportEditsForSymbols", + function (file: DocumentIdentifier, symbols: readonly Symbol[], options: GetImportEditsForSymbolsOptions = {}): readonly TextEdit[] { + return owner.languageService.getImportEditsForSymbols(file, symbols, options); + }, + function* (file: DocumentIdentifier, symbols: readonly Symbol[], options: GetImportEditsForSymbolsOptions = {}): Generator { + return yield* owner.languageService.getImportEditsForSymbols.gen(file, symbols, options); + }, + ); + } + + dispose(): void { + this.checker.dispose(); + } +} + +export class LanguageService { + private snapshotId: number; + private project: Project; + private client: Client; + private objectRegistry: ProjectObjectRegistry; + + constructor( + snapshotId: number, + project: Project, + client: Client, + objectRegistry: ProjectObjectRegistry, + ) { + this.snapshotId = snapshotId; + this.project = project; + this.client = client; + this.objectRegistry = objectRegistry; + } + + get getImportAdderEdits(): { + (file: DocumentIdentifier, actions: readonly APIImportAdderAction[]): readonly TextEdit[]; + gen(file: DocumentIdentifier, actions: readonly APIImportAdderAction[]): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getImportAdderEdits", + function (file: DocumentIdentifier, actions: readonly APIImportAdderAction[]): readonly TextEdit[] { + const requestActions: ImportAdderAction[] = actions.map(action => { + switch (action.kind) { + case "importSymbol": + const importSymbolAction: ImportAdderAction = { + kind: "importSymbol", + symbol: action.symbol.id, + }; + if (action.isValidTypeOnlyUseSite !== undefined) { + importSymbolAction.isValidTypeOnlyUseSite = action.isValidTypeOnlyUseSite; + } + return importSymbolAction; + default: + return assertNever(action.kind); + } + }); + + const data = owner.client.apiRequest("getImportAdderEdits", { + snapshot: owner.snapshotId, + project: owner.project.id, + file, + actions: requestActions, + }); + return data ?? []; + }, + function* (file: DocumentIdentifier, actions: readonly APIImportAdderAction[]): Generator { + const requestActions: ImportAdderAction[] = actions.map(action => { + switch (action.kind) { + case "importSymbol": + const importSymbolAction: ImportAdderAction = { + kind: "importSymbol", + symbol: action.symbol.id, + }; + if (action.isValidTypeOnlyUseSite !== undefined) { + importSymbolAction.isValidTypeOnlyUseSite = action.isValidTypeOnlyUseSite; + } + return importSymbolAction; + default: + return assertNever(action.kind); + } + }); + + const data = yield* apiRequest("getImportAdderEdits", { + snapshot: owner.snapshotId, + project: owner.project.id, + file, + actions: requestActions, + }); + return data ?? []; + }, + ); + } + + get getImportEditsForSymbols(): { + (file: DocumentIdentifier, symbols: readonly Symbol[], options?: GetImportEditsForSymbolsOptions): readonly TextEdit[]; + gen(file: DocumentIdentifier, symbols: readonly Symbol[], options?: GetImportEditsForSymbolsOptions): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getImportEditsForSymbols", + function (file: DocumentIdentifier, symbols: readonly Symbol[], options: GetImportEditsForSymbolsOptions = {}): readonly TextEdit[] { + return owner.getImportAdderEdits( + file, + symbols.map((symbol): APIImportAdderAction => { + if (options.isValidTypeOnlyUseSite !== undefined) { + return { + kind: "importSymbol", + symbol, + isValidTypeOnlyUseSite: options.isValidTypeOnlyUseSite, + }; + } + return { + kind: "importSymbol", + symbol, + }; + }), + ); + }, + function* (file: DocumentIdentifier, symbols: readonly Symbol[], options: GetImportEditsForSymbolsOptions = {}): Generator { + return yield* owner.getImportAdderEdits.gen( + file, + symbols.map((symbol): APIImportAdderAction => { + if (options.isValidTypeOnlyUseSite !== undefined) { + return { + kind: "importSymbol", + symbol, + isValidTypeOnlyUseSite: options.isValidTypeOnlyUseSite, + }; + } + return { + kind: "importSymbol", + symbol, + }; + }), + ); + }, + ); + } + + get getReferencedSymbolsForNode(): { + (node: Node, position: number): ReferencedSymbolEntry[]; + gen(node: Node, position: number): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getReferencedSymbolsForNode", + function (node: Node, position: number): ReferencedSymbolEntry[] { + const data = owner.client.apiRequest("getReferencedSymbolsForNode", { + snapshot: owner.snapshotId, + project: owner.project.id, + node: getNodeId(node), + position, + }); + return (data ?? []).map(entry => ({ + definition: new NodeHandle(entry.definition, owner.project), + symbol: entry.symbol ? owner.objectRegistry.getOrCreateSymbol(entry.symbol) : undefined, + references: (entry.references ?? []).map(h => new NodeHandle(h, owner.project)), + })); + }, + function* (node: Node, position: number): Generator { + const data = yield* apiRequest("getReferencedSymbolsForNode", { + snapshot: owner.snapshotId, + project: owner.project.id, + node: getNodeId(node), + position, + }); + return (data ?? []).map(entry => ({ + definition: new NodeHandle(entry.definition, owner.project), + symbol: entry.symbol ? owner.objectRegistry.getOrCreateSymbol(entry.symbol) : undefined, + references: (entry.references ?? []).map(h => new NodeHandle(h, owner.project)), + })); + }, + ); + } + + get getSignatureUsage(): { + (signatureDecl: Node): SignatureUsage[]; + gen(signatureDecl: Node): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getSignatureUsage", + function (signatureDecl: Node): SignatureUsage[] { + const data = owner.client.apiRequest("getSignatureUsages", { + snapshot: owner.snapshotId, + project: owner.project.id, + signatureDecl: getNodeId(signatureDecl), + }); + return (data ?? []).map(entry => ({ + name: new NodeHandle(entry.name, owner.project), + call: entry.call ? new NodeHandle(entry.call, owner.project) : undefined, + })); + }, + function* (signatureDecl: Node): Generator { + const data = yield* apiRequest("getSignatureUsages", { + snapshot: owner.snapshotId, + project: owner.project.id, + signatureDecl: getNodeId(signatureDecl), + }); + return (data ?? []).map(entry => ({ + name: new NodeHandle(entry.name, owner.project), + call: entry.call ? new NodeHandle(entry.call, owner.project) : undefined, + })); + }, + ); + } + + get getCompletionsAtPosition(): { + (document: string, position: number, options?: CompletionOptions): CompletionInfo | undefined; + gen(document: string, position: number, options?: CompletionOptions): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getCompletionsAtPosition", + function (document: string, position: number, options?: CompletionOptions): CompletionInfo | undefined { + const data = owner.client.apiRequest("getCompletionsAtPosition", { + snapshot: owner.snapshotId, + project: owner.project.id, + file: document, + position, + ...(options?.triggerCharacter !== undefined ? { triggerCharacter: options.triggerCharacter } : {}), + ...(options?.includeSymbol !== undefined ? { includeSymbol: options.includeSymbol } : {}), + }); + if (!data) return undefined; + return { + isIncomplete: data.isIncomplete, + entries: data.entries.map(e => ({ + ...e, + symbol: e.symbol ? owner.objectRegistry.getOrCreateSymbol(e.symbol) : undefined, + })), + }; + }, + function* (document: string, position: number, options?: CompletionOptions): Generator { + const data = yield* apiRequest("getCompletionsAtPosition", { + snapshot: owner.snapshotId, + project: owner.project.id, + file: document, + position, + ...(options?.triggerCharacter !== undefined ? { triggerCharacter: options.triggerCharacter } : {}), + ...(options?.includeSymbol !== undefined ? { includeSymbol: options.includeSymbol } : {}), + }); + if (!data) return undefined; + return { + isIncomplete: data.isIncomplete, + entries: data.entries.map(e => ({ + ...e, + symbol: e.symbol ? owner.objectRegistry.getOrCreateSymbol(e.symbol) : undefined, + })), + }; + }, + ); + } +} + +export class Program implements FormatDiagnosticsHost { + private snapshotId: number; + private project: Project; + private client: Client; + private sourceFileCache: SourceFileCache; + private toPath: (fileName: string) => Path; + private formatDiagnosticsHost: FormatDiagnosticsHost; + private decoder = new Wtf8Decoder(); + private sourceFileMetadataCache = new Map(); + + constructor( + snapshotId: number, + project: Project, + client: Client, + sourceFileCache: SourceFileCache, + toPath: (fileName: string) => Path, + formatDiagnosticsHost: FormatDiagnosticsHost, + ) { + this.snapshotId = snapshotId; + this.project = project; + this.client = client; + this.sourceFileCache = sourceFileCache; + this.toPath = toPath; + this.formatDiagnosticsHost = formatDiagnosticsHost; + } + + getCurrentDirectory(): string { + return this.project.currentDirectory; + } + + getCanonicalFileName(fileName: string): string { + return this.formatDiagnosticsHost.getCanonicalFileName(fileName); + } + + getNewLine(): string { + return this.project.compilerOptions.newLine === NewLineKind.CRLF ? "\r\n" : "\n"; + } + + getCompilerOptions(): CompilerOptions { + return this.project.compilerOptions; + } + + get getSourceFile(): { + (file: DocumentIdentifier): SourceFile | undefined; + gen(file: DocumentIdentifier): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getSourceFile", + function (file: DocumentIdentifier): SourceFile | undefined { + const fileName = resolveFileName(file); + const path = owner.toPath(fileName); + + // Check if we already have a retained cache entry for this (snapshot, project) pair + const retained = owner.sourceFileCache.getRetained(path, owner.snapshotId, owner.project.id); + if (retained) { + return retained; + } + + // Fetch from server + const binaryData = owner.client.apiRequestBinary("getSourceFile", { + snapshot: owner.snapshotId, + project: owner.project.id, + file, + }); + if (!binaryData) { + return undefined; + } + + const view = new DataView(binaryData.buffer, binaryData.byteOffset, binaryData.byteLength); + const contentHash = readSourceFileHash(view); + const parseOptionsKey = readParseOptionsKey(view); + + // Create a new RemoteSourceFile and cache it (set returns existing if hash matches) + const sourceFile = new RemoteSourceFile(binaryData, owner.decoder, owner.client.getTimingCollector()) as unknown as SourceFile; + return owner.sourceFileCache.set(path, sourceFile, parseOptionsKey, contentHash, owner.snapshotId, owner.project.id); + }, + function* (file: DocumentIdentifier): Generator { + const fileName = resolveFileName(file); + const path = owner.toPath(fileName); + + // Check if we already have a retained cache entry for this (snapshot, project) pair + const retained = owner.sourceFileCache.getRetained(path, owner.snapshotId, owner.project.id); + if (retained) { + return retained; + } + + // Fetch from server + const binaryData = owner.client.apiRequestBinary("getSourceFile", { + snapshot: owner.snapshotId, + project: owner.project.id, + file, + }); + if (!binaryData) { + return undefined; + } + + const view = new DataView(binaryData.buffer, binaryData.byteOffset, binaryData.byteLength); + const contentHash = readSourceFileHash(view); + const parseOptionsKey = readParseOptionsKey(view); + + // Create a new RemoteSourceFile and cache it (set returns existing if hash matches) + const sourceFile = new RemoteSourceFile(binaryData, owner.decoder, owner.client.getTimingCollector()) as unknown as SourceFile; + return owner.sourceFileCache.set(path, sourceFile, parseOptionsKey, contentHash, owner.snapshotId, owner.project.id); + }, + ); + } + + get getSourceFileNames(): { + (): readonly string[]; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getSourceFileNames", + function (): readonly string[] { + const data = owner.client.apiRequest("getSourceFileNames", { + snapshot: owner.snapshotId, + project: owner.project.id, + }); + return data ?? []; + }, + function* (): Generator { + const data = yield* apiRequest("getSourceFileNames", { + snapshot: owner.snapshotId, + project: owner.project.id, + }); + return data ?? []; + }, + ); + } + + /** + * Returns program-stored metadata for the given source file, or `undefined` if the file + * is not part of the program. Metadata is fetched lazily per file and cached on this + * `Program` instance. + */ + get getSourceFileMetadata(): { + (fileName: string): SourceFileMetadata | undefined; + gen(fileName: string): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getSourceFileMetadata", + function (fileName: string): SourceFileMetadata | undefined { + return owner.getSourceFileMetadataByPath(owner.toPath(fileName)); + }, + function* (fileName: string): Generator { + return yield* owner.getSourceFileMetadataByPath.gen(owner.toPath(fileName)); + }, + ); + } + + /** + * Returns program-stored metadata for the source file at the given path, or `undefined` + * if the file is not part of the program. Like {@link getSourceFileMetadata}, but skips + * the file name to path conversion. Metadata is fetched lazily per file and cached on + * this `Program` instance. + */ + get getSourceFileMetadataByPath(): { + (path: Path): SourceFileMetadata | undefined; + gen(path: Path): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getSourceFileMetadataByPath", + function (path: Path): SourceFileMetadata | undefined { + let metadata = owner.sourceFileMetadataCache.get(path); + if (metadata === undefined) { + metadata = owner.fetchSourceFileMetadata(path); + owner.sourceFileMetadataCache.set(path, metadata); + } + return metadata; + }, + function* (path: Path): Generator { + let metadata = owner.sourceFileMetadataCache.get(path); + if (metadata === undefined) { + metadata = yield* owner.fetchSourceFileMetadata.gen(path); + owner.sourceFileMetadataCache.set(path, metadata); + } + return metadata; + }, + ); + } + + private get fetchSourceFileMetadata(): { + (path: Path): SourceFileMetadata | undefined; + gen(path: Path): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "fetchSourceFileMetadata", + function (path: Path): SourceFileMetadata | undefined { + const data = owner.client.apiRequest("getSourceFileMetadata", { + snapshot: owner.snapshotId, + project: owner.project.id, + file: path, + }); + return data ?? undefined; + }, + function* (path: Path): Generator { + const data = yield* apiRequest("getSourceFileMetadata", { + snapshot: owner.snapshotId, + project: owner.project.id, + file: path, + }); + return data ?? undefined; + }, + ); + } + + /** + * Returns whether the given source file was loaded as part of an external library + * (e.g. a dependency resolved from `node_modules`). The underlying program metadata is + * fetched lazily per file and cached on this `Program` instance. + */ + get isSourceFileFromExternalLibrary(): { + (file: SourceFile): boolean; + gen(file: SourceFile): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "isSourceFileFromExternalLibrary", + function (file: SourceFile): boolean { + const metadata = owner.getSourceFileMetadataByPath(file.path); + return metadata?.isFromExternalLibrary ?? false; + }, + function* (file: SourceFile): Generator { + const metadata = yield* owner.getSourceFileMetadataByPath.gen(file.path); + return metadata?.isFromExternalLibrary ?? false; + }, + ); + } + + /** + * Returns whether the given source file is a default library file (e.g. `lib.d.ts`). + * The underlying program metadata is fetched lazily per file and cached on this + * `Program` instance. + */ + get isSourceFileDefaultLibrary(): { + (file: SourceFile): boolean; + gen(file: SourceFile): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "isSourceFileDefaultLibrary", + function (file: SourceFile): boolean { + const metadata = owner.getSourceFileMetadataByPath(file.path); + return metadata?.isDefaultLibrary ?? false; + }, + function* (file: SourceFile): Generator { + const metadata = yield* owner.getSourceFileMetadataByPath.gen(file.path); + return metadata?.isDefaultLibrary ?? false; + }, + ); + } + + /** + * Get all config source file names associated with this program's project config. + * Includes the root config file and any extended config files. + */ + get getConfigFileNames(): { + (): readonly string[]; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getConfigFileNames", + function (): readonly string[] { + const data = owner.client.apiRequest("getConfigFileNames", { + snapshot: owner.snapshotId, + project: owner.project.id, + }); + return data ?? []; + }, + function* (): Generator { + const data = yield* apiRequest("getConfigFileNames", { + snapshot: owner.snapshotId, + project: owner.project.id, + }); + return data ?? []; + }, + ); + } + + /** + * Get a config source file by file name/URI. + * This can return the project's root tsconfig file or one of its extended config files. + */ + get getConfigSourceFile(): { + (file: DocumentIdentifier): SourceFile | undefined; + gen(file: DocumentIdentifier): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getConfigSourceFile", + function (file: DocumentIdentifier): SourceFile | undefined { + const binaryData = owner.client.apiRequestBinary("getConfigSourceFile", { + snapshot: owner.snapshotId, + project: owner.project.id, + file, + }); + if (!binaryData) { + return undefined; + } + + return new RemoteSourceFile(binaryData, owner.decoder) as unknown as SourceFile; + }, + function* (file: DocumentIdentifier): Generator { + const binaryData = owner.client.apiRequestBinary("getConfigSourceFile", { + snapshot: owner.snapshotId, + project: owner.project.id, + file, + }); + if (!binaryData) { + return undefined; + } + + return new RemoteSourceFile(binaryData, owner.decoder) as unknown as SourceFile; + }, + ); + } + + /** + * Get syntactic (parse) diagnostics for specific files or all files. + * @param file - Optional file(s) to get diagnostics for. If omitted, returns diagnostics for all files. + */ + get getSyntacticDiagnostics(): { + (file?: DocumentIdentifier | readonly DocumentIdentifier[]): readonly Diagnostic[]; + gen(file?: DocumentIdentifier | readonly DocumentIdentifier[]): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getSyntacticDiagnostics", + function (file?: DocumentIdentifier | readonly DocumentIdentifier[]): readonly Diagnostic[] { + const files = file === undefined ? undefined + : Array.isArray(file) ? file + : [file]; + const data = owner.client.apiRequest("getSyntacticDiagnostics", { + snapshot: owner.snapshotId, + project: owner.project.id, + ...(files !== undefined ? { files } : {}), + }); + return data ?? []; + }, + function* (file?: DocumentIdentifier | readonly DocumentIdentifier[]): Generator { + const files = file === undefined ? undefined + : Array.isArray(file) ? file + : [file]; + const data = yield* apiRequest("getSyntacticDiagnostics", { + snapshot: owner.snapshotId, + project: owner.project.id, + ...(files !== undefined ? { files } : {}), + }); + return data ?? []; + }, + ); + } + + /** + * Get binder diagnostics for specific files or all files. + * @param file - Optional file(s) to get diagnostics for. If omitted, returns diagnostics for all files. + */ + get getBindDiagnostics(): { + (file?: DocumentIdentifier | readonly DocumentIdentifier[]): readonly Diagnostic[]; + gen(file?: DocumentIdentifier | readonly DocumentIdentifier[]): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getBindDiagnostics", + function (file?: DocumentIdentifier | readonly DocumentIdentifier[]): readonly Diagnostic[] { + const files = file === undefined ? undefined + : Array.isArray(file) ? file + : [file]; + const data = owner.client.apiRequest("getBindDiagnostics", { + snapshot: owner.snapshotId, + project: owner.project.id, + ...(files !== undefined ? { files } : {}), + }); + return data ?? []; + }, + function* (file?: DocumentIdentifier | readonly DocumentIdentifier[]): Generator { + const files = file === undefined ? undefined + : Array.isArray(file) ? file + : [file]; + const data = yield* apiRequest("getBindDiagnostics", { + snapshot: owner.snapshotId, + project: owner.project.id, + ...(files !== undefined ? { files } : {}), + }); + return data ?? []; + }, + ); + } + + /** + * Get semantic (type-check) diagnostics for specific files or all files. + * @param file - Optional file(s) to get diagnostics for. If omitted, returns diagnostics for all files. + */ + get getSemanticDiagnostics(): { + (file?: DocumentIdentifier | readonly DocumentIdentifier[]): readonly Diagnostic[]; + gen(file?: DocumentIdentifier | readonly DocumentIdentifier[]): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getSemanticDiagnostics", + function (file?: DocumentIdentifier | readonly DocumentIdentifier[]): readonly Diagnostic[] { + const files = file === undefined ? undefined + : Array.isArray(file) ? file + : [file]; + const data = owner.client.apiRequest("getSemanticDiagnostics", { + snapshot: owner.snapshotId, + project: owner.project.id, + ...(files !== undefined ? { files } : {}), + }); + return data ?? []; + }, + function* (file?: DocumentIdentifier | readonly DocumentIdentifier[]): Generator { + const files = file === undefined ? undefined + : Array.isArray(file) ? file + : [file]; + const data = yield* apiRequest("getSemanticDiagnostics", { + snapshot: owner.snapshotId, + project: owner.project.id, + ...(files !== undefined ? { files } : {}), + }); + return data ?? []; + }, + ); + } + + /** + * Get suggestion diagnostics for specific files or all files. + * @param file - Optional file(s) to get diagnostics for. If omitted, returns diagnostics for all files. + */ + get getSuggestionDiagnostics(): { + (file?: DocumentIdentifier | readonly DocumentIdentifier[]): readonly Diagnostic[]; + gen(file?: DocumentIdentifier | readonly DocumentIdentifier[]): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getSuggestionDiagnostics", + function (file?: DocumentIdentifier | readonly DocumentIdentifier[]): readonly Diagnostic[] { + const files = file === undefined ? undefined + : Array.isArray(file) ? file + : [file]; + const data = owner.client.apiRequest("getSuggestionDiagnostics", { + snapshot: owner.snapshotId, + project: owner.project.id, + ...(files !== undefined ? { files } : {}), + }); + return data ?? []; + }, + function* (file?: DocumentIdentifier | readonly DocumentIdentifier[]): Generator { + const files = file === undefined ? undefined + : Array.isArray(file) ? file + : [file]; + const data = yield* apiRequest("getSuggestionDiagnostics", { + snapshot: owner.snapshotId, + project: owner.project.id, + ...(files !== undefined ? { files } : {}), + }); + return data ?? []; + }, + ); + } + + /** + * Get declaration emit diagnostics for specific files or all files. + * @param file - Optional file(s) to get diagnostics for. If omitted, returns diagnostics for all files. + */ + get getDeclarationDiagnostics(): { + (file?: DocumentIdentifier | readonly DocumentIdentifier[]): readonly Diagnostic[]; + gen(file?: DocumentIdentifier | readonly DocumentIdentifier[]): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getDeclarationDiagnostics", + function (file?: DocumentIdentifier | readonly DocumentIdentifier[]): readonly Diagnostic[] { + const files = file === undefined ? undefined + : Array.isArray(file) ? file + : [file]; + const data = owner.client.apiRequest("getDeclarationDiagnostics", { + snapshot: owner.snapshotId, + project: owner.project.id, + ...(files !== undefined ? { files } : {}), + }); + return data ?? []; + }, + function* (file?: DocumentIdentifier | readonly DocumentIdentifier[]): Generator { + const files = file === undefined ? undefined + : Array.isArray(file) ? file + : [file]; + const data = yield* apiRequest("getDeclarationDiagnostics", { + snapshot: owner.snapshotId, + project: owner.project.id, + ...(files !== undefined ? { files } : {}), + }); + return data ?? []; + }, + ); + } + + /** + * Get program-wide diagnostics for the project, including compiler options diagnostics. + */ + get getProgramDiagnostics(): { + (): readonly Diagnostic[]; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getProgramDiagnostics", + function (): readonly Diagnostic[] { + const data = owner.client.apiRequest("getProgramDiagnostics", { + snapshot: owner.snapshotId, + project: owner.project.id, + }); + return data ?? []; + }, + function* (): Generator { + const data = yield* apiRequest("getProgramDiagnostics", { + snapshot: owner.snapshotId, + project: owner.project.id, + }); + return data ?? []; + }, + ); + } + + /** + * Get global (non-file-specific) semantic diagnostics for the project. + */ + get getGlobalDiagnostics(): { + (): readonly Diagnostic[]; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getGlobalDiagnostics", + function (): readonly Diagnostic[] { + const data = owner.client.apiRequest("getGlobalDiagnostics", { + snapshot: owner.snapshotId, + project: owner.project.id, + }); + return data ?? []; + }, + function* (): Generator { + const data = yield* apiRequest("getGlobalDiagnostics", { + snapshot: owner.snapshotId, + project: owner.project.id, + }); + return data ?? []; + }, + ); + } + + /** + * Get config file parsing diagnostics for the project. + */ + get getConfigFileParsingDiagnostics(): { + (): readonly Diagnostic[]; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getConfigFileParsingDiagnostics", + function (): readonly Diagnostic[] { + const data = owner.client.apiRequest("getConfigFileParsingDiagnostics", { + snapshot: owner.snapshotId, + project: owner.project.id, + }); + return data ?? []; + }, + function* (): Generator { + const data = yield* apiRequest("getConfigFileParsingDiagnostics", { + snapshot: owner.snapshotId, + project: owner.project.id, + }); + return data ?? []; + }, + ); + } + + /** + * Emits files to the configured filesystem. + * + * When the API has a virtual filesystem with a `writeFile` callback, output + * is written there. Otherwise, the server writes directly to the host filesystem. + */ + get emit(): { + (emitOnly?: EmitOnly): EmitResult; + gen(emitOnly?: EmitOnly): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "emit", + function (emitOnly?: EmitOnly): EmitResult { + const response = owner.client.apiRequest("emit", { + snapshot: owner.snapshotId, + project: owner.project.id, + ...(emitOnly !== undefined ? { emitOnly } : {}), + }); + return { + emitSkipped: response.emitSkipped, + diagnostics: response.diagnostics, + emittedFiles: response.emittedFiles, + }; + }, + function* (emitOnly?: EmitOnly): Generator { + const response = yield* apiRequest("emit", { + snapshot: owner.snapshotId, + project: owner.project.id, + ...(emitOnly !== undefined ? { emitOnly } : {}), + }); + return { + emitSkipped: response.emitSkipped, + diagnostics: response.diagnostics, + emittedFiles: response.emittedFiles, + }; + }, + ); + } + + /** + * Emits files and returns their contents without writing to the filesystem. + */ + get emitToString(): { + (emitOnly?: EmitOnly): EmitOutput; + gen(emitOnly?: EmitOnly): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "emitToString", + function (emitOnly?: EmitOnly): EmitOutput { + const response = owner.client.apiRequest("emitToString", { + snapshot: owner.snapshotId, + project: owner.project.id, + ...(emitOnly !== undefined ? { emitOnly } : {}), + }); + return toEmitOutput(response); + }, + function* (emitOnly?: EmitOnly): Generator { + const response = yield* apiRequest("emitToString", { + snapshot: owner.snapshotId, + project: owner.project.id, + ...(emitOnly !== undefined ? { emitOnly } : {}), + }); + return toEmitOutput(response); + }, + ); + } + + /** + * Gets JavaScript output for selected files regardless of project `noEmit`, `emitDeclarationOnly`, and `noEmitOnError` settings. + */ + get getJavaScriptEmit(): { + (files: readonly DocumentIdentifier[]): EmitOutput; + gen(files: readonly DocumentIdentifier[]): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getJavaScriptEmit", + function (files: readonly DocumentIdentifier[]): EmitOutput { + const response = owner.client.apiRequest("getJavaScriptEmit", { + snapshot: owner.snapshotId, + project: owner.project.id, + files, + }); + return toEmitOutput(response); + }, + function* (files: readonly DocumentIdentifier[]): Generator { + const response = yield* apiRequest("getJavaScriptEmit", { + snapshot: owner.snapshotId, + project: owner.project.id, + files, + }); + return toEmitOutput(response); + }, + ); + } + + /** + * Gets declaration output for selected files regardless of project `noEmit`, `declaration`, `emitDeclarationOnly`, and `noEmitOnError` settings. + */ + get getDeclarationEmit(): { + (files: readonly DocumentIdentifier[]): EmitOutput; + gen(files: readonly DocumentIdentifier[]): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getDeclarationEmit", + function (files: readonly DocumentIdentifier[]): EmitOutput { + const response = owner.client.apiRequest("getDeclarationEmit", { + snapshot: owner.snapshotId, + project: owner.project.id, + files, + }); + return toEmitOutput(response); + }, + function* (files: readonly DocumentIdentifier[]): Generator { + const response = yield* apiRequest("getDeclarationEmit", { + snapshot: owner.snapshotId, + project: owner.project.id, + files, + }); + return toEmitOutput(response); + }, + ); + } +} + +function toEmitOutput(response: ProtocolEmitOutputResponse): EmitOutput { + const outputFiles = new Map(); + for (const { fileName, ...outputFile } of response.outputFiles) { + outputFiles.set(fileName, outputFile); + } + return { + emitSkipped: response.emitSkipped, + diagnostics: response.diagnostics, + outputFiles, + }; +} + +export class Checker { + private snapshotId: number; + private project: Project; + private client: Client; + private objectRegistry: ProjectObjectRegistry; + private wellKnownSymbols: { unknown: number; undefined: number; arguments: number; } | undefined; + private wellKnownSignatures: { unknown: number; } | undefined; + + constructor( + snapshotId: number, + project: Project, + client: Client, + objectRegistry: ProjectObjectRegistry, + ) { + this.snapshotId = snapshotId; + this.project = project; + this.client = client; + this.objectRegistry = objectRegistry; + } + + dispose(): void { + this.objectRegistry.clear(); + } + + get getSymbolAtLocation(): { + (node: Node): Symbol | undefined; + (nodes: readonly Node[]): (Symbol | undefined)[]; + gen(node: Node): Generator; + gen(nodes: readonly Node[]): Generator; + } { + const owner = this; + function getSymbolAtLocation(node: Node): Symbol | undefined; + function getSymbolAtLocation(nodes: readonly Node[]): (Symbol | undefined)[]; + function getSymbolAtLocation(nodeOrNodes: Node | readonly Node[]): Symbol | (Symbol | undefined)[] | undefined { + if (Array.isArray(nodeOrNodes)) { + const data = owner.client.apiRequest("getSymbolsAtLocations", { + snapshot: owner.snapshotId, + project: owner.project.id, + locations: nodeOrNodes.map(node => getNodeId(node)), + }); + return data.map(d => d ? owner.objectRegistry.getOrCreateSymbol(d) : undefined); + } + const data = owner.client.apiRequest("getSymbolAtLocation", { + snapshot: owner.snapshotId, + project: owner.project.id, + location: getNodeId(nodeOrNodes as Node), + }); + return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; + } + function gen(node: Node): Generator; + function gen(nodes: readonly Node[]): Generator; + function* gen(nodeOrNodes: Node | readonly Node[]): Generator { + if (Array.isArray(nodeOrNodes)) { + const data = yield* apiRequest("getSymbolsAtLocations", { + snapshot: owner.snapshotId, + project: owner.project.id, + locations: nodeOrNodes.map(node => getNodeId(node)), + }); + return data.map(d => d ? owner.objectRegistry.getOrCreateSymbol(d) : undefined); + } + const data = yield* apiRequest("getSymbolAtLocation", { + snapshot: owner.snapshotId, + project: owner.project.id, + location: getNodeId(nodeOrNodes as Node), + }); + return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; + } + return cacheGeneratorMethod(owner, "getSymbolAtLocation", getSymbolAtLocation, gen); + } + + get getSymbolAtPosition(): { + (file: DocumentIdentifier, position: number): Symbol | undefined; + (file: DocumentIdentifier, positions: readonly number[]): (Symbol | undefined)[]; + gen(file: DocumentIdentifier, position: number): Generator; + gen(file: DocumentIdentifier, positions: readonly number[]): Generator; + } { + const owner = this; + function getSymbolAtPosition(file: DocumentIdentifier, position: number): Symbol | undefined; + function getSymbolAtPosition(file: DocumentIdentifier, positions: readonly number[]): (Symbol | undefined)[]; + function getSymbolAtPosition(file: DocumentIdentifier, positionOrPositions: number | readonly number[]): Symbol | (Symbol | undefined)[] | undefined { + if (typeof positionOrPositions === "number") { + const data = owner.client.apiRequest("getSymbolAtPosition", { + snapshot: owner.snapshotId, + project: owner.project.id, + file, + position: positionOrPositions, + }); + return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; + } + const data = owner.client.apiRequest("getSymbolsAtPositions", { + snapshot: owner.snapshotId, + project: owner.project.id, + file, + positions: positionOrPositions, + }); + return data.map(d => d ? owner.objectRegistry.getOrCreateSymbol(d) : undefined); + } + function gen(file: DocumentIdentifier, position: number): Generator; + function gen(file: DocumentIdentifier, positions: readonly number[]): Generator; + function* gen(file: DocumentIdentifier, positionOrPositions: number | readonly number[]): Generator { + if (typeof positionOrPositions === "number") { + const data = yield* apiRequest("getSymbolAtPosition", { + snapshot: owner.snapshotId, + project: owner.project.id, + file, + position: positionOrPositions, + }); + return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; + } + const data = yield* apiRequest("getSymbolsAtPositions", { + snapshot: owner.snapshotId, + project: owner.project.id, + file, + positions: positionOrPositions, + }); + return data.map(d => d ? owner.objectRegistry.getOrCreateSymbol(d) : undefined); + } + return cacheGeneratorMethod(owner, "getSymbolAtPosition", getSymbolAtPosition, gen); + } + + get getSymbolOfSourceFile(): { + (file: DocumentIdentifier): Symbol | undefined; + (files: readonly DocumentIdentifier[]): (Symbol | undefined)[]; + gen(file: DocumentIdentifier): Generator; + gen(files: readonly DocumentIdentifier[]): Generator; + } { + const owner = this; + function getSymbolOfSourceFile(file: DocumentIdentifier): Symbol | undefined; + function getSymbolOfSourceFile(files: readonly DocumentIdentifier[]): (Symbol | undefined)[]; + function getSymbolOfSourceFile(fileOrFiles: DocumentIdentifier | readonly DocumentIdentifier[]): Symbol | (Symbol | undefined)[] | undefined { + if (Array.isArray(fileOrFiles)) { + const data = owner.client.apiRequest("getSymbolsOfSourceFiles", { + snapshot: owner.snapshotId, + project: owner.project.id, + files: fileOrFiles, + }); + return data.map(d => d ? owner.objectRegistry.getOrCreateSymbol(d) : undefined); + } + const data = owner.client.apiRequest("getSymbolOfSourceFile", { + snapshot: owner.snapshotId, + project: owner.project.id, + file: fileOrFiles as DocumentIdentifier, + }); + return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; + } + function gen(file: DocumentIdentifier): Generator; + function gen(files: readonly DocumentIdentifier[]): Generator; + function* gen(fileOrFiles: DocumentIdentifier | readonly DocumentIdentifier[]): Generator { + if (Array.isArray(fileOrFiles)) { + const data = yield* apiRequest("getSymbolsOfSourceFiles", { + snapshot: owner.snapshotId, + project: owner.project.id, + files: fileOrFiles, + }); + return data.map(d => d ? owner.objectRegistry.getOrCreateSymbol(d) : undefined); + } + const data = yield* apiRequest("getSymbolOfSourceFile", { + snapshot: owner.snapshotId, + project: owner.project.id, + file: fileOrFiles as DocumentIdentifier, + }); + return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; + } + return cacheGeneratorMethod(owner, "getSymbolOfSourceFile", getSymbolOfSourceFile, gen); + } + + /** + * Get the type of a symbol. Always returns a type; for symbols whose type + * cannot be determined the checker yields the error type (use + * {@link Type.isErrorType} to detect it). + */ + get getTypeOfSymbol(): { + (symbol: Symbol): Type; + (symbols: readonly Symbol[]): Type[]; + gen(symbol: Symbol): Generator; + gen(symbols: readonly Symbol[]): Generator; + } { + const owner = this; + function getTypeOfSymbol(symbol: Symbol): Type; + function getTypeOfSymbol(symbols: readonly Symbol[]): Type[]; + function getTypeOfSymbol(symbolOrSymbols: Symbol | readonly Symbol[]): Type | Type[] { + if (Array.isArray(symbolOrSymbols)) { + const data = owner.client.apiRequest("getTypesOfSymbols", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbols: symbolOrSymbols.map(s => s.id), + }); + return data.map(d => owner.objectRegistry.getOrCreateType(d)); + } + const data = owner.client.apiRequest("getTypeOfSymbol", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: (symbolOrSymbols as Symbol).id, + }); + return owner.objectRegistry.getOrCreateType(data); + } + function gen(symbol: Symbol): Generator; + function gen(symbols: readonly Symbol[]): Generator; + function* gen(symbolOrSymbols: Symbol | readonly Symbol[]): Generator { + if (Array.isArray(symbolOrSymbols)) { + const data = yield* apiRequest("getTypesOfSymbols", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbols: symbolOrSymbols.map(s => s.id), + }); + return data.map(d => owner.objectRegistry.getOrCreateType(d)); + } + const data = yield* apiRequest("getTypeOfSymbol", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: (symbolOrSymbols as Symbol).id, + }); + return owner.objectRegistry.getOrCreateType(data); + } + return cacheGeneratorMethod(owner, "getTypeOfSymbol", getTypeOfSymbol, gen); + } + + /** + * Get the declared type of a symbol. Always returns a type; for symbols whose + * declared type cannot be determined the checker yields the error type (use + * {@link Type.isErrorType} to detect it). + */ + get getDeclaredTypeOfSymbol(): { + (symbol: Symbol): Type; + gen(symbol: Symbol): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getDeclaredTypeOfSymbol", + function (symbol: Symbol): Type { + const data = owner.client.apiRequest("getDeclaredTypeOfSymbol", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: symbol.id, + }); + return owner.objectRegistry.getOrCreateType(data); + }, + function* (symbol: Symbol): Generator { + const data = yield* apiRequest("getDeclaredTypeOfSymbol", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: symbol.id, + }); + return owner.objectRegistry.getOrCreateType(data); + }, + ); + } + + /** + * Get the type of a symbol, excluding the missing type when + * `exactOptionalPropertyTypes: true` is set; for symbols whose + * type cannot be determined the checker yields the error type + * (use {@link Type.isErrorType} to detect it). + */ + get getNonMissingTypeOfSymbol(): { + (symbol: Symbol): Type; + gen(symbol: Symbol): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getNonMissingTypeOfSymbol", + function (symbol: Symbol): Type { + const data = owner.client.apiRequest("getNonMissingTypeOfSymbol", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: symbol.id, + }); + return owner.objectRegistry.getOrCreateType(data); + }, + function* (symbol: Symbol): Generator { + const data = yield* apiRequest("getNonMissingTypeOfSymbol", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: symbol.id, + }); + return owner.objectRegistry.getOrCreateType(data); + }, + ); + } + + get getReferencesToSymbolInFile(): { + (file: DocumentIdentifier, symbol: Symbol): NodeHandle[]; + gen(file: DocumentIdentifier, symbol: Symbol): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getReferencesToSymbolInFile", + function (file: DocumentIdentifier, symbol: Symbol): NodeHandle[] { + const data = owner.client.apiRequest("getReferencesToSymbolInFile", { + snapshot: owner.snapshotId, + project: owner.project.id, + file, + symbol: symbol.id, + }); + return (data ?? []).map(h => new NodeHandle(h, owner.project)); + }, + function* (file: DocumentIdentifier, symbol: Symbol): Generator { + const data = yield* apiRequest("getReferencesToSymbolInFile", { + snapshot: owner.snapshotId, + project: owner.project.id, + file, + symbol: symbol.id, + }); + return (data ?? []).map(h => new NodeHandle(h, owner.project)); + }, + ); + } + + /** @deprecated Use `project.languageService.getReferencedSymbolsForNode`. */ + get getReferencedSymbolsForNode(): { + (node: Node, position: number): ReferencedSymbolEntry[]; + gen(node: Node, position: number): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getReferencedSymbolsForNode", + function (node: Node, position: number): ReferencedSymbolEntry[] { + return owner.project.languageService.getReferencedSymbolsForNode(node, position); + }, + function* (node: Node, position: number): Generator { + return yield* owner.project.languageService.getReferencedSymbolsForNode.gen(node, position); + }, + ); + } + + /** @deprecated Use `project.languageService.getSignatureUsage`. */ + get getSignatureUsage(): { + (signatureDecl: Node): SignatureUsage[]; + gen(signatureDecl: Node): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getSignatureUsage", + function (signatureDecl: Node): SignatureUsage[] { + return owner.project.languageService.getSignatureUsage(signatureDecl); + }, + function* (signatureDecl: Node): Generator { + return yield* owner.project.languageService.getSignatureUsage.gen(signatureDecl); + }, + ); + } + + /** @deprecated Use `project.languageService.getCompletionsAtPosition`. */ + get getCompletionsAtPosition(): { + (document: string, position: number, options?: CompletionOptions): CompletionInfo | undefined; + gen(document: string, position: number, options?: CompletionOptions): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getCompletionsAtPosition", + function (document: string, position: number, options?: CompletionOptions): CompletionInfo | undefined { + return owner.project.languageService.getCompletionsAtPosition(document, position, options); + }, + function* (document: string, position: number, options?: CompletionOptions): Generator { + return yield* owner.project.languageService.getCompletionsAtPosition.gen(document, position, options); + }, + ); + } + + /** + * Get the type at a node location. Always returns a type; for nodes whose + * type cannot be determined the checker yields the error type (use + * {@link Type.isErrorType} to detect it). + */ + get getTypeAtLocation(): { + (node: Node): Type; + (nodes: readonly Node[]): Type[]; + gen(node: Node): Generator; + gen(nodes: readonly Node[]): Generator; + } { + const owner = this; + function getTypeAtLocation(node: Node): Type; + function getTypeAtLocation(nodes: readonly Node[]): Type[]; + function getTypeAtLocation(nodeOrNodes: Node | readonly Node[]): Type | Type[] { + if (Array.isArray(nodeOrNodes)) { + const data = owner.client.apiRequest("getTypeAtLocations", { + snapshot: owner.snapshotId, + project: owner.project.id, + locations: nodeOrNodes.map(node => getNodeId(node)), + }); + return data.map(d => owner.objectRegistry.getOrCreateType(d)); + } + const data = owner.client.apiRequest("getTypeAtLocation", { + snapshot: owner.snapshotId, + project: owner.project.id, + location: getNodeId(nodeOrNodes as Node), + }); + return owner.objectRegistry.getOrCreateType(data); + } + function gen(node: Node): Generator; + function gen(nodes: readonly Node[]): Generator; + function* gen(nodeOrNodes: Node | readonly Node[]): Generator { + if (Array.isArray(nodeOrNodes)) { + const data = yield* apiRequest("getTypeAtLocations", { + snapshot: owner.snapshotId, + project: owner.project.id, + locations: nodeOrNodes.map(node => getNodeId(node)), + }); + return data.map(d => owner.objectRegistry.getOrCreateType(d)); + } + const data = yield* apiRequest("getTypeAtLocation", { + snapshot: owner.snapshotId, + project: owner.project.id, + location: getNodeId(nodeOrNodes as Node), + }); + return owner.objectRegistry.getOrCreateType(data); + } + return cacheGeneratorMethod(owner, "getTypeAtLocation", getTypeAtLocation, gen); + } + + get getSignaturesOfType(): { + (type: Type, kind: SignatureKind): readonly Signature[]; + gen(type: Type, kind: SignatureKind): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getSignaturesOfType", + function (type: Type, kind: SignatureKind): readonly Signature[] { + return kind === SignatureKind.Call ? type.getCallSignatures() : type.getConstructSignatures(); + }, + function* (type: Type, kind: SignatureKind): Generator { + return kind === SignatureKind.Call ? (yield* type.getCallSignatures.gen()) : (yield* type.getConstructSignatures.gen()); + }, + ); + } + + /** + * Get the resolved signature of a call-like expression. Always returns a + * signature; when a call cannot be resolved the checker yields the unknown + * signature (use {@link Checker.isUnknownSignature} to detect it). + */ + get getResolvedSignature(): { + (node: Node): Signature; + gen(node: Node): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getResolvedSignature", + function (node: Node): Signature { + const data = owner.client.apiRequest("getResolvedSignature", { + snapshot: owner.snapshotId, + project: owner.project.id, + location: getNodeId(node), + }); + return owner.objectRegistry.getOrCreateSignature(data); + }, + function* (node: Node): Generator { + const data = yield* apiRequest("getResolvedSignature", { + snapshot: owner.snapshotId, + project: owner.project.id, + location: getNodeId(node), + }); + return owner.objectRegistry.getOrCreateSignature(data); + }, + ); + } + + get getTypeAtPosition(): { + (file: DocumentIdentifier, position: number): Type | undefined; + (file: DocumentIdentifier, positions: readonly number[]): (Type | undefined)[]; + gen(file: DocumentIdentifier, position: number): Generator; + gen(file: DocumentIdentifier, positions: readonly number[]): Generator; + } { + const owner = this; + function getTypeAtPosition(file: DocumentIdentifier, position: number): Type | undefined; + function getTypeAtPosition(file: DocumentIdentifier, positions: readonly number[]): (Type | undefined)[]; + function getTypeAtPosition(file: DocumentIdentifier, positionOrPositions: number | readonly number[]): Type | (Type | undefined)[] | undefined { + if (typeof positionOrPositions === "number") { + const data = owner.client.apiRequest("getTypeAtPosition", { + snapshot: owner.snapshotId, + project: owner.project.id, + file, + position: positionOrPositions, + }); + return data ? owner.objectRegistry.getOrCreateType(data) : undefined; + } + const data = owner.client.apiRequest("getTypesAtPositions", { + snapshot: owner.snapshotId, + project: owner.project.id, + file, + positions: positionOrPositions, + }); + return data.map(d => d ? owner.objectRegistry.getOrCreateType(d) : undefined); + } + function gen(file: DocumentIdentifier, position: number): Generator; + function gen(file: DocumentIdentifier, positions: readonly number[]): Generator; + function* gen(file: DocumentIdentifier, positionOrPositions: number | readonly number[]): Generator { + if (typeof positionOrPositions === "number") { + const data = yield* apiRequest("getTypeAtPosition", { + snapshot: owner.snapshotId, + project: owner.project.id, + file, + position: positionOrPositions, + }); + return data ? owner.objectRegistry.getOrCreateType(data) : undefined; + } + const data = yield* apiRequest("getTypesAtPositions", { + snapshot: owner.snapshotId, + project: owner.project.id, + file, + positions: positionOrPositions, + }); + return data.map(d => d ? owner.objectRegistry.getOrCreateType(d) : undefined); + } + return cacheGeneratorMethod(owner, "getTypeAtPosition", getTypeAtPosition, gen); + } + + get resolveName(): { + (name: string, meaning: SymbolFlags, location?: Node | DocumentPosition, excludeGlobals?: boolean): Symbol | undefined; + gen(name: string, meaning: SymbolFlags, location?: Node | DocumentPosition, excludeGlobals?: boolean): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "resolveName", + function (name: string, meaning: SymbolFlags, location?: Node | DocumentPosition, excludeGlobals?: boolean): Symbol | undefined { + // Distinguish Node (has `kind`) from DocumentPosition (has `document` and `position`) + const isNode = location && "kind" in location; + const data = owner.client.apiRequest("resolveName", { + snapshot: owner.snapshotId, + project: owner.project.id, + name, + meaning, + ...(isNode ? { location: getNodeId(location as Node) } : {}), + ...(!isNode && location + ? { + file: (location as DocumentPosition).document, + position: (location as DocumentPosition).position, + } + : {}), + ...(excludeGlobals !== undefined ? { excludeGlobals } : {}), + }); + return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; + }, + function* (name: string, meaning: SymbolFlags, location?: Node | DocumentPosition, excludeGlobals?: boolean): Generator { + // Distinguish Node (has `kind`) from DocumentPosition (has `document` and `position`) + const isNode = location && "kind" in location; + const data = yield* apiRequest("resolveName", { + snapshot: owner.snapshotId, + project: owner.project.id, + name, + meaning, + ...(isNode ? { location: getNodeId(location as Node) } : {}), + ...(!isNode && location + ? { + file: (location as DocumentPosition).document, + position: (location as DocumentPosition).position, + } + : {}), + ...(excludeGlobals !== undefined ? { excludeGlobals } : {}), + }); + return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; + }, + ); + } + + /** + * Returns all symbols with the given meaning that are visible at `location`. + */ + get getSymbolsInScope(): { + (location: Node | DocumentPosition, meaning: SymbolFlags): readonly Symbol[]; + gen(location: Node | DocumentPosition, meaning: SymbolFlags): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getSymbolsInScope", + function (location: Node | DocumentPosition, meaning: SymbolFlags): readonly Symbol[] { + // Distinguish Node (has `kind`) from DocumentPosition (has `document` and `position`) + const isNode = "kind" in location; + const data = owner.client.apiRequest("getSymbolsInScope", { + snapshot: owner.snapshotId, + project: owner.project.id, + meaning, + ...(isNode + ? { location: getNodeId(location as Node) } + : { + file: (location as DocumentPosition).document, + position: (location as DocumentPosition).position, + }), + }); + return data ? data.map(d => owner.objectRegistry.getOrCreateSymbol(d)) : []; + }, + function* (location: Node | DocumentPosition, meaning: SymbolFlags): Generator { + // Distinguish Node (has `kind`) from DocumentPosition (has `document` and `position`) + const isNode = "kind" in location; + const data = yield* apiRequest("getSymbolsInScope", { + snapshot: owner.snapshotId, + project: owner.project.id, + meaning, + ...(isNode + ? { location: getNodeId(location as Node) } + : { + file: (location as DocumentPosition).document, + position: (location as DocumentPosition).position, + }), + }); + return data ? data.map(d => owner.objectRegistry.getOrCreateSymbol(d)) : []; + }, + ); + } + + get getResolvedSymbol(): { + (node: Identifier): Symbol | undefined; + gen(node: Identifier): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getResolvedSymbol", + function (node: Identifier): Symbol | undefined { + const text = node.text; + if (!text) return undefined; + return owner.resolveName(text, SymbolFlags.Value | SymbolFlags.ExportValue, node); + }, + function* (node: Identifier): Generator { + const text = node.text; + if (!text) return undefined; + return yield* owner.resolveName.gen(text, SymbolFlags.Value | SymbolFlags.ExportValue, node); + }, + ); + } + + get getContextualType(): { + (node: Expression): Type | undefined; + gen(node: Expression): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getContextualType", + function (node: Expression): Type | undefined { + const data = owner.client.apiRequest("getContextualType", { + snapshot: owner.snapshotId, + project: owner.project.id, + location: getNodeId(node), + }); + return data ? owner.objectRegistry.getOrCreateType(data) : undefined; + }, + function* (node: Expression): Generator { + const data = yield* apiRequest("getContextualType", { + snapshot: owner.snapshotId, + project: owner.project.id, + location: getNodeId(node), + }); + return data ? owner.objectRegistry.getOrCreateType(data) : undefined; + }, + ); + } + + /** Get the base type of a literal type (e.g. `number` for `42`). Always returns a type. */ + get getBaseTypeOfLiteralType(): { + (type: Type): Type; + gen(type: Type): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getBaseTypeOfLiteralType", + function (type: Type): Type { + const data = owner.client.apiRequest("getBaseTypeOfLiteralType", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: type.id, + }); + return owner.objectRegistry.getOrCreateType(data); + }, + function* (type: Type): Generator { + const data = yield* apiRequest("getBaseTypeOfLiteralType", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: type.id, + }); + return owner.objectRegistry.getOrCreateType(data); + }, + ); + } + + /** Get the type with `null` and `undefined` removed. Always returns a type. */ + get getNonNullableType(): { + (type: Type): Type; + gen(type: Type): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getNonNullableType", + function (type: Type): Type { + return type.getNonNullableType(); + }, + function* (type: Type): Generator { + return yield* type.getNonNullableType.gen(); + }, + ); + } + + /** + * Get the type for a type node. Always returns a type; for type nodes whose + * type cannot be determined the checker yields the error type (use + * {@link Type.isErrorType} to detect it). + */ + get getTypeFromTypeNode(): { + (node: TypeNode): Type; + gen(node: TypeNode): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getTypeFromTypeNode", + function (node: TypeNode): Type { + const data = owner.client.apiRequest("getTypeFromTypeNode", { + snapshot: owner.snapshotId, + project: owner.project.id, + location: getNodeId(node), + }); + return owner.objectRegistry.getOrCreateType(data); + }, + function* (node: TypeNode): Generator { + const data = yield* apiRequest("getTypeFromTypeNode", { + snapshot: owner.snapshotId, + project: owner.project.id, + location: getNodeId(node), + }); + return owner.objectRegistry.getOrCreateType(data); + }, + ); + } + + /** Get the widened type. Always returns a type. */ + get getWidenedType(): { + (type: Type): Type; + gen(type: Type): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getWidenedType", + function (type: Type): Type { + const data = owner.client.apiRequest("getWidenedType", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: type.id, + }); + return owner.objectRegistry.getOrCreateType(data); + }, + function* (type: Type): Generator { + const data = yield* apiRequest("getWidenedType", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: type.id, + }); + return owner.objectRegistry.getOrCreateType(data); + }, + ); + } + + /** + * Get the type of the parameter at the given index in a signature. Always + * returns a type; an out-of-range index yields the `any` type. + */ + get getParameterType(): { + (signature: Signature, index: number): Type; + gen(signature: Signature, index: number): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getParameterType", + function (signature: Signature, index: number): Type { + const data = owner.client.apiRequest("getParameterType", { + snapshot: owner.snapshotId, + project: owner.project.id, + signature: signature.id, + index, + }); + return owner.objectRegistry.getOrCreateType(data); + }, + function* (signature: Signature, index: number): Generator { + const data = yield* apiRequest("getParameterType", { + snapshot: owner.snapshotId, + project: owner.project.id, + signature: signature.id, + index, + }); + return owner.objectRegistry.getOrCreateType(data); + }, + ); + } + + get isArrayLikeType(): { + (type: Type): boolean; + gen(type: Type): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "isArrayLikeType", + function (type: Type): boolean { + return owner.client.apiRequest("isArrayLikeType", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: type.id, + }); + }, + function* (type: Type): Generator { + return yield* apiRequest("isArrayLikeType", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: type.id, + }); + }, + ); + } + + get isTypeAssignableTo(): { + (source: Type, target: Type): boolean; + gen(source: Type, target: Type): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "isTypeAssignableTo", + function (source: Type, target: Type): boolean { + return owner.client.apiRequest("isTypeAssignableTo", { + snapshot: owner.snapshotId, + project: owner.project.id, + source: source.id, + target: target.id, + }); + }, + function* (source: Type, target: Type): Generator { + return yield* apiRequest("isTypeAssignableTo", { + snapshot: owner.snapshotId, + project: owner.project.id, + source: source.id, + target: target.id, + }); + }, + ); + } + + get getShorthandAssignmentValueSymbol(): { + (node: Node): Symbol | undefined; + gen(node: Node): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getShorthandAssignmentValueSymbol", + function (node: Node): Symbol | undefined { + const data = owner.client.apiRequest("getShorthandAssignmentValueSymbol", { + snapshot: owner.snapshotId, + project: owner.project.id, + location: getNodeId(node), + }); + return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; + }, + function* (node: Node): Generator { + const data = yield* apiRequest("getShorthandAssignmentValueSymbol", { + snapshot: owner.snapshotId, + project: owner.project.id, + location: getNodeId(node), + }); + return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; + }, + ); + } + + /** + * Get the type of a symbol as narrowed at a specific location. Always returns + * a type; for symbols whose type cannot be determined the checker yields the + * error type (use {@link Type.isErrorType} to detect it). + */ + get getTypeOfSymbolAtLocation(): { + (symbol: Symbol, location: Node): Type; + gen(symbol: Symbol, location: Node): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getTypeOfSymbolAtLocation", + function (symbol: Symbol, location: Node): Type { + const data = owner.client.apiRequest("getTypeOfSymbolAtLocation", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: symbol.id, + location: getNodeId(location), + }); + return owner.objectRegistry.getOrCreateType(data); + }, + function* (symbol: Symbol, location: Node): Generator { + const data = yield* apiRequest("getTypeOfSymbolAtLocation", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: symbol.id, + location: getNodeId(location), + }); + return owner.objectRegistry.getOrCreateType(data); + }, + ); + } + + private get getIntrinsicType(): { + (method: IntrinsicTypeMethod): Type; + gen(method: IntrinsicTypeMethod): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getIntrinsicType", + function (method: IntrinsicTypeMethod): Type { + const data = owner.client.apiRequest(method, { + snapshot: owner.snapshotId, + project: owner.project.id, + }); + return owner.objectRegistry.getOrCreateType(data); + }, + function* (method: IntrinsicTypeMethod): Generator { + const data = yield* apiRequest(method, { + snapshot: owner.snapshotId, + project: owner.project.id, + }); + return owner.objectRegistry.getOrCreateType(data); + }, + ); + } + + get getAnyType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getAnyType", + function (): Type { + return owner.getIntrinsicType("getAnyType"); + }, + function* (): Generator { + return yield* owner.getIntrinsicType.gen("getAnyType"); + }, + ); + } + get getStringType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getStringType", + function (): Type { + return owner.getIntrinsicType("getStringType"); + }, + function* (): Generator { + return yield* owner.getIntrinsicType.gen("getStringType"); + }, + ); + } + get getNumberType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getNumberType", + function (): Type { + return owner.getIntrinsicType("getNumberType"); + }, + function* (): Generator { + return yield* owner.getIntrinsicType.gen("getNumberType"); + }, + ); + } + get getBooleanType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getBooleanType", + function (): Type { + return owner.getIntrinsicType("getBooleanType"); + }, + function* (): Generator { + return yield* owner.getIntrinsicType.gen("getBooleanType"); + }, + ); + } + get getVoidType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getVoidType", + function (): Type { + return owner.getIntrinsicType("getVoidType"); + }, + function* (): Generator { + return yield* owner.getIntrinsicType.gen("getVoidType"); + }, + ); + } + get getUndefinedType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getUndefinedType", + function (): Type { + return owner.getIntrinsicType("getUndefinedType"); + }, + function* (): Generator { + return yield* owner.getIntrinsicType.gen("getUndefinedType"); + }, + ); + } + get getNullType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getNullType", + function (): Type { + return owner.getIntrinsicType("getNullType"); + }, + function* (): Generator { + return yield* owner.getIntrinsicType.gen("getNullType"); + }, + ); + } + get getNeverType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getNeverType", + function (): Type { + return owner.getIntrinsicType("getNeverType"); + }, + function* (): Generator { + return yield* owner.getIntrinsicType.gen("getNeverType"); + }, + ); + } + get getUnknownType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getUnknownType", + function (): Type { + return owner.getIntrinsicType("getUnknownType"); + }, + function* (): Generator { + return yield* owner.getIntrinsicType.gen("getUnknownType"); + }, + ); + } + get getBigIntType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getBigIntType", + function (): Type { + return owner.getIntrinsicType("getBigIntType"); + }, + function* (): Generator { + return yield* owner.getIntrinsicType.gen("getBigIntType"); + }, + ); + } + get getESSymbolType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getESSymbolType", + function (): Type { + return owner.getIntrinsicType("getESSymbolType"); + }, + function* (): Generator { + return yield* owner.getIntrinsicType.gen("getESSymbolType"); + }, + ); + } + get getNonPrimitiveType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getNonPrimitiveType", + function (): Type { + return owner.getIntrinsicType("getNonPrimitiveType"); + }, + function* (): Generator { + return yield* owner.getIntrinsicType.gen("getNonPrimitiveType"); + }, + ); + } + + get typeToTypeNode(): { + (type: Type, enclosingDeclaration?: Node, flags?: number): TypeNode | undefined; + gen(type: Type, enclosingDeclaration?: Node, flags?: number): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "typeToTypeNode", + function (type: Type, enclosingDeclaration?: Node, flags?: number): TypeNode | undefined { + const binaryData = owner.client.apiRequestBinary("typeToTypeNode", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: type.id, + ...(enclosingDeclaration ? { location: getNodeId(enclosingDeclaration) } : {}), + ...(flags !== undefined ? { flags } : {}), + }); + if (!binaryData) return undefined; + return decodeNode(binaryData) as TypeNode; + }, + function* (type: Type, enclosingDeclaration?: Node, flags?: number): Generator { + const binaryData = owner.client.apiRequestBinary("typeToTypeNode", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: type.id, + ...(enclosingDeclaration ? { location: getNodeId(enclosingDeclaration) } : {}), + ...(flags !== undefined ? { flags } : {}), + }); + if (!binaryData) return undefined; + return decodeNode(binaryData) as TypeNode; + }, + ); + } + + get signatureToSignatureDeclaration(): { + (signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): Node | undefined; + gen(signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "signatureToSignatureDeclaration", + function (signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): Node | undefined { + const binaryData = owner.client.apiRequestBinary("signatureToSignatureDeclaration", { + snapshot: owner.snapshotId, + project: owner.project.id, + signature: signature.id, + kind, + ...(enclosingDeclaration ? { location: getNodeId(enclosingDeclaration) } : {}), + ...(flags !== undefined ? { flags } : {}), + }); + if (!binaryData) return undefined; + return decodeNode(binaryData) as Node; + }, + function* (signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): Generator { + const binaryData = owner.client.apiRequestBinary("signatureToSignatureDeclaration", { + snapshot: owner.snapshotId, + project: owner.project.id, + signature: signature.id, + kind, + ...(enclosingDeclaration ? { location: getNodeId(enclosingDeclaration) } : {}), + ...(flags !== undefined ? { flags } : {}), + }); + if (!binaryData) return undefined; + return decodeNode(binaryData) as Node; + }, + ); + } + + get typeToString(): { + (type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string; + gen(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "typeToString", + function (type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string { + const result = owner.client.apiRequest("typeToString", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: type.id, + ...(enclosingDeclaration ? { location: getNodeId(enclosingDeclaration) } : {}), + ...(flags !== undefined ? { flags } : {}), + }); + if (typeof result !== "string") throw new TypeError("typeToString returned a non-string result"); + return result; + }, + function* (type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): Generator { + const result = yield* apiRequest("typeToString", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: type.id, + ...(enclosingDeclaration ? { location: getNodeId(enclosingDeclaration) } : {}), + ...(flags !== undefined ? { flags } : {}), + }); + if (typeof result !== "string") throw new TypeError("typeToString returned a non-string result"); + return result; + }, + ); + } + + get isContextSensitive(): { + (node: Node): boolean; + gen(node: Node): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "isContextSensitive", + function (node: Node): boolean { + return owner.client.apiRequest("isContextSensitive", { + snapshot: owner.snapshotId, + project: owner.project.id, + location: getNodeId(node), + }); + }, + function* (node: Node): Generator { + return yield* apiRequest("isContextSensitive", { + snapshot: owner.snapshotId, + project: owner.project.id, + location: getNodeId(node), + }); + }, + ); + } + + get isArrayType(): { + (type: Type): boolean; + gen(type: Type): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "isArrayType", + function (type: Type): boolean { + return owner.client.apiRequest("isArrayType", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: type.id, + }); + }, + function* (type: Type): Generator { + return yield* apiRequest("isArrayType", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: type.id, + }); + }, + ); + } + + get isTupleType(): { + (type: Type): boolean; + gen(type: Type): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "isTupleType", + function (type: Type): boolean { + return owner.client.apiRequest("isTupleType", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: type.id, + }); + }, + function* (type: Type): Generator { + return yield* apiRequest("isTupleType", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: type.id, + }); + }, + ); + } + + /** + * The following symbols are considered read-only: + * - Properties with a `readonly` modifier + * - Variables declared with `const` + * - Get accessors without matching set accessors + * - Enum members + * - `Object.defineProperty` assignments with `writable: false` or no setter + * - Unions and intersections of the above + */ + get isReadonlySymbol(): { + (symbol: Symbol): boolean; + gen(symbol: Symbol): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "isReadonlySymbol", + function (symbol: Symbol): boolean { + return owner.client.apiRequest("isReadonlySymbol", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: symbol.id, + }); + }, + function* (symbol: Symbol): Generator { + return yield* apiRequest("isReadonlySymbol", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: symbol.id, + }); + }, + ); + } + + /** Get the return type of a signature. Always returns a type. */ + get getReturnTypeOfSignature(): { + (signature: Signature): Type; + gen(signature: Signature): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getReturnTypeOfSignature", + function (signature: Signature): Type { + return signature.getReturnType(); + }, + function* (signature: Signature): Generator { + return yield* signature.getReturnType.gen(); + }, + ); + } + + /** + * Get the rest type of a signature. Always returns a type; a signature with + * no rest parameter yields the `any` type. + */ + get getRestTypeOfSignature(): { + (signature: Signature): Type; + gen(signature: Signature): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getRestTypeOfSignature", + function (signature: Signature): Type { + const data = owner.client.apiRequest("getRestTypeOfSignature", { + snapshot: owner.snapshotId, + project: owner.project.id, + signature: signature.id, + }); + return owner.objectRegistry.getOrCreateType(data); + }, + function* (signature: Signature): Generator { + const data = yield* apiRequest("getRestTypeOfSignature", { + snapshot: owner.snapshotId, + project: owner.project.id, + signature: signature.id, + }); + return owner.objectRegistry.getOrCreateType(data); + }, + ); + } + + get getTypePredicateOfSignature(): { + (signature: Signature): TypePredicate | undefined; + gen(signature: Signature): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getTypePredicateOfSignature", + function (signature: Signature): TypePredicate | undefined { + const data = owner.client.apiRequest("getTypePredicateOfSignature", { + snapshot: owner.snapshotId, + project: owner.project.id, + signature: signature.id, + }); + if (!data) return undefined; + return { + kind: data.kind, + parameterIndex: data.parameterIndex, + parameterName: data.parameterName, + type: data.type ? owner.objectRegistry.getOrCreateType(data.type) : undefined, + } as TypePredicate; + }, + function* (signature: Signature): Generator { + const data = yield* apiRequest("getTypePredicateOfSignature", { + snapshot: owner.snapshotId, + project: owner.project.id, + signature: signature.id, + }); + if (!data) return undefined; + return { + kind: data.kind, + parameterIndex: data.parameterIndex, + parameterName: data.parameterName, + type: data.type ? owner.objectRegistry.getOrCreateType(data.type) : undefined, + } as TypePredicate; + }, + ); + } + + /** + * Get the base types of a class or interface type. A type with no base types + * yields an empty array. + */ + get getBaseTypes(): { + (type: InterfaceType): readonly Type[]; + gen(type: InterfaceType): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getBaseTypes", + function (type: InterfaceType): readonly Type[] { + return type.getBaseTypes() ?? []; + }, + function* (type: InterfaceType): Generator { + return (yield* type.getBaseTypes.gen()) ?? []; + }, + ); + } + + /** Get the apparent type of a type. Always returns a type. */ + get getApparentType(): { + (type: Type): Type; + gen(type: Type): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getApparentType", + function (type: Type): Type { + return type.getApparentType(); + }, + function* (type: Type): Generator { + return yield* type.getApparentType.gen(); + }, + ); + } + + /** Get the reduced type of a type. Always returns a type. */ + get getReducedType(): { + (type: Type): Type; + gen(type: Type): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getReducedType", + function (type: Type): Type { + return type.getReducedType(); + }, + function* (type: Type): Generator { + return yield* type.getReducedType.gen(); + }, + ); + } + + get getPropertiesOfType(): { + (type: Type): readonly Symbol[]; + gen(type: Type): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getPropertiesOfType", + function (type: Type): readonly Symbol[] { + return type.getProperties(); + }, + function* (type: Type): Generator { + return yield* type.getProperties.gen(); + }, + ); + } + + get getIndexInfosOfType(): { + (type: Type): readonly IndexInfo[]; + gen(type: Type): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getIndexInfosOfType", + function (type: Type): readonly IndexInfo[] { + return type.getIndexInfos(); + }, + function* (type: Type): Generator { + return yield* type.getIndexInfos.gen(); + }, + ); + } + + /** + * Get the constraint of a type parameter (the `T` in ``), or + * undefined if it has none. + */ + get getConstraintOfTypeParameter(): { + (type: TypeParameter): Type | undefined; + gen(type: TypeParameter): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getConstraintOfTypeParameter", + function (type: TypeParameter): Type | undefined { + return type.getConstraint(); + }, + function* (type: TypeParameter): Generator { + return yield* type.getConstraint.gen(); + }, + ); + } + + get getDefaultFromTypeParameter(): { + (type: TypeParameter): Type | undefined; + gen(type: TypeParameter): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getDefaultFromTypeParameter", + function (type: TypeParameter): Type | undefined { + return type.getDefault(); + }, + function* (type: TypeParameter): Generator { + return yield* type.getDefault.gen(); + }, + ); + } + + get getBaseConstraintOfType(): { + (type: Type): Type | undefined; + gen(type: Type): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getBaseConstraintOfType", + function (type: Type): Type | undefined { + const data = owner.client.apiRequest("getBaseConstraintOfType", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: type.id, + }); + return data ? owner.objectRegistry.getOrCreateType(data) : undefined; + }, + function* (type: Type): Generator { + const data = yield* apiRequest("getBaseConstraintOfType", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: type.id, + }); + return data ? owner.objectRegistry.getOrCreateType(data) : undefined; + }, + ); + } + + get getPropertyOfType(): { + (type: Type, name: string): Symbol | undefined; + gen(type: Type, name: string): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getPropertyOfType", + function (type: Type, name: string): Symbol | undefined { + const data = owner.client.apiRequest("getPropertyOfType", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: type.id, + name, + }); + return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; + }, + function* (type: Type, name: string): Generator { + const data = yield* apiRequest("getPropertyOfType", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: type.id, + name, + }); + return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; + }, + ); + } + + get getConstantValue(): { + (node: Node): string | number | undefined; + gen(node: Node): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getConstantValue", + function (node: Node): string | number | undefined { + const data = owner.client.apiRequest("getConstantValue", { + snapshot: owner.snapshotId, + project: owner.project.id, + location: getNodeId(node), + }); + return typeof data === "string" || typeof data === "number" ? data : undefined; + }, + function* (node: Node): Generator { + const data = yield* apiRequest("getConstantValue", { + snapshot: owner.snapshotId, + project: owner.project.id, + location: getNodeId(node), + }); + return typeof data === "string" || typeof data === "number" ? data : undefined; + }, + ); + } + + /** Get the signature of a function-like declaration. Always returns a signature. */ + get getSignatureFromDeclaration(): { + (node: Node): Signature; + gen(node: Node): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getSignatureFromDeclaration", + function (node: Node): Signature { + const data = owner.client.apiRequest("getSignatureFromDeclaration", { + snapshot: owner.snapshotId, + project: owner.project.id, + location: getNodeId(node), + }); + return owner.objectRegistry.getOrCreateSignature(data); + }, + function* (node: Node): Generator { + const data = yield* apiRequest("getSignatureFromDeclaration", { + snapshot: owner.snapshotId, + project: owner.project.id, + location: getNodeId(node), + }); + return owner.objectRegistry.getOrCreateSignature(data); + }, + ); + } + + get getExportSpecifierLocalTargetSymbol(): { + (node: Node): Symbol | undefined; + gen(node: Node): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getExportSpecifierLocalTargetSymbol", + function (node: Node): Symbol | undefined { + const data = owner.client.apiRequest("getExportSpecifierLocalTargetSymbol", { + snapshot: owner.snapshotId, + project: owner.project.id, + location: getNodeId(node), + }); + return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; + }, + function* (node: Node): Generator { + const data = yield* apiRequest("getExportSpecifierLocalTargetSymbol", { + snapshot: owner.snapshotId, + project: owner.project.id, + location: getNodeId(node), + }); + return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; + }, + ); + } + + /** + * Follow all aliases to get the original symbol. Always returns a symbol; for + * an unresolved alias the checker yields the unknown symbol (use + * {@link Checker.isUnknownSymbol} to detect it). + */ + get getAliasedSymbol(): { + (symbol: Symbol): Symbol; + gen(symbol: Symbol): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getAliasedSymbol", + function (symbol: Symbol): Symbol { + const data = owner.client.apiRequest("getAliasedSymbol", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: symbol.id, + }); + return owner.objectRegistry.getOrCreateSymbol(data); + }, + function* (symbol: Symbol): Generator { + const data = yield* apiRequest("getAliasedSymbol", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: symbol.id, + }); + return owner.objectRegistry.getOrCreateSymbol(data); + }, + ); + } + + /** + * Get the fully qualified name of a symbol, walking up its parent chain + * (e.g. `"/path/to/module".Namespace.Name`). + */ + get getFullyQualifiedName(): { + (symbol: Symbol): string; + gen(symbol: Symbol): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getFullyQualifiedName", + function (symbol: Symbol): string { + return owner.client.apiRequest("getFullyQualifiedName", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: symbol.id, + }); + }, + function* (symbol: Symbol): Generator { + return yield* apiRequest("getFullyQualifiedName", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: symbol.id, + }); + }, + ); + } + + get getImmediateAliasedSymbol(): { + (symbol: Symbol): Symbol | undefined; + gen(symbol: Symbol): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getImmediateAliasedSymbol", + function (symbol: Symbol): Symbol | undefined { + const data = owner.client.apiRequest("getImmediateAliasedSymbol", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: symbol.id, + }); + return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; + }, + function* (symbol: Symbol): Generator { + const data = yield* apiRequest("getImmediateAliasedSymbol", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: symbol.id, + }); + return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; + }, + ); + } + + /** + * Get the target symbol if instantiated, or the provided symbol otherwise. + */ + get getTargetSymbol(): { + (symbol: Symbol): Symbol; + gen(symbol: Symbol): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getTargetSymbol", + function (symbol: Symbol): Symbol { + if (symbol.checkFlags & CheckFlags.Instantiated) { + const data = owner.client.apiRequest("getTargetSymbol", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: symbol.id, + }); + return owner.objectRegistry.getOrCreateSymbol(data); + } + return symbol; + }, + function* (symbol: Symbol): Generator { + if (symbol.checkFlags & CheckFlags.Instantiated) { + const data = yield* apiRequest("getTargetSymbol", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: symbol.id, + }); + return owner.objectRegistry.getOrCreateSymbol(data); + } + return symbol; + }, + ); + } + + /** + * Fetch (once, then cache) the handle ids of the per-checker singleton + * symbols (unknown, undefined, arguments). These ids are stable for the life + * of the project's checker, so identity checks against them are local after + * the first call. + */ + private get getWellKnownSymbols(): { + (): { unknown: number; undefined: number; arguments: number; }; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getWellKnownSymbols", + function (): { unknown: number; undefined: number; arguments: number; } { + return owner.wellKnownSymbols ??= owner.client.apiRequest("getWellKnownSymbols", { + snapshot: owner.snapshotId, + project: owner.project.id, + }); + }, + function* (): Generator { + return owner.wellKnownSymbols ??= yield* apiRequest("getWellKnownSymbols", { + snapshot: owner.snapshotId, + project: owner.project.id, + }); + }, + ); + } + + /** + * Returns `true` if the symbol is the checker's "unknown" symbol (e.g. the + * result of {@link Checker.getAliasedSymbol} on an unresolved alias). + */ + get isUnknownSymbol(): { + (symbol: Symbol): boolean; + gen(symbol: Symbol): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "isUnknownSymbol", + function (symbol: Symbol): boolean { + return symbol.id === (owner.getWellKnownSymbols()).unknown; + }, + function* (symbol: Symbol): Generator { + return symbol.id === (yield* owner.getWellKnownSymbols.gen()).unknown; + }, + ); + } + + /** + * Returns `true` if the symbol is the checker's "undefined" symbol. + */ + get isUndefinedSymbol(): { + (symbol: Symbol): boolean; + gen(symbol: Symbol): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "isUndefinedSymbol", + function (symbol: Symbol): boolean { + return symbol.id === (owner.getWellKnownSymbols()).undefined; + }, + function* (symbol: Symbol): Generator { + return symbol.id === (yield* owner.getWellKnownSymbols.gen()).undefined; + }, + ); + } + + /** + * Returns `true` if the symbol is the checker's "arguments" symbol. + */ + get isArgumentsSymbol(): { + (symbol: Symbol): boolean; + gen(symbol: Symbol): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "isArgumentsSymbol", + function (symbol: Symbol): boolean { + return symbol.id === (owner.getWellKnownSymbols()).arguments; + }, + function* (symbol: Symbol): Generator { + return symbol.id === (yield* owner.getWellKnownSymbols.gen()).arguments; + }, + ); + } + + /** + * Fetch (once, then cache) the handle id of the per-checker unknown + * signature. This id is stable for the life of the project's checker, so + * identity checks against it are local after the first call. + */ + private get getWellKnownSignatures(): { + (): { unknown: number; }; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getWellKnownSignatures", + function (): { unknown: number; } { + return owner.wellKnownSignatures ??= owner.client.apiRequest("getWellKnownSignatures", { + snapshot: owner.snapshotId, + project: owner.project.id, + }); + }, + function* (): Generator { + return owner.wellKnownSignatures ??= yield* apiRequest("getWellKnownSignatures", { + snapshot: owner.snapshotId, + project: owner.project.id, + }); + }, + ); + } + + /** + * Returns `true` if the signature is the checker's "unknown" signature (e.g. + * the result of {@link Checker.getResolvedSignature} on a call that cannot be + * resolved). + */ + get isUnknownSignature(): { + (signature: Signature): boolean; + gen(signature: Signature): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "isUnknownSignature", + function (signature: Signature): boolean { + return signature.id === (owner.getWellKnownSignatures()).unknown; + }, + function* (signature: Signature): Generator { + return signature.id === (yield* owner.getWellKnownSignatures.gen()).unknown; + }, + ); + } + + get getExportsOfModule(): { + (symbol: Symbol): readonly Symbol[]; + gen(symbol: Symbol): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getExportsOfModule", + function (symbol: Symbol): readonly Symbol[] { + const data = owner.client.apiRequest("getExportsOfModule", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: symbol.id, + }); + return data ? data.map(d => owner.objectRegistry.getOrCreateSymbol(d)) : []; + }, + function* (symbol: Symbol): Generator { + const data = yield* apiRequest("getExportsOfModule", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: symbol.id, + }); + return data ? data.map(d => owner.objectRegistry.getOrCreateSymbol(d)) : []; + }, + ); + } + + get getMemberInModuleExports(): { + (symbol: Symbol, name: string): Symbol | undefined; + gen(symbol: Symbol, name: string): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getMemberInModuleExports", + function (symbol: Symbol, name: string): Symbol | undefined { + const data = owner.client.apiRequest("getMemberInModuleExports", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: symbol.id, + name, + }); + return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; + }, + function* (symbol: Symbol, name: string): Generator { + const data = yield* apiRequest("getMemberInModuleExports", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: symbol.id, + name, + }); + return data ? owner.objectRegistry.getOrCreateSymbol(data) : undefined; + }, + ); + } + + get getJsDocTagsOfSymbol(): { + (symbol: Symbol): readonly JSDocTagInfo[]; + gen(symbol: Symbol): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getJsDocTagsOfSymbol", + function (symbol: Symbol): readonly JSDocTagInfo[] { + const data = owner.client.apiRequest("getJsDocTags", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: symbol.id, + }); + return data ?? []; + }, + function* (symbol: Symbol): Generator { + const data = yield* apiRequest("getJsDocTags", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: symbol.id, + }); + return data ?? []; + }, + ); + } + + get getDocumentationCommentOfSymbol(): { + (symbol: Symbol): string; + gen(symbol: Symbol): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getDocumentationCommentOfSymbol", + function (symbol: Symbol): string { + return owner.client.apiRequest("getDocumentationComment", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: symbol.id, + }); + }, + function* (symbol: Symbol): Generator { + return yield* apiRequest("getDocumentationComment", { + snapshot: owner.snapshotId, + project: owner.project.id, + symbol: symbol.id, + }); + }, + ); + } + + /** + * Get the type arguments of a type reference (e.g. the `string` in `Array`). + */ + get getTypeArguments(): { + (type: TypeReference): readonly Type[]; + gen(type: TypeReference): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getTypeArguments", + function (type: TypeReference): readonly Type[] { + const data = owner.client.apiRequest("getTypeArguments", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: type.id, + }); + return data ? data.map(d => owner.objectRegistry.getOrCreateType(d)) : []; + }, + function* (type: TypeReference): Generator { + const data = yield* apiRequest("getTypeArguments", { + snapshot: owner.snapshotId, + project: owner.project.id, + type: type.id, + }); + return data ? data.map(d => owner.objectRegistry.getOrCreateType(d)) : []; + }, + ); + } +} + +export interface PrintNodeOptions { + preserveSourceNewlines?: boolean | undefined; + neverAsciiEscape?: boolean | undefined; + terminateUnterminatedLiterals?: boolean | undefined; +} + +export class Emitter { + private client: Client; + + constructor(client: Client) { + this.client = client; + } + + get printNode(): { + (node: Node, options?: PrintNodeOptions): string; + gen(node: Node, options?: PrintNodeOptions): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "printNode", + function (node: Node, options: PrintNodeOptions = {}): string { + const encoded = encodeNode(node); + const base64 = uint8ArrayToBase64(encoded); + return owner.client.apiRequest("printNode", { + data: base64, + ...(options.preserveSourceNewlines !== undefined ? { preserveSourceNewlines: options.preserveSourceNewlines } : {}), + ...(options.neverAsciiEscape !== undefined ? { neverAsciiEscape: options.neverAsciiEscape } : {}), + ...(options.terminateUnterminatedLiterals !== undefined ? { terminateUnterminatedLiterals: options.terminateUnterminatedLiterals } : {}), + }); + }, + function* (node: Node, options: PrintNodeOptions = {}): Generator { + const encoded = encodeNode(node); + const base64 = uint8ArrayToBase64(encoded); + return yield* apiRequest("printNode", { + data: base64, + ...(options.preserveSourceNewlines !== undefined ? { preserveSourceNewlines: options.preserveSourceNewlines } : {}), + ...(options.neverAsciiEscape !== undefined ? { neverAsciiEscape: options.neverAsciiEscape } : {}), + ...(options.terminateUnterminatedLiterals !== undefined ? { terminateUnterminatedLiterals: options.terminateUnterminatedLiterals } : {}), + }); + }, + ); + } +} + +export class SnapshotInternalAPI { + private snapshotId: number; + private client: Client; + + constructor(snapshotId: number, client: Client) { + this.snapshotId = snapshotId; + this.client = client; + } + + /** + * Format a synthesized node with the correct indentation for insertion at a + * specific position in an existing source file. + * + * @param node The synthesized AST node to format. + * @param file The target file where the node will be inserted. + * @param position The UTF-16 code-unit offset in the target file for insertion. + * @returns The formatted text of the node, indented for the insertion position. + */ + get formatNodeForInsertion(): { + (node: Node, file: DocumentIdentifier, position: number): string; + gen(node: Node, file: DocumentIdentifier, position: number): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "formatNodeForInsertion", + function (node: Node, file: DocumentIdentifier, position: number): string { + const data = owner.client.apiRequest("getDefaultProjectForFile", { + snapshot: owner.snapshotId, + file, + }); + if (!data) { + throw new Error(`No project found for file: ${typeof file === "string" ? file : file.uri}`); + } + + const encoded = encodeNode(node); + const base64 = uint8ArrayToBase64(encoded); + return owner.client.apiRequest("formatNodeForInsertion", { + snapshot: owner.snapshotId, + project: data.id, + file, + position, + data: base64, + }); + }, + function* (node: Node, file: DocumentIdentifier, position: number): Generator { + const data = yield* apiRequest("getDefaultProjectForFile", { + snapshot: owner.snapshotId, + file, + }); + if (!data) { + throw new Error(`No project found for file: ${typeof file === "string" ? file : file.uri}`); + } + + const encoded = encodeNode(node); + const base64 = uint8ArrayToBase64(encoded); + return yield* apiRequest("formatNodeForInsertion", { + snapshot: owner.snapshotId, + project: data.id, + file, + position, + data: base64, + }); + }, + ); + } +} + +export class NodeHandle { + /** + * The project this handle was produced in, used as the default for {@link resolve}. + * Node handles are only meaningful within a project's program, so the producing project + * is remembered so callers don't have to pass it explicitly. + */ + private readonly canonicalProject: Project; + readonly index: number; + readonly kind: SyntaxKind; + readonly path: Path; + + constructor(handle: string, canonicalProject: Project) { + const parsed = parseNodeHandle(handle); + this.index = parsed.index; + this.kind = parsed.kind; + this.path = parsed.path; + this.canonicalProject = canonicalProject; + } + + /** + * Resolve this handle to the actual AST node by fetching the source file from a project + * and looking up the node by index. If no project is passed, the project that produced + * the handle is used. + */ + get resolve(): { + (project?: Project): T | undefined; + gen(project?: Project): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "resolve", + function (project: Project = owner.canonicalProject): T | undefined { + const sourceFile = project.program.getSourceFile(owner.path); + if (!sourceFile) { + return undefined; + } + return (sourceFile as unknown as RemoteSourceFile).getOrCreateNodeAtIndex(owner.index) as T | undefined; + }, + function* (project: Project = owner.canonicalProject): Generator { + const sourceFile = yield* project.program.getSourceFile.gen(owner.path); + if (!sourceFile) { + return undefined; + } + return (sourceFile as unknown as RemoteSourceFile).getOrCreateNodeAtIndex(owner.index) as T | undefined; + }, + ); + } +} + +/** A symbol definition paired with all of its reference nodes. */ +export interface ReferencedSymbolEntry { + /** The node handle for the symbol's definition. */ + definition: NodeHandle; + /** The resolved symbol for the definition, if available. */ + symbol?: Symbol | undefined; + /** The node handles for each reference to the symbol. */ + references: NodeHandle[]; +} + +/** A single usage of a signature, pairing the reference name with its call expression (if any). */ +export interface SignatureUsage { + /** The node handle for the name reference. */ + name: NodeHandle; + /** The node handle for the call expression, if the reference is invoked. */ + call?: NodeHandle | undefined; +} + +export class Symbol { + private objectRegistry: SnapshotObjectRegistry; + /** + * The project this symbol was first observed in, used as the default project for + * lookups that need a project context (members/exports/parent). Symbols are shared + * snapshot-wide, so these lookups can otherwise be ambiguous about which project to use. + */ + private readonly canonicalProject: Project; + + readonly id: number; + /** The escaped (`__String`) name, used as the key in member/export tables. */ + readonly escapedName: __String; + /** The display name (escaped underscores removed). */ + readonly name: string; + readonly flags: SymbolFlags; + readonly checkFlags: CheckFlags; + readonly declarations: readonly NodeHandle[]; + readonly valueDeclaration: NodeHandle | undefined; + private readonly parent!: number; + private readonly exportSymbol!: number; + private membersCache: ReadonlyMap<__String, Symbol> | undefined; + private exportsCache: ReadonlyMap<__String, Symbol> | undefined; + + constructor(data: SymbolResponse, objectRegistry: SnapshotObjectRegistry) { + this.objectRegistry = objectRegistry; + + this.id = data.id; + this.escapedName = data.name as __String; + this.name = unescapeLeadingUnderscores(data.name as __String); + this.flags = data.flags; + this.checkFlags = data.checkFlags; + const canonicalProject = objectRegistry.getProject(data.project as Path); + if (!canonicalProject) { + throw new Error(`Symbol ${data.id} references unknown canonical project '${data.project}'`); + } + this.canonicalProject = canonicalProject; + this.declarations = (data.declarations ?? []).map(d => new NodeHandle(d, canonicalProject)); + this.valueDeclaration = data.valueDeclaration ? new NodeHandle(data.valueDeclaration, canonicalProject) : undefined; + + if (data.parent !== undefined) this.parent = data.parent; + if (data.exportSymbol !== undefined) this.exportSymbol = data.exportSymbol; + } + + get getParent(): { + (): Symbol | undefined; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getParent", + function (): Symbol | undefined { + return owner.objectRegistry.fetchSymbol(owner, "getParentOfSymbol", owner.parent, owner.canonicalProject.id); + }, + function* (): Generator { + return yield* owner.objectRegistry.fetchSymbol.gen(owner, "getParentOfSymbol", owner.parent, owner.canonicalProject.id); + }, + ); + } + + /** + * Get this symbol's members keyed by escaped name. The result is cached on + * the symbol, so repeated calls do not round-trip to the server. + */ + get getMembers(): { + (): ReadonlyMap<__String, Symbol>; + gen(): Generator, ProtocolResponse["result"]>; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getMembers", + function (): ReadonlyMap<__String, Symbol> { + return owner.membersCache ??= owner.fetchSymbolTable("getMembersOfSymbol"); + }, + function* (): Generator, ProtocolResponse["result"]> { + return owner.membersCache ??= yield* owner.fetchSymbolTable.gen("getMembersOfSymbol"); + }, + ); + } + + /** + * Get this symbol's exports keyed by escaped name. The result is cached on + * the symbol, so repeated calls do not round-trip to the server. + */ + get getExports(): { + (): ReadonlyMap<__String, Symbol>; + gen(): Generator, ProtocolResponse["result"]>; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getExports", + function (): ReadonlyMap<__String, Symbol> { + return owner.exportsCache ??= owner.fetchSymbolTable("getExportsOfSymbol"); + }, + function* (): Generator, ProtocolResponse["result"]> { + return owner.exportsCache ??= yield* owner.fetchSymbolTable.gen("getExportsOfSymbol"); + }, + ); + } + + private get fetchSymbolTable(): { + (method: SymbolsPropertyMethod): ReadonlyMap<__String, Symbol>; + gen(method: SymbolsPropertyMethod): Generator, ProtocolResponse["result"]>; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "fetchSymbolTable", + function (method: SymbolsPropertyMethod): ReadonlyMap<__String, Symbol> { + const symbols = owner.objectRegistry.fetchSymbols(owner, method, undefined, owner.canonicalProject.id); + const table = new Map<__String, Symbol>(); + for (const symbol of symbols) { + table.set(symbol.escapedName, symbol); + } + return table; + }, + function* (method: SymbolsPropertyMethod): Generator, ProtocolResponse["result"]> { + const symbols = yield* owner.objectRegistry.fetchSymbols.gen(owner, method, undefined, owner.canonicalProject.id); + const table = new Map<__String, Symbol>(); + for (const symbol of symbols) { + table.set(symbol.escapedName, symbol); + } + return table; + }, + ); + } + + get getExportSymbol(): { + (): Symbol; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getExportSymbol", + function (): Symbol { + if (!owner.exportSymbol) return owner; + return owner.objectRegistry.fetchSymbol(owner, "getExportSymbolOfSymbol", owner.exportSymbol, owner.canonicalProject.id); + }, + function* (): Generator { + if (!owner.exportSymbol) return owner; + return yield* owner.objectRegistry.fetchSymbol.gen(owner, "getExportSymbolOfSymbol", owner.exportSymbol, owner.canonicalProject.id); + }, + ); + } + + get getJsDocTags(): { + (checker: Checker): readonly JSDocTagInfo[]; + gen(checker: Checker): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getJsDocTags", + function (checker: Checker): readonly JSDocTagInfo[] { + return checker.getJsDocTagsOfSymbol(owner); + }, + function* (checker: Checker): Generator { + return yield* checker.getJsDocTagsOfSymbol.gen(owner); + }, + ); + } + + get getDocumentationComment(): { + (checker: Checker): string; + gen(checker: Checker): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getDocumentationComment", + function (checker: Checker): string { + return checker.getDocumentationCommentOfSymbol(owner); + }, + function* (checker: Checker): Generator { + return yield* checker.getDocumentationCommentOfSymbol.gen(owner); + }, + ); + } +} + +class TypeObject implements Type { + private objectRegistry: ProjectObjectRegistry; + + // Fields included in TypeResponse. References to other objects are stored as IDs + // and resolved lazily via the object registry. + readonly id: number; + readonly flags: TypeFlags; + readonly objectFlags!: ObjectFlags; + readonly symbol!: number; + readonly value!: string | number | boolean | bigint; + readonly intrinsicName!: string; + readonly isThisType!: boolean; + readonly freshType!: number; + readonly regularType!: number; + readonly target!: number; + readonly typeParameters!: readonly number[]; + readonly outerTypeParameters!: readonly number[]; + readonly localTypeParameters!: readonly number[]; + readonly aliasTypeArguments!: readonly number[]; + readonly aliasSymbol!: number; + readonly elementFlags!: readonly ElementFlags[]; + readonly fixedLength!: number; + readonly readonly!: boolean; + readonly texts!: readonly string[]; + readonly objectType!: number; + readonly indexType!: number; + readonly checkType!: number; + readonly extendsType!: number; + readonly baseType!: number; + readonly substConstraint!: number; + + // Cached results of lazy fetches, not included in TypeResponse + // (typically because they require some amount of computation or + // could cause an arbitrarily large number of types to be cached + // on the server for ID-based lookup). `false` is a sentinel value + // indicating a fetch has not yet occurred. + private trueType: number | false; + private falseType: number | false; + private constraint: number | false; + private default: number | false; + private nonNullableType: number | false; + private apparentType: number | false; + private reducedType: number | false; + private properties: readonly Symbol[] | false; + private apparentProperties: readonly Symbol[] | false; + private callSignatures: readonly Signature[] | false; + private constructSignatures: readonly Signature[] | false; + private indexInfos: readonly IndexInfo[] | false; + private baseTypes: readonly Type[] | false; + private stringIndexType: Type | undefined | false; + private numberIndexType: Type | undefined | false; + + constructor(data: TypeResponse, objectRegistry: ProjectObjectRegistry) { + this.objectRegistry = objectRegistry; + + this.id = data.id; + this.flags = data.flags; + if (data.objectFlags !== undefined) this.objectFlags = data.objectFlags; + if (data.symbol !== undefined) this.symbol = data.symbol; + if (data.value != null) { + // BigInt literal values are serialized as decimal strings (e.g. "-123") because + // JSON cannot represent bigint. Decode them back into a real bigint here. + const value = data.value as string | number | boolean; + this.value = (data.flags & TypeFlags.BigIntLiteral) ? BigInt(value as string) : value; + } + if (data.intrinsicName !== undefined) this.intrinsicName = data.intrinsicName; + if (data.isThisType !== undefined) this.isThisType = data.isThisType; + if (data.freshType !== undefined) this.freshType = data.freshType; + if (data.regularType !== undefined) this.regularType = data.regularType; + if (data.target !== undefined) this.target = data.target; + this.typeParameters = data.typeParameters ?? []; + this.outerTypeParameters = data.outerTypeParameters ?? []; + this.localTypeParameters = data.localTypeParameters ?? []; + this.aliasTypeArguments = data.aliasTypeArguments ?? []; + if (data.aliasSymbol !== undefined) this.aliasSymbol = data.aliasSymbol; + if (data.elementFlags !== undefined) this.elementFlags = data.elementFlags; + if (data.fixedLength !== undefined) this.fixedLength = data.fixedLength; + if (data.readonly !== undefined) this.readonly = data.readonly; + if (data.texts !== undefined) this.texts = data.texts; + if (data.objectType !== undefined) this.objectType = data.objectType; + if (data.indexType !== undefined) this.indexType = data.indexType; + if (data.checkType !== undefined) this.checkType = data.checkType; + if (data.extendsType !== undefined) this.extendsType = data.extendsType; + if (data.baseType !== undefined) this.baseType = data.baseType; + if (data.substConstraint !== undefined) this.substConstraint = data.substConstraint; + + this.trueType = false; + this.falseType = false; + this.constraint = false; + this.default = false; + this.nonNullableType = false; + this.apparentType = false; + this.reducedType = false; + this.properties = false; + this.apparentProperties = false; + this.callSignatures = false; + this.constructSignatures = false; + this.indexInfos = false; + this.baseTypes = false; + this.stringIndexType = false; + this.numberIndexType = false; + } + + get getSymbol(): { + (): Symbol | undefined; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getSymbol", + function (): Symbol | undefined { + return owner.objectRegistry.fetchSymbol(owner, "getSymbolOfType", owner.symbol); + }, + function* (): Generator { + return yield* owner.objectRegistry.fetchSymbol.gen(owner, "getSymbolOfType", owner.symbol); + }, + ); + } + + get getProperties(): { + (): readonly Symbol[]; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getProperties", + function (): readonly Symbol[] { + if (owner.properties === false) { + owner.properties = owner.objectRegistry.fetchPropertiesOfType(owner); + } + return owner.properties; + }, + function* (): Generator { + if (owner.properties === false) { + owner.properties = yield* owner.objectRegistry.fetchPropertiesOfType.gen(owner); + } + return owner.properties; + }, + ); + } + + get getProperty(): { + (propertyName: string): Symbol | undefined; + gen(propertyName: string): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getProperty", + function (propertyName: string): Symbol | undefined { + return owner.objectRegistry.fetchPropertyOfType(owner, propertyName); + }, + function* (propertyName: string): Generator { + return yield* owner.objectRegistry.fetchPropertyOfType.gen(owner, propertyName); + }, + ); + } + + get getApparentProperties(): { + (): readonly Symbol[]; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getApparentProperties", + function (): readonly Symbol[] { + if (owner.apparentProperties === false) { + owner.apparentProperties = owner.objectRegistry.fetchApparentPropertiesOfType(owner); + } + return owner.apparentProperties; + }, + function* (): Generator { + if (owner.apparentProperties === false) { + owner.apparentProperties = yield* owner.objectRegistry.fetchApparentPropertiesOfType.gen(owner); + } + return owner.apparentProperties; + }, + ); + } + + get getCallSignatures(): { + (): readonly Signature[]; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getCallSignatures", + function (): readonly Signature[] { + if (owner.callSignatures === false) { + owner.callSignatures = owner.objectRegistry.fetchSignaturesOfType(owner, SignatureKind.Call); + } + return owner.callSignatures; + }, + function* (): Generator { + if (owner.callSignatures === false) { + owner.callSignatures = yield* owner.objectRegistry.fetchSignaturesOfType.gen(owner, SignatureKind.Call); + } + return owner.callSignatures; + }, + ); + } + + get getConstructSignatures(): { + (): readonly Signature[]; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getConstructSignatures", + function (): readonly Signature[] { + if (owner.constructSignatures === false) { + owner.constructSignatures = owner.objectRegistry.fetchSignaturesOfType(owner, SignatureKind.Construct); + } + return owner.constructSignatures; + }, + function* (): Generator { + if (owner.constructSignatures === false) { + owner.constructSignatures = yield* owner.objectRegistry.fetchSignaturesOfType.gen(owner, SignatureKind.Construct); + } + return owner.constructSignatures; + }, + ); + } + + get getNonNullableType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getNonNullableType", + function (): Type { + const result = owner.objectRegistry.fetchType(owner, "getNonNullableType", owner.nonNullableType); + owner.nonNullableType = result.id; + return result; + }, + function* (): Generator { + const result = yield* owner.objectRegistry.fetchType.gen(owner, "getNonNullableType", owner.nonNullableType); + owner.nonNullableType = result.id; + return result; + }, + ); + } + + get getStringIndexType(): { + (): Type | undefined; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getStringIndexType", + function (): Type | undefined { + if (owner.stringIndexType === false) { + owner.stringIndexType = owner.getStringIndexTypeWorker(); + } + return owner.stringIndexType; + }, + function* (): Generator { + if (owner.stringIndexType === false) { + owner.stringIndexType = yield* owner.getStringIndexTypeWorker.gen(); + } + return owner.stringIndexType; + }, + ); + } + + private get getStringIndexTypeWorker(): { + (): Type | undefined; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getStringIndexTypeWorker", + function (): Type | undefined { + const infos = owner.getIndexInfos(); + return infos.find(info => (info.keyType.flags & TypeFlags.String) !== 0)?.valueType; + }, + function* (): Generator { + const infos = yield* owner.getIndexInfos.gen(); + return infos.find(info => (info.keyType.flags & TypeFlags.String) !== 0)?.valueType; + }, + ); + } + + get getNumberIndexType(): { + (): Type | undefined; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getNumberIndexType", + function (): Type | undefined { + if (owner.numberIndexType === false) { + owner.numberIndexType = owner.getNumberIndexTypeWorker(); + } + return owner.numberIndexType; + }, + function* (): Generator { + if (owner.numberIndexType === false) { + owner.numberIndexType = yield* owner.getNumberIndexTypeWorker.gen(); + } + return owner.numberIndexType; + }, + ); + } + + private get getNumberIndexTypeWorker(): { + (): Type | undefined; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getNumberIndexTypeWorker", + function (): Type | undefined { + const infos = owner.getIndexInfos(); + return infos.find(info => (info.keyType.flags & TypeFlags.Number) !== 0)?.valueType; + }, + function* (): Generator { + const infos = yield* owner.getIndexInfos.gen(); + return infos.find(info => (info.keyType.flags & TypeFlags.Number) !== 0)?.valueType; + }, + ); + } + + get getApparentType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getApparentType", + function (): Type { + const result = owner.objectRegistry.fetchType(owner, "getApparentType", owner.apparentType); + owner.apparentType = result.id; + return result; + }, + function* (): Generator { + const result = yield* owner.objectRegistry.fetchType.gen(owner, "getApparentType", owner.apparentType); + owner.apparentType = result.id; + return result; + }, + ); + } + + get getReducedType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getReducedType", + function (): Type { + const result = owner.objectRegistry.fetchType(owner, "getReducedType", owner.reducedType); + owner.reducedType = result.id; + return result; + }, + function* (): Generator { + const result = yield* owner.objectRegistry.fetchType.gen(owner, "getReducedType", owner.reducedType); + owner.reducedType = result.id; + return result; + }, + ); + } + + get getIndexInfos(): { + (): readonly IndexInfo[]; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getIndexInfos", + function (): readonly IndexInfo[] { + if (owner.indexInfos === false) { + owner.indexInfos = owner.objectRegistry.fetchIndexInfosOfType(owner); + } + return owner.indexInfos; + }, + function* (): Generator { + if (owner.indexInfos === false) { + owner.indexInfos = yield* owner.objectRegistry.fetchIndexInfosOfType.gen(owner); + } + return owner.indexInfos; + }, + ); + } + + get getAliasSymbol(): { + (): Symbol | undefined; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getAliasSymbol", + function (): Symbol | undefined { + return owner.objectRegistry.fetchSymbol(owner, "getAliasSymbolOfType", owner.aliasSymbol); + }, + function* (): Generator { + return yield* owner.objectRegistry.fetchSymbol.gen(owner, "getAliasSymbolOfType", owner.aliasSymbol); + }, + ); + } + + get getTarget(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getTarget", + function (): Type { + return owner.objectRegistry.fetchType(owner, "getTargetOfType", owner.target); + }, + function* (): Generator { + return yield* owner.objectRegistry.fetchType.gen(owner, "getTargetOfType", owner.target); + }, + ); + } + + get getFreshType(): { + (): FreshableType | undefined; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getFreshType", + function (): FreshableType | undefined { + return owner.objectRegistry.fetchOptionalType(owner, "getFreshTypeOfType", owner.freshType); + }, + function* (): Generator { + return yield* owner.objectRegistry.fetchOptionalType.gen(owner, "getFreshTypeOfType", owner.freshType); + }, + ); + } + + get getRegularType(): { + (): FreshableType | undefined; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getRegularType", + function (): FreshableType | undefined { + return owner.objectRegistry.fetchOptionalType(owner, "getRegularTypeOfType", owner.regularType); + }, + function* (): Generator { + return yield* owner.objectRegistry.fetchOptionalType.gen(owner, "getRegularTypeOfType", owner.regularType); + }, + ); + } + + get getTypes(): { + (): readonly Type[] | undefined; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getTypes", + function (): readonly Type[] | undefined { + // Only union, intersection, and template literal types have constituent + // types; any other kind has none, so return undefined rather than sending + // a request the server cannot satisfy. + if (!(owner.flags & (TypeFlags.UnionOrIntersection | TypeFlags.TemplateLiteral))) { + return undefined; + } + return owner.objectRegistry.fetchTypes(owner, "getTypesOfType"); + }, + function* (): Generator { + // Only union, intersection, and template literal types have constituent + // types; any other kind has none, so return undefined rather than sending + // a request the server cannot satisfy. + if (!(owner.flags & (TypeFlags.UnionOrIntersection | TypeFlags.TemplateLiteral))) { + return undefined; + } + return yield* owner.objectRegistry.fetchTypes.gen(owner, "getTypesOfType"); + }, + ); + } + + get getTypeParameters(): { + (): readonly TypeParameter[]; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getTypeParameters", + function (): readonly TypeParameter[] { + return owner.objectRegistry.fetchTypes(owner, "getTypeParametersOfType", owner.typeParameters) as readonly TypeParameter[]; + }, + function* (): Generator { + return (yield* owner.objectRegistry.fetchTypes.gen(owner, "getTypeParametersOfType", owner.typeParameters)) as readonly TypeParameter[]; + }, + ); + } + + get getOuterTypeParameters(): { + (): readonly TypeParameter[]; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getOuterTypeParameters", + function (): readonly TypeParameter[] { + return owner.objectRegistry.fetchTypes(owner, "getOuterTypeParametersOfType", owner.outerTypeParameters) as readonly TypeParameter[]; + }, + function* (): Generator { + return (yield* owner.objectRegistry.fetchTypes.gen(owner, "getOuterTypeParametersOfType", owner.outerTypeParameters)) as readonly TypeParameter[]; + }, + ); + } + + get getLocalTypeParameters(): { + (): readonly TypeParameter[]; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getLocalTypeParameters", + function (): readonly TypeParameter[] { + return owner.objectRegistry.fetchTypes(owner, "getLocalTypeParametersOfType", owner.localTypeParameters) as readonly TypeParameter[]; + }, + function* (): Generator { + return (yield* owner.objectRegistry.fetchTypes.gen(owner, "getLocalTypeParametersOfType", owner.localTypeParameters)) as readonly TypeParameter[]; + }, + ); + } + + get getAliasTypeArguments(): { + (): readonly Type[]; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getAliasTypeArguments", + function (): readonly Type[] { + return owner.objectRegistry.fetchTypes(owner, "getAliasTypeArgumentsOfType", owner.aliasTypeArguments); + }, + function* (): Generator { + return yield* owner.objectRegistry.fetchTypes.gen(owner, "getAliasTypeArgumentsOfType", owner.aliasTypeArguments); + }, + ); + } + + get getObjectType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getObjectType", + function (): Type { + return owner.objectRegistry.fetchType(owner, "getObjectTypeOfType", owner.objectType); + }, + function* (): Generator { + return yield* owner.objectRegistry.fetchType.gen(owner, "getObjectTypeOfType", owner.objectType); + }, + ); + } + + get getIndexType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getIndexType", + function (): Type { + return owner.objectRegistry.fetchType(owner, "getIndexTypeOfType", owner.indexType); + }, + function* (): Generator { + return yield* owner.objectRegistry.fetchType.gen(owner, "getIndexTypeOfType", owner.indexType); + }, + ); + } + + get getCheckType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getCheckType", + function (): Type { + return owner.objectRegistry.fetchType(owner, "getCheckTypeOfType", owner.checkType); + }, + function* (): Generator { + return yield* owner.objectRegistry.fetchType.gen(owner, "getCheckTypeOfType", owner.checkType); + }, + ); + } + + get getExtendsType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getExtendsType", + function (): Type { + return owner.objectRegistry.fetchType(owner, "getExtendsTypeOfType", owner.extendsType); + }, + function* (): Generator { + return yield* owner.objectRegistry.fetchType.gen(owner, "getExtendsTypeOfType", owner.extendsType); + }, + ); + } + + get getBaseType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getBaseType", + function (): Type { + return owner.objectRegistry.fetchType(owner, "getBaseTypeOfType", owner.baseType); + }, + function* (): Generator { + return yield* owner.objectRegistry.fetchType.gen(owner, "getBaseTypeOfType", owner.baseType); + }, + ); + } + + get getConstraint(): { + (): Type | undefined; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getConstraint", + function (): Type | undefined { + // Type parameters resolve their constraint lazily through the checker, + // whereas substitution types carry a preloaded constraint handle. + if (owner.flags & TypeFlags.TypeParameter) { + const result = owner.objectRegistry.fetchOptionalType(owner, "getConstraintOfTypeParameter", owner.constraint); + owner.constraint = result ? result.id : 0; + return result; + } + return owner.objectRegistry.fetchType(owner, "getConstraintOfType", owner.substConstraint); + }, + function* (): Generator { + // Type parameters resolve their constraint lazily through the checker, + // whereas substitution types carry a preloaded constraint handle. + if (owner.flags & TypeFlags.TypeParameter) { + const result = yield* owner.objectRegistry.fetchOptionalType.gen(owner, "getConstraintOfTypeParameter", owner.constraint); + owner.constraint = result ? result.id : 0; + return result; + } + return yield* owner.objectRegistry.fetchType.gen(owner, "getConstraintOfType", owner.substConstraint); + }, + ); + } + + get getDefault(): { + (): Type | undefined; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getDefault", + function (): Type | undefined { + const result = owner.objectRegistry.fetchOptionalType(owner, "getDefaultFromTypeParameter", owner.default); + owner.default = result ? result.id : 0; + return result; + }, + function* (): Generator { + const result = yield* owner.objectRegistry.fetchOptionalType.gen(owner, "getDefaultFromTypeParameter", owner.default); + owner.default = result ? result.id : 0; + return result; + }, + ); + } + + get getTrueType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getTrueType", + function (): Type { + const result = owner.objectRegistry.fetchType(owner, "getTrueTypeOfConditionalType", owner.trueType); + owner.trueType = result.id; + return result; + }, + function* (): Generator { + const result = yield* owner.objectRegistry.fetchType.gen(owner, "getTrueTypeOfConditionalType", owner.trueType); + owner.trueType = result.id; + return result; + }, + ); + } + + get getFalseType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getFalseType", + function (): Type { + const result = owner.objectRegistry.fetchType(owner, "getFalseTypeOfConditionalType", owner.falseType); + owner.falseType = result.id; + return result; + }, + function* (): Generator { + const result = yield* owner.objectRegistry.fetchType.gen(owner, "getFalseTypeOfConditionalType", owner.falseType); + owner.falseType = result.id; + return result; + }, + ); + } + + /** + * Get the base types of this type. Returns `undefined` for any type that is + * not a class or interface. + */ + get getBaseTypes(): { + (): readonly Type[] | undefined; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getBaseTypes", + function (): readonly Type[] | undefined { + if (!owner.isClassOrInterface()) { + return undefined; + } + if (owner.baseTypes === false) { + owner.baseTypes = owner.objectRegistry.fetchBaseTypes(owner); + } + return owner.baseTypes; + }, + function* (): Generator { + if (!owner.isClassOrInterface()) { + return undefined; + } + if (owner.baseTypes === false) { + owner.baseTypes = yield* owner.objectRegistry.fetchBaseTypes.gen(owner); + } + return owner.baseTypes; + }, + ); + } + + isClassOrInterface(): this is InterfaceType { + return isClassOrInterfaceType(this); + } + + isUnionType(): this is UnionType { + return isUnionType(this); + } + + isIntersectionType(): this is IntersectionType { + return isIntersectionType(this); + } + + isObjectType(): this is ObjectType { + return isObjectType(this); + } + + isIntrinsicType(): this is IntrinsicType { + return isIntrinsicType(this); + } + + isErrorType(): boolean { + return isErrorType(this); + } + + isLiteralType(): this is LiteralType { + return isLiteralType(this); + } + + isStringLiteralType(): this is StringLiteralType { + return isStringLiteralType(this); + } + + isNumberLiteralType(): this is NumberLiteralType { + return isNumberLiteralType(this); + } + + isBigIntLiteralType(): this is BigIntLiteralType { + return isBigIntLiteralType(this); + } + + isBooleanLiteralType(): this is BooleanLiteralType { + return isBooleanLiteralType(this); + } + + isTypeReference(): this is TypeReference { + return isTypeReference(this); + } + + isTupleType(): this is TupleType { + return isTupleType(this); + } + + isIndexType(): this is IndexType { + return isIndexType(this); + } + + isIndexedAccessType(): this is IndexedAccessType { + return isIndexedAccessType(this); + } + + isConditionalType(): this is ConditionalType { + return isConditionalType(this); + } + + isSubstitutionType(): this is SubstitutionType { + return isSubstitutionType(this); + } + + isTemplateLiteralType(): this is TemplateLiteralType { + return isTemplateLiteralType(this); + } + + isStringMappingType(): this is StringMappingType { + return isStringMappingType(this); + } + + isTypeParameter(): this is TypeParameter { + return isTypeParameter(this); + } +} + +export function isUnionType(type: Type): type is UnionType { + return (type.flags & TypeFlags.Union) !== 0; +} + +export function isIntersectionType(type: Type): type is IntersectionType { + return (type.flags & TypeFlags.Intersection) !== 0; +} + +export function isObjectType(type: Type): type is ObjectType { + return (type.flags & TypeFlags.Object) !== 0; +} + +export function isClassOrInterfaceType(type: Type): type is InterfaceType { + return isObjectType(type) && (type.objectFlags & ObjectFlags.ClassOrInterface) !== 0; +} + +export function isIntrinsicType(type: Type): type is IntrinsicType { + return (type.flags & TypeFlags.Intrinsic) !== 0; +} + +/** + * Whether this is the error type — the placeholder the checker produces when a + * type cannot be determined (e.g. an unresolved reference). It is an intrinsic + * type named `"error"` (this covers both the singleton error type and the + * per-alias error types manufactured for unresolved type alias references). + */ +export function isErrorType(type: Type): boolean { + return isIntrinsicType(type) && type.intrinsicName === "error"; +} + +export function isLiteralType(type: Type): type is LiteralType { + return (type.flags & TypeFlags.Literal) !== 0; +} + +export function isStringLiteralType(type: Type): type is StringLiteralType { + return (type.flags & TypeFlags.StringLiteral) !== 0; +} + +export function isNumberLiteralType(type: Type): type is NumberLiteralType { + return (type.flags & TypeFlags.NumberLiteral) !== 0; +} + +export function isBigIntLiteralType(type: Type): type is BigIntLiteralType { + return (type.flags & TypeFlags.BigIntLiteral) !== 0; +} + +export function isBooleanLiteralType(type: Type): type is BooleanLiteralType { + return (type.flags & TypeFlags.BooleanLiteral) !== 0; +} + +export function isTypeReference(type: Type): type is TypeReference { + return isObjectType(type) && (type.objectFlags & ObjectFlags.Reference) !== 0; +} + +export function isTupleType(type: Type): type is TupleType { + return isObjectType(type) && (type.objectFlags & ObjectFlags.Tuple) !== 0; +} + +export function isIndexType(type: Type): type is IndexType { + return (type.flags & TypeFlags.Index) !== 0; +} + +export function isIndexedAccessType(type: Type): type is IndexedAccessType { + return (type.flags & TypeFlags.IndexedAccess) !== 0; +} + +export function isConditionalType(type: Type): type is ConditionalType { + return (type.flags & TypeFlags.Conditional) !== 0; +} + +export function isSubstitutionType(type: Type): type is SubstitutionType { + return (type.flags & TypeFlags.Substitution) !== 0; +} + +export function isTemplateLiteralType(type: Type): type is TemplateLiteralType { + return (type.flags & TypeFlags.TemplateLiteral) !== 0; +} + +export function isStringMappingType(type: Type): type is StringMappingType { + return (type.flags & TypeFlags.StringMapping) !== 0; +} + +export function isTypeParameter(type: Type): type is TypeParameter { + return (type.flags & TypeFlags.TypeParameter) !== 0; +} + +export class Signature { + private flags: number; + private objectRegistry: ProjectObjectRegistry; + + readonly id: number; + readonly declaration?: NodeHandle | undefined; + readonly typeParameters?: readonly number[] | undefined; + readonly parameters: readonly number[]; + readonly thisParameter?: number | undefined; + readonly target?: number | undefined; + private returnType: number | false; + + constructor(data: SignatureResponse, project: Project, objectRegistry: ProjectObjectRegistry) { + this.id = data.id; + this.flags = data.flags; + this.objectRegistry = objectRegistry; + this.declaration = data.declaration ? new NodeHandle(data.declaration, project) : undefined; + this.typeParameters = data.typeParameters ?? []; + this.parameters = data.parameters ?? []; + this.thisParameter = data.thisParameter; + this.target = data.target; + this.returnType = false; + } + + get getTypeParameters(): { + (): readonly TypeParameter[]; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getTypeParameters", + function (): readonly TypeParameter[] { + return owner.objectRegistry.fetchTypes(owner, "getTypeParametersOfSignature", owner.typeParameters) as readonly TypeParameter[]; + }, + function* (): Generator { + return (yield* owner.objectRegistry.fetchTypes.gen(owner, "getTypeParametersOfSignature", owner.typeParameters)) as readonly TypeParameter[]; + }, + ); + } + + get getParameters(): { + (): readonly Symbol[]; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getParameters", + function (): readonly Symbol[] { + return owner.objectRegistry.fetchSymbols(owner, "getParametersOfSignature", owner.parameters); + }, + function* (): Generator { + return yield* owner.objectRegistry.fetchSymbols.gen(owner, "getParametersOfSignature", owner.parameters); + }, + ); + } + + get getThisParameter(): { + (): Symbol | undefined; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getThisParameter", + function (): Symbol | undefined { + return owner.objectRegistry.fetchSymbol(owner, "getThisParameterOfSignature", owner.thisParameter); + }, + function* (): Generator { + return yield* owner.objectRegistry.fetchSymbol.gen(owner, "getThisParameterOfSignature", owner.thisParameter); + }, + ); + } + + get getTarget(): { + (): Signature | undefined; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getTarget", + function (): Signature | undefined { + return owner.objectRegistry.fetchSignature(owner, "getTargetOfSignature", owner.target); + }, + function* (): Generator { + return yield* owner.objectRegistry.fetchSignature.gen(owner, "getTargetOfSignature", owner.target); + }, + ); + } + + get getReturnType(): { + (): Type; + gen(): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getReturnType", + function (): Type { + const result = owner.objectRegistry.fetchType(owner, "getReturnTypeOfSignature", owner.returnType); + owner.returnType = result.id; + return result; + }, + function* (): Generator { + const result = yield* owner.objectRegistry.fetchType.gen(owner, "getReturnTypeOfSignature", owner.returnType); + owner.returnType = result.id; + return result; + }, + ); + } + + get getTypeParameterAtPosition(): { + (pos: number): Type; + gen(pos: number): Generator; + } { + const owner = this; + return cacheGeneratorMethod( + owner, + "getTypeParameterAtPosition", + function (pos: number): Type { + return owner.objectRegistry.fetchTypeParameterAtPosition(owner, pos); + }, + function* (pos: number): Generator { + return yield* owner.objectRegistry.fetchTypeParameterAtPosition.gen(owner, pos); + }, + ); + } + + get hasRestParameter(): boolean { + return (this.flags & SignatureFlags.HasRestParameter) !== 0; + } + + get isConstruct(): boolean { + return (this.flags & SignatureFlags.Construct) !== 0; + } + + get isAbstract(): boolean { + return (this.flags & SignatureFlags.Abstract) !== 0; + } +} diff --git a/packages/typescript/src/api/sync/client.ts b/packages/typescript/src/api/sync/client.ts index 027e30f8a57d0..c19d1d856006f 100644 --- a/packages/typescript/src/api/sync/client.ts +++ b/packages/typescript/src/api/sync/client.ts @@ -1,167 +1,167 @@ -import { fsCallbackNames } from "../fs.ts"; -import { - type ClientOptions, - type ClientSocketOptions, - type ClientSpawnOptions, - getAPIProcessArgs, - isSpawnOptions, - resolveExePath, -} from "../options.ts"; -import type { - APIMethodInfo, - APIRequest, - BatchRequestsResponse, - SourceFileResponseMethod, -} from "../proto.ts"; -import { SyncRpcChannel } from "../syncChannel.ts"; -import { - combineTimingInfo, - disabledTimingInfo, - type ServerTimingInfo, - TimingCollector, - type TimingInfo, -} from "../timing.ts"; - -export type { ClientOptions, ClientSocketOptions, ClientSpawnOptions }; - -export class Client { - private channel: SyncRpcChannel; - private encoder = new TextEncoder(); - private timing: TimingCollector | undefined; - - constructor(options: ClientOptions) { - if (!isSpawnOptions(options)) { - throw new Error("Socket connections are not yet supported in the sync client"); - } - - const args = getAPIProcessArgs(options, false); - - // Enable virtual FS callbacks for each provided FS function - const enabledCallbacks: (typeof fsCallbackNames[number])[] = []; - if (options.fs) { - for (const name of fsCallbackNames) { - if (options.fs[name]) { - enabledCallbacks.push(name); - } - } - } - if (enabledCallbacks.length > 0) { - args.push(`--callbacks=${enabledCallbacks.join(",")}`); - } - - const collectTiming = options.collectTiming ?? false; - if (collectTiming) { - this.timing = new TimingCollector(); - } - - const channel = new SyncRpcChannel(resolveExePath(options), args, collectTiming); - this.channel = channel; - - if (options.fs) { - for (const name of enabledCallbacks) { - if (name === "writeFile") { - if (!options.fs.writeFile) continue; - const callback = options.fs.writeFile; - - channel.registerCallback(name, (_, arg) => { - const { path, data } = JSON.parse(arg); - callback(path, data); - return ""; - }); - - continue; - } - - const callback = options.fs[name]!; - channel.registerCallback(name, (_, arg) => { - const result = callback(JSON.parse(arg)); - if (name === "readFile") { - // readFile has 3 returns: string (content), null (not found), undefined (fall back). - // Wrap in object to preserve null vs undefined distinction. - if (result === undefined) return ""; - return JSON.stringify({ content: result }); - } - return JSON.stringify(result) ?? ""; - }); - } - } - } - - apiRequest(method: K, params?: APIMethodInfo[K]["params"]): APIMethodInfo[K]["result"] { - const encodedPayload = JSON.stringify(params); - const start = performance.now(); - const result = this.channel.requestSync(method, encodedPayload); - this.recordTiming(method, start); - if (result.length) { - return JSON.parse(result) as APIMethodInfo[K]["result"]; - } - return undefined as APIMethodInfo[K]["result"]; - } - - batchRequests(requests: readonly APIRequest[]): BatchRequestsResponse { - return this.apiRequest("batchRequests", { requests }); - } - - apiRequestBinary(method: K, params?: APIMethodInfo[K]["params"]): Uint8Array | undefined { - const start = performance.now(); - const result = this.channel.requestBinarySync(method, this.encoder.encode(JSON.stringify(params))); - this.recordTiming(method, start); - if (result.length === 0) return undefined; - return result; - } - - echo(payload: string): string { - return this.channel.requestSync("echo", payload); - } - - echoBinary(payload: Uint8Array): Uint8Array { - return this.channel.requestBinarySync("echo", payload); - } - - /** - * Returns a combined timing snapshot: client-measured round-trip and byte - * counts folded together with the server's own per-request processing time - * (fetched via a getServerTiming request) and estimated transport overhead. - */ - getTimingInfo(): TimingInfo { - if (!this.timing) { - return disabledTimingInfo(); - } - const local = this.timing.getInfo(); - // requestSync bypasses recordTiming, so this query does not pollute the - // client-side collector. - const result = this.channel.requestSync("getServerTiming", ""); - return combineTimingInfo(local, JSON.parse(result) as ServerTimingInfo); - } - - resetTimingInfo(): void { - if (!this.timing) return; - this.timing.reset(); - // Keep the server's collection in sync so combined totals stay meaningful. - this.channel.requestSync("resetServerTiming", ""); - } - - /** - * Returns the timing collector that per-node materialization is reported - * into, or undefined when timing collection is disabled. The returned - * collector is the same one folded into {@link getTimingInfo}, so - * materialization totals surface alongside request timings. - */ - getTimingCollector(): TimingCollector | undefined { - return this.timing; - } - - private recordTiming(method: string, start: number): void { - if (!this.timing) return; - this.timing.record({ - method, - roundTripMs: performance.now() - start, - bytesSent: this.channel.lastBytesSent, - bytesReceived: this.channel.lastBytesReceived, - }); - } - - close(): void { - this.channel.close(); - } -} +import { fsCallbackNames } from "../fs.ts"; +import { + type ClientOptions, + type ClientSocketOptions, + type ClientSpawnOptions, + getAPIProcessArgs, + isSpawnOptions, + resolveExePath, +} from "../options.ts"; +import type { + APIMethodInfo, + APIRequest, + BatchRequestsResponse, + SourceFileResponseMethod, +} from "../proto.ts"; +import { SyncRpcChannel } from "../syncChannel.ts"; +import { + combineTimingInfo, + disabledTimingInfo, + type ServerTimingInfo, + TimingCollector, + type TimingInfo, +} from "../timing.ts"; + +export type { ClientOptions, ClientSocketOptions, ClientSpawnOptions }; + +export class Client { + private channel: SyncRpcChannel; + private encoder = new TextEncoder(); + private timing: TimingCollector | undefined; + + constructor(options: ClientOptions) { + if (!isSpawnOptions(options)) { + throw new Error("Socket connections are not yet supported in the sync client"); + } + + const args = getAPIProcessArgs(options, false); + + // Enable virtual FS callbacks for each provided FS function + const enabledCallbacks: (typeof fsCallbackNames[number])[] = []; + if (options.fs) { + for (const name of fsCallbackNames) { + if (options.fs[name]) { + enabledCallbacks.push(name); + } + } + } + if (enabledCallbacks.length > 0) { + args.push(`--callbacks=${enabledCallbacks.join(",")}`); + } + + const collectTiming = options.collectTiming ?? false; + if (collectTiming) { + this.timing = new TimingCollector(); + } + + const channel = new SyncRpcChannel(resolveExePath(options), args, collectTiming); + this.channel = channel; + + if (options.fs) { + for (const name of enabledCallbacks) { + if (name === "writeFile") { + if (!options.fs.writeFile) continue; + const callback = options.fs.writeFile; + + channel.registerCallback(name, (_, arg) => { + const { path, data } = JSON.parse(arg); + callback(path, data); + return ""; + }); + + continue; + } + + const callback = options.fs[name]!; + channel.registerCallback(name, (_, arg) => { + const result = callback(JSON.parse(arg)); + if (name === "readFile") { + // readFile has 3 returns: string (content), null (not found), undefined (fall back). + // Wrap in object to preserve null vs undefined distinction. + if (result === undefined) return ""; + return JSON.stringify({ content: result }); + } + return JSON.stringify(result) ?? ""; + }); + } + } + } + + apiRequest(method: K, params?: APIMethodInfo[K]["params"]): APIMethodInfo[K]["result"] { + const encodedPayload = JSON.stringify(params); + const start = performance.now(); + const result = this.channel.requestSync(method, encodedPayload); + this.recordTiming(method, start); + if (result.length) { + return JSON.parse(result) as APIMethodInfo[K]["result"]; + } + return undefined as APIMethodInfo[K]["result"]; + } + + batchRequests(requests: readonly APIRequest[]): BatchRequestsResponse { + return this.apiRequest("batchRequests", { requests }); + } + + apiRequestBinary(method: K, params?: APIMethodInfo[K]["params"]): Uint8Array | undefined { + const start = performance.now(); + const result = this.channel.requestBinarySync(method, this.encoder.encode(JSON.stringify(params))); + this.recordTiming(method, start); + if (result.length === 0) return undefined; + return result; + } + + echo(payload: string): string { + return this.channel.requestSync("echo", payload); + } + + echoBinary(payload: Uint8Array): Uint8Array { + return this.channel.requestBinarySync("echo", payload); + } + + /** + * Returns a combined timing snapshot: client-measured round-trip and byte + * counts folded together with the server's own per-request processing time + * (fetched via a getServerTiming request) and estimated transport overhead. + */ + getTimingInfo(): TimingInfo { + if (!this.timing) { + return disabledTimingInfo(); + } + const local = this.timing.getInfo(); + // requestSync bypasses recordTiming, so this query does not pollute the + // client-side collector. + const result = this.channel.requestSync("getServerTiming", ""); + return combineTimingInfo(local, JSON.parse(result) as ServerTimingInfo); + } + + resetTimingInfo(): void { + if (!this.timing) return; + this.timing.reset(); + // Keep the server's collection in sync so combined totals stay meaningful. + this.channel.requestSync("resetServerTiming", ""); + } + + /** + * Returns the timing collector that per-node materialization is reported + * into, or undefined when timing collection is disabled. The returned + * collector is the same one folded into {@link getTimingInfo}, so + * materialization totals surface alongside request timings. + */ + getTimingCollector(): TimingCollector | undefined { + return this.timing; + } + + private recordTiming(method: string, start: number): void { + if (!this.timing) return; + this.timing.record({ + method, + roundTripMs: performance.now() - start, + bytesSent: this.channel.lastBytesSent, + bytesReceived: this.channel.lastBytesReceived, + }); + } + + close(): void { + this.channel.close(); + } +} diff --git a/packages/typescript/src/api/sync/generatorSupport.ts b/packages/typescript/src/api/sync/generatorSupport.ts index 97f89abcaafa7..7ce824f239109 100644 --- a/packages/typescript/src/api/sync/generatorSupport.ts +++ b/packages/typescript/src/api/sync/generatorSupport.ts @@ -1,114 +1,114 @@ -import type { - APIMethodInfo, - APIRequest, - APIResponse, -} from "../proto.ts"; - -export function cacheGeneratorMethod any, Gen extends (...args: any[]) => APIRequestGenerator>( - owner: object, - name: PropertyKey, - sync: Sync, - gen: Gen, -): Sync & { readonly gen: Gen; } { - const method = Object.assign(sync, { gen }); - Object.defineProperty(owner, name, { configurable: true, value: method }); - return method; -} - -export function apiRequest( - method: Method, - params: APIMethodInfo[Method]["params"], -): Generator; -export function* apiRequest(method: PropertyKey, params: unknown): Generator<{ method: PropertyKey; params: unknown; }, unknown, unknown> { - return yield { method, params }; -} -export type APIRequestGenerator = Generator; -type GeneratorReturn = T extends Generator ? R : never; -export type ExecutedGeneratorsResults = { - [K in keyof T]: GeneratorReturn; -}; - -interface GeneratorResponse { - result: unknown; - error?: string | undefined; -} - -export function all( - ...requestGenerators: T -): Generator, readonly GeneratorResponse[]>; -export function* all( - ...requestGenerators: T -): Generator, readonly GeneratorResponse[]> { - const results: any[] = []; - const requestObjects: (APIRequest | readonly APIRequest[] | undefined)[] = []; - const completedIndices = new Set(); - for (let i = 0; i < requestGenerators.length; i++) { - const state = requestGenerators[i].next(); - if (state.done) { - results[i] = state.value; - completedIndices.add(i); - } - else { - requestObjects[i] = state.value; - } - } - while (completedIndices.size < requestGenerators.length) { - const requests: APIRequest[] = []; - const responseIndices: (number | readonly number[] | undefined)[] = []; - const responseIndexByDeduplicationKey = new Map(); - const addRequest = (request: APIRequest): number => { - const deduplicationKey = getRequestDeduplicationKey(request); - let responseIndex = deduplicationKey === undefined ? undefined : responseIndexByDeduplicationKey.get(deduplicationKey); - if (responseIndex === undefined) { - responseIndex = requests.length; - requests.push(request); - if (deduplicationKey !== undefined) responseIndexByDeduplicationKey.set(deduplicationKey, responseIndex); - } - return responseIndex; - }; - for (let i = 0; i < requestGenerators.length; i++) { - if (completedIndices.has(i)) continue; - - const request = requestObjects[i]!; - responseIndices[i] = isRequestGroup(request) ? request.map(addRequest) : addRequest(request); - } - - const responses = yield requests; - for (let i = 0; i < requestGenerators.length; i++) { - if (completedIndices.has(i)) continue; - - const requestGenerator = requestGenerators[i]; - const responseIndex = responseIndices[i]!; - let state: IteratorResult; - if (typeof responseIndex === "number") { - const result = responses[responseIndex]; - state = result.error - ? requestGenerator.throw(new Error(result.error)) - : requestGenerator.next(result.result); - } - else { - state = requestGenerator.next(responseIndex.map(index => responses[index])); - } - if (state.done) { - results[i] = state.value; - completedIndices.add(i); - requestObjects[i] = undefined; - } - else requestObjects[i] = state.value; - } - } - return results as any[] as ExecutedGeneratorsResults; -} - -function isRequestGroup(request: APIRequest | readonly APIRequest[]): request is readonly APIRequest[] { - return Array.isArray(request); -} - -function getRequestDeduplicationKey(request: APIRequest): string | undefined { - switch (request.method) { - case "initialize": - return request.method; - default: - return undefined; - } -} +import type { + APIMethodInfo, + APIRequest, + APIResponse, +} from "../proto.ts"; + +export function cacheGeneratorMethod any, Gen extends (...args: any[]) => APIRequestGenerator>( + owner: object, + name: PropertyKey, + sync: Sync, + gen: Gen, +): Sync & { readonly gen: Gen; } { + const method = Object.assign(sync, { gen }); + Object.defineProperty(owner, name, { configurable: true, value: method }); + return method; +} + +export function apiRequest( + method: Method, + params: APIMethodInfo[Method]["params"], +): Generator; +export function* apiRequest(method: PropertyKey, params: unknown): Generator<{ method: PropertyKey; params: unknown; }, unknown, unknown> { + return yield { method, params }; +} +export type APIRequestGenerator = Generator; +type GeneratorReturn = T extends Generator ? R : never; +export type ExecutedGeneratorsResults = { + [K in keyof T]: GeneratorReturn; +}; + +interface GeneratorResponse { + result: unknown; + error?: string | undefined; +} + +export function all( + ...requestGenerators: T +): Generator, readonly GeneratorResponse[]>; +export function* all( + ...requestGenerators: T +): Generator, readonly GeneratorResponse[]> { + const results: any[] = []; + const requestObjects: (APIRequest | readonly APIRequest[] | undefined)[] = []; + const completedIndices = new Set(); + for (let i = 0; i < requestGenerators.length; i++) { + const state = requestGenerators[i].next(); + if (state.done) { + results[i] = state.value; + completedIndices.add(i); + } + else { + requestObjects[i] = state.value; + } + } + while (completedIndices.size < requestGenerators.length) { + const requests: APIRequest[] = []; + const responseIndices: (number | readonly number[] | undefined)[] = []; + const responseIndexByDeduplicationKey = new Map(); + const addRequest = (request: APIRequest): number => { + const deduplicationKey = getRequestDeduplicationKey(request); + let responseIndex = deduplicationKey === undefined ? undefined : responseIndexByDeduplicationKey.get(deduplicationKey); + if (responseIndex === undefined) { + responseIndex = requests.length; + requests.push(request); + if (deduplicationKey !== undefined) responseIndexByDeduplicationKey.set(deduplicationKey, responseIndex); + } + return responseIndex; + }; + for (let i = 0; i < requestGenerators.length; i++) { + if (completedIndices.has(i)) continue; + + const request = requestObjects[i]!; + responseIndices[i] = isRequestGroup(request) ? request.map(addRequest) : addRequest(request); + } + + const responses = yield requests; + for (let i = 0; i < requestGenerators.length; i++) { + if (completedIndices.has(i)) continue; + + const requestGenerator = requestGenerators[i]; + const responseIndex = responseIndices[i]!; + let state: IteratorResult; + if (typeof responseIndex === "number") { + const result = responses[responseIndex]; + state = result.error + ? requestGenerator.throw(new Error(result.error)) + : requestGenerator.next(result.result); + } + else { + state = requestGenerator.next(responseIndex.map(index => responses[index])); + } + if (state.done) { + results[i] = state.value; + completedIndices.add(i); + requestObjects[i] = undefined; + } + else requestObjects[i] = state.value; + } + } + return results as any[] as ExecutedGeneratorsResults; +} + +function isRequestGroup(request: APIRequest | readonly APIRequest[]): request is readonly APIRequest[] { + return Array.isArray(request); +} + +function getRequestDeduplicationKey(request: APIRequest): string | undefined { + switch (request.method) { + case "initialize": + return request.method; + default: + return undefined; + } +} diff --git a/packages/typescript/src/api/sync/types.ts b/packages/typescript/src/api/sync/types.ts index 43a650ef67621..f573dc808ec1e 100644 --- a/packages/typescript/src/api/sync/types.ts +++ b/packages/typescript/src/api/sync/types.ts @@ -1,524 +1,524 @@ -// -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// !!! THIS FILE IS AUTO-GENERATED - DO NOT EDIT !!! -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// -// Source: src/api/async/types.ts -// Regenerate: npm run generate (from packages/typescript) -// -import type { - APIRequest as ProtocolRequest, - APIResponse as ProtocolResponse, -} from "../proto.ts"; - -import type { CompletionItemKind } from "#enums/completionItemKind"; -import type { ElementFlags } from "#enums/elementFlags"; -import type { ObjectFlags } from "#enums/objectFlags"; -import type { TypeFlags } from "#enums/typeFlags"; -import type { TypePredicateKind } from "#enums/typePredicateKind"; -import type { IndexSignatureDeclaration } from "../../ast/ast.ts"; -import type { Diagnostic } from "../proto.ts"; -import type { - NodeHandle, - Signature, - Symbol, -} from "./api.ts"; - -export type { Diagnostic } from "../proto.ts"; - -/** - * A TypeScript type. - * - * Use TypeFlags to determine the specific kind of type and access - * kind-specific properties. For example: - * - * ```ts - * if (type.flags & TypeFlags.StringLiteral) { - * console.log((type as StringLiteralType).value); // string - * } - * ``` - */ -export interface Type { - /** Type flags — use to determine the specific kind of type. */ - readonly flags: TypeFlags; - - /** Unique identifier for this type */ - readonly id: number; - - /** Get the symbol associated with this type, if any */ - getSymbol: { - (): Symbol | undefined; - gen(): Generator; - }; - - /** Get the properties of this type. */ - getProperties: { - (): readonly Symbol[]; - gen(): Generator; - }; - - /** Get a named property of this type, if present. */ - getProperty: { - (propertyName: string): Symbol | undefined; - gen(propertyName: string): Generator; - }; - - /** Get the properties of the apparent type of this type. */ - getApparentProperties: { - (): readonly Symbol[]; - gen(): Generator; - }; - - /** Get the apparent type of this type. */ - getApparentType: { - (): Type; - gen(): Generator; - }; - - /** Get the reduced type of this type. */ - getReducedType: { - (): Type; - gen(): Generator; - }; - - /** Get the call signatures of this type. */ - getCallSignatures: { - (): readonly Signature[]; - gen(): Generator; - }; - - /** Get the construct signatures of this type. */ - getConstructSignatures: { - (): readonly Signature[]; - gen(): Generator; - }; - - /** Get this type with `null` and `undefined` removed. */ - getNonNullableType: { - (): Type; - gen(): Generator; - }; - - /** Get this type's string index value type, if present. */ - getStringIndexType: { - (): Type | undefined; - gen(): Generator; - }; - - /** Get this type's number index value type, if present. */ - getNumberIndexType: { - (): Type | undefined; - gen(): Generator; - }; - - /** Get all index information for this type. */ - getIndexInfos: { - (): readonly IndexInfo[]; - gen(): Generator; - }; - - /** Get the type arguments of the type alias this type was instantiated from, if any */ - getAliasTypeArguments: { - (): readonly Type[]; - gen(): Generator; - }; - - /** Get the symbol of the type alias this type was instantiated from, if any */ - getAliasSymbol: { - (): Symbol | undefined; - gen(): Generator; - }; - - /** - * Get the base types of this type, or `undefined` if it is not a class or - * interface type. - */ - getBaseTypes: { - (): readonly Type[] | undefined; - gen(): Generator; - }; - - /** Whether this type is a class or interface type */ - isClassOrInterface(): this is InterfaceType; - /** Whether this type is a union type */ - isUnionType(): this is UnionType; - /** Whether this type is an intersection type */ - isIntersectionType(): this is IntersectionType; - /** Whether this type is an object type */ - isObjectType(): this is ObjectType; - /** Whether this type is an intrinsic primitive type */ - isIntrinsicType(): this is IntrinsicType; - /** - * Whether this is the error type — the placeholder produced when a type - * cannot be determined (e.g. an unresolved reference). - */ - isErrorType(): boolean; - /** Whether this type is a literal type */ - isLiteralType(): this is LiteralType; - /** Whether this type is a string literal type */ - isStringLiteralType(): this is StringLiteralType; - /** Whether this type is a number literal type */ - isNumberLiteralType(): this is NumberLiteralType; - /** Whether this type is a bigint literal type */ - isBigIntLiteralType(): this is BigIntLiteralType; - /** Whether this type is a boolean literal type */ - isBooleanLiteralType(): this is BooleanLiteralType; - /** Whether this type is a type reference */ - isTypeReference(): this is TypeReference; - /** Whether this type is a tuple type */ - isTupleType(): this is TupleType; - /** Whether this type is an index type (`keyof T`) */ - isIndexType(): this is IndexType; - /** Whether this type is an indexed access type (`T[K]`) */ - isIndexedAccessType(): this is IndexedAccessType; - /** Whether this type is a conditional type */ - isConditionalType(): this is ConditionalType; - /** Whether this type is a substitution type */ - isSubstitutionType(): this is SubstitutionType; - /** Whether this type is a template literal type */ - isTemplateLiteralType(): this is TemplateLiteralType; - /** Whether this type is a string mapping type */ - isStringMappingType(): this is StringMappingType; - /** Whether this type is a type parameter */ - isTypeParameter(): this is TypeParameter; -} - -/** - * Freshable types (TypeFlags.Freshable) - literal types (TypeFlags.Literal) and computed enum types (TypeFlags.Enum). - */ -export interface FreshableType extends Type { - /** Get the fresh version of this type, if any */ - getFreshType: { - (): FreshableType | undefined; - gen(): Generator; - }; - /** Get the regular (non-fresh) version of this type, if any */ - getRegularType: { - (): FreshableType | undefined; - gen(): Generator; - }; -} - -/** Literal types: StringLiteral, NumberLiteral, BigIntLiteral, BooleanLiteral */ -export interface LiteralType extends FreshableType { - /** The literal value. Use TypeFlags to narrow to a specific literal subtype with a concrete value type. */ - readonly value: string | number | boolean | bigint; -} - -/** String literal types (TypeFlags.StringLiteral) */ -export interface StringLiteralType extends LiteralType { - /** The string value of the literal */ - readonly value: string; -} - -/** Numeric literal types (TypeFlags.NumberLiteral) */ -export interface NumberLiteralType extends LiteralType { - /** The numeric value of the literal */ - readonly value: number; -} - -/** BigInt literal types (TypeFlags.BigIntLiteral) */ -export interface BigIntLiteralType extends LiteralType { - /** The bigint value of the literal */ - readonly value: bigint; -} - -/** Boolean literal types (TypeFlags.BooleanLiteral) */ -export interface BooleanLiteralType extends LiteralType { - /** The boolean value of the literal */ - readonly value: boolean; -} - -/** Object types (TypeFlags.Object) */ -export interface ObjectType extends Type { - /** Object flags — use to determine the specific kind of object type. */ - readonly objectFlags: ObjectFlags; -} - -/** Type references (ObjectFlags.Reference) — e.g. Array, Map */ -export interface TypeReference extends ObjectType { - /** Get the generic target type (e.g. Array for Array) */ - getTarget: { - (): Type; - gen(): Generator; - }; -} - -/** Interface types — classes and interfaces (ObjectFlags.ClassOrInterface) */ -export interface InterfaceType extends TypeReference { - /** Get all type parameters (outer + local, excluding thisType) */ - getTypeParameters: { - (): readonly TypeParameter[]; - gen(): Generator; - }; - /** Get outer type parameters from enclosing declarations */ - getOuterTypeParameters: { - (): readonly TypeParameter[]; - gen(): Generator; - }; - /** Get local type parameters declared on this interface/class */ - getLocalTypeParameters: { - (): readonly TypeParameter[]; - gen(): Generator; - }; -} - -/** Tuple types (ObjectFlags.Tuple) */ -export interface TupleType extends InterfaceType { - /** Per-element flags (Required, Optional, Rest, Variadic) */ - readonly elementFlags: readonly ElementFlags[]; - /** Number of initial required or optional elements */ - readonly fixedLength: number; - /** Whether the tuple is readonly */ - readonly readonly: boolean; -} - -/** Union or intersection types (TypeFlags.Union | TypeFlags.Intersection) */ -export interface UnionOrIntersectionType extends Type { - /** Get the constituent types */ - getTypes: { - (): readonly Type[]; - gen(): Generator; - }; -} - -/** Union types (TypeFlags.Union) */ -export interface UnionType extends UnionOrIntersectionType { -} - -/** Intersection types (TypeFlags.Intersection) */ -export interface IntersectionType extends UnionOrIntersectionType { -} - -/** Structured types (TypeFlags.StructuredType) */ -export type StructuredType = ObjectType | UnionType | IntersectionType; - -/** Type parameters (TypeFlags.TypeParameter) */ -export interface TypeParameter extends Type { - /** True if this is the synthetic `this` type of an interface, class, or tuple */ - readonly isThisType?: boolean | undefined; - /** Get the constraint (the `T` in ``), or undefined if it has none */ - getConstraint: { - (): Type | undefined; - gen(): Generator; - }; - /** Get the default type (the `T` in ``), or undefined if it has none */ - getDefault: { - (): Type | undefined; - gen(): Generator; - }; -} - -/** Index types — keyof T (TypeFlags.Index) */ -export interface IndexType extends Type { - /** Get the target type T in `keyof T` */ - getTarget: { - (): Type; - gen(): Generator; - }; -} - -/** Indexed access types — T[K] (TypeFlags.IndexedAccess) */ -export interface IndexedAccessType extends Type { - /** Get the object type T in `T[K]` */ - getObjectType: { - (): Type; - gen(): Generator; - }; - /** Get the index type K in `T[K]` */ - getIndexType: { - (): Type; - gen(): Generator; - }; -} - -/** Conditional types — T extends U ? X : Y (TypeFlags.Conditional) */ -export interface ConditionalType extends Type { - /** Get the check type T in `T extends U ? X : Y` */ - getCheckType: { - (): Type; - gen(): Generator; - }; - /** Get the extends type U in `T extends U ? X : Y` */ - getExtendsType: { - (): Type; - gen(): Generator; - }; - /** Get the true type X in `T extends U ? X : Y` */ - getTrueType: { - (): Type; - gen(): Generator; - }; - /** Get the false type Y in `T extends U ? X : Y` */ - getFalseType: { - (): Type; - gen(): Generator; - }; -} - -/** Substitution types (TypeFlags.Substitution) */ -export interface SubstitutionType extends Type { - getBaseType: { - (): Type; - gen(): Generator; - }; - getConstraint: { - (): Type; - gen(): Generator; - }; -} - -/** Template literal types (TypeFlags.TemplateLiteral) */ -export interface TemplateLiteralType extends Type { - /** Text segments (always one more than the number of type spans) */ - readonly texts: readonly string[]; - /** Get the types interspersed between text segments */ - getTypes: { - (): readonly Type[]; - gen(): Generator; - }; -} - -/** String mapping types — Uppercase, Lowercase, etc. (TypeFlags.StringMapping) */ -export interface StringMappingType extends Type { - /** Get the mapped type */ - getTarget: { - (): Type; - gen(): Generator; - }; -} - -/** Intrinsic types — any, unknown, string, number, bigint, symbol, void, undefined, null, never, object (TypeFlags.Intrinsic) */ -export interface IntrinsicType extends Type { - /** The intrinsic type name (e.g. "any", "string", "never") */ - readonly intrinsicName: string; -} - -/** Base for all type predicates */ -export interface TypePredicateBase { - readonly kind: TypePredicateKind; - readonly type: Type | undefined; -} - -/** `this is T` */ -export interface ThisTypePredicate extends TypePredicateBase { - readonly kind: TypePredicateKind.This; - readonly parameterName: undefined; - readonly parameterIndex: undefined; - readonly type: Type; -} - -/** `x is T` */ -export interface IdentifierTypePredicate extends TypePredicateBase { - readonly kind: TypePredicateKind.Identifier; - readonly parameterName: string; - readonly parameterIndex: number; - readonly type: Type; -} - -/** `asserts this is T` */ -export interface AssertsThisTypePredicate extends TypePredicateBase { - readonly kind: TypePredicateKind.AssertsThis; - readonly parameterName: undefined; - readonly parameterIndex: undefined; - readonly type: Type | undefined; -} - -/** `asserts x is T` */ -export interface AssertsIdentifierTypePredicate extends TypePredicateBase { - readonly kind: TypePredicateKind.AssertsIdentifier; - readonly parameterName: string; - readonly parameterIndex: number; - readonly type: Type | undefined; -} - -/** A type predicate — e.g. `x is T` or `asserts x is T` */ -export type TypePredicate = ThisTypePredicate | IdentifierTypePredicate | AssertsThisTypePredicate | AssertsIdentifierTypePredicate; - -/** An index signature — e.g. `[key: string]: T` */ -export interface IndexInfo { - /** The index key type (e.g. string or number) */ - readonly keyType: Type; - /** The index value type */ - readonly valueType: Type; - /** Whether the index signature is readonly */ - readonly isReadonly: boolean; - /** The index signature declaration, if any */ - readonly declaration?: NodeHandle | undefined; -} - -/** - * A single JSDoc tag attached to a symbol — e.g. `@param`, `@returns`. - */ -export interface JSDocTagInfo { - /** The tag name, without the leading `@` — e.g. `"param"`. */ - readonly name: string; - /** The rendered tag text, if any — e.g. `"a the first number"` for `@param a the first number`. */ - readonly text?: string | undefined; -} - -export interface CompletionEntryLabelDetails { - detail?: string | undefined; - description?: string | undefined; -} - -/** Options for {@link LanguageService.getCompletionsAtPosition}. */ -export interface CompletionOptions { - triggerCharacter?: string | undefined; - /** Include a `symbol` property on each completion entry. Only populated for symbol-based completions (not keywords or literals). */ - includeSymbol?: boolean | undefined; -} - -/** A single completion item returned by {@link LanguageService.getCompletionsAtPosition}. */ -export interface CompletionEntry { - readonly name: string; - readonly kind?: CompletionItemKind | undefined; - readonly sortText?: string | undefined; - readonly insertText?: string | undefined; - readonly filterText?: string | undefined; - readonly detail?: string | undefined; - readonly labelDetails?: CompletionEntryLabelDetails | undefined; - /** The symbol associated with this completion entry. Only set when `includeSymbol: true` is passed and a symbol is available. */ - readonly symbol?: Symbol | undefined; -} - -/** The result of {@link LanguageService.getCompletionsAtPosition}. */ -export interface CompletionInfo { - readonly isIncomplete: boolean; - readonly entries: readonly CompletionEntry[]; -} - -export interface FormatDiagnosticsHost { - getCurrentDirectory(): string; - getCanonicalFileName(fileName: string): string; - getNewLine(): string; -} - -export interface EmitOutputFile { - readonly text: string; - readonly sourceFileName?: string | undefined; -} - -export interface EmitResult { - readonly emitSkipped: boolean; - readonly diagnostics: readonly Diagnostic[]; - readonly emittedFiles: readonly string[]; -} - -export interface EmitOutput { - readonly emitSkipped: boolean; - readonly diagnostics: readonly Diagnostic[]; - readonly outputFiles: ReadonlyMap; -} - -export interface ImportSymbolAction { - readonly kind: "importSymbol"; - readonly symbol: Symbol; - readonly isValidTypeOnlyUseSite?: boolean; -} - -export type ImportAdderAction = ImportSymbolAction; - -export interface GetImportEditsForSymbolsOptions { - readonly isValidTypeOnlyUseSite?: boolean; -} +// +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// !!! THIS FILE IS AUTO-GENERATED - DO NOT EDIT !!! +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// +// Source: src/api/async/types.ts +// Regenerate: npm run generate (from packages/typescript) +// +import type { + APIRequest as ProtocolRequest, + APIResponse as ProtocolResponse, +} from "../proto.ts"; + +import type { CompletionItemKind } from "#enums/completionItemKind"; +import type { ElementFlags } from "#enums/elementFlags"; +import type { ObjectFlags } from "#enums/objectFlags"; +import type { TypeFlags } from "#enums/typeFlags"; +import type { TypePredicateKind } from "#enums/typePredicateKind"; +import type { IndexSignatureDeclaration } from "../../ast/ast.ts"; +import type { Diagnostic } from "../proto.ts"; +import type { + NodeHandle, + Signature, + Symbol, +} from "./api.ts"; + +export type { Diagnostic } from "../proto.ts"; + +/** + * A TypeScript type. + * + * Use TypeFlags to determine the specific kind of type and access + * kind-specific properties. For example: + * + * ```ts + * if (type.flags & TypeFlags.StringLiteral) { + * console.log((type as StringLiteralType).value); // string + * } + * ``` + */ +export interface Type { + /** Type flags — use to determine the specific kind of type. */ + readonly flags: TypeFlags; + + /** Unique identifier for this type */ + readonly id: number; + + /** Get the symbol associated with this type, if any */ + getSymbol: { + (): Symbol | undefined; + gen(): Generator; + }; + + /** Get the properties of this type. */ + getProperties: { + (): readonly Symbol[]; + gen(): Generator; + }; + + /** Get a named property of this type, if present. */ + getProperty: { + (propertyName: string): Symbol | undefined; + gen(propertyName: string): Generator; + }; + + /** Get the properties of the apparent type of this type. */ + getApparentProperties: { + (): readonly Symbol[]; + gen(): Generator; + }; + + /** Get the apparent type of this type. */ + getApparentType: { + (): Type; + gen(): Generator; + }; + + /** Get the reduced type of this type. */ + getReducedType: { + (): Type; + gen(): Generator; + }; + + /** Get the call signatures of this type. */ + getCallSignatures: { + (): readonly Signature[]; + gen(): Generator; + }; + + /** Get the construct signatures of this type. */ + getConstructSignatures: { + (): readonly Signature[]; + gen(): Generator; + }; + + /** Get this type with `null` and `undefined` removed. */ + getNonNullableType: { + (): Type; + gen(): Generator; + }; + + /** Get this type's string index value type, if present. */ + getStringIndexType: { + (): Type | undefined; + gen(): Generator; + }; + + /** Get this type's number index value type, if present. */ + getNumberIndexType: { + (): Type | undefined; + gen(): Generator; + }; + + /** Get all index information for this type. */ + getIndexInfos: { + (): readonly IndexInfo[]; + gen(): Generator; + }; + + /** Get the type arguments of the type alias this type was instantiated from, if any */ + getAliasTypeArguments: { + (): readonly Type[]; + gen(): Generator; + }; + + /** Get the symbol of the type alias this type was instantiated from, if any */ + getAliasSymbol: { + (): Symbol | undefined; + gen(): Generator; + }; + + /** + * Get the base types of this type, or `undefined` if it is not a class or + * interface type. + */ + getBaseTypes: { + (): readonly Type[] | undefined; + gen(): Generator; + }; + + /** Whether this type is a class or interface type */ + isClassOrInterface(): this is InterfaceType; + /** Whether this type is a union type */ + isUnionType(): this is UnionType; + /** Whether this type is an intersection type */ + isIntersectionType(): this is IntersectionType; + /** Whether this type is an object type */ + isObjectType(): this is ObjectType; + /** Whether this type is an intrinsic primitive type */ + isIntrinsicType(): this is IntrinsicType; + /** + * Whether this is the error type — the placeholder produced when a type + * cannot be determined (e.g. an unresolved reference). + */ + isErrorType(): boolean; + /** Whether this type is a literal type */ + isLiteralType(): this is LiteralType; + /** Whether this type is a string literal type */ + isStringLiteralType(): this is StringLiteralType; + /** Whether this type is a number literal type */ + isNumberLiteralType(): this is NumberLiteralType; + /** Whether this type is a bigint literal type */ + isBigIntLiteralType(): this is BigIntLiteralType; + /** Whether this type is a boolean literal type */ + isBooleanLiteralType(): this is BooleanLiteralType; + /** Whether this type is a type reference */ + isTypeReference(): this is TypeReference; + /** Whether this type is a tuple type */ + isTupleType(): this is TupleType; + /** Whether this type is an index type (`keyof T`) */ + isIndexType(): this is IndexType; + /** Whether this type is an indexed access type (`T[K]`) */ + isIndexedAccessType(): this is IndexedAccessType; + /** Whether this type is a conditional type */ + isConditionalType(): this is ConditionalType; + /** Whether this type is a substitution type */ + isSubstitutionType(): this is SubstitutionType; + /** Whether this type is a template literal type */ + isTemplateLiteralType(): this is TemplateLiteralType; + /** Whether this type is a string mapping type */ + isStringMappingType(): this is StringMappingType; + /** Whether this type is a type parameter */ + isTypeParameter(): this is TypeParameter; +} + +/** + * Freshable types (TypeFlags.Freshable) - literal types (TypeFlags.Literal) and computed enum types (TypeFlags.Enum). + */ +export interface FreshableType extends Type { + /** Get the fresh version of this type, if any */ + getFreshType: { + (): FreshableType | undefined; + gen(): Generator; + }; + /** Get the regular (non-fresh) version of this type, if any */ + getRegularType: { + (): FreshableType | undefined; + gen(): Generator; + }; +} + +/** Literal types: StringLiteral, NumberLiteral, BigIntLiteral, BooleanLiteral */ +export interface LiteralType extends FreshableType { + /** The literal value. Use TypeFlags to narrow to a specific literal subtype with a concrete value type. */ + readonly value: string | number | boolean | bigint; +} + +/** String literal types (TypeFlags.StringLiteral) */ +export interface StringLiteralType extends LiteralType { + /** The string value of the literal */ + readonly value: string; +} + +/** Numeric literal types (TypeFlags.NumberLiteral) */ +export interface NumberLiteralType extends LiteralType { + /** The numeric value of the literal */ + readonly value: number; +} + +/** BigInt literal types (TypeFlags.BigIntLiteral) */ +export interface BigIntLiteralType extends LiteralType { + /** The bigint value of the literal */ + readonly value: bigint; +} + +/** Boolean literal types (TypeFlags.BooleanLiteral) */ +export interface BooleanLiteralType extends LiteralType { + /** The boolean value of the literal */ + readonly value: boolean; +} + +/** Object types (TypeFlags.Object) */ +export interface ObjectType extends Type { + /** Object flags — use to determine the specific kind of object type. */ + readonly objectFlags: ObjectFlags; +} + +/** Type references (ObjectFlags.Reference) — e.g. Array, Map */ +export interface TypeReference extends ObjectType { + /** Get the generic target type (e.g. Array for Array) */ + getTarget: { + (): Type; + gen(): Generator; + }; +} + +/** Interface types — classes and interfaces (ObjectFlags.ClassOrInterface) */ +export interface InterfaceType extends TypeReference { + /** Get all type parameters (outer + local, excluding thisType) */ + getTypeParameters: { + (): readonly TypeParameter[]; + gen(): Generator; + }; + /** Get outer type parameters from enclosing declarations */ + getOuterTypeParameters: { + (): readonly TypeParameter[]; + gen(): Generator; + }; + /** Get local type parameters declared on this interface/class */ + getLocalTypeParameters: { + (): readonly TypeParameter[]; + gen(): Generator; + }; +} + +/** Tuple types (ObjectFlags.Tuple) */ +export interface TupleType extends InterfaceType { + /** Per-element flags (Required, Optional, Rest, Variadic) */ + readonly elementFlags: readonly ElementFlags[]; + /** Number of initial required or optional elements */ + readonly fixedLength: number; + /** Whether the tuple is readonly */ + readonly readonly: boolean; +} + +/** Union or intersection types (TypeFlags.Union | TypeFlags.Intersection) */ +export interface UnionOrIntersectionType extends Type { + /** Get the constituent types */ + getTypes: { + (): readonly Type[]; + gen(): Generator; + }; +} + +/** Union types (TypeFlags.Union) */ +export interface UnionType extends UnionOrIntersectionType { +} + +/** Intersection types (TypeFlags.Intersection) */ +export interface IntersectionType extends UnionOrIntersectionType { +} + +/** Structured types (TypeFlags.StructuredType) */ +export type StructuredType = ObjectType | UnionType | IntersectionType; + +/** Type parameters (TypeFlags.TypeParameter) */ +export interface TypeParameter extends Type { + /** True if this is the synthetic `this` type of an interface, class, or tuple */ + readonly isThisType?: boolean | undefined; + /** Get the constraint (the `T` in ``), or undefined if it has none */ + getConstraint: { + (): Type | undefined; + gen(): Generator; + }; + /** Get the default type (the `T` in ``), or undefined if it has none */ + getDefault: { + (): Type | undefined; + gen(): Generator; + }; +} + +/** Index types — keyof T (TypeFlags.Index) */ +export interface IndexType extends Type { + /** Get the target type T in `keyof T` */ + getTarget: { + (): Type; + gen(): Generator; + }; +} + +/** Indexed access types — T[K] (TypeFlags.IndexedAccess) */ +export interface IndexedAccessType extends Type { + /** Get the object type T in `T[K]` */ + getObjectType: { + (): Type; + gen(): Generator; + }; + /** Get the index type K in `T[K]` */ + getIndexType: { + (): Type; + gen(): Generator; + }; +} + +/** Conditional types — T extends U ? X : Y (TypeFlags.Conditional) */ +export interface ConditionalType extends Type { + /** Get the check type T in `T extends U ? X : Y` */ + getCheckType: { + (): Type; + gen(): Generator; + }; + /** Get the extends type U in `T extends U ? X : Y` */ + getExtendsType: { + (): Type; + gen(): Generator; + }; + /** Get the true type X in `T extends U ? X : Y` */ + getTrueType: { + (): Type; + gen(): Generator; + }; + /** Get the false type Y in `T extends U ? X : Y` */ + getFalseType: { + (): Type; + gen(): Generator; + }; +} + +/** Substitution types (TypeFlags.Substitution) */ +export interface SubstitutionType extends Type { + getBaseType: { + (): Type; + gen(): Generator; + }; + getConstraint: { + (): Type; + gen(): Generator; + }; +} + +/** Template literal types (TypeFlags.TemplateLiteral) */ +export interface TemplateLiteralType extends Type { + /** Text segments (always one more than the number of type spans) */ + readonly texts: readonly string[]; + /** Get the types interspersed between text segments */ + getTypes: { + (): readonly Type[]; + gen(): Generator; + }; +} + +/** String mapping types — Uppercase, Lowercase, etc. (TypeFlags.StringMapping) */ +export interface StringMappingType extends Type { + /** Get the mapped type */ + getTarget: { + (): Type; + gen(): Generator; + }; +} + +/** Intrinsic types — any, unknown, string, number, bigint, symbol, void, undefined, null, never, object (TypeFlags.Intrinsic) */ +export interface IntrinsicType extends Type { + /** The intrinsic type name (e.g. "any", "string", "never") */ + readonly intrinsicName: string; +} + +/** Base for all type predicates */ +export interface TypePredicateBase { + readonly kind: TypePredicateKind; + readonly type: Type | undefined; +} + +/** `this is T` */ +export interface ThisTypePredicate extends TypePredicateBase { + readonly kind: TypePredicateKind.This; + readonly parameterName: undefined; + readonly parameterIndex: undefined; + readonly type: Type; +} + +/** `x is T` */ +export interface IdentifierTypePredicate extends TypePredicateBase { + readonly kind: TypePredicateKind.Identifier; + readonly parameterName: string; + readonly parameterIndex: number; + readonly type: Type; +} + +/** `asserts this is T` */ +export interface AssertsThisTypePredicate extends TypePredicateBase { + readonly kind: TypePredicateKind.AssertsThis; + readonly parameterName: undefined; + readonly parameterIndex: undefined; + readonly type: Type | undefined; +} + +/** `asserts x is T` */ +export interface AssertsIdentifierTypePredicate extends TypePredicateBase { + readonly kind: TypePredicateKind.AssertsIdentifier; + readonly parameterName: string; + readonly parameterIndex: number; + readonly type: Type | undefined; +} + +/** A type predicate — e.g. `x is T` or `asserts x is T` */ +export type TypePredicate = ThisTypePredicate | IdentifierTypePredicate | AssertsThisTypePredicate | AssertsIdentifierTypePredicate; + +/** An index signature — e.g. `[key: string]: T` */ +export interface IndexInfo { + /** The index key type (e.g. string or number) */ + readonly keyType: Type; + /** The index value type */ + readonly valueType: Type; + /** Whether the index signature is readonly */ + readonly isReadonly: boolean; + /** The index signature declaration, if any */ + readonly declaration?: NodeHandle | undefined; +} + +/** + * A single JSDoc tag attached to a symbol — e.g. `@param`, `@returns`. + */ +export interface JSDocTagInfo { + /** The tag name, without the leading `@` — e.g. `"param"`. */ + readonly name: string; + /** The rendered tag text, if any — e.g. `"a the first number"` for `@param a the first number`. */ + readonly text?: string | undefined; +} + +export interface CompletionEntryLabelDetails { + detail?: string | undefined; + description?: string | undefined; +} + +/** Options for {@link LanguageService.getCompletionsAtPosition}. */ +export interface CompletionOptions { + triggerCharacter?: string | undefined; + /** Include a `symbol` property on each completion entry. Only populated for symbol-based completions (not keywords or literals). */ + includeSymbol?: boolean | undefined; +} + +/** A single completion item returned by {@link LanguageService.getCompletionsAtPosition}. */ +export interface CompletionEntry { + readonly name: string; + readonly kind?: CompletionItemKind | undefined; + readonly sortText?: string | undefined; + readonly insertText?: string | undefined; + readonly filterText?: string | undefined; + readonly detail?: string | undefined; + readonly labelDetails?: CompletionEntryLabelDetails | undefined; + /** The symbol associated with this completion entry. Only set when `includeSymbol: true` is passed and a symbol is available. */ + readonly symbol?: Symbol | undefined; +} + +/** The result of {@link LanguageService.getCompletionsAtPosition}. */ +export interface CompletionInfo { + readonly isIncomplete: boolean; + readonly entries: readonly CompletionEntry[]; +} + +export interface FormatDiagnosticsHost { + getCurrentDirectory(): string; + getCanonicalFileName(fileName: string): string; + getNewLine(): string; +} + +export interface EmitOutputFile { + readonly text: string; + readonly sourceFileName?: string | undefined; +} + +export interface EmitResult { + readonly emitSkipped: boolean; + readonly diagnostics: readonly Diagnostic[]; + readonly emittedFiles: readonly string[]; +} + +export interface EmitOutput { + readonly emitSkipped: boolean; + readonly diagnostics: readonly Diagnostic[]; + readonly outputFiles: ReadonlyMap; +} + +export interface ImportSymbolAction { + readonly kind: "importSymbol"; + readonly symbol: Symbol; + readonly isValidTypeOnlyUseSite?: boolean; +} + +export type ImportAdderAction = ImportSymbolAction; + +export interface GetImportEditsForSymbolsOptions { + readonly isValidTypeOnlyUseSite?: boolean; +} diff --git a/packages/typescript/src/api/syncChannel.ts b/packages/typescript/src/api/syncChannel.ts index 122203c85e38e..2c5c2db9c92cf 100644 --- a/packages/typescript/src/api/syncChannel.ts +++ b/packages/typescript/src/api/syncChannel.ts @@ -1,620 +1,620 @@ -/** - * Pure JS replacement for @typescript/libsyncrpc. - * - * Spawns a child process and communicates with it synchronously over - * stdin/stdout pipes using the same MessagePack-based tuple protocol: - * [MessageType (u8), method (bin), payload (bin)] - * - * Synchronous I/O is achieved by calling fs.readSync / fs.writeSync - * directly on the pipe file descriptors obtained from the spawned - * ChildProcess. - */ - -import { - type ChildProcess, - spawn, -} from "node:child_process"; -import { - closeSync, - openSync, - readSync, - writeSync, -} from "node:fs"; -import type { - Readable, - Writable, -} from "node:stream"; - -interface StdioHandle { - fd: number; - setBlocking?: (value: boolean) => void; -} - -interface StdoutWithHandle extends Readable { - _handle: StdioHandle; - unref: () => void; -} - -interface StdinWithHandle extends Writable { - _handle: StdioHandle; - unref: () => void; -} - -import { - binHeaderSize, - MSGPACK_BIN16, - MSGPACK_BIN32, - MSGPACK_BIN8, - MSGPACK_FIXARRAY3, - MSGPACK_UINT8, - writeBinHeader, -} from "./node/msgpack.ts"; - -// ── MessageType constants ──────────────────────────────────────────── -// Sent by channel (parent → child) -const MSG_REQUEST = 1; -const MSG_CALL_RESPONSE = 2; -const MSG_CALL_ERROR = 3; -// Sent by child (child → parent) -const MSG_RESPONSE = 4; -const MSG_ERROR = 5; -const MSG_CALL = 6; - -// Pre-allocated buffer used by Atomics.wait for tiny sleeps when a -// non-blocking fd returns EAGAIN. -const sleepBuf = new Int32Array(new SharedArrayBuffer(4)); - -// Shared empty buffer – avoids allocating Buffer.alloc(0) on every -// zero-length bin field. -const EMPTY_BUF = Buffer.alloc(0); - -// ── Global cleanup tracking ───────────────────────────────────────── -// Track all live child processes so they can be killed on process exit. -// This mimics the auto-cleanup behavior of the native libsyncrpc module, -// whose Rust/C++ destructors would kill children automatically. -const liveChildren = new Set(); - -process.on("exit", () => { - for (const child of liveChildren) { - try { - child.kill(); - } - catch { - // swallow – process may already be dead - } - } - liveChildren.clear(); -}); - -/** - * SyncRpcChannel – drop-in replacement for the native libsyncrpc class. - * - * API surface intentionally matches the original: - * - constructor(exe, args) - * - requestSync(method, payload): string - * - requestBinarySync(method, payload): Uint8Array - * - registerCallback(name, cb) - * - close() - * - * The protocol is unversioned; both sides (this JS channel and the Go - * child process) must be built from the same tree. - * - * This class is **not** thread-safe. All calls must originate from a - * single thread — do not share an instance across worker threads. - */ -export class SyncRpcChannel { - private child: ChildProcess; - private readFd: number; - private writeFd: number; - private pipeFd: number | undefined; - private callbacks = new Map string>(); - - private methodBufCache = new Map(); - - // When true, the payload byte lengths of each request/response are recorded - // and exposed via the `last*` fields below. These count only the JSON/binary - // payload bytes, not the MessagePack tuple framing (message type, method - // name, and length headers). - private readonly collectTiming: boolean; - - // Per-request payload byte measurements for the most recently completed - // request. Only meaningful when `collectTiming` is true. Callers read these - // immediately after a request returns (the channel is strictly serial). - lastBytesSent = 0; - lastBytesReceived = 0; - - private _msgType = 0; - private _msgName: Buffer = EMPTY_BUF; - private _msgPayload: Buffer = EMPTY_BUF; - - private headerBuf = Buffer.allocUnsafe(4); - - // Read-ahead buffer – reduces readSync syscalls by buffering data from the pipe. - private readBuf = Buffer.allocUnsafe(65536); - private readBufPos = 0; - private readBufLen = 0; - - // Write buffer – assembles entire tuples for a single writeSync. - private writeBuf = Buffer.allocUnsafe(65536); - - constructor(exe: string, args: string[], collectTiming = false) { - this.collectTiming = collectTiming; - const isWindows = process.platform === "win32"; - - if (isWindows) { - // On Windows, libuv pipe handles don't expose POSIX fds, so - // readSync/writeSync can't be used on stdio pipes. Instead, - // we create a Windows named pipe path, pass it to the child - // via --pipe, and open it with fs.openSync which returns a - // real C-runtime fd backed by a proper HANDLE. - const pipePath = `\\\\.\\pipe\\tsgo-sync-${process.pid}-${Date.now()}`; - this.child = spawn(exe, [...args, "--pipe", pipePath], { - stdio: ["ignore", "ignore", "inherit"], - }); - - // Retry openSync until the child creates the named pipe. - let fd: number | undefined; - for (let i = 0; i < 500; i++) { - try { - fd = openSync(pipePath, "r+"); - break; - } - catch { - if (this.child.exitCode !== null) { - throw new Error( - `Child process exited with code ${this.child.exitCode} before pipe was ready`, - ); - } - Atomics.wait(sleepBuf, 0, 0, 10); - } - } - if (fd === undefined) { - this.child.kill(); - throw new Error("SyncRpcChannel: timed out connecting to named pipe"); - } - this.readFd = fd; - this.writeFd = fd; - this.pipeFd = fd; - } - else { - // POSIX: use stdio pipe file descriptors directly. - this.child = spawn(exe, args, { - stdio: ["pipe", "pipe", "inherit"], - }); - - const stdout = this.child.stdout! as StdoutWithHandle; - const stdin = this.child.stdin! as StdinWithHandle; - - this.readFd = stdout._handle.fd; - this.writeFd = stdin._handle.fd; - - if (typeof this.readFd !== "number" || this.readFd < 0 || typeof this.writeFd !== "number" || this.writeFd < 0) { - stdout.destroy(); - stdin.destroy(); - this.child.kill(); - throw new Error( - "SyncRpcChannel: could not obtain pipe file descriptors.", - ); - } - - // Set the pipe handles to blocking mode. Under node --test's - // process isolation, pipes are created in non-blocking mode - // (for the IPC channel). This causes readSync/writeSync to get - // EAGAIN, requiring costly 1ms sleeps per retry. Setting - // blocking mode ensures readSync blocks properly until data - // arrives, matching the behavior of the native libsyncrpc. - stdout._handle.setBlocking?.(true); - stdin._handle.setBlocking?.(true); - - // Prevent Node's event-loop from reading stdout or keeping the - // process alive – we will use fs.readSync exclusively. - stdout.pause(); - stdout.unref(); - stdin.unref(); - } - - // Track for auto-cleanup on process exit. - liveChildren.add(this.child); - this.child.unref(); - } - - // ── Public API ────────────────────────────────────────────────── - - /** - * Send a request and synchronously wait for the response (string). - * Handles Call (callback) messages from the child inline. - */ - requestSync(method: string, payload: string): string { - this.ensureOpen(); - const result = this.requestBytesSync(method, payload); - return result.toString("utf-8"); - } - - /** - * Send a request and synchronously wait for the response (binary). - * Handles Call (callback) messages from the child inline. - */ - requestBinarySync(method: string, payload: Uint8Array): Uint8Array { - this.ensureOpen(); - return this.requestBytesSync(method, payload); - } - - /** Register a string→string callback that the child may invoke. */ - registerCallback(name: string, callback: (name: string, payload: string) => string): void { - this.callbacks.set(name, callback); - } - - /** Kill the child process and release resources. */ - close(): void { - try { - liveChildren.delete(this.child); - if (this.pipeFd !== undefined) { - closeSync(this.pipeFd); - this.pipeFd = undefined; - } - // Destroy the stdio streams so that their pipe handles are closed - // and no longer prevent the event loop from draining. - this.child.stdout?.destroy(); - this.child.stdin?.destroy(); - this.child.kill(); - this.readFd = -1; - this.writeFd = -1; - } - catch { - // swallow – process may already be dead - } - } - - // ── Core request loop ─────────────────────────────────────────── - - private ensureOpen(): void { - if (this.readFd < 0) { - throw new Error("SyncRpcChannel is closed"); - } - } - - private getMethodBuf(method: string): Buffer { - let buf = this.methodBufCache.get(method); - if (buf === undefined) { - buf = Buffer.from(method, "utf-8"); - this.methodBufCache.set(method, buf); - } - return buf; - } - - private requestBytesSync(method: string, payload: Buffer | Uint8Array | string): Buffer { - const methodBuf = this.getMethodBuf(method); - if (this.collectTiming) { - this.lastBytesSent = typeof payload === "string" - ? Buffer.byteLength(payload, "utf-8") - : payload.length; - this.lastBytesReceived = 0; - } - this.writeTuple(MSG_REQUEST, methodBuf, payload); - - for (;;) { - this.readTuple(); - - switch (this._msgType) { - case MSG_RESPONSE: { - // Compare raw bytes instead of decoding to string. - if (!methodBuf.equals(this._msgName)) { - throw new Error( - `name mismatch for response: expected \`${method}\`, got \`${this._msgName.toString("utf-8")}\``, - ); - } - if (this.collectTiming) { - this.lastBytesReceived = this._msgPayload.length; - } - return this._msgPayload; - } - case MSG_ERROR: { - if (methodBuf.equals(this._msgName)) { - throw new Error(this._msgPayload.toString("utf-8")); - } - throw new Error( - `name mismatch for response: expected \`${method}\`, got \`${this._msgName.toString("utf-8")}\``, - ); - } - case MSG_CALL: { - this.handleCall(this._msgName.toString("utf-8"), this._msgPayload); - break; - } - default: - throw new Error(`Invalid message type from child: ${this._msgType}`); - } - } - } - - // ── Callback handling ─────────────────────────────────────────── - - /** - * Handle an incoming MSG_CALL from the child process. - * - * After sending the error response back to the child, this method - * intentionally re-throws to abort the caller's request loop. - * A failed callback is treated as unrecoverable to match the - * behavior of the native libsyncrpc addon. - */ - private handleCall(name: string, payload: Buffer): void { - const cb = this.callbacks.get(name); - if (!cb) { - const errMsg = `unknown callback: \`${name}\`. Please make sure to register it on the JavaScript side before invoking it.`; - this.writeTuple( - MSG_CALL_ERROR, - Buffer.from(name, "utf-8"), - Buffer.from(errMsg, "utf-8"), - ); - throw new Error(`no callback named \`${name}\` found`); - } - - try { - const result = cb(name, payload.toString("utf-8")); - this.writeTuple( - MSG_CALL_RESPONSE, - Buffer.from(name, "utf-8"), - Buffer.from(result, "utf-8"), - ); - } - catch (e: unknown) { - const errMsg = String(e instanceof Error ? e.message : e).trim(); - this.writeTuple( - MSG_CALL_ERROR, - Buffer.from(name, "utf-8"), - Buffer.from(errMsg, "utf-8"), - ); - throw new Error(`Error calling callback \`${name}\`: ${errMsg}`); - } - } - - // ── MessagePack tuple write ───────────────────────────────────── - - /** - * Write a complete [type, name, payload] tuple in as few writeSync - * calls as possible. For messages that fit in the pre-allocated - * write buffer (64 KB), everything is assembled and sent in a single - * syscall. Larger messages use two syscalls: one for the header - * portion and one for the payload data. - */ - private writeTuple(type: number, name: Buffer, payload: Buffer | Uint8Array | string): void { - const nameLen = name.length; - const payloadIsString = typeof payload === "string"; - const payloadLen = payloadIsString ? Buffer.byteLength(payload, "utf-8") : payload.length; - const nameHdrSize = binHeaderSize(nameLen); - const payloadHdrSize = binHeaderSize(payloadLen); - const headerSize = 2 + nameHdrSize + nameLen + payloadHdrSize; - const totalSize = headerSize + payloadLen; - - if (totalSize <= this.writeBuf.length) { - // Small message: assemble into write buffer, one syscall - let off = 0; - this.writeBuf[off++] = MSGPACK_FIXARRAY3; - this.writeBuf[off++] = type; - off = writeBinHeader(this.writeBuf, off, nameLen); - name.copy(this.writeBuf, off); - off += nameLen; - off = writeBinHeader(this.writeBuf, off, payloadLen); - if (payloadLen > 0) { - if (payloadIsString) { - // Encode string directly into write buffer — avoids - // Buffer.from(string, 'utf-8') allocation entirely. - this.writeBuf.write(payload as string, off, payloadLen, "utf-8"); - } - else if (payload instanceof Buffer) { - (payload as Buffer).copy(this.writeBuf, off); - } - else { - this.writeBuf.set(payload as Uint8Array, off); - } - } - this.writeAllBuf(this.writeBuf, totalSize); - } - else { - // Large message: header + name in one call, payload in another - let off = 0; - this.writeBuf[off++] = MSGPACK_FIXARRAY3; - this.writeBuf[off++] = type; - off = writeBinHeader(this.writeBuf, off, nameLen); - name.copy(this.writeBuf, off); - off += nameLen; - off = writeBinHeader(this.writeBuf, off, payloadLen); - this.writeAllBuf(this.writeBuf, off); - if (payloadLen > 0) { - if (payloadIsString) { - // Large string: must allocate (can't stream-encode - // across multiple writeSync calls). - this.writeAllBuf(Buffer.from(payload as string, "utf-8")); - } - else { - this.writeAllBuf(payload as Buffer | Uint8Array); - } - } - } - } - - // ── MessagePack tuple read ────────────────────────────────────── - - /** - * Read a [type, name, payload] tuple into instance fields - * (_msgType, _msgName, _msgPayload) to avoid allocating a - * short-lived 3-element array on every call. - */ - private readTuple(): void { - // Fixed 3-element array marker - const marker = this.readByte(); - if (marker !== MSGPACK_FIXARRAY3) { - throw new Error( - `Expected fixed 3-element array (0x93), received: 0x${marker.toString(16)}`, - ); - } - - // Message type – positive fixint or uint8 - const tb = this.readByte(); - if (tb <= 0x7f) { - this._msgType = tb; - } - else if (tb === MSGPACK_UINT8) { - this._msgType = this.readByte(); - } - else { - throw new Error( - `Expected positive fixint or uint8 marker, received: 0x${tb.toString(16)}`, - ); - } - - this._msgName = this.readBin(); - this._msgPayload = this.readBin(); - } - - /** - * Read a MessagePack bin field. - */ - private readBin(): Buffer { - const marker = this.readByte(); - let size: number; - switch (marker) { - case MSGPACK_BIN8: - size = this.readByte(); - break; - case MSGPACK_BIN16: - this.readExactInto(this.headerBuf, 2); - size = (this.headerBuf[0] << 8) | this.headerBuf[1]; - break; - case MSGPACK_BIN32: - this.readExactInto(this.headerBuf, 4); - size = this.headerBuf.readUInt32BE(0); - break; - default: - throw new Error( - `Expected binary data (0xc4-0xc6), received: 0x${marker.toString(16)}`, - ); - } - if (size === 0) return EMPTY_BUF; - return this.readExact(size); - } - - // ── Low-level synchronous I/O ─────────────────────────────────── - - /** Build an EOF error with the child's exit code/signal if available. */ - private eofError(): Error { - const code = this.child.exitCode; - const signal = this.child.signalCode; - const detail = signal ? `killed by signal ${signal}` : code !== null ? `exited with code ${code}` : "unknown reason"; - return new Error(`Unexpected EOF while reading from child process (${detail})`); - } - - /** Read a single byte from the buffered read-ahead. */ - private readByte(): number { - if (this.readBufPos >= this.readBufLen) { - this.fillReadBuffer(); - } - return this.readBuf[this.readBufPos++]; - } - - private readExact(length: number): Buffer { - // Use allocUnsafeSlow (not allocUnsafe) so the buffer has its own - // backing ArrayBuffer at byteOffset 0. This is critical because - // callers such as RemoteSourceFile create DataView/Uint8Array over - // buffer.buffer with absolute offsets. Buffer.allocUnsafe returns - // slices of a shared pool whose byteOffset is non-zero, corrupting - // those downstream views. - // - // allocUnsafeSlow (vs alloc) skips zero-fill, which matters for - // large transfers — e.g. checker.ts at ~57 MB saves ~5 ms of - // unnecessary memset. The buffer is immediately filled by - // readExactInto so uninitialized memory is never exposed. - const buf = Buffer.allocUnsafeSlow(length); - this.readExactInto(buf, length); - return buf; - } - - /** - * Fill the internal read-ahead buffer from the pipe fd. - * Retries on EAGAIN for non-blocking mode compatibility. - */ - private fillReadBuffer(): void { - this.readBufPos = 0; - this.readBufLen = 0; - for (;;) { - try { - const n = readSync(this.readFd, this.readBuf, 0, this.readBuf.length, null); - if (n === 0) { - throw this.eofError(); - } - this.readBufLen = n; - return; - } - catch (e: unknown) { - if (e instanceof Error && ("code" in e) && ((e as NodeJS.ErrnoException).code === "EAGAIN" || (e as NodeJS.ErrnoException).code === "EWOULDBLOCK")) { - Atomics.wait(sleepBuf, 0, 0, 1); - continue; - } - throw e; - } - } - } - - /** - * Synchronously read exactly `length` bytes into `buffer`. - * Serves from the internal read-ahead buffer first; for large reads - * that exceed the buffer size, reads directly from the fd to avoid - * an extra copy. - */ - private readExactInto(buffer: Buffer, length: number): void { - let pos = 0; - while (pos < length) { - const avail = this.readBufLen - this.readBufPos; - if (avail > 0) { - // Serve from read-ahead buffer - const toCopy = Math.min(avail, length - pos); - this.readBuf.copy(buffer, pos, this.readBufPos, this.readBufPos + toCopy); - this.readBufPos += toCopy; - pos += toCopy; - } - else if (length - pos >= this.readBuf.length) { - // Remaining data is larger than read buffer; read directly - // into the target to avoid unnecessary copying. - try { - const n = readSync(this.readFd, buffer, pos, length - pos, null); - if (n === 0) { - throw this.eofError(); - } - pos += n; - } - catch (e: unknown) { - if (e instanceof Error && ("code" in e) && ((e as NodeJS.ErrnoException).code === "EAGAIN" || (e as NodeJS.ErrnoException).code === "EWOULDBLOCK")) { - Atomics.wait(sleepBuf, 0, 0, 1); - continue; - } - throw e; - } - } - else { - // Refill the read-ahead buffer - this.fillReadBuffer(); - } - } - } - - /** - * Synchronously write all bytes from `data` (up to `length`). - * Retries on EAGAIN. - */ - private writeAllBuf(data: Buffer | Uint8Array, length?: number): void { - const total = length ?? data.length; - let pos = 0; - while (pos < total) { - try { - const n = writeSync(this.writeFd, data, pos, total - pos); - pos += n; - } - catch (e: unknown) { - if (e instanceof Error && ("code" in e) && ((e as NodeJS.ErrnoException).code === "EAGAIN" || (e as NodeJS.ErrnoException).code === "EWOULDBLOCK")) { - Atomics.wait(sleepBuf, 0, 0, 1); - continue; - } - throw e; - } - } - } -} +/** + * Pure JS replacement for @typescript/libsyncrpc. + * + * Spawns a child process and communicates with it synchronously over + * stdin/stdout pipes using the same MessagePack-based tuple protocol: + * [MessageType (u8), method (bin), payload (bin)] + * + * Synchronous I/O is achieved by calling fs.readSync / fs.writeSync + * directly on the pipe file descriptors obtained from the spawned + * ChildProcess. + */ + +import { + type ChildProcess, + spawn, +} from "node:child_process"; +import { + closeSync, + openSync, + readSync, + writeSync, +} from "node:fs"; +import type { + Readable, + Writable, +} from "node:stream"; + +interface StdioHandle { + fd: number; + setBlocking?: (value: boolean) => void; +} + +interface StdoutWithHandle extends Readable { + _handle: StdioHandle; + unref: () => void; +} + +interface StdinWithHandle extends Writable { + _handle: StdioHandle; + unref: () => void; +} + +import { + binHeaderSize, + MSGPACK_BIN16, + MSGPACK_BIN32, + MSGPACK_BIN8, + MSGPACK_FIXARRAY3, + MSGPACK_UINT8, + writeBinHeader, +} from "./node/msgpack.ts"; + +// ── MessageType constants ──────────────────────────────────────────── +// Sent by channel (parent → child) +const MSG_REQUEST = 1; +const MSG_CALL_RESPONSE = 2; +const MSG_CALL_ERROR = 3; +// Sent by child (child → parent) +const MSG_RESPONSE = 4; +const MSG_ERROR = 5; +const MSG_CALL = 6; + +// Pre-allocated buffer used by Atomics.wait for tiny sleeps when a +// non-blocking fd returns EAGAIN. +const sleepBuf = new Int32Array(new SharedArrayBuffer(4)); + +// Shared empty buffer – avoids allocating Buffer.alloc(0) on every +// zero-length bin field. +const EMPTY_BUF = Buffer.alloc(0); + +// ── Global cleanup tracking ───────────────────────────────────────── +// Track all live child processes so they can be killed on process exit. +// This mimics the auto-cleanup behavior of the native libsyncrpc module, +// whose Rust/C++ destructors would kill children automatically. +const liveChildren = new Set(); + +process.on("exit", () => { + for (const child of liveChildren) { + try { + child.kill(); + } + catch { + // swallow – process may already be dead + } + } + liveChildren.clear(); +}); + +/** + * SyncRpcChannel – drop-in replacement for the native libsyncrpc class. + * + * API surface intentionally matches the original: + * - constructor(exe, args) + * - requestSync(method, payload): string + * - requestBinarySync(method, payload): Uint8Array + * - registerCallback(name, cb) + * - close() + * + * The protocol is unversioned; both sides (this JS channel and the Go + * child process) must be built from the same tree. + * + * This class is **not** thread-safe. All calls must originate from a + * single thread — do not share an instance across worker threads. + */ +export class SyncRpcChannel { + private child: ChildProcess; + private readFd: number; + private writeFd: number; + private pipeFd: number | undefined; + private callbacks = new Map string>(); + + private methodBufCache = new Map(); + + // When true, the payload byte lengths of each request/response are recorded + // and exposed via the `last*` fields below. These count only the JSON/binary + // payload bytes, not the MessagePack tuple framing (message type, method + // name, and length headers). + private readonly collectTiming: boolean; + + // Per-request payload byte measurements for the most recently completed + // request. Only meaningful when `collectTiming` is true. Callers read these + // immediately after a request returns (the channel is strictly serial). + lastBytesSent = 0; + lastBytesReceived = 0; + + private _msgType = 0; + private _msgName: Buffer = EMPTY_BUF; + private _msgPayload: Buffer = EMPTY_BUF; + + private headerBuf = Buffer.allocUnsafe(4); + + // Read-ahead buffer – reduces readSync syscalls by buffering data from the pipe. + private readBuf = Buffer.allocUnsafe(65536); + private readBufPos = 0; + private readBufLen = 0; + + // Write buffer – assembles entire tuples for a single writeSync. + private writeBuf = Buffer.allocUnsafe(65536); + + constructor(exe: string, args: string[], collectTiming = false) { + this.collectTiming = collectTiming; + const isWindows = process.platform === "win32"; + + if (isWindows) { + // On Windows, libuv pipe handles don't expose POSIX fds, so + // readSync/writeSync can't be used on stdio pipes. Instead, + // we create a Windows named pipe path, pass it to the child + // via --pipe, and open it with fs.openSync which returns a + // real C-runtime fd backed by a proper HANDLE. + const pipePath = `\\\\.\\pipe\\tsgo-sync-${process.pid}-${Date.now()}`; + this.child = spawn(exe, [...args, "--pipe", pipePath], { + stdio: ["ignore", "ignore", "inherit"], + }); + + // Retry openSync until the child creates the named pipe. + let fd: number | undefined; + for (let i = 0; i < 500; i++) { + try { + fd = openSync(pipePath, "r+"); + break; + } + catch { + if (this.child.exitCode !== null) { + throw new Error( + `Child process exited with code ${this.child.exitCode} before pipe was ready`, + ); + } + Atomics.wait(sleepBuf, 0, 0, 10); + } + } + if (fd === undefined) { + this.child.kill(); + throw new Error("SyncRpcChannel: timed out connecting to named pipe"); + } + this.readFd = fd; + this.writeFd = fd; + this.pipeFd = fd; + } + else { + // POSIX: use stdio pipe file descriptors directly. + this.child = spawn(exe, args, { + stdio: ["pipe", "pipe", "inherit"], + }); + + const stdout = this.child.stdout! as StdoutWithHandle; + const stdin = this.child.stdin! as StdinWithHandle; + + this.readFd = stdout._handle.fd; + this.writeFd = stdin._handle.fd; + + if (typeof this.readFd !== "number" || this.readFd < 0 || typeof this.writeFd !== "number" || this.writeFd < 0) { + stdout.destroy(); + stdin.destroy(); + this.child.kill(); + throw new Error( + "SyncRpcChannel: could not obtain pipe file descriptors.", + ); + } + + // Set the pipe handles to blocking mode. Under node --test's + // process isolation, pipes are created in non-blocking mode + // (for the IPC channel). This causes readSync/writeSync to get + // EAGAIN, requiring costly 1ms sleeps per retry. Setting + // blocking mode ensures readSync blocks properly until data + // arrives, matching the behavior of the native libsyncrpc. + stdout._handle.setBlocking?.(true); + stdin._handle.setBlocking?.(true); + + // Prevent Node's event-loop from reading stdout or keeping the + // process alive – we will use fs.readSync exclusively. + stdout.pause(); + stdout.unref(); + stdin.unref(); + } + + // Track for auto-cleanup on process exit. + liveChildren.add(this.child); + this.child.unref(); + } + + // ── Public API ────────────────────────────────────────────────── + + /** + * Send a request and synchronously wait for the response (string). + * Handles Call (callback) messages from the child inline. + */ + requestSync(method: string, payload: string): string { + this.ensureOpen(); + const result = this.requestBytesSync(method, payload); + return result.toString("utf-8"); + } + + /** + * Send a request and synchronously wait for the response (binary). + * Handles Call (callback) messages from the child inline. + */ + requestBinarySync(method: string, payload: Uint8Array): Uint8Array { + this.ensureOpen(); + return this.requestBytesSync(method, payload); + } + + /** Register a string→string callback that the child may invoke. */ + registerCallback(name: string, callback: (name: string, payload: string) => string): void { + this.callbacks.set(name, callback); + } + + /** Kill the child process and release resources. */ + close(): void { + try { + liveChildren.delete(this.child); + if (this.pipeFd !== undefined) { + closeSync(this.pipeFd); + this.pipeFd = undefined; + } + // Destroy the stdio streams so that their pipe handles are closed + // and no longer prevent the event loop from draining. + this.child.stdout?.destroy(); + this.child.stdin?.destroy(); + this.child.kill(); + this.readFd = -1; + this.writeFd = -1; + } + catch { + // swallow – process may already be dead + } + } + + // ── Core request loop ─────────────────────────────────────────── + + private ensureOpen(): void { + if (this.readFd < 0) { + throw new Error("SyncRpcChannel is closed"); + } + } + + private getMethodBuf(method: string): Buffer { + let buf = this.methodBufCache.get(method); + if (buf === undefined) { + buf = Buffer.from(method, "utf-8"); + this.methodBufCache.set(method, buf); + } + return buf; + } + + private requestBytesSync(method: string, payload: Buffer | Uint8Array | string): Buffer { + const methodBuf = this.getMethodBuf(method); + if (this.collectTiming) { + this.lastBytesSent = typeof payload === "string" + ? Buffer.byteLength(payload, "utf-8") + : payload.length; + this.lastBytesReceived = 0; + } + this.writeTuple(MSG_REQUEST, methodBuf, payload); + + for (;;) { + this.readTuple(); + + switch (this._msgType) { + case MSG_RESPONSE: { + // Compare raw bytes instead of decoding to string. + if (!methodBuf.equals(this._msgName)) { + throw new Error( + `name mismatch for response: expected \`${method}\`, got \`${this._msgName.toString("utf-8")}\``, + ); + } + if (this.collectTiming) { + this.lastBytesReceived = this._msgPayload.length; + } + return this._msgPayload; + } + case MSG_ERROR: { + if (methodBuf.equals(this._msgName)) { + throw new Error(this._msgPayload.toString("utf-8")); + } + throw new Error( + `name mismatch for response: expected \`${method}\`, got \`${this._msgName.toString("utf-8")}\``, + ); + } + case MSG_CALL: { + this.handleCall(this._msgName.toString("utf-8"), this._msgPayload); + break; + } + default: + throw new Error(`Invalid message type from child: ${this._msgType}`); + } + } + } + + // ── Callback handling ─────────────────────────────────────────── + + /** + * Handle an incoming MSG_CALL from the child process. + * + * After sending the error response back to the child, this method + * intentionally re-throws to abort the caller's request loop. + * A failed callback is treated as unrecoverable to match the + * behavior of the native libsyncrpc addon. + */ + private handleCall(name: string, payload: Buffer): void { + const cb = this.callbacks.get(name); + if (!cb) { + const errMsg = `unknown callback: \`${name}\`. Please make sure to register it on the JavaScript side before invoking it.`; + this.writeTuple( + MSG_CALL_ERROR, + Buffer.from(name, "utf-8"), + Buffer.from(errMsg, "utf-8"), + ); + throw new Error(`no callback named \`${name}\` found`); + } + + try { + const result = cb(name, payload.toString("utf-8")); + this.writeTuple( + MSG_CALL_RESPONSE, + Buffer.from(name, "utf-8"), + Buffer.from(result, "utf-8"), + ); + } + catch (e: unknown) { + const errMsg = String(e instanceof Error ? e.message : e).trim(); + this.writeTuple( + MSG_CALL_ERROR, + Buffer.from(name, "utf-8"), + Buffer.from(errMsg, "utf-8"), + ); + throw new Error(`Error calling callback \`${name}\`: ${errMsg}`); + } + } + + // ── MessagePack tuple write ───────────────────────────────────── + + /** + * Write a complete [type, name, payload] tuple in as few writeSync + * calls as possible. For messages that fit in the pre-allocated + * write buffer (64 KB), everything is assembled and sent in a single + * syscall. Larger messages use two syscalls: one for the header + * portion and one for the payload data. + */ + private writeTuple(type: number, name: Buffer, payload: Buffer | Uint8Array | string): void { + const nameLen = name.length; + const payloadIsString = typeof payload === "string"; + const payloadLen = payloadIsString ? Buffer.byteLength(payload, "utf-8") : payload.length; + const nameHdrSize = binHeaderSize(nameLen); + const payloadHdrSize = binHeaderSize(payloadLen); + const headerSize = 2 + nameHdrSize + nameLen + payloadHdrSize; + const totalSize = headerSize + payloadLen; + + if (totalSize <= this.writeBuf.length) { + // Small message: assemble into write buffer, one syscall + let off = 0; + this.writeBuf[off++] = MSGPACK_FIXARRAY3; + this.writeBuf[off++] = type; + off = writeBinHeader(this.writeBuf, off, nameLen); + name.copy(this.writeBuf, off); + off += nameLen; + off = writeBinHeader(this.writeBuf, off, payloadLen); + if (payloadLen > 0) { + if (payloadIsString) { + // Encode string directly into write buffer — avoids + // Buffer.from(string, 'utf-8') allocation entirely. + this.writeBuf.write(payload as string, off, payloadLen, "utf-8"); + } + else if (payload instanceof Buffer) { + (payload as Buffer).copy(this.writeBuf, off); + } + else { + this.writeBuf.set(payload as Uint8Array, off); + } + } + this.writeAllBuf(this.writeBuf, totalSize); + } + else { + // Large message: header + name in one call, payload in another + let off = 0; + this.writeBuf[off++] = MSGPACK_FIXARRAY3; + this.writeBuf[off++] = type; + off = writeBinHeader(this.writeBuf, off, nameLen); + name.copy(this.writeBuf, off); + off += nameLen; + off = writeBinHeader(this.writeBuf, off, payloadLen); + this.writeAllBuf(this.writeBuf, off); + if (payloadLen > 0) { + if (payloadIsString) { + // Large string: must allocate (can't stream-encode + // across multiple writeSync calls). + this.writeAllBuf(Buffer.from(payload as string, "utf-8")); + } + else { + this.writeAllBuf(payload as Buffer | Uint8Array); + } + } + } + } + + // ── MessagePack tuple read ────────────────────────────────────── + + /** + * Read a [type, name, payload] tuple into instance fields + * (_msgType, _msgName, _msgPayload) to avoid allocating a + * short-lived 3-element array on every call. + */ + private readTuple(): void { + // Fixed 3-element array marker + const marker = this.readByte(); + if (marker !== MSGPACK_FIXARRAY3) { + throw new Error( + `Expected fixed 3-element array (0x93), received: 0x${marker.toString(16)}`, + ); + } + + // Message type – positive fixint or uint8 + const tb = this.readByte(); + if (tb <= 0x7f) { + this._msgType = tb; + } + else if (tb === MSGPACK_UINT8) { + this._msgType = this.readByte(); + } + else { + throw new Error( + `Expected positive fixint or uint8 marker, received: 0x${tb.toString(16)}`, + ); + } + + this._msgName = this.readBin(); + this._msgPayload = this.readBin(); + } + + /** + * Read a MessagePack bin field. + */ + private readBin(): Buffer { + const marker = this.readByte(); + let size: number; + switch (marker) { + case MSGPACK_BIN8: + size = this.readByte(); + break; + case MSGPACK_BIN16: + this.readExactInto(this.headerBuf, 2); + size = (this.headerBuf[0] << 8) | this.headerBuf[1]; + break; + case MSGPACK_BIN32: + this.readExactInto(this.headerBuf, 4); + size = this.headerBuf.readUInt32BE(0); + break; + default: + throw new Error( + `Expected binary data (0xc4-0xc6), received: 0x${marker.toString(16)}`, + ); + } + if (size === 0) return EMPTY_BUF; + return this.readExact(size); + } + + // ── Low-level synchronous I/O ─────────────────────────────────── + + /** Build an EOF error with the child's exit code/signal if available. */ + private eofError(): Error { + const code = this.child.exitCode; + const signal = this.child.signalCode; + const detail = signal ? `killed by signal ${signal}` : code !== null ? `exited with code ${code}` : "unknown reason"; + return new Error(`Unexpected EOF while reading from child process (${detail})`); + } + + /** Read a single byte from the buffered read-ahead. */ + private readByte(): number { + if (this.readBufPos >= this.readBufLen) { + this.fillReadBuffer(); + } + return this.readBuf[this.readBufPos++]; + } + + private readExact(length: number): Buffer { + // Use allocUnsafeSlow (not allocUnsafe) so the buffer has its own + // backing ArrayBuffer at byteOffset 0. This is critical because + // callers such as RemoteSourceFile create DataView/Uint8Array over + // buffer.buffer with absolute offsets. Buffer.allocUnsafe returns + // slices of a shared pool whose byteOffset is non-zero, corrupting + // those downstream views. + // + // allocUnsafeSlow (vs alloc) skips zero-fill, which matters for + // large transfers — e.g. checker.ts at ~57 MB saves ~5 ms of + // unnecessary memset. The buffer is immediately filled by + // readExactInto so uninitialized memory is never exposed. + const buf = Buffer.allocUnsafeSlow(length); + this.readExactInto(buf, length); + return buf; + } + + /** + * Fill the internal read-ahead buffer from the pipe fd. + * Retries on EAGAIN for non-blocking mode compatibility. + */ + private fillReadBuffer(): void { + this.readBufPos = 0; + this.readBufLen = 0; + for (;;) { + try { + const n = readSync(this.readFd, this.readBuf, 0, this.readBuf.length, null); + if (n === 0) { + throw this.eofError(); + } + this.readBufLen = n; + return; + } + catch (e: unknown) { + if (e instanceof Error && ("code" in e) && ((e as NodeJS.ErrnoException).code === "EAGAIN" || (e as NodeJS.ErrnoException).code === "EWOULDBLOCK")) { + Atomics.wait(sleepBuf, 0, 0, 1); + continue; + } + throw e; + } + } + } + + /** + * Synchronously read exactly `length` bytes into `buffer`. + * Serves from the internal read-ahead buffer first; for large reads + * that exceed the buffer size, reads directly from the fd to avoid + * an extra copy. + */ + private readExactInto(buffer: Buffer, length: number): void { + let pos = 0; + while (pos < length) { + const avail = this.readBufLen - this.readBufPos; + if (avail > 0) { + // Serve from read-ahead buffer + const toCopy = Math.min(avail, length - pos); + this.readBuf.copy(buffer, pos, this.readBufPos, this.readBufPos + toCopy); + this.readBufPos += toCopy; + pos += toCopy; + } + else if (length - pos >= this.readBuf.length) { + // Remaining data is larger than read buffer; read directly + // into the target to avoid unnecessary copying. + try { + const n = readSync(this.readFd, buffer, pos, length - pos, null); + if (n === 0) { + throw this.eofError(); + } + pos += n; + } + catch (e: unknown) { + if (e instanceof Error && ("code" in e) && ((e as NodeJS.ErrnoException).code === "EAGAIN" || (e as NodeJS.ErrnoException).code === "EWOULDBLOCK")) { + Atomics.wait(sleepBuf, 0, 0, 1); + continue; + } + throw e; + } + } + else { + // Refill the read-ahead buffer + this.fillReadBuffer(); + } + } + } + + /** + * Synchronously write all bytes from `data` (up to `length`). + * Retries on EAGAIN. + */ + private writeAllBuf(data: Buffer | Uint8Array, length?: number): void { + const total = length ?? data.length; + let pos = 0; + while (pos < total) { + try { + const n = writeSync(this.writeFd, data, pos, total - pos); + pos += n; + } + catch (e: unknown) { + if (e instanceof Error && ("code" in e) && ((e as NodeJS.ErrnoException).code === "EAGAIN" || (e as NodeJS.ErrnoException).code === "EWOULDBLOCK")) { + Atomics.wait(sleepBuf, 0, 0, 1); + continue; + } + throw e; + } + } + } +} diff --git a/packages/typescript/src/api/timing.ts b/packages/typescript/src/api/timing.ts index 41eba1998a8c6..c280cbdf9b78a 100644 --- a/packages/typescript/src/api/timing.ts +++ b/packages/typescript/src/api/timing.ts @@ -1,288 +1,288 @@ -/** - * Client-side collection of per-request timing and transfer measurements. - * - * When enabled, each request records its round-trip latency and the number of - * payload bytes sent and received, accumulated into running totals and a - * fixed-size ring buffer of the most recent requests. - * - * The server measures its own per-request processing time independently. When a - * timing snapshot is requested, the client fetches the server's collection via - * a `getServerTiming` request and folds it into the returned {@link TimingInfo}, - * yielding per-request and total server processing time and an estimated - * transport overhead (round-trip minus server processing time). Normal response - * messages are left unchanged. - */ - -/** Number of most-recent requests retained in the ring buffer. */ -export const RECENT_REQUEST_CAPACITY = 5; - -/** A single request's measured timing and transfer sample. */ -export interface RequestTiming { - /** The API method that was invoked. */ - method: string; - /** Wall-clock round-trip time measured by the client, in milliseconds. */ - roundTripMs: number; - /** Number of request payload bytes sent to the server. */ - bytesSent: number; - /** Number of response payload bytes received from the server. */ - bytesReceived: number; - /** Wall-clock timestamp ({@link Date.now}) captured when the request completed. */ - timestamp: number; - /** - * Server-side processing time for this request, in milliseconds, as folded - * in from the server's own timing collection. Undefined when server timing - * for the request could not be matched. - */ - serverTimeMs?: number; - /** - * Estimated transport overhead for this request, in milliseconds - * (`roundTripMs - serverTimeMs`, clamped to be non-negative). Present - * exactly when {@link serverTimeMs} is. - */ - transportOverheadMs?: number; -} - -/** Running totals accumulated across every measured request. */ -export interface TimingAccumulators { - /** Number of requests measured. */ - requestCount: number; - /** Sum of round-trip latencies, in milliseconds. */ - roundTripMs: number; - /** Sum of request payload bytes sent. */ - bytesSent: number; - /** Sum of response payload bytes received. */ - bytesReceived: number; - /** Sum of server-side processing time, in milliseconds. */ - serverTimeMs: number; - /** - * Estimated total transport overhead, in milliseconds - * (`roundTripMs - serverTimeMs`, clamped to be non-negative). - */ - transportOverheadMs: number; - /** - * Number of AST nodes materialized from binary source-file responses as the - * client walked the returned trees. Materialization is lazy and happens on - * demand, so this accrues after the originating request completes. - */ - nodesMaterialized: number; - /** - * Number of source files fetched from the server (each decoded into a - * lazily-materialized tree). - */ - sourceFilesFetched: number; - /** - * Number of AST nodes across all fetched source files that can be - * materialized on demand. Each fetched file contributes its full node count - * (excluding the pre-materialized source-file node), whether or not those - * nodes are ever walked. Serves as the denominator for the share of fetched - * nodes that end up materialized (`nodesMaterialized / nodesFetched`). - */ - nodesFetched: number; -} - -/** A point-in-time snapshot of collected timing information. */ -export interface TimingInfo { - /** Whether timing collection is enabled for this API instance. */ - enabled: boolean; - /** Running totals across every measured request. */ - totals: TimingAccumulators; - /** - * The most recent requests, up to {@link RECENT_REQUEST_CAPACITY}, ordered - * from oldest to newest. - */ - recentRequests: RequestTiming[]; -} - -/** A raw measurement handed to {@link TimingCollector.record}. */ -export interface TimingSample { - method: string; - roundTripMs: number; - bytesSent: number; - bytesReceived: number; -} - -/** - * A single server-side request's processing-time sample, as returned by a - * `getServerTiming` request. This is an internal wire shape; consumers see the - * folded-in {@link RequestTiming.serverTimeMs}. - */ -export interface ServerRequestTiming { - /** The API method that was handled. */ - method: string; - /** Server-side processing time, in milliseconds. */ - processingTimeMs: number; - /** Unix timestamp in milliseconds captured when the request completed. */ - timestamp: number; -} - -/** Running totals accumulated on the server across every handled request. */ -export interface ServerTimingTotals { - /** Total number of requests handled. */ - requestCount: number; - /** Sum of server-side processing time, in milliseconds. */ - totalProcessingTimeMs: number; -} - -/** - * A snapshot of the server's own timing collection, retrieved via a - * `getServerTiming` request. This is an internal wire shape used to compute the - * server-derived fields of {@link TimingInfo}. - */ -export interface ServerTimingInfo { - /** Whether server-side timing collection is enabled. */ - enabled: boolean; - /** Running totals across every request the server handled. */ - totals: ServerTimingTotals; - /** - * The most recent requests as seen by the server, ordered from oldest to - * newest. - */ - recentRequests: ServerRequestTiming[]; -} - -function emptyAccumulators(): TimingAccumulators { - return { - requestCount: 0, - roundTripMs: 0, - bytesSent: 0, - bytesReceived: 0, - serverTimeMs: 0, - transportOverheadMs: 0, - nodesMaterialized: 0, - sourceFilesFetched: 0, - nodesFetched: 0, - }; -} - -/** Returns a snapshot representing a disabled (never-collecting) timing state. */ -export function disabledTimingInfo(): TimingInfo { - return { - enabled: false, - totals: emptyAccumulators(), - recentRequests: [], - }; -} - -/** Returns a snapshot representing disabled server-side timing collection. */ -export function disabledServerTimingInfo(): ServerTimingInfo { - return { - enabled: false, - totals: { requestCount: 0, totalProcessingTimeMs: 0 }, - recentRequests: [], - }; -} - -/** - * Folds a server-side timing snapshot into a client-side snapshot, producing a - * combined {@link TimingInfo} with per-request and total server processing time - * plus estimated transport overhead. - * - * Recent requests are paired newest-to-newest and only matched when the method - * names agree, so that requests recorded by only one side (e.g. the meta - * requests used to fetch timing) do not misalign the two ring buffers. - */ -export function combineTimingInfo(client: TimingInfo, server: ServerTimingInfo): TimingInfo { - if (!client.enabled) { - return client; - } - - const serverTimeMs = server.totals.totalProcessingTimeMs; - const totals: TimingAccumulators = { - ...client.totals, - serverTimeMs, - transportOverheadMs: Math.max(0, client.totals.roundTripMs - serverTimeMs), - }; - - const recentRequests = client.recentRequests.map(r => ({ ...r })); - const serverRecent = server.recentRequests; - const pairs = Math.min(recentRequests.length, serverRecent.length); - for (let i = 1; i <= pairs; i++) { - const c = recentRequests[recentRequests.length - i]; - const s = serverRecent[serverRecent.length - i]; - if (c.method === s.method) { - c.serverTimeMs = s.processingTimeMs; - c.transportOverheadMs = Math.max(0, c.roundTripMs - s.processingTimeMs); - } - } - - return { - enabled: true, - totals, - recentRequests, - }; -} - -/** - * Accumulates request timing samples into running totals and a fixed-size ring - * buffer of the most recent requests. - */ -export class TimingCollector { - private totals: TimingAccumulators = emptyAccumulators(); - // Ring buffer of the most recent requests. `ring` grows to at most - // RECENT_REQUEST_CAPACITY; once full, `head` marks the oldest entry. - private ring: RequestTiming[] = []; - private head = 0; - - /** Records a single request's measurements. */ - record(sample: TimingSample): void { - this.totals.requestCount++; - this.totals.roundTripMs += sample.roundTripMs; - this.totals.bytesSent += sample.bytesSent; - this.totals.bytesReceived += sample.bytesReceived; - - const entry: RequestTiming = { - method: sample.method, - roundTripMs: sample.roundTripMs, - bytesSent: sample.bytesSent, - bytesReceived: sample.bytesReceived, - timestamp: Date.now(), - }; - - if (this.ring.length < RECENT_REQUEST_CAPACITY) { - this.ring.push(entry); - } - else { - this.ring[this.head] = entry; - this.head = (this.head + 1) % RECENT_REQUEST_CAPACITY; - } - } - - /** - * Records a single AST node materialization. Called on demand as the consumer - * walks a binary source-file response's tree, so it is not tied to any one - * request. - */ - recordMaterialization(): void { - this.totals.nodesMaterialized++; - } - - /** - * Records a fetched source file: increments the fetched-file counter and adds - * the file's materializable node count to the fetched-node total, which serves - * as the denominator for the share of fetched nodes that end up materialized. - */ - recordSourceFileFetched(materializableNodeCount: number): void { - this.totals.sourceFilesFetched++; - this.totals.nodesFetched += materializableNodeCount; - } - - /** Returns a snapshot of the collected timing information. */ - getInfo(): TimingInfo { - const recentRequests: RequestTiming[] = []; - for (let i = 0; i < this.ring.length; i++) { - recentRequests.push(this.ring[(this.head + i) % this.ring.length]); - } - return { - enabled: true, - totals: { ...this.totals }, - recentRequests, - }; - } - - /** Clears all accumulated totals and recent-request history. */ - reset(): void { - this.totals = emptyAccumulators(); - this.ring = []; - this.head = 0; - } -} +/** + * Client-side collection of per-request timing and transfer measurements. + * + * When enabled, each request records its round-trip latency and the number of + * payload bytes sent and received, accumulated into running totals and a + * fixed-size ring buffer of the most recent requests. + * + * The server measures its own per-request processing time independently. When a + * timing snapshot is requested, the client fetches the server's collection via + * a `getServerTiming` request and folds it into the returned {@link TimingInfo}, + * yielding per-request and total server processing time and an estimated + * transport overhead (round-trip minus server processing time). Normal response + * messages are left unchanged. + */ + +/** Number of most-recent requests retained in the ring buffer. */ +export const RECENT_REQUEST_CAPACITY = 5; + +/** A single request's measured timing and transfer sample. */ +export interface RequestTiming { + /** The API method that was invoked. */ + method: string; + /** Wall-clock round-trip time measured by the client, in milliseconds. */ + roundTripMs: number; + /** Number of request payload bytes sent to the server. */ + bytesSent: number; + /** Number of response payload bytes received from the server. */ + bytesReceived: number; + /** Wall-clock timestamp ({@link Date.now}) captured when the request completed. */ + timestamp: number; + /** + * Server-side processing time for this request, in milliseconds, as folded + * in from the server's own timing collection. Undefined when server timing + * for the request could not be matched. + */ + serverTimeMs?: number; + /** + * Estimated transport overhead for this request, in milliseconds + * (`roundTripMs - serverTimeMs`, clamped to be non-negative). Present + * exactly when {@link serverTimeMs} is. + */ + transportOverheadMs?: number; +} + +/** Running totals accumulated across every measured request. */ +export interface TimingAccumulators { + /** Number of requests measured. */ + requestCount: number; + /** Sum of round-trip latencies, in milliseconds. */ + roundTripMs: number; + /** Sum of request payload bytes sent. */ + bytesSent: number; + /** Sum of response payload bytes received. */ + bytesReceived: number; + /** Sum of server-side processing time, in milliseconds. */ + serverTimeMs: number; + /** + * Estimated total transport overhead, in milliseconds + * (`roundTripMs - serverTimeMs`, clamped to be non-negative). + */ + transportOverheadMs: number; + /** + * Number of AST nodes materialized from binary source-file responses as the + * client walked the returned trees. Materialization is lazy and happens on + * demand, so this accrues after the originating request completes. + */ + nodesMaterialized: number; + /** + * Number of source files fetched from the server (each decoded into a + * lazily-materialized tree). + */ + sourceFilesFetched: number; + /** + * Number of AST nodes across all fetched source files that can be + * materialized on demand. Each fetched file contributes its full node count + * (excluding the pre-materialized source-file node), whether or not those + * nodes are ever walked. Serves as the denominator for the share of fetched + * nodes that end up materialized (`nodesMaterialized / nodesFetched`). + */ + nodesFetched: number; +} + +/** A point-in-time snapshot of collected timing information. */ +export interface TimingInfo { + /** Whether timing collection is enabled for this API instance. */ + enabled: boolean; + /** Running totals across every measured request. */ + totals: TimingAccumulators; + /** + * The most recent requests, up to {@link RECENT_REQUEST_CAPACITY}, ordered + * from oldest to newest. + */ + recentRequests: RequestTiming[]; +} + +/** A raw measurement handed to {@link TimingCollector.record}. */ +export interface TimingSample { + method: string; + roundTripMs: number; + bytesSent: number; + bytesReceived: number; +} + +/** + * A single server-side request's processing-time sample, as returned by a + * `getServerTiming` request. This is an internal wire shape; consumers see the + * folded-in {@link RequestTiming.serverTimeMs}. + */ +export interface ServerRequestTiming { + /** The API method that was handled. */ + method: string; + /** Server-side processing time, in milliseconds. */ + processingTimeMs: number; + /** Unix timestamp in milliseconds captured when the request completed. */ + timestamp: number; +} + +/** Running totals accumulated on the server across every handled request. */ +export interface ServerTimingTotals { + /** Total number of requests handled. */ + requestCount: number; + /** Sum of server-side processing time, in milliseconds. */ + totalProcessingTimeMs: number; +} + +/** + * A snapshot of the server's own timing collection, retrieved via a + * `getServerTiming` request. This is an internal wire shape used to compute the + * server-derived fields of {@link TimingInfo}. + */ +export interface ServerTimingInfo { + /** Whether server-side timing collection is enabled. */ + enabled: boolean; + /** Running totals across every request the server handled. */ + totals: ServerTimingTotals; + /** + * The most recent requests as seen by the server, ordered from oldest to + * newest. + */ + recentRequests: ServerRequestTiming[]; +} + +function emptyAccumulators(): TimingAccumulators { + return { + requestCount: 0, + roundTripMs: 0, + bytesSent: 0, + bytesReceived: 0, + serverTimeMs: 0, + transportOverheadMs: 0, + nodesMaterialized: 0, + sourceFilesFetched: 0, + nodesFetched: 0, + }; +} + +/** Returns a snapshot representing a disabled (never-collecting) timing state. */ +export function disabledTimingInfo(): TimingInfo { + return { + enabled: false, + totals: emptyAccumulators(), + recentRequests: [], + }; +} + +/** Returns a snapshot representing disabled server-side timing collection. */ +export function disabledServerTimingInfo(): ServerTimingInfo { + return { + enabled: false, + totals: { requestCount: 0, totalProcessingTimeMs: 0 }, + recentRequests: [], + }; +} + +/** + * Folds a server-side timing snapshot into a client-side snapshot, producing a + * combined {@link TimingInfo} with per-request and total server processing time + * plus estimated transport overhead. + * + * Recent requests are paired newest-to-newest and only matched when the method + * names agree, so that requests recorded by only one side (e.g. the meta + * requests used to fetch timing) do not misalign the two ring buffers. + */ +export function combineTimingInfo(client: TimingInfo, server: ServerTimingInfo): TimingInfo { + if (!client.enabled) { + return client; + } + + const serverTimeMs = server.totals.totalProcessingTimeMs; + const totals: TimingAccumulators = { + ...client.totals, + serverTimeMs, + transportOverheadMs: Math.max(0, client.totals.roundTripMs - serverTimeMs), + }; + + const recentRequests = client.recentRequests.map(r => ({ ...r })); + const serverRecent = server.recentRequests; + const pairs = Math.min(recentRequests.length, serverRecent.length); + for (let i = 1; i <= pairs; i++) { + const c = recentRequests[recentRequests.length - i]; + const s = serverRecent[serverRecent.length - i]; + if (c.method === s.method) { + c.serverTimeMs = s.processingTimeMs; + c.transportOverheadMs = Math.max(0, c.roundTripMs - s.processingTimeMs); + } + } + + return { + enabled: true, + totals, + recentRequests, + }; +} + +/** + * Accumulates request timing samples into running totals and a fixed-size ring + * buffer of the most recent requests. + */ +export class TimingCollector { + private totals: TimingAccumulators = emptyAccumulators(); + // Ring buffer of the most recent requests. `ring` grows to at most + // RECENT_REQUEST_CAPACITY; once full, `head` marks the oldest entry. + private ring: RequestTiming[] = []; + private head = 0; + + /** Records a single request's measurements. */ + record(sample: TimingSample): void { + this.totals.requestCount++; + this.totals.roundTripMs += sample.roundTripMs; + this.totals.bytesSent += sample.bytesSent; + this.totals.bytesReceived += sample.bytesReceived; + + const entry: RequestTiming = { + method: sample.method, + roundTripMs: sample.roundTripMs, + bytesSent: sample.bytesSent, + bytesReceived: sample.bytesReceived, + timestamp: Date.now(), + }; + + if (this.ring.length < RECENT_REQUEST_CAPACITY) { + this.ring.push(entry); + } + else { + this.ring[this.head] = entry; + this.head = (this.head + 1) % RECENT_REQUEST_CAPACITY; + } + } + + /** + * Records a single AST node materialization. Called on demand as the consumer + * walks a binary source-file response's tree, so it is not tied to any one + * request. + */ + recordMaterialization(): void { + this.totals.nodesMaterialized++; + } + + /** + * Records a fetched source file: increments the fetched-file counter and adds + * the file's materializable node count to the fetched-node total, which serves + * as the denominator for the share of fetched nodes that end up materialized. + */ + recordSourceFileFetched(materializableNodeCount: number): void { + this.totals.sourceFilesFetched++; + this.totals.nodesFetched += materializableNodeCount; + } + + /** Returns a snapshot of the collected timing information. */ + getInfo(): TimingInfo { + const recentRequests: RequestTiming[] = []; + for (let i = 0; i < this.ring.length; i++) { + recentRequests.push(this.ring[(this.head + i) % this.ring.length]); + } + return { + enabled: true, + totals: { ...this.totals }, + recentRequests, + }; + } + + /** Clears all accumulated totals and recent-request history. */ + reset(): void { + this.totals = emptyAccumulators(); + this.ring = []; + this.head = 0; + } +} diff --git a/packages/typescript/src/ast/ast.generated.ts b/packages/typescript/src/ast/ast.generated.ts index 76ddfe362ae96..5b9534d7bf66b 100644 --- a/packages/typescript/src/ast/ast.generated.ts +++ b/packages/typescript/src/ast/ast.generated.ts @@ -1,1524 +1,1524 @@ -// Code generated by tools/scripts/tsc/generate-ts-ast.ts. DO NOT EDIT. - -import type { ModifierFlags } from "#enums/modifierFlags"; -import type { NodeFlags } from "#enums/nodeFlags"; -import { SyntaxKind } from "#enums/syntaxKind"; -import { TokenFlags } from "#enums/tokenFlags"; -import type { - JsxTagNamePropertyAccess, - Node, - NodeArray, -} from "./ast.ts"; -export type TriviaSyntaxKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia | SyntaxKind.NewLineTrivia | SyntaxKind.WhitespaceTrivia | SyntaxKind.ConflictMarkerTrivia; -export type LiteralSyntaxKind = SyntaxKind.NumericLiteral | SyntaxKind.BigIntLiteral | SyntaxKind.StringLiteral | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.RegularExpressionLiteral | SyntaxKind.NoSubstitutionTemplateLiteral; -export type PseudoLiteralSyntaxKind = SyntaxKind.TemplateHead | SyntaxKind.TemplateMiddle | SyntaxKind.TemplateTail; -export type PunctuationSyntaxKind = - | SyntaxKind.OpenBraceToken - | SyntaxKind.CloseBraceToken - | SyntaxKind.OpenParenToken - | SyntaxKind.CloseParenToken - | SyntaxKind.OpenBracketToken - | SyntaxKind.CloseBracketToken - | SyntaxKind.DotToken - | SyntaxKind.DotDotDotToken - | SyntaxKind.SemicolonToken - | SyntaxKind.CommaToken - | SyntaxKind.QuestionDotToken - | SyntaxKind.LessThanToken - | SyntaxKind.LessThanSlashToken - | SyntaxKind.GreaterThanToken - | SyntaxKind.LessThanEqualsToken - | SyntaxKind.GreaterThanEqualsToken - | SyntaxKind.EqualsEqualsToken - | SyntaxKind.ExclamationEqualsToken - | SyntaxKind.EqualsEqualsEqualsToken - | SyntaxKind.ExclamationEqualsEqualsToken - | SyntaxKind.EqualsGreaterThanToken - | SyntaxKind.PlusToken - | SyntaxKind.MinusToken - | SyntaxKind.AsteriskToken - | SyntaxKind.AsteriskAsteriskToken - | SyntaxKind.SlashToken - | SyntaxKind.PercentToken - | SyntaxKind.PlusPlusToken - | SyntaxKind.MinusMinusToken - | SyntaxKind.LessThanLessThanToken - | SyntaxKind.GreaterThanGreaterThanToken - | SyntaxKind.GreaterThanGreaterThanGreaterThanToken - | SyntaxKind.AmpersandToken - | SyntaxKind.BarToken - | SyntaxKind.CaretToken - | SyntaxKind.ExclamationToken - | SyntaxKind.TildeToken - | SyntaxKind.AmpersandAmpersandToken - | SyntaxKind.BarBarToken - | SyntaxKind.QuestionToken - | SyntaxKind.ColonToken - | SyntaxKind.AtToken - | SyntaxKind.QuestionQuestionToken - | SyntaxKind.BacktickToken - | SyntaxKind.HashToken - | SyntaxKind.EqualsToken - | SyntaxKind.PlusEqualsToken - | SyntaxKind.MinusEqualsToken - | SyntaxKind.AsteriskEqualsToken - | SyntaxKind.AsteriskAsteriskEqualsToken - | SyntaxKind.SlashEqualsToken - | SyntaxKind.PercentEqualsToken - | SyntaxKind.LessThanLessThanEqualsToken - | SyntaxKind.GreaterThanGreaterThanEqualsToken - | SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken - | SyntaxKind.AmpersandEqualsToken - | SyntaxKind.BarEqualsToken - | SyntaxKind.BarBarEqualsToken - | SyntaxKind.AmpersandAmpersandEqualsToken - | SyntaxKind.QuestionQuestionEqualsToken - | SyntaxKind.CaretEqualsToken; -export type KeywordSyntaxKind = - | SyntaxKind.BreakKeyword - | SyntaxKind.CaseKeyword - | SyntaxKind.CatchKeyword - | SyntaxKind.ClassKeyword - | SyntaxKind.ConstKeyword - | SyntaxKind.ContinueKeyword - | SyntaxKind.DebuggerKeyword - | SyntaxKind.DefaultKeyword - | SyntaxKind.DeleteKeyword - | SyntaxKind.DoKeyword - | SyntaxKind.ElseKeyword - | SyntaxKind.EnumKeyword - | SyntaxKind.ExportKeyword - | SyntaxKind.ExtendsKeyword - | SyntaxKind.FalseKeyword - | SyntaxKind.FinallyKeyword - | SyntaxKind.ForKeyword - | SyntaxKind.FunctionKeyword - | SyntaxKind.IfKeyword - | SyntaxKind.ImportKeyword - | SyntaxKind.InKeyword - | SyntaxKind.InstanceOfKeyword - | SyntaxKind.NewKeyword - | SyntaxKind.NullKeyword - | SyntaxKind.ReturnKeyword - | SyntaxKind.SuperKeyword - | SyntaxKind.SwitchKeyword - | SyntaxKind.ThisKeyword - | SyntaxKind.ThrowKeyword - | SyntaxKind.TrueKeyword - | SyntaxKind.TryKeyword - | SyntaxKind.TypeOfKeyword - | SyntaxKind.VarKeyword - | SyntaxKind.VoidKeyword - | SyntaxKind.WhileKeyword - | SyntaxKind.WithKeyword - | SyntaxKind.ImplementsKeyword - | SyntaxKind.InterfaceKeyword - | SyntaxKind.LetKeyword - | SyntaxKind.PackageKeyword - | SyntaxKind.PrivateKeyword - | SyntaxKind.ProtectedKeyword - | SyntaxKind.PublicKeyword - | SyntaxKind.StaticKeyword - | SyntaxKind.YieldKeyword - | SyntaxKind.AbstractKeyword - | SyntaxKind.AccessorKeyword - | SyntaxKind.AsKeyword - | SyntaxKind.AssertsKeyword - | SyntaxKind.AssertKeyword - | SyntaxKind.AnyKeyword - | SyntaxKind.AsyncKeyword - | SyntaxKind.AwaitKeyword - | SyntaxKind.BooleanKeyword - | SyntaxKind.ConstructorKeyword - | SyntaxKind.DeclareKeyword - | SyntaxKind.GetKeyword - | SyntaxKind.ImmediateKeyword - | SyntaxKind.InferKeyword - | SyntaxKind.IntrinsicKeyword - | SyntaxKind.IsKeyword - | SyntaxKind.KeyOfKeyword - | SyntaxKind.ModuleKeyword - | SyntaxKind.NamespaceKeyword - | SyntaxKind.NeverKeyword - | SyntaxKind.OutKeyword - | SyntaxKind.ReadonlyKeyword - | SyntaxKind.RequireKeyword - | SyntaxKind.NumberKeyword - | SyntaxKind.ObjectKeyword - | SyntaxKind.SatisfiesKeyword - | SyntaxKind.SetKeyword - | SyntaxKind.StringKeyword - | SyntaxKind.SymbolKeyword - | SyntaxKind.TypeKeyword - | SyntaxKind.UndefinedKeyword - | SyntaxKind.UniqueKeyword - | SyntaxKind.UnknownKeyword - | SyntaxKind.UsingKeyword - | SyntaxKind.FromKeyword - | SyntaxKind.GlobalKeyword - | SyntaxKind.BigIntKeyword - | SyntaxKind.OverrideKeyword - | SyntaxKind.OfKeyword - | SyntaxKind.DeferKeyword; -export type ModifierSyntaxKind = SyntaxKind.AbstractKeyword | SyntaxKind.AccessorKeyword | SyntaxKind.AsyncKeyword | SyntaxKind.ConstKeyword | SyntaxKind.DeclareKeyword | SyntaxKind.DefaultKeyword | SyntaxKind.ExportKeyword | SyntaxKind.InKeyword | SyntaxKind.PrivateKeyword | SyntaxKind.ProtectedKeyword | SyntaxKind.PublicKeyword | SyntaxKind.ReadonlyKeyword | SyntaxKind.OutKeyword | SyntaxKind.OverrideKeyword | SyntaxKind.StaticKeyword; -export type KeywordTypeSyntaxKind = SyntaxKind.AnyKeyword | SyntaxKind.BigIntKeyword | SyntaxKind.BooleanKeyword | SyntaxKind.IntrinsicKeyword | SyntaxKind.NeverKeyword | SyntaxKind.NumberKeyword | SyntaxKind.ObjectKeyword | SyntaxKind.StringKeyword | SyntaxKind.SymbolKeyword | SyntaxKind.UndefinedKeyword | SyntaxKind.UnknownKeyword | SyntaxKind.VoidKeyword; -export type KeywordExpressionSyntaxKind = SyntaxKind.NullKeyword | SyntaxKind.TrueKeyword | SyntaxKind.FalseKeyword | SyntaxKind.ThisKeyword | SyntaxKind.SuperKeyword | SyntaxKind.ImportKeyword; -export type TokenSyntaxKind = - | SyntaxKind.Unknown - | SyntaxKind.EndOfFile - | SyntaxKind.SingleLineCommentTrivia - | SyntaxKind.MultiLineCommentTrivia - | SyntaxKind.NewLineTrivia - | SyntaxKind.WhitespaceTrivia - | SyntaxKind.ConflictMarkerTrivia - | SyntaxKind.NonTextFileMarkerTrivia - | SyntaxKind.NumericLiteral - | SyntaxKind.BigIntLiteral - | SyntaxKind.StringLiteral - | SyntaxKind.JsxText - | SyntaxKind.JsxTextAllWhiteSpaces - | SyntaxKind.RegularExpressionLiteral - | SyntaxKind.NoSubstitutionTemplateLiteral - | SyntaxKind.TemplateHead - | SyntaxKind.TemplateMiddle - | SyntaxKind.TemplateTail - | SyntaxKind.OpenBraceToken - | SyntaxKind.CloseBraceToken - | SyntaxKind.OpenParenToken - | SyntaxKind.CloseParenToken - | SyntaxKind.OpenBracketToken - | SyntaxKind.CloseBracketToken - | SyntaxKind.DotToken - | SyntaxKind.DotDotDotToken - | SyntaxKind.SemicolonToken - | SyntaxKind.CommaToken - | SyntaxKind.QuestionDotToken - | SyntaxKind.LessThanToken - | SyntaxKind.LessThanSlashToken - | SyntaxKind.GreaterThanToken - | SyntaxKind.LessThanEqualsToken - | SyntaxKind.GreaterThanEqualsToken - | SyntaxKind.EqualsEqualsToken - | SyntaxKind.ExclamationEqualsToken - | SyntaxKind.EqualsEqualsEqualsToken - | SyntaxKind.ExclamationEqualsEqualsToken - | SyntaxKind.EqualsGreaterThanToken - | SyntaxKind.PlusToken - | SyntaxKind.MinusToken - | SyntaxKind.AsteriskToken - | SyntaxKind.AsteriskAsteriskToken - | SyntaxKind.SlashToken - | SyntaxKind.PercentToken - | SyntaxKind.PlusPlusToken - | SyntaxKind.MinusMinusToken - | SyntaxKind.LessThanLessThanToken - | SyntaxKind.GreaterThanGreaterThanToken - | SyntaxKind.GreaterThanGreaterThanGreaterThanToken - | SyntaxKind.AmpersandToken - | SyntaxKind.BarToken - | SyntaxKind.CaretToken - | SyntaxKind.ExclamationToken - | SyntaxKind.TildeToken - | SyntaxKind.AmpersandAmpersandToken - | SyntaxKind.BarBarToken - | SyntaxKind.QuestionToken - | SyntaxKind.ColonToken - | SyntaxKind.AtToken - | SyntaxKind.QuestionQuestionToken - | SyntaxKind.BacktickToken - | SyntaxKind.HashToken - | SyntaxKind.EqualsToken - | SyntaxKind.PlusEqualsToken - | SyntaxKind.MinusEqualsToken - | SyntaxKind.AsteriskEqualsToken - | SyntaxKind.AsteriskAsteriskEqualsToken - | SyntaxKind.SlashEqualsToken - | SyntaxKind.PercentEqualsToken - | SyntaxKind.LessThanLessThanEqualsToken - | SyntaxKind.GreaterThanGreaterThanEqualsToken - | SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken - | SyntaxKind.AmpersandEqualsToken - | SyntaxKind.BarEqualsToken - | SyntaxKind.BarBarEqualsToken - | SyntaxKind.AmpersandAmpersandEqualsToken - | SyntaxKind.QuestionQuestionEqualsToken - | SyntaxKind.CaretEqualsToken - | SyntaxKind.Identifier - | SyntaxKind.PrivateIdentifier - | SyntaxKind.JSDocCommentTextToken - | SyntaxKind.BreakKeyword - | SyntaxKind.CaseKeyword - | SyntaxKind.CatchKeyword - | SyntaxKind.ClassKeyword - | SyntaxKind.ConstKeyword - | SyntaxKind.ContinueKeyword - | SyntaxKind.DebuggerKeyword - | SyntaxKind.DefaultKeyword - | SyntaxKind.DeleteKeyword - | SyntaxKind.DoKeyword - | SyntaxKind.ElseKeyword - | SyntaxKind.EnumKeyword - | SyntaxKind.ExportKeyword - | SyntaxKind.ExtendsKeyword - | SyntaxKind.FalseKeyword - | SyntaxKind.FinallyKeyword - | SyntaxKind.ForKeyword - | SyntaxKind.FunctionKeyword - | SyntaxKind.IfKeyword - | SyntaxKind.ImportKeyword - | SyntaxKind.InKeyword - | SyntaxKind.InstanceOfKeyword - | SyntaxKind.NewKeyword - | SyntaxKind.NullKeyword - | SyntaxKind.ReturnKeyword - | SyntaxKind.SuperKeyword - | SyntaxKind.SwitchKeyword - | SyntaxKind.ThisKeyword - | SyntaxKind.ThrowKeyword - | SyntaxKind.TrueKeyword - | SyntaxKind.TryKeyword - | SyntaxKind.TypeOfKeyword - | SyntaxKind.VarKeyword - | SyntaxKind.VoidKeyword - | SyntaxKind.WhileKeyword - | SyntaxKind.WithKeyword - | SyntaxKind.ImplementsKeyword - | SyntaxKind.InterfaceKeyword - | SyntaxKind.LetKeyword - | SyntaxKind.PackageKeyword - | SyntaxKind.PrivateKeyword - | SyntaxKind.ProtectedKeyword - | SyntaxKind.PublicKeyword - | SyntaxKind.StaticKeyword - | SyntaxKind.YieldKeyword - | SyntaxKind.AbstractKeyword - | SyntaxKind.AccessorKeyword - | SyntaxKind.AsKeyword - | SyntaxKind.AssertsKeyword - | SyntaxKind.AssertKeyword - | SyntaxKind.AnyKeyword - | SyntaxKind.AsyncKeyword - | SyntaxKind.AwaitKeyword - | SyntaxKind.BooleanKeyword - | SyntaxKind.ConstructorKeyword - | SyntaxKind.DeclareKeyword - | SyntaxKind.GetKeyword - | SyntaxKind.ImmediateKeyword - | SyntaxKind.InferKeyword - | SyntaxKind.IntrinsicKeyword - | SyntaxKind.IsKeyword - | SyntaxKind.KeyOfKeyword - | SyntaxKind.ModuleKeyword - | SyntaxKind.NamespaceKeyword - | SyntaxKind.NeverKeyword - | SyntaxKind.OutKeyword - | SyntaxKind.ReadonlyKeyword - | SyntaxKind.RequireKeyword - | SyntaxKind.NumberKeyword - | SyntaxKind.ObjectKeyword - | SyntaxKind.SatisfiesKeyword - | SyntaxKind.SetKeyword - | SyntaxKind.StringKeyword - | SyntaxKind.SymbolKeyword - | SyntaxKind.TypeKeyword - | SyntaxKind.UndefinedKeyword - | SyntaxKind.UniqueKeyword - | SyntaxKind.UnknownKeyword - | SyntaxKind.UsingKeyword - | SyntaxKind.FromKeyword - | SyntaxKind.GlobalKeyword - | SyntaxKind.BigIntKeyword - | SyntaxKind.OverrideKeyword - | SyntaxKind.OfKeyword - | SyntaxKind.DeferKeyword; -export type JsxTokenSyntaxKind = SyntaxKind.LessThanSlashToken | SyntaxKind.EndOfFile | SyntaxKind.ConflictMarkerTrivia | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.OpenBraceToken | SyntaxKind.LessThanToken; -export type JSDocNodeSyntaxKind = - | SyntaxKind.JSDocTypeExpression - | SyntaxKind.JSDocNameReference - | SyntaxKind.JSDocAllType - | SyntaxKind.JSDocNullableType - | SyntaxKind.JSDocNonNullableType - | SyntaxKind.JSDocOptionalType - | SyntaxKind.JSDocVariadicType - | SyntaxKind.JSDoc - | SyntaxKind.JSDocText - | SyntaxKind.JSDocTypeLiteral - | SyntaxKind.JSDocSignature - | SyntaxKind.JSDocLink - | SyntaxKind.JSDocLinkCode - | SyntaxKind.JSDocLinkPlain - | SyntaxKind.JSDocUnknownTag - | SyntaxKind.JSDocAugmentsTag - | SyntaxKind.JSDocImplementsTag - | SyntaxKind.JSDocDeprecatedTag - | SyntaxKind.JSDocPublicTag - | SyntaxKind.JSDocPrivateTag - | SyntaxKind.JSDocProtectedTag - | SyntaxKind.JSDocReadonlyTag - | SyntaxKind.JSDocOverrideTag - | SyntaxKind.JSDocCallbackTag - | SyntaxKind.JSDocOverloadTag - | SyntaxKind.JSDocParameterTag - | SyntaxKind.JSDocReturnTag - | SyntaxKind.JSDocThisTag - | SyntaxKind.JSDocTypeTag - | SyntaxKind.JSDocTemplateTag - | SyntaxKind.JSDocTypedefTag - | SyntaxKind.JSDocSeeTag - | SyntaxKind.JSDocPropertyTag - | SyntaxKind.JSDocThrowsTag - | SyntaxKind.JSDocSatisfiesTag - | SyntaxKind.JSDocImportTag; -export type ImportPhaseModifierSyntaxKind = SyntaxKind.TypeKeyword | SyntaxKind.DeferKeyword; -export type PostfixUnaryOperator = SyntaxKind.PlusPlusToken | SyntaxKind.MinusMinusToken; -export type PrefixUnaryOperator = SyntaxKind.PlusToken | SyntaxKind.MinusToken | SyntaxKind.TildeToken | SyntaxKind.ExclamationToken | SyntaxKind.PlusPlusToken | SyntaxKind.MinusMinusToken; -export type AssignmentOperator = SyntaxKind.EqualsToken | CompoundAssignmentOperator; -export type BinaryOperator = AssignmentOperatorOrHigher | SyntaxKind.CommaToken; -export type ExponentiationOperator = SyntaxKind.AsteriskAsteriskToken; -export type MultiplicativeOperator = SyntaxKind.AsteriskToken | SyntaxKind.SlashToken | SyntaxKind.PercentToken; -export type MultiplicativeOperatorOrHigher = ExponentiationOperator | MultiplicativeOperator; -export type AdditiveOperator = SyntaxKind.PlusToken | SyntaxKind.MinusToken; -export type AdditiveOperatorOrHigher = MultiplicativeOperatorOrHigher | AdditiveOperator; -export type ShiftOperator = SyntaxKind.LessThanLessThanToken | SyntaxKind.GreaterThanGreaterThanToken | SyntaxKind.GreaterThanGreaterThanGreaterThanToken; -export type ShiftOperatorOrHigher = AdditiveOperatorOrHigher | ShiftOperator; -export type RelationalOperator = SyntaxKind.LessThanToken | SyntaxKind.LessThanEqualsToken | SyntaxKind.GreaterThanToken | SyntaxKind.GreaterThanEqualsToken | SyntaxKind.InstanceOfKeyword | SyntaxKind.InKeyword; -export type RelationalOperatorOrHigher = ShiftOperatorOrHigher | RelationalOperator; -export type EqualityOperator = SyntaxKind.EqualsEqualsToken | SyntaxKind.EqualsEqualsEqualsToken | SyntaxKind.ExclamationEqualsEqualsToken | SyntaxKind.ExclamationEqualsToken; -export type EqualityOperatorOrHigher = RelationalOperatorOrHigher | EqualityOperator; -export type BitwiseOperator = SyntaxKind.AmpersandToken | SyntaxKind.BarToken | SyntaxKind.CaretToken; -export type BitwiseOperatorOrHigher = EqualityOperatorOrHigher | BitwiseOperator; -export type LogicalOperator = SyntaxKind.AmpersandAmpersandToken | SyntaxKind.BarBarToken; -export type LogicalOperatorOrHigher = BitwiseOperatorOrHigher | LogicalOperator; -export type CompoundAssignmentOperator = SyntaxKind.PlusEqualsToken | SyntaxKind.MinusEqualsToken | SyntaxKind.AsteriskAsteriskEqualsToken | SyntaxKind.AsteriskEqualsToken | SyntaxKind.SlashEqualsToken | SyntaxKind.PercentEqualsToken | SyntaxKind.AmpersandEqualsToken | SyntaxKind.BarEqualsToken | SyntaxKind.CaretEqualsToken | SyntaxKind.LessThanLessThanEqualsToken | SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken | SyntaxKind.GreaterThanGreaterThanEqualsToken | SyntaxKind.BarBarEqualsToken | SyntaxKind.AmpersandAmpersandEqualsToken | SyntaxKind.QuestionQuestionEqualsToken; -export type AssignmentOperatorOrHigher = SyntaxKind.QuestionQuestionToken | LogicalOperatorOrHigher | AssignmentOperator; -export type LogicalOrCoalescingAssignmentOperator = SyntaxKind.AmpersandAmpersandEqualsToken | SyntaxKind.BarBarEqualsToken | SyntaxKind.QuestionQuestionEqualsToken; - -export interface NodeBase extends Node { - readonly flags: NodeFlags; -} -export interface StatementBase extends NodeBase { - readonly _statementBrand: any; -} -export interface IterationStatementBase extends StatementBase { - readonly statement: Statement; -} -export interface ExpressionBase extends NodeBase { - readonly _expressionBrand: any; -} -export interface UnaryExpressionBase extends ExpressionBase { - readonly _unaryExpressionBrand: any; -} -export interface UpdateExpressionBase extends UnaryExpressionBase { - readonly _updateExpressionBrand: any; -} -export interface LeftHandSideExpressionBase extends UpdateExpressionBase { - readonly _leftHandSideExpressionBrand: any; -} -export interface MemberExpressionBase extends LeftHandSideExpressionBase { - readonly _memberExpressionBrand: any; -} -export interface PrimaryExpressionBase extends MemberExpressionBase { - readonly _primaryExpressionBrand: any; -} -export interface TypeNodeBase extends NodeBase { - readonly _typeNodeBrand: any; -} -export interface NodeWithTypeArgumentsBase extends TypeNodeBase { - readonly typeArguments?: NodeArray; -} -export interface JSDocTypeBase extends TypeNodeBase { - readonly _jsDocTypeBrand: any; -} -export interface DeclarationBase extends Node { - readonly _declarationBrand: any; -} -export interface ModifiersBase extends Node { - readonly modifiers?: NodeArray; - readonly modifierFlags: ModifierFlags; -} -export interface FunctionLikeBase extends Node { - readonly _declarationBrand: any; - readonly typeParameters?: NodeArray; - readonly parameters: NodeArray; - readonly type?: TypeNode; - readonly fullSignature?: TypeNode; -} -export interface BodyBase extends Node { - readonly asteriskToken?: AsteriskToken; - readonly body?: NodeBody; -} -export interface FunctionLikeWithBodyBase extends FunctionLikeBase, BodyBase { - readonly _functionLikeDeclarationBrand: any; - readonly body?: BlockOrExpression; -} -export interface ClassLikeBase extends ModifiersBase { - readonly _declarationBrand: any; - readonly name?: Identifier; - readonly typeParameters?: NodeArray; - readonly heritageClauses?: NodeArray; - readonly members: NodeArray; -} -export interface LiteralLikeNodeBase extends Node { - readonly text: string; - readonly tokenFlags: TokenFlags; -} -export interface LiteralExpressionBase extends PrimaryExpressionBase, LiteralLikeNodeBase { - readonly _literalExpressionBrand: any; -} -export interface TemplateLiteralLikeNodeBase extends LiteralLikeNodeBase { - readonly rawText: string; - readonly templateFlags: TokenFlags; -} -export interface TypeElementBase extends Node { - readonly _typeElementBrand: any; -} -export interface ClassElementBase extends Node { - readonly _classElementBrand: any; -} -export interface NamedMemberBase extends ModifiersBase { - readonly _declarationBrand: any; - readonly name: PropertyName; - readonly postfixToken?: QuestionToken | ExclamationToken; -} -export interface ObjectLiteralElementBase extends Node { - readonly _objectLiteralBrand: any; -} -export interface UnionOrIntersectionTypeNodeBase extends TypeNodeBase { - readonly types: NodeArray; -} -export interface JSDocTagBase extends NodeBase { - readonly tagName: Identifier; - readonly comment?: NodeArray; -} -export interface JSDocCommentBase extends NodeBase { - readonly text: string; -} - -export interface Token extends NodeBase { - readonly kind: TKind; -} -export interface Identifier extends PrimaryExpressionBase { - readonly kind: SyntaxKind.Identifier; - readonly text: string; -} -export interface PrivateIdentifier extends PrimaryExpressionBase { - readonly kind: SyntaxKind.PrivateIdentifier; - readonly text: string; -} -export interface QualifiedName extends NodeBase { - readonly kind: SyntaxKind.QualifiedName; - readonly left: EntityName; - readonly right: MemberName; -} -export interface ComputedPropertyName extends NodeBase { - readonly kind: SyntaxKind.ComputedPropertyName; - readonly expression: Expression; -} -export interface Decorator extends NodeBase { - readonly kind: SyntaxKind.Decorator; - readonly expression: LeftHandSideExpression; -} -export interface EmptyStatement extends StatementBase { - readonly kind: SyntaxKind.EmptyStatement; -} -export interface IfStatement extends StatementBase { - readonly kind: SyntaxKind.IfStatement; - readonly expression: Expression; - readonly thenStatement: Statement; - readonly elseStatement?: Statement; -} -export interface DoStatement extends IterationStatementBase { - readonly kind: SyntaxKind.DoStatement; - readonly expression: Expression; -} -export interface WhileStatement extends IterationStatementBase { - readonly kind: SyntaxKind.WhileStatement; - readonly expression: Expression; -} -export interface ForStatement extends IterationStatementBase { - readonly kind: SyntaxKind.ForStatement; - readonly initializer?: ForInitializer; - readonly condition?: Expression; - readonly incrementor?: Expression; -} -export interface BreakStatement extends StatementBase { - readonly kind: SyntaxKind.BreakStatement; - readonly label?: Identifier; -} -export interface ContinueStatement extends StatementBase { - readonly kind: SyntaxKind.ContinueStatement; - readonly label?: Identifier; -} -export interface ReturnStatement extends StatementBase { - readonly kind: SyntaxKind.ReturnStatement; - readonly expression?: Expression; -} -export interface WithStatement extends StatementBase { - readonly kind: SyntaxKind.WithStatement; - readonly expression: Expression; - readonly statement: Statement; -} -export interface SwitchStatement extends StatementBase { - readonly kind: SyntaxKind.SwitchStatement; - readonly expression: Expression; - readonly caseBlock: CaseBlock; -} -export interface CaseBlock extends NodeBase { - readonly kind: SyntaxKind.CaseBlock; - readonly clauses: NodeArray; -} -export interface ThrowStatement extends StatementBase { - readonly kind: SyntaxKind.ThrowStatement; - readonly expression: Expression; -} -export interface TryStatement extends StatementBase { - readonly kind: SyntaxKind.TryStatement; - readonly tryBlock: Block; - readonly catchClause?: CatchClause; - readonly finallyBlock?: Block; -} -export interface CatchClause extends NodeBase { - readonly kind: SyntaxKind.CatchClause; - readonly variableDeclaration?: VariableDeclaration; - readonly block: Block; -} -export interface DebuggerStatement extends StatementBase { - readonly kind: SyntaxKind.DebuggerStatement; -} -export interface LabeledStatement extends StatementBase { - readonly kind: SyntaxKind.LabeledStatement; - readonly label: Identifier; - readonly statement: Statement; -} -export interface ExpressionStatement extends StatementBase { - readonly kind: SyntaxKind.ExpressionStatement; - readonly expression: Expression; -} -export interface Block extends StatementBase { - readonly kind: SyntaxKind.Block; - readonly statements: NodeArray; - readonly multiLine: boolean; -} -export interface VariableStatement extends StatementBase, ModifiersBase { - readonly kind: SyntaxKind.VariableStatement; - readonly declarationList: VariableDeclarationList; -} -export interface VariableDeclaration extends NodeBase, DeclarationBase { - readonly kind: SyntaxKind.VariableDeclaration; - readonly name: BindingName; - readonly exclamationToken?: ExclamationToken; - readonly type?: TypeNode; - readonly initializer?: Expression; -} -export interface VariableDeclarationList extends NodeBase { - readonly kind: SyntaxKind.VariableDeclarationList; - readonly declarations: NodeArray; -} -export interface ParameterDeclaration extends NodeBase, DeclarationBase, ModifiersBase { - readonly kind: SyntaxKind.Parameter; - readonly dotDotDotToken?: DotDotDotToken; - readonly name: BindingName; - readonly questionToken?: QuestionToken; - readonly type?: TypeNode; - readonly initializer?: Expression; -} -export interface BindingElement extends NodeBase, DeclarationBase { - readonly kind: SyntaxKind.BindingElement; - readonly dotDotDotToken?: DotDotDotToken; - readonly propertyName?: PropertyName; - readonly name?: BindingName; - readonly initializer?: Expression; -} -export interface MissingDeclaration extends StatementBase, DeclarationBase, ModifiersBase { - readonly kind: SyntaxKind.MissingDeclaration; -} -export interface FunctionDeclaration extends StatementBase, DeclarationBase, ModifiersBase, FunctionLikeWithBodyBase { - readonly kind: SyntaxKind.FunctionDeclaration; - readonly name?: Identifier; - readonly body?: FunctionBody; -} -export interface ClassDeclaration extends StatementBase, DeclarationBase, ClassLikeBase { - readonly kind: SyntaxKind.ClassDeclaration; -} -export interface ClassExpression extends PrimaryExpressionBase, DeclarationBase, ClassLikeBase { - readonly kind: SyntaxKind.ClassExpression; -} -export interface HeritageClause extends NodeBase { - readonly kind: SyntaxKind.HeritageClause; - readonly token: SyntaxKind.ExtendsKeyword | SyntaxKind.ImplementsKeyword; - readonly types: NodeArray; -} -export interface InterfaceDeclaration extends StatementBase, DeclarationBase, ModifiersBase { - readonly kind: SyntaxKind.InterfaceDeclaration; - readonly name: Identifier; - readonly typeParameters?: NodeArray; - readonly heritageClauses?: NodeArray; - readonly members: NodeArray; -} -export interface TypeAliasDeclaration extends StatementBase, DeclarationBase, ModifiersBase { - readonly kind: SyntaxKind.TypeAliasDeclaration; - readonly name: Identifier; - readonly typeParameters?: NodeArray; - readonly type: TypeNode; -} -export interface EnumMember extends NodeBase, DeclarationBase, NamedMemberBase { - readonly kind: SyntaxKind.EnumMember; - readonly initializer?: Expression; -} -export interface EnumDeclaration extends StatementBase, DeclarationBase, ModifiersBase { - readonly kind: SyntaxKind.EnumDeclaration; - readonly name: Identifier; - readonly members: NodeArray; -} -export interface ModuleBlock extends StatementBase { - readonly kind: SyntaxKind.ModuleBlock; - readonly statements: NodeArray; -} -export interface NotEmittedStatement extends StatementBase { - readonly kind: SyntaxKind.NotEmittedStatement; -} -export interface NotEmittedTypeElement extends NodeBase, TypeElementBase { - readonly kind: SyntaxKind.NotEmittedTypeElement; -} -export interface ImportDeclaration extends StatementBase, ModifiersBase, DeclarationBase { - readonly kind: SyntaxKind.ImportDeclaration; - readonly importClause?: ImportClause; - readonly moduleSpecifier: Expression; - readonly attributes?: ImportAttributes; -} -export interface ExternalModuleReference extends NodeBase { - readonly kind: SyntaxKind.ExternalModuleReference; - readonly expression: Expression; -} -export interface NamespaceImport extends NodeBase, DeclarationBase { - readonly kind: SyntaxKind.NamespaceImport; - readonly name: Identifier; -} -export interface NamedImports extends NodeBase { - readonly kind: SyntaxKind.NamedImports; - readonly elements: NodeArray; -} -export interface ExportAssignment extends StatementBase, DeclarationBase, ModifiersBase { - readonly kind: SyntaxKind.ExportAssignment; - readonly isExportEquals: boolean; - readonly type: TypeNode; - readonly expression: Expression; -} -export interface NamespaceExportDeclaration extends StatementBase, DeclarationBase, ModifiersBase { - readonly kind: SyntaxKind.NamespaceExportDeclaration; - readonly name: Identifier; -} -export interface NamespaceExport extends NodeBase, DeclarationBase { - readonly kind: SyntaxKind.NamespaceExport; - readonly name: ModuleExportName; -} -export interface NamedExports extends NodeBase { - readonly kind: SyntaxKind.NamedExports; - readonly elements: NodeArray; -} -export interface ExportSpecifier extends NodeBase, DeclarationBase { - readonly kind: SyntaxKind.ExportSpecifier; - readonly isTypeOnly: boolean; - readonly propertyName?: ModuleExportName; - readonly name: ModuleExportName; -} -export interface CallSignatureDeclaration extends NodeBase, DeclarationBase, FunctionLikeBase, TypeElementBase { - readonly kind: SyntaxKind.CallSignature; -} -export interface ConstructSignatureDeclaration extends NodeBase, DeclarationBase, FunctionLikeBase, TypeElementBase { - readonly kind: SyntaxKind.ConstructSignature; -} -export interface ConstructorDeclaration extends NodeBase, DeclarationBase, ModifiersBase, FunctionLikeWithBodyBase, ClassElementBase { - readonly kind: SyntaxKind.Constructor; - readonly body?: FunctionBody; -} -export interface GetAccessorDeclaration extends NodeBase, DeclarationBase, NamedMemberBase, FunctionLikeWithBodyBase, TypeElementBase, ClassElementBase, ObjectLiteralElementBase { - readonly kind: SyntaxKind.GetAccessor; - readonly body?: FunctionBody; -} -export interface SetAccessorDeclaration extends NodeBase, DeclarationBase, NamedMemberBase, FunctionLikeWithBodyBase, TypeElementBase, ClassElementBase, ObjectLiteralElementBase { - readonly kind: SyntaxKind.SetAccessor; - readonly body?: FunctionBody; -} -export interface IndexSignatureDeclaration extends NodeBase, DeclarationBase, ModifiersBase, FunctionLikeBase, TypeElementBase, ClassElementBase { - readonly kind: SyntaxKind.IndexSignature; - readonly type: TypeNode; -} -export interface MethodSignatureDeclaration extends NodeBase, DeclarationBase, NamedMemberBase, FunctionLikeBase, TypeElementBase { - readonly kind: SyntaxKind.MethodSignature; -} -export interface MethodDeclaration extends NodeBase, DeclarationBase, NamedMemberBase, FunctionLikeWithBodyBase, ClassElementBase, ObjectLiteralElementBase { - readonly kind: SyntaxKind.MethodDeclaration; - readonly body?: FunctionBody; -} -export interface PropertySignatureDeclaration extends NodeBase, DeclarationBase, NamedMemberBase, TypeElementBase { - readonly kind: SyntaxKind.PropertySignature; - readonly type: TypeNode; - readonly initializer: Expression; -} -export interface PropertyDeclaration extends NodeBase, DeclarationBase, NamedMemberBase, ClassElementBase { - readonly kind: SyntaxKind.PropertyDeclaration; - readonly type?: TypeNode; - readonly initializer?: Expression; -} -export interface SemicolonClassElement extends NodeBase, DeclarationBase, ClassElementBase { - readonly kind: SyntaxKind.SemicolonClassElement; -} -export interface ClassStaticBlockDeclaration extends NodeBase, DeclarationBase, ModifiersBase, ClassElementBase { - readonly kind: SyntaxKind.ClassStaticBlockDeclaration; - readonly body: Block; -} -export interface OmittedExpression extends ExpressionBase { - readonly kind: SyntaxKind.OmittedExpression; -} -export interface KeywordExpression extends ExpressionBase { - readonly kind: TKind; -} -export interface StringLiteral extends LiteralExpressionBase { - readonly kind: SyntaxKind.StringLiteral; -} -export interface NumericLiteral extends LiteralExpressionBase { - readonly kind: SyntaxKind.NumericLiteral; -} -export interface BigIntLiteral extends LiteralExpressionBase { - readonly kind: SyntaxKind.BigIntLiteral; -} -export interface RegularExpressionLiteral extends LiteralExpressionBase { - readonly kind: SyntaxKind.RegularExpressionLiteral; -} -export interface NoSubstitutionTemplateLiteral extends ExpressionBase, TemplateLiteralLikeNodeBase, DeclarationBase { - readonly kind: SyntaxKind.NoSubstitutionTemplateLiteral; -} -export interface BinaryExpression extends ExpressionBase, DeclarationBase, ModifiersBase { - readonly kind: SyntaxKind.BinaryExpression; - readonly left: Expression; - readonly type?: TypeNode; - readonly operatorToken: BinaryOperatorToken; - readonly right: Expression; -} -export interface PrefixUnaryExpression extends UpdateExpressionBase { - readonly kind: SyntaxKind.PrefixUnaryExpression; - readonly operator: SyntaxKind.PlusToken | SyntaxKind.MinusToken | SyntaxKind.TildeToken | SyntaxKind.ExclamationToken | SyntaxKind.PlusPlusToken | SyntaxKind.MinusMinusToken; - readonly operand: Expression; -} -export interface PostfixUnaryExpression extends UpdateExpressionBase { - readonly kind: SyntaxKind.PostfixUnaryExpression; - readonly operand: Expression; - readonly operator: SyntaxKind.PlusPlusToken | SyntaxKind.MinusMinusToken; -} -export interface YieldExpression extends ExpressionBase { - readonly kind: SyntaxKind.YieldExpression; - readonly asteriskToken?: AsteriskToken; - readonly expression?: Expression; -} -export interface ArrowFunction extends ExpressionBase, DeclarationBase, ModifiersBase, FunctionLikeWithBodyBase { - readonly kind: SyntaxKind.ArrowFunction; - readonly equalsGreaterThanToken: EqualsGreaterThanToken; - readonly body: ConciseBody; -} -export interface FunctionExpression extends PrimaryExpressionBase, DeclarationBase, ModifiersBase, FunctionLikeWithBodyBase { - readonly kind: SyntaxKind.FunctionExpression; - readonly name?: Identifier; - readonly body: FunctionBody; -} -export interface AsExpression extends ExpressionBase { - readonly kind: SyntaxKind.AsExpression; - readonly expression: Expression; - readonly type: TypeNode; -} -export interface SatisfiesExpression extends ExpressionBase { - readonly kind: SyntaxKind.SatisfiesExpression; - readonly expression: Expression; - readonly type: TypeNode; -} -export interface ConditionalExpression extends ExpressionBase { - readonly kind: SyntaxKind.ConditionalExpression; - readonly condition: Expression; - readonly questionToken: QuestionToken; - readonly whenTrue: Expression; - readonly colonToken: ColonToken; - readonly whenFalse: Expression; -} -export interface PropertyAccessExpression extends MemberExpressionBase { - readonly kind: SyntaxKind.PropertyAccessExpression; - readonly expression: Expression; - readonly questionDotToken?: QuestionDotToken; - readonly name: MemberName; -} -export interface ElementAccessExpression extends MemberExpressionBase { - readonly kind: SyntaxKind.ElementAccessExpression; - readonly expression: Expression; - readonly questionDotToken?: QuestionDotToken; - readonly argumentExpression: Expression; -} -export interface CallExpression extends LeftHandSideExpressionBase, DeclarationBase { - readonly kind: SyntaxKind.CallExpression; - readonly expression: Expression; - readonly questionDotToken?: QuestionDotToken; - readonly typeArguments?: NodeArray; - readonly arguments: NodeArray; -} -export interface NewExpression extends PrimaryExpressionBase { - readonly kind: SyntaxKind.NewExpression; - readonly expression: Expression; - readonly typeArguments?: NodeArray; - readonly arguments?: NodeArray; -} -export interface MetaProperty extends PrimaryExpressionBase { - readonly kind: SyntaxKind.MetaProperty; - readonly keywordToken: SyntaxKind.ImportKeyword | SyntaxKind.NewKeyword; - readonly name: Identifier; -} -export interface NonNullExpression extends LeftHandSideExpressionBase { - readonly kind: SyntaxKind.NonNullExpression; - readonly expression: Expression; -} -export interface SpreadElement extends ExpressionBase { - readonly kind: SyntaxKind.SpreadElement; - readonly expression: Expression; -} -export interface TemplateExpression extends PrimaryExpressionBase { - readonly kind: SyntaxKind.TemplateExpression; - readonly head: TemplateHead; - readonly templateSpans: NodeArray; -} -export interface TemplateSpan extends NodeBase { - readonly kind: SyntaxKind.TemplateSpan; - readonly expression: Expression; - readonly literal: TemplateMiddleOrTail; -} -export interface TaggedTemplateExpression extends MemberExpressionBase { - readonly kind: SyntaxKind.TaggedTemplateExpression; - readonly tag: Expression; - readonly questionDotToken: QuestionDotToken; - readonly typeArguments?: NodeArray; - readonly template: TemplateLiteral; -} -export interface ParenthesizedExpression extends PrimaryExpressionBase { - readonly kind: SyntaxKind.ParenthesizedExpression; - readonly expression: Expression; -} -export interface ArrayLiteralExpression extends PrimaryExpressionBase { - readonly kind: SyntaxKind.ArrayLiteralExpression; - readonly elements: NodeArray; - readonly multiLine: boolean; -} -export interface ObjectLiteralExpression extends PrimaryExpressionBase, DeclarationBase { - readonly kind: SyntaxKind.ObjectLiteralExpression; - readonly properties: NodeArray; - readonly multiLine: boolean; -} -export interface SpreadAssignment extends NodeBase, DeclarationBase, ObjectLiteralElementBase { - readonly kind: SyntaxKind.SpreadAssignment; - readonly expression: Expression; -} -export interface PropertyAssignment extends NodeBase, DeclarationBase, NamedMemberBase, ObjectLiteralElementBase { - readonly kind: SyntaxKind.PropertyAssignment; - readonly type: TypeNode; - readonly initializer: Expression; -} -export interface ShorthandPropertyAssignment extends NodeBase, DeclarationBase, NamedMemberBase, ObjectLiteralElementBase { - readonly kind: SyntaxKind.ShorthandPropertyAssignment; - readonly type: TypeNode; - readonly equalsToken?: EqualsToken; - readonly objectAssignmentInitializer?: Expression; -} -export interface DeleteExpression extends UnaryExpressionBase { - readonly kind: SyntaxKind.DeleteExpression; - readonly expression: Expression; -} -export interface TypeOfExpression extends UnaryExpressionBase { - readonly kind: SyntaxKind.TypeOfExpression; - readonly expression: Expression; -} -export interface VoidExpression extends UnaryExpressionBase { - readonly kind: SyntaxKind.VoidExpression; - readonly expression: Expression; -} -export interface AwaitExpression extends UnaryExpressionBase { - readonly kind: SyntaxKind.AwaitExpression; - readonly expression: Expression; -} -export interface TypeAssertion extends UnaryExpressionBase { - readonly kind: SyntaxKind.TypeAssertionExpression; - readonly type: TypeNode; - readonly expression: Expression; -} -export interface KeywordTypeNode extends TypeNodeBase { - readonly kind: TKind; -} -export interface UnionTypeNode extends UnionOrIntersectionTypeNodeBase { - readonly kind: SyntaxKind.UnionType; -} -export interface IntersectionTypeNode extends UnionOrIntersectionTypeNodeBase { - readonly kind: SyntaxKind.IntersectionType; -} -export interface ConditionalTypeNode extends TypeNodeBase { - readonly kind: SyntaxKind.ConditionalType; - readonly checkType: TypeNode; - readonly extendsType: TypeNode; - readonly trueType: TypeNode; - readonly falseType: TypeNode; -} -export interface TypeOperatorNode extends TypeNodeBase { - readonly kind: SyntaxKind.TypeOperator; - readonly operator: SyntaxKind.KeyOfKeyword | SyntaxKind.ReadonlyKeyword | SyntaxKind.UniqueKeyword; - readonly type: TypeNode; -} -export interface InferTypeNode extends TypeNodeBase { - readonly kind: SyntaxKind.InferType; - readonly typeParameter: TypeParameterDeclaration; -} -export interface ArrayTypeNode extends TypeNodeBase { - readonly kind: SyntaxKind.ArrayType; - readonly elementType: TypeNode; -} -export interface IndexedAccessTypeNode extends TypeNodeBase { - readonly kind: SyntaxKind.IndexedAccessType; - readonly objectType: TypeNode; - readonly indexType: TypeNode; -} -export interface TypeReferenceNode extends NodeWithTypeArgumentsBase { - readonly kind: SyntaxKind.TypeReference; - readonly typeName: EntityName; -} -export interface ExpressionWithTypeArguments extends MemberExpressionBase { - readonly kind: SyntaxKind.ExpressionWithTypeArguments; - readonly expression: Expression; - readonly typeArguments?: NodeArray; -} -export interface LiteralTypeNode extends TypeNodeBase { - readonly kind: SyntaxKind.LiteralType; - readonly literal: Node; -} -export interface ThisTypeNode extends TypeNodeBase { - readonly kind: SyntaxKind.ThisType; -} -export interface TypePredicateNode extends TypeNodeBase { - readonly kind: SyntaxKind.TypePredicate; - readonly assertsModifier?: AssertsKeyword; - readonly parameterName: TypePredicateParameterName; - readonly type?: TypeNode; -} -export interface ImportAttribute extends NodeBase { - readonly kind: SyntaxKind.ImportAttribute; - readonly name: ImportAttributeName; - readonly value: Expression; -} -export interface ImportAttributes extends NodeBase { - readonly kind: SyntaxKind.ImportAttributes; - readonly token: SyntaxKind.WithKeyword | SyntaxKind.AssertKeyword; - readonly attributes: NodeArray; - readonly multiLine: boolean; -} -export interface TypeQueryNode extends NodeWithTypeArgumentsBase { - readonly kind: SyntaxKind.TypeQuery; - readonly exprName: EntityName; -} -export interface MappedTypeNode extends TypeNodeBase, DeclarationBase { - readonly kind: SyntaxKind.MappedType; - readonly readonlyToken?: ReadonlyKeyword | PlusToken | MinusToken; - readonly typeParameter: TypeParameterDeclaration; - readonly nameType?: TypeNode; - readonly questionToken?: QuestionToken | PlusToken | MinusToken; - readonly type?: TypeNode; - readonly members?: NodeArray; -} -export interface TypeLiteralNode extends TypeNodeBase, DeclarationBase { - readonly kind: SyntaxKind.TypeLiteral; - readonly members: NodeArray; -} -export interface TupleTypeNode extends TypeNodeBase { - readonly kind: SyntaxKind.TupleType; - readonly elements: NodeArray; -} -export interface NamedTupleMember extends TypeNodeBase, DeclarationBase { - readonly kind: SyntaxKind.NamedTupleMember; - readonly dotDotDotToken?: DotDotDotToken; - readonly name: Identifier; - readonly questionToken?: QuestionToken; - readonly type: TypeNode; -} -export interface OptionalTypeNode extends TypeNodeBase { - readonly kind: SyntaxKind.OptionalType; - readonly type: TypeNode; -} -export interface RestTypeNode extends TypeNodeBase { - readonly kind: SyntaxKind.RestType; - readonly type: TypeNode; -} -export interface ParenthesizedTypeNode extends TypeNodeBase { - readonly kind: SyntaxKind.ParenthesizedType; - readonly type: TypeNode; -} -export interface FunctionTypeNode extends TypeNodeBase, DeclarationBase, ModifiersBase, FunctionLikeBase { - readonly kind: SyntaxKind.FunctionType; -} -export interface ConstructorTypeNode extends TypeNodeBase, DeclarationBase, ModifiersBase, FunctionLikeBase { - readonly kind: SyntaxKind.ConstructorType; -} -export interface TemplateHead extends NodeBase, TemplateLiteralLikeNodeBase { - readonly kind: SyntaxKind.TemplateHead; -} -export interface TemplateMiddle extends NodeBase, TemplateLiteralLikeNodeBase { - readonly kind: SyntaxKind.TemplateMiddle; -} -export interface TemplateTail extends NodeBase, TemplateLiteralLikeNodeBase { - readonly kind: SyntaxKind.TemplateTail; -} -export interface TemplateLiteralTypeNode extends TypeNodeBase { - readonly kind: SyntaxKind.TemplateLiteralType; - readonly head: TemplateHead; - readonly templateSpans: NodeArray; -} -export interface TemplateLiteralTypeSpan extends TypeNodeBase { - readonly kind: SyntaxKind.TemplateLiteralTypeSpan; - readonly type: TypeNode; - readonly literal: TemplateMiddleOrTail; -} -export interface SyntheticExpression extends ExpressionBase { - readonly kind: SyntaxKind.SyntheticExpression; - readonly type: any; - readonly isSpread: boolean; - readonly tupleNameSource?: Node; -} -export interface PartiallyEmittedExpression extends LeftHandSideExpressionBase { - readonly kind: SyntaxKind.PartiallyEmittedExpression; - readonly expression: Expression; -} -export interface JsxElement extends PrimaryExpressionBase { - readonly kind: SyntaxKind.JsxElement; - readonly openingElement: JsxOpeningElement; - readonly children: NodeArray; - readonly closingElement: JsxClosingElement; -} -export interface JsxAttributes extends PrimaryExpressionBase, DeclarationBase { - readonly kind: SyntaxKind.JsxAttributes; - readonly properties: NodeArray; -} -export interface JsxNamespacedName extends ExpressionBase { - readonly kind: SyntaxKind.JsxNamespacedName; - readonly namespace: Identifier; - readonly name: Identifier; -} -export interface JsxOpeningElement extends ExpressionBase { - readonly kind: SyntaxKind.JsxOpeningElement; - readonly tagName: JsxTagNameExpression; - readonly typeArguments?: NodeArray; - readonly attributes: JsxAttributes; -} -export interface JsxSelfClosingElement extends PrimaryExpressionBase { - readonly kind: SyntaxKind.JsxSelfClosingElement; - readonly tagName: JsxTagNameExpression; - readonly typeArguments?: NodeArray; - readonly attributes: JsxAttributes; -} -export interface JsxFragment extends PrimaryExpressionBase { - readonly kind: SyntaxKind.JsxFragment; - readonly openingFragment: JsxOpeningFragment; - readonly children: NodeArray; - readonly closingFragment: JsxClosingFragment; -} -export interface JsxOpeningFragment extends ExpressionBase { - readonly kind: SyntaxKind.JsxOpeningFragment; -} -export interface JsxClosingFragment extends ExpressionBase { - readonly kind: SyntaxKind.JsxClosingFragment; -} -export interface JsxAttribute extends NodeBase, DeclarationBase { - readonly kind: SyntaxKind.JsxAttribute; - readonly name: JsxAttributeName; - readonly initializer?: JsxAttributeValue; -} -export interface JsxSpreadAttribute extends NodeBase, ObjectLiteralElementBase { - readonly kind: SyntaxKind.JsxSpreadAttribute; - readonly expression: Expression; -} -export interface JsxClosingElement extends NodeBase { - readonly kind: SyntaxKind.JsxClosingElement; - readonly tagName: JsxTagNameExpression; -} -export interface JsxExpression extends ExpressionBase { - readonly kind: SyntaxKind.JsxExpression; - readonly dotDotDotToken?: DotDotDotToken; - readonly expression?: Expression; -} -export interface JsxText extends ExpressionBase, LiteralLikeNodeBase { - readonly kind: SyntaxKind.JsxText; - readonly containsOnlyTriviaWhiteSpaces: boolean; -} -export interface SyntaxList extends NodeBase { - readonly kind: SyntaxKind.SyntaxList; - readonly children: readonly Node[]; -} -export interface JSDoc extends NodeBase { - readonly kind: SyntaxKind.JSDoc; - readonly comment: NodeArray; - readonly tags?: NodeArray; -} -export interface JSDocTypeExpression extends TypeNodeBase { - readonly kind: SyntaxKind.JSDocTypeExpression; - readonly type: TypeNode; -} -export interface JSDocNonNullableType extends JSDocTypeBase { - readonly kind: SyntaxKind.JSDocNonNullableType; - readonly type: TypeNode; -} -export interface JSDocNullableType extends JSDocTypeBase { - readonly kind: SyntaxKind.JSDocNullableType; - readonly type: TypeNode; -} -export interface JSDocAllType extends JSDocTypeBase { - readonly kind: SyntaxKind.JSDocAllType; -} -export interface JSDocVariadicType extends JSDocTypeBase { - readonly kind: SyntaxKind.JSDocVariadicType; - readonly type: TypeNode; -} -export interface JSDocOptionalType extends JSDocTypeBase { - readonly kind: SyntaxKind.JSDocOptionalType; - readonly type: TypeNode; -} -export interface JSDocTypeTag extends JSDocTagBase { - readonly kind: SyntaxKind.JSDocTypeTag; - readonly typeExpression: Node; -} -export interface JSDocUnknownTag extends JSDocTagBase { - readonly kind: SyntaxKind.JSDocUnknownTag; -} -export interface JSDocTemplateTag extends JSDocTagBase { - readonly kind: SyntaxKind.JSDocTemplateTag; - readonly constraint: Node; - readonly typeParameters: NodeArray; -} -export interface JSDocReturnTag extends JSDocTagBase { - readonly kind: SyntaxKind.JSDocReturnTag; - readonly typeExpression?: TypeNode; -} -export interface JSDocPublicTag extends JSDocTagBase { - readonly kind: SyntaxKind.JSDocPublicTag; -} -export interface JSDocPrivateTag extends JSDocTagBase { - readonly kind: SyntaxKind.JSDocPrivateTag; -} -export interface JSDocProtectedTag extends JSDocTagBase { - readonly kind: SyntaxKind.JSDocProtectedTag; -} -export interface JSDocReadonlyTag extends JSDocTagBase { - readonly kind: SyntaxKind.JSDocReadonlyTag; -} -export interface JSDocOverrideTag extends JSDocTagBase { - readonly kind: SyntaxKind.JSDocOverrideTag; -} -export interface JSDocDeprecatedTag extends JSDocTagBase { - readonly kind: SyntaxKind.JSDocDeprecatedTag; -} -export interface JSDocSeeTag extends JSDocTagBase { - readonly kind: SyntaxKind.JSDocSeeTag; - readonly nameExpression: TypeNode; -} -export interface JSDocImplementsTag extends JSDocTagBase { - readonly kind: SyntaxKind.JSDocImplementsTag; - readonly className: ExpressionWithTypeArguments; -} -export interface JSDocAugmentsTag extends JSDocTagBase { - readonly kind: SyntaxKind.JSDocAugmentsTag; - readonly className: ExpressionWithTypeArguments; -} -export interface JSDocSatisfiesTag extends JSDocTagBase { - readonly kind: SyntaxKind.JSDocSatisfiesTag; - readonly typeExpression: TypeNode; -} -export interface JSDocThrowsTag extends JSDocTagBase { - readonly kind: SyntaxKind.JSDocThrowsTag; - readonly typeExpression?: TypeNode; -} -export interface JSDocThisTag extends JSDocTagBase { - readonly kind: SyntaxKind.JSDocThisTag; - readonly typeExpression: TypeNode; -} -export interface JSDocImportTag extends JSDocTagBase { - readonly kind: SyntaxKind.JSDocImportTag; - readonly importClause?: ImportClause; - readonly moduleSpecifier: Expression; - readonly attributes?: ImportAttributes; -} -export interface JSDocCallbackTag extends JSDocTagBase { - readonly kind: SyntaxKind.JSDocCallbackTag; - readonly typeExpression: TypeNode; - readonly name?: JSDocFullName; -} -export interface JSDocOverloadTag extends JSDocTagBase { - readonly kind: SyntaxKind.JSDocOverloadTag; - readonly typeExpression: TypeNode; -} -export interface JSDocTypedefTag extends JSDocTagBase { - readonly kind: SyntaxKind.JSDocTypedefTag; - readonly typeExpression?: Node; - readonly name?: JSDocFullName; -} -export interface JSDocSignature extends JSDocTypeBase, DeclarationBase, FunctionLikeBase { - readonly kind: SyntaxKind.JSDocSignature; -} -export interface JSDocNameReference extends TypeNodeBase { - readonly kind: SyntaxKind.JSDocNameReference; - readonly name: EntityName; -} -export interface ModuleDeclaration extends StatementBase, DeclarationBase, ModifiersBase, BodyBase { - readonly kind: SyntaxKind.ModuleDeclaration; - readonly keyword: SyntaxKind.ModuleKeyword | SyntaxKind.NamespaceKeyword; - readonly name: ModuleName; - readonly body?: ModuleBody; -} -export interface ImportEqualsDeclaration extends StatementBase, DeclarationBase, ModifiersBase { - readonly kind: SyntaxKind.ImportEqualsDeclaration; - readonly isTypeOnly: boolean; - readonly name: Identifier; - readonly moduleReference: ModuleReference; -} -export interface ExportDeclaration extends StatementBase, DeclarationBase, ModifiersBase { - readonly kind: SyntaxKind.ExportDeclaration; - readonly isTypeOnly: boolean; - readonly exportClause?: NamedExportBindings; - readonly moduleSpecifier?: Expression; - readonly attributes?: ImportAttributes; -} -export interface ImportTypeNode extends NodeWithTypeArgumentsBase { - readonly kind: SyntaxKind.ImportType; - readonly isTypeOf: boolean; - readonly argument: TypeNode; - readonly attributes?: ImportAttributes; - readonly qualifier?: EntityName; -} -export interface ImportClause extends NodeBase, DeclarationBase { - readonly kind: SyntaxKind.ImportClause; - readonly phaseModifier?: ImportPhaseModifierSyntaxKind; - readonly name?: Identifier; - readonly namedBindings?: NamedImportBindings; -} -export interface ImportSpecifier extends NodeBase, DeclarationBase { - readonly kind: SyntaxKind.ImportSpecifier; - readonly isTypeOnly: boolean; - readonly propertyName?: ModuleExportName; - readonly name: Identifier; -} -export interface JSDocText extends JSDocCommentBase { - readonly kind: SyntaxKind.JSDocText; -} -export interface JSDocLink extends JSDocCommentBase { - readonly kind: SyntaxKind.JSDocLink; - readonly name?: EntityName; -} -export interface JSDocLinkPlain extends JSDocCommentBase { - readonly kind: SyntaxKind.JSDocLinkPlain; - readonly name?: EntityName; -} -export interface JSDocLinkCode extends JSDocCommentBase { - readonly kind: SyntaxKind.JSDocLinkCode; - readonly name?: EntityName; -} -export interface TypeParameterDeclaration extends NodeBase, DeclarationBase, ModifiersBase { - readonly kind: SyntaxKind.TypeParameter; - readonly name: Identifier; - readonly constraint?: TypeNode; - readonly expression?: Expression; - readonly defaultType?: TypeNode; -} -export interface SyntheticReferenceExpression extends ExpressionBase { - readonly kind: SyntaxKind.SyntheticReferenceExpression; - readonly expression: Expression; - readonly thisArg: Expression; -} -export interface JSDocTypeLiteral extends JSDocTypeBase, DeclarationBase { - readonly kind: SyntaxKind.JSDocTypeLiteral; - readonly jsdocPropertyTags?: readonly JSDocTag[]; - readonly isArrayType: boolean; -} - -export type Expression = ExpressionBase; -export type Statement = StatementBase; -export type TypeNode = TypeNodeBase; -export type HeritageClauseElement = ExpressionWithTypeArguments | TypeReferenceNode; -export type BlockOrExpression = Block | Expression; -export type NodeBody = Block | Expression | ModuleBlock | ModuleDeclaration; -export type AccessExpression = PropertyAccessExpression | ElementAccessExpression; -export type DeclarationName = Identifier | PrivateIdentifier | StringLiteral | NumericLiteral | BigIntLiteral | NoSubstitutionTemplateLiteral | ComputedPropertyName | BindingPattern | ElementAccessExpression; -export type ModuleName = Identifier | StringLiteral; -export type ModuleExportName = Identifier | StringLiteral; -export type PropertyName = Identifier | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | ComputedPropertyName | PrivateIdentifier | BigIntLiteral; -export type ModuleBody = ModuleBlock | ModuleDeclaration; -export type JSDocFullName = Identifier | ModuleDeclaration; -export type ForInitializer = Expression | MissingDeclaration | VariableDeclarationList; -export type ModuleReference = Identifier | QualifiedName | ExternalModuleReference; -export type NamedImportBindings = NamespaceImport | NamedImports; -export type NamedExportBindings = NamespaceExport | NamedExports; -export type MemberName = Identifier | PrivateIdentifier; -export type EntityName = Identifier | QualifiedName; -export type BindingName = Identifier | BindingPattern; -export type ModifierLike = Modifier | Decorator; -export type JsxChild = JsxText | JsxExpression | JsxElement | JsxSelfClosingElement | JsxFragment; -export type JsxAttributeLike = JsxAttribute | JsxSpreadAttribute; -export type JsxAttributeName = Identifier | JsxNamespacedName; -export type JsxAttributeValue = StringLiteral | JsxExpression | JsxElement | JsxSelfClosingElement | JsxFragment; -export type JsxTagNameExpression = Identifier | ThisExpression | JsxTagNamePropertyAccess | JsxNamespacedName; -export type ClassLikeDeclaration = ClassDeclaration | ClassExpression; -export type AccessorDeclaration = GetAccessorDeclaration | SetAccessorDeclaration; -export type LiteralLikeNode = StringLiteral | NumericLiteral | BigIntLiteral | RegularExpressionLiteral | TemplateLiteralLikeNode | JsxText; -export type LiteralExpression = StringLiteral | NumericLiteral | BigIntLiteral | RegularExpressionLiteral | NoSubstitutionTemplateLiteral; -export type UnionOrIntersectionTypeNode = UnionTypeNode | IntersectionTypeNode; -export type TemplateLiteralLikeNode = TemplateHead | TemplateMiddle | TemplateTail; -export type TemplateMiddleOrTail = TemplateMiddle | TemplateTail; -export type TemplateLiteral = TemplateExpression | NoSubstitutionTemplateLiteral; -export type TypePredicateParameterName = Identifier | ThisTypeNode; -export type ImportAttributeName = Identifier | StringLiteral; -export type LeftHandSideExpression = LeftHandSideExpressionBase; -export type JSDocComment = JSDocText | JSDocLink | JSDocLinkCode | JSDocLinkPlain; -export type SignatureDeclaration = CallSignatureDeclaration | ConstructSignatureDeclaration | MethodSignatureDeclaration | IndexSignatureDeclaration | FunctionTypeNode | ConstructorTypeNode | FunctionDeclaration | MethodDeclaration | ConstructorDeclaration | AccessorDeclaration | FunctionExpression | ArrowFunction; -export type StringLiteralLikeNode = StringLiteral | NoSubstitutionTemplateLiteral; -export type NumericOrStringLikeLiteral = StringLiteralLikeNode | NumericLiteral; -export type ObjectLiteralLikeNode = ObjectLiteralExpression | ObjectBindingPattern; -export type ObjectTypeDeclaration = ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode; -export type JsxOpeningLikeElement = JsxOpeningElement | JsxSelfClosingElement; -export type NamedImportsOrExports = NamedImports | NamedExports; -export type BreakOrContinueStatement = BreakStatement | ContinueStatement; -export type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator | JsxOpeningLikeElement | BinaryExpression; -export type FunctionLikeDeclaration = FunctionDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ConstructorDeclaration | FunctionExpression | ArrowFunction; -export type VariableOrParameterDeclaration = VariableDeclaration | ParameterDeclaration; -export type VariableOrPropertyDeclaration = VariableDeclaration | PropertyDeclaration; -export type CallOrNewExpression = CallExpression | NewExpression; -export type ImportClauseOrBindingPattern = ImportClause | BindingPattern; -export type AnyImportSyntax = ImportDeclaration | ImportEqualsDeclaration; -export type Declaration = DeclarationBase; -export type ClassElement = ClassElementBase; -export type TypeElement = TypeElementBase; -export type ObjectLiteralElement = ObjectLiteralElementBase; -export type JSDocTag = JSDocTagBase; -export type ArrayBindingElement = BindingElement | OmittedExpression; -export type AssertionExpression = TypeAssertion | AsExpression; -export type BooleanLiteral = TrueLiteral | FalseLiteral; -export type ConciseBody = Block | Expression; -export type DestructuringAssignment = ObjectDestructuringAssignment | ArrayDestructuringAssignment; -export type LiteralToken = NumericLiteral | BigIntLiteral | StringLiteral | JsxText | RegularExpressionLiteral | NoSubstitutionTemplateLiteral; -export type Modifier = AbstractKeyword | AccessorKeyword | AsyncKeyword | ConstKeyword | DeclareKeyword | DefaultKeyword | ExportKeyword | InKeyword | PrivateKeyword | ProtectedKeyword | PublicKeyword | ReadonlyKeyword | OutKeyword | OverrideKeyword | StaticKeyword; -export type ObjectLiteralElementLike = PropertyAssignment | ShorthandPropertyAssignment | SpreadAssignment | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration; -export type PropertyNameLiteral = Identifier | StringLiteral | NumericLiteral; -export type PseudoLiteralToken = TemplateHead | TemplateMiddle | TemplateTail; -export type TemplateLiteralToken = NoSubstitutionTemplateLiteral | PseudoLiteralToken; -export type ArrayDestructuringAssignment = BinaryExpression; -export type ObjectDestructuringAssignment = BinaryExpression; -export type FunctionBody = Block; -export type IncrementExpression = UpdateExpressionBase; - -export interface ForInStatement extends StatementBase { - readonly kind: SyntaxKind.ForInStatement; - readonly awaitModifier?: AwaitKeyword; - readonly initializer: ForInitializer; - readonly expression: Expression; - readonly statement: Statement; -} -export interface ForOfStatement extends StatementBase { - readonly kind: SyntaxKind.ForOfStatement; - readonly awaitModifier?: AwaitKeyword; - readonly initializer: ForInitializer; - readonly expression: Expression; - readonly statement: Statement; -} -export interface CaseClause extends NodeBase { - readonly kind: SyntaxKind.CaseClause; - readonly expression: Expression; - readonly statements: NodeArray; -} -export interface DefaultClause extends NodeBase { - readonly kind: SyntaxKind.DefaultClause; - readonly expression: Expression; - readonly statements: NodeArray; -} -export interface ObjectBindingPattern extends NodeBase { - readonly kind: SyntaxKind.ObjectBindingPattern; - readonly elements: NodeArray; -} -export interface ArrayBindingPattern extends NodeBase { - readonly kind: SyntaxKind.ArrayBindingPattern; - readonly elements: NodeArray; -} -export interface JSDocParameterTag extends JSDocTagBase { - readonly kind: SyntaxKind.JSDocParameterTag; - readonly name: EntityName; - readonly isBracketed: boolean; - readonly typeExpression?: TypeNode; - readonly isNameFirst: boolean; -} -export interface JSDocPropertyTag extends JSDocTagBase { - readonly kind: SyntaxKind.JSDocPropertyTag; - readonly name: EntityName; - readonly isBracketed: boolean; - readonly typeExpression?: TypeNode; - readonly isNameFirst: boolean; -} -export type ForInOrOfStatement = ForInStatement | ForOfStatement; -export type CaseOrDefaultClause = CaseClause | DefaultClause; -export type BindingPattern = ObjectBindingPattern | ArrayBindingPattern; -export type JSDocParameterOrPropertyTag = JSDocParameterTag | JSDocPropertyTag; -export type EndOfFile = Token; -export type DotToken = Token; -export type DotDotDotToken = Token; -export type QuestionToken = Token; -export type ExclamationToken = Token; -export type ColonToken = Token; -export type EqualsToken = Token; -export type AsteriskToken = Token; -export type EqualsGreaterThanToken = Token; -export type PlusToken = Token; -export type MinusToken = Token; -export type QuestionDotToken = Token; -export type AssertsKeyword = Token; -export type AssertKeyword = Token; -export type AwaitKeyword = Token; -export type CaseKeyword = Token; -export type AbstractKeyword = Token; -export type AccessorKeyword = Token; -export type AsyncKeyword = Token; -export type ConstKeyword = Token; -export type DeclareKeyword = Token; -export type DefaultKeyword = Token; -export type ExportKeyword = Token; -export type InKeyword = Token; -export type PrivateKeyword = Token; -export type ProtectedKeyword = Token; -export type PublicKeyword = Token; -export type ReadonlyKeyword = Token; -export type OutKeyword = Token; -export type OverrideKeyword = Token; -export type StaticKeyword = Token; -export type BinaryOperatorToken = Token; -export type AssignmentOperatorToken = Token; -export type NullLiteral = KeywordExpression; -export type TrueLiteral = KeywordExpression; -export type FalseLiteral = KeywordExpression; -export type ThisExpression = KeywordExpression; -export type SuperExpression = KeywordExpression; -export type ImportExpression = KeywordExpression; -export type StatementList = NodeArray; -export type CaseClausesList = NodeArray; -export type VariableDeclarationNodeList = NodeArray; -export type BindingElementList = NodeArray; -export type TypeParameterList = NodeArray; -export type ParameterList = NodeArray; -export type HeritageClauseList = NodeArray; -export type ClassElementList = NodeArray; -export type TypeElementList = NodeArray; -export type ExpressionWithTypeArgumentsList = NodeArray; -export type HeritageClauseElementList = NodeArray; -export type EnumMemberList = NodeArray; -export type ImportSpecifierList = NodeArray; -export type ExportSpecifierList = NodeArray; -export type TypeArgumentList = NodeArray; -export type ArgumentList = NodeArray; -export type TemplateSpanList = NodeArray; -export type ElementList = NodeArray; -export type PropertyDefinitionList = NodeArray; -export type TypeList = NodeArray; -export type ImportAttributeList = NodeArray; -export type TemplateLiteralTypeSpanList = NodeArray; -export type JsxChildList = NodeArray; -export type JsxAttributeList = NodeArray; +// Code generated by tools/scripts/tsc/generate-ts-ast.ts. DO NOT EDIT. + +import type { ModifierFlags } from "#enums/modifierFlags"; +import type { NodeFlags } from "#enums/nodeFlags"; +import { SyntaxKind } from "#enums/syntaxKind"; +import { TokenFlags } from "#enums/tokenFlags"; +import type { + JsxTagNamePropertyAccess, + Node, + NodeArray, +} from "./ast.ts"; +export type TriviaSyntaxKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia | SyntaxKind.NewLineTrivia | SyntaxKind.WhitespaceTrivia | SyntaxKind.ConflictMarkerTrivia; +export type LiteralSyntaxKind = SyntaxKind.NumericLiteral | SyntaxKind.BigIntLiteral | SyntaxKind.StringLiteral | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.RegularExpressionLiteral | SyntaxKind.NoSubstitutionTemplateLiteral; +export type PseudoLiteralSyntaxKind = SyntaxKind.TemplateHead | SyntaxKind.TemplateMiddle | SyntaxKind.TemplateTail; +export type PunctuationSyntaxKind = + | SyntaxKind.OpenBraceToken + | SyntaxKind.CloseBraceToken + | SyntaxKind.OpenParenToken + | SyntaxKind.CloseParenToken + | SyntaxKind.OpenBracketToken + | SyntaxKind.CloseBracketToken + | SyntaxKind.DotToken + | SyntaxKind.DotDotDotToken + | SyntaxKind.SemicolonToken + | SyntaxKind.CommaToken + | SyntaxKind.QuestionDotToken + | SyntaxKind.LessThanToken + | SyntaxKind.LessThanSlashToken + | SyntaxKind.GreaterThanToken + | SyntaxKind.LessThanEqualsToken + | SyntaxKind.GreaterThanEqualsToken + | SyntaxKind.EqualsEqualsToken + | SyntaxKind.ExclamationEqualsToken + | SyntaxKind.EqualsEqualsEqualsToken + | SyntaxKind.ExclamationEqualsEqualsToken + | SyntaxKind.EqualsGreaterThanToken + | SyntaxKind.PlusToken + | SyntaxKind.MinusToken + | SyntaxKind.AsteriskToken + | SyntaxKind.AsteriskAsteriskToken + | SyntaxKind.SlashToken + | SyntaxKind.PercentToken + | SyntaxKind.PlusPlusToken + | SyntaxKind.MinusMinusToken + | SyntaxKind.LessThanLessThanToken + | SyntaxKind.GreaterThanGreaterThanToken + | SyntaxKind.GreaterThanGreaterThanGreaterThanToken + | SyntaxKind.AmpersandToken + | SyntaxKind.BarToken + | SyntaxKind.CaretToken + | SyntaxKind.ExclamationToken + | SyntaxKind.TildeToken + | SyntaxKind.AmpersandAmpersandToken + | SyntaxKind.BarBarToken + | SyntaxKind.QuestionToken + | SyntaxKind.ColonToken + | SyntaxKind.AtToken + | SyntaxKind.QuestionQuestionToken + | SyntaxKind.BacktickToken + | SyntaxKind.HashToken + | SyntaxKind.EqualsToken + | SyntaxKind.PlusEqualsToken + | SyntaxKind.MinusEqualsToken + | SyntaxKind.AsteriskEqualsToken + | SyntaxKind.AsteriskAsteriskEqualsToken + | SyntaxKind.SlashEqualsToken + | SyntaxKind.PercentEqualsToken + | SyntaxKind.LessThanLessThanEqualsToken + | SyntaxKind.GreaterThanGreaterThanEqualsToken + | SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken + | SyntaxKind.AmpersandEqualsToken + | SyntaxKind.BarEqualsToken + | SyntaxKind.BarBarEqualsToken + | SyntaxKind.AmpersandAmpersandEqualsToken + | SyntaxKind.QuestionQuestionEqualsToken + | SyntaxKind.CaretEqualsToken; +export type KeywordSyntaxKind = + | SyntaxKind.BreakKeyword + | SyntaxKind.CaseKeyword + | SyntaxKind.CatchKeyword + | SyntaxKind.ClassKeyword + | SyntaxKind.ConstKeyword + | SyntaxKind.ContinueKeyword + | SyntaxKind.DebuggerKeyword + | SyntaxKind.DefaultKeyword + | SyntaxKind.DeleteKeyword + | SyntaxKind.DoKeyword + | SyntaxKind.ElseKeyword + | SyntaxKind.EnumKeyword + | SyntaxKind.ExportKeyword + | SyntaxKind.ExtendsKeyword + | SyntaxKind.FalseKeyword + | SyntaxKind.FinallyKeyword + | SyntaxKind.ForKeyword + | SyntaxKind.FunctionKeyword + | SyntaxKind.IfKeyword + | SyntaxKind.ImportKeyword + | SyntaxKind.InKeyword + | SyntaxKind.InstanceOfKeyword + | SyntaxKind.NewKeyword + | SyntaxKind.NullKeyword + | SyntaxKind.ReturnKeyword + | SyntaxKind.SuperKeyword + | SyntaxKind.SwitchKeyword + | SyntaxKind.ThisKeyword + | SyntaxKind.ThrowKeyword + | SyntaxKind.TrueKeyword + | SyntaxKind.TryKeyword + | SyntaxKind.TypeOfKeyword + | SyntaxKind.VarKeyword + | SyntaxKind.VoidKeyword + | SyntaxKind.WhileKeyword + | SyntaxKind.WithKeyword + | SyntaxKind.ImplementsKeyword + | SyntaxKind.InterfaceKeyword + | SyntaxKind.LetKeyword + | SyntaxKind.PackageKeyword + | SyntaxKind.PrivateKeyword + | SyntaxKind.ProtectedKeyword + | SyntaxKind.PublicKeyword + | SyntaxKind.StaticKeyword + | SyntaxKind.YieldKeyword + | SyntaxKind.AbstractKeyword + | SyntaxKind.AccessorKeyword + | SyntaxKind.AsKeyword + | SyntaxKind.AssertsKeyword + | SyntaxKind.AssertKeyword + | SyntaxKind.AnyKeyword + | SyntaxKind.AsyncKeyword + | SyntaxKind.AwaitKeyword + | SyntaxKind.BooleanKeyword + | SyntaxKind.ConstructorKeyword + | SyntaxKind.DeclareKeyword + | SyntaxKind.GetKeyword + | SyntaxKind.ImmediateKeyword + | SyntaxKind.InferKeyword + | SyntaxKind.IntrinsicKeyword + | SyntaxKind.IsKeyword + | SyntaxKind.KeyOfKeyword + | SyntaxKind.ModuleKeyword + | SyntaxKind.NamespaceKeyword + | SyntaxKind.NeverKeyword + | SyntaxKind.OutKeyword + | SyntaxKind.ReadonlyKeyword + | SyntaxKind.RequireKeyword + | SyntaxKind.NumberKeyword + | SyntaxKind.ObjectKeyword + | SyntaxKind.SatisfiesKeyword + | SyntaxKind.SetKeyword + | SyntaxKind.StringKeyword + | SyntaxKind.SymbolKeyword + | SyntaxKind.TypeKeyword + | SyntaxKind.UndefinedKeyword + | SyntaxKind.UniqueKeyword + | SyntaxKind.UnknownKeyword + | SyntaxKind.UsingKeyword + | SyntaxKind.FromKeyword + | SyntaxKind.GlobalKeyword + | SyntaxKind.BigIntKeyword + | SyntaxKind.OverrideKeyword + | SyntaxKind.OfKeyword + | SyntaxKind.DeferKeyword; +export type ModifierSyntaxKind = SyntaxKind.AbstractKeyword | SyntaxKind.AccessorKeyword | SyntaxKind.AsyncKeyword | SyntaxKind.ConstKeyword | SyntaxKind.DeclareKeyword | SyntaxKind.DefaultKeyword | SyntaxKind.ExportKeyword | SyntaxKind.InKeyword | SyntaxKind.PrivateKeyword | SyntaxKind.ProtectedKeyword | SyntaxKind.PublicKeyword | SyntaxKind.ReadonlyKeyword | SyntaxKind.OutKeyword | SyntaxKind.OverrideKeyword | SyntaxKind.StaticKeyword; +export type KeywordTypeSyntaxKind = SyntaxKind.AnyKeyword | SyntaxKind.BigIntKeyword | SyntaxKind.BooleanKeyword | SyntaxKind.IntrinsicKeyword | SyntaxKind.NeverKeyword | SyntaxKind.NumberKeyword | SyntaxKind.ObjectKeyword | SyntaxKind.StringKeyword | SyntaxKind.SymbolKeyword | SyntaxKind.UndefinedKeyword | SyntaxKind.UnknownKeyword | SyntaxKind.VoidKeyword; +export type KeywordExpressionSyntaxKind = SyntaxKind.NullKeyword | SyntaxKind.TrueKeyword | SyntaxKind.FalseKeyword | SyntaxKind.ThisKeyword | SyntaxKind.SuperKeyword | SyntaxKind.ImportKeyword; +export type TokenSyntaxKind = + | SyntaxKind.Unknown + | SyntaxKind.EndOfFile + | SyntaxKind.SingleLineCommentTrivia + | SyntaxKind.MultiLineCommentTrivia + | SyntaxKind.NewLineTrivia + | SyntaxKind.WhitespaceTrivia + | SyntaxKind.ConflictMarkerTrivia + | SyntaxKind.NonTextFileMarkerTrivia + | SyntaxKind.NumericLiteral + | SyntaxKind.BigIntLiteral + | SyntaxKind.StringLiteral + | SyntaxKind.JsxText + | SyntaxKind.JsxTextAllWhiteSpaces + | SyntaxKind.RegularExpressionLiteral + | SyntaxKind.NoSubstitutionTemplateLiteral + | SyntaxKind.TemplateHead + | SyntaxKind.TemplateMiddle + | SyntaxKind.TemplateTail + | SyntaxKind.OpenBraceToken + | SyntaxKind.CloseBraceToken + | SyntaxKind.OpenParenToken + | SyntaxKind.CloseParenToken + | SyntaxKind.OpenBracketToken + | SyntaxKind.CloseBracketToken + | SyntaxKind.DotToken + | SyntaxKind.DotDotDotToken + | SyntaxKind.SemicolonToken + | SyntaxKind.CommaToken + | SyntaxKind.QuestionDotToken + | SyntaxKind.LessThanToken + | SyntaxKind.LessThanSlashToken + | SyntaxKind.GreaterThanToken + | SyntaxKind.LessThanEqualsToken + | SyntaxKind.GreaterThanEqualsToken + | SyntaxKind.EqualsEqualsToken + | SyntaxKind.ExclamationEqualsToken + | SyntaxKind.EqualsEqualsEqualsToken + | SyntaxKind.ExclamationEqualsEqualsToken + | SyntaxKind.EqualsGreaterThanToken + | SyntaxKind.PlusToken + | SyntaxKind.MinusToken + | SyntaxKind.AsteriskToken + | SyntaxKind.AsteriskAsteriskToken + | SyntaxKind.SlashToken + | SyntaxKind.PercentToken + | SyntaxKind.PlusPlusToken + | SyntaxKind.MinusMinusToken + | SyntaxKind.LessThanLessThanToken + | SyntaxKind.GreaterThanGreaterThanToken + | SyntaxKind.GreaterThanGreaterThanGreaterThanToken + | SyntaxKind.AmpersandToken + | SyntaxKind.BarToken + | SyntaxKind.CaretToken + | SyntaxKind.ExclamationToken + | SyntaxKind.TildeToken + | SyntaxKind.AmpersandAmpersandToken + | SyntaxKind.BarBarToken + | SyntaxKind.QuestionToken + | SyntaxKind.ColonToken + | SyntaxKind.AtToken + | SyntaxKind.QuestionQuestionToken + | SyntaxKind.BacktickToken + | SyntaxKind.HashToken + | SyntaxKind.EqualsToken + | SyntaxKind.PlusEqualsToken + | SyntaxKind.MinusEqualsToken + | SyntaxKind.AsteriskEqualsToken + | SyntaxKind.AsteriskAsteriskEqualsToken + | SyntaxKind.SlashEqualsToken + | SyntaxKind.PercentEqualsToken + | SyntaxKind.LessThanLessThanEqualsToken + | SyntaxKind.GreaterThanGreaterThanEqualsToken + | SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken + | SyntaxKind.AmpersandEqualsToken + | SyntaxKind.BarEqualsToken + | SyntaxKind.BarBarEqualsToken + | SyntaxKind.AmpersandAmpersandEqualsToken + | SyntaxKind.QuestionQuestionEqualsToken + | SyntaxKind.CaretEqualsToken + | SyntaxKind.Identifier + | SyntaxKind.PrivateIdentifier + | SyntaxKind.JSDocCommentTextToken + | SyntaxKind.BreakKeyword + | SyntaxKind.CaseKeyword + | SyntaxKind.CatchKeyword + | SyntaxKind.ClassKeyword + | SyntaxKind.ConstKeyword + | SyntaxKind.ContinueKeyword + | SyntaxKind.DebuggerKeyword + | SyntaxKind.DefaultKeyword + | SyntaxKind.DeleteKeyword + | SyntaxKind.DoKeyword + | SyntaxKind.ElseKeyword + | SyntaxKind.EnumKeyword + | SyntaxKind.ExportKeyword + | SyntaxKind.ExtendsKeyword + | SyntaxKind.FalseKeyword + | SyntaxKind.FinallyKeyword + | SyntaxKind.ForKeyword + | SyntaxKind.FunctionKeyword + | SyntaxKind.IfKeyword + | SyntaxKind.ImportKeyword + | SyntaxKind.InKeyword + | SyntaxKind.InstanceOfKeyword + | SyntaxKind.NewKeyword + | SyntaxKind.NullKeyword + | SyntaxKind.ReturnKeyword + | SyntaxKind.SuperKeyword + | SyntaxKind.SwitchKeyword + | SyntaxKind.ThisKeyword + | SyntaxKind.ThrowKeyword + | SyntaxKind.TrueKeyword + | SyntaxKind.TryKeyword + | SyntaxKind.TypeOfKeyword + | SyntaxKind.VarKeyword + | SyntaxKind.VoidKeyword + | SyntaxKind.WhileKeyword + | SyntaxKind.WithKeyword + | SyntaxKind.ImplementsKeyword + | SyntaxKind.InterfaceKeyword + | SyntaxKind.LetKeyword + | SyntaxKind.PackageKeyword + | SyntaxKind.PrivateKeyword + | SyntaxKind.ProtectedKeyword + | SyntaxKind.PublicKeyword + | SyntaxKind.StaticKeyword + | SyntaxKind.YieldKeyword + | SyntaxKind.AbstractKeyword + | SyntaxKind.AccessorKeyword + | SyntaxKind.AsKeyword + | SyntaxKind.AssertsKeyword + | SyntaxKind.AssertKeyword + | SyntaxKind.AnyKeyword + | SyntaxKind.AsyncKeyword + | SyntaxKind.AwaitKeyword + | SyntaxKind.BooleanKeyword + | SyntaxKind.ConstructorKeyword + | SyntaxKind.DeclareKeyword + | SyntaxKind.GetKeyword + | SyntaxKind.ImmediateKeyword + | SyntaxKind.InferKeyword + | SyntaxKind.IntrinsicKeyword + | SyntaxKind.IsKeyword + | SyntaxKind.KeyOfKeyword + | SyntaxKind.ModuleKeyword + | SyntaxKind.NamespaceKeyword + | SyntaxKind.NeverKeyword + | SyntaxKind.OutKeyword + | SyntaxKind.ReadonlyKeyword + | SyntaxKind.RequireKeyword + | SyntaxKind.NumberKeyword + | SyntaxKind.ObjectKeyword + | SyntaxKind.SatisfiesKeyword + | SyntaxKind.SetKeyword + | SyntaxKind.StringKeyword + | SyntaxKind.SymbolKeyword + | SyntaxKind.TypeKeyword + | SyntaxKind.UndefinedKeyword + | SyntaxKind.UniqueKeyword + | SyntaxKind.UnknownKeyword + | SyntaxKind.UsingKeyword + | SyntaxKind.FromKeyword + | SyntaxKind.GlobalKeyword + | SyntaxKind.BigIntKeyword + | SyntaxKind.OverrideKeyword + | SyntaxKind.OfKeyword + | SyntaxKind.DeferKeyword; +export type JsxTokenSyntaxKind = SyntaxKind.LessThanSlashToken | SyntaxKind.EndOfFile | SyntaxKind.ConflictMarkerTrivia | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.OpenBraceToken | SyntaxKind.LessThanToken; +export type JSDocNodeSyntaxKind = + | SyntaxKind.JSDocTypeExpression + | SyntaxKind.JSDocNameReference + | SyntaxKind.JSDocAllType + | SyntaxKind.JSDocNullableType + | SyntaxKind.JSDocNonNullableType + | SyntaxKind.JSDocOptionalType + | SyntaxKind.JSDocVariadicType + | SyntaxKind.JSDoc + | SyntaxKind.JSDocText + | SyntaxKind.JSDocTypeLiteral + | SyntaxKind.JSDocSignature + | SyntaxKind.JSDocLink + | SyntaxKind.JSDocLinkCode + | SyntaxKind.JSDocLinkPlain + | SyntaxKind.JSDocUnknownTag + | SyntaxKind.JSDocAugmentsTag + | SyntaxKind.JSDocImplementsTag + | SyntaxKind.JSDocDeprecatedTag + | SyntaxKind.JSDocPublicTag + | SyntaxKind.JSDocPrivateTag + | SyntaxKind.JSDocProtectedTag + | SyntaxKind.JSDocReadonlyTag + | SyntaxKind.JSDocOverrideTag + | SyntaxKind.JSDocCallbackTag + | SyntaxKind.JSDocOverloadTag + | SyntaxKind.JSDocParameterTag + | SyntaxKind.JSDocReturnTag + | SyntaxKind.JSDocThisTag + | SyntaxKind.JSDocTypeTag + | SyntaxKind.JSDocTemplateTag + | SyntaxKind.JSDocTypedefTag + | SyntaxKind.JSDocSeeTag + | SyntaxKind.JSDocPropertyTag + | SyntaxKind.JSDocThrowsTag + | SyntaxKind.JSDocSatisfiesTag + | SyntaxKind.JSDocImportTag; +export type ImportPhaseModifierSyntaxKind = SyntaxKind.TypeKeyword | SyntaxKind.DeferKeyword; +export type PostfixUnaryOperator = SyntaxKind.PlusPlusToken | SyntaxKind.MinusMinusToken; +export type PrefixUnaryOperator = SyntaxKind.PlusToken | SyntaxKind.MinusToken | SyntaxKind.TildeToken | SyntaxKind.ExclamationToken | SyntaxKind.PlusPlusToken | SyntaxKind.MinusMinusToken; +export type AssignmentOperator = SyntaxKind.EqualsToken | CompoundAssignmentOperator; +export type BinaryOperator = AssignmentOperatorOrHigher | SyntaxKind.CommaToken; +export type ExponentiationOperator = SyntaxKind.AsteriskAsteriskToken; +export type MultiplicativeOperator = SyntaxKind.AsteriskToken | SyntaxKind.SlashToken | SyntaxKind.PercentToken; +export type MultiplicativeOperatorOrHigher = ExponentiationOperator | MultiplicativeOperator; +export type AdditiveOperator = SyntaxKind.PlusToken | SyntaxKind.MinusToken; +export type AdditiveOperatorOrHigher = MultiplicativeOperatorOrHigher | AdditiveOperator; +export type ShiftOperator = SyntaxKind.LessThanLessThanToken | SyntaxKind.GreaterThanGreaterThanToken | SyntaxKind.GreaterThanGreaterThanGreaterThanToken; +export type ShiftOperatorOrHigher = AdditiveOperatorOrHigher | ShiftOperator; +export type RelationalOperator = SyntaxKind.LessThanToken | SyntaxKind.LessThanEqualsToken | SyntaxKind.GreaterThanToken | SyntaxKind.GreaterThanEqualsToken | SyntaxKind.InstanceOfKeyword | SyntaxKind.InKeyword; +export type RelationalOperatorOrHigher = ShiftOperatorOrHigher | RelationalOperator; +export type EqualityOperator = SyntaxKind.EqualsEqualsToken | SyntaxKind.EqualsEqualsEqualsToken | SyntaxKind.ExclamationEqualsEqualsToken | SyntaxKind.ExclamationEqualsToken; +export type EqualityOperatorOrHigher = RelationalOperatorOrHigher | EqualityOperator; +export type BitwiseOperator = SyntaxKind.AmpersandToken | SyntaxKind.BarToken | SyntaxKind.CaretToken; +export type BitwiseOperatorOrHigher = EqualityOperatorOrHigher | BitwiseOperator; +export type LogicalOperator = SyntaxKind.AmpersandAmpersandToken | SyntaxKind.BarBarToken; +export type LogicalOperatorOrHigher = BitwiseOperatorOrHigher | LogicalOperator; +export type CompoundAssignmentOperator = SyntaxKind.PlusEqualsToken | SyntaxKind.MinusEqualsToken | SyntaxKind.AsteriskAsteriskEqualsToken | SyntaxKind.AsteriskEqualsToken | SyntaxKind.SlashEqualsToken | SyntaxKind.PercentEqualsToken | SyntaxKind.AmpersandEqualsToken | SyntaxKind.BarEqualsToken | SyntaxKind.CaretEqualsToken | SyntaxKind.LessThanLessThanEqualsToken | SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken | SyntaxKind.GreaterThanGreaterThanEqualsToken | SyntaxKind.BarBarEqualsToken | SyntaxKind.AmpersandAmpersandEqualsToken | SyntaxKind.QuestionQuestionEqualsToken; +export type AssignmentOperatorOrHigher = SyntaxKind.QuestionQuestionToken | LogicalOperatorOrHigher | AssignmentOperator; +export type LogicalOrCoalescingAssignmentOperator = SyntaxKind.AmpersandAmpersandEqualsToken | SyntaxKind.BarBarEqualsToken | SyntaxKind.QuestionQuestionEqualsToken; + +export interface NodeBase extends Node { + readonly flags: NodeFlags; +} +export interface StatementBase extends NodeBase { + readonly _statementBrand: any; +} +export interface IterationStatementBase extends StatementBase { + readonly statement: Statement; +} +export interface ExpressionBase extends NodeBase { + readonly _expressionBrand: any; +} +export interface UnaryExpressionBase extends ExpressionBase { + readonly _unaryExpressionBrand: any; +} +export interface UpdateExpressionBase extends UnaryExpressionBase { + readonly _updateExpressionBrand: any; +} +export interface LeftHandSideExpressionBase extends UpdateExpressionBase { + readonly _leftHandSideExpressionBrand: any; +} +export interface MemberExpressionBase extends LeftHandSideExpressionBase { + readonly _memberExpressionBrand: any; +} +export interface PrimaryExpressionBase extends MemberExpressionBase { + readonly _primaryExpressionBrand: any; +} +export interface TypeNodeBase extends NodeBase { + readonly _typeNodeBrand: any; +} +export interface NodeWithTypeArgumentsBase extends TypeNodeBase { + readonly typeArguments?: NodeArray; +} +export interface JSDocTypeBase extends TypeNodeBase { + readonly _jsDocTypeBrand: any; +} +export interface DeclarationBase extends Node { + readonly _declarationBrand: any; +} +export interface ModifiersBase extends Node { + readonly modifiers?: NodeArray; + readonly modifierFlags: ModifierFlags; +} +export interface FunctionLikeBase extends Node { + readonly _declarationBrand: any; + readonly typeParameters?: NodeArray; + readonly parameters: NodeArray; + readonly type?: TypeNode; + readonly fullSignature?: TypeNode; +} +export interface BodyBase extends Node { + readonly asteriskToken?: AsteriskToken; + readonly body?: NodeBody; +} +export interface FunctionLikeWithBodyBase extends FunctionLikeBase, BodyBase { + readonly _functionLikeDeclarationBrand: any; + readonly body?: BlockOrExpression; +} +export interface ClassLikeBase extends ModifiersBase { + readonly _declarationBrand: any; + readonly name?: Identifier; + readonly typeParameters?: NodeArray; + readonly heritageClauses?: NodeArray; + readonly members: NodeArray; +} +export interface LiteralLikeNodeBase extends Node { + readonly text: string; + readonly tokenFlags: TokenFlags; +} +export interface LiteralExpressionBase extends PrimaryExpressionBase, LiteralLikeNodeBase { + readonly _literalExpressionBrand: any; +} +export interface TemplateLiteralLikeNodeBase extends LiteralLikeNodeBase { + readonly rawText: string; + readonly templateFlags: TokenFlags; +} +export interface TypeElementBase extends Node { + readonly _typeElementBrand: any; +} +export interface ClassElementBase extends Node { + readonly _classElementBrand: any; +} +export interface NamedMemberBase extends ModifiersBase { + readonly _declarationBrand: any; + readonly name: PropertyName; + readonly postfixToken?: QuestionToken | ExclamationToken; +} +export interface ObjectLiteralElementBase extends Node { + readonly _objectLiteralBrand: any; +} +export interface UnionOrIntersectionTypeNodeBase extends TypeNodeBase { + readonly types: NodeArray; +} +export interface JSDocTagBase extends NodeBase { + readonly tagName: Identifier; + readonly comment?: NodeArray; +} +export interface JSDocCommentBase extends NodeBase { + readonly text: string; +} + +export interface Token extends NodeBase { + readonly kind: TKind; +} +export interface Identifier extends PrimaryExpressionBase { + readonly kind: SyntaxKind.Identifier; + readonly text: string; +} +export interface PrivateIdentifier extends PrimaryExpressionBase { + readonly kind: SyntaxKind.PrivateIdentifier; + readonly text: string; +} +export interface QualifiedName extends NodeBase { + readonly kind: SyntaxKind.QualifiedName; + readonly left: EntityName; + readonly right: MemberName; +} +export interface ComputedPropertyName extends NodeBase { + readonly kind: SyntaxKind.ComputedPropertyName; + readonly expression: Expression; +} +export interface Decorator extends NodeBase { + readonly kind: SyntaxKind.Decorator; + readonly expression: LeftHandSideExpression; +} +export interface EmptyStatement extends StatementBase { + readonly kind: SyntaxKind.EmptyStatement; +} +export interface IfStatement extends StatementBase { + readonly kind: SyntaxKind.IfStatement; + readonly expression: Expression; + readonly thenStatement: Statement; + readonly elseStatement?: Statement; +} +export interface DoStatement extends IterationStatementBase { + readonly kind: SyntaxKind.DoStatement; + readonly expression: Expression; +} +export interface WhileStatement extends IterationStatementBase { + readonly kind: SyntaxKind.WhileStatement; + readonly expression: Expression; +} +export interface ForStatement extends IterationStatementBase { + readonly kind: SyntaxKind.ForStatement; + readonly initializer?: ForInitializer; + readonly condition?: Expression; + readonly incrementor?: Expression; +} +export interface BreakStatement extends StatementBase { + readonly kind: SyntaxKind.BreakStatement; + readonly label?: Identifier; +} +export interface ContinueStatement extends StatementBase { + readonly kind: SyntaxKind.ContinueStatement; + readonly label?: Identifier; +} +export interface ReturnStatement extends StatementBase { + readonly kind: SyntaxKind.ReturnStatement; + readonly expression?: Expression; +} +export interface WithStatement extends StatementBase { + readonly kind: SyntaxKind.WithStatement; + readonly expression: Expression; + readonly statement: Statement; +} +export interface SwitchStatement extends StatementBase { + readonly kind: SyntaxKind.SwitchStatement; + readonly expression: Expression; + readonly caseBlock: CaseBlock; +} +export interface CaseBlock extends NodeBase { + readonly kind: SyntaxKind.CaseBlock; + readonly clauses: NodeArray; +} +export interface ThrowStatement extends StatementBase { + readonly kind: SyntaxKind.ThrowStatement; + readonly expression: Expression; +} +export interface TryStatement extends StatementBase { + readonly kind: SyntaxKind.TryStatement; + readonly tryBlock: Block; + readonly catchClause?: CatchClause; + readonly finallyBlock?: Block; +} +export interface CatchClause extends NodeBase { + readonly kind: SyntaxKind.CatchClause; + readonly variableDeclaration?: VariableDeclaration; + readonly block: Block; +} +export interface DebuggerStatement extends StatementBase { + readonly kind: SyntaxKind.DebuggerStatement; +} +export interface LabeledStatement extends StatementBase { + readonly kind: SyntaxKind.LabeledStatement; + readonly label: Identifier; + readonly statement: Statement; +} +export interface ExpressionStatement extends StatementBase { + readonly kind: SyntaxKind.ExpressionStatement; + readonly expression: Expression; +} +export interface Block extends StatementBase { + readonly kind: SyntaxKind.Block; + readonly statements: NodeArray; + readonly multiLine: boolean; +} +export interface VariableStatement extends StatementBase, ModifiersBase { + readonly kind: SyntaxKind.VariableStatement; + readonly declarationList: VariableDeclarationList; +} +export interface VariableDeclaration extends NodeBase, DeclarationBase { + readonly kind: SyntaxKind.VariableDeclaration; + readonly name: BindingName; + readonly exclamationToken?: ExclamationToken; + readonly type?: TypeNode; + readonly initializer?: Expression; +} +export interface VariableDeclarationList extends NodeBase { + readonly kind: SyntaxKind.VariableDeclarationList; + readonly declarations: NodeArray; +} +export interface ParameterDeclaration extends NodeBase, DeclarationBase, ModifiersBase { + readonly kind: SyntaxKind.Parameter; + readonly dotDotDotToken?: DotDotDotToken; + readonly name: BindingName; + readonly questionToken?: QuestionToken; + readonly type?: TypeNode; + readonly initializer?: Expression; +} +export interface BindingElement extends NodeBase, DeclarationBase { + readonly kind: SyntaxKind.BindingElement; + readonly dotDotDotToken?: DotDotDotToken; + readonly propertyName?: PropertyName; + readonly name?: BindingName; + readonly initializer?: Expression; +} +export interface MissingDeclaration extends StatementBase, DeclarationBase, ModifiersBase { + readonly kind: SyntaxKind.MissingDeclaration; +} +export interface FunctionDeclaration extends StatementBase, DeclarationBase, ModifiersBase, FunctionLikeWithBodyBase { + readonly kind: SyntaxKind.FunctionDeclaration; + readonly name?: Identifier; + readonly body?: FunctionBody; +} +export interface ClassDeclaration extends StatementBase, DeclarationBase, ClassLikeBase { + readonly kind: SyntaxKind.ClassDeclaration; +} +export interface ClassExpression extends PrimaryExpressionBase, DeclarationBase, ClassLikeBase { + readonly kind: SyntaxKind.ClassExpression; +} +export interface HeritageClause extends NodeBase { + readonly kind: SyntaxKind.HeritageClause; + readonly token: SyntaxKind.ExtendsKeyword | SyntaxKind.ImplementsKeyword; + readonly types: NodeArray; +} +export interface InterfaceDeclaration extends StatementBase, DeclarationBase, ModifiersBase { + readonly kind: SyntaxKind.InterfaceDeclaration; + readonly name: Identifier; + readonly typeParameters?: NodeArray; + readonly heritageClauses?: NodeArray; + readonly members: NodeArray; +} +export interface TypeAliasDeclaration extends StatementBase, DeclarationBase, ModifiersBase { + readonly kind: SyntaxKind.TypeAliasDeclaration; + readonly name: Identifier; + readonly typeParameters?: NodeArray; + readonly type: TypeNode; +} +export interface EnumMember extends NodeBase, DeclarationBase, NamedMemberBase { + readonly kind: SyntaxKind.EnumMember; + readonly initializer?: Expression; +} +export interface EnumDeclaration extends StatementBase, DeclarationBase, ModifiersBase { + readonly kind: SyntaxKind.EnumDeclaration; + readonly name: Identifier; + readonly members: NodeArray; +} +export interface ModuleBlock extends StatementBase { + readonly kind: SyntaxKind.ModuleBlock; + readonly statements: NodeArray; +} +export interface NotEmittedStatement extends StatementBase { + readonly kind: SyntaxKind.NotEmittedStatement; +} +export interface NotEmittedTypeElement extends NodeBase, TypeElementBase { + readonly kind: SyntaxKind.NotEmittedTypeElement; +} +export interface ImportDeclaration extends StatementBase, ModifiersBase, DeclarationBase { + readonly kind: SyntaxKind.ImportDeclaration; + readonly importClause?: ImportClause; + readonly moduleSpecifier: Expression; + readonly attributes?: ImportAttributes; +} +export interface ExternalModuleReference extends NodeBase { + readonly kind: SyntaxKind.ExternalModuleReference; + readonly expression: Expression; +} +export interface NamespaceImport extends NodeBase, DeclarationBase { + readonly kind: SyntaxKind.NamespaceImport; + readonly name: Identifier; +} +export interface NamedImports extends NodeBase { + readonly kind: SyntaxKind.NamedImports; + readonly elements: NodeArray; +} +export interface ExportAssignment extends StatementBase, DeclarationBase, ModifiersBase { + readonly kind: SyntaxKind.ExportAssignment; + readonly isExportEquals: boolean; + readonly type: TypeNode; + readonly expression: Expression; +} +export interface NamespaceExportDeclaration extends StatementBase, DeclarationBase, ModifiersBase { + readonly kind: SyntaxKind.NamespaceExportDeclaration; + readonly name: Identifier; +} +export interface NamespaceExport extends NodeBase, DeclarationBase { + readonly kind: SyntaxKind.NamespaceExport; + readonly name: ModuleExportName; +} +export interface NamedExports extends NodeBase { + readonly kind: SyntaxKind.NamedExports; + readonly elements: NodeArray; +} +export interface ExportSpecifier extends NodeBase, DeclarationBase { + readonly kind: SyntaxKind.ExportSpecifier; + readonly isTypeOnly: boolean; + readonly propertyName?: ModuleExportName; + readonly name: ModuleExportName; +} +export interface CallSignatureDeclaration extends NodeBase, DeclarationBase, FunctionLikeBase, TypeElementBase { + readonly kind: SyntaxKind.CallSignature; +} +export interface ConstructSignatureDeclaration extends NodeBase, DeclarationBase, FunctionLikeBase, TypeElementBase { + readonly kind: SyntaxKind.ConstructSignature; +} +export interface ConstructorDeclaration extends NodeBase, DeclarationBase, ModifiersBase, FunctionLikeWithBodyBase, ClassElementBase { + readonly kind: SyntaxKind.Constructor; + readonly body?: FunctionBody; +} +export interface GetAccessorDeclaration extends NodeBase, DeclarationBase, NamedMemberBase, FunctionLikeWithBodyBase, TypeElementBase, ClassElementBase, ObjectLiteralElementBase { + readonly kind: SyntaxKind.GetAccessor; + readonly body?: FunctionBody; +} +export interface SetAccessorDeclaration extends NodeBase, DeclarationBase, NamedMemberBase, FunctionLikeWithBodyBase, TypeElementBase, ClassElementBase, ObjectLiteralElementBase { + readonly kind: SyntaxKind.SetAccessor; + readonly body?: FunctionBody; +} +export interface IndexSignatureDeclaration extends NodeBase, DeclarationBase, ModifiersBase, FunctionLikeBase, TypeElementBase, ClassElementBase { + readonly kind: SyntaxKind.IndexSignature; + readonly type: TypeNode; +} +export interface MethodSignatureDeclaration extends NodeBase, DeclarationBase, NamedMemberBase, FunctionLikeBase, TypeElementBase { + readonly kind: SyntaxKind.MethodSignature; +} +export interface MethodDeclaration extends NodeBase, DeclarationBase, NamedMemberBase, FunctionLikeWithBodyBase, ClassElementBase, ObjectLiteralElementBase { + readonly kind: SyntaxKind.MethodDeclaration; + readonly body?: FunctionBody; +} +export interface PropertySignatureDeclaration extends NodeBase, DeclarationBase, NamedMemberBase, TypeElementBase { + readonly kind: SyntaxKind.PropertySignature; + readonly type: TypeNode; + readonly initializer: Expression; +} +export interface PropertyDeclaration extends NodeBase, DeclarationBase, NamedMemberBase, ClassElementBase { + readonly kind: SyntaxKind.PropertyDeclaration; + readonly type?: TypeNode; + readonly initializer?: Expression; +} +export interface SemicolonClassElement extends NodeBase, DeclarationBase, ClassElementBase { + readonly kind: SyntaxKind.SemicolonClassElement; +} +export interface ClassStaticBlockDeclaration extends NodeBase, DeclarationBase, ModifiersBase, ClassElementBase { + readonly kind: SyntaxKind.ClassStaticBlockDeclaration; + readonly body: Block; +} +export interface OmittedExpression extends ExpressionBase { + readonly kind: SyntaxKind.OmittedExpression; +} +export interface KeywordExpression extends ExpressionBase { + readonly kind: TKind; +} +export interface StringLiteral extends LiteralExpressionBase { + readonly kind: SyntaxKind.StringLiteral; +} +export interface NumericLiteral extends LiteralExpressionBase { + readonly kind: SyntaxKind.NumericLiteral; +} +export interface BigIntLiteral extends LiteralExpressionBase { + readonly kind: SyntaxKind.BigIntLiteral; +} +export interface RegularExpressionLiteral extends LiteralExpressionBase { + readonly kind: SyntaxKind.RegularExpressionLiteral; +} +export interface NoSubstitutionTemplateLiteral extends ExpressionBase, TemplateLiteralLikeNodeBase, DeclarationBase { + readonly kind: SyntaxKind.NoSubstitutionTemplateLiteral; +} +export interface BinaryExpression extends ExpressionBase, DeclarationBase, ModifiersBase { + readonly kind: SyntaxKind.BinaryExpression; + readonly left: Expression; + readonly type?: TypeNode; + readonly operatorToken: BinaryOperatorToken; + readonly right: Expression; +} +export interface PrefixUnaryExpression extends UpdateExpressionBase { + readonly kind: SyntaxKind.PrefixUnaryExpression; + readonly operator: SyntaxKind.PlusToken | SyntaxKind.MinusToken | SyntaxKind.TildeToken | SyntaxKind.ExclamationToken | SyntaxKind.PlusPlusToken | SyntaxKind.MinusMinusToken; + readonly operand: Expression; +} +export interface PostfixUnaryExpression extends UpdateExpressionBase { + readonly kind: SyntaxKind.PostfixUnaryExpression; + readonly operand: Expression; + readonly operator: SyntaxKind.PlusPlusToken | SyntaxKind.MinusMinusToken; +} +export interface YieldExpression extends ExpressionBase { + readonly kind: SyntaxKind.YieldExpression; + readonly asteriskToken?: AsteriskToken; + readonly expression?: Expression; +} +export interface ArrowFunction extends ExpressionBase, DeclarationBase, ModifiersBase, FunctionLikeWithBodyBase { + readonly kind: SyntaxKind.ArrowFunction; + readonly equalsGreaterThanToken: EqualsGreaterThanToken; + readonly body: ConciseBody; +} +export interface FunctionExpression extends PrimaryExpressionBase, DeclarationBase, ModifiersBase, FunctionLikeWithBodyBase { + readonly kind: SyntaxKind.FunctionExpression; + readonly name?: Identifier; + readonly body: FunctionBody; +} +export interface AsExpression extends ExpressionBase { + readonly kind: SyntaxKind.AsExpression; + readonly expression: Expression; + readonly type: TypeNode; +} +export interface SatisfiesExpression extends ExpressionBase { + readonly kind: SyntaxKind.SatisfiesExpression; + readonly expression: Expression; + readonly type: TypeNode; +} +export interface ConditionalExpression extends ExpressionBase { + readonly kind: SyntaxKind.ConditionalExpression; + readonly condition: Expression; + readonly questionToken: QuestionToken; + readonly whenTrue: Expression; + readonly colonToken: ColonToken; + readonly whenFalse: Expression; +} +export interface PropertyAccessExpression extends MemberExpressionBase { + readonly kind: SyntaxKind.PropertyAccessExpression; + readonly expression: Expression; + readonly questionDotToken?: QuestionDotToken; + readonly name: MemberName; +} +export interface ElementAccessExpression extends MemberExpressionBase { + readonly kind: SyntaxKind.ElementAccessExpression; + readonly expression: Expression; + readonly questionDotToken?: QuestionDotToken; + readonly argumentExpression: Expression; +} +export interface CallExpression extends LeftHandSideExpressionBase, DeclarationBase { + readonly kind: SyntaxKind.CallExpression; + readonly expression: Expression; + readonly questionDotToken?: QuestionDotToken; + readonly typeArguments?: NodeArray; + readonly arguments: NodeArray; +} +export interface NewExpression extends PrimaryExpressionBase { + readonly kind: SyntaxKind.NewExpression; + readonly expression: Expression; + readonly typeArguments?: NodeArray; + readonly arguments?: NodeArray; +} +export interface MetaProperty extends PrimaryExpressionBase { + readonly kind: SyntaxKind.MetaProperty; + readonly keywordToken: SyntaxKind.ImportKeyword | SyntaxKind.NewKeyword; + readonly name: Identifier; +} +export interface NonNullExpression extends LeftHandSideExpressionBase { + readonly kind: SyntaxKind.NonNullExpression; + readonly expression: Expression; +} +export interface SpreadElement extends ExpressionBase { + readonly kind: SyntaxKind.SpreadElement; + readonly expression: Expression; +} +export interface TemplateExpression extends PrimaryExpressionBase { + readonly kind: SyntaxKind.TemplateExpression; + readonly head: TemplateHead; + readonly templateSpans: NodeArray; +} +export interface TemplateSpan extends NodeBase { + readonly kind: SyntaxKind.TemplateSpan; + readonly expression: Expression; + readonly literal: TemplateMiddleOrTail; +} +export interface TaggedTemplateExpression extends MemberExpressionBase { + readonly kind: SyntaxKind.TaggedTemplateExpression; + readonly tag: Expression; + readonly questionDotToken: QuestionDotToken; + readonly typeArguments?: NodeArray; + readonly template: TemplateLiteral; +} +export interface ParenthesizedExpression extends PrimaryExpressionBase { + readonly kind: SyntaxKind.ParenthesizedExpression; + readonly expression: Expression; +} +export interface ArrayLiteralExpression extends PrimaryExpressionBase { + readonly kind: SyntaxKind.ArrayLiteralExpression; + readonly elements: NodeArray; + readonly multiLine: boolean; +} +export interface ObjectLiteralExpression extends PrimaryExpressionBase, DeclarationBase { + readonly kind: SyntaxKind.ObjectLiteralExpression; + readonly properties: NodeArray; + readonly multiLine: boolean; +} +export interface SpreadAssignment extends NodeBase, DeclarationBase, ObjectLiteralElementBase { + readonly kind: SyntaxKind.SpreadAssignment; + readonly expression: Expression; +} +export interface PropertyAssignment extends NodeBase, DeclarationBase, NamedMemberBase, ObjectLiteralElementBase { + readonly kind: SyntaxKind.PropertyAssignment; + readonly type: TypeNode; + readonly initializer: Expression; +} +export interface ShorthandPropertyAssignment extends NodeBase, DeclarationBase, NamedMemberBase, ObjectLiteralElementBase { + readonly kind: SyntaxKind.ShorthandPropertyAssignment; + readonly type: TypeNode; + readonly equalsToken?: EqualsToken; + readonly objectAssignmentInitializer?: Expression; +} +export interface DeleteExpression extends UnaryExpressionBase { + readonly kind: SyntaxKind.DeleteExpression; + readonly expression: Expression; +} +export interface TypeOfExpression extends UnaryExpressionBase { + readonly kind: SyntaxKind.TypeOfExpression; + readonly expression: Expression; +} +export interface VoidExpression extends UnaryExpressionBase { + readonly kind: SyntaxKind.VoidExpression; + readonly expression: Expression; +} +export interface AwaitExpression extends UnaryExpressionBase { + readonly kind: SyntaxKind.AwaitExpression; + readonly expression: Expression; +} +export interface TypeAssertion extends UnaryExpressionBase { + readonly kind: SyntaxKind.TypeAssertionExpression; + readonly type: TypeNode; + readonly expression: Expression; +} +export interface KeywordTypeNode extends TypeNodeBase { + readonly kind: TKind; +} +export interface UnionTypeNode extends UnionOrIntersectionTypeNodeBase { + readonly kind: SyntaxKind.UnionType; +} +export interface IntersectionTypeNode extends UnionOrIntersectionTypeNodeBase { + readonly kind: SyntaxKind.IntersectionType; +} +export interface ConditionalTypeNode extends TypeNodeBase { + readonly kind: SyntaxKind.ConditionalType; + readonly checkType: TypeNode; + readonly extendsType: TypeNode; + readonly trueType: TypeNode; + readonly falseType: TypeNode; +} +export interface TypeOperatorNode extends TypeNodeBase { + readonly kind: SyntaxKind.TypeOperator; + readonly operator: SyntaxKind.KeyOfKeyword | SyntaxKind.ReadonlyKeyword | SyntaxKind.UniqueKeyword; + readonly type: TypeNode; +} +export interface InferTypeNode extends TypeNodeBase { + readonly kind: SyntaxKind.InferType; + readonly typeParameter: TypeParameterDeclaration; +} +export interface ArrayTypeNode extends TypeNodeBase { + readonly kind: SyntaxKind.ArrayType; + readonly elementType: TypeNode; +} +export interface IndexedAccessTypeNode extends TypeNodeBase { + readonly kind: SyntaxKind.IndexedAccessType; + readonly objectType: TypeNode; + readonly indexType: TypeNode; +} +export interface TypeReferenceNode extends NodeWithTypeArgumentsBase { + readonly kind: SyntaxKind.TypeReference; + readonly typeName: EntityName; +} +export interface ExpressionWithTypeArguments extends MemberExpressionBase { + readonly kind: SyntaxKind.ExpressionWithTypeArguments; + readonly expression: Expression; + readonly typeArguments?: NodeArray; +} +export interface LiteralTypeNode extends TypeNodeBase { + readonly kind: SyntaxKind.LiteralType; + readonly literal: Node; +} +export interface ThisTypeNode extends TypeNodeBase { + readonly kind: SyntaxKind.ThisType; +} +export interface TypePredicateNode extends TypeNodeBase { + readonly kind: SyntaxKind.TypePredicate; + readonly assertsModifier?: AssertsKeyword; + readonly parameterName: TypePredicateParameterName; + readonly type?: TypeNode; +} +export interface ImportAttribute extends NodeBase { + readonly kind: SyntaxKind.ImportAttribute; + readonly name: ImportAttributeName; + readonly value: Expression; +} +export interface ImportAttributes extends NodeBase { + readonly kind: SyntaxKind.ImportAttributes; + readonly token: SyntaxKind.WithKeyword | SyntaxKind.AssertKeyword; + readonly attributes: NodeArray; + readonly multiLine: boolean; +} +export interface TypeQueryNode extends NodeWithTypeArgumentsBase { + readonly kind: SyntaxKind.TypeQuery; + readonly exprName: EntityName; +} +export interface MappedTypeNode extends TypeNodeBase, DeclarationBase { + readonly kind: SyntaxKind.MappedType; + readonly readonlyToken?: ReadonlyKeyword | PlusToken | MinusToken; + readonly typeParameter: TypeParameterDeclaration; + readonly nameType?: TypeNode; + readonly questionToken?: QuestionToken | PlusToken | MinusToken; + readonly type?: TypeNode; + readonly members?: NodeArray; +} +export interface TypeLiteralNode extends TypeNodeBase, DeclarationBase { + readonly kind: SyntaxKind.TypeLiteral; + readonly members: NodeArray; +} +export interface TupleTypeNode extends TypeNodeBase { + readonly kind: SyntaxKind.TupleType; + readonly elements: NodeArray; +} +export interface NamedTupleMember extends TypeNodeBase, DeclarationBase { + readonly kind: SyntaxKind.NamedTupleMember; + readonly dotDotDotToken?: DotDotDotToken; + readonly name: Identifier; + readonly questionToken?: QuestionToken; + readonly type: TypeNode; +} +export interface OptionalTypeNode extends TypeNodeBase { + readonly kind: SyntaxKind.OptionalType; + readonly type: TypeNode; +} +export interface RestTypeNode extends TypeNodeBase { + readonly kind: SyntaxKind.RestType; + readonly type: TypeNode; +} +export interface ParenthesizedTypeNode extends TypeNodeBase { + readonly kind: SyntaxKind.ParenthesizedType; + readonly type: TypeNode; +} +export interface FunctionTypeNode extends TypeNodeBase, DeclarationBase, ModifiersBase, FunctionLikeBase { + readonly kind: SyntaxKind.FunctionType; +} +export interface ConstructorTypeNode extends TypeNodeBase, DeclarationBase, ModifiersBase, FunctionLikeBase { + readonly kind: SyntaxKind.ConstructorType; +} +export interface TemplateHead extends NodeBase, TemplateLiteralLikeNodeBase { + readonly kind: SyntaxKind.TemplateHead; +} +export interface TemplateMiddle extends NodeBase, TemplateLiteralLikeNodeBase { + readonly kind: SyntaxKind.TemplateMiddle; +} +export interface TemplateTail extends NodeBase, TemplateLiteralLikeNodeBase { + readonly kind: SyntaxKind.TemplateTail; +} +export interface TemplateLiteralTypeNode extends TypeNodeBase { + readonly kind: SyntaxKind.TemplateLiteralType; + readonly head: TemplateHead; + readonly templateSpans: NodeArray; +} +export interface TemplateLiteralTypeSpan extends TypeNodeBase { + readonly kind: SyntaxKind.TemplateLiteralTypeSpan; + readonly type: TypeNode; + readonly literal: TemplateMiddleOrTail; +} +export interface SyntheticExpression extends ExpressionBase { + readonly kind: SyntaxKind.SyntheticExpression; + readonly type: any; + readonly isSpread: boolean; + readonly tupleNameSource?: Node; +} +export interface PartiallyEmittedExpression extends LeftHandSideExpressionBase { + readonly kind: SyntaxKind.PartiallyEmittedExpression; + readonly expression: Expression; +} +export interface JsxElement extends PrimaryExpressionBase { + readonly kind: SyntaxKind.JsxElement; + readonly openingElement: JsxOpeningElement; + readonly children: NodeArray; + readonly closingElement: JsxClosingElement; +} +export interface JsxAttributes extends PrimaryExpressionBase, DeclarationBase { + readonly kind: SyntaxKind.JsxAttributes; + readonly properties: NodeArray; +} +export interface JsxNamespacedName extends ExpressionBase { + readonly kind: SyntaxKind.JsxNamespacedName; + readonly namespace: Identifier; + readonly name: Identifier; +} +export interface JsxOpeningElement extends ExpressionBase { + readonly kind: SyntaxKind.JsxOpeningElement; + readonly tagName: JsxTagNameExpression; + readonly typeArguments?: NodeArray; + readonly attributes: JsxAttributes; +} +export interface JsxSelfClosingElement extends PrimaryExpressionBase { + readonly kind: SyntaxKind.JsxSelfClosingElement; + readonly tagName: JsxTagNameExpression; + readonly typeArguments?: NodeArray; + readonly attributes: JsxAttributes; +} +export interface JsxFragment extends PrimaryExpressionBase { + readonly kind: SyntaxKind.JsxFragment; + readonly openingFragment: JsxOpeningFragment; + readonly children: NodeArray; + readonly closingFragment: JsxClosingFragment; +} +export interface JsxOpeningFragment extends ExpressionBase { + readonly kind: SyntaxKind.JsxOpeningFragment; +} +export interface JsxClosingFragment extends ExpressionBase { + readonly kind: SyntaxKind.JsxClosingFragment; +} +export interface JsxAttribute extends NodeBase, DeclarationBase { + readonly kind: SyntaxKind.JsxAttribute; + readonly name: JsxAttributeName; + readonly initializer?: JsxAttributeValue; +} +export interface JsxSpreadAttribute extends NodeBase, ObjectLiteralElementBase { + readonly kind: SyntaxKind.JsxSpreadAttribute; + readonly expression: Expression; +} +export interface JsxClosingElement extends NodeBase { + readonly kind: SyntaxKind.JsxClosingElement; + readonly tagName: JsxTagNameExpression; +} +export interface JsxExpression extends ExpressionBase { + readonly kind: SyntaxKind.JsxExpression; + readonly dotDotDotToken?: DotDotDotToken; + readonly expression?: Expression; +} +export interface JsxText extends ExpressionBase, LiteralLikeNodeBase { + readonly kind: SyntaxKind.JsxText; + readonly containsOnlyTriviaWhiteSpaces: boolean; +} +export interface SyntaxList extends NodeBase { + readonly kind: SyntaxKind.SyntaxList; + readonly children: readonly Node[]; +} +export interface JSDoc extends NodeBase { + readonly kind: SyntaxKind.JSDoc; + readonly comment: NodeArray; + readonly tags?: NodeArray; +} +export interface JSDocTypeExpression extends TypeNodeBase { + readonly kind: SyntaxKind.JSDocTypeExpression; + readonly type: TypeNode; +} +export interface JSDocNonNullableType extends JSDocTypeBase { + readonly kind: SyntaxKind.JSDocNonNullableType; + readonly type: TypeNode; +} +export interface JSDocNullableType extends JSDocTypeBase { + readonly kind: SyntaxKind.JSDocNullableType; + readonly type: TypeNode; +} +export interface JSDocAllType extends JSDocTypeBase { + readonly kind: SyntaxKind.JSDocAllType; +} +export interface JSDocVariadicType extends JSDocTypeBase { + readonly kind: SyntaxKind.JSDocVariadicType; + readonly type: TypeNode; +} +export interface JSDocOptionalType extends JSDocTypeBase { + readonly kind: SyntaxKind.JSDocOptionalType; + readonly type: TypeNode; +} +export interface JSDocTypeTag extends JSDocTagBase { + readonly kind: SyntaxKind.JSDocTypeTag; + readonly typeExpression: Node; +} +export interface JSDocUnknownTag extends JSDocTagBase { + readonly kind: SyntaxKind.JSDocUnknownTag; +} +export interface JSDocTemplateTag extends JSDocTagBase { + readonly kind: SyntaxKind.JSDocTemplateTag; + readonly constraint: Node; + readonly typeParameters: NodeArray; +} +export interface JSDocReturnTag extends JSDocTagBase { + readonly kind: SyntaxKind.JSDocReturnTag; + readonly typeExpression?: TypeNode; +} +export interface JSDocPublicTag extends JSDocTagBase { + readonly kind: SyntaxKind.JSDocPublicTag; +} +export interface JSDocPrivateTag extends JSDocTagBase { + readonly kind: SyntaxKind.JSDocPrivateTag; +} +export interface JSDocProtectedTag extends JSDocTagBase { + readonly kind: SyntaxKind.JSDocProtectedTag; +} +export interface JSDocReadonlyTag extends JSDocTagBase { + readonly kind: SyntaxKind.JSDocReadonlyTag; +} +export interface JSDocOverrideTag extends JSDocTagBase { + readonly kind: SyntaxKind.JSDocOverrideTag; +} +export interface JSDocDeprecatedTag extends JSDocTagBase { + readonly kind: SyntaxKind.JSDocDeprecatedTag; +} +export interface JSDocSeeTag extends JSDocTagBase { + readonly kind: SyntaxKind.JSDocSeeTag; + readonly nameExpression: TypeNode; +} +export interface JSDocImplementsTag extends JSDocTagBase { + readonly kind: SyntaxKind.JSDocImplementsTag; + readonly className: ExpressionWithTypeArguments; +} +export interface JSDocAugmentsTag extends JSDocTagBase { + readonly kind: SyntaxKind.JSDocAugmentsTag; + readonly className: ExpressionWithTypeArguments; +} +export interface JSDocSatisfiesTag extends JSDocTagBase { + readonly kind: SyntaxKind.JSDocSatisfiesTag; + readonly typeExpression: TypeNode; +} +export interface JSDocThrowsTag extends JSDocTagBase { + readonly kind: SyntaxKind.JSDocThrowsTag; + readonly typeExpression?: TypeNode; +} +export interface JSDocThisTag extends JSDocTagBase { + readonly kind: SyntaxKind.JSDocThisTag; + readonly typeExpression: TypeNode; +} +export interface JSDocImportTag extends JSDocTagBase { + readonly kind: SyntaxKind.JSDocImportTag; + readonly importClause?: ImportClause; + readonly moduleSpecifier: Expression; + readonly attributes?: ImportAttributes; +} +export interface JSDocCallbackTag extends JSDocTagBase { + readonly kind: SyntaxKind.JSDocCallbackTag; + readonly typeExpression: TypeNode; + readonly name?: JSDocFullName; +} +export interface JSDocOverloadTag extends JSDocTagBase { + readonly kind: SyntaxKind.JSDocOverloadTag; + readonly typeExpression: TypeNode; +} +export interface JSDocTypedefTag extends JSDocTagBase { + readonly kind: SyntaxKind.JSDocTypedefTag; + readonly typeExpression?: Node; + readonly name?: JSDocFullName; +} +export interface JSDocSignature extends JSDocTypeBase, DeclarationBase, FunctionLikeBase { + readonly kind: SyntaxKind.JSDocSignature; +} +export interface JSDocNameReference extends TypeNodeBase { + readonly kind: SyntaxKind.JSDocNameReference; + readonly name: EntityName; +} +export interface ModuleDeclaration extends StatementBase, DeclarationBase, ModifiersBase, BodyBase { + readonly kind: SyntaxKind.ModuleDeclaration; + readonly keyword: SyntaxKind.ModuleKeyword | SyntaxKind.NamespaceKeyword; + readonly name: ModuleName; + readonly body?: ModuleBody; +} +export interface ImportEqualsDeclaration extends StatementBase, DeclarationBase, ModifiersBase { + readonly kind: SyntaxKind.ImportEqualsDeclaration; + readonly isTypeOnly: boolean; + readonly name: Identifier; + readonly moduleReference: ModuleReference; +} +export interface ExportDeclaration extends StatementBase, DeclarationBase, ModifiersBase { + readonly kind: SyntaxKind.ExportDeclaration; + readonly isTypeOnly: boolean; + readonly exportClause?: NamedExportBindings; + readonly moduleSpecifier?: Expression; + readonly attributes?: ImportAttributes; +} +export interface ImportTypeNode extends NodeWithTypeArgumentsBase { + readonly kind: SyntaxKind.ImportType; + readonly isTypeOf: boolean; + readonly argument: TypeNode; + readonly attributes?: ImportAttributes; + readonly qualifier?: EntityName; +} +export interface ImportClause extends NodeBase, DeclarationBase { + readonly kind: SyntaxKind.ImportClause; + readonly phaseModifier?: ImportPhaseModifierSyntaxKind; + readonly name?: Identifier; + readonly namedBindings?: NamedImportBindings; +} +export interface ImportSpecifier extends NodeBase, DeclarationBase { + readonly kind: SyntaxKind.ImportSpecifier; + readonly isTypeOnly: boolean; + readonly propertyName?: ModuleExportName; + readonly name: Identifier; +} +export interface JSDocText extends JSDocCommentBase { + readonly kind: SyntaxKind.JSDocText; +} +export interface JSDocLink extends JSDocCommentBase { + readonly kind: SyntaxKind.JSDocLink; + readonly name?: EntityName; +} +export interface JSDocLinkPlain extends JSDocCommentBase { + readonly kind: SyntaxKind.JSDocLinkPlain; + readonly name?: EntityName; +} +export interface JSDocLinkCode extends JSDocCommentBase { + readonly kind: SyntaxKind.JSDocLinkCode; + readonly name?: EntityName; +} +export interface TypeParameterDeclaration extends NodeBase, DeclarationBase, ModifiersBase { + readonly kind: SyntaxKind.TypeParameter; + readonly name: Identifier; + readonly constraint?: TypeNode; + readonly expression?: Expression; + readonly defaultType?: TypeNode; +} +export interface SyntheticReferenceExpression extends ExpressionBase { + readonly kind: SyntaxKind.SyntheticReferenceExpression; + readonly expression: Expression; + readonly thisArg: Expression; +} +export interface JSDocTypeLiteral extends JSDocTypeBase, DeclarationBase { + readonly kind: SyntaxKind.JSDocTypeLiteral; + readonly jsdocPropertyTags?: readonly JSDocTag[]; + readonly isArrayType: boolean; +} + +export type Expression = ExpressionBase; +export type Statement = StatementBase; +export type TypeNode = TypeNodeBase; +export type HeritageClauseElement = ExpressionWithTypeArguments | TypeReferenceNode; +export type BlockOrExpression = Block | Expression; +export type NodeBody = Block | Expression | ModuleBlock | ModuleDeclaration; +export type AccessExpression = PropertyAccessExpression | ElementAccessExpression; +export type DeclarationName = Identifier | PrivateIdentifier | StringLiteral | NumericLiteral | BigIntLiteral | NoSubstitutionTemplateLiteral | ComputedPropertyName | BindingPattern | ElementAccessExpression; +export type ModuleName = Identifier | StringLiteral; +export type ModuleExportName = Identifier | StringLiteral; +export type PropertyName = Identifier | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | ComputedPropertyName | PrivateIdentifier | BigIntLiteral; +export type ModuleBody = ModuleBlock | ModuleDeclaration; +export type JSDocFullName = Identifier | ModuleDeclaration; +export type ForInitializer = Expression | MissingDeclaration | VariableDeclarationList; +export type ModuleReference = Identifier | QualifiedName | ExternalModuleReference; +export type NamedImportBindings = NamespaceImport | NamedImports; +export type NamedExportBindings = NamespaceExport | NamedExports; +export type MemberName = Identifier | PrivateIdentifier; +export type EntityName = Identifier | QualifiedName; +export type BindingName = Identifier | BindingPattern; +export type ModifierLike = Modifier | Decorator; +export type JsxChild = JsxText | JsxExpression | JsxElement | JsxSelfClosingElement | JsxFragment; +export type JsxAttributeLike = JsxAttribute | JsxSpreadAttribute; +export type JsxAttributeName = Identifier | JsxNamespacedName; +export type JsxAttributeValue = StringLiteral | JsxExpression | JsxElement | JsxSelfClosingElement | JsxFragment; +export type JsxTagNameExpression = Identifier | ThisExpression | JsxTagNamePropertyAccess | JsxNamespacedName; +export type ClassLikeDeclaration = ClassDeclaration | ClassExpression; +export type AccessorDeclaration = GetAccessorDeclaration | SetAccessorDeclaration; +export type LiteralLikeNode = StringLiteral | NumericLiteral | BigIntLiteral | RegularExpressionLiteral | TemplateLiteralLikeNode | JsxText; +export type LiteralExpression = StringLiteral | NumericLiteral | BigIntLiteral | RegularExpressionLiteral | NoSubstitutionTemplateLiteral; +export type UnionOrIntersectionTypeNode = UnionTypeNode | IntersectionTypeNode; +export type TemplateLiteralLikeNode = TemplateHead | TemplateMiddle | TemplateTail; +export type TemplateMiddleOrTail = TemplateMiddle | TemplateTail; +export type TemplateLiteral = TemplateExpression | NoSubstitutionTemplateLiteral; +export type TypePredicateParameterName = Identifier | ThisTypeNode; +export type ImportAttributeName = Identifier | StringLiteral; +export type LeftHandSideExpression = LeftHandSideExpressionBase; +export type JSDocComment = JSDocText | JSDocLink | JSDocLinkCode | JSDocLinkPlain; +export type SignatureDeclaration = CallSignatureDeclaration | ConstructSignatureDeclaration | MethodSignatureDeclaration | IndexSignatureDeclaration | FunctionTypeNode | ConstructorTypeNode | FunctionDeclaration | MethodDeclaration | ConstructorDeclaration | AccessorDeclaration | FunctionExpression | ArrowFunction; +export type StringLiteralLikeNode = StringLiteral | NoSubstitutionTemplateLiteral; +export type NumericOrStringLikeLiteral = StringLiteralLikeNode | NumericLiteral; +export type ObjectLiteralLikeNode = ObjectLiteralExpression | ObjectBindingPattern; +export type ObjectTypeDeclaration = ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode; +export type JsxOpeningLikeElement = JsxOpeningElement | JsxSelfClosingElement; +export type NamedImportsOrExports = NamedImports | NamedExports; +export type BreakOrContinueStatement = BreakStatement | ContinueStatement; +export type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator | JsxOpeningLikeElement | BinaryExpression; +export type FunctionLikeDeclaration = FunctionDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ConstructorDeclaration | FunctionExpression | ArrowFunction; +export type VariableOrParameterDeclaration = VariableDeclaration | ParameterDeclaration; +export type VariableOrPropertyDeclaration = VariableDeclaration | PropertyDeclaration; +export type CallOrNewExpression = CallExpression | NewExpression; +export type ImportClauseOrBindingPattern = ImportClause | BindingPattern; +export type AnyImportSyntax = ImportDeclaration | ImportEqualsDeclaration; +export type Declaration = DeclarationBase; +export type ClassElement = ClassElementBase; +export type TypeElement = TypeElementBase; +export type ObjectLiteralElement = ObjectLiteralElementBase; +export type JSDocTag = JSDocTagBase; +export type ArrayBindingElement = BindingElement | OmittedExpression; +export type AssertionExpression = TypeAssertion | AsExpression; +export type BooleanLiteral = TrueLiteral | FalseLiteral; +export type ConciseBody = Block | Expression; +export type DestructuringAssignment = ObjectDestructuringAssignment | ArrayDestructuringAssignment; +export type LiteralToken = NumericLiteral | BigIntLiteral | StringLiteral | JsxText | RegularExpressionLiteral | NoSubstitutionTemplateLiteral; +export type Modifier = AbstractKeyword | AccessorKeyword | AsyncKeyword | ConstKeyword | DeclareKeyword | DefaultKeyword | ExportKeyword | InKeyword | PrivateKeyword | ProtectedKeyword | PublicKeyword | ReadonlyKeyword | OutKeyword | OverrideKeyword | StaticKeyword; +export type ObjectLiteralElementLike = PropertyAssignment | ShorthandPropertyAssignment | SpreadAssignment | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration; +export type PropertyNameLiteral = Identifier | StringLiteral | NumericLiteral; +export type PseudoLiteralToken = TemplateHead | TemplateMiddle | TemplateTail; +export type TemplateLiteralToken = NoSubstitutionTemplateLiteral | PseudoLiteralToken; +export type ArrayDestructuringAssignment = BinaryExpression; +export type ObjectDestructuringAssignment = BinaryExpression; +export type FunctionBody = Block; +export type IncrementExpression = UpdateExpressionBase; + +export interface ForInStatement extends StatementBase { + readonly kind: SyntaxKind.ForInStatement; + readonly awaitModifier?: AwaitKeyword; + readonly initializer: ForInitializer; + readonly expression: Expression; + readonly statement: Statement; +} +export interface ForOfStatement extends StatementBase { + readonly kind: SyntaxKind.ForOfStatement; + readonly awaitModifier?: AwaitKeyword; + readonly initializer: ForInitializer; + readonly expression: Expression; + readonly statement: Statement; +} +export interface CaseClause extends NodeBase { + readonly kind: SyntaxKind.CaseClause; + readonly expression: Expression; + readonly statements: NodeArray; +} +export interface DefaultClause extends NodeBase { + readonly kind: SyntaxKind.DefaultClause; + readonly expression: Expression; + readonly statements: NodeArray; +} +export interface ObjectBindingPattern extends NodeBase { + readonly kind: SyntaxKind.ObjectBindingPattern; + readonly elements: NodeArray; +} +export interface ArrayBindingPattern extends NodeBase { + readonly kind: SyntaxKind.ArrayBindingPattern; + readonly elements: NodeArray; +} +export interface JSDocParameterTag extends JSDocTagBase { + readonly kind: SyntaxKind.JSDocParameterTag; + readonly name: EntityName; + readonly isBracketed: boolean; + readonly typeExpression?: TypeNode; + readonly isNameFirst: boolean; +} +export interface JSDocPropertyTag extends JSDocTagBase { + readonly kind: SyntaxKind.JSDocPropertyTag; + readonly name: EntityName; + readonly isBracketed: boolean; + readonly typeExpression?: TypeNode; + readonly isNameFirst: boolean; +} +export type ForInOrOfStatement = ForInStatement | ForOfStatement; +export type CaseOrDefaultClause = CaseClause | DefaultClause; +export type BindingPattern = ObjectBindingPattern | ArrayBindingPattern; +export type JSDocParameterOrPropertyTag = JSDocParameterTag | JSDocPropertyTag; +export type EndOfFile = Token; +export type DotToken = Token; +export type DotDotDotToken = Token; +export type QuestionToken = Token; +export type ExclamationToken = Token; +export type ColonToken = Token; +export type EqualsToken = Token; +export type AsteriskToken = Token; +export type EqualsGreaterThanToken = Token; +export type PlusToken = Token; +export type MinusToken = Token; +export type QuestionDotToken = Token; +export type AssertsKeyword = Token; +export type AssertKeyword = Token; +export type AwaitKeyword = Token; +export type CaseKeyword = Token; +export type AbstractKeyword = Token; +export type AccessorKeyword = Token; +export type AsyncKeyword = Token; +export type ConstKeyword = Token; +export type DeclareKeyword = Token; +export type DefaultKeyword = Token; +export type ExportKeyword = Token; +export type InKeyword = Token; +export type PrivateKeyword = Token; +export type ProtectedKeyword = Token; +export type PublicKeyword = Token; +export type ReadonlyKeyword = Token; +export type OutKeyword = Token; +export type OverrideKeyword = Token; +export type StaticKeyword = Token; +export type BinaryOperatorToken = Token; +export type AssignmentOperatorToken = Token; +export type NullLiteral = KeywordExpression; +export type TrueLiteral = KeywordExpression; +export type FalseLiteral = KeywordExpression; +export type ThisExpression = KeywordExpression; +export type SuperExpression = KeywordExpression; +export type ImportExpression = KeywordExpression; +export type StatementList = NodeArray; +export type CaseClausesList = NodeArray; +export type VariableDeclarationNodeList = NodeArray; +export type BindingElementList = NodeArray; +export type TypeParameterList = NodeArray; +export type ParameterList = NodeArray; +export type HeritageClauseList = NodeArray; +export type ClassElementList = NodeArray; +export type TypeElementList = NodeArray; +export type ExpressionWithTypeArgumentsList = NodeArray; +export type HeritageClauseElementList = NodeArray; +export type EnumMemberList = NodeArray; +export type ImportSpecifierList = NodeArray; +export type ExportSpecifierList = NodeArray; +export type TypeArgumentList = NodeArray; +export type ArgumentList = NodeArray; +export type TemplateSpanList = NodeArray; +export type ElementList = NodeArray; +export type PropertyDefinitionList = NodeArray; +export type TypeList = NodeArray; +export type ImportAttributeList = NodeArray; +export type TemplateLiteralTypeSpanList = NodeArray; +export type JsxChildList = NodeArray; +export type JsxAttributeList = NodeArray; diff --git a/packages/typescript/src/ast/ast.ts b/packages/typescript/src/ast/ast.ts index 804f0c0c6a33d..9ac155afcc574 100644 --- a/packages/typescript/src/ast/ast.ts +++ b/packages/typescript/src/ast/ast.ts @@ -1,223 +1,223 @@ -// ast.ts — Hand-written AST type definitions -// Generated types are in ast.generated.ts - -import type { DiagnosticDirectivePolicy } from "#enums/diagnosticDirectivePolicy"; -import type { InternalSymbolName } from "#enums/internalSymbolName"; -import type { LanguageVariant } from "#enums/languageVariant"; -import type { NodeFlags } from "#enums/nodeFlags"; -import type { ScriptKind } from "#enums/scriptKind"; -import { SyntaxKind } from "#enums/syntaxKind"; -import type { - AssertionExpression, - BindingElement, - CaseClause, - ComputedPropertyName, - Decorator, - DefaultClause, - DoStatement, - EndOfFile, - EntityName, - EnumMember, - Expression, - ExpressionStatement, - ForInStatement, - ForOfStatement, - ForStatement, - Identifier, - IfStatement, - JsxAttribute, - JsxExpression, - JsxSpreadAttribute, - KeywordSyntaxKind, - ModifierSyntaxKind, - ParameterDeclaration, - PropertyAccessExpression, - PropertyAssignment, - PropertyDeclaration, - PropertySignatureDeclaration, - PunctuationSyntaxKind, - ReturnStatement, - SatisfiesExpression, - ShorthandPropertyAssignment, - SpreadAssignment, - Statement, - SwitchStatement, - TemplateSpan, - ThisExpression, - ThrowStatement, - Token, - VariableDeclaration, - WhileStatement, - WithStatement, -} from "./ast.generated.ts"; -import type { SpanMap } from "./spanMap.ts"; - -export { SyntaxKind } from "#enums/syntaxKind"; -export { TokenFlags } from "#enums/tokenFlags"; - -export * from "./ast.generated.ts"; - -// ── Core types ── - -export type Path = string & { __pathBrand: any; }; - -/** - * The escaped form of a symbol/identifier name. Internal compiler names are - * prefixed with `__` (e.g. `__type`, `__call`), and user names that already - * begin with `__` carry an extra leading underscore. Use - * {@link unescapeLeadingUnderscores} to recover the display name and - * {@link escapeLeadingUnderscores} to produce a key from a display name. - */ -export type __String = (string & { __escapedIdentifier: void; }) | InternalSymbolName; - -export interface TextRange { - pos: number; - end: number; -} - -export interface ReadonlyTextRange { - readonly pos: number; - readonly end: number; -} - -export interface NodeArray extends ReadonlyArray, ReadonlyTextRange { - hasTrailingComma?: boolean; - transformFlags: number; -} - -export interface Node extends ReadonlyTextRange { - readonly kind: SyntaxKind; - readonly flags: NodeFlags; - readonly parent: Node; - readonly jsDoc?: readonly Node[] | undefined; - forEachChild(visitor: (node: Node) => T, visitArray?: (nodes: NodeArray) => T): T | undefined; - getSourceFile(): SourceFile; - getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number; - getFullStart(): number; - getEnd(): number; - getWidth(sourceFile?: SourceFile): number; - getFullWidth(): number; - getLeadingTriviaWidth(sourceFile?: SourceFile): number; - getFullText(sourceFile?: SourceFile): string; - getText(sourceFile?: SourceFile): string; -} - -export interface FileReference extends TextRange { - readonly fileName: string; - readonly resolutionMode: number; - readonly preserve: boolean; -} - -export interface LineAndCharacter { - /** 0-based line number. */ - readonly line: number; - /** 0-based character offset, in UTF-16 code units, from the start of the line. */ - readonly character: number; -} - -export interface MappedDiagnosticDirective { - readonly originalRange: ReadonlyTextRange; - readonly virtualRange: ReadonlyTextRange; - readonly policy: DiagnosticDirectivePolicy; - readonly unusedCode: number; -} - -export interface SourceFile extends Node { - readonly kind: SyntaxKind.SourceFile; - readonly statements: NodeArray; - readonly endOfFileToken: EndOfFile; - readonly text: string; - readonly originalText: string; - readonly spanMap: SpanMap | undefined; - /** Identity of the content mapper that produced this source file. */ - readonly contentMapper?: string; - /** Filename used to determine the syntax and module semantics of the transformed content. */ - readonly virtualFileName?: string; - /** Framework-specific diagnostic directives applied to the transformed content. */ - readonly diagnosticDirectives?: readonly MappedDiagnosticDirective[]; - /** Compiler-assigned filenames of supplemental outputs associated with this canonical source file. */ - readonly supplementalSourceFileNames?: readonly string[]; - /** Canonical source filename associated with this supplemental output, if this is supplemental. */ - readonly canonicalSourceFileName?: string; - readonly fileName: string; - readonly path: Path; - readonly languageVariant: LanguageVariant; - readonly scriptKind: ScriptKind; - readonly isDeclarationFile: boolean; - readonly referencedFiles: readonly FileReference[]; - readonly typeReferenceDirectives: readonly FileReference[]; - readonly libReferenceDirectives: readonly FileReference[]; - readonly imports: readonly Node[]; - readonly moduleAugmentations: readonly Node[]; - readonly ambientModuleNames: readonly string[]; - readonly externalModuleIndicator: Node | true | undefined; - /** Returns the UTF-16 code unit offset of the start of each line. */ - getLineStarts(): readonly number[]; - /** Converts a UTF-16 code unit position into a 0-based line and character. */ - getLineAndCharacterOfPosition(position: number): LineAndCharacter; - /** Converts a 0-based line and character into a UTF-16 code unit position. */ - getPositionOfLineAndCharacter(line: number, character: number): number; - /** @internal */ - tokenCache?: Map; -} - -// ── Token hierarchy ── - -export type PunctuationToken = Token; -export type KeywordToken = Token; -export type ModifierToken = KeywordToken; - -// ── Narrowing interfaces ── - -export interface PropertyAccessEntityNameExpression extends PropertyAccessExpression { - readonly expression: EntityNameExpression; - readonly name: Identifier; -} - -export type EntityNameExpression = Identifier | PropertyAccessEntityNameExpression; -export type EntityNameOrEntityNameExpression = EntityName | EntityNameExpression; - -export interface JsxTagNamePropertyAccess extends PropertyAccessExpression { - readonly expression: Identifier | ThisExpression | JsxTagNamePropertyAccess; -} - -export type HasExpressionInitializer = - | VariableDeclaration - | ParameterDeclaration - | BindingElement - | PropertyDeclaration - | PropertyAssignment - | EnumMember; - -export type HasInitializer = - | HasExpressionInitializer - | ForStatement - | ForInStatement - | ForOfStatement - | JsxAttribute; - -export type HasIllegalExpressionInitializer = PropertySignatureDeclaration; - -export type HasExpression = - | ExpressionStatement - | IfStatement - | DoStatement - | WhileStatement - | ReturnStatement - | WithStatement - | SwitchStatement - | CaseClause - | DefaultClause - | ThrowStatement - | AssertionExpression - | TemplateSpan - | ComputedPropertyName - | Decorator - | JsxExpression - | JsxSpreadAttribute - | SpreadAssignment - | SatisfiesExpression; - -export interface ObjectAssignmentInitializer extends ShorthandPropertyAssignment { - readonly objectAssignmentInitializer: Expression; -} +// ast.ts — Hand-written AST type definitions +// Generated types are in ast.generated.ts + +import type { DiagnosticDirectivePolicy } from "#enums/diagnosticDirectivePolicy"; +import type { InternalSymbolName } from "#enums/internalSymbolName"; +import type { LanguageVariant } from "#enums/languageVariant"; +import type { NodeFlags } from "#enums/nodeFlags"; +import type { ScriptKind } from "#enums/scriptKind"; +import { SyntaxKind } from "#enums/syntaxKind"; +import type { + AssertionExpression, + BindingElement, + CaseClause, + ComputedPropertyName, + Decorator, + DefaultClause, + DoStatement, + EndOfFile, + EntityName, + EnumMember, + Expression, + ExpressionStatement, + ForInStatement, + ForOfStatement, + ForStatement, + Identifier, + IfStatement, + JsxAttribute, + JsxExpression, + JsxSpreadAttribute, + KeywordSyntaxKind, + ModifierSyntaxKind, + ParameterDeclaration, + PropertyAccessExpression, + PropertyAssignment, + PropertyDeclaration, + PropertySignatureDeclaration, + PunctuationSyntaxKind, + ReturnStatement, + SatisfiesExpression, + ShorthandPropertyAssignment, + SpreadAssignment, + Statement, + SwitchStatement, + TemplateSpan, + ThisExpression, + ThrowStatement, + Token, + VariableDeclaration, + WhileStatement, + WithStatement, +} from "./ast.generated.ts"; +import type { SpanMap } from "./spanMap.ts"; + +export { SyntaxKind } from "#enums/syntaxKind"; +export { TokenFlags } from "#enums/tokenFlags"; + +export * from "./ast.generated.ts"; + +// ── Core types ── + +export type Path = string & { __pathBrand: any; }; + +/** + * The escaped form of a symbol/identifier name. Internal compiler names are + * prefixed with `__` (e.g. `__type`, `__call`), and user names that already + * begin with `__` carry an extra leading underscore. Use + * {@link unescapeLeadingUnderscores} to recover the display name and + * {@link escapeLeadingUnderscores} to produce a key from a display name. + */ +export type __String = (string & { __escapedIdentifier: void; }) | InternalSymbolName; + +export interface TextRange { + pos: number; + end: number; +} + +export interface ReadonlyTextRange { + readonly pos: number; + readonly end: number; +} + +export interface NodeArray extends ReadonlyArray, ReadonlyTextRange { + hasTrailingComma?: boolean; + transformFlags: number; +} + +export interface Node extends ReadonlyTextRange { + readonly kind: SyntaxKind; + readonly flags: NodeFlags; + readonly parent: Node; + readonly jsDoc?: readonly Node[] | undefined; + forEachChild(visitor: (node: Node) => T, visitArray?: (nodes: NodeArray) => T): T | undefined; + getSourceFile(): SourceFile; + getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number; + getFullStart(): number; + getEnd(): number; + getWidth(sourceFile?: SourceFile): number; + getFullWidth(): number; + getLeadingTriviaWidth(sourceFile?: SourceFile): number; + getFullText(sourceFile?: SourceFile): string; + getText(sourceFile?: SourceFile): string; +} + +export interface FileReference extends TextRange { + readonly fileName: string; + readonly resolutionMode: number; + readonly preserve: boolean; +} + +export interface LineAndCharacter { + /** 0-based line number. */ + readonly line: number; + /** 0-based character offset, in UTF-16 code units, from the start of the line. */ + readonly character: number; +} + +export interface MappedDiagnosticDirective { + readonly originalRange: ReadonlyTextRange; + readonly virtualRange: ReadonlyTextRange; + readonly policy: DiagnosticDirectivePolicy; + readonly unusedCode: number; +} + +export interface SourceFile extends Node { + readonly kind: SyntaxKind.SourceFile; + readonly statements: NodeArray; + readonly endOfFileToken: EndOfFile; + readonly text: string; + readonly originalText: string; + readonly spanMap: SpanMap | undefined; + /** Identity of the content mapper that produced this source file. */ + readonly contentMapper?: string; + /** Filename used to determine the syntax and module semantics of the transformed content. */ + readonly virtualFileName?: string; + /** Framework-specific diagnostic directives applied to the transformed content. */ + readonly diagnosticDirectives?: readonly MappedDiagnosticDirective[]; + /** Compiler-assigned filenames of supplemental outputs associated with this canonical source file. */ + readonly supplementalSourceFileNames?: readonly string[]; + /** Canonical source filename associated with this supplemental output, if this is supplemental. */ + readonly canonicalSourceFileName?: string; + readonly fileName: string; + readonly path: Path; + readonly languageVariant: LanguageVariant; + readonly scriptKind: ScriptKind; + readonly isDeclarationFile: boolean; + readonly referencedFiles: readonly FileReference[]; + readonly typeReferenceDirectives: readonly FileReference[]; + readonly libReferenceDirectives: readonly FileReference[]; + readonly imports: readonly Node[]; + readonly moduleAugmentations: readonly Node[]; + readonly ambientModuleNames: readonly string[]; + readonly externalModuleIndicator: Node | true | undefined; + /** Returns the UTF-16 code unit offset of the start of each line. */ + getLineStarts(): readonly number[]; + /** Converts a UTF-16 code unit position into a 0-based line and character. */ + getLineAndCharacterOfPosition(position: number): LineAndCharacter; + /** Converts a 0-based line and character into a UTF-16 code unit position. */ + getPositionOfLineAndCharacter(line: number, character: number): number; + /** @internal */ + tokenCache?: Map; +} + +// ── Token hierarchy ── + +export type PunctuationToken = Token; +export type KeywordToken = Token; +export type ModifierToken = KeywordToken; + +// ── Narrowing interfaces ── + +export interface PropertyAccessEntityNameExpression extends PropertyAccessExpression { + readonly expression: EntityNameExpression; + readonly name: Identifier; +} + +export type EntityNameExpression = Identifier | PropertyAccessEntityNameExpression; +export type EntityNameOrEntityNameExpression = EntityName | EntityNameExpression; + +export interface JsxTagNamePropertyAccess extends PropertyAccessExpression { + readonly expression: Identifier | ThisExpression | JsxTagNamePropertyAccess; +} + +export type HasExpressionInitializer = + | VariableDeclaration + | ParameterDeclaration + | BindingElement + | PropertyDeclaration + | PropertyAssignment + | EnumMember; + +export type HasInitializer = + | HasExpressionInitializer + | ForStatement + | ForInStatement + | ForOfStatement + | JsxAttribute; + +export type HasIllegalExpressionInitializer = PropertySignatureDeclaration; + +export type HasExpression = + | ExpressionStatement + | IfStatement + | DoStatement + | WhileStatement + | ReturnStatement + | WithStatement + | SwitchStatement + | CaseClause + | DefaultClause + | ThrowStatement + | AssertionExpression + | TemplateSpan + | ComputedPropertyName + | Decorator + | JsxExpression + | JsxSpreadAttribute + | SpreadAssignment + | SatisfiesExpression; + +export interface ObjectAssignmentInitializer extends ShorthandPropertyAssignment { + readonly objectAssignmentInitializer: Expression; +} diff --git a/packages/typescript/src/ast/astnav.ts b/packages/typescript/src/ast/astnav.ts index 96e4edecaed74..962b87f1430d3 100644 --- a/packages/typescript/src/ast/astnav.ts +++ b/packages/typescript/src/ast/astnav.ts @@ -1,695 +1,695 @@ -import { NodeFlags } from "#enums/nodeFlags"; -import { SyntaxKind } from "#enums/syntaxKind"; -import type { TokenSyntaxKind } from "./ast.generated.ts"; -import type { - Node, - NodeArray, - SourceFile, -} from "./ast.ts"; -import { createToken } from "./factory.generated.ts"; -import { - isJSDocNodeKind, - isKeywordKind, - isPrivateIdentifier, - isPropertyNameLiteral, - isTokenKind, -} from "./is.ts"; -import { - createScanner, - skipTrivia, -} from "./scanner.ts"; - -export function getTokenAtPosition(sourceFile: SourceFile, position: number): Node { - return getTokenAtPositionImpl(sourceFile, position, /*allowPositionInLeadingTrivia*/ true, /*includePrecedingTokenAtEndPosition*/ undefined); -} - -export function getTouchingPropertyName(sourceFile: SourceFile, position: number): Node { - return getTokenAtPositionImpl(sourceFile, position, /*allowPositionInLeadingTrivia*/ false, node => isPropertyNameLiteral(node) || isKeywordKind(node.kind) || isPrivateIdentifier(node)); -} - -export function getTouchingToken(sourceFile: SourceFile, position: number): Node { - return getTokenAtPositionImpl(sourceFile, position, /*allowPositionInLeadingTrivia*/ false, /*includePrecedingTokenAtEndPosition*/ undefined); -} - -/** - * Finds the token that starts immediately after `previousToken` ends, searching - * within `parent`. Returns `undefined` if no such token exists. - */ -export function findNextToken(previousToken: Node, parent: Node, sourceFile: SourceFile): Node | undefined { - return find(parent); - - function find(n: Node): Node | undefined { - if (isTokenKind(n.kind) && n.pos === previousToken.end) { - // This is the token that starts at the end of previousToken – return it. - return n; - } - - // Find the child node that contains `previousToken` or starts immediately after it. - let foundNode: Node | undefined; - - const visitChild = (node: Node) => { - if (node.flags & NodeFlags.Reparsed) { - return undefined; - } - if (node.pos <= previousToken.end && node.end > previousToken.end) { - foundNode = node; - } - return undefined; - }; - - // Visit JSDoc children first (mirrors Go's VisitEachChildAndJSDoc). - if (n.jsDoc) { - for (const jsdoc of n.jsDoc) { - visitChild(jsdoc); - } - } - - n.forEachChild( - visitChild, - nodes => { - if (nodes.length > 0 && foundNode === undefined) { - for (const node of nodes) { - if (node.flags & NodeFlags.Reparsed) continue; - if (node.pos > previousToken.end) break; - if (node.end > previousToken.end) { - foundNode = node; - break; - } - } - } - return undefined; - }, - ); - - // Recurse into the found child. - if (foundNode !== undefined) { - return find(foundNode); - } - - // No AST child covers the position; use the scanner to find the syntactic token. - // The scanner is initialized at `previousToken.end`, so tokenFullStart === previousToken.end. - const startPos = previousToken.end; - if (startPos >= n.pos && startPos < n.end) { - const scanner = getScannerForSourceFile(sourceFile, startPos); - const token = scanner.getToken(); - const tokenFullStart = scanner.getTokenFullStart(); - const tokenEnd = scanner.getTokenEnd(); - const flags = scanner.getTokenFlags(); - return getOrCreateToken(sourceFile, token, tokenFullStart, tokenEnd, n, flags); - } - - return undefined; - } -} - -/** - * Finds the leftmost token satisfying `position < token.end`. - * If the position is in the trivia of that leftmost token, or the token is invalid, - * returns the rightmost valid token with `token.end <= position`. - * Excludes `JsxText` tokens containing only whitespace. - */ -export function findPrecedingToken(sourceFile: SourceFile, position: number): Node | undefined { - return findPrecedingTokenImpl(sourceFile, position, sourceFile); -} - -function getTokenAtPositionImpl( - sourceFile: SourceFile, - position: number, - allowPositionInLeadingTrivia: boolean, - includePrecedingTokenAtEndPosition: ((node: Node) => boolean) | undefined, -): Node { - let current: Node = sourceFile; - let nodeAfterLeft: Node | undefined; - const state: { - next: Node | undefined; - prevSubtree: Node | undefined; - left: number; - } = { - next: undefined, - prevSubtree: undefined, - left: 0, - }; - - const getIncludedPrecedingToken = (subtree: Node): Node | undefined => { - const child = findPrecedingTokenImpl(sourceFile, position, subtree); - if (child !== undefined && child.end === position && includePrecedingTokenAtEndPosition!(child)) { - return child; - } - return undefined; - }; - - const testNode = (node: Node): number => { - if (node.kind !== SyntaxKind.EndOfFile && node.end === position && includePrecedingTokenAtEndPosition !== undefined) { - if (state.prevSubtree !== undefined && getIncludedPrecedingToken(state.prevSubtree) !== undefined) { - return 0; - } - state.prevSubtree = node; - } - // A node "contains" the position if position < end, except nodes at the file end - // treat end as inclusive (there's nowhere else to look). This applies to the EOF - // token itself, and to JSDoc nodes reaching EOF (e.g. unterminated JSDoc comments). - if ( - node.end < position || node.end === position && - node.kind !== SyntaxKind.EndOfFile && - (!isJSDocNodeKind(node.kind) || node.end !== sourceFile.endOfFileToken.end) - ) { - return -1; - } - const nodePos = getPosition(node, sourceFile, allowPositionInLeadingTrivia); - if (nodePos > position) { - return 1; - } - return 0; - }; - - while (true) { - // Visit each child of current to find the one containing the position. - state.next = undefined; - nodeAfterLeft = undefined as Node | undefined; - - // In Strada, JSDoc nodes with a single comment represent that comment as a string - // property (not a child node), so forEachChild does not visit it. We replicate this - // by detecting single-comment NodeLists in visitList and skipping their elements in visitNode. - let skipSingleCommentChildren = false; - - const visitNode = (node: Node) => { - if (node.flags & NodeFlags.Reparsed) { - return undefined; - } - if (skipSingleCommentChildren && isJSDocCommentChildKind(node.kind)) { - return undefined; - } - if (nodeAfterLeft === undefined) { - nodeAfterLeft = node; - } - if (state.next === undefined) { - const result = testNode(node); - switch (result) { - case -1: - if (!isJSDocNodeKind(node.kind)) { - state.left = node.end; - } - nodeAfterLeft = undefined; - break; - case 0: - state.next = node; - break; - } - } - return undefined; - }; - - // Visit JSDoc children first, then regular children (mirrors Go's VisitEachChildAndJSDoc). - if (current.jsDoc) { - for (const jsdoc of current.jsDoc) { - visitNode(jsdoc); - } - } - - current.forEachChild( - visitNode, - nodes => { - // Track whether this NodeList is a single-comment list that should be skipped. - // The flag affects the subsequent forEachNode(visitNode) calls for this NodeList. - skipSingleCommentChildren = isJSDocSingleCommentNodeList(nodes); - if (nodes.length === 0 || skipSingleCommentChildren) { - return undefined; - } - if (nodeAfterLeft === undefined) { - for (const node of nodes) { - if (!(node.flags & NodeFlags.Reparsed)) { - nodeAfterLeft = node; - break; - } - } - } - if (state.next === undefined) { - if (nodes.end === position && includePrecedingTokenAtEndPosition !== undefined) { - state.left = nodes.end; - nodeAfterLeft = undefined; - state.prevSubtree = nodes[nodes.length - 1]; - } - else if (nodes.end <= position) { - state.left = nodes.end; - nodeAfterLeft = undefined; - } - else if (nodes.pos <= position) { - binarySearchNodeList(nodes, testNode, (node, middle, arr) => { - state.left = node.end; - nodeAfterLeft = undefined; - for (let i = middle + 1; i < arr.length; i++) { - if (!(arr[i].flags & NodeFlags.Reparsed)) { - nodeAfterLeft = arr[i]; - break; - } - } - }, found => { - state.next = found; - }); - } - } - return undefined; - }, - ); - - // If prevSubtree was set, check if the rightmost token of prevSubtree should be returned. - if (state.prevSubtree !== undefined) { - const child = getIncludedPrecedingToken(state.prevSubtree); - if (child !== undefined) { - return child; - } - state.prevSubtree = undefined; - } - - if (state.next === undefined) { - if (isTokenKind(current.kind) || shouldSkipChild(current)) { - return current; - } - // Use the scanner to find a token not stored in the AST. - const scanner = getScannerForSourceFile(sourceFile, state.left); - let end = current.end; - const afterLeft = nodeAfterLeft; - if (afterLeft !== undefined) { - end = afterLeft.pos; - } - while (state.left < end) { - const token = scanner.getToken(); - const tokenFullStart = scanner.getTokenFullStart(); - const tokenStart = allowPositionInLeadingTrivia ? tokenFullStart : scanner.getTokenStart(); - const tokenEnd = scanner.getTokenEnd(); - const flags = scanner.getTokenFlags(); - if (tokenEnd > end) { - break; - } - if (tokenStart <= position && position < tokenEnd) { - if (token === SyntaxKind.Identifier || !isTokenKind(token)) { - if (isJSDocNodeKind(current.kind)) { - return current; - } - throw new Error(`did not expect ${SyntaxKind[current.kind]} to have ${SyntaxKind[token]} in its trivia`); - } - return getOrCreateToken(sourceFile, token, tokenFullStart, tokenEnd, current, flags); - } - if (includePrecedingTokenAtEndPosition !== undefined && tokenEnd === position) { - const prevToken = getOrCreateToken(sourceFile, token, tokenFullStart, tokenEnd, current, flags); - if (includePrecedingTokenAtEndPosition(prevToken)) { - return prevToken; - } - } - state.left = tokenEnd; - scanner.scan(); - } - return current; - } - - current = state.next; - state.left = current.pos; - nodeAfterLeft = undefined; - } -} - -function getPosition(node: Node, sourceFile: SourceFile, allowPositionInLeadingTrivia: boolean): number { - if (allowPositionInLeadingTrivia) { - return node.pos; - } - return getTokenPosOfNode(node, sourceFile, /*includeJSDoc*/ true); -} - -/** @internal */ -export function getTokenPosOfNode(node: Node, sourceFile: SourceFile, includeJSDoc?: boolean): number { - // With nodes that have no width (i.e. 'Missing' nodes), we actually *don't* - // want to skip trivia because this will launch us forward to the next token. - if (nodeIsMissing(node)) { - return node.pos; - } - if (isJSDocNodeKind(node.kind) || node.kind === SyntaxKind.JsxText) { - return skipTrivia(sourceFile.text, node.pos, /*stopAfterLineBreak*/ false, /*stopAtComments*/ true); - } - if (includeJSDoc && node.jsDoc && node.jsDoc.length > 0) { - return getTokenPosOfNode(node.jsDoc[0], sourceFile, /*includeJSDoc*/ false); - } - return skipTrivia(sourceFile.text, node.pos, /*stopAfterLineBreak*/ false, /*stopAtComments*/ false, /*inJSDoc*/ !!(node.flags & NodeFlags.JSDoc)); -} - -function nodeIsMissing(node: Node): boolean { - return node.pos === node.end && node.pos >= 0 && node.kind !== SyntaxKind.EndOfFile; -} - -function findPrecedingTokenImpl(sourceFile: SourceFile, position: number, startNode: Node): Node | undefined { - const find = (n: Node): Node | undefined => { - if (isTokenKind(n.kind) && n.kind !== SyntaxKind.EndOfFile) { - return n; - } - - let foundChild: Node | undefined; - let prevChild: Node | undefined; - - // Visit JSDoc nodes first (mirrors Go's VisitEachChildAndJSDoc). - if (n.jsDoc) { - for (const jsdoc of n.jsDoc) { - if (jsdoc.flags & NodeFlags.Reparsed) continue; - if (foundChild !== undefined) break; - if (position < jsdoc.end && (prevChild === undefined || prevChild.end <= position)) { - foundChild = jsdoc; - } - else { - prevChild = jsdoc; - } - } - } - - let skipSingleCommentChildrenImpl = false; - n.forEachChild( - node => { - if (node.flags & NodeFlags.Reparsed) { - return undefined; - } - if (skipSingleCommentChildrenImpl && isJSDocCommentChildKind(node.kind)) { - return undefined; - } - if (foundChild !== undefined) { - return undefined; - } - if (position < node.end && (prevChild === undefined || prevChild.end <= position)) { - foundChild = node; - } - else { - prevChild = node; - } - return undefined; - }, - nodes => { - skipSingleCommentChildrenImpl = isJSDocSingleCommentNodeList(nodes); - if (foundChild !== undefined) { - return undefined; - } - if (nodes.length > 0 && !skipSingleCommentChildrenImpl) { - const index = binarySearchForPrecedingToken(nodes, position); - if (index >= 0 && !(nodes[index].flags & NodeFlags.Reparsed)) { - foundChild = nodes[index]; - } - const lookupIndex = index >= 0 ? index - 1 : nodes.length - 1; - for (let i = lookupIndex; i >= 0; i--) { - if (!(nodes[i].flags & NodeFlags.Reparsed)) { - if (prevChild === undefined) { - prevChild = nodes[i]; - } - break; - } - } - } - return undefined; - }, - ); - - if (foundChild !== undefined) { - const start = getTokenPosOfNode(foundChild, sourceFile, /*includeJSDoc*/ true); - if (start >= position) { - if (position >= foundChild.pos) { - // We are in the leading trivia of foundChild. Check for JSDoc nodes of n - // preceding foundChild, mirroring Go's findPrecedingToken logic. - let jsDoc: Node | undefined; - if (n.jsDoc) { - for (let i = n.jsDoc.length - 1; i >= 0; i--) { - if (n.jsDoc[i].pos >= foundChild.pos) { - jsDoc = n.jsDoc[i]; - break; - } - } - } - if (jsDoc !== undefined) { - if (position < jsDoc.end) { - return find(jsDoc); - } - return findRightmostValidToken(sourceFile, jsDoc.end, n, position); - } - return findRightmostValidToken(sourceFile, foundChild.pos, n, -1); - } - // Answer is in tokens between two visited children. - return findRightmostValidToken(sourceFile, foundChild.pos, n, position); - } - return find(foundChild); - } - - if (position >= n.end) { - return findRightmostValidToken(sourceFile, n.end, n, -1); - } - return findRightmostValidToken(sourceFile, n.end, n, position); - }; - - return find(startNode); -} - -function findRightmostValidToken(sourceFile: SourceFile, endPos: number, containingNode: Node, position: number): Node | undefined { - if (position === -1) { - position = containingNode.end; - } - - const find = (n: Node, endPos: number): Node | undefined => { - if (isTokenKind(n.kind) && n.kind !== SyntaxKind.EndOfFile) { - return n; - } - - let rightmostValidNode: Node | undefined; - let hasChildren = false; - - // Visit JSDoc nodes first (mirrors Go's VisitEachChildAndJSDoc). - if (n.jsDoc) { - hasChildren = true; - for (const jsdoc of n.jsDoc) { - if (jsdoc.flags & NodeFlags.Reparsed) continue; - if (jsdoc.end > endPos || getTokenPosOfNode(jsdoc, sourceFile) >= position) continue; - if (isValidPrecedingNode(jsdoc, sourceFile)) { - rightmostValidNode = jsdoc; - } - } - } - - let skipSingleCommentChildren = false; - n.forEachChild( - node => { - if (node.flags & NodeFlags.Reparsed) { - return undefined; - } - if (skipSingleCommentChildren && isJSDocCommentChildKind(node.kind)) { - return undefined; - } - hasChildren = true; - if (node.end > endPos || getTokenPosOfNode(node, sourceFile) >= position) { - return undefined; - } - if (isValidPrecedingNode(node, sourceFile)) { - rightmostValidNode = node; - } - return undefined; - }, - nodes => { - // Skip single-comment JSDoc NodeLists (e.g. JSDocText children of a JSDoc node): - // In Go, these are stored as string properties and are never visited as children. - skipSingleCommentChildren = isJSDocSingleCommentNodeList(nodes); - if (nodes.length > 0 && !skipSingleCommentChildren) { - hasChildren = true; - for (let i = nodes.length - 1; i >= 0; i--) { - const node = nodes[i]; - if (node.flags & NodeFlags.Reparsed) continue; - if (node.end > endPos || getTokenPosOfNode(node, sourceFile) >= position) continue; - if (isValidPrecedingNode(node, sourceFile)) { - rightmostValidNode = node; - break; - } - } - } - return undefined; - }, - ); - - // Scan for syntactic tokens (e.g. `{`, `,`) between AST nodes, matching Go's - // findRightmostValidToken scanner step. - if (!shouldSkipChild(n)) { - const startPos = rightmostValidNode !== undefined ? rightmostValidNode.end : n.pos; - const targetEnd = Math.min(endPos, position); - if (startPos < targetEnd) { - const scanner = getScannerForSourceFile(sourceFile, startPos); - let pos = startPos; - let lastScannedToken: Node | undefined; - while (pos < targetEnd) { - const tokenStart = scanner.getTokenStart(); - if (tokenStart >= position) break; - const tokenFullStart = scanner.getTokenFullStart(); - const tokenEnd = scanner.getTokenEnd(); - const token = scanner.getToken(); - const flags = scanner.getTokenFlags(); - lastScannedToken = getOrCreateToken(sourceFile, token, tokenFullStart, tokenEnd, n, flags); - pos = tokenEnd; - scanner.scan(); - } - if (lastScannedToken !== undefined) { - return lastScannedToken; - } - } - } - - if (!hasChildren) { - if (n !== containingNode) { - return n; - } - return undefined; - } - - if (rightmostValidNode !== undefined) { - return find(rightmostValidNode, rightmostValidNode.end); - } - return undefined; - }; - - return find(containingNode, endPos); -} - -function isValidPrecedingNode(node: Node, sourceFile: SourceFile): boolean { - if (node.kind === SyntaxKind.EndOfFile) { - return false; - } - const start = getTokenPosOfNode(node, sourceFile); - const width = node.end - start; - return width > 0; -} - -function shouldSkipChild(node: Node): boolean { - return node.kind === SyntaxKind.JSDoc || - node.kind === SyntaxKind.JSDocText || - node.kind === SyntaxKind.JSDocTypeLiteral || - node.kind === SyntaxKind.JSDocSignature || - node.kind === SyntaxKind.JSDocLink || - node.kind === SyntaxKind.JSDocLinkCode || - node.kind === SyntaxKind.JSDocLinkPlain || - isJSDocTag(node); -} - -function isJSDocTag(node: Node): boolean { - return node.kind >= SyntaxKind.FirstJSDocTagNode && node.kind <= SyntaxKind.LastJSDocTagNode; -} - -// In Strada, if a JSDoc node has a single comment, that comment is represented as a string -// property as a simplification, and therefore that comment is not visited by forEachChild. -// To match, we skip single-element comment NodeLists within JSDoc/JSDocTag nodes. -function isJSDocCommentChildKind(kind: SyntaxKind): boolean { - switch (kind) { - case SyntaxKind.JSDocText: - case SyntaxKind.JSDocLink: - case SyntaxKind.JSDocLinkCode: - case SyntaxKind.JSDocLinkPlain: - return true; - default: - return false; - } -} - -function isJSDocSingleCommentNodeList(nodes: NodeArray): boolean { - return nodes.length === 1 && isJSDocCommentChildKind(nodes[0].kind); -} - -function getScannerForSourceFile(sourceFile: SourceFile, pos: number) { - const scanner = createScanner(/*skipTrivia*/ true, sourceFile.languageVariant, sourceFile.text); - scanner.resetTokenState(pos); - scanner.scan(); - return scanner; -} - -type Mutable = { -readonly [K in keyof T]: T[K]; }; - -function getOrCreateToken(sourceFile: SourceFile, kind: SyntaxKind, pos: number, end: number, parent: Node, _flags: number): Node { - const key = `${pos}_${end}`; - if (!sourceFile.tokenCache) { - sourceFile.tokenCache = new Map(); - } - - const existing = sourceFile.tokenCache.get(key); - if (existing !== undefined) { - return existing; - } - - const token: Mutable = createToken(kind as TokenSyntaxKind); - token.pos = pos; - token.end = end; - token.parent = parent; - sourceFile.tokenCache.set(key, token); - return token; -} - -/** Binary search a node list for the node containing position. */ -function binarySearchNodeList( - nodes: NodeArray, - testNode: (node: Node) => number, - onLeft: (node: Node, index: number, arr: NodeArray) => void, - onMatch: (node: Node) => void, -): void { - let lo = 0; - let hi = nodes.length - 1; - while (lo <= hi) { - const mid = (lo + hi) >>> 1; - const node = nodes[mid]; - if (node.flags & NodeFlags.Reparsed) { - // Skip reparsed nodes: try to find a non-reparsed node nearby - let found = false; - for (let i = mid + 1; i <= hi; i++) { - if (!(nodes[i].flags & NodeFlags.Reparsed)) { - const cmp = testNode(nodes[i]); - if (cmp < 0) { - onLeft(nodes[i], i, nodes); - lo = i + 1; - } - else if (cmp > 0) { - hi = i - 1; - } - else { - onMatch(nodes[i]); - return; - } - found = true; - break; - } - } - if (!found) { - hi = mid - 1; - } - continue; - } - const cmp = testNode(node); - if (cmp < 0) { - onLeft(node, mid, nodes); - lo = mid + 1; - } - else if (cmp > 0) { - hi = mid - 1; - } - else { - onMatch(node); - return; - } - } -} - -function binarySearchForPrecedingToken(nodes: NodeArray, position: number): number { - let lo = 0; - let hi = nodes.length - 1; - let result = -1; - while (lo <= hi) { - const mid = (lo + hi) >>> 1; - const node = nodes[mid]; - if (node.flags & NodeFlags.Reparsed) { - lo = mid + 1; - continue; - } - if (position < node.end) { - if (mid === 0 || position >= nodes[mid - 1].end) { - result = mid; - break; - } - hi = mid - 1; - } - else { - lo = mid + 1; - } - } - return result; -} +import { NodeFlags } from "#enums/nodeFlags"; +import { SyntaxKind } from "#enums/syntaxKind"; +import type { TokenSyntaxKind } from "./ast.generated.ts"; +import type { + Node, + NodeArray, + SourceFile, +} from "./ast.ts"; +import { createToken } from "./factory.generated.ts"; +import { + isJSDocNodeKind, + isKeywordKind, + isPrivateIdentifier, + isPropertyNameLiteral, + isTokenKind, +} from "./is.ts"; +import { + createScanner, + skipTrivia, +} from "./scanner.ts"; + +export function getTokenAtPosition(sourceFile: SourceFile, position: number): Node { + return getTokenAtPositionImpl(sourceFile, position, /*allowPositionInLeadingTrivia*/ true, /*includePrecedingTokenAtEndPosition*/ undefined); +} + +export function getTouchingPropertyName(sourceFile: SourceFile, position: number): Node { + return getTokenAtPositionImpl(sourceFile, position, /*allowPositionInLeadingTrivia*/ false, node => isPropertyNameLiteral(node) || isKeywordKind(node.kind) || isPrivateIdentifier(node)); +} + +export function getTouchingToken(sourceFile: SourceFile, position: number): Node { + return getTokenAtPositionImpl(sourceFile, position, /*allowPositionInLeadingTrivia*/ false, /*includePrecedingTokenAtEndPosition*/ undefined); +} + +/** + * Finds the token that starts immediately after `previousToken` ends, searching + * within `parent`. Returns `undefined` if no such token exists. + */ +export function findNextToken(previousToken: Node, parent: Node, sourceFile: SourceFile): Node | undefined { + return find(parent); + + function find(n: Node): Node | undefined { + if (isTokenKind(n.kind) && n.pos === previousToken.end) { + // This is the token that starts at the end of previousToken – return it. + return n; + } + + // Find the child node that contains `previousToken` or starts immediately after it. + let foundNode: Node | undefined; + + const visitChild = (node: Node) => { + if (node.flags & NodeFlags.Reparsed) { + return undefined; + } + if (node.pos <= previousToken.end && node.end > previousToken.end) { + foundNode = node; + } + return undefined; + }; + + // Visit JSDoc children first (mirrors Go's VisitEachChildAndJSDoc). + if (n.jsDoc) { + for (const jsdoc of n.jsDoc) { + visitChild(jsdoc); + } + } + + n.forEachChild( + visitChild, + nodes => { + if (nodes.length > 0 && foundNode === undefined) { + for (const node of nodes) { + if (node.flags & NodeFlags.Reparsed) continue; + if (node.pos > previousToken.end) break; + if (node.end > previousToken.end) { + foundNode = node; + break; + } + } + } + return undefined; + }, + ); + + // Recurse into the found child. + if (foundNode !== undefined) { + return find(foundNode); + } + + // No AST child covers the position; use the scanner to find the syntactic token. + // The scanner is initialized at `previousToken.end`, so tokenFullStart === previousToken.end. + const startPos = previousToken.end; + if (startPos >= n.pos && startPos < n.end) { + const scanner = getScannerForSourceFile(sourceFile, startPos); + const token = scanner.getToken(); + const tokenFullStart = scanner.getTokenFullStart(); + const tokenEnd = scanner.getTokenEnd(); + const flags = scanner.getTokenFlags(); + return getOrCreateToken(sourceFile, token, tokenFullStart, tokenEnd, n, flags); + } + + return undefined; + } +} + +/** + * Finds the leftmost token satisfying `position < token.end`. + * If the position is in the trivia of that leftmost token, or the token is invalid, + * returns the rightmost valid token with `token.end <= position`. + * Excludes `JsxText` tokens containing only whitespace. + */ +export function findPrecedingToken(sourceFile: SourceFile, position: number): Node | undefined { + return findPrecedingTokenImpl(sourceFile, position, sourceFile); +} + +function getTokenAtPositionImpl( + sourceFile: SourceFile, + position: number, + allowPositionInLeadingTrivia: boolean, + includePrecedingTokenAtEndPosition: ((node: Node) => boolean) | undefined, +): Node { + let current: Node = sourceFile; + let nodeAfterLeft: Node | undefined; + const state: { + next: Node | undefined; + prevSubtree: Node | undefined; + left: number; + } = { + next: undefined, + prevSubtree: undefined, + left: 0, + }; + + const getIncludedPrecedingToken = (subtree: Node): Node | undefined => { + const child = findPrecedingTokenImpl(sourceFile, position, subtree); + if (child !== undefined && child.end === position && includePrecedingTokenAtEndPosition!(child)) { + return child; + } + return undefined; + }; + + const testNode = (node: Node): number => { + if (node.kind !== SyntaxKind.EndOfFile && node.end === position && includePrecedingTokenAtEndPosition !== undefined) { + if (state.prevSubtree !== undefined && getIncludedPrecedingToken(state.prevSubtree) !== undefined) { + return 0; + } + state.prevSubtree = node; + } + // A node "contains" the position if position < end, except nodes at the file end + // treat end as inclusive (there's nowhere else to look). This applies to the EOF + // token itself, and to JSDoc nodes reaching EOF (e.g. unterminated JSDoc comments). + if ( + node.end < position || node.end === position && + node.kind !== SyntaxKind.EndOfFile && + (!isJSDocNodeKind(node.kind) || node.end !== sourceFile.endOfFileToken.end) + ) { + return -1; + } + const nodePos = getPosition(node, sourceFile, allowPositionInLeadingTrivia); + if (nodePos > position) { + return 1; + } + return 0; + }; + + while (true) { + // Visit each child of current to find the one containing the position. + state.next = undefined; + nodeAfterLeft = undefined as Node | undefined; + + // In Strada, JSDoc nodes with a single comment represent that comment as a string + // property (not a child node), so forEachChild does not visit it. We replicate this + // by detecting single-comment NodeLists in visitList and skipping their elements in visitNode. + let skipSingleCommentChildren = false; + + const visitNode = (node: Node) => { + if (node.flags & NodeFlags.Reparsed) { + return undefined; + } + if (skipSingleCommentChildren && isJSDocCommentChildKind(node.kind)) { + return undefined; + } + if (nodeAfterLeft === undefined) { + nodeAfterLeft = node; + } + if (state.next === undefined) { + const result = testNode(node); + switch (result) { + case -1: + if (!isJSDocNodeKind(node.kind)) { + state.left = node.end; + } + nodeAfterLeft = undefined; + break; + case 0: + state.next = node; + break; + } + } + return undefined; + }; + + // Visit JSDoc children first, then regular children (mirrors Go's VisitEachChildAndJSDoc). + if (current.jsDoc) { + for (const jsdoc of current.jsDoc) { + visitNode(jsdoc); + } + } + + current.forEachChild( + visitNode, + nodes => { + // Track whether this NodeList is a single-comment list that should be skipped. + // The flag affects the subsequent forEachNode(visitNode) calls for this NodeList. + skipSingleCommentChildren = isJSDocSingleCommentNodeList(nodes); + if (nodes.length === 0 || skipSingleCommentChildren) { + return undefined; + } + if (nodeAfterLeft === undefined) { + for (const node of nodes) { + if (!(node.flags & NodeFlags.Reparsed)) { + nodeAfterLeft = node; + break; + } + } + } + if (state.next === undefined) { + if (nodes.end === position && includePrecedingTokenAtEndPosition !== undefined) { + state.left = nodes.end; + nodeAfterLeft = undefined; + state.prevSubtree = nodes[nodes.length - 1]; + } + else if (nodes.end <= position) { + state.left = nodes.end; + nodeAfterLeft = undefined; + } + else if (nodes.pos <= position) { + binarySearchNodeList(nodes, testNode, (node, middle, arr) => { + state.left = node.end; + nodeAfterLeft = undefined; + for (let i = middle + 1; i < arr.length; i++) { + if (!(arr[i].flags & NodeFlags.Reparsed)) { + nodeAfterLeft = arr[i]; + break; + } + } + }, found => { + state.next = found; + }); + } + } + return undefined; + }, + ); + + // If prevSubtree was set, check if the rightmost token of prevSubtree should be returned. + if (state.prevSubtree !== undefined) { + const child = getIncludedPrecedingToken(state.prevSubtree); + if (child !== undefined) { + return child; + } + state.prevSubtree = undefined; + } + + if (state.next === undefined) { + if (isTokenKind(current.kind) || shouldSkipChild(current)) { + return current; + } + // Use the scanner to find a token not stored in the AST. + const scanner = getScannerForSourceFile(sourceFile, state.left); + let end = current.end; + const afterLeft = nodeAfterLeft; + if (afterLeft !== undefined) { + end = afterLeft.pos; + } + while (state.left < end) { + const token = scanner.getToken(); + const tokenFullStart = scanner.getTokenFullStart(); + const tokenStart = allowPositionInLeadingTrivia ? tokenFullStart : scanner.getTokenStart(); + const tokenEnd = scanner.getTokenEnd(); + const flags = scanner.getTokenFlags(); + if (tokenEnd > end) { + break; + } + if (tokenStart <= position && position < tokenEnd) { + if (token === SyntaxKind.Identifier || !isTokenKind(token)) { + if (isJSDocNodeKind(current.kind)) { + return current; + } + throw new Error(`did not expect ${SyntaxKind[current.kind]} to have ${SyntaxKind[token]} in its trivia`); + } + return getOrCreateToken(sourceFile, token, tokenFullStart, tokenEnd, current, flags); + } + if (includePrecedingTokenAtEndPosition !== undefined && tokenEnd === position) { + const prevToken = getOrCreateToken(sourceFile, token, tokenFullStart, tokenEnd, current, flags); + if (includePrecedingTokenAtEndPosition(prevToken)) { + return prevToken; + } + } + state.left = tokenEnd; + scanner.scan(); + } + return current; + } + + current = state.next; + state.left = current.pos; + nodeAfterLeft = undefined; + } +} + +function getPosition(node: Node, sourceFile: SourceFile, allowPositionInLeadingTrivia: boolean): number { + if (allowPositionInLeadingTrivia) { + return node.pos; + } + return getTokenPosOfNode(node, sourceFile, /*includeJSDoc*/ true); +} + +/** @internal */ +export function getTokenPosOfNode(node: Node, sourceFile: SourceFile, includeJSDoc?: boolean): number { + // With nodes that have no width (i.e. 'Missing' nodes), we actually *don't* + // want to skip trivia because this will launch us forward to the next token. + if (nodeIsMissing(node)) { + return node.pos; + } + if (isJSDocNodeKind(node.kind) || node.kind === SyntaxKind.JsxText) { + return skipTrivia(sourceFile.text, node.pos, /*stopAfterLineBreak*/ false, /*stopAtComments*/ true); + } + if (includeJSDoc && node.jsDoc && node.jsDoc.length > 0) { + return getTokenPosOfNode(node.jsDoc[0], sourceFile, /*includeJSDoc*/ false); + } + return skipTrivia(sourceFile.text, node.pos, /*stopAfterLineBreak*/ false, /*stopAtComments*/ false, /*inJSDoc*/ !!(node.flags & NodeFlags.JSDoc)); +} + +function nodeIsMissing(node: Node): boolean { + return node.pos === node.end && node.pos >= 0 && node.kind !== SyntaxKind.EndOfFile; +} + +function findPrecedingTokenImpl(sourceFile: SourceFile, position: number, startNode: Node): Node | undefined { + const find = (n: Node): Node | undefined => { + if (isTokenKind(n.kind) && n.kind !== SyntaxKind.EndOfFile) { + return n; + } + + let foundChild: Node | undefined; + let prevChild: Node | undefined; + + // Visit JSDoc nodes first (mirrors Go's VisitEachChildAndJSDoc). + if (n.jsDoc) { + for (const jsdoc of n.jsDoc) { + if (jsdoc.flags & NodeFlags.Reparsed) continue; + if (foundChild !== undefined) break; + if (position < jsdoc.end && (prevChild === undefined || prevChild.end <= position)) { + foundChild = jsdoc; + } + else { + prevChild = jsdoc; + } + } + } + + let skipSingleCommentChildrenImpl = false; + n.forEachChild( + node => { + if (node.flags & NodeFlags.Reparsed) { + return undefined; + } + if (skipSingleCommentChildrenImpl && isJSDocCommentChildKind(node.kind)) { + return undefined; + } + if (foundChild !== undefined) { + return undefined; + } + if (position < node.end && (prevChild === undefined || prevChild.end <= position)) { + foundChild = node; + } + else { + prevChild = node; + } + return undefined; + }, + nodes => { + skipSingleCommentChildrenImpl = isJSDocSingleCommentNodeList(nodes); + if (foundChild !== undefined) { + return undefined; + } + if (nodes.length > 0 && !skipSingleCommentChildrenImpl) { + const index = binarySearchForPrecedingToken(nodes, position); + if (index >= 0 && !(nodes[index].flags & NodeFlags.Reparsed)) { + foundChild = nodes[index]; + } + const lookupIndex = index >= 0 ? index - 1 : nodes.length - 1; + for (let i = lookupIndex; i >= 0; i--) { + if (!(nodes[i].flags & NodeFlags.Reparsed)) { + if (prevChild === undefined) { + prevChild = nodes[i]; + } + break; + } + } + } + return undefined; + }, + ); + + if (foundChild !== undefined) { + const start = getTokenPosOfNode(foundChild, sourceFile, /*includeJSDoc*/ true); + if (start >= position) { + if (position >= foundChild.pos) { + // We are in the leading trivia of foundChild. Check for JSDoc nodes of n + // preceding foundChild, mirroring Go's findPrecedingToken logic. + let jsDoc: Node | undefined; + if (n.jsDoc) { + for (let i = n.jsDoc.length - 1; i >= 0; i--) { + if (n.jsDoc[i].pos >= foundChild.pos) { + jsDoc = n.jsDoc[i]; + break; + } + } + } + if (jsDoc !== undefined) { + if (position < jsDoc.end) { + return find(jsDoc); + } + return findRightmostValidToken(sourceFile, jsDoc.end, n, position); + } + return findRightmostValidToken(sourceFile, foundChild.pos, n, -1); + } + // Answer is in tokens between two visited children. + return findRightmostValidToken(sourceFile, foundChild.pos, n, position); + } + return find(foundChild); + } + + if (position >= n.end) { + return findRightmostValidToken(sourceFile, n.end, n, -1); + } + return findRightmostValidToken(sourceFile, n.end, n, position); + }; + + return find(startNode); +} + +function findRightmostValidToken(sourceFile: SourceFile, endPos: number, containingNode: Node, position: number): Node | undefined { + if (position === -1) { + position = containingNode.end; + } + + const find = (n: Node, endPos: number): Node | undefined => { + if (isTokenKind(n.kind) && n.kind !== SyntaxKind.EndOfFile) { + return n; + } + + let rightmostValidNode: Node | undefined; + let hasChildren = false; + + // Visit JSDoc nodes first (mirrors Go's VisitEachChildAndJSDoc). + if (n.jsDoc) { + hasChildren = true; + for (const jsdoc of n.jsDoc) { + if (jsdoc.flags & NodeFlags.Reparsed) continue; + if (jsdoc.end > endPos || getTokenPosOfNode(jsdoc, sourceFile) >= position) continue; + if (isValidPrecedingNode(jsdoc, sourceFile)) { + rightmostValidNode = jsdoc; + } + } + } + + let skipSingleCommentChildren = false; + n.forEachChild( + node => { + if (node.flags & NodeFlags.Reparsed) { + return undefined; + } + if (skipSingleCommentChildren && isJSDocCommentChildKind(node.kind)) { + return undefined; + } + hasChildren = true; + if (node.end > endPos || getTokenPosOfNode(node, sourceFile) >= position) { + return undefined; + } + if (isValidPrecedingNode(node, sourceFile)) { + rightmostValidNode = node; + } + return undefined; + }, + nodes => { + // Skip single-comment JSDoc NodeLists (e.g. JSDocText children of a JSDoc node): + // In Go, these are stored as string properties and are never visited as children. + skipSingleCommentChildren = isJSDocSingleCommentNodeList(nodes); + if (nodes.length > 0 && !skipSingleCommentChildren) { + hasChildren = true; + for (let i = nodes.length - 1; i >= 0; i--) { + const node = nodes[i]; + if (node.flags & NodeFlags.Reparsed) continue; + if (node.end > endPos || getTokenPosOfNode(node, sourceFile) >= position) continue; + if (isValidPrecedingNode(node, sourceFile)) { + rightmostValidNode = node; + break; + } + } + } + return undefined; + }, + ); + + // Scan for syntactic tokens (e.g. `{`, `,`) between AST nodes, matching Go's + // findRightmostValidToken scanner step. + if (!shouldSkipChild(n)) { + const startPos = rightmostValidNode !== undefined ? rightmostValidNode.end : n.pos; + const targetEnd = Math.min(endPos, position); + if (startPos < targetEnd) { + const scanner = getScannerForSourceFile(sourceFile, startPos); + let pos = startPos; + let lastScannedToken: Node | undefined; + while (pos < targetEnd) { + const tokenStart = scanner.getTokenStart(); + if (tokenStart >= position) break; + const tokenFullStart = scanner.getTokenFullStart(); + const tokenEnd = scanner.getTokenEnd(); + const token = scanner.getToken(); + const flags = scanner.getTokenFlags(); + lastScannedToken = getOrCreateToken(sourceFile, token, tokenFullStart, tokenEnd, n, flags); + pos = tokenEnd; + scanner.scan(); + } + if (lastScannedToken !== undefined) { + return lastScannedToken; + } + } + } + + if (!hasChildren) { + if (n !== containingNode) { + return n; + } + return undefined; + } + + if (rightmostValidNode !== undefined) { + return find(rightmostValidNode, rightmostValidNode.end); + } + return undefined; + }; + + return find(containingNode, endPos); +} + +function isValidPrecedingNode(node: Node, sourceFile: SourceFile): boolean { + if (node.kind === SyntaxKind.EndOfFile) { + return false; + } + const start = getTokenPosOfNode(node, sourceFile); + const width = node.end - start; + return width > 0; +} + +function shouldSkipChild(node: Node): boolean { + return node.kind === SyntaxKind.JSDoc || + node.kind === SyntaxKind.JSDocText || + node.kind === SyntaxKind.JSDocTypeLiteral || + node.kind === SyntaxKind.JSDocSignature || + node.kind === SyntaxKind.JSDocLink || + node.kind === SyntaxKind.JSDocLinkCode || + node.kind === SyntaxKind.JSDocLinkPlain || + isJSDocTag(node); +} + +function isJSDocTag(node: Node): boolean { + return node.kind >= SyntaxKind.FirstJSDocTagNode && node.kind <= SyntaxKind.LastJSDocTagNode; +} + +// In Strada, if a JSDoc node has a single comment, that comment is represented as a string +// property as a simplification, and therefore that comment is not visited by forEachChild. +// To match, we skip single-element comment NodeLists within JSDoc/JSDocTag nodes. +function isJSDocCommentChildKind(kind: SyntaxKind): boolean { + switch (kind) { + case SyntaxKind.JSDocText: + case SyntaxKind.JSDocLink: + case SyntaxKind.JSDocLinkCode: + case SyntaxKind.JSDocLinkPlain: + return true; + default: + return false; + } +} + +function isJSDocSingleCommentNodeList(nodes: NodeArray): boolean { + return nodes.length === 1 && isJSDocCommentChildKind(nodes[0].kind); +} + +function getScannerForSourceFile(sourceFile: SourceFile, pos: number) { + const scanner = createScanner(/*skipTrivia*/ true, sourceFile.languageVariant, sourceFile.text); + scanner.resetTokenState(pos); + scanner.scan(); + return scanner; +} + +type Mutable = { -readonly [K in keyof T]: T[K]; }; + +function getOrCreateToken(sourceFile: SourceFile, kind: SyntaxKind, pos: number, end: number, parent: Node, _flags: number): Node { + const key = `${pos}_${end}`; + if (!sourceFile.tokenCache) { + sourceFile.tokenCache = new Map(); + } + + const existing = sourceFile.tokenCache.get(key); + if (existing !== undefined) { + return existing; + } + + const token: Mutable = createToken(kind as TokenSyntaxKind); + token.pos = pos; + token.end = end; + token.parent = parent; + sourceFile.tokenCache.set(key, token); + return token; +} + +/** Binary search a node list for the node containing position. */ +function binarySearchNodeList( + nodes: NodeArray, + testNode: (node: Node) => number, + onLeft: (node: Node, index: number, arr: NodeArray) => void, + onMatch: (node: Node) => void, +): void { + let lo = 0; + let hi = nodes.length - 1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + const node = nodes[mid]; + if (node.flags & NodeFlags.Reparsed) { + // Skip reparsed nodes: try to find a non-reparsed node nearby + let found = false; + for (let i = mid + 1; i <= hi; i++) { + if (!(nodes[i].flags & NodeFlags.Reparsed)) { + const cmp = testNode(nodes[i]); + if (cmp < 0) { + onLeft(nodes[i], i, nodes); + lo = i + 1; + } + else if (cmp > 0) { + hi = i - 1; + } + else { + onMatch(nodes[i]); + return; + } + found = true; + break; + } + } + if (!found) { + hi = mid - 1; + } + continue; + } + const cmp = testNode(node); + if (cmp < 0) { + onLeft(node, mid, nodes); + lo = mid + 1; + } + else if (cmp > 0) { + hi = mid - 1; + } + else { + onMatch(node); + return; + } + } +} + +function binarySearchForPrecedingToken(nodes: NodeArray, position: number): number { + let lo = 0; + let hi = nodes.length - 1; + let result = -1; + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + const node = nodes[mid]; + if (node.flags & NodeFlags.Reparsed) { + lo = mid + 1; + continue; + } + if (position < node.end) { + if (mid === 0 || position >= nodes[mid - 1].end) { + result = mid; + break; + } + hi = mid - 1; + } + else { + lo = mid + 1; + } + } + return result; +} diff --git a/packages/typescript/src/ast/clone.ts b/packages/typescript/src/ast/clone.ts index f36af7617a1a8..5b132bbb261bc 100644 --- a/packages/typescript/src/ast/clone.ts +++ b/packages/typescript/src/ast/clone.ts @@ -1,141 +1,141 @@ -import { SyntaxKind } from "#enums/syntaxKind"; -import type { - Node, - NodeArray, - NumericLiteral, - ReadonlyTextRange, - StringLiteral, -} from "./ast.ts"; -import { - cloneNode, - createNodeArray, - createNumericLiteral, - createStringLiteral, -} from "./factory.generated.ts"; -import { visitEachChild } from "./visitor.ts"; - -function isArray(value: any): value is readonly unknown[] { - // See: https://github.com/microsoft/TypeScript/issues/17002 - return Array.isArray(value); -} - -function forEachChildRecursively(rootNode: Node, cbNode: (node: Node, parent: Node) => T | "skip" | undefined, cbNodes?: (nodes: NodeArray, parent: Node) => T | "skip" | undefined): T | undefined { - const queue: (Node | NodeArray)[] = gatherPossibleChildren(rootNode); - const parents: Node[] = []; // tracks parent references for elements in queue - while (parents.length < queue.length) { - parents.push(rootNode); - } - while (queue.length !== 0) { - const current = queue.pop()!; - const parent = parents.pop()!; - if (isArray(current)) { - if (cbNodes) { - const res = cbNodes(current, parent); - if (res) { - if (res === "skip") continue; - return res; - } - } - for (let i = current.length - 1; i >= 0; --i) { - queue.push(current[i]); - parents.push(parent); - } - } - else { - const res = cbNode(current, parent); - if (res) { - if (res === "skip") continue; - return res; - } - if (current.kind >= SyntaxKind.FirstNode) { - // add children in reverse order to the queue, so popping gives the first child - for (const child of gatherPossibleChildren(current)) { - queue.push(child); - parents.push(current); - } - } - } - } -} - -function gatherPossibleChildren(node: Node) { - const children: (Node | NodeArray)[] = []; - node.forEachChild(addWorkItem, addWorkItem); // By using a stack above and `unshift` here, we emulate a depth-first preorder traversal - return children; - - function addWorkItem(n: Node | NodeArray) { - children.unshift(n); - } -} - -function setParentRecursive(rootNode: T | undefined): T | undefined { - if (rootNode === undefined) return rootNode; - forEachChildRecursively(rootNode, (child, parent) => { - (child as any).parent = parent; - return undefined; - }); - return rootNode; -} - -function setTextRange(node: T, range: ReadonlyTextRange | undefined): T { - if (range) { - (node as any).pos = range.pos; - (node as any).end = range.end; - } - return node; -} - -/** - * Creates a deep clone of a node and its subtree, synthesizing new nodes for every child. - * The resulting tree has fully set parent pointers. - * - * @param node The node to clone. - * @param includeTrivia Whether to preserve the text range (pos/end) on the clone. - */ -export function getSynthesizedDeepClone(node: T, includeTrivia?: boolean): T; -export function getSynthesizedDeepClone(node: T | undefined, includeTrivia?: boolean): T | undefined; -export function getSynthesizedDeepClone(node: T | undefined, includeTrivia = true): T | undefined { - const clone = node && getSynthesizedDeepCloneWorker(node); - if (clone && !includeTrivia) { - (clone as any).pos = -1; - (clone as any).end = -1; - } - return setParentRecursive(clone); -} - -/** - * Creates deep clones of a NodeArray and all its elements. - */ -export function getSynthesizedDeepClones(nodes: NodeArray, includeTrivia?: boolean): NodeArray; -export function getSynthesizedDeepClones(nodes: NodeArray | undefined, includeTrivia?: boolean): NodeArray | undefined; -export function getSynthesizedDeepClones(nodes: NodeArray | undefined, includeTrivia = true): NodeArray | undefined { - if (nodes) { - const cloned = createNodeArray( - nodes.map(n => getSynthesizedDeepClone(n, includeTrivia)), - nodes.pos, - nodes.end, - ); - return cloned; - } - return nodes; -} - -function getSynthesizedDeepCloneWorker(node: T): T { - const visited = visitEachChild(node, n => getSynthesizedDeepCloneWorker(n)); - - if (visited === node) { - // Leaf node — visitEachChild returned the same node since there are no children. - // We need to explicitly clone it. - const clone = node.kind === SyntaxKind.StringLiteral - ? createStringLiteral((node as Node as StringLiteral).text, (node as Node as StringLiteral).tokenFlags) as Node as T - : node.kind === SyntaxKind.NumericLiteral - ? createNumericLiteral((node as Node as NumericLiteral).text, (node as Node as NumericLiteral).tokenFlags) as Node as T - : cloneNode(node); - return setTextRange(clone, node); - } - - // visitEachChild already created a new node with visited children. - // Clear the parent since setParentRecursive will set it later. - (visited as any).parent = undefined!; - return visited; -} +import { SyntaxKind } from "#enums/syntaxKind"; +import type { + Node, + NodeArray, + NumericLiteral, + ReadonlyTextRange, + StringLiteral, +} from "./ast.ts"; +import { + cloneNode, + createNodeArray, + createNumericLiteral, + createStringLiteral, +} from "./factory.generated.ts"; +import { visitEachChild } from "./visitor.ts"; + +function isArray(value: any): value is readonly unknown[] { + // See: https://github.com/microsoft/TypeScript/issues/17002 + return Array.isArray(value); +} + +function forEachChildRecursively(rootNode: Node, cbNode: (node: Node, parent: Node) => T | "skip" | undefined, cbNodes?: (nodes: NodeArray, parent: Node) => T | "skip" | undefined): T | undefined { + const queue: (Node | NodeArray)[] = gatherPossibleChildren(rootNode); + const parents: Node[] = []; // tracks parent references for elements in queue + while (parents.length < queue.length) { + parents.push(rootNode); + } + while (queue.length !== 0) { + const current = queue.pop()!; + const parent = parents.pop()!; + if (isArray(current)) { + if (cbNodes) { + const res = cbNodes(current, parent); + if (res) { + if (res === "skip") continue; + return res; + } + } + for (let i = current.length - 1; i >= 0; --i) { + queue.push(current[i]); + parents.push(parent); + } + } + else { + const res = cbNode(current, parent); + if (res) { + if (res === "skip") continue; + return res; + } + if (current.kind >= SyntaxKind.FirstNode) { + // add children in reverse order to the queue, so popping gives the first child + for (const child of gatherPossibleChildren(current)) { + queue.push(child); + parents.push(current); + } + } + } + } +} + +function gatherPossibleChildren(node: Node) { + const children: (Node | NodeArray)[] = []; + node.forEachChild(addWorkItem, addWorkItem); // By using a stack above and `unshift` here, we emulate a depth-first preorder traversal + return children; + + function addWorkItem(n: Node | NodeArray) { + children.unshift(n); + } +} + +function setParentRecursive(rootNode: T | undefined): T | undefined { + if (rootNode === undefined) return rootNode; + forEachChildRecursively(rootNode, (child, parent) => { + (child as any).parent = parent; + return undefined; + }); + return rootNode; +} + +function setTextRange(node: T, range: ReadonlyTextRange | undefined): T { + if (range) { + (node as any).pos = range.pos; + (node as any).end = range.end; + } + return node; +} + +/** + * Creates a deep clone of a node and its subtree, synthesizing new nodes for every child. + * The resulting tree has fully set parent pointers. + * + * @param node The node to clone. + * @param includeTrivia Whether to preserve the text range (pos/end) on the clone. + */ +export function getSynthesizedDeepClone(node: T, includeTrivia?: boolean): T; +export function getSynthesizedDeepClone(node: T | undefined, includeTrivia?: boolean): T | undefined; +export function getSynthesizedDeepClone(node: T | undefined, includeTrivia = true): T | undefined { + const clone = node && getSynthesizedDeepCloneWorker(node); + if (clone && !includeTrivia) { + (clone as any).pos = -1; + (clone as any).end = -1; + } + return setParentRecursive(clone); +} + +/** + * Creates deep clones of a NodeArray and all its elements. + */ +export function getSynthesizedDeepClones(nodes: NodeArray, includeTrivia?: boolean): NodeArray; +export function getSynthesizedDeepClones(nodes: NodeArray | undefined, includeTrivia?: boolean): NodeArray | undefined; +export function getSynthesizedDeepClones(nodes: NodeArray | undefined, includeTrivia = true): NodeArray | undefined { + if (nodes) { + const cloned = createNodeArray( + nodes.map(n => getSynthesizedDeepClone(n, includeTrivia)), + nodes.pos, + nodes.end, + ); + return cloned; + } + return nodes; +} + +function getSynthesizedDeepCloneWorker(node: T): T { + const visited = visitEachChild(node, n => getSynthesizedDeepCloneWorker(n)); + + if (visited === node) { + // Leaf node — visitEachChild returned the same node since there are no children. + // We need to explicitly clone it. + const clone = node.kind === SyntaxKind.StringLiteral + ? createStringLiteral((node as Node as StringLiteral).text, (node as Node as StringLiteral).tokenFlags) as Node as T + : node.kind === SyntaxKind.NumericLiteral + ? createNumericLiteral((node as Node as NumericLiteral).text, (node as Node as NumericLiteral).tokenFlags) as Node as T + : cloneNode(node); + return setTextRange(clone, node); + } + + // visitEachChild already created a new node with visited children. + // Clear the parent since setParentRecursive will set it later. + (visited as any).parent = undefined!; + return visited; +} diff --git a/packages/typescript/src/ast/factory.generated.ts b/packages/typescript/src/ast/factory.generated.ts index 787aa5910c820..f468f0e1e8f49 100644 --- a/packages/typescript/src/ast/factory.generated.ts +++ b/packages/typescript/src/ast/factory.generated.ts @@ -1,3836 +1,3836 @@ -// Code generated by tools/scripts/tsc/generate-ts-ast.ts. DO NOT EDIT. - -import { NodeFlags } from "#enums/nodeFlags"; -import { SyntaxKind } from "#enums/syntaxKind"; -import { TokenFlags } from "#enums/tokenFlags"; -import type { - ArrayBindingPattern, - ArrayLiteralExpression, - ArrayTypeNode, - ArrowFunction, - AsExpression, - AssertsKeyword, - AsteriskToken, - AwaitExpression, - AwaitKeyword, - BigIntLiteral, - BinaryExpression, - BinaryOperatorToken, - BindingElement, - BindingName, - Block, - BreakStatement, - CallExpression, - CallSignatureDeclaration, - CaseBlock, - CaseClause, - CaseOrDefaultClause, - CatchClause, - ClassDeclaration, - ClassElement, - ClassExpression, - ClassStaticBlockDeclaration, - ColonToken, - ComputedPropertyName, - ConciseBody, - ConditionalExpression, - ConditionalTypeNode, - ConstructorDeclaration, - ConstructorTypeNode, - ConstructSignatureDeclaration, - ContinueStatement, - DebuggerStatement, - Decorator, - DefaultClause, - DeleteExpression, - DoStatement, - DotDotDotToken, - ElementAccessExpression, - EmptyStatement, - EndOfFile, - EntityName, - EnumDeclaration, - EnumMember, - EqualsGreaterThanToken, - EqualsToken, - ExclamationToken, - ExportAssignment, - ExportDeclaration, - ExportSpecifier, - Expression, - ExpressionStatement, - ExpressionWithTypeArguments, - ExternalModuleReference, - ForInitializer, - ForInStatement, - ForOfStatement, - ForStatement, - FunctionBody, - FunctionDeclaration, - FunctionExpression, - FunctionTypeNode, - GetAccessorDeclaration, - HeritageClause, - HeritageClauseElement, - Identifier, - IfStatement, - ImportAttribute, - ImportAttributeName, - ImportAttributes, - ImportClause, - ImportDeclaration, - ImportEqualsDeclaration, - ImportPhaseModifierSyntaxKind, - ImportSpecifier, - ImportTypeNode, - IndexedAccessTypeNode, - IndexSignatureDeclaration, - InferTypeNode, - InterfaceDeclaration, - IntersectionTypeNode, - JSDoc, - JSDocAllType, - JSDocAugmentsTag, - JSDocCallbackTag, - JSDocComment, - JSDocDeprecatedTag, - JSDocFullName, - JSDocImplementsTag, - JSDocImportTag, - JSDocLink, - JSDocLinkCode, - JSDocLinkPlain, - JSDocNameReference, - JSDocNonNullableType, - JSDocNullableType, - JSDocOptionalType, - JSDocOverloadTag, - JSDocOverrideTag, - JSDocParameterTag, - JSDocPrivateTag, - JSDocPropertyTag, - JSDocProtectedTag, - JSDocPublicTag, - JSDocReadonlyTag, - JSDocReturnTag, - JSDocSatisfiesTag, - JSDocSeeTag, - JSDocSignature, - JSDocTag, - JSDocTemplateTag, - JSDocText, - JSDocThisTag, - JSDocThrowsTag, - JSDocTypedefTag, - JSDocTypeExpression, - JSDocTypeLiteral, - JSDocTypeTag, - JSDocUnknownTag, - JSDocVariadicType, - JsxAttribute, - JsxAttributeLike, - JsxAttributeName, - JsxAttributes, - JsxAttributeValue, - JsxChild, - JsxClosingElement, - JsxClosingFragment, - JsxElement, - JsxExpression, - JsxFragment, - JsxNamespacedName, - JsxOpeningElement, - JsxOpeningFragment, - JsxSelfClosingElement, - JsxSpreadAttribute, - JsxTagNameExpression, - JsxText, - KeywordExpression, - KeywordExpressionSyntaxKind, - KeywordTypeNode, - KeywordTypeSyntaxKind, - LabeledStatement, - LeftHandSideExpression, - LiteralTypeNode, - MappedTypeNode, - MemberName, - MetaProperty, - MethodDeclaration, - MethodSignatureDeclaration, - MinusToken, - MissingDeclaration, - ModifierLike, - ModuleBlock, - ModuleBody, - ModuleDeclaration, - ModuleExportName, - ModuleName, - ModuleReference, - NamedExportBindings, - NamedExports, - NamedImportBindings, - NamedImports, - NamedTupleMember, - NamespaceExport, - NamespaceExportDeclaration, - NamespaceImport, - NewExpression, - Node, - NodeArray, - NonNullExpression, - NoSubstitutionTemplateLiteral, - NotEmittedStatement, - NotEmittedTypeElement, - NumericLiteral, - ObjectBindingPattern, - ObjectLiteralElementLike, - ObjectLiteralExpression, - OmittedExpression, - OptionalTypeNode, - ParameterDeclaration, - ParenthesizedExpression, - ParenthesizedTypeNode, - PartiallyEmittedExpression, - Path, - PlusToken, - PostfixUnaryExpression, - PrefixUnaryExpression, - PrivateIdentifier, - PropertyAccessExpression, - PropertyAssignment, - PropertyDeclaration, - PropertyName, - PropertySignatureDeclaration, - QualifiedName, - QuestionDotToken, - QuestionToken, - ReadonlyKeyword, - RegularExpressionLiteral, - RestTypeNode, - ReturnStatement, - SatisfiesExpression, - SemicolonClassElement, - SetAccessorDeclaration, - ShorthandPropertyAssignment, - SourceFile, - SpreadAssignment, - SpreadElement, - Statement, - StringLiteral, - SwitchStatement, - SyntaxList, - SyntheticExpression, - SyntheticReferenceExpression, - TaggedTemplateExpression, - TemplateExpression, - TemplateHead, - TemplateLiteral, - TemplateLiteralTypeNode, - TemplateLiteralTypeSpan, - TemplateMiddle, - TemplateMiddleOrTail, - TemplateSpan, - TemplateTail, - ThisTypeNode, - ThrowStatement, - Token, - TokenSyntaxKind, - TryStatement, - TupleTypeNode, - TypeAliasDeclaration, - TypeAssertion, - TypeElement, - TypeLiteralNode, - TypeNode, - TypeOfExpression, - TypeOperatorNode, - TypeParameterDeclaration, - TypePredicateNode, - TypePredicateParameterName, - TypeQueryNode, - TypeReferenceNode, - UnionTypeNode, - VariableDeclaration, - VariableDeclarationList, - VariableStatement, - VoidExpression, - WhileStatement, - WithStatement, - YieldExpression, -} from "./ast.ts"; -import { getTokenPosOfNode } from "./astnav.ts"; -import { cloneSourceFileData } from "./utils.ts"; -import { - forEachChildOfJSDocParameterTag, - forEachChildOfJSDocPropertyTag, -} from "./visitor.ts"; - -export class NodeObject { - readonly kind: SyntaxKind; - flags: NodeFlags = 0 as NodeFlags; - readonly pos: number = -1; - readonly end: number = -1; - parent: Node = undefined!; - _data: any; - - constructor(kind: SyntaxKind, data: any) { - this.kind = kind; - this._data = data; - } - - get ambientModuleNames(): any { - return this._data?.ambientModuleNames; - } - get argument(): any { - return this._data?.argument; - } - get argumentExpression(): any { - return this._data?.argumentExpression; - } - get arguments(): any { - return this._data?.arguments; - } - get assertsModifier(): any { - return this._data?.assertsModifier; - } - get asteriskToken(): any { - return this._data?.asteriskToken; - } - get attributes(): any { - return this._data?.attributes; - } - get awaitModifier(): any { - return this._data?.awaitModifier; - } - get block(): any { - return this._data?.block; - } - get body(): any { - return this._data?.body; - } - get canonicalSourceFileName(): any { - return this._data?.canonicalSourceFileName; - } - get caseBlock(): any { - return this._data?.caseBlock; - } - get catchClause(): any { - return this._data?.catchClause; - } - get checkType(): any { - return this._data?.checkType; - } - get children(): any { - return this._data?.children; - } - get className(): any { - return this._data?.className; - } - get clauses(): any { - return this._data?.clauses; - } - get closingElement(): any { - return this._data?.closingElement; - } - get closingFragment(): any { - return this._data?.closingFragment; - } - get colonToken(): any { - return this._data?.colonToken; - } - get comment(): any { - return this._data?.comment; - } - get condition(): any { - return this._data?.condition; - } - get constraint(): any { - return this._data?.constraint; - } - get containsOnlyTriviaWhiteSpaces(): any { - return this._data?.containsOnlyTriviaWhiteSpaces; - } - get contentMapper(): any { - return this._data?.contentMapper; - } - get declarationList(): any { - return this._data?.declarationList; - } - get declarations(): any { - return this._data?.declarations; - } - get defaultType(): any { - return this._data?.defaultType; - } - get diagnosticDirectives(): any { - return this._data?.diagnosticDirectives; - } - get dotDotDotToken(): any { - return this._data?.dotDotDotToken; - } - get elementType(): any { - return this._data?.elementType; - } - get elements(): any { - return this._data?.elements; - } - get elseStatement(): any { - return this._data?.elseStatement; - } - get endOfFileToken(): any { - return this._data?.endOfFileToken; - } - get equalsGreaterThanToken(): any { - return this._data?.equalsGreaterThanToken; - } - get equalsToken(): any { - return this._data?.equalsToken; - } - get exclamationToken(): any { - return this._data?.exclamationToken; - } - get exportClause(): any { - return this._data?.exportClause; - } - get exprName(): any { - return this._data?.exprName; - } - get expression(): any { - return this._data?.expression; - } - get extendsType(): any { - return this._data?.extendsType; - } - get externalModuleIndicator(): any { - return this._data?.externalModuleIndicator; - } - get falseType(): any { - return this._data?.falseType; - } - get fileName(): any { - return this._data?.fileName; - } - get finallyBlock(): any { - return this._data?.finallyBlock; - } - get head(): any { - return this._data?.head; - } - get heritageClauses(): any { - return this._data?.heritageClauses; - } - get importClause(): any { - return this._data?.importClause; - } - get imports(): any { - return this._data?.imports; - } - get incrementor(): any { - return this._data?.incrementor; - } - get indexType(): any { - return this._data?.indexType; - } - get initializer(): any { - return this._data?.initializer; - } - get isArrayType(): any { - return this._data?.isArrayType; - } - get isBracketed(): any { - return this._data?.isBracketed; - } - get isDeclarationFile(): any { - return this._data?.isDeclarationFile; - } - get isExportEquals(): any { - return this._data?.isExportEquals; - } - get isNameFirst(): any { - return this._data?.isNameFirst; - } - get isSpread(): any { - return this._data?.isSpread; - } - get isTypeOf(): any { - return this._data?.isTypeOf; - } - get isTypeOnly(): any { - return this._data?.isTypeOnly; - } - get jsdocPropertyTags(): any { - return this._data?.jsdocPropertyTags; - } - get keyword(): any { - return this._data?.keyword; - } - get keywordToken(): any { - return this._data?.keywordToken; - } - get label(): any { - return this._data?.label; - } - get languageVariant(): any { - return this._data?.languageVariant; - } - get left(): any { - return this._data?.left; - } - get libReferenceDirectives(): any { - return this._data?.libReferenceDirectives; - } - get literal(): any { - return this._data?.literal; - } - get members(): any { - return this._data?.members; - } - get modifiers(): any { - return this._data?.modifiers; - } - get moduleAugmentations(): any { - return this._data?.moduleAugmentations; - } - get moduleReference(): any { - return this._data?.moduleReference; - } - get moduleSpecifier(): any { - return this._data?.moduleSpecifier; - } - get multiLine(): any { - return this._data?.multiLine; - } - get name(): any { - return this._data?.name; - } - get nameExpression(): any { - return this._data?.nameExpression; - } - get nameType(): any { - return this._data?.nameType; - } - get namedBindings(): any { - return this._data?.namedBindings; - } - get namespace(): any { - return this._data?.namespace; - } - get objectAssignmentInitializer(): any { - return this._data?.objectAssignmentInitializer; - } - get objectType(): any { - return this._data?.objectType; - } - get openingElement(): any { - return this._data?.openingElement; - } - get openingFragment(): any { - return this._data?.openingFragment; - } - get operand(): any { - return this._data?.operand; - } - get operator(): any { - return this._data?.operator; - } - get operatorToken(): any { - return this._data?.operatorToken; - } - get originalText(): any { - return this._data?.originalText; - } - get parameterName(): any { - return this._data?.parameterName; - } - get parameters(): any { - return this._data?.parameters; - } - get path(): any { - return this._data?.path; - } - get phaseModifier(): any { - return this._data?.phaseModifier; - } - get postfixToken(): any { - return this._data?.postfixToken; - } - get properties(): any { - return this._data?.properties; - } - get propertyName(): any { - return this._data?.propertyName; - } - get qualifier(): any { - return this._data?.qualifier; - } - get questionDotToken(): any { - return this._data?.questionDotToken; - } - get questionToken(): any { - return this._data?.questionToken; - } - get rawText(): any { - return this._data?.rawText; - } - get readonlyToken(): any { - return this._data?.readonlyToken; - } - get referencedFiles(): any { - return this._data?.referencedFiles; - } - get right(): any { - return this._data?.right; - } - get scriptKind(): any { - return this._data?.scriptKind; - } - get spanMap(): any { - return this._data?.spanMap; - } - get statement(): any { - return this._data?.statement; - } - get statements(): any { - return this._data?.statements; - } - get supplementalSourceFileNames(): any { - return this._data?.supplementalSourceFileNames; - } - get tag(): any { - return this._data?.tag; - } - get tagName(): any { - return this._data?.tagName; - } - get tags(): any { - return this._data?.tags; - } - get template(): any { - return this._data?.template; - } - get templateFlags(): any { - return this._data?.templateFlags; - } - get templateSpans(): any { - return this._data?.templateSpans; - } - get text(): any { - return this._data?.text; - } - get thenStatement(): any { - return this._data?.thenStatement; - } - get thisArg(): any { - return this._data?.thisArg; - } - get token(): any { - return this._data?.token; - } - get tokenCache(): any { - return this._data?.tokenCache; - } - get tokenFlags(): any { - return this._data?.tokenFlags; - } - get trueType(): any { - return this._data?.trueType; - } - get tryBlock(): any { - return this._data?.tryBlock; - } - get tupleNameSource(): any { - return this._data?.tupleNameSource; - } - get type(): any { - return this._data?.type; - } - get typeArguments(): any { - return this._data?.typeArguments; - } - get typeExpression(): any { - return this._data?.typeExpression; - } - get typeName(): any { - return this._data?.typeName; - } - get typeParameter(): any { - return this._data?.typeParameter; - } - get typeParameters(): any { - return this._data?.typeParameters; - } - get typeReferenceDirectives(): any { - return this._data?.typeReferenceDirectives; - } - get types(): any { - return this._data?.types; - } - get value(): any { - return this._data?.value; - } - get variableDeclaration(): any { - return this._data?.variableDeclaration; - } - get virtualFileName(): any { - return this._data?.virtualFileName; - } - get whenFalse(): any { - return this._data?.whenFalse; - } - get whenTrue(): any { - return this._data?.whenTrue; - } - - forEachChild(visitor: (node: Node) => T, visitArray?: (nodes: NodeArray) => T): T | undefined { - const fn = forEachChildTable[this.kind]; - return fn ? fn(this._data, visitor, visitArray) : undefined; - } - - getSourceFile(): SourceFile { - let node: Node = this as unknown as Node; - while (node.parent) node = node.parent; - return node as unknown as SourceFile; - } - - getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number { - return getTokenPosOfNode(this as unknown as Node, sourceFile ?? this.getSourceFile(), includeJsDocComment); - } - - getFullStart(): number { - return this.pos; - } - - getEnd(): number { - return this.end; - } - - getWidth(sourceFile?: SourceFile): number { - return this.getEnd() - this.getStart(sourceFile); - } - - getFullWidth(): number { - return this.end - this.pos; - } - - getLeadingTriviaWidth(sourceFile?: SourceFile): number { - return this.getStart(sourceFile) - this.pos; - } - - getFullText(sourceFile?: SourceFile): string { - return (sourceFile ?? this.getSourceFile()).text.substring(this.pos, this.end); - } - - getText(sourceFile?: SourceFile): string { - sourceFile ??= this.getSourceFile(); - return sourceFile.text.substring(this.getStart(sourceFile), this.end); - } -} - -function isNodeArray(array: readonly T[]): array is NodeArray { - return "pos" in array && "end" in array; -} - -export function createNodeArray(elements: readonly T[], pos: number = -1, end: number = -1): NodeArray { - if (isNodeArray(elements)) return elements; - const arr = elements.slice() as unknown as NodeArray & { pos: number; end: number; }; - arr.pos = pos; - arr.end = end; - return arr; -} - -export function cloneNode(node: T): T { - const data = cloneNodeData(node); - const clone = new NodeObject(node.kind, data); - (clone as any).flags = node.flags; - (clone as any).pos = node.pos; - (clone as any).end = node.end; - return clone as unknown as T; -} - -function cloneNodeData(node: Node): any { - const n = node as any; - switch (node.kind) { - case SyntaxKind.Identifier: - return { text: n.text }; - case SyntaxKind.PrivateIdentifier: - return { text: n.text }; - case SyntaxKind.QualifiedName: - return { left: n.left, right: n.right }; - case SyntaxKind.ComputedPropertyName: - return { expression: n.expression }; - case SyntaxKind.Decorator: - return { expression: n.expression }; - case SyntaxKind.IfStatement: - return { expression: n.expression, thenStatement: n.thenStatement, elseStatement: n.elseStatement }; - case SyntaxKind.DoStatement: - return { statement: n.statement, expression: n.expression }; - case SyntaxKind.WhileStatement: - return { expression: n.expression, statement: n.statement }; - case SyntaxKind.ForStatement: - return { initializer: n.initializer, condition: n.condition, incrementor: n.incrementor, statement: n.statement }; - case SyntaxKind.BreakStatement: - return { label: n.label }; - case SyntaxKind.ContinueStatement: - return { label: n.label }; - case SyntaxKind.ReturnStatement: - return { expression: n.expression }; - case SyntaxKind.WithStatement: - return { expression: n.expression, statement: n.statement }; - case SyntaxKind.SwitchStatement: - return { expression: n.expression, caseBlock: n.caseBlock }; - case SyntaxKind.CaseBlock: - return { clauses: n.clauses }; - case SyntaxKind.ThrowStatement: - return { expression: n.expression }; - case SyntaxKind.TryStatement: - return { tryBlock: n.tryBlock, catchClause: n.catchClause, finallyBlock: n.finallyBlock }; - case SyntaxKind.CatchClause: - return { variableDeclaration: n.variableDeclaration, block: n.block }; - case SyntaxKind.LabeledStatement: - return { label: n.label, statement: n.statement }; - case SyntaxKind.ExpressionStatement: - return { expression: n.expression }; - case SyntaxKind.Block: - return { statements: n.statements, multiLine: n.multiLine }; - case SyntaxKind.VariableStatement: - return { modifiers: n.modifiers, declarationList: n.declarationList }; - case SyntaxKind.VariableDeclaration: - return { name: n.name, exclamationToken: n.exclamationToken, type: n.type, initializer: n.initializer }; - case SyntaxKind.VariableDeclarationList: - return { declarations: n.declarations }; - case SyntaxKind.Parameter: - return { modifiers: n.modifiers, dotDotDotToken: n.dotDotDotToken, name: n.name, questionToken: n.questionToken, type: n.type, initializer: n.initializer }; - case SyntaxKind.BindingElement: - return { dotDotDotToken: n.dotDotDotToken, propertyName: n.propertyName, name: n.name, initializer: n.initializer }; - case SyntaxKind.MissingDeclaration: - return { modifiers: n.modifiers }; - case SyntaxKind.FunctionDeclaration: - return { modifiers: n.modifiers, asteriskToken: n.asteriskToken, name: n.name, typeParameters: n.typeParameters, parameters: n.parameters, type: n.type, body: n.body }; - case SyntaxKind.ClassDeclaration: - return { modifiers: n.modifiers, name: n.name, typeParameters: n.typeParameters, heritageClauses: n.heritageClauses, members: n.members }; - case SyntaxKind.ClassExpression: - return { modifiers: n.modifiers, name: n.name, typeParameters: n.typeParameters, heritageClauses: n.heritageClauses, members: n.members }; - case SyntaxKind.HeritageClause: - return { token: n.token, types: n.types }; - case SyntaxKind.InterfaceDeclaration: - return { modifiers: n.modifiers, name: n.name, typeParameters: n.typeParameters, heritageClauses: n.heritageClauses, members: n.members }; - case SyntaxKind.TypeAliasDeclaration: - case SyntaxKind.JSTypeAliasDeclaration: - return { modifiers: n.modifiers, name: n.name, typeParameters: n.typeParameters, type: n.type }; - case SyntaxKind.EnumMember: - return { name: n.name, initializer: n.initializer }; - case SyntaxKind.EnumDeclaration: - return { modifiers: n.modifiers, name: n.name, members: n.members }; - case SyntaxKind.ModuleBlock: - return { statements: n.statements }; - case SyntaxKind.ImportDeclaration: - case SyntaxKind.JSImportDeclaration: - return { modifiers: n.modifiers, importClause: n.importClause, moduleSpecifier: n.moduleSpecifier, attributes: n.attributes }; - case SyntaxKind.ExternalModuleReference: - return { expression: n.expression }; - case SyntaxKind.NamespaceImport: - return { name: n.name }; - case SyntaxKind.NamedImports: - return { elements: n.elements }; - case SyntaxKind.ExportAssignment: - return { modifiers: n.modifiers, isExportEquals: n.isExportEquals, type: n.type, expression: n.expression }; - case SyntaxKind.NamespaceExportDeclaration: - return { modifiers: n.modifiers, name: n.name }; - case SyntaxKind.NamespaceExport: - return { name: n.name }; - case SyntaxKind.NamedExports: - return { elements: n.elements }; - case SyntaxKind.ExportSpecifier: - return { isTypeOnly: n.isTypeOnly, propertyName: n.propertyName, name: n.name }; - case SyntaxKind.CallSignature: - return { typeParameters: n.typeParameters, parameters: n.parameters, type: n.type }; - case SyntaxKind.ConstructSignature: - return { typeParameters: n.typeParameters, parameters: n.parameters, type: n.type }; - case SyntaxKind.Constructor: - return { modifiers: n.modifiers, typeParameters: n.typeParameters, parameters: n.parameters, type: n.type, body: n.body }; - case SyntaxKind.GetAccessor: - return { modifiers: n.modifiers, name: n.name, typeParameters: n.typeParameters, parameters: n.parameters, type: n.type, body: n.body }; - case SyntaxKind.SetAccessor: - return { modifiers: n.modifiers, name: n.name, typeParameters: n.typeParameters, parameters: n.parameters, type: n.type, body: n.body }; - case SyntaxKind.IndexSignature: - return { modifiers: n.modifiers, parameters: n.parameters, type: n.type }; - case SyntaxKind.MethodSignature: - return { modifiers: n.modifiers, name: n.name, postfixToken: n.postfixToken, typeParameters: n.typeParameters, parameters: n.parameters, type: n.type }; - case SyntaxKind.MethodDeclaration: - return { modifiers: n.modifiers, asteriskToken: n.asteriskToken, name: n.name, postfixToken: n.postfixToken, typeParameters: n.typeParameters, parameters: n.parameters, type: n.type, body: n.body }; - case SyntaxKind.PropertySignature: - return { modifiers: n.modifiers, name: n.name, postfixToken: n.postfixToken, type: n.type, initializer: n.initializer }; - case SyntaxKind.PropertyDeclaration: - return { modifiers: n.modifiers, name: n.name, postfixToken: n.postfixToken, type: n.type, initializer: n.initializer }; - case SyntaxKind.ClassStaticBlockDeclaration: - return { modifiers: n.modifiers, body: n.body }; - case SyntaxKind.StringLiteral: - return { text: n.text, tokenFlags: n.tokenFlags }; - case SyntaxKind.NumericLiteral: - return { text: n.text, tokenFlags: n.tokenFlags }; - case SyntaxKind.BigIntLiteral: - return { text: n.text, tokenFlags: n.tokenFlags }; - case SyntaxKind.RegularExpressionLiteral: - return { text: n.text, tokenFlags: n.tokenFlags }; - case SyntaxKind.NoSubstitutionTemplateLiteral: - return { text: n.text, templateFlags: n.templateFlags }; - case SyntaxKind.BinaryExpression: - return { modifiers: n.modifiers, left: n.left, type: n.type, operatorToken: n.operatorToken, right: n.right }; - case SyntaxKind.PrefixUnaryExpression: - return { operator: n.operator, operand: n.operand }; - case SyntaxKind.PostfixUnaryExpression: - return { operand: n.operand, operator: n.operator }; - case SyntaxKind.YieldExpression: - return { asteriskToken: n.asteriskToken, expression: n.expression }; - case SyntaxKind.ArrowFunction: - return { modifiers: n.modifiers, typeParameters: n.typeParameters, parameters: n.parameters, type: n.type, equalsGreaterThanToken: n.equalsGreaterThanToken, body: n.body }; - case SyntaxKind.FunctionExpression: - return { modifiers: n.modifiers, asteriskToken: n.asteriskToken, name: n.name, typeParameters: n.typeParameters, parameters: n.parameters, type: n.type, body: n.body }; - case SyntaxKind.AsExpression: - return { expression: n.expression, type: n.type }; - case SyntaxKind.SatisfiesExpression: - return { expression: n.expression, type: n.type }; - case SyntaxKind.ConditionalExpression: - return { condition: n.condition, questionToken: n.questionToken, whenTrue: n.whenTrue, colonToken: n.colonToken, whenFalse: n.whenFalse }; - case SyntaxKind.PropertyAccessExpression: - return { expression: n.expression, questionDotToken: n.questionDotToken, name: n.name }; - case SyntaxKind.ElementAccessExpression: - return { expression: n.expression, questionDotToken: n.questionDotToken, argumentExpression: n.argumentExpression }; - case SyntaxKind.CallExpression: - return { expression: n.expression, questionDotToken: n.questionDotToken, typeArguments: n.typeArguments, arguments: n.arguments }; - case SyntaxKind.NewExpression: - return { expression: n.expression, typeArguments: n.typeArguments, arguments: n.arguments }; - case SyntaxKind.MetaProperty: - return { keywordToken: n.keywordToken, name: n.name }; - case SyntaxKind.NonNullExpression: - return { expression: n.expression }; - case SyntaxKind.SpreadElement: - return { expression: n.expression }; - case SyntaxKind.TemplateExpression: - return { head: n.head, templateSpans: n.templateSpans }; - case SyntaxKind.TemplateSpan: - return { expression: n.expression, literal: n.literal }; - case SyntaxKind.TaggedTemplateExpression: - return { tag: n.tag, questionDotToken: n.questionDotToken, typeArguments: n.typeArguments, template: n.template }; - case SyntaxKind.ParenthesizedExpression: - return { expression: n.expression }; - case SyntaxKind.ArrayLiteralExpression: - return { elements: n.elements, multiLine: n.multiLine }; - case SyntaxKind.ObjectLiteralExpression: - return { properties: n.properties, multiLine: n.multiLine }; - case SyntaxKind.SpreadAssignment: - return { expression: n.expression }; - case SyntaxKind.PropertyAssignment: - return { modifiers: n.modifiers, name: n.name, postfixToken: n.postfixToken, type: n.type, initializer: n.initializer }; - case SyntaxKind.ShorthandPropertyAssignment: - return { modifiers: n.modifiers, name: n.name, postfixToken: n.postfixToken, type: n.type, equalsToken: n.equalsToken, objectAssignmentInitializer: n.objectAssignmentInitializer }; - case SyntaxKind.DeleteExpression: - return { expression: n.expression }; - case SyntaxKind.TypeOfExpression: - return { expression: n.expression }; - case SyntaxKind.VoidExpression: - return { expression: n.expression }; - case SyntaxKind.AwaitExpression: - return { expression: n.expression }; - case SyntaxKind.TypeAssertionExpression: - return { type: n.type, expression: n.expression }; - case SyntaxKind.UnionType: - return { types: n.types }; - case SyntaxKind.IntersectionType: - return { types: n.types }; - case SyntaxKind.ConditionalType: - return { checkType: n.checkType, extendsType: n.extendsType, trueType: n.trueType, falseType: n.falseType }; - case SyntaxKind.TypeOperator: - return { operator: n.operator, type: n.type }; - case SyntaxKind.InferType: - return { typeParameter: n.typeParameter }; - case SyntaxKind.ArrayType: - return { elementType: n.elementType }; - case SyntaxKind.IndexedAccessType: - return { objectType: n.objectType, indexType: n.indexType }; - case SyntaxKind.TypeReference: - return { typeName: n.typeName, typeArguments: n.typeArguments }; - case SyntaxKind.ExpressionWithTypeArguments: - return { expression: n.expression, typeArguments: n.typeArguments }; - case SyntaxKind.LiteralType: - return { literal: n.literal }; - case SyntaxKind.TypePredicate: - return { assertsModifier: n.assertsModifier, parameterName: n.parameterName, type: n.type }; - case SyntaxKind.ImportAttribute: - return { name: n.name, value: n.value }; - case SyntaxKind.ImportAttributes: - return { token: n.token, attributes: n.attributes, multiLine: n.multiLine }; - case SyntaxKind.TypeQuery: - return { exprName: n.exprName, typeArguments: n.typeArguments }; - case SyntaxKind.MappedType: - return { readonlyToken: n.readonlyToken, typeParameter: n.typeParameter, nameType: n.nameType, questionToken: n.questionToken, type: n.type, members: n.members }; - case SyntaxKind.TypeLiteral: - return { members: n.members }; - case SyntaxKind.TupleType: - return { elements: n.elements }; - case SyntaxKind.NamedTupleMember: - return { dotDotDotToken: n.dotDotDotToken, name: n.name, questionToken: n.questionToken, type: n.type }; - case SyntaxKind.OptionalType: - return { type: n.type }; - case SyntaxKind.RestType: - return { type: n.type }; - case SyntaxKind.ParenthesizedType: - return { type: n.type }; - case SyntaxKind.FunctionType: - return { typeParameters: n.typeParameters, parameters: n.parameters, type: n.type }; - case SyntaxKind.ConstructorType: - return { modifiers: n.modifiers, typeParameters: n.typeParameters, parameters: n.parameters, type: n.type }; - case SyntaxKind.TemplateHead: - return { text: n.text, rawText: n.rawText, templateFlags: n.templateFlags }; - case SyntaxKind.TemplateMiddle: - return { text: n.text, rawText: n.rawText, templateFlags: n.templateFlags }; - case SyntaxKind.TemplateTail: - return { text: n.text, rawText: n.rawText, templateFlags: n.templateFlags }; - case SyntaxKind.TemplateLiteralType: - return { head: n.head, templateSpans: n.templateSpans }; - case SyntaxKind.TemplateLiteralTypeSpan: - return { type: n.type, literal: n.literal }; - case SyntaxKind.SyntheticExpression: - return { type: n.type, isSpread: n.isSpread, tupleNameSource: n.tupleNameSource }; - case SyntaxKind.PartiallyEmittedExpression: - return { expression: n.expression }; - case SyntaxKind.JsxElement: - return { openingElement: n.openingElement, children: n.children, closingElement: n.closingElement }; - case SyntaxKind.JsxAttributes: - return { properties: n.properties }; - case SyntaxKind.JsxNamespacedName: - return { namespace: n.namespace, name: n.name }; - case SyntaxKind.JsxOpeningElement: - return { tagName: n.tagName, typeArguments: n.typeArguments, attributes: n.attributes }; - case SyntaxKind.JsxSelfClosingElement: - return { tagName: n.tagName, typeArguments: n.typeArguments, attributes: n.attributes }; - case SyntaxKind.JsxFragment: - return { openingFragment: n.openingFragment, children: n.children, closingFragment: n.closingFragment }; - case SyntaxKind.JsxAttribute: - return { name: n.name, initializer: n.initializer }; - case SyntaxKind.JsxSpreadAttribute: - return { expression: n.expression }; - case SyntaxKind.JsxClosingElement: - return { tagName: n.tagName }; - case SyntaxKind.JsxExpression: - return { dotDotDotToken: n.dotDotDotToken, expression: n.expression }; - case SyntaxKind.JsxText: - return { text: n.text, containsOnlyTriviaWhiteSpaces: n.containsOnlyTriviaWhiteSpaces }; - case SyntaxKind.SyntaxList: - return { children: n.children }; - case SyntaxKind.JSDoc: - return { comment: n.comment, tags: n.tags }; - case SyntaxKind.JSDocTypeExpression: - return { type: n.type }; - case SyntaxKind.JSDocNonNullableType: - return { type: n.type }; - case SyntaxKind.JSDocNullableType: - return { type: n.type }; - case SyntaxKind.JSDocVariadicType: - return { type: n.type }; - case SyntaxKind.JSDocOptionalType: - return { type: n.type }; - case SyntaxKind.JSDocTypeTag: - return { tagName: n.tagName, typeExpression: n.typeExpression, comment: n.comment }; - case SyntaxKind.JSDocUnknownTag: - return { tagName: n.tagName, comment: n.comment }; - case SyntaxKind.JSDocTemplateTag: - return { tagName: n.tagName, constraint: n.constraint, typeParameters: n.typeParameters, comment: n.comment }; - case SyntaxKind.JSDocReturnTag: - return { tagName: n.tagName, typeExpression: n.typeExpression, comment: n.comment }; - case SyntaxKind.JSDocPublicTag: - return { tagName: n.tagName, comment: n.comment }; - case SyntaxKind.JSDocPrivateTag: - return { tagName: n.tagName, comment: n.comment }; - case SyntaxKind.JSDocProtectedTag: - return { tagName: n.tagName, comment: n.comment }; - case SyntaxKind.JSDocReadonlyTag: - return { tagName: n.tagName, comment: n.comment }; - case SyntaxKind.JSDocOverrideTag: - return { tagName: n.tagName, comment: n.comment }; - case SyntaxKind.JSDocDeprecatedTag: - return { tagName: n.tagName, comment: n.comment }; - case SyntaxKind.JSDocSeeTag: - return { tagName: n.tagName, nameExpression: n.nameExpression, comment: n.comment }; - case SyntaxKind.JSDocImplementsTag: - return { tagName: n.tagName, className: n.className, comment: n.comment }; - case SyntaxKind.JSDocAugmentsTag: - return { tagName: n.tagName, className: n.className, comment: n.comment }; - case SyntaxKind.JSDocSatisfiesTag: - return { tagName: n.tagName, typeExpression: n.typeExpression, comment: n.comment }; - case SyntaxKind.JSDocThrowsTag: - return { tagName: n.tagName, typeExpression: n.typeExpression, comment: n.comment }; - case SyntaxKind.JSDocThisTag: - return { tagName: n.tagName, typeExpression: n.typeExpression, comment: n.comment }; - case SyntaxKind.JSDocImportTag: - return { tagName: n.tagName, importClause: n.importClause, moduleSpecifier: n.moduleSpecifier, attributes: n.attributes, comment: n.comment }; - case SyntaxKind.JSDocCallbackTag: - return { tagName: n.tagName, typeExpression: n.typeExpression, name: n.name, comment: n.comment }; - case SyntaxKind.JSDocOverloadTag: - return { tagName: n.tagName, typeExpression: n.typeExpression, comment: n.comment }; - case SyntaxKind.JSDocTypedefTag: - return { tagName: n.tagName, typeExpression: n.typeExpression, name: n.name, comment: n.comment }; - case SyntaxKind.JSDocSignature: - return { typeParameters: n.typeParameters, parameters: n.parameters, type: n.type }; - case SyntaxKind.JSDocNameReference: - return { name: n.name }; - case SyntaxKind.ModuleDeclaration: - return { modifiers: n.modifiers, keyword: n.keyword, name: n.name, body: n.body }; - case SyntaxKind.ImportEqualsDeclaration: - return { modifiers: n.modifiers, isTypeOnly: n.isTypeOnly, name: n.name, moduleReference: n.moduleReference }; - case SyntaxKind.ExportDeclaration: - return { modifiers: n.modifiers, isTypeOnly: n.isTypeOnly, exportClause: n.exportClause, moduleSpecifier: n.moduleSpecifier, attributes: n.attributes }; - case SyntaxKind.ImportType: - return { isTypeOf: n.isTypeOf, argument: n.argument, attributes: n.attributes, qualifier: n.qualifier, typeArguments: n.typeArguments }; - case SyntaxKind.ImportClause: - return { phaseModifier: n.phaseModifier, name: n.name, namedBindings: n.namedBindings }; - case SyntaxKind.ImportSpecifier: - return { isTypeOnly: n.isTypeOnly, propertyName: n.propertyName, name: n.name }; - case SyntaxKind.JSDocText: - return { text: n.text }; - case SyntaxKind.JSDocLink: - return { name: n.name, text: n.text }; - case SyntaxKind.JSDocLinkPlain: - return { name: n.name, text: n.text }; - case SyntaxKind.JSDocLinkCode: - return { name: n.name, text: n.text }; - case SyntaxKind.TypeParameter: - return { modifiers: n.modifiers, name: n.name, constraint: n.constraint, expression: n.expression, defaultType: n.defaultType }; - case SyntaxKind.SyntheticReferenceExpression: - return { expression: n.expression, thisArg: n.thisArg }; - case SyntaxKind.JSDocTypeLiteral: - return { jsdocPropertyTags: n.jsdocPropertyTags, isArrayType: n.isArrayType }; - case SyntaxKind.ForInStatement: - return { awaitModifier: n.awaitModifier, initializer: n.initializer, expression: n.expression, statement: n.statement }; - case SyntaxKind.ForOfStatement: - return { awaitModifier: n.awaitModifier, initializer: n.initializer, expression: n.expression, statement: n.statement }; - case SyntaxKind.CaseClause: - return { expression: n.expression, statements: n.statements }; - case SyntaxKind.DefaultClause: - return { expression: n.expression, statements: n.statements }; - case SyntaxKind.ObjectBindingPattern: - return { elements: n.elements }; - case SyntaxKind.ArrayBindingPattern: - return { elements: n.elements }; - case SyntaxKind.JSDocParameterTag: - return { tagName: n.tagName, name: n.name, isBracketed: n.isBracketed, typeExpression: n.typeExpression, isNameFirst: n.isNameFirst, comment: n.comment }; - case SyntaxKind.JSDocPropertyTag: - return { tagName: n.tagName, name: n.name, isBracketed: n.isBracketed, typeExpression: n.typeExpression, isNameFirst: n.isNameFirst, comment: n.comment }; - case SyntaxKind.SourceFile: - return cloneSourceFileData(n); - default: - return undefined; - } -} - -type ForEachChildFunction = (data: any, cbNode: (node: Node) => T, cbNodes: ((nodes: NodeArray) => T) | undefined) => T | undefined; - -const forEachChildTable: Record = { - [SyntaxKind.QualifiedName]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.left) || - visitNode(cbNode, data.right), - [SyntaxKind.ComputedPropertyName]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), - [SyntaxKind.Decorator]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), - [SyntaxKind.IfStatement]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.expression) || - visitNode(cbNode, data.thenStatement) || - visitNode(cbNode, data.elseStatement), - [SyntaxKind.DoStatement]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.statement) || - visitNode(cbNode, data.expression), - [SyntaxKind.WhileStatement]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.expression) || - visitNode(cbNode, data.statement), - [SyntaxKind.ForStatement]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.initializer) || - visitNode(cbNode, data.condition) || - visitNode(cbNode, data.incrementor) || - visitNode(cbNode, data.statement), - [SyntaxKind.BreakStatement]: (data, cbNode, cbNodes) => visitNode(cbNode, data.label), - [SyntaxKind.ContinueStatement]: (data, cbNode, cbNodes) => visitNode(cbNode, data.label), - [SyntaxKind.ReturnStatement]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), - [SyntaxKind.WithStatement]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.expression) || - visitNode(cbNode, data.statement), - [SyntaxKind.SwitchStatement]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.expression) || - visitNode(cbNode, data.caseBlock), - [SyntaxKind.CaseBlock]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.clauses), - [SyntaxKind.ThrowStatement]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), - [SyntaxKind.TryStatement]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.tryBlock) || - visitNode(cbNode, data.catchClause) || - visitNode(cbNode, data.finallyBlock), - [SyntaxKind.CatchClause]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.variableDeclaration) || - visitNode(cbNode, data.block), - [SyntaxKind.LabeledStatement]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.label) || - visitNode(cbNode, data.statement), - [SyntaxKind.ExpressionStatement]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), - [SyntaxKind.Block]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.statements), - [SyntaxKind.VariableStatement]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.declarationList), - [SyntaxKind.VariableDeclaration]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.name) || - visitNode(cbNode, data.exclamationToken) || - visitNode(cbNode, data.type) || - visitNode(cbNode, data.initializer), - [SyntaxKind.VariableDeclarationList]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.declarations), - [SyntaxKind.Parameter]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.dotDotDotToken) || - visitNode(cbNode, data.name) || - visitNode(cbNode, data.questionToken) || - visitNode(cbNode, data.type) || - visitNode(cbNode, data.initializer), - [SyntaxKind.BindingElement]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.dotDotDotToken) || - visitNode(cbNode, data.propertyName) || - visitNode(cbNode, data.name) || - visitNode(cbNode, data.initializer), - [SyntaxKind.MissingDeclaration]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.modifiers), - [SyntaxKind.FunctionDeclaration]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.asteriskToken) || - visitNode(cbNode, data.name) || - visitNodes(cbNode, cbNodes, data.typeParameters) || - visitNodes(cbNode, cbNodes, data.parameters) || - visitNode(cbNode, data.type) || - visitNode(cbNode, data.body), - [SyntaxKind.ClassDeclaration]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.name) || - visitNodes(cbNode, cbNodes, data.typeParameters) || - visitNodes(cbNode, cbNodes, data.heritageClauses) || - visitNodes(cbNode, cbNodes, data.members), - [SyntaxKind.ClassExpression]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.name) || - visitNodes(cbNode, cbNodes, data.typeParameters) || - visitNodes(cbNode, cbNodes, data.heritageClauses) || - visitNodes(cbNode, cbNodes, data.members), - [SyntaxKind.HeritageClause]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.types), - [SyntaxKind.InterfaceDeclaration]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.name) || - visitNodes(cbNode, cbNodes, data.typeParameters) || - visitNodes(cbNode, cbNodes, data.heritageClauses) || - visitNodes(cbNode, cbNodes, data.members), - [SyntaxKind.TypeAliasDeclaration]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.name) || - visitNodes(cbNode, cbNodes, data.typeParameters) || - visitNode(cbNode, data.type), - [SyntaxKind.JSTypeAliasDeclaration]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.name) || - visitNodes(cbNode, cbNodes, data.typeParameters) || - visitNode(cbNode, data.type), - [SyntaxKind.EnumMember]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.name) || - visitNode(cbNode, data.initializer), - [SyntaxKind.EnumDeclaration]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.name) || - visitNodes(cbNode, cbNodes, data.members), - [SyntaxKind.ModuleBlock]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.statements), - [SyntaxKind.ImportDeclaration]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.importClause) || - visitNode(cbNode, data.moduleSpecifier) || - visitNode(cbNode, data.attributes), - [SyntaxKind.JSImportDeclaration]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.importClause) || - visitNode(cbNode, data.moduleSpecifier) || - visitNode(cbNode, data.attributes), - [SyntaxKind.ExternalModuleReference]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), - [SyntaxKind.NamespaceImport]: (data, cbNode, cbNodes) => visitNode(cbNode, data.name), - [SyntaxKind.NamedImports]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.elements), - [SyntaxKind.ExportAssignment]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.type) || - visitNode(cbNode, data.expression), - [SyntaxKind.NamespaceExportDeclaration]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.name), - [SyntaxKind.NamespaceExport]: (data, cbNode, cbNodes) => visitNode(cbNode, data.name), - [SyntaxKind.NamedExports]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.elements), - [SyntaxKind.ExportSpecifier]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.propertyName) || - visitNode(cbNode, data.name), - [SyntaxKind.CallSignature]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.typeParameters) || - visitNodes(cbNode, cbNodes, data.parameters) || - visitNode(cbNode, data.type), - [SyntaxKind.ConstructSignature]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.typeParameters) || - visitNodes(cbNode, cbNodes, data.parameters) || - visitNode(cbNode, data.type), - [SyntaxKind.Constructor]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNodes(cbNode, cbNodes, data.typeParameters) || - visitNodes(cbNode, cbNodes, data.parameters) || - visitNode(cbNode, data.type) || - visitNode(cbNode, data.body), - [SyntaxKind.GetAccessor]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.name) || - visitNodes(cbNode, cbNodes, data.typeParameters) || - visitNodes(cbNode, cbNodes, data.parameters) || - visitNode(cbNode, data.type) || - visitNode(cbNode, data.body), - [SyntaxKind.SetAccessor]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.name) || - visitNodes(cbNode, cbNodes, data.typeParameters) || - visitNodes(cbNode, cbNodes, data.parameters) || - visitNode(cbNode, data.type) || - visitNode(cbNode, data.body), - [SyntaxKind.IndexSignature]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNodes(cbNode, cbNodes, data.parameters) || - visitNode(cbNode, data.type), - [SyntaxKind.MethodSignature]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.name) || - visitNode(cbNode, data.postfixToken) || - visitNodes(cbNode, cbNodes, data.typeParameters) || - visitNodes(cbNode, cbNodes, data.parameters) || - visitNode(cbNode, data.type), - [SyntaxKind.MethodDeclaration]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.asteriskToken) || - visitNode(cbNode, data.name) || - visitNode(cbNode, data.postfixToken) || - visitNodes(cbNode, cbNodes, data.typeParameters) || - visitNodes(cbNode, cbNodes, data.parameters) || - visitNode(cbNode, data.type) || - visitNode(cbNode, data.body), - [SyntaxKind.PropertySignature]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.name) || - visitNode(cbNode, data.postfixToken) || - visitNode(cbNode, data.type) || - visitNode(cbNode, data.initializer), - [SyntaxKind.PropertyDeclaration]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.name) || - visitNode(cbNode, data.postfixToken) || - visitNode(cbNode, data.type) || - visitNode(cbNode, data.initializer), - [SyntaxKind.ClassStaticBlockDeclaration]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.body), - [SyntaxKind.BinaryExpression]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.left) || - visitNode(cbNode, data.type) || - visitNode(cbNode, data.operatorToken) || - visitNode(cbNode, data.right), - [SyntaxKind.PrefixUnaryExpression]: (data, cbNode, cbNodes) => visitNode(cbNode, data.operand), - [SyntaxKind.PostfixUnaryExpression]: (data, cbNode, cbNodes) => visitNode(cbNode, data.operand), - [SyntaxKind.YieldExpression]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.asteriskToken) || - visitNode(cbNode, data.expression), - [SyntaxKind.ArrowFunction]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNodes(cbNode, cbNodes, data.typeParameters) || - visitNodes(cbNode, cbNodes, data.parameters) || - visitNode(cbNode, data.type) || - visitNode(cbNode, data.equalsGreaterThanToken) || - visitNode(cbNode, data.body), - [SyntaxKind.FunctionExpression]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.asteriskToken) || - visitNode(cbNode, data.name) || - visitNodes(cbNode, cbNodes, data.typeParameters) || - visitNodes(cbNode, cbNodes, data.parameters) || - visitNode(cbNode, data.type) || - visitNode(cbNode, data.body), - [SyntaxKind.AsExpression]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.expression) || - visitNode(cbNode, data.type), - [SyntaxKind.SatisfiesExpression]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.expression) || - visitNode(cbNode, data.type), - [SyntaxKind.ConditionalExpression]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.condition) || - visitNode(cbNode, data.questionToken) || - visitNode(cbNode, data.whenTrue) || - visitNode(cbNode, data.colonToken) || - visitNode(cbNode, data.whenFalse), - [SyntaxKind.PropertyAccessExpression]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.expression) || - visitNode(cbNode, data.questionDotToken) || - visitNode(cbNode, data.name), - [SyntaxKind.ElementAccessExpression]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.expression) || - visitNode(cbNode, data.questionDotToken) || - visitNode(cbNode, data.argumentExpression), - [SyntaxKind.CallExpression]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.expression) || - visitNode(cbNode, data.questionDotToken) || - visitNodes(cbNode, cbNodes, data.typeArguments) || - visitNodes(cbNode, cbNodes, data.arguments), - [SyntaxKind.NewExpression]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.expression) || - visitNodes(cbNode, cbNodes, data.typeArguments) || - visitNodes(cbNode, cbNodes, data.arguments), - [SyntaxKind.MetaProperty]: (data, cbNode, cbNodes) => visitNode(cbNode, data.name), - [SyntaxKind.NonNullExpression]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), - [SyntaxKind.SpreadElement]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), - [SyntaxKind.TemplateExpression]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.head) || - visitNodes(cbNode, cbNodes, data.templateSpans), - [SyntaxKind.TemplateSpan]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.expression) || - visitNode(cbNode, data.literal), - [SyntaxKind.TaggedTemplateExpression]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.tag) || - visitNode(cbNode, data.questionDotToken) || - visitNodes(cbNode, cbNodes, data.typeArguments) || - visitNode(cbNode, data.template), - [SyntaxKind.ParenthesizedExpression]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), - [SyntaxKind.ArrayLiteralExpression]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.elements), - [SyntaxKind.ObjectLiteralExpression]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.properties), - [SyntaxKind.SpreadAssignment]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), - [SyntaxKind.PropertyAssignment]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.name) || - visitNode(cbNode, data.postfixToken) || - visitNode(cbNode, data.type) || - visitNode(cbNode, data.initializer), - [SyntaxKind.ShorthandPropertyAssignment]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.name) || - visitNode(cbNode, data.postfixToken) || - visitNode(cbNode, data.type) || - visitNode(cbNode, data.equalsToken) || - visitNode(cbNode, data.objectAssignmentInitializer), - [SyntaxKind.DeleteExpression]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), - [SyntaxKind.TypeOfExpression]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), - [SyntaxKind.VoidExpression]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), - [SyntaxKind.AwaitExpression]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), - [SyntaxKind.TypeAssertionExpression]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.type) || - visitNode(cbNode, data.expression), - [SyntaxKind.UnionType]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.types), - [SyntaxKind.IntersectionType]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.types), - [SyntaxKind.ConditionalType]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.checkType) || - visitNode(cbNode, data.extendsType) || - visitNode(cbNode, data.trueType) || - visitNode(cbNode, data.falseType), - [SyntaxKind.TypeOperator]: (data, cbNode, cbNodes) => visitNode(cbNode, data.type), - [SyntaxKind.InferType]: (data, cbNode, cbNodes) => visitNode(cbNode, data.typeParameter), - [SyntaxKind.ArrayType]: (data, cbNode, cbNodes) => visitNode(cbNode, data.elementType), - [SyntaxKind.IndexedAccessType]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.objectType) || - visitNode(cbNode, data.indexType), - [SyntaxKind.TypeReference]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.typeName) || - visitNodes(cbNode, cbNodes, data.typeArguments), - [SyntaxKind.ExpressionWithTypeArguments]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.expression) || - visitNodes(cbNode, cbNodes, data.typeArguments), - [SyntaxKind.LiteralType]: (data, cbNode, cbNodes) => visitNode(cbNode, data.literal), - [SyntaxKind.TypePredicate]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.assertsModifier) || - visitNode(cbNode, data.parameterName) || - visitNode(cbNode, data.type), - [SyntaxKind.ImportAttribute]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.name) || - visitNode(cbNode, data.value), - [SyntaxKind.ImportAttributes]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.attributes), - [SyntaxKind.TypeQuery]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.exprName) || - visitNodes(cbNode, cbNodes, data.typeArguments), - [SyntaxKind.MappedType]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.readonlyToken) || - visitNode(cbNode, data.typeParameter) || - visitNode(cbNode, data.nameType) || - visitNode(cbNode, data.questionToken) || - visitNode(cbNode, data.type) || - visitNodes(cbNode, cbNodes, data.members), - [SyntaxKind.TypeLiteral]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.members), - [SyntaxKind.TupleType]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.elements), - [SyntaxKind.NamedTupleMember]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.dotDotDotToken) || - visitNode(cbNode, data.name) || - visitNode(cbNode, data.questionToken) || - visitNode(cbNode, data.type), - [SyntaxKind.OptionalType]: (data, cbNode, cbNodes) => visitNode(cbNode, data.type), - [SyntaxKind.RestType]: (data, cbNode, cbNodes) => visitNode(cbNode, data.type), - [SyntaxKind.ParenthesizedType]: (data, cbNode, cbNodes) => visitNode(cbNode, data.type), - [SyntaxKind.FunctionType]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.typeParameters) || - visitNodes(cbNode, cbNodes, data.parameters) || - visitNode(cbNode, data.type), - [SyntaxKind.ConstructorType]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNodes(cbNode, cbNodes, data.typeParameters) || - visitNodes(cbNode, cbNodes, data.parameters) || - visitNode(cbNode, data.type), - [SyntaxKind.TemplateLiteralType]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.head) || - visitNodes(cbNode, cbNodes, data.templateSpans), - [SyntaxKind.TemplateLiteralTypeSpan]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.type) || - visitNode(cbNode, data.literal), - [SyntaxKind.SyntheticExpression]: (data, cbNode, cbNodes) => visitNode(cbNode, data.tupleNameSource), - [SyntaxKind.PartiallyEmittedExpression]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), - [SyntaxKind.JsxElement]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.openingElement) || - visitNodes(cbNode, cbNodes, data.children) || - visitNode(cbNode, data.closingElement), - [SyntaxKind.JsxAttributes]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.properties), - [SyntaxKind.JsxNamespacedName]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.namespace) || - visitNode(cbNode, data.name), - [SyntaxKind.JsxOpeningElement]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.tagName) || - visitNodes(cbNode, cbNodes, data.typeArguments) || - visitNode(cbNode, data.attributes), - [SyntaxKind.JsxSelfClosingElement]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.tagName) || - visitNodes(cbNode, cbNodes, data.typeArguments) || - visitNode(cbNode, data.attributes), - [SyntaxKind.JsxFragment]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.openingFragment) || - visitNodes(cbNode, cbNodes, data.children) || - visitNode(cbNode, data.closingFragment), - [SyntaxKind.JsxAttribute]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.name) || - visitNode(cbNode, data.initializer), - [SyntaxKind.JsxSpreadAttribute]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), - [SyntaxKind.JsxClosingElement]: (data, cbNode, cbNodes) => visitNode(cbNode, data.tagName), - [SyntaxKind.JsxExpression]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.dotDotDotToken) || - visitNode(cbNode, data.expression), - [SyntaxKind.SyntaxList]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.children), - [SyntaxKind.JSDoc]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.comment) || - visitNodes(cbNode, cbNodes, data.tags), - [SyntaxKind.JSDocTypeExpression]: (data, cbNode, cbNodes) => visitNode(cbNode, data.type), - [SyntaxKind.JSDocNonNullableType]: (data, cbNode, cbNodes) => visitNode(cbNode, data.type), - [SyntaxKind.JSDocNullableType]: (data, cbNode, cbNodes) => visitNode(cbNode, data.type), - [SyntaxKind.JSDocVariadicType]: (data, cbNode, cbNodes) => visitNode(cbNode, data.type), - [SyntaxKind.JSDocOptionalType]: (data, cbNode, cbNodes) => visitNode(cbNode, data.type), - [SyntaxKind.JSDocTypeTag]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.tagName) || - visitNode(cbNode, data.typeExpression) || - visitNodes(cbNode, cbNodes, data.comment), - [SyntaxKind.JSDocUnknownTag]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.tagName) || - visitNodes(cbNode, cbNodes, data.comment), - [SyntaxKind.JSDocTemplateTag]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.tagName) || - visitNode(cbNode, data.constraint) || - visitNodes(cbNode, cbNodes, data.typeParameters) || - visitNodes(cbNode, cbNodes, data.comment), - [SyntaxKind.JSDocReturnTag]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.tagName) || - visitNode(cbNode, data.typeExpression) || - visitNodes(cbNode, cbNodes, data.comment), - [SyntaxKind.JSDocPublicTag]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.tagName) || - visitNodes(cbNode, cbNodes, data.comment), - [SyntaxKind.JSDocPrivateTag]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.tagName) || - visitNodes(cbNode, cbNodes, data.comment), - [SyntaxKind.JSDocProtectedTag]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.tagName) || - visitNodes(cbNode, cbNodes, data.comment), - [SyntaxKind.JSDocReadonlyTag]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.tagName) || - visitNodes(cbNode, cbNodes, data.comment), - [SyntaxKind.JSDocOverrideTag]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.tagName) || - visitNodes(cbNode, cbNodes, data.comment), - [SyntaxKind.JSDocDeprecatedTag]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.tagName) || - visitNodes(cbNode, cbNodes, data.comment), - [SyntaxKind.JSDocSeeTag]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.tagName) || - visitNode(cbNode, data.nameExpression) || - visitNodes(cbNode, cbNodes, data.comment), - [SyntaxKind.JSDocImplementsTag]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.tagName) || - visitNode(cbNode, data.className) || - visitNodes(cbNode, cbNodes, data.comment), - [SyntaxKind.JSDocAugmentsTag]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.tagName) || - visitNode(cbNode, data.className) || - visitNodes(cbNode, cbNodes, data.comment), - [SyntaxKind.JSDocSatisfiesTag]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.tagName) || - visitNode(cbNode, data.typeExpression) || - visitNodes(cbNode, cbNodes, data.comment), - [SyntaxKind.JSDocThrowsTag]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.tagName) || - visitNode(cbNode, data.typeExpression) || - visitNodes(cbNode, cbNodes, data.comment), - [SyntaxKind.JSDocThisTag]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.tagName) || - visitNode(cbNode, data.typeExpression) || - visitNodes(cbNode, cbNodes, data.comment), - [SyntaxKind.JSDocImportTag]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.tagName) || - visitNode(cbNode, data.importClause) || - visitNode(cbNode, data.moduleSpecifier) || - visitNode(cbNode, data.attributes) || - visitNodes(cbNode, cbNodes, data.comment), - [SyntaxKind.JSDocCallbackTag]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.tagName) || - visitNode(cbNode, data.typeExpression) || - visitNode(cbNode, data.name) || - visitNodes(cbNode, cbNodes, data.comment), - [SyntaxKind.JSDocOverloadTag]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.tagName) || - visitNode(cbNode, data.typeExpression) || - visitNodes(cbNode, cbNodes, data.comment), - [SyntaxKind.JSDocTypedefTag]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.tagName) || - visitNode(cbNode, data.typeExpression) || - visitNode(cbNode, data.name) || - visitNodes(cbNode, cbNodes, data.comment), - [SyntaxKind.JSDocSignature]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.typeParameters) || - visitNodes(cbNode, cbNodes, data.parameters) || - visitNode(cbNode, data.type), - [SyntaxKind.JSDocNameReference]: (data, cbNode, cbNodes) => visitNode(cbNode, data.name), - [SyntaxKind.ModuleDeclaration]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.name) || - visitNode(cbNode, data.body), - [SyntaxKind.ImportEqualsDeclaration]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.name) || - visitNode(cbNode, data.moduleReference), - [SyntaxKind.ExportDeclaration]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.exportClause) || - visitNode(cbNode, data.moduleSpecifier) || - visitNode(cbNode, data.attributes), - [SyntaxKind.ImportType]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.argument) || - visitNode(cbNode, data.attributes) || - visitNode(cbNode, data.qualifier) || - visitNodes(cbNode, cbNodes, data.typeArguments), - [SyntaxKind.ImportClause]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.name) || - visitNode(cbNode, data.namedBindings), - [SyntaxKind.ImportSpecifier]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.propertyName) || - visitNode(cbNode, data.name), - [SyntaxKind.JSDocLink]: (data, cbNode, cbNodes) => visitNode(cbNode, data.name), - [SyntaxKind.JSDocLinkPlain]: (data, cbNode, cbNodes) => visitNode(cbNode, data.name), - [SyntaxKind.JSDocLinkCode]: (data, cbNode, cbNodes) => visitNode(cbNode, data.name), - [SyntaxKind.TypeParameter]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.modifiers) || - visitNode(cbNode, data.name) || - visitNode(cbNode, data.constraint) || - visitNode(cbNode, data.expression) || - visitNode(cbNode, data.defaultType), - [SyntaxKind.SyntheticReferenceExpression]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.expression) || - visitNode(cbNode, data.thisArg), - [SyntaxKind.JSDocTypeLiteral]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.jsdocPropertyTags), - [SyntaxKind.ForInStatement]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.awaitModifier) || - visitNode(cbNode, data.initializer) || - visitNode(cbNode, data.expression) || - visitNode(cbNode, data.statement), - [SyntaxKind.ForOfStatement]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.awaitModifier) || - visitNode(cbNode, data.initializer) || - visitNode(cbNode, data.expression) || - visitNode(cbNode, data.statement), - [SyntaxKind.CaseClause]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.expression) || - visitNodes(cbNode, cbNodes, data.statements), - [SyntaxKind.DefaultClause]: (data, cbNode, cbNodes) => - visitNode(cbNode, data.expression) || - visitNodes(cbNode, cbNodes, data.statements), - [SyntaxKind.ObjectBindingPattern]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.elements), - [SyntaxKind.ArrayBindingPattern]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.elements), - [SyntaxKind.JSDocParameterTag]: forEachChildOfJSDocParameterTag, - [SyntaxKind.JSDocPropertyTag]: forEachChildOfJSDocPropertyTag, - [SyntaxKind.SourceFile]: (data, cbNode, cbNodes) => - visitNodes(cbNode, cbNodes, data.statements) || - visitNode(cbNode, data.endOfFileToken), -}; - -function visitNode(cbNode: (node: Node) => T, node: Node | undefined): T | undefined { - return node ? cbNode(node) : undefined; -} - -function visitNodes(cbNode: (node: Node) => T, cbNodes: ((nodes: NodeArray) => T) | undefined, nodes: NodeArray | undefined): T | undefined { - if (!nodes) return undefined; - if (cbNodes) return cbNodes(nodes); - for (const node of nodes) { - const result = cbNode(node); - if (result) return result; - } - return undefined; -} - -export function createToken(kind: TKind): Token { - return new NodeObject(kind, undefined) as unknown as Token; -} - -export function createIdentifier(text: string): Identifier { - return new NodeObject(SyntaxKind.Identifier, { - text, - }) as unknown as Identifier; -} - -export function createPrivateIdentifier(text: string): PrivateIdentifier { - return new NodeObject(SyntaxKind.PrivateIdentifier, { - text, - }) as unknown as PrivateIdentifier; -} - -export function createQualifiedName(left: EntityName, right: MemberName): QualifiedName { - return new NodeObject(SyntaxKind.QualifiedName, { - left, - right, - }) as unknown as QualifiedName; -} - -export function createComputedPropertyName(expression: Expression): ComputedPropertyName { - return new NodeObject(SyntaxKind.ComputedPropertyName, { - expression, - }) as unknown as ComputedPropertyName; -} - -export function createDecorator(expression: LeftHandSideExpression): Decorator { - return new NodeObject(SyntaxKind.Decorator, { - expression, - }) as unknown as Decorator; -} - -export function createEmptyStatement(): EmptyStatement { - return new NodeObject(SyntaxKind.EmptyStatement, undefined) as unknown as EmptyStatement; -} - -export function createIfStatement(expression: Expression, thenStatement: Statement, elseStatement?: Statement): IfStatement { - return new NodeObject(SyntaxKind.IfStatement, { - expression, - thenStatement, - elseStatement, - }) as unknown as IfStatement; -} - -export function createDoStatement(statement: Statement, expression: Expression): DoStatement { - return new NodeObject(SyntaxKind.DoStatement, { - statement, - expression, - }) as unknown as DoStatement; -} - -export function createWhileStatement(expression: Expression, statement: Statement): WhileStatement { - return new NodeObject(SyntaxKind.WhileStatement, { - expression, - statement, - }) as unknown as WhileStatement; -} - -export function createForStatement(initializer: ForInitializer | undefined, condition: Expression | undefined, incrementor: Expression | undefined, statement: Statement): ForStatement { - return new NodeObject(SyntaxKind.ForStatement, { - initializer, - condition, - incrementor, - statement, - }) as unknown as ForStatement; -} - -export function createBreakStatement(label?: Identifier): BreakStatement { - return new NodeObject(SyntaxKind.BreakStatement, { - label, - }) as unknown as BreakStatement; -} - -export function createContinueStatement(label?: Identifier): ContinueStatement { - return new NodeObject(SyntaxKind.ContinueStatement, { - label, - }) as unknown as ContinueStatement; -} - -export function createReturnStatement(expression?: Expression): ReturnStatement { - return new NodeObject(SyntaxKind.ReturnStatement, { - expression, - }) as unknown as ReturnStatement; -} - -export function createWithStatement(expression: Expression, statement: Statement): WithStatement { - return new NodeObject(SyntaxKind.WithStatement, { - expression, - statement, - }) as unknown as WithStatement; -} - -export function createSwitchStatement(expression: Expression, caseBlock: CaseBlock): SwitchStatement { - return new NodeObject(SyntaxKind.SwitchStatement, { - expression, - caseBlock, - }) as unknown as SwitchStatement; -} - -export function createCaseBlock(clauses: readonly CaseOrDefaultClause[]): CaseBlock { - return new NodeObject(SyntaxKind.CaseBlock, { - clauses: createNodeArray(clauses), - }) as unknown as CaseBlock; -} - -export function createThrowStatement(expression: Expression): ThrowStatement { - return new NodeObject(SyntaxKind.ThrowStatement, { - expression, - }) as unknown as ThrowStatement; -} - -export function createTryStatement(tryBlock: Block, catchClause?: CatchClause, finallyBlock?: Block): TryStatement { - return new NodeObject(SyntaxKind.TryStatement, { - tryBlock, - catchClause, - finallyBlock, - }) as unknown as TryStatement; -} - -export function createCatchClause(variableDeclaration: VariableDeclaration | undefined, block: Block): CatchClause { - return new NodeObject(SyntaxKind.CatchClause, { - variableDeclaration, - block, - }) as unknown as CatchClause; -} - -export function createDebuggerStatement(): DebuggerStatement { - return new NodeObject(SyntaxKind.DebuggerStatement, undefined) as unknown as DebuggerStatement; -} - -export function createLabeledStatement(label: Identifier, statement: Statement): LabeledStatement { - return new NodeObject(SyntaxKind.LabeledStatement, { - label, - statement, - }) as unknown as LabeledStatement; -} - -export function createExpressionStatement(expression: Expression): ExpressionStatement { - return new NodeObject(SyntaxKind.ExpressionStatement, { - expression, - }) as unknown as ExpressionStatement; -} - -export function createBlock(statements: readonly Statement[], multiLine?: boolean): Block { - return new NodeObject(SyntaxKind.Block, { - statements: createNodeArray(statements), - multiLine, - }) as unknown as Block; -} - -export function createVariableStatement(modifiers: readonly ModifierLike[] | undefined, declarationList: VariableDeclarationList): VariableStatement { - return new NodeObject(SyntaxKind.VariableStatement, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - declarationList, - }) as unknown as VariableStatement; -} - -export function createVariableDeclaration(name: BindingName, exclamationToken?: ExclamationToken, type?: TypeNode, initializer?: Expression): VariableDeclaration { - return new NodeObject(SyntaxKind.VariableDeclaration, { - name, - exclamationToken, - type, - initializer, - }) as unknown as VariableDeclaration; -} - -export function createVariableDeclarationList(declarations: readonly VariableDeclaration[], flags: NodeFlags): VariableDeclarationList { - const node = new NodeObject(SyntaxKind.VariableDeclarationList, { - declarations: createNodeArray(declarations), - }) as unknown as VariableDeclarationList; - (node as any).flags = flags; - return node; -} - -export function createParameterDeclaration(modifiers: readonly ModifierLike[] | undefined, dotDotDotToken: DotDotDotToken | undefined, name: BindingName, questionToken?: QuestionToken, type?: TypeNode, initializer?: Expression): ParameterDeclaration { - return new NodeObject(SyntaxKind.Parameter, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - dotDotDotToken, - name, - questionToken, - type, - initializer, - }) as unknown as ParameterDeclaration; -} - -export function createBindingElement(dotDotDotToken?: DotDotDotToken, propertyName?: PropertyName, name?: BindingName, initializer?: Expression): BindingElement { - return new NodeObject(SyntaxKind.BindingElement, { - dotDotDotToken, - propertyName, - name, - initializer, - }) as unknown as BindingElement; -} - -export function createMissingDeclaration(modifiers?: readonly ModifierLike[]): MissingDeclaration { - return new NodeObject(SyntaxKind.MissingDeclaration, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - }) as unknown as MissingDeclaration; -} - -export function createFunctionDeclaration(modifiers: readonly ModifierLike[] | undefined, asteriskToken: AsteriskToken | undefined, name: Identifier | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode, body?: FunctionBody): FunctionDeclaration { - return new NodeObject(SyntaxKind.FunctionDeclaration, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - asteriskToken, - name, - typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, - parameters: createNodeArray(parameters), - type, - body, - }) as unknown as FunctionDeclaration; -} - -export function createClassDeclaration(modifiers: readonly ModifierLike[] | undefined, name: Identifier | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, heritageClauses: readonly HeritageClause[] | undefined, members: readonly ClassElement[]): ClassDeclaration { - return new NodeObject(SyntaxKind.ClassDeclaration, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - name, - typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, - heritageClauses: heritageClauses ? createNodeArray(heritageClauses) : undefined, - members: createNodeArray(members), - }) as unknown as ClassDeclaration; -} - -export function createClassExpression(modifiers: readonly ModifierLike[] | undefined, name: Identifier | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, heritageClauses: readonly HeritageClause[] | undefined, members: readonly ClassElement[]): ClassExpression { - return new NodeObject(SyntaxKind.ClassExpression, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - name, - typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, - heritageClauses: heritageClauses ? createNodeArray(heritageClauses) : undefined, - members: createNodeArray(members), - }) as unknown as ClassExpression; -} - -export function createHeritageClause(token: SyntaxKind.ExtendsKeyword | SyntaxKind.ImplementsKeyword, types: readonly HeritageClauseElement[]): HeritageClause { - return new NodeObject(SyntaxKind.HeritageClause, { - token, - types: createNodeArray(types), - }) as unknown as HeritageClause; -} - -export function createInterfaceDeclaration(modifiers: readonly ModifierLike[] | undefined, name: Identifier, typeParameters: readonly TypeParameterDeclaration[] | undefined, heritageClauses: readonly HeritageClause[] | undefined, members: readonly TypeElement[]): InterfaceDeclaration { - return new NodeObject(SyntaxKind.InterfaceDeclaration, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - name, - typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, - heritageClauses: heritageClauses ? createNodeArray(heritageClauses) : undefined, - members: createNodeArray(members), - }) as unknown as InterfaceDeclaration; -} - -export function createTypeAliasDeclaration(modifiers: readonly ModifierLike[] | undefined, name: Identifier, typeParameters: readonly TypeParameterDeclaration[] | undefined, type: TypeNode): TypeAliasDeclaration { - return new NodeObject(SyntaxKind.TypeAliasDeclaration, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - name, - typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, - type, - }) as unknown as TypeAliasDeclaration; -} - -export function createEnumMember(name: PropertyName, initializer?: Expression): EnumMember { - return new NodeObject(SyntaxKind.EnumMember, { - name, - initializer, - }) as unknown as EnumMember; -} - -export function createEnumDeclaration(modifiers: readonly ModifierLike[] | undefined, name: Identifier, members: readonly EnumMember[]): EnumDeclaration { - return new NodeObject(SyntaxKind.EnumDeclaration, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - name, - members: createNodeArray(members), - }) as unknown as EnumDeclaration; -} - -export function createModuleBlock(statements: readonly Statement[]): ModuleBlock { - return new NodeObject(SyntaxKind.ModuleBlock, { - statements: createNodeArray(statements), - }) as unknown as ModuleBlock; -} - -export function createNotEmittedStatement(): NotEmittedStatement { - return new NodeObject(SyntaxKind.NotEmittedStatement, undefined) as unknown as NotEmittedStatement; -} - -export function createNotEmittedTypeElement(): NotEmittedTypeElement { - return new NodeObject(SyntaxKind.NotEmittedTypeElement, undefined) as unknown as NotEmittedTypeElement; -} - -export function createImportDeclaration(modifiers: readonly ModifierLike[] | undefined, importClause: ImportClause | undefined, moduleSpecifier: Expression, attributes?: ImportAttributes): ImportDeclaration { - return new NodeObject(SyntaxKind.ImportDeclaration, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - importClause, - moduleSpecifier, - attributes, - }) as unknown as ImportDeclaration; -} - -export function createExternalModuleReference(expression: Expression): ExternalModuleReference { - return new NodeObject(SyntaxKind.ExternalModuleReference, { - expression, - }) as unknown as ExternalModuleReference; -} - -export function createNamespaceImport(name: Identifier): NamespaceImport { - return new NodeObject(SyntaxKind.NamespaceImport, { - name, - }) as unknown as NamespaceImport; -} - -export function createNamedImports(elements: readonly ImportSpecifier[]): NamedImports { - return new NodeObject(SyntaxKind.NamedImports, { - elements: createNodeArray(elements), - }) as unknown as NamedImports; -} - -export function createExportAssignment(modifiers: readonly ModifierLike[] | undefined, isExportEquals: boolean = false, type: TypeNode, expression: Expression): ExportAssignment { - return new NodeObject(SyntaxKind.ExportAssignment, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - isExportEquals, - type, - expression, - }) as unknown as ExportAssignment; -} - -export function createNamespaceExportDeclaration(modifiers: readonly ModifierLike[] | undefined, name: Identifier): NamespaceExportDeclaration { - return new NodeObject(SyntaxKind.NamespaceExportDeclaration, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - name, - }) as unknown as NamespaceExportDeclaration; -} - -export function createNamespaceExport(name: ModuleExportName): NamespaceExport { - return new NodeObject(SyntaxKind.NamespaceExport, { - name, - }) as unknown as NamespaceExport; -} - -export function createNamedExports(elements: readonly ExportSpecifier[]): NamedExports { - return new NodeObject(SyntaxKind.NamedExports, { - elements: createNodeArray(elements), - }) as unknown as NamedExports; -} - -export function createExportSpecifier(isTypeOnly: boolean = false, propertyName: ModuleExportName | undefined, name: ModuleExportName): ExportSpecifier { - return new NodeObject(SyntaxKind.ExportSpecifier, { - isTypeOnly, - propertyName, - name, - }) as unknown as ExportSpecifier; -} - -export function createCallSignatureDeclaration(typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode): CallSignatureDeclaration { - return new NodeObject(SyntaxKind.CallSignature, { - typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, - parameters: createNodeArray(parameters), - type, - }) as unknown as CallSignatureDeclaration; -} - -export function createConstructSignatureDeclaration(typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode): ConstructSignatureDeclaration { - return new NodeObject(SyntaxKind.ConstructSignature, { - typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, - parameters: createNodeArray(parameters), - type, - }) as unknown as ConstructSignatureDeclaration; -} - -export function createConstructorDeclaration(modifiers: readonly ModifierLike[] | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode, body?: FunctionBody): ConstructorDeclaration { - return new NodeObject(SyntaxKind.Constructor, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, - parameters: createNodeArray(parameters), - type, - body, - }) as unknown as ConstructorDeclaration; -} - -export function createGetAccessorDeclaration(modifiers: readonly ModifierLike[] | undefined, name: PropertyName, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode, body?: FunctionBody): GetAccessorDeclaration { - return new NodeObject(SyntaxKind.GetAccessor, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - name, - typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, - parameters: createNodeArray(parameters), - type, - body, - }) as unknown as GetAccessorDeclaration; -} - -export function createSetAccessorDeclaration(modifiers: readonly ModifierLike[] | undefined, name: PropertyName, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode, body?: FunctionBody): SetAccessorDeclaration { - return new NodeObject(SyntaxKind.SetAccessor, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - name, - typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, - parameters: createNodeArray(parameters), - type, - body, - }) as unknown as SetAccessorDeclaration; -} - -export function createIndexSignatureDeclaration(modifiers: readonly ModifierLike[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode): IndexSignatureDeclaration { - return new NodeObject(SyntaxKind.IndexSignature, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - parameters: createNodeArray(parameters), - type, - }) as unknown as IndexSignatureDeclaration; -} - -export function createMethodSignatureDeclaration(modifiers: readonly ModifierLike[] | undefined, name: PropertyName, postfixToken: QuestionToken | ExclamationToken | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode): MethodSignatureDeclaration { - return new NodeObject(SyntaxKind.MethodSignature, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - name, - postfixToken, - typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, - parameters: createNodeArray(parameters), - type, - }) as unknown as MethodSignatureDeclaration; -} - -export function createMethodDeclaration(modifiers: readonly ModifierLike[] | undefined, asteriskToken: AsteriskToken | undefined, name: PropertyName, postfixToken: QuestionToken | ExclamationToken | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode, body?: FunctionBody): MethodDeclaration { - return new NodeObject(SyntaxKind.MethodDeclaration, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - asteriskToken, - name, - postfixToken, - typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, - parameters: createNodeArray(parameters), - type, - body, - }) as unknown as MethodDeclaration; -} - -export function createPropertySignatureDeclaration(modifiers: readonly ModifierLike[] | undefined, name: PropertyName, postfixToken: QuestionToken | ExclamationToken | undefined, type: TypeNode, initializer: Expression): PropertySignatureDeclaration { - return new NodeObject(SyntaxKind.PropertySignature, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - name, - postfixToken, - type, - initializer, - }) as unknown as PropertySignatureDeclaration; -} - -export function createPropertyDeclaration(modifiers: readonly ModifierLike[] | undefined, name: PropertyName, postfixToken?: QuestionToken | ExclamationToken, type?: TypeNode, initializer?: Expression): PropertyDeclaration { - return new NodeObject(SyntaxKind.PropertyDeclaration, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - name, - postfixToken, - type, - initializer, - }) as unknown as PropertyDeclaration; -} - -export function createSemicolonClassElement(): SemicolonClassElement { - return new NodeObject(SyntaxKind.SemicolonClassElement, undefined) as unknown as SemicolonClassElement; -} - -export function createClassStaticBlockDeclaration(modifiers: readonly ModifierLike[] | undefined, body: Block): ClassStaticBlockDeclaration { - return new NodeObject(SyntaxKind.ClassStaticBlockDeclaration, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - body, - }) as unknown as ClassStaticBlockDeclaration; -} - -export function createOmittedExpression(): OmittedExpression { - return new NodeObject(SyntaxKind.OmittedExpression, undefined) as unknown as OmittedExpression; -} - -export function createKeywordExpression(kind: TKind): KeywordExpression { - return new NodeObject(kind, undefined) as unknown as KeywordExpression; -} - -export function createStringLiteral(text: string, tokenFlags: TokenFlags): StringLiteral { - return new NodeObject(SyntaxKind.StringLiteral, { - text, - tokenFlags, - }) as unknown as StringLiteral; -} - -export function createNumericLiteral(text: string, tokenFlags: TokenFlags): NumericLiteral { - return new NodeObject(SyntaxKind.NumericLiteral, { - text, - tokenFlags, - }) as unknown as NumericLiteral; -} - -export function createBigIntLiteral(text: string, tokenFlags: TokenFlags): BigIntLiteral { - return new NodeObject(SyntaxKind.BigIntLiteral, { - text, - tokenFlags, - }) as unknown as BigIntLiteral; -} - -export function createRegularExpressionLiteral(text: string, tokenFlags: TokenFlags): RegularExpressionLiteral { - return new NodeObject(SyntaxKind.RegularExpressionLiteral, { - text, - tokenFlags, - }) as unknown as RegularExpressionLiteral; -} - -export function createNoSubstitutionTemplateLiteral(text: string, templateFlags: TokenFlags): NoSubstitutionTemplateLiteral { - return new NodeObject(SyntaxKind.NoSubstitutionTemplateLiteral, { - text, - templateFlags, - }) as unknown as NoSubstitutionTemplateLiteral; -} - -export function createBinaryExpression(modifiers: readonly ModifierLike[] | undefined, left: Expression, type: TypeNode | undefined, operatorToken: BinaryOperatorToken, right: Expression): BinaryExpression { - return new NodeObject(SyntaxKind.BinaryExpression, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - left, - type, - operatorToken, - right, - }) as unknown as BinaryExpression; -} - -export function createPrefixUnaryExpression(operator: SyntaxKind.PlusToken | SyntaxKind.MinusToken | SyntaxKind.TildeToken | SyntaxKind.ExclamationToken | SyntaxKind.PlusPlusToken | SyntaxKind.MinusMinusToken, operand: Expression): PrefixUnaryExpression { - return new NodeObject(SyntaxKind.PrefixUnaryExpression, { - operator, - operand, - }) as unknown as PrefixUnaryExpression; -} - -export function createPostfixUnaryExpression(operand: Expression, operator: SyntaxKind.PlusPlusToken | SyntaxKind.MinusMinusToken): PostfixUnaryExpression { - return new NodeObject(SyntaxKind.PostfixUnaryExpression, { - operand, - operator, - }) as unknown as PostfixUnaryExpression; -} - -export function createYieldExpression(asteriskToken?: AsteriskToken, expression?: Expression): YieldExpression { - return new NodeObject(SyntaxKind.YieldExpression, { - asteriskToken, - expression, - }) as unknown as YieldExpression; -} - -export function createArrowFunction(modifiers: readonly ModifierLike[] | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined, equalsGreaterThanToken: EqualsGreaterThanToken, body: ConciseBody): ArrowFunction { - return new NodeObject(SyntaxKind.ArrowFunction, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, - parameters: createNodeArray(parameters), - type, - equalsGreaterThanToken, - body, - }) as unknown as ArrowFunction; -} - -export function createFunctionExpression(modifiers: readonly ModifierLike[] | undefined, asteriskToken: AsteriskToken | undefined, name: Identifier | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined, body: FunctionBody): FunctionExpression { - return new NodeObject(SyntaxKind.FunctionExpression, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - asteriskToken, - name, - typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, - parameters: createNodeArray(parameters), - type, - body, - }) as unknown as FunctionExpression; -} - -export function createAsExpression(expression: Expression, type: TypeNode): AsExpression { - return new NodeObject(SyntaxKind.AsExpression, { - expression, - type, - }) as unknown as AsExpression; -} - -export function createSatisfiesExpression(expression: Expression, type: TypeNode): SatisfiesExpression { - return new NodeObject(SyntaxKind.SatisfiesExpression, { - expression, - type, - }) as unknown as SatisfiesExpression; -} - -export function createConditionalExpression(condition: Expression, questionToken: QuestionToken, whenTrue: Expression, colonToken: ColonToken, whenFalse: Expression): ConditionalExpression { - return new NodeObject(SyntaxKind.ConditionalExpression, { - condition, - questionToken, - whenTrue, - colonToken, - whenFalse, - }) as unknown as ConditionalExpression; -} - -export function createPropertyAccessExpression(expression: Expression, questionDotToken: QuestionDotToken | undefined, name: MemberName, flags: NodeFlags): PropertyAccessExpression { - const node = new NodeObject(SyntaxKind.PropertyAccessExpression, { - expression, - questionDotToken, - name, - }) as unknown as PropertyAccessExpression; - (node as any).flags = flags; - return node; -} - -export function createElementAccessExpression(expression: Expression, questionDotToken: QuestionDotToken | undefined, argumentExpression: Expression, flags: NodeFlags): ElementAccessExpression { - const node = new NodeObject(SyntaxKind.ElementAccessExpression, { - expression, - questionDotToken, - argumentExpression, - }) as unknown as ElementAccessExpression; - (node as any).flags = flags; - return node; -} - -export function createCallExpression(expression: Expression, questionDotToken: QuestionDotToken | undefined, typeArguments: readonly TypeNode[] | undefined, arguments_: readonly Expression[], flags: NodeFlags): CallExpression { - const node = new NodeObject(SyntaxKind.CallExpression, { - expression, - questionDotToken, - typeArguments: typeArguments ? createNodeArray(typeArguments) : undefined, - arguments: createNodeArray(arguments_), - }) as unknown as CallExpression; - (node as any).flags = flags; - return node; -} - -export function createNewExpression(expression: Expression, typeArguments?: readonly TypeNode[], arguments_?: readonly Expression[]): NewExpression { - return new NodeObject(SyntaxKind.NewExpression, { - expression, - typeArguments: typeArguments ? createNodeArray(typeArguments) : undefined, - arguments: arguments_ ? createNodeArray(arguments_) : undefined, - }) as unknown as NewExpression; -} - -export function createMetaProperty(keywordToken: SyntaxKind.ImportKeyword | SyntaxKind.NewKeyword, name: Identifier): MetaProperty { - return new NodeObject(SyntaxKind.MetaProperty, { - keywordToken, - name, - }) as unknown as MetaProperty; -} - -export function createNonNullExpression(expression: Expression, flags: NodeFlags): NonNullExpression { - const node = new NodeObject(SyntaxKind.NonNullExpression, { - expression, - }) as unknown as NonNullExpression; - (node as any).flags = flags; - return node; -} - -export function createSpreadElement(expression: Expression): SpreadElement { - return new NodeObject(SyntaxKind.SpreadElement, { - expression, - }) as unknown as SpreadElement; -} - -export function createTemplateExpression(head: TemplateHead, templateSpans: readonly TemplateSpan[]): TemplateExpression { - return new NodeObject(SyntaxKind.TemplateExpression, { - head, - templateSpans: createNodeArray(templateSpans), - }) as unknown as TemplateExpression; -} - -export function createTemplateSpan(expression: Expression, literal: TemplateMiddleOrTail): TemplateSpan { - return new NodeObject(SyntaxKind.TemplateSpan, { - expression, - literal, - }) as unknown as TemplateSpan; -} - -export function createTaggedTemplateExpression(tag: Expression, questionDotToken: QuestionDotToken, typeArguments: readonly TypeNode[] | undefined, template: TemplateLiteral, flags: NodeFlags): TaggedTemplateExpression { - const node = new NodeObject(SyntaxKind.TaggedTemplateExpression, { - tag, - questionDotToken, - typeArguments: typeArguments ? createNodeArray(typeArguments) : undefined, - template, - }) as unknown as TaggedTemplateExpression; - (node as any).flags = flags; - return node; -} - -export function createParenthesizedExpression(expression: Expression): ParenthesizedExpression { - return new NodeObject(SyntaxKind.ParenthesizedExpression, { - expression, - }) as unknown as ParenthesizedExpression; -} - -export function createArrayLiteralExpression(elements: readonly Expression[], multiLine?: boolean): ArrayLiteralExpression { - return new NodeObject(SyntaxKind.ArrayLiteralExpression, { - elements: createNodeArray(elements), - multiLine, - }) as unknown as ArrayLiteralExpression; -} - -export function createObjectLiteralExpression(properties: readonly ObjectLiteralElementLike[], multiLine?: boolean): ObjectLiteralExpression { - return new NodeObject(SyntaxKind.ObjectLiteralExpression, { - properties: createNodeArray(properties), - multiLine, - }) as unknown as ObjectLiteralExpression; -} - -export function createSpreadAssignment(expression: Expression): SpreadAssignment { - return new NodeObject(SyntaxKind.SpreadAssignment, { - expression, - }) as unknown as SpreadAssignment; -} - -export function createPropertyAssignment(modifiers: readonly ModifierLike[] | undefined, name: PropertyName, postfixToken: QuestionToken | ExclamationToken | undefined, type: TypeNode, initializer: Expression): PropertyAssignment { - return new NodeObject(SyntaxKind.PropertyAssignment, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - name, - postfixToken, - type, - initializer, - }) as unknown as PropertyAssignment; -} - -export function createShorthandPropertyAssignment(modifiers: readonly ModifierLike[] | undefined, name: PropertyName, postfixToken: QuestionToken | ExclamationToken | undefined, type: TypeNode, equalsToken?: EqualsToken, objectAssignmentInitializer?: Expression): ShorthandPropertyAssignment { - return new NodeObject(SyntaxKind.ShorthandPropertyAssignment, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - name, - postfixToken, - type, - equalsToken, - objectAssignmentInitializer, - }) as unknown as ShorthandPropertyAssignment; -} - -export function createDeleteExpression(expression: Expression): DeleteExpression { - return new NodeObject(SyntaxKind.DeleteExpression, { - expression, - }) as unknown as DeleteExpression; -} - -export function createTypeOfExpression(expression: Expression): TypeOfExpression { - return new NodeObject(SyntaxKind.TypeOfExpression, { - expression, - }) as unknown as TypeOfExpression; -} - -export function createVoidExpression(expression: Expression): VoidExpression { - return new NodeObject(SyntaxKind.VoidExpression, { - expression, - }) as unknown as VoidExpression; -} - -export function createAwaitExpression(expression: Expression): AwaitExpression { - return new NodeObject(SyntaxKind.AwaitExpression, { - expression, - }) as unknown as AwaitExpression; -} - -export function createTypeAssertion(type: TypeNode, expression: Expression): TypeAssertion { - return new NodeObject(SyntaxKind.TypeAssertionExpression, { - type, - expression, - }) as unknown as TypeAssertion; -} - -export function createKeywordTypeNode(kind: TKind): KeywordTypeNode { - return new NodeObject(kind, undefined) as unknown as KeywordTypeNode; -} - -export function createUnionTypeNode(types: readonly TypeNode[]): UnionTypeNode { - return new NodeObject(SyntaxKind.UnionType, { - types: createNodeArray(types), - }) as unknown as UnionTypeNode; -} - -export function createIntersectionTypeNode(types: readonly TypeNode[]): IntersectionTypeNode { - return new NodeObject(SyntaxKind.IntersectionType, { - types: createNodeArray(types), - }) as unknown as IntersectionTypeNode; -} - -export function createConditionalTypeNode(checkType: TypeNode, extendsType: TypeNode, trueType: TypeNode, falseType: TypeNode): ConditionalTypeNode { - return new NodeObject(SyntaxKind.ConditionalType, { - checkType, - extendsType, - trueType, - falseType, - }) as unknown as ConditionalTypeNode; -} - -export function createTypeOperatorNode(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.ReadonlyKeyword | SyntaxKind.UniqueKeyword, type: TypeNode): TypeOperatorNode { - return new NodeObject(SyntaxKind.TypeOperator, { - operator, - type, - }) as unknown as TypeOperatorNode; -} - -export function createInferTypeNode(typeParameter: TypeParameterDeclaration): InferTypeNode { - return new NodeObject(SyntaxKind.InferType, { - typeParameter, - }) as unknown as InferTypeNode; -} - -export function createArrayTypeNode(elementType: TypeNode): ArrayTypeNode { - return new NodeObject(SyntaxKind.ArrayType, { - elementType, - }) as unknown as ArrayTypeNode; -} - -export function createIndexedAccessTypeNode(objectType: TypeNode, indexType: TypeNode): IndexedAccessTypeNode { - return new NodeObject(SyntaxKind.IndexedAccessType, { - objectType, - indexType, - }) as unknown as IndexedAccessTypeNode; -} - -export function createTypeReferenceNode(typeName: EntityName, typeArguments?: readonly TypeNode[]): TypeReferenceNode { - return new NodeObject(SyntaxKind.TypeReference, { - typeName, - typeArguments: typeArguments ? createNodeArray(typeArguments) : undefined, - }) as unknown as TypeReferenceNode; -} - -export function createExpressionWithTypeArguments(expression: Expression, typeArguments?: readonly TypeNode[]): ExpressionWithTypeArguments { - return new NodeObject(SyntaxKind.ExpressionWithTypeArguments, { - expression, - typeArguments: typeArguments ? createNodeArray(typeArguments) : undefined, - }) as unknown as ExpressionWithTypeArguments; -} - -export function createLiteralTypeNode(literal: Node): LiteralTypeNode { - return new NodeObject(SyntaxKind.LiteralType, { - literal, - }) as unknown as LiteralTypeNode; -} - -export function createThisTypeNode(): ThisTypeNode { - return new NodeObject(SyntaxKind.ThisType, undefined) as unknown as ThisTypeNode; -} - -export function createTypePredicateNode(assertsModifier: AssertsKeyword | undefined, parameterName: TypePredicateParameterName, type?: TypeNode): TypePredicateNode { - return new NodeObject(SyntaxKind.TypePredicate, { - assertsModifier, - parameterName, - type, - }) as unknown as TypePredicateNode; -} - -export function createImportAttribute(name: ImportAttributeName, value: Expression): ImportAttribute { - return new NodeObject(SyntaxKind.ImportAttribute, { - name, - value, - }) as unknown as ImportAttribute; -} - -export function createImportAttributes(token: SyntaxKind.WithKeyword | SyntaxKind.AssertKeyword, attributes: readonly ImportAttribute[], multiLine?: boolean): ImportAttributes { - return new NodeObject(SyntaxKind.ImportAttributes, { - token, - attributes: createNodeArray(attributes), - multiLine, - }) as unknown as ImportAttributes; -} - -export function createTypeQueryNode(exprName: EntityName, typeArguments?: readonly TypeNode[]): TypeQueryNode { - return new NodeObject(SyntaxKind.TypeQuery, { - exprName, - typeArguments: typeArguments ? createNodeArray(typeArguments) : undefined, - }) as unknown as TypeQueryNode; -} - -export function createMappedTypeNode(readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType?: TypeNode, questionToken?: QuestionToken | PlusToken | MinusToken, type?: TypeNode, members?: readonly TypeElement[]): MappedTypeNode { - return new NodeObject(SyntaxKind.MappedType, { - readonlyToken, - typeParameter, - nameType, - questionToken, - type, - members: members ? createNodeArray(members) : undefined, - }) as unknown as MappedTypeNode; -} - -export function createTypeLiteralNode(members: readonly TypeElement[]): TypeLiteralNode { - return new NodeObject(SyntaxKind.TypeLiteral, { - members: createNodeArray(members), - }) as unknown as TypeLiteralNode; -} - -export function createTupleTypeNode(elements: readonly TypeNode[]): TupleTypeNode { - return new NodeObject(SyntaxKind.TupleType, { - elements: createNodeArray(elements), - }) as unknown as TupleTypeNode; -} - -export function createNamedTupleMember(dotDotDotToken: DotDotDotToken | undefined, name: Identifier, questionToken: QuestionToken | undefined, type: TypeNode): NamedTupleMember { - return new NodeObject(SyntaxKind.NamedTupleMember, { - dotDotDotToken, - name, - questionToken, - type, - }) as unknown as NamedTupleMember; -} - -export function createOptionalTypeNode(type: TypeNode): OptionalTypeNode { - return new NodeObject(SyntaxKind.OptionalType, { - type, - }) as unknown as OptionalTypeNode; -} - -export function createRestTypeNode(type: TypeNode): RestTypeNode { - return new NodeObject(SyntaxKind.RestType, { - type, - }) as unknown as RestTypeNode; -} - -export function createParenthesizedTypeNode(type: TypeNode): ParenthesizedTypeNode { - return new NodeObject(SyntaxKind.ParenthesizedType, { - type, - }) as unknown as ParenthesizedTypeNode; -} - -export function createFunctionTypeNode(typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode): FunctionTypeNode { - return new NodeObject(SyntaxKind.FunctionType, { - typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, - parameters: createNodeArray(parameters), - type, - }) as unknown as FunctionTypeNode; -} - -export function createConstructorTypeNode(modifiers: readonly ModifierLike[] | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode): ConstructorTypeNode { - return new NodeObject(SyntaxKind.ConstructorType, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, - parameters: createNodeArray(parameters), - type, - }) as unknown as ConstructorTypeNode; -} - -export function createTemplateHead(text: string, rawText: string, templateFlags: TokenFlags): TemplateHead { - return new NodeObject(SyntaxKind.TemplateHead, { - text, - rawText, - templateFlags, - }) as unknown as TemplateHead; -} - -export function createTemplateMiddle(text: string, rawText: string, templateFlags: TokenFlags): TemplateMiddle { - return new NodeObject(SyntaxKind.TemplateMiddle, { - text, - rawText, - templateFlags, - }) as unknown as TemplateMiddle; -} - -export function createTemplateTail(text: string, rawText: string, templateFlags: TokenFlags): TemplateTail { - return new NodeObject(SyntaxKind.TemplateTail, { - text, - rawText, - templateFlags, - }) as unknown as TemplateTail; -} - -export function createTemplateLiteralTypeNode(head: TemplateHead, templateSpans: readonly TemplateLiteralTypeSpan[]): TemplateLiteralTypeNode { - return new NodeObject(SyntaxKind.TemplateLiteralType, { - head, - templateSpans: createNodeArray(templateSpans), - }) as unknown as TemplateLiteralTypeNode; -} - -export function createTemplateLiteralTypeSpan(type: TypeNode, literal: TemplateMiddleOrTail): TemplateLiteralTypeSpan { - return new NodeObject(SyntaxKind.TemplateLiteralTypeSpan, { - type, - literal, - }) as unknown as TemplateLiteralTypeSpan; -} - -export function createSyntheticExpression(type: any, isSpread?: boolean, tupleNameSource?: Node): SyntheticExpression { - return new NodeObject(SyntaxKind.SyntheticExpression, { - type, - isSpread, - tupleNameSource, - }) as unknown as SyntheticExpression; -} - -export function createPartiallyEmittedExpression(expression: Expression): PartiallyEmittedExpression { - return new NodeObject(SyntaxKind.PartiallyEmittedExpression, { - expression, - }) as unknown as PartiallyEmittedExpression; -} - -export function createJsxElement(openingElement: JsxOpeningElement, children: readonly JsxChild[], closingElement: JsxClosingElement): JsxElement { - return new NodeObject(SyntaxKind.JsxElement, { - openingElement, - children: createNodeArray(children), - closingElement, - }) as unknown as JsxElement; -} - -export function createJsxAttributes(properties: readonly JsxAttributeLike[]): JsxAttributes { - return new NodeObject(SyntaxKind.JsxAttributes, { - properties: createNodeArray(properties), - }) as unknown as JsxAttributes; -} - -export function createJsxNamespacedName(namespace: Identifier, name: Identifier): JsxNamespacedName { - return new NodeObject(SyntaxKind.JsxNamespacedName, { - namespace, - name, - }) as unknown as JsxNamespacedName; -} - -export function createJsxOpeningElement(tagName: JsxTagNameExpression, typeArguments: readonly TypeNode[] | undefined, attributes: JsxAttributes): JsxOpeningElement { - return new NodeObject(SyntaxKind.JsxOpeningElement, { - tagName, - typeArguments: typeArguments ? createNodeArray(typeArguments) : undefined, - attributes, - }) as unknown as JsxOpeningElement; -} - -export function createJsxSelfClosingElement(tagName: JsxTagNameExpression, typeArguments: readonly TypeNode[] | undefined, attributes: JsxAttributes): JsxSelfClosingElement { - return new NodeObject(SyntaxKind.JsxSelfClosingElement, { - tagName, - typeArguments: typeArguments ? createNodeArray(typeArguments) : undefined, - attributes, - }) as unknown as JsxSelfClosingElement; -} - -export function createJsxFragment(openingFragment: JsxOpeningFragment, children: readonly JsxChild[], closingFragment: JsxClosingFragment): JsxFragment { - return new NodeObject(SyntaxKind.JsxFragment, { - openingFragment, - children: createNodeArray(children), - closingFragment, - }) as unknown as JsxFragment; -} - -export function createJsxOpeningFragment(): JsxOpeningFragment { - return new NodeObject(SyntaxKind.JsxOpeningFragment, undefined) as unknown as JsxOpeningFragment; -} - -export function createJsxClosingFragment(): JsxClosingFragment { - return new NodeObject(SyntaxKind.JsxClosingFragment, undefined) as unknown as JsxClosingFragment; -} - -export function createJsxAttribute(name: JsxAttributeName, initializer?: JsxAttributeValue): JsxAttribute { - return new NodeObject(SyntaxKind.JsxAttribute, { - name, - initializer, - }) as unknown as JsxAttribute; -} - -export function createJsxSpreadAttribute(expression: Expression): JsxSpreadAttribute { - return new NodeObject(SyntaxKind.JsxSpreadAttribute, { - expression, - }) as unknown as JsxSpreadAttribute; -} - -export function createJsxClosingElement(tagName: JsxTagNameExpression): JsxClosingElement { - return new NodeObject(SyntaxKind.JsxClosingElement, { - tagName, - }) as unknown as JsxClosingElement; -} - -export function createJsxExpression(dotDotDotToken?: DotDotDotToken, expression?: Expression): JsxExpression { - return new NodeObject(SyntaxKind.JsxExpression, { - dotDotDotToken, - expression, - }) as unknown as JsxExpression; -} - -export function createJsxText(text: string, containsOnlyTriviaWhiteSpaces?: boolean): JsxText { - return new NodeObject(SyntaxKind.JsxText, { - text, - containsOnlyTriviaWhiteSpaces, - }) as unknown as JsxText; -} - -export function createSyntaxList(children: readonly Node[]): SyntaxList { - return new NodeObject(SyntaxKind.SyntaxList, { - children, - }) as unknown as SyntaxList; -} - -export function createJSDoc(comment: readonly JSDocComment[], tags?: readonly JSDocTag[]): JSDoc { - return new NodeObject(SyntaxKind.JSDoc, { - comment: createNodeArray(comment), - tags: tags ? createNodeArray(tags) : undefined, - }) as unknown as JSDoc; -} - -export function createJSDocTypeExpression(type: TypeNode): JSDocTypeExpression { - return new NodeObject(SyntaxKind.JSDocTypeExpression, { - type, - }) as unknown as JSDocTypeExpression; -} - -export function createJSDocNonNullableType(type: TypeNode): JSDocNonNullableType { - return new NodeObject(SyntaxKind.JSDocNonNullableType, { - type, - }) as unknown as JSDocNonNullableType; -} - -export function createJSDocNullableType(type: TypeNode): JSDocNullableType { - return new NodeObject(SyntaxKind.JSDocNullableType, { - type, - }) as unknown as JSDocNullableType; -} - -export function createJSDocAllType(): JSDocAllType { - return new NodeObject(SyntaxKind.JSDocAllType, undefined) as unknown as JSDocAllType; -} - -export function createJSDocVariadicType(type: TypeNode): JSDocVariadicType { - return new NodeObject(SyntaxKind.JSDocVariadicType, { - type, - }) as unknown as JSDocVariadicType; -} - -export function createJSDocOptionalType(type: TypeNode): JSDocOptionalType { - return new NodeObject(SyntaxKind.JSDocOptionalType, { - type, - }) as unknown as JSDocOptionalType; -} - -export function createJSDocTypeTag(tagName: Identifier, typeExpression: Node, comment?: readonly JSDocComment[]): JSDocTypeTag { - return new NodeObject(SyntaxKind.JSDocTypeTag, { - tagName, - typeExpression, - comment: comment ? createNodeArray(comment) : undefined, - }) as unknown as JSDocTypeTag; -} - -export function createJSDocUnknownTag(tagName: Identifier, comment?: readonly JSDocComment[]): JSDocUnknownTag { - return new NodeObject(SyntaxKind.JSDocUnknownTag, { - tagName, - comment: comment ? createNodeArray(comment) : undefined, - }) as unknown as JSDocUnknownTag; -} - -export function createJSDocTemplateTag(tagName: Identifier, constraint: Node, typeParameters: readonly TypeParameterDeclaration[], comment?: readonly JSDocComment[]): JSDocTemplateTag { - return new NodeObject(SyntaxKind.JSDocTemplateTag, { - tagName, - constraint, - typeParameters: createNodeArray(typeParameters), - comment: comment ? createNodeArray(comment) : undefined, - }) as unknown as JSDocTemplateTag; -} - -export function createJSDocReturnTag(tagName: Identifier, typeExpression?: TypeNode, comment?: readonly JSDocComment[]): JSDocReturnTag { - return new NodeObject(SyntaxKind.JSDocReturnTag, { - tagName, - typeExpression, - comment: comment ? createNodeArray(comment) : undefined, - }) as unknown as JSDocReturnTag; -} - -export function createJSDocPublicTag(tagName: Identifier, comment?: readonly JSDocComment[]): JSDocPublicTag { - return new NodeObject(SyntaxKind.JSDocPublicTag, { - tagName, - comment: comment ? createNodeArray(comment) : undefined, - }) as unknown as JSDocPublicTag; -} - -export function createJSDocPrivateTag(tagName: Identifier, comment?: readonly JSDocComment[]): JSDocPrivateTag { - return new NodeObject(SyntaxKind.JSDocPrivateTag, { - tagName, - comment: comment ? createNodeArray(comment) : undefined, - }) as unknown as JSDocPrivateTag; -} - -export function createJSDocProtectedTag(tagName: Identifier, comment?: readonly JSDocComment[]): JSDocProtectedTag { - return new NodeObject(SyntaxKind.JSDocProtectedTag, { - tagName, - comment: comment ? createNodeArray(comment) : undefined, - }) as unknown as JSDocProtectedTag; -} - -export function createJSDocReadonlyTag(tagName: Identifier, comment?: readonly JSDocComment[]): JSDocReadonlyTag { - return new NodeObject(SyntaxKind.JSDocReadonlyTag, { - tagName, - comment: comment ? createNodeArray(comment) : undefined, - }) as unknown as JSDocReadonlyTag; -} - -export function createJSDocOverrideTag(tagName: Identifier, comment?: readonly JSDocComment[]): JSDocOverrideTag { - return new NodeObject(SyntaxKind.JSDocOverrideTag, { - tagName, - comment: comment ? createNodeArray(comment) : undefined, - }) as unknown as JSDocOverrideTag; -} - -export function createJSDocDeprecatedTag(tagName: Identifier, comment?: readonly JSDocComment[]): JSDocDeprecatedTag { - return new NodeObject(SyntaxKind.JSDocDeprecatedTag, { - tagName, - comment: comment ? createNodeArray(comment) : undefined, - }) as unknown as JSDocDeprecatedTag; -} - -export function createJSDocSeeTag(tagName: Identifier, nameExpression: TypeNode, comment?: readonly JSDocComment[]): JSDocSeeTag { - return new NodeObject(SyntaxKind.JSDocSeeTag, { - tagName, - nameExpression, - comment: comment ? createNodeArray(comment) : undefined, - }) as unknown as JSDocSeeTag; -} - -export function createJSDocImplementsTag(tagName: Identifier, className: ExpressionWithTypeArguments, comment?: readonly JSDocComment[]): JSDocImplementsTag { - return new NodeObject(SyntaxKind.JSDocImplementsTag, { - tagName, - className, - comment: comment ? createNodeArray(comment) : undefined, - }) as unknown as JSDocImplementsTag; -} - -export function createJSDocAugmentsTag(tagName: Identifier, className: ExpressionWithTypeArguments, comment?: readonly JSDocComment[]): JSDocAugmentsTag { - return new NodeObject(SyntaxKind.JSDocAugmentsTag, { - tagName, - className, - comment: comment ? createNodeArray(comment) : undefined, - }) as unknown as JSDocAugmentsTag; -} - -export function createJSDocSatisfiesTag(tagName: Identifier, typeExpression: TypeNode, comment?: readonly JSDocComment[]): JSDocSatisfiesTag { - return new NodeObject(SyntaxKind.JSDocSatisfiesTag, { - tagName, - typeExpression, - comment: comment ? createNodeArray(comment) : undefined, - }) as unknown as JSDocSatisfiesTag; -} - -export function createJSDocThrowsTag(tagName: Identifier, typeExpression?: TypeNode, comment?: readonly JSDocComment[]): JSDocThrowsTag { - return new NodeObject(SyntaxKind.JSDocThrowsTag, { - tagName, - typeExpression, - comment: comment ? createNodeArray(comment) : undefined, - }) as unknown as JSDocThrowsTag; -} - -export function createJSDocThisTag(tagName: Identifier, typeExpression: TypeNode, comment?: readonly JSDocComment[]): JSDocThisTag { - return new NodeObject(SyntaxKind.JSDocThisTag, { - tagName, - typeExpression, - comment: comment ? createNodeArray(comment) : undefined, - }) as unknown as JSDocThisTag; -} - -export function createJSDocImportTag(tagName: Identifier, importClause: ImportClause | undefined, moduleSpecifier: Expression, attributes?: ImportAttributes, comment?: readonly JSDocComment[]): JSDocImportTag { - return new NodeObject(SyntaxKind.JSDocImportTag, { - tagName, - importClause, - moduleSpecifier, - attributes, - comment: comment ? createNodeArray(comment) : undefined, - }) as unknown as JSDocImportTag; -} - -export function createJSDocCallbackTag(tagName: Identifier, typeExpression: TypeNode, name?: JSDocFullName, comment?: readonly JSDocComment[]): JSDocCallbackTag { - return new NodeObject(SyntaxKind.JSDocCallbackTag, { - tagName, - typeExpression, - name, - comment: comment ? createNodeArray(comment) : undefined, - }) as unknown as JSDocCallbackTag; -} - -export function createJSDocOverloadTag(tagName: Identifier, typeExpression: TypeNode, comment?: readonly JSDocComment[]): JSDocOverloadTag { - return new NodeObject(SyntaxKind.JSDocOverloadTag, { - tagName, - typeExpression, - comment: comment ? createNodeArray(comment) : undefined, - }) as unknown as JSDocOverloadTag; -} - -export function createJSDocTypedefTag(tagName: Identifier, typeExpression?: Node, name?: JSDocFullName, comment?: readonly JSDocComment[]): JSDocTypedefTag { - return new NodeObject(SyntaxKind.JSDocTypedefTag, { - tagName, - typeExpression, - name, - comment: comment ? createNodeArray(comment) : undefined, - }) as unknown as JSDocTypedefTag; -} - -export function createJSDocSignature(typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode): JSDocSignature { - return new NodeObject(SyntaxKind.JSDocSignature, { - typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, - parameters: createNodeArray(parameters), - type, - }) as unknown as JSDocSignature; -} - -export function createJSDocNameReference(name: EntityName): JSDocNameReference { - return new NodeObject(SyntaxKind.JSDocNameReference, { - name, - }) as unknown as JSDocNameReference; -} - -export function createModuleDeclaration(modifiers: readonly ModifierLike[] | undefined, keyword: SyntaxKind.ModuleKeyword | SyntaxKind.NamespaceKeyword, name: ModuleName, body?: ModuleBody): ModuleDeclaration { - return new NodeObject(SyntaxKind.ModuleDeclaration, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - keyword, - name, - body, - }) as unknown as ModuleDeclaration; -} - -export function createImportEqualsDeclaration(modifiers: readonly ModifierLike[] | undefined, isTypeOnly: boolean = false, name: Identifier, moduleReference: ModuleReference): ImportEqualsDeclaration { - return new NodeObject(SyntaxKind.ImportEqualsDeclaration, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - isTypeOnly, - name, - moduleReference, - }) as unknown as ImportEqualsDeclaration; -} - -export function createExportDeclaration(modifiers?: readonly ModifierLike[], isTypeOnly?: boolean, exportClause?: NamedExportBindings, moduleSpecifier?: Expression, attributes?: ImportAttributes): ExportDeclaration { - return new NodeObject(SyntaxKind.ExportDeclaration, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - isTypeOnly, - exportClause, - moduleSpecifier, - attributes, - }) as unknown as ExportDeclaration; -} - -export function createImportTypeNode(isTypeOf: boolean = false, argument: TypeNode, attributes?: ImportAttributes, qualifier?: EntityName, typeArguments?: readonly TypeNode[]): ImportTypeNode { - return new NodeObject(SyntaxKind.ImportType, { - isTypeOf, - argument, - attributes, - qualifier, - typeArguments: typeArguments ? createNodeArray(typeArguments) : undefined, - }) as unknown as ImportTypeNode; -} - -export function createImportClause(phaseModifier?: ImportPhaseModifierSyntaxKind, name?: Identifier, namedBindings?: NamedImportBindings): ImportClause { - return new NodeObject(SyntaxKind.ImportClause, { - phaseModifier, - name, - namedBindings, - }) as unknown as ImportClause; -} - -export function createImportSpecifier(isTypeOnly: boolean = false, propertyName: ModuleExportName | undefined, name: Identifier): ImportSpecifier { - return new NodeObject(SyntaxKind.ImportSpecifier, { - isTypeOnly, - propertyName, - name, - }) as unknown as ImportSpecifier; -} - -export function createJSDocText(text: string): JSDocText { - return new NodeObject(SyntaxKind.JSDocText, { - text, - }) as unknown as JSDocText; -} - -export function createJSDocLink(name: EntityName | undefined, text: string): JSDocLink { - return new NodeObject(SyntaxKind.JSDocLink, { - name, - text, - }) as unknown as JSDocLink; -} - -export function createJSDocLinkPlain(name: EntityName | undefined, text: string): JSDocLinkPlain { - return new NodeObject(SyntaxKind.JSDocLinkPlain, { - name, - text, - }) as unknown as JSDocLinkPlain; -} - -export function createJSDocLinkCode(name: EntityName | undefined, text: string): JSDocLinkCode { - return new NodeObject(SyntaxKind.JSDocLinkCode, { - name, - text, - }) as unknown as JSDocLinkCode; -} - -export function createTypeParameterDeclaration(modifiers: readonly ModifierLike[] | undefined, name: Identifier, constraint?: TypeNode, expression?: Expression, defaultType?: TypeNode): TypeParameterDeclaration { - return new NodeObject(SyntaxKind.TypeParameter, { - modifiers: modifiers ? createNodeArray(modifiers) : undefined, - name, - constraint, - expression, - defaultType, - }) as unknown as TypeParameterDeclaration; -} - -export function createSyntheticReferenceExpression(expression: Expression, thisArg: Expression): SyntheticReferenceExpression { - return new NodeObject(SyntaxKind.SyntheticReferenceExpression, { - expression, - thisArg, - }) as unknown as SyntheticReferenceExpression; -} - -export function createJSDocTypeLiteral(jsdocPropertyTags?: readonly JSDocTag[], isArrayType?: boolean): JSDocTypeLiteral { - return new NodeObject(SyntaxKind.JSDocTypeLiteral, { - jsdocPropertyTags, - isArrayType, - }) as unknown as JSDocTypeLiteral; -} - -export function createForInStatement(awaitModifier: AwaitKeyword | undefined, initializer: ForInitializer, expression: Expression, statement: Statement): ForInStatement { - return new NodeObject(SyntaxKind.ForInStatement, { - awaitModifier, - initializer, - expression, - statement, - }) as unknown as ForInStatement; -} - -export function createForOfStatement(awaitModifier: AwaitKeyword | undefined, initializer: ForInitializer, expression: Expression, statement: Statement): ForOfStatement { - return new NodeObject(SyntaxKind.ForOfStatement, { - awaitModifier, - initializer, - expression, - statement, - }) as unknown as ForOfStatement; -} - -export function createCaseClause(expression: Expression, statements: readonly Statement[]): CaseClause { - return new NodeObject(SyntaxKind.CaseClause, { - expression, - statements: createNodeArray(statements), - }) as unknown as CaseClause; -} - -export function createDefaultClause(expression: Expression, statements: readonly Statement[]): DefaultClause { - return new NodeObject(SyntaxKind.DefaultClause, { - expression, - statements: createNodeArray(statements), - }) as unknown as DefaultClause; -} - -export function createObjectBindingPattern(elements: readonly BindingElement[]): ObjectBindingPattern { - return new NodeObject(SyntaxKind.ObjectBindingPattern, { - elements: createNodeArray(elements), - }) as unknown as ObjectBindingPattern; -} - -export function createArrayBindingPattern(elements: readonly BindingElement[]): ArrayBindingPattern { - return new NodeObject(SyntaxKind.ArrayBindingPattern, { - elements: createNodeArray(elements), - }) as unknown as ArrayBindingPattern; -} - -export function createJSDocParameterTag(tagName: Identifier, name: EntityName, isBracketed: boolean, typeExpression: TypeNode | undefined, isNameFirst: boolean, comment: readonly JSDocComment[] | undefined): JSDocParameterTag { - return new NodeObject(SyntaxKind.JSDocParameterTag, { - tagName, - name, - isBracketed, - typeExpression, - isNameFirst, - comment: comment ? createNodeArray(comment) : undefined, - }) as unknown as JSDocParameterTag; -} - -export function createJSDocPropertyTag(tagName: Identifier, name: EntityName, isBracketed: boolean, typeExpression: TypeNode | undefined, isNameFirst: boolean, comment: readonly JSDocComment[] | undefined): JSDocPropertyTag { - return new NodeObject(SyntaxKind.JSDocPropertyTag, { - tagName, - name, - isBracketed, - typeExpression, - isNameFirst, - comment: comment ? createNodeArray(comment) : undefined, - }) as unknown as JSDocPropertyTag; -} - -export function updateQualifiedName(node: QualifiedName, left: EntityName, right: MemberName): QualifiedName { - return node.left !== left || node.right !== right ? createQualifiedName(left, right) : node; -} - -export function updateComputedPropertyName(node: ComputedPropertyName, expression: Expression): ComputedPropertyName { - return node.expression !== expression ? createComputedPropertyName(expression) : node; -} - -export function updateDecorator(node: Decorator, expression: LeftHandSideExpression): Decorator { - return node.expression !== expression ? createDecorator(expression) : node; -} - -export function updateIfStatement(node: IfStatement, expression: Expression, thenStatement: Statement, elseStatement?: Statement): IfStatement { - return node.expression !== expression || node.thenStatement !== thenStatement || node.elseStatement !== elseStatement ? createIfStatement(expression, thenStatement, elseStatement) : node; -} - -export function updateDoStatement(node: DoStatement, statement: Statement, expression: Expression): DoStatement { - return node.statement !== statement || node.expression !== expression ? createDoStatement(statement, expression) : node; -} - -export function updateWhileStatement(node: WhileStatement, expression: Expression, statement: Statement): WhileStatement { - return node.expression !== expression || node.statement !== statement ? createWhileStatement(expression, statement) : node; -} - -export function updateForStatement(node: ForStatement, initializer: ForInitializer | undefined, condition: Expression | undefined, incrementor: Expression | undefined, statement: Statement): ForStatement { - return node.initializer !== initializer || node.condition !== condition || node.incrementor !== incrementor || node.statement !== statement ? createForStatement(initializer, condition, incrementor, statement) : node; -} - -export function updateBreakStatement(node: BreakStatement, label?: Identifier): BreakStatement { - return node.label !== label ? createBreakStatement(label) : node; -} - -export function updateContinueStatement(node: ContinueStatement, label?: Identifier): ContinueStatement { - return node.label !== label ? createContinueStatement(label) : node; -} - -export function updateReturnStatement(node: ReturnStatement, expression?: Expression): ReturnStatement { - return node.expression !== expression ? createReturnStatement(expression) : node; -} - -export function updateWithStatement(node: WithStatement, expression: Expression, statement: Statement): WithStatement { - return node.expression !== expression || node.statement !== statement ? createWithStatement(expression, statement) : node; -} - -export function updateSwitchStatement(node: SwitchStatement, expression: Expression, caseBlock: CaseBlock): SwitchStatement { - return node.expression !== expression || node.caseBlock !== caseBlock ? createSwitchStatement(expression, caseBlock) : node; -} - -export function updateCaseBlock(node: CaseBlock, clauses: readonly CaseOrDefaultClause[]): CaseBlock { - return node.clauses !== clauses ? createCaseBlock(clauses) : node; -} - -export function updateThrowStatement(node: ThrowStatement, expression: Expression): ThrowStatement { - return node.expression !== expression ? createThrowStatement(expression) : node; -} - -export function updateTryStatement(node: TryStatement, tryBlock: Block, catchClause?: CatchClause, finallyBlock?: Block): TryStatement { - return node.tryBlock !== tryBlock || node.catchClause !== catchClause || node.finallyBlock !== finallyBlock ? createTryStatement(tryBlock, catchClause, finallyBlock) : node; -} - -export function updateCatchClause(node: CatchClause, variableDeclaration: VariableDeclaration | undefined, block: Block): CatchClause { - return node.variableDeclaration !== variableDeclaration || node.block !== block ? createCatchClause(variableDeclaration, block) : node; -} - -export function updateLabeledStatement(node: LabeledStatement, label: Identifier, statement: Statement): LabeledStatement { - return node.label !== label || node.statement !== statement ? createLabeledStatement(label, statement) : node; -} - -export function updateExpressionStatement(node: ExpressionStatement, expression: Expression): ExpressionStatement { - return node.expression !== expression ? createExpressionStatement(expression) : node; -} - -export function updateBlock(node: Block, statements: readonly Statement[]): Block { - return node.statements !== statements ? createBlock(statements, node.multiLine) : node; -} - -export function updateVariableStatement(node: VariableStatement, modifiers: readonly ModifierLike[] | undefined, declarationList: VariableDeclarationList): VariableStatement { - return node.modifiers !== modifiers || node.declarationList !== declarationList ? createVariableStatement(modifiers, declarationList) : node; -} - -export function updateVariableDeclaration(node: VariableDeclaration, name: BindingName, exclamationToken?: ExclamationToken, type?: TypeNode, initializer?: Expression): VariableDeclaration { - return node.name !== name || node.exclamationToken !== exclamationToken || node.type !== type || node.initializer !== initializer ? createVariableDeclaration(name, exclamationToken, type, initializer) : node; -} - -export function updateVariableDeclarationList(node: VariableDeclarationList, declarations: readonly VariableDeclaration[]): VariableDeclarationList { - return node.declarations !== declarations ? createVariableDeclarationList(declarations, node.flags) : node; -} - -export function updateParameterDeclaration(node: ParameterDeclaration, modifiers: readonly ModifierLike[] | undefined, dotDotDotToken: DotDotDotToken | undefined, name: BindingName, questionToken?: QuestionToken, type?: TypeNode, initializer?: Expression): ParameterDeclaration { - return node.modifiers !== modifiers || node.dotDotDotToken !== dotDotDotToken || node.name !== name || node.questionToken !== questionToken || node.type !== type || node.initializer !== initializer ? createParameterDeclaration(modifiers, dotDotDotToken, name, questionToken, type, initializer) : node; -} - -export function updateBindingElement(node: BindingElement, dotDotDotToken?: DotDotDotToken, propertyName?: PropertyName, name?: BindingName, initializer?: Expression): BindingElement { - return node.dotDotDotToken !== dotDotDotToken || node.propertyName !== propertyName || node.name !== name || node.initializer !== initializer ? createBindingElement(dotDotDotToken, propertyName, name, initializer) : node; -} - -export function updateMissingDeclaration(node: MissingDeclaration, modifiers?: readonly ModifierLike[]): MissingDeclaration { - return node.modifiers !== modifiers ? createMissingDeclaration(modifiers) : node; -} - -export function updateFunctionDeclaration(node: FunctionDeclaration, modifiers: readonly ModifierLike[] | undefined, asteriskToken: AsteriskToken | undefined, name: Identifier | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode, body?: FunctionBody): FunctionDeclaration { - return node.modifiers !== modifiers || node.asteriskToken !== asteriskToken || node.name !== name || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type || node.body !== body ? createFunctionDeclaration(modifiers, asteriskToken, name, typeParameters, parameters, type, body) : node; -} - -export function updateClassDeclaration(node: ClassDeclaration, modifiers: readonly ModifierLike[] | undefined, name: Identifier | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, heritageClauses: readonly HeritageClause[] | undefined, members: readonly ClassElement[]): ClassDeclaration { - return node.modifiers !== modifiers || node.name !== name || node.typeParameters !== typeParameters || node.heritageClauses !== heritageClauses || node.members !== members ? createClassDeclaration(modifiers, name, typeParameters, heritageClauses, members) : node; -} - -export function updateClassExpression(node: ClassExpression, modifiers: readonly ModifierLike[] | undefined, name: Identifier | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, heritageClauses: readonly HeritageClause[] | undefined, members: readonly ClassElement[]): ClassExpression { - return node.modifiers !== modifiers || node.name !== name || node.typeParameters !== typeParameters || node.heritageClauses !== heritageClauses || node.members !== members ? createClassExpression(modifiers, name, typeParameters, heritageClauses, members) : node; -} - -export function updateHeritageClause(node: HeritageClause, types: readonly HeritageClauseElement[]): HeritageClause { - return node.types !== types ? createHeritageClause(node.token, types) : node; -} - -export function updateInterfaceDeclaration(node: InterfaceDeclaration, modifiers: readonly ModifierLike[] | undefined, name: Identifier, typeParameters: readonly TypeParameterDeclaration[] | undefined, heritageClauses: readonly HeritageClause[] | undefined, members: readonly TypeElement[]): InterfaceDeclaration { - return node.modifiers !== modifiers || node.name !== name || node.typeParameters !== typeParameters || node.heritageClauses !== heritageClauses || node.members !== members ? createInterfaceDeclaration(modifiers, name, typeParameters, heritageClauses, members) : node; -} - -export function updateTypeAliasDeclaration(node: TypeAliasDeclaration, modifiers: readonly ModifierLike[] | undefined, name: Identifier, typeParameters: readonly TypeParameterDeclaration[] | undefined, type: TypeNode): TypeAliasDeclaration { - return node.modifiers !== modifiers || node.name !== name || node.typeParameters !== typeParameters || node.type !== type ? createTypeAliasDeclaration(modifiers, name, typeParameters, type) : node; -} - -export function updateEnumMember(node: EnumMember, name: PropertyName, initializer?: Expression): EnumMember { - return node.name !== name || node.initializer !== initializer ? createEnumMember(name, initializer) : node; -} - -export function updateEnumDeclaration(node: EnumDeclaration, modifiers: readonly ModifierLike[] | undefined, name: Identifier, members: readonly EnumMember[]): EnumDeclaration { - return node.modifiers !== modifiers || node.name !== name || node.members !== members ? createEnumDeclaration(modifiers, name, members) : node; -} - -export function updateModuleBlock(node: ModuleBlock, statements: readonly Statement[]): ModuleBlock { - return node.statements !== statements ? createModuleBlock(statements) : node; -} - -export function updateImportDeclaration(node: ImportDeclaration, modifiers: readonly ModifierLike[] | undefined, importClause: ImportClause | undefined, moduleSpecifier: Expression, attributes?: ImportAttributes): ImportDeclaration { - return node.modifiers !== modifiers || node.importClause !== importClause || node.moduleSpecifier !== moduleSpecifier || node.attributes !== attributes ? createImportDeclaration(modifiers, importClause, moduleSpecifier, attributes) : node; -} - -export function updateExternalModuleReference(node: ExternalModuleReference, expression: Expression): ExternalModuleReference { - return node.expression !== expression ? createExternalModuleReference(expression) : node; -} - -export function updateNamespaceImport(node: NamespaceImport, name: Identifier): NamespaceImport { - return node.name !== name ? createNamespaceImport(name) : node; -} - -export function updateNamedImports(node: NamedImports, elements: readonly ImportSpecifier[]): NamedImports { - return node.elements !== elements ? createNamedImports(elements) : node; -} - -export function updateExportAssignment(node: ExportAssignment, modifiers: readonly ModifierLike[] | undefined, type: TypeNode, expression: Expression): ExportAssignment { - return node.modifiers !== modifiers || node.type !== type || node.expression !== expression ? createExportAssignment(modifiers, node.isExportEquals, type, expression) : node; -} - -export function updateNamespaceExportDeclaration(node: NamespaceExportDeclaration, modifiers: readonly ModifierLike[] | undefined, name: Identifier): NamespaceExportDeclaration { - return node.modifiers !== modifiers || node.name !== name ? createNamespaceExportDeclaration(modifiers, name) : node; -} - -export function updateNamespaceExport(node: NamespaceExport, name: ModuleExportName): NamespaceExport { - return node.name !== name ? createNamespaceExport(name) : node; -} - -export function updateNamedExports(node: NamedExports, elements: readonly ExportSpecifier[]): NamedExports { - return node.elements !== elements ? createNamedExports(elements) : node; -} - -export function updateExportSpecifier(node: ExportSpecifier, propertyName: ModuleExportName | undefined, name: ModuleExportName): ExportSpecifier { - return node.propertyName !== propertyName || node.name !== name ? createExportSpecifier(node.isTypeOnly, propertyName, name) : node; -} - -export function updateCallSignatureDeclaration(node: CallSignatureDeclaration, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode): CallSignatureDeclaration { - return node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type ? createCallSignatureDeclaration(typeParameters, parameters, type) : node; -} - -export function updateConstructSignatureDeclaration(node: ConstructSignatureDeclaration, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode): ConstructSignatureDeclaration { - return node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type ? createConstructSignatureDeclaration(typeParameters, parameters, type) : node; -} - -export function updateConstructorDeclaration(node: ConstructorDeclaration, modifiers: readonly ModifierLike[] | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode, body?: FunctionBody): ConstructorDeclaration { - return node.modifiers !== modifiers || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type || node.body !== body ? createConstructorDeclaration(modifiers, typeParameters, parameters, type, body) : node; -} - -export function updateGetAccessorDeclaration(node: GetAccessorDeclaration, modifiers: readonly ModifierLike[] | undefined, name: PropertyName, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode, body?: FunctionBody): GetAccessorDeclaration { - return node.modifiers !== modifiers || node.name !== name || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type || node.body !== body ? createGetAccessorDeclaration(modifiers, name, typeParameters, parameters, type, body) : node; -} - -export function updateSetAccessorDeclaration(node: SetAccessorDeclaration, modifiers: readonly ModifierLike[] | undefined, name: PropertyName, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode, body?: FunctionBody): SetAccessorDeclaration { - return node.modifiers !== modifiers || node.name !== name || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type || node.body !== body ? createSetAccessorDeclaration(modifiers, name, typeParameters, parameters, type, body) : node; -} - -export function updateIndexSignatureDeclaration(node: IndexSignatureDeclaration, modifiers: readonly ModifierLike[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode): IndexSignatureDeclaration { - return node.modifiers !== modifiers || node.parameters !== parameters || node.type !== type ? createIndexSignatureDeclaration(modifiers, parameters, type) : node; -} - -export function updateMethodSignatureDeclaration(node: MethodSignatureDeclaration, modifiers: readonly ModifierLike[] | undefined, name: PropertyName, postfixToken: QuestionToken | ExclamationToken | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode): MethodSignatureDeclaration { - return node.modifiers !== modifiers || node.name !== name || node.postfixToken !== postfixToken || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type ? createMethodSignatureDeclaration(modifiers, name, postfixToken, typeParameters, parameters, type) : node; -} - -export function updateMethodDeclaration(node: MethodDeclaration, modifiers: readonly ModifierLike[] | undefined, asteriskToken: AsteriskToken | undefined, name: PropertyName, postfixToken: QuestionToken | ExclamationToken | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode, body?: FunctionBody): MethodDeclaration { - return node.modifiers !== modifiers || node.asteriskToken !== asteriskToken || node.name !== name || node.postfixToken !== postfixToken || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type || node.body !== body ? createMethodDeclaration(modifiers, asteriskToken, name, postfixToken, typeParameters, parameters, type, body) : node; -} - -export function updatePropertySignatureDeclaration(node: PropertySignatureDeclaration, modifiers: readonly ModifierLike[] | undefined, name: PropertyName, postfixToken: QuestionToken | ExclamationToken | undefined, type: TypeNode, initializer: Expression): PropertySignatureDeclaration { - return node.modifiers !== modifiers || node.name !== name || node.postfixToken !== postfixToken || node.type !== type || node.initializer !== initializer ? createPropertySignatureDeclaration(modifiers, name, postfixToken, type, initializer) : node; -} - -export function updatePropertyDeclaration(node: PropertyDeclaration, modifiers: readonly ModifierLike[] | undefined, name: PropertyName, postfixToken?: QuestionToken | ExclamationToken, type?: TypeNode, initializer?: Expression): PropertyDeclaration { - return node.modifiers !== modifiers || node.name !== name || node.postfixToken !== postfixToken || node.type !== type || node.initializer !== initializer ? createPropertyDeclaration(modifiers, name, postfixToken, type, initializer) : node; -} - -export function updateClassStaticBlockDeclaration(node: ClassStaticBlockDeclaration, modifiers: readonly ModifierLike[] | undefined, body: Block): ClassStaticBlockDeclaration { - return node.modifiers !== modifiers || node.body !== body ? createClassStaticBlockDeclaration(modifiers, body) : node; -} - -export function updateBinaryExpression(node: BinaryExpression, modifiers: readonly ModifierLike[] | undefined, left: Expression, type: TypeNode | undefined, operatorToken: BinaryOperatorToken, right: Expression): BinaryExpression { - return node.modifiers !== modifiers || node.left !== left || node.type !== type || node.operatorToken !== operatorToken || node.right !== right ? createBinaryExpression(modifiers, left, type, operatorToken, right) : node; -} - -export function updatePrefixUnaryExpression(node: PrefixUnaryExpression, operand: Expression): PrefixUnaryExpression { - return node.operand !== operand ? createPrefixUnaryExpression(node.operator, operand) : node; -} - -export function updatePostfixUnaryExpression(node: PostfixUnaryExpression, operand: Expression): PostfixUnaryExpression { - return node.operand !== operand ? createPostfixUnaryExpression(operand, node.operator) : node; -} - -export function updateYieldExpression(node: YieldExpression, asteriskToken?: AsteriskToken, expression?: Expression): YieldExpression { - return node.asteriskToken !== asteriskToken || node.expression !== expression ? createYieldExpression(asteriskToken, expression) : node; -} - -export function updateArrowFunction(node: ArrowFunction, modifiers: readonly ModifierLike[] | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined, equalsGreaterThanToken: EqualsGreaterThanToken, body: ConciseBody): ArrowFunction { - return node.modifiers !== modifiers || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type || node.equalsGreaterThanToken !== equalsGreaterThanToken || node.body !== body ? createArrowFunction(modifiers, typeParameters, parameters, type, equalsGreaterThanToken, body) : node; -} - -export function updateFunctionExpression(node: FunctionExpression, modifiers: readonly ModifierLike[] | undefined, asteriskToken: AsteriskToken | undefined, name: Identifier | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined, body: FunctionBody): FunctionExpression { - return node.modifiers !== modifiers || node.asteriskToken !== asteriskToken || node.name !== name || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type || node.body !== body ? createFunctionExpression(modifiers, asteriskToken, name, typeParameters, parameters, type, body) : node; -} - -export function updateAsExpression(node: AsExpression, expression: Expression, type: TypeNode): AsExpression { - return node.expression !== expression || node.type !== type ? createAsExpression(expression, type) : node; -} - -export function updateSatisfiesExpression(node: SatisfiesExpression, expression: Expression, type: TypeNode): SatisfiesExpression { - return node.expression !== expression || node.type !== type ? createSatisfiesExpression(expression, type) : node; -} - -export function updateConditionalExpression(node: ConditionalExpression, condition: Expression, questionToken: QuestionToken, whenTrue: Expression, colonToken: ColonToken, whenFalse: Expression): ConditionalExpression { - return node.condition !== condition || node.questionToken !== questionToken || node.whenTrue !== whenTrue || node.colonToken !== colonToken || node.whenFalse !== whenFalse ? createConditionalExpression(condition, questionToken, whenTrue, colonToken, whenFalse) : node; -} - -export function updatePropertyAccessExpression(node: PropertyAccessExpression, expression: Expression, questionDotToken: QuestionDotToken | undefined, name: MemberName): PropertyAccessExpression { - return node.expression !== expression || node.questionDotToken !== questionDotToken || node.name !== name ? createPropertyAccessExpression(expression, questionDotToken, name, node.flags) : node; -} - -export function updateElementAccessExpression(node: ElementAccessExpression, expression: Expression, questionDotToken: QuestionDotToken | undefined, argumentExpression: Expression): ElementAccessExpression { - return node.expression !== expression || node.questionDotToken !== questionDotToken || node.argumentExpression !== argumentExpression ? createElementAccessExpression(expression, questionDotToken, argumentExpression, node.flags) : node; -} - -export function updateCallExpression(node: CallExpression, expression: Expression, questionDotToken: QuestionDotToken | undefined, typeArguments: readonly TypeNode[] | undefined, arguments_: readonly Expression[]): CallExpression { - return node.expression !== expression || node.questionDotToken !== questionDotToken || node.typeArguments !== typeArguments || node.arguments !== arguments_ ? createCallExpression(expression, questionDotToken, typeArguments, arguments_, node.flags) : node; -} - -export function updateNewExpression(node: NewExpression, expression: Expression, typeArguments?: readonly TypeNode[], arguments_?: readonly Expression[]): NewExpression { - return node.expression !== expression || node.typeArguments !== typeArguments || node.arguments !== arguments_ ? createNewExpression(expression, typeArguments, arguments_) : node; -} - -export function updateMetaProperty(node: MetaProperty, name: Identifier): MetaProperty { - return node.name !== name ? createMetaProperty(node.keywordToken, name) : node; -} - -export function updateNonNullExpression(node: NonNullExpression, expression: Expression): NonNullExpression { - return node.expression !== expression ? createNonNullExpression(expression, node.flags) : node; -} - -export function updateSpreadElement(node: SpreadElement, expression: Expression): SpreadElement { - return node.expression !== expression ? createSpreadElement(expression) : node; -} - -export function updateTemplateExpression(node: TemplateExpression, head: TemplateHead, templateSpans: readonly TemplateSpan[]): TemplateExpression { - return node.head !== head || node.templateSpans !== templateSpans ? createTemplateExpression(head, templateSpans) : node; -} - -export function updateTemplateSpan(node: TemplateSpan, expression: Expression, literal: TemplateMiddleOrTail): TemplateSpan { - return node.expression !== expression || node.literal !== literal ? createTemplateSpan(expression, literal) : node; -} - -export function updateTaggedTemplateExpression(node: TaggedTemplateExpression, tag: Expression, questionDotToken: QuestionDotToken, typeArguments: readonly TypeNode[] | undefined, template: TemplateLiteral): TaggedTemplateExpression { - return node.tag !== tag || node.questionDotToken !== questionDotToken || node.typeArguments !== typeArguments || node.template !== template ? createTaggedTemplateExpression(tag, questionDotToken, typeArguments, template, node.flags) : node; -} - -export function updateParenthesizedExpression(node: ParenthesizedExpression, expression: Expression): ParenthesizedExpression { - return node.expression !== expression ? createParenthesizedExpression(expression) : node; -} - -export function updateArrayLiteralExpression(node: ArrayLiteralExpression, elements: readonly Expression[]): ArrayLiteralExpression { - return node.elements !== elements ? createArrayLiteralExpression(elements, node.multiLine) : node; -} - -export function updateObjectLiteralExpression(node: ObjectLiteralExpression, properties: readonly ObjectLiteralElementLike[]): ObjectLiteralExpression { - return node.properties !== properties ? createObjectLiteralExpression(properties, node.multiLine) : node; -} - -export function updateSpreadAssignment(node: SpreadAssignment, expression: Expression): SpreadAssignment { - return node.expression !== expression ? createSpreadAssignment(expression) : node; -} - -export function updatePropertyAssignment(node: PropertyAssignment, modifiers: readonly ModifierLike[] | undefined, name: PropertyName, postfixToken: QuestionToken | ExclamationToken | undefined, type: TypeNode, initializer: Expression): PropertyAssignment { - return node.modifiers !== modifiers || node.name !== name || node.postfixToken !== postfixToken || node.type !== type || node.initializer !== initializer ? createPropertyAssignment(modifiers, name, postfixToken, type, initializer) : node; -} - -export function updateShorthandPropertyAssignment(node: ShorthandPropertyAssignment, modifiers: readonly ModifierLike[] | undefined, name: PropertyName, postfixToken: QuestionToken | ExclamationToken | undefined, type: TypeNode, equalsToken?: EqualsToken, objectAssignmentInitializer?: Expression): ShorthandPropertyAssignment { - return node.modifiers !== modifiers || node.name !== name || node.postfixToken !== postfixToken || node.type !== type || node.equalsToken !== equalsToken || node.objectAssignmentInitializer !== objectAssignmentInitializer ? createShorthandPropertyAssignment(modifiers, name, postfixToken, type, equalsToken, objectAssignmentInitializer) : node; -} - -export function updateDeleteExpression(node: DeleteExpression, expression: Expression): DeleteExpression { - return node.expression !== expression ? createDeleteExpression(expression) : node; -} - -export function updateTypeOfExpression(node: TypeOfExpression, expression: Expression): TypeOfExpression { - return node.expression !== expression ? createTypeOfExpression(expression) : node; -} - -export function updateVoidExpression(node: VoidExpression, expression: Expression): VoidExpression { - return node.expression !== expression ? createVoidExpression(expression) : node; -} - -export function updateAwaitExpression(node: AwaitExpression, expression: Expression): AwaitExpression { - return node.expression !== expression ? createAwaitExpression(expression) : node; -} - -export function updateTypeAssertion(node: TypeAssertion, type: TypeNode, expression: Expression): TypeAssertion { - return node.type !== type || node.expression !== expression ? createTypeAssertion(type, expression) : node; -} - -export function updateUnionTypeNode(node: UnionTypeNode, types: readonly TypeNode[]): UnionTypeNode { - return node.types !== types ? createUnionTypeNode(types) : node; -} - -export function updateIntersectionTypeNode(node: IntersectionTypeNode, types: readonly TypeNode[]): IntersectionTypeNode { - return node.types !== types ? createIntersectionTypeNode(types) : node; -} - -export function updateConditionalTypeNode(node: ConditionalTypeNode, checkType: TypeNode, extendsType: TypeNode, trueType: TypeNode, falseType: TypeNode): ConditionalTypeNode { - return node.checkType !== checkType || node.extendsType !== extendsType || node.trueType !== trueType || node.falseType !== falseType ? createConditionalTypeNode(checkType, extendsType, trueType, falseType) : node; -} - -export function updateTypeOperatorNode(node: TypeOperatorNode, type: TypeNode): TypeOperatorNode { - return node.type !== type ? createTypeOperatorNode(node.operator, type) : node; -} - -export function updateInferTypeNode(node: InferTypeNode, typeParameter: TypeParameterDeclaration): InferTypeNode { - return node.typeParameter !== typeParameter ? createInferTypeNode(typeParameter) : node; -} - -export function updateArrayTypeNode(node: ArrayTypeNode, elementType: TypeNode): ArrayTypeNode { - return node.elementType !== elementType ? createArrayTypeNode(elementType) : node; -} - -export function updateIndexedAccessTypeNode(node: IndexedAccessTypeNode, objectType: TypeNode, indexType: TypeNode): IndexedAccessTypeNode { - return node.objectType !== objectType || node.indexType !== indexType ? createIndexedAccessTypeNode(objectType, indexType) : node; -} - -export function updateTypeReferenceNode(node: TypeReferenceNode, typeName: EntityName, typeArguments?: readonly TypeNode[]): TypeReferenceNode { - return node.typeName !== typeName || node.typeArguments !== typeArguments ? createTypeReferenceNode(typeName, typeArguments) : node; -} - -export function updateExpressionWithTypeArguments(node: ExpressionWithTypeArguments, expression: Expression, typeArguments?: readonly TypeNode[]): ExpressionWithTypeArguments { - return node.expression !== expression || node.typeArguments !== typeArguments ? createExpressionWithTypeArguments(expression, typeArguments) : node; -} - -export function updateLiteralTypeNode(node: LiteralTypeNode, literal: Node): LiteralTypeNode { - return node.literal !== literal ? createLiteralTypeNode(literal) : node; -} - -export function updateTypePredicateNode(node: TypePredicateNode, assertsModifier: AssertsKeyword | undefined, parameterName: TypePredicateParameterName, type?: TypeNode): TypePredicateNode { - return node.assertsModifier !== assertsModifier || node.parameterName !== parameterName || node.type !== type ? createTypePredicateNode(assertsModifier, parameterName, type) : node; -} - -export function updateImportAttribute(node: ImportAttribute, name: ImportAttributeName, value: Expression): ImportAttribute { - return node.name !== name || node.value !== value ? createImportAttribute(name, value) : node; -} - -export function updateImportAttributes(node: ImportAttributes, attributes: readonly ImportAttribute[]): ImportAttributes { - return node.attributes !== attributes ? createImportAttributes(node.token, attributes, node.multiLine) : node; -} - -export function updateTypeQueryNode(node: TypeQueryNode, exprName: EntityName, typeArguments?: readonly TypeNode[]): TypeQueryNode { - return node.exprName !== exprName || node.typeArguments !== typeArguments ? createTypeQueryNode(exprName, typeArguments) : node; -} - -export function updateMappedTypeNode(node: MappedTypeNode, readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType?: TypeNode, questionToken?: QuestionToken | PlusToken | MinusToken, type?: TypeNode, members?: readonly TypeElement[]): MappedTypeNode { - return node.readonlyToken !== readonlyToken || node.typeParameter !== typeParameter || node.nameType !== nameType || node.questionToken !== questionToken || node.type !== type || node.members !== members ? createMappedTypeNode(readonlyToken, typeParameter, nameType, questionToken, type, members) : node; -} - -export function updateTypeLiteralNode(node: TypeLiteralNode, members: readonly TypeElement[]): TypeLiteralNode { - return node.members !== members ? createTypeLiteralNode(members) : node; -} - -export function updateTupleTypeNode(node: TupleTypeNode, elements: readonly TypeNode[]): TupleTypeNode { - return node.elements !== elements ? createTupleTypeNode(elements) : node; -} - -export function updateNamedTupleMember(node: NamedTupleMember, dotDotDotToken: DotDotDotToken | undefined, name: Identifier, questionToken: QuestionToken | undefined, type: TypeNode): NamedTupleMember { - return node.dotDotDotToken !== dotDotDotToken || node.name !== name || node.questionToken !== questionToken || node.type !== type ? createNamedTupleMember(dotDotDotToken, name, questionToken, type) : node; -} - -export function updateOptionalTypeNode(node: OptionalTypeNode, type: TypeNode): OptionalTypeNode { - return node.type !== type ? createOptionalTypeNode(type) : node; -} - -export function updateRestTypeNode(node: RestTypeNode, type: TypeNode): RestTypeNode { - return node.type !== type ? createRestTypeNode(type) : node; -} - -export function updateParenthesizedTypeNode(node: ParenthesizedTypeNode, type: TypeNode): ParenthesizedTypeNode { - return node.type !== type ? createParenthesizedTypeNode(type) : node; -} - -export function updateFunctionTypeNode(node: FunctionTypeNode, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode): FunctionTypeNode { - return node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type ? createFunctionTypeNode(typeParameters, parameters, type) : node; -} - -export function updateConstructorTypeNode(node: ConstructorTypeNode, modifiers: readonly ModifierLike[] | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode): ConstructorTypeNode { - return node.modifiers !== modifiers || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type ? createConstructorTypeNode(modifiers, typeParameters, parameters, type) : node; -} - -export function updateTemplateLiteralTypeNode(node: TemplateLiteralTypeNode, head: TemplateHead, templateSpans: readonly TemplateLiteralTypeSpan[]): TemplateLiteralTypeNode { - return node.head !== head || node.templateSpans !== templateSpans ? createTemplateLiteralTypeNode(head, templateSpans) : node; -} - -export function updateTemplateLiteralTypeSpan(node: TemplateLiteralTypeSpan, type: TypeNode, literal: TemplateMiddleOrTail): TemplateLiteralTypeSpan { - return node.type !== type || node.literal !== literal ? createTemplateLiteralTypeSpan(type, literal) : node; -} - -export function updateSyntheticExpression(node: SyntheticExpression, tupleNameSource?: Node): SyntheticExpression { - return node.tupleNameSource !== tupleNameSource ? createSyntheticExpression(node.type, node.isSpread, tupleNameSource) : node; -} - -export function updatePartiallyEmittedExpression(node: PartiallyEmittedExpression, expression: Expression): PartiallyEmittedExpression { - return node.expression !== expression ? createPartiallyEmittedExpression(expression) : node; -} - -export function updateJsxElement(node: JsxElement, openingElement: JsxOpeningElement, children: readonly JsxChild[], closingElement: JsxClosingElement): JsxElement { - return node.openingElement !== openingElement || node.children !== children || node.closingElement !== closingElement ? createJsxElement(openingElement, children, closingElement) : node; -} - -export function updateJsxAttributes(node: JsxAttributes, properties: readonly JsxAttributeLike[]): JsxAttributes { - return node.properties !== properties ? createJsxAttributes(properties) : node; -} - -export function updateJsxNamespacedName(node: JsxNamespacedName, namespace: Identifier, name: Identifier): JsxNamespacedName { - return node.namespace !== namespace || node.name !== name ? createJsxNamespacedName(namespace, name) : node; -} - -export function updateJsxOpeningElement(node: JsxOpeningElement, tagName: JsxTagNameExpression, typeArguments: readonly TypeNode[] | undefined, attributes: JsxAttributes): JsxOpeningElement { - return node.tagName !== tagName || node.typeArguments !== typeArguments || node.attributes !== attributes ? createJsxOpeningElement(tagName, typeArguments, attributes) : node; -} - -export function updateJsxSelfClosingElement(node: JsxSelfClosingElement, tagName: JsxTagNameExpression, typeArguments: readonly TypeNode[] | undefined, attributes: JsxAttributes): JsxSelfClosingElement { - return node.tagName !== tagName || node.typeArguments !== typeArguments || node.attributes !== attributes ? createJsxSelfClosingElement(tagName, typeArguments, attributes) : node; -} - -export function updateJsxFragment(node: JsxFragment, openingFragment: JsxOpeningFragment, children: readonly JsxChild[], closingFragment: JsxClosingFragment): JsxFragment { - return node.openingFragment !== openingFragment || node.children !== children || node.closingFragment !== closingFragment ? createJsxFragment(openingFragment, children, closingFragment) : node; -} - -export function updateJsxAttribute(node: JsxAttribute, name: JsxAttributeName, initializer?: JsxAttributeValue): JsxAttribute { - return node.name !== name || node.initializer !== initializer ? createJsxAttribute(name, initializer) : node; -} - -export function updateJsxSpreadAttribute(node: JsxSpreadAttribute, expression: Expression): JsxSpreadAttribute { - return node.expression !== expression ? createJsxSpreadAttribute(expression) : node; -} - -export function updateJsxClosingElement(node: JsxClosingElement, tagName: JsxTagNameExpression): JsxClosingElement { - return node.tagName !== tagName ? createJsxClosingElement(tagName) : node; -} - -export function updateJsxExpression(node: JsxExpression, dotDotDotToken?: DotDotDotToken, expression?: Expression): JsxExpression { - return node.dotDotDotToken !== dotDotDotToken || node.expression !== expression ? createJsxExpression(dotDotDotToken, expression) : node; -} - -export function updateSyntaxList(node: SyntaxList, children: readonly Node[]): SyntaxList { - return node.children !== children ? createSyntaxList(children) : node; -} - -export function updateJSDoc(node: JSDoc, comment: readonly JSDocComment[], tags?: readonly JSDocTag[]): JSDoc { - return node.comment !== comment || node.tags !== tags ? createJSDoc(comment, tags) : node; -} - -export function updateJSDocTypeExpression(node: JSDocTypeExpression, type: TypeNode): JSDocTypeExpression { - return node.type !== type ? createJSDocTypeExpression(type) : node; -} - -export function updateJSDocNonNullableType(node: JSDocNonNullableType, type: TypeNode): JSDocNonNullableType { - return node.type !== type ? createJSDocNonNullableType(type) : node; -} - -export function updateJSDocNullableType(node: JSDocNullableType, type: TypeNode): JSDocNullableType { - return node.type !== type ? createJSDocNullableType(type) : node; -} - -export function updateJSDocVariadicType(node: JSDocVariadicType, type: TypeNode): JSDocVariadicType { - return node.type !== type ? createJSDocVariadicType(type) : node; -} - -export function updateJSDocOptionalType(node: JSDocOptionalType, type: TypeNode): JSDocOptionalType { - return node.type !== type ? createJSDocOptionalType(type) : node; -} - -export function updateJSDocTypeTag(node: JSDocTypeTag, tagName: Identifier, typeExpression: Node, comment?: readonly JSDocComment[]): JSDocTypeTag { - return node.tagName !== tagName || node.typeExpression !== typeExpression || node.comment !== comment ? createJSDocTypeTag(tagName, typeExpression, comment) : node; -} - -export function updateJSDocUnknownTag(node: JSDocUnknownTag, tagName: Identifier, comment?: readonly JSDocComment[]): JSDocUnknownTag { - return node.tagName !== tagName || node.comment !== comment ? createJSDocUnknownTag(tagName, comment) : node; -} - -export function updateJSDocTemplateTag(node: JSDocTemplateTag, tagName: Identifier, constraint: Node, typeParameters: readonly TypeParameterDeclaration[], comment?: readonly JSDocComment[]): JSDocTemplateTag { - return node.tagName !== tagName || node.constraint !== constraint || node.typeParameters !== typeParameters || node.comment !== comment ? createJSDocTemplateTag(tagName, constraint, typeParameters, comment) : node; -} - -export function updateJSDocReturnTag(node: JSDocReturnTag, tagName: Identifier, typeExpression?: TypeNode, comment?: readonly JSDocComment[]): JSDocReturnTag { - return node.tagName !== tagName || node.typeExpression !== typeExpression || node.comment !== comment ? createJSDocReturnTag(tagName, typeExpression, comment) : node; -} - -export function updateJSDocPublicTag(node: JSDocPublicTag, tagName: Identifier, comment?: readonly JSDocComment[]): JSDocPublicTag { - return node.tagName !== tagName || node.comment !== comment ? createJSDocPublicTag(tagName, comment) : node; -} - -export function updateJSDocPrivateTag(node: JSDocPrivateTag, tagName: Identifier, comment?: readonly JSDocComment[]): JSDocPrivateTag { - return node.tagName !== tagName || node.comment !== comment ? createJSDocPrivateTag(tagName, comment) : node; -} - -export function updateJSDocProtectedTag(node: JSDocProtectedTag, tagName: Identifier, comment?: readonly JSDocComment[]): JSDocProtectedTag { - return node.tagName !== tagName || node.comment !== comment ? createJSDocProtectedTag(tagName, comment) : node; -} - -export function updateJSDocReadonlyTag(node: JSDocReadonlyTag, tagName: Identifier, comment?: readonly JSDocComment[]): JSDocReadonlyTag { - return node.tagName !== tagName || node.comment !== comment ? createJSDocReadonlyTag(tagName, comment) : node; -} - -export function updateJSDocOverrideTag(node: JSDocOverrideTag, tagName: Identifier, comment?: readonly JSDocComment[]): JSDocOverrideTag { - return node.tagName !== tagName || node.comment !== comment ? createJSDocOverrideTag(tagName, comment) : node; -} - -export function updateJSDocDeprecatedTag(node: JSDocDeprecatedTag, tagName: Identifier, comment?: readonly JSDocComment[]): JSDocDeprecatedTag { - return node.tagName !== tagName || node.comment !== comment ? createJSDocDeprecatedTag(tagName, comment) : node; -} - -export function updateJSDocSeeTag(node: JSDocSeeTag, tagName: Identifier, nameExpression: TypeNode, comment?: readonly JSDocComment[]): JSDocSeeTag { - return node.tagName !== tagName || node.nameExpression !== nameExpression || node.comment !== comment ? createJSDocSeeTag(tagName, nameExpression, comment) : node; -} - -export function updateJSDocImplementsTag(node: JSDocImplementsTag, tagName: Identifier, className: ExpressionWithTypeArguments, comment?: readonly JSDocComment[]): JSDocImplementsTag { - return node.tagName !== tagName || node.className !== className || node.comment !== comment ? createJSDocImplementsTag(tagName, className, comment) : node; -} - -export function updateJSDocAugmentsTag(node: JSDocAugmentsTag, tagName: Identifier, className: ExpressionWithTypeArguments, comment?: readonly JSDocComment[]): JSDocAugmentsTag { - return node.tagName !== tagName || node.className !== className || node.comment !== comment ? createJSDocAugmentsTag(tagName, className, comment) : node; -} - -export function updateJSDocSatisfiesTag(node: JSDocSatisfiesTag, tagName: Identifier, typeExpression: TypeNode, comment?: readonly JSDocComment[]): JSDocSatisfiesTag { - return node.tagName !== tagName || node.typeExpression !== typeExpression || node.comment !== comment ? createJSDocSatisfiesTag(tagName, typeExpression, comment) : node; -} - -export function updateJSDocThrowsTag(node: JSDocThrowsTag, tagName: Identifier, typeExpression?: TypeNode, comment?: readonly JSDocComment[]): JSDocThrowsTag { - return node.tagName !== tagName || node.typeExpression !== typeExpression || node.comment !== comment ? createJSDocThrowsTag(tagName, typeExpression, comment) : node; -} - -export function updateJSDocThisTag(node: JSDocThisTag, tagName: Identifier, typeExpression: TypeNode, comment?: readonly JSDocComment[]): JSDocThisTag { - return node.tagName !== tagName || node.typeExpression !== typeExpression || node.comment !== comment ? createJSDocThisTag(tagName, typeExpression, comment) : node; -} - -export function updateJSDocImportTag(node: JSDocImportTag, tagName: Identifier, importClause: ImportClause | undefined, moduleSpecifier: Expression, attributes?: ImportAttributes, comment?: readonly JSDocComment[]): JSDocImportTag { - return node.tagName !== tagName || node.importClause !== importClause || node.moduleSpecifier !== moduleSpecifier || node.attributes !== attributes || node.comment !== comment ? createJSDocImportTag(tagName, importClause, moduleSpecifier, attributes, comment) : node; -} - -export function updateJSDocCallbackTag(node: JSDocCallbackTag, tagName: Identifier, typeExpression: TypeNode, name?: JSDocFullName, comment?: readonly JSDocComment[]): JSDocCallbackTag { - return node.tagName !== tagName || node.typeExpression !== typeExpression || node.name !== name || node.comment !== comment ? createJSDocCallbackTag(tagName, typeExpression, name, comment) : node; -} - -export function updateJSDocOverloadTag(node: JSDocOverloadTag, tagName: Identifier, typeExpression: TypeNode, comment?: readonly JSDocComment[]): JSDocOverloadTag { - return node.tagName !== tagName || node.typeExpression !== typeExpression || node.comment !== comment ? createJSDocOverloadTag(tagName, typeExpression, comment) : node; -} - -export function updateJSDocTypedefTag(node: JSDocTypedefTag, tagName: Identifier, typeExpression?: Node, name?: JSDocFullName, comment?: readonly JSDocComment[]): JSDocTypedefTag { - return node.tagName !== tagName || node.typeExpression !== typeExpression || node.name !== name || node.comment !== comment ? createJSDocTypedefTag(tagName, typeExpression, name, comment) : node; -} - -export function updateJSDocSignature(node: JSDocSignature, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode): JSDocSignature { - return node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type ? createJSDocSignature(typeParameters, parameters, type) : node; -} - -export function updateJSDocNameReference(node: JSDocNameReference, name: EntityName): JSDocNameReference { - return node.name !== name ? createJSDocNameReference(name) : node; -} - -export function updateModuleDeclaration(node: ModuleDeclaration, modifiers: readonly ModifierLike[] | undefined, name: ModuleName, body?: ModuleBody): ModuleDeclaration { - return node.modifiers !== modifiers || node.name !== name || node.body !== body ? createModuleDeclaration(modifiers, node.keyword, name, body) : node; -} - -export function updateImportEqualsDeclaration(node: ImportEqualsDeclaration, modifiers: readonly ModifierLike[] | undefined, name: Identifier, moduleReference: ModuleReference): ImportEqualsDeclaration { - return node.modifiers !== modifiers || node.name !== name || node.moduleReference !== moduleReference ? createImportEqualsDeclaration(modifiers, node.isTypeOnly, name, moduleReference) : node; -} - -export function updateExportDeclaration(node: ExportDeclaration, modifiers?: readonly ModifierLike[], exportClause?: NamedExportBindings, moduleSpecifier?: Expression, attributes?: ImportAttributes): ExportDeclaration { - return node.modifiers !== modifiers || node.exportClause !== exportClause || node.moduleSpecifier !== moduleSpecifier || node.attributes !== attributes ? createExportDeclaration(modifiers, node.isTypeOnly, exportClause, moduleSpecifier, attributes) : node; -} - -export function updateImportTypeNode(node: ImportTypeNode, argument: TypeNode, attributes?: ImportAttributes, qualifier?: EntityName, typeArguments?: readonly TypeNode[]): ImportTypeNode { - return node.argument !== argument || node.attributes !== attributes || node.qualifier !== qualifier || node.typeArguments !== typeArguments ? createImportTypeNode(node.isTypeOf, argument, attributes, qualifier, typeArguments) : node; -} - -export function updateImportClause(node: ImportClause, name?: Identifier, namedBindings?: NamedImportBindings): ImportClause { - return node.name !== name || node.namedBindings !== namedBindings ? createImportClause(node.phaseModifier, name, namedBindings) : node; -} - -export function updateImportSpecifier(node: ImportSpecifier, propertyName: ModuleExportName | undefined, name: Identifier): ImportSpecifier { - return node.propertyName !== propertyName || node.name !== name ? createImportSpecifier(node.isTypeOnly, propertyName, name) : node; -} - -export function updateJSDocLink(node: JSDocLink, name?: EntityName): JSDocLink { - return node.name !== name ? createJSDocLink(name, node.text) : node; -} - -export function updateJSDocLinkPlain(node: JSDocLinkPlain, name?: EntityName): JSDocLinkPlain { - return node.name !== name ? createJSDocLinkPlain(name, node.text) : node; -} - -export function updateJSDocLinkCode(node: JSDocLinkCode, name?: EntityName): JSDocLinkCode { - return node.name !== name ? createJSDocLinkCode(name, node.text) : node; -} - -export function updateTypeParameterDeclaration(node: TypeParameterDeclaration, modifiers: readonly ModifierLike[] | undefined, name: Identifier, constraint?: TypeNode, expression?: Expression, defaultType?: TypeNode): TypeParameterDeclaration { - return node.modifiers !== modifiers || node.name !== name || node.constraint !== constraint || node.expression !== expression || node.defaultType !== defaultType ? createTypeParameterDeclaration(modifiers, name, constraint, expression, defaultType) : node; -} - -export function updateSyntheticReferenceExpression(node: SyntheticReferenceExpression, expression: Expression, thisArg: Expression): SyntheticReferenceExpression { - return node.expression !== expression || node.thisArg !== thisArg ? createSyntheticReferenceExpression(expression, thisArg) : node; -} - -export function updateJSDocTypeLiteral(node: JSDocTypeLiteral, jsdocPropertyTags?: readonly JSDocTag[]): JSDocTypeLiteral { - return node.jsdocPropertyTags !== jsdocPropertyTags ? createJSDocTypeLiteral(jsdocPropertyTags, node.isArrayType) : node; -} - -export function updateForInStatement(node: ForInStatement, awaitModifier: AwaitKeyword | undefined, initializer: ForInitializer, expression: Expression, statement: Statement): ForInStatement { - return node.awaitModifier !== awaitModifier || node.initializer !== initializer || node.expression !== expression || node.statement !== statement ? createForInStatement(awaitModifier, initializer, expression, statement) : node; -} - -export function updateForOfStatement(node: ForOfStatement, awaitModifier: AwaitKeyword | undefined, initializer: ForInitializer, expression: Expression, statement: Statement): ForOfStatement { - return node.awaitModifier !== awaitModifier || node.initializer !== initializer || node.expression !== expression || node.statement !== statement ? createForOfStatement(awaitModifier, initializer, expression, statement) : node; -} - -export function updateCaseClause(node: CaseClause, expression: Expression, statements: readonly Statement[]): CaseClause { - return node.expression !== expression || node.statements !== statements ? createCaseClause(expression, statements) : node; -} - -export function updateDefaultClause(node: DefaultClause, expression: Expression, statements: readonly Statement[]): DefaultClause { - return node.expression !== expression || node.statements !== statements ? createDefaultClause(expression, statements) : node; -} - -export function updateObjectBindingPattern(node: ObjectBindingPattern, elements: readonly BindingElement[]): ObjectBindingPattern { - return node.elements !== elements ? createObjectBindingPattern(elements) : node; -} - -export function updateArrayBindingPattern(node: ArrayBindingPattern, elements: readonly BindingElement[]): ArrayBindingPattern { - return node.elements !== elements ? createArrayBindingPattern(elements) : node; -} - -export function updateJSDocParameterTag(node: JSDocParameterTag, tagName: Identifier, name: EntityName, typeExpression: TypeNode | undefined, comment: readonly JSDocComment[] | undefined): JSDocParameterTag { - return node.tagName !== tagName || node.name !== name || node.typeExpression !== typeExpression || node.comment !== comment ? createJSDocParameterTag(tagName, name, node.isBracketed, typeExpression, node.isNameFirst, comment) : node; -} - -export function updateJSDocPropertyTag(node: JSDocPropertyTag, tagName: Identifier, name: EntityName, typeExpression: TypeNode | undefined, comment: readonly JSDocComment[] | undefined): JSDocPropertyTag { - return node.tagName !== tagName || node.name !== name || node.typeExpression !== typeExpression || node.comment !== comment ? createJSDocPropertyTag(tagName, name, node.isBracketed, typeExpression, node.isNameFirst, comment) : node; -} - -export function createSourceFile(statements: readonly Statement[], endOfFileToken: EndOfFile, text: string, fileName: string, path: Path): SourceFile { - return new NodeObject(SyntaxKind.SourceFile, { - statements: createNodeArray(statements), - endOfFileToken, - text, - originalText: text, - spanMap: undefined, - contentMapper: undefined, - virtualFileName: undefined, - diagnosticDirectives: undefined, - fileName, - path, - }) as unknown as SourceFile; -} - -function cloneSourceFileWithChanges(source: SourceFile, statements: readonly Statement[], endOfFileToken: EndOfFile): SourceFile { - return new NodeObject(SyntaxKind.SourceFile, { - ...cloneSourceFileData(source), - statements: createNodeArray(statements), - endOfFileToken, - }) as unknown as SourceFile; -} - -export function updateSourceFile(node: SourceFile, statements: readonly Statement[], endOfFileToken: EndOfFile): SourceFile { - return node.statements !== statements || node.endOfFileToken !== endOfFileToken - ? cloneSourceFileWithChanges(node, statements, endOfFileToken) - : node; -} +// Code generated by tools/scripts/tsc/generate-ts-ast.ts. DO NOT EDIT. + +import { NodeFlags } from "#enums/nodeFlags"; +import { SyntaxKind } from "#enums/syntaxKind"; +import { TokenFlags } from "#enums/tokenFlags"; +import type { + ArrayBindingPattern, + ArrayLiteralExpression, + ArrayTypeNode, + ArrowFunction, + AsExpression, + AssertsKeyword, + AsteriskToken, + AwaitExpression, + AwaitKeyword, + BigIntLiteral, + BinaryExpression, + BinaryOperatorToken, + BindingElement, + BindingName, + Block, + BreakStatement, + CallExpression, + CallSignatureDeclaration, + CaseBlock, + CaseClause, + CaseOrDefaultClause, + CatchClause, + ClassDeclaration, + ClassElement, + ClassExpression, + ClassStaticBlockDeclaration, + ColonToken, + ComputedPropertyName, + ConciseBody, + ConditionalExpression, + ConditionalTypeNode, + ConstructorDeclaration, + ConstructorTypeNode, + ConstructSignatureDeclaration, + ContinueStatement, + DebuggerStatement, + Decorator, + DefaultClause, + DeleteExpression, + DoStatement, + DotDotDotToken, + ElementAccessExpression, + EmptyStatement, + EndOfFile, + EntityName, + EnumDeclaration, + EnumMember, + EqualsGreaterThanToken, + EqualsToken, + ExclamationToken, + ExportAssignment, + ExportDeclaration, + ExportSpecifier, + Expression, + ExpressionStatement, + ExpressionWithTypeArguments, + ExternalModuleReference, + ForInitializer, + ForInStatement, + ForOfStatement, + ForStatement, + FunctionBody, + FunctionDeclaration, + FunctionExpression, + FunctionTypeNode, + GetAccessorDeclaration, + HeritageClause, + HeritageClauseElement, + Identifier, + IfStatement, + ImportAttribute, + ImportAttributeName, + ImportAttributes, + ImportClause, + ImportDeclaration, + ImportEqualsDeclaration, + ImportPhaseModifierSyntaxKind, + ImportSpecifier, + ImportTypeNode, + IndexedAccessTypeNode, + IndexSignatureDeclaration, + InferTypeNode, + InterfaceDeclaration, + IntersectionTypeNode, + JSDoc, + JSDocAllType, + JSDocAugmentsTag, + JSDocCallbackTag, + JSDocComment, + JSDocDeprecatedTag, + JSDocFullName, + JSDocImplementsTag, + JSDocImportTag, + JSDocLink, + JSDocLinkCode, + JSDocLinkPlain, + JSDocNameReference, + JSDocNonNullableType, + JSDocNullableType, + JSDocOptionalType, + JSDocOverloadTag, + JSDocOverrideTag, + JSDocParameterTag, + JSDocPrivateTag, + JSDocPropertyTag, + JSDocProtectedTag, + JSDocPublicTag, + JSDocReadonlyTag, + JSDocReturnTag, + JSDocSatisfiesTag, + JSDocSeeTag, + JSDocSignature, + JSDocTag, + JSDocTemplateTag, + JSDocText, + JSDocThisTag, + JSDocThrowsTag, + JSDocTypedefTag, + JSDocTypeExpression, + JSDocTypeLiteral, + JSDocTypeTag, + JSDocUnknownTag, + JSDocVariadicType, + JsxAttribute, + JsxAttributeLike, + JsxAttributeName, + JsxAttributes, + JsxAttributeValue, + JsxChild, + JsxClosingElement, + JsxClosingFragment, + JsxElement, + JsxExpression, + JsxFragment, + JsxNamespacedName, + JsxOpeningElement, + JsxOpeningFragment, + JsxSelfClosingElement, + JsxSpreadAttribute, + JsxTagNameExpression, + JsxText, + KeywordExpression, + KeywordExpressionSyntaxKind, + KeywordTypeNode, + KeywordTypeSyntaxKind, + LabeledStatement, + LeftHandSideExpression, + LiteralTypeNode, + MappedTypeNode, + MemberName, + MetaProperty, + MethodDeclaration, + MethodSignatureDeclaration, + MinusToken, + MissingDeclaration, + ModifierLike, + ModuleBlock, + ModuleBody, + ModuleDeclaration, + ModuleExportName, + ModuleName, + ModuleReference, + NamedExportBindings, + NamedExports, + NamedImportBindings, + NamedImports, + NamedTupleMember, + NamespaceExport, + NamespaceExportDeclaration, + NamespaceImport, + NewExpression, + Node, + NodeArray, + NonNullExpression, + NoSubstitutionTemplateLiteral, + NotEmittedStatement, + NotEmittedTypeElement, + NumericLiteral, + ObjectBindingPattern, + ObjectLiteralElementLike, + ObjectLiteralExpression, + OmittedExpression, + OptionalTypeNode, + ParameterDeclaration, + ParenthesizedExpression, + ParenthesizedTypeNode, + PartiallyEmittedExpression, + Path, + PlusToken, + PostfixUnaryExpression, + PrefixUnaryExpression, + PrivateIdentifier, + PropertyAccessExpression, + PropertyAssignment, + PropertyDeclaration, + PropertyName, + PropertySignatureDeclaration, + QualifiedName, + QuestionDotToken, + QuestionToken, + ReadonlyKeyword, + RegularExpressionLiteral, + RestTypeNode, + ReturnStatement, + SatisfiesExpression, + SemicolonClassElement, + SetAccessorDeclaration, + ShorthandPropertyAssignment, + SourceFile, + SpreadAssignment, + SpreadElement, + Statement, + StringLiteral, + SwitchStatement, + SyntaxList, + SyntheticExpression, + SyntheticReferenceExpression, + TaggedTemplateExpression, + TemplateExpression, + TemplateHead, + TemplateLiteral, + TemplateLiteralTypeNode, + TemplateLiteralTypeSpan, + TemplateMiddle, + TemplateMiddleOrTail, + TemplateSpan, + TemplateTail, + ThisTypeNode, + ThrowStatement, + Token, + TokenSyntaxKind, + TryStatement, + TupleTypeNode, + TypeAliasDeclaration, + TypeAssertion, + TypeElement, + TypeLiteralNode, + TypeNode, + TypeOfExpression, + TypeOperatorNode, + TypeParameterDeclaration, + TypePredicateNode, + TypePredicateParameterName, + TypeQueryNode, + TypeReferenceNode, + UnionTypeNode, + VariableDeclaration, + VariableDeclarationList, + VariableStatement, + VoidExpression, + WhileStatement, + WithStatement, + YieldExpression, +} from "./ast.ts"; +import { getTokenPosOfNode } from "./astnav.ts"; +import { cloneSourceFileData } from "./utils.ts"; +import { + forEachChildOfJSDocParameterTag, + forEachChildOfJSDocPropertyTag, +} from "./visitor.ts"; + +export class NodeObject { + readonly kind: SyntaxKind; + flags: NodeFlags = 0 as NodeFlags; + readonly pos: number = -1; + readonly end: number = -1; + parent: Node = undefined!; + _data: any; + + constructor(kind: SyntaxKind, data: any) { + this.kind = kind; + this._data = data; + } + + get ambientModuleNames(): any { + return this._data?.ambientModuleNames; + } + get argument(): any { + return this._data?.argument; + } + get argumentExpression(): any { + return this._data?.argumentExpression; + } + get arguments(): any { + return this._data?.arguments; + } + get assertsModifier(): any { + return this._data?.assertsModifier; + } + get asteriskToken(): any { + return this._data?.asteriskToken; + } + get attributes(): any { + return this._data?.attributes; + } + get awaitModifier(): any { + return this._data?.awaitModifier; + } + get block(): any { + return this._data?.block; + } + get body(): any { + return this._data?.body; + } + get canonicalSourceFileName(): any { + return this._data?.canonicalSourceFileName; + } + get caseBlock(): any { + return this._data?.caseBlock; + } + get catchClause(): any { + return this._data?.catchClause; + } + get checkType(): any { + return this._data?.checkType; + } + get children(): any { + return this._data?.children; + } + get className(): any { + return this._data?.className; + } + get clauses(): any { + return this._data?.clauses; + } + get closingElement(): any { + return this._data?.closingElement; + } + get closingFragment(): any { + return this._data?.closingFragment; + } + get colonToken(): any { + return this._data?.colonToken; + } + get comment(): any { + return this._data?.comment; + } + get condition(): any { + return this._data?.condition; + } + get constraint(): any { + return this._data?.constraint; + } + get containsOnlyTriviaWhiteSpaces(): any { + return this._data?.containsOnlyTriviaWhiteSpaces; + } + get contentMapper(): any { + return this._data?.contentMapper; + } + get declarationList(): any { + return this._data?.declarationList; + } + get declarations(): any { + return this._data?.declarations; + } + get defaultType(): any { + return this._data?.defaultType; + } + get diagnosticDirectives(): any { + return this._data?.diagnosticDirectives; + } + get dotDotDotToken(): any { + return this._data?.dotDotDotToken; + } + get elementType(): any { + return this._data?.elementType; + } + get elements(): any { + return this._data?.elements; + } + get elseStatement(): any { + return this._data?.elseStatement; + } + get endOfFileToken(): any { + return this._data?.endOfFileToken; + } + get equalsGreaterThanToken(): any { + return this._data?.equalsGreaterThanToken; + } + get equalsToken(): any { + return this._data?.equalsToken; + } + get exclamationToken(): any { + return this._data?.exclamationToken; + } + get exportClause(): any { + return this._data?.exportClause; + } + get exprName(): any { + return this._data?.exprName; + } + get expression(): any { + return this._data?.expression; + } + get extendsType(): any { + return this._data?.extendsType; + } + get externalModuleIndicator(): any { + return this._data?.externalModuleIndicator; + } + get falseType(): any { + return this._data?.falseType; + } + get fileName(): any { + return this._data?.fileName; + } + get finallyBlock(): any { + return this._data?.finallyBlock; + } + get head(): any { + return this._data?.head; + } + get heritageClauses(): any { + return this._data?.heritageClauses; + } + get importClause(): any { + return this._data?.importClause; + } + get imports(): any { + return this._data?.imports; + } + get incrementor(): any { + return this._data?.incrementor; + } + get indexType(): any { + return this._data?.indexType; + } + get initializer(): any { + return this._data?.initializer; + } + get isArrayType(): any { + return this._data?.isArrayType; + } + get isBracketed(): any { + return this._data?.isBracketed; + } + get isDeclarationFile(): any { + return this._data?.isDeclarationFile; + } + get isExportEquals(): any { + return this._data?.isExportEquals; + } + get isNameFirst(): any { + return this._data?.isNameFirst; + } + get isSpread(): any { + return this._data?.isSpread; + } + get isTypeOf(): any { + return this._data?.isTypeOf; + } + get isTypeOnly(): any { + return this._data?.isTypeOnly; + } + get jsdocPropertyTags(): any { + return this._data?.jsdocPropertyTags; + } + get keyword(): any { + return this._data?.keyword; + } + get keywordToken(): any { + return this._data?.keywordToken; + } + get label(): any { + return this._data?.label; + } + get languageVariant(): any { + return this._data?.languageVariant; + } + get left(): any { + return this._data?.left; + } + get libReferenceDirectives(): any { + return this._data?.libReferenceDirectives; + } + get literal(): any { + return this._data?.literal; + } + get members(): any { + return this._data?.members; + } + get modifiers(): any { + return this._data?.modifiers; + } + get moduleAugmentations(): any { + return this._data?.moduleAugmentations; + } + get moduleReference(): any { + return this._data?.moduleReference; + } + get moduleSpecifier(): any { + return this._data?.moduleSpecifier; + } + get multiLine(): any { + return this._data?.multiLine; + } + get name(): any { + return this._data?.name; + } + get nameExpression(): any { + return this._data?.nameExpression; + } + get nameType(): any { + return this._data?.nameType; + } + get namedBindings(): any { + return this._data?.namedBindings; + } + get namespace(): any { + return this._data?.namespace; + } + get objectAssignmentInitializer(): any { + return this._data?.objectAssignmentInitializer; + } + get objectType(): any { + return this._data?.objectType; + } + get openingElement(): any { + return this._data?.openingElement; + } + get openingFragment(): any { + return this._data?.openingFragment; + } + get operand(): any { + return this._data?.operand; + } + get operator(): any { + return this._data?.operator; + } + get operatorToken(): any { + return this._data?.operatorToken; + } + get originalText(): any { + return this._data?.originalText; + } + get parameterName(): any { + return this._data?.parameterName; + } + get parameters(): any { + return this._data?.parameters; + } + get path(): any { + return this._data?.path; + } + get phaseModifier(): any { + return this._data?.phaseModifier; + } + get postfixToken(): any { + return this._data?.postfixToken; + } + get properties(): any { + return this._data?.properties; + } + get propertyName(): any { + return this._data?.propertyName; + } + get qualifier(): any { + return this._data?.qualifier; + } + get questionDotToken(): any { + return this._data?.questionDotToken; + } + get questionToken(): any { + return this._data?.questionToken; + } + get rawText(): any { + return this._data?.rawText; + } + get readonlyToken(): any { + return this._data?.readonlyToken; + } + get referencedFiles(): any { + return this._data?.referencedFiles; + } + get right(): any { + return this._data?.right; + } + get scriptKind(): any { + return this._data?.scriptKind; + } + get spanMap(): any { + return this._data?.spanMap; + } + get statement(): any { + return this._data?.statement; + } + get statements(): any { + return this._data?.statements; + } + get supplementalSourceFileNames(): any { + return this._data?.supplementalSourceFileNames; + } + get tag(): any { + return this._data?.tag; + } + get tagName(): any { + return this._data?.tagName; + } + get tags(): any { + return this._data?.tags; + } + get template(): any { + return this._data?.template; + } + get templateFlags(): any { + return this._data?.templateFlags; + } + get templateSpans(): any { + return this._data?.templateSpans; + } + get text(): any { + return this._data?.text; + } + get thenStatement(): any { + return this._data?.thenStatement; + } + get thisArg(): any { + return this._data?.thisArg; + } + get token(): any { + return this._data?.token; + } + get tokenCache(): any { + return this._data?.tokenCache; + } + get tokenFlags(): any { + return this._data?.tokenFlags; + } + get trueType(): any { + return this._data?.trueType; + } + get tryBlock(): any { + return this._data?.tryBlock; + } + get tupleNameSource(): any { + return this._data?.tupleNameSource; + } + get type(): any { + return this._data?.type; + } + get typeArguments(): any { + return this._data?.typeArguments; + } + get typeExpression(): any { + return this._data?.typeExpression; + } + get typeName(): any { + return this._data?.typeName; + } + get typeParameter(): any { + return this._data?.typeParameter; + } + get typeParameters(): any { + return this._data?.typeParameters; + } + get typeReferenceDirectives(): any { + return this._data?.typeReferenceDirectives; + } + get types(): any { + return this._data?.types; + } + get value(): any { + return this._data?.value; + } + get variableDeclaration(): any { + return this._data?.variableDeclaration; + } + get virtualFileName(): any { + return this._data?.virtualFileName; + } + get whenFalse(): any { + return this._data?.whenFalse; + } + get whenTrue(): any { + return this._data?.whenTrue; + } + + forEachChild(visitor: (node: Node) => T, visitArray?: (nodes: NodeArray) => T): T | undefined { + const fn = forEachChildTable[this.kind]; + return fn ? fn(this._data, visitor, visitArray) : undefined; + } + + getSourceFile(): SourceFile { + let node: Node = this as unknown as Node; + while (node.parent) node = node.parent; + return node as unknown as SourceFile; + } + + getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number { + return getTokenPosOfNode(this as unknown as Node, sourceFile ?? this.getSourceFile(), includeJsDocComment); + } + + getFullStart(): number { + return this.pos; + } + + getEnd(): number { + return this.end; + } + + getWidth(sourceFile?: SourceFile): number { + return this.getEnd() - this.getStart(sourceFile); + } + + getFullWidth(): number { + return this.end - this.pos; + } + + getLeadingTriviaWidth(sourceFile?: SourceFile): number { + return this.getStart(sourceFile) - this.pos; + } + + getFullText(sourceFile?: SourceFile): string { + return (sourceFile ?? this.getSourceFile()).text.substring(this.pos, this.end); + } + + getText(sourceFile?: SourceFile): string { + sourceFile ??= this.getSourceFile(); + return sourceFile.text.substring(this.getStart(sourceFile), this.end); + } +} + +function isNodeArray(array: readonly T[]): array is NodeArray { + return "pos" in array && "end" in array; +} + +export function createNodeArray(elements: readonly T[], pos: number = -1, end: number = -1): NodeArray { + if (isNodeArray(elements)) return elements; + const arr = elements.slice() as unknown as NodeArray & { pos: number; end: number; }; + arr.pos = pos; + arr.end = end; + return arr; +} + +export function cloneNode(node: T): T { + const data = cloneNodeData(node); + const clone = new NodeObject(node.kind, data); + (clone as any).flags = node.flags; + (clone as any).pos = node.pos; + (clone as any).end = node.end; + return clone as unknown as T; +} + +function cloneNodeData(node: Node): any { + const n = node as any; + switch (node.kind) { + case SyntaxKind.Identifier: + return { text: n.text }; + case SyntaxKind.PrivateIdentifier: + return { text: n.text }; + case SyntaxKind.QualifiedName: + return { left: n.left, right: n.right }; + case SyntaxKind.ComputedPropertyName: + return { expression: n.expression }; + case SyntaxKind.Decorator: + return { expression: n.expression }; + case SyntaxKind.IfStatement: + return { expression: n.expression, thenStatement: n.thenStatement, elseStatement: n.elseStatement }; + case SyntaxKind.DoStatement: + return { statement: n.statement, expression: n.expression }; + case SyntaxKind.WhileStatement: + return { expression: n.expression, statement: n.statement }; + case SyntaxKind.ForStatement: + return { initializer: n.initializer, condition: n.condition, incrementor: n.incrementor, statement: n.statement }; + case SyntaxKind.BreakStatement: + return { label: n.label }; + case SyntaxKind.ContinueStatement: + return { label: n.label }; + case SyntaxKind.ReturnStatement: + return { expression: n.expression }; + case SyntaxKind.WithStatement: + return { expression: n.expression, statement: n.statement }; + case SyntaxKind.SwitchStatement: + return { expression: n.expression, caseBlock: n.caseBlock }; + case SyntaxKind.CaseBlock: + return { clauses: n.clauses }; + case SyntaxKind.ThrowStatement: + return { expression: n.expression }; + case SyntaxKind.TryStatement: + return { tryBlock: n.tryBlock, catchClause: n.catchClause, finallyBlock: n.finallyBlock }; + case SyntaxKind.CatchClause: + return { variableDeclaration: n.variableDeclaration, block: n.block }; + case SyntaxKind.LabeledStatement: + return { label: n.label, statement: n.statement }; + case SyntaxKind.ExpressionStatement: + return { expression: n.expression }; + case SyntaxKind.Block: + return { statements: n.statements, multiLine: n.multiLine }; + case SyntaxKind.VariableStatement: + return { modifiers: n.modifiers, declarationList: n.declarationList }; + case SyntaxKind.VariableDeclaration: + return { name: n.name, exclamationToken: n.exclamationToken, type: n.type, initializer: n.initializer }; + case SyntaxKind.VariableDeclarationList: + return { declarations: n.declarations }; + case SyntaxKind.Parameter: + return { modifiers: n.modifiers, dotDotDotToken: n.dotDotDotToken, name: n.name, questionToken: n.questionToken, type: n.type, initializer: n.initializer }; + case SyntaxKind.BindingElement: + return { dotDotDotToken: n.dotDotDotToken, propertyName: n.propertyName, name: n.name, initializer: n.initializer }; + case SyntaxKind.MissingDeclaration: + return { modifiers: n.modifiers }; + case SyntaxKind.FunctionDeclaration: + return { modifiers: n.modifiers, asteriskToken: n.asteriskToken, name: n.name, typeParameters: n.typeParameters, parameters: n.parameters, type: n.type, body: n.body }; + case SyntaxKind.ClassDeclaration: + return { modifiers: n.modifiers, name: n.name, typeParameters: n.typeParameters, heritageClauses: n.heritageClauses, members: n.members }; + case SyntaxKind.ClassExpression: + return { modifiers: n.modifiers, name: n.name, typeParameters: n.typeParameters, heritageClauses: n.heritageClauses, members: n.members }; + case SyntaxKind.HeritageClause: + return { token: n.token, types: n.types }; + case SyntaxKind.InterfaceDeclaration: + return { modifiers: n.modifiers, name: n.name, typeParameters: n.typeParameters, heritageClauses: n.heritageClauses, members: n.members }; + case SyntaxKind.TypeAliasDeclaration: + case SyntaxKind.JSTypeAliasDeclaration: + return { modifiers: n.modifiers, name: n.name, typeParameters: n.typeParameters, type: n.type }; + case SyntaxKind.EnumMember: + return { name: n.name, initializer: n.initializer }; + case SyntaxKind.EnumDeclaration: + return { modifiers: n.modifiers, name: n.name, members: n.members }; + case SyntaxKind.ModuleBlock: + return { statements: n.statements }; + case SyntaxKind.ImportDeclaration: + case SyntaxKind.JSImportDeclaration: + return { modifiers: n.modifiers, importClause: n.importClause, moduleSpecifier: n.moduleSpecifier, attributes: n.attributes }; + case SyntaxKind.ExternalModuleReference: + return { expression: n.expression }; + case SyntaxKind.NamespaceImport: + return { name: n.name }; + case SyntaxKind.NamedImports: + return { elements: n.elements }; + case SyntaxKind.ExportAssignment: + return { modifiers: n.modifiers, isExportEquals: n.isExportEquals, type: n.type, expression: n.expression }; + case SyntaxKind.NamespaceExportDeclaration: + return { modifiers: n.modifiers, name: n.name }; + case SyntaxKind.NamespaceExport: + return { name: n.name }; + case SyntaxKind.NamedExports: + return { elements: n.elements }; + case SyntaxKind.ExportSpecifier: + return { isTypeOnly: n.isTypeOnly, propertyName: n.propertyName, name: n.name }; + case SyntaxKind.CallSignature: + return { typeParameters: n.typeParameters, parameters: n.parameters, type: n.type }; + case SyntaxKind.ConstructSignature: + return { typeParameters: n.typeParameters, parameters: n.parameters, type: n.type }; + case SyntaxKind.Constructor: + return { modifiers: n.modifiers, typeParameters: n.typeParameters, parameters: n.parameters, type: n.type, body: n.body }; + case SyntaxKind.GetAccessor: + return { modifiers: n.modifiers, name: n.name, typeParameters: n.typeParameters, parameters: n.parameters, type: n.type, body: n.body }; + case SyntaxKind.SetAccessor: + return { modifiers: n.modifiers, name: n.name, typeParameters: n.typeParameters, parameters: n.parameters, type: n.type, body: n.body }; + case SyntaxKind.IndexSignature: + return { modifiers: n.modifiers, parameters: n.parameters, type: n.type }; + case SyntaxKind.MethodSignature: + return { modifiers: n.modifiers, name: n.name, postfixToken: n.postfixToken, typeParameters: n.typeParameters, parameters: n.parameters, type: n.type }; + case SyntaxKind.MethodDeclaration: + return { modifiers: n.modifiers, asteriskToken: n.asteriskToken, name: n.name, postfixToken: n.postfixToken, typeParameters: n.typeParameters, parameters: n.parameters, type: n.type, body: n.body }; + case SyntaxKind.PropertySignature: + return { modifiers: n.modifiers, name: n.name, postfixToken: n.postfixToken, type: n.type, initializer: n.initializer }; + case SyntaxKind.PropertyDeclaration: + return { modifiers: n.modifiers, name: n.name, postfixToken: n.postfixToken, type: n.type, initializer: n.initializer }; + case SyntaxKind.ClassStaticBlockDeclaration: + return { modifiers: n.modifiers, body: n.body }; + case SyntaxKind.StringLiteral: + return { text: n.text, tokenFlags: n.tokenFlags }; + case SyntaxKind.NumericLiteral: + return { text: n.text, tokenFlags: n.tokenFlags }; + case SyntaxKind.BigIntLiteral: + return { text: n.text, tokenFlags: n.tokenFlags }; + case SyntaxKind.RegularExpressionLiteral: + return { text: n.text, tokenFlags: n.tokenFlags }; + case SyntaxKind.NoSubstitutionTemplateLiteral: + return { text: n.text, templateFlags: n.templateFlags }; + case SyntaxKind.BinaryExpression: + return { modifiers: n.modifiers, left: n.left, type: n.type, operatorToken: n.operatorToken, right: n.right }; + case SyntaxKind.PrefixUnaryExpression: + return { operator: n.operator, operand: n.operand }; + case SyntaxKind.PostfixUnaryExpression: + return { operand: n.operand, operator: n.operator }; + case SyntaxKind.YieldExpression: + return { asteriskToken: n.asteriskToken, expression: n.expression }; + case SyntaxKind.ArrowFunction: + return { modifiers: n.modifiers, typeParameters: n.typeParameters, parameters: n.parameters, type: n.type, equalsGreaterThanToken: n.equalsGreaterThanToken, body: n.body }; + case SyntaxKind.FunctionExpression: + return { modifiers: n.modifiers, asteriskToken: n.asteriskToken, name: n.name, typeParameters: n.typeParameters, parameters: n.parameters, type: n.type, body: n.body }; + case SyntaxKind.AsExpression: + return { expression: n.expression, type: n.type }; + case SyntaxKind.SatisfiesExpression: + return { expression: n.expression, type: n.type }; + case SyntaxKind.ConditionalExpression: + return { condition: n.condition, questionToken: n.questionToken, whenTrue: n.whenTrue, colonToken: n.colonToken, whenFalse: n.whenFalse }; + case SyntaxKind.PropertyAccessExpression: + return { expression: n.expression, questionDotToken: n.questionDotToken, name: n.name }; + case SyntaxKind.ElementAccessExpression: + return { expression: n.expression, questionDotToken: n.questionDotToken, argumentExpression: n.argumentExpression }; + case SyntaxKind.CallExpression: + return { expression: n.expression, questionDotToken: n.questionDotToken, typeArguments: n.typeArguments, arguments: n.arguments }; + case SyntaxKind.NewExpression: + return { expression: n.expression, typeArguments: n.typeArguments, arguments: n.arguments }; + case SyntaxKind.MetaProperty: + return { keywordToken: n.keywordToken, name: n.name }; + case SyntaxKind.NonNullExpression: + return { expression: n.expression }; + case SyntaxKind.SpreadElement: + return { expression: n.expression }; + case SyntaxKind.TemplateExpression: + return { head: n.head, templateSpans: n.templateSpans }; + case SyntaxKind.TemplateSpan: + return { expression: n.expression, literal: n.literal }; + case SyntaxKind.TaggedTemplateExpression: + return { tag: n.tag, questionDotToken: n.questionDotToken, typeArguments: n.typeArguments, template: n.template }; + case SyntaxKind.ParenthesizedExpression: + return { expression: n.expression }; + case SyntaxKind.ArrayLiteralExpression: + return { elements: n.elements, multiLine: n.multiLine }; + case SyntaxKind.ObjectLiteralExpression: + return { properties: n.properties, multiLine: n.multiLine }; + case SyntaxKind.SpreadAssignment: + return { expression: n.expression }; + case SyntaxKind.PropertyAssignment: + return { modifiers: n.modifiers, name: n.name, postfixToken: n.postfixToken, type: n.type, initializer: n.initializer }; + case SyntaxKind.ShorthandPropertyAssignment: + return { modifiers: n.modifiers, name: n.name, postfixToken: n.postfixToken, type: n.type, equalsToken: n.equalsToken, objectAssignmentInitializer: n.objectAssignmentInitializer }; + case SyntaxKind.DeleteExpression: + return { expression: n.expression }; + case SyntaxKind.TypeOfExpression: + return { expression: n.expression }; + case SyntaxKind.VoidExpression: + return { expression: n.expression }; + case SyntaxKind.AwaitExpression: + return { expression: n.expression }; + case SyntaxKind.TypeAssertionExpression: + return { type: n.type, expression: n.expression }; + case SyntaxKind.UnionType: + return { types: n.types }; + case SyntaxKind.IntersectionType: + return { types: n.types }; + case SyntaxKind.ConditionalType: + return { checkType: n.checkType, extendsType: n.extendsType, trueType: n.trueType, falseType: n.falseType }; + case SyntaxKind.TypeOperator: + return { operator: n.operator, type: n.type }; + case SyntaxKind.InferType: + return { typeParameter: n.typeParameter }; + case SyntaxKind.ArrayType: + return { elementType: n.elementType }; + case SyntaxKind.IndexedAccessType: + return { objectType: n.objectType, indexType: n.indexType }; + case SyntaxKind.TypeReference: + return { typeName: n.typeName, typeArguments: n.typeArguments }; + case SyntaxKind.ExpressionWithTypeArguments: + return { expression: n.expression, typeArguments: n.typeArguments }; + case SyntaxKind.LiteralType: + return { literal: n.literal }; + case SyntaxKind.TypePredicate: + return { assertsModifier: n.assertsModifier, parameterName: n.parameterName, type: n.type }; + case SyntaxKind.ImportAttribute: + return { name: n.name, value: n.value }; + case SyntaxKind.ImportAttributes: + return { token: n.token, attributes: n.attributes, multiLine: n.multiLine }; + case SyntaxKind.TypeQuery: + return { exprName: n.exprName, typeArguments: n.typeArguments }; + case SyntaxKind.MappedType: + return { readonlyToken: n.readonlyToken, typeParameter: n.typeParameter, nameType: n.nameType, questionToken: n.questionToken, type: n.type, members: n.members }; + case SyntaxKind.TypeLiteral: + return { members: n.members }; + case SyntaxKind.TupleType: + return { elements: n.elements }; + case SyntaxKind.NamedTupleMember: + return { dotDotDotToken: n.dotDotDotToken, name: n.name, questionToken: n.questionToken, type: n.type }; + case SyntaxKind.OptionalType: + return { type: n.type }; + case SyntaxKind.RestType: + return { type: n.type }; + case SyntaxKind.ParenthesizedType: + return { type: n.type }; + case SyntaxKind.FunctionType: + return { typeParameters: n.typeParameters, parameters: n.parameters, type: n.type }; + case SyntaxKind.ConstructorType: + return { modifiers: n.modifiers, typeParameters: n.typeParameters, parameters: n.parameters, type: n.type }; + case SyntaxKind.TemplateHead: + return { text: n.text, rawText: n.rawText, templateFlags: n.templateFlags }; + case SyntaxKind.TemplateMiddle: + return { text: n.text, rawText: n.rawText, templateFlags: n.templateFlags }; + case SyntaxKind.TemplateTail: + return { text: n.text, rawText: n.rawText, templateFlags: n.templateFlags }; + case SyntaxKind.TemplateLiteralType: + return { head: n.head, templateSpans: n.templateSpans }; + case SyntaxKind.TemplateLiteralTypeSpan: + return { type: n.type, literal: n.literal }; + case SyntaxKind.SyntheticExpression: + return { type: n.type, isSpread: n.isSpread, tupleNameSource: n.tupleNameSource }; + case SyntaxKind.PartiallyEmittedExpression: + return { expression: n.expression }; + case SyntaxKind.JsxElement: + return { openingElement: n.openingElement, children: n.children, closingElement: n.closingElement }; + case SyntaxKind.JsxAttributes: + return { properties: n.properties }; + case SyntaxKind.JsxNamespacedName: + return { namespace: n.namespace, name: n.name }; + case SyntaxKind.JsxOpeningElement: + return { tagName: n.tagName, typeArguments: n.typeArguments, attributes: n.attributes }; + case SyntaxKind.JsxSelfClosingElement: + return { tagName: n.tagName, typeArguments: n.typeArguments, attributes: n.attributes }; + case SyntaxKind.JsxFragment: + return { openingFragment: n.openingFragment, children: n.children, closingFragment: n.closingFragment }; + case SyntaxKind.JsxAttribute: + return { name: n.name, initializer: n.initializer }; + case SyntaxKind.JsxSpreadAttribute: + return { expression: n.expression }; + case SyntaxKind.JsxClosingElement: + return { tagName: n.tagName }; + case SyntaxKind.JsxExpression: + return { dotDotDotToken: n.dotDotDotToken, expression: n.expression }; + case SyntaxKind.JsxText: + return { text: n.text, containsOnlyTriviaWhiteSpaces: n.containsOnlyTriviaWhiteSpaces }; + case SyntaxKind.SyntaxList: + return { children: n.children }; + case SyntaxKind.JSDoc: + return { comment: n.comment, tags: n.tags }; + case SyntaxKind.JSDocTypeExpression: + return { type: n.type }; + case SyntaxKind.JSDocNonNullableType: + return { type: n.type }; + case SyntaxKind.JSDocNullableType: + return { type: n.type }; + case SyntaxKind.JSDocVariadicType: + return { type: n.type }; + case SyntaxKind.JSDocOptionalType: + return { type: n.type }; + case SyntaxKind.JSDocTypeTag: + return { tagName: n.tagName, typeExpression: n.typeExpression, comment: n.comment }; + case SyntaxKind.JSDocUnknownTag: + return { tagName: n.tagName, comment: n.comment }; + case SyntaxKind.JSDocTemplateTag: + return { tagName: n.tagName, constraint: n.constraint, typeParameters: n.typeParameters, comment: n.comment }; + case SyntaxKind.JSDocReturnTag: + return { tagName: n.tagName, typeExpression: n.typeExpression, comment: n.comment }; + case SyntaxKind.JSDocPublicTag: + return { tagName: n.tagName, comment: n.comment }; + case SyntaxKind.JSDocPrivateTag: + return { tagName: n.tagName, comment: n.comment }; + case SyntaxKind.JSDocProtectedTag: + return { tagName: n.tagName, comment: n.comment }; + case SyntaxKind.JSDocReadonlyTag: + return { tagName: n.tagName, comment: n.comment }; + case SyntaxKind.JSDocOverrideTag: + return { tagName: n.tagName, comment: n.comment }; + case SyntaxKind.JSDocDeprecatedTag: + return { tagName: n.tagName, comment: n.comment }; + case SyntaxKind.JSDocSeeTag: + return { tagName: n.tagName, nameExpression: n.nameExpression, comment: n.comment }; + case SyntaxKind.JSDocImplementsTag: + return { tagName: n.tagName, className: n.className, comment: n.comment }; + case SyntaxKind.JSDocAugmentsTag: + return { tagName: n.tagName, className: n.className, comment: n.comment }; + case SyntaxKind.JSDocSatisfiesTag: + return { tagName: n.tagName, typeExpression: n.typeExpression, comment: n.comment }; + case SyntaxKind.JSDocThrowsTag: + return { tagName: n.tagName, typeExpression: n.typeExpression, comment: n.comment }; + case SyntaxKind.JSDocThisTag: + return { tagName: n.tagName, typeExpression: n.typeExpression, comment: n.comment }; + case SyntaxKind.JSDocImportTag: + return { tagName: n.tagName, importClause: n.importClause, moduleSpecifier: n.moduleSpecifier, attributes: n.attributes, comment: n.comment }; + case SyntaxKind.JSDocCallbackTag: + return { tagName: n.tagName, typeExpression: n.typeExpression, name: n.name, comment: n.comment }; + case SyntaxKind.JSDocOverloadTag: + return { tagName: n.tagName, typeExpression: n.typeExpression, comment: n.comment }; + case SyntaxKind.JSDocTypedefTag: + return { tagName: n.tagName, typeExpression: n.typeExpression, name: n.name, comment: n.comment }; + case SyntaxKind.JSDocSignature: + return { typeParameters: n.typeParameters, parameters: n.parameters, type: n.type }; + case SyntaxKind.JSDocNameReference: + return { name: n.name }; + case SyntaxKind.ModuleDeclaration: + return { modifiers: n.modifiers, keyword: n.keyword, name: n.name, body: n.body }; + case SyntaxKind.ImportEqualsDeclaration: + return { modifiers: n.modifiers, isTypeOnly: n.isTypeOnly, name: n.name, moduleReference: n.moduleReference }; + case SyntaxKind.ExportDeclaration: + return { modifiers: n.modifiers, isTypeOnly: n.isTypeOnly, exportClause: n.exportClause, moduleSpecifier: n.moduleSpecifier, attributes: n.attributes }; + case SyntaxKind.ImportType: + return { isTypeOf: n.isTypeOf, argument: n.argument, attributes: n.attributes, qualifier: n.qualifier, typeArguments: n.typeArguments }; + case SyntaxKind.ImportClause: + return { phaseModifier: n.phaseModifier, name: n.name, namedBindings: n.namedBindings }; + case SyntaxKind.ImportSpecifier: + return { isTypeOnly: n.isTypeOnly, propertyName: n.propertyName, name: n.name }; + case SyntaxKind.JSDocText: + return { text: n.text }; + case SyntaxKind.JSDocLink: + return { name: n.name, text: n.text }; + case SyntaxKind.JSDocLinkPlain: + return { name: n.name, text: n.text }; + case SyntaxKind.JSDocLinkCode: + return { name: n.name, text: n.text }; + case SyntaxKind.TypeParameter: + return { modifiers: n.modifiers, name: n.name, constraint: n.constraint, expression: n.expression, defaultType: n.defaultType }; + case SyntaxKind.SyntheticReferenceExpression: + return { expression: n.expression, thisArg: n.thisArg }; + case SyntaxKind.JSDocTypeLiteral: + return { jsdocPropertyTags: n.jsdocPropertyTags, isArrayType: n.isArrayType }; + case SyntaxKind.ForInStatement: + return { awaitModifier: n.awaitModifier, initializer: n.initializer, expression: n.expression, statement: n.statement }; + case SyntaxKind.ForOfStatement: + return { awaitModifier: n.awaitModifier, initializer: n.initializer, expression: n.expression, statement: n.statement }; + case SyntaxKind.CaseClause: + return { expression: n.expression, statements: n.statements }; + case SyntaxKind.DefaultClause: + return { expression: n.expression, statements: n.statements }; + case SyntaxKind.ObjectBindingPattern: + return { elements: n.elements }; + case SyntaxKind.ArrayBindingPattern: + return { elements: n.elements }; + case SyntaxKind.JSDocParameterTag: + return { tagName: n.tagName, name: n.name, isBracketed: n.isBracketed, typeExpression: n.typeExpression, isNameFirst: n.isNameFirst, comment: n.comment }; + case SyntaxKind.JSDocPropertyTag: + return { tagName: n.tagName, name: n.name, isBracketed: n.isBracketed, typeExpression: n.typeExpression, isNameFirst: n.isNameFirst, comment: n.comment }; + case SyntaxKind.SourceFile: + return cloneSourceFileData(n); + default: + return undefined; + } +} + +type ForEachChildFunction = (data: any, cbNode: (node: Node) => T, cbNodes: ((nodes: NodeArray) => T) | undefined) => T | undefined; + +const forEachChildTable: Record = { + [SyntaxKind.QualifiedName]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.left) || + visitNode(cbNode, data.right), + [SyntaxKind.ComputedPropertyName]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), + [SyntaxKind.Decorator]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), + [SyntaxKind.IfStatement]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.expression) || + visitNode(cbNode, data.thenStatement) || + visitNode(cbNode, data.elseStatement), + [SyntaxKind.DoStatement]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.statement) || + visitNode(cbNode, data.expression), + [SyntaxKind.WhileStatement]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.expression) || + visitNode(cbNode, data.statement), + [SyntaxKind.ForStatement]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.initializer) || + visitNode(cbNode, data.condition) || + visitNode(cbNode, data.incrementor) || + visitNode(cbNode, data.statement), + [SyntaxKind.BreakStatement]: (data, cbNode, cbNodes) => visitNode(cbNode, data.label), + [SyntaxKind.ContinueStatement]: (data, cbNode, cbNodes) => visitNode(cbNode, data.label), + [SyntaxKind.ReturnStatement]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), + [SyntaxKind.WithStatement]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.expression) || + visitNode(cbNode, data.statement), + [SyntaxKind.SwitchStatement]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.expression) || + visitNode(cbNode, data.caseBlock), + [SyntaxKind.CaseBlock]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.clauses), + [SyntaxKind.ThrowStatement]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), + [SyntaxKind.TryStatement]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.tryBlock) || + visitNode(cbNode, data.catchClause) || + visitNode(cbNode, data.finallyBlock), + [SyntaxKind.CatchClause]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.variableDeclaration) || + visitNode(cbNode, data.block), + [SyntaxKind.LabeledStatement]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.label) || + visitNode(cbNode, data.statement), + [SyntaxKind.ExpressionStatement]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), + [SyntaxKind.Block]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.statements), + [SyntaxKind.VariableStatement]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.declarationList), + [SyntaxKind.VariableDeclaration]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.name) || + visitNode(cbNode, data.exclamationToken) || + visitNode(cbNode, data.type) || + visitNode(cbNode, data.initializer), + [SyntaxKind.VariableDeclarationList]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.declarations), + [SyntaxKind.Parameter]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.dotDotDotToken) || + visitNode(cbNode, data.name) || + visitNode(cbNode, data.questionToken) || + visitNode(cbNode, data.type) || + visitNode(cbNode, data.initializer), + [SyntaxKind.BindingElement]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.dotDotDotToken) || + visitNode(cbNode, data.propertyName) || + visitNode(cbNode, data.name) || + visitNode(cbNode, data.initializer), + [SyntaxKind.MissingDeclaration]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.modifiers), + [SyntaxKind.FunctionDeclaration]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.asteriskToken) || + visitNode(cbNode, data.name) || + visitNodes(cbNode, cbNodes, data.typeParameters) || + visitNodes(cbNode, cbNodes, data.parameters) || + visitNode(cbNode, data.type) || + visitNode(cbNode, data.body), + [SyntaxKind.ClassDeclaration]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.name) || + visitNodes(cbNode, cbNodes, data.typeParameters) || + visitNodes(cbNode, cbNodes, data.heritageClauses) || + visitNodes(cbNode, cbNodes, data.members), + [SyntaxKind.ClassExpression]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.name) || + visitNodes(cbNode, cbNodes, data.typeParameters) || + visitNodes(cbNode, cbNodes, data.heritageClauses) || + visitNodes(cbNode, cbNodes, data.members), + [SyntaxKind.HeritageClause]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.types), + [SyntaxKind.InterfaceDeclaration]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.name) || + visitNodes(cbNode, cbNodes, data.typeParameters) || + visitNodes(cbNode, cbNodes, data.heritageClauses) || + visitNodes(cbNode, cbNodes, data.members), + [SyntaxKind.TypeAliasDeclaration]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.name) || + visitNodes(cbNode, cbNodes, data.typeParameters) || + visitNode(cbNode, data.type), + [SyntaxKind.JSTypeAliasDeclaration]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.name) || + visitNodes(cbNode, cbNodes, data.typeParameters) || + visitNode(cbNode, data.type), + [SyntaxKind.EnumMember]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.name) || + visitNode(cbNode, data.initializer), + [SyntaxKind.EnumDeclaration]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.name) || + visitNodes(cbNode, cbNodes, data.members), + [SyntaxKind.ModuleBlock]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.statements), + [SyntaxKind.ImportDeclaration]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.importClause) || + visitNode(cbNode, data.moduleSpecifier) || + visitNode(cbNode, data.attributes), + [SyntaxKind.JSImportDeclaration]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.importClause) || + visitNode(cbNode, data.moduleSpecifier) || + visitNode(cbNode, data.attributes), + [SyntaxKind.ExternalModuleReference]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), + [SyntaxKind.NamespaceImport]: (data, cbNode, cbNodes) => visitNode(cbNode, data.name), + [SyntaxKind.NamedImports]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.elements), + [SyntaxKind.ExportAssignment]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.type) || + visitNode(cbNode, data.expression), + [SyntaxKind.NamespaceExportDeclaration]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.name), + [SyntaxKind.NamespaceExport]: (data, cbNode, cbNodes) => visitNode(cbNode, data.name), + [SyntaxKind.NamedExports]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.elements), + [SyntaxKind.ExportSpecifier]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.propertyName) || + visitNode(cbNode, data.name), + [SyntaxKind.CallSignature]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.typeParameters) || + visitNodes(cbNode, cbNodes, data.parameters) || + visitNode(cbNode, data.type), + [SyntaxKind.ConstructSignature]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.typeParameters) || + visitNodes(cbNode, cbNodes, data.parameters) || + visitNode(cbNode, data.type), + [SyntaxKind.Constructor]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNodes(cbNode, cbNodes, data.typeParameters) || + visitNodes(cbNode, cbNodes, data.parameters) || + visitNode(cbNode, data.type) || + visitNode(cbNode, data.body), + [SyntaxKind.GetAccessor]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.name) || + visitNodes(cbNode, cbNodes, data.typeParameters) || + visitNodes(cbNode, cbNodes, data.parameters) || + visitNode(cbNode, data.type) || + visitNode(cbNode, data.body), + [SyntaxKind.SetAccessor]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.name) || + visitNodes(cbNode, cbNodes, data.typeParameters) || + visitNodes(cbNode, cbNodes, data.parameters) || + visitNode(cbNode, data.type) || + visitNode(cbNode, data.body), + [SyntaxKind.IndexSignature]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNodes(cbNode, cbNodes, data.parameters) || + visitNode(cbNode, data.type), + [SyntaxKind.MethodSignature]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.name) || + visitNode(cbNode, data.postfixToken) || + visitNodes(cbNode, cbNodes, data.typeParameters) || + visitNodes(cbNode, cbNodes, data.parameters) || + visitNode(cbNode, data.type), + [SyntaxKind.MethodDeclaration]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.asteriskToken) || + visitNode(cbNode, data.name) || + visitNode(cbNode, data.postfixToken) || + visitNodes(cbNode, cbNodes, data.typeParameters) || + visitNodes(cbNode, cbNodes, data.parameters) || + visitNode(cbNode, data.type) || + visitNode(cbNode, data.body), + [SyntaxKind.PropertySignature]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.name) || + visitNode(cbNode, data.postfixToken) || + visitNode(cbNode, data.type) || + visitNode(cbNode, data.initializer), + [SyntaxKind.PropertyDeclaration]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.name) || + visitNode(cbNode, data.postfixToken) || + visitNode(cbNode, data.type) || + visitNode(cbNode, data.initializer), + [SyntaxKind.ClassStaticBlockDeclaration]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.body), + [SyntaxKind.BinaryExpression]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.left) || + visitNode(cbNode, data.type) || + visitNode(cbNode, data.operatorToken) || + visitNode(cbNode, data.right), + [SyntaxKind.PrefixUnaryExpression]: (data, cbNode, cbNodes) => visitNode(cbNode, data.operand), + [SyntaxKind.PostfixUnaryExpression]: (data, cbNode, cbNodes) => visitNode(cbNode, data.operand), + [SyntaxKind.YieldExpression]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.asteriskToken) || + visitNode(cbNode, data.expression), + [SyntaxKind.ArrowFunction]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNodes(cbNode, cbNodes, data.typeParameters) || + visitNodes(cbNode, cbNodes, data.parameters) || + visitNode(cbNode, data.type) || + visitNode(cbNode, data.equalsGreaterThanToken) || + visitNode(cbNode, data.body), + [SyntaxKind.FunctionExpression]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.asteriskToken) || + visitNode(cbNode, data.name) || + visitNodes(cbNode, cbNodes, data.typeParameters) || + visitNodes(cbNode, cbNodes, data.parameters) || + visitNode(cbNode, data.type) || + visitNode(cbNode, data.body), + [SyntaxKind.AsExpression]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.expression) || + visitNode(cbNode, data.type), + [SyntaxKind.SatisfiesExpression]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.expression) || + visitNode(cbNode, data.type), + [SyntaxKind.ConditionalExpression]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.condition) || + visitNode(cbNode, data.questionToken) || + visitNode(cbNode, data.whenTrue) || + visitNode(cbNode, data.colonToken) || + visitNode(cbNode, data.whenFalse), + [SyntaxKind.PropertyAccessExpression]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.expression) || + visitNode(cbNode, data.questionDotToken) || + visitNode(cbNode, data.name), + [SyntaxKind.ElementAccessExpression]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.expression) || + visitNode(cbNode, data.questionDotToken) || + visitNode(cbNode, data.argumentExpression), + [SyntaxKind.CallExpression]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.expression) || + visitNode(cbNode, data.questionDotToken) || + visitNodes(cbNode, cbNodes, data.typeArguments) || + visitNodes(cbNode, cbNodes, data.arguments), + [SyntaxKind.NewExpression]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.expression) || + visitNodes(cbNode, cbNodes, data.typeArguments) || + visitNodes(cbNode, cbNodes, data.arguments), + [SyntaxKind.MetaProperty]: (data, cbNode, cbNodes) => visitNode(cbNode, data.name), + [SyntaxKind.NonNullExpression]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), + [SyntaxKind.SpreadElement]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), + [SyntaxKind.TemplateExpression]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.head) || + visitNodes(cbNode, cbNodes, data.templateSpans), + [SyntaxKind.TemplateSpan]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.expression) || + visitNode(cbNode, data.literal), + [SyntaxKind.TaggedTemplateExpression]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.tag) || + visitNode(cbNode, data.questionDotToken) || + visitNodes(cbNode, cbNodes, data.typeArguments) || + visitNode(cbNode, data.template), + [SyntaxKind.ParenthesizedExpression]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), + [SyntaxKind.ArrayLiteralExpression]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.elements), + [SyntaxKind.ObjectLiteralExpression]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.properties), + [SyntaxKind.SpreadAssignment]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), + [SyntaxKind.PropertyAssignment]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.name) || + visitNode(cbNode, data.postfixToken) || + visitNode(cbNode, data.type) || + visitNode(cbNode, data.initializer), + [SyntaxKind.ShorthandPropertyAssignment]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.name) || + visitNode(cbNode, data.postfixToken) || + visitNode(cbNode, data.type) || + visitNode(cbNode, data.equalsToken) || + visitNode(cbNode, data.objectAssignmentInitializer), + [SyntaxKind.DeleteExpression]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), + [SyntaxKind.TypeOfExpression]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), + [SyntaxKind.VoidExpression]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), + [SyntaxKind.AwaitExpression]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), + [SyntaxKind.TypeAssertionExpression]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.type) || + visitNode(cbNode, data.expression), + [SyntaxKind.UnionType]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.types), + [SyntaxKind.IntersectionType]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.types), + [SyntaxKind.ConditionalType]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.checkType) || + visitNode(cbNode, data.extendsType) || + visitNode(cbNode, data.trueType) || + visitNode(cbNode, data.falseType), + [SyntaxKind.TypeOperator]: (data, cbNode, cbNodes) => visitNode(cbNode, data.type), + [SyntaxKind.InferType]: (data, cbNode, cbNodes) => visitNode(cbNode, data.typeParameter), + [SyntaxKind.ArrayType]: (data, cbNode, cbNodes) => visitNode(cbNode, data.elementType), + [SyntaxKind.IndexedAccessType]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.objectType) || + visitNode(cbNode, data.indexType), + [SyntaxKind.TypeReference]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.typeName) || + visitNodes(cbNode, cbNodes, data.typeArguments), + [SyntaxKind.ExpressionWithTypeArguments]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.expression) || + visitNodes(cbNode, cbNodes, data.typeArguments), + [SyntaxKind.LiteralType]: (data, cbNode, cbNodes) => visitNode(cbNode, data.literal), + [SyntaxKind.TypePredicate]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.assertsModifier) || + visitNode(cbNode, data.parameterName) || + visitNode(cbNode, data.type), + [SyntaxKind.ImportAttribute]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.name) || + visitNode(cbNode, data.value), + [SyntaxKind.ImportAttributes]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.attributes), + [SyntaxKind.TypeQuery]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.exprName) || + visitNodes(cbNode, cbNodes, data.typeArguments), + [SyntaxKind.MappedType]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.readonlyToken) || + visitNode(cbNode, data.typeParameter) || + visitNode(cbNode, data.nameType) || + visitNode(cbNode, data.questionToken) || + visitNode(cbNode, data.type) || + visitNodes(cbNode, cbNodes, data.members), + [SyntaxKind.TypeLiteral]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.members), + [SyntaxKind.TupleType]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.elements), + [SyntaxKind.NamedTupleMember]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.dotDotDotToken) || + visitNode(cbNode, data.name) || + visitNode(cbNode, data.questionToken) || + visitNode(cbNode, data.type), + [SyntaxKind.OptionalType]: (data, cbNode, cbNodes) => visitNode(cbNode, data.type), + [SyntaxKind.RestType]: (data, cbNode, cbNodes) => visitNode(cbNode, data.type), + [SyntaxKind.ParenthesizedType]: (data, cbNode, cbNodes) => visitNode(cbNode, data.type), + [SyntaxKind.FunctionType]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.typeParameters) || + visitNodes(cbNode, cbNodes, data.parameters) || + visitNode(cbNode, data.type), + [SyntaxKind.ConstructorType]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNodes(cbNode, cbNodes, data.typeParameters) || + visitNodes(cbNode, cbNodes, data.parameters) || + visitNode(cbNode, data.type), + [SyntaxKind.TemplateLiteralType]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.head) || + visitNodes(cbNode, cbNodes, data.templateSpans), + [SyntaxKind.TemplateLiteralTypeSpan]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.type) || + visitNode(cbNode, data.literal), + [SyntaxKind.SyntheticExpression]: (data, cbNode, cbNodes) => visitNode(cbNode, data.tupleNameSource), + [SyntaxKind.PartiallyEmittedExpression]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), + [SyntaxKind.JsxElement]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.openingElement) || + visitNodes(cbNode, cbNodes, data.children) || + visitNode(cbNode, data.closingElement), + [SyntaxKind.JsxAttributes]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.properties), + [SyntaxKind.JsxNamespacedName]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.namespace) || + visitNode(cbNode, data.name), + [SyntaxKind.JsxOpeningElement]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.tagName) || + visitNodes(cbNode, cbNodes, data.typeArguments) || + visitNode(cbNode, data.attributes), + [SyntaxKind.JsxSelfClosingElement]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.tagName) || + visitNodes(cbNode, cbNodes, data.typeArguments) || + visitNode(cbNode, data.attributes), + [SyntaxKind.JsxFragment]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.openingFragment) || + visitNodes(cbNode, cbNodes, data.children) || + visitNode(cbNode, data.closingFragment), + [SyntaxKind.JsxAttribute]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.name) || + visitNode(cbNode, data.initializer), + [SyntaxKind.JsxSpreadAttribute]: (data, cbNode, cbNodes) => visitNode(cbNode, data.expression), + [SyntaxKind.JsxClosingElement]: (data, cbNode, cbNodes) => visitNode(cbNode, data.tagName), + [SyntaxKind.JsxExpression]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.dotDotDotToken) || + visitNode(cbNode, data.expression), + [SyntaxKind.SyntaxList]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.children), + [SyntaxKind.JSDoc]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.comment) || + visitNodes(cbNode, cbNodes, data.tags), + [SyntaxKind.JSDocTypeExpression]: (data, cbNode, cbNodes) => visitNode(cbNode, data.type), + [SyntaxKind.JSDocNonNullableType]: (data, cbNode, cbNodes) => visitNode(cbNode, data.type), + [SyntaxKind.JSDocNullableType]: (data, cbNode, cbNodes) => visitNode(cbNode, data.type), + [SyntaxKind.JSDocVariadicType]: (data, cbNode, cbNodes) => visitNode(cbNode, data.type), + [SyntaxKind.JSDocOptionalType]: (data, cbNode, cbNodes) => visitNode(cbNode, data.type), + [SyntaxKind.JSDocTypeTag]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.tagName) || + visitNode(cbNode, data.typeExpression) || + visitNodes(cbNode, cbNodes, data.comment), + [SyntaxKind.JSDocUnknownTag]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.tagName) || + visitNodes(cbNode, cbNodes, data.comment), + [SyntaxKind.JSDocTemplateTag]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.tagName) || + visitNode(cbNode, data.constraint) || + visitNodes(cbNode, cbNodes, data.typeParameters) || + visitNodes(cbNode, cbNodes, data.comment), + [SyntaxKind.JSDocReturnTag]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.tagName) || + visitNode(cbNode, data.typeExpression) || + visitNodes(cbNode, cbNodes, data.comment), + [SyntaxKind.JSDocPublicTag]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.tagName) || + visitNodes(cbNode, cbNodes, data.comment), + [SyntaxKind.JSDocPrivateTag]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.tagName) || + visitNodes(cbNode, cbNodes, data.comment), + [SyntaxKind.JSDocProtectedTag]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.tagName) || + visitNodes(cbNode, cbNodes, data.comment), + [SyntaxKind.JSDocReadonlyTag]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.tagName) || + visitNodes(cbNode, cbNodes, data.comment), + [SyntaxKind.JSDocOverrideTag]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.tagName) || + visitNodes(cbNode, cbNodes, data.comment), + [SyntaxKind.JSDocDeprecatedTag]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.tagName) || + visitNodes(cbNode, cbNodes, data.comment), + [SyntaxKind.JSDocSeeTag]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.tagName) || + visitNode(cbNode, data.nameExpression) || + visitNodes(cbNode, cbNodes, data.comment), + [SyntaxKind.JSDocImplementsTag]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.tagName) || + visitNode(cbNode, data.className) || + visitNodes(cbNode, cbNodes, data.comment), + [SyntaxKind.JSDocAugmentsTag]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.tagName) || + visitNode(cbNode, data.className) || + visitNodes(cbNode, cbNodes, data.comment), + [SyntaxKind.JSDocSatisfiesTag]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.tagName) || + visitNode(cbNode, data.typeExpression) || + visitNodes(cbNode, cbNodes, data.comment), + [SyntaxKind.JSDocThrowsTag]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.tagName) || + visitNode(cbNode, data.typeExpression) || + visitNodes(cbNode, cbNodes, data.comment), + [SyntaxKind.JSDocThisTag]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.tagName) || + visitNode(cbNode, data.typeExpression) || + visitNodes(cbNode, cbNodes, data.comment), + [SyntaxKind.JSDocImportTag]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.tagName) || + visitNode(cbNode, data.importClause) || + visitNode(cbNode, data.moduleSpecifier) || + visitNode(cbNode, data.attributes) || + visitNodes(cbNode, cbNodes, data.comment), + [SyntaxKind.JSDocCallbackTag]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.tagName) || + visitNode(cbNode, data.typeExpression) || + visitNode(cbNode, data.name) || + visitNodes(cbNode, cbNodes, data.comment), + [SyntaxKind.JSDocOverloadTag]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.tagName) || + visitNode(cbNode, data.typeExpression) || + visitNodes(cbNode, cbNodes, data.comment), + [SyntaxKind.JSDocTypedefTag]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.tagName) || + visitNode(cbNode, data.typeExpression) || + visitNode(cbNode, data.name) || + visitNodes(cbNode, cbNodes, data.comment), + [SyntaxKind.JSDocSignature]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.typeParameters) || + visitNodes(cbNode, cbNodes, data.parameters) || + visitNode(cbNode, data.type), + [SyntaxKind.JSDocNameReference]: (data, cbNode, cbNodes) => visitNode(cbNode, data.name), + [SyntaxKind.ModuleDeclaration]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.name) || + visitNode(cbNode, data.body), + [SyntaxKind.ImportEqualsDeclaration]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.name) || + visitNode(cbNode, data.moduleReference), + [SyntaxKind.ExportDeclaration]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.exportClause) || + visitNode(cbNode, data.moduleSpecifier) || + visitNode(cbNode, data.attributes), + [SyntaxKind.ImportType]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.argument) || + visitNode(cbNode, data.attributes) || + visitNode(cbNode, data.qualifier) || + visitNodes(cbNode, cbNodes, data.typeArguments), + [SyntaxKind.ImportClause]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.name) || + visitNode(cbNode, data.namedBindings), + [SyntaxKind.ImportSpecifier]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.propertyName) || + visitNode(cbNode, data.name), + [SyntaxKind.JSDocLink]: (data, cbNode, cbNodes) => visitNode(cbNode, data.name), + [SyntaxKind.JSDocLinkPlain]: (data, cbNode, cbNodes) => visitNode(cbNode, data.name), + [SyntaxKind.JSDocLinkCode]: (data, cbNode, cbNodes) => visitNode(cbNode, data.name), + [SyntaxKind.TypeParameter]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.modifiers) || + visitNode(cbNode, data.name) || + visitNode(cbNode, data.constraint) || + visitNode(cbNode, data.expression) || + visitNode(cbNode, data.defaultType), + [SyntaxKind.SyntheticReferenceExpression]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.expression) || + visitNode(cbNode, data.thisArg), + [SyntaxKind.JSDocTypeLiteral]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.jsdocPropertyTags), + [SyntaxKind.ForInStatement]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.awaitModifier) || + visitNode(cbNode, data.initializer) || + visitNode(cbNode, data.expression) || + visitNode(cbNode, data.statement), + [SyntaxKind.ForOfStatement]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.awaitModifier) || + visitNode(cbNode, data.initializer) || + visitNode(cbNode, data.expression) || + visitNode(cbNode, data.statement), + [SyntaxKind.CaseClause]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.expression) || + visitNodes(cbNode, cbNodes, data.statements), + [SyntaxKind.DefaultClause]: (data, cbNode, cbNodes) => + visitNode(cbNode, data.expression) || + visitNodes(cbNode, cbNodes, data.statements), + [SyntaxKind.ObjectBindingPattern]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.elements), + [SyntaxKind.ArrayBindingPattern]: (data, cbNode, cbNodes) => visitNodes(cbNode, cbNodes, data.elements), + [SyntaxKind.JSDocParameterTag]: forEachChildOfJSDocParameterTag, + [SyntaxKind.JSDocPropertyTag]: forEachChildOfJSDocPropertyTag, + [SyntaxKind.SourceFile]: (data, cbNode, cbNodes) => + visitNodes(cbNode, cbNodes, data.statements) || + visitNode(cbNode, data.endOfFileToken), +}; + +function visitNode(cbNode: (node: Node) => T, node: Node | undefined): T | undefined { + return node ? cbNode(node) : undefined; +} + +function visitNodes(cbNode: (node: Node) => T, cbNodes: ((nodes: NodeArray) => T) | undefined, nodes: NodeArray | undefined): T | undefined { + if (!nodes) return undefined; + if (cbNodes) return cbNodes(nodes); + for (const node of nodes) { + const result = cbNode(node); + if (result) return result; + } + return undefined; +} + +export function createToken(kind: TKind): Token { + return new NodeObject(kind, undefined) as unknown as Token; +} + +export function createIdentifier(text: string): Identifier { + return new NodeObject(SyntaxKind.Identifier, { + text, + }) as unknown as Identifier; +} + +export function createPrivateIdentifier(text: string): PrivateIdentifier { + return new NodeObject(SyntaxKind.PrivateIdentifier, { + text, + }) as unknown as PrivateIdentifier; +} + +export function createQualifiedName(left: EntityName, right: MemberName): QualifiedName { + return new NodeObject(SyntaxKind.QualifiedName, { + left, + right, + }) as unknown as QualifiedName; +} + +export function createComputedPropertyName(expression: Expression): ComputedPropertyName { + return new NodeObject(SyntaxKind.ComputedPropertyName, { + expression, + }) as unknown as ComputedPropertyName; +} + +export function createDecorator(expression: LeftHandSideExpression): Decorator { + return new NodeObject(SyntaxKind.Decorator, { + expression, + }) as unknown as Decorator; +} + +export function createEmptyStatement(): EmptyStatement { + return new NodeObject(SyntaxKind.EmptyStatement, undefined) as unknown as EmptyStatement; +} + +export function createIfStatement(expression: Expression, thenStatement: Statement, elseStatement?: Statement): IfStatement { + return new NodeObject(SyntaxKind.IfStatement, { + expression, + thenStatement, + elseStatement, + }) as unknown as IfStatement; +} + +export function createDoStatement(statement: Statement, expression: Expression): DoStatement { + return new NodeObject(SyntaxKind.DoStatement, { + statement, + expression, + }) as unknown as DoStatement; +} + +export function createWhileStatement(expression: Expression, statement: Statement): WhileStatement { + return new NodeObject(SyntaxKind.WhileStatement, { + expression, + statement, + }) as unknown as WhileStatement; +} + +export function createForStatement(initializer: ForInitializer | undefined, condition: Expression | undefined, incrementor: Expression | undefined, statement: Statement): ForStatement { + return new NodeObject(SyntaxKind.ForStatement, { + initializer, + condition, + incrementor, + statement, + }) as unknown as ForStatement; +} + +export function createBreakStatement(label?: Identifier): BreakStatement { + return new NodeObject(SyntaxKind.BreakStatement, { + label, + }) as unknown as BreakStatement; +} + +export function createContinueStatement(label?: Identifier): ContinueStatement { + return new NodeObject(SyntaxKind.ContinueStatement, { + label, + }) as unknown as ContinueStatement; +} + +export function createReturnStatement(expression?: Expression): ReturnStatement { + return new NodeObject(SyntaxKind.ReturnStatement, { + expression, + }) as unknown as ReturnStatement; +} + +export function createWithStatement(expression: Expression, statement: Statement): WithStatement { + return new NodeObject(SyntaxKind.WithStatement, { + expression, + statement, + }) as unknown as WithStatement; +} + +export function createSwitchStatement(expression: Expression, caseBlock: CaseBlock): SwitchStatement { + return new NodeObject(SyntaxKind.SwitchStatement, { + expression, + caseBlock, + }) as unknown as SwitchStatement; +} + +export function createCaseBlock(clauses: readonly CaseOrDefaultClause[]): CaseBlock { + return new NodeObject(SyntaxKind.CaseBlock, { + clauses: createNodeArray(clauses), + }) as unknown as CaseBlock; +} + +export function createThrowStatement(expression: Expression): ThrowStatement { + return new NodeObject(SyntaxKind.ThrowStatement, { + expression, + }) as unknown as ThrowStatement; +} + +export function createTryStatement(tryBlock: Block, catchClause?: CatchClause, finallyBlock?: Block): TryStatement { + return new NodeObject(SyntaxKind.TryStatement, { + tryBlock, + catchClause, + finallyBlock, + }) as unknown as TryStatement; +} + +export function createCatchClause(variableDeclaration: VariableDeclaration | undefined, block: Block): CatchClause { + return new NodeObject(SyntaxKind.CatchClause, { + variableDeclaration, + block, + }) as unknown as CatchClause; +} + +export function createDebuggerStatement(): DebuggerStatement { + return new NodeObject(SyntaxKind.DebuggerStatement, undefined) as unknown as DebuggerStatement; +} + +export function createLabeledStatement(label: Identifier, statement: Statement): LabeledStatement { + return new NodeObject(SyntaxKind.LabeledStatement, { + label, + statement, + }) as unknown as LabeledStatement; +} + +export function createExpressionStatement(expression: Expression): ExpressionStatement { + return new NodeObject(SyntaxKind.ExpressionStatement, { + expression, + }) as unknown as ExpressionStatement; +} + +export function createBlock(statements: readonly Statement[], multiLine?: boolean): Block { + return new NodeObject(SyntaxKind.Block, { + statements: createNodeArray(statements), + multiLine, + }) as unknown as Block; +} + +export function createVariableStatement(modifiers: readonly ModifierLike[] | undefined, declarationList: VariableDeclarationList): VariableStatement { + return new NodeObject(SyntaxKind.VariableStatement, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + declarationList, + }) as unknown as VariableStatement; +} + +export function createVariableDeclaration(name: BindingName, exclamationToken?: ExclamationToken, type?: TypeNode, initializer?: Expression): VariableDeclaration { + return new NodeObject(SyntaxKind.VariableDeclaration, { + name, + exclamationToken, + type, + initializer, + }) as unknown as VariableDeclaration; +} + +export function createVariableDeclarationList(declarations: readonly VariableDeclaration[], flags: NodeFlags): VariableDeclarationList { + const node = new NodeObject(SyntaxKind.VariableDeclarationList, { + declarations: createNodeArray(declarations), + }) as unknown as VariableDeclarationList; + (node as any).flags = flags; + return node; +} + +export function createParameterDeclaration(modifiers: readonly ModifierLike[] | undefined, dotDotDotToken: DotDotDotToken | undefined, name: BindingName, questionToken?: QuestionToken, type?: TypeNode, initializer?: Expression): ParameterDeclaration { + return new NodeObject(SyntaxKind.Parameter, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + dotDotDotToken, + name, + questionToken, + type, + initializer, + }) as unknown as ParameterDeclaration; +} + +export function createBindingElement(dotDotDotToken?: DotDotDotToken, propertyName?: PropertyName, name?: BindingName, initializer?: Expression): BindingElement { + return new NodeObject(SyntaxKind.BindingElement, { + dotDotDotToken, + propertyName, + name, + initializer, + }) as unknown as BindingElement; +} + +export function createMissingDeclaration(modifiers?: readonly ModifierLike[]): MissingDeclaration { + return new NodeObject(SyntaxKind.MissingDeclaration, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + }) as unknown as MissingDeclaration; +} + +export function createFunctionDeclaration(modifiers: readonly ModifierLike[] | undefined, asteriskToken: AsteriskToken | undefined, name: Identifier | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode, body?: FunctionBody): FunctionDeclaration { + return new NodeObject(SyntaxKind.FunctionDeclaration, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + asteriskToken, + name, + typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, + parameters: createNodeArray(parameters), + type, + body, + }) as unknown as FunctionDeclaration; +} + +export function createClassDeclaration(modifiers: readonly ModifierLike[] | undefined, name: Identifier | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, heritageClauses: readonly HeritageClause[] | undefined, members: readonly ClassElement[]): ClassDeclaration { + return new NodeObject(SyntaxKind.ClassDeclaration, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + name, + typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, + heritageClauses: heritageClauses ? createNodeArray(heritageClauses) : undefined, + members: createNodeArray(members), + }) as unknown as ClassDeclaration; +} + +export function createClassExpression(modifiers: readonly ModifierLike[] | undefined, name: Identifier | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, heritageClauses: readonly HeritageClause[] | undefined, members: readonly ClassElement[]): ClassExpression { + return new NodeObject(SyntaxKind.ClassExpression, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + name, + typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, + heritageClauses: heritageClauses ? createNodeArray(heritageClauses) : undefined, + members: createNodeArray(members), + }) as unknown as ClassExpression; +} + +export function createHeritageClause(token: SyntaxKind.ExtendsKeyword | SyntaxKind.ImplementsKeyword, types: readonly HeritageClauseElement[]): HeritageClause { + return new NodeObject(SyntaxKind.HeritageClause, { + token, + types: createNodeArray(types), + }) as unknown as HeritageClause; +} + +export function createInterfaceDeclaration(modifiers: readonly ModifierLike[] | undefined, name: Identifier, typeParameters: readonly TypeParameterDeclaration[] | undefined, heritageClauses: readonly HeritageClause[] | undefined, members: readonly TypeElement[]): InterfaceDeclaration { + return new NodeObject(SyntaxKind.InterfaceDeclaration, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + name, + typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, + heritageClauses: heritageClauses ? createNodeArray(heritageClauses) : undefined, + members: createNodeArray(members), + }) as unknown as InterfaceDeclaration; +} + +export function createTypeAliasDeclaration(modifiers: readonly ModifierLike[] | undefined, name: Identifier, typeParameters: readonly TypeParameterDeclaration[] | undefined, type: TypeNode): TypeAliasDeclaration { + return new NodeObject(SyntaxKind.TypeAliasDeclaration, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + name, + typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, + type, + }) as unknown as TypeAliasDeclaration; +} + +export function createEnumMember(name: PropertyName, initializer?: Expression): EnumMember { + return new NodeObject(SyntaxKind.EnumMember, { + name, + initializer, + }) as unknown as EnumMember; +} + +export function createEnumDeclaration(modifiers: readonly ModifierLike[] | undefined, name: Identifier, members: readonly EnumMember[]): EnumDeclaration { + return new NodeObject(SyntaxKind.EnumDeclaration, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + name, + members: createNodeArray(members), + }) as unknown as EnumDeclaration; +} + +export function createModuleBlock(statements: readonly Statement[]): ModuleBlock { + return new NodeObject(SyntaxKind.ModuleBlock, { + statements: createNodeArray(statements), + }) as unknown as ModuleBlock; +} + +export function createNotEmittedStatement(): NotEmittedStatement { + return new NodeObject(SyntaxKind.NotEmittedStatement, undefined) as unknown as NotEmittedStatement; +} + +export function createNotEmittedTypeElement(): NotEmittedTypeElement { + return new NodeObject(SyntaxKind.NotEmittedTypeElement, undefined) as unknown as NotEmittedTypeElement; +} + +export function createImportDeclaration(modifiers: readonly ModifierLike[] | undefined, importClause: ImportClause | undefined, moduleSpecifier: Expression, attributes?: ImportAttributes): ImportDeclaration { + return new NodeObject(SyntaxKind.ImportDeclaration, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + importClause, + moduleSpecifier, + attributes, + }) as unknown as ImportDeclaration; +} + +export function createExternalModuleReference(expression: Expression): ExternalModuleReference { + return new NodeObject(SyntaxKind.ExternalModuleReference, { + expression, + }) as unknown as ExternalModuleReference; +} + +export function createNamespaceImport(name: Identifier): NamespaceImport { + return new NodeObject(SyntaxKind.NamespaceImport, { + name, + }) as unknown as NamespaceImport; +} + +export function createNamedImports(elements: readonly ImportSpecifier[]): NamedImports { + return new NodeObject(SyntaxKind.NamedImports, { + elements: createNodeArray(elements), + }) as unknown as NamedImports; +} + +export function createExportAssignment(modifiers: readonly ModifierLike[] | undefined, isExportEquals: boolean = false, type: TypeNode, expression: Expression): ExportAssignment { + return new NodeObject(SyntaxKind.ExportAssignment, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + isExportEquals, + type, + expression, + }) as unknown as ExportAssignment; +} + +export function createNamespaceExportDeclaration(modifiers: readonly ModifierLike[] | undefined, name: Identifier): NamespaceExportDeclaration { + return new NodeObject(SyntaxKind.NamespaceExportDeclaration, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + name, + }) as unknown as NamespaceExportDeclaration; +} + +export function createNamespaceExport(name: ModuleExportName): NamespaceExport { + return new NodeObject(SyntaxKind.NamespaceExport, { + name, + }) as unknown as NamespaceExport; +} + +export function createNamedExports(elements: readonly ExportSpecifier[]): NamedExports { + return new NodeObject(SyntaxKind.NamedExports, { + elements: createNodeArray(elements), + }) as unknown as NamedExports; +} + +export function createExportSpecifier(isTypeOnly: boolean = false, propertyName: ModuleExportName | undefined, name: ModuleExportName): ExportSpecifier { + return new NodeObject(SyntaxKind.ExportSpecifier, { + isTypeOnly, + propertyName, + name, + }) as unknown as ExportSpecifier; +} + +export function createCallSignatureDeclaration(typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode): CallSignatureDeclaration { + return new NodeObject(SyntaxKind.CallSignature, { + typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, + parameters: createNodeArray(parameters), + type, + }) as unknown as CallSignatureDeclaration; +} + +export function createConstructSignatureDeclaration(typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode): ConstructSignatureDeclaration { + return new NodeObject(SyntaxKind.ConstructSignature, { + typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, + parameters: createNodeArray(parameters), + type, + }) as unknown as ConstructSignatureDeclaration; +} + +export function createConstructorDeclaration(modifiers: readonly ModifierLike[] | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode, body?: FunctionBody): ConstructorDeclaration { + return new NodeObject(SyntaxKind.Constructor, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, + parameters: createNodeArray(parameters), + type, + body, + }) as unknown as ConstructorDeclaration; +} + +export function createGetAccessorDeclaration(modifiers: readonly ModifierLike[] | undefined, name: PropertyName, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode, body?: FunctionBody): GetAccessorDeclaration { + return new NodeObject(SyntaxKind.GetAccessor, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + name, + typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, + parameters: createNodeArray(parameters), + type, + body, + }) as unknown as GetAccessorDeclaration; +} + +export function createSetAccessorDeclaration(modifiers: readonly ModifierLike[] | undefined, name: PropertyName, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode, body?: FunctionBody): SetAccessorDeclaration { + return new NodeObject(SyntaxKind.SetAccessor, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + name, + typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, + parameters: createNodeArray(parameters), + type, + body, + }) as unknown as SetAccessorDeclaration; +} + +export function createIndexSignatureDeclaration(modifiers: readonly ModifierLike[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode): IndexSignatureDeclaration { + return new NodeObject(SyntaxKind.IndexSignature, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + parameters: createNodeArray(parameters), + type, + }) as unknown as IndexSignatureDeclaration; +} + +export function createMethodSignatureDeclaration(modifiers: readonly ModifierLike[] | undefined, name: PropertyName, postfixToken: QuestionToken | ExclamationToken | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode): MethodSignatureDeclaration { + return new NodeObject(SyntaxKind.MethodSignature, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + name, + postfixToken, + typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, + parameters: createNodeArray(parameters), + type, + }) as unknown as MethodSignatureDeclaration; +} + +export function createMethodDeclaration(modifiers: readonly ModifierLike[] | undefined, asteriskToken: AsteriskToken | undefined, name: PropertyName, postfixToken: QuestionToken | ExclamationToken | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode, body?: FunctionBody): MethodDeclaration { + return new NodeObject(SyntaxKind.MethodDeclaration, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + asteriskToken, + name, + postfixToken, + typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, + parameters: createNodeArray(parameters), + type, + body, + }) as unknown as MethodDeclaration; +} + +export function createPropertySignatureDeclaration(modifiers: readonly ModifierLike[] | undefined, name: PropertyName, postfixToken: QuestionToken | ExclamationToken | undefined, type: TypeNode, initializer: Expression): PropertySignatureDeclaration { + return new NodeObject(SyntaxKind.PropertySignature, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + name, + postfixToken, + type, + initializer, + }) as unknown as PropertySignatureDeclaration; +} + +export function createPropertyDeclaration(modifiers: readonly ModifierLike[] | undefined, name: PropertyName, postfixToken?: QuestionToken | ExclamationToken, type?: TypeNode, initializer?: Expression): PropertyDeclaration { + return new NodeObject(SyntaxKind.PropertyDeclaration, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + name, + postfixToken, + type, + initializer, + }) as unknown as PropertyDeclaration; +} + +export function createSemicolonClassElement(): SemicolonClassElement { + return new NodeObject(SyntaxKind.SemicolonClassElement, undefined) as unknown as SemicolonClassElement; +} + +export function createClassStaticBlockDeclaration(modifiers: readonly ModifierLike[] | undefined, body: Block): ClassStaticBlockDeclaration { + return new NodeObject(SyntaxKind.ClassStaticBlockDeclaration, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + body, + }) as unknown as ClassStaticBlockDeclaration; +} + +export function createOmittedExpression(): OmittedExpression { + return new NodeObject(SyntaxKind.OmittedExpression, undefined) as unknown as OmittedExpression; +} + +export function createKeywordExpression(kind: TKind): KeywordExpression { + return new NodeObject(kind, undefined) as unknown as KeywordExpression; +} + +export function createStringLiteral(text: string, tokenFlags: TokenFlags): StringLiteral { + return new NodeObject(SyntaxKind.StringLiteral, { + text, + tokenFlags, + }) as unknown as StringLiteral; +} + +export function createNumericLiteral(text: string, tokenFlags: TokenFlags): NumericLiteral { + return new NodeObject(SyntaxKind.NumericLiteral, { + text, + tokenFlags, + }) as unknown as NumericLiteral; +} + +export function createBigIntLiteral(text: string, tokenFlags: TokenFlags): BigIntLiteral { + return new NodeObject(SyntaxKind.BigIntLiteral, { + text, + tokenFlags, + }) as unknown as BigIntLiteral; +} + +export function createRegularExpressionLiteral(text: string, tokenFlags: TokenFlags): RegularExpressionLiteral { + return new NodeObject(SyntaxKind.RegularExpressionLiteral, { + text, + tokenFlags, + }) as unknown as RegularExpressionLiteral; +} + +export function createNoSubstitutionTemplateLiteral(text: string, templateFlags: TokenFlags): NoSubstitutionTemplateLiteral { + return new NodeObject(SyntaxKind.NoSubstitutionTemplateLiteral, { + text, + templateFlags, + }) as unknown as NoSubstitutionTemplateLiteral; +} + +export function createBinaryExpression(modifiers: readonly ModifierLike[] | undefined, left: Expression, type: TypeNode | undefined, operatorToken: BinaryOperatorToken, right: Expression): BinaryExpression { + return new NodeObject(SyntaxKind.BinaryExpression, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + left, + type, + operatorToken, + right, + }) as unknown as BinaryExpression; +} + +export function createPrefixUnaryExpression(operator: SyntaxKind.PlusToken | SyntaxKind.MinusToken | SyntaxKind.TildeToken | SyntaxKind.ExclamationToken | SyntaxKind.PlusPlusToken | SyntaxKind.MinusMinusToken, operand: Expression): PrefixUnaryExpression { + return new NodeObject(SyntaxKind.PrefixUnaryExpression, { + operator, + operand, + }) as unknown as PrefixUnaryExpression; +} + +export function createPostfixUnaryExpression(operand: Expression, operator: SyntaxKind.PlusPlusToken | SyntaxKind.MinusMinusToken): PostfixUnaryExpression { + return new NodeObject(SyntaxKind.PostfixUnaryExpression, { + operand, + operator, + }) as unknown as PostfixUnaryExpression; +} + +export function createYieldExpression(asteriskToken?: AsteriskToken, expression?: Expression): YieldExpression { + return new NodeObject(SyntaxKind.YieldExpression, { + asteriskToken, + expression, + }) as unknown as YieldExpression; +} + +export function createArrowFunction(modifiers: readonly ModifierLike[] | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined, equalsGreaterThanToken: EqualsGreaterThanToken, body: ConciseBody): ArrowFunction { + return new NodeObject(SyntaxKind.ArrowFunction, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, + parameters: createNodeArray(parameters), + type, + equalsGreaterThanToken, + body, + }) as unknown as ArrowFunction; +} + +export function createFunctionExpression(modifiers: readonly ModifierLike[] | undefined, asteriskToken: AsteriskToken | undefined, name: Identifier | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined, body: FunctionBody): FunctionExpression { + return new NodeObject(SyntaxKind.FunctionExpression, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + asteriskToken, + name, + typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, + parameters: createNodeArray(parameters), + type, + body, + }) as unknown as FunctionExpression; +} + +export function createAsExpression(expression: Expression, type: TypeNode): AsExpression { + return new NodeObject(SyntaxKind.AsExpression, { + expression, + type, + }) as unknown as AsExpression; +} + +export function createSatisfiesExpression(expression: Expression, type: TypeNode): SatisfiesExpression { + return new NodeObject(SyntaxKind.SatisfiesExpression, { + expression, + type, + }) as unknown as SatisfiesExpression; +} + +export function createConditionalExpression(condition: Expression, questionToken: QuestionToken, whenTrue: Expression, colonToken: ColonToken, whenFalse: Expression): ConditionalExpression { + return new NodeObject(SyntaxKind.ConditionalExpression, { + condition, + questionToken, + whenTrue, + colonToken, + whenFalse, + }) as unknown as ConditionalExpression; +} + +export function createPropertyAccessExpression(expression: Expression, questionDotToken: QuestionDotToken | undefined, name: MemberName, flags: NodeFlags): PropertyAccessExpression { + const node = new NodeObject(SyntaxKind.PropertyAccessExpression, { + expression, + questionDotToken, + name, + }) as unknown as PropertyAccessExpression; + (node as any).flags = flags; + return node; +} + +export function createElementAccessExpression(expression: Expression, questionDotToken: QuestionDotToken | undefined, argumentExpression: Expression, flags: NodeFlags): ElementAccessExpression { + const node = new NodeObject(SyntaxKind.ElementAccessExpression, { + expression, + questionDotToken, + argumentExpression, + }) as unknown as ElementAccessExpression; + (node as any).flags = flags; + return node; +} + +export function createCallExpression(expression: Expression, questionDotToken: QuestionDotToken | undefined, typeArguments: readonly TypeNode[] | undefined, arguments_: readonly Expression[], flags: NodeFlags): CallExpression { + const node = new NodeObject(SyntaxKind.CallExpression, { + expression, + questionDotToken, + typeArguments: typeArguments ? createNodeArray(typeArguments) : undefined, + arguments: createNodeArray(arguments_), + }) as unknown as CallExpression; + (node as any).flags = flags; + return node; +} + +export function createNewExpression(expression: Expression, typeArguments?: readonly TypeNode[], arguments_?: readonly Expression[]): NewExpression { + return new NodeObject(SyntaxKind.NewExpression, { + expression, + typeArguments: typeArguments ? createNodeArray(typeArguments) : undefined, + arguments: arguments_ ? createNodeArray(arguments_) : undefined, + }) as unknown as NewExpression; +} + +export function createMetaProperty(keywordToken: SyntaxKind.ImportKeyword | SyntaxKind.NewKeyword, name: Identifier): MetaProperty { + return new NodeObject(SyntaxKind.MetaProperty, { + keywordToken, + name, + }) as unknown as MetaProperty; +} + +export function createNonNullExpression(expression: Expression, flags: NodeFlags): NonNullExpression { + const node = new NodeObject(SyntaxKind.NonNullExpression, { + expression, + }) as unknown as NonNullExpression; + (node as any).flags = flags; + return node; +} + +export function createSpreadElement(expression: Expression): SpreadElement { + return new NodeObject(SyntaxKind.SpreadElement, { + expression, + }) as unknown as SpreadElement; +} + +export function createTemplateExpression(head: TemplateHead, templateSpans: readonly TemplateSpan[]): TemplateExpression { + return new NodeObject(SyntaxKind.TemplateExpression, { + head, + templateSpans: createNodeArray(templateSpans), + }) as unknown as TemplateExpression; +} + +export function createTemplateSpan(expression: Expression, literal: TemplateMiddleOrTail): TemplateSpan { + return new NodeObject(SyntaxKind.TemplateSpan, { + expression, + literal, + }) as unknown as TemplateSpan; +} + +export function createTaggedTemplateExpression(tag: Expression, questionDotToken: QuestionDotToken, typeArguments: readonly TypeNode[] | undefined, template: TemplateLiteral, flags: NodeFlags): TaggedTemplateExpression { + const node = new NodeObject(SyntaxKind.TaggedTemplateExpression, { + tag, + questionDotToken, + typeArguments: typeArguments ? createNodeArray(typeArguments) : undefined, + template, + }) as unknown as TaggedTemplateExpression; + (node as any).flags = flags; + return node; +} + +export function createParenthesizedExpression(expression: Expression): ParenthesizedExpression { + return new NodeObject(SyntaxKind.ParenthesizedExpression, { + expression, + }) as unknown as ParenthesizedExpression; +} + +export function createArrayLiteralExpression(elements: readonly Expression[], multiLine?: boolean): ArrayLiteralExpression { + return new NodeObject(SyntaxKind.ArrayLiteralExpression, { + elements: createNodeArray(elements), + multiLine, + }) as unknown as ArrayLiteralExpression; +} + +export function createObjectLiteralExpression(properties: readonly ObjectLiteralElementLike[], multiLine?: boolean): ObjectLiteralExpression { + return new NodeObject(SyntaxKind.ObjectLiteralExpression, { + properties: createNodeArray(properties), + multiLine, + }) as unknown as ObjectLiteralExpression; +} + +export function createSpreadAssignment(expression: Expression): SpreadAssignment { + return new NodeObject(SyntaxKind.SpreadAssignment, { + expression, + }) as unknown as SpreadAssignment; +} + +export function createPropertyAssignment(modifiers: readonly ModifierLike[] | undefined, name: PropertyName, postfixToken: QuestionToken | ExclamationToken | undefined, type: TypeNode, initializer: Expression): PropertyAssignment { + return new NodeObject(SyntaxKind.PropertyAssignment, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + name, + postfixToken, + type, + initializer, + }) as unknown as PropertyAssignment; +} + +export function createShorthandPropertyAssignment(modifiers: readonly ModifierLike[] | undefined, name: PropertyName, postfixToken: QuestionToken | ExclamationToken | undefined, type: TypeNode, equalsToken?: EqualsToken, objectAssignmentInitializer?: Expression): ShorthandPropertyAssignment { + return new NodeObject(SyntaxKind.ShorthandPropertyAssignment, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + name, + postfixToken, + type, + equalsToken, + objectAssignmentInitializer, + }) as unknown as ShorthandPropertyAssignment; +} + +export function createDeleteExpression(expression: Expression): DeleteExpression { + return new NodeObject(SyntaxKind.DeleteExpression, { + expression, + }) as unknown as DeleteExpression; +} + +export function createTypeOfExpression(expression: Expression): TypeOfExpression { + return new NodeObject(SyntaxKind.TypeOfExpression, { + expression, + }) as unknown as TypeOfExpression; +} + +export function createVoidExpression(expression: Expression): VoidExpression { + return new NodeObject(SyntaxKind.VoidExpression, { + expression, + }) as unknown as VoidExpression; +} + +export function createAwaitExpression(expression: Expression): AwaitExpression { + return new NodeObject(SyntaxKind.AwaitExpression, { + expression, + }) as unknown as AwaitExpression; +} + +export function createTypeAssertion(type: TypeNode, expression: Expression): TypeAssertion { + return new NodeObject(SyntaxKind.TypeAssertionExpression, { + type, + expression, + }) as unknown as TypeAssertion; +} + +export function createKeywordTypeNode(kind: TKind): KeywordTypeNode { + return new NodeObject(kind, undefined) as unknown as KeywordTypeNode; +} + +export function createUnionTypeNode(types: readonly TypeNode[]): UnionTypeNode { + return new NodeObject(SyntaxKind.UnionType, { + types: createNodeArray(types), + }) as unknown as UnionTypeNode; +} + +export function createIntersectionTypeNode(types: readonly TypeNode[]): IntersectionTypeNode { + return new NodeObject(SyntaxKind.IntersectionType, { + types: createNodeArray(types), + }) as unknown as IntersectionTypeNode; +} + +export function createConditionalTypeNode(checkType: TypeNode, extendsType: TypeNode, trueType: TypeNode, falseType: TypeNode): ConditionalTypeNode { + return new NodeObject(SyntaxKind.ConditionalType, { + checkType, + extendsType, + trueType, + falseType, + }) as unknown as ConditionalTypeNode; +} + +export function createTypeOperatorNode(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.ReadonlyKeyword | SyntaxKind.UniqueKeyword, type: TypeNode): TypeOperatorNode { + return new NodeObject(SyntaxKind.TypeOperator, { + operator, + type, + }) as unknown as TypeOperatorNode; +} + +export function createInferTypeNode(typeParameter: TypeParameterDeclaration): InferTypeNode { + return new NodeObject(SyntaxKind.InferType, { + typeParameter, + }) as unknown as InferTypeNode; +} + +export function createArrayTypeNode(elementType: TypeNode): ArrayTypeNode { + return new NodeObject(SyntaxKind.ArrayType, { + elementType, + }) as unknown as ArrayTypeNode; +} + +export function createIndexedAccessTypeNode(objectType: TypeNode, indexType: TypeNode): IndexedAccessTypeNode { + return new NodeObject(SyntaxKind.IndexedAccessType, { + objectType, + indexType, + }) as unknown as IndexedAccessTypeNode; +} + +export function createTypeReferenceNode(typeName: EntityName, typeArguments?: readonly TypeNode[]): TypeReferenceNode { + return new NodeObject(SyntaxKind.TypeReference, { + typeName, + typeArguments: typeArguments ? createNodeArray(typeArguments) : undefined, + }) as unknown as TypeReferenceNode; +} + +export function createExpressionWithTypeArguments(expression: Expression, typeArguments?: readonly TypeNode[]): ExpressionWithTypeArguments { + return new NodeObject(SyntaxKind.ExpressionWithTypeArguments, { + expression, + typeArguments: typeArguments ? createNodeArray(typeArguments) : undefined, + }) as unknown as ExpressionWithTypeArguments; +} + +export function createLiteralTypeNode(literal: Node): LiteralTypeNode { + return new NodeObject(SyntaxKind.LiteralType, { + literal, + }) as unknown as LiteralTypeNode; +} + +export function createThisTypeNode(): ThisTypeNode { + return new NodeObject(SyntaxKind.ThisType, undefined) as unknown as ThisTypeNode; +} + +export function createTypePredicateNode(assertsModifier: AssertsKeyword | undefined, parameterName: TypePredicateParameterName, type?: TypeNode): TypePredicateNode { + return new NodeObject(SyntaxKind.TypePredicate, { + assertsModifier, + parameterName, + type, + }) as unknown as TypePredicateNode; +} + +export function createImportAttribute(name: ImportAttributeName, value: Expression): ImportAttribute { + return new NodeObject(SyntaxKind.ImportAttribute, { + name, + value, + }) as unknown as ImportAttribute; +} + +export function createImportAttributes(token: SyntaxKind.WithKeyword | SyntaxKind.AssertKeyword, attributes: readonly ImportAttribute[], multiLine?: boolean): ImportAttributes { + return new NodeObject(SyntaxKind.ImportAttributes, { + token, + attributes: createNodeArray(attributes), + multiLine, + }) as unknown as ImportAttributes; +} + +export function createTypeQueryNode(exprName: EntityName, typeArguments?: readonly TypeNode[]): TypeQueryNode { + return new NodeObject(SyntaxKind.TypeQuery, { + exprName, + typeArguments: typeArguments ? createNodeArray(typeArguments) : undefined, + }) as unknown as TypeQueryNode; +} + +export function createMappedTypeNode(readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType?: TypeNode, questionToken?: QuestionToken | PlusToken | MinusToken, type?: TypeNode, members?: readonly TypeElement[]): MappedTypeNode { + return new NodeObject(SyntaxKind.MappedType, { + readonlyToken, + typeParameter, + nameType, + questionToken, + type, + members: members ? createNodeArray(members) : undefined, + }) as unknown as MappedTypeNode; +} + +export function createTypeLiteralNode(members: readonly TypeElement[]): TypeLiteralNode { + return new NodeObject(SyntaxKind.TypeLiteral, { + members: createNodeArray(members), + }) as unknown as TypeLiteralNode; +} + +export function createTupleTypeNode(elements: readonly TypeNode[]): TupleTypeNode { + return new NodeObject(SyntaxKind.TupleType, { + elements: createNodeArray(elements), + }) as unknown as TupleTypeNode; +} + +export function createNamedTupleMember(dotDotDotToken: DotDotDotToken | undefined, name: Identifier, questionToken: QuestionToken | undefined, type: TypeNode): NamedTupleMember { + return new NodeObject(SyntaxKind.NamedTupleMember, { + dotDotDotToken, + name, + questionToken, + type, + }) as unknown as NamedTupleMember; +} + +export function createOptionalTypeNode(type: TypeNode): OptionalTypeNode { + return new NodeObject(SyntaxKind.OptionalType, { + type, + }) as unknown as OptionalTypeNode; +} + +export function createRestTypeNode(type: TypeNode): RestTypeNode { + return new NodeObject(SyntaxKind.RestType, { + type, + }) as unknown as RestTypeNode; +} + +export function createParenthesizedTypeNode(type: TypeNode): ParenthesizedTypeNode { + return new NodeObject(SyntaxKind.ParenthesizedType, { + type, + }) as unknown as ParenthesizedTypeNode; +} + +export function createFunctionTypeNode(typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode): FunctionTypeNode { + return new NodeObject(SyntaxKind.FunctionType, { + typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, + parameters: createNodeArray(parameters), + type, + }) as unknown as FunctionTypeNode; +} + +export function createConstructorTypeNode(modifiers: readonly ModifierLike[] | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode): ConstructorTypeNode { + return new NodeObject(SyntaxKind.ConstructorType, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, + parameters: createNodeArray(parameters), + type, + }) as unknown as ConstructorTypeNode; +} + +export function createTemplateHead(text: string, rawText: string, templateFlags: TokenFlags): TemplateHead { + return new NodeObject(SyntaxKind.TemplateHead, { + text, + rawText, + templateFlags, + }) as unknown as TemplateHead; +} + +export function createTemplateMiddle(text: string, rawText: string, templateFlags: TokenFlags): TemplateMiddle { + return new NodeObject(SyntaxKind.TemplateMiddle, { + text, + rawText, + templateFlags, + }) as unknown as TemplateMiddle; +} + +export function createTemplateTail(text: string, rawText: string, templateFlags: TokenFlags): TemplateTail { + return new NodeObject(SyntaxKind.TemplateTail, { + text, + rawText, + templateFlags, + }) as unknown as TemplateTail; +} + +export function createTemplateLiteralTypeNode(head: TemplateHead, templateSpans: readonly TemplateLiteralTypeSpan[]): TemplateLiteralTypeNode { + return new NodeObject(SyntaxKind.TemplateLiteralType, { + head, + templateSpans: createNodeArray(templateSpans), + }) as unknown as TemplateLiteralTypeNode; +} + +export function createTemplateLiteralTypeSpan(type: TypeNode, literal: TemplateMiddleOrTail): TemplateLiteralTypeSpan { + return new NodeObject(SyntaxKind.TemplateLiteralTypeSpan, { + type, + literal, + }) as unknown as TemplateLiteralTypeSpan; +} + +export function createSyntheticExpression(type: any, isSpread?: boolean, tupleNameSource?: Node): SyntheticExpression { + return new NodeObject(SyntaxKind.SyntheticExpression, { + type, + isSpread, + tupleNameSource, + }) as unknown as SyntheticExpression; +} + +export function createPartiallyEmittedExpression(expression: Expression): PartiallyEmittedExpression { + return new NodeObject(SyntaxKind.PartiallyEmittedExpression, { + expression, + }) as unknown as PartiallyEmittedExpression; +} + +export function createJsxElement(openingElement: JsxOpeningElement, children: readonly JsxChild[], closingElement: JsxClosingElement): JsxElement { + return new NodeObject(SyntaxKind.JsxElement, { + openingElement, + children: createNodeArray(children), + closingElement, + }) as unknown as JsxElement; +} + +export function createJsxAttributes(properties: readonly JsxAttributeLike[]): JsxAttributes { + return new NodeObject(SyntaxKind.JsxAttributes, { + properties: createNodeArray(properties), + }) as unknown as JsxAttributes; +} + +export function createJsxNamespacedName(namespace: Identifier, name: Identifier): JsxNamespacedName { + return new NodeObject(SyntaxKind.JsxNamespacedName, { + namespace, + name, + }) as unknown as JsxNamespacedName; +} + +export function createJsxOpeningElement(tagName: JsxTagNameExpression, typeArguments: readonly TypeNode[] | undefined, attributes: JsxAttributes): JsxOpeningElement { + return new NodeObject(SyntaxKind.JsxOpeningElement, { + tagName, + typeArguments: typeArguments ? createNodeArray(typeArguments) : undefined, + attributes, + }) as unknown as JsxOpeningElement; +} + +export function createJsxSelfClosingElement(tagName: JsxTagNameExpression, typeArguments: readonly TypeNode[] | undefined, attributes: JsxAttributes): JsxSelfClosingElement { + return new NodeObject(SyntaxKind.JsxSelfClosingElement, { + tagName, + typeArguments: typeArguments ? createNodeArray(typeArguments) : undefined, + attributes, + }) as unknown as JsxSelfClosingElement; +} + +export function createJsxFragment(openingFragment: JsxOpeningFragment, children: readonly JsxChild[], closingFragment: JsxClosingFragment): JsxFragment { + return new NodeObject(SyntaxKind.JsxFragment, { + openingFragment, + children: createNodeArray(children), + closingFragment, + }) as unknown as JsxFragment; +} + +export function createJsxOpeningFragment(): JsxOpeningFragment { + return new NodeObject(SyntaxKind.JsxOpeningFragment, undefined) as unknown as JsxOpeningFragment; +} + +export function createJsxClosingFragment(): JsxClosingFragment { + return new NodeObject(SyntaxKind.JsxClosingFragment, undefined) as unknown as JsxClosingFragment; +} + +export function createJsxAttribute(name: JsxAttributeName, initializer?: JsxAttributeValue): JsxAttribute { + return new NodeObject(SyntaxKind.JsxAttribute, { + name, + initializer, + }) as unknown as JsxAttribute; +} + +export function createJsxSpreadAttribute(expression: Expression): JsxSpreadAttribute { + return new NodeObject(SyntaxKind.JsxSpreadAttribute, { + expression, + }) as unknown as JsxSpreadAttribute; +} + +export function createJsxClosingElement(tagName: JsxTagNameExpression): JsxClosingElement { + return new NodeObject(SyntaxKind.JsxClosingElement, { + tagName, + }) as unknown as JsxClosingElement; +} + +export function createJsxExpression(dotDotDotToken?: DotDotDotToken, expression?: Expression): JsxExpression { + return new NodeObject(SyntaxKind.JsxExpression, { + dotDotDotToken, + expression, + }) as unknown as JsxExpression; +} + +export function createJsxText(text: string, containsOnlyTriviaWhiteSpaces?: boolean): JsxText { + return new NodeObject(SyntaxKind.JsxText, { + text, + containsOnlyTriviaWhiteSpaces, + }) as unknown as JsxText; +} + +export function createSyntaxList(children: readonly Node[]): SyntaxList { + return new NodeObject(SyntaxKind.SyntaxList, { + children, + }) as unknown as SyntaxList; +} + +export function createJSDoc(comment: readonly JSDocComment[], tags?: readonly JSDocTag[]): JSDoc { + return new NodeObject(SyntaxKind.JSDoc, { + comment: createNodeArray(comment), + tags: tags ? createNodeArray(tags) : undefined, + }) as unknown as JSDoc; +} + +export function createJSDocTypeExpression(type: TypeNode): JSDocTypeExpression { + return new NodeObject(SyntaxKind.JSDocTypeExpression, { + type, + }) as unknown as JSDocTypeExpression; +} + +export function createJSDocNonNullableType(type: TypeNode): JSDocNonNullableType { + return new NodeObject(SyntaxKind.JSDocNonNullableType, { + type, + }) as unknown as JSDocNonNullableType; +} + +export function createJSDocNullableType(type: TypeNode): JSDocNullableType { + return new NodeObject(SyntaxKind.JSDocNullableType, { + type, + }) as unknown as JSDocNullableType; +} + +export function createJSDocAllType(): JSDocAllType { + return new NodeObject(SyntaxKind.JSDocAllType, undefined) as unknown as JSDocAllType; +} + +export function createJSDocVariadicType(type: TypeNode): JSDocVariadicType { + return new NodeObject(SyntaxKind.JSDocVariadicType, { + type, + }) as unknown as JSDocVariadicType; +} + +export function createJSDocOptionalType(type: TypeNode): JSDocOptionalType { + return new NodeObject(SyntaxKind.JSDocOptionalType, { + type, + }) as unknown as JSDocOptionalType; +} + +export function createJSDocTypeTag(tagName: Identifier, typeExpression: Node, comment?: readonly JSDocComment[]): JSDocTypeTag { + return new NodeObject(SyntaxKind.JSDocTypeTag, { + tagName, + typeExpression, + comment: comment ? createNodeArray(comment) : undefined, + }) as unknown as JSDocTypeTag; +} + +export function createJSDocUnknownTag(tagName: Identifier, comment?: readonly JSDocComment[]): JSDocUnknownTag { + return new NodeObject(SyntaxKind.JSDocUnknownTag, { + tagName, + comment: comment ? createNodeArray(comment) : undefined, + }) as unknown as JSDocUnknownTag; +} + +export function createJSDocTemplateTag(tagName: Identifier, constraint: Node, typeParameters: readonly TypeParameterDeclaration[], comment?: readonly JSDocComment[]): JSDocTemplateTag { + return new NodeObject(SyntaxKind.JSDocTemplateTag, { + tagName, + constraint, + typeParameters: createNodeArray(typeParameters), + comment: comment ? createNodeArray(comment) : undefined, + }) as unknown as JSDocTemplateTag; +} + +export function createJSDocReturnTag(tagName: Identifier, typeExpression?: TypeNode, comment?: readonly JSDocComment[]): JSDocReturnTag { + return new NodeObject(SyntaxKind.JSDocReturnTag, { + tagName, + typeExpression, + comment: comment ? createNodeArray(comment) : undefined, + }) as unknown as JSDocReturnTag; +} + +export function createJSDocPublicTag(tagName: Identifier, comment?: readonly JSDocComment[]): JSDocPublicTag { + return new NodeObject(SyntaxKind.JSDocPublicTag, { + tagName, + comment: comment ? createNodeArray(comment) : undefined, + }) as unknown as JSDocPublicTag; +} + +export function createJSDocPrivateTag(tagName: Identifier, comment?: readonly JSDocComment[]): JSDocPrivateTag { + return new NodeObject(SyntaxKind.JSDocPrivateTag, { + tagName, + comment: comment ? createNodeArray(comment) : undefined, + }) as unknown as JSDocPrivateTag; +} + +export function createJSDocProtectedTag(tagName: Identifier, comment?: readonly JSDocComment[]): JSDocProtectedTag { + return new NodeObject(SyntaxKind.JSDocProtectedTag, { + tagName, + comment: comment ? createNodeArray(comment) : undefined, + }) as unknown as JSDocProtectedTag; +} + +export function createJSDocReadonlyTag(tagName: Identifier, comment?: readonly JSDocComment[]): JSDocReadonlyTag { + return new NodeObject(SyntaxKind.JSDocReadonlyTag, { + tagName, + comment: comment ? createNodeArray(comment) : undefined, + }) as unknown as JSDocReadonlyTag; +} + +export function createJSDocOverrideTag(tagName: Identifier, comment?: readonly JSDocComment[]): JSDocOverrideTag { + return new NodeObject(SyntaxKind.JSDocOverrideTag, { + tagName, + comment: comment ? createNodeArray(comment) : undefined, + }) as unknown as JSDocOverrideTag; +} + +export function createJSDocDeprecatedTag(tagName: Identifier, comment?: readonly JSDocComment[]): JSDocDeprecatedTag { + return new NodeObject(SyntaxKind.JSDocDeprecatedTag, { + tagName, + comment: comment ? createNodeArray(comment) : undefined, + }) as unknown as JSDocDeprecatedTag; +} + +export function createJSDocSeeTag(tagName: Identifier, nameExpression: TypeNode, comment?: readonly JSDocComment[]): JSDocSeeTag { + return new NodeObject(SyntaxKind.JSDocSeeTag, { + tagName, + nameExpression, + comment: comment ? createNodeArray(comment) : undefined, + }) as unknown as JSDocSeeTag; +} + +export function createJSDocImplementsTag(tagName: Identifier, className: ExpressionWithTypeArguments, comment?: readonly JSDocComment[]): JSDocImplementsTag { + return new NodeObject(SyntaxKind.JSDocImplementsTag, { + tagName, + className, + comment: comment ? createNodeArray(comment) : undefined, + }) as unknown as JSDocImplementsTag; +} + +export function createJSDocAugmentsTag(tagName: Identifier, className: ExpressionWithTypeArguments, comment?: readonly JSDocComment[]): JSDocAugmentsTag { + return new NodeObject(SyntaxKind.JSDocAugmentsTag, { + tagName, + className, + comment: comment ? createNodeArray(comment) : undefined, + }) as unknown as JSDocAugmentsTag; +} + +export function createJSDocSatisfiesTag(tagName: Identifier, typeExpression: TypeNode, comment?: readonly JSDocComment[]): JSDocSatisfiesTag { + return new NodeObject(SyntaxKind.JSDocSatisfiesTag, { + tagName, + typeExpression, + comment: comment ? createNodeArray(comment) : undefined, + }) as unknown as JSDocSatisfiesTag; +} + +export function createJSDocThrowsTag(tagName: Identifier, typeExpression?: TypeNode, comment?: readonly JSDocComment[]): JSDocThrowsTag { + return new NodeObject(SyntaxKind.JSDocThrowsTag, { + tagName, + typeExpression, + comment: comment ? createNodeArray(comment) : undefined, + }) as unknown as JSDocThrowsTag; +} + +export function createJSDocThisTag(tagName: Identifier, typeExpression: TypeNode, comment?: readonly JSDocComment[]): JSDocThisTag { + return new NodeObject(SyntaxKind.JSDocThisTag, { + tagName, + typeExpression, + comment: comment ? createNodeArray(comment) : undefined, + }) as unknown as JSDocThisTag; +} + +export function createJSDocImportTag(tagName: Identifier, importClause: ImportClause | undefined, moduleSpecifier: Expression, attributes?: ImportAttributes, comment?: readonly JSDocComment[]): JSDocImportTag { + return new NodeObject(SyntaxKind.JSDocImportTag, { + tagName, + importClause, + moduleSpecifier, + attributes, + comment: comment ? createNodeArray(comment) : undefined, + }) as unknown as JSDocImportTag; +} + +export function createJSDocCallbackTag(tagName: Identifier, typeExpression: TypeNode, name?: JSDocFullName, comment?: readonly JSDocComment[]): JSDocCallbackTag { + return new NodeObject(SyntaxKind.JSDocCallbackTag, { + tagName, + typeExpression, + name, + comment: comment ? createNodeArray(comment) : undefined, + }) as unknown as JSDocCallbackTag; +} + +export function createJSDocOverloadTag(tagName: Identifier, typeExpression: TypeNode, comment?: readonly JSDocComment[]): JSDocOverloadTag { + return new NodeObject(SyntaxKind.JSDocOverloadTag, { + tagName, + typeExpression, + comment: comment ? createNodeArray(comment) : undefined, + }) as unknown as JSDocOverloadTag; +} + +export function createJSDocTypedefTag(tagName: Identifier, typeExpression?: Node, name?: JSDocFullName, comment?: readonly JSDocComment[]): JSDocTypedefTag { + return new NodeObject(SyntaxKind.JSDocTypedefTag, { + tagName, + typeExpression, + name, + comment: comment ? createNodeArray(comment) : undefined, + }) as unknown as JSDocTypedefTag; +} + +export function createJSDocSignature(typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode): JSDocSignature { + return new NodeObject(SyntaxKind.JSDocSignature, { + typeParameters: typeParameters ? createNodeArray(typeParameters) : undefined, + parameters: createNodeArray(parameters), + type, + }) as unknown as JSDocSignature; +} + +export function createJSDocNameReference(name: EntityName): JSDocNameReference { + return new NodeObject(SyntaxKind.JSDocNameReference, { + name, + }) as unknown as JSDocNameReference; +} + +export function createModuleDeclaration(modifiers: readonly ModifierLike[] | undefined, keyword: SyntaxKind.ModuleKeyword | SyntaxKind.NamespaceKeyword, name: ModuleName, body?: ModuleBody): ModuleDeclaration { + return new NodeObject(SyntaxKind.ModuleDeclaration, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + keyword, + name, + body, + }) as unknown as ModuleDeclaration; +} + +export function createImportEqualsDeclaration(modifiers: readonly ModifierLike[] | undefined, isTypeOnly: boolean = false, name: Identifier, moduleReference: ModuleReference): ImportEqualsDeclaration { + return new NodeObject(SyntaxKind.ImportEqualsDeclaration, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + isTypeOnly, + name, + moduleReference, + }) as unknown as ImportEqualsDeclaration; +} + +export function createExportDeclaration(modifiers?: readonly ModifierLike[], isTypeOnly?: boolean, exportClause?: NamedExportBindings, moduleSpecifier?: Expression, attributes?: ImportAttributes): ExportDeclaration { + return new NodeObject(SyntaxKind.ExportDeclaration, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + isTypeOnly, + exportClause, + moduleSpecifier, + attributes, + }) as unknown as ExportDeclaration; +} + +export function createImportTypeNode(isTypeOf: boolean = false, argument: TypeNode, attributes?: ImportAttributes, qualifier?: EntityName, typeArguments?: readonly TypeNode[]): ImportTypeNode { + return new NodeObject(SyntaxKind.ImportType, { + isTypeOf, + argument, + attributes, + qualifier, + typeArguments: typeArguments ? createNodeArray(typeArguments) : undefined, + }) as unknown as ImportTypeNode; +} + +export function createImportClause(phaseModifier?: ImportPhaseModifierSyntaxKind, name?: Identifier, namedBindings?: NamedImportBindings): ImportClause { + return new NodeObject(SyntaxKind.ImportClause, { + phaseModifier, + name, + namedBindings, + }) as unknown as ImportClause; +} + +export function createImportSpecifier(isTypeOnly: boolean = false, propertyName: ModuleExportName | undefined, name: Identifier): ImportSpecifier { + return new NodeObject(SyntaxKind.ImportSpecifier, { + isTypeOnly, + propertyName, + name, + }) as unknown as ImportSpecifier; +} + +export function createJSDocText(text: string): JSDocText { + return new NodeObject(SyntaxKind.JSDocText, { + text, + }) as unknown as JSDocText; +} + +export function createJSDocLink(name: EntityName | undefined, text: string): JSDocLink { + return new NodeObject(SyntaxKind.JSDocLink, { + name, + text, + }) as unknown as JSDocLink; +} + +export function createJSDocLinkPlain(name: EntityName | undefined, text: string): JSDocLinkPlain { + return new NodeObject(SyntaxKind.JSDocLinkPlain, { + name, + text, + }) as unknown as JSDocLinkPlain; +} + +export function createJSDocLinkCode(name: EntityName | undefined, text: string): JSDocLinkCode { + return new NodeObject(SyntaxKind.JSDocLinkCode, { + name, + text, + }) as unknown as JSDocLinkCode; +} + +export function createTypeParameterDeclaration(modifiers: readonly ModifierLike[] | undefined, name: Identifier, constraint?: TypeNode, expression?: Expression, defaultType?: TypeNode): TypeParameterDeclaration { + return new NodeObject(SyntaxKind.TypeParameter, { + modifiers: modifiers ? createNodeArray(modifiers) : undefined, + name, + constraint, + expression, + defaultType, + }) as unknown as TypeParameterDeclaration; +} + +export function createSyntheticReferenceExpression(expression: Expression, thisArg: Expression): SyntheticReferenceExpression { + return new NodeObject(SyntaxKind.SyntheticReferenceExpression, { + expression, + thisArg, + }) as unknown as SyntheticReferenceExpression; +} + +export function createJSDocTypeLiteral(jsdocPropertyTags?: readonly JSDocTag[], isArrayType?: boolean): JSDocTypeLiteral { + return new NodeObject(SyntaxKind.JSDocTypeLiteral, { + jsdocPropertyTags, + isArrayType, + }) as unknown as JSDocTypeLiteral; +} + +export function createForInStatement(awaitModifier: AwaitKeyword | undefined, initializer: ForInitializer, expression: Expression, statement: Statement): ForInStatement { + return new NodeObject(SyntaxKind.ForInStatement, { + awaitModifier, + initializer, + expression, + statement, + }) as unknown as ForInStatement; +} + +export function createForOfStatement(awaitModifier: AwaitKeyword | undefined, initializer: ForInitializer, expression: Expression, statement: Statement): ForOfStatement { + return new NodeObject(SyntaxKind.ForOfStatement, { + awaitModifier, + initializer, + expression, + statement, + }) as unknown as ForOfStatement; +} + +export function createCaseClause(expression: Expression, statements: readonly Statement[]): CaseClause { + return new NodeObject(SyntaxKind.CaseClause, { + expression, + statements: createNodeArray(statements), + }) as unknown as CaseClause; +} + +export function createDefaultClause(expression: Expression, statements: readonly Statement[]): DefaultClause { + return new NodeObject(SyntaxKind.DefaultClause, { + expression, + statements: createNodeArray(statements), + }) as unknown as DefaultClause; +} + +export function createObjectBindingPattern(elements: readonly BindingElement[]): ObjectBindingPattern { + return new NodeObject(SyntaxKind.ObjectBindingPattern, { + elements: createNodeArray(elements), + }) as unknown as ObjectBindingPattern; +} + +export function createArrayBindingPattern(elements: readonly BindingElement[]): ArrayBindingPattern { + return new NodeObject(SyntaxKind.ArrayBindingPattern, { + elements: createNodeArray(elements), + }) as unknown as ArrayBindingPattern; +} + +export function createJSDocParameterTag(tagName: Identifier, name: EntityName, isBracketed: boolean, typeExpression: TypeNode | undefined, isNameFirst: boolean, comment: readonly JSDocComment[] | undefined): JSDocParameterTag { + return new NodeObject(SyntaxKind.JSDocParameterTag, { + tagName, + name, + isBracketed, + typeExpression, + isNameFirst, + comment: comment ? createNodeArray(comment) : undefined, + }) as unknown as JSDocParameterTag; +} + +export function createJSDocPropertyTag(tagName: Identifier, name: EntityName, isBracketed: boolean, typeExpression: TypeNode | undefined, isNameFirst: boolean, comment: readonly JSDocComment[] | undefined): JSDocPropertyTag { + return new NodeObject(SyntaxKind.JSDocPropertyTag, { + tagName, + name, + isBracketed, + typeExpression, + isNameFirst, + comment: comment ? createNodeArray(comment) : undefined, + }) as unknown as JSDocPropertyTag; +} + +export function updateQualifiedName(node: QualifiedName, left: EntityName, right: MemberName): QualifiedName { + return node.left !== left || node.right !== right ? createQualifiedName(left, right) : node; +} + +export function updateComputedPropertyName(node: ComputedPropertyName, expression: Expression): ComputedPropertyName { + return node.expression !== expression ? createComputedPropertyName(expression) : node; +} + +export function updateDecorator(node: Decorator, expression: LeftHandSideExpression): Decorator { + return node.expression !== expression ? createDecorator(expression) : node; +} + +export function updateIfStatement(node: IfStatement, expression: Expression, thenStatement: Statement, elseStatement?: Statement): IfStatement { + return node.expression !== expression || node.thenStatement !== thenStatement || node.elseStatement !== elseStatement ? createIfStatement(expression, thenStatement, elseStatement) : node; +} + +export function updateDoStatement(node: DoStatement, statement: Statement, expression: Expression): DoStatement { + return node.statement !== statement || node.expression !== expression ? createDoStatement(statement, expression) : node; +} + +export function updateWhileStatement(node: WhileStatement, expression: Expression, statement: Statement): WhileStatement { + return node.expression !== expression || node.statement !== statement ? createWhileStatement(expression, statement) : node; +} + +export function updateForStatement(node: ForStatement, initializer: ForInitializer | undefined, condition: Expression | undefined, incrementor: Expression | undefined, statement: Statement): ForStatement { + return node.initializer !== initializer || node.condition !== condition || node.incrementor !== incrementor || node.statement !== statement ? createForStatement(initializer, condition, incrementor, statement) : node; +} + +export function updateBreakStatement(node: BreakStatement, label?: Identifier): BreakStatement { + return node.label !== label ? createBreakStatement(label) : node; +} + +export function updateContinueStatement(node: ContinueStatement, label?: Identifier): ContinueStatement { + return node.label !== label ? createContinueStatement(label) : node; +} + +export function updateReturnStatement(node: ReturnStatement, expression?: Expression): ReturnStatement { + return node.expression !== expression ? createReturnStatement(expression) : node; +} + +export function updateWithStatement(node: WithStatement, expression: Expression, statement: Statement): WithStatement { + return node.expression !== expression || node.statement !== statement ? createWithStatement(expression, statement) : node; +} + +export function updateSwitchStatement(node: SwitchStatement, expression: Expression, caseBlock: CaseBlock): SwitchStatement { + return node.expression !== expression || node.caseBlock !== caseBlock ? createSwitchStatement(expression, caseBlock) : node; +} + +export function updateCaseBlock(node: CaseBlock, clauses: readonly CaseOrDefaultClause[]): CaseBlock { + return node.clauses !== clauses ? createCaseBlock(clauses) : node; +} + +export function updateThrowStatement(node: ThrowStatement, expression: Expression): ThrowStatement { + return node.expression !== expression ? createThrowStatement(expression) : node; +} + +export function updateTryStatement(node: TryStatement, tryBlock: Block, catchClause?: CatchClause, finallyBlock?: Block): TryStatement { + return node.tryBlock !== tryBlock || node.catchClause !== catchClause || node.finallyBlock !== finallyBlock ? createTryStatement(tryBlock, catchClause, finallyBlock) : node; +} + +export function updateCatchClause(node: CatchClause, variableDeclaration: VariableDeclaration | undefined, block: Block): CatchClause { + return node.variableDeclaration !== variableDeclaration || node.block !== block ? createCatchClause(variableDeclaration, block) : node; +} + +export function updateLabeledStatement(node: LabeledStatement, label: Identifier, statement: Statement): LabeledStatement { + return node.label !== label || node.statement !== statement ? createLabeledStatement(label, statement) : node; +} + +export function updateExpressionStatement(node: ExpressionStatement, expression: Expression): ExpressionStatement { + return node.expression !== expression ? createExpressionStatement(expression) : node; +} + +export function updateBlock(node: Block, statements: readonly Statement[]): Block { + return node.statements !== statements ? createBlock(statements, node.multiLine) : node; +} + +export function updateVariableStatement(node: VariableStatement, modifiers: readonly ModifierLike[] | undefined, declarationList: VariableDeclarationList): VariableStatement { + return node.modifiers !== modifiers || node.declarationList !== declarationList ? createVariableStatement(modifiers, declarationList) : node; +} + +export function updateVariableDeclaration(node: VariableDeclaration, name: BindingName, exclamationToken?: ExclamationToken, type?: TypeNode, initializer?: Expression): VariableDeclaration { + return node.name !== name || node.exclamationToken !== exclamationToken || node.type !== type || node.initializer !== initializer ? createVariableDeclaration(name, exclamationToken, type, initializer) : node; +} + +export function updateVariableDeclarationList(node: VariableDeclarationList, declarations: readonly VariableDeclaration[]): VariableDeclarationList { + return node.declarations !== declarations ? createVariableDeclarationList(declarations, node.flags) : node; +} + +export function updateParameterDeclaration(node: ParameterDeclaration, modifiers: readonly ModifierLike[] | undefined, dotDotDotToken: DotDotDotToken | undefined, name: BindingName, questionToken?: QuestionToken, type?: TypeNode, initializer?: Expression): ParameterDeclaration { + return node.modifiers !== modifiers || node.dotDotDotToken !== dotDotDotToken || node.name !== name || node.questionToken !== questionToken || node.type !== type || node.initializer !== initializer ? createParameterDeclaration(modifiers, dotDotDotToken, name, questionToken, type, initializer) : node; +} + +export function updateBindingElement(node: BindingElement, dotDotDotToken?: DotDotDotToken, propertyName?: PropertyName, name?: BindingName, initializer?: Expression): BindingElement { + return node.dotDotDotToken !== dotDotDotToken || node.propertyName !== propertyName || node.name !== name || node.initializer !== initializer ? createBindingElement(dotDotDotToken, propertyName, name, initializer) : node; +} + +export function updateMissingDeclaration(node: MissingDeclaration, modifiers?: readonly ModifierLike[]): MissingDeclaration { + return node.modifiers !== modifiers ? createMissingDeclaration(modifiers) : node; +} + +export function updateFunctionDeclaration(node: FunctionDeclaration, modifiers: readonly ModifierLike[] | undefined, asteriskToken: AsteriskToken | undefined, name: Identifier | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode, body?: FunctionBody): FunctionDeclaration { + return node.modifiers !== modifiers || node.asteriskToken !== asteriskToken || node.name !== name || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type || node.body !== body ? createFunctionDeclaration(modifiers, asteriskToken, name, typeParameters, parameters, type, body) : node; +} + +export function updateClassDeclaration(node: ClassDeclaration, modifiers: readonly ModifierLike[] | undefined, name: Identifier | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, heritageClauses: readonly HeritageClause[] | undefined, members: readonly ClassElement[]): ClassDeclaration { + return node.modifiers !== modifiers || node.name !== name || node.typeParameters !== typeParameters || node.heritageClauses !== heritageClauses || node.members !== members ? createClassDeclaration(modifiers, name, typeParameters, heritageClauses, members) : node; +} + +export function updateClassExpression(node: ClassExpression, modifiers: readonly ModifierLike[] | undefined, name: Identifier | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, heritageClauses: readonly HeritageClause[] | undefined, members: readonly ClassElement[]): ClassExpression { + return node.modifiers !== modifiers || node.name !== name || node.typeParameters !== typeParameters || node.heritageClauses !== heritageClauses || node.members !== members ? createClassExpression(modifiers, name, typeParameters, heritageClauses, members) : node; +} + +export function updateHeritageClause(node: HeritageClause, types: readonly HeritageClauseElement[]): HeritageClause { + return node.types !== types ? createHeritageClause(node.token, types) : node; +} + +export function updateInterfaceDeclaration(node: InterfaceDeclaration, modifiers: readonly ModifierLike[] | undefined, name: Identifier, typeParameters: readonly TypeParameterDeclaration[] | undefined, heritageClauses: readonly HeritageClause[] | undefined, members: readonly TypeElement[]): InterfaceDeclaration { + return node.modifiers !== modifiers || node.name !== name || node.typeParameters !== typeParameters || node.heritageClauses !== heritageClauses || node.members !== members ? createInterfaceDeclaration(modifiers, name, typeParameters, heritageClauses, members) : node; +} + +export function updateTypeAliasDeclaration(node: TypeAliasDeclaration, modifiers: readonly ModifierLike[] | undefined, name: Identifier, typeParameters: readonly TypeParameterDeclaration[] | undefined, type: TypeNode): TypeAliasDeclaration { + return node.modifiers !== modifiers || node.name !== name || node.typeParameters !== typeParameters || node.type !== type ? createTypeAliasDeclaration(modifiers, name, typeParameters, type) : node; +} + +export function updateEnumMember(node: EnumMember, name: PropertyName, initializer?: Expression): EnumMember { + return node.name !== name || node.initializer !== initializer ? createEnumMember(name, initializer) : node; +} + +export function updateEnumDeclaration(node: EnumDeclaration, modifiers: readonly ModifierLike[] | undefined, name: Identifier, members: readonly EnumMember[]): EnumDeclaration { + return node.modifiers !== modifiers || node.name !== name || node.members !== members ? createEnumDeclaration(modifiers, name, members) : node; +} + +export function updateModuleBlock(node: ModuleBlock, statements: readonly Statement[]): ModuleBlock { + return node.statements !== statements ? createModuleBlock(statements) : node; +} + +export function updateImportDeclaration(node: ImportDeclaration, modifiers: readonly ModifierLike[] | undefined, importClause: ImportClause | undefined, moduleSpecifier: Expression, attributes?: ImportAttributes): ImportDeclaration { + return node.modifiers !== modifiers || node.importClause !== importClause || node.moduleSpecifier !== moduleSpecifier || node.attributes !== attributes ? createImportDeclaration(modifiers, importClause, moduleSpecifier, attributes) : node; +} + +export function updateExternalModuleReference(node: ExternalModuleReference, expression: Expression): ExternalModuleReference { + return node.expression !== expression ? createExternalModuleReference(expression) : node; +} + +export function updateNamespaceImport(node: NamespaceImport, name: Identifier): NamespaceImport { + return node.name !== name ? createNamespaceImport(name) : node; +} + +export function updateNamedImports(node: NamedImports, elements: readonly ImportSpecifier[]): NamedImports { + return node.elements !== elements ? createNamedImports(elements) : node; +} + +export function updateExportAssignment(node: ExportAssignment, modifiers: readonly ModifierLike[] | undefined, type: TypeNode, expression: Expression): ExportAssignment { + return node.modifiers !== modifiers || node.type !== type || node.expression !== expression ? createExportAssignment(modifiers, node.isExportEquals, type, expression) : node; +} + +export function updateNamespaceExportDeclaration(node: NamespaceExportDeclaration, modifiers: readonly ModifierLike[] | undefined, name: Identifier): NamespaceExportDeclaration { + return node.modifiers !== modifiers || node.name !== name ? createNamespaceExportDeclaration(modifiers, name) : node; +} + +export function updateNamespaceExport(node: NamespaceExport, name: ModuleExportName): NamespaceExport { + return node.name !== name ? createNamespaceExport(name) : node; +} + +export function updateNamedExports(node: NamedExports, elements: readonly ExportSpecifier[]): NamedExports { + return node.elements !== elements ? createNamedExports(elements) : node; +} + +export function updateExportSpecifier(node: ExportSpecifier, propertyName: ModuleExportName | undefined, name: ModuleExportName): ExportSpecifier { + return node.propertyName !== propertyName || node.name !== name ? createExportSpecifier(node.isTypeOnly, propertyName, name) : node; +} + +export function updateCallSignatureDeclaration(node: CallSignatureDeclaration, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode): CallSignatureDeclaration { + return node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type ? createCallSignatureDeclaration(typeParameters, parameters, type) : node; +} + +export function updateConstructSignatureDeclaration(node: ConstructSignatureDeclaration, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode): ConstructSignatureDeclaration { + return node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type ? createConstructSignatureDeclaration(typeParameters, parameters, type) : node; +} + +export function updateConstructorDeclaration(node: ConstructorDeclaration, modifiers: readonly ModifierLike[] | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode, body?: FunctionBody): ConstructorDeclaration { + return node.modifiers !== modifiers || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type || node.body !== body ? createConstructorDeclaration(modifiers, typeParameters, parameters, type, body) : node; +} + +export function updateGetAccessorDeclaration(node: GetAccessorDeclaration, modifiers: readonly ModifierLike[] | undefined, name: PropertyName, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode, body?: FunctionBody): GetAccessorDeclaration { + return node.modifiers !== modifiers || node.name !== name || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type || node.body !== body ? createGetAccessorDeclaration(modifiers, name, typeParameters, parameters, type, body) : node; +} + +export function updateSetAccessorDeclaration(node: SetAccessorDeclaration, modifiers: readonly ModifierLike[] | undefined, name: PropertyName, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode, body?: FunctionBody): SetAccessorDeclaration { + return node.modifiers !== modifiers || node.name !== name || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type || node.body !== body ? createSetAccessorDeclaration(modifiers, name, typeParameters, parameters, type, body) : node; +} + +export function updateIndexSignatureDeclaration(node: IndexSignatureDeclaration, modifiers: readonly ModifierLike[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode): IndexSignatureDeclaration { + return node.modifiers !== modifiers || node.parameters !== parameters || node.type !== type ? createIndexSignatureDeclaration(modifiers, parameters, type) : node; +} + +export function updateMethodSignatureDeclaration(node: MethodSignatureDeclaration, modifiers: readonly ModifierLike[] | undefined, name: PropertyName, postfixToken: QuestionToken | ExclamationToken | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode): MethodSignatureDeclaration { + return node.modifiers !== modifiers || node.name !== name || node.postfixToken !== postfixToken || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type ? createMethodSignatureDeclaration(modifiers, name, postfixToken, typeParameters, parameters, type) : node; +} + +export function updateMethodDeclaration(node: MethodDeclaration, modifiers: readonly ModifierLike[] | undefined, asteriskToken: AsteriskToken | undefined, name: PropertyName, postfixToken: QuestionToken | ExclamationToken | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode, body?: FunctionBody): MethodDeclaration { + return node.modifiers !== modifiers || node.asteriskToken !== asteriskToken || node.name !== name || node.postfixToken !== postfixToken || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type || node.body !== body ? createMethodDeclaration(modifiers, asteriskToken, name, postfixToken, typeParameters, parameters, type, body) : node; +} + +export function updatePropertySignatureDeclaration(node: PropertySignatureDeclaration, modifiers: readonly ModifierLike[] | undefined, name: PropertyName, postfixToken: QuestionToken | ExclamationToken | undefined, type: TypeNode, initializer: Expression): PropertySignatureDeclaration { + return node.modifiers !== modifiers || node.name !== name || node.postfixToken !== postfixToken || node.type !== type || node.initializer !== initializer ? createPropertySignatureDeclaration(modifiers, name, postfixToken, type, initializer) : node; +} + +export function updatePropertyDeclaration(node: PropertyDeclaration, modifiers: readonly ModifierLike[] | undefined, name: PropertyName, postfixToken?: QuestionToken | ExclamationToken, type?: TypeNode, initializer?: Expression): PropertyDeclaration { + return node.modifiers !== modifiers || node.name !== name || node.postfixToken !== postfixToken || node.type !== type || node.initializer !== initializer ? createPropertyDeclaration(modifiers, name, postfixToken, type, initializer) : node; +} + +export function updateClassStaticBlockDeclaration(node: ClassStaticBlockDeclaration, modifiers: readonly ModifierLike[] | undefined, body: Block): ClassStaticBlockDeclaration { + return node.modifiers !== modifiers || node.body !== body ? createClassStaticBlockDeclaration(modifiers, body) : node; +} + +export function updateBinaryExpression(node: BinaryExpression, modifiers: readonly ModifierLike[] | undefined, left: Expression, type: TypeNode | undefined, operatorToken: BinaryOperatorToken, right: Expression): BinaryExpression { + return node.modifiers !== modifiers || node.left !== left || node.type !== type || node.operatorToken !== operatorToken || node.right !== right ? createBinaryExpression(modifiers, left, type, operatorToken, right) : node; +} + +export function updatePrefixUnaryExpression(node: PrefixUnaryExpression, operand: Expression): PrefixUnaryExpression { + return node.operand !== operand ? createPrefixUnaryExpression(node.operator, operand) : node; +} + +export function updatePostfixUnaryExpression(node: PostfixUnaryExpression, operand: Expression): PostfixUnaryExpression { + return node.operand !== operand ? createPostfixUnaryExpression(operand, node.operator) : node; +} + +export function updateYieldExpression(node: YieldExpression, asteriskToken?: AsteriskToken, expression?: Expression): YieldExpression { + return node.asteriskToken !== asteriskToken || node.expression !== expression ? createYieldExpression(asteriskToken, expression) : node; +} + +export function updateArrowFunction(node: ArrowFunction, modifiers: readonly ModifierLike[] | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined, equalsGreaterThanToken: EqualsGreaterThanToken, body: ConciseBody): ArrowFunction { + return node.modifiers !== modifiers || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type || node.equalsGreaterThanToken !== equalsGreaterThanToken || node.body !== body ? createArrowFunction(modifiers, typeParameters, parameters, type, equalsGreaterThanToken, body) : node; +} + +export function updateFunctionExpression(node: FunctionExpression, modifiers: readonly ModifierLike[] | undefined, asteriskToken: AsteriskToken | undefined, name: Identifier | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined, body: FunctionBody): FunctionExpression { + return node.modifiers !== modifiers || node.asteriskToken !== asteriskToken || node.name !== name || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type || node.body !== body ? createFunctionExpression(modifiers, asteriskToken, name, typeParameters, parameters, type, body) : node; +} + +export function updateAsExpression(node: AsExpression, expression: Expression, type: TypeNode): AsExpression { + return node.expression !== expression || node.type !== type ? createAsExpression(expression, type) : node; +} + +export function updateSatisfiesExpression(node: SatisfiesExpression, expression: Expression, type: TypeNode): SatisfiesExpression { + return node.expression !== expression || node.type !== type ? createSatisfiesExpression(expression, type) : node; +} + +export function updateConditionalExpression(node: ConditionalExpression, condition: Expression, questionToken: QuestionToken, whenTrue: Expression, colonToken: ColonToken, whenFalse: Expression): ConditionalExpression { + return node.condition !== condition || node.questionToken !== questionToken || node.whenTrue !== whenTrue || node.colonToken !== colonToken || node.whenFalse !== whenFalse ? createConditionalExpression(condition, questionToken, whenTrue, colonToken, whenFalse) : node; +} + +export function updatePropertyAccessExpression(node: PropertyAccessExpression, expression: Expression, questionDotToken: QuestionDotToken | undefined, name: MemberName): PropertyAccessExpression { + return node.expression !== expression || node.questionDotToken !== questionDotToken || node.name !== name ? createPropertyAccessExpression(expression, questionDotToken, name, node.flags) : node; +} + +export function updateElementAccessExpression(node: ElementAccessExpression, expression: Expression, questionDotToken: QuestionDotToken | undefined, argumentExpression: Expression): ElementAccessExpression { + return node.expression !== expression || node.questionDotToken !== questionDotToken || node.argumentExpression !== argumentExpression ? createElementAccessExpression(expression, questionDotToken, argumentExpression, node.flags) : node; +} + +export function updateCallExpression(node: CallExpression, expression: Expression, questionDotToken: QuestionDotToken | undefined, typeArguments: readonly TypeNode[] | undefined, arguments_: readonly Expression[]): CallExpression { + return node.expression !== expression || node.questionDotToken !== questionDotToken || node.typeArguments !== typeArguments || node.arguments !== arguments_ ? createCallExpression(expression, questionDotToken, typeArguments, arguments_, node.flags) : node; +} + +export function updateNewExpression(node: NewExpression, expression: Expression, typeArguments?: readonly TypeNode[], arguments_?: readonly Expression[]): NewExpression { + return node.expression !== expression || node.typeArguments !== typeArguments || node.arguments !== arguments_ ? createNewExpression(expression, typeArguments, arguments_) : node; +} + +export function updateMetaProperty(node: MetaProperty, name: Identifier): MetaProperty { + return node.name !== name ? createMetaProperty(node.keywordToken, name) : node; +} + +export function updateNonNullExpression(node: NonNullExpression, expression: Expression): NonNullExpression { + return node.expression !== expression ? createNonNullExpression(expression, node.flags) : node; +} + +export function updateSpreadElement(node: SpreadElement, expression: Expression): SpreadElement { + return node.expression !== expression ? createSpreadElement(expression) : node; +} + +export function updateTemplateExpression(node: TemplateExpression, head: TemplateHead, templateSpans: readonly TemplateSpan[]): TemplateExpression { + return node.head !== head || node.templateSpans !== templateSpans ? createTemplateExpression(head, templateSpans) : node; +} + +export function updateTemplateSpan(node: TemplateSpan, expression: Expression, literal: TemplateMiddleOrTail): TemplateSpan { + return node.expression !== expression || node.literal !== literal ? createTemplateSpan(expression, literal) : node; +} + +export function updateTaggedTemplateExpression(node: TaggedTemplateExpression, tag: Expression, questionDotToken: QuestionDotToken, typeArguments: readonly TypeNode[] | undefined, template: TemplateLiteral): TaggedTemplateExpression { + return node.tag !== tag || node.questionDotToken !== questionDotToken || node.typeArguments !== typeArguments || node.template !== template ? createTaggedTemplateExpression(tag, questionDotToken, typeArguments, template, node.flags) : node; +} + +export function updateParenthesizedExpression(node: ParenthesizedExpression, expression: Expression): ParenthesizedExpression { + return node.expression !== expression ? createParenthesizedExpression(expression) : node; +} + +export function updateArrayLiteralExpression(node: ArrayLiteralExpression, elements: readonly Expression[]): ArrayLiteralExpression { + return node.elements !== elements ? createArrayLiteralExpression(elements, node.multiLine) : node; +} + +export function updateObjectLiteralExpression(node: ObjectLiteralExpression, properties: readonly ObjectLiteralElementLike[]): ObjectLiteralExpression { + return node.properties !== properties ? createObjectLiteralExpression(properties, node.multiLine) : node; +} + +export function updateSpreadAssignment(node: SpreadAssignment, expression: Expression): SpreadAssignment { + return node.expression !== expression ? createSpreadAssignment(expression) : node; +} + +export function updatePropertyAssignment(node: PropertyAssignment, modifiers: readonly ModifierLike[] | undefined, name: PropertyName, postfixToken: QuestionToken | ExclamationToken | undefined, type: TypeNode, initializer: Expression): PropertyAssignment { + return node.modifiers !== modifiers || node.name !== name || node.postfixToken !== postfixToken || node.type !== type || node.initializer !== initializer ? createPropertyAssignment(modifiers, name, postfixToken, type, initializer) : node; +} + +export function updateShorthandPropertyAssignment(node: ShorthandPropertyAssignment, modifiers: readonly ModifierLike[] | undefined, name: PropertyName, postfixToken: QuestionToken | ExclamationToken | undefined, type: TypeNode, equalsToken?: EqualsToken, objectAssignmentInitializer?: Expression): ShorthandPropertyAssignment { + return node.modifiers !== modifiers || node.name !== name || node.postfixToken !== postfixToken || node.type !== type || node.equalsToken !== equalsToken || node.objectAssignmentInitializer !== objectAssignmentInitializer ? createShorthandPropertyAssignment(modifiers, name, postfixToken, type, equalsToken, objectAssignmentInitializer) : node; +} + +export function updateDeleteExpression(node: DeleteExpression, expression: Expression): DeleteExpression { + return node.expression !== expression ? createDeleteExpression(expression) : node; +} + +export function updateTypeOfExpression(node: TypeOfExpression, expression: Expression): TypeOfExpression { + return node.expression !== expression ? createTypeOfExpression(expression) : node; +} + +export function updateVoidExpression(node: VoidExpression, expression: Expression): VoidExpression { + return node.expression !== expression ? createVoidExpression(expression) : node; +} + +export function updateAwaitExpression(node: AwaitExpression, expression: Expression): AwaitExpression { + return node.expression !== expression ? createAwaitExpression(expression) : node; +} + +export function updateTypeAssertion(node: TypeAssertion, type: TypeNode, expression: Expression): TypeAssertion { + return node.type !== type || node.expression !== expression ? createTypeAssertion(type, expression) : node; +} + +export function updateUnionTypeNode(node: UnionTypeNode, types: readonly TypeNode[]): UnionTypeNode { + return node.types !== types ? createUnionTypeNode(types) : node; +} + +export function updateIntersectionTypeNode(node: IntersectionTypeNode, types: readonly TypeNode[]): IntersectionTypeNode { + return node.types !== types ? createIntersectionTypeNode(types) : node; +} + +export function updateConditionalTypeNode(node: ConditionalTypeNode, checkType: TypeNode, extendsType: TypeNode, trueType: TypeNode, falseType: TypeNode): ConditionalTypeNode { + return node.checkType !== checkType || node.extendsType !== extendsType || node.trueType !== trueType || node.falseType !== falseType ? createConditionalTypeNode(checkType, extendsType, trueType, falseType) : node; +} + +export function updateTypeOperatorNode(node: TypeOperatorNode, type: TypeNode): TypeOperatorNode { + return node.type !== type ? createTypeOperatorNode(node.operator, type) : node; +} + +export function updateInferTypeNode(node: InferTypeNode, typeParameter: TypeParameterDeclaration): InferTypeNode { + return node.typeParameter !== typeParameter ? createInferTypeNode(typeParameter) : node; +} + +export function updateArrayTypeNode(node: ArrayTypeNode, elementType: TypeNode): ArrayTypeNode { + return node.elementType !== elementType ? createArrayTypeNode(elementType) : node; +} + +export function updateIndexedAccessTypeNode(node: IndexedAccessTypeNode, objectType: TypeNode, indexType: TypeNode): IndexedAccessTypeNode { + return node.objectType !== objectType || node.indexType !== indexType ? createIndexedAccessTypeNode(objectType, indexType) : node; +} + +export function updateTypeReferenceNode(node: TypeReferenceNode, typeName: EntityName, typeArguments?: readonly TypeNode[]): TypeReferenceNode { + return node.typeName !== typeName || node.typeArguments !== typeArguments ? createTypeReferenceNode(typeName, typeArguments) : node; +} + +export function updateExpressionWithTypeArguments(node: ExpressionWithTypeArguments, expression: Expression, typeArguments?: readonly TypeNode[]): ExpressionWithTypeArguments { + return node.expression !== expression || node.typeArguments !== typeArguments ? createExpressionWithTypeArguments(expression, typeArguments) : node; +} + +export function updateLiteralTypeNode(node: LiteralTypeNode, literal: Node): LiteralTypeNode { + return node.literal !== literal ? createLiteralTypeNode(literal) : node; +} + +export function updateTypePredicateNode(node: TypePredicateNode, assertsModifier: AssertsKeyword | undefined, parameterName: TypePredicateParameterName, type?: TypeNode): TypePredicateNode { + return node.assertsModifier !== assertsModifier || node.parameterName !== parameterName || node.type !== type ? createTypePredicateNode(assertsModifier, parameterName, type) : node; +} + +export function updateImportAttribute(node: ImportAttribute, name: ImportAttributeName, value: Expression): ImportAttribute { + return node.name !== name || node.value !== value ? createImportAttribute(name, value) : node; +} + +export function updateImportAttributes(node: ImportAttributes, attributes: readonly ImportAttribute[]): ImportAttributes { + return node.attributes !== attributes ? createImportAttributes(node.token, attributes, node.multiLine) : node; +} + +export function updateTypeQueryNode(node: TypeQueryNode, exprName: EntityName, typeArguments?: readonly TypeNode[]): TypeQueryNode { + return node.exprName !== exprName || node.typeArguments !== typeArguments ? createTypeQueryNode(exprName, typeArguments) : node; +} + +export function updateMappedTypeNode(node: MappedTypeNode, readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, nameType?: TypeNode, questionToken?: QuestionToken | PlusToken | MinusToken, type?: TypeNode, members?: readonly TypeElement[]): MappedTypeNode { + return node.readonlyToken !== readonlyToken || node.typeParameter !== typeParameter || node.nameType !== nameType || node.questionToken !== questionToken || node.type !== type || node.members !== members ? createMappedTypeNode(readonlyToken, typeParameter, nameType, questionToken, type, members) : node; +} + +export function updateTypeLiteralNode(node: TypeLiteralNode, members: readonly TypeElement[]): TypeLiteralNode { + return node.members !== members ? createTypeLiteralNode(members) : node; +} + +export function updateTupleTypeNode(node: TupleTypeNode, elements: readonly TypeNode[]): TupleTypeNode { + return node.elements !== elements ? createTupleTypeNode(elements) : node; +} + +export function updateNamedTupleMember(node: NamedTupleMember, dotDotDotToken: DotDotDotToken | undefined, name: Identifier, questionToken: QuestionToken | undefined, type: TypeNode): NamedTupleMember { + return node.dotDotDotToken !== dotDotDotToken || node.name !== name || node.questionToken !== questionToken || node.type !== type ? createNamedTupleMember(dotDotDotToken, name, questionToken, type) : node; +} + +export function updateOptionalTypeNode(node: OptionalTypeNode, type: TypeNode): OptionalTypeNode { + return node.type !== type ? createOptionalTypeNode(type) : node; +} + +export function updateRestTypeNode(node: RestTypeNode, type: TypeNode): RestTypeNode { + return node.type !== type ? createRestTypeNode(type) : node; +} + +export function updateParenthesizedTypeNode(node: ParenthesizedTypeNode, type: TypeNode): ParenthesizedTypeNode { + return node.type !== type ? createParenthesizedTypeNode(type) : node; +} + +export function updateFunctionTypeNode(node: FunctionTypeNode, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode): FunctionTypeNode { + return node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type ? createFunctionTypeNode(typeParameters, parameters, type) : node; +} + +export function updateConstructorTypeNode(node: ConstructorTypeNode, modifiers: readonly ModifierLike[] | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode): ConstructorTypeNode { + return node.modifiers !== modifiers || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type ? createConstructorTypeNode(modifiers, typeParameters, parameters, type) : node; +} + +export function updateTemplateLiteralTypeNode(node: TemplateLiteralTypeNode, head: TemplateHead, templateSpans: readonly TemplateLiteralTypeSpan[]): TemplateLiteralTypeNode { + return node.head !== head || node.templateSpans !== templateSpans ? createTemplateLiteralTypeNode(head, templateSpans) : node; +} + +export function updateTemplateLiteralTypeSpan(node: TemplateLiteralTypeSpan, type: TypeNode, literal: TemplateMiddleOrTail): TemplateLiteralTypeSpan { + return node.type !== type || node.literal !== literal ? createTemplateLiteralTypeSpan(type, literal) : node; +} + +export function updateSyntheticExpression(node: SyntheticExpression, tupleNameSource?: Node): SyntheticExpression { + return node.tupleNameSource !== tupleNameSource ? createSyntheticExpression(node.type, node.isSpread, tupleNameSource) : node; +} + +export function updatePartiallyEmittedExpression(node: PartiallyEmittedExpression, expression: Expression): PartiallyEmittedExpression { + return node.expression !== expression ? createPartiallyEmittedExpression(expression) : node; +} + +export function updateJsxElement(node: JsxElement, openingElement: JsxOpeningElement, children: readonly JsxChild[], closingElement: JsxClosingElement): JsxElement { + return node.openingElement !== openingElement || node.children !== children || node.closingElement !== closingElement ? createJsxElement(openingElement, children, closingElement) : node; +} + +export function updateJsxAttributes(node: JsxAttributes, properties: readonly JsxAttributeLike[]): JsxAttributes { + return node.properties !== properties ? createJsxAttributes(properties) : node; +} + +export function updateJsxNamespacedName(node: JsxNamespacedName, namespace: Identifier, name: Identifier): JsxNamespacedName { + return node.namespace !== namespace || node.name !== name ? createJsxNamespacedName(namespace, name) : node; +} + +export function updateJsxOpeningElement(node: JsxOpeningElement, tagName: JsxTagNameExpression, typeArguments: readonly TypeNode[] | undefined, attributes: JsxAttributes): JsxOpeningElement { + return node.tagName !== tagName || node.typeArguments !== typeArguments || node.attributes !== attributes ? createJsxOpeningElement(tagName, typeArguments, attributes) : node; +} + +export function updateJsxSelfClosingElement(node: JsxSelfClosingElement, tagName: JsxTagNameExpression, typeArguments: readonly TypeNode[] | undefined, attributes: JsxAttributes): JsxSelfClosingElement { + return node.tagName !== tagName || node.typeArguments !== typeArguments || node.attributes !== attributes ? createJsxSelfClosingElement(tagName, typeArguments, attributes) : node; +} + +export function updateJsxFragment(node: JsxFragment, openingFragment: JsxOpeningFragment, children: readonly JsxChild[], closingFragment: JsxClosingFragment): JsxFragment { + return node.openingFragment !== openingFragment || node.children !== children || node.closingFragment !== closingFragment ? createJsxFragment(openingFragment, children, closingFragment) : node; +} + +export function updateJsxAttribute(node: JsxAttribute, name: JsxAttributeName, initializer?: JsxAttributeValue): JsxAttribute { + return node.name !== name || node.initializer !== initializer ? createJsxAttribute(name, initializer) : node; +} + +export function updateJsxSpreadAttribute(node: JsxSpreadAttribute, expression: Expression): JsxSpreadAttribute { + return node.expression !== expression ? createJsxSpreadAttribute(expression) : node; +} + +export function updateJsxClosingElement(node: JsxClosingElement, tagName: JsxTagNameExpression): JsxClosingElement { + return node.tagName !== tagName ? createJsxClosingElement(tagName) : node; +} + +export function updateJsxExpression(node: JsxExpression, dotDotDotToken?: DotDotDotToken, expression?: Expression): JsxExpression { + return node.dotDotDotToken !== dotDotDotToken || node.expression !== expression ? createJsxExpression(dotDotDotToken, expression) : node; +} + +export function updateSyntaxList(node: SyntaxList, children: readonly Node[]): SyntaxList { + return node.children !== children ? createSyntaxList(children) : node; +} + +export function updateJSDoc(node: JSDoc, comment: readonly JSDocComment[], tags?: readonly JSDocTag[]): JSDoc { + return node.comment !== comment || node.tags !== tags ? createJSDoc(comment, tags) : node; +} + +export function updateJSDocTypeExpression(node: JSDocTypeExpression, type: TypeNode): JSDocTypeExpression { + return node.type !== type ? createJSDocTypeExpression(type) : node; +} + +export function updateJSDocNonNullableType(node: JSDocNonNullableType, type: TypeNode): JSDocNonNullableType { + return node.type !== type ? createJSDocNonNullableType(type) : node; +} + +export function updateJSDocNullableType(node: JSDocNullableType, type: TypeNode): JSDocNullableType { + return node.type !== type ? createJSDocNullableType(type) : node; +} + +export function updateJSDocVariadicType(node: JSDocVariadicType, type: TypeNode): JSDocVariadicType { + return node.type !== type ? createJSDocVariadicType(type) : node; +} + +export function updateJSDocOptionalType(node: JSDocOptionalType, type: TypeNode): JSDocOptionalType { + return node.type !== type ? createJSDocOptionalType(type) : node; +} + +export function updateJSDocTypeTag(node: JSDocTypeTag, tagName: Identifier, typeExpression: Node, comment?: readonly JSDocComment[]): JSDocTypeTag { + return node.tagName !== tagName || node.typeExpression !== typeExpression || node.comment !== comment ? createJSDocTypeTag(tagName, typeExpression, comment) : node; +} + +export function updateJSDocUnknownTag(node: JSDocUnknownTag, tagName: Identifier, comment?: readonly JSDocComment[]): JSDocUnknownTag { + return node.tagName !== tagName || node.comment !== comment ? createJSDocUnknownTag(tagName, comment) : node; +} + +export function updateJSDocTemplateTag(node: JSDocTemplateTag, tagName: Identifier, constraint: Node, typeParameters: readonly TypeParameterDeclaration[], comment?: readonly JSDocComment[]): JSDocTemplateTag { + return node.tagName !== tagName || node.constraint !== constraint || node.typeParameters !== typeParameters || node.comment !== comment ? createJSDocTemplateTag(tagName, constraint, typeParameters, comment) : node; +} + +export function updateJSDocReturnTag(node: JSDocReturnTag, tagName: Identifier, typeExpression?: TypeNode, comment?: readonly JSDocComment[]): JSDocReturnTag { + return node.tagName !== tagName || node.typeExpression !== typeExpression || node.comment !== comment ? createJSDocReturnTag(tagName, typeExpression, comment) : node; +} + +export function updateJSDocPublicTag(node: JSDocPublicTag, tagName: Identifier, comment?: readonly JSDocComment[]): JSDocPublicTag { + return node.tagName !== tagName || node.comment !== comment ? createJSDocPublicTag(tagName, comment) : node; +} + +export function updateJSDocPrivateTag(node: JSDocPrivateTag, tagName: Identifier, comment?: readonly JSDocComment[]): JSDocPrivateTag { + return node.tagName !== tagName || node.comment !== comment ? createJSDocPrivateTag(tagName, comment) : node; +} + +export function updateJSDocProtectedTag(node: JSDocProtectedTag, tagName: Identifier, comment?: readonly JSDocComment[]): JSDocProtectedTag { + return node.tagName !== tagName || node.comment !== comment ? createJSDocProtectedTag(tagName, comment) : node; +} + +export function updateJSDocReadonlyTag(node: JSDocReadonlyTag, tagName: Identifier, comment?: readonly JSDocComment[]): JSDocReadonlyTag { + return node.tagName !== tagName || node.comment !== comment ? createJSDocReadonlyTag(tagName, comment) : node; +} + +export function updateJSDocOverrideTag(node: JSDocOverrideTag, tagName: Identifier, comment?: readonly JSDocComment[]): JSDocOverrideTag { + return node.tagName !== tagName || node.comment !== comment ? createJSDocOverrideTag(tagName, comment) : node; +} + +export function updateJSDocDeprecatedTag(node: JSDocDeprecatedTag, tagName: Identifier, comment?: readonly JSDocComment[]): JSDocDeprecatedTag { + return node.tagName !== tagName || node.comment !== comment ? createJSDocDeprecatedTag(tagName, comment) : node; +} + +export function updateJSDocSeeTag(node: JSDocSeeTag, tagName: Identifier, nameExpression: TypeNode, comment?: readonly JSDocComment[]): JSDocSeeTag { + return node.tagName !== tagName || node.nameExpression !== nameExpression || node.comment !== comment ? createJSDocSeeTag(tagName, nameExpression, comment) : node; +} + +export function updateJSDocImplementsTag(node: JSDocImplementsTag, tagName: Identifier, className: ExpressionWithTypeArguments, comment?: readonly JSDocComment[]): JSDocImplementsTag { + return node.tagName !== tagName || node.className !== className || node.comment !== comment ? createJSDocImplementsTag(tagName, className, comment) : node; +} + +export function updateJSDocAugmentsTag(node: JSDocAugmentsTag, tagName: Identifier, className: ExpressionWithTypeArguments, comment?: readonly JSDocComment[]): JSDocAugmentsTag { + return node.tagName !== tagName || node.className !== className || node.comment !== comment ? createJSDocAugmentsTag(tagName, className, comment) : node; +} + +export function updateJSDocSatisfiesTag(node: JSDocSatisfiesTag, tagName: Identifier, typeExpression: TypeNode, comment?: readonly JSDocComment[]): JSDocSatisfiesTag { + return node.tagName !== tagName || node.typeExpression !== typeExpression || node.comment !== comment ? createJSDocSatisfiesTag(tagName, typeExpression, comment) : node; +} + +export function updateJSDocThrowsTag(node: JSDocThrowsTag, tagName: Identifier, typeExpression?: TypeNode, comment?: readonly JSDocComment[]): JSDocThrowsTag { + return node.tagName !== tagName || node.typeExpression !== typeExpression || node.comment !== comment ? createJSDocThrowsTag(tagName, typeExpression, comment) : node; +} + +export function updateJSDocThisTag(node: JSDocThisTag, tagName: Identifier, typeExpression: TypeNode, comment?: readonly JSDocComment[]): JSDocThisTag { + return node.tagName !== tagName || node.typeExpression !== typeExpression || node.comment !== comment ? createJSDocThisTag(tagName, typeExpression, comment) : node; +} + +export function updateJSDocImportTag(node: JSDocImportTag, tagName: Identifier, importClause: ImportClause | undefined, moduleSpecifier: Expression, attributes?: ImportAttributes, comment?: readonly JSDocComment[]): JSDocImportTag { + return node.tagName !== tagName || node.importClause !== importClause || node.moduleSpecifier !== moduleSpecifier || node.attributes !== attributes || node.comment !== comment ? createJSDocImportTag(tagName, importClause, moduleSpecifier, attributes, comment) : node; +} + +export function updateJSDocCallbackTag(node: JSDocCallbackTag, tagName: Identifier, typeExpression: TypeNode, name?: JSDocFullName, comment?: readonly JSDocComment[]): JSDocCallbackTag { + return node.tagName !== tagName || node.typeExpression !== typeExpression || node.name !== name || node.comment !== comment ? createJSDocCallbackTag(tagName, typeExpression, name, comment) : node; +} + +export function updateJSDocOverloadTag(node: JSDocOverloadTag, tagName: Identifier, typeExpression: TypeNode, comment?: readonly JSDocComment[]): JSDocOverloadTag { + return node.tagName !== tagName || node.typeExpression !== typeExpression || node.comment !== comment ? createJSDocOverloadTag(tagName, typeExpression, comment) : node; +} + +export function updateJSDocTypedefTag(node: JSDocTypedefTag, tagName: Identifier, typeExpression?: Node, name?: JSDocFullName, comment?: readonly JSDocComment[]): JSDocTypedefTag { + return node.tagName !== tagName || node.typeExpression !== typeExpression || node.name !== name || node.comment !== comment ? createJSDocTypedefTag(tagName, typeExpression, name, comment) : node; +} + +export function updateJSDocSignature(node: JSDocSignature, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type?: TypeNode): JSDocSignature { + return node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type ? createJSDocSignature(typeParameters, parameters, type) : node; +} + +export function updateJSDocNameReference(node: JSDocNameReference, name: EntityName): JSDocNameReference { + return node.name !== name ? createJSDocNameReference(name) : node; +} + +export function updateModuleDeclaration(node: ModuleDeclaration, modifiers: readonly ModifierLike[] | undefined, name: ModuleName, body?: ModuleBody): ModuleDeclaration { + return node.modifiers !== modifiers || node.name !== name || node.body !== body ? createModuleDeclaration(modifiers, node.keyword, name, body) : node; +} + +export function updateImportEqualsDeclaration(node: ImportEqualsDeclaration, modifiers: readonly ModifierLike[] | undefined, name: Identifier, moduleReference: ModuleReference): ImportEqualsDeclaration { + return node.modifiers !== modifiers || node.name !== name || node.moduleReference !== moduleReference ? createImportEqualsDeclaration(modifiers, node.isTypeOnly, name, moduleReference) : node; +} + +export function updateExportDeclaration(node: ExportDeclaration, modifiers?: readonly ModifierLike[], exportClause?: NamedExportBindings, moduleSpecifier?: Expression, attributes?: ImportAttributes): ExportDeclaration { + return node.modifiers !== modifiers || node.exportClause !== exportClause || node.moduleSpecifier !== moduleSpecifier || node.attributes !== attributes ? createExportDeclaration(modifiers, node.isTypeOnly, exportClause, moduleSpecifier, attributes) : node; +} + +export function updateImportTypeNode(node: ImportTypeNode, argument: TypeNode, attributes?: ImportAttributes, qualifier?: EntityName, typeArguments?: readonly TypeNode[]): ImportTypeNode { + return node.argument !== argument || node.attributes !== attributes || node.qualifier !== qualifier || node.typeArguments !== typeArguments ? createImportTypeNode(node.isTypeOf, argument, attributes, qualifier, typeArguments) : node; +} + +export function updateImportClause(node: ImportClause, name?: Identifier, namedBindings?: NamedImportBindings): ImportClause { + return node.name !== name || node.namedBindings !== namedBindings ? createImportClause(node.phaseModifier, name, namedBindings) : node; +} + +export function updateImportSpecifier(node: ImportSpecifier, propertyName: ModuleExportName | undefined, name: Identifier): ImportSpecifier { + return node.propertyName !== propertyName || node.name !== name ? createImportSpecifier(node.isTypeOnly, propertyName, name) : node; +} + +export function updateJSDocLink(node: JSDocLink, name?: EntityName): JSDocLink { + return node.name !== name ? createJSDocLink(name, node.text) : node; +} + +export function updateJSDocLinkPlain(node: JSDocLinkPlain, name?: EntityName): JSDocLinkPlain { + return node.name !== name ? createJSDocLinkPlain(name, node.text) : node; +} + +export function updateJSDocLinkCode(node: JSDocLinkCode, name?: EntityName): JSDocLinkCode { + return node.name !== name ? createJSDocLinkCode(name, node.text) : node; +} + +export function updateTypeParameterDeclaration(node: TypeParameterDeclaration, modifiers: readonly ModifierLike[] | undefined, name: Identifier, constraint?: TypeNode, expression?: Expression, defaultType?: TypeNode): TypeParameterDeclaration { + return node.modifiers !== modifiers || node.name !== name || node.constraint !== constraint || node.expression !== expression || node.defaultType !== defaultType ? createTypeParameterDeclaration(modifiers, name, constraint, expression, defaultType) : node; +} + +export function updateSyntheticReferenceExpression(node: SyntheticReferenceExpression, expression: Expression, thisArg: Expression): SyntheticReferenceExpression { + return node.expression !== expression || node.thisArg !== thisArg ? createSyntheticReferenceExpression(expression, thisArg) : node; +} + +export function updateJSDocTypeLiteral(node: JSDocTypeLiteral, jsdocPropertyTags?: readonly JSDocTag[]): JSDocTypeLiteral { + return node.jsdocPropertyTags !== jsdocPropertyTags ? createJSDocTypeLiteral(jsdocPropertyTags, node.isArrayType) : node; +} + +export function updateForInStatement(node: ForInStatement, awaitModifier: AwaitKeyword | undefined, initializer: ForInitializer, expression: Expression, statement: Statement): ForInStatement { + return node.awaitModifier !== awaitModifier || node.initializer !== initializer || node.expression !== expression || node.statement !== statement ? createForInStatement(awaitModifier, initializer, expression, statement) : node; +} + +export function updateForOfStatement(node: ForOfStatement, awaitModifier: AwaitKeyword | undefined, initializer: ForInitializer, expression: Expression, statement: Statement): ForOfStatement { + return node.awaitModifier !== awaitModifier || node.initializer !== initializer || node.expression !== expression || node.statement !== statement ? createForOfStatement(awaitModifier, initializer, expression, statement) : node; +} + +export function updateCaseClause(node: CaseClause, expression: Expression, statements: readonly Statement[]): CaseClause { + return node.expression !== expression || node.statements !== statements ? createCaseClause(expression, statements) : node; +} + +export function updateDefaultClause(node: DefaultClause, expression: Expression, statements: readonly Statement[]): DefaultClause { + return node.expression !== expression || node.statements !== statements ? createDefaultClause(expression, statements) : node; +} + +export function updateObjectBindingPattern(node: ObjectBindingPattern, elements: readonly BindingElement[]): ObjectBindingPattern { + return node.elements !== elements ? createObjectBindingPattern(elements) : node; +} + +export function updateArrayBindingPattern(node: ArrayBindingPattern, elements: readonly BindingElement[]): ArrayBindingPattern { + return node.elements !== elements ? createArrayBindingPattern(elements) : node; +} + +export function updateJSDocParameterTag(node: JSDocParameterTag, tagName: Identifier, name: EntityName, typeExpression: TypeNode | undefined, comment: readonly JSDocComment[] | undefined): JSDocParameterTag { + return node.tagName !== tagName || node.name !== name || node.typeExpression !== typeExpression || node.comment !== comment ? createJSDocParameterTag(tagName, name, node.isBracketed, typeExpression, node.isNameFirst, comment) : node; +} + +export function updateJSDocPropertyTag(node: JSDocPropertyTag, tagName: Identifier, name: EntityName, typeExpression: TypeNode | undefined, comment: readonly JSDocComment[] | undefined): JSDocPropertyTag { + return node.tagName !== tagName || node.name !== name || node.typeExpression !== typeExpression || node.comment !== comment ? createJSDocPropertyTag(tagName, name, node.isBracketed, typeExpression, node.isNameFirst, comment) : node; +} + +export function createSourceFile(statements: readonly Statement[], endOfFileToken: EndOfFile, text: string, fileName: string, path: Path): SourceFile { + return new NodeObject(SyntaxKind.SourceFile, { + statements: createNodeArray(statements), + endOfFileToken, + text, + originalText: text, + spanMap: undefined, + contentMapper: undefined, + virtualFileName: undefined, + diagnosticDirectives: undefined, + fileName, + path, + }) as unknown as SourceFile; +} + +function cloneSourceFileWithChanges(source: SourceFile, statements: readonly Statement[], endOfFileToken: EndOfFile): SourceFile { + return new NodeObject(SyntaxKind.SourceFile, { + ...cloneSourceFileData(source), + statements: createNodeArray(statements), + endOfFileToken, + }) as unknown as SourceFile; +} + +export function updateSourceFile(node: SourceFile, statements: readonly Statement[], endOfFileToken: EndOfFile): SourceFile { + return node.statements !== statements || node.endOfFileToken !== endOfFileToken + ? cloneSourceFileWithChanges(node, statements, endOfFileToken) + : node; +} diff --git a/packages/typescript/src/ast/index.ts b/packages/typescript/src/ast/index.ts index fcef01b579dc5..81875d6685f29 100644 --- a/packages/typescript/src/ast/index.ts +++ b/packages/typescript/src/ast/index.ts @@ -1,24 +1,24 @@ -export { CharacterCodes } from "#enums/characterCodes"; -export { CommentDirectiveType } from "#enums/commentDirectiveType"; -export { DiagnosticDirectivePolicy } from "#enums/diagnosticDirectivePolicy"; -export { InternalSymbolName } from "#enums/internalSymbolName"; -export { LanguageVariant } from "#enums/languageVariant"; -export { ModifierFlags } from "#enums/modifierFlags"; -export { NodeFlags } from "#enums/nodeFlags"; -export { RegularExpressionFlags } from "#enums/regularExpressionFlags"; -export { ScriptKind } from "#enums/scriptKind"; -export { ScriptTarget } from "#enums/scriptTarget"; -export { SpanMapFeature } from "#enums/spanMapFeature"; -export { SpanMapFidelity } from "#enums/spanMapFidelity"; -export { SpanMapKind } from "#enums/spanMapKind"; -export { SyntaxKind } from "#enums/syntaxKind"; -export { TokenFlags } from "#enums/tokenFlags"; -export * from "./ast.ts"; -export * from "./astnav.ts"; -export * from "./clone.ts"; -export * from "./is.ts"; -export * from "./jsdoc.ts"; -export * from "./scanner.ts"; -export * from "./spanMap.ts"; -export * from "./utils.ts"; -export * from "./visitor.ts"; +export { CharacterCodes } from "#enums/characterCodes"; +export { CommentDirectiveType } from "#enums/commentDirectiveType"; +export { DiagnosticDirectivePolicy } from "#enums/diagnosticDirectivePolicy"; +export { InternalSymbolName } from "#enums/internalSymbolName"; +export { LanguageVariant } from "#enums/languageVariant"; +export { ModifierFlags } from "#enums/modifierFlags"; +export { NodeFlags } from "#enums/nodeFlags"; +export { RegularExpressionFlags } from "#enums/regularExpressionFlags"; +export { ScriptKind } from "#enums/scriptKind"; +export { ScriptTarget } from "#enums/scriptTarget"; +export { SpanMapFeature } from "#enums/spanMapFeature"; +export { SpanMapFidelity } from "#enums/spanMapFidelity"; +export { SpanMapKind } from "#enums/spanMapKind"; +export { SyntaxKind } from "#enums/syntaxKind"; +export { TokenFlags } from "#enums/tokenFlags"; +export * from "./ast.ts"; +export * from "./astnav.ts"; +export * from "./clone.ts"; +export * from "./is.ts"; +export * from "./jsdoc.ts"; +export * from "./scanner.ts"; +export * from "./spanMap.ts"; +export * from "./utils.ts"; +export * from "./visitor.ts"; diff --git a/packages/typescript/src/ast/is.generated.ts b/packages/typescript/src/ast/is.generated.ts index 2654438f83ab3..992da782dd831 100644 --- a/packages/typescript/src/ast/is.generated.ts +++ b/packages/typescript/src/ast/is.generated.ts @@ -1,3324 +1,3324 @@ -// Code generated by tools/scripts/tsc/generate-ts-ast.ts. DO NOT EDIT. - -import { SyntaxKind } from "#enums/syntaxKind"; -import type { NodeHandle as AsyncNodeHandle } from "../api/async/api.ts"; -import type { NodeHandle as SyncNodeHandle } from "../api/sync/api.ts"; -import type { - AbstractKeyword, - AccessExpression, - AccessorDeclaration, - AccessorKeyword, - AdditiveOperator, - AdditiveOperatorOrHigher, - AnyImportSyntax, - ArrayBindingElement, - ArrayBindingPattern, - ArrayDestructuringAssignment, - ArrayLiteralExpression, - ArrayTypeNode, - ArrowFunction, - AsExpression, - AssertionExpression, - AssertKeyword, - AssertsKeyword, - AssignmentOperator, - AssignmentOperatorOrHigher, - AssignmentOperatorToken, - AsteriskToken, - AsyncKeyword, - AwaitExpression, - AwaitKeyword, - BigIntLiteral, - BinaryExpression, - BinaryOperator, - BinaryOperatorToken, - BindingElement, - BindingName, - BitwiseOperator, - BitwiseOperatorOrHigher, - Block, - BooleanLiteral, - BreakOrContinueStatement, - BreakStatement, - CallExpression, - CallLikeExpression, - CallOrNewExpression, - CallSignatureDeclaration, - CaseBlock, - CaseClause, - CaseKeyword, - CatchClause, - ClassDeclaration, - ClassExpression, - ClassLikeDeclaration, - ClassStaticBlockDeclaration, - ColonToken, - CompoundAssignmentOperator, - ComputedPropertyName, - ConditionalExpression, - ConditionalTypeNode, - ConstKeyword, - ConstructorDeclaration, - ConstructorTypeNode, - ConstructSignatureDeclaration, - ContinueStatement, - DebuggerStatement, - DeclarationName, - DeclareKeyword, - Decorator, - DefaultClause, - DefaultKeyword, - DeleteExpression, - DestructuringAssignment, - DoStatement, - DotDotDotToken, - DotToken, - ElementAccessExpression, - EmptyStatement, - EndOfFile, - EntityName, - EnumDeclaration, - EnumMember, - EqualityOperator, - EqualityOperatorOrHigher, - EqualsGreaterThanToken, - EqualsToken, - ExclamationToken, - ExponentiationOperator, - ExportAssignment, - ExportDeclaration, - ExportKeyword, - ExportSpecifier, - ExpressionStatement, - ExpressionWithTypeArguments, - ExternalModuleReference, - FalseLiteral, - ForInStatement, - ForOfStatement, - ForStatement, - FunctionBody, - FunctionDeclaration, - FunctionExpression, - FunctionLikeDeclaration, - FunctionTypeNode, - GetAccessorDeclaration, - HeritageClause, - HeritageClauseElement, - Identifier, - IfStatement, - ImportAttribute, - ImportAttributeName, - ImportAttributes, - ImportClause, - ImportClauseOrBindingPattern, - ImportDeclaration, - ImportEqualsDeclaration, - ImportExpression, - ImportPhaseModifierSyntaxKind, - ImportSpecifier, - ImportTypeNode, - IndexedAccessTypeNode, - IndexSignatureDeclaration, - InferTypeNode, - InKeyword, - InterfaceDeclaration, - IntersectionTypeNode, - JSDoc, - JSDocAllType, - JSDocAugmentsTag, - JSDocCallbackTag, - JSDocComment, - JSDocDeprecatedTag, - JSDocFullName, - JSDocImplementsTag, - JSDocImportTag, - JSDocLink, - JSDocLinkCode, - JSDocLinkPlain, - JSDocNameReference, - JSDocNonNullableType, - JSDocNullableType, - JSDocOptionalType, - JSDocOverloadTag, - JSDocOverrideTag, - JSDocParameterTag, - JSDocPrivateTag, - JSDocPropertyTag, - JSDocProtectedTag, - JSDocPublicTag, - JSDocReadonlyTag, - JSDocReturnTag, - JSDocSatisfiesTag, - JSDocSeeTag, - JSDocSignature, - JSDocTemplateTag, - JSDocText, - JSDocThisTag, - JSDocThrowsTag, - JSDocTypedefTag, - JSDocTypeExpression, - JSDocTypeLiteral, - JSDocTypeTag, - JSDocUnknownTag, - JSDocVariadicType, - JsxAttribute, - JsxAttributeLike, - JsxAttributeName, - JsxAttributes, - JsxAttributeValue, - JsxChild, - JsxClosingElement, - JsxClosingFragment, - JsxElement, - JsxExpression, - JsxFragment, - JsxNamespacedName, - JsxOpeningElement, - JsxOpeningFragment, - JsxOpeningLikeElement, - JsxSelfClosingElement, - JsxSpreadAttribute, - JsxText, - JsxTokenSyntaxKind, - KeywordExpression, - KeywordExpressionSyntaxKind, - KeywordTypeNode, - KeywordTypeSyntaxKind, - LabeledStatement, - LiteralExpression, - LiteralLikeNode, - LiteralToken, - LiteralTypeNode, - LogicalOperator, - LogicalOperatorOrHigher, - LogicalOrCoalescingAssignmentOperator, - MappedTypeNode, - MemberName, - MetaProperty, - MethodDeclaration, - MethodSignatureDeclaration, - MinusToken, - MissingDeclaration, - Modifier, - ModifierLike, - ModifierSyntaxKind, - ModuleBlock, - ModuleBody, - ModuleDeclaration, - ModuleExportName, - ModuleName, - ModuleReference, - MultiplicativeOperator, - MultiplicativeOperatorOrHigher, - NamedExportBindings, - NamedExports, - NamedImportBindings, - NamedImports, - NamedImportsOrExports, - NamedTupleMember, - NamespaceExport, - NamespaceExportDeclaration, - NamespaceImport, - NewExpression, - Node, - NonNullExpression, - NoSubstitutionTemplateLiteral, - NotEmittedStatement, - NotEmittedTypeElement, - NullLiteral, - NumericLiteral, - NumericOrStringLikeLiteral, - ObjectBindingPattern, - ObjectDestructuringAssignment, - ObjectLiteralElementLike, - ObjectLiteralExpression, - ObjectLiteralLikeNode, - ObjectTypeDeclaration, - OmittedExpression, - OptionalTypeNode, - OutKeyword, - OverrideKeyword, - ParameterDeclaration, - ParenthesizedExpression, - ParenthesizedTypeNode, - PartiallyEmittedExpression, - PlusToken, - PostfixUnaryExpression, - PostfixUnaryOperator, - PrefixUnaryExpression, - PrefixUnaryOperator, - PrivateIdentifier, - PrivateKeyword, - PropertyAccessExpression, - PropertyAssignment, - PropertyDeclaration, - PropertyName, - PropertyNameLiteral, - PropertySignatureDeclaration, - ProtectedKeyword, - PseudoLiteralSyntaxKind, - PseudoLiteralToken, - PublicKeyword, - QualifiedName, - QuestionDotToken, - QuestionToken, - ReadonlyKeyword, - RegularExpressionLiteral, - RelationalOperator, - RelationalOperatorOrHigher, - RestTypeNode, - ReturnStatement, - SatisfiesExpression, - SemicolonClassElement, - SetAccessorDeclaration, - ShiftOperator, - ShiftOperatorOrHigher, - ShorthandPropertyAssignment, - SignatureDeclaration, - SourceFile, - SpreadAssignment, - SpreadElement, - StaticKeyword, - StringLiteral, - StringLiteralLikeNode, - SuperExpression, - SwitchStatement, - SyntaxList, - SyntheticExpression, - SyntheticReferenceExpression, - TaggedTemplateExpression, - TemplateExpression, - TemplateHead, - TemplateLiteral, - TemplateLiteralLikeNode, - TemplateLiteralToken, - TemplateLiteralTypeNode, - TemplateLiteralTypeSpan, - TemplateMiddle, - TemplateMiddleOrTail, - TemplateSpan, - TemplateTail, - ThisExpression, - ThisTypeNode, - ThrowStatement, - Token, - TriviaSyntaxKind, - TrueLiteral, - TryStatement, - TupleTypeNode, - TypeAliasDeclaration, - TypeAssertion, - TypeLiteralNode, - TypeOfExpression, - TypeOperatorNode, - TypeParameterDeclaration, - TypePredicateNode, - TypePredicateParameterName, - TypeQueryNode, - TypeReferenceNode, - UnionOrIntersectionTypeNode, - UnionTypeNode, - VariableDeclaration, - VariableDeclarationList, - VariableOrParameterDeclaration, - VariableOrPropertyDeclaration, - VariableStatement, - VoidExpression, - WhileStatement, - WithStatement, - YieldExpression, -} from "./ast.ts"; -type NodeHandleLike = { kind: SyntaxKind; resolve(...args: any): any; }; -type SpecializeNodeHandle, U extends Node> = T extends AsyncNodeHandle ? AsyncNodeHandle & T : SyncNodeHandle & T; - -export function isToken(node: Node): node is Token { - return isTokenKind(node.kind); -} - -export declare namespace isToken { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isToken.Handle = isToken as any; - -export function isIdentifier(node: Node): node is Identifier { - return node.kind === SyntaxKind.Identifier; -} - -export declare namespace isIdentifier { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isIdentifier.Handle = isIdentifier as any; - -export function isPrivateIdentifier(node: Node): node is PrivateIdentifier { - return node.kind === SyntaxKind.PrivateIdentifier; -} - -export declare namespace isPrivateIdentifier { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isPrivateIdentifier.Handle = isPrivateIdentifier as any; - -export function isQualifiedName(node: Node): node is QualifiedName { - return node.kind === SyntaxKind.QualifiedName; -} - -export declare namespace isQualifiedName { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isQualifiedName.Handle = isQualifiedName as any; - -export function isComputedPropertyName(node: Node): node is ComputedPropertyName { - return node.kind === SyntaxKind.ComputedPropertyName; -} - -export declare namespace isComputedPropertyName { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isComputedPropertyName.Handle = isComputedPropertyName as any; - -export function isDecorator(node: Node): node is Decorator { - return node.kind === SyntaxKind.Decorator; -} - -export declare namespace isDecorator { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isDecorator.Handle = isDecorator as any; - -export function isEmptyStatement(node: Node): node is EmptyStatement { - return node.kind === SyntaxKind.EmptyStatement; -} - -export declare namespace isEmptyStatement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isEmptyStatement.Handle = isEmptyStatement as any; - -export function isIfStatement(node: Node): node is IfStatement { - return node.kind === SyntaxKind.IfStatement; -} - -export declare namespace isIfStatement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isIfStatement.Handle = isIfStatement as any; - -export function isDoStatement(node: Node): node is DoStatement { - return node.kind === SyntaxKind.DoStatement; -} - -export declare namespace isDoStatement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isDoStatement.Handle = isDoStatement as any; - -export function isWhileStatement(node: Node): node is WhileStatement { - return node.kind === SyntaxKind.WhileStatement; -} - -export declare namespace isWhileStatement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isWhileStatement.Handle = isWhileStatement as any; - -export function isForStatement(node: Node): node is ForStatement { - return node.kind === SyntaxKind.ForStatement; -} - -export declare namespace isForStatement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isForStatement.Handle = isForStatement as any; - -export function isBreakStatement(node: Node): node is BreakStatement { - return node.kind === SyntaxKind.BreakStatement; -} - -export declare namespace isBreakStatement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isBreakStatement.Handle = isBreakStatement as any; - -export function isContinueStatement(node: Node): node is ContinueStatement { - return node.kind === SyntaxKind.ContinueStatement; -} - -export declare namespace isContinueStatement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isContinueStatement.Handle = isContinueStatement as any; - -export function isReturnStatement(node: Node): node is ReturnStatement { - return node.kind === SyntaxKind.ReturnStatement; -} - -export declare namespace isReturnStatement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isReturnStatement.Handle = isReturnStatement as any; - -export function isWithStatement(node: Node): node is WithStatement { - return node.kind === SyntaxKind.WithStatement; -} - -export declare namespace isWithStatement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isWithStatement.Handle = isWithStatement as any; - -export function isSwitchStatement(node: Node): node is SwitchStatement { - return node.kind === SyntaxKind.SwitchStatement; -} - -export declare namespace isSwitchStatement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isSwitchStatement.Handle = isSwitchStatement as any; - -export function isCaseBlock(node: Node): node is CaseBlock { - return node.kind === SyntaxKind.CaseBlock; -} - -export declare namespace isCaseBlock { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isCaseBlock.Handle = isCaseBlock as any; - -export function isThrowStatement(node: Node): node is ThrowStatement { - return node.kind === SyntaxKind.ThrowStatement; -} - -export declare namespace isThrowStatement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isThrowStatement.Handle = isThrowStatement as any; - -export function isTryStatement(node: Node): node is TryStatement { - return node.kind === SyntaxKind.TryStatement; -} - -export declare namespace isTryStatement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isTryStatement.Handle = isTryStatement as any; - -export function isCatchClause(node: Node): node is CatchClause { - return node.kind === SyntaxKind.CatchClause; -} - -export declare namespace isCatchClause { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isCatchClause.Handle = isCatchClause as any; - -export function isDebuggerStatement(node: Node): node is DebuggerStatement { - return node.kind === SyntaxKind.DebuggerStatement; -} - -export declare namespace isDebuggerStatement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isDebuggerStatement.Handle = isDebuggerStatement as any; - -export function isLabeledStatement(node: Node): node is LabeledStatement { - return node.kind === SyntaxKind.LabeledStatement; -} - -export declare namespace isLabeledStatement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isLabeledStatement.Handle = isLabeledStatement as any; - -export function isExpressionStatement(node: Node): node is ExpressionStatement { - return node.kind === SyntaxKind.ExpressionStatement; -} - -export declare namespace isExpressionStatement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isExpressionStatement.Handle = isExpressionStatement as any; - -export function isBlock(node: Node): node is Block { - return node.kind === SyntaxKind.Block; -} - -export declare namespace isBlock { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isBlock.Handle = isBlock as any; - -export function isVariableStatement(node: Node): node is VariableStatement { - return node.kind === SyntaxKind.VariableStatement; -} - -export declare namespace isVariableStatement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isVariableStatement.Handle = isVariableStatement as any; - -export function isVariableDeclaration(node: Node): node is VariableDeclaration { - return node.kind === SyntaxKind.VariableDeclaration; -} - -export declare namespace isVariableDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isVariableDeclaration.Handle = isVariableDeclaration as any; - -export function isVariableDeclarationList(node: Node): node is VariableDeclarationList { - return node.kind === SyntaxKind.VariableDeclarationList; -} - -export declare namespace isVariableDeclarationList { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isVariableDeclarationList.Handle = isVariableDeclarationList as any; - -export function isParameterDeclaration(node: Node): node is ParameterDeclaration { - return node.kind === SyntaxKind.Parameter; -} - -export declare namespace isParameterDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isParameterDeclaration.Handle = isParameterDeclaration as any; - -export function isBindingElement(node: Node): node is BindingElement { - return node.kind === SyntaxKind.BindingElement; -} - -export declare namespace isBindingElement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isBindingElement.Handle = isBindingElement as any; - -export function isMissingDeclaration(node: Node): node is MissingDeclaration { - return node.kind === SyntaxKind.MissingDeclaration; -} - -export declare namespace isMissingDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isMissingDeclaration.Handle = isMissingDeclaration as any; - -export function isFunctionDeclaration(node: Node): node is FunctionDeclaration { - return node.kind === SyntaxKind.FunctionDeclaration; -} - -export declare namespace isFunctionDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isFunctionDeclaration.Handle = isFunctionDeclaration as any; - -export function isClassDeclaration(node: Node): node is ClassDeclaration { - return node.kind === SyntaxKind.ClassDeclaration; -} - -export declare namespace isClassDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isClassDeclaration.Handle = isClassDeclaration as any; - -export function isClassExpression(node: Node): node is ClassExpression { - return node.kind === SyntaxKind.ClassExpression; -} - -export declare namespace isClassExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isClassExpression.Handle = isClassExpression as any; - -export function isHeritageClause(node: Node): node is HeritageClause { - return node.kind === SyntaxKind.HeritageClause; -} - -export declare namespace isHeritageClause { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isHeritageClause.Handle = isHeritageClause as any; - -export function isInterfaceDeclaration(node: Node): node is InterfaceDeclaration { - return node.kind === SyntaxKind.InterfaceDeclaration; -} - -export declare namespace isInterfaceDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isInterfaceDeclaration.Handle = isInterfaceDeclaration as any; - -export function isTypeAliasDeclaration(node: Node): node is TypeAliasDeclaration { - switch (node.kind) { - case SyntaxKind.TypeAliasDeclaration: - case SyntaxKind.JSTypeAliasDeclaration: - return true; - default: - return false; - } -} - -export declare namespace isTypeAliasDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isTypeAliasDeclaration.Handle = isTypeAliasDeclaration as any; - -export function isEnumMember(node: Node): node is EnumMember { - return node.kind === SyntaxKind.EnumMember; -} - -export declare namespace isEnumMember { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isEnumMember.Handle = isEnumMember as any; - -export function isEnumDeclaration(node: Node): node is EnumDeclaration { - return node.kind === SyntaxKind.EnumDeclaration; -} - -export declare namespace isEnumDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isEnumDeclaration.Handle = isEnumDeclaration as any; - -export function isModuleBlock(node: Node): node is ModuleBlock { - return node.kind === SyntaxKind.ModuleBlock; -} - -export declare namespace isModuleBlock { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isModuleBlock.Handle = isModuleBlock as any; - -export function isNotEmittedStatement(node: Node): node is NotEmittedStatement { - return node.kind === SyntaxKind.NotEmittedStatement; -} - -export declare namespace isNotEmittedStatement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isNotEmittedStatement.Handle = isNotEmittedStatement as any; - -export function isNotEmittedTypeElement(node: Node): node is NotEmittedTypeElement { - return node.kind === SyntaxKind.NotEmittedTypeElement; -} - -export declare namespace isNotEmittedTypeElement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isNotEmittedTypeElement.Handle = isNotEmittedTypeElement as any; - -export function isImportDeclaration(node: Node): node is ImportDeclaration { - switch (node.kind) { - case SyntaxKind.ImportDeclaration: - case SyntaxKind.JSImportDeclaration: - return true; - default: - return false; - } -} - -export declare namespace isImportDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isImportDeclaration.Handle = isImportDeclaration as any; - -export function isExternalModuleReference(node: Node): node is ExternalModuleReference { - return node.kind === SyntaxKind.ExternalModuleReference; -} - -export declare namespace isExternalModuleReference { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isExternalModuleReference.Handle = isExternalModuleReference as any; - -export function isNamespaceImport(node: Node): node is NamespaceImport { - return node.kind === SyntaxKind.NamespaceImport; -} - -export declare namespace isNamespaceImport { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isNamespaceImport.Handle = isNamespaceImport as any; - -export function isNamedImports(node: Node): node is NamedImports { - return node.kind === SyntaxKind.NamedImports; -} - -export declare namespace isNamedImports { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isNamedImports.Handle = isNamedImports as any; - -export function isExportAssignment(node: Node): node is ExportAssignment { - return node.kind === SyntaxKind.ExportAssignment; -} - -export declare namespace isExportAssignment { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isExportAssignment.Handle = isExportAssignment as any; - -export function isNamespaceExportDeclaration(node: Node): node is NamespaceExportDeclaration { - return node.kind === SyntaxKind.NamespaceExportDeclaration; -} - -export declare namespace isNamespaceExportDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isNamespaceExportDeclaration.Handle = isNamespaceExportDeclaration as any; - -export function isNamespaceExport(node: Node): node is NamespaceExport { - return node.kind === SyntaxKind.NamespaceExport; -} - -export declare namespace isNamespaceExport { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isNamespaceExport.Handle = isNamespaceExport as any; - -export function isNamedExports(node: Node): node is NamedExports { - return node.kind === SyntaxKind.NamedExports; -} - -export declare namespace isNamedExports { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isNamedExports.Handle = isNamedExports as any; - -export function isExportSpecifier(node: Node): node is ExportSpecifier { - return node.kind === SyntaxKind.ExportSpecifier; -} - -export declare namespace isExportSpecifier { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isExportSpecifier.Handle = isExportSpecifier as any; - -export function isCallSignatureDeclaration(node: Node): node is CallSignatureDeclaration { - return node.kind === SyntaxKind.CallSignature; -} - -export declare namespace isCallSignatureDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isCallSignatureDeclaration.Handle = isCallSignatureDeclaration as any; - -export function isConstructSignatureDeclaration(node: Node): node is ConstructSignatureDeclaration { - return node.kind === SyntaxKind.ConstructSignature; -} - -export declare namespace isConstructSignatureDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isConstructSignatureDeclaration.Handle = isConstructSignatureDeclaration as any; - -export function isConstructorDeclaration(node: Node): node is ConstructorDeclaration { - return node.kind === SyntaxKind.Constructor; -} - -export declare namespace isConstructorDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isConstructorDeclaration.Handle = isConstructorDeclaration as any; - -export function isGetAccessorDeclaration(node: Node): node is GetAccessorDeclaration { - return node.kind === SyntaxKind.GetAccessor; -} - -export declare namespace isGetAccessorDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isGetAccessorDeclaration.Handle = isGetAccessorDeclaration as any; - -export function isSetAccessorDeclaration(node: Node): node is SetAccessorDeclaration { - return node.kind === SyntaxKind.SetAccessor; -} - -export declare namespace isSetAccessorDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isSetAccessorDeclaration.Handle = isSetAccessorDeclaration as any; - -export function isIndexSignatureDeclaration(node: Node): node is IndexSignatureDeclaration { - return node.kind === SyntaxKind.IndexSignature; -} - -export declare namespace isIndexSignatureDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isIndexSignatureDeclaration.Handle = isIndexSignatureDeclaration as any; - -export function isMethodSignatureDeclaration(node: Node): node is MethodSignatureDeclaration { - return node.kind === SyntaxKind.MethodSignature; -} - -export declare namespace isMethodSignatureDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isMethodSignatureDeclaration.Handle = isMethodSignatureDeclaration as any; - -export function isMethodDeclaration(node: Node): node is MethodDeclaration { - return node.kind === SyntaxKind.MethodDeclaration; -} - -export declare namespace isMethodDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isMethodDeclaration.Handle = isMethodDeclaration as any; - -export function isPropertySignatureDeclaration(node: Node): node is PropertySignatureDeclaration { - return node.kind === SyntaxKind.PropertySignature; -} - -export declare namespace isPropertySignatureDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isPropertySignatureDeclaration.Handle = isPropertySignatureDeclaration as any; - -export function isPropertyDeclaration(node: Node): node is PropertyDeclaration { - return node.kind === SyntaxKind.PropertyDeclaration; -} - -export declare namespace isPropertyDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isPropertyDeclaration.Handle = isPropertyDeclaration as any; - -export function isSemicolonClassElement(node: Node): node is SemicolonClassElement { - return node.kind === SyntaxKind.SemicolonClassElement; -} - -export declare namespace isSemicolonClassElement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isSemicolonClassElement.Handle = isSemicolonClassElement as any; - -export function isClassStaticBlockDeclaration(node: Node): node is ClassStaticBlockDeclaration { - return node.kind === SyntaxKind.ClassStaticBlockDeclaration; -} - -export declare namespace isClassStaticBlockDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isClassStaticBlockDeclaration.Handle = isClassStaticBlockDeclaration as any; - -export function isOmittedExpression(node: Node): node is OmittedExpression { - return node.kind === SyntaxKind.OmittedExpression; -} - -export declare namespace isOmittedExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isOmittedExpression.Handle = isOmittedExpression as any; - -export function isKeywordExpression(node: Node): node is KeywordExpression { - return isKeywordExpressionKind(node.kind); -} - -export declare namespace isKeywordExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isKeywordExpression.Handle = isKeywordExpression as any; - -export function isStringLiteral(node: Node): node is StringLiteral { - return node.kind === SyntaxKind.StringLiteral; -} - -export declare namespace isStringLiteral { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isStringLiteral.Handle = isStringLiteral as any; - -export function isNumericLiteral(node: Node): node is NumericLiteral { - return node.kind === SyntaxKind.NumericLiteral; -} - -export declare namespace isNumericLiteral { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isNumericLiteral.Handle = isNumericLiteral as any; - -export function isBigIntLiteral(node: Node): node is BigIntLiteral { - return node.kind === SyntaxKind.BigIntLiteral; -} - -export declare namespace isBigIntLiteral { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isBigIntLiteral.Handle = isBigIntLiteral as any; - -export function isRegularExpressionLiteral(node: Node): node is RegularExpressionLiteral { - return node.kind === SyntaxKind.RegularExpressionLiteral; -} - -export declare namespace isRegularExpressionLiteral { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isRegularExpressionLiteral.Handle = isRegularExpressionLiteral as any; - -export function isNoSubstitutionTemplateLiteral(node: Node): node is NoSubstitutionTemplateLiteral { - return node.kind === SyntaxKind.NoSubstitutionTemplateLiteral; -} - -export declare namespace isNoSubstitutionTemplateLiteral { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isNoSubstitutionTemplateLiteral.Handle = isNoSubstitutionTemplateLiteral as any; - -export function isBinaryExpression(node: Node): node is BinaryExpression { - return node.kind === SyntaxKind.BinaryExpression; -} - -export declare namespace isBinaryExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isBinaryExpression.Handle = isBinaryExpression as any; - -export function isPrefixUnaryExpression(node: Node): node is PrefixUnaryExpression { - return node.kind === SyntaxKind.PrefixUnaryExpression; -} - -export declare namespace isPrefixUnaryExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isPrefixUnaryExpression.Handle = isPrefixUnaryExpression as any; - -export function isPostfixUnaryExpression(node: Node): node is PostfixUnaryExpression { - return node.kind === SyntaxKind.PostfixUnaryExpression; -} - -export declare namespace isPostfixUnaryExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isPostfixUnaryExpression.Handle = isPostfixUnaryExpression as any; - -export function isYieldExpression(node: Node): node is YieldExpression { - return node.kind === SyntaxKind.YieldExpression; -} - -export declare namespace isYieldExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isYieldExpression.Handle = isYieldExpression as any; - -export function isArrowFunction(node: Node): node is ArrowFunction { - return node.kind === SyntaxKind.ArrowFunction; -} - -export declare namespace isArrowFunction { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isArrowFunction.Handle = isArrowFunction as any; - -export function isFunctionExpression(node: Node): node is FunctionExpression { - return node.kind === SyntaxKind.FunctionExpression; -} - -export declare namespace isFunctionExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isFunctionExpression.Handle = isFunctionExpression as any; - -export function isAsExpression(node: Node): node is AsExpression { - return node.kind === SyntaxKind.AsExpression; -} - -export declare namespace isAsExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isAsExpression.Handle = isAsExpression as any; - -export function isSatisfiesExpression(node: Node): node is SatisfiesExpression { - return node.kind === SyntaxKind.SatisfiesExpression; -} - -export declare namespace isSatisfiesExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isSatisfiesExpression.Handle = isSatisfiesExpression as any; - -export function isConditionalExpression(node: Node): node is ConditionalExpression { - return node.kind === SyntaxKind.ConditionalExpression; -} - -export declare namespace isConditionalExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isConditionalExpression.Handle = isConditionalExpression as any; - -export function isPropertyAccessExpression(node: Node): node is PropertyAccessExpression { - return node.kind === SyntaxKind.PropertyAccessExpression; -} - -export declare namespace isPropertyAccessExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isPropertyAccessExpression.Handle = isPropertyAccessExpression as any; - -export function isElementAccessExpression(node: Node): node is ElementAccessExpression { - return node.kind === SyntaxKind.ElementAccessExpression; -} - -export declare namespace isElementAccessExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isElementAccessExpression.Handle = isElementAccessExpression as any; - -export function isCallExpression(node: Node): node is CallExpression { - return node.kind === SyntaxKind.CallExpression; -} - -export declare namespace isCallExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isCallExpression.Handle = isCallExpression as any; - -export function isNewExpression(node: Node): node is NewExpression { - return node.kind === SyntaxKind.NewExpression; -} - -export declare namespace isNewExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isNewExpression.Handle = isNewExpression as any; - -export function isMetaProperty(node: Node): node is MetaProperty { - return node.kind === SyntaxKind.MetaProperty; -} - -export declare namespace isMetaProperty { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isMetaProperty.Handle = isMetaProperty as any; - -export function isNonNullExpression(node: Node): node is NonNullExpression { - return node.kind === SyntaxKind.NonNullExpression; -} - -export declare namespace isNonNullExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isNonNullExpression.Handle = isNonNullExpression as any; - -export function isSpreadElement(node: Node): node is SpreadElement { - return node.kind === SyntaxKind.SpreadElement; -} - -export declare namespace isSpreadElement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isSpreadElement.Handle = isSpreadElement as any; - -export function isTemplateExpression(node: Node): node is TemplateExpression { - return node.kind === SyntaxKind.TemplateExpression; -} - -export declare namespace isTemplateExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isTemplateExpression.Handle = isTemplateExpression as any; - -export function isTemplateSpan(node: Node): node is TemplateSpan { - return node.kind === SyntaxKind.TemplateSpan; -} - -export declare namespace isTemplateSpan { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isTemplateSpan.Handle = isTemplateSpan as any; - -export function isTaggedTemplateExpression(node: Node): node is TaggedTemplateExpression { - return node.kind === SyntaxKind.TaggedTemplateExpression; -} - -export declare namespace isTaggedTemplateExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isTaggedTemplateExpression.Handle = isTaggedTemplateExpression as any; - -export function isParenthesizedExpression(node: Node): node is ParenthesizedExpression { - return node.kind === SyntaxKind.ParenthesizedExpression; -} - -export declare namespace isParenthesizedExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isParenthesizedExpression.Handle = isParenthesizedExpression as any; - -export function isArrayLiteralExpression(node: Node): node is ArrayLiteralExpression { - return node.kind === SyntaxKind.ArrayLiteralExpression; -} - -export declare namespace isArrayLiteralExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isArrayLiteralExpression.Handle = isArrayLiteralExpression as any; - -export function isObjectLiteralExpression(node: Node): node is ObjectLiteralExpression { - return node.kind === SyntaxKind.ObjectLiteralExpression; -} - -export declare namespace isObjectLiteralExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isObjectLiteralExpression.Handle = isObjectLiteralExpression as any; - -export function isSpreadAssignment(node: Node): node is SpreadAssignment { - return node.kind === SyntaxKind.SpreadAssignment; -} - -export declare namespace isSpreadAssignment { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isSpreadAssignment.Handle = isSpreadAssignment as any; - -export function isPropertyAssignment(node: Node): node is PropertyAssignment { - return node.kind === SyntaxKind.PropertyAssignment; -} - -export declare namespace isPropertyAssignment { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isPropertyAssignment.Handle = isPropertyAssignment as any; - -export function isShorthandPropertyAssignment(node: Node): node is ShorthandPropertyAssignment { - return node.kind === SyntaxKind.ShorthandPropertyAssignment; -} - -export declare namespace isShorthandPropertyAssignment { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isShorthandPropertyAssignment.Handle = isShorthandPropertyAssignment as any; - -export function isDeleteExpression(node: Node): node is DeleteExpression { - return node.kind === SyntaxKind.DeleteExpression; -} - -export declare namespace isDeleteExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isDeleteExpression.Handle = isDeleteExpression as any; - -export function isTypeOfExpression(node: Node): node is TypeOfExpression { - return node.kind === SyntaxKind.TypeOfExpression; -} - -export declare namespace isTypeOfExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isTypeOfExpression.Handle = isTypeOfExpression as any; - -export function isVoidExpression(node: Node): node is VoidExpression { - return node.kind === SyntaxKind.VoidExpression; -} - -export declare namespace isVoidExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isVoidExpression.Handle = isVoidExpression as any; - -export function isAwaitExpression(node: Node): node is AwaitExpression { - return node.kind === SyntaxKind.AwaitExpression; -} - -export declare namespace isAwaitExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isAwaitExpression.Handle = isAwaitExpression as any; - -export function isTypeAssertion(node: Node): node is TypeAssertion { - return node.kind === SyntaxKind.TypeAssertionExpression; -} - -export declare namespace isTypeAssertion { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isTypeAssertion.Handle = isTypeAssertion as any; - -export function isKeywordTypeNode(node: Node): node is KeywordTypeNode { - return isKeywordTypeKind(node.kind); -} - -export declare namespace isKeywordTypeNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isKeywordTypeNode.Handle = isKeywordTypeNode as any; - -export function isUnionTypeNode(node: Node): node is UnionTypeNode { - return node.kind === SyntaxKind.UnionType; -} - -export declare namespace isUnionTypeNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isUnionTypeNode.Handle = isUnionTypeNode as any; - -export function isIntersectionTypeNode(node: Node): node is IntersectionTypeNode { - return node.kind === SyntaxKind.IntersectionType; -} - -export declare namespace isIntersectionTypeNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isIntersectionTypeNode.Handle = isIntersectionTypeNode as any; - -export function isConditionalTypeNode(node: Node): node is ConditionalTypeNode { - return node.kind === SyntaxKind.ConditionalType; -} - -export declare namespace isConditionalTypeNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isConditionalTypeNode.Handle = isConditionalTypeNode as any; - -export function isTypeOperatorNode(node: Node): node is TypeOperatorNode { - return node.kind === SyntaxKind.TypeOperator; -} - -export declare namespace isTypeOperatorNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isTypeOperatorNode.Handle = isTypeOperatorNode as any; - -export function isInferTypeNode(node: Node): node is InferTypeNode { - return node.kind === SyntaxKind.InferType; -} - -export declare namespace isInferTypeNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isInferTypeNode.Handle = isInferTypeNode as any; - -export function isArrayTypeNode(node: Node): node is ArrayTypeNode { - return node.kind === SyntaxKind.ArrayType; -} - -export declare namespace isArrayTypeNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isArrayTypeNode.Handle = isArrayTypeNode as any; - -export function isIndexedAccessTypeNode(node: Node): node is IndexedAccessTypeNode { - return node.kind === SyntaxKind.IndexedAccessType; -} - -export declare namespace isIndexedAccessTypeNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isIndexedAccessTypeNode.Handle = isIndexedAccessTypeNode as any; - -export function isTypeReferenceNode(node: Node): node is TypeReferenceNode { - return node.kind === SyntaxKind.TypeReference; -} - -export declare namespace isTypeReferenceNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isTypeReferenceNode.Handle = isTypeReferenceNode as any; - -export function isExpressionWithTypeArguments(node: Node): node is ExpressionWithTypeArguments { - return node.kind === SyntaxKind.ExpressionWithTypeArguments; -} - -export declare namespace isExpressionWithTypeArguments { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isExpressionWithTypeArguments.Handle = isExpressionWithTypeArguments as any; - -export function isLiteralTypeNode(node: Node): node is LiteralTypeNode { - return node.kind === SyntaxKind.LiteralType; -} - -export declare namespace isLiteralTypeNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isLiteralTypeNode.Handle = isLiteralTypeNode as any; - -export function isThisTypeNode(node: Node): node is ThisTypeNode { - return node.kind === SyntaxKind.ThisType; -} - -export declare namespace isThisTypeNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isThisTypeNode.Handle = isThisTypeNode as any; - -export function isTypePredicateNode(node: Node): node is TypePredicateNode { - return node.kind === SyntaxKind.TypePredicate; -} - -export declare namespace isTypePredicateNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isTypePredicateNode.Handle = isTypePredicateNode as any; - -export function isImportAttribute(node: Node): node is ImportAttribute { - return node.kind === SyntaxKind.ImportAttribute; -} - -export declare namespace isImportAttribute { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isImportAttribute.Handle = isImportAttribute as any; - -export function isImportAttributes(node: Node): node is ImportAttributes { - return node.kind === SyntaxKind.ImportAttributes; -} - -export declare namespace isImportAttributes { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isImportAttributes.Handle = isImportAttributes as any; - -export function isTypeQueryNode(node: Node): node is TypeQueryNode { - return node.kind === SyntaxKind.TypeQuery; -} - -export declare namespace isTypeQueryNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isTypeQueryNode.Handle = isTypeQueryNode as any; - -export function isMappedTypeNode(node: Node): node is MappedTypeNode { - return node.kind === SyntaxKind.MappedType; -} - -export declare namespace isMappedTypeNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isMappedTypeNode.Handle = isMappedTypeNode as any; - -export function isTypeLiteralNode(node: Node): node is TypeLiteralNode { - return node.kind === SyntaxKind.TypeLiteral; -} - -export declare namespace isTypeLiteralNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isTypeLiteralNode.Handle = isTypeLiteralNode as any; - -export function isTupleTypeNode(node: Node): node is TupleTypeNode { - return node.kind === SyntaxKind.TupleType; -} - -export declare namespace isTupleTypeNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isTupleTypeNode.Handle = isTupleTypeNode as any; - -export function isNamedTupleMember(node: Node): node is NamedTupleMember { - return node.kind === SyntaxKind.NamedTupleMember; -} - -export declare namespace isNamedTupleMember { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isNamedTupleMember.Handle = isNamedTupleMember as any; - -export function isOptionalTypeNode(node: Node): node is OptionalTypeNode { - return node.kind === SyntaxKind.OptionalType; -} - -export declare namespace isOptionalTypeNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isOptionalTypeNode.Handle = isOptionalTypeNode as any; - -export function isRestTypeNode(node: Node): node is RestTypeNode { - return node.kind === SyntaxKind.RestType; -} - -export declare namespace isRestTypeNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isRestTypeNode.Handle = isRestTypeNode as any; - -export function isParenthesizedTypeNode(node: Node): node is ParenthesizedTypeNode { - return node.kind === SyntaxKind.ParenthesizedType; -} - -export declare namespace isParenthesizedTypeNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isParenthesizedTypeNode.Handle = isParenthesizedTypeNode as any; - -export function isFunctionTypeNode(node: Node): node is FunctionTypeNode { - return node.kind === SyntaxKind.FunctionType; -} - -export declare namespace isFunctionTypeNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isFunctionTypeNode.Handle = isFunctionTypeNode as any; - -export function isConstructorTypeNode(node: Node): node is ConstructorTypeNode { - return node.kind === SyntaxKind.ConstructorType; -} - -export declare namespace isConstructorTypeNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isConstructorTypeNode.Handle = isConstructorTypeNode as any; - -export function isTemplateHead(node: Node): node is TemplateHead { - return node.kind === SyntaxKind.TemplateHead; -} - -export declare namespace isTemplateHead { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isTemplateHead.Handle = isTemplateHead as any; - -export function isTemplateMiddle(node: Node): node is TemplateMiddle { - return node.kind === SyntaxKind.TemplateMiddle; -} - -export declare namespace isTemplateMiddle { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isTemplateMiddle.Handle = isTemplateMiddle as any; - -export function isTemplateTail(node: Node): node is TemplateTail { - return node.kind === SyntaxKind.TemplateTail; -} - -export declare namespace isTemplateTail { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isTemplateTail.Handle = isTemplateTail as any; - -export function isTemplateLiteralTypeNode(node: Node): node is TemplateLiteralTypeNode { - return node.kind === SyntaxKind.TemplateLiteralType; -} - -export declare namespace isTemplateLiteralTypeNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isTemplateLiteralTypeNode.Handle = isTemplateLiteralTypeNode as any; - -export function isTemplateLiteralTypeSpan(node: Node): node is TemplateLiteralTypeSpan { - return node.kind === SyntaxKind.TemplateLiteralTypeSpan; -} - -export declare namespace isTemplateLiteralTypeSpan { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isTemplateLiteralTypeSpan.Handle = isTemplateLiteralTypeSpan as any; - -export function isSyntheticExpression(node: Node): node is SyntheticExpression { - return node.kind === SyntaxKind.SyntheticExpression; -} - -export declare namespace isSyntheticExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isSyntheticExpression.Handle = isSyntheticExpression as any; - -export function isPartiallyEmittedExpression(node: Node): node is PartiallyEmittedExpression { - return node.kind === SyntaxKind.PartiallyEmittedExpression; -} - -export declare namespace isPartiallyEmittedExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isPartiallyEmittedExpression.Handle = isPartiallyEmittedExpression as any; - -export function isJsxElement(node: Node): node is JsxElement { - return node.kind === SyntaxKind.JsxElement; -} - -export declare namespace isJsxElement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJsxElement.Handle = isJsxElement as any; - -export function isJsxAttributes(node: Node): node is JsxAttributes { - return node.kind === SyntaxKind.JsxAttributes; -} - -export declare namespace isJsxAttributes { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJsxAttributes.Handle = isJsxAttributes as any; - -export function isJsxNamespacedName(node: Node): node is JsxNamespacedName { - return node.kind === SyntaxKind.JsxNamespacedName; -} - -export declare namespace isJsxNamespacedName { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJsxNamespacedName.Handle = isJsxNamespacedName as any; - -export function isJsxOpeningElement(node: Node): node is JsxOpeningElement { - return node.kind === SyntaxKind.JsxOpeningElement; -} - -export declare namespace isJsxOpeningElement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJsxOpeningElement.Handle = isJsxOpeningElement as any; - -export function isJsxSelfClosingElement(node: Node): node is JsxSelfClosingElement { - return node.kind === SyntaxKind.JsxSelfClosingElement; -} - -export declare namespace isJsxSelfClosingElement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJsxSelfClosingElement.Handle = isJsxSelfClosingElement as any; - -export function isJsxFragment(node: Node): node is JsxFragment { - return node.kind === SyntaxKind.JsxFragment; -} - -export declare namespace isJsxFragment { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJsxFragment.Handle = isJsxFragment as any; - -export function isJsxOpeningFragment(node: Node): node is JsxOpeningFragment { - return node.kind === SyntaxKind.JsxOpeningFragment; -} - -export declare namespace isJsxOpeningFragment { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJsxOpeningFragment.Handle = isJsxOpeningFragment as any; - -export function isJsxClosingFragment(node: Node): node is JsxClosingFragment { - return node.kind === SyntaxKind.JsxClosingFragment; -} - -export declare namespace isJsxClosingFragment { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJsxClosingFragment.Handle = isJsxClosingFragment as any; - -export function isJsxAttribute(node: Node): node is JsxAttribute { - return node.kind === SyntaxKind.JsxAttribute; -} - -export declare namespace isJsxAttribute { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJsxAttribute.Handle = isJsxAttribute as any; - -export function isJsxSpreadAttribute(node: Node): node is JsxSpreadAttribute { - return node.kind === SyntaxKind.JsxSpreadAttribute; -} - -export declare namespace isJsxSpreadAttribute { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJsxSpreadAttribute.Handle = isJsxSpreadAttribute as any; - -export function isJsxClosingElement(node: Node): node is JsxClosingElement { - return node.kind === SyntaxKind.JsxClosingElement; -} - -export declare namespace isJsxClosingElement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJsxClosingElement.Handle = isJsxClosingElement as any; - -export function isJsxExpression(node: Node): node is JsxExpression { - return node.kind === SyntaxKind.JsxExpression; -} - -export declare namespace isJsxExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJsxExpression.Handle = isJsxExpression as any; - -export function isJsxText(node: Node): node is JsxText { - return node.kind === SyntaxKind.JsxText; -} - -export declare namespace isJsxText { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJsxText.Handle = isJsxText as any; - -export function isSyntaxList(node: Node): node is SyntaxList { - return node.kind === SyntaxKind.SyntaxList; -} - -export declare namespace isSyntaxList { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isSyntaxList.Handle = isSyntaxList as any; - -export function isJSDoc(node: Node): node is JSDoc { - return node.kind === SyntaxKind.JSDoc; -} - -export declare namespace isJSDoc { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDoc.Handle = isJSDoc as any; - -export function isJSDocTypeExpression(node: Node): node is JSDocTypeExpression { - return node.kind === SyntaxKind.JSDocTypeExpression; -} - -export declare namespace isJSDocTypeExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocTypeExpression.Handle = isJSDocTypeExpression as any; - -export function isJSDocNonNullableType(node: Node): node is JSDocNonNullableType { - return node.kind === SyntaxKind.JSDocNonNullableType; -} - -export declare namespace isJSDocNonNullableType { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocNonNullableType.Handle = isJSDocNonNullableType as any; - -export function isJSDocNullableType(node: Node): node is JSDocNullableType { - return node.kind === SyntaxKind.JSDocNullableType; -} - -export declare namespace isJSDocNullableType { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocNullableType.Handle = isJSDocNullableType as any; - -export function isJSDocAllType(node: Node): node is JSDocAllType { - return node.kind === SyntaxKind.JSDocAllType; -} - -export declare namespace isJSDocAllType { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocAllType.Handle = isJSDocAllType as any; - -export function isJSDocVariadicType(node: Node): node is JSDocVariadicType { - return node.kind === SyntaxKind.JSDocVariadicType; -} - -export declare namespace isJSDocVariadicType { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocVariadicType.Handle = isJSDocVariadicType as any; - -export function isJSDocOptionalType(node: Node): node is JSDocOptionalType { - return node.kind === SyntaxKind.JSDocOptionalType; -} - -export declare namespace isJSDocOptionalType { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocOptionalType.Handle = isJSDocOptionalType as any; - -export function isJSDocTypeTag(node: Node): node is JSDocTypeTag { - return node.kind === SyntaxKind.JSDocTypeTag; -} - -export declare namespace isJSDocTypeTag { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocTypeTag.Handle = isJSDocTypeTag as any; - -export function isJSDocUnknownTag(node: Node): node is JSDocUnknownTag { - return node.kind === SyntaxKind.JSDocUnknownTag; -} - -export declare namespace isJSDocUnknownTag { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocUnknownTag.Handle = isJSDocUnknownTag as any; - -export function isJSDocTemplateTag(node: Node): node is JSDocTemplateTag { - return node.kind === SyntaxKind.JSDocTemplateTag; -} - -export declare namespace isJSDocTemplateTag { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocTemplateTag.Handle = isJSDocTemplateTag as any; - -export function isJSDocReturnTag(node: Node): node is JSDocReturnTag { - return node.kind === SyntaxKind.JSDocReturnTag; -} - -export declare namespace isJSDocReturnTag { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocReturnTag.Handle = isJSDocReturnTag as any; - -export function isJSDocPublicTag(node: Node): node is JSDocPublicTag { - return node.kind === SyntaxKind.JSDocPublicTag; -} - -export declare namespace isJSDocPublicTag { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocPublicTag.Handle = isJSDocPublicTag as any; - -export function isJSDocPrivateTag(node: Node): node is JSDocPrivateTag { - return node.kind === SyntaxKind.JSDocPrivateTag; -} - -export declare namespace isJSDocPrivateTag { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocPrivateTag.Handle = isJSDocPrivateTag as any; - -export function isJSDocProtectedTag(node: Node): node is JSDocProtectedTag { - return node.kind === SyntaxKind.JSDocProtectedTag; -} - -export declare namespace isJSDocProtectedTag { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocProtectedTag.Handle = isJSDocProtectedTag as any; - -export function isJSDocReadonlyTag(node: Node): node is JSDocReadonlyTag { - return node.kind === SyntaxKind.JSDocReadonlyTag; -} - -export declare namespace isJSDocReadonlyTag { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocReadonlyTag.Handle = isJSDocReadonlyTag as any; - -export function isJSDocOverrideTag(node: Node): node is JSDocOverrideTag { - return node.kind === SyntaxKind.JSDocOverrideTag; -} - -export declare namespace isJSDocOverrideTag { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocOverrideTag.Handle = isJSDocOverrideTag as any; - -export function isJSDocDeprecatedTag(node: Node): node is JSDocDeprecatedTag { - return node.kind === SyntaxKind.JSDocDeprecatedTag; -} - -export declare namespace isJSDocDeprecatedTag { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocDeprecatedTag.Handle = isJSDocDeprecatedTag as any; - -export function isJSDocSeeTag(node: Node): node is JSDocSeeTag { - return node.kind === SyntaxKind.JSDocSeeTag; -} - -export declare namespace isJSDocSeeTag { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocSeeTag.Handle = isJSDocSeeTag as any; - -export function isJSDocImplementsTag(node: Node): node is JSDocImplementsTag { - return node.kind === SyntaxKind.JSDocImplementsTag; -} - -export declare namespace isJSDocImplementsTag { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocImplementsTag.Handle = isJSDocImplementsTag as any; - -export function isJSDocAugmentsTag(node: Node): node is JSDocAugmentsTag { - return node.kind === SyntaxKind.JSDocAugmentsTag; -} - -export declare namespace isJSDocAugmentsTag { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocAugmentsTag.Handle = isJSDocAugmentsTag as any; - -export function isJSDocSatisfiesTag(node: Node): node is JSDocSatisfiesTag { - return node.kind === SyntaxKind.JSDocSatisfiesTag; -} - -export declare namespace isJSDocSatisfiesTag { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocSatisfiesTag.Handle = isJSDocSatisfiesTag as any; - -export function isJSDocThrowsTag(node: Node): node is JSDocThrowsTag { - return node.kind === SyntaxKind.JSDocThrowsTag; -} - -export declare namespace isJSDocThrowsTag { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocThrowsTag.Handle = isJSDocThrowsTag as any; - -export function isJSDocThisTag(node: Node): node is JSDocThisTag { - return node.kind === SyntaxKind.JSDocThisTag; -} - -export declare namespace isJSDocThisTag { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocThisTag.Handle = isJSDocThisTag as any; - -export function isJSDocImportTag(node: Node): node is JSDocImportTag { - return node.kind === SyntaxKind.JSDocImportTag; -} - -export declare namespace isJSDocImportTag { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocImportTag.Handle = isJSDocImportTag as any; - -export function isJSDocCallbackTag(node: Node): node is JSDocCallbackTag { - return node.kind === SyntaxKind.JSDocCallbackTag; -} - -export declare namespace isJSDocCallbackTag { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocCallbackTag.Handle = isJSDocCallbackTag as any; - -export function isJSDocOverloadTag(node: Node): node is JSDocOverloadTag { - return node.kind === SyntaxKind.JSDocOverloadTag; -} - -export declare namespace isJSDocOverloadTag { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocOverloadTag.Handle = isJSDocOverloadTag as any; - -export function isJSDocTypedefTag(node: Node): node is JSDocTypedefTag { - return node.kind === SyntaxKind.JSDocTypedefTag; -} - -export declare namespace isJSDocTypedefTag { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocTypedefTag.Handle = isJSDocTypedefTag as any; - -export function isJSDocSignature(node: Node): node is JSDocSignature { - return node.kind === SyntaxKind.JSDocSignature; -} - -export declare namespace isJSDocSignature { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocSignature.Handle = isJSDocSignature as any; - -export function isJSDocNameReference(node: Node): node is JSDocNameReference { - return node.kind === SyntaxKind.JSDocNameReference; -} - -export declare namespace isJSDocNameReference { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocNameReference.Handle = isJSDocNameReference as any; - -export function isSourceFile(node: Node): node is SourceFile { - return node.kind === SyntaxKind.SourceFile; -} - -export declare namespace isSourceFile { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isSourceFile.Handle = isSourceFile as any; - -export function isModuleDeclaration(node: Node): node is ModuleDeclaration { - return node.kind === SyntaxKind.ModuleDeclaration; -} - -export declare namespace isModuleDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isModuleDeclaration.Handle = isModuleDeclaration as any; - -export function isImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration { - return node.kind === SyntaxKind.ImportEqualsDeclaration; -} - -export declare namespace isImportEqualsDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isImportEqualsDeclaration.Handle = isImportEqualsDeclaration as any; - -export function isExportDeclaration(node: Node): node is ExportDeclaration { - return node.kind === SyntaxKind.ExportDeclaration; -} - -export declare namespace isExportDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isExportDeclaration.Handle = isExportDeclaration as any; - -export function isImportTypeNode(node: Node): node is ImportTypeNode { - return node.kind === SyntaxKind.ImportType; -} - -export declare namespace isImportTypeNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isImportTypeNode.Handle = isImportTypeNode as any; - -export function isImportClause(node: Node): node is ImportClause { - return node.kind === SyntaxKind.ImportClause; -} - -export declare namespace isImportClause { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isImportClause.Handle = isImportClause as any; - -export function isImportSpecifier(node: Node): node is ImportSpecifier { - return node.kind === SyntaxKind.ImportSpecifier; -} - -export declare namespace isImportSpecifier { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isImportSpecifier.Handle = isImportSpecifier as any; - -export function isJSDocText(node: Node): node is JSDocText { - return node.kind === SyntaxKind.JSDocText; -} - -export declare namespace isJSDocText { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocText.Handle = isJSDocText as any; - -export function isJSDocLink(node: Node): node is JSDocLink { - return node.kind === SyntaxKind.JSDocLink; -} - -export declare namespace isJSDocLink { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocLink.Handle = isJSDocLink as any; - -export function isJSDocLinkPlain(node: Node): node is JSDocLinkPlain { - return node.kind === SyntaxKind.JSDocLinkPlain; -} - -export declare namespace isJSDocLinkPlain { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocLinkPlain.Handle = isJSDocLinkPlain as any; - -export function isJSDocLinkCode(node: Node): node is JSDocLinkCode { - return node.kind === SyntaxKind.JSDocLinkCode; -} - -export declare namespace isJSDocLinkCode { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocLinkCode.Handle = isJSDocLinkCode as any; - -export function isTypeParameterDeclaration(node: Node): node is TypeParameterDeclaration { - return node.kind === SyntaxKind.TypeParameter; -} - -export declare namespace isTypeParameterDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isTypeParameterDeclaration.Handle = isTypeParameterDeclaration as any; - -export function isSyntheticReferenceExpression(node: Node): node is SyntheticReferenceExpression { - return node.kind === SyntaxKind.SyntheticReferenceExpression; -} - -export declare namespace isSyntheticReferenceExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isSyntheticReferenceExpression.Handle = isSyntheticReferenceExpression as any; - -export function isJSDocTypeLiteral(node: Node): node is JSDocTypeLiteral { - return node.kind === SyntaxKind.JSDocTypeLiteral; -} - -export declare namespace isJSDocTypeLiteral { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocTypeLiteral.Handle = isJSDocTypeLiteral as any; - -export function isForInStatement(node: Node): node is ForInStatement { - return node.kind === SyntaxKind.ForInStatement; -} - -export declare namespace isForInStatement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isForInStatement.Handle = isForInStatement as any; - -export function isForOfStatement(node: Node): node is ForOfStatement { - return node.kind === SyntaxKind.ForOfStatement; -} - -export declare namespace isForOfStatement { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isForOfStatement.Handle = isForOfStatement as any; - -export function isCaseClause(node: Node): node is CaseClause { - return node.kind === SyntaxKind.CaseClause; -} - -export declare namespace isCaseClause { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isCaseClause.Handle = isCaseClause as any; - -export function isDefaultClause(node: Node): node is DefaultClause { - return node.kind === SyntaxKind.DefaultClause; -} - -export declare namespace isDefaultClause { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isDefaultClause.Handle = isDefaultClause as any; - -export function isObjectBindingPattern(node: Node): node is ObjectBindingPattern { - return node.kind === SyntaxKind.ObjectBindingPattern; -} - -export declare namespace isObjectBindingPattern { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isObjectBindingPattern.Handle = isObjectBindingPattern as any; - -export function isArrayBindingPattern(node: Node): node is ArrayBindingPattern { - return node.kind === SyntaxKind.ArrayBindingPattern; -} - -export declare namespace isArrayBindingPattern { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isArrayBindingPattern.Handle = isArrayBindingPattern as any; - -export function isJSDocParameterTag(node: Node): node is JSDocParameterTag { - return node.kind === SyntaxKind.JSDocParameterTag; -} - -export declare namespace isJSDocParameterTag { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocParameterTag.Handle = isJSDocParameterTag as any; - -export function isJSDocPropertyTag(node: Node): node is JSDocPropertyTag { - return node.kind === SyntaxKind.JSDocPropertyTag; -} - -export declare namespace isJSDocPropertyTag { - function Handle>(node: T): node is SpecializeNodeHandle; -} -isJSDocPropertyTag.Handle = isJSDocPropertyTag as any; - -export function isHeritageClauseElement(node: Node): node is HeritageClauseElement { - return node.kind === SyntaxKind.ExpressionWithTypeArguments || node.kind === SyntaxKind.TypeReference; -} - -export declare namespace isHeritageClauseElement { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isHeritageClauseElement.Handle = isHeritageClauseElement as any; - -export function isAccessExpression(node: Node): node is AccessExpression { - return node.kind === SyntaxKind.PropertyAccessExpression || node.kind === SyntaxKind.ElementAccessExpression; -} - -export declare namespace isAccessExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isAccessExpression.Handle = isAccessExpression as any; - -export function isDeclarationName(node: Node): node is DeclarationName { - const kind = node.kind; - return kind === SyntaxKind.Identifier || kind === SyntaxKind.PrivateIdentifier || kind === SyntaxKind.StringLiteral || kind === SyntaxKind.NumericLiteral || kind === SyntaxKind.BigIntLiteral || kind === SyntaxKind.NoSubstitutionTemplateLiteral || kind === SyntaxKind.ComputedPropertyName || kind === SyntaxKind.ObjectBindingPattern || kind === SyntaxKind.ArrayBindingPattern || kind === SyntaxKind.ElementAccessExpression; -} - -export declare namespace isDeclarationName { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isDeclarationName.Handle = isDeclarationName as any; - -export function isModuleName(node: Node): node is ModuleName { - return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.StringLiteral; -} - -export declare namespace isModuleName { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isModuleName.Handle = isModuleName as any; - -export function isModuleExportName(node: Node): node is ModuleExportName { - return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.StringLiteral; -} - -export declare namespace isModuleExportName { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isModuleExportName.Handle = isModuleExportName as any; - -export function isPropertyName(node: Node): node is PropertyName { - const kind = node.kind; - return kind === SyntaxKind.Identifier || kind === SyntaxKind.StringLiteral || kind === SyntaxKind.NoSubstitutionTemplateLiteral || kind === SyntaxKind.NumericLiteral || kind === SyntaxKind.ComputedPropertyName || kind === SyntaxKind.PrivateIdentifier || kind === SyntaxKind.BigIntLiteral; -} - -export declare namespace isPropertyName { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isPropertyName.Handle = isPropertyName as any; - -export function isModuleBody(node: Node): node is ModuleBody { - return node.kind === SyntaxKind.ModuleBlock || node.kind === SyntaxKind.ModuleDeclaration; -} - -export declare namespace isModuleBody { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isModuleBody.Handle = isModuleBody as any; - -export function isJSDocFullName(node: Node): node is JSDocFullName { - return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ModuleDeclaration; -} - -export declare namespace isJSDocFullName { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isJSDocFullName.Handle = isJSDocFullName as any; - -export function isModuleReference(node: Node): node is ModuleReference { - return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.ExternalModuleReference; -} - -export declare namespace isModuleReference { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isModuleReference.Handle = isModuleReference as any; - -export function isNamedImportBindings(node: Node): node is NamedImportBindings { - return node.kind === SyntaxKind.NamespaceImport || node.kind === SyntaxKind.NamedImports; -} - -export declare namespace isNamedImportBindings { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isNamedImportBindings.Handle = isNamedImportBindings as any; - -export function isNamedExportBindings(node: Node): node is NamedExportBindings { - return node.kind === SyntaxKind.NamespaceExport || node.kind === SyntaxKind.NamedExports; -} - -export declare namespace isNamedExportBindings { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isNamedExportBindings.Handle = isNamedExportBindings as any; - -export function isMemberName(node: Node): node is MemberName { - return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PrivateIdentifier; -} - -export declare namespace isMemberName { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isMemberName.Handle = isMemberName as any; - -export function isEntityName(node: Node): node is EntityName { - return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName; -} - -export declare namespace isEntityName { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isEntityName.Handle = isEntityName as any; - -export function isBindingName(node: Node): node is BindingName { - return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ObjectBindingPattern || node.kind === SyntaxKind.ArrayBindingPattern; -} - -export declare namespace isBindingName { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isBindingName.Handle = isBindingName as any; - -export function isModifierLike(node: Node): node is ModifierLike { - const kind = node.kind; - return kind === SyntaxKind.AbstractKeyword || kind === SyntaxKind.AccessorKeyword || kind === SyntaxKind.AsyncKeyword || kind === SyntaxKind.ConstKeyword || kind === SyntaxKind.DeclareKeyword || kind === SyntaxKind.DefaultKeyword || kind === SyntaxKind.ExportKeyword || kind === SyntaxKind.InKeyword || kind === SyntaxKind.PrivateKeyword || kind === SyntaxKind.ProtectedKeyword || kind === SyntaxKind.PublicKeyword || kind === SyntaxKind.ReadonlyKeyword || kind === SyntaxKind.OutKeyword || kind === SyntaxKind.OverrideKeyword || kind === SyntaxKind.StaticKeyword || kind === SyntaxKind.Decorator; -} - -export declare namespace isModifierLike { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isModifierLike.Handle = isModifierLike as any; - -export function isJsxChild(node: Node): node is JsxChild { - const kind = node.kind; - return kind === SyntaxKind.JsxText || kind === SyntaxKind.JsxExpression || kind === SyntaxKind.JsxElement || kind === SyntaxKind.JsxSelfClosingElement || kind === SyntaxKind.JsxFragment; -} - -export declare namespace isJsxChild { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isJsxChild.Handle = isJsxChild as any; - -export function isJsxAttributeLike(node: Node): node is JsxAttributeLike { - return node.kind === SyntaxKind.JsxAttribute || node.kind === SyntaxKind.JsxSpreadAttribute; -} - -export declare namespace isJsxAttributeLike { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isJsxAttributeLike.Handle = isJsxAttributeLike as any; - -export function isJsxAttributeName(node: Node): node is JsxAttributeName { - return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.JsxNamespacedName; -} - -export declare namespace isJsxAttributeName { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isJsxAttributeName.Handle = isJsxAttributeName as any; - -export function isJsxAttributeValue(node: Node): node is JsxAttributeValue { - const kind = node.kind; - return kind === SyntaxKind.StringLiteral || kind === SyntaxKind.JsxExpression || kind === SyntaxKind.JsxElement || kind === SyntaxKind.JsxSelfClosingElement || kind === SyntaxKind.JsxFragment; -} - -export declare namespace isJsxAttributeValue { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isJsxAttributeValue.Handle = isJsxAttributeValue as any; - -export function isClassLikeDeclaration(node: Node): node is ClassLikeDeclaration { - return node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression; -} - -export declare namespace isClassLikeDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isClassLikeDeclaration.Handle = isClassLikeDeclaration as any; - -export function isAccessorDeclaration(node: Node): node is AccessorDeclaration { - return node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.SetAccessor; -} - -export declare namespace isAccessorDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isAccessorDeclaration.Handle = isAccessorDeclaration as any; - -export function isLiteralLikeNode(node: Node): node is LiteralLikeNode { - const kind = node.kind; - return kind === SyntaxKind.StringLiteral || kind === SyntaxKind.NumericLiteral || kind === SyntaxKind.BigIntLiteral || kind === SyntaxKind.RegularExpressionLiteral || kind === SyntaxKind.TemplateHead || kind === SyntaxKind.TemplateMiddle || kind === SyntaxKind.TemplateTail || kind === SyntaxKind.JsxText; -} - -export declare namespace isLiteralLikeNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isLiteralLikeNode.Handle = isLiteralLikeNode as any; - -export function isLiteralExpression(node: Node): node is LiteralExpression { - const kind = node.kind; - return kind === SyntaxKind.StringLiteral || kind === SyntaxKind.NumericLiteral || kind === SyntaxKind.BigIntLiteral || kind === SyntaxKind.RegularExpressionLiteral || kind === SyntaxKind.NoSubstitutionTemplateLiteral; -} - -export declare namespace isLiteralExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isLiteralExpression.Handle = isLiteralExpression as any; - -export function isUnionOrIntersectionTypeNode(node: Node): node is UnionOrIntersectionTypeNode { - return node.kind === SyntaxKind.UnionType || node.kind === SyntaxKind.IntersectionType; -} - -export declare namespace isUnionOrIntersectionTypeNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isUnionOrIntersectionTypeNode.Handle = isUnionOrIntersectionTypeNode as any; - -export function isTemplateLiteralLikeNode(node: Node): node is TemplateLiteralLikeNode { - const kind = node.kind; - return isPseudoLiteralKind(kind); -} - -export declare namespace isTemplateLiteralLikeNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isTemplateLiteralLikeNode.Handle = isTemplateLiteralLikeNode as any; - -export function isTemplateMiddleOrTail(node: Node): node is TemplateMiddleOrTail { - return node.kind === SyntaxKind.TemplateMiddle || node.kind === SyntaxKind.TemplateTail; -} - -export declare namespace isTemplateMiddleOrTail { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isTemplateMiddleOrTail.Handle = isTemplateMiddleOrTail as any; - -export function isTemplateLiteral(node: Node): node is TemplateLiteral { - return node.kind === SyntaxKind.TemplateExpression || node.kind === SyntaxKind.NoSubstitutionTemplateLiteral; -} - -export declare namespace isTemplateLiteral { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isTemplateLiteral.Handle = isTemplateLiteral as any; - -export function isTypePredicateParameterName(node: Node): node is TypePredicateParameterName { - return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisType; -} - -export declare namespace isTypePredicateParameterName { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isTypePredicateParameterName.Handle = isTypePredicateParameterName as any; - -export function isImportAttributeName(node: Node): node is ImportAttributeName { - return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.StringLiteral; -} - -export declare namespace isImportAttributeName { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isImportAttributeName.Handle = isImportAttributeName as any; - -export function isJSDocComment(node: Node): node is JSDocComment { - const kind = node.kind; - return kind === SyntaxKind.JSDocText || kind === SyntaxKind.JSDocLink || kind === SyntaxKind.JSDocLinkCode || kind === SyntaxKind.JSDocLinkPlain; -} - -export declare namespace isJSDocComment { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isJSDocComment.Handle = isJSDocComment as any; - -export function isSignatureDeclaration(node: Node): node is SignatureDeclaration { - const kind = node.kind; - return kind === SyntaxKind.CallSignature || kind === SyntaxKind.ConstructSignature || kind === SyntaxKind.MethodSignature || kind === SyntaxKind.IndexSignature || kind === SyntaxKind.FunctionType || kind === SyntaxKind.ConstructorType || kind === SyntaxKind.FunctionDeclaration || kind === SyntaxKind.MethodDeclaration || kind === SyntaxKind.Constructor || kind === SyntaxKind.GetAccessor || kind === SyntaxKind.SetAccessor || kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction; -} - -export declare namespace isSignatureDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isSignatureDeclaration.Handle = isSignatureDeclaration as any; - -export function isStringLiteralLikeNode(node: Node): node is StringLiteralLikeNode { - return node.kind === SyntaxKind.StringLiteral || node.kind === SyntaxKind.NoSubstitutionTemplateLiteral; -} - -export declare namespace isStringLiteralLikeNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isStringLiteralLikeNode.Handle = isStringLiteralLikeNode as any; - -export function isNumericOrStringLikeLiteral(node: Node): node is NumericOrStringLikeLiteral { - return node.kind === SyntaxKind.StringLiteral || node.kind === SyntaxKind.NoSubstitutionTemplateLiteral || node.kind === SyntaxKind.NumericLiteral; -} - -export declare namespace isNumericOrStringLikeLiteral { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isNumericOrStringLikeLiteral.Handle = isNumericOrStringLikeLiteral as any; - -export function isObjectLiteralLikeNode(node: Node): node is ObjectLiteralLikeNode { - return node.kind === SyntaxKind.ObjectLiteralExpression || node.kind === SyntaxKind.ObjectBindingPattern; -} - -export declare namespace isObjectLiteralLikeNode { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isObjectLiteralLikeNode.Handle = isObjectLiteralLikeNode as any; - -export function isObjectTypeDeclaration(node: Node): node is ObjectTypeDeclaration { - const kind = node.kind; - return kind === SyntaxKind.ClassDeclaration || kind === SyntaxKind.ClassExpression || kind === SyntaxKind.InterfaceDeclaration || kind === SyntaxKind.TypeLiteral; -} - -export declare namespace isObjectTypeDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isObjectTypeDeclaration.Handle = isObjectTypeDeclaration as any; - -export function isJsxOpeningLikeElement(node: Node): node is JsxOpeningLikeElement { - return node.kind === SyntaxKind.JsxOpeningElement || node.kind === SyntaxKind.JsxSelfClosingElement; -} - -export declare namespace isJsxOpeningLikeElement { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isJsxOpeningLikeElement.Handle = isJsxOpeningLikeElement as any; - -export function isNamedImportsOrExports(node: Node): node is NamedImportsOrExports { - return node.kind === SyntaxKind.NamedImports || node.kind === SyntaxKind.NamedExports; -} - -export declare namespace isNamedImportsOrExports { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isNamedImportsOrExports.Handle = isNamedImportsOrExports as any; - -export function isBreakOrContinueStatement(node: Node): node is BreakOrContinueStatement { - return node.kind === SyntaxKind.BreakStatement || node.kind === SyntaxKind.ContinueStatement; -} - -export declare namespace isBreakOrContinueStatement { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isBreakOrContinueStatement.Handle = isBreakOrContinueStatement as any; - -export function isCallLikeExpression(node: Node): node is CallLikeExpression { - const kind = node.kind; - return kind === SyntaxKind.CallExpression || kind === SyntaxKind.NewExpression || kind === SyntaxKind.TaggedTemplateExpression || kind === SyntaxKind.Decorator || kind === SyntaxKind.JsxOpeningElement || kind === SyntaxKind.JsxSelfClosingElement || kind === SyntaxKind.BinaryExpression; -} - -export declare namespace isCallLikeExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isCallLikeExpression.Handle = isCallLikeExpression as any; - -export function isFunctionLikeDeclaration(node: Node): node is FunctionLikeDeclaration { - const kind = node.kind; - return kind === SyntaxKind.FunctionDeclaration || kind === SyntaxKind.MethodDeclaration || kind === SyntaxKind.GetAccessor || kind === SyntaxKind.SetAccessor || kind === SyntaxKind.Constructor || kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction; -} - -export declare namespace isFunctionLikeDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isFunctionLikeDeclaration.Handle = isFunctionLikeDeclaration as any; - -export function isVariableOrParameterDeclaration(node: Node): node is VariableOrParameterDeclaration { - return node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.Parameter; -} - -export declare namespace isVariableOrParameterDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isVariableOrParameterDeclaration.Handle = isVariableOrParameterDeclaration as any; - -export function isVariableOrPropertyDeclaration(node: Node): node is VariableOrPropertyDeclaration { - return node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.PropertyDeclaration; -} - -export declare namespace isVariableOrPropertyDeclaration { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isVariableOrPropertyDeclaration.Handle = isVariableOrPropertyDeclaration as any; - -export function isCallOrNewExpression(node: Node): node is CallOrNewExpression { - return node.kind === SyntaxKind.CallExpression || node.kind === SyntaxKind.NewExpression; -} - -export declare namespace isCallOrNewExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isCallOrNewExpression.Handle = isCallOrNewExpression as any; - -export function isImportClauseOrBindingPattern(node: Node): node is ImportClauseOrBindingPattern { - return node.kind === SyntaxKind.ImportClause || node.kind === SyntaxKind.ObjectBindingPattern || node.kind === SyntaxKind.ArrayBindingPattern; -} - -export declare namespace isImportClauseOrBindingPattern { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isImportClauseOrBindingPattern.Handle = isImportClauseOrBindingPattern as any; - -export function isAnyImportSyntax(node: Node): node is AnyImportSyntax { - return node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.JSImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration; -} - -export declare namespace isAnyImportSyntax { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isAnyImportSyntax.Handle = isAnyImportSyntax as any; - -export function isArrayBindingElement(node: Node): node is ArrayBindingElement { - return node.kind === SyntaxKind.BindingElement || node.kind === SyntaxKind.OmittedExpression; -} - -export declare namespace isArrayBindingElement { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isArrayBindingElement.Handle = isArrayBindingElement as any; - -export function isAssertionExpression(node: Node): node is AssertionExpression { - return node.kind === SyntaxKind.TypeAssertionExpression || node.kind === SyntaxKind.AsExpression; -} - -export declare namespace isAssertionExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isAssertionExpression.Handle = isAssertionExpression as any; - -export function isBooleanLiteral(node: Node): node is BooleanLiteral { - return node.kind === SyntaxKind.TrueKeyword || node.kind === SyntaxKind.FalseKeyword; -} - -export declare namespace isBooleanLiteral { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isBooleanLiteral.Handle = isBooleanLiteral as any; - -export function isDestructuringAssignment(node: Node): node is DestructuringAssignment { - return node.kind === SyntaxKind.BinaryExpression; -} - -export declare namespace isDestructuringAssignment { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isDestructuringAssignment.Handle = isDestructuringAssignment as any; - -export function isLiteralToken(node: Node): node is LiteralToken { - const kind = node.kind; - return kind === SyntaxKind.NumericLiteral || kind === SyntaxKind.BigIntLiteral || kind === SyntaxKind.StringLiteral || kind === SyntaxKind.JsxText || kind === SyntaxKind.RegularExpressionLiteral || kind === SyntaxKind.NoSubstitutionTemplateLiteral; -} - -export declare namespace isLiteralToken { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isLiteralToken.Handle = isLiteralToken as any; - -export function isModifier(node: Node): node is Modifier { - const kind = node.kind; - return isModifierKind(kind); -} - -export declare namespace isModifier { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isModifier.Handle = isModifier as any; - -export function isObjectLiteralElementLike(node: Node): node is ObjectLiteralElementLike { - const kind = node.kind; - return kind === SyntaxKind.PropertyAssignment || kind === SyntaxKind.ShorthandPropertyAssignment || kind === SyntaxKind.SpreadAssignment || kind === SyntaxKind.MethodDeclaration || kind === SyntaxKind.GetAccessor || kind === SyntaxKind.SetAccessor; -} - -export declare namespace isObjectLiteralElementLike { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isObjectLiteralElementLike.Handle = isObjectLiteralElementLike as any; - -export function isPropertyNameLiteral(node: Node): node is PropertyNameLiteral { - return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.StringLiteral || node.kind === SyntaxKind.NumericLiteral; -} - -export declare namespace isPropertyNameLiteral { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isPropertyNameLiteral.Handle = isPropertyNameLiteral as any; - -export function isPseudoLiteralToken(node: Node): node is PseudoLiteralToken { - const kind = node.kind; - return isPseudoLiteralKind(kind); -} - -export declare namespace isPseudoLiteralToken { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isPseudoLiteralToken.Handle = isPseudoLiteralToken as any; - -export function isTemplateLiteralToken(node: Node): node is TemplateLiteralToken { - const kind = node.kind; - return kind === SyntaxKind.NoSubstitutionTemplateLiteral || kind === SyntaxKind.TemplateHead || kind === SyntaxKind.TemplateMiddle || kind === SyntaxKind.TemplateTail; -} - -export declare namespace isTemplateLiteralToken { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isTemplateLiteralToken.Handle = isTemplateLiteralToken as any; - -export function isArrayDestructuringAssignment(node: Node): node is ArrayDestructuringAssignment { - return node.kind === SyntaxKind.BinaryExpression; -} - -export declare namespace isArrayDestructuringAssignment { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isArrayDestructuringAssignment.Handle = isArrayDestructuringAssignment as any; - -export function isObjectDestructuringAssignment(node: Node): node is ObjectDestructuringAssignment { - return node.kind === SyntaxKind.BinaryExpression; -} - -export declare namespace isObjectDestructuringAssignment { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isObjectDestructuringAssignment.Handle = isObjectDestructuringAssignment as any; - -export function isFunctionBody(node: Node): node is FunctionBody { - return node.kind === SyntaxKind.Block; -} - -export declare namespace isFunctionBody { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isFunctionBody.Handle = isFunctionBody as any; - -export function isTriviaKind(kind: SyntaxKind): kind is TriviaSyntaxKind { - return kind === SyntaxKind.SingleLineCommentTrivia - || kind === SyntaxKind.MultiLineCommentTrivia - || kind === SyntaxKind.NewLineTrivia - || kind === SyntaxKind.WhitespaceTrivia - || kind === SyntaxKind.ConflictMarkerTrivia; -} - -export function isPseudoLiteralKind(kind: SyntaxKind): kind is PseudoLiteralSyntaxKind { - return kind === SyntaxKind.TemplateHead - || kind === SyntaxKind.TemplateMiddle - || kind === SyntaxKind.TemplateTail; -} - -export function isModifierKind(kind: SyntaxKind): kind is ModifierSyntaxKind { - return kind === SyntaxKind.AbstractKeyword - || kind === SyntaxKind.AccessorKeyword - || kind === SyntaxKind.AsyncKeyword - || kind === SyntaxKind.ConstKeyword - || kind === SyntaxKind.DeclareKeyword - || kind === SyntaxKind.DefaultKeyword - || kind === SyntaxKind.ExportKeyword - || kind === SyntaxKind.InKeyword - || kind === SyntaxKind.PrivateKeyword - || kind === SyntaxKind.ProtectedKeyword - || kind === SyntaxKind.PublicKeyword - || kind === SyntaxKind.ReadonlyKeyword - || kind === SyntaxKind.OutKeyword - || kind === SyntaxKind.OverrideKeyword - || kind === SyntaxKind.StaticKeyword; -} - -export function isKeywordTypeKind(kind: SyntaxKind): kind is KeywordTypeSyntaxKind { - return kind === SyntaxKind.AnyKeyword - || kind === SyntaxKind.BigIntKeyword - || kind === SyntaxKind.BooleanKeyword - || kind === SyntaxKind.IntrinsicKeyword - || kind === SyntaxKind.NeverKeyword - || kind === SyntaxKind.NumberKeyword - || kind === SyntaxKind.ObjectKeyword - || kind === SyntaxKind.StringKeyword - || kind === SyntaxKind.SymbolKeyword - || kind === SyntaxKind.UndefinedKeyword - || kind === SyntaxKind.UnknownKeyword - || kind === SyntaxKind.VoidKeyword; -} - -export function isKeywordExpressionKind(kind: SyntaxKind): kind is KeywordExpressionSyntaxKind { - return kind === SyntaxKind.NullKeyword - || kind === SyntaxKind.TrueKeyword - || kind === SyntaxKind.FalseKeyword - || kind === SyntaxKind.ThisKeyword - || kind === SyntaxKind.SuperKeyword - || kind === SyntaxKind.ImportKeyword; -} - -export function isJsxTokenKind(kind: SyntaxKind): kind is JsxTokenSyntaxKind { - return kind === SyntaxKind.LessThanSlashToken - || kind === SyntaxKind.EndOfFile - || kind === SyntaxKind.ConflictMarkerTrivia - || kind === SyntaxKind.JsxText - || kind === SyntaxKind.JsxTextAllWhiteSpaces - || kind === SyntaxKind.OpenBraceToken - || kind === SyntaxKind.LessThanToken; -} - -export function isImportPhaseModifierKind(kind: SyntaxKind): kind is ImportPhaseModifierSyntaxKind { - return kind === SyntaxKind.TypeKeyword - || kind === SyntaxKind.DeferKeyword; -} - -export function isPostfixUnaryOperator(kind: SyntaxKind): kind is PostfixUnaryOperator { - return kind === SyntaxKind.PlusPlusToken - || kind === SyntaxKind.MinusMinusToken; -} - -export function isPrefixUnaryOperator(kind: SyntaxKind): kind is PrefixUnaryOperator { - return kind === SyntaxKind.PlusToken - || kind === SyntaxKind.MinusToken - || kind === SyntaxKind.TildeToken - || kind === SyntaxKind.ExclamationToken - || kind === SyntaxKind.PlusPlusToken - || kind === SyntaxKind.MinusMinusToken; -} - -export function isAssignmentOperator(kind: SyntaxKind): kind is AssignmentOperator { - return kind === SyntaxKind.EqualsToken - || isCompoundAssignmentOperator(kind); -} - -export function isBinaryOperator(kind: SyntaxKind): kind is BinaryOperator { - return isAssignmentOperatorOrHigher(kind) - || kind === SyntaxKind.CommaToken; -} - -export function isExponentiationOperator(kind: SyntaxKind): kind is ExponentiationOperator { - return kind === SyntaxKind.AsteriskAsteriskToken; -} - -export function isMultiplicativeOperator(kind: SyntaxKind): kind is MultiplicativeOperator { - return kind === SyntaxKind.AsteriskToken - || kind === SyntaxKind.SlashToken - || kind === SyntaxKind.PercentToken; -} - -export function isMultiplicativeOperatorOrHigher(kind: SyntaxKind): kind is MultiplicativeOperatorOrHigher { - return isExponentiationOperator(kind) - || isMultiplicativeOperator(kind); -} - -export function isAdditiveOperator(kind: SyntaxKind): kind is AdditiveOperator { - return kind === SyntaxKind.PlusToken - || kind === SyntaxKind.MinusToken; -} - -export function isAdditiveOperatorOrHigher(kind: SyntaxKind): kind is AdditiveOperatorOrHigher { - return isMultiplicativeOperatorOrHigher(kind) - || isAdditiveOperator(kind); -} - -export function isShiftOperator(kind: SyntaxKind): kind is ShiftOperator { - return kind === SyntaxKind.LessThanLessThanToken - || kind === SyntaxKind.GreaterThanGreaterThanToken - || kind === SyntaxKind.GreaterThanGreaterThanGreaterThanToken; -} - -export function isShiftOperatorOrHigher(kind: SyntaxKind): kind is ShiftOperatorOrHigher { - return isAdditiveOperatorOrHigher(kind) - || isShiftOperator(kind); -} - -export function isRelationalOperator(kind: SyntaxKind): kind is RelationalOperator { - return kind === SyntaxKind.LessThanToken - || kind === SyntaxKind.LessThanEqualsToken - || kind === SyntaxKind.GreaterThanToken - || kind === SyntaxKind.GreaterThanEqualsToken - || kind === SyntaxKind.InstanceOfKeyword - || kind === SyntaxKind.InKeyword; -} - -export function isRelationalOperatorOrHigher(kind: SyntaxKind): kind is RelationalOperatorOrHigher { - return isShiftOperatorOrHigher(kind) - || isRelationalOperator(kind); -} - -export function isEqualityOperator(kind: SyntaxKind): kind is EqualityOperator { - return kind === SyntaxKind.EqualsEqualsToken - || kind === SyntaxKind.EqualsEqualsEqualsToken - || kind === SyntaxKind.ExclamationEqualsEqualsToken - || kind === SyntaxKind.ExclamationEqualsToken; -} - -export function isEqualityOperatorOrHigher(kind: SyntaxKind): kind is EqualityOperatorOrHigher { - return isRelationalOperatorOrHigher(kind) - || isEqualityOperator(kind); -} - -export function isBitwiseOperator(kind: SyntaxKind): kind is BitwiseOperator { - return kind === SyntaxKind.AmpersandToken - || kind === SyntaxKind.BarToken - || kind === SyntaxKind.CaretToken; -} - -export function isBitwiseOperatorOrHigher(kind: SyntaxKind): kind is BitwiseOperatorOrHigher { - return isEqualityOperatorOrHigher(kind) - || isBitwiseOperator(kind); -} - -export function isLogicalOperator(kind: SyntaxKind): kind is LogicalOperator { - return kind === SyntaxKind.AmpersandAmpersandToken - || kind === SyntaxKind.BarBarToken; -} - -export function isLogicalOperatorOrHigher(kind: SyntaxKind): kind is LogicalOperatorOrHigher { - return isBitwiseOperatorOrHigher(kind) - || isLogicalOperator(kind); -} - -export function isCompoundAssignmentOperator(kind: SyntaxKind): kind is CompoundAssignmentOperator { - return kind === SyntaxKind.PlusEqualsToken - || kind === SyntaxKind.MinusEqualsToken - || kind === SyntaxKind.AsteriskAsteriskEqualsToken - || kind === SyntaxKind.AsteriskEqualsToken - || kind === SyntaxKind.SlashEqualsToken - || kind === SyntaxKind.PercentEqualsToken - || kind === SyntaxKind.AmpersandEqualsToken - || kind === SyntaxKind.BarEqualsToken - || kind === SyntaxKind.CaretEqualsToken - || kind === SyntaxKind.LessThanLessThanEqualsToken - || kind === SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken - || kind === SyntaxKind.GreaterThanGreaterThanEqualsToken - || kind === SyntaxKind.BarBarEqualsToken - || kind === SyntaxKind.AmpersandAmpersandEqualsToken - || kind === SyntaxKind.QuestionQuestionEqualsToken; -} - -export function isAssignmentOperatorOrHigher(kind: SyntaxKind): kind is AssignmentOperatorOrHigher { - return kind === SyntaxKind.QuestionQuestionToken - || isLogicalOperatorOrHigher(kind) - || isAssignmentOperator(kind); -} - -export function isLogicalOrCoalescingAssignmentOperator(kind: SyntaxKind): kind is LogicalOrCoalescingAssignmentOperator { - return kind === SyntaxKind.AmpersandAmpersandEqualsToken - || kind === SyntaxKind.BarBarEqualsToken - || kind === SyntaxKind.QuestionQuestionEqualsToken; -} - -export function isLiteralKind(kind: SyntaxKind): boolean { - return kind >= SyntaxKind.FirstLiteralToken && kind <= SyntaxKind.LastLiteralToken; -} - -export function isPunctuationKind(kind: SyntaxKind): boolean { - return kind >= SyntaxKind.FirstPunctuation && kind <= SyntaxKind.LastPunctuation; -} - -export function isKeywordKind(kind: SyntaxKind): boolean { - return kind >= SyntaxKind.FirstKeyword && kind <= SyntaxKind.LastKeyword; -} - -export function isTokenKind(kind: SyntaxKind): boolean { - return kind >= SyntaxKind.FirstToken && kind <= SyntaxKind.LastToken; -} - -export function isJSDocNodeKind(kind: SyntaxKind): boolean { - return kind >= SyntaxKind.FirstJSDocNode && kind <= SyntaxKind.LastJSDocNode; -} - -export function isEndOfFile(node: Node): node is EndOfFile { - return node.kind === SyntaxKind.EndOfFile; -} - -export declare namespace isEndOfFile { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isEndOfFile.Handle = isEndOfFile as any; - -export function isDotToken(node: Node): node is DotToken { - return node.kind === SyntaxKind.DotToken; -} - -export declare namespace isDotToken { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isDotToken.Handle = isDotToken as any; - -export function isDotDotDotToken(node: Node): node is DotDotDotToken { - return node.kind === SyntaxKind.DotDotDotToken; -} - -export declare namespace isDotDotDotToken { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isDotDotDotToken.Handle = isDotDotDotToken as any; - -export function isQuestionToken(node: Node): node is QuestionToken { - return node.kind === SyntaxKind.QuestionToken; -} - -export declare namespace isQuestionToken { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isQuestionToken.Handle = isQuestionToken as any; - -export function isExclamationToken(node: Node): node is ExclamationToken { - return node.kind === SyntaxKind.ExclamationToken; -} - -export declare namespace isExclamationToken { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isExclamationToken.Handle = isExclamationToken as any; - -export function isColonToken(node: Node): node is ColonToken { - return node.kind === SyntaxKind.ColonToken; -} - -export declare namespace isColonToken { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isColonToken.Handle = isColonToken as any; - -export function isEqualsToken(node: Node): node is EqualsToken { - return node.kind === SyntaxKind.EqualsToken; -} - -export declare namespace isEqualsToken { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isEqualsToken.Handle = isEqualsToken as any; - -export function isAsteriskToken(node: Node): node is AsteriskToken { - return node.kind === SyntaxKind.AsteriskToken; -} - -export declare namespace isAsteriskToken { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isAsteriskToken.Handle = isAsteriskToken as any; - -export function isEqualsGreaterThanToken(node: Node): node is EqualsGreaterThanToken { - return node.kind === SyntaxKind.EqualsGreaterThanToken; -} - -export declare namespace isEqualsGreaterThanToken { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isEqualsGreaterThanToken.Handle = isEqualsGreaterThanToken as any; - -export function isPlusToken(node: Node): node is PlusToken { - return node.kind === SyntaxKind.PlusToken; -} - -export declare namespace isPlusToken { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isPlusToken.Handle = isPlusToken as any; - -export function isMinusToken(node: Node): node is MinusToken { - return node.kind === SyntaxKind.MinusToken; -} - -export declare namespace isMinusToken { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isMinusToken.Handle = isMinusToken as any; - -export function isQuestionDotToken(node: Node): node is QuestionDotToken { - return node.kind === SyntaxKind.QuestionDotToken; -} - -export declare namespace isQuestionDotToken { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isQuestionDotToken.Handle = isQuestionDotToken as any; - -export function isAssertsKeyword(node: Node): node is AssertsKeyword { - return node.kind === SyntaxKind.AssertsKeyword; -} - -export declare namespace isAssertsKeyword { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isAssertsKeyword.Handle = isAssertsKeyword as any; - -export function isAssertKeyword(node: Node): node is AssertKeyword { - return node.kind === SyntaxKind.AssertKeyword; -} - -export declare namespace isAssertKeyword { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isAssertKeyword.Handle = isAssertKeyword as any; - -export function isAwaitKeyword(node: Node): node is AwaitKeyword { - return node.kind === SyntaxKind.AwaitKeyword; -} - -export declare namespace isAwaitKeyword { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isAwaitKeyword.Handle = isAwaitKeyword as any; - -export function isCaseKeyword(node: Node): node is CaseKeyword { - return node.kind === SyntaxKind.CaseKeyword; -} - -export declare namespace isCaseKeyword { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isCaseKeyword.Handle = isCaseKeyword as any; - -export function isAbstractKeyword(node: Node): node is AbstractKeyword { - return node.kind === SyntaxKind.AbstractKeyword; -} - -export declare namespace isAbstractKeyword { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isAbstractKeyword.Handle = isAbstractKeyword as any; - -export function isAccessorKeyword(node: Node): node is AccessorKeyword { - return node.kind === SyntaxKind.AccessorKeyword; -} - -export declare namespace isAccessorKeyword { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isAccessorKeyword.Handle = isAccessorKeyword as any; - -export function isAsyncKeyword(node: Node): node is AsyncKeyword { - return node.kind === SyntaxKind.AsyncKeyword; -} - -export declare namespace isAsyncKeyword { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isAsyncKeyword.Handle = isAsyncKeyword as any; - -export function isConstKeyword(node: Node): node is ConstKeyword { - return node.kind === SyntaxKind.ConstKeyword; -} - -export declare namespace isConstKeyword { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isConstKeyword.Handle = isConstKeyword as any; - -export function isDeclareKeyword(node: Node): node is DeclareKeyword { - return node.kind === SyntaxKind.DeclareKeyword; -} - -export declare namespace isDeclareKeyword { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isDeclareKeyword.Handle = isDeclareKeyword as any; - -export function isDefaultKeyword(node: Node): node is DefaultKeyword { - return node.kind === SyntaxKind.DefaultKeyword; -} - -export declare namespace isDefaultKeyword { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isDefaultKeyword.Handle = isDefaultKeyword as any; - -export function isExportKeyword(node: Node): node is ExportKeyword { - return node.kind === SyntaxKind.ExportKeyword; -} - -export declare namespace isExportKeyword { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isExportKeyword.Handle = isExportKeyword as any; - -export function isInKeyword(node: Node): node is InKeyword { - return node.kind === SyntaxKind.InKeyword; -} - -export declare namespace isInKeyword { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isInKeyword.Handle = isInKeyword as any; - -export function isPrivateKeyword(node: Node): node is PrivateKeyword { - return node.kind === SyntaxKind.PrivateKeyword; -} - -export declare namespace isPrivateKeyword { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isPrivateKeyword.Handle = isPrivateKeyword as any; - -export function isProtectedKeyword(node: Node): node is ProtectedKeyword { - return node.kind === SyntaxKind.ProtectedKeyword; -} - -export declare namespace isProtectedKeyword { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isProtectedKeyword.Handle = isProtectedKeyword as any; - -export function isPublicKeyword(node: Node): node is PublicKeyword { - return node.kind === SyntaxKind.PublicKeyword; -} - -export declare namespace isPublicKeyword { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isPublicKeyword.Handle = isPublicKeyword as any; - -export function isReadonlyKeyword(node: Node): node is ReadonlyKeyword { - return node.kind === SyntaxKind.ReadonlyKeyword; -} - -export declare namespace isReadonlyKeyword { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isReadonlyKeyword.Handle = isReadonlyKeyword as any; - -export function isOutKeyword(node: Node): node is OutKeyword { - return node.kind === SyntaxKind.OutKeyword; -} - -export declare namespace isOutKeyword { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isOutKeyword.Handle = isOutKeyword as any; - -export function isOverrideKeyword(node: Node): node is OverrideKeyword { - return node.kind === SyntaxKind.OverrideKeyword; -} - -export declare namespace isOverrideKeyword { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isOverrideKeyword.Handle = isOverrideKeyword as any; - -export function isStaticKeyword(node: Node): node is StaticKeyword { - return node.kind === SyntaxKind.StaticKeyword; -} - -export declare namespace isStaticKeyword { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isStaticKeyword.Handle = isStaticKeyword as any; - -export function isBinaryOperatorToken(node: Node): node is BinaryOperatorToken { - return isBinaryOperator(node.kind); -} - -export declare namespace isBinaryOperatorToken { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isBinaryOperatorToken.Handle = isBinaryOperatorToken as any; - -export function isAssignmentOperatorToken(node: Node): node is AssignmentOperatorToken { - return isAssignmentOperator(node.kind); -} - -export declare namespace isAssignmentOperatorToken { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isAssignmentOperatorToken.Handle = isAssignmentOperatorToken as any; - -export function isNullLiteral(node: Node): node is NullLiteral { - return node.kind === SyntaxKind.NullKeyword; -} - -export declare namespace isNullLiteral { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isNullLiteral.Handle = isNullLiteral as any; - -export function isTrueLiteral(node: Node): node is TrueLiteral { - return node.kind === SyntaxKind.TrueKeyword; -} - -export declare namespace isTrueLiteral { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isTrueLiteral.Handle = isTrueLiteral as any; - -export function isFalseLiteral(node: Node): node is FalseLiteral { - return node.kind === SyntaxKind.FalseKeyword; -} - -export declare namespace isFalseLiteral { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isFalseLiteral.Handle = isFalseLiteral as any; - -export function isThisExpression(node: Node): node is ThisExpression { - return node.kind === SyntaxKind.ThisKeyword; -} - -export declare namespace isThisExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isThisExpression.Handle = isThisExpression as any; - -export function isSuperExpression(node: Node): node is SuperExpression { - return node.kind === SyntaxKind.SuperKeyword; -} - -export declare namespace isSuperExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isSuperExpression.Handle = isSuperExpression as any; - -export function isImportExpression(node: Node): node is ImportExpression { - return node.kind === SyntaxKind.ImportKeyword; -} - -export declare namespace isImportExpression { - function Handle>(node: T): node is SpecializeNodeHandle; -} - -isImportExpression.Handle = isImportExpression as any; +// Code generated by tools/scripts/tsc/generate-ts-ast.ts. DO NOT EDIT. + +import { SyntaxKind } from "#enums/syntaxKind"; +import type { NodeHandle as AsyncNodeHandle } from "../api/async/api.ts"; +import type { NodeHandle as SyncNodeHandle } from "../api/sync/api.ts"; +import type { + AbstractKeyword, + AccessExpression, + AccessorDeclaration, + AccessorKeyword, + AdditiveOperator, + AdditiveOperatorOrHigher, + AnyImportSyntax, + ArrayBindingElement, + ArrayBindingPattern, + ArrayDestructuringAssignment, + ArrayLiteralExpression, + ArrayTypeNode, + ArrowFunction, + AsExpression, + AssertionExpression, + AssertKeyword, + AssertsKeyword, + AssignmentOperator, + AssignmentOperatorOrHigher, + AssignmentOperatorToken, + AsteriskToken, + AsyncKeyword, + AwaitExpression, + AwaitKeyword, + BigIntLiteral, + BinaryExpression, + BinaryOperator, + BinaryOperatorToken, + BindingElement, + BindingName, + BitwiseOperator, + BitwiseOperatorOrHigher, + Block, + BooleanLiteral, + BreakOrContinueStatement, + BreakStatement, + CallExpression, + CallLikeExpression, + CallOrNewExpression, + CallSignatureDeclaration, + CaseBlock, + CaseClause, + CaseKeyword, + CatchClause, + ClassDeclaration, + ClassExpression, + ClassLikeDeclaration, + ClassStaticBlockDeclaration, + ColonToken, + CompoundAssignmentOperator, + ComputedPropertyName, + ConditionalExpression, + ConditionalTypeNode, + ConstKeyword, + ConstructorDeclaration, + ConstructorTypeNode, + ConstructSignatureDeclaration, + ContinueStatement, + DebuggerStatement, + DeclarationName, + DeclareKeyword, + Decorator, + DefaultClause, + DefaultKeyword, + DeleteExpression, + DestructuringAssignment, + DoStatement, + DotDotDotToken, + DotToken, + ElementAccessExpression, + EmptyStatement, + EndOfFile, + EntityName, + EnumDeclaration, + EnumMember, + EqualityOperator, + EqualityOperatorOrHigher, + EqualsGreaterThanToken, + EqualsToken, + ExclamationToken, + ExponentiationOperator, + ExportAssignment, + ExportDeclaration, + ExportKeyword, + ExportSpecifier, + ExpressionStatement, + ExpressionWithTypeArguments, + ExternalModuleReference, + FalseLiteral, + ForInStatement, + ForOfStatement, + ForStatement, + FunctionBody, + FunctionDeclaration, + FunctionExpression, + FunctionLikeDeclaration, + FunctionTypeNode, + GetAccessorDeclaration, + HeritageClause, + HeritageClauseElement, + Identifier, + IfStatement, + ImportAttribute, + ImportAttributeName, + ImportAttributes, + ImportClause, + ImportClauseOrBindingPattern, + ImportDeclaration, + ImportEqualsDeclaration, + ImportExpression, + ImportPhaseModifierSyntaxKind, + ImportSpecifier, + ImportTypeNode, + IndexedAccessTypeNode, + IndexSignatureDeclaration, + InferTypeNode, + InKeyword, + InterfaceDeclaration, + IntersectionTypeNode, + JSDoc, + JSDocAllType, + JSDocAugmentsTag, + JSDocCallbackTag, + JSDocComment, + JSDocDeprecatedTag, + JSDocFullName, + JSDocImplementsTag, + JSDocImportTag, + JSDocLink, + JSDocLinkCode, + JSDocLinkPlain, + JSDocNameReference, + JSDocNonNullableType, + JSDocNullableType, + JSDocOptionalType, + JSDocOverloadTag, + JSDocOverrideTag, + JSDocParameterTag, + JSDocPrivateTag, + JSDocPropertyTag, + JSDocProtectedTag, + JSDocPublicTag, + JSDocReadonlyTag, + JSDocReturnTag, + JSDocSatisfiesTag, + JSDocSeeTag, + JSDocSignature, + JSDocTemplateTag, + JSDocText, + JSDocThisTag, + JSDocThrowsTag, + JSDocTypedefTag, + JSDocTypeExpression, + JSDocTypeLiteral, + JSDocTypeTag, + JSDocUnknownTag, + JSDocVariadicType, + JsxAttribute, + JsxAttributeLike, + JsxAttributeName, + JsxAttributes, + JsxAttributeValue, + JsxChild, + JsxClosingElement, + JsxClosingFragment, + JsxElement, + JsxExpression, + JsxFragment, + JsxNamespacedName, + JsxOpeningElement, + JsxOpeningFragment, + JsxOpeningLikeElement, + JsxSelfClosingElement, + JsxSpreadAttribute, + JsxText, + JsxTokenSyntaxKind, + KeywordExpression, + KeywordExpressionSyntaxKind, + KeywordTypeNode, + KeywordTypeSyntaxKind, + LabeledStatement, + LiteralExpression, + LiteralLikeNode, + LiteralToken, + LiteralTypeNode, + LogicalOperator, + LogicalOperatorOrHigher, + LogicalOrCoalescingAssignmentOperator, + MappedTypeNode, + MemberName, + MetaProperty, + MethodDeclaration, + MethodSignatureDeclaration, + MinusToken, + MissingDeclaration, + Modifier, + ModifierLike, + ModifierSyntaxKind, + ModuleBlock, + ModuleBody, + ModuleDeclaration, + ModuleExportName, + ModuleName, + ModuleReference, + MultiplicativeOperator, + MultiplicativeOperatorOrHigher, + NamedExportBindings, + NamedExports, + NamedImportBindings, + NamedImports, + NamedImportsOrExports, + NamedTupleMember, + NamespaceExport, + NamespaceExportDeclaration, + NamespaceImport, + NewExpression, + Node, + NonNullExpression, + NoSubstitutionTemplateLiteral, + NotEmittedStatement, + NotEmittedTypeElement, + NullLiteral, + NumericLiteral, + NumericOrStringLikeLiteral, + ObjectBindingPattern, + ObjectDestructuringAssignment, + ObjectLiteralElementLike, + ObjectLiteralExpression, + ObjectLiteralLikeNode, + ObjectTypeDeclaration, + OmittedExpression, + OptionalTypeNode, + OutKeyword, + OverrideKeyword, + ParameterDeclaration, + ParenthesizedExpression, + ParenthesizedTypeNode, + PartiallyEmittedExpression, + PlusToken, + PostfixUnaryExpression, + PostfixUnaryOperator, + PrefixUnaryExpression, + PrefixUnaryOperator, + PrivateIdentifier, + PrivateKeyword, + PropertyAccessExpression, + PropertyAssignment, + PropertyDeclaration, + PropertyName, + PropertyNameLiteral, + PropertySignatureDeclaration, + ProtectedKeyword, + PseudoLiteralSyntaxKind, + PseudoLiteralToken, + PublicKeyword, + QualifiedName, + QuestionDotToken, + QuestionToken, + ReadonlyKeyword, + RegularExpressionLiteral, + RelationalOperator, + RelationalOperatorOrHigher, + RestTypeNode, + ReturnStatement, + SatisfiesExpression, + SemicolonClassElement, + SetAccessorDeclaration, + ShiftOperator, + ShiftOperatorOrHigher, + ShorthandPropertyAssignment, + SignatureDeclaration, + SourceFile, + SpreadAssignment, + SpreadElement, + StaticKeyword, + StringLiteral, + StringLiteralLikeNode, + SuperExpression, + SwitchStatement, + SyntaxList, + SyntheticExpression, + SyntheticReferenceExpression, + TaggedTemplateExpression, + TemplateExpression, + TemplateHead, + TemplateLiteral, + TemplateLiteralLikeNode, + TemplateLiteralToken, + TemplateLiteralTypeNode, + TemplateLiteralTypeSpan, + TemplateMiddle, + TemplateMiddleOrTail, + TemplateSpan, + TemplateTail, + ThisExpression, + ThisTypeNode, + ThrowStatement, + Token, + TriviaSyntaxKind, + TrueLiteral, + TryStatement, + TupleTypeNode, + TypeAliasDeclaration, + TypeAssertion, + TypeLiteralNode, + TypeOfExpression, + TypeOperatorNode, + TypeParameterDeclaration, + TypePredicateNode, + TypePredicateParameterName, + TypeQueryNode, + TypeReferenceNode, + UnionOrIntersectionTypeNode, + UnionTypeNode, + VariableDeclaration, + VariableDeclarationList, + VariableOrParameterDeclaration, + VariableOrPropertyDeclaration, + VariableStatement, + VoidExpression, + WhileStatement, + WithStatement, + YieldExpression, +} from "./ast.ts"; +type NodeHandleLike = { kind: SyntaxKind; resolve(...args: any): any; }; +type SpecializeNodeHandle, U extends Node> = T extends AsyncNodeHandle ? AsyncNodeHandle & T : SyncNodeHandle & T; + +export function isToken(node: Node): node is Token { + return isTokenKind(node.kind); +} + +export declare namespace isToken { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isToken.Handle = isToken as any; + +export function isIdentifier(node: Node): node is Identifier { + return node.kind === SyntaxKind.Identifier; +} + +export declare namespace isIdentifier { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isIdentifier.Handle = isIdentifier as any; + +export function isPrivateIdentifier(node: Node): node is PrivateIdentifier { + return node.kind === SyntaxKind.PrivateIdentifier; +} + +export declare namespace isPrivateIdentifier { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isPrivateIdentifier.Handle = isPrivateIdentifier as any; + +export function isQualifiedName(node: Node): node is QualifiedName { + return node.kind === SyntaxKind.QualifiedName; +} + +export declare namespace isQualifiedName { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isQualifiedName.Handle = isQualifiedName as any; + +export function isComputedPropertyName(node: Node): node is ComputedPropertyName { + return node.kind === SyntaxKind.ComputedPropertyName; +} + +export declare namespace isComputedPropertyName { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isComputedPropertyName.Handle = isComputedPropertyName as any; + +export function isDecorator(node: Node): node is Decorator { + return node.kind === SyntaxKind.Decorator; +} + +export declare namespace isDecorator { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isDecorator.Handle = isDecorator as any; + +export function isEmptyStatement(node: Node): node is EmptyStatement { + return node.kind === SyntaxKind.EmptyStatement; +} + +export declare namespace isEmptyStatement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isEmptyStatement.Handle = isEmptyStatement as any; + +export function isIfStatement(node: Node): node is IfStatement { + return node.kind === SyntaxKind.IfStatement; +} + +export declare namespace isIfStatement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isIfStatement.Handle = isIfStatement as any; + +export function isDoStatement(node: Node): node is DoStatement { + return node.kind === SyntaxKind.DoStatement; +} + +export declare namespace isDoStatement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isDoStatement.Handle = isDoStatement as any; + +export function isWhileStatement(node: Node): node is WhileStatement { + return node.kind === SyntaxKind.WhileStatement; +} + +export declare namespace isWhileStatement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isWhileStatement.Handle = isWhileStatement as any; + +export function isForStatement(node: Node): node is ForStatement { + return node.kind === SyntaxKind.ForStatement; +} + +export declare namespace isForStatement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isForStatement.Handle = isForStatement as any; + +export function isBreakStatement(node: Node): node is BreakStatement { + return node.kind === SyntaxKind.BreakStatement; +} + +export declare namespace isBreakStatement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isBreakStatement.Handle = isBreakStatement as any; + +export function isContinueStatement(node: Node): node is ContinueStatement { + return node.kind === SyntaxKind.ContinueStatement; +} + +export declare namespace isContinueStatement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isContinueStatement.Handle = isContinueStatement as any; + +export function isReturnStatement(node: Node): node is ReturnStatement { + return node.kind === SyntaxKind.ReturnStatement; +} + +export declare namespace isReturnStatement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isReturnStatement.Handle = isReturnStatement as any; + +export function isWithStatement(node: Node): node is WithStatement { + return node.kind === SyntaxKind.WithStatement; +} + +export declare namespace isWithStatement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isWithStatement.Handle = isWithStatement as any; + +export function isSwitchStatement(node: Node): node is SwitchStatement { + return node.kind === SyntaxKind.SwitchStatement; +} + +export declare namespace isSwitchStatement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isSwitchStatement.Handle = isSwitchStatement as any; + +export function isCaseBlock(node: Node): node is CaseBlock { + return node.kind === SyntaxKind.CaseBlock; +} + +export declare namespace isCaseBlock { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isCaseBlock.Handle = isCaseBlock as any; + +export function isThrowStatement(node: Node): node is ThrowStatement { + return node.kind === SyntaxKind.ThrowStatement; +} + +export declare namespace isThrowStatement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isThrowStatement.Handle = isThrowStatement as any; + +export function isTryStatement(node: Node): node is TryStatement { + return node.kind === SyntaxKind.TryStatement; +} + +export declare namespace isTryStatement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isTryStatement.Handle = isTryStatement as any; + +export function isCatchClause(node: Node): node is CatchClause { + return node.kind === SyntaxKind.CatchClause; +} + +export declare namespace isCatchClause { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isCatchClause.Handle = isCatchClause as any; + +export function isDebuggerStatement(node: Node): node is DebuggerStatement { + return node.kind === SyntaxKind.DebuggerStatement; +} + +export declare namespace isDebuggerStatement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isDebuggerStatement.Handle = isDebuggerStatement as any; + +export function isLabeledStatement(node: Node): node is LabeledStatement { + return node.kind === SyntaxKind.LabeledStatement; +} + +export declare namespace isLabeledStatement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isLabeledStatement.Handle = isLabeledStatement as any; + +export function isExpressionStatement(node: Node): node is ExpressionStatement { + return node.kind === SyntaxKind.ExpressionStatement; +} + +export declare namespace isExpressionStatement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isExpressionStatement.Handle = isExpressionStatement as any; + +export function isBlock(node: Node): node is Block { + return node.kind === SyntaxKind.Block; +} + +export declare namespace isBlock { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isBlock.Handle = isBlock as any; + +export function isVariableStatement(node: Node): node is VariableStatement { + return node.kind === SyntaxKind.VariableStatement; +} + +export declare namespace isVariableStatement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isVariableStatement.Handle = isVariableStatement as any; + +export function isVariableDeclaration(node: Node): node is VariableDeclaration { + return node.kind === SyntaxKind.VariableDeclaration; +} + +export declare namespace isVariableDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isVariableDeclaration.Handle = isVariableDeclaration as any; + +export function isVariableDeclarationList(node: Node): node is VariableDeclarationList { + return node.kind === SyntaxKind.VariableDeclarationList; +} + +export declare namespace isVariableDeclarationList { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isVariableDeclarationList.Handle = isVariableDeclarationList as any; + +export function isParameterDeclaration(node: Node): node is ParameterDeclaration { + return node.kind === SyntaxKind.Parameter; +} + +export declare namespace isParameterDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isParameterDeclaration.Handle = isParameterDeclaration as any; + +export function isBindingElement(node: Node): node is BindingElement { + return node.kind === SyntaxKind.BindingElement; +} + +export declare namespace isBindingElement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isBindingElement.Handle = isBindingElement as any; + +export function isMissingDeclaration(node: Node): node is MissingDeclaration { + return node.kind === SyntaxKind.MissingDeclaration; +} + +export declare namespace isMissingDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isMissingDeclaration.Handle = isMissingDeclaration as any; + +export function isFunctionDeclaration(node: Node): node is FunctionDeclaration { + return node.kind === SyntaxKind.FunctionDeclaration; +} + +export declare namespace isFunctionDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isFunctionDeclaration.Handle = isFunctionDeclaration as any; + +export function isClassDeclaration(node: Node): node is ClassDeclaration { + return node.kind === SyntaxKind.ClassDeclaration; +} + +export declare namespace isClassDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isClassDeclaration.Handle = isClassDeclaration as any; + +export function isClassExpression(node: Node): node is ClassExpression { + return node.kind === SyntaxKind.ClassExpression; +} + +export declare namespace isClassExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isClassExpression.Handle = isClassExpression as any; + +export function isHeritageClause(node: Node): node is HeritageClause { + return node.kind === SyntaxKind.HeritageClause; +} + +export declare namespace isHeritageClause { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isHeritageClause.Handle = isHeritageClause as any; + +export function isInterfaceDeclaration(node: Node): node is InterfaceDeclaration { + return node.kind === SyntaxKind.InterfaceDeclaration; +} + +export declare namespace isInterfaceDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isInterfaceDeclaration.Handle = isInterfaceDeclaration as any; + +export function isTypeAliasDeclaration(node: Node): node is TypeAliasDeclaration { + switch (node.kind) { + case SyntaxKind.TypeAliasDeclaration: + case SyntaxKind.JSTypeAliasDeclaration: + return true; + default: + return false; + } +} + +export declare namespace isTypeAliasDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isTypeAliasDeclaration.Handle = isTypeAliasDeclaration as any; + +export function isEnumMember(node: Node): node is EnumMember { + return node.kind === SyntaxKind.EnumMember; +} + +export declare namespace isEnumMember { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isEnumMember.Handle = isEnumMember as any; + +export function isEnumDeclaration(node: Node): node is EnumDeclaration { + return node.kind === SyntaxKind.EnumDeclaration; +} + +export declare namespace isEnumDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isEnumDeclaration.Handle = isEnumDeclaration as any; + +export function isModuleBlock(node: Node): node is ModuleBlock { + return node.kind === SyntaxKind.ModuleBlock; +} + +export declare namespace isModuleBlock { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isModuleBlock.Handle = isModuleBlock as any; + +export function isNotEmittedStatement(node: Node): node is NotEmittedStatement { + return node.kind === SyntaxKind.NotEmittedStatement; +} + +export declare namespace isNotEmittedStatement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isNotEmittedStatement.Handle = isNotEmittedStatement as any; + +export function isNotEmittedTypeElement(node: Node): node is NotEmittedTypeElement { + return node.kind === SyntaxKind.NotEmittedTypeElement; +} + +export declare namespace isNotEmittedTypeElement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isNotEmittedTypeElement.Handle = isNotEmittedTypeElement as any; + +export function isImportDeclaration(node: Node): node is ImportDeclaration { + switch (node.kind) { + case SyntaxKind.ImportDeclaration: + case SyntaxKind.JSImportDeclaration: + return true; + default: + return false; + } +} + +export declare namespace isImportDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isImportDeclaration.Handle = isImportDeclaration as any; + +export function isExternalModuleReference(node: Node): node is ExternalModuleReference { + return node.kind === SyntaxKind.ExternalModuleReference; +} + +export declare namespace isExternalModuleReference { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isExternalModuleReference.Handle = isExternalModuleReference as any; + +export function isNamespaceImport(node: Node): node is NamespaceImport { + return node.kind === SyntaxKind.NamespaceImport; +} + +export declare namespace isNamespaceImport { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isNamespaceImport.Handle = isNamespaceImport as any; + +export function isNamedImports(node: Node): node is NamedImports { + return node.kind === SyntaxKind.NamedImports; +} + +export declare namespace isNamedImports { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isNamedImports.Handle = isNamedImports as any; + +export function isExportAssignment(node: Node): node is ExportAssignment { + return node.kind === SyntaxKind.ExportAssignment; +} + +export declare namespace isExportAssignment { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isExportAssignment.Handle = isExportAssignment as any; + +export function isNamespaceExportDeclaration(node: Node): node is NamespaceExportDeclaration { + return node.kind === SyntaxKind.NamespaceExportDeclaration; +} + +export declare namespace isNamespaceExportDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isNamespaceExportDeclaration.Handle = isNamespaceExportDeclaration as any; + +export function isNamespaceExport(node: Node): node is NamespaceExport { + return node.kind === SyntaxKind.NamespaceExport; +} + +export declare namespace isNamespaceExport { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isNamespaceExport.Handle = isNamespaceExport as any; + +export function isNamedExports(node: Node): node is NamedExports { + return node.kind === SyntaxKind.NamedExports; +} + +export declare namespace isNamedExports { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isNamedExports.Handle = isNamedExports as any; + +export function isExportSpecifier(node: Node): node is ExportSpecifier { + return node.kind === SyntaxKind.ExportSpecifier; +} + +export declare namespace isExportSpecifier { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isExportSpecifier.Handle = isExportSpecifier as any; + +export function isCallSignatureDeclaration(node: Node): node is CallSignatureDeclaration { + return node.kind === SyntaxKind.CallSignature; +} + +export declare namespace isCallSignatureDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isCallSignatureDeclaration.Handle = isCallSignatureDeclaration as any; + +export function isConstructSignatureDeclaration(node: Node): node is ConstructSignatureDeclaration { + return node.kind === SyntaxKind.ConstructSignature; +} + +export declare namespace isConstructSignatureDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isConstructSignatureDeclaration.Handle = isConstructSignatureDeclaration as any; + +export function isConstructorDeclaration(node: Node): node is ConstructorDeclaration { + return node.kind === SyntaxKind.Constructor; +} + +export declare namespace isConstructorDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isConstructorDeclaration.Handle = isConstructorDeclaration as any; + +export function isGetAccessorDeclaration(node: Node): node is GetAccessorDeclaration { + return node.kind === SyntaxKind.GetAccessor; +} + +export declare namespace isGetAccessorDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isGetAccessorDeclaration.Handle = isGetAccessorDeclaration as any; + +export function isSetAccessorDeclaration(node: Node): node is SetAccessorDeclaration { + return node.kind === SyntaxKind.SetAccessor; +} + +export declare namespace isSetAccessorDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isSetAccessorDeclaration.Handle = isSetAccessorDeclaration as any; + +export function isIndexSignatureDeclaration(node: Node): node is IndexSignatureDeclaration { + return node.kind === SyntaxKind.IndexSignature; +} + +export declare namespace isIndexSignatureDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isIndexSignatureDeclaration.Handle = isIndexSignatureDeclaration as any; + +export function isMethodSignatureDeclaration(node: Node): node is MethodSignatureDeclaration { + return node.kind === SyntaxKind.MethodSignature; +} + +export declare namespace isMethodSignatureDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isMethodSignatureDeclaration.Handle = isMethodSignatureDeclaration as any; + +export function isMethodDeclaration(node: Node): node is MethodDeclaration { + return node.kind === SyntaxKind.MethodDeclaration; +} + +export declare namespace isMethodDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isMethodDeclaration.Handle = isMethodDeclaration as any; + +export function isPropertySignatureDeclaration(node: Node): node is PropertySignatureDeclaration { + return node.kind === SyntaxKind.PropertySignature; +} + +export declare namespace isPropertySignatureDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isPropertySignatureDeclaration.Handle = isPropertySignatureDeclaration as any; + +export function isPropertyDeclaration(node: Node): node is PropertyDeclaration { + return node.kind === SyntaxKind.PropertyDeclaration; +} + +export declare namespace isPropertyDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isPropertyDeclaration.Handle = isPropertyDeclaration as any; + +export function isSemicolonClassElement(node: Node): node is SemicolonClassElement { + return node.kind === SyntaxKind.SemicolonClassElement; +} + +export declare namespace isSemicolonClassElement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isSemicolonClassElement.Handle = isSemicolonClassElement as any; + +export function isClassStaticBlockDeclaration(node: Node): node is ClassStaticBlockDeclaration { + return node.kind === SyntaxKind.ClassStaticBlockDeclaration; +} + +export declare namespace isClassStaticBlockDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isClassStaticBlockDeclaration.Handle = isClassStaticBlockDeclaration as any; + +export function isOmittedExpression(node: Node): node is OmittedExpression { + return node.kind === SyntaxKind.OmittedExpression; +} + +export declare namespace isOmittedExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isOmittedExpression.Handle = isOmittedExpression as any; + +export function isKeywordExpression(node: Node): node is KeywordExpression { + return isKeywordExpressionKind(node.kind); +} + +export declare namespace isKeywordExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isKeywordExpression.Handle = isKeywordExpression as any; + +export function isStringLiteral(node: Node): node is StringLiteral { + return node.kind === SyntaxKind.StringLiteral; +} + +export declare namespace isStringLiteral { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isStringLiteral.Handle = isStringLiteral as any; + +export function isNumericLiteral(node: Node): node is NumericLiteral { + return node.kind === SyntaxKind.NumericLiteral; +} + +export declare namespace isNumericLiteral { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isNumericLiteral.Handle = isNumericLiteral as any; + +export function isBigIntLiteral(node: Node): node is BigIntLiteral { + return node.kind === SyntaxKind.BigIntLiteral; +} + +export declare namespace isBigIntLiteral { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isBigIntLiteral.Handle = isBigIntLiteral as any; + +export function isRegularExpressionLiteral(node: Node): node is RegularExpressionLiteral { + return node.kind === SyntaxKind.RegularExpressionLiteral; +} + +export declare namespace isRegularExpressionLiteral { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isRegularExpressionLiteral.Handle = isRegularExpressionLiteral as any; + +export function isNoSubstitutionTemplateLiteral(node: Node): node is NoSubstitutionTemplateLiteral { + return node.kind === SyntaxKind.NoSubstitutionTemplateLiteral; +} + +export declare namespace isNoSubstitutionTemplateLiteral { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isNoSubstitutionTemplateLiteral.Handle = isNoSubstitutionTemplateLiteral as any; + +export function isBinaryExpression(node: Node): node is BinaryExpression { + return node.kind === SyntaxKind.BinaryExpression; +} + +export declare namespace isBinaryExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isBinaryExpression.Handle = isBinaryExpression as any; + +export function isPrefixUnaryExpression(node: Node): node is PrefixUnaryExpression { + return node.kind === SyntaxKind.PrefixUnaryExpression; +} + +export declare namespace isPrefixUnaryExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isPrefixUnaryExpression.Handle = isPrefixUnaryExpression as any; + +export function isPostfixUnaryExpression(node: Node): node is PostfixUnaryExpression { + return node.kind === SyntaxKind.PostfixUnaryExpression; +} + +export declare namespace isPostfixUnaryExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isPostfixUnaryExpression.Handle = isPostfixUnaryExpression as any; + +export function isYieldExpression(node: Node): node is YieldExpression { + return node.kind === SyntaxKind.YieldExpression; +} + +export declare namespace isYieldExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isYieldExpression.Handle = isYieldExpression as any; + +export function isArrowFunction(node: Node): node is ArrowFunction { + return node.kind === SyntaxKind.ArrowFunction; +} + +export declare namespace isArrowFunction { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isArrowFunction.Handle = isArrowFunction as any; + +export function isFunctionExpression(node: Node): node is FunctionExpression { + return node.kind === SyntaxKind.FunctionExpression; +} + +export declare namespace isFunctionExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isFunctionExpression.Handle = isFunctionExpression as any; + +export function isAsExpression(node: Node): node is AsExpression { + return node.kind === SyntaxKind.AsExpression; +} + +export declare namespace isAsExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isAsExpression.Handle = isAsExpression as any; + +export function isSatisfiesExpression(node: Node): node is SatisfiesExpression { + return node.kind === SyntaxKind.SatisfiesExpression; +} + +export declare namespace isSatisfiesExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isSatisfiesExpression.Handle = isSatisfiesExpression as any; + +export function isConditionalExpression(node: Node): node is ConditionalExpression { + return node.kind === SyntaxKind.ConditionalExpression; +} + +export declare namespace isConditionalExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isConditionalExpression.Handle = isConditionalExpression as any; + +export function isPropertyAccessExpression(node: Node): node is PropertyAccessExpression { + return node.kind === SyntaxKind.PropertyAccessExpression; +} + +export declare namespace isPropertyAccessExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isPropertyAccessExpression.Handle = isPropertyAccessExpression as any; + +export function isElementAccessExpression(node: Node): node is ElementAccessExpression { + return node.kind === SyntaxKind.ElementAccessExpression; +} + +export declare namespace isElementAccessExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isElementAccessExpression.Handle = isElementAccessExpression as any; + +export function isCallExpression(node: Node): node is CallExpression { + return node.kind === SyntaxKind.CallExpression; +} + +export declare namespace isCallExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isCallExpression.Handle = isCallExpression as any; + +export function isNewExpression(node: Node): node is NewExpression { + return node.kind === SyntaxKind.NewExpression; +} + +export declare namespace isNewExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isNewExpression.Handle = isNewExpression as any; + +export function isMetaProperty(node: Node): node is MetaProperty { + return node.kind === SyntaxKind.MetaProperty; +} + +export declare namespace isMetaProperty { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isMetaProperty.Handle = isMetaProperty as any; + +export function isNonNullExpression(node: Node): node is NonNullExpression { + return node.kind === SyntaxKind.NonNullExpression; +} + +export declare namespace isNonNullExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isNonNullExpression.Handle = isNonNullExpression as any; + +export function isSpreadElement(node: Node): node is SpreadElement { + return node.kind === SyntaxKind.SpreadElement; +} + +export declare namespace isSpreadElement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isSpreadElement.Handle = isSpreadElement as any; + +export function isTemplateExpression(node: Node): node is TemplateExpression { + return node.kind === SyntaxKind.TemplateExpression; +} + +export declare namespace isTemplateExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isTemplateExpression.Handle = isTemplateExpression as any; + +export function isTemplateSpan(node: Node): node is TemplateSpan { + return node.kind === SyntaxKind.TemplateSpan; +} + +export declare namespace isTemplateSpan { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isTemplateSpan.Handle = isTemplateSpan as any; + +export function isTaggedTemplateExpression(node: Node): node is TaggedTemplateExpression { + return node.kind === SyntaxKind.TaggedTemplateExpression; +} + +export declare namespace isTaggedTemplateExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isTaggedTemplateExpression.Handle = isTaggedTemplateExpression as any; + +export function isParenthesizedExpression(node: Node): node is ParenthesizedExpression { + return node.kind === SyntaxKind.ParenthesizedExpression; +} + +export declare namespace isParenthesizedExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isParenthesizedExpression.Handle = isParenthesizedExpression as any; + +export function isArrayLiteralExpression(node: Node): node is ArrayLiteralExpression { + return node.kind === SyntaxKind.ArrayLiteralExpression; +} + +export declare namespace isArrayLiteralExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isArrayLiteralExpression.Handle = isArrayLiteralExpression as any; + +export function isObjectLiteralExpression(node: Node): node is ObjectLiteralExpression { + return node.kind === SyntaxKind.ObjectLiteralExpression; +} + +export declare namespace isObjectLiteralExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isObjectLiteralExpression.Handle = isObjectLiteralExpression as any; + +export function isSpreadAssignment(node: Node): node is SpreadAssignment { + return node.kind === SyntaxKind.SpreadAssignment; +} + +export declare namespace isSpreadAssignment { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isSpreadAssignment.Handle = isSpreadAssignment as any; + +export function isPropertyAssignment(node: Node): node is PropertyAssignment { + return node.kind === SyntaxKind.PropertyAssignment; +} + +export declare namespace isPropertyAssignment { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isPropertyAssignment.Handle = isPropertyAssignment as any; + +export function isShorthandPropertyAssignment(node: Node): node is ShorthandPropertyAssignment { + return node.kind === SyntaxKind.ShorthandPropertyAssignment; +} + +export declare namespace isShorthandPropertyAssignment { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isShorthandPropertyAssignment.Handle = isShorthandPropertyAssignment as any; + +export function isDeleteExpression(node: Node): node is DeleteExpression { + return node.kind === SyntaxKind.DeleteExpression; +} + +export declare namespace isDeleteExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isDeleteExpression.Handle = isDeleteExpression as any; + +export function isTypeOfExpression(node: Node): node is TypeOfExpression { + return node.kind === SyntaxKind.TypeOfExpression; +} + +export declare namespace isTypeOfExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isTypeOfExpression.Handle = isTypeOfExpression as any; + +export function isVoidExpression(node: Node): node is VoidExpression { + return node.kind === SyntaxKind.VoidExpression; +} + +export declare namespace isVoidExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isVoidExpression.Handle = isVoidExpression as any; + +export function isAwaitExpression(node: Node): node is AwaitExpression { + return node.kind === SyntaxKind.AwaitExpression; +} + +export declare namespace isAwaitExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isAwaitExpression.Handle = isAwaitExpression as any; + +export function isTypeAssertion(node: Node): node is TypeAssertion { + return node.kind === SyntaxKind.TypeAssertionExpression; +} + +export declare namespace isTypeAssertion { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isTypeAssertion.Handle = isTypeAssertion as any; + +export function isKeywordTypeNode(node: Node): node is KeywordTypeNode { + return isKeywordTypeKind(node.kind); +} + +export declare namespace isKeywordTypeNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isKeywordTypeNode.Handle = isKeywordTypeNode as any; + +export function isUnionTypeNode(node: Node): node is UnionTypeNode { + return node.kind === SyntaxKind.UnionType; +} + +export declare namespace isUnionTypeNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isUnionTypeNode.Handle = isUnionTypeNode as any; + +export function isIntersectionTypeNode(node: Node): node is IntersectionTypeNode { + return node.kind === SyntaxKind.IntersectionType; +} + +export declare namespace isIntersectionTypeNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isIntersectionTypeNode.Handle = isIntersectionTypeNode as any; + +export function isConditionalTypeNode(node: Node): node is ConditionalTypeNode { + return node.kind === SyntaxKind.ConditionalType; +} + +export declare namespace isConditionalTypeNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isConditionalTypeNode.Handle = isConditionalTypeNode as any; + +export function isTypeOperatorNode(node: Node): node is TypeOperatorNode { + return node.kind === SyntaxKind.TypeOperator; +} + +export declare namespace isTypeOperatorNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isTypeOperatorNode.Handle = isTypeOperatorNode as any; + +export function isInferTypeNode(node: Node): node is InferTypeNode { + return node.kind === SyntaxKind.InferType; +} + +export declare namespace isInferTypeNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isInferTypeNode.Handle = isInferTypeNode as any; + +export function isArrayTypeNode(node: Node): node is ArrayTypeNode { + return node.kind === SyntaxKind.ArrayType; +} + +export declare namespace isArrayTypeNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isArrayTypeNode.Handle = isArrayTypeNode as any; + +export function isIndexedAccessTypeNode(node: Node): node is IndexedAccessTypeNode { + return node.kind === SyntaxKind.IndexedAccessType; +} + +export declare namespace isIndexedAccessTypeNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isIndexedAccessTypeNode.Handle = isIndexedAccessTypeNode as any; + +export function isTypeReferenceNode(node: Node): node is TypeReferenceNode { + return node.kind === SyntaxKind.TypeReference; +} + +export declare namespace isTypeReferenceNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isTypeReferenceNode.Handle = isTypeReferenceNode as any; + +export function isExpressionWithTypeArguments(node: Node): node is ExpressionWithTypeArguments { + return node.kind === SyntaxKind.ExpressionWithTypeArguments; +} + +export declare namespace isExpressionWithTypeArguments { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isExpressionWithTypeArguments.Handle = isExpressionWithTypeArguments as any; + +export function isLiteralTypeNode(node: Node): node is LiteralTypeNode { + return node.kind === SyntaxKind.LiteralType; +} + +export declare namespace isLiteralTypeNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isLiteralTypeNode.Handle = isLiteralTypeNode as any; + +export function isThisTypeNode(node: Node): node is ThisTypeNode { + return node.kind === SyntaxKind.ThisType; +} + +export declare namespace isThisTypeNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isThisTypeNode.Handle = isThisTypeNode as any; + +export function isTypePredicateNode(node: Node): node is TypePredicateNode { + return node.kind === SyntaxKind.TypePredicate; +} + +export declare namespace isTypePredicateNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isTypePredicateNode.Handle = isTypePredicateNode as any; + +export function isImportAttribute(node: Node): node is ImportAttribute { + return node.kind === SyntaxKind.ImportAttribute; +} + +export declare namespace isImportAttribute { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isImportAttribute.Handle = isImportAttribute as any; + +export function isImportAttributes(node: Node): node is ImportAttributes { + return node.kind === SyntaxKind.ImportAttributes; +} + +export declare namespace isImportAttributes { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isImportAttributes.Handle = isImportAttributes as any; + +export function isTypeQueryNode(node: Node): node is TypeQueryNode { + return node.kind === SyntaxKind.TypeQuery; +} + +export declare namespace isTypeQueryNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isTypeQueryNode.Handle = isTypeQueryNode as any; + +export function isMappedTypeNode(node: Node): node is MappedTypeNode { + return node.kind === SyntaxKind.MappedType; +} + +export declare namespace isMappedTypeNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isMappedTypeNode.Handle = isMappedTypeNode as any; + +export function isTypeLiteralNode(node: Node): node is TypeLiteralNode { + return node.kind === SyntaxKind.TypeLiteral; +} + +export declare namespace isTypeLiteralNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isTypeLiteralNode.Handle = isTypeLiteralNode as any; + +export function isTupleTypeNode(node: Node): node is TupleTypeNode { + return node.kind === SyntaxKind.TupleType; +} + +export declare namespace isTupleTypeNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isTupleTypeNode.Handle = isTupleTypeNode as any; + +export function isNamedTupleMember(node: Node): node is NamedTupleMember { + return node.kind === SyntaxKind.NamedTupleMember; +} + +export declare namespace isNamedTupleMember { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isNamedTupleMember.Handle = isNamedTupleMember as any; + +export function isOptionalTypeNode(node: Node): node is OptionalTypeNode { + return node.kind === SyntaxKind.OptionalType; +} + +export declare namespace isOptionalTypeNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isOptionalTypeNode.Handle = isOptionalTypeNode as any; + +export function isRestTypeNode(node: Node): node is RestTypeNode { + return node.kind === SyntaxKind.RestType; +} + +export declare namespace isRestTypeNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isRestTypeNode.Handle = isRestTypeNode as any; + +export function isParenthesizedTypeNode(node: Node): node is ParenthesizedTypeNode { + return node.kind === SyntaxKind.ParenthesizedType; +} + +export declare namespace isParenthesizedTypeNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isParenthesizedTypeNode.Handle = isParenthesizedTypeNode as any; + +export function isFunctionTypeNode(node: Node): node is FunctionTypeNode { + return node.kind === SyntaxKind.FunctionType; +} + +export declare namespace isFunctionTypeNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isFunctionTypeNode.Handle = isFunctionTypeNode as any; + +export function isConstructorTypeNode(node: Node): node is ConstructorTypeNode { + return node.kind === SyntaxKind.ConstructorType; +} + +export declare namespace isConstructorTypeNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isConstructorTypeNode.Handle = isConstructorTypeNode as any; + +export function isTemplateHead(node: Node): node is TemplateHead { + return node.kind === SyntaxKind.TemplateHead; +} + +export declare namespace isTemplateHead { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isTemplateHead.Handle = isTemplateHead as any; + +export function isTemplateMiddle(node: Node): node is TemplateMiddle { + return node.kind === SyntaxKind.TemplateMiddle; +} + +export declare namespace isTemplateMiddle { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isTemplateMiddle.Handle = isTemplateMiddle as any; + +export function isTemplateTail(node: Node): node is TemplateTail { + return node.kind === SyntaxKind.TemplateTail; +} + +export declare namespace isTemplateTail { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isTemplateTail.Handle = isTemplateTail as any; + +export function isTemplateLiteralTypeNode(node: Node): node is TemplateLiteralTypeNode { + return node.kind === SyntaxKind.TemplateLiteralType; +} + +export declare namespace isTemplateLiteralTypeNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isTemplateLiteralTypeNode.Handle = isTemplateLiteralTypeNode as any; + +export function isTemplateLiteralTypeSpan(node: Node): node is TemplateLiteralTypeSpan { + return node.kind === SyntaxKind.TemplateLiteralTypeSpan; +} + +export declare namespace isTemplateLiteralTypeSpan { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isTemplateLiteralTypeSpan.Handle = isTemplateLiteralTypeSpan as any; + +export function isSyntheticExpression(node: Node): node is SyntheticExpression { + return node.kind === SyntaxKind.SyntheticExpression; +} + +export declare namespace isSyntheticExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isSyntheticExpression.Handle = isSyntheticExpression as any; + +export function isPartiallyEmittedExpression(node: Node): node is PartiallyEmittedExpression { + return node.kind === SyntaxKind.PartiallyEmittedExpression; +} + +export declare namespace isPartiallyEmittedExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isPartiallyEmittedExpression.Handle = isPartiallyEmittedExpression as any; + +export function isJsxElement(node: Node): node is JsxElement { + return node.kind === SyntaxKind.JsxElement; +} + +export declare namespace isJsxElement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJsxElement.Handle = isJsxElement as any; + +export function isJsxAttributes(node: Node): node is JsxAttributes { + return node.kind === SyntaxKind.JsxAttributes; +} + +export declare namespace isJsxAttributes { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJsxAttributes.Handle = isJsxAttributes as any; + +export function isJsxNamespacedName(node: Node): node is JsxNamespacedName { + return node.kind === SyntaxKind.JsxNamespacedName; +} + +export declare namespace isJsxNamespacedName { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJsxNamespacedName.Handle = isJsxNamespacedName as any; + +export function isJsxOpeningElement(node: Node): node is JsxOpeningElement { + return node.kind === SyntaxKind.JsxOpeningElement; +} + +export declare namespace isJsxOpeningElement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJsxOpeningElement.Handle = isJsxOpeningElement as any; + +export function isJsxSelfClosingElement(node: Node): node is JsxSelfClosingElement { + return node.kind === SyntaxKind.JsxSelfClosingElement; +} + +export declare namespace isJsxSelfClosingElement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJsxSelfClosingElement.Handle = isJsxSelfClosingElement as any; + +export function isJsxFragment(node: Node): node is JsxFragment { + return node.kind === SyntaxKind.JsxFragment; +} + +export declare namespace isJsxFragment { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJsxFragment.Handle = isJsxFragment as any; + +export function isJsxOpeningFragment(node: Node): node is JsxOpeningFragment { + return node.kind === SyntaxKind.JsxOpeningFragment; +} + +export declare namespace isJsxOpeningFragment { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJsxOpeningFragment.Handle = isJsxOpeningFragment as any; + +export function isJsxClosingFragment(node: Node): node is JsxClosingFragment { + return node.kind === SyntaxKind.JsxClosingFragment; +} + +export declare namespace isJsxClosingFragment { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJsxClosingFragment.Handle = isJsxClosingFragment as any; + +export function isJsxAttribute(node: Node): node is JsxAttribute { + return node.kind === SyntaxKind.JsxAttribute; +} + +export declare namespace isJsxAttribute { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJsxAttribute.Handle = isJsxAttribute as any; + +export function isJsxSpreadAttribute(node: Node): node is JsxSpreadAttribute { + return node.kind === SyntaxKind.JsxSpreadAttribute; +} + +export declare namespace isJsxSpreadAttribute { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJsxSpreadAttribute.Handle = isJsxSpreadAttribute as any; + +export function isJsxClosingElement(node: Node): node is JsxClosingElement { + return node.kind === SyntaxKind.JsxClosingElement; +} + +export declare namespace isJsxClosingElement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJsxClosingElement.Handle = isJsxClosingElement as any; + +export function isJsxExpression(node: Node): node is JsxExpression { + return node.kind === SyntaxKind.JsxExpression; +} + +export declare namespace isJsxExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJsxExpression.Handle = isJsxExpression as any; + +export function isJsxText(node: Node): node is JsxText { + return node.kind === SyntaxKind.JsxText; +} + +export declare namespace isJsxText { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJsxText.Handle = isJsxText as any; + +export function isSyntaxList(node: Node): node is SyntaxList { + return node.kind === SyntaxKind.SyntaxList; +} + +export declare namespace isSyntaxList { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isSyntaxList.Handle = isSyntaxList as any; + +export function isJSDoc(node: Node): node is JSDoc { + return node.kind === SyntaxKind.JSDoc; +} + +export declare namespace isJSDoc { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDoc.Handle = isJSDoc as any; + +export function isJSDocTypeExpression(node: Node): node is JSDocTypeExpression { + return node.kind === SyntaxKind.JSDocTypeExpression; +} + +export declare namespace isJSDocTypeExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocTypeExpression.Handle = isJSDocTypeExpression as any; + +export function isJSDocNonNullableType(node: Node): node is JSDocNonNullableType { + return node.kind === SyntaxKind.JSDocNonNullableType; +} + +export declare namespace isJSDocNonNullableType { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocNonNullableType.Handle = isJSDocNonNullableType as any; + +export function isJSDocNullableType(node: Node): node is JSDocNullableType { + return node.kind === SyntaxKind.JSDocNullableType; +} + +export declare namespace isJSDocNullableType { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocNullableType.Handle = isJSDocNullableType as any; + +export function isJSDocAllType(node: Node): node is JSDocAllType { + return node.kind === SyntaxKind.JSDocAllType; +} + +export declare namespace isJSDocAllType { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocAllType.Handle = isJSDocAllType as any; + +export function isJSDocVariadicType(node: Node): node is JSDocVariadicType { + return node.kind === SyntaxKind.JSDocVariadicType; +} + +export declare namespace isJSDocVariadicType { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocVariadicType.Handle = isJSDocVariadicType as any; + +export function isJSDocOptionalType(node: Node): node is JSDocOptionalType { + return node.kind === SyntaxKind.JSDocOptionalType; +} + +export declare namespace isJSDocOptionalType { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocOptionalType.Handle = isJSDocOptionalType as any; + +export function isJSDocTypeTag(node: Node): node is JSDocTypeTag { + return node.kind === SyntaxKind.JSDocTypeTag; +} + +export declare namespace isJSDocTypeTag { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocTypeTag.Handle = isJSDocTypeTag as any; + +export function isJSDocUnknownTag(node: Node): node is JSDocUnknownTag { + return node.kind === SyntaxKind.JSDocUnknownTag; +} + +export declare namespace isJSDocUnknownTag { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocUnknownTag.Handle = isJSDocUnknownTag as any; + +export function isJSDocTemplateTag(node: Node): node is JSDocTemplateTag { + return node.kind === SyntaxKind.JSDocTemplateTag; +} + +export declare namespace isJSDocTemplateTag { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocTemplateTag.Handle = isJSDocTemplateTag as any; + +export function isJSDocReturnTag(node: Node): node is JSDocReturnTag { + return node.kind === SyntaxKind.JSDocReturnTag; +} + +export declare namespace isJSDocReturnTag { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocReturnTag.Handle = isJSDocReturnTag as any; + +export function isJSDocPublicTag(node: Node): node is JSDocPublicTag { + return node.kind === SyntaxKind.JSDocPublicTag; +} + +export declare namespace isJSDocPublicTag { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocPublicTag.Handle = isJSDocPublicTag as any; + +export function isJSDocPrivateTag(node: Node): node is JSDocPrivateTag { + return node.kind === SyntaxKind.JSDocPrivateTag; +} + +export declare namespace isJSDocPrivateTag { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocPrivateTag.Handle = isJSDocPrivateTag as any; + +export function isJSDocProtectedTag(node: Node): node is JSDocProtectedTag { + return node.kind === SyntaxKind.JSDocProtectedTag; +} + +export declare namespace isJSDocProtectedTag { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocProtectedTag.Handle = isJSDocProtectedTag as any; + +export function isJSDocReadonlyTag(node: Node): node is JSDocReadonlyTag { + return node.kind === SyntaxKind.JSDocReadonlyTag; +} + +export declare namespace isJSDocReadonlyTag { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocReadonlyTag.Handle = isJSDocReadonlyTag as any; + +export function isJSDocOverrideTag(node: Node): node is JSDocOverrideTag { + return node.kind === SyntaxKind.JSDocOverrideTag; +} + +export declare namespace isJSDocOverrideTag { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocOverrideTag.Handle = isJSDocOverrideTag as any; + +export function isJSDocDeprecatedTag(node: Node): node is JSDocDeprecatedTag { + return node.kind === SyntaxKind.JSDocDeprecatedTag; +} + +export declare namespace isJSDocDeprecatedTag { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocDeprecatedTag.Handle = isJSDocDeprecatedTag as any; + +export function isJSDocSeeTag(node: Node): node is JSDocSeeTag { + return node.kind === SyntaxKind.JSDocSeeTag; +} + +export declare namespace isJSDocSeeTag { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocSeeTag.Handle = isJSDocSeeTag as any; + +export function isJSDocImplementsTag(node: Node): node is JSDocImplementsTag { + return node.kind === SyntaxKind.JSDocImplementsTag; +} + +export declare namespace isJSDocImplementsTag { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocImplementsTag.Handle = isJSDocImplementsTag as any; + +export function isJSDocAugmentsTag(node: Node): node is JSDocAugmentsTag { + return node.kind === SyntaxKind.JSDocAugmentsTag; +} + +export declare namespace isJSDocAugmentsTag { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocAugmentsTag.Handle = isJSDocAugmentsTag as any; + +export function isJSDocSatisfiesTag(node: Node): node is JSDocSatisfiesTag { + return node.kind === SyntaxKind.JSDocSatisfiesTag; +} + +export declare namespace isJSDocSatisfiesTag { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocSatisfiesTag.Handle = isJSDocSatisfiesTag as any; + +export function isJSDocThrowsTag(node: Node): node is JSDocThrowsTag { + return node.kind === SyntaxKind.JSDocThrowsTag; +} + +export declare namespace isJSDocThrowsTag { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocThrowsTag.Handle = isJSDocThrowsTag as any; + +export function isJSDocThisTag(node: Node): node is JSDocThisTag { + return node.kind === SyntaxKind.JSDocThisTag; +} + +export declare namespace isJSDocThisTag { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocThisTag.Handle = isJSDocThisTag as any; + +export function isJSDocImportTag(node: Node): node is JSDocImportTag { + return node.kind === SyntaxKind.JSDocImportTag; +} + +export declare namespace isJSDocImportTag { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocImportTag.Handle = isJSDocImportTag as any; + +export function isJSDocCallbackTag(node: Node): node is JSDocCallbackTag { + return node.kind === SyntaxKind.JSDocCallbackTag; +} + +export declare namespace isJSDocCallbackTag { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocCallbackTag.Handle = isJSDocCallbackTag as any; + +export function isJSDocOverloadTag(node: Node): node is JSDocOverloadTag { + return node.kind === SyntaxKind.JSDocOverloadTag; +} + +export declare namespace isJSDocOverloadTag { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocOverloadTag.Handle = isJSDocOverloadTag as any; + +export function isJSDocTypedefTag(node: Node): node is JSDocTypedefTag { + return node.kind === SyntaxKind.JSDocTypedefTag; +} + +export declare namespace isJSDocTypedefTag { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocTypedefTag.Handle = isJSDocTypedefTag as any; + +export function isJSDocSignature(node: Node): node is JSDocSignature { + return node.kind === SyntaxKind.JSDocSignature; +} + +export declare namespace isJSDocSignature { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocSignature.Handle = isJSDocSignature as any; + +export function isJSDocNameReference(node: Node): node is JSDocNameReference { + return node.kind === SyntaxKind.JSDocNameReference; +} + +export declare namespace isJSDocNameReference { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocNameReference.Handle = isJSDocNameReference as any; + +export function isSourceFile(node: Node): node is SourceFile { + return node.kind === SyntaxKind.SourceFile; +} + +export declare namespace isSourceFile { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isSourceFile.Handle = isSourceFile as any; + +export function isModuleDeclaration(node: Node): node is ModuleDeclaration { + return node.kind === SyntaxKind.ModuleDeclaration; +} + +export declare namespace isModuleDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isModuleDeclaration.Handle = isModuleDeclaration as any; + +export function isImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration { + return node.kind === SyntaxKind.ImportEqualsDeclaration; +} + +export declare namespace isImportEqualsDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isImportEqualsDeclaration.Handle = isImportEqualsDeclaration as any; + +export function isExportDeclaration(node: Node): node is ExportDeclaration { + return node.kind === SyntaxKind.ExportDeclaration; +} + +export declare namespace isExportDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isExportDeclaration.Handle = isExportDeclaration as any; + +export function isImportTypeNode(node: Node): node is ImportTypeNode { + return node.kind === SyntaxKind.ImportType; +} + +export declare namespace isImportTypeNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isImportTypeNode.Handle = isImportTypeNode as any; + +export function isImportClause(node: Node): node is ImportClause { + return node.kind === SyntaxKind.ImportClause; +} + +export declare namespace isImportClause { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isImportClause.Handle = isImportClause as any; + +export function isImportSpecifier(node: Node): node is ImportSpecifier { + return node.kind === SyntaxKind.ImportSpecifier; +} + +export declare namespace isImportSpecifier { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isImportSpecifier.Handle = isImportSpecifier as any; + +export function isJSDocText(node: Node): node is JSDocText { + return node.kind === SyntaxKind.JSDocText; +} + +export declare namespace isJSDocText { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocText.Handle = isJSDocText as any; + +export function isJSDocLink(node: Node): node is JSDocLink { + return node.kind === SyntaxKind.JSDocLink; +} + +export declare namespace isJSDocLink { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocLink.Handle = isJSDocLink as any; + +export function isJSDocLinkPlain(node: Node): node is JSDocLinkPlain { + return node.kind === SyntaxKind.JSDocLinkPlain; +} + +export declare namespace isJSDocLinkPlain { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocLinkPlain.Handle = isJSDocLinkPlain as any; + +export function isJSDocLinkCode(node: Node): node is JSDocLinkCode { + return node.kind === SyntaxKind.JSDocLinkCode; +} + +export declare namespace isJSDocLinkCode { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocLinkCode.Handle = isJSDocLinkCode as any; + +export function isTypeParameterDeclaration(node: Node): node is TypeParameterDeclaration { + return node.kind === SyntaxKind.TypeParameter; +} + +export declare namespace isTypeParameterDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isTypeParameterDeclaration.Handle = isTypeParameterDeclaration as any; + +export function isSyntheticReferenceExpression(node: Node): node is SyntheticReferenceExpression { + return node.kind === SyntaxKind.SyntheticReferenceExpression; +} + +export declare namespace isSyntheticReferenceExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isSyntheticReferenceExpression.Handle = isSyntheticReferenceExpression as any; + +export function isJSDocTypeLiteral(node: Node): node is JSDocTypeLiteral { + return node.kind === SyntaxKind.JSDocTypeLiteral; +} + +export declare namespace isJSDocTypeLiteral { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocTypeLiteral.Handle = isJSDocTypeLiteral as any; + +export function isForInStatement(node: Node): node is ForInStatement { + return node.kind === SyntaxKind.ForInStatement; +} + +export declare namespace isForInStatement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isForInStatement.Handle = isForInStatement as any; + +export function isForOfStatement(node: Node): node is ForOfStatement { + return node.kind === SyntaxKind.ForOfStatement; +} + +export declare namespace isForOfStatement { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isForOfStatement.Handle = isForOfStatement as any; + +export function isCaseClause(node: Node): node is CaseClause { + return node.kind === SyntaxKind.CaseClause; +} + +export declare namespace isCaseClause { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isCaseClause.Handle = isCaseClause as any; + +export function isDefaultClause(node: Node): node is DefaultClause { + return node.kind === SyntaxKind.DefaultClause; +} + +export declare namespace isDefaultClause { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isDefaultClause.Handle = isDefaultClause as any; + +export function isObjectBindingPattern(node: Node): node is ObjectBindingPattern { + return node.kind === SyntaxKind.ObjectBindingPattern; +} + +export declare namespace isObjectBindingPattern { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isObjectBindingPattern.Handle = isObjectBindingPattern as any; + +export function isArrayBindingPattern(node: Node): node is ArrayBindingPattern { + return node.kind === SyntaxKind.ArrayBindingPattern; +} + +export declare namespace isArrayBindingPattern { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isArrayBindingPattern.Handle = isArrayBindingPattern as any; + +export function isJSDocParameterTag(node: Node): node is JSDocParameterTag { + return node.kind === SyntaxKind.JSDocParameterTag; +} + +export declare namespace isJSDocParameterTag { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocParameterTag.Handle = isJSDocParameterTag as any; + +export function isJSDocPropertyTag(node: Node): node is JSDocPropertyTag { + return node.kind === SyntaxKind.JSDocPropertyTag; +} + +export declare namespace isJSDocPropertyTag { + function Handle>(node: T): node is SpecializeNodeHandle; +} +isJSDocPropertyTag.Handle = isJSDocPropertyTag as any; + +export function isHeritageClauseElement(node: Node): node is HeritageClauseElement { + return node.kind === SyntaxKind.ExpressionWithTypeArguments || node.kind === SyntaxKind.TypeReference; +} + +export declare namespace isHeritageClauseElement { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isHeritageClauseElement.Handle = isHeritageClauseElement as any; + +export function isAccessExpression(node: Node): node is AccessExpression { + return node.kind === SyntaxKind.PropertyAccessExpression || node.kind === SyntaxKind.ElementAccessExpression; +} + +export declare namespace isAccessExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isAccessExpression.Handle = isAccessExpression as any; + +export function isDeclarationName(node: Node): node is DeclarationName { + const kind = node.kind; + return kind === SyntaxKind.Identifier || kind === SyntaxKind.PrivateIdentifier || kind === SyntaxKind.StringLiteral || kind === SyntaxKind.NumericLiteral || kind === SyntaxKind.BigIntLiteral || kind === SyntaxKind.NoSubstitutionTemplateLiteral || kind === SyntaxKind.ComputedPropertyName || kind === SyntaxKind.ObjectBindingPattern || kind === SyntaxKind.ArrayBindingPattern || kind === SyntaxKind.ElementAccessExpression; +} + +export declare namespace isDeclarationName { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isDeclarationName.Handle = isDeclarationName as any; + +export function isModuleName(node: Node): node is ModuleName { + return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.StringLiteral; +} + +export declare namespace isModuleName { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isModuleName.Handle = isModuleName as any; + +export function isModuleExportName(node: Node): node is ModuleExportName { + return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.StringLiteral; +} + +export declare namespace isModuleExportName { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isModuleExportName.Handle = isModuleExportName as any; + +export function isPropertyName(node: Node): node is PropertyName { + const kind = node.kind; + return kind === SyntaxKind.Identifier || kind === SyntaxKind.StringLiteral || kind === SyntaxKind.NoSubstitutionTemplateLiteral || kind === SyntaxKind.NumericLiteral || kind === SyntaxKind.ComputedPropertyName || kind === SyntaxKind.PrivateIdentifier || kind === SyntaxKind.BigIntLiteral; +} + +export declare namespace isPropertyName { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isPropertyName.Handle = isPropertyName as any; + +export function isModuleBody(node: Node): node is ModuleBody { + return node.kind === SyntaxKind.ModuleBlock || node.kind === SyntaxKind.ModuleDeclaration; +} + +export declare namespace isModuleBody { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isModuleBody.Handle = isModuleBody as any; + +export function isJSDocFullName(node: Node): node is JSDocFullName { + return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ModuleDeclaration; +} + +export declare namespace isJSDocFullName { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isJSDocFullName.Handle = isJSDocFullName as any; + +export function isModuleReference(node: Node): node is ModuleReference { + return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.ExternalModuleReference; +} + +export declare namespace isModuleReference { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isModuleReference.Handle = isModuleReference as any; + +export function isNamedImportBindings(node: Node): node is NamedImportBindings { + return node.kind === SyntaxKind.NamespaceImport || node.kind === SyntaxKind.NamedImports; +} + +export declare namespace isNamedImportBindings { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isNamedImportBindings.Handle = isNamedImportBindings as any; + +export function isNamedExportBindings(node: Node): node is NamedExportBindings { + return node.kind === SyntaxKind.NamespaceExport || node.kind === SyntaxKind.NamedExports; +} + +export declare namespace isNamedExportBindings { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isNamedExportBindings.Handle = isNamedExportBindings as any; + +export function isMemberName(node: Node): node is MemberName { + return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PrivateIdentifier; +} + +export declare namespace isMemberName { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isMemberName.Handle = isMemberName as any; + +export function isEntityName(node: Node): node is EntityName { + return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName; +} + +export declare namespace isEntityName { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isEntityName.Handle = isEntityName as any; + +export function isBindingName(node: Node): node is BindingName { + return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ObjectBindingPattern || node.kind === SyntaxKind.ArrayBindingPattern; +} + +export declare namespace isBindingName { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isBindingName.Handle = isBindingName as any; + +export function isModifierLike(node: Node): node is ModifierLike { + const kind = node.kind; + return kind === SyntaxKind.AbstractKeyword || kind === SyntaxKind.AccessorKeyword || kind === SyntaxKind.AsyncKeyword || kind === SyntaxKind.ConstKeyword || kind === SyntaxKind.DeclareKeyword || kind === SyntaxKind.DefaultKeyword || kind === SyntaxKind.ExportKeyword || kind === SyntaxKind.InKeyword || kind === SyntaxKind.PrivateKeyword || kind === SyntaxKind.ProtectedKeyword || kind === SyntaxKind.PublicKeyword || kind === SyntaxKind.ReadonlyKeyword || kind === SyntaxKind.OutKeyword || kind === SyntaxKind.OverrideKeyword || kind === SyntaxKind.StaticKeyword || kind === SyntaxKind.Decorator; +} + +export declare namespace isModifierLike { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isModifierLike.Handle = isModifierLike as any; + +export function isJsxChild(node: Node): node is JsxChild { + const kind = node.kind; + return kind === SyntaxKind.JsxText || kind === SyntaxKind.JsxExpression || kind === SyntaxKind.JsxElement || kind === SyntaxKind.JsxSelfClosingElement || kind === SyntaxKind.JsxFragment; +} + +export declare namespace isJsxChild { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isJsxChild.Handle = isJsxChild as any; + +export function isJsxAttributeLike(node: Node): node is JsxAttributeLike { + return node.kind === SyntaxKind.JsxAttribute || node.kind === SyntaxKind.JsxSpreadAttribute; +} + +export declare namespace isJsxAttributeLike { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isJsxAttributeLike.Handle = isJsxAttributeLike as any; + +export function isJsxAttributeName(node: Node): node is JsxAttributeName { + return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.JsxNamespacedName; +} + +export declare namespace isJsxAttributeName { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isJsxAttributeName.Handle = isJsxAttributeName as any; + +export function isJsxAttributeValue(node: Node): node is JsxAttributeValue { + const kind = node.kind; + return kind === SyntaxKind.StringLiteral || kind === SyntaxKind.JsxExpression || kind === SyntaxKind.JsxElement || kind === SyntaxKind.JsxSelfClosingElement || kind === SyntaxKind.JsxFragment; +} + +export declare namespace isJsxAttributeValue { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isJsxAttributeValue.Handle = isJsxAttributeValue as any; + +export function isClassLikeDeclaration(node: Node): node is ClassLikeDeclaration { + return node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression; +} + +export declare namespace isClassLikeDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isClassLikeDeclaration.Handle = isClassLikeDeclaration as any; + +export function isAccessorDeclaration(node: Node): node is AccessorDeclaration { + return node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.SetAccessor; +} + +export declare namespace isAccessorDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isAccessorDeclaration.Handle = isAccessorDeclaration as any; + +export function isLiteralLikeNode(node: Node): node is LiteralLikeNode { + const kind = node.kind; + return kind === SyntaxKind.StringLiteral || kind === SyntaxKind.NumericLiteral || kind === SyntaxKind.BigIntLiteral || kind === SyntaxKind.RegularExpressionLiteral || kind === SyntaxKind.TemplateHead || kind === SyntaxKind.TemplateMiddle || kind === SyntaxKind.TemplateTail || kind === SyntaxKind.JsxText; +} + +export declare namespace isLiteralLikeNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isLiteralLikeNode.Handle = isLiteralLikeNode as any; + +export function isLiteralExpression(node: Node): node is LiteralExpression { + const kind = node.kind; + return kind === SyntaxKind.StringLiteral || kind === SyntaxKind.NumericLiteral || kind === SyntaxKind.BigIntLiteral || kind === SyntaxKind.RegularExpressionLiteral || kind === SyntaxKind.NoSubstitutionTemplateLiteral; +} + +export declare namespace isLiteralExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isLiteralExpression.Handle = isLiteralExpression as any; + +export function isUnionOrIntersectionTypeNode(node: Node): node is UnionOrIntersectionTypeNode { + return node.kind === SyntaxKind.UnionType || node.kind === SyntaxKind.IntersectionType; +} + +export declare namespace isUnionOrIntersectionTypeNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isUnionOrIntersectionTypeNode.Handle = isUnionOrIntersectionTypeNode as any; + +export function isTemplateLiteralLikeNode(node: Node): node is TemplateLiteralLikeNode { + const kind = node.kind; + return isPseudoLiteralKind(kind); +} + +export declare namespace isTemplateLiteralLikeNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isTemplateLiteralLikeNode.Handle = isTemplateLiteralLikeNode as any; + +export function isTemplateMiddleOrTail(node: Node): node is TemplateMiddleOrTail { + return node.kind === SyntaxKind.TemplateMiddle || node.kind === SyntaxKind.TemplateTail; +} + +export declare namespace isTemplateMiddleOrTail { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isTemplateMiddleOrTail.Handle = isTemplateMiddleOrTail as any; + +export function isTemplateLiteral(node: Node): node is TemplateLiteral { + return node.kind === SyntaxKind.TemplateExpression || node.kind === SyntaxKind.NoSubstitutionTemplateLiteral; +} + +export declare namespace isTemplateLiteral { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isTemplateLiteral.Handle = isTemplateLiteral as any; + +export function isTypePredicateParameterName(node: Node): node is TypePredicateParameterName { + return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisType; +} + +export declare namespace isTypePredicateParameterName { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isTypePredicateParameterName.Handle = isTypePredicateParameterName as any; + +export function isImportAttributeName(node: Node): node is ImportAttributeName { + return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.StringLiteral; +} + +export declare namespace isImportAttributeName { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isImportAttributeName.Handle = isImportAttributeName as any; + +export function isJSDocComment(node: Node): node is JSDocComment { + const kind = node.kind; + return kind === SyntaxKind.JSDocText || kind === SyntaxKind.JSDocLink || kind === SyntaxKind.JSDocLinkCode || kind === SyntaxKind.JSDocLinkPlain; +} + +export declare namespace isJSDocComment { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isJSDocComment.Handle = isJSDocComment as any; + +export function isSignatureDeclaration(node: Node): node is SignatureDeclaration { + const kind = node.kind; + return kind === SyntaxKind.CallSignature || kind === SyntaxKind.ConstructSignature || kind === SyntaxKind.MethodSignature || kind === SyntaxKind.IndexSignature || kind === SyntaxKind.FunctionType || kind === SyntaxKind.ConstructorType || kind === SyntaxKind.FunctionDeclaration || kind === SyntaxKind.MethodDeclaration || kind === SyntaxKind.Constructor || kind === SyntaxKind.GetAccessor || kind === SyntaxKind.SetAccessor || kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction; +} + +export declare namespace isSignatureDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isSignatureDeclaration.Handle = isSignatureDeclaration as any; + +export function isStringLiteralLikeNode(node: Node): node is StringLiteralLikeNode { + return node.kind === SyntaxKind.StringLiteral || node.kind === SyntaxKind.NoSubstitutionTemplateLiteral; +} + +export declare namespace isStringLiteralLikeNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isStringLiteralLikeNode.Handle = isStringLiteralLikeNode as any; + +export function isNumericOrStringLikeLiteral(node: Node): node is NumericOrStringLikeLiteral { + return node.kind === SyntaxKind.StringLiteral || node.kind === SyntaxKind.NoSubstitutionTemplateLiteral || node.kind === SyntaxKind.NumericLiteral; +} + +export declare namespace isNumericOrStringLikeLiteral { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isNumericOrStringLikeLiteral.Handle = isNumericOrStringLikeLiteral as any; + +export function isObjectLiteralLikeNode(node: Node): node is ObjectLiteralLikeNode { + return node.kind === SyntaxKind.ObjectLiteralExpression || node.kind === SyntaxKind.ObjectBindingPattern; +} + +export declare namespace isObjectLiteralLikeNode { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isObjectLiteralLikeNode.Handle = isObjectLiteralLikeNode as any; + +export function isObjectTypeDeclaration(node: Node): node is ObjectTypeDeclaration { + const kind = node.kind; + return kind === SyntaxKind.ClassDeclaration || kind === SyntaxKind.ClassExpression || kind === SyntaxKind.InterfaceDeclaration || kind === SyntaxKind.TypeLiteral; +} + +export declare namespace isObjectTypeDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isObjectTypeDeclaration.Handle = isObjectTypeDeclaration as any; + +export function isJsxOpeningLikeElement(node: Node): node is JsxOpeningLikeElement { + return node.kind === SyntaxKind.JsxOpeningElement || node.kind === SyntaxKind.JsxSelfClosingElement; +} + +export declare namespace isJsxOpeningLikeElement { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isJsxOpeningLikeElement.Handle = isJsxOpeningLikeElement as any; + +export function isNamedImportsOrExports(node: Node): node is NamedImportsOrExports { + return node.kind === SyntaxKind.NamedImports || node.kind === SyntaxKind.NamedExports; +} + +export declare namespace isNamedImportsOrExports { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isNamedImportsOrExports.Handle = isNamedImportsOrExports as any; + +export function isBreakOrContinueStatement(node: Node): node is BreakOrContinueStatement { + return node.kind === SyntaxKind.BreakStatement || node.kind === SyntaxKind.ContinueStatement; +} + +export declare namespace isBreakOrContinueStatement { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isBreakOrContinueStatement.Handle = isBreakOrContinueStatement as any; + +export function isCallLikeExpression(node: Node): node is CallLikeExpression { + const kind = node.kind; + return kind === SyntaxKind.CallExpression || kind === SyntaxKind.NewExpression || kind === SyntaxKind.TaggedTemplateExpression || kind === SyntaxKind.Decorator || kind === SyntaxKind.JsxOpeningElement || kind === SyntaxKind.JsxSelfClosingElement || kind === SyntaxKind.BinaryExpression; +} + +export declare namespace isCallLikeExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isCallLikeExpression.Handle = isCallLikeExpression as any; + +export function isFunctionLikeDeclaration(node: Node): node is FunctionLikeDeclaration { + const kind = node.kind; + return kind === SyntaxKind.FunctionDeclaration || kind === SyntaxKind.MethodDeclaration || kind === SyntaxKind.GetAccessor || kind === SyntaxKind.SetAccessor || kind === SyntaxKind.Constructor || kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction; +} + +export declare namespace isFunctionLikeDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isFunctionLikeDeclaration.Handle = isFunctionLikeDeclaration as any; + +export function isVariableOrParameterDeclaration(node: Node): node is VariableOrParameterDeclaration { + return node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.Parameter; +} + +export declare namespace isVariableOrParameterDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isVariableOrParameterDeclaration.Handle = isVariableOrParameterDeclaration as any; + +export function isVariableOrPropertyDeclaration(node: Node): node is VariableOrPropertyDeclaration { + return node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.PropertyDeclaration; +} + +export declare namespace isVariableOrPropertyDeclaration { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isVariableOrPropertyDeclaration.Handle = isVariableOrPropertyDeclaration as any; + +export function isCallOrNewExpression(node: Node): node is CallOrNewExpression { + return node.kind === SyntaxKind.CallExpression || node.kind === SyntaxKind.NewExpression; +} + +export declare namespace isCallOrNewExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isCallOrNewExpression.Handle = isCallOrNewExpression as any; + +export function isImportClauseOrBindingPattern(node: Node): node is ImportClauseOrBindingPattern { + return node.kind === SyntaxKind.ImportClause || node.kind === SyntaxKind.ObjectBindingPattern || node.kind === SyntaxKind.ArrayBindingPattern; +} + +export declare namespace isImportClauseOrBindingPattern { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isImportClauseOrBindingPattern.Handle = isImportClauseOrBindingPattern as any; + +export function isAnyImportSyntax(node: Node): node is AnyImportSyntax { + return node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.JSImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration; +} + +export declare namespace isAnyImportSyntax { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isAnyImportSyntax.Handle = isAnyImportSyntax as any; + +export function isArrayBindingElement(node: Node): node is ArrayBindingElement { + return node.kind === SyntaxKind.BindingElement || node.kind === SyntaxKind.OmittedExpression; +} + +export declare namespace isArrayBindingElement { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isArrayBindingElement.Handle = isArrayBindingElement as any; + +export function isAssertionExpression(node: Node): node is AssertionExpression { + return node.kind === SyntaxKind.TypeAssertionExpression || node.kind === SyntaxKind.AsExpression; +} + +export declare namespace isAssertionExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isAssertionExpression.Handle = isAssertionExpression as any; + +export function isBooleanLiteral(node: Node): node is BooleanLiteral { + return node.kind === SyntaxKind.TrueKeyword || node.kind === SyntaxKind.FalseKeyword; +} + +export declare namespace isBooleanLiteral { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isBooleanLiteral.Handle = isBooleanLiteral as any; + +export function isDestructuringAssignment(node: Node): node is DestructuringAssignment { + return node.kind === SyntaxKind.BinaryExpression; +} + +export declare namespace isDestructuringAssignment { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isDestructuringAssignment.Handle = isDestructuringAssignment as any; + +export function isLiteralToken(node: Node): node is LiteralToken { + const kind = node.kind; + return kind === SyntaxKind.NumericLiteral || kind === SyntaxKind.BigIntLiteral || kind === SyntaxKind.StringLiteral || kind === SyntaxKind.JsxText || kind === SyntaxKind.RegularExpressionLiteral || kind === SyntaxKind.NoSubstitutionTemplateLiteral; +} + +export declare namespace isLiteralToken { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isLiteralToken.Handle = isLiteralToken as any; + +export function isModifier(node: Node): node is Modifier { + const kind = node.kind; + return isModifierKind(kind); +} + +export declare namespace isModifier { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isModifier.Handle = isModifier as any; + +export function isObjectLiteralElementLike(node: Node): node is ObjectLiteralElementLike { + const kind = node.kind; + return kind === SyntaxKind.PropertyAssignment || kind === SyntaxKind.ShorthandPropertyAssignment || kind === SyntaxKind.SpreadAssignment || kind === SyntaxKind.MethodDeclaration || kind === SyntaxKind.GetAccessor || kind === SyntaxKind.SetAccessor; +} + +export declare namespace isObjectLiteralElementLike { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isObjectLiteralElementLike.Handle = isObjectLiteralElementLike as any; + +export function isPropertyNameLiteral(node: Node): node is PropertyNameLiteral { + return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.StringLiteral || node.kind === SyntaxKind.NumericLiteral; +} + +export declare namespace isPropertyNameLiteral { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isPropertyNameLiteral.Handle = isPropertyNameLiteral as any; + +export function isPseudoLiteralToken(node: Node): node is PseudoLiteralToken { + const kind = node.kind; + return isPseudoLiteralKind(kind); +} + +export declare namespace isPseudoLiteralToken { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isPseudoLiteralToken.Handle = isPseudoLiteralToken as any; + +export function isTemplateLiteralToken(node: Node): node is TemplateLiteralToken { + const kind = node.kind; + return kind === SyntaxKind.NoSubstitutionTemplateLiteral || kind === SyntaxKind.TemplateHead || kind === SyntaxKind.TemplateMiddle || kind === SyntaxKind.TemplateTail; +} + +export declare namespace isTemplateLiteralToken { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isTemplateLiteralToken.Handle = isTemplateLiteralToken as any; + +export function isArrayDestructuringAssignment(node: Node): node is ArrayDestructuringAssignment { + return node.kind === SyntaxKind.BinaryExpression; +} + +export declare namespace isArrayDestructuringAssignment { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isArrayDestructuringAssignment.Handle = isArrayDestructuringAssignment as any; + +export function isObjectDestructuringAssignment(node: Node): node is ObjectDestructuringAssignment { + return node.kind === SyntaxKind.BinaryExpression; +} + +export declare namespace isObjectDestructuringAssignment { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isObjectDestructuringAssignment.Handle = isObjectDestructuringAssignment as any; + +export function isFunctionBody(node: Node): node is FunctionBody { + return node.kind === SyntaxKind.Block; +} + +export declare namespace isFunctionBody { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isFunctionBody.Handle = isFunctionBody as any; + +export function isTriviaKind(kind: SyntaxKind): kind is TriviaSyntaxKind { + return kind === SyntaxKind.SingleLineCommentTrivia + || kind === SyntaxKind.MultiLineCommentTrivia + || kind === SyntaxKind.NewLineTrivia + || kind === SyntaxKind.WhitespaceTrivia + || kind === SyntaxKind.ConflictMarkerTrivia; +} + +export function isPseudoLiteralKind(kind: SyntaxKind): kind is PseudoLiteralSyntaxKind { + return kind === SyntaxKind.TemplateHead + || kind === SyntaxKind.TemplateMiddle + || kind === SyntaxKind.TemplateTail; +} + +export function isModifierKind(kind: SyntaxKind): kind is ModifierSyntaxKind { + return kind === SyntaxKind.AbstractKeyword + || kind === SyntaxKind.AccessorKeyword + || kind === SyntaxKind.AsyncKeyword + || kind === SyntaxKind.ConstKeyword + || kind === SyntaxKind.DeclareKeyword + || kind === SyntaxKind.DefaultKeyword + || kind === SyntaxKind.ExportKeyword + || kind === SyntaxKind.InKeyword + || kind === SyntaxKind.PrivateKeyword + || kind === SyntaxKind.ProtectedKeyword + || kind === SyntaxKind.PublicKeyword + || kind === SyntaxKind.ReadonlyKeyword + || kind === SyntaxKind.OutKeyword + || kind === SyntaxKind.OverrideKeyword + || kind === SyntaxKind.StaticKeyword; +} + +export function isKeywordTypeKind(kind: SyntaxKind): kind is KeywordTypeSyntaxKind { + return kind === SyntaxKind.AnyKeyword + || kind === SyntaxKind.BigIntKeyword + || kind === SyntaxKind.BooleanKeyword + || kind === SyntaxKind.IntrinsicKeyword + || kind === SyntaxKind.NeverKeyword + || kind === SyntaxKind.NumberKeyword + || kind === SyntaxKind.ObjectKeyword + || kind === SyntaxKind.StringKeyword + || kind === SyntaxKind.SymbolKeyword + || kind === SyntaxKind.UndefinedKeyword + || kind === SyntaxKind.UnknownKeyword + || kind === SyntaxKind.VoidKeyword; +} + +export function isKeywordExpressionKind(kind: SyntaxKind): kind is KeywordExpressionSyntaxKind { + return kind === SyntaxKind.NullKeyword + || kind === SyntaxKind.TrueKeyword + || kind === SyntaxKind.FalseKeyword + || kind === SyntaxKind.ThisKeyword + || kind === SyntaxKind.SuperKeyword + || kind === SyntaxKind.ImportKeyword; +} + +export function isJsxTokenKind(kind: SyntaxKind): kind is JsxTokenSyntaxKind { + return kind === SyntaxKind.LessThanSlashToken + || kind === SyntaxKind.EndOfFile + || kind === SyntaxKind.ConflictMarkerTrivia + || kind === SyntaxKind.JsxText + || kind === SyntaxKind.JsxTextAllWhiteSpaces + || kind === SyntaxKind.OpenBraceToken + || kind === SyntaxKind.LessThanToken; +} + +export function isImportPhaseModifierKind(kind: SyntaxKind): kind is ImportPhaseModifierSyntaxKind { + return kind === SyntaxKind.TypeKeyword + || kind === SyntaxKind.DeferKeyword; +} + +export function isPostfixUnaryOperator(kind: SyntaxKind): kind is PostfixUnaryOperator { + return kind === SyntaxKind.PlusPlusToken + || kind === SyntaxKind.MinusMinusToken; +} + +export function isPrefixUnaryOperator(kind: SyntaxKind): kind is PrefixUnaryOperator { + return kind === SyntaxKind.PlusToken + || kind === SyntaxKind.MinusToken + || kind === SyntaxKind.TildeToken + || kind === SyntaxKind.ExclamationToken + || kind === SyntaxKind.PlusPlusToken + || kind === SyntaxKind.MinusMinusToken; +} + +export function isAssignmentOperator(kind: SyntaxKind): kind is AssignmentOperator { + return kind === SyntaxKind.EqualsToken + || isCompoundAssignmentOperator(kind); +} + +export function isBinaryOperator(kind: SyntaxKind): kind is BinaryOperator { + return isAssignmentOperatorOrHigher(kind) + || kind === SyntaxKind.CommaToken; +} + +export function isExponentiationOperator(kind: SyntaxKind): kind is ExponentiationOperator { + return kind === SyntaxKind.AsteriskAsteriskToken; +} + +export function isMultiplicativeOperator(kind: SyntaxKind): kind is MultiplicativeOperator { + return kind === SyntaxKind.AsteriskToken + || kind === SyntaxKind.SlashToken + || kind === SyntaxKind.PercentToken; +} + +export function isMultiplicativeOperatorOrHigher(kind: SyntaxKind): kind is MultiplicativeOperatorOrHigher { + return isExponentiationOperator(kind) + || isMultiplicativeOperator(kind); +} + +export function isAdditiveOperator(kind: SyntaxKind): kind is AdditiveOperator { + return kind === SyntaxKind.PlusToken + || kind === SyntaxKind.MinusToken; +} + +export function isAdditiveOperatorOrHigher(kind: SyntaxKind): kind is AdditiveOperatorOrHigher { + return isMultiplicativeOperatorOrHigher(kind) + || isAdditiveOperator(kind); +} + +export function isShiftOperator(kind: SyntaxKind): kind is ShiftOperator { + return kind === SyntaxKind.LessThanLessThanToken + || kind === SyntaxKind.GreaterThanGreaterThanToken + || kind === SyntaxKind.GreaterThanGreaterThanGreaterThanToken; +} + +export function isShiftOperatorOrHigher(kind: SyntaxKind): kind is ShiftOperatorOrHigher { + return isAdditiveOperatorOrHigher(kind) + || isShiftOperator(kind); +} + +export function isRelationalOperator(kind: SyntaxKind): kind is RelationalOperator { + return kind === SyntaxKind.LessThanToken + || kind === SyntaxKind.LessThanEqualsToken + || kind === SyntaxKind.GreaterThanToken + || kind === SyntaxKind.GreaterThanEqualsToken + || kind === SyntaxKind.InstanceOfKeyword + || kind === SyntaxKind.InKeyword; +} + +export function isRelationalOperatorOrHigher(kind: SyntaxKind): kind is RelationalOperatorOrHigher { + return isShiftOperatorOrHigher(kind) + || isRelationalOperator(kind); +} + +export function isEqualityOperator(kind: SyntaxKind): kind is EqualityOperator { + return kind === SyntaxKind.EqualsEqualsToken + || kind === SyntaxKind.EqualsEqualsEqualsToken + || kind === SyntaxKind.ExclamationEqualsEqualsToken + || kind === SyntaxKind.ExclamationEqualsToken; +} + +export function isEqualityOperatorOrHigher(kind: SyntaxKind): kind is EqualityOperatorOrHigher { + return isRelationalOperatorOrHigher(kind) + || isEqualityOperator(kind); +} + +export function isBitwiseOperator(kind: SyntaxKind): kind is BitwiseOperator { + return kind === SyntaxKind.AmpersandToken + || kind === SyntaxKind.BarToken + || kind === SyntaxKind.CaretToken; +} + +export function isBitwiseOperatorOrHigher(kind: SyntaxKind): kind is BitwiseOperatorOrHigher { + return isEqualityOperatorOrHigher(kind) + || isBitwiseOperator(kind); +} + +export function isLogicalOperator(kind: SyntaxKind): kind is LogicalOperator { + return kind === SyntaxKind.AmpersandAmpersandToken + || kind === SyntaxKind.BarBarToken; +} + +export function isLogicalOperatorOrHigher(kind: SyntaxKind): kind is LogicalOperatorOrHigher { + return isBitwiseOperatorOrHigher(kind) + || isLogicalOperator(kind); +} + +export function isCompoundAssignmentOperator(kind: SyntaxKind): kind is CompoundAssignmentOperator { + return kind === SyntaxKind.PlusEqualsToken + || kind === SyntaxKind.MinusEqualsToken + || kind === SyntaxKind.AsteriskAsteriskEqualsToken + || kind === SyntaxKind.AsteriskEqualsToken + || kind === SyntaxKind.SlashEqualsToken + || kind === SyntaxKind.PercentEqualsToken + || kind === SyntaxKind.AmpersandEqualsToken + || kind === SyntaxKind.BarEqualsToken + || kind === SyntaxKind.CaretEqualsToken + || kind === SyntaxKind.LessThanLessThanEqualsToken + || kind === SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken + || kind === SyntaxKind.GreaterThanGreaterThanEqualsToken + || kind === SyntaxKind.BarBarEqualsToken + || kind === SyntaxKind.AmpersandAmpersandEqualsToken + || kind === SyntaxKind.QuestionQuestionEqualsToken; +} + +export function isAssignmentOperatorOrHigher(kind: SyntaxKind): kind is AssignmentOperatorOrHigher { + return kind === SyntaxKind.QuestionQuestionToken + || isLogicalOperatorOrHigher(kind) + || isAssignmentOperator(kind); +} + +export function isLogicalOrCoalescingAssignmentOperator(kind: SyntaxKind): kind is LogicalOrCoalescingAssignmentOperator { + return kind === SyntaxKind.AmpersandAmpersandEqualsToken + || kind === SyntaxKind.BarBarEqualsToken + || kind === SyntaxKind.QuestionQuestionEqualsToken; +} + +export function isLiteralKind(kind: SyntaxKind): boolean { + return kind >= SyntaxKind.FirstLiteralToken && kind <= SyntaxKind.LastLiteralToken; +} + +export function isPunctuationKind(kind: SyntaxKind): boolean { + return kind >= SyntaxKind.FirstPunctuation && kind <= SyntaxKind.LastPunctuation; +} + +export function isKeywordKind(kind: SyntaxKind): boolean { + return kind >= SyntaxKind.FirstKeyword && kind <= SyntaxKind.LastKeyword; +} + +export function isTokenKind(kind: SyntaxKind): boolean { + return kind >= SyntaxKind.FirstToken && kind <= SyntaxKind.LastToken; +} + +export function isJSDocNodeKind(kind: SyntaxKind): boolean { + return kind >= SyntaxKind.FirstJSDocNode && kind <= SyntaxKind.LastJSDocNode; +} + +export function isEndOfFile(node: Node): node is EndOfFile { + return node.kind === SyntaxKind.EndOfFile; +} + +export declare namespace isEndOfFile { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isEndOfFile.Handle = isEndOfFile as any; + +export function isDotToken(node: Node): node is DotToken { + return node.kind === SyntaxKind.DotToken; +} + +export declare namespace isDotToken { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isDotToken.Handle = isDotToken as any; + +export function isDotDotDotToken(node: Node): node is DotDotDotToken { + return node.kind === SyntaxKind.DotDotDotToken; +} + +export declare namespace isDotDotDotToken { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isDotDotDotToken.Handle = isDotDotDotToken as any; + +export function isQuestionToken(node: Node): node is QuestionToken { + return node.kind === SyntaxKind.QuestionToken; +} + +export declare namespace isQuestionToken { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isQuestionToken.Handle = isQuestionToken as any; + +export function isExclamationToken(node: Node): node is ExclamationToken { + return node.kind === SyntaxKind.ExclamationToken; +} + +export declare namespace isExclamationToken { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isExclamationToken.Handle = isExclamationToken as any; + +export function isColonToken(node: Node): node is ColonToken { + return node.kind === SyntaxKind.ColonToken; +} + +export declare namespace isColonToken { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isColonToken.Handle = isColonToken as any; + +export function isEqualsToken(node: Node): node is EqualsToken { + return node.kind === SyntaxKind.EqualsToken; +} + +export declare namespace isEqualsToken { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isEqualsToken.Handle = isEqualsToken as any; + +export function isAsteriskToken(node: Node): node is AsteriskToken { + return node.kind === SyntaxKind.AsteriskToken; +} + +export declare namespace isAsteriskToken { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isAsteriskToken.Handle = isAsteriskToken as any; + +export function isEqualsGreaterThanToken(node: Node): node is EqualsGreaterThanToken { + return node.kind === SyntaxKind.EqualsGreaterThanToken; +} + +export declare namespace isEqualsGreaterThanToken { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isEqualsGreaterThanToken.Handle = isEqualsGreaterThanToken as any; + +export function isPlusToken(node: Node): node is PlusToken { + return node.kind === SyntaxKind.PlusToken; +} + +export declare namespace isPlusToken { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isPlusToken.Handle = isPlusToken as any; + +export function isMinusToken(node: Node): node is MinusToken { + return node.kind === SyntaxKind.MinusToken; +} + +export declare namespace isMinusToken { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isMinusToken.Handle = isMinusToken as any; + +export function isQuestionDotToken(node: Node): node is QuestionDotToken { + return node.kind === SyntaxKind.QuestionDotToken; +} + +export declare namespace isQuestionDotToken { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isQuestionDotToken.Handle = isQuestionDotToken as any; + +export function isAssertsKeyword(node: Node): node is AssertsKeyword { + return node.kind === SyntaxKind.AssertsKeyword; +} + +export declare namespace isAssertsKeyword { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isAssertsKeyword.Handle = isAssertsKeyword as any; + +export function isAssertKeyword(node: Node): node is AssertKeyword { + return node.kind === SyntaxKind.AssertKeyword; +} + +export declare namespace isAssertKeyword { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isAssertKeyword.Handle = isAssertKeyword as any; + +export function isAwaitKeyword(node: Node): node is AwaitKeyword { + return node.kind === SyntaxKind.AwaitKeyword; +} + +export declare namespace isAwaitKeyword { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isAwaitKeyword.Handle = isAwaitKeyword as any; + +export function isCaseKeyword(node: Node): node is CaseKeyword { + return node.kind === SyntaxKind.CaseKeyword; +} + +export declare namespace isCaseKeyword { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isCaseKeyword.Handle = isCaseKeyword as any; + +export function isAbstractKeyword(node: Node): node is AbstractKeyword { + return node.kind === SyntaxKind.AbstractKeyword; +} + +export declare namespace isAbstractKeyword { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isAbstractKeyword.Handle = isAbstractKeyword as any; + +export function isAccessorKeyword(node: Node): node is AccessorKeyword { + return node.kind === SyntaxKind.AccessorKeyword; +} + +export declare namespace isAccessorKeyword { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isAccessorKeyword.Handle = isAccessorKeyword as any; + +export function isAsyncKeyword(node: Node): node is AsyncKeyword { + return node.kind === SyntaxKind.AsyncKeyword; +} + +export declare namespace isAsyncKeyword { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isAsyncKeyword.Handle = isAsyncKeyword as any; + +export function isConstKeyword(node: Node): node is ConstKeyword { + return node.kind === SyntaxKind.ConstKeyword; +} + +export declare namespace isConstKeyword { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isConstKeyword.Handle = isConstKeyword as any; + +export function isDeclareKeyword(node: Node): node is DeclareKeyword { + return node.kind === SyntaxKind.DeclareKeyword; +} + +export declare namespace isDeclareKeyword { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isDeclareKeyword.Handle = isDeclareKeyword as any; + +export function isDefaultKeyword(node: Node): node is DefaultKeyword { + return node.kind === SyntaxKind.DefaultKeyword; +} + +export declare namespace isDefaultKeyword { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isDefaultKeyword.Handle = isDefaultKeyword as any; + +export function isExportKeyword(node: Node): node is ExportKeyword { + return node.kind === SyntaxKind.ExportKeyword; +} + +export declare namespace isExportKeyword { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isExportKeyword.Handle = isExportKeyword as any; + +export function isInKeyword(node: Node): node is InKeyword { + return node.kind === SyntaxKind.InKeyword; +} + +export declare namespace isInKeyword { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isInKeyword.Handle = isInKeyword as any; + +export function isPrivateKeyword(node: Node): node is PrivateKeyword { + return node.kind === SyntaxKind.PrivateKeyword; +} + +export declare namespace isPrivateKeyword { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isPrivateKeyword.Handle = isPrivateKeyword as any; + +export function isProtectedKeyword(node: Node): node is ProtectedKeyword { + return node.kind === SyntaxKind.ProtectedKeyword; +} + +export declare namespace isProtectedKeyword { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isProtectedKeyword.Handle = isProtectedKeyword as any; + +export function isPublicKeyword(node: Node): node is PublicKeyword { + return node.kind === SyntaxKind.PublicKeyword; +} + +export declare namespace isPublicKeyword { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isPublicKeyword.Handle = isPublicKeyword as any; + +export function isReadonlyKeyword(node: Node): node is ReadonlyKeyword { + return node.kind === SyntaxKind.ReadonlyKeyword; +} + +export declare namespace isReadonlyKeyword { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isReadonlyKeyword.Handle = isReadonlyKeyword as any; + +export function isOutKeyword(node: Node): node is OutKeyword { + return node.kind === SyntaxKind.OutKeyword; +} + +export declare namespace isOutKeyword { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isOutKeyword.Handle = isOutKeyword as any; + +export function isOverrideKeyword(node: Node): node is OverrideKeyword { + return node.kind === SyntaxKind.OverrideKeyword; +} + +export declare namespace isOverrideKeyword { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isOverrideKeyword.Handle = isOverrideKeyword as any; + +export function isStaticKeyword(node: Node): node is StaticKeyword { + return node.kind === SyntaxKind.StaticKeyword; +} + +export declare namespace isStaticKeyword { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isStaticKeyword.Handle = isStaticKeyword as any; + +export function isBinaryOperatorToken(node: Node): node is BinaryOperatorToken { + return isBinaryOperator(node.kind); +} + +export declare namespace isBinaryOperatorToken { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isBinaryOperatorToken.Handle = isBinaryOperatorToken as any; + +export function isAssignmentOperatorToken(node: Node): node is AssignmentOperatorToken { + return isAssignmentOperator(node.kind); +} + +export declare namespace isAssignmentOperatorToken { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isAssignmentOperatorToken.Handle = isAssignmentOperatorToken as any; + +export function isNullLiteral(node: Node): node is NullLiteral { + return node.kind === SyntaxKind.NullKeyword; +} + +export declare namespace isNullLiteral { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isNullLiteral.Handle = isNullLiteral as any; + +export function isTrueLiteral(node: Node): node is TrueLiteral { + return node.kind === SyntaxKind.TrueKeyword; +} + +export declare namespace isTrueLiteral { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isTrueLiteral.Handle = isTrueLiteral as any; + +export function isFalseLiteral(node: Node): node is FalseLiteral { + return node.kind === SyntaxKind.FalseKeyword; +} + +export declare namespace isFalseLiteral { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isFalseLiteral.Handle = isFalseLiteral as any; + +export function isThisExpression(node: Node): node is ThisExpression { + return node.kind === SyntaxKind.ThisKeyword; +} + +export declare namespace isThisExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isThisExpression.Handle = isThisExpression as any; + +export function isSuperExpression(node: Node): node is SuperExpression { + return node.kind === SyntaxKind.SuperKeyword; +} + +export declare namespace isSuperExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isSuperExpression.Handle = isSuperExpression as any; + +export function isImportExpression(node: Node): node is ImportExpression { + return node.kind === SyntaxKind.ImportKeyword; +} + +export declare namespace isImportExpression { + function Handle>(node: T): node is SpecializeNodeHandle; +} + +isImportExpression.Handle = isImportExpression as any; diff --git a/packages/typescript/src/ast/is.ts b/packages/typescript/src/ast/is.ts index ecf91f4c74f3b..963f045edc1a3 100644 --- a/packages/typescript/src/ast/is.ts +++ b/packages/typescript/src/ast/is.ts @@ -1,582 +1,582 @@ -// is.ts — Hand-written type guard functions -// Generated guards are in is.generated.ts - -import { ModifierFlags } from "#enums/modifierFlags"; -import { NodeFlags } from "#enums/nodeFlags"; -import { OuterExpressionKinds } from "#enums/outerExpressionKinds"; -import { ScriptKind } from "#enums/scriptKind"; -import { SyntaxKind } from "#enums/syntaxKind"; -import type { - AsExpression, - BindingPattern, - BlockOrExpression, - BooleanLiteral, - ComputedPropertyName, - ConciseBody, - ExclamationToken, - Expression, - ExpressionWithTypeArguments, - ForInitializer, - ForInOrOfStatement, - ForStatement, - Identifier, - JSDocTypeExpression, - JSDocTypeLiteral, - JsxTagNameExpression, - LeftHandSideExpression, - LiteralExpression, - MetaProperty, - MinusToken, - ModuleDeclaration, - Node, - NonNullExpression, - NullLiteral, - ParenthesizedExpression, - PartiallyEmittedExpression, - PlusToken, - PrefixUnaryExpression, - QuestionToken, - ReadonlyKeyword, - SatisfiesExpression, - Statement, - TemplateMiddle, - TemplateTail, - ThisTypeNode, - TypeAssertion, - TypeNode, - UnaryExpressionBase, -} from "./ast.ts"; -import { - isBinaryExpression, - isClassLikeDeclaration, - isComputedPropertyName, - isHeritageClause, - isIdentifier, - isJSDocAugmentsTag, - isJSDocImplementsTag, - isJSDocLink, - isJSDocLinkCode, - isJSDocLinkPlain, - isJSDocNameReference, - isLiteralExpression, - isShorthandPropertyAssignment, -} from "./is.generated.ts"; -import { - hasExpression, - hasInitializer, - hasObjectAssignmentInitializer, -} from "./utils.ts"; - -export * from "./is.generated.ts"; - -type JSDocNamespaceDeclaration = ModuleDeclaration; - -type WrappedExpression = - | ParenthesizedExpression - | TypeAssertion - | AsExpression - | SatisfiesExpression - | ExpressionWithTypeArguments - | NonNullExpression - | PartiallyEmittedExpression; - -type OuterExpression = WrappedExpression; - -export function isTypeNode(node: Node): node is TypeNode { - return isTypeNodeKind(node.kind); -} - -function isTypeNodeKind(kind: SyntaxKind): boolean { - return kind >= SyntaxKind.FirstTypeNode && kind <= SyntaxKind.LastTypeNode - || kind === SyntaxKind.AnyKeyword - || kind === SyntaxKind.UnknownKeyword - || kind === SyntaxKind.NumberKeyword - || kind === SyntaxKind.BigIntKeyword - || kind === SyntaxKind.ObjectKeyword - || kind === SyntaxKind.BooleanKeyword - || kind === SyntaxKind.StringKeyword - || kind === SyntaxKind.SymbolKeyword - || kind === SyntaxKind.VoidKeyword - || kind === SyntaxKind.UndefinedKeyword - || kind === SyntaxKind.NeverKeyword - || kind === SyntaxKind.IntrinsicKeyword - || kind === SyntaxKind.ExpressionWithTypeArguments - || kind === SyntaxKind.JSDocAllType - || kind === SyntaxKind.JSDocNullableType - || kind === SyntaxKind.JSDocNonNullableType - || kind === SyntaxKind.JSDocOptionalType - || kind === SyntaxKind.JSDocVariadicType - || kind === SyntaxKind.JSDocTypeExpression - || kind === SyntaxKind.JSDocTypeLiteral - || kind === SyntaxKind.JSDocSignature; -} - -export function isStatement(node: Node): node is Statement { - const kind = node.kind; - return kind === SyntaxKind.VariableStatement || kind === SyntaxKind.EmptyStatement - || kind === SyntaxKind.ExpressionStatement || kind === SyntaxKind.IfStatement - || kind === SyntaxKind.DoStatement || kind === SyntaxKind.WhileStatement - || kind === SyntaxKind.ForStatement || kind === SyntaxKind.ForInStatement - || kind === SyntaxKind.ForOfStatement || kind === SyntaxKind.ContinueStatement - || kind === SyntaxKind.BreakStatement || kind === SyntaxKind.ReturnStatement - || kind === SyntaxKind.WithStatement || kind === SyntaxKind.SwitchStatement - || kind === SyntaxKind.LabeledStatement || kind === SyntaxKind.ThrowStatement - || kind === SyntaxKind.TryStatement || kind === SyntaxKind.DebuggerStatement - || kind === SyntaxKind.InterfaceDeclaration || kind === SyntaxKind.TypeAliasDeclaration - || kind === SyntaxKind.EnumDeclaration || kind === SyntaxKind.ModuleDeclaration - || kind === SyntaxKind.ImportDeclaration || kind === SyntaxKind.ImportEqualsDeclaration - || kind === SyntaxKind.ExportDeclaration || kind === SyntaxKind.ExportAssignment - || kind === SyntaxKind.NamespaceExportDeclaration || kind === SyntaxKind.FunctionDeclaration - || kind === SyntaxKind.ClassDeclaration || kind === SyntaxKind.MissingDeclaration - || kind === SyntaxKind.NotEmittedStatement || kind === SyntaxKind.Block; -} - -export function isExpression(node: Node): node is Expression { - const kind = node.kind; - return kind === SyntaxKind.ConditionalExpression || kind === SyntaxKind.YieldExpression - || kind === SyntaxKind.ArrowFunction || kind === SyntaxKind.BinaryExpression - || kind === SyntaxKind.SpreadElement || kind === SyntaxKind.AsExpression - || kind === SyntaxKind.OmittedExpression - || kind === SyntaxKind.SatisfiesExpression - || kind === SyntaxKind.PrefixUnaryExpression || kind === SyntaxKind.PostfixUnaryExpression - || kind === SyntaxKind.DeleteExpression || kind === SyntaxKind.TypeOfExpression - || kind === SyntaxKind.VoidExpression || kind === SyntaxKind.AwaitExpression - || kind === SyntaxKind.TypeAssertionExpression - || kind === SyntaxKind.CallExpression || kind === SyntaxKind.NewExpression - || kind === SyntaxKind.TaggedTemplateExpression || kind === SyntaxKind.NonNullExpression - || kind === SyntaxKind.MetaProperty || kind === SyntaxKind.JsxExpression - || kind === SyntaxKind.PropertyAccessExpression || kind === SyntaxKind.ElementAccessExpression - || kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ClassExpression - || kind === SyntaxKind.ParenthesizedExpression || kind === SyntaxKind.ArrayLiteralExpression - || kind === SyntaxKind.ObjectLiteralExpression || kind === SyntaxKind.TemplateExpression - || kind === SyntaxKind.Identifier - || kind === SyntaxKind.PrivateIdentifier - || kind === SyntaxKind.NumericLiteral || kind === SyntaxKind.BigIntLiteral - || kind === SyntaxKind.StringLiteral || kind === SyntaxKind.RegularExpressionLiteral - || kind === SyntaxKind.NoSubstitutionTemplateLiteral || kind === SyntaxKind.JsxElement - || kind === SyntaxKind.JsxSelfClosingElement || kind === SyntaxKind.JsxFragment - || kind === SyntaxKind.NullKeyword || kind === SyntaxKind.TrueKeyword - || kind === SyntaxKind.FalseKeyword || kind === SyntaxKind.ThisKeyword - || kind === SyntaxKind.SuperKeyword || kind === SyntaxKind.ImportKeyword - || kind === SyntaxKind.ExpressionWithTypeArguments; -} - -export function isValidTypeOnlyAliasUseSite(useSite: Node): boolean { - return !!(useSite.flags & NodeFlags.Ambient) - || isInJSDoc(useSite) - || isPartOfTypeQuery(useSite) - || isIdentifierInNonEmittingHeritageClause(useSite) - || isPartOfPossiblyValidTypeOrAbstractComputedPropertyName(useSite) - || !(isExpressionNode(useSite) || isShorthandPropertyNameUseSite(useSite)); -} - -function isInJSDoc(node: Node): boolean { - return !!(node.flags & NodeFlags.JSDoc); -} - -function isPartOfTypeQuery(node: Node): boolean { - while (node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.Identifier) { - node = node.parent; - } - return node.kind === SyntaxKind.TypeQuery; -} - -function isIdentifierInNonEmittingHeritageClause(node: Node): boolean { - if (!isIdentifier(node)) return false; - - const heritageClause = findAncestor(node.parent, parent => { - switch (parent.kind) { - case SyntaxKind.HeritageClause: - return true; - case SyntaxKind.PropertyAccessExpression: - case SyntaxKind.ExpressionWithTypeArguments: - return false; - default: - return "quit"; - } - }); - - return heritageClause !== undefined && - isHeritageClause(heritageClause) && - (heritageClause.token === SyntaxKind.ImplementsKeyword || heritageClause.parent.kind === SyntaxKind.InterfaceDeclaration); -} - -function isPartOfPossiblyValidTypeOrAbstractComputedPropertyName(node: Node): boolean { - while (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PropertyAccessExpression) { - node = node.parent; - } - if (!isComputedPropertyName(node)) { - return false; - } - if (hasModifierFlags(node.parent) && node.parent.modifierFlags & ModifierFlags.Abstract) { - return true; - } - const containerKind = node.parent.parent.kind; - return containerKind === SyntaxKind.InterfaceDeclaration || containerKind === SyntaxKind.TypeLiteral; -} - -function isShorthandPropertyNameUseSite(useSite: Node): boolean { - return isIdentifier(useSite) && isShorthandPropertyAssignment(useSite.parent) && useSite.parent.name === useSite; -} - -type AncestorCallbackResult = boolean | "quit"; - -function findAncestor(node: Node, callback: (node: Node) => AncestorCallbackResult): Node | undefined { - while (node) { - const result = callback(node); - if (result === "quit") { - return undefined; - } - if (result) { - return node; - } - node = node.parent; - } -} - -function hasModifierFlags(node: Node): node is Node & { readonly modifierFlags: ModifierFlags; } { - return "modifierFlags" in node; -} - -function isExpressionNode(node: Node): boolean { - switch (node.kind) { - case SyntaxKind.SuperKeyword: - case SyntaxKind.NullKeyword: - case SyntaxKind.TrueKeyword: - case SyntaxKind.FalseKeyword: - case SyntaxKind.RegularExpressionLiteral: - case SyntaxKind.ArrayLiteralExpression: - case SyntaxKind.ObjectLiteralExpression: - case SyntaxKind.PropertyAccessExpression: - case SyntaxKind.ElementAccessExpression: - case SyntaxKind.CallExpression: - case SyntaxKind.NewExpression: - case SyntaxKind.TaggedTemplateExpression: - case SyntaxKind.AsExpression: - case SyntaxKind.TypeAssertionExpression: - case SyntaxKind.SatisfiesExpression: - case SyntaxKind.NonNullExpression: - case SyntaxKind.ParenthesizedExpression: - case SyntaxKind.FunctionExpression: - case SyntaxKind.ClassExpression: - case SyntaxKind.ArrowFunction: - case SyntaxKind.VoidExpression: - case SyntaxKind.DeleteExpression: - case SyntaxKind.TypeOfExpression: - case SyntaxKind.PrefixUnaryExpression: - case SyntaxKind.PostfixUnaryExpression: - case SyntaxKind.BinaryExpression: - case SyntaxKind.ConditionalExpression: - case SyntaxKind.SpreadElement: - case SyntaxKind.TemplateExpression: - case SyntaxKind.OmittedExpression: - case SyntaxKind.JsxElement: - case SyntaxKind.JsxSelfClosingElement: - case SyntaxKind.JsxFragment: - case SyntaxKind.YieldExpression: - case SyntaxKind.AwaitExpression: - return true; - case SyntaxKind.MetaProperty: - return !isImportCall(node.parent) || node.parent.expression !== node; - case SyntaxKind.ExpressionWithTypeArguments: - return !isHeritageClause(node.parent); - case SyntaxKind.QualifiedName: - while (node.parent.kind === SyntaxKind.QualifiedName) { - node = node.parent; - } - return node.parent.kind === SyntaxKind.TypeQuery || isInJSDocNameReference(node) || isJSXTagName(node); - case SyntaxKind.PrivateIdentifier: - return isBinaryExpression(node.parent) && node.parent.left === node && node.parent.operatorToken.kind === SyntaxKind.InKeyword; - case SyntaxKind.Identifier: - if (node.parent.kind === SyntaxKind.TypeQuery || isInJSDocNameReference(node) || isJSXTagName(node)) { - return true; - } - // falls through - case SyntaxKind.NumericLiteral: - case SyntaxKind.BigIntLiteral: - case SyntaxKind.StringLiteral: - case SyntaxKind.NoSubstitutionTemplateLiteral: - case SyntaxKind.ThisKeyword: - return isInExpressionContext(node); - default: - return false; - } -} - -function isImportCall(node: Node): node is Node & { readonly expression: MetaProperty; } { - if (node.kind !== SyntaxKind.CallExpression || !hasExpression(node) || !node.expression) { - return false; - } - return node.expression.kind === SyntaxKind.MetaProperty - && (node.expression as MetaProperty).keywordToken === SyntaxKind.ImportKeyword; -} - -function isJSDocLinkLike(node: Node): boolean { - return isJSDocLink(node) || isJSDocLinkCode(node) || isJSDocLinkPlain(node); -} - -function isInJSDocNameReference(node: Node): boolean { - return isJSDocLinkLike(node.parent) || isJSDocNameReference(node.parent); -} - -function isInExpressionContext(node: Node): boolean { - const parent = node.parent; - switch (parent.kind) { - case SyntaxKind.VariableDeclaration: - case SyntaxKind.Parameter: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.PropertySignature: - case SyntaxKind.EnumMember: - case SyntaxKind.PropertyAssignment: - case SyntaxKind.BindingElement: - return hasInitializer(parent) && parent.initializer === node; - case SyntaxKind.ExpressionStatement: - case SyntaxKind.IfStatement: - case SyntaxKind.DoStatement: - case SyntaxKind.WhileStatement: - case SyntaxKind.ReturnStatement: - case SyntaxKind.WithStatement: - case SyntaxKind.SwitchStatement: - case SyntaxKind.CaseClause: - case SyntaxKind.DefaultClause: - case SyntaxKind.ThrowStatement: - return hasExpression(parent) && parent.expression === node; - case SyntaxKind.ForStatement: - return isForStatementExpression(node, parent as ForStatement); - case SyntaxKind.ForInStatement: - case SyntaxKind.ForOfStatement: - return isForInOrOfStatementExpression(node, parent as ForInOrOfStatement); - case SyntaxKind.TypeAssertionExpression: - case SyntaxKind.AsExpression: - return hasExpression(parent) && parent.expression === node; - case SyntaxKind.TemplateSpan: - return hasExpression(parent) && parent.expression === node; - case SyntaxKind.ComputedPropertyName: - return (parent as ComputedPropertyName).expression === node; - case SyntaxKind.Decorator: - case SyntaxKind.JsxExpression: - case SyntaxKind.JsxSpreadAttribute: - case SyntaxKind.SpreadAssignment: - return true; - case SyntaxKind.ExpressionWithTypeArguments: - return (parent as ExpressionWithTypeArguments).expression === node && !isPartOfTypeExpressionWithTypeArguments(parent as ExpressionWithTypeArguments); - case SyntaxKind.ShorthandPropertyAssignment: - return hasObjectAssignmentInitializer(parent) && parent.objectAssignmentInitializer === node; - case SyntaxKind.SatisfiesExpression: - return hasExpression(parent) && parent.expression === node; - default: - return isExpressionNode(parent); - } -} - -function isPartOfTypeExpressionWithTypeArguments(node: ExpressionWithTypeArguments): boolean { - const parent = node.parent; - return isHeritageClause(parent) && (!isClassLikeDeclaration(parent.parent) || parent.token === SyntaxKind.ImplementsKeyword) || - isJSDocImplementsTag(parent) || - isJSDocAugmentsTag(parent); -} - -function isForStatementExpression(node: Node, parent: ForStatement): boolean { - return parent.initializer === node && parent.initializer.kind !== SyntaxKind.VariableDeclarationList || - parent.condition === node || - parent.incrementor === node; -} - -function isForInOrOfStatementExpression(node: Node, parent: ForInOrOfStatement): boolean { - return parent.initializer === node && parent.initializer.kind !== SyntaxKind.VariableDeclarationList || - parent.expression === node; -} - -function isJSXTagName(node: Node): boolean { - const parent = node.parent; - switch (parent.kind) { - case SyntaxKind.JsxOpeningElement: - case SyntaxKind.JsxSelfClosingElement: - case SyntaxKind.JsxClosingElement: - return hasTagName(parent) && parent.tagName === node; - default: - return false; - } -} - -function hasTagName(node: Node): node is Node & { readonly tagName: Node; } { - return "tagName" in node; -} - -export function isBlockOrExpression(node: Node): node is BlockOrExpression { - return node.kind === SyntaxKind.Block || isExpression(node); -} - -export function isLeftHandSideExpression(node: Node): node is LeftHandSideExpression { - return isLeftHandSideExpressionKind(skipPartiallyEmittedExpressions(node).kind); -} - -export function skipPartiallyEmittedExpressions(node: Expression): Expression; -export function skipPartiallyEmittedExpressions(node: Node): Node; -export function skipPartiallyEmittedExpressions(node: Node) { - return skipOuterExpressions(node, OuterExpressionKinds.PartiallyEmittedExpressions); -} - -function isLeftHandSideExpressionKind(kind: SyntaxKind): boolean { - switch (kind) { - case SyntaxKind.PropertyAccessExpression: - case SyntaxKind.ElementAccessExpression: - case SyntaxKind.NewExpression: - case SyntaxKind.CallExpression: - case SyntaxKind.JsxElement: - case SyntaxKind.JsxSelfClosingElement: - case SyntaxKind.JsxFragment: - case SyntaxKind.TaggedTemplateExpression: - case SyntaxKind.ArrayLiteralExpression: - case SyntaxKind.ParenthesizedExpression: - case SyntaxKind.ObjectLiteralExpression: - case SyntaxKind.ClassExpression: - case SyntaxKind.FunctionExpression: - case SyntaxKind.Identifier: - case SyntaxKind.PrivateIdentifier: // technically this is only an Expression if it's in a `#field in expr` BinaryExpression - case SyntaxKind.RegularExpressionLiteral: - case SyntaxKind.NumericLiteral: - case SyntaxKind.BigIntLiteral: - case SyntaxKind.StringLiteral: - case SyntaxKind.NoSubstitutionTemplateLiteral: - case SyntaxKind.TemplateExpression: - case SyntaxKind.FalseKeyword: - case SyntaxKind.NullKeyword: - case SyntaxKind.ThisKeyword: - case SyntaxKind.TrueKeyword: - case SyntaxKind.SuperKeyword: - case SyntaxKind.NonNullExpression: - case SyntaxKind.ExpressionWithTypeArguments: - case SyntaxKind.MetaProperty: - case SyntaxKind.ImportKeyword: // technically this is only an Expression if it's in a CallExpression - case SyntaxKind.MissingDeclaration: - return true; - default: - return false; - } -} - -export function isUnaryExpression(node: Node): node is UnaryExpressionBase { - return isUnaryExpressionKind(skipPartiallyEmittedExpressions(node).kind); -} - -function isUnaryExpressionKind(kind: SyntaxKind): boolean { - switch (kind) { - case SyntaxKind.PrefixUnaryExpression: - case SyntaxKind.PostfixUnaryExpression: - case SyntaxKind.DeleteExpression: - case SyntaxKind.TypeOfExpression: - case SyntaxKind.VoidExpression: - case SyntaxKind.AwaitExpression: - case SyntaxKind.TypeAssertionExpression: - return true; - default: - return isLeftHandSideExpressionKind(kind); - } -} - -/** @internal */ -export function isOuterExpression(node: Node, kinds: OuterExpressionKinds = OuterExpressionKinds.All): node is OuterExpression { - switch (node.kind) { - case SyntaxKind.ParenthesizedExpression: - if (kinds & OuterExpressionKinds.ExcludeJSDocTypeAssertion && isJSDocTypeAssertion(node as ParenthesizedExpression)) { - return false; - } - return (kinds & OuterExpressionKinds.Parentheses) !== 0; - case SyntaxKind.TypeAssertionExpression: - case SyntaxKind.AsExpression: - return (kinds & OuterExpressionKinds.TypeAssertions) !== 0; - case SyntaxKind.SatisfiesExpression: - return (kinds & (OuterExpressionKinds.ExpressionsWithTypeArguments | OuterExpressionKinds.Satisfies)) !== 0; - case SyntaxKind.ExpressionWithTypeArguments: - return (kinds & OuterExpressionKinds.ExpressionsWithTypeArguments) !== 0; - case SyntaxKind.NonNullExpression: - return (kinds & OuterExpressionKinds.NonNullAssertions) !== 0; - case SyntaxKind.PartiallyEmittedExpression: - return (kinds & OuterExpressionKinds.PartiallyEmittedExpressions) !== 0; - } - return false; -} - -/** @internal */ -export function skipOuterExpressions(node: WrappedExpression): T; -/** @internal */ -export function skipOuterExpressions(node: Expression, kinds?: OuterExpressionKinds): Expression; -/** @internal */ -export function skipOuterExpressions(node: Node, kinds?: OuterExpressionKinds): Node; -/** @internal */ -export function skipOuterExpressions(node: Node, kinds = OuterExpressionKinds.All) { - while (isOuterExpression(node, kinds)) { - node = node.expression; - } - return node; -} - -function isJSDocTypeAssertion(node: ParenthesizedExpression): boolean { - const sourceFile = node.getSourceFile(); - if (sourceFile.scriptKind !== ScriptKind.JS && sourceFile.scriptKind !== ScriptKind.JSX) { - return false; - } - const expression = node.expression; - if (expression.kind !== SyntaxKind.AsExpression) { - return false; - } - const asExpression = expression as AsExpression; - return !!asExpression.type - && (asExpression.type.flags & NodeFlags.Reparsed) !== 0; -} - -export function isBindingPattern(node: Node): node is BindingPattern { - return node.kind === SyntaxKind.ObjectBindingPattern || node.kind === SyntaxKind.ArrayBindingPattern; -} - -export function isConciseBody(node: Node): node is ConciseBody { - return node.kind === SyntaxKind.Block || isExpression(node); -} - -export function isForInitializer(node: Node): node is ForInitializer { - return node.kind === SyntaxKind.VariableDeclarationList || isExpression(node); -} - -export function isQuestionOrExclamationToken(node: Node): node is QuestionToken | ExclamationToken { - return node.kind === SyntaxKind.QuestionToken || node.kind === SyntaxKind.ExclamationToken; -} - -export function isIdentifierOrThisTypeNode(node: Node): node is Identifier | ThisTypeNode { - return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisType; -} - -export function isReadonlyKeywordOrPlusOrMinusToken(node: Node): node is ReadonlyKeyword | PlusToken | MinusToken { - return node.kind === SyntaxKind.ReadonlyKeyword || node.kind === SyntaxKind.PlusToken || node.kind === SyntaxKind.MinusToken; -} - -export function isQuestionOrPlusOrMinusToken(node: Node): node is QuestionToken | PlusToken | MinusToken { - return node.kind === SyntaxKind.QuestionToken || node.kind === SyntaxKind.PlusToken || node.kind === SyntaxKind.MinusToken; -} - -export function isTemplateMiddleOrTemplateTail(node: Node): node is TemplateMiddle | TemplateTail { - return node.kind === SyntaxKind.TemplateMiddle || node.kind === SyntaxKind.TemplateTail; -} - -export function isLiteralTypeLiteral(node: Node): node is NullLiteral | BooleanLiteral | LiteralExpression | PrefixUnaryExpression { - const kind = node.kind; - return kind === SyntaxKind.NullKeyword || kind === SyntaxKind.TrueKeyword - || kind === SyntaxKind.FalseKeyword || kind === SyntaxKind.PrefixUnaryExpression - || isLiteralExpression(node); -} - -export function isIdentifierOrJSDocNamespaceDeclaration(node: Node): node is Identifier | JSDocNamespaceDeclaration { - return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ModuleDeclaration; -} - -export function isJSDocTypeExpressionOrJSDocTypeLiteral(node: Node): node is JSDocTypeExpression | JSDocTypeLiteral { - return node.kind === SyntaxKind.JSDocTypeExpression || node.kind === SyntaxKind.JSDocTypeLiteral; -} - -export function isJsxTagNameExpression(node: Node): node is JsxTagNameExpression { - const kind = node.kind; - return kind === SyntaxKind.ThisKeyword - || kind === SyntaxKind.Identifier - || kind === SyntaxKind.PropertyAccessExpression - || kind === SyntaxKind.JsxNamespacedName; -} +// is.ts — Hand-written type guard functions +// Generated guards are in is.generated.ts + +import { ModifierFlags } from "#enums/modifierFlags"; +import { NodeFlags } from "#enums/nodeFlags"; +import { OuterExpressionKinds } from "#enums/outerExpressionKinds"; +import { ScriptKind } from "#enums/scriptKind"; +import { SyntaxKind } from "#enums/syntaxKind"; +import type { + AsExpression, + BindingPattern, + BlockOrExpression, + BooleanLiteral, + ComputedPropertyName, + ConciseBody, + ExclamationToken, + Expression, + ExpressionWithTypeArguments, + ForInitializer, + ForInOrOfStatement, + ForStatement, + Identifier, + JSDocTypeExpression, + JSDocTypeLiteral, + JsxTagNameExpression, + LeftHandSideExpression, + LiteralExpression, + MetaProperty, + MinusToken, + ModuleDeclaration, + Node, + NonNullExpression, + NullLiteral, + ParenthesizedExpression, + PartiallyEmittedExpression, + PlusToken, + PrefixUnaryExpression, + QuestionToken, + ReadonlyKeyword, + SatisfiesExpression, + Statement, + TemplateMiddle, + TemplateTail, + ThisTypeNode, + TypeAssertion, + TypeNode, + UnaryExpressionBase, +} from "./ast.ts"; +import { + isBinaryExpression, + isClassLikeDeclaration, + isComputedPropertyName, + isHeritageClause, + isIdentifier, + isJSDocAugmentsTag, + isJSDocImplementsTag, + isJSDocLink, + isJSDocLinkCode, + isJSDocLinkPlain, + isJSDocNameReference, + isLiteralExpression, + isShorthandPropertyAssignment, +} from "./is.generated.ts"; +import { + hasExpression, + hasInitializer, + hasObjectAssignmentInitializer, +} from "./utils.ts"; + +export * from "./is.generated.ts"; + +type JSDocNamespaceDeclaration = ModuleDeclaration; + +type WrappedExpression = + | ParenthesizedExpression + | TypeAssertion + | AsExpression + | SatisfiesExpression + | ExpressionWithTypeArguments + | NonNullExpression + | PartiallyEmittedExpression; + +type OuterExpression = WrappedExpression; + +export function isTypeNode(node: Node): node is TypeNode { + return isTypeNodeKind(node.kind); +} + +function isTypeNodeKind(kind: SyntaxKind): boolean { + return kind >= SyntaxKind.FirstTypeNode && kind <= SyntaxKind.LastTypeNode + || kind === SyntaxKind.AnyKeyword + || kind === SyntaxKind.UnknownKeyword + || kind === SyntaxKind.NumberKeyword + || kind === SyntaxKind.BigIntKeyword + || kind === SyntaxKind.ObjectKeyword + || kind === SyntaxKind.BooleanKeyword + || kind === SyntaxKind.StringKeyword + || kind === SyntaxKind.SymbolKeyword + || kind === SyntaxKind.VoidKeyword + || kind === SyntaxKind.UndefinedKeyword + || kind === SyntaxKind.NeverKeyword + || kind === SyntaxKind.IntrinsicKeyword + || kind === SyntaxKind.ExpressionWithTypeArguments + || kind === SyntaxKind.JSDocAllType + || kind === SyntaxKind.JSDocNullableType + || kind === SyntaxKind.JSDocNonNullableType + || kind === SyntaxKind.JSDocOptionalType + || kind === SyntaxKind.JSDocVariadicType + || kind === SyntaxKind.JSDocTypeExpression + || kind === SyntaxKind.JSDocTypeLiteral + || kind === SyntaxKind.JSDocSignature; +} + +export function isStatement(node: Node): node is Statement { + const kind = node.kind; + return kind === SyntaxKind.VariableStatement || kind === SyntaxKind.EmptyStatement + || kind === SyntaxKind.ExpressionStatement || kind === SyntaxKind.IfStatement + || kind === SyntaxKind.DoStatement || kind === SyntaxKind.WhileStatement + || kind === SyntaxKind.ForStatement || kind === SyntaxKind.ForInStatement + || kind === SyntaxKind.ForOfStatement || kind === SyntaxKind.ContinueStatement + || kind === SyntaxKind.BreakStatement || kind === SyntaxKind.ReturnStatement + || kind === SyntaxKind.WithStatement || kind === SyntaxKind.SwitchStatement + || kind === SyntaxKind.LabeledStatement || kind === SyntaxKind.ThrowStatement + || kind === SyntaxKind.TryStatement || kind === SyntaxKind.DebuggerStatement + || kind === SyntaxKind.InterfaceDeclaration || kind === SyntaxKind.TypeAliasDeclaration + || kind === SyntaxKind.EnumDeclaration || kind === SyntaxKind.ModuleDeclaration + || kind === SyntaxKind.ImportDeclaration || kind === SyntaxKind.ImportEqualsDeclaration + || kind === SyntaxKind.ExportDeclaration || kind === SyntaxKind.ExportAssignment + || kind === SyntaxKind.NamespaceExportDeclaration || kind === SyntaxKind.FunctionDeclaration + || kind === SyntaxKind.ClassDeclaration || kind === SyntaxKind.MissingDeclaration + || kind === SyntaxKind.NotEmittedStatement || kind === SyntaxKind.Block; +} + +export function isExpression(node: Node): node is Expression { + const kind = node.kind; + return kind === SyntaxKind.ConditionalExpression || kind === SyntaxKind.YieldExpression + || kind === SyntaxKind.ArrowFunction || kind === SyntaxKind.BinaryExpression + || kind === SyntaxKind.SpreadElement || kind === SyntaxKind.AsExpression + || kind === SyntaxKind.OmittedExpression + || kind === SyntaxKind.SatisfiesExpression + || kind === SyntaxKind.PrefixUnaryExpression || kind === SyntaxKind.PostfixUnaryExpression + || kind === SyntaxKind.DeleteExpression || kind === SyntaxKind.TypeOfExpression + || kind === SyntaxKind.VoidExpression || kind === SyntaxKind.AwaitExpression + || kind === SyntaxKind.TypeAssertionExpression + || kind === SyntaxKind.CallExpression || kind === SyntaxKind.NewExpression + || kind === SyntaxKind.TaggedTemplateExpression || kind === SyntaxKind.NonNullExpression + || kind === SyntaxKind.MetaProperty || kind === SyntaxKind.JsxExpression + || kind === SyntaxKind.PropertyAccessExpression || kind === SyntaxKind.ElementAccessExpression + || kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ClassExpression + || kind === SyntaxKind.ParenthesizedExpression || kind === SyntaxKind.ArrayLiteralExpression + || kind === SyntaxKind.ObjectLiteralExpression || kind === SyntaxKind.TemplateExpression + || kind === SyntaxKind.Identifier + || kind === SyntaxKind.PrivateIdentifier + || kind === SyntaxKind.NumericLiteral || kind === SyntaxKind.BigIntLiteral + || kind === SyntaxKind.StringLiteral || kind === SyntaxKind.RegularExpressionLiteral + || kind === SyntaxKind.NoSubstitutionTemplateLiteral || kind === SyntaxKind.JsxElement + || kind === SyntaxKind.JsxSelfClosingElement || kind === SyntaxKind.JsxFragment + || kind === SyntaxKind.NullKeyword || kind === SyntaxKind.TrueKeyword + || kind === SyntaxKind.FalseKeyword || kind === SyntaxKind.ThisKeyword + || kind === SyntaxKind.SuperKeyword || kind === SyntaxKind.ImportKeyword + || kind === SyntaxKind.ExpressionWithTypeArguments; +} + +export function isValidTypeOnlyAliasUseSite(useSite: Node): boolean { + return !!(useSite.flags & NodeFlags.Ambient) + || isInJSDoc(useSite) + || isPartOfTypeQuery(useSite) + || isIdentifierInNonEmittingHeritageClause(useSite) + || isPartOfPossiblyValidTypeOrAbstractComputedPropertyName(useSite) + || !(isExpressionNode(useSite) || isShorthandPropertyNameUseSite(useSite)); +} + +function isInJSDoc(node: Node): boolean { + return !!(node.flags & NodeFlags.JSDoc); +} + +function isPartOfTypeQuery(node: Node): boolean { + while (node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.Identifier) { + node = node.parent; + } + return node.kind === SyntaxKind.TypeQuery; +} + +function isIdentifierInNonEmittingHeritageClause(node: Node): boolean { + if (!isIdentifier(node)) return false; + + const heritageClause = findAncestor(node.parent, parent => { + switch (parent.kind) { + case SyntaxKind.HeritageClause: + return true; + case SyntaxKind.PropertyAccessExpression: + case SyntaxKind.ExpressionWithTypeArguments: + return false; + default: + return "quit"; + } + }); + + return heritageClause !== undefined && + isHeritageClause(heritageClause) && + (heritageClause.token === SyntaxKind.ImplementsKeyword || heritageClause.parent.kind === SyntaxKind.InterfaceDeclaration); +} + +function isPartOfPossiblyValidTypeOrAbstractComputedPropertyName(node: Node): boolean { + while (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PropertyAccessExpression) { + node = node.parent; + } + if (!isComputedPropertyName(node)) { + return false; + } + if (hasModifierFlags(node.parent) && node.parent.modifierFlags & ModifierFlags.Abstract) { + return true; + } + const containerKind = node.parent.parent.kind; + return containerKind === SyntaxKind.InterfaceDeclaration || containerKind === SyntaxKind.TypeLiteral; +} + +function isShorthandPropertyNameUseSite(useSite: Node): boolean { + return isIdentifier(useSite) && isShorthandPropertyAssignment(useSite.parent) && useSite.parent.name === useSite; +} + +type AncestorCallbackResult = boolean | "quit"; + +function findAncestor(node: Node, callback: (node: Node) => AncestorCallbackResult): Node | undefined { + while (node) { + const result = callback(node); + if (result === "quit") { + return undefined; + } + if (result) { + return node; + } + node = node.parent; + } +} + +function hasModifierFlags(node: Node): node is Node & { readonly modifierFlags: ModifierFlags; } { + return "modifierFlags" in node; +} + +function isExpressionNode(node: Node): boolean { + switch (node.kind) { + case SyntaxKind.SuperKeyword: + case SyntaxKind.NullKeyword: + case SyntaxKind.TrueKeyword: + case SyntaxKind.FalseKeyword: + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.ArrayLiteralExpression: + case SyntaxKind.ObjectLiteralExpression: + case SyntaxKind.PropertyAccessExpression: + case SyntaxKind.ElementAccessExpression: + case SyntaxKind.CallExpression: + case SyntaxKind.NewExpression: + case SyntaxKind.TaggedTemplateExpression: + case SyntaxKind.AsExpression: + case SyntaxKind.TypeAssertionExpression: + case SyntaxKind.SatisfiesExpression: + case SyntaxKind.NonNullExpression: + case SyntaxKind.ParenthesizedExpression: + case SyntaxKind.FunctionExpression: + case SyntaxKind.ClassExpression: + case SyntaxKind.ArrowFunction: + case SyntaxKind.VoidExpression: + case SyntaxKind.DeleteExpression: + case SyntaxKind.TypeOfExpression: + case SyntaxKind.PrefixUnaryExpression: + case SyntaxKind.PostfixUnaryExpression: + case SyntaxKind.BinaryExpression: + case SyntaxKind.ConditionalExpression: + case SyntaxKind.SpreadElement: + case SyntaxKind.TemplateExpression: + case SyntaxKind.OmittedExpression: + case SyntaxKind.JsxElement: + case SyntaxKind.JsxSelfClosingElement: + case SyntaxKind.JsxFragment: + case SyntaxKind.YieldExpression: + case SyntaxKind.AwaitExpression: + return true; + case SyntaxKind.MetaProperty: + return !isImportCall(node.parent) || node.parent.expression !== node; + case SyntaxKind.ExpressionWithTypeArguments: + return !isHeritageClause(node.parent); + case SyntaxKind.QualifiedName: + while (node.parent.kind === SyntaxKind.QualifiedName) { + node = node.parent; + } + return node.parent.kind === SyntaxKind.TypeQuery || isInJSDocNameReference(node) || isJSXTagName(node); + case SyntaxKind.PrivateIdentifier: + return isBinaryExpression(node.parent) && node.parent.left === node && node.parent.operatorToken.kind === SyntaxKind.InKeyword; + case SyntaxKind.Identifier: + if (node.parent.kind === SyntaxKind.TypeQuery || isInJSDocNameReference(node) || isJSXTagName(node)) { + return true; + } + // falls through + case SyntaxKind.NumericLiteral: + case SyntaxKind.BigIntLiteral: + case SyntaxKind.StringLiteral: + case SyntaxKind.NoSubstitutionTemplateLiteral: + case SyntaxKind.ThisKeyword: + return isInExpressionContext(node); + default: + return false; + } +} + +function isImportCall(node: Node): node is Node & { readonly expression: MetaProperty; } { + if (node.kind !== SyntaxKind.CallExpression || !hasExpression(node) || !node.expression) { + return false; + } + return node.expression.kind === SyntaxKind.MetaProperty + && (node.expression as MetaProperty).keywordToken === SyntaxKind.ImportKeyword; +} + +function isJSDocLinkLike(node: Node): boolean { + return isJSDocLink(node) || isJSDocLinkCode(node) || isJSDocLinkPlain(node); +} + +function isInJSDocNameReference(node: Node): boolean { + return isJSDocLinkLike(node.parent) || isJSDocNameReference(node.parent); +} + +function isInExpressionContext(node: Node): boolean { + const parent = node.parent; + switch (parent.kind) { + case SyntaxKind.VariableDeclaration: + case SyntaxKind.Parameter: + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.PropertySignature: + case SyntaxKind.EnumMember: + case SyntaxKind.PropertyAssignment: + case SyntaxKind.BindingElement: + return hasInitializer(parent) && parent.initializer === node; + case SyntaxKind.ExpressionStatement: + case SyntaxKind.IfStatement: + case SyntaxKind.DoStatement: + case SyntaxKind.WhileStatement: + case SyntaxKind.ReturnStatement: + case SyntaxKind.WithStatement: + case SyntaxKind.SwitchStatement: + case SyntaxKind.CaseClause: + case SyntaxKind.DefaultClause: + case SyntaxKind.ThrowStatement: + return hasExpression(parent) && parent.expression === node; + case SyntaxKind.ForStatement: + return isForStatementExpression(node, parent as ForStatement); + case SyntaxKind.ForInStatement: + case SyntaxKind.ForOfStatement: + return isForInOrOfStatementExpression(node, parent as ForInOrOfStatement); + case SyntaxKind.TypeAssertionExpression: + case SyntaxKind.AsExpression: + return hasExpression(parent) && parent.expression === node; + case SyntaxKind.TemplateSpan: + return hasExpression(parent) && parent.expression === node; + case SyntaxKind.ComputedPropertyName: + return (parent as ComputedPropertyName).expression === node; + case SyntaxKind.Decorator: + case SyntaxKind.JsxExpression: + case SyntaxKind.JsxSpreadAttribute: + case SyntaxKind.SpreadAssignment: + return true; + case SyntaxKind.ExpressionWithTypeArguments: + return (parent as ExpressionWithTypeArguments).expression === node && !isPartOfTypeExpressionWithTypeArguments(parent as ExpressionWithTypeArguments); + case SyntaxKind.ShorthandPropertyAssignment: + return hasObjectAssignmentInitializer(parent) && parent.objectAssignmentInitializer === node; + case SyntaxKind.SatisfiesExpression: + return hasExpression(parent) && parent.expression === node; + default: + return isExpressionNode(parent); + } +} + +function isPartOfTypeExpressionWithTypeArguments(node: ExpressionWithTypeArguments): boolean { + const parent = node.parent; + return isHeritageClause(parent) && (!isClassLikeDeclaration(parent.parent) || parent.token === SyntaxKind.ImplementsKeyword) || + isJSDocImplementsTag(parent) || + isJSDocAugmentsTag(parent); +} + +function isForStatementExpression(node: Node, parent: ForStatement): boolean { + return parent.initializer === node && parent.initializer.kind !== SyntaxKind.VariableDeclarationList || + parent.condition === node || + parent.incrementor === node; +} + +function isForInOrOfStatementExpression(node: Node, parent: ForInOrOfStatement): boolean { + return parent.initializer === node && parent.initializer.kind !== SyntaxKind.VariableDeclarationList || + parent.expression === node; +} + +function isJSXTagName(node: Node): boolean { + const parent = node.parent; + switch (parent.kind) { + case SyntaxKind.JsxOpeningElement: + case SyntaxKind.JsxSelfClosingElement: + case SyntaxKind.JsxClosingElement: + return hasTagName(parent) && parent.tagName === node; + default: + return false; + } +} + +function hasTagName(node: Node): node is Node & { readonly tagName: Node; } { + return "tagName" in node; +} + +export function isBlockOrExpression(node: Node): node is BlockOrExpression { + return node.kind === SyntaxKind.Block || isExpression(node); +} + +export function isLeftHandSideExpression(node: Node): node is LeftHandSideExpression { + return isLeftHandSideExpressionKind(skipPartiallyEmittedExpressions(node).kind); +} + +export function skipPartiallyEmittedExpressions(node: Expression): Expression; +export function skipPartiallyEmittedExpressions(node: Node): Node; +export function skipPartiallyEmittedExpressions(node: Node) { + return skipOuterExpressions(node, OuterExpressionKinds.PartiallyEmittedExpressions); +} + +function isLeftHandSideExpressionKind(kind: SyntaxKind): boolean { + switch (kind) { + case SyntaxKind.PropertyAccessExpression: + case SyntaxKind.ElementAccessExpression: + case SyntaxKind.NewExpression: + case SyntaxKind.CallExpression: + case SyntaxKind.JsxElement: + case SyntaxKind.JsxSelfClosingElement: + case SyntaxKind.JsxFragment: + case SyntaxKind.TaggedTemplateExpression: + case SyntaxKind.ArrayLiteralExpression: + case SyntaxKind.ParenthesizedExpression: + case SyntaxKind.ObjectLiteralExpression: + case SyntaxKind.ClassExpression: + case SyntaxKind.FunctionExpression: + case SyntaxKind.Identifier: + case SyntaxKind.PrivateIdentifier: // technically this is only an Expression if it's in a `#field in expr` BinaryExpression + case SyntaxKind.RegularExpressionLiteral: + case SyntaxKind.NumericLiteral: + case SyntaxKind.BigIntLiteral: + case SyntaxKind.StringLiteral: + case SyntaxKind.NoSubstitutionTemplateLiteral: + case SyntaxKind.TemplateExpression: + case SyntaxKind.FalseKeyword: + case SyntaxKind.NullKeyword: + case SyntaxKind.ThisKeyword: + case SyntaxKind.TrueKeyword: + case SyntaxKind.SuperKeyword: + case SyntaxKind.NonNullExpression: + case SyntaxKind.ExpressionWithTypeArguments: + case SyntaxKind.MetaProperty: + case SyntaxKind.ImportKeyword: // technically this is only an Expression if it's in a CallExpression + case SyntaxKind.MissingDeclaration: + return true; + default: + return false; + } +} + +export function isUnaryExpression(node: Node): node is UnaryExpressionBase { + return isUnaryExpressionKind(skipPartiallyEmittedExpressions(node).kind); +} + +function isUnaryExpressionKind(kind: SyntaxKind): boolean { + switch (kind) { + case SyntaxKind.PrefixUnaryExpression: + case SyntaxKind.PostfixUnaryExpression: + case SyntaxKind.DeleteExpression: + case SyntaxKind.TypeOfExpression: + case SyntaxKind.VoidExpression: + case SyntaxKind.AwaitExpression: + case SyntaxKind.TypeAssertionExpression: + return true; + default: + return isLeftHandSideExpressionKind(kind); + } +} + +/** @internal */ +export function isOuterExpression(node: Node, kinds: OuterExpressionKinds = OuterExpressionKinds.All): node is OuterExpression { + switch (node.kind) { + case SyntaxKind.ParenthesizedExpression: + if (kinds & OuterExpressionKinds.ExcludeJSDocTypeAssertion && isJSDocTypeAssertion(node as ParenthesizedExpression)) { + return false; + } + return (kinds & OuterExpressionKinds.Parentheses) !== 0; + case SyntaxKind.TypeAssertionExpression: + case SyntaxKind.AsExpression: + return (kinds & OuterExpressionKinds.TypeAssertions) !== 0; + case SyntaxKind.SatisfiesExpression: + return (kinds & (OuterExpressionKinds.ExpressionsWithTypeArguments | OuterExpressionKinds.Satisfies)) !== 0; + case SyntaxKind.ExpressionWithTypeArguments: + return (kinds & OuterExpressionKinds.ExpressionsWithTypeArguments) !== 0; + case SyntaxKind.NonNullExpression: + return (kinds & OuterExpressionKinds.NonNullAssertions) !== 0; + case SyntaxKind.PartiallyEmittedExpression: + return (kinds & OuterExpressionKinds.PartiallyEmittedExpressions) !== 0; + } + return false; +} + +/** @internal */ +export function skipOuterExpressions(node: WrappedExpression): T; +/** @internal */ +export function skipOuterExpressions(node: Expression, kinds?: OuterExpressionKinds): Expression; +/** @internal */ +export function skipOuterExpressions(node: Node, kinds?: OuterExpressionKinds): Node; +/** @internal */ +export function skipOuterExpressions(node: Node, kinds = OuterExpressionKinds.All) { + while (isOuterExpression(node, kinds)) { + node = node.expression; + } + return node; +} + +function isJSDocTypeAssertion(node: ParenthesizedExpression): boolean { + const sourceFile = node.getSourceFile(); + if (sourceFile.scriptKind !== ScriptKind.JS && sourceFile.scriptKind !== ScriptKind.JSX) { + return false; + } + const expression = node.expression; + if (expression.kind !== SyntaxKind.AsExpression) { + return false; + } + const asExpression = expression as AsExpression; + return !!asExpression.type + && (asExpression.type.flags & NodeFlags.Reparsed) !== 0; +} + +export function isBindingPattern(node: Node): node is BindingPattern { + return node.kind === SyntaxKind.ObjectBindingPattern || node.kind === SyntaxKind.ArrayBindingPattern; +} + +export function isConciseBody(node: Node): node is ConciseBody { + return node.kind === SyntaxKind.Block || isExpression(node); +} + +export function isForInitializer(node: Node): node is ForInitializer { + return node.kind === SyntaxKind.VariableDeclarationList || isExpression(node); +} + +export function isQuestionOrExclamationToken(node: Node): node is QuestionToken | ExclamationToken { + return node.kind === SyntaxKind.QuestionToken || node.kind === SyntaxKind.ExclamationToken; +} + +export function isIdentifierOrThisTypeNode(node: Node): node is Identifier | ThisTypeNode { + return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisType; +} + +export function isReadonlyKeywordOrPlusOrMinusToken(node: Node): node is ReadonlyKeyword | PlusToken | MinusToken { + return node.kind === SyntaxKind.ReadonlyKeyword || node.kind === SyntaxKind.PlusToken || node.kind === SyntaxKind.MinusToken; +} + +export function isQuestionOrPlusOrMinusToken(node: Node): node is QuestionToken | PlusToken | MinusToken { + return node.kind === SyntaxKind.QuestionToken || node.kind === SyntaxKind.PlusToken || node.kind === SyntaxKind.MinusToken; +} + +export function isTemplateMiddleOrTemplateTail(node: Node): node is TemplateMiddle | TemplateTail { + return node.kind === SyntaxKind.TemplateMiddle || node.kind === SyntaxKind.TemplateTail; +} + +export function isLiteralTypeLiteral(node: Node): node is NullLiteral | BooleanLiteral | LiteralExpression | PrefixUnaryExpression { + const kind = node.kind; + return kind === SyntaxKind.NullKeyword || kind === SyntaxKind.TrueKeyword + || kind === SyntaxKind.FalseKeyword || kind === SyntaxKind.PrefixUnaryExpression + || isLiteralExpression(node); +} + +export function isIdentifierOrJSDocNamespaceDeclaration(node: Node): node is Identifier | JSDocNamespaceDeclaration { + return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ModuleDeclaration; +} + +export function isJSDocTypeExpressionOrJSDocTypeLiteral(node: Node): node is JSDocTypeExpression | JSDocTypeLiteral { + return node.kind === SyntaxKind.JSDocTypeExpression || node.kind === SyntaxKind.JSDocTypeLiteral; +} + +export function isJsxTagNameExpression(node: Node): node is JsxTagNameExpression { + const kind = node.kind; + return kind === SyntaxKind.ThisKeyword + || kind === SyntaxKind.Identifier + || kind === SyntaxKind.PropertyAccessExpression + || kind === SyntaxKind.JsxNamespacedName; +} diff --git a/packages/typescript/src/ast/jsdoc.ts b/packages/typescript/src/ast/jsdoc.ts index 4047923fa2e4c..a603b73d05542 100644 --- a/packages/typescript/src/ast/jsdoc.ts +++ b/packages/typescript/src/ast/jsdoc.ts @@ -1,247 +1,247 @@ -import { assertNever } from "../internal/utils.ts"; -import type { - JSDoc, - JSDocComment, - JSDocLink, - JSDocLinkCode, - JSDocLinkPlain, - JSDocParameterTag, - JSDocTag, - JSDocTemplateTag, - JsxTagNameExpression, - ParameterDeclaration, - PrivateIdentifier, - TypeParameterDeclaration, - VariableDeclarationList, -} from "./ast.generated.ts"; -import { - type EntityNameOrEntityNameExpression, - type Node, - type NodeArray, - SyntaxKind, -} from "./ast.ts"; -import { - isIdentifier, - isJSDoc, - isJSDocOverloadTag, - isJSDocParameterTag, - isJSDocSatisfiesTag, - isJSDocTemplateTag, - isJSDocTypeTag, - isParenthesizedExpression, - isPrivateIdentifier, -} from "./is.generated.ts"; - -/** Get all JSDoc tags related to a node, including those on parent nodes. */ -export function getJSDocTags(node: Node): readonly JSDocTag[] { - return getJSDocCommentsAndTags(node); -} - -/** Gets all JSDoc tags that match a specified predicate */ -export function getAllJSDocTags(node: Node, predicate: (tag: JSDocTag) => tag is T): readonly T[] { - return getJSDocTags(node).filter(predicate); -} - -/** Gets all JSDoc tags of a specified kind */ -export function getAllJSDocTagsOfKind(node: Node, kind: SyntaxKind): readonly JSDocTag[] { - return getJSDocTags(node).filter(doc => doc.kind === kind); -} - -/** Gets the text of a jsdoc comment, flattening links to their text. */ -export function getTextOfJSDocComment(comment?: string | NodeArray): string | undefined { - return typeof comment === "string" ? comment - : comment?.map(c => c.kind === SyntaxKind.JSDocText ? c.text : formatJSDocLink(c)).join(""); -} - -function isVariableLike(node: Node): boolean { - switch (node.kind) { - case SyntaxKind.BindingElement: - case SyntaxKind.EnumMember: - case SyntaxKind.Parameter: - case SyntaxKind.PropertyAssignment: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.PropertySignature: - case SyntaxKind.ShorthandPropertyAssignment: - case SyntaxKind.VariableDeclaration: - return true; - } - return false; -} - -function getJSDocNodes(node: Node): JSDoc[] { - const jsDoc = node.jsDoc; - if (!jsDoc || jsDoc.length === 0) { - return []; - } - const result: JSDoc[] = []; - for (const j of jsDoc) { - if (isJSDoc(j)) { - result.push(j); - } - } - return result; -} - -/** - * Determines whether a host node owns a JSDoc tag. A `@type` / `@satisfies` tag - * attached to a parenthesized expression belongs only to that expression. - */ -function ownsJSDocTag(hostNode: Node, tag: JSDocTag): boolean { - return !(isJSDocTypeTag(tag) || isJSDocSatisfiesTag(tag)) - || !tag.parent - || !isJSDoc(tag.parent) - || !tag.parent.parent - || !isParenthesizedExpression(tag.parent.parent) - || tag.parent.parent === hostNode; -} - -function filterOwnedJSDocTags(hostNode: Node, comments: JSDoc[]): JSDocTag[] { - const result: JSDocTag[] = []; - const lastJSDoc = comments[comments.length - 1]; - for (const jsDoc of comments) { - if (!jsDoc.tags) { - continue; - } - if (jsDoc === lastJSDoc) { - for (const tag of jsDoc.tags) { - if (ownsJSDocTag(hostNode, tag)) { - result.push(tag); - } - } - } - else { - // Tags from earlier comments only contribute their `@overload` tags. - for (const tag of jsDoc.tags) { - if (isJSDocOverloadTag(tag)) { - result.push(tag); - } - } - } - } - return result; -} - -function getJSDocParameterTags(param: ParameterDeclaration): JSDocParameterTag[] { - const result: JSDocParameterTag[] = []; - const name = param.name; - const parentTags = getJSDocTags(param.parent); - if (name && isIdentifier(name)) { - for (const tag of parentTags) { - if (isJSDocParameterTag(tag) && isIdentifier(tag.name) && tag.name.text === name.text) { - result.push(tag); - } - } - } - else if (name) { - // Binding patterns and JSDoc function syntax match parameter tags by position. - const parameters = (param.parent as { parameters?: readonly Node[]; }).parameters; - const i = parameters ? [...parameters].indexOf(param) : -1; - if (i > -1) { - const paramTags = parentTags.filter(isJSDocParameterTag); - if (i < paramTags.length) { - result.push(paramTags[i]); - } - } - } - return result; -} - -function getJSDocTypeParameterTags(typeParam: TypeParameterDeclaration): JSDocTemplateTag[] { - const result: JSDocTemplateTag[] = []; - const name = typeParam.name.text; - for (const tag of getJSDocTags(typeParam.parent)) { - if (isJSDocTemplateTag(tag) && [...tag.typeParameters].some(tp => tp.name.text === name)) { - result.push(tag); - } - } - return result; -} - -// Keep in sync with ast.GetNextJSDocCommentLocation. -function getNextJSDocCommentLocation(node: Node): Node | undefined { - const parent = node.parent; - if (parent) { - switch (parent.kind) { - case SyntaxKind.PropertyAssignment: - case SyntaxKind.ExportAssignment: - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.VariableDeclaration: - case SyntaxKind.SatisfiesExpression: - case SyntaxKind.ReturnStatement: - case SyntaxKind.VariableStatement: - case SyntaxKind.ExpressionStatement: - return parent; - case SyntaxKind.VariableDeclarationList: - if ((parent as VariableDeclarationList).declarations[0] === node) { - return parent; - } - break; - } - } - return undefined; -} - -function getJSDocCommentsAndTags(hostNode: Node): JSDocTag[] { - const result: JSDocTag[] = []; - // Pull parameter comments from a declaring initializer (e.g. `var x = function () {}`). - if (isVariableLike(hostNode)) { - const initializer = (hostNode as { initializer?: Node; }).initializer; - if (initializer) { - const initJSDoc = getJSDocNodes(initializer); - if (initJSDoc.length) { - result.push(...filterOwnedJSDocTags(hostNode, initJSDoc)); - } - } - } - - let node: Node | undefined = hostNode; - while (node && node.parent) { - const jsDocNodes = getJSDocNodes(node); - if (jsDocNodes.length) { - result.push(...filterOwnedJSDocTags(hostNode, jsDocNodes)); - } - - if (node.kind === SyntaxKind.Parameter) { - result.push(...getJSDocParameterTags(node as ParameterDeclaration)); - break; - } - if (node.kind === SyntaxKind.TypeParameter) { - result.push(...getJSDocTypeParameterTags(node as TypeParameterDeclaration)); - break; - } - node = getNextJSDocCommentLocation(node); - } - return result; -} - -function formatJSDocLink(link: JSDocLink | JSDocLinkCode | JSDocLinkPlain) { - const kind = link.kind === SyntaxKind.JSDocLink ? "link" - : link.kind === SyntaxKind.JSDocLinkCode ? "linkcode" - : "linkplain"; - const name = link.name ? entityNameToString(link.name) : ""; - const space = link.name && (link.text === "" || link.text.startsWith("://")) ? "" : " "; - return `{@${kind} ${name}${space}${link.text}}`; -} - -function entityNameToString(name: EntityNameOrEntityNameExpression | JsxTagNameExpression | PrivateIdentifier): string { - switch (name.kind) { - case SyntaxKind.ThisKeyword: - return "this"; - case SyntaxKind.PrivateIdentifier: - case SyntaxKind.Identifier: - return name.getFullWidth() === 0 ? name.text : name.getText(); - case SyntaxKind.QualifiedName: - return entityNameToString(name.left) + "." + entityNameToString(name.right); - case SyntaxKind.PropertyAccessExpression: - if (isIdentifier(name.name) || isPrivateIdentifier(name.name)) { - return entityNameToString(name.expression) + "." + entityNameToString(name.name); - } - else { - return assertNever(name.name); - } - case SyntaxKind.JsxNamespacedName: - return entityNameToString(name.namespace) + ":" + entityNameToString(name.name); - default: - return assertNever(name); - } -} +import { assertNever } from "../internal/utils.ts"; +import type { + JSDoc, + JSDocComment, + JSDocLink, + JSDocLinkCode, + JSDocLinkPlain, + JSDocParameterTag, + JSDocTag, + JSDocTemplateTag, + JsxTagNameExpression, + ParameterDeclaration, + PrivateIdentifier, + TypeParameterDeclaration, + VariableDeclarationList, +} from "./ast.generated.ts"; +import { + type EntityNameOrEntityNameExpression, + type Node, + type NodeArray, + SyntaxKind, +} from "./ast.ts"; +import { + isIdentifier, + isJSDoc, + isJSDocOverloadTag, + isJSDocParameterTag, + isJSDocSatisfiesTag, + isJSDocTemplateTag, + isJSDocTypeTag, + isParenthesizedExpression, + isPrivateIdentifier, +} from "./is.generated.ts"; + +/** Get all JSDoc tags related to a node, including those on parent nodes. */ +export function getJSDocTags(node: Node): readonly JSDocTag[] { + return getJSDocCommentsAndTags(node); +} + +/** Gets all JSDoc tags that match a specified predicate */ +export function getAllJSDocTags(node: Node, predicate: (tag: JSDocTag) => tag is T): readonly T[] { + return getJSDocTags(node).filter(predicate); +} + +/** Gets all JSDoc tags of a specified kind */ +export function getAllJSDocTagsOfKind(node: Node, kind: SyntaxKind): readonly JSDocTag[] { + return getJSDocTags(node).filter(doc => doc.kind === kind); +} + +/** Gets the text of a jsdoc comment, flattening links to their text. */ +export function getTextOfJSDocComment(comment?: string | NodeArray): string | undefined { + return typeof comment === "string" ? comment + : comment?.map(c => c.kind === SyntaxKind.JSDocText ? c.text : formatJSDocLink(c)).join(""); +} + +function isVariableLike(node: Node): boolean { + switch (node.kind) { + case SyntaxKind.BindingElement: + case SyntaxKind.EnumMember: + case SyntaxKind.Parameter: + case SyntaxKind.PropertyAssignment: + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.PropertySignature: + case SyntaxKind.ShorthandPropertyAssignment: + case SyntaxKind.VariableDeclaration: + return true; + } + return false; +} + +function getJSDocNodes(node: Node): JSDoc[] { + const jsDoc = node.jsDoc; + if (!jsDoc || jsDoc.length === 0) { + return []; + } + const result: JSDoc[] = []; + for (const j of jsDoc) { + if (isJSDoc(j)) { + result.push(j); + } + } + return result; +} + +/** + * Determines whether a host node owns a JSDoc tag. A `@type` / `@satisfies` tag + * attached to a parenthesized expression belongs only to that expression. + */ +function ownsJSDocTag(hostNode: Node, tag: JSDocTag): boolean { + return !(isJSDocTypeTag(tag) || isJSDocSatisfiesTag(tag)) + || !tag.parent + || !isJSDoc(tag.parent) + || !tag.parent.parent + || !isParenthesizedExpression(tag.parent.parent) + || tag.parent.parent === hostNode; +} + +function filterOwnedJSDocTags(hostNode: Node, comments: JSDoc[]): JSDocTag[] { + const result: JSDocTag[] = []; + const lastJSDoc = comments[comments.length - 1]; + for (const jsDoc of comments) { + if (!jsDoc.tags) { + continue; + } + if (jsDoc === lastJSDoc) { + for (const tag of jsDoc.tags) { + if (ownsJSDocTag(hostNode, tag)) { + result.push(tag); + } + } + } + else { + // Tags from earlier comments only contribute their `@overload` tags. + for (const tag of jsDoc.tags) { + if (isJSDocOverloadTag(tag)) { + result.push(tag); + } + } + } + } + return result; +} + +function getJSDocParameterTags(param: ParameterDeclaration): JSDocParameterTag[] { + const result: JSDocParameterTag[] = []; + const name = param.name; + const parentTags = getJSDocTags(param.parent); + if (name && isIdentifier(name)) { + for (const tag of parentTags) { + if (isJSDocParameterTag(tag) && isIdentifier(tag.name) && tag.name.text === name.text) { + result.push(tag); + } + } + } + else if (name) { + // Binding patterns and JSDoc function syntax match parameter tags by position. + const parameters = (param.parent as { parameters?: readonly Node[]; }).parameters; + const i = parameters ? [...parameters].indexOf(param) : -1; + if (i > -1) { + const paramTags = parentTags.filter(isJSDocParameterTag); + if (i < paramTags.length) { + result.push(paramTags[i]); + } + } + } + return result; +} + +function getJSDocTypeParameterTags(typeParam: TypeParameterDeclaration): JSDocTemplateTag[] { + const result: JSDocTemplateTag[] = []; + const name = typeParam.name.text; + for (const tag of getJSDocTags(typeParam.parent)) { + if (isJSDocTemplateTag(tag) && [...tag.typeParameters].some(tp => tp.name.text === name)) { + result.push(tag); + } + } + return result; +} + +// Keep in sync with ast.GetNextJSDocCommentLocation. +function getNextJSDocCommentLocation(node: Node): Node | undefined { + const parent = node.parent; + if (parent) { + switch (parent.kind) { + case SyntaxKind.PropertyAssignment: + case SyntaxKind.ExportAssignment: + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.VariableDeclaration: + case SyntaxKind.SatisfiesExpression: + case SyntaxKind.ReturnStatement: + case SyntaxKind.VariableStatement: + case SyntaxKind.ExpressionStatement: + return parent; + case SyntaxKind.VariableDeclarationList: + if ((parent as VariableDeclarationList).declarations[0] === node) { + return parent; + } + break; + } + } + return undefined; +} + +function getJSDocCommentsAndTags(hostNode: Node): JSDocTag[] { + const result: JSDocTag[] = []; + // Pull parameter comments from a declaring initializer (e.g. `var x = function () {}`). + if (isVariableLike(hostNode)) { + const initializer = (hostNode as { initializer?: Node; }).initializer; + if (initializer) { + const initJSDoc = getJSDocNodes(initializer); + if (initJSDoc.length) { + result.push(...filterOwnedJSDocTags(hostNode, initJSDoc)); + } + } + } + + let node: Node | undefined = hostNode; + while (node && node.parent) { + const jsDocNodes = getJSDocNodes(node); + if (jsDocNodes.length) { + result.push(...filterOwnedJSDocTags(hostNode, jsDocNodes)); + } + + if (node.kind === SyntaxKind.Parameter) { + result.push(...getJSDocParameterTags(node as ParameterDeclaration)); + break; + } + if (node.kind === SyntaxKind.TypeParameter) { + result.push(...getJSDocTypeParameterTags(node as TypeParameterDeclaration)); + break; + } + node = getNextJSDocCommentLocation(node); + } + return result; +} + +function formatJSDocLink(link: JSDocLink | JSDocLinkCode | JSDocLinkPlain) { + const kind = link.kind === SyntaxKind.JSDocLink ? "link" + : link.kind === SyntaxKind.JSDocLinkCode ? "linkcode" + : "linkplain"; + const name = link.name ? entityNameToString(link.name) : ""; + const space = link.name && (link.text === "" || link.text.startsWith("://")) ? "" : " "; + return `{@${kind} ${name}${space}${link.text}}`; +} + +function entityNameToString(name: EntityNameOrEntityNameExpression | JsxTagNameExpression | PrivateIdentifier): string { + switch (name.kind) { + case SyntaxKind.ThisKeyword: + return "this"; + case SyntaxKind.PrivateIdentifier: + case SyntaxKind.Identifier: + return name.getFullWidth() === 0 ? name.text : name.getText(); + case SyntaxKind.QualifiedName: + return entityNameToString(name.left) + "." + entityNameToString(name.right); + case SyntaxKind.PropertyAccessExpression: + if (isIdentifier(name.name) || isPrivateIdentifier(name.name)) { + return entityNameToString(name.expression) + "." + entityNameToString(name.name); + } + else { + return assertNever(name.name); + } + case SyntaxKind.JsxNamespacedName: + return entityNameToString(name.namespace) + ":" + entityNameToString(name.name); + default: + return assertNever(name); + } +} diff --git a/packages/typescript/src/ast/scanner.ts b/packages/typescript/src/ast/scanner.ts index 8e956bb967f54..96f062c765505 100644 --- a/packages/typescript/src/ast/scanner.ts +++ b/packages/typescript/src/ast/scanner.ts @@ -1,2532 +1,2532 @@ -import { CharacterCodes } from "#enums/characterCodes"; -import { CommentDirectiveType } from "#enums/commentDirectiveType"; -import { LanguageVariant } from "#enums/languageVariant"; -import { RegularExpressionFlags } from "#enums/regularExpressionFlags"; -import { ScriptTarget } from "#enums/scriptTarget"; -import { SyntaxKind } from "#enums/syntaxKind"; -import { TokenFlags } from "#enums/tokenFlags"; -import type { - JsxTokenSyntaxKind, - KeywordSyntaxKind, -} from "./ast.ts"; - -export type JSDocTokenKind = SyntaxKind.EndOfFile | SyntaxKind.WhitespaceTrivia | SyntaxKind.AtToken | SyntaxKind.NewLineTrivia | SyntaxKind.AsteriskToken | SyntaxKind.OpenBraceToken | SyntaxKind.CloseBraceToken | SyntaxKind.LessThanToken | SyntaxKind.GreaterThanToken | SyntaxKind.OpenBracketToken | SyntaxKind.CloseBracketToken | SyntaxKind.OpenParenToken | SyntaxKind.CloseParenToken | SyntaxKind.EqualsToken | SyntaxKind.CommaToken | SyntaxKind.DotToken | SyntaxKind.Identifier | SyntaxKind.BacktickToken | SyntaxKind.HashToken | SyntaxKind.Unknown | KeywordSyntaxKind; - -export interface CommentDirective { - range: { pos: number; end: number; }; - type: CommentDirectiveType; -} - -// Internal-only, not exported -const EscapeSequenceScanningFlags = { - String: 1 << 0, - ReportErrors: 1 << 1, - RegularExpression: 1 << 2, - AnnexB: 1 << 3, - AnyUnicodeMode: 1 << 4, - AtomEscape: 1 << 5, - ReportInvalidEscapeErrors: (1 << 2) | (1 << 1), - AllowExtendedUnicodeEscape: (1 << 0) | (1 << 4), -} as const; -type EscapeSequenceScanningFlags = typeof EscapeSequenceScanningFlags[keyof typeof EscapeSequenceScanningFlags]; - -export function tokenIsIdentifierOrKeyword(token: SyntaxKind): boolean { - return token >= SyntaxKind.Identifier; -} - -export function tokenIsIdentifierOrKeywordOrGreaterThan(token: SyntaxKind): boolean { - return token === SyntaxKind.GreaterThanToken || tokenIsIdentifierOrKeyword(token); -} - -export interface Scanner { - getTokenFullStart(): number; - getToken(): SyntaxKind; - getTokenStart(): number; - getTokenEnd(): number; - getTokenText(): string; - getTokenValue(): string; - hasUnicodeEscape(): boolean; - hasExtendedUnicodeEscape(): boolean; - hasPrecedingLineBreak(): boolean; - hasPrecedingJSDocComment(): boolean; - hasPrecedingJSDocLeadingAsterisks(): boolean; - hasPrecedingJSDocWithDeprecatedTag(): boolean; - hasPrecedingJSDocWithSeeOrLink(): boolean; - isIdentifier(): boolean; - isReservedWord(): boolean; - isUnterminated(): boolean; - getNumericLiteralFlags(): TokenFlags; - getCommentDirectives(): CommentDirective[] | undefined; - getTokenFlags(): TokenFlags; - reScanGreaterToken(): SyntaxKind; - reScanSlashToken(): SyntaxKind; - reScanAsteriskEqualsToken(): SyntaxKind; - reScanTemplateToken(isTaggedTemplate: boolean): SyntaxKind; - reScanTemplateHeadOrNoSubstitutionTemplate(): SyntaxKind; - scanJsxIdentifier(): SyntaxKind; - scanJsxAttributeValue(): SyntaxKind; - reScanJsxAttributeValue(): SyntaxKind; - reScanJsxToken(allowMultilineJsxText?: boolean): JsxTokenSyntaxKind; - reScanLessThanToken(): SyntaxKind; - reScanHashToken(): SyntaxKind; - reScanQuestionToken(): SyntaxKind; - reScanInvalidIdentifier(): SyntaxKind; - scanJsxToken(): JsxTokenSyntaxKind; - scanJsDocToken(): JSDocTokenKind; - scanJSDocCommentTextToken(inBackticks: boolean): JSDocTokenKind | SyntaxKind.JSDocCommentTextToken; - scan(): SyntaxKind; - - getText(): string; - clearCommentDirectives(): void; - setText(text: string | undefined, start?: number, length?: number): void; - setLanguageVariant(variant: LanguageVariant): void; - resetTokenState(pos: number): void; - setSkipJsDocLeadingAsterisks(skip: boolean): void; - lookAhead(callback: () => T): T; - scanRange(start: number, length: number, callback: () => T): T; - tryScan(callback: () => T): T; -} - -export const textToKeywordObj: Record = { - abstract: SyntaxKind.AbstractKeyword, - accessor: SyntaxKind.AccessorKeyword, - any: SyntaxKind.AnyKeyword, - as: SyntaxKind.AsKeyword, - asserts: SyntaxKind.AssertsKeyword, - assert: SyntaxKind.AssertKeyword, - bigint: SyntaxKind.BigIntKeyword, - boolean: SyntaxKind.BooleanKeyword, - break: SyntaxKind.BreakKeyword, - case: SyntaxKind.CaseKeyword, - catch: SyntaxKind.CatchKeyword, - class: SyntaxKind.ClassKeyword, - continue: SyntaxKind.ContinueKeyword, - const: SyntaxKind.ConstKeyword, - ["" + "constructor"]: SyntaxKind.ConstructorKeyword, - debugger: SyntaxKind.DebuggerKeyword, - declare: SyntaxKind.DeclareKeyword, - default: SyntaxKind.DefaultKeyword, - defer: SyntaxKind.DeferKeyword, - delete: SyntaxKind.DeleteKeyword, - do: SyntaxKind.DoKeyword, - else: SyntaxKind.ElseKeyword, - enum: SyntaxKind.EnumKeyword, - export: SyntaxKind.ExportKeyword, - extends: SyntaxKind.ExtendsKeyword, - false: SyntaxKind.FalseKeyword, - finally: SyntaxKind.FinallyKeyword, - for: SyntaxKind.ForKeyword, - from: SyntaxKind.FromKeyword, - function: SyntaxKind.FunctionKeyword, - get: SyntaxKind.GetKeyword, - if: SyntaxKind.IfKeyword, - implements: SyntaxKind.ImplementsKeyword, - import: SyntaxKind.ImportKeyword, - in: SyntaxKind.InKeyword, - infer: SyntaxKind.InferKeyword, - instanceof: SyntaxKind.InstanceOfKeyword, - interface: SyntaxKind.InterfaceKeyword, - intrinsic: SyntaxKind.IntrinsicKeyword, - is: SyntaxKind.IsKeyword, - keyof: SyntaxKind.KeyOfKeyword, - let: SyntaxKind.LetKeyword, - module: SyntaxKind.ModuleKeyword, - namespace: SyntaxKind.NamespaceKeyword, - never: SyntaxKind.NeverKeyword, - new: SyntaxKind.NewKeyword, - null: SyntaxKind.NullKeyword, - number: SyntaxKind.NumberKeyword, - object: SyntaxKind.ObjectKeyword, - package: SyntaxKind.PackageKeyword, - private: SyntaxKind.PrivateKeyword, - protected: SyntaxKind.ProtectedKeyword, - public: SyntaxKind.PublicKeyword, - override: SyntaxKind.OverrideKeyword, - out: SyntaxKind.OutKeyword, - readonly: SyntaxKind.ReadonlyKeyword, - require: SyntaxKind.RequireKeyword, - global: SyntaxKind.GlobalKeyword, - return: SyntaxKind.ReturnKeyword, - satisfies: SyntaxKind.SatisfiesKeyword, - set: SyntaxKind.SetKeyword, - static: SyntaxKind.StaticKeyword, - string: SyntaxKind.StringKeyword, - super: SyntaxKind.SuperKeyword, - switch: SyntaxKind.SwitchKeyword, - symbol: SyntaxKind.SymbolKeyword, - this: SyntaxKind.ThisKeyword, - throw: SyntaxKind.ThrowKeyword, - true: SyntaxKind.TrueKeyword, - try: SyntaxKind.TryKeyword, - type: SyntaxKind.TypeKeyword, - typeof: SyntaxKind.TypeOfKeyword, - undefined: SyntaxKind.UndefinedKeyword, - unique: SyntaxKind.UniqueKeyword, - unknown: SyntaxKind.UnknownKeyword, - using: SyntaxKind.UsingKeyword, - var: SyntaxKind.VarKeyword, - void: SyntaxKind.VoidKeyword, - while: SyntaxKind.WhileKeyword, - with: SyntaxKind.WithKeyword, - yield: SyntaxKind.YieldKeyword, - async: SyntaxKind.AsyncKeyword, - await: SyntaxKind.AwaitKeyword, - of: SyntaxKind.OfKeyword, -}; - -const textToKeyword = new Map(Object.entries(textToKeywordObj)); - -const textToToken = new Map(Object.entries({ - ...textToKeywordObj, - "{": SyntaxKind.OpenBraceToken, - "}": SyntaxKind.CloseBraceToken, - "(": SyntaxKind.OpenParenToken, - ")": SyntaxKind.CloseParenToken, - "[": SyntaxKind.OpenBracketToken, - "]": SyntaxKind.CloseBracketToken, - ".": SyntaxKind.DotToken, - "...": SyntaxKind.DotDotDotToken, - ";": SyntaxKind.SemicolonToken, - ",": SyntaxKind.CommaToken, - "<": SyntaxKind.LessThanToken, - ">": SyntaxKind.GreaterThanToken, - "<=": SyntaxKind.LessThanEqualsToken, - ">=": SyntaxKind.GreaterThanEqualsToken, - "==": SyntaxKind.EqualsEqualsToken, - "!=": SyntaxKind.ExclamationEqualsToken, - "===": SyntaxKind.EqualsEqualsEqualsToken, - "!==": SyntaxKind.ExclamationEqualsEqualsToken, - "=>": SyntaxKind.EqualsGreaterThanToken, - "+": SyntaxKind.PlusToken, - "-": SyntaxKind.MinusToken, - "**": SyntaxKind.AsteriskAsteriskToken, - "*": SyntaxKind.AsteriskToken, - "/": SyntaxKind.SlashToken, - "%": SyntaxKind.PercentToken, - "++": SyntaxKind.PlusPlusToken, - "--": SyntaxKind.MinusMinusToken, - "<<": SyntaxKind.LessThanLessThanToken, - ">": SyntaxKind.GreaterThanGreaterThanToken, - ">>>": SyntaxKind.GreaterThanGreaterThanGreaterThanToken, - "&": SyntaxKind.AmpersandToken, - "|": SyntaxKind.BarToken, - "^": SyntaxKind.CaretToken, - "!": SyntaxKind.ExclamationToken, - "~": SyntaxKind.TildeToken, - "&&": SyntaxKind.AmpersandAmpersandToken, - "||": SyntaxKind.BarBarToken, - "?": SyntaxKind.QuestionToken, - "??": SyntaxKind.QuestionQuestionToken, - "?.": SyntaxKind.QuestionDotToken, - ":": SyntaxKind.ColonToken, - "=": SyntaxKind.EqualsToken, - "+=": SyntaxKind.PlusEqualsToken, - "-=": SyntaxKind.MinusEqualsToken, - "*=": SyntaxKind.AsteriskEqualsToken, - "**=": SyntaxKind.AsteriskAsteriskEqualsToken, - "/=": SyntaxKind.SlashEqualsToken, - "%=": SyntaxKind.PercentEqualsToken, - "<<=": SyntaxKind.LessThanLessThanEqualsToken, - ">>=": SyntaxKind.GreaterThanGreaterThanEqualsToken, - ">>>=": SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken, - "&=": SyntaxKind.AmpersandEqualsToken, - "|=": SyntaxKind.BarEqualsToken, - "^=": SyntaxKind.CaretEqualsToken, - "||=": SyntaxKind.BarBarEqualsToken, - "&&=": SyntaxKind.AmpersandAmpersandEqualsToken, - "??=": SyntaxKind.QuestionQuestionEqualsToken, - "@": SyntaxKind.AtToken, - "#": SyntaxKind.HashToken, - "`": SyntaxKind.BacktickToken, -} as Record)); - -const charCodeToRegExpFlag = new Map([ - [CharacterCodes.d, RegularExpressionFlags.HasIndices], - [CharacterCodes.g, RegularExpressionFlags.Global], - [CharacterCodes.i, RegularExpressionFlags.IgnoreCase], - [CharacterCodes.m, RegularExpressionFlags.Multiline], - [CharacterCodes.s, RegularExpressionFlags.DotAll], - [CharacterCodes.u, RegularExpressionFlags.Unicode], - [CharacterCodes.v, RegularExpressionFlags.UnicodeSets], - [CharacterCodes.y, RegularExpressionFlags.Sticky], -]); - -/** - * Generated by scripts/regenerate-unicode-identifier-parts.mjs on node v22.1.0 with unicode 15.1 - * based on http://www.unicode.org/reports/tr31/ and https://www.ecma-international.org/ecma-262/6.0/#sec-names-and-keywords - * unicodeESNextIdentifierStart corresponds to the ID_Start and Other_ID_Start property, and - * unicodeESNextIdentifierPart corresponds to ID_Continue, Other_ID_Continue, plus ID_Start and Other_ID_Start - */ -// dprint-ignore -const unicodeESNextIdentifierStart = [65, 90, 97, 122, 170, 170, 181, 181, 186, 186, 192, 214, 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750, 880, 884, 886, 887, 890, 893, 895, 895, 902, 902, 904, 906, 908, 908, 910, 929, 931, 1013, 1015, 1153, 1162, 1327, 1329, 1366, 1369, 1369, 1376, 1416, 1488, 1514, 1519, 1522, 1568, 1610, 1646, 1647, 1649, 1747, 1749, 1749, 1765, 1766, 1774, 1775, 1786, 1788, 1791, 1791, 1808, 1808, 1810, 1839, 1869, 1957, 1969, 1969, 1994, 2026, 2036, 2037, 2042, 2042, 2048, 2069, 2074, 2074, 2084, 2084, 2088, 2088, 2112, 2136, 2144, 2154, 2160, 2183, 2185, 2190, 2208, 2249, 2308, 2361, 2365, 2365, 2384, 2384, 2392, 2401, 2417, 2432, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2493, 2493, 2510, 2510, 2524, 2525, 2527, 2529, 2544, 2545, 2556, 2556, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2649, 2652, 2654, 2654, 2674, 2676, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2749, 2749, 2768, 2768, 2784, 2785, 2809, 2809, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2869, 2873, 2877, 2877, 2908, 2909, 2911, 2913, 2929, 2929, 2947, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, 3024, 3024, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3129, 3133, 3133, 3160, 3162, 3165, 3165, 3168, 3169, 3200, 3200, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3261, 3261, 3293, 3294, 3296, 3297, 3313, 3314, 3332, 3340, 3342, 3344, 3346, 3386, 3389, 3389, 3406, 3406, 3412, 3414, 3423, 3425, 3450, 3455, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3585, 3632, 3634, 3635, 3648, 3654, 3713, 3714, 3716, 3716, 3718, 3722, 3724, 3747, 3749, 3749, 3751, 3760, 3762, 3763, 3773, 3773, 3776, 3780, 3782, 3782, 3804, 3807, 3840, 3840, 3904, 3911, 3913, 3948, 3976, 3980, 4096, 4138, 4159, 4159, 4176, 4181, 4186, 4189, 4193, 4193, 4197, 4198, 4206, 4208, 4213, 4225, 4238, 4238, 4256, 4293, 4295, 4295, 4301, 4301, 4304, 4346, 4348, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880, 4882, 4885, 4888, 4954, 4992, 5007, 5024, 5109, 5112, 5117, 5121, 5740, 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5880, 5888, 5905, 5919, 5937, 5952, 5969, 5984, 5996, 5998, 6000, 6016, 6067, 6103, 6103, 6108, 6108, 6176, 6264, 6272, 6312, 6314, 6314, 6320, 6389, 6400, 6430, 6480, 6509, 6512, 6516, 6528, 6571, 6576, 6601, 6656, 6678, 6688, 6740, 6823, 6823, 6917, 6963, 6981, 6988, 7043, 7072, 7086, 7087, 7098, 7141, 7168, 7203, 7245, 7247, 7258, 7293, 7296, 7304, 7312, 7354, 7357, 7359, 7401, 7404, 7406, 7411, 7413, 7414, 7418, 7418, 7424, 7615, 7680, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8305, 8305, 8319, 8319, 8336, 8348, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8472, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8505, 8508, 8511, 8517, 8521, 8526, 8526, 8544, 8584, 11264, 11492, 11499, 11502, 11506, 11507, 11520, 11557, 11559, 11559, 11565, 11565, 11568, 11623, 11631, 11631, 11648, 11670, 11680, 11686, 11688, 11694, 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726, 11728, 11734, 11736, 11742, 12293, 12295, 12321, 12329, 12337, 12341, 12344, 12348, 12353, 12438, 12443, 12447, 12449, 12538, 12540, 12543, 12549, 12591, 12593, 12686, 12704, 12735, 12784, 12799, 13312, 19903, 19968, 42124, 42192, 42237, 42240, 42508, 42512, 42527, 42538, 42539, 42560, 42606, 42623, 42653, 42656, 42735, 42775, 42783, 42786, 42888, 42891, 42954, 42960, 42961, 42963, 42963, 42965, 42969, 42994, 43009, 43011, 43013, 43015, 43018, 43020, 43042, 43072, 43123, 43138, 43187, 43250, 43255, 43259, 43259, 43261, 43262, 43274, 43301, 43312, 43334, 43360, 43388, 43396, 43442, 43471, 43471, 43488, 43492, 43494, 43503, 43514, 43518, 43520, 43560, 43584, 43586, 43588, 43595, 43616, 43638, 43642, 43642, 43646, 43695, 43697, 43697, 43701, 43702, 43705, 43709, 43712, 43712, 43714, 43714, 43739, 43741, 43744, 43754, 43762, 43764, 43777, 43782, 43785, 43790, 43793, 43798, 43808, 43814, 43816, 43822, 43824, 43866, 43868, 43881, 43888, 44002, 44032, 55203, 55216, 55238, 55243, 55291, 63744, 64109, 64112, 64217, 64256, 64262, 64275, 64279, 64285, 64285, 64287, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65136, 65140, 65142, 65276, 65313, 65338, 65345, 65370, 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500, 65536, 65547, 65549, 65574, 65576, 65594, 65596, 65597, 65599, 65613, 65616, 65629, 65664, 65786, 65856, 65908, 66176, 66204, 66208, 66256, 66304, 66335, 66349, 66378, 66384, 66421, 66432, 66461, 66464, 66499, 66504, 66511, 66513, 66517, 66560, 66717, 66736, 66771, 66776, 66811, 66816, 66855, 66864, 66915, 66928, 66938, 66940, 66954, 66956, 66962, 66964, 66965, 66967, 66977, 66979, 66993, 66995, 67001, 67003, 67004, 67072, 67382, 67392, 67413, 67424, 67431, 67456, 67461, 67463, 67504, 67506, 67514, 67584, 67589, 67592, 67592, 67594, 67637, 67639, 67640, 67644, 67644, 67647, 67669, 67680, 67702, 67712, 67742, 67808, 67826, 67828, 67829, 67840, 67861, 67872, 67897, 67968, 68023, 68030, 68031, 68096, 68096, 68112, 68115, 68117, 68119, 68121, 68149, 68192, 68220, 68224, 68252, 68288, 68295, 68297, 68324, 68352, 68405, 68416, 68437, 68448, 68466, 68480, 68497, 68608, 68680, 68736, 68786, 68800, 68850, 68864, 68899, 69248, 69289, 69296, 69297, 69376, 69404, 69415, 69415, 69424, 69445, 69488, 69505, 69552, 69572, 69600, 69622, 69635, 69687, 69745, 69746, 69749, 69749, 69763, 69807, 69840, 69864, 69891, 69926, 69956, 69956, 69959, 69959, 69968, 70002, 70006, 70006, 70019, 70066, 70081, 70084, 70106, 70106, 70108, 70108, 70144, 70161, 70163, 70187, 70207, 70208, 70272, 70278, 70280, 70280, 70282, 70285, 70287, 70301, 70303, 70312, 70320, 70366, 70405, 70412, 70415, 70416, 70419, 70440, 70442, 70448, 70450, 70451, 70453, 70457, 70461, 70461, 70480, 70480, 70493, 70497, 70656, 70708, 70727, 70730, 70751, 70753, 70784, 70831, 70852, 70853, 70855, 70855, 71040, 71086, 71128, 71131, 71168, 71215, 71236, 71236, 71296, 71338, 71352, 71352, 71424, 71450, 71488, 71494, 71680, 71723, 71840, 71903, 71935, 71942, 71945, 71945, 71948, 71955, 71957, 71958, 71960, 71983, 71999, 71999, 72001, 72001, 72096, 72103, 72106, 72144, 72161, 72161, 72163, 72163, 72192, 72192, 72203, 72242, 72250, 72250, 72272, 72272, 72284, 72329, 72349, 72349, 72368, 72440, 72704, 72712, 72714, 72750, 72768, 72768, 72818, 72847, 72960, 72966, 72968, 72969, 72971, 73008, 73030, 73030, 73056, 73061, 73063, 73064, 73066, 73097, 73112, 73112, 73440, 73458, 73474, 73474, 73476, 73488, 73490, 73523, 73648, 73648, 73728, 74649, 74752, 74862, 74880, 75075, 77712, 77808, 77824, 78895, 78913, 78918, 82944, 83526, 92160, 92728, 92736, 92766, 92784, 92862, 92880, 92909, 92928, 92975, 92992, 92995, 93027, 93047, 93053, 93071, 93760, 93823, 93952, 94026, 94032, 94032, 94099, 94111, 94176, 94177, 94179, 94179, 94208, 100343, 100352, 101589, 101632, 101640, 110576, 110579, 110581, 110587, 110589, 110590, 110592, 110882, 110898, 110898, 110928, 110930, 110933, 110933, 110948, 110951, 110960, 111355, 113664, 113770, 113776, 113788, 113792, 113800, 113808, 113817, 119808, 119892, 119894, 119964, 119966, 119967, 119970, 119970, 119973, 119974, 119977, 119980, 119982, 119993, 119995, 119995, 119997, 120003, 120005, 120069, 120071, 120074, 120077, 120084, 120086, 120092, 120094, 120121, 120123, 120126, 120128, 120132, 120134, 120134, 120138, 120144, 120146, 120485, 120488, 120512, 120514, 120538, 120540, 120570, 120572, 120596, 120598, 120628, 120630, 120654, 120656, 120686, 120688, 120712, 120714, 120744, 120746, 120770, 120772, 120779, 122624, 122654, 122661, 122666, 122928, 122989, 123136, 123180, 123191, 123197, 123214, 123214, 123536, 123565, 123584, 123627, 124112, 124139, 124896, 124902, 124904, 124907, 124909, 124910, 124912, 124926, 124928, 125124, 125184, 125251, 125259, 125259, 126464, 126467, 126469, 126495, 126497, 126498, 126500, 126500, 126503, 126503, 126505, 126514, 126516, 126519, 126521, 126521, 126523, 126523, 126530, 126530, 126535, 126535, 126537, 126537, 126539, 126539, 126541, 126543, 126545, 126546, 126548, 126548, 126551, 126551, 126553, 126553, 126555, 126555, 126557, 126557, 126559, 126559, 126561, 126562, 126564, 126564, 126567, 126570, 126572, 126578, 126580, 126583, 126585, 126588, 126590, 126590, 126592, 126601, 126603, 126619, 126625, 126627, 126629, 126633, 126635, 126651, 131072, 173791, 173824, 177977, 177984, 178205, 178208, 183969, 183984, 191456, 191472, 192093, 194560, 195101, 196608, 201546, 201552, 205743]; -// dprint-ignore -const unicodeESNextIdentifierPart = [48, 57, 65, 90, 95, 95, 97, 122, 170, 170, 181, 181, 183, 183, 186, 186, 192, 214, 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750, 768, 884, 886, 887, 890, 893, 895, 895, 902, 906, 908, 908, 910, 929, 931, 1013, 1015, 1153, 1155, 1159, 1162, 1327, 1329, 1366, 1369, 1369, 1376, 1416, 1425, 1469, 1471, 1471, 1473, 1474, 1476, 1477, 1479, 1479, 1488, 1514, 1519, 1522, 1552, 1562, 1568, 1641, 1646, 1747, 1749, 1756, 1759, 1768, 1770, 1788, 1791, 1791, 1808, 1866, 1869, 1969, 1984, 2037, 2042, 2042, 2045, 2045, 2048, 2093, 2112, 2139, 2144, 2154, 2160, 2183, 2185, 2190, 2200, 2273, 2275, 2403, 2406, 2415, 2417, 2435, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2492, 2500, 2503, 2504, 2507, 2510, 2519, 2519, 2524, 2525, 2527, 2531, 2534, 2545, 2556, 2556, 2558, 2558, 2561, 2563, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2620, 2620, 2622, 2626, 2631, 2632, 2635, 2637, 2641, 2641, 2649, 2652, 2654, 2654, 2662, 2677, 2689, 2691, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2748, 2757, 2759, 2761, 2763, 2765, 2768, 2768, 2784, 2787, 2790, 2799, 2809, 2815, 2817, 2819, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2869, 2873, 2876, 2884, 2887, 2888, 2891, 2893, 2901, 2903, 2908, 2909, 2911, 2915, 2918, 2927, 2929, 2929, 2946, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, 3006, 3010, 3014, 3016, 3018, 3021, 3024, 3024, 3031, 3031, 3046, 3055, 3072, 3084, 3086, 3088, 3090, 3112, 3114, 3129, 3132, 3140, 3142, 3144, 3146, 3149, 3157, 3158, 3160, 3162, 3165, 3165, 3168, 3171, 3174, 3183, 3200, 3203, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3260, 3268, 3270, 3272, 3274, 3277, 3285, 3286, 3293, 3294, 3296, 3299, 3302, 3311, 3313, 3315, 3328, 3340, 3342, 3344, 3346, 3396, 3398, 3400, 3402, 3406, 3412, 3415, 3423, 3427, 3430, 3439, 3450, 3455, 3457, 3459, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3530, 3530, 3535, 3540, 3542, 3542, 3544, 3551, 3558, 3567, 3570, 3571, 3585, 3642, 3648, 3662, 3664, 3673, 3713, 3714, 3716, 3716, 3718, 3722, 3724, 3747, 3749, 3749, 3751, 3773, 3776, 3780, 3782, 3782, 3784, 3790, 3792, 3801, 3804, 3807, 3840, 3840, 3864, 3865, 3872, 3881, 3893, 3893, 3895, 3895, 3897, 3897, 3902, 3911, 3913, 3948, 3953, 3972, 3974, 3991, 3993, 4028, 4038, 4038, 4096, 4169, 4176, 4253, 4256, 4293, 4295, 4295, 4301, 4301, 4304, 4346, 4348, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880, 4882, 4885, 4888, 4954, 4957, 4959, 4969, 4977, 4992, 5007, 5024, 5109, 5112, 5117, 5121, 5740, 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5880, 5888, 5909, 5919, 5940, 5952, 5971, 5984, 5996, 5998, 6000, 6002, 6003, 6016, 6099, 6103, 6103, 6108, 6109, 6112, 6121, 6155, 6157, 6159, 6169, 6176, 6264, 6272, 6314, 6320, 6389, 6400, 6430, 6432, 6443, 6448, 6459, 6470, 6509, 6512, 6516, 6528, 6571, 6576, 6601, 6608, 6618, 6656, 6683, 6688, 6750, 6752, 6780, 6783, 6793, 6800, 6809, 6823, 6823, 6832, 6845, 6847, 6862, 6912, 6988, 6992, 7001, 7019, 7027, 7040, 7155, 7168, 7223, 7232, 7241, 7245, 7293, 7296, 7304, 7312, 7354, 7357, 7359, 7376, 7378, 7380, 7418, 7424, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8204, 8205, 8255, 8256, 8276, 8276, 8305, 8305, 8319, 8319, 8336, 8348, 8400, 8412, 8417, 8417, 8421, 8432, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8472, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8505, 8508, 8511, 8517, 8521, 8526, 8526, 8544, 8584, 11264, 11492, 11499, 11507, 11520, 11557, 11559, 11559, 11565, 11565, 11568, 11623, 11631, 11631, 11647, 11670, 11680, 11686, 11688, 11694, 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726, 11728, 11734, 11736, 11742, 11744, 11775, 12293, 12295, 12321, 12335, 12337, 12341, 12344, 12348, 12353, 12438, 12441, 12447, 12449, 12543, 12549, 12591, 12593, 12686, 12704, 12735, 12784, 12799, 13312, 19903, 19968, 42124, 42192, 42237, 42240, 42508, 42512, 42539, 42560, 42607, 42612, 42621, 42623, 42737, 42775, 42783, 42786, 42888, 42891, 42954, 42960, 42961, 42963, 42963, 42965, 42969, 42994, 43047, 43052, 43052, 43072, 43123, 43136, 43205, 43216, 43225, 43232, 43255, 43259, 43259, 43261, 43309, 43312, 43347, 43360, 43388, 43392, 43456, 43471, 43481, 43488, 43518, 43520, 43574, 43584, 43597, 43600, 43609, 43616, 43638, 43642, 43714, 43739, 43741, 43744, 43759, 43762, 43766, 43777, 43782, 43785, 43790, 43793, 43798, 43808, 43814, 43816, 43822, 43824, 43866, 43868, 43881, 43888, 44010, 44012, 44013, 44016, 44025, 44032, 55203, 55216, 55238, 55243, 55291, 63744, 64109, 64112, 64217, 64256, 64262, 64275, 64279, 64285, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65024, 65039, 65056, 65071, 65075, 65076, 65101, 65103, 65136, 65140, 65142, 65276, 65296, 65305, 65313, 65338, 65343, 65343, 65345, 65370, 65381, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500, 65536, 65547, 65549, 65574, 65576, 65594, 65596, 65597, 65599, 65613, 65616, 65629, 65664, 65786, 65856, 65908, 66045, 66045, 66176, 66204, 66208, 66256, 66272, 66272, 66304, 66335, 66349, 66378, 66384, 66426, 66432, 66461, 66464, 66499, 66504, 66511, 66513, 66517, 66560, 66717, 66720, 66729, 66736, 66771, 66776, 66811, 66816, 66855, 66864, 66915, 66928, 66938, 66940, 66954, 66956, 66962, 66964, 66965, 66967, 66977, 66979, 66993, 66995, 67001, 67003, 67004, 67072, 67382, 67392, 67413, 67424, 67431, 67456, 67461, 67463, 67504, 67506, 67514, 67584, 67589, 67592, 67592, 67594, 67637, 67639, 67640, 67644, 67644, 67647, 67669, 67680, 67702, 67712, 67742, 67808, 67826, 67828, 67829, 67840, 67861, 67872, 67897, 67968, 68023, 68030, 68031, 68096, 68099, 68101, 68102, 68108, 68115, 68117, 68119, 68121, 68149, 68152, 68154, 68159, 68159, 68192, 68220, 68224, 68252, 68288, 68295, 68297, 68326, 68352, 68405, 68416, 68437, 68448, 68466, 68480, 68497, 68608, 68680, 68736, 68786, 68800, 68850, 68864, 68903, 68912, 68921, 69248, 69289, 69291, 69292, 69296, 69297, 69373, 69404, 69415, 69415, 69424, 69456, 69488, 69509, 69552, 69572, 69600, 69622, 69632, 69702, 69734, 69749, 69759, 69818, 69826, 69826, 69840, 69864, 69872, 69881, 69888, 69940, 69942, 69951, 69956, 69959, 69968, 70003, 70006, 70006, 70016, 70084, 70089, 70092, 70094, 70106, 70108, 70108, 70144, 70161, 70163, 70199, 70206, 70209, 70272, 70278, 70280, 70280, 70282, 70285, 70287, 70301, 70303, 70312, 70320, 70378, 70384, 70393, 70400, 70403, 70405, 70412, 70415, 70416, 70419, 70440, 70442, 70448, 70450, 70451, 70453, 70457, 70459, 70468, 70471, 70472, 70475, 70477, 70480, 70480, 70487, 70487, 70493, 70499, 70502, 70508, 70512, 70516, 70656, 70730, 70736, 70745, 70750, 70753, 70784, 70853, 70855, 70855, 70864, 70873, 71040, 71093, 71096, 71104, 71128, 71133, 71168, 71232, 71236, 71236, 71248, 71257, 71296, 71352, 71360, 71369, 71424, 71450, 71453, 71467, 71472, 71481, 71488, 71494, 71680, 71738, 71840, 71913, 71935, 71942, 71945, 71945, 71948, 71955, 71957, 71958, 71960, 71989, 71991, 71992, 71995, 72003, 72016, 72025, 72096, 72103, 72106, 72151, 72154, 72161, 72163, 72164, 72192, 72254, 72263, 72263, 72272, 72345, 72349, 72349, 72368, 72440, 72704, 72712, 72714, 72758, 72760, 72768, 72784, 72793, 72818, 72847, 72850, 72871, 72873, 72886, 72960, 72966, 72968, 72969, 72971, 73014, 73018, 73018, 73020, 73021, 73023, 73031, 73040, 73049, 73056, 73061, 73063, 73064, 73066, 73102, 73104, 73105, 73107, 73112, 73120, 73129, 73440, 73462, 73472, 73488, 73490, 73530, 73534, 73538, 73552, 73561, 73648, 73648, 73728, 74649, 74752, 74862, 74880, 75075, 77712, 77808, 77824, 78895, 78912, 78933, 82944, 83526, 92160, 92728, 92736, 92766, 92768, 92777, 92784, 92862, 92864, 92873, 92880, 92909, 92912, 92916, 92928, 92982, 92992, 92995, 93008, 93017, 93027, 93047, 93053, 93071, 93760, 93823, 93952, 94026, 94031, 94087, 94095, 94111, 94176, 94177, 94179, 94180, 94192, 94193, 94208, 100343, 100352, 101589, 101632, 101640, 110576, 110579, 110581, 110587, 110589, 110590, 110592, 110882, 110898, 110898, 110928, 110930, 110933, 110933, 110948, 110951, 110960, 111355, 113664, 113770, 113776, 113788, 113792, 113800, 113808, 113817, 113821, 113822, 118528, 118573, 118576, 118598, 119141, 119145, 119149, 119154, 119163, 119170, 119173, 119179, 119210, 119213, 119362, 119364, 119808, 119892, 119894, 119964, 119966, 119967, 119970, 119970, 119973, 119974, 119977, 119980, 119982, 119993, 119995, 119995, 119997, 120003, 120005, 120069, 120071, 120074, 120077, 120084, 120086, 120092, 120094, 120121, 120123, 120126, 120128, 120132, 120134, 120134, 120138, 120144, 120146, 120485, 120488, 120512, 120514, 120538, 120540, 120570, 120572, 120596, 120598, 120628, 120630, 120654, 120656, 120686, 120688, 120712, 120714, 120744, 120746, 120770, 120772, 120779, 120782, 120831, 121344, 121398, 121403, 121452, 121461, 121461, 121476, 121476, 121499, 121503, 121505, 121519, 122624, 122654, 122661, 122666, 122880, 122886, 122888, 122904, 122907, 122913, 122915, 122916, 122918, 122922, 122928, 122989, 123023, 123023, 123136, 123180, 123184, 123197, 123200, 123209, 123214, 123214, 123536, 123566, 123584, 123641, 124112, 124153, 124896, 124902, 124904, 124907, 124909, 124910, 124912, 124926, 124928, 125124, 125136, 125142, 125184, 125259, 125264, 125273, 126464, 126467, 126469, 126495, 126497, 126498, 126500, 126500, 126503, 126503, 126505, 126514, 126516, 126519, 126521, 126521, 126523, 126523, 126530, 126530, 126535, 126535, 126537, 126537, 126539, 126539, 126541, 126543, 126545, 126546, 126548, 126548, 126551, 126551, 126553, 126553, 126555, 126555, 126557, 126557, 126559, 126559, 126561, 126562, 126564, 126564, 126567, 126570, 126572, 126578, 126580, 126583, 126585, 126588, 126590, 126590, 126592, 126601, 126603, 126619, 126625, 126627, 126629, 126633, 126635, 126651, 130032, 130041, 131072, 173791, 173824, 177977, 177984, 178205, 178208, 183969, 183984, 191456, 191472, 192093, 194560, 195101, 196608, 201546, 201552, 205743, 917760, 917999]; - -const commentDirectiveRegExSingleLine = /^\/\/\/?\s*@(ts-expect-error|ts-ignore)/; -const commentDirectiveRegExMultiLine = /^(?:\/|\*)*\s*@(ts-expect-error|ts-ignore)/; - -const jsDocTagTerminators = new Set([" ", "\t", "\n", "\r", "}", "*"]); - -function hasJSDocTag(text: string, offset: number, ...tags: string[]): boolean { - for (const tag of tags) { - if (text.startsWith(tag, offset)) { - if (offset + tag.length === text.length) { - return true; - } - if (jsDocTagTerminators.has(text[offset + tag.length])) { - return true; - } - } - } - return false; -} - -function scanJSDocCommentForTags(text: string, tokenFlags: TokenFlags): TokenFlags { - let offset = 0; - while (true) { - const i = text.indexOf("@", offset); - if (i < 0) { - return tokenFlags; - } - offset = i + 1; - if (!(tokenFlags & TokenFlags.PrecedingJSDocWithDeprecated) && hasJSDocTag(text, offset, "deprecated")) { - tokenFlags |= TokenFlags.PrecedingJSDocWithDeprecated; - } - if (!(tokenFlags & TokenFlags.PrecedingJSDocWithSeeOrLink) && hasJSDocTag(text, offset, "see", "link", "linkcode", "linkplain")) { - tokenFlags |= TokenFlags.PrecedingJSDocWithSeeOrLink; - } - if ( - (tokenFlags & (TokenFlags.PrecedingJSDocWithDeprecated | TokenFlags.PrecedingJSDocWithSeeOrLink)) === - (TokenFlags.PrecedingJSDocWithDeprecated | TokenFlags.PrecedingJSDocWithSeeOrLink) - ) { - return tokenFlags; - } - } -} - -function lookupInUnicodeMap(code: number, map: readonly number[]): boolean { - if (code < map[0]) { - return false; - } - - let lo = 0; - let hi: number = map.length; - let mid: number; - - while (lo + 1 < hi) { - mid = lo + (hi - lo) / 2; - mid -= mid % 2; - if (map[mid] <= code && code <= map[mid + 1]) { - return true; - } - - if (code < map[mid]) { - hi = mid; - } - else { - lo = mid + 2; - } - } - - return false; -} - -export function isUnicodeIdentifierStart(code: number): boolean { - return lookupInUnicodeMap(code, unicodeESNextIdentifierStart); -} - -function isUnicodeIdentifierPart(code: number): boolean { - return lookupInUnicodeMap(code, unicodeESNextIdentifierPart); -} - -function makeReverseMap(source: Map): T[] { - const result: T[] = []; - source.forEach((value, name) => { - result[value] = name; - }); - return result; -} - -const tokenStrings = makeReverseMap(textToToken); - -export function tokenToString(t: SyntaxKind): string | undefined { - return tokenStrings[t]; -} - -export function stringToToken(s: string): SyntaxKind | undefined { - return textToToken.get(s); -} - -export function characterCodeToRegularExpressionFlag(ch: number): RegularExpressionFlags | undefined { - return charCodeToRegExpFlag.get(ch); -} - -export function computeLineStarts(text: string): number[] { - const result: number[] = []; - let pos = 0; - let lineStart = 0; - while (pos < text.length) { - const ch = text.charCodeAt(pos); - pos++; - switch (ch) { - case CharacterCodes.carriageReturn: - if (text.charCodeAt(pos) === CharacterCodes.lineFeed) { - pos++; - } - // falls through - case CharacterCodes.lineFeed: - result.push(lineStart); - lineStart = pos; - break; - default: - if (ch > CharacterCodes.maxAsciiCharacter && isLineBreak(ch)) { - result.push(lineStart); - lineStart = pos; - } - break; - } - } - result.push(lineStart); - return result; -} - -export function isWhiteSpaceLike(ch: number): boolean { - return isWhiteSpaceSingleLine(ch) || isLineBreak(ch); -} - -export function isWhiteSpaceSingleLine(ch: number): boolean { - return ch === CharacterCodes.space || - ch === CharacterCodes.tab || - ch === CharacterCodes.verticalTab || - ch === CharacterCodes.formFeed || - ch === CharacterCodes.nonBreakingSpace || - ch === CharacterCodes.nextLine || - ch === CharacterCodes.ogham || - ch >= CharacterCodes.enQuad && ch <= CharacterCodes.zeroWidthSpace || - ch === CharacterCodes.narrowNoBreakSpace || - ch === CharacterCodes.mathematicalSpace || - ch === CharacterCodes.ideographicSpace || - ch === CharacterCodes.byteOrderMark; -} - -export function isLineBreak(ch: number): boolean { - return ch === CharacterCodes.lineFeed || - ch === CharacterCodes.carriageReturn || - ch === CharacterCodes.lineSeparator || - ch === CharacterCodes.paragraphSeparator; -} - -function isDigit(ch: number): boolean { - return ch >= CharacterCodes._0 && ch <= CharacterCodes._9; -} - -function isHexDigit(ch: number): boolean { - return isDigit(ch) || ch >= CharacterCodes.A && ch <= CharacterCodes.F || ch >= CharacterCodes.a && ch <= CharacterCodes.f; -} - -function isOctalDigit(ch: number): boolean { - return ch >= CharacterCodes._0 && ch <= CharacterCodes._7; -} - -export function couldStartTrivia(text: string, pos: number): boolean { - const ch = text.charCodeAt(pos); - switch (ch) { - case CharacterCodes.carriageReturn: - case CharacterCodes.lineFeed: - case CharacterCodes.tab: - case CharacterCodes.verticalTab: - case CharacterCodes.formFeed: - case CharacterCodes.space: - case CharacterCodes.slash: - case CharacterCodes.lessThan: - case CharacterCodes.bar: - case CharacterCodes.equals: - case CharacterCodes.greaterThan: - return true; - case CharacterCodes.hash: - return pos === 0; - default: - return ch > CharacterCodes.maxAsciiCharacter; - } -} - -export function skipTrivia(text: string, pos: number, stopAfterLineBreak?: boolean, stopAtComments?: boolean, inJSDoc?: boolean): number { - if (pos < 0) { - return pos; - } - - let canConsumeStar = false; - while (true) { - const ch = text.charCodeAt(pos); - switch (ch) { - case CharacterCodes.carriageReturn: - if (text.charCodeAt(pos + 1) === CharacterCodes.lineFeed) { - pos++; - } - // falls through - case CharacterCodes.lineFeed: - pos++; - if (stopAfterLineBreak) { - return pos; - } - canConsumeStar = !!inJSDoc; - continue; - case CharacterCodes.tab: - case CharacterCodes.verticalTab: - case CharacterCodes.formFeed: - case CharacterCodes.space: - pos++; - continue; - case CharacterCodes.slash: - if (stopAtComments) { - break; - } - if (text.charCodeAt(pos + 1) === CharacterCodes.slash) { - pos += 2; - while (pos < text.length) { - if (isLineBreak(text.charCodeAt(pos))) { - break; - } - pos++; - } - canConsumeStar = false; - continue; - } - if (text.charCodeAt(pos + 1) === CharacterCodes.asterisk) { - pos += 2; - while (pos < text.length) { - if (text.charCodeAt(pos) === CharacterCodes.asterisk && text.charCodeAt(pos + 1) === CharacterCodes.slash) { - pos += 2; - break; - } - pos++; - } - canConsumeStar = false; - continue; - } - break; - - case CharacterCodes.lessThan: - case CharacterCodes.bar: - case CharacterCodes.equals: - case CharacterCodes.greaterThan: - if (isConflictMarkerTrivia(text, pos)) { - pos = scanConflictMarkerTrivia(text, pos); - canConsumeStar = false; - continue; - } - break; - - case CharacterCodes.hash: - if (pos === 0 && isShebangTrivia(text, pos)) { - pos = scanShebangTrivia(text, pos); - continue; - } - break; - - case CharacterCodes.asterisk: - if (canConsumeStar) { - pos++; - canConsumeStar = false; - continue; - } - break; - - default: - if (ch > CharacterCodes.maxAsciiCharacter && (isWhiteSpaceLike(ch))) { - pos++; - continue; - } - break; - } - return pos; - } -} - -function isConflictMarkerTrivia(text: string, pos: number) { - if (pos >= text.length) { - return false; - } - - const ch = text.charCodeAt(pos); - - if (pos === 0 || isLineBreak(text.charCodeAt(pos - 1))) { - if (ch === CharacterCodes.lessThan || ch === CharacterCodes.greaterThan || ch === CharacterCodes.equals) { - if (pos + 6 < text.length && text.charCodeAt(pos + 1) === ch && text.charCodeAt(pos + 2) === ch && text.charCodeAt(pos + 3) === ch && text.charCodeAt(pos + 4) === ch && text.charCodeAt(pos + 5) === ch && text.charCodeAt(pos + 6) === ch) { - return ch === CharacterCodes.equals || text.charCodeAt(pos + 7) === CharacterCodes.space; - } - } - if (ch === CharacterCodes.bar && pos + 6 < text.length && text.charCodeAt(pos + 1) === ch && text.charCodeAt(pos + 2) === ch && text.charCodeAt(pos + 3) === ch && text.charCodeAt(pos + 4) === ch && text.charCodeAt(pos + 5) === ch && text.charCodeAt(pos + 6) === ch) { - return true; - } - } - - return false; -} - -function scanConflictMarkerTrivia(text: string, pos: number): number { - const ch = text.charCodeAt(pos); - const len = text.length; - - if (ch === CharacterCodes.lessThan || ch === CharacterCodes.greaterThan) { - while (pos < len && !isLineBreak(text.charCodeAt(pos))) { - pos++; - } - } - else { - // Consume everything from the start of a ||||||| or ======= marker to the start - // of the next ======= or >>>>>>> marker. - pos += 7; // skip marker - while (pos < len) { - const currentChar = text.charCodeAt(pos); - if ((currentChar === CharacterCodes.equals || currentChar === CharacterCodes.greaterThan) && isConflictMarkerTrivia(text, pos)) { - break; - } - pos++; - } - } - - return pos; -} - -function isShebangTrivia(text: string, pos: number) { - return pos === 0 && text.charCodeAt(0) === CharacterCodes.hash && text.charCodeAt(1) === CharacterCodes.exclamation; -} - -function scanShebangTrivia(text: string, pos: number) { - pos += 2; - while (pos < text.length) { - if (isLineBreak(text.charCodeAt(pos))) { - break; - } - pos++; - } - return pos; -} - -export type CommentKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia; - -export interface CommentRange { - pos: number; - end: number; - hasTrailingNewLine?: boolean; - kind: CommentKind; -} - -function iterateCommentRanges( - reduce: boolean, - text: string, - pos: number, - trailing: boolean, - cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T, memo: U | undefined) => U, - state: T, - initial?: U, -): U | undefined { - let pendingPos!: number; - let pendingEnd!: number; - let pendingKind!: CommentKind; - let pendingHasTrailingNewLine!: boolean; - let hasPendingCommentRange = false; - let collecting = trailing; - let accumulator = initial; - if (pos === 0) { - collecting = true; - const shebang = getShebang(text); - if (shebang) { - pos = shebang.length; - } - } - scan: - while (pos >= 0 && pos < text.length) { - const ch = text.charCodeAt(pos); - switch (ch) { - case CharacterCodes.carriageReturn: - if (text.charCodeAt(pos + 1) === CharacterCodes.lineFeed) { - pos++; - } - // falls through - case CharacterCodes.lineFeed: - pos++; - if (trailing) { - break scan; - } - collecting = true; - if (hasPendingCommentRange) { - pendingHasTrailingNewLine = true; - } - continue; - case CharacterCodes.tab: - case CharacterCodes.verticalTab: - case CharacterCodes.formFeed: - case CharacterCodes.space: - pos++; - continue; - case CharacterCodes.slash: - const nextChar = text.charCodeAt(pos + 1); - let hasTrailingNewLine = false; - if (nextChar === CharacterCodes.slash || nextChar === CharacterCodes.asterisk) { - const kind: CommentKind = nextChar === CharacterCodes.slash ? SyntaxKind.SingleLineCommentTrivia : SyntaxKind.MultiLineCommentTrivia; - const startPos = pos; - pos += 2; - if (nextChar === CharacterCodes.slash) { - while (pos < text.length) { - if (isLineBreak(text.charCodeAt(pos))) { - hasTrailingNewLine = true; - break; - } - pos++; - } - } - else { - while (pos < text.length) { - if (text.charCodeAt(pos) === CharacterCodes.asterisk && text.charCodeAt(pos + 1) === CharacterCodes.slash) { - pos += 2; - break; - } - pos++; - } - } - - if (collecting) { - if (hasPendingCommentRange) { - accumulator = cb(pendingPos, pendingEnd, pendingKind, pendingHasTrailingNewLine, state, accumulator); - if (!reduce && accumulator) { - return accumulator; - } - } - - pendingPos = startPos; - pendingEnd = pos; - pendingKind = kind; - pendingHasTrailingNewLine = hasTrailingNewLine; - hasPendingCommentRange = true; - } - continue; - } - break scan; - default: - if (ch > CharacterCodes.maxAsciiCharacter && (isWhiteSpaceLike(ch))) { - if (hasPendingCommentRange && isLineBreak(ch)) { - pendingHasTrailingNewLine = true; - } - pos++; - continue; - } - break scan; - } - } - - if (hasPendingCommentRange) { - accumulator = cb(pendingPos, pendingEnd, pendingKind, pendingHasTrailingNewLine, state, accumulator); - } - - return accumulator; -} - -function appendCommentRange(pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, _state: any, comments: CommentRange[] = []) { - comments.push({ kind, pos, end, hasTrailingNewLine }); - return comments; -} - -export function forEachLeadingCommentRange(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T) => U, state: T): U | undefined; -export function forEachLeadingCommentRange(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean) => U): U | undefined; -export function forEachLeadingCommentRange(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T) => U, state?: T): U | undefined { - return iterateCommentRanges(/*reduce*/ false, text, pos, /*trailing*/ false, cb, state!, /*initial*/ undefined); -} - -export function forEachTrailingCommentRange(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T) => U, state: T): U | undefined; -export function forEachTrailingCommentRange(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean) => U): U | undefined; -export function forEachTrailingCommentRange(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T) => U, state?: T): U | undefined { - return iterateCommentRanges(/*reduce*/ false, text, pos, /*trailing*/ true, cb, state!, /*initial*/ undefined); -} - -export function reduceEachLeadingCommentRange(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T) => U, state: T, initial: U): U | undefined { - return iterateCommentRanges(/*reduce*/ true, text, pos, /*trailing*/ false, cb, state, initial); -} - -export function reduceEachTrailingCommentRange(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T) => U, state: T, initial: U): U | undefined { - return iterateCommentRanges(/*reduce*/ true, text, pos, /*trailing*/ true, cb, state, initial); -} - -export function getLeadingCommentRanges(text: string, pos: number): CommentRange[] | undefined { - return reduceEachLeadingCommentRange(text, pos, appendCommentRange, /*state*/ undefined as any, /*initial*/ undefined as any as CommentRange[]); -} - -export function getTrailingCommentRanges(text: string, pos: number): CommentRange[] | undefined { - return reduceEachTrailingCommentRange(text, pos, appendCommentRange, /*state*/ undefined as any, /*initial*/ undefined as any as CommentRange[]); -} - -export function getShebang(text: string): string | undefined { - const shebang = /^#!.*/; - if (isShebangTrivia(text, 0)) { - const match = shebang.exec(text); - if (match) { - return match[0]; - } - } - return undefined; -} - -export function isIdentifierStart(ch: number, _languageVersion?: ScriptTarget): boolean { - return ch >= CharacterCodes.A && ch <= CharacterCodes.Z || ch >= CharacterCodes.a && ch <= CharacterCodes.z || - ch === CharacterCodes.$ || ch === CharacterCodes._ || - ch > CharacterCodes.maxAsciiCharacter && isUnicodeIdentifierStart(ch); -} - -export function isIdentifierPart(ch: number, _languageVersion?: ScriptTarget, identifierVariant?: LanguageVariant): boolean { - return ch >= CharacterCodes.A && ch <= CharacterCodes.Z || ch >= CharacterCodes.a && ch <= CharacterCodes.z || - ch >= CharacterCodes._0 && ch <= CharacterCodes._9 || ch === CharacterCodes.$ || ch === CharacterCodes._ || - // "-" and ":" are valid in JSX Identifiers - (identifierVariant === LanguageVariant.JSX ? (ch === CharacterCodes.minus || ch === CharacterCodes.colon) : false) || - ch > CharacterCodes.maxAsciiCharacter && isUnicodeIdentifierPart(ch); -} - -export function isIdentifierText(name: string, _languageVersion?: ScriptTarget, identifierVariant?: LanguageVariant): boolean { - let ch = name.codePointAt(0)!; - if (!isIdentifierStart(ch)) { - return false; - } - - for (let i = charSize(ch); i < name.length; i += charSize(ch)) { - if (!isIdentifierPart(ch = name.codePointAt(i)!, undefined, identifierVariant)) { - return false; - } - } - - return true; -} - -function codePointAt(s: string, i: number): number { - return s.codePointAt(i)!; -} - -function charSize(ch: number) { - if (ch >= 0x10000) { - return 2; - } - if (ch === CharacterCodes.EOF) { - return 0; - } - return 1; -} - -export function utf16EncodeAsString(codePoint: number): string { - return String.fromCodePoint(codePoint); -} - -function parsePseudoBigInt(stringValue: string): string { - let log2Base: number; - switch (stringValue.charCodeAt(1)) { - case CharacterCodes.b: - case CharacterCodes.B: - log2Base = 1; - break; - case CharacterCodes.o: - case CharacterCodes.O: - log2Base = 3; - break; - case CharacterCodes.x: - case CharacterCodes.X: - log2Base = 4; - break; - default: - // decimals for now just peel the trailing "n" and return - const nIndex = stringValue.length - 1; - let nonZeroStart = 0; - while (stringValue.charCodeAt(nonZeroStart) === CharacterCodes._0) { - nonZeroStart++; - } - return stringValue.slice(nonZeroStart, nIndex) || "0"; - } - // exit early for zero - const startIndex = 2; - const endIndex = stringValue.length - 1; - const bitsNeeded = (endIndex - startIndex) * log2Base; - // Stores the value specified by the string as a LE array of 16-bit integers - // using Uint16 instead of Uint32 so combining steps can use bitwise operators - const segments = new Uint16Array((bitsNeeded >>> 4) + (bitsNeeded & 15 ? 1 : 0)); - // Add the digits, one at a time - for (let i = endIndex - 1, bitOffset = 0; i >= startIndex; i--, bitOffset += log2Base) { - const segment = bitOffset >>> 4; - const digitChar = stringValue.charCodeAt(i); - // Find character value - const digit = digitChar <= CharacterCodes._9 - ? digitChar - CharacterCodes._0 - : 10 + digitChar - (digitChar <= CharacterCodes.F ? CharacterCodes.A : CharacterCodes.a); - // Add bits into segment - const shiftedDigit = digit << (bitOffset & 15); - segments[segment] |= shiftedDigit; - // overflow to next segment - const residual = shiftedDigit >>> 16; - if (residual) segments[segment + 1] |= residual; - } - // Repeatedly divide by 10 to find each decimal digit - let base10Value = ""; - let firstNonzeroSegment = segments.length - 1; - let segmentsRemaining = true; - while (segmentsRemaining) { - let mod10 = 0; - segmentsRemaining = false; - for (let segment = firstNonzeroSegment; segment >= 0; segment--) { - const newSegment = mod10 << 16 | segments[segment]; - const segmentValue = (newSegment / 10) | 0; - segments[segment] = segmentValue; - mod10 = newSegment - segmentValue * 10; - if (segmentValue && !segmentsRemaining) { - firstNonzeroSegment = segment; - segmentsRemaining = true; - } - } - base10Value = mod10 + base10Value; - } - return base10Value; -} - -// Creates a scanner over a (possibly unspecified) range of a piece of text. -export function createScanner( - skipTrivia: boolean, - languageVariant: LanguageVariant = LanguageVariant.Standard, - textInitial?: string, - start?: number, - length?: number, -): Scanner { - // Why var? It avoids TDZ checks in the runtime which can be costly. - // See: https://github.com/microsoft/TypeScript/issues/52924 - /* eslint-disable no-var */ - var text = textInitial!; - - // Current position (end position of text of current token) - var pos: number; - - // end of text - var end: number; - - // Start position of whitespace before current token - var fullStartPos: number; - - // Start position of text of current token - var tokenStart: number; - - var token: SyntaxKind; - var tokenValue!: string; - var tokenFlags: TokenFlags; - - var commentDirectives: CommentDirective[] | undefined; - var skipJsDocLeadingAsterisks = 0; - - setText(text, start, length); - - var scanner: Scanner = { - getTokenFullStart: () => fullStartPos, - getTokenEnd: () => pos, - getToken: () => token, - getTokenStart: () => tokenStart, - getTokenText: () => text.substring(tokenStart, pos), - getTokenValue: () => tokenValue, - hasUnicodeEscape: () => (tokenFlags & TokenFlags.UnicodeEscape) !== 0, - hasExtendedUnicodeEscape: () => (tokenFlags & TokenFlags.ExtendedUnicodeEscape) !== 0, - hasPrecedingLineBreak: () => (tokenFlags & TokenFlags.PrecedingLineBreak) !== 0, - hasPrecedingJSDocComment: () => (tokenFlags & TokenFlags.PrecedingJSDocComment) !== 0, - hasPrecedingJSDocLeadingAsterisks: () => (tokenFlags & TokenFlags.PrecedingJSDocLeadingAsterisks) !== 0, - hasPrecedingJSDocWithDeprecatedTag: () => (tokenFlags & TokenFlags.PrecedingJSDocWithDeprecated) !== 0, - hasPrecedingJSDocWithSeeOrLink: () => (tokenFlags & TokenFlags.PrecedingJSDocWithSeeOrLink) !== 0, - isIdentifier: () => token === SyntaxKind.Identifier || token > SyntaxKind.LastReservedWord, - isReservedWord: () => token >= SyntaxKind.FirstReservedWord && token <= SyntaxKind.LastReservedWord, - isUnterminated: () => (tokenFlags & TokenFlags.Unterminated) !== 0, - getCommentDirectives: () => commentDirectives, - getNumericLiteralFlags: () => tokenFlags & TokenFlags.NumericLiteralFlags, - getTokenFlags: () => tokenFlags, - reScanGreaterToken, - reScanAsteriskEqualsToken, - reScanSlashToken, - reScanTemplateToken, - reScanTemplateHeadOrNoSubstitutionTemplate, - scanJsxIdentifier, - scanJsxAttributeValue, - reScanJsxAttributeValue, - reScanJsxToken, - reScanLessThanToken, - reScanHashToken, - reScanQuestionToken, - reScanInvalidIdentifier, - scanJsxToken, - scanJsDocToken, - scanJSDocCommentTextToken, - scan, - getText, - clearCommentDirectives, - setText, - setLanguageVariant, - resetTokenState, - setSkipJsDocLeadingAsterisks, - tryScan, - lookAhead, - scanRange, - }; - /* eslint-enable no-var */ - - return scanner; - - function codePointUnchecked(pos: number) { - return codePointAt(text, pos); - } - - function codePointChecked(pos: number) { - return pos >= 0 && pos < end ? codePointUnchecked(pos) : CharacterCodes.EOF; - } - - function charCodeUnchecked(pos: number) { - return text.charCodeAt(pos); - } - - function charCodeChecked(pos: number) { - return pos >= 0 && pos < end ? charCodeUnchecked(pos) : CharacterCodes.EOF; - } - - function scanNumberFragment(): string { - let start = pos; - let allowSeparator = false; - let isPreviousTokenSeparator = false; - let result = ""; - while (true) { - const ch = charCodeUnchecked(pos); - if (ch === CharacterCodes._) { - tokenFlags |= TokenFlags.ContainsSeparator; - if (allowSeparator) { - allowSeparator = false; - isPreviousTokenSeparator = true; - result += text.substring(start, pos); - } - else { - tokenFlags |= TokenFlags.ContainsInvalidSeparator; - } - pos++; - start = pos; - continue; - } - if (isDigit(ch)) { - allowSeparator = true; - isPreviousTokenSeparator = false; - pos++; - continue; - } - break; - } - if (charCodeUnchecked(pos - 1) === CharacterCodes._) { - tokenFlags |= TokenFlags.ContainsInvalidSeparator; - } - return result + text.substring(start, pos); - } - - function scanNumber(): SyntaxKind { - let start = pos; - let mainFragment: string; - if (charCodeUnchecked(pos) === CharacterCodes._0) { - pos++; - if (charCodeUnchecked(pos) === CharacterCodes._) { - tokenFlags |= TokenFlags.ContainsSeparator | TokenFlags.ContainsInvalidSeparator; - pos--; - mainFragment = scanNumberFragment(); - } - else if (!scanDigits()) { - tokenFlags |= TokenFlags.ContainsLeadingZero; - mainFragment = "" + +tokenValue; - } - else if (!tokenValue) { - mainFragment = "0"; - } - else { - tokenValue = "" + parseInt(tokenValue, 8); - tokenFlags |= TokenFlags.Octal; - const withMinus = token === SyntaxKind.MinusToken; - const literal = (withMinus ? "-" : "") + "0o" + (+tokenValue).toString(8); - if (withMinus) start--; - return SyntaxKind.NumericLiteral; - } - } - else { - mainFragment = scanNumberFragment(); - } - let decimalFragment: string | undefined; - let scientificFragment: string | undefined; - if (charCodeUnchecked(pos) === CharacterCodes.dot) { - pos++; - decimalFragment = scanNumberFragment(); - } - let end = pos; - if (charCodeUnchecked(pos) === CharacterCodes.E || charCodeUnchecked(pos) === CharacterCodes.e) { - pos++; - tokenFlags |= TokenFlags.Scientific; - if (charCodeUnchecked(pos) === CharacterCodes.plus || charCodeUnchecked(pos) === CharacterCodes.minus) pos++; - const preNumericPart = pos; - const finalFragment = scanNumberFragment(); - if (finalFragment) { - scientificFragment = text.substring(end, preNumericPart) + finalFragment; - end = pos; - } - } - let result: string; - if (tokenFlags & TokenFlags.ContainsSeparator) { - result = mainFragment; - if (decimalFragment) { - result += "." + decimalFragment; - } - if (scientificFragment) { - result += scientificFragment; - } - } - else { - result = text.substring(start, end); - } - - if (tokenFlags & TokenFlags.ContainsLeadingZero) { - tokenValue = "" + +result; - return SyntaxKind.NumericLiteral; - } - - if (decimalFragment !== undefined || tokenFlags & TokenFlags.Scientific) { - checkForIdentifierStartAfterNumericLiteral(); - tokenValue = "" + +result; - return SyntaxKind.NumericLiteral; - } - else { - tokenValue = result; - const type = checkBigIntSuffix(); - checkForIdentifierStartAfterNumericLiteral(); - return type; - } - } - - function checkForIdentifierStartAfterNumericLiteral() { - if (!isIdentifierStart(codePointUnchecked(pos))) { - return; - } - - const identifierStart = pos; - const { length } = scanIdentifierParts(); - - if (!(length === 1 && text[identifierStart] === "n")) { - pos = identifierStart; - } - } - - function scanDigits(): boolean { - const start = pos; - let isOctal = true; - while (isDigit(charCodeChecked(pos))) { - if (!isOctalDigit(charCodeUnchecked(pos))) { - isOctal = false; - } - pos++; - } - tokenValue = text.substring(start, pos); - return isOctal; - } - - function scanExactNumberOfHexDigits(count: number, canHaveSeparators: boolean): number { - const valueString = scanHexDigits(count, /*scanAsManyAsPossible*/ false, canHaveSeparators); - return valueString ? parseInt(valueString, 16) : -1; - } - - function scanMinimumNumberOfHexDigits(count: number, canHaveSeparators: boolean): string { - return scanHexDigits(count, /*scanAsManyAsPossible*/ true, canHaveSeparators); - } - - function scanHexDigits(minCount: number, scanAsManyAsPossible: boolean, canHaveSeparators: boolean): string { - let valueChars: number[] = []; - let allowSeparator = false; - while (valueChars.length < minCount || scanAsManyAsPossible) { - let ch = charCodeUnchecked(pos); - if (canHaveSeparators && ch === CharacterCodes._) { - tokenFlags |= TokenFlags.ContainsSeparator; - if (allowSeparator) { - allowSeparator = false; - } - pos++; - continue; - } - allowSeparator = canHaveSeparators; - if (ch >= CharacterCodes.A && ch <= CharacterCodes.F) { - ch += CharacterCodes.a - CharacterCodes.A; - } - else if ( - !((ch >= CharacterCodes._0 && ch <= CharacterCodes._9) || - (ch >= CharacterCodes.a && ch <= CharacterCodes.f)) - ) { - break; - } - valueChars.push(ch); - pos++; - } - if (valueChars.length < minCount) { - valueChars = []; - } - return String.fromCharCode(...valueChars); - } - - function scanString(jsxAttributeString = false): string { - const quote = charCodeUnchecked(pos); - pos++; - let result = ""; - let start = pos; - while (true) { - if (pos >= end) { - result += text.substring(start, pos); - tokenFlags |= TokenFlags.Unterminated; - break; - } - const ch = charCodeUnchecked(pos); - if (ch === quote) { - result += text.substring(start, pos); - pos++; - break; - } - if (ch === CharacterCodes.backslash && !jsxAttributeString) { - result += text.substring(start, pos); - result += scanEscapeSequence(EscapeSequenceScanningFlags.String | EscapeSequenceScanningFlags.ReportErrors); - start = pos; - continue; - } - - if ((ch === CharacterCodes.lineFeed || ch === CharacterCodes.carriageReturn) && !jsxAttributeString) { - result += text.substring(start, pos); - tokenFlags |= TokenFlags.Unterminated; - break; - } - pos++; - } - return result; - } - - function scanTemplateAndSetTokenValue(shouldEmitInvalidEscapeError: boolean): SyntaxKind { - const startedWithBacktick = charCodeUnchecked(pos) === CharacterCodes.backtick; - - pos++; - let start = pos; - let contents = ""; - let resultingToken!: SyntaxKind; - - while (true) { - if (pos >= end) { - contents += text.substring(start, pos); - tokenFlags |= TokenFlags.Unterminated; - resultingToken = startedWithBacktick ? SyntaxKind.NoSubstitutionTemplateLiteral : SyntaxKind.TemplateTail; - break; - } - - const currChar = charCodeUnchecked(pos); - - if (currChar === CharacterCodes.backtick) { - contents += text.substring(start, pos); - pos++; - resultingToken = startedWithBacktick ? SyntaxKind.NoSubstitutionTemplateLiteral : SyntaxKind.TemplateTail; - break; - } - - if (currChar === CharacterCodes.$ && pos + 1 < end && charCodeUnchecked(pos + 1) === CharacterCodes.openBrace) { - contents += text.substring(start, pos); - pos += 2; - resultingToken = startedWithBacktick ? SyntaxKind.TemplateHead : SyntaxKind.TemplateMiddle; - break; - } - - if (currChar === CharacterCodes.backslash) { - contents += text.substring(start, pos); - contents += scanEscapeSequence(EscapeSequenceScanningFlags.String | (shouldEmitInvalidEscapeError ? EscapeSequenceScanningFlags.ReportErrors : 0)); - start = pos; - continue; - } - - if (currChar === CharacterCodes.carriageReturn) { - contents += text.substring(start, pos); - pos++; - - if (pos < end && charCodeUnchecked(pos) === CharacterCodes.lineFeed) { - pos++; - } - - contents += "\n"; - start = pos; - continue; - } - - pos++; - } - - tokenValue = contents; - return resultingToken; - } - - function scanEscapeSequence(flags: EscapeSequenceScanningFlags): string { - const start = pos; - pos++; - if (pos >= end) { - return ""; - } - const ch = charCodeUnchecked(pos); - pos++; - switch (ch) { - case CharacterCodes._0: - if (pos >= end || !isDigit(charCodeUnchecked(pos))) { - return "\0"; - } - // falls through - case CharacterCodes._1: - case CharacterCodes._2: - case CharacterCodes._3: - if (pos < end && isOctalDigit(charCodeUnchecked(pos))) { - pos++; - } - // falls through - case CharacterCodes._4: - case CharacterCodes._5: - case CharacterCodes._6: - case CharacterCodes._7: - if (pos < end && isOctalDigit(charCodeUnchecked(pos))) { - pos++; - } - tokenFlags |= TokenFlags.ContainsInvalidEscape; - if (flags & EscapeSequenceScanningFlags.ReportInvalidEscapeErrors) { - const code = parseInt(text.substring(start + 1, pos), 8); - return String.fromCharCode(code); - } - return text.substring(start, pos); - case CharacterCodes._8: - case CharacterCodes._9: - tokenFlags |= TokenFlags.ContainsInvalidEscape; - if (flags & EscapeSequenceScanningFlags.ReportInvalidEscapeErrors) { - return String.fromCharCode(ch); - } - return text.substring(start, pos); - case CharacterCodes.b: - return "\b"; - case CharacterCodes.t: - return "\t"; - case CharacterCodes.n: - return "\n"; - case CharacterCodes.v: - return "\v"; - case CharacterCodes.f: - return "\f"; - case CharacterCodes.r: - return "\r"; - case CharacterCodes.singleQuote: - return "'"; - case CharacterCodes.doubleQuote: - return '"'; - case CharacterCodes.u: - if (pos < end && charCodeUnchecked(pos) === CharacterCodes.openBrace) { - pos -= 2; - const result = scanExtendedUnicodeEscape(); - if (!(flags & EscapeSequenceScanningFlags.AllowExtendedUnicodeEscape)) { - tokenFlags |= TokenFlags.ContainsInvalidEscape; - } - return result; - } - // '\uDDDD' - for (; pos < start + 6; pos++) { - if (!(pos < end && isHexDigit(charCodeUnchecked(pos)))) { - tokenFlags |= TokenFlags.ContainsInvalidEscape; - return text.substring(start, pos); - } - } - tokenFlags |= TokenFlags.UnicodeEscape; - { - const escapedValue = parseInt(text.substring(start + 2, pos), 16); - const escapedValueString = String.fromCharCode(escapedValue); - if ( - flags & EscapeSequenceScanningFlags.AnyUnicodeMode && escapedValue >= 0xD800 && escapedValue <= 0xDBFF && - pos + 6 < end && text.substring(pos, pos + 2) === "\\u" && charCodeUnchecked(pos + 2) !== CharacterCodes.openBrace - ) { - const nextStart = pos; - let nextPos = pos + 2; - for (; nextPos < nextStart + 6; nextPos++) { - if (!isHexDigit(charCodeUnchecked(nextPos))) { - return escapedValueString; - } - } - const nextEscapedValue = parseInt(text.substring(nextStart + 2, nextPos), 16); - if (nextEscapedValue >= 0xDC00 && nextEscapedValue <= 0xDFFF) { - pos = nextPos; - return escapedValueString + String.fromCharCode(nextEscapedValue); - } - } - return escapedValueString; - } - - case CharacterCodes.x: - for (; pos < start + 4; pos++) { - if (!(pos < end && isHexDigit(charCodeUnchecked(pos)))) { - tokenFlags |= TokenFlags.ContainsInvalidEscape; - return text.substring(start, pos); - } - } - tokenFlags |= TokenFlags.HexEscape; - return String.fromCharCode(parseInt(text.substring(start + 2, pos), 16)); - - case CharacterCodes.carriageReturn: - if (pos < end && charCodeUnchecked(pos) === CharacterCodes.lineFeed) { - pos++; - } - // falls through - case CharacterCodes.lineFeed: - case CharacterCodes.lineSeparator: - case CharacterCodes.paragraphSeparator: - return ""; - default: - return String.fromCharCode(ch); - } - } - - function scanExtendedUnicodeEscape(): string { - const start = pos; - pos += 3; - const escapedValueString = scanMinimumNumberOfHexDigits(1, /*canHaveSeparators*/ false); - const escapedValue = escapedValueString ? parseInt(escapedValueString, 16) : -1; - let isInvalidExtendedEscape = false; - - if (escapedValue < 0) { - isInvalidExtendedEscape = true; - } - else if (escapedValue > 0x10FFFF) { - isInvalidExtendedEscape = true; - } - - if (pos >= end) { - isInvalidExtendedEscape = true; - } - else if (charCodeUnchecked(pos) === CharacterCodes.closeBrace) { - pos++; - } - else { - isInvalidExtendedEscape = true; - } - - if (isInvalidExtendedEscape) { - tokenFlags |= TokenFlags.ContainsInvalidEscape; - return text.substring(start, pos); - } - - tokenFlags |= TokenFlags.ExtendedUnicodeEscape; - return utf16EncodeAsString(escapedValue); - } - - function peekUnicodeEscape(): number { - if (pos + 5 < end && charCodeUnchecked(pos + 1) === CharacterCodes.u) { - const start = pos; - pos += 2; - const value = scanExactNumberOfHexDigits(4, /*canHaveSeparators*/ false); - pos = start; - return value; - } - return -1; - } - - function peekExtendedUnicodeEscape(): number { - if (codePointUnchecked(pos + 1) === CharacterCodes.u && codePointUnchecked(pos + 2) === CharacterCodes.openBrace) { - const start = pos; - pos += 3; - const escapedValueString = scanMinimumNumberOfHexDigits(1, /*canHaveSeparators*/ false); - const escapedValue = escapedValueString ? parseInt(escapedValueString, 16) : -1; - pos = start; - return escapedValue; - } - return -1; - } - - function scanIdentifierParts(): string { - let result = ""; - let start = pos; - while (pos < end) { - let ch = codePointUnchecked(pos); - if (isIdentifierPart(ch)) { - pos += charSize(ch); - } - else if (ch === CharacterCodes.backslash) { - ch = peekExtendedUnicodeEscape(); - if (ch >= 0 && isIdentifierPart(ch)) { - result += scanExtendedUnicodeEscape(); - start = pos; - continue; - } - ch = peekUnicodeEscape(); - if (!(ch >= 0 && isIdentifierPart(ch))) { - break; - } - tokenFlags |= TokenFlags.UnicodeEscape; - result += text.substring(start, pos); - result += utf16EncodeAsString(ch); - pos += 6; - start = pos; - } - else { - break; - } - } - result += text.substring(start, pos); - return result; - } - - function getIdentifierToken(): SyntaxKind.Identifier | KeywordSyntaxKind { - const len = tokenValue.length; - if (len >= 2 && len <= 12) { - const ch = tokenValue.charCodeAt(0); - if (ch >= CharacterCodes.a && ch <= CharacterCodes.z) { - const keyword = textToKeyword.get(tokenValue); - if (keyword !== undefined) { - return token = keyword; - } - } - } - return token = SyntaxKind.Identifier; - } - - function scanBinaryOrOctalDigits(base: 2 | 8): string { - let value = ""; - let separatorAllowed = false; - let isPreviousTokenSeparator = false; - while (true) { - const ch = charCodeUnchecked(pos); - if (ch === CharacterCodes._) { - tokenFlags |= TokenFlags.ContainsSeparator; - if (separatorAllowed) { - separatorAllowed = false; - isPreviousTokenSeparator = true; - } - pos++; - continue; - } - separatorAllowed = true; - if (!isDigit(ch) || ch - CharacterCodes._0 >= base) { - break; - } - value += text[pos]; - pos++; - isPreviousTokenSeparator = false; - } - return value; - } - - function checkBigIntSuffix(): SyntaxKind { - if (charCodeUnchecked(pos) === CharacterCodes.n) { - tokenValue += "n"; - if (tokenFlags & TokenFlags.BinaryOrOctalSpecifier) { - tokenValue = parsePseudoBigInt(tokenValue) + "n"; - } - pos++; - return SyntaxKind.BigIntLiteral; - } - else { - const numericValue = tokenFlags & TokenFlags.BinarySpecifier - ? parseInt(tokenValue.slice(2), 2) - : tokenFlags & TokenFlags.OctalSpecifier - ? parseInt(tokenValue.slice(2), 8) - : +tokenValue; - tokenValue = "" + numericValue; - return SyntaxKind.NumericLiteral; - } - } - - function scan(): SyntaxKind { - fullStartPos = pos; - tokenFlags = TokenFlags.None; - while (true) { - tokenStart = pos; - if (pos >= end) { - return token = SyntaxKind.EndOfFile; - } - - const ch = codePointUnchecked(pos); - if (pos === 0) { - if (ch === CharacterCodes.hash && isShebangTrivia(text, pos)) { - pos = scanShebangTrivia(text, pos); - if (skipTrivia) { - continue; - } - else { - return token = SyntaxKind.Unknown; - } - } - } - - switch (ch) { - case CharacterCodes.lineFeed: - case CharacterCodes.carriageReturn: - tokenFlags |= TokenFlags.PrecedingLineBreak; - if (skipTrivia) { - pos++; - continue; - } - else { - if (ch === CharacterCodes.carriageReturn && pos + 1 < end && charCodeUnchecked(pos + 1) === CharacterCodes.lineFeed) { - pos += 2; - } - else { - pos++; - } - return token = SyntaxKind.NewLineTrivia; - } - case CharacterCodes.tab: - case CharacterCodes.verticalTab: - case CharacterCodes.formFeed: - case CharacterCodes.space: - case CharacterCodes.nonBreakingSpace: - case CharacterCodes.ogham: - case CharacterCodes.enQuad: - case CharacterCodes.emQuad: - case CharacterCodes.enSpace: - case CharacterCodes.emSpace: - case CharacterCodes.threePerEmSpace: - case CharacterCodes.fourPerEmSpace: - case CharacterCodes.sixPerEmSpace: - case CharacterCodes.figureSpace: - case CharacterCodes.punctuationSpace: - case CharacterCodes.thinSpace: - case CharacterCodes.hairSpace: - case CharacterCodes.zeroWidthSpace: - case CharacterCodes.narrowNoBreakSpace: - case CharacterCodes.mathematicalSpace: - case CharacterCodes.ideographicSpace: - case CharacterCodes.byteOrderMark: - if (skipTrivia) { - pos++; - continue; - } - else { - while (pos < end && isWhiteSpaceSingleLine(charCodeUnchecked(pos))) { - pos++; - } - return token = SyntaxKind.WhitespaceTrivia; - } - case CharacterCodes.exclamation: - if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) { - if (charCodeUnchecked(pos + 2) === CharacterCodes.equals) { - return pos += 3, token = SyntaxKind.ExclamationEqualsEqualsToken; - } - return pos += 2, token = SyntaxKind.ExclamationEqualsToken; - } - pos++; - return token = SyntaxKind.ExclamationToken; - case CharacterCodes.doubleQuote: - case CharacterCodes.singleQuote: - tokenValue = scanString(); - return token = SyntaxKind.StringLiteral; - case CharacterCodes.backtick: - return token = scanTemplateAndSetTokenValue(/*shouldEmitInvalidEscapeError*/ false); - case CharacterCodes.percent: - if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) { - return pos += 2, token = SyntaxKind.PercentEqualsToken; - } - pos++; - return token = SyntaxKind.PercentToken; - case CharacterCodes.ampersand: - if (charCodeUnchecked(pos + 1) === CharacterCodes.ampersand) { - if (charCodeUnchecked(pos + 2) === CharacterCodes.equals) { - return pos += 3, token = SyntaxKind.AmpersandAmpersandEqualsToken; - } - return pos += 2, token = SyntaxKind.AmpersandAmpersandToken; - } - if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) { - return pos += 2, token = SyntaxKind.AmpersandEqualsToken; - } - pos++; - return token = SyntaxKind.AmpersandToken; - case CharacterCodes.openParen: - pos++; - return token = SyntaxKind.OpenParenToken; - case CharacterCodes.closeParen: - pos++; - return token = SyntaxKind.CloseParenToken; - case CharacterCodes.asterisk: - if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) { - return pos += 2, token = SyntaxKind.AsteriskEqualsToken; - } - if (charCodeUnchecked(pos + 1) === CharacterCodes.asterisk) { - if (charCodeUnchecked(pos + 2) === CharacterCodes.equals) { - return pos += 3, token = SyntaxKind.AsteriskAsteriskEqualsToken; - } - return pos += 2, token = SyntaxKind.AsteriskAsteriskToken; - } - pos++; - if ( - skipJsDocLeadingAsterisks && - (tokenFlags & TokenFlags.PrecedingJSDocLeadingAsterisks) === 0 && - (tokenFlags & TokenFlags.PrecedingLineBreak) - ) { - tokenFlags |= TokenFlags.PrecedingJSDocLeadingAsterisks; - continue; - } - return token = SyntaxKind.AsteriskToken; - case CharacterCodes.plus: - if (charCodeUnchecked(pos + 1) === CharacterCodes.plus) { - return pos += 2, token = SyntaxKind.PlusPlusToken; - } - if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) { - return pos += 2, token = SyntaxKind.PlusEqualsToken; - } - pos++; - return token = SyntaxKind.PlusToken; - case CharacterCodes.comma: - pos++; - return token = SyntaxKind.CommaToken; - case CharacterCodes.minus: - if (charCodeUnchecked(pos + 1) === CharacterCodes.minus) { - return pos += 2, token = SyntaxKind.MinusMinusToken; - } - if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) { - return pos += 2, token = SyntaxKind.MinusEqualsToken; - } - pos++; - return token = SyntaxKind.MinusToken; - case CharacterCodes.dot: - if (isDigit(charCodeUnchecked(pos + 1))) { - scanNumber(); - return token = SyntaxKind.NumericLiteral; - } - if (charCodeUnchecked(pos + 1) === CharacterCodes.dot && charCodeUnchecked(pos + 2) === CharacterCodes.dot) { - return pos += 3, token = SyntaxKind.DotDotDotToken; - } - pos++; - return token = SyntaxKind.DotToken; - case CharacterCodes.slash: - // Single-line comment - if (charCodeUnchecked(pos + 1) === CharacterCodes.slash) { - pos += 2; - - while (pos < end) { - if (isLineBreak(charCodeUnchecked(pos))) { - break; - } - pos++; - } - - commentDirectives = appendIfCommentDirective( - commentDirectives, - text.slice(tokenStart, pos), - commentDirectiveRegExSingleLine, - tokenStart, - ); - - if (skipTrivia) { - continue; - } - else { - return token = SyntaxKind.SingleLineCommentTrivia; - } - } - // Multi-line comment - if (charCodeUnchecked(pos + 1) === CharacterCodes.asterisk) { - pos += 2; - const isJSDoc = charCodeUnchecked(pos) === CharacterCodes.asterisk && charCodeUnchecked(pos + 1) !== CharacterCodes.slash; - - let commentClosed = false; - let lastLineStart = tokenStart; - while (pos < end) { - const ch = charCodeUnchecked(pos); - - if (ch === CharacterCodes.asterisk && charCodeUnchecked(pos + 1) === CharacterCodes.slash) { - pos += 2; - commentClosed = true; - break; - } - - pos++; - - if (isLineBreak(ch)) { - lastLineStart = pos; - tokenFlags |= TokenFlags.PrecedingLineBreak; - } - } - - if (isJSDoc) { - tokenFlags |= TokenFlags.PrecedingJSDocComment; - tokenFlags = scanJSDocCommentForTags(text.slice(tokenStart, pos), tokenFlags); - } - - commentDirectives = appendIfCommentDirective(commentDirectives, text.slice(lastLineStart, pos), commentDirectiveRegExMultiLine, lastLineStart); - - if (skipTrivia) { - continue; - } - else { - if (!commentClosed) { - tokenFlags |= TokenFlags.Unterminated; - } - return token = SyntaxKind.MultiLineCommentTrivia; - } - } - - if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) { - return pos += 2, token = SyntaxKind.SlashEqualsToken; - } - - pos++; - return token = SyntaxKind.SlashToken; - - case CharacterCodes._0: - if (pos + 2 < end && (charCodeUnchecked(pos + 1) === CharacterCodes.X || charCodeUnchecked(pos + 1) === CharacterCodes.x)) { - pos += 2; - tokenValue = scanMinimumNumberOfHexDigits(1, /*canHaveSeparators*/ true); - if (!tokenValue) { - tokenValue = "0"; - } - tokenValue = "0x" + tokenValue; - tokenFlags |= TokenFlags.HexSpecifier; - return token = checkBigIntSuffix(); - } - else if (pos + 2 < end && (charCodeUnchecked(pos + 1) === CharacterCodes.B || charCodeUnchecked(pos + 1) === CharacterCodes.b)) { - pos += 2; - tokenValue = scanBinaryOrOctalDigits(2); - if (!tokenValue) { - tokenValue = "0"; - } - tokenValue = "0b" + tokenValue; - tokenFlags |= TokenFlags.BinarySpecifier; - return token = checkBigIntSuffix(); - } - else if (pos + 2 < end && (charCodeUnchecked(pos + 1) === CharacterCodes.O || charCodeUnchecked(pos + 1) === CharacterCodes.o)) { - pos += 2; - tokenValue = scanBinaryOrOctalDigits(8); - if (!tokenValue) { - tokenValue = "0"; - } - tokenValue = "0o" + tokenValue; - tokenFlags |= TokenFlags.OctalSpecifier; - return token = checkBigIntSuffix(); - } - // falls through - case CharacterCodes._1: - case CharacterCodes._2: - case CharacterCodes._3: - case CharacterCodes._4: - case CharacterCodes._5: - case CharacterCodes._6: - case CharacterCodes._7: - case CharacterCodes._8: - case CharacterCodes._9: - return token = scanNumber(); - case CharacterCodes.colon: - pos++; - return token = SyntaxKind.ColonToken; - case CharacterCodes.semicolon: - pos++; - return token = SyntaxKind.SemicolonToken; - case CharacterCodes.lessThan: - if (isConflictMarkerTrivia(text, pos)) { - pos = scanConflictMarkerTrivia(text, pos); - if (skipTrivia) { - continue; - } - else { - return token = SyntaxKind.ConflictMarkerTrivia; - } - } - - if (charCodeUnchecked(pos + 1) === CharacterCodes.lessThan) { - if (charCodeUnchecked(pos + 2) === CharacterCodes.equals) { - return pos += 3, token = SyntaxKind.LessThanLessThanEqualsToken; - } - return pos += 2, token = SyntaxKind.LessThanLessThanToken; - } - if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) { - return pos += 2, token = SyntaxKind.LessThanEqualsToken; - } - if ( - languageVariant === LanguageVariant.JSX && - charCodeUnchecked(pos + 1) === CharacterCodes.slash && - charCodeUnchecked(pos + 2) !== CharacterCodes.asterisk - ) { - return pos += 2, token = SyntaxKind.LessThanSlashToken; - } - pos++; - return token = SyntaxKind.LessThanToken; - case CharacterCodes.equals: - if (isConflictMarkerTrivia(text, pos)) { - pos = scanConflictMarkerTrivia(text, pos); - if (skipTrivia) { - continue; - } - else { - return token = SyntaxKind.ConflictMarkerTrivia; - } - } - - if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) { - if (charCodeUnchecked(pos + 2) === CharacterCodes.equals) { - return pos += 3, token = SyntaxKind.EqualsEqualsEqualsToken; - } - return pos += 2, token = SyntaxKind.EqualsEqualsToken; - } - if (charCodeUnchecked(pos + 1) === CharacterCodes.greaterThan) { - return pos += 2, token = SyntaxKind.EqualsGreaterThanToken; - } - pos++; - return token = SyntaxKind.EqualsToken; - case CharacterCodes.greaterThan: - if (isConflictMarkerTrivia(text, pos)) { - pos = scanConflictMarkerTrivia(text, pos); - if (skipTrivia) { - continue; - } - else { - return token = SyntaxKind.ConflictMarkerTrivia; - } - } - - pos++; - return token = SyntaxKind.GreaterThanToken; - case CharacterCodes.question: - if (charCodeUnchecked(pos + 1) === CharacterCodes.dot && !isDigit(charCodeUnchecked(pos + 2))) { - return pos += 2, token = SyntaxKind.QuestionDotToken; - } - if (charCodeUnchecked(pos + 1) === CharacterCodes.question) { - if (charCodeUnchecked(pos + 2) === CharacterCodes.equals) { - return pos += 3, token = SyntaxKind.QuestionQuestionEqualsToken; - } - return pos += 2, token = SyntaxKind.QuestionQuestionToken; - } - pos++; - return token = SyntaxKind.QuestionToken; - case CharacterCodes.openBracket: - pos++; - return token = SyntaxKind.OpenBracketToken; - case CharacterCodes.closeBracket: - pos++; - return token = SyntaxKind.CloseBracketToken; - case CharacterCodes.caret: - if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) { - return pos += 2, token = SyntaxKind.CaretEqualsToken; - } - pos++; - return token = SyntaxKind.CaretToken; - case CharacterCodes.openBrace: - pos++; - return token = SyntaxKind.OpenBraceToken; - case CharacterCodes.bar: - if (isConflictMarkerTrivia(text, pos)) { - pos = scanConflictMarkerTrivia(text, pos); - if (skipTrivia) { - continue; - } - else { - return token = SyntaxKind.ConflictMarkerTrivia; - } - } - - if (charCodeUnchecked(pos + 1) === CharacterCodes.bar) { - if (charCodeUnchecked(pos + 2) === CharacterCodes.equals) { - return pos += 3, token = SyntaxKind.BarBarEqualsToken; - } - return pos += 2, token = SyntaxKind.BarBarToken; - } - if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) { - return pos += 2, token = SyntaxKind.BarEqualsToken; - } - pos++; - return token = SyntaxKind.BarToken; - case CharacterCodes.closeBrace: - pos++; - return token = SyntaxKind.CloseBraceToken; - case CharacterCodes.tilde: - pos++; - return token = SyntaxKind.TildeToken; - case CharacterCodes.at: - pos++; - return token = SyntaxKind.AtToken; - case CharacterCodes.backslash: { - const extendedCookedChar = peekExtendedUnicodeEscape(); - if (extendedCookedChar >= 0 && isIdentifierStart(extendedCookedChar)) { - tokenValue = scanExtendedUnicodeEscape() + scanIdentifierParts(); - return token = getIdentifierToken(); - } - - const cookedChar = peekUnicodeEscape(); - if (cookedChar >= 0 && isIdentifierStart(cookedChar)) { - pos += 6; - tokenFlags |= TokenFlags.UnicodeEscape; - tokenValue = String.fromCharCode(cookedChar) + scanIdentifierParts(); - return token = getIdentifierToken(); - } - - pos++; - return token = SyntaxKind.Unknown; - } - case CharacterCodes.hash: - if (pos !== 0 && text[pos + 1] === "!") { - pos++; - return token = SyntaxKind.Unknown; - } - - { - const charAfterHash = codePointUnchecked(pos + 1); - if (charAfterHash === CharacterCodes.backslash) { - pos++; - const extendedCookedChar = peekExtendedUnicodeEscape(); - if (extendedCookedChar >= 0 && isIdentifierStart(extendedCookedChar)) { - tokenValue = "#" + scanExtendedUnicodeEscape() + scanIdentifierParts(); - return token = SyntaxKind.PrivateIdentifier; - } - - const cookedChar = peekUnicodeEscape(); - if (cookedChar >= 0 && isIdentifierStart(cookedChar)) { - pos += 6; - tokenFlags |= TokenFlags.UnicodeEscape; - tokenValue = "#" + String.fromCharCode(cookedChar) + scanIdentifierParts(); - return token = SyntaxKind.PrivateIdentifier; - } - pos--; - } - - if (isIdentifierStart(charAfterHash)) { - pos++; - scanIdentifier(charAfterHash); - } - else { - tokenValue = "#"; - } - return token = SyntaxKind.PrivateIdentifier; - } - case CharacterCodes.replacementCharacter: - pos = end; - return token = SyntaxKind.NonTextFileMarkerTrivia; - default: { - const identifierKind = scanIdentifier(ch); - if (identifierKind) { - return token = identifierKind; - } - else if (isWhiteSpaceSingleLine(ch)) { - pos += charSize(ch); - continue; - } - else if (isLineBreak(ch)) { - tokenFlags |= TokenFlags.PrecedingLineBreak; - pos += charSize(ch); - continue; - } - const size = charSize(ch); - pos += size; - return token = SyntaxKind.Unknown; - } - } - } - } - - function reScanInvalidIdentifier(): SyntaxKind { - pos = tokenStart = fullStartPos; - tokenFlags = 0; - const ch = codePointUnchecked(pos); - const identifierKind = scanIdentifier(ch); - if (identifierKind) { - return token = identifierKind; - } - pos += charSize(ch); - return token; // Still `SyntaxKind.Unknown` - } - - function scanIdentifier(startCharacter: number) { - let ch = startCharacter; - if (isIdentifierStart(ch)) { - pos += charSize(ch); - while (pos < end && isIdentifierPart(ch = codePointUnchecked(pos))) pos += charSize(ch); - tokenValue = text.substring(tokenStart, pos); - if (ch === CharacterCodes.backslash) { - tokenValue += scanIdentifierParts(); - } - return getIdentifierToken(); - } - } - - function reScanGreaterToken(): SyntaxKind { - if (token === SyntaxKind.GreaterThanToken) { - if (charCodeUnchecked(pos) === CharacterCodes.greaterThan) { - if (charCodeUnchecked(pos + 1) === CharacterCodes.greaterThan) { - if (charCodeUnchecked(pos + 2) === CharacterCodes.equals) { - return pos += 3, token = SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken; - } - return pos += 2, token = SyntaxKind.GreaterThanGreaterThanGreaterThanToken; - } - if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) { - return pos += 2, token = SyntaxKind.GreaterThanGreaterThanEqualsToken; - } - pos++; - return token = SyntaxKind.GreaterThanGreaterThanToken; - } - if (charCodeUnchecked(pos) === CharacterCodes.equals) { - pos++; - return token = SyntaxKind.GreaterThanEqualsToken; - } - } - return token; - } - - function reScanAsteriskEqualsToken(): SyntaxKind { - pos = tokenStart + 1; - return token = SyntaxKind.EqualsToken; - } - - function reScanSlashToken(): SyntaxKind { - if (token === SyntaxKind.SlashToken || token === SyntaxKind.SlashEqualsToken) { - const startOfRegExpBody = tokenStart + 1; - pos = startOfRegExpBody; - let inEscape = false; - let inCharacterClass = false; - while (true) { - const ch = charCodeChecked(pos); - if (ch === CharacterCodes.EOF || isLineBreak(ch)) { - tokenFlags |= TokenFlags.Unterminated; - break; - } - - if (inEscape) { - inEscape = false; - } - else if (ch === CharacterCodes.slash && !inCharacterClass) { - break; - } - else if (ch === CharacterCodes.openBracket) { - inCharacterClass = true; - } - else if (ch === CharacterCodes.backslash) { - inEscape = true; - } - else if (ch === CharacterCodes.closeBracket) { - inCharacterClass = false; - } - pos++; - } - - if (tokenFlags & TokenFlags.Unterminated) { - // Find best recovery position - pos = startOfRegExpBody; - inEscape = false; - let characterClassDepth = 0; - let inDecimalQuantifier = false; - let groupDepth = 0; - while (pos < end && !isLineBreak(charCodeUnchecked(pos))) { - const ch = charCodeUnchecked(pos); - if (inEscape) { - inEscape = false; - } - else if (ch === CharacterCodes.backslash) { - inEscape = true; - } - else if (ch === CharacterCodes.openBracket) { - characterClassDepth++; - } - else if (ch === CharacterCodes.closeBracket && characterClassDepth) { - characterClassDepth--; - } - else if (!characterClassDepth) { - if (ch === CharacterCodes.openBrace) { - inDecimalQuantifier = true; - } - else if (ch === CharacterCodes.closeBrace && inDecimalQuantifier) { - inDecimalQuantifier = false; - } - else if (!inDecimalQuantifier) { - if (ch === CharacterCodes.openParen) { - groupDepth++; - } - else if (ch === CharacterCodes.closeParen && groupDepth) { - groupDepth--; - } - else if (ch === CharacterCodes.closeParen || ch === CharacterCodes.closeBracket || ch === CharacterCodes.closeBrace) { - break; - } - } - } - pos++; - } - while (isWhiteSpaceLike(charCodeChecked(pos - 1)) || charCodeChecked(pos - 1) === CharacterCodes.semicolon) pos--; - } - else { - pos++; - let regExpFlags: number = RegularExpressionFlags.None; - while (true) { - const ch = codePointChecked(pos); - if (ch === CharacterCodes.EOF || !isIdentifierPart(ch)) { - break; - } - const size = charSize(ch); - const flag = characterCodeToRegularExpressionFlag(ch); - if (flag !== undefined) { - regExpFlags |= flag; - } - pos += size; - } - } - tokenValue = text.substring(tokenStart, pos); - token = SyntaxKind.RegularExpressionLiteral; - } - return token; - } - - function appendIfCommentDirective( - commentDirectives: CommentDirective[] | undefined, - text: string, - commentDirectiveRegEx: RegExp, - lineStart: number, - ) { - const type = getDirectiveFromComment(text.trimStart(), commentDirectiveRegEx); - if (type === undefined) { - return commentDirectives; - } - - if (!commentDirectives) { - commentDirectives = []; - } - commentDirectives.push({ - range: { pos: lineStart, end: pos }, - type, - }); - return commentDirectives; - } - - function getDirectiveFromComment(text: string, commentDirectiveRegEx: RegExp) { - const match = commentDirectiveRegEx.exec(text); - if (!match) { - return undefined; - } - - switch (match[1]) { - case "ts-expect-error": - return CommentDirectiveType.ExpectError; - case "ts-ignore": - return CommentDirectiveType.Ignore; - } - - return undefined; - } - - function reScanTemplateToken(isTaggedTemplate: boolean): SyntaxKind { - pos = tokenStart; - return token = scanTemplateAndSetTokenValue(!isTaggedTemplate); - } - - function reScanTemplateHeadOrNoSubstitutionTemplate(): SyntaxKind { - pos = tokenStart; - return token = scanTemplateAndSetTokenValue(/*shouldEmitInvalidEscapeError*/ true); - } - - function reScanJsxToken(allowMultilineJsxText = true): JsxTokenSyntaxKind { - pos = tokenStart = fullStartPos; - return token = scanJsxToken(allowMultilineJsxText); - } - - function reScanLessThanToken(): SyntaxKind { - if (token === SyntaxKind.LessThanLessThanToken) { - pos = tokenStart + 1; - return token = SyntaxKind.LessThanToken; - } - return token; - } - - function reScanHashToken(): SyntaxKind { - if (token === SyntaxKind.PrivateIdentifier) { - pos = tokenStart + 1; - return token = SyntaxKind.HashToken; - } - return token; - } - - function reScanQuestionToken(): SyntaxKind { - pos = tokenStart + 1; - return token = SyntaxKind.QuestionToken; - } - - function scanJsxToken(allowMultilineJsxText = true): JsxTokenSyntaxKind { - fullStartPos = tokenStart = pos; - - if (pos >= end) { - return token = SyntaxKind.EndOfFile; - } - - let char = charCodeUnchecked(pos); - if (char === CharacterCodes.lessThan) { - if (charCodeUnchecked(pos + 1) === CharacterCodes.slash) { - pos += 2; - return token = SyntaxKind.LessThanSlashToken; - } - pos++; - return token = SyntaxKind.LessThanToken; - } - - if (char === CharacterCodes.openBrace) { - pos++; - return token = SyntaxKind.OpenBraceToken; - } - - let firstNonWhitespace = 0; - - while (pos < end) { - char = charCodeUnchecked(pos); - if (char === CharacterCodes.openBrace) { - break; - } - if (char === CharacterCodes.lessThan) { - if (isConflictMarkerTrivia(text, pos)) { - pos = scanConflictMarkerTrivia(text, pos); - return token = SyntaxKind.ConflictMarkerTrivia; - } - break; - } - - if (isLineBreak(char) && firstNonWhitespace === 0) { - firstNonWhitespace = -1; - } - else if (!allowMultilineJsxText && isLineBreak(char) && firstNonWhitespace > 0) { - break; - } - else if (!isWhiteSpaceLike(char)) { - firstNonWhitespace = pos; - } - - pos++; - } - - tokenValue = text.substring(fullStartPos, pos); - - return firstNonWhitespace === -1 ? SyntaxKind.JsxTextAllWhiteSpaces : SyntaxKind.JsxText; - } - - function scanJsxIdentifier(): SyntaxKind { - if (tokenIsIdentifierOrKeyword(token)) { - while (pos < end) { - const ch = charCodeUnchecked(pos); - if (ch === CharacterCodes.minus) { - tokenValue += "-"; - pos++; - continue; - } - const oldPos = pos; - tokenValue += scanIdentifierParts(); - if (pos === oldPos) { - break; - } - } - return getIdentifierToken(); - } - return token; - } - - function scanJsxAttributeValue(): SyntaxKind { - fullStartPos = pos; - - switch (charCodeUnchecked(pos)) { - case CharacterCodes.doubleQuote: - case CharacterCodes.singleQuote: - tokenValue = scanString(/*jsxAttributeString*/ true); - return token = SyntaxKind.StringLiteral; - default: - return scan(); - } - } - - function reScanJsxAttributeValue(): SyntaxKind { - pos = tokenStart = fullStartPos; - return scanJsxAttributeValue(); - } - - function scanJSDocCommentTextToken(inBackticks: boolean): JSDocTokenKind | SyntaxKind.JSDocCommentTextToken { - fullStartPos = tokenStart = pos; - tokenFlags = TokenFlags.None; - if (pos >= end) { - return token = SyntaxKind.EndOfFile; - } - for (let ch = charCodeUnchecked(pos); pos < end && (!isLineBreak(ch) && ch !== CharacterCodes.backtick); ch = codePointUnchecked(++pos)) { - if (!inBackticks) { - if (ch === CharacterCodes.openBrace) { - break; - } - else if ( - ch === CharacterCodes.at - && pos - 1 >= 0 && isWhiteSpaceSingleLine(charCodeUnchecked(pos - 1)) - && pos + 1 < end && isIdentifierStart(charCodeUnchecked(pos + 1)) - ) { - break; - } - } - } - if (pos === tokenStart) { - return scanJsDocToken(); - } - tokenValue = text.substring(tokenStart, pos); - return token = SyntaxKind.JSDocCommentTextToken; - } - - function scanJsDocToken(): JSDocTokenKind { - fullStartPos = tokenStart = pos; - tokenFlags = TokenFlags.None; - if (pos >= end) { - return token = SyntaxKind.EndOfFile; - } - - const ch = codePointUnchecked(pos); - pos += charSize(ch); - switch (ch) { - case CharacterCodes.tab: - case CharacterCodes.verticalTab: - case CharacterCodes.formFeed: - case CharacterCodes.space: - while (pos < end && isWhiteSpaceSingleLine(charCodeUnchecked(pos))) { - pos++; - } - return token = SyntaxKind.WhitespaceTrivia; - case CharacterCodes.at: - return token = SyntaxKind.AtToken; - case CharacterCodes.carriageReturn: - if (charCodeUnchecked(pos) === CharacterCodes.lineFeed) { - pos++; - } - // falls through - case CharacterCodes.lineFeed: - tokenFlags |= TokenFlags.PrecedingLineBreak; - return token = SyntaxKind.NewLineTrivia; - case CharacterCodes.asterisk: - return token = SyntaxKind.AsteriskToken; - case CharacterCodes.openBrace: - return token = SyntaxKind.OpenBraceToken; - case CharacterCodes.closeBrace: - return token = SyntaxKind.CloseBraceToken; - case CharacterCodes.openBracket: - return token = SyntaxKind.OpenBracketToken; - case CharacterCodes.closeBracket: - return token = SyntaxKind.CloseBracketToken; - case CharacterCodes.openParen: - return token = SyntaxKind.OpenParenToken; - case CharacterCodes.closeParen: - return token = SyntaxKind.CloseParenToken; - case CharacterCodes.lessThan: - return token = SyntaxKind.LessThanToken; - case CharacterCodes.greaterThan: - return token = SyntaxKind.GreaterThanToken; - case CharacterCodes.equals: - return token = SyntaxKind.EqualsToken; - case CharacterCodes.comma: - return token = SyntaxKind.CommaToken; - case CharacterCodes.dot: - return token = SyntaxKind.DotToken; - case CharacterCodes.backtick: - return token = SyntaxKind.BacktickToken; - case CharacterCodes.hash: - return token = SyntaxKind.HashToken; - case CharacterCodes.backslash: - pos--; - { - const extendedCookedChar = peekExtendedUnicodeEscape(); - if (extendedCookedChar >= 0 && isIdentifierStart(extendedCookedChar)) { - tokenValue = scanExtendedUnicodeEscape() + scanIdentifierParts(); - return token = getIdentifierToken(); - } - - const cookedChar = peekUnicodeEscape(); - if (cookedChar >= 0 && isIdentifierStart(cookedChar)) { - pos += 6; - tokenFlags |= TokenFlags.UnicodeEscape; - tokenValue = String.fromCharCode(cookedChar) + scanIdentifierParts(); - return token = getIdentifierToken(); - } - } - pos++; - return token = SyntaxKind.Unknown; - } - - if (isIdentifierStart(ch)) { - let char = ch; - while (pos < end && isIdentifierPart(char = codePointUnchecked(pos)) || char === CharacterCodes.minus) pos += charSize(char); - tokenValue = text.substring(tokenStart, pos); - if (char === CharacterCodes.backslash) { - tokenValue += scanIdentifierParts(); - } - return token = getIdentifierToken(); - } - else { - return token = SyntaxKind.Unknown; - } - } - - function speculationHelper(callback: () => T, isLookahead: boolean): T { - const savePos = pos; - const saveStartPos = fullStartPos; - const saveTokenPos = tokenStart; - const saveToken = token; - const saveTokenValue = tokenValue; - const saveTokenFlags = tokenFlags; - const result = callback(); - - if (!result || isLookahead) { - pos = savePos; - fullStartPos = saveStartPos; - tokenStart = saveTokenPos; - token = saveToken; - tokenValue = saveTokenValue; - tokenFlags = saveTokenFlags; - } - return result; - } - - function scanRange(start: number, length: number, callback: () => T): T { - const saveEnd = end; - const savePos = pos; - const saveStartPos = fullStartPos; - const saveTokenPos = tokenStart; - const saveToken = token; - const saveTokenValue = tokenValue; - const saveTokenFlags = tokenFlags; - const saveErrorExpectations = commentDirectives; - - setText(text, start, length); - const result = callback(); - - end = saveEnd; - pos = savePos; - fullStartPos = saveStartPos; - tokenStart = saveTokenPos; - token = saveToken; - tokenValue = saveTokenValue; - tokenFlags = saveTokenFlags; - commentDirectives = saveErrorExpectations; - - return result; - } - - function lookAhead(callback: () => T): T { - return speculationHelper(callback, /*isLookahead*/ true); - } - - function tryScan(callback: () => T): T { - return speculationHelper(callback, /*isLookahead*/ false); - } - - function getText(): string { - return text; - } - - function clearCommentDirectives() { - commentDirectives = undefined; - } - - function setText(newText: string | undefined, start: number | undefined, length: number | undefined) { - text = newText || ""; - end = length === undefined ? text.length : start! + length; - resetTokenState(start || 0); - } - - function setLanguageVariant(variant: LanguageVariant) { - languageVariant = variant; - } - - function resetTokenState(position: number) { - pos = position; - fullStartPos = position; - tokenStart = position; - token = SyntaxKind.Unknown; - tokenValue = undefined!; - tokenFlags = TokenFlags.None; - } - - function setSkipJsDocLeadingAsterisks(skip: boolean) { - skipJsDocLeadingAsterisks += skip ? 1 : -1; - } -} +import { CharacterCodes } from "#enums/characterCodes"; +import { CommentDirectiveType } from "#enums/commentDirectiveType"; +import { LanguageVariant } from "#enums/languageVariant"; +import { RegularExpressionFlags } from "#enums/regularExpressionFlags"; +import { ScriptTarget } from "#enums/scriptTarget"; +import { SyntaxKind } from "#enums/syntaxKind"; +import { TokenFlags } from "#enums/tokenFlags"; +import type { + JsxTokenSyntaxKind, + KeywordSyntaxKind, +} from "./ast.ts"; + +export type JSDocTokenKind = SyntaxKind.EndOfFile | SyntaxKind.WhitespaceTrivia | SyntaxKind.AtToken | SyntaxKind.NewLineTrivia | SyntaxKind.AsteriskToken | SyntaxKind.OpenBraceToken | SyntaxKind.CloseBraceToken | SyntaxKind.LessThanToken | SyntaxKind.GreaterThanToken | SyntaxKind.OpenBracketToken | SyntaxKind.CloseBracketToken | SyntaxKind.OpenParenToken | SyntaxKind.CloseParenToken | SyntaxKind.EqualsToken | SyntaxKind.CommaToken | SyntaxKind.DotToken | SyntaxKind.Identifier | SyntaxKind.BacktickToken | SyntaxKind.HashToken | SyntaxKind.Unknown | KeywordSyntaxKind; + +export interface CommentDirective { + range: { pos: number; end: number; }; + type: CommentDirectiveType; +} + +// Internal-only, not exported +const EscapeSequenceScanningFlags = { + String: 1 << 0, + ReportErrors: 1 << 1, + RegularExpression: 1 << 2, + AnnexB: 1 << 3, + AnyUnicodeMode: 1 << 4, + AtomEscape: 1 << 5, + ReportInvalidEscapeErrors: (1 << 2) | (1 << 1), + AllowExtendedUnicodeEscape: (1 << 0) | (1 << 4), +} as const; +type EscapeSequenceScanningFlags = typeof EscapeSequenceScanningFlags[keyof typeof EscapeSequenceScanningFlags]; + +export function tokenIsIdentifierOrKeyword(token: SyntaxKind): boolean { + return token >= SyntaxKind.Identifier; +} + +export function tokenIsIdentifierOrKeywordOrGreaterThan(token: SyntaxKind): boolean { + return token === SyntaxKind.GreaterThanToken || tokenIsIdentifierOrKeyword(token); +} + +export interface Scanner { + getTokenFullStart(): number; + getToken(): SyntaxKind; + getTokenStart(): number; + getTokenEnd(): number; + getTokenText(): string; + getTokenValue(): string; + hasUnicodeEscape(): boolean; + hasExtendedUnicodeEscape(): boolean; + hasPrecedingLineBreak(): boolean; + hasPrecedingJSDocComment(): boolean; + hasPrecedingJSDocLeadingAsterisks(): boolean; + hasPrecedingJSDocWithDeprecatedTag(): boolean; + hasPrecedingJSDocWithSeeOrLink(): boolean; + isIdentifier(): boolean; + isReservedWord(): boolean; + isUnterminated(): boolean; + getNumericLiteralFlags(): TokenFlags; + getCommentDirectives(): CommentDirective[] | undefined; + getTokenFlags(): TokenFlags; + reScanGreaterToken(): SyntaxKind; + reScanSlashToken(): SyntaxKind; + reScanAsteriskEqualsToken(): SyntaxKind; + reScanTemplateToken(isTaggedTemplate: boolean): SyntaxKind; + reScanTemplateHeadOrNoSubstitutionTemplate(): SyntaxKind; + scanJsxIdentifier(): SyntaxKind; + scanJsxAttributeValue(): SyntaxKind; + reScanJsxAttributeValue(): SyntaxKind; + reScanJsxToken(allowMultilineJsxText?: boolean): JsxTokenSyntaxKind; + reScanLessThanToken(): SyntaxKind; + reScanHashToken(): SyntaxKind; + reScanQuestionToken(): SyntaxKind; + reScanInvalidIdentifier(): SyntaxKind; + scanJsxToken(): JsxTokenSyntaxKind; + scanJsDocToken(): JSDocTokenKind; + scanJSDocCommentTextToken(inBackticks: boolean): JSDocTokenKind | SyntaxKind.JSDocCommentTextToken; + scan(): SyntaxKind; + + getText(): string; + clearCommentDirectives(): void; + setText(text: string | undefined, start?: number, length?: number): void; + setLanguageVariant(variant: LanguageVariant): void; + resetTokenState(pos: number): void; + setSkipJsDocLeadingAsterisks(skip: boolean): void; + lookAhead(callback: () => T): T; + scanRange(start: number, length: number, callback: () => T): T; + tryScan(callback: () => T): T; +} + +export const textToKeywordObj: Record = { + abstract: SyntaxKind.AbstractKeyword, + accessor: SyntaxKind.AccessorKeyword, + any: SyntaxKind.AnyKeyword, + as: SyntaxKind.AsKeyword, + asserts: SyntaxKind.AssertsKeyword, + assert: SyntaxKind.AssertKeyword, + bigint: SyntaxKind.BigIntKeyword, + boolean: SyntaxKind.BooleanKeyword, + break: SyntaxKind.BreakKeyword, + case: SyntaxKind.CaseKeyword, + catch: SyntaxKind.CatchKeyword, + class: SyntaxKind.ClassKeyword, + continue: SyntaxKind.ContinueKeyword, + const: SyntaxKind.ConstKeyword, + ["" + "constructor"]: SyntaxKind.ConstructorKeyword, + debugger: SyntaxKind.DebuggerKeyword, + declare: SyntaxKind.DeclareKeyword, + default: SyntaxKind.DefaultKeyword, + defer: SyntaxKind.DeferKeyword, + delete: SyntaxKind.DeleteKeyword, + do: SyntaxKind.DoKeyword, + else: SyntaxKind.ElseKeyword, + enum: SyntaxKind.EnumKeyword, + export: SyntaxKind.ExportKeyword, + extends: SyntaxKind.ExtendsKeyword, + false: SyntaxKind.FalseKeyword, + finally: SyntaxKind.FinallyKeyword, + for: SyntaxKind.ForKeyword, + from: SyntaxKind.FromKeyword, + function: SyntaxKind.FunctionKeyword, + get: SyntaxKind.GetKeyword, + if: SyntaxKind.IfKeyword, + implements: SyntaxKind.ImplementsKeyword, + import: SyntaxKind.ImportKeyword, + in: SyntaxKind.InKeyword, + infer: SyntaxKind.InferKeyword, + instanceof: SyntaxKind.InstanceOfKeyword, + interface: SyntaxKind.InterfaceKeyword, + intrinsic: SyntaxKind.IntrinsicKeyword, + is: SyntaxKind.IsKeyword, + keyof: SyntaxKind.KeyOfKeyword, + let: SyntaxKind.LetKeyword, + module: SyntaxKind.ModuleKeyword, + namespace: SyntaxKind.NamespaceKeyword, + never: SyntaxKind.NeverKeyword, + new: SyntaxKind.NewKeyword, + null: SyntaxKind.NullKeyword, + number: SyntaxKind.NumberKeyword, + object: SyntaxKind.ObjectKeyword, + package: SyntaxKind.PackageKeyword, + private: SyntaxKind.PrivateKeyword, + protected: SyntaxKind.ProtectedKeyword, + public: SyntaxKind.PublicKeyword, + override: SyntaxKind.OverrideKeyword, + out: SyntaxKind.OutKeyword, + readonly: SyntaxKind.ReadonlyKeyword, + require: SyntaxKind.RequireKeyword, + global: SyntaxKind.GlobalKeyword, + return: SyntaxKind.ReturnKeyword, + satisfies: SyntaxKind.SatisfiesKeyword, + set: SyntaxKind.SetKeyword, + static: SyntaxKind.StaticKeyword, + string: SyntaxKind.StringKeyword, + super: SyntaxKind.SuperKeyword, + switch: SyntaxKind.SwitchKeyword, + symbol: SyntaxKind.SymbolKeyword, + this: SyntaxKind.ThisKeyword, + throw: SyntaxKind.ThrowKeyword, + true: SyntaxKind.TrueKeyword, + try: SyntaxKind.TryKeyword, + type: SyntaxKind.TypeKeyword, + typeof: SyntaxKind.TypeOfKeyword, + undefined: SyntaxKind.UndefinedKeyword, + unique: SyntaxKind.UniqueKeyword, + unknown: SyntaxKind.UnknownKeyword, + using: SyntaxKind.UsingKeyword, + var: SyntaxKind.VarKeyword, + void: SyntaxKind.VoidKeyword, + while: SyntaxKind.WhileKeyword, + with: SyntaxKind.WithKeyword, + yield: SyntaxKind.YieldKeyword, + async: SyntaxKind.AsyncKeyword, + await: SyntaxKind.AwaitKeyword, + of: SyntaxKind.OfKeyword, +}; + +const textToKeyword = new Map(Object.entries(textToKeywordObj)); + +const textToToken = new Map(Object.entries({ + ...textToKeywordObj, + "{": SyntaxKind.OpenBraceToken, + "}": SyntaxKind.CloseBraceToken, + "(": SyntaxKind.OpenParenToken, + ")": SyntaxKind.CloseParenToken, + "[": SyntaxKind.OpenBracketToken, + "]": SyntaxKind.CloseBracketToken, + ".": SyntaxKind.DotToken, + "...": SyntaxKind.DotDotDotToken, + ";": SyntaxKind.SemicolonToken, + ",": SyntaxKind.CommaToken, + "<": SyntaxKind.LessThanToken, + ">": SyntaxKind.GreaterThanToken, + "<=": SyntaxKind.LessThanEqualsToken, + ">=": SyntaxKind.GreaterThanEqualsToken, + "==": SyntaxKind.EqualsEqualsToken, + "!=": SyntaxKind.ExclamationEqualsToken, + "===": SyntaxKind.EqualsEqualsEqualsToken, + "!==": SyntaxKind.ExclamationEqualsEqualsToken, + "=>": SyntaxKind.EqualsGreaterThanToken, + "+": SyntaxKind.PlusToken, + "-": SyntaxKind.MinusToken, + "**": SyntaxKind.AsteriskAsteriskToken, + "*": SyntaxKind.AsteriskToken, + "/": SyntaxKind.SlashToken, + "%": SyntaxKind.PercentToken, + "++": SyntaxKind.PlusPlusToken, + "--": SyntaxKind.MinusMinusToken, + "<<": SyntaxKind.LessThanLessThanToken, + ">": SyntaxKind.GreaterThanGreaterThanToken, + ">>>": SyntaxKind.GreaterThanGreaterThanGreaterThanToken, + "&": SyntaxKind.AmpersandToken, + "|": SyntaxKind.BarToken, + "^": SyntaxKind.CaretToken, + "!": SyntaxKind.ExclamationToken, + "~": SyntaxKind.TildeToken, + "&&": SyntaxKind.AmpersandAmpersandToken, + "||": SyntaxKind.BarBarToken, + "?": SyntaxKind.QuestionToken, + "??": SyntaxKind.QuestionQuestionToken, + "?.": SyntaxKind.QuestionDotToken, + ":": SyntaxKind.ColonToken, + "=": SyntaxKind.EqualsToken, + "+=": SyntaxKind.PlusEqualsToken, + "-=": SyntaxKind.MinusEqualsToken, + "*=": SyntaxKind.AsteriskEqualsToken, + "**=": SyntaxKind.AsteriskAsteriskEqualsToken, + "/=": SyntaxKind.SlashEqualsToken, + "%=": SyntaxKind.PercentEqualsToken, + "<<=": SyntaxKind.LessThanLessThanEqualsToken, + ">>=": SyntaxKind.GreaterThanGreaterThanEqualsToken, + ">>>=": SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken, + "&=": SyntaxKind.AmpersandEqualsToken, + "|=": SyntaxKind.BarEqualsToken, + "^=": SyntaxKind.CaretEqualsToken, + "||=": SyntaxKind.BarBarEqualsToken, + "&&=": SyntaxKind.AmpersandAmpersandEqualsToken, + "??=": SyntaxKind.QuestionQuestionEqualsToken, + "@": SyntaxKind.AtToken, + "#": SyntaxKind.HashToken, + "`": SyntaxKind.BacktickToken, +} as Record)); + +const charCodeToRegExpFlag = new Map([ + [CharacterCodes.d, RegularExpressionFlags.HasIndices], + [CharacterCodes.g, RegularExpressionFlags.Global], + [CharacterCodes.i, RegularExpressionFlags.IgnoreCase], + [CharacterCodes.m, RegularExpressionFlags.Multiline], + [CharacterCodes.s, RegularExpressionFlags.DotAll], + [CharacterCodes.u, RegularExpressionFlags.Unicode], + [CharacterCodes.v, RegularExpressionFlags.UnicodeSets], + [CharacterCodes.y, RegularExpressionFlags.Sticky], +]); + +/** + * Generated by scripts/regenerate-unicode-identifier-parts.mjs on node v22.1.0 with unicode 15.1 + * based on http://www.unicode.org/reports/tr31/ and https://www.ecma-international.org/ecma-262/6.0/#sec-names-and-keywords + * unicodeESNextIdentifierStart corresponds to the ID_Start and Other_ID_Start property, and + * unicodeESNextIdentifierPart corresponds to ID_Continue, Other_ID_Continue, plus ID_Start and Other_ID_Start + */ +// dprint-ignore +const unicodeESNextIdentifierStart = [65, 90, 97, 122, 170, 170, 181, 181, 186, 186, 192, 214, 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750, 880, 884, 886, 887, 890, 893, 895, 895, 902, 902, 904, 906, 908, 908, 910, 929, 931, 1013, 1015, 1153, 1162, 1327, 1329, 1366, 1369, 1369, 1376, 1416, 1488, 1514, 1519, 1522, 1568, 1610, 1646, 1647, 1649, 1747, 1749, 1749, 1765, 1766, 1774, 1775, 1786, 1788, 1791, 1791, 1808, 1808, 1810, 1839, 1869, 1957, 1969, 1969, 1994, 2026, 2036, 2037, 2042, 2042, 2048, 2069, 2074, 2074, 2084, 2084, 2088, 2088, 2112, 2136, 2144, 2154, 2160, 2183, 2185, 2190, 2208, 2249, 2308, 2361, 2365, 2365, 2384, 2384, 2392, 2401, 2417, 2432, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2493, 2493, 2510, 2510, 2524, 2525, 2527, 2529, 2544, 2545, 2556, 2556, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2649, 2652, 2654, 2654, 2674, 2676, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2749, 2749, 2768, 2768, 2784, 2785, 2809, 2809, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2869, 2873, 2877, 2877, 2908, 2909, 2911, 2913, 2929, 2929, 2947, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, 3024, 3024, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3129, 3133, 3133, 3160, 3162, 3165, 3165, 3168, 3169, 3200, 3200, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3261, 3261, 3293, 3294, 3296, 3297, 3313, 3314, 3332, 3340, 3342, 3344, 3346, 3386, 3389, 3389, 3406, 3406, 3412, 3414, 3423, 3425, 3450, 3455, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3585, 3632, 3634, 3635, 3648, 3654, 3713, 3714, 3716, 3716, 3718, 3722, 3724, 3747, 3749, 3749, 3751, 3760, 3762, 3763, 3773, 3773, 3776, 3780, 3782, 3782, 3804, 3807, 3840, 3840, 3904, 3911, 3913, 3948, 3976, 3980, 4096, 4138, 4159, 4159, 4176, 4181, 4186, 4189, 4193, 4193, 4197, 4198, 4206, 4208, 4213, 4225, 4238, 4238, 4256, 4293, 4295, 4295, 4301, 4301, 4304, 4346, 4348, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880, 4882, 4885, 4888, 4954, 4992, 5007, 5024, 5109, 5112, 5117, 5121, 5740, 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5880, 5888, 5905, 5919, 5937, 5952, 5969, 5984, 5996, 5998, 6000, 6016, 6067, 6103, 6103, 6108, 6108, 6176, 6264, 6272, 6312, 6314, 6314, 6320, 6389, 6400, 6430, 6480, 6509, 6512, 6516, 6528, 6571, 6576, 6601, 6656, 6678, 6688, 6740, 6823, 6823, 6917, 6963, 6981, 6988, 7043, 7072, 7086, 7087, 7098, 7141, 7168, 7203, 7245, 7247, 7258, 7293, 7296, 7304, 7312, 7354, 7357, 7359, 7401, 7404, 7406, 7411, 7413, 7414, 7418, 7418, 7424, 7615, 7680, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8305, 8305, 8319, 8319, 8336, 8348, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8472, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8505, 8508, 8511, 8517, 8521, 8526, 8526, 8544, 8584, 11264, 11492, 11499, 11502, 11506, 11507, 11520, 11557, 11559, 11559, 11565, 11565, 11568, 11623, 11631, 11631, 11648, 11670, 11680, 11686, 11688, 11694, 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726, 11728, 11734, 11736, 11742, 12293, 12295, 12321, 12329, 12337, 12341, 12344, 12348, 12353, 12438, 12443, 12447, 12449, 12538, 12540, 12543, 12549, 12591, 12593, 12686, 12704, 12735, 12784, 12799, 13312, 19903, 19968, 42124, 42192, 42237, 42240, 42508, 42512, 42527, 42538, 42539, 42560, 42606, 42623, 42653, 42656, 42735, 42775, 42783, 42786, 42888, 42891, 42954, 42960, 42961, 42963, 42963, 42965, 42969, 42994, 43009, 43011, 43013, 43015, 43018, 43020, 43042, 43072, 43123, 43138, 43187, 43250, 43255, 43259, 43259, 43261, 43262, 43274, 43301, 43312, 43334, 43360, 43388, 43396, 43442, 43471, 43471, 43488, 43492, 43494, 43503, 43514, 43518, 43520, 43560, 43584, 43586, 43588, 43595, 43616, 43638, 43642, 43642, 43646, 43695, 43697, 43697, 43701, 43702, 43705, 43709, 43712, 43712, 43714, 43714, 43739, 43741, 43744, 43754, 43762, 43764, 43777, 43782, 43785, 43790, 43793, 43798, 43808, 43814, 43816, 43822, 43824, 43866, 43868, 43881, 43888, 44002, 44032, 55203, 55216, 55238, 55243, 55291, 63744, 64109, 64112, 64217, 64256, 64262, 64275, 64279, 64285, 64285, 64287, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65136, 65140, 65142, 65276, 65313, 65338, 65345, 65370, 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500, 65536, 65547, 65549, 65574, 65576, 65594, 65596, 65597, 65599, 65613, 65616, 65629, 65664, 65786, 65856, 65908, 66176, 66204, 66208, 66256, 66304, 66335, 66349, 66378, 66384, 66421, 66432, 66461, 66464, 66499, 66504, 66511, 66513, 66517, 66560, 66717, 66736, 66771, 66776, 66811, 66816, 66855, 66864, 66915, 66928, 66938, 66940, 66954, 66956, 66962, 66964, 66965, 66967, 66977, 66979, 66993, 66995, 67001, 67003, 67004, 67072, 67382, 67392, 67413, 67424, 67431, 67456, 67461, 67463, 67504, 67506, 67514, 67584, 67589, 67592, 67592, 67594, 67637, 67639, 67640, 67644, 67644, 67647, 67669, 67680, 67702, 67712, 67742, 67808, 67826, 67828, 67829, 67840, 67861, 67872, 67897, 67968, 68023, 68030, 68031, 68096, 68096, 68112, 68115, 68117, 68119, 68121, 68149, 68192, 68220, 68224, 68252, 68288, 68295, 68297, 68324, 68352, 68405, 68416, 68437, 68448, 68466, 68480, 68497, 68608, 68680, 68736, 68786, 68800, 68850, 68864, 68899, 69248, 69289, 69296, 69297, 69376, 69404, 69415, 69415, 69424, 69445, 69488, 69505, 69552, 69572, 69600, 69622, 69635, 69687, 69745, 69746, 69749, 69749, 69763, 69807, 69840, 69864, 69891, 69926, 69956, 69956, 69959, 69959, 69968, 70002, 70006, 70006, 70019, 70066, 70081, 70084, 70106, 70106, 70108, 70108, 70144, 70161, 70163, 70187, 70207, 70208, 70272, 70278, 70280, 70280, 70282, 70285, 70287, 70301, 70303, 70312, 70320, 70366, 70405, 70412, 70415, 70416, 70419, 70440, 70442, 70448, 70450, 70451, 70453, 70457, 70461, 70461, 70480, 70480, 70493, 70497, 70656, 70708, 70727, 70730, 70751, 70753, 70784, 70831, 70852, 70853, 70855, 70855, 71040, 71086, 71128, 71131, 71168, 71215, 71236, 71236, 71296, 71338, 71352, 71352, 71424, 71450, 71488, 71494, 71680, 71723, 71840, 71903, 71935, 71942, 71945, 71945, 71948, 71955, 71957, 71958, 71960, 71983, 71999, 71999, 72001, 72001, 72096, 72103, 72106, 72144, 72161, 72161, 72163, 72163, 72192, 72192, 72203, 72242, 72250, 72250, 72272, 72272, 72284, 72329, 72349, 72349, 72368, 72440, 72704, 72712, 72714, 72750, 72768, 72768, 72818, 72847, 72960, 72966, 72968, 72969, 72971, 73008, 73030, 73030, 73056, 73061, 73063, 73064, 73066, 73097, 73112, 73112, 73440, 73458, 73474, 73474, 73476, 73488, 73490, 73523, 73648, 73648, 73728, 74649, 74752, 74862, 74880, 75075, 77712, 77808, 77824, 78895, 78913, 78918, 82944, 83526, 92160, 92728, 92736, 92766, 92784, 92862, 92880, 92909, 92928, 92975, 92992, 92995, 93027, 93047, 93053, 93071, 93760, 93823, 93952, 94026, 94032, 94032, 94099, 94111, 94176, 94177, 94179, 94179, 94208, 100343, 100352, 101589, 101632, 101640, 110576, 110579, 110581, 110587, 110589, 110590, 110592, 110882, 110898, 110898, 110928, 110930, 110933, 110933, 110948, 110951, 110960, 111355, 113664, 113770, 113776, 113788, 113792, 113800, 113808, 113817, 119808, 119892, 119894, 119964, 119966, 119967, 119970, 119970, 119973, 119974, 119977, 119980, 119982, 119993, 119995, 119995, 119997, 120003, 120005, 120069, 120071, 120074, 120077, 120084, 120086, 120092, 120094, 120121, 120123, 120126, 120128, 120132, 120134, 120134, 120138, 120144, 120146, 120485, 120488, 120512, 120514, 120538, 120540, 120570, 120572, 120596, 120598, 120628, 120630, 120654, 120656, 120686, 120688, 120712, 120714, 120744, 120746, 120770, 120772, 120779, 122624, 122654, 122661, 122666, 122928, 122989, 123136, 123180, 123191, 123197, 123214, 123214, 123536, 123565, 123584, 123627, 124112, 124139, 124896, 124902, 124904, 124907, 124909, 124910, 124912, 124926, 124928, 125124, 125184, 125251, 125259, 125259, 126464, 126467, 126469, 126495, 126497, 126498, 126500, 126500, 126503, 126503, 126505, 126514, 126516, 126519, 126521, 126521, 126523, 126523, 126530, 126530, 126535, 126535, 126537, 126537, 126539, 126539, 126541, 126543, 126545, 126546, 126548, 126548, 126551, 126551, 126553, 126553, 126555, 126555, 126557, 126557, 126559, 126559, 126561, 126562, 126564, 126564, 126567, 126570, 126572, 126578, 126580, 126583, 126585, 126588, 126590, 126590, 126592, 126601, 126603, 126619, 126625, 126627, 126629, 126633, 126635, 126651, 131072, 173791, 173824, 177977, 177984, 178205, 178208, 183969, 183984, 191456, 191472, 192093, 194560, 195101, 196608, 201546, 201552, 205743]; +// dprint-ignore +const unicodeESNextIdentifierPart = [48, 57, 65, 90, 95, 95, 97, 122, 170, 170, 181, 181, 183, 183, 186, 186, 192, 214, 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750, 768, 884, 886, 887, 890, 893, 895, 895, 902, 906, 908, 908, 910, 929, 931, 1013, 1015, 1153, 1155, 1159, 1162, 1327, 1329, 1366, 1369, 1369, 1376, 1416, 1425, 1469, 1471, 1471, 1473, 1474, 1476, 1477, 1479, 1479, 1488, 1514, 1519, 1522, 1552, 1562, 1568, 1641, 1646, 1747, 1749, 1756, 1759, 1768, 1770, 1788, 1791, 1791, 1808, 1866, 1869, 1969, 1984, 2037, 2042, 2042, 2045, 2045, 2048, 2093, 2112, 2139, 2144, 2154, 2160, 2183, 2185, 2190, 2200, 2273, 2275, 2403, 2406, 2415, 2417, 2435, 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482, 2486, 2489, 2492, 2500, 2503, 2504, 2507, 2510, 2519, 2519, 2524, 2525, 2527, 2531, 2534, 2545, 2556, 2556, 2558, 2558, 2561, 2563, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608, 2610, 2611, 2613, 2614, 2616, 2617, 2620, 2620, 2622, 2626, 2631, 2632, 2635, 2637, 2641, 2641, 2649, 2652, 2654, 2654, 2662, 2677, 2689, 2691, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736, 2738, 2739, 2741, 2745, 2748, 2757, 2759, 2761, 2763, 2765, 2768, 2768, 2784, 2787, 2790, 2799, 2809, 2815, 2817, 2819, 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867, 2869, 2873, 2876, 2884, 2887, 2888, 2891, 2893, 2901, 2903, 2908, 2909, 2911, 2915, 2918, 2927, 2929, 2929, 2946, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970, 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001, 3006, 3010, 3014, 3016, 3018, 3021, 3024, 3024, 3031, 3031, 3046, 3055, 3072, 3084, 3086, 3088, 3090, 3112, 3114, 3129, 3132, 3140, 3142, 3144, 3146, 3149, 3157, 3158, 3160, 3162, 3165, 3165, 3168, 3171, 3174, 3183, 3200, 3203, 3205, 3212, 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3260, 3268, 3270, 3272, 3274, 3277, 3285, 3286, 3293, 3294, 3296, 3299, 3302, 3311, 3313, 3315, 3328, 3340, 3342, 3344, 3346, 3396, 3398, 3400, 3402, 3406, 3412, 3415, 3423, 3427, 3430, 3439, 3450, 3455, 3457, 3459, 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526, 3530, 3530, 3535, 3540, 3542, 3542, 3544, 3551, 3558, 3567, 3570, 3571, 3585, 3642, 3648, 3662, 3664, 3673, 3713, 3714, 3716, 3716, 3718, 3722, 3724, 3747, 3749, 3749, 3751, 3773, 3776, 3780, 3782, 3782, 3784, 3790, 3792, 3801, 3804, 3807, 3840, 3840, 3864, 3865, 3872, 3881, 3893, 3893, 3895, 3895, 3897, 3897, 3902, 3911, 3913, 3948, 3953, 3972, 3974, 3991, 3993, 4028, 4038, 4038, 4096, 4169, 4176, 4253, 4256, 4293, 4295, 4295, 4301, 4301, 4304, 4346, 4348, 4680, 4682, 4685, 4688, 4694, 4696, 4696, 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789, 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880, 4882, 4885, 4888, 4954, 4957, 4959, 4969, 4977, 4992, 5007, 5024, 5109, 5112, 5117, 5121, 5740, 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5880, 5888, 5909, 5919, 5940, 5952, 5971, 5984, 5996, 5998, 6000, 6002, 6003, 6016, 6099, 6103, 6103, 6108, 6109, 6112, 6121, 6155, 6157, 6159, 6169, 6176, 6264, 6272, 6314, 6320, 6389, 6400, 6430, 6432, 6443, 6448, 6459, 6470, 6509, 6512, 6516, 6528, 6571, 6576, 6601, 6608, 6618, 6656, 6683, 6688, 6750, 6752, 6780, 6783, 6793, 6800, 6809, 6823, 6823, 6832, 6845, 6847, 6862, 6912, 6988, 6992, 7001, 7019, 7027, 7040, 7155, 7168, 7223, 7232, 7241, 7245, 7293, 7296, 7304, 7312, 7354, 7357, 7359, 7376, 7378, 7380, 7418, 7424, 7957, 7960, 7965, 7968, 8005, 8008, 8013, 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061, 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140, 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188, 8204, 8205, 8255, 8256, 8276, 8276, 8305, 8305, 8319, 8319, 8336, 8348, 8400, 8412, 8417, 8417, 8421, 8432, 8450, 8450, 8455, 8455, 8458, 8467, 8469, 8469, 8472, 8477, 8484, 8484, 8486, 8486, 8488, 8488, 8490, 8505, 8508, 8511, 8517, 8521, 8526, 8526, 8544, 8584, 11264, 11492, 11499, 11507, 11520, 11557, 11559, 11559, 11565, 11565, 11568, 11623, 11631, 11631, 11647, 11670, 11680, 11686, 11688, 11694, 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726, 11728, 11734, 11736, 11742, 11744, 11775, 12293, 12295, 12321, 12335, 12337, 12341, 12344, 12348, 12353, 12438, 12441, 12447, 12449, 12543, 12549, 12591, 12593, 12686, 12704, 12735, 12784, 12799, 13312, 19903, 19968, 42124, 42192, 42237, 42240, 42508, 42512, 42539, 42560, 42607, 42612, 42621, 42623, 42737, 42775, 42783, 42786, 42888, 42891, 42954, 42960, 42961, 42963, 42963, 42965, 42969, 42994, 43047, 43052, 43052, 43072, 43123, 43136, 43205, 43216, 43225, 43232, 43255, 43259, 43259, 43261, 43309, 43312, 43347, 43360, 43388, 43392, 43456, 43471, 43481, 43488, 43518, 43520, 43574, 43584, 43597, 43600, 43609, 43616, 43638, 43642, 43714, 43739, 43741, 43744, 43759, 43762, 43766, 43777, 43782, 43785, 43790, 43793, 43798, 43808, 43814, 43816, 43822, 43824, 43866, 43868, 43881, 43888, 44010, 44012, 44013, 44016, 44025, 44032, 55203, 55216, 55238, 55243, 55291, 63744, 64109, 64112, 64217, 64256, 64262, 64275, 64279, 64285, 64296, 64298, 64310, 64312, 64316, 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433, 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019, 65024, 65039, 65056, 65071, 65075, 65076, 65101, 65103, 65136, 65140, 65142, 65276, 65296, 65305, 65313, 65338, 65343, 65343, 65345, 65370, 65381, 65470, 65474, 65479, 65482, 65487, 65490, 65495, 65498, 65500, 65536, 65547, 65549, 65574, 65576, 65594, 65596, 65597, 65599, 65613, 65616, 65629, 65664, 65786, 65856, 65908, 66045, 66045, 66176, 66204, 66208, 66256, 66272, 66272, 66304, 66335, 66349, 66378, 66384, 66426, 66432, 66461, 66464, 66499, 66504, 66511, 66513, 66517, 66560, 66717, 66720, 66729, 66736, 66771, 66776, 66811, 66816, 66855, 66864, 66915, 66928, 66938, 66940, 66954, 66956, 66962, 66964, 66965, 66967, 66977, 66979, 66993, 66995, 67001, 67003, 67004, 67072, 67382, 67392, 67413, 67424, 67431, 67456, 67461, 67463, 67504, 67506, 67514, 67584, 67589, 67592, 67592, 67594, 67637, 67639, 67640, 67644, 67644, 67647, 67669, 67680, 67702, 67712, 67742, 67808, 67826, 67828, 67829, 67840, 67861, 67872, 67897, 67968, 68023, 68030, 68031, 68096, 68099, 68101, 68102, 68108, 68115, 68117, 68119, 68121, 68149, 68152, 68154, 68159, 68159, 68192, 68220, 68224, 68252, 68288, 68295, 68297, 68326, 68352, 68405, 68416, 68437, 68448, 68466, 68480, 68497, 68608, 68680, 68736, 68786, 68800, 68850, 68864, 68903, 68912, 68921, 69248, 69289, 69291, 69292, 69296, 69297, 69373, 69404, 69415, 69415, 69424, 69456, 69488, 69509, 69552, 69572, 69600, 69622, 69632, 69702, 69734, 69749, 69759, 69818, 69826, 69826, 69840, 69864, 69872, 69881, 69888, 69940, 69942, 69951, 69956, 69959, 69968, 70003, 70006, 70006, 70016, 70084, 70089, 70092, 70094, 70106, 70108, 70108, 70144, 70161, 70163, 70199, 70206, 70209, 70272, 70278, 70280, 70280, 70282, 70285, 70287, 70301, 70303, 70312, 70320, 70378, 70384, 70393, 70400, 70403, 70405, 70412, 70415, 70416, 70419, 70440, 70442, 70448, 70450, 70451, 70453, 70457, 70459, 70468, 70471, 70472, 70475, 70477, 70480, 70480, 70487, 70487, 70493, 70499, 70502, 70508, 70512, 70516, 70656, 70730, 70736, 70745, 70750, 70753, 70784, 70853, 70855, 70855, 70864, 70873, 71040, 71093, 71096, 71104, 71128, 71133, 71168, 71232, 71236, 71236, 71248, 71257, 71296, 71352, 71360, 71369, 71424, 71450, 71453, 71467, 71472, 71481, 71488, 71494, 71680, 71738, 71840, 71913, 71935, 71942, 71945, 71945, 71948, 71955, 71957, 71958, 71960, 71989, 71991, 71992, 71995, 72003, 72016, 72025, 72096, 72103, 72106, 72151, 72154, 72161, 72163, 72164, 72192, 72254, 72263, 72263, 72272, 72345, 72349, 72349, 72368, 72440, 72704, 72712, 72714, 72758, 72760, 72768, 72784, 72793, 72818, 72847, 72850, 72871, 72873, 72886, 72960, 72966, 72968, 72969, 72971, 73014, 73018, 73018, 73020, 73021, 73023, 73031, 73040, 73049, 73056, 73061, 73063, 73064, 73066, 73102, 73104, 73105, 73107, 73112, 73120, 73129, 73440, 73462, 73472, 73488, 73490, 73530, 73534, 73538, 73552, 73561, 73648, 73648, 73728, 74649, 74752, 74862, 74880, 75075, 77712, 77808, 77824, 78895, 78912, 78933, 82944, 83526, 92160, 92728, 92736, 92766, 92768, 92777, 92784, 92862, 92864, 92873, 92880, 92909, 92912, 92916, 92928, 92982, 92992, 92995, 93008, 93017, 93027, 93047, 93053, 93071, 93760, 93823, 93952, 94026, 94031, 94087, 94095, 94111, 94176, 94177, 94179, 94180, 94192, 94193, 94208, 100343, 100352, 101589, 101632, 101640, 110576, 110579, 110581, 110587, 110589, 110590, 110592, 110882, 110898, 110898, 110928, 110930, 110933, 110933, 110948, 110951, 110960, 111355, 113664, 113770, 113776, 113788, 113792, 113800, 113808, 113817, 113821, 113822, 118528, 118573, 118576, 118598, 119141, 119145, 119149, 119154, 119163, 119170, 119173, 119179, 119210, 119213, 119362, 119364, 119808, 119892, 119894, 119964, 119966, 119967, 119970, 119970, 119973, 119974, 119977, 119980, 119982, 119993, 119995, 119995, 119997, 120003, 120005, 120069, 120071, 120074, 120077, 120084, 120086, 120092, 120094, 120121, 120123, 120126, 120128, 120132, 120134, 120134, 120138, 120144, 120146, 120485, 120488, 120512, 120514, 120538, 120540, 120570, 120572, 120596, 120598, 120628, 120630, 120654, 120656, 120686, 120688, 120712, 120714, 120744, 120746, 120770, 120772, 120779, 120782, 120831, 121344, 121398, 121403, 121452, 121461, 121461, 121476, 121476, 121499, 121503, 121505, 121519, 122624, 122654, 122661, 122666, 122880, 122886, 122888, 122904, 122907, 122913, 122915, 122916, 122918, 122922, 122928, 122989, 123023, 123023, 123136, 123180, 123184, 123197, 123200, 123209, 123214, 123214, 123536, 123566, 123584, 123641, 124112, 124153, 124896, 124902, 124904, 124907, 124909, 124910, 124912, 124926, 124928, 125124, 125136, 125142, 125184, 125259, 125264, 125273, 126464, 126467, 126469, 126495, 126497, 126498, 126500, 126500, 126503, 126503, 126505, 126514, 126516, 126519, 126521, 126521, 126523, 126523, 126530, 126530, 126535, 126535, 126537, 126537, 126539, 126539, 126541, 126543, 126545, 126546, 126548, 126548, 126551, 126551, 126553, 126553, 126555, 126555, 126557, 126557, 126559, 126559, 126561, 126562, 126564, 126564, 126567, 126570, 126572, 126578, 126580, 126583, 126585, 126588, 126590, 126590, 126592, 126601, 126603, 126619, 126625, 126627, 126629, 126633, 126635, 126651, 130032, 130041, 131072, 173791, 173824, 177977, 177984, 178205, 178208, 183969, 183984, 191456, 191472, 192093, 194560, 195101, 196608, 201546, 201552, 205743, 917760, 917999]; + +const commentDirectiveRegExSingleLine = /^\/\/\/?\s*@(ts-expect-error|ts-ignore)/; +const commentDirectiveRegExMultiLine = /^(?:\/|\*)*\s*@(ts-expect-error|ts-ignore)/; + +const jsDocTagTerminators = new Set([" ", "\t", "\n", "\r", "}", "*"]); + +function hasJSDocTag(text: string, offset: number, ...tags: string[]): boolean { + for (const tag of tags) { + if (text.startsWith(tag, offset)) { + if (offset + tag.length === text.length) { + return true; + } + if (jsDocTagTerminators.has(text[offset + tag.length])) { + return true; + } + } + } + return false; +} + +function scanJSDocCommentForTags(text: string, tokenFlags: TokenFlags): TokenFlags { + let offset = 0; + while (true) { + const i = text.indexOf("@", offset); + if (i < 0) { + return tokenFlags; + } + offset = i + 1; + if (!(tokenFlags & TokenFlags.PrecedingJSDocWithDeprecated) && hasJSDocTag(text, offset, "deprecated")) { + tokenFlags |= TokenFlags.PrecedingJSDocWithDeprecated; + } + if (!(tokenFlags & TokenFlags.PrecedingJSDocWithSeeOrLink) && hasJSDocTag(text, offset, "see", "link", "linkcode", "linkplain")) { + tokenFlags |= TokenFlags.PrecedingJSDocWithSeeOrLink; + } + if ( + (tokenFlags & (TokenFlags.PrecedingJSDocWithDeprecated | TokenFlags.PrecedingJSDocWithSeeOrLink)) === + (TokenFlags.PrecedingJSDocWithDeprecated | TokenFlags.PrecedingJSDocWithSeeOrLink) + ) { + return tokenFlags; + } + } +} + +function lookupInUnicodeMap(code: number, map: readonly number[]): boolean { + if (code < map[0]) { + return false; + } + + let lo = 0; + let hi: number = map.length; + let mid: number; + + while (lo + 1 < hi) { + mid = lo + (hi - lo) / 2; + mid -= mid % 2; + if (map[mid] <= code && code <= map[mid + 1]) { + return true; + } + + if (code < map[mid]) { + hi = mid; + } + else { + lo = mid + 2; + } + } + + return false; +} + +export function isUnicodeIdentifierStart(code: number): boolean { + return lookupInUnicodeMap(code, unicodeESNextIdentifierStart); +} + +function isUnicodeIdentifierPart(code: number): boolean { + return lookupInUnicodeMap(code, unicodeESNextIdentifierPart); +} + +function makeReverseMap(source: Map): T[] { + const result: T[] = []; + source.forEach((value, name) => { + result[value] = name; + }); + return result; +} + +const tokenStrings = makeReverseMap(textToToken); + +export function tokenToString(t: SyntaxKind): string | undefined { + return tokenStrings[t]; +} + +export function stringToToken(s: string): SyntaxKind | undefined { + return textToToken.get(s); +} + +export function characterCodeToRegularExpressionFlag(ch: number): RegularExpressionFlags | undefined { + return charCodeToRegExpFlag.get(ch); +} + +export function computeLineStarts(text: string): number[] { + const result: number[] = []; + let pos = 0; + let lineStart = 0; + while (pos < text.length) { + const ch = text.charCodeAt(pos); + pos++; + switch (ch) { + case CharacterCodes.carriageReturn: + if (text.charCodeAt(pos) === CharacterCodes.lineFeed) { + pos++; + } + // falls through + case CharacterCodes.lineFeed: + result.push(lineStart); + lineStart = pos; + break; + default: + if (ch > CharacterCodes.maxAsciiCharacter && isLineBreak(ch)) { + result.push(lineStart); + lineStart = pos; + } + break; + } + } + result.push(lineStart); + return result; +} + +export function isWhiteSpaceLike(ch: number): boolean { + return isWhiteSpaceSingleLine(ch) || isLineBreak(ch); +} + +export function isWhiteSpaceSingleLine(ch: number): boolean { + return ch === CharacterCodes.space || + ch === CharacterCodes.tab || + ch === CharacterCodes.verticalTab || + ch === CharacterCodes.formFeed || + ch === CharacterCodes.nonBreakingSpace || + ch === CharacterCodes.nextLine || + ch === CharacterCodes.ogham || + ch >= CharacterCodes.enQuad && ch <= CharacterCodes.zeroWidthSpace || + ch === CharacterCodes.narrowNoBreakSpace || + ch === CharacterCodes.mathematicalSpace || + ch === CharacterCodes.ideographicSpace || + ch === CharacterCodes.byteOrderMark; +} + +export function isLineBreak(ch: number): boolean { + return ch === CharacterCodes.lineFeed || + ch === CharacterCodes.carriageReturn || + ch === CharacterCodes.lineSeparator || + ch === CharacterCodes.paragraphSeparator; +} + +function isDigit(ch: number): boolean { + return ch >= CharacterCodes._0 && ch <= CharacterCodes._9; +} + +function isHexDigit(ch: number): boolean { + return isDigit(ch) || ch >= CharacterCodes.A && ch <= CharacterCodes.F || ch >= CharacterCodes.a && ch <= CharacterCodes.f; +} + +function isOctalDigit(ch: number): boolean { + return ch >= CharacterCodes._0 && ch <= CharacterCodes._7; +} + +export function couldStartTrivia(text: string, pos: number): boolean { + const ch = text.charCodeAt(pos); + switch (ch) { + case CharacterCodes.carriageReturn: + case CharacterCodes.lineFeed: + case CharacterCodes.tab: + case CharacterCodes.verticalTab: + case CharacterCodes.formFeed: + case CharacterCodes.space: + case CharacterCodes.slash: + case CharacterCodes.lessThan: + case CharacterCodes.bar: + case CharacterCodes.equals: + case CharacterCodes.greaterThan: + return true; + case CharacterCodes.hash: + return pos === 0; + default: + return ch > CharacterCodes.maxAsciiCharacter; + } +} + +export function skipTrivia(text: string, pos: number, stopAfterLineBreak?: boolean, stopAtComments?: boolean, inJSDoc?: boolean): number { + if (pos < 0) { + return pos; + } + + let canConsumeStar = false; + while (true) { + const ch = text.charCodeAt(pos); + switch (ch) { + case CharacterCodes.carriageReturn: + if (text.charCodeAt(pos + 1) === CharacterCodes.lineFeed) { + pos++; + } + // falls through + case CharacterCodes.lineFeed: + pos++; + if (stopAfterLineBreak) { + return pos; + } + canConsumeStar = !!inJSDoc; + continue; + case CharacterCodes.tab: + case CharacterCodes.verticalTab: + case CharacterCodes.formFeed: + case CharacterCodes.space: + pos++; + continue; + case CharacterCodes.slash: + if (stopAtComments) { + break; + } + if (text.charCodeAt(pos + 1) === CharacterCodes.slash) { + pos += 2; + while (pos < text.length) { + if (isLineBreak(text.charCodeAt(pos))) { + break; + } + pos++; + } + canConsumeStar = false; + continue; + } + if (text.charCodeAt(pos + 1) === CharacterCodes.asterisk) { + pos += 2; + while (pos < text.length) { + if (text.charCodeAt(pos) === CharacterCodes.asterisk && text.charCodeAt(pos + 1) === CharacterCodes.slash) { + pos += 2; + break; + } + pos++; + } + canConsumeStar = false; + continue; + } + break; + + case CharacterCodes.lessThan: + case CharacterCodes.bar: + case CharacterCodes.equals: + case CharacterCodes.greaterThan: + if (isConflictMarkerTrivia(text, pos)) { + pos = scanConflictMarkerTrivia(text, pos); + canConsumeStar = false; + continue; + } + break; + + case CharacterCodes.hash: + if (pos === 0 && isShebangTrivia(text, pos)) { + pos = scanShebangTrivia(text, pos); + continue; + } + break; + + case CharacterCodes.asterisk: + if (canConsumeStar) { + pos++; + canConsumeStar = false; + continue; + } + break; + + default: + if (ch > CharacterCodes.maxAsciiCharacter && (isWhiteSpaceLike(ch))) { + pos++; + continue; + } + break; + } + return pos; + } +} + +function isConflictMarkerTrivia(text: string, pos: number) { + if (pos >= text.length) { + return false; + } + + const ch = text.charCodeAt(pos); + + if (pos === 0 || isLineBreak(text.charCodeAt(pos - 1))) { + if (ch === CharacterCodes.lessThan || ch === CharacterCodes.greaterThan || ch === CharacterCodes.equals) { + if (pos + 6 < text.length && text.charCodeAt(pos + 1) === ch && text.charCodeAt(pos + 2) === ch && text.charCodeAt(pos + 3) === ch && text.charCodeAt(pos + 4) === ch && text.charCodeAt(pos + 5) === ch && text.charCodeAt(pos + 6) === ch) { + return ch === CharacterCodes.equals || text.charCodeAt(pos + 7) === CharacterCodes.space; + } + } + if (ch === CharacterCodes.bar && pos + 6 < text.length && text.charCodeAt(pos + 1) === ch && text.charCodeAt(pos + 2) === ch && text.charCodeAt(pos + 3) === ch && text.charCodeAt(pos + 4) === ch && text.charCodeAt(pos + 5) === ch && text.charCodeAt(pos + 6) === ch) { + return true; + } + } + + return false; +} + +function scanConflictMarkerTrivia(text: string, pos: number): number { + const ch = text.charCodeAt(pos); + const len = text.length; + + if (ch === CharacterCodes.lessThan || ch === CharacterCodes.greaterThan) { + while (pos < len && !isLineBreak(text.charCodeAt(pos))) { + pos++; + } + } + else { + // Consume everything from the start of a ||||||| or ======= marker to the start + // of the next ======= or >>>>>>> marker. + pos += 7; // skip marker + while (pos < len) { + const currentChar = text.charCodeAt(pos); + if ((currentChar === CharacterCodes.equals || currentChar === CharacterCodes.greaterThan) && isConflictMarkerTrivia(text, pos)) { + break; + } + pos++; + } + } + + return pos; +} + +function isShebangTrivia(text: string, pos: number) { + return pos === 0 && text.charCodeAt(0) === CharacterCodes.hash && text.charCodeAt(1) === CharacterCodes.exclamation; +} + +function scanShebangTrivia(text: string, pos: number) { + pos += 2; + while (pos < text.length) { + if (isLineBreak(text.charCodeAt(pos))) { + break; + } + pos++; + } + return pos; +} + +export type CommentKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia; + +export interface CommentRange { + pos: number; + end: number; + hasTrailingNewLine?: boolean; + kind: CommentKind; +} + +function iterateCommentRanges( + reduce: boolean, + text: string, + pos: number, + trailing: boolean, + cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T, memo: U | undefined) => U, + state: T, + initial?: U, +): U | undefined { + let pendingPos!: number; + let pendingEnd!: number; + let pendingKind!: CommentKind; + let pendingHasTrailingNewLine!: boolean; + let hasPendingCommentRange = false; + let collecting = trailing; + let accumulator = initial; + if (pos === 0) { + collecting = true; + const shebang = getShebang(text); + if (shebang) { + pos = shebang.length; + } + } + scan: + while (pos >= 0 && pos < text.length) { + const ch = text.charCodeAt(pos); + switch (ch) { + case CharacterCodes.carriageReturn: + if (text.charCodeAt(pos + 1) === CharacterCodes.lineFeed) { + pos++; + } + // falls through + case CharacterCodes.lineFeed: + pos++; + if (trailing) { + break scan; + } + collecting = true; + if (hasPendingCommentRange) { + pendingHasTrailingNewLine = true; + } + continue; + case CharacterCodes.tab: + case CharacterCodes.verticalTab: + case CharacterCodes.formFeed: + case CharacterCodes.space: + pos++; + continue; + case CharacterCodes.slash: + const nextChar = text.charCodeAt(pos + 1); + let hasTrailingNewLine = false; + if (nextChar === CharacterCodes.slash || nextChar === CharacterCodes.asterisk) { + const kind: CommentKind = nextChar === CharacterCodes.slash ? SyntaxKind.SingleLineCommentTrivia : SyntaxKind.MultiLineCommentTrivia; + const startPos = pos; + pos += 2; + if (nextChar === CharacterCodes.slash) { + while (pos < text.length) { + if (isLineBreak(text.charCodeAt(pos))) { + hasTrailingNewLine = true; + break; + } + pos++; + } + } + else { + while (pos < text.length) { + if (text.charCodeAt(pos) === CharacterCodes.asterisk && text.charCodeAt(pos + 1) === CharacterCodes.slash) { + pos += 2; + break; + } + pos++; + } + } + + if (collecting) { + if (hasPendingCommentRange) { + accumulator = cb(pendingPos, pendingEnd, pendingKind, pendingHasTrailingNewLine, state, accumulator); + if (!reduce && accumulator) { + return accumulator; + } + } + + pendingPos = startPos; + pendingEnd = pos; + pendingKind = kind; + pendingHasTrailingNewLine = hasTrailingNewLine; + hasPendingCommentRange = true; + } + continue; + } + break scan; + default: + if (ch > CharacterCodes.maxAsciiCharacter && (isWhiteSpaceLike(ch))) { + if (hasPendingCommentRange && isLineBreak(ch)) { + pendingHasTrailingNewLine = true; + } + pos++; + continue; + } + break scan; + } + } + + if (hasPendingCommentRange) { + accumulator = cb(pendingPos, pendingEnd, pendingKind, pendingHasTrailingNewLine, state, accumulator); + } + + return accumulator; +} + +function appendCommentRange(pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, _state: any, comments: CommentRange[] = []) { + comments.push({ kind, pos, end, hasTrailingNewLine }); + return comments; +} + +export function forEachLeadingCommentRange(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T) => U, state: T): U | undefined; +export function forEachLeadingCommentRange(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean) => U): U | undefined; +export function forEachLeadingCommentRange(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T) => U, state?: T): U | undefined { + return iterateCommentRanges(/*reduce*/ false, text, pos, /*trailing*/ false, cb, state!, /*initial*/ undefined); +} + +export function forEachTrailingCommentRange(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T) => U, state: T): U | undefined; +export function forEachTrailingCommentRange(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean) => U): U | undefined; +export function forEachTrailingCommentRange(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T) => U, state?: T): U | undefined { + return iterateCommentRanges(/*reduce*/ false, text, pos, /*trailing*/ true, cb, state!, /*initial*/ undefined); +} + +export function reduceEachLeadingCommentRange(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T) => U, state: T, initial: U): U | undefined { + return iterateCommentRanges(/*reduce*/ true, text, pos, /*trailing*/ false, cb, state, initial); +} + +export function reduceEachTrailingCommentRange(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T) => U, state: T, initial: U): U | undefined { + return iterateCommentRanges(/*reduce*/ true, text, pos, /*trailing*/ true, cb, state, initial); +} + +export function getLeadingCommentRanges(text: string, pos: number): CommentRange[] | undefined { + return reduceEachLeadingCommentRange(text, pos, appendCommentRange, /*state*/ undefined as any, /*initial*/ undefined as any as CommentRange[]); +} + +export function getTrailingCommentRanges(text: string, pos: number): CommentRange[] | undefined { + return reduceEachTrailingCommentRange(text, pos, appendCommentRange, /*state*/ undefined as any, /*initial*/ undefined as any as CommentRange[]); +} + +export function getShebang(text: string): string | undefined { + const shebang = /^#!.*/; + if (isShebangTrivia(text, 0)) { + const match = shebang.exec(text); + if (match) { + return match[0]; + } + } + return undefined; +} + +export function isIdentifierStart(ch: number, _languageVersion?: ScriptTarget): boolean { + return ch >= CharacterCodes.A && ch <= CharacterCodes.Z || ch >= CharacterCodes.a && ch <= CharacterCodes.z || + ch === CharacterCodes.$ || ch === CharacterCodes._ || + ch > CharacterCodes.maxAsciiCharacter && isUnicodeIdentifierStart(ch); +} + +export function isIdentifierPart(ch: number, _languageVersion?: ScriptTarget, identifierVariant?: LanguageVariant): boolean { + return ch >= CharacterCodes.A && ch <= CharacterCodes.Z || ch >= CharacterCodes.a && ch <= CharacterCodes.z || + ch >= CharacterCodes._0 && ch <= CharacterCodes._9 || ch === CharacterCodes.$ || ch === CharacterCodes._ || + // "-" and ":" are valid in JSX Identifiers + (identifierVariant === LanguageVariant.JSX ? (ch === CharacterCodes.minus || ch === CharacterCodes.colon) : false) || + ch > CharacterCodes.maxAsciiCharacter && isUnicodeIdentifierPart(ch); +} + +export function isIdentifierText(name: string, _languageVersion?: ScriptTarget, identifierVariant?: LanguageVariant): boolean { + let ch = name.codePointAt(0)!; + if (!isIdentifierStart(ch)) { + return false; + } + + for (let i = charSize(ch); i < name.length; i += charSize(ch)) { + if (!isIdentifierPart(ch = name.codePointAt(i)!, undefined, identifierVariant)) { + return false; + } + } + + return true; +} + +function codePointAt(s: string, i: number): number { + return s.codePointAt(i)!; +} + +function charSize(ch: number) { + if (ch >= 0x10000) { + return 2; + } + if (ch === CharacterCodes.EOF) { + return 0; + } + return 1; +} + +export function utf16EncodeAsString(codePoint: number): string { + return String.fromCodePoint(codePoint); +} + +function parsePseudoBigInt(stringValue: string): string { + let log2Base: number; + switch (stringValue.charCodeAt(1)) { + case CharacterCodes.b: + case CharacterCodes.B: + log2Base = 1; + break; + case CharacterCodes.o: + case CharacterCodes.O: + log2Base = 3; + break; + case CharacterCodes.x: + case CharacterCodes.X: + log2Base = 4; + break; + default: + // decimals for now just peel the trailing "n" and return + const nIndex = stringValue.length - 1; + let nonZeroStart = 0; + while (stringValue.charCodeAt(nonZeroStart) === CharacterCodes._0) { + nonZeroStart++; + } + return stringValue.slice(nonZeroStart, nIndex) || "0"; + } + // exit early for zero + const startIndex = 2; + const endIndex = stringValue.length - 1; + const bitsNeeded = (endIndex - startIndex) * log2Base; + // Stores the value specified by the string as a LE array of 16-bit integers + // using Uint16 instead of Uint32 so combining steps can use bitwise operators + const segments = new Uint16Array((bitsNeeded >>> 4) + (bitsNeeded & 15 ? 1 : 0)); + // Add the digits, one at a time + for (let i = endIndex - 1, bitOffset = 0; i >= startIndex; i--, bitOffset += log2Base) { + const segment = bitOffset >>> 4; + const digitChar = stringValue.charCodeAt(i); + // Find character value + const digit = digitChar <= CharacterCodes._9 + ? digitChar - CharacterCodes._0 + : 10 + digitChar - (digitChar <= CharacterCodes.F ? CharacterCodes.A : CharacterCodes.a); + // Add bits into segment + const shiftedDigit = digit << (bitOffset & 15); + segments[segment] |= shiftedDigit; + // overflow to next segment + const residual = shiftedDigit >>> 16; + if (residual) segments[segment + 1] |= residual; + } + // Repeatedly divide by 10 to find each decimal digit + let base10Value = ""; + let firstNonzeroSegment = segments.length - 1; + let segmentsRemaining = true; + while (segmentsRemaining) { + let mod10 = 0; + segmentsRemaining = false; + for (let segment = firstNonzeroSegment; segment >= 0; segment--) { + const newSegment = mod10 << 16 | segments[segment]; + const segmentValue = (newSegment / 10) | 0; + segments[segment] = segmentValue; + mod10 = newSegment - segmentValue * 10; + if (segmentValue && !segmentsRemaining) { + firstNonzeroSegment = segment; + segmentsRemaining = true; + } + } + base10Value = mod10 + base10Value; + } + return base10Value; +} + +// Creates a scanner over a (possibly unspecified) range of a piece of text. +export function createScanner( + skipTrivia: boolean, + languageVariant: LanguageVariant = LanguageVariant.Standard, + textInitial?: string, + start?: number, + length?: number, +): Scanner { + // Why var? It avoids TDZ checks in the runtime which can be costly. + // See: https://github.com/microsoft/TypeScript/issues/52924 + /* eslint-disable no-var */ + var text = textInitial!; + + // Current position (end position of text of current token) + var pos: number; + + // end of text + var end: number; + + // Start position of whitespace before current token + var fullStartPos: number; + + // Start position of text of current token + var tokenStart: number; + + var token: SyntaxKind; + var tokenValue!: string; + var tokenFlags: TokenFlags; + + var commentDirectives: CommentDirective[] | undefined; + var skipJsDocLeadingAsterisks = 0; + + setText(text, start, length); + + var scanner: Scanner = { + getTokenFullStart: () => fullStartPos, + getTokenEnd: () => pos, + getToken: () => token, + getTokenStart: () => tokenStart, + getTokenText: () => text.substring(tokenStart, pos), + getTokenValue: () => tokenValue, + hasUnicodeEscape: () => (tokenFlags & TokenFlags.UnicodeEscape) !== 0, + hasExtendedUnicodeEscape: () => (tokenFlags & TokenFlags.ExtendedUnicodeEscape) !== 0, + hasPrecedingLineBreak: () => (tokenFlags & TokenFlags.PrecedingLineBreak) !== 0, + hasPrecedingJSDocComment: () => (tokenFlags & TokenFlags.PrecedingJSDocComment) !== 0, + hasPrecedingJSDocLeadingAsterisks: () => (tokenFlags & TokenFlags.PrecedingJSDocLeadingAsterisks) !== 0, + hasPrecedingJSDocWithDeprecatedTag: () => (tokenFlags & TokenFlags.PrecedingJSDocWithDeprecated) !== 0, + hasPrecedingJSDocWithSeeOrLink: () => (tokenFlags & TokenFlags.PrecedingJSDocWithSeeOrLink) !== 0, + isIdentifier: () => token === SyntaxKind.Identifier || token > SyntaxKind.LastReservedWord, + isReservedWord: () => token >= SyntaxKind.FirstReservedWord && token <= SyntaxKind.LastReservedWord, + isUnterminated: () => (tokenFlags & TokenFlags.Unterminated) !== 0, + getCommentDirectives: () => commentDirectives, + getNumericLiteralFlags: () => tokenFlags & TokenFlags.NumericLiteralFlags, + getTokenFlags: () => tokenFlags, + reScanGreaterToken, + reScanAsteriskEqualsToken, + reScanSlashToken, + reScanTemplateToken, + reScanTemplateHeadOrNoSubstitutionTemplate, + scanJsxIdentifier, + scanJsxAttributeValue, + reScanJsxAttributeValue, + reScanJsxToken, + reScanLessThanToken, + reScanHashToken, + reScanQuestionToken, + reScanInvalidIdentifier, + scanJsxToken, + scanJsDocToken, + scanJSDocCommentTextToken, + scan, + getText, + clearCommentDirectives, + setText, + setLanguageVariant, + resetTokenState, + setSkipJsDocLeadingAsterisks, + tryScan, + lookAhead, + scanRange, + }; + /* eslint-enable no-var */ + + return scanner; + + function codePointUnchecked(pos: number) { + return codePointAt(text, pos); + } + + function codePointChecked(pos: number) { + return pos >= 0 && pos < end ? codePointUnchecked(pos) : CharacterCodes.EOF; + } + + function charCodeUnchecked(pos: number) { + return text.charCodeAt(pos); + } + + function charCodeChecked(pos: number) { + return pos >= 0 && pos < end ? charCodeUnchecked(pos) : CharacterCodes.EOF; + } + + function scanNumberFragment(): string { + let start = pos; + let allowSeparator = false; + let isPreviousTokenSeparator = false; + let result = ""; + while (true) { + const ch = charCodeUnchecked(pos); + if (ch === CharacterCodes._) { + tokenFlags |= TokenFlags.ContainsSeparator; + if (allowSeparator) { + allowSeparator = false; + isPreviousTokenSeparator = true; + result += text.substring(start, pos); + } + else { + tokenFlags |= TokenFlags.ContainsInvalidSeparator; + } + pos++; + start = pos; + continue; + } + if (isDigit(ch)) { + allowSeparator = true; + isPreviousTokenSeparator = false; + pos++; + continue; + } + break; + } + if (charCodeUnchecked(pos - 1) === CharacterCodes._) { + tokenFlags |= TokenFlags.ContainsInvalidSeparator; + } + return result + text.substring(start, pos); + } + + function scanNumber(): SyntaxKind { + let start = pos; + let mainFragment: string; + if (charCodeUnchecked(pos) === CharacterCodes._0) { + pos++; + if (charCodeUnchecked(pos) === CharacterCodes._) { + tokenFlags |= TokenFlags.ContainsSeparator | TokenFlags.ContainsInvalidSeparator; + pos--; + mainFragment = scanNumberFragment(); + } + else if (!scanDigits()) { + tokenFlags |= TokenFlags.ContainsLeadingZero; + mainFragment = "" + +tokenValue; + } + else if (!tokenValue) { + mainFragment = "0"; + } + else { + tokenValue = "" + parseInt(tokenValue, 8); + tokenFlags |= TokenFlags.Octal; + const withMinus = token === SyntaxKind.MinusToken; + const literal = (withMinus ? "-" : "") + "0o" + (+tokenValue).toString(8); + if (withMinus) start--; + return SyntaxKind.NumericLiteral; + } + } + else { + mainFragment = scanNumberFragment(); + } + let decimalFragment: string | undefined; + let scientificFragment: string | undefined; + if (charCodeUnchecked(pos) === CharacterCodes.dot) { + pos++; + decimalFragment = scanNumberFragment(); + } + let end = pos; + if (charCodeUnchecked(pos) === CharacterCodes.E || charCodeUnchecked(pos) === CharacterCodes.e) { + pos++; + tokenFlags |= TokenFlags.Scientific; + if (charCodeUnchecked(pos) === CharacterCodes.plus || charCodeUnchecked(pos) === CharacterCodes.minus) pos++; + const preNumericPart = pos; + const finalFragment = scanNumberFragment(); + if (finalFragment) { + scientificFragment = text.substring(end, preNumericPart) + finalFragment; + end = pos; + } + } + let result: string; + if (tokenFlags & TokenFlags.ContainsSeparator) { + result = mainFragment; + if (decimalFragment) { + result += "." + decimalFragment; + } + if (scientificFragment) { + result += scientificFragment; + } + } + else { + result = text.substring(start, end); + } + + if (tokenFlags & TokenFlags.ContainsLeadingZero) { + tokenValue = "" + +result; + return SyntaxKind.NumericLiteral; + } + + if (decimalFragment !== undefined || tokenFlags & TokenFlags.Scientific) { + checkForIdentifierStartAfterNumericLiteral(); + tokenValue = "" + +result; + return SyntaxKind.NumericLiteral; + } + else { + tokenValue = result; + const type = checkBigIntSuffix(); + checkForIdentifierStartAfterNumericLiteral(); + return type; + } + } + + function checkForIdentifierStartAfterNumericLiteral() { + if (!isIdentifierStart(codePointUnchecked(pos))) { + return; + } + + const identifierStart = pos; + const { length } = scanIdentifierParts(); + + if (!(length === 1 && text[identifierStart] === "n")) { + pos = identifierStart; + } + } + + function scanDigits(): boolean { + const start = pos; + let isOctal = true; + while (isDigit(charCodeChecked(pos))) { + if (!isOctalDigit(charCodeUnchecked(pos))) { + isOctal = false; + } + pos++; + } + tokenValue = text.substring(start, pos); + return isOctal; + } + + function scanExactNumberOfHexDigits(count: number, canHaveSeparators: boolean): number { + const valueString = scanHexDigits(count, /*scanAsManyAsPossible*/ false, canHaveSeparators); + return valueString ? parseInt(valueString, 16) : -1; + } + + function scanMinimumNumberOfHexDigits(count: number, canHaveSeparators: boolean): string { + return scanHexDigits(count, /*scanAsManyAsPossible*/ true, canHaveSeparators); + } + + function scanHexDigits(minCount: number, scanAsManyAsPossible: boolean, canHaveSeparators: boolean): string { + let valueChars: number[] = []; + let allowSeparator = false; + while (valueChars.length < minCount || scanAsManyAsPossible) { + let ch = charCodeUnchecked(pos); + if (canHaveSeparators && ch === CharacterCodes._) { + tokenFlags |= TokenFlags.ContainsSeparator; + if (allowSeparator) { + allowSeparator = false; + } + pos++; + continue; + } + allowSeparator = canHaveSeparators; + if (ch >= CharacterCodes.A && ch <= CharacterCodes.F) { + ch += CharacterCodes.a - CharacterCodes.A; + } + else if ( + !((ch >= CharacterCodes._0 && ch <= CharacterCodes._9) || + (ch >= CharacterCodes.a && ch <= CharacterCodes.f)) + ) { + break; + } + valueChars.push(ch); + pos++; + } + if (valueChars.length < minCount) { + valueChars = []; + } + return String.fromCharCode(...valueChars); + } + + function scanString(jsxAttributeString = false): string { + const quote = charCodeUnchecked(pos); + pos++; + let result = ""; + let start = pos; + while (true) { + if (pos >= end) { + result += text.substring(start, pos); + tokenFlags |= TokenFlags.Unterminated; + break; + } + const ch = charCodeUnchecked(pos); + if (ch === quote) { + result += text.substring(start, pos); + pos++; + break; + } + if (ch === CharacterCodes.backslash && !jsxAttributeString) { + result += text.substring(start, pos); + result += scanEscapeSequence(EscapeSequenceScanningFlags.String | EscapeSequenceScanningFlags.ReportErrors); + start = pos; + continue; + } + + if ((ch === CharacterCodes.lineFeed || ch === CharacterCodes.carriageReturn) && !jsxAttributeString) { + result += text.substring(start, pos); + tokenFlags |= TokenFlags.Unterminated; + break; + } + pos++; + } + return result; + } + + function scanTemplateAndSetTokenValue(shouldEmitInvalidEscapeError: boolean): SyntaxKind { + const startedWithBacktick = charCodeUnchecked(pos) === CharacterCodes.backtick; + + pos++; + let start = pos; + let contents = ""; + let resultingToken!: SyntaxKind; + + while (true) { + if (pos >= end) { + contents += text.substring(start, pos); + tokenFlags |= TokenFlags.Unterminated; + resultingToken = startedWithBacktick ? SyntaxKind.NoSubstitutionTemplateLiteral : SyntaxKind.TemplateTail; + break; + } + + const currChar = charCodeUnchecked(pos); + + if (currChar === CharacterCodes.backtick) { + contents += text.substring(start, pos); + pos++; + resultingToken = startedWithBacktick ? SyntaxKind.NoSubstitutionTemplateLiteral : SyntaxKind.TemplateTail; + break; + } + + if (currChar === CharacterCodes.$ && pos + 1 < end && charCodeUnchecked(pos + 1) === CharacterCodes.openBrace) { + contents += text.substring(start, pos); + pos += 2; + resultingToken = startedWithBacktick ? SyntaxKind.TemplateHead : SyntaxKind.TemplateMiddle; + break; + } + + if (currChar === CharacterCodes.backslash) { + contents += text.substring(start, pos); + contents += scanEscapeSequence(EscapeSequenceScanningFlags.String | (shouldEmitInvalidEscapeError ? EscapeSequenceScanningFlags.ReportErrors : 0)); + start = pos; + continue; + } + + if (currChar === CharacterCodes.carriageReturn) { + contents += text.substring(start, pos); + pos++; + + if (pos < end && charCodeUnchecked(pos) === CharacterCodes.lineFeed) { + pos++; + } + + contents += "\n"; + start = pos; + continue; + } + + pos++; + } + + tokenValue = contents; + return resultingToken; + } + + function scanEscapeSequence(flags: EscapeSequenceScanningFlags): string { + const start = pos; + pos++; + if (pos >= end) { + return ""; + } + const ch = charCodeUnchecked(pos); + pos++; + switch (ch) { + case CharacterCodes._0: + if (pos >= end || !isDigit(charCodeUnchecked(pos))) { + return "\0"; + } + // falls through + case CharacterCodes._1: + case CharacterCodes._2: + case CharacterCodes._3: + if (pos < end && isOctalDigit(charCodeUnchecked(pos))) { + pos++; + } + // falls through + case CharacterCodes._4: + case CharacterCodes._5: + case CharacterCodes._6: + case CharacterCodes._7: + if (pos < end && isOctalDigit(charCodeUnchecked(pos))) { + pos++; + } + tokenFlags |= TokenFlags.ContainsInvalidEscape; + if (flags & EscapeSequenceScanningFlags.ReportInvalidEscapeErrors) { + const code = parseInt(text.substring(start + 1, pos), 8); + return String.fromCharCode(code); + } + return text.substring(start, pos); + case CharacterCodes._8: + case CharacterCodes._9: + tokenFlags |= TokenFlags.ContainsInvalidEscape; + if (flags & EscapeSequenceScanningFlags.ReportInvalidEscapeErrors) { + return String.fromCharCode(ch); + } + return text.substring(start, pos); + case CharacterCodes.b: + return "\b"; + case CharacterCodes.t: + return "\t"; + case CharacterCodes.n: + return "\n"; + case CharacterCodes.v: + return "\v"; + case CharacterCodes.f: + return "\f"; + case CharacterCodes.r: + return "\r"; + case CharacterCodes.singleQuote: + return "'"; + case CharacterCodes.doubleQuote: + return '"'; + case CharacterCodes.u: + if (pos < end && charCodeUnchecked(pos) === CharacterCodes.openBrace) { + pos -= 2; + const result = scanExtendedUnicodeEscape(); + if (!(flags & EscapeSequenceScanningFlags.AllowExtendedUnicodeEscape)) { + tokenFlags |= TokenFlags.ContainsInvalidEscape; + } + return result; + } + // '\uDDDD' + for (; pos < start + 6; pos++) { + if (!(pos < end && isHexDigit(charCodeUnchecked(pos)))) { + tokenFlags |= TokenFlags.ContainsInvalidEscape; + return text.substring(start, pos); + } + } + tokenFlags |= TokenFlags.UnicodeEscape; + { + const escapedValue = parseInt(text.substring(start + 2, pos), 16); + const escapedValueString = String.fromCharCode(escapedValue); + if ( + flags & EscapeSequenceScanningFlags.AnyUnicodeMode && escapedValue >= 0xD800 && escapedValue <= 0xDBFF && + pos + 6 < end && text.substring(pos, pos + 2) === "\\u" && charCodeUnchecked(pos + 2) !== CharacterCodes.openBrace + ) { + const nextStart = pos; + let nextPos = pos + 2; + for (; nextPos < nextStart + 6; nextPos++) { + if (!isHexDigit(charCodeUnchecked(nextPos))) { + return escapedValueString; + } + } + const nextEscapedValue = parseInt(text.substring(nextStart + 2, nextPos), 16); + if (nextEscapedValue >= 0xDC00 && nextEscapedValue <= 0xDFFF) { + pos = nextPos; + return escapedValueString + String.fromCharCode(nextEscapedValue); + } + } + return escapedValueString; + } + + case CharacterCodes.x: + for (; pos < start + 4; pos++) { + if (!(pos < end && isHexDigit(charCodeUnchecked(pos)))) { + tokenFlags |= TokenFlags.ContainsInvalidEscape; + return text.substring(start, pos); + } + } + tokenFlags |= TokenFlags.HexEscape; + return String.fromCharCode(parseInt(text.substring(start + 2, pos), 16)); + + case CharacterCodes.carriageReturn: + if (pos < end && charCodeUnchecked(pos) === CharacterCodes.lineFeed) { + pos++; + } + // falls through + case CharacterCodes.lineFeed: + case CharacterCodes.lineSeparator: + case CharacterCodes.paragraphSeparator: + return ""; + default: + return String.fromCharCode(ch); + } + } + + function scanExtendedUnicodeEscape(): string { + const start = pos; + pos += 3; + const escapedValueString = scanMinimumNumberOfHexDigits(1, /*canHaveSeparators*/ false); + const escapedValue = escapedValueString ? parseInt(escapedValueString, 16) : -1; + let isInvalidExtendedEscape = false; + + if (escapedValue < 0) { + isInvalidExtendedEscape = true; + } + else if (escapedValue > 0x10FFFF) { + isInvalidExtendedEscape = true; + } + + if (pos >= end) { + isInvalidExtendedEscape = true; + } + else if (charCodeUnchecked(pos) === CharacterCodes.closeBrace) { + pos++; + } + else { + isInvalidExtendedEscape = true; + } + + if (isInvalidExtendedEscape) { + tokenFlags |= TokenFlags.ContainsInvalidEscape; + return text.substring(start, pos); + } + + tokenFlags |= TokenFlags.ExtendedUnicodeEscape; + return utf16EncodeAsString(escapedValue); + } + + function peekUnicodeEscape(): number { + if (pos + 5 < end && charCodeUnchecked(pos + 1) === CharacterCodes.u) { + const start = pos; + pos += 2; + const value = scanExactNumberOfHexDigits(4, /*canHaveSeparators*/ false); + pos = start; + return value; + } + return -1; + } + + function peekExtendedUnicodeEscape(): number { + if (codePointUnchecked(pos + 1) === CharacterCodes.u && codePointUnchecked(pos + 2) === CharacterCodes.openBrace) { + const start = pos; + pos += 3; + const escapedValueString = scanMinimumNumberOfHexDigits(1, /*canHaveSeparators*/ false); + const escapedValue = escapedValueString ? parseInt(escapedValueString, 16) : -1; + pos = start; + return escapedValue; + } + return -1; + } + + function scanIdentifierParts(): string { + let result = ""; + let start = pos; + while (pos < end) { + let ch = codePointUnchecked(pos); + if (isIdentifierPart(ch)) { + pos += charSize(ch); + } + else if (ch === CharacterCodes.backslash) { + ch = peekExtendedUnicodeEscape(); + if (ch >= 0 && isIdentifierPart(ch)) { + result += scanExtendedUnicodeEscape(); + start = pos; + continue; + } + ch = peekUnicodeEscape(); + if (!(ch >= 0 && isIdentifierPart(ch))) { + break; + } + tokenFlags |= TokenFlags.UnicodeEscape; + result += text.substring(start, pos); + result += utf16EncodeAsString(ch); + pos += 6; + start = pos; + } + else { + break; + } + } + result += text.substring(start, pos); + return result; + } + + function getIdentifierToken(): SyntaxKind.Identifier | KeywordSyntaxKind { + const len = tokenValue.length; + if (len >= 2 && len <= 12) { + const ch = tokenValue.charCodeAt(0); + if (ch >= CharacterCodes.a && ch <= CharacterCodes.z) { + const keyword = textToKeyword.get(tokenValue); + if (keyword !== undefined) { + return token = keyword; + } + } + } + return token = SyntaxKind.Identifier; + } + + function scanBinaryOrOctalDigits(base: 2 | 8): string { + let value = ""; + let separatorAllowed = false; + let isPreviousTokenSeparator = false; + while (true) { + const ch = charCodeUnchecked(pos); + if (ch === CharacterCodes._) { + tokenFlags |= TokenFlags.ContainsSeparator; + if (separatorAllowed) { + separatorAllowed = false; + isPreviousTokenSeparator = true; + } + pos++; + continue; + } + separatorAllowed = true; + if (!isDigit(ch) || ch - CharacterCodes._0 >= base) { + break; + } + value += text[pos]; + pos++; + isPreviousTokenSeparator = false; + } + return value; + } + + function checkBigIntSuffix(): SyntaxKind { + if (charCodeUnchecked(pos) === CharacterCodes.n) { + tokenValue += "n"; + if (tokenFlags & TokenFlags.BinaryOrOctalSpecifier) { + tokenValue = parsePseudoBigInt(tokenValue) + "n"; + } + pos++; + return SyntaxKind.BigIntLiteral; + } + else { + const numericValue = tokenFlags & TokenFlags.BinarySpecifier + ? parseInt(tokenValue.slice(2), 2) + : tokenFlags & TokenFlags.OctalSpecifier + ? parseInt(tokenValue.slice(2), 8) + : +tokenValue; + tokenValue = "" + numericValue; + return SyntaxKind.NumericLiteral; + } + } + + function scan(): SyntaxKind { + fullStartPos = pos; + tokenFlags = TokenFlags.None; + while (true) { + tokenStart = pos; + if (pos >= end) { + return token = SyntaxKind.EndOfFile; + } + + const ch = codePointUnchecked(pos); + if (pos === 0) { + if (ch === CharacterCodes.hash && isShebangTrivia(text, pos)) { + pos = scanShebangTrivia(text, pos); + if (skipTrivia) { + continue; + } + else { + return token = SyntaxKind.Unknown; + } + } + } + + switch (ch) { + case CharacterCodes.lineFeed: + case CharacterCodes.carriageReturn: + tokenFlags |= TokenFlags.PrecedingLineBreak; + if (skipTrivia) { + pos++; + continue; + } + else { + if (ch === CharacterCodes.carriageReturn && pos + 1 < end && charCodeUnchecked(pos + 1) === CharacterCodes.lineFeed) { + pos += 2; + } + else { + pos++; + } + return token = SyntaxKind.NewLineTrivia; + } + case CharacterCodes.tab: + case CharacterCodes.verticalTab: + case CharacterCodes.formFeed: + case CharacterCodes.space: + case CharacterCodes.nonBreakingSpace: + case CharacterCodes.ogham: + case CharacterCodes.enQuad: + case CharacterCodes.emQuad: + case CharacterCodes.enSpace: + case CharacterCodes.emSpace: + case CharacterCodes.threePerEmSpace: + case CharacterCodes.fourPerEmSpace: + case CharacterCodes.sixPerEmSpace: + case CharacterCodes.figureSpace: + case CharacterCodes.punctuationSpace: + case CharacterCodes.thinSpace: + case CharacterCodes.hairSpace: + case CharacterCodes.zeroWidthSpace: + case CharacterCodes.narrowNoBreakSpace: + case CharacterCodes.mathematicalSpace: + case CharacterCodes.ideographicSpace: + case CharacterCodes.byteOrderMark: + if (skipTrivia) { + pos++; + continue; + } + else { + while (pos < end && isWhiteSpaceSingleLine(charCodeUnchecked(pos))) { + pos++; + } + return token = SyntaxKind.WhitespaceTrivia; + } + case CharacterCodes.exclamation: + if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) { + if (charCodeUnchecked(pos + 2) === CharacterCodes.equals) { + return pos += 3, token = SyntaxKind.ExclamationEqualsEqualsToken; + } + return pos += 2, token = SyntaxKind.ExclamationEqualsToken; + } + pos++; + return token = SyntaxKind.ExclamationToken; + case CharacterCodes.doubleQuote: + case CharacterCodes.singleQuote: + tokenValue = scanString(); + return token = SyntaxKind.StringLiteral; + case CharacterCodes.backtick: + return token = scanTemplateAndSetTokenValue(/*shouldEmitInvalidEscapeError*/ false); + case CharacterCodes.percent: + if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) { + return pos += 2, token = SyntaxKind.PercentEqualsToken; + } + pos++; + return token = SyntaxKind.PercentToken; + case CharacterCodes.ampersand: + if (charCodeUnchecked(pos + 1) === CharacterCodes.ampersand) { + if (charCodeUnchecked(pos + 2) === CharacterCodes.equals) { + return pos += 3, token = SyntaxKind.AmpersandAmpersandEqualsToken; + } + return pos += 2, token = SyntaxKind.AmpersandAmpersandToken; + } + if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) { + return pos += 2, token = SyntaxKind.AmpersandEqualsToken; + } + pos++; + return token = SyntaxKind.AmpersandToken; + case CharacterCodes.openParen: + pos++; + return token = SyntaxKind.OpenParenToken; + case CharacterCodes.closeParen: + pos++; + return token = SyntaxKind.CloseParenToken; + case CharacterCodes.asterisk: + if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) { + return pos += 2, token = SyntaxKind.AsteriskEqualsToken; + } + if (charCodeUnchecked(pos + 1) === CharacterCodes.asterisk) { + if (charCodeUnchecked(pos + 2) === CharacterCodes.equals) { + return pos += 3, token = SyntaxKind.AsteriskAsteriskEqualsToken; + } + return pos += 2, token = SyntaxKind.AsteriskAsteriskToken; + } + pos++; + if ( + skipJsDocLeadingAsterisks && + (tokenFlags & TokenFlags.PrecedingJSDocLeadingAsterisks) === 0 && + (tokenFlags & TokenFlags.PrecedingLineBreak) + ) { + tokenFlags |= TokenFlags.PrecedingJSDocLeadingAsterisks; + continue; + } + return token = SyntaxKind.AsteriskToken; + case CharacterCodes.plus: + if (charCodeUnchecked(pos + 1) === CharacterCodes.plus) { + return pos += 2, token = SyntaxKind.PlusPlusToken; + } + if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) { + return pos += 2, token = SyntaxKind.PlusEqualsToken; + } + pos++; + return token = SyntaxKind.PlusToken; + case CharacterCodes.comma: + pos++; + return token = SyntaxKind.CommaToken; + case CharacterCodes.minus: + if (charCodeUnchecked(pos + 1) === CharacterCodes.minus) { + return pos += 2, token = SyntaxKind.MinusMinusToken; + } + if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) { + return pos += 2, token = SyntaxKind.MinusEqualsToken; + } + pos++; + return token = SyntaxKind.MinusToken; + case CharacterCodes.dot: + if (isDigit(charCodeUnchecked(pos + 1))) { + scanNumber(); + return token = SyntaxKind.NumericLiteral; + } + if (charCodeUnchecked(pos + 1) === CharacterCodes.dot && charCodeUnchecked(pos + 2) === CharacterCodes.dot) { + return pos += 3, token = SyntaxKind.DotDotDotToken; + } + pos++; + return token = SyntaxKind.DotToken; + case CharacterCodes.slash: + // Single-line comment + if (charCodeUnchecked(pos + 1) === CharacterCodes.slash) { + pos += 2; + + while (pos < end) { + if (isLineBreak(charCodeUnchecked(pos))) { + break; + } + pos++; + } + + commentDirectives = appendIfCommentDirective( + commentDirectives, + text.slice(tokenStart, pos), + commentDirectiveRegExSingleLine, + tokenStart, + ); + + if (skipTrivia) { + continue; + } + else { + return token = SyntaxKind.SingleLineCommentTrivia; + } + } + // Multi-line comment + if (charCodeUnchecked(pos + 1) === CharacterCodes.asterisk) { + pos += 2; + const isJSDoc = charCodeUnchecked(pos) === CharacterCodes.asterisk && charCodeUnchecked(pos + 1) !== CharacterCodes.slash; + + let commentClosed = false; + let lastLineStart = tokenStart; + while (pos < end) { + const ch = charCodeUnchecked(pos); + + if (ch === CharacterCodes.asterisk && charCodeUnchecked(pos + 1) === CharacterCodes.slash) { + pos += 2; + commentClosed = true; + break; + } + + pos++; + + if (isLineBreak(ch)) { + lastLineStart = pos; + tokenFlags |= TokenFlags.PrecedingLineBreak; + } + } + + if (isJSDoc) { + tokenFlags |= TokenFlags.PrecedingJSDocComment; + tokenFlags = scanJSDocCommentForTags(text.slice(tokenStart, pos), tokenFlags); + } + + commentDirectives = appendIfCommentDirective(commentDirectives, text.slice(lastLineStart, pos), commentDirectiveRegExMultiLine, lastLineStart); + + if (skipTrivia) { + continue; + } + else { + if (!commentClosed) { + tokenFlags |= TokenFlags.Unterminated; + } + return token = SyntaxKind.MultiLineCommentTrivia; + } + } + + if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) { + return pos += 2, token = SyntaxKind.SlashEqualsToken; + } + + pos++; + return token = SyntaxKind.SlashToken; + + case CharacterCodes._0: + if (pos + 2 < end && (charCodeUnchecked(pos + 1) === CharacterCodes.X || charCodeUnchecked(pos + 1) === CharacterCodes.x)) { + pos += 2; + tokenValue = scanMinimumNumberOfHexDigits(1, /*canHaveSeparators*/ true); + if (!tokenValue) { + tokenValue = "0"; + } + tokenValue = "0x" + tokenValue; + tokenFlags |= TokenFlags.HexSpecifier; + return token = checkBigIntSuffix(); + } + else if (pos + 2 < end && (charCodeUnchecked(pos + 1) === CharacterCodes.B || charCodeUnchecked(pos + 1) === CharacterCodes.b)) { + pos += 2; + tokenValue = scanBinaryOrOctalDigits(2); + if (!tokenValue) { + tokenValue = "0"; + } + tokenValue = "0b" + tokenValue; + tokenFlags |= TokenFlags.BinarySpecifier; + return token = checkBigIntSuffix(); + } + else if (pos + 2 < end && (charCodeUnchecked(pos + 1) === CharacterCodes.O || charCodeUnchecked(pos + 1) === CharacterCodes.o)) { + pos += 2; + tokenValue = scanBinaryOrOctalDigits(8); + if (!tokenValue) { + tokenValue = "0"; + } + tokenValue = "0o" + tokenValue; + tokenFlags |= TokenFlags.OctalSpecifier; + return token = checkBigIntSuffix(); + } + // falls through + case CharacterCodes._1: + case CharacterCodes._2: + case CharacterCodes._3: + case CharacterCodes._4: + case CharacterCodes._5: + case CharacterCodes._6: + case CharacterCodes._7: + case CharacterCodes._8: + case CharacterCodes._9: + return token = scanNumber(); + case CharacterCodes.colon: + pos++; + return token = SyntaxKind.ColonToken; + case CharacterCodes.semicolon: + pos++; + return token = SyntaxKind.SemicolonToken; + case CharacterCodes.lessThan: + if (isConflictMarkerTrivia(text, pos)) { + pos = scanConflictMarkerTrivia(text, pos); + if (skipTrivia) { + continue; + } + else { + return token = SyntaxKind.ConflictMarkerTrivia; + } + } + + if (charCodeUnchecked(pos + 1) === CharacterCodes.lessThan) { + if (charCodeUnchecked(pos + 2) === CharacterCodes.equals) { + return pos += 3, token = SyntaxKind.LessThanLessThanEqualsToken; + } + return pos += 2, token = SyntaxKind.LessThanLessThanToken; + } + if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) { + return pos += 2, token = SyntaxKind.LessThanEqualsToken; + } + if ( + languageVariant === LanguageVariant.JSX && + charCodeUnchecked(pos + 1) === CharacterCodes.slash && + charCodeUnchecked(pos + 2) !== CharacterCodes.asterisk + ) { + return pos += 2, token = SyntaxKind.LessThanSlashToken; + } + pos++; + return token = SyntaxKind.LessThanToken; + case CharacterCodes.equals: + if (isConflictMarkerTrivia(text, pos)) { + pos = scanConflictMarkerTrivia(text, pos); + if (skipTrivia) { + continue; + } + else { + return token = SyntaxKind.ConflictMarkerTrivia; + } + } + + if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) { + if (charCodeUnchecked(pos + 2) === CharacterCodes.equals) { + return pos += 3, token = SyntaxKind.EqualsEqualsEqualsToken; + } + return pos += 2, token = SyntaxKind.EqualsEqualsToken; + } + if (charCodeUnchecked(pos + 1) === CharacterCodes.greaterThan) { + return pos += 2, token = SyntaxKind.EqualsGreaterThanToken; + } + pos++; + return token = SyntaxKind.EqualsToken; + case CharacterCodes.greaterThan: + if (isConflictMarkerTrivia(text, pos)) { + pos = scanConflictMarkerTrivia(text, pos); + if (skipTrivia) { + continue; + } + else { + return token = SyntaxKind.ConflictMarkerTrivia; + } + } + + pos++; + return token = SyntaxKind.GreaterThanToken; + case CharacterCodes.question: + if (charCodeUnchecked(pos + 1) === CharacterCodes.dot && !isDigit(charCodeUnchecked(pos + 2))) { + return pos += 2, token = SyntaxKind.QuestionDotToken; + } + if (charCodeUnchecked(pos + 1) === CharacterCodes.question) { + if (charCodeUnchecked(pos + 2) === CharacterCodes.equals) { + return pos += 3, token = SyntaxKind.QuestionQuestionEqualsToken; + } + return pos += 2, token = SyntaxKind.QuestionQuestionToken; + } + pos++; + return token = SyntaxKind.QuestionToken; + case CharacterCodes.openBracket: + pos++; + return token = SyntaxKind.OpenBracketToken; + case CharacterCodes.closeBracket: + pos++; + return token = SyntaxKind.CloseBracketToken; + case CharacterCodes.caret: + if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) { + return pos += 2, token = SyntaxKind.CaretEqualsToken; + } + pos++; + return token = SyntaxKind.CaretToken; + case CharacterCodes.openBrace: + pos++; + return token = SyntaxKind.OpenBraceToken; + case CharacterCodes.bar: + if (isConflictMarkerTrivia(text, pos)) { + pos = scanConflictMarkerTrivia(text, pos); + if (skipTrivia) { + continue; + } + else { + return token = SyntaxKind.ConflictMarkerTrivia; + } + } + + if (charCodeUnchecked(pos + 1) === CharacterCodes.bar) { + if (charCodeUnchecked(pos + 2) === CharacterCodes.equals) { + return pos += 3, token = SyntaxKind.BarBarEqualsToken; + } + return pos += 2, token = SyntaxKind.BarBarToken; + } + if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) { + return pos += 2, token = SyntaxKind.BarEqualsToken; + } + pos++; + return token = SyntaxKind.BarToken; + case CharacterCodes.closeBrace: + pos++; + return token = SyntaxKind.CloseBraceToken; + case CharacterCodes.tilde: + pos++; + return token = SyntaxKind.TildeToken; + case CharacterCodes.at: + pos++; + return token = SyntaxKind.AtToken; + case CharacterCodes.backslash: { + const extendedCookedChar = peekExtendedUnicodeEscape(); + if (extendedCookedChar >= 0 && isIdentifierStart(extendedCookedChar)) { + tokenValue = scanExtendedUnicodeEscape() + scanIdentifierParts(); + return token = getIdentifierToken(); + } + + const cookedChar = peekUnicodeEscape(); + if (cookedChar >= 0 && isIdentifierStart(cookedChar)) { + pos += 6; + tokenFlags |= TokenFlags.UnicodeEscape; + tokenValue = String.fromCharCode(cookedChar) + scanIdentifierParts(); + return token = getIdentifierToken(); + } + + pos++; + return token = SyntaxKind.Unknown; + } + case CharacterCodes.hash: + if (pos !== 0 && text[pos + 1] === "!") { + pos++; + return token = SyntaxKind.Unknown; + } + + { + const charAfterHash = codePointUnchecked(pos + 1); + if (charAfterHash === CharacterCodes.backslash) { + pos++; + const extendedCookedChar = peekExtendedUnicodeEscape(); + if (extendedCookedChar >= 0 && isIdentifierStart(extendedCookedChar)) { + tokenValue = "#" + scanExtendedUnicodeEscape() + scanIdentifierParts(); + return token = SyntaxKind.PrivateIdentifier; + } + + const cookedChar = peekUnicodeEscape(); + if (cookedChar >= 0 && isIdentifierStart(cookedChar)) { + pos += 6; + tokenFlags |= TokenFlags.UnicodeEscape; + tokenValue = "#" + String.fromCharCode(cookedChar) + scanIdentifierParts(); + return token = SyntaxKind.PrivateIdentifier; + } + pos--; + } + + if (isIdentifierStart(charAfterHash)) { + pos++; + scanIdentifier(charAfterHash); + } + else { + tokenValue = "#"; + } + return token = SyntaxKind.PrivateIdentifier; + } + case CharacterCodes.replacementCharacter: + pos = end; + return token = SyntaxKind.NonTextFileMarkerTrivia; + default: { + const identifierKind = scanIdentifier(ch); + if (identifierKind) { + return token = identifierKind; + } + else if (isWhiteSpaceSingleLine(ch)) { + pos += charSize(ch); + continue; + } + else if (isLineBreak(ch)) { + tokenFlags |= TokenFlags.PrecedingLineBreak; + pos += charSize(ch); + continue; + } + const size = charSize(ch); + pos += size; + return token = SyntaxKind.Unknown; + } + } + } + } + + function reScanInvalidIdentifier(): SyntaxKind { + pos = tokenStart = fullStartPos; + tokenFlags = 0; + const ch = codePointUnchecked(pos); + const identifierKind = scanIdentifier(ch); + if (identifierKind) { + return token = identifierKind; + } + pos += charSize(ch); + return token; // Still `SyntaxKind.Unknown` + } + + function scanIdentifier(startCharacter: number) { + let ch = startCharacter; + if (isIdentifierStart(ch)) { + pos += charSize(ch); + while (pos < end && isIdentifierPart(ch = codePointUnchecked(pos))) pos += charSize(ch); + tokenValue = text.substring(tokenStart, pos); + if (ch === CharacterCodes.backslash) { + tokenValue += scanIdentifierParts(); + } + return getIdentifierToken(); + } + } + + function reScanGreaterToken(): SyntaxKind { + if (token === SyntaxKind.GreaterThanToken) { + if (charCodeUnchecked(pos) === CharacterCodes.greaterThan) { + if (charCodeUnchecked(pos + 1) === CharacterCodes.greaterThan) { + if (charCodeUnchecked(pos + 2) === CharacterCodes.equals) { + return pos += 3, token = SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken; + } + return pos += 2, token = SyntaxKind.GreaterThanGreaterThanGreaterThanToken; + } + if (charCodeUnchecked(pos + 1) === CharacterCodes.equals) { + return pos += 2, token = SyntaxKind.GreaterThanGreaterThanEqualsToken; + } + pos++; + return token = SyntaxKind.GreaterThanGreaterThanToken; + } + if (charCodeUnchecked(pos) === CharacterCodes.equals) { + pos++; + return token = SyntaxKind.GreaterThanEqualsToken; + } + } + return token; + } + + function reScanAsteriskEqualsToken(): SyntaxKind { + pos = tokenStart + 1; + return token = SyntaxKind.EqualsToken; + } + + function reScanSlashToken(): SyntaxKind { + if (token === SyntaxKind.SlashToken || token === SyntaxKind.SlashEqualsToken) { + const startOfRegExpBody = tokenStart + 1; + pos = startOfRegExpBody; + let inEscape = false; + let inCharacterClass = false; + while (true) { + const ch = charCodeChecked(pos); + if (ch === CharacterCodes.EOF || isLineBreak(ch)) { + tokenFlags |= TokenFlags.Unterminated; + break; + } + + if (inEscape) { + inEscape = false; + } + else if (ch === CharacterCodes.slash && !inCharacterClass) { + break; + } + else if (ch === CharacterCodes.openBracket) { + inCharacterClass = true; + } + else if (ch === CharacterCodes.backslash) { + inEscape = true; + } + else if (ch === CharacterCodes.closeBracket) { + inCharacterClass = false; + } + pos++; + } + + if (tokenFlags & TokenFlags.Unterminated) { + // Find best recovery position + pos = startOfRegExpBody; + inEscape = false; + let characterClassDepth = 0; + let inDecimalQuantifier = false; + let groupDepth = 0; + while (pos < end && !isLineBreak(charCodeUnchecked(pos))) { + const ch = charCodeUnchecked(pos); + if (inEscape) { + inEscape = false; + } + else if (ch === CharacterCodes.backslash) { + inEscape = true; + } + else if (ch === CharacterCodes.openBracket) { + characterClassDepth++; + } + else if (ch === CharacterCodes.closeBracket && characterClassDepth) { + characterClassDepth--; + } + else if (!characterClassDepth) { + if (ch === CharacterCodes.openBrace) { + inDecimalQuantifier = true; + } + else if (ch === CharacterCodes.closeBrace && inDecimalQuantifier) { + inDecimalQuantifier = false; + } + else if (!inDecimalQuantifier) { + if (ch === CharacterCodes.openParen) { + groupDepth++; + } + else if (ch === CharacterCodes.closeParen && groupDepth) { + groupDepth--; + } + else if (ch === CharacterCodes.closeParen || ch === CharacterCodes.closeBracket || ch === CharacterCodes.closeBrace) { + break; + } + } + } + pos++; + } + while (isWhiteSpaceLike(charCodeChecked(pos - 1)) || charCodeChecked(pos - 1) === CharacterCodes.semicolon) pos--; + } + else { + pos++; + let regExpFlags: number = RegularExpressionFlags.None; + while (true) { + const ch = codePointChecked(pos); + if (ch === CharacterCodes.EOF || !isIdentifierPart(ch)) { + break; + } + const size = charSize(ch); + const flag = characterCodeToRegularExpressionFlag(ch); + if (flag !== undefined) { + regExpFlags |= flag; + } + pos += size; + } + } + tokenValue = text.substring(tokenStart, pos); + token = SyntaxKind.RegularExpressionLiteral; + } + return token; + } + + function appendIfCommentDirective( + commentDirectives: CommentDirective[] | undefined, + text: string, + commentDirectiveRegEx: RegExp, + lineStart: number, + ) { + const type = getDirectiveFromComment(text.trimStart(), commentDirectiveRegEx); + if (type === undefined) { + return commentDirectives; + } + + if (!commentDirectives) { + commentDirectives = []; + } + commentDirectives.push({ + range: { pos: lineStart, end: pos }, + type, + }); + return commentDirectives; + } + + function getDirectiveFromComment(text: string, commentDirectiveRegEx: RegExp) { + const match = commentDirectiveRegEx.exec(text); + if (!match) { + return undefined; + } + + switch (match[1]) { + case "ts-expect-error": + return CommentDirectiveType.ExpectError; + case "ts-ignore": + return CommentDirectiveType.Ignore; + } + + return undefined; + } + + function reScanTemplateToken(isTaggedTemplate: boolean): SyntaxKind { + pos = tokenStart; + return token = scanTemplateAndSetTokenValue(!isTaggedTemplate); + } + + function reScanTemplateHeadOrNoSubstitutionTemplate(): SyntaxKind { + pos = tokenStart; + return token = scanTemplateAndSetTokenValue(/*shouldEmitInvalidEscapeError*/ true); + } + + function reScanJsxToken(allowMultilineJsxText = true): JsxTokenSyntaxKind { + pos = tokenStart = fullStartPos; + return token = scanJsxToken(allowMultilineJsxText); + } + + function reScanLessThanToken(): SyntaxKind { + if (token === SyntaxKind.LessThanLessThanToken) { + pos = tokenStart + 1; + return token = SyntaxKind.LessThanToken; + } + return token; + } + + function reScanHashToken(): SyntaxKind { + if (token === SyntaxKind.PrivateIdentifier) { + pos = tokenStart + 1; + return token = SyntaxKind.HashToken; + } + return token; + } + + function reScanQuestionToken(): SyntaxKind { + pos = tokenStart + 1; + return token = SyntaxKind.QuestionToken; + } + + function scanJsxToken(allowMultilineJsxText = true): JsxTokenSyntaxKind { + fullStartPos = tokenStart = pos; + + if (pos >= end) { + return token = SyntaxKind.EndOfFile; + } + + let char = charCodeUnchecked(pos); + if (char === CharacterCodes.lessThan) { + if (charCodeUnchecked(pos + 1) === CharacterCodes.slash) { + pos += 2; + return token = SyntaxKind.LessThanSlashToken; + } + pos++; + return token = SyntaxKind.LessThanToken; + } + + if (char === CharacterCodes.openBrace) { + pos++; + return token = SyntaxKind.OpenBraceToken; + } + + let firstNonWhitespace = 0; + + while (pos < end) { + char = charCodeUnchecked(pos); + if (char === CharacterCodes.openBrace) { + break; + } + if (char === CharacterCodes.lessThan) { + if (isConflictMarkerTrivia(text, pos)) { + pos = scanConflictMarkerTrivia(text, pos); + return token = SyntaxKind.ConflictMarkerTrivia; + } + break; + } + + if (isLineBreak(char) && firstNonWhitespace === 0) { + firstNonWhitespace = -1; + } + else if (!allowMultilineJsxText && isLineBreak(char) && firstNonWhitespace > 0) { + break; + } + else if (!isWhiteSpaceLike(char)) { + firstNonWhitespace = pos; + } + + pos++; + } + + tokenValue = text.substring(fullStartPos, pos); + + return firstNonWhitespace === -1 ? SyntaxKind.JsxTextAllWhiteSpaces : SyntaxKind.JsxText; + } + + function scanJsxIdentifier(): SyntaxKind { + if (tokenIsIdentifierOrKeyword(token)) { + while (pos < end) { + const ch = charCodeUnchecked(pos); + if (ch === CharacterCodes.minus) { + tokenValue += "-"; + pos++; + continue; + } + const oldPos = pos; + tokenValue += scanIdentifierParts(); + if (pos === oldPos) { + break; + } + } + return getIdentifierToken(); + } + return token; + } + + function scanJsxAttributeValue(): SyntaxKind { + fullStartPos = pos; + + switch (charCodeUnchecked(pos)) { + case CharacterCodes.doubleQuote: + case CharacterCodes.singleQuote: + tokenValue = scanString(/*jsxAttributeString*/ true); + return token = SyntaxKind.StringLiteral; + default: + return scan(); + } + } + + function reScanJsxAttributeValue(): SyntaxKind { + pos = tokenStart = fullStartPos; + return scanJsxAttributeValue(); + } + + function scanJSDocCommentTextToken(inBackticks: boolean): JSDocTokenKind | SyntaxKind.JSDocCommentTextToken { + fullStartPos = tokenStart = pos; + tokenFlags = TokenFlags.None; + if (pos >= end) { + return token = SyntaxKind.EndOfFile; + } + for (let ch = charCodeUnchecked(pos); pos < end && (!isLineBreak(ch) && ch !== CharacterCodes.backtick); ch = codePointUnchecked(++pos)) { + if (!inBackticks) { + if (ch === CharacterCodes.openBrace) { + break; + } + else if ( + ch === CharacterCodes.at + && pos - 1 >= 0 && isWhiteSpaceSingleLine(charCodeUnchecked(pos - 1)) + && pos + 1 < end && isIdentifierStart(charCodeUnchecked(pos + 1)) + ) { + break; + } + } + } + if (pos === tokenStart) { + return scanJsDocToken(); + } + tokenValue = text.substring(tokenStart, pos); + return token = SyntaxKind.JSDocCommentTextToken; + } + + function scanJsDocToken(): JSDocTokenKind { + fullStartPos = tokenStart = pos; + tokenFlags = TokenFlags.None; + if (pos >= end) { + return token = SyntaxKind.EndOfFile; + } + + const ch = codePointUnchecked(pos); + pos += charSize(ch); + switch (ch) { + case CharacterCodes.tab: + case CharacterCodes.verticalTab: + case CharacterCodes.formFeed: + case CharacterCodes.space: + while (pos < end && isWhiteSpaceSingleLine(charCodeUnchecked(pos))) { + pos++; + } + return token = SyntaxKind.WhitespaceTrivia; + case CharacterCodes.at: + return token = SyntaxKind.AtToken; + case CharacterCodes.carriageReturn: + if (charCodeUnchecked(pos) === CharacterCodes.lineFeed) { + pos++; + } + // falls through + case CharacterCodes.lineFeed: + tokenFlags |= TokenFlags.PrecedingLineBreak; + return token = SyntaxKind.NewLineTrivia; + case CharacterCodes.asterisk: + return token = SyntaxKind.AsteriskToken; + case CharacterCodes.openBrace: + return token = SyntaxKind.OpenBraceToken; + case CharacterCodes.closeBrace: + return token = SyntaxKind.CloseBraceToken; + case CharacterCodes.openBracket: + return token = SyntaxKind.OpenBracketToken; + case CharacterCodes.closeBracket: + return token = SyntaxKind.CloseBracketToken; + case CharacterCodes.openParen: + return token = SyntaxKind.OpenParenToken; + case CharacterCodes.closeParen: + return token = SyntaxKind.CloseParenToken; + case CharacterCodes.lessThan: + return token = SyntaxKind.LessThanToken; + case CharacterCodes.greaterThan: + return token = SyntaxKind.GreaterThanToken; + case CharacterCodes.equals: + return token = SyntaxKind.EqualsToken; + case CharacterCodes.comma: + return token = SyntaxKind.CommaToken; + case CharacterCodes.dot: + return token = SyntaxKind.DotToken; + case CharacterCodes.backtick: + return token = SyntaxKind.BacktickToken; + case CharacterCodes.hash: + return token = SyntaxKind.HashToken; + case CharacterCodes.backslash: + pos--; + { + const extendedCookedChar = peekExtendedUnicodeEscape(); + if (extendedCookedChar >= 0 && isIdentifierStart(extendedCookedChar)) { + tokenValue = scanExtendedUnicodeEscape() + scanIdentifierParts(); + return token = getIdentifierToken(); + } + + const cookedChar = peekUnicodeEscape(); + if (cookedChar >= 0 && isIdentifierStart(cookedChar)) { + pos += 6; + tokenFlags |= TokenFlags.UnicodeEscape; + tokenValue = String.fromCharCode(cookedChar) + scanIdentifierParts(); + return token = getIdentifierToken(); + } + } + pos++; + return token = SyntaxKind.Unknown; + } + + if (isIdentifierStart(ch)) { + let char = ch; + while (pos < end && isIdentifierPart(char = codePointUnchecked(pos)) || char === CharacterCodes.minus) pos += charSize(char); + tokenValue = text.substring(tokenStart, pos); + if (char === CharacterCodes.backslash) { + tokenValue += scanIdentifierParts(); + } + return token = getIdentifierToken(); + } + else { + return token = SyntaxKind.Unknown; + } + } + + function speculationHelper(callback: () => T, isLookahead: boolean): T { + const savePos = pos; + const saveStartPos = fullStartPos; + const saveTokenPos = tokenStart; + const saveToken = token; + const saveTokenValue = tokenValue; + const saveTokenFlags = tokenFlags; + const result = callback(); + + if (!result || isLookahead) { + pos = savePos; + fullStartPos = saveStartPos; + tokenStart = saveTokenPos; + token = saveToken; + tokenValue = saveTokenValue; + tokenFlags = saveTokenFlags; + } + return result; + } + + function scanRange(start: number, length: number, callback: () => T): T { + const saveEnd = end; + const savePos = pos; + const saveStartPos = fullStartPos; + const saveTokenPos = tokenStart; + const saveToken = token; + const saveTokenValue = tokenValue; + const saveTokenFlags = tokenFlags; + const saveErrorExpectations = commentDirectives; + + setText(text, start, length); + const result = callback(); + + end = saveEnd; + pos = savePos; + fullStartPos = saveStartPos; + tokenStart = saveTokenPos; + token = saveToken; + tokenValue = saveTokenValue; + tokenFlags = saveTokenFlags; + commentDirectives = saveErrorExpectations; + + return result; + } + + function lookAhead(callback: () => T): T { + return speculationHelper(callback, /*isLookahead*/ true); + } + + function tryScan(callback: () => T): T { + return speculationHelper(callback, /*isLookahead*/ false); + } + + function getText(): string { + return text; + } + + function clearCommentDirectives() { + commentDirectives = undefined; + } + + function setText(newText: string | undefined, start: number | undefined, length: number | undefined) { + text = newText || ""; + end = length === undefined ? text.length : start! + length; + resetTokenState(start || 0); + } + + function setLanguageVariant(variant: LanguageVariant) { + languageVariant = variant; + } + + function resetTokenState(position: number) { + pos = position; + fullStartPos = position; + tokenStart = position; + token = SyntaxKind.Unknown; + tokenValue = undefined!; + tokenFlags = TokenFlags.None; + } + + function setSkipJsDocLeadingAsterisks(skip: boolean) { + skipJsDocLeadingAsterisks += skip ? 1 : -1; + } +} diff --git a/packages/typescript/src/ast/spanMap.ts b/packages/typescript/src/ast/spanMap.ts index dee0610ab2bee..09648de559e17 100644 --- a/packages/typescript/src/ast/spanMap.ts +++ b/packages/typescript/src/ast/spanMap.ts @@ -1,483 +1,483 @@ -import { SpanMapFeature } from "#enums/spanMapFeature"; -import { SpanMapFidelity } from "#enums/spanMapFidelity"; -import { SpanMapKind } from "#enums/spanMapKind"; -import type { ReadonlyTextRange } from "./ast.ts"; - -export { SpanMapFeature, SpanMapFidelity, SpanMapKind }; - -// Keep this in sync with spanmap.go - -/** Maps one half-open virtual range to one half-open original range. */ -export interface SpanMapSegment { - readonly virtualStart: number; - readonly virtualEnd: number; - readonly originalStart: number; - readonly originalEnd: number; - readonly kind: SpanMapKind; - readonly features?: SpanMapFeature; -} - -/** Internal segment representation after omitted features have been normalized to `All`. */ -type NormalizedSpanMapSegment = SpanMapSegment & { readonly features: SpanMapFeature; }; - -/** Lazily built interval index for original-to-virtual lookups. */ -interface OriginalIndex { - readonly segments: readonly NormalizedSpanMapSegment[]; - readonly leafCount: number; - readonly maxEnds: readonly number[]; -} - -/** One virtual projection of an original position and its mapping fidelity. */ -export interface MappedPosition { - readonly position: number; - readonly fidelity: SpanMapFidelity; -} - -/** One virtual projection of an original range and its mapping fidelity. */ -export interface MappedRange { - readonly range: ReadonlyTextRange; - readonly fidelity: SpanMapFidelity; -} - -/** Provides bidirectional span-aware mapping between virtual and original text. */ -export class SpanMap { - readonly segments: readonly NormalizedSpanMapSegment[]; - private originalIndex: OriginalIndex | undefined; - - /** Copies and sorts segments by virtual start, normalizing omitted features to `All`. */ - constructor(segments: readonly SpanMapSegment[]) { - this.segments = segments - .map(segment => ({ ...segment, features: segment.features ?? SpanMapFeature.All })) - .sort((left, right) => left.virtualStart - right.virtualStart); - } - - /** Reports whether a mapping is a precise, edit-safe projection through one verbatim segment. */ - static isExact(fidelity: SpanMapFidelity): boolean { - return fidelity === SpanMapFidelity.Exact; - } - - /** Reports whether a mapping lies in one verbatim or atom segment. */ - static isSingleSegment(fidelity: SpanMapFidelity): boolean { - return fidelity === SpanMapFidelity.Exact || fidelity === SpanMapFidelity.Atom; - } - - /** Reports whether the input had no counterpart in the target text. */ - static isNone(fidelity: SpanMapFidelity): boolean { - return fidelity === SpanMapFidelity.None; - } - - /** - * Maps a virtual range to original text. Gaps map to insertion points with `None` fidelity, - * and ranges crossing segment boundaries map their endpoints with `Approximate` fidelity. - */ - virtualToOriginalSpan(range: ReadonlyTextRange): MappedRange { - return this.mapRange(range, this.segments, false); - } - - /** Maps a visible LS result only when every covered segment participates in `feature`. */ - virtualToOriginalSpanForFeature(range: ReadonlyTextRange, feature: SpanMapFeature): MappedRange { - const mapped = this.virtualToOriginalSpan(range); - return this.virtualRangeSupportsFeature(range, feature) ? mapped : { ...mapped, fidelity: SpanMapFidelity.None }; - } - - /** Maps a virtual position to original text, using `None` fidelity for synthesized gaps. */ - virtualToOriginalPosition(position: number): MappedPosition { - return this.mapPoint(position, this.segments, false); - } - - virtualToOriginalPositionForFeature(position: number, feature: SpanMapFeature): MappedPosition { - const mapped = this.virtualToOriginalPosition(position); - const [index, inside] = segmentIndexAt(this.segments, position, false); - return inside && supportsFeature(this.segments[index], feature) ? mapped : { ...mapped, fidelity: SpanMapFidelity.None }; - } - - /** - * Returns every virtual projection of an original position whose segment participates in `feature`. - * Segment ends are inclusive for point mapping, so adjacent spans may both produce projections. - * Results are ordered by virtual position; uncovered or disabled positions produce no results. - */ - originalToVirtualPositions(position: number, feature: SpanMapFeature): readonly MappedPosition[] { - const groups = segmentGroupsAtOriginalPosition(this.getOriginalIndex(), position); - const results: MappedPosition[] = []; - for (const group of groups) { - for (const segment of group.segments) { - if (!supportsFeature(segment, feature)) continue; - const mapped = segment.kind === SpanMapKind.Verbatim - ? { position: mapVerbatimPosition(segment, position, true), fidelity: SpanMapFidelity.Exact } - : { position: group.atEnd ? segment.virtualEnd : segment.virtualStart, fidelity: SpanMapFidelity.Atom }; - if (!results.some(result => result.position === mapped.position && result.fidelity === mapped.fidelity)) { - results.push(mapped); - } - } - } - return results.sort((left, right) => left.position - right.position); - } - - /** - * Returns every feature-compatible virtual projection of an original range. - * A range contained by one or more segments produces one exact or atom result per matching segment. - * - * A range that starts in one group and ends in another can have several possible virtual ranges. For - * example, suppose two original segments are each copied twice into the virtual text: - * - * ```text - * original: [ A ][ B ] - * [---) range from inside A to inside B - * - * virtual: [ A ][ B ] [ A ][ B ] - * ^ ^ ^ ^ - * start end start end - * 1 3 11 13 - * ``` - * - * The map says that the range may start at 1 or 11 and end at 3 or 13, but it does not say which copy of A - * belongs with which copy of B. We choose the smallest range around each possible location, producing [1,3) - * and [11,13). We do not return [1,13), because it contains both smaller candidates and would include code - * that may be unrelated to the original range. These cross-group results have approximate fidelity. - */ - originalToVirtualSpans(range: ReadonlyTextRange, feature: SpanMapFeature): readonly MappedRange[] { - const start = range.pos; - const end = Math.max(range.end, start); - if (start === end) { - return this.originalToVirtualPositions(start, feature).map(({ position, fidelity }) => ({ - range: { pos: position, end: position }, - fidelity, - })); - } - const lastCharacter = end - 1; - const originalIndex = this.getOriginalIndex(); - const startSegments = segmentsAtOriginalPosition(originalIndex, start); - const endSegments = segmentsAtOriginalPosition(originalIndex, lastCharacter); - if (!startSegments || !endSegments) return []; - const containing = startSegments.filter(segment => end <= segment.originalEnd); - if (containing.length > 0) { - const results = [...originalToVirtualSpansInSegments(containing, start, end, feature)]; - if (results.length > 0) return results.sort((left, right) => left.range.pos - right.range.pos); - } - const starts = [...originalStartProjections(startSegments, start, feature)].sort((left, right) => left - right); - const ends = [...originalEndProjections(endSegments, end, feature)].sort((left, right) => left - right); - if (starts.length === 0 || ends.length === 0) return []; - return starts.flatMap((virtualStart, index) => { - const virtualEnd = ends.find(end => end >= virtualStart); - return virtualEnd === undefined || index + 1 < starts.length && starts[index + 1] <= virtualEnd - ? [] - : [{ range: { pos: virtualStart, end: virtualEnd }, fidelity: SpanMapFidelity.Approximate }]; - }); - } - - /** Maps one range through an ordered segment index in the direction selected by `reverse`. */ - private mapRange(range: ReadonlyTextRange, segments: readonly SpanMapSegment[], reverse: boolean): MappedRange { - const start = range.pos; - const end = Math.max(range.end, start); - if (start === end) { - const { position, fidelity } = this.mapPoint(start, segments, reverse); - return { range: { pos: position, end: position }, fidelity }; - } - const [startIndex, startInside] = segmentIndexAt(segments, start, reverse); - const endProbe = end - 1; - const [endIndex, endInside] = segmentIndexAt(segments, endProbe, reverse); - - if (startIndex === endIndex && startInside === endInside) { - if (startInside) { - const segment = segments[startIndex]; - if (segment.kind === SpanMapKind.Verbatim) { - const mappedStart = mapVerbatimPosition(segment, start, reverse); - const mappedEnd = Math.max(mappedStart, mapVerbatimPosition(segment, end, reverse)); - return { range: { pos: mappedStart, end: mappedEnd }, fidelity: SpanMapFidelity.Exact }; - } - return { range: targetRange(segment, reverse), fidelity: SpanMapFidelity.Atom }; - } - const position = insertionPoint(segments, startIndex, reverse); - return { range: { pos: position, end: position }, fidelity: SpanMapFidelity.None }; - } - - const mappedStart = mapBoundary(segments, start, startIndex, startInside, reverse, false); - const mappedEnd = Math.max(mappedStart, mapBoundary(segments, end, endIndex, endInside, reverse, true)); - return { range: { pos: mappedStart, end: mappedEnd }, fidelity: SpanMapFidelity.Approximate }; - } - - /** Maps one position through an ordered segment index in the direction selected by `reverse`. */ - private mapPoint(position: number, segments: readonly SpanMapSegment[], reverse: boolean): MappedPosition { - const [index, inside] = segmentIndexAt(segments, position, reverse); - if (!inside) { - return { position: insertionPoint(segments, index, reverse), fidelity: SpanMapFidelity.None }; - } - const segment = segments[index]; - if (segment.kind === SpanMapKind.Verbatim) { - return { position: mapVerbatimPosition(segment, position, reverse), fidelity: SpanMapFidelity.Exact }; - } - return { - position: reverse ? segment.virtualStart : segment.originalStart, - fidelity: SpanMapFidelity.Atom, - }; - } - - /** Returns the lazily built original-text interval index. */ - private getOriginalIndex(): OriginalIndex { - if (this.originalIndex) return this.originalIndex; - const segments = [...this.segments].sort((left, right) => - left.originalStart - right.originalStart - || left.originalEnd - right.originalEnd - || left.virtualStart - right.virtualStart - ); - let leafCount = 1; - while (leafCount < segments.length) leafCount *= 2; - const maxEnds = new Array(2 * leafCount).fill(0); - for (let i = 0; i < segments.length; i++) maxEnds[leafCount + i] = segments[i].originalEnd; - for (let i = leafCount - 1; i > 0; i--) maxEnds[i] = Math.max(maxEnds[2 * i], maxEnds[2 * i + 1]); - return this.originalIndex = { segments, leafCount, maxEnds }; - } - - private virtualRangeSupportsFeature(range: ReadonlyTextRange, feature: SpanMapFeature): boolean { - const start = range.pos; - const end = Math.max(range.end, start); - if (start === end) { - const [index, inside] = segmentIndexAt(this.segments, start, false); - return inside && supportsFeature(this.segments[index], feature); - } - let [index, inside] = segmentIndexAt(this.segments, start, false); - if (!inside) return false; - let coveredThrough = start; - while (index < this.segments.length && coveredThrough < end) { - const segment = this.segments[index]; - if (segment.virtualStart > coveredThrough || segment.virtualEnd <= coveredThrough || !supportsFeature(segment, feature)) return false; - coveredThrough = segment.virtualEnd; - index++; - } - return coveredThrough >= end; - } -} - -/** - * Maps the inclusive start of an original range through every matching segment. Verbatim segments preserve - * the offset within the segment; atoms map to their virtual start. - * - * ```text - * original: [---------) - * ^ start - * - * virtual: [---------) [---------) - * ^ ^ - * result result - * ``` - */ -function originalStartProjections(segments: readonly NormalizedSpanMapSegment[], start: number, feature: SpanMapFeature): readonly number[] { - return segments - .filter(segment => supportsFeature(segment, feature)) - .map(segment => - segment.kind === SpanMapKind.Verbatim - ? mapVerbatimPosition(segment, start, true) - : segment.virtualStart - ); -} - -/** - * Maps the exclusive end of an original range through every matching segment. The caller uses `end - 1` - * to find the segment containing the final character, while this helper maps the `end` boundary itself. - * - * ```text - * original: [---------)[ next segment ) - * ^`-- end - * `--- end - 1 - * - * virtual: [---------) [---------) - * ^ ^ - * result result - * ``` - */ -function originalEndProjections(segments: readonly NormalizedSpanMapSegment[], end: number, feature: SpanMapFeature): readonly number[] { - return segments - .filter(segment => supportsFeature(segment, feature)) - .map(segment => - segment.kind === SpanMapKind.Verbatim - ? mapVerbatimPosition(segment, end, true) - : segment.virtualEnd - ); -} - -/** Maps a range fully contained by each segment. */ -function originalToVirtualSpansInSegments(segments: readonly NormalizedSpanMapSegment[], start: number, end: number, feature: SpanMapFeature): readonly MappedRange[] { - return segments - .filter(segment => supportsFeature(segment, feature)) - .map(segment => { - if (segment.kind === SpanMapKind.Verbatim) { - const mappedStart = mapVerbatimPosition(segment, start, true); - const mappedEnd = Math.max(mappedStart, mapVerbatimPosition(segment, end, true)); - return { range: { pos: mappedStart, end: mappedEnd }, fidelity: SpanMapFidelity.Exact }; - } - return { range: { pos: segment.virtualStart, end: segment.virtualEnd }, fidelity: SpanMapFidelity.Atom }; - }); -} - -/** Reports whether two segments belong to the same duplicate group. */ -function sameOriginalRange(left: SpanMapSegment, right: SpanMapSegment): boolean { - return left.originalStart === right.originalStart && left.originalEnd === right.originalEnd; -} - -/** - * Returns every mapping segment containing the original-text `position`. - * Segment ends are exclusive; starts, including zero-length segment starts, are included. - */ -function segmentsAtOriginalPosition(index: OriginalIndex, position: number): readonly NormalizedSpanMapSegment[] | undefined { - // Query intervals that contain position strictly before their exclusive end. Segments starting exactly at - // position are appended separately so zero-length segments are included while maxEnd <= position is pruned. - const start = firstOriginalSegmentAtOrAfter(index.segments, position); - const results = segmentsEndingAtOrAfter(index, start, position, false); - const end = firstOriginalSegmentAfter(index.segments, position); - results.push(...index.segments.slice(start, end)); - return results.length > 0 ? results : undefined; -} - -/** Returns segments among `[0, limit)` whose original end reaches `position`. */ -function segmentsEndingAtOrAfter(index: OriginalIndex, limit: number, position: number, includeEnd: boolean): NormalizedSpanMapSegment[] { - const results: NormalizedSpanMapSegment[] = []; - collectSegmentsEndingAtOrAfter(index, 1, 0, index.leafCount, limit, position, includeEnd, results); - return results; -} - -/** Walks the flat max-end tree left-to-right, preserving original-text order. */ -function collectSegmentsEndingAtOrAfter( - index: OriginalIndex, - node: number, - start: number, - end: number, - limit: number, - position: number, - includeEnd: boolean, - results: NormalizedSpanMapSegment[], -): void { - const maxEnd = index.maxEnds[node]; - if (start >= limit || maxEnd < position || !includeEnd && maxEnd === position) return; - if (end - start === 1) { - results.push(index.segments[start]); - return; - } - const middle = start + ((end - start) >>> 1); - collectSegmentsEndingAtOrAfter(index, 2 * node, start, middle, limit, position, includeEnd, results); - collectSegmentsEndingAtOrAfter(index, 2 * node + 1, middle, end, limit, position, includeEnd, results); -} - -/** Returns the first original-ordered segment whose start is greater than or equal to `position`. */ -function firstOriginalSegmentAtOrAfter(segments: readonly NormalizedSpanMapSegment[], position: number): number { - let low = 0; - let high = segments.length; - while (low < high) { - const middle = (low + high) >>> 1; - if (segments[middle].originalStart < position) low = middle + 1; - else high = middle; - } - return low; -} - -/** Returns the first original-ordered segment whose start is greater than `position`. */ -function firstOriginalSegmentAfter(segments: readonly NormalizedSpanMapSegment[], position: number): number { - let low = 0; - let high = segments.length; - while (low < high) { - const middle = (low + high) >>> 1; - if (segments[middle].originalStart <= position) low = middle + 1; - else high = middle; - } - return low; -} - -interface SegmentGroupAtOriginalPosition { - readonly segments: readonly NormalizedSpanMapSegment[]; - readonly atEnd: boolean; -} - -/** - * Returns every group of equal-range mapping segments containing or touching the original-text `position`. - * Segment ends are included for point mapping: - * - * ```text - * original: [--- A ---)[--- B ---) - * ^ position - * - * virtual: [ A1 ) [ A2 ) [ B1 ) [ B2 ) - * left group right group - * atEnd: true atEnd: false - * ``` - */ -function segmentGroupsAtOriginalPosition(index: OriginalIndex, position: number): readonly SegmentGroupAtOriginalPosition[] { - const limit = firstOriginalSegmentAfter(index.segments, position); - const segments = segmentsEndingAtOrAfter(index, limit, position, true); - const groups: SegmentGroupAtOriginalPosition[] = []; - for (let start = 0; start < segments.length;) { - let end = start + 1; - while (end < segments.length && sameOriginalRange(segments[start], segments[end])) end++; - const segment = segments[start]; - if (position <= segment.originalEnd) { - groups.push({ - segments: segments.slice(start, end), - atEnd: position === segment.originalEnd && position !== segment.originalStart, - }); - } - start = end; - } - return groups; -} - -/** Reports whether a segment participates in an original-to-virtual query for `features`. */ -function supportsFeature(segment: NormalizedSpanMapSegment, feature: SpanMapFeature): boolean { - return (segment.features & feature) !== 0; -} - -/** - * Finds the segment containing `position`, or the preceding segment when `position` is in a gap. - * The boolean distinguishes containment from a gap; `reverse` selects original rather than virtual coordinates. - */ -function segmentIndexAt(segments: readonly SpanMapSegment[], position: number, reverse: boolean): [number, boolean] { - let low = 0; - let high = segments.length; - while (low < high) { - const middle = (low + high) >>> 1; - const start = reverse ? segments[middle].originalStart : segments[middle].virtualStart; - if (start < position) low = middle + 1; - else high = middle; - } - if (low < segments.length && (reverse ? segments[low].originalStart : segments[low].virtualStart) === position) { - return [low, true]; - } - const previous = low - 1; - if (previous >= 0) { - const end = reverse ? segments[previous].originalEnd : segments[previous].virtualEnd; - if (position < end || previous === segments.length - 1 && position === end) return [previous, true]; - } - return [previous, false]; -} - -/** Returns the target insertion point for a gap following `previous`, or zero before the first segment. */ -function insertionPoint(segments: readonly SpanMapSegment[], previous: number, reverse: boolean): number { - if (previous < 0) return 0; - return reverse ? segments[previous].virtualEnd : segments[previous].originalEnd; -} - -/** Linearly maps and clamps a position within a length-preserving verbatim segment. */ -function mapVerbatimPosition(segment: SpanMapSegment, position: number, reverse: boolean): number { - const sourceStart = reverse ? segment.originalStart : segment.virtualStart; - const targetStart = reverse ? segment.virtualStart : segment.originalStart; - const targetEnd = reverse ? segment.virtualEnd : segment.originalEnd; - return clamp(targetStart + position - sourceStart, targetStart, targetEnd); -} - -/** Maps a range boundary, using insertion points for gaps and the selected endpoint for atoms. */ -function mapBoundary(segments: readonly SpanMapSegment[], position: number, index: number, inside: boolean, reverse: boolean, high: boolean): number { - if (!inside) return insertionPoint(segments, index, reverse); - const segment = segments[index]; - if (segment.kind === SpanMapKind.Verbatim) return mapVerbatimPosition(segment, position, reverse); - if (reverse) return high ? segment.virtualEnd : segment.virtualStart; - return high ? segment.originalEnd : segment.originalStart; -} - -/** Returns the complete target range of a segment in the selected direction. */ -function targetRange(segment: SpanMapSegment, reverse: boolean): ReadonlyTextRange { - return reverse - ? { pos: segment.virtualStart, end: segment.virtualEnd } - : { pos: segment.originalStart, end: segment.originalEnd }; -} - -/** Confines `value` to the inclusive interval [`low`, `high`]. */ -function clamp(value: number, low: number, high: number): number { - return Math.max(low, Math.min(value, high)); -} +import { SpanMapFeature } from "#enums/spanMapFeature"; +import { SpanMapFidelity } from "#enums/spanMapFidelity"; +import { SpanMapKind } from "#enums/spanMapKind"; +import type { ReadonlyTextRange } from "./ast.ts"; + +export { SpanMapFeature, SpanMapFidelity, SpanMapKind }; + +// Keep this in sync with spanmap.go + +/** Maps one half-open virtual range to one half-open original range. */ +export interface SpanMapSegment { + readonly virtualStart: number; + readonly virtualEnd: number; + readonly originalStart: number; + readonly originalEnd: number; + readonly kind: SpanMapKind; + readonly features?: SpanMapFeature; +} + +/** Internal segment representation after omitted features have been normalized to `All`. */ +type NormalizedSpanMapSegment = SpanMapSegment & { readonly features: SpanMapFeature; }; + +/** Lazily built interval index for original-to-virtual lookups. */ +interface OriginalIndex { + readonly segments: readonly NormalizedSpanMapSegment[]; + readonly leafCount: number; + readonly maxEnds: readonly number[]; +} + +/** One virtual projection of an original position and its mapping fidelity. */ +export interface MappedPosition { + readonly position: number; + readonly fidelity: SpanMapFidelity; +} + +/** One virtual projection of an original range and its mapping fidelity. */ +export interface MappedRange { + readonly range: ReadonlyTextRange; + readonly fidelity: SpanMapFidelity; +} + +/** Provides bidirectional span-aware mapping between virtual and original text. */ +export class SpanMap { + readonly segments: readonly NormalizedSpanMapSegment[]; + private originalIndex: OriginalIndex | undefined; + + /** Copies and sorts segments by virtual start, normalizing omitted features to `All`. */ + constructor(segments: readonly SpanMapSegment[]) { + this.segments = segments + .map(segment => ({ ...segment, features: segment.features ?? SpanMapFeature.All })) + .sort((left, right) => left.virtualStart - right.virtualStart); + } + + /** Reports whether a mapping is a precise, edit-safe projection through one verbatim segment. */ + static isExact(fidelity: SpanMapFidelity): boolean { + return fidelity === SpanMapFidelity.Exact; + } + + /** Reports whether a mapping lies in one verbatim or atom segment. */ + static isSingleSegment(fidelity: SpanMapFidelity): boolean { + return fidelity === SpanMapFidelity.Exact || fidelity === SpanMapFidelity.Atom; + } + + /** Reports whether the input had no counterpart in the target text. */ + static isNone(fidelity: SpanMapFidelity): boolean { + return fidelity === SpanMapFidelity.None; + } + + /** + * Maps a virtual range to original text. Gaps map to insertion points with `None` fidelity, + * and ranges crossing segment boundaries map their endpoints with `Approximate` fidelity. + */ + virtualToOriginalSpan(range: ReadonlyTextRange): MappedRange { + return this.mapRange(range, this.segments, false); + } + + /** Maps a visible LS result only when every covered segment participates in `feature`. */ + virtualToOriginalSpanForFeature(range: ReadonlyTextRange, feature: SpanMapFeature): MappedRange { + const mapped = this.virtualToOriginalSpan(range); + return this.virtualRangeSupportsFeature(range, feature) ? mapped : { ...mapped, fidelity: SpanMapFidelity.None }; + } + + /** Maps a virtual position to original text, using `None` fidelity for synthesized gaps. */ + virtualToOriginalPosition(position: number): MappedPosition { + return this.mapPoint(position, this.segments, false); + } + + virtualToOriginalPositionForFeature(position: number, feature: SpanMapFeature): MappedPosition { + const mapped = this.virtualToOriginalPosition(position); + const [index, inside] = segmentIndexAt(this.segments, position, false); + return inside && supportsFeature(this.segments[index], feature) ? mapped : { ...mapped, fidelity: SpanMapFidelity.None }; + } + + /** + * Returns every virtual projection of an original position whose segment participates in `feature`. + * Segment ends are inclusive for point mapping, so adjacent spans may both produce projections. + * Results are ordered by virtual position; uncovered or disabled positions produce no results. + */ + originalToVirtualPositions(position: number, feature: SpanMapFeature): readonly MappedPosition[] { + const groups = segmentGroupsAtOriginalPosition(this.getOriginalIndex(), position); + const results: MappedPosition[] = []; + for (const group of groups) { + for (const segment of group.segments) { + if (!supportsFeature(segment, feature)) continue; + const mapped = segment.kind === SpanMapKind.Verbatim + ? { position: mapVerbatimPosition(segment, position, true), fidelity: SpanMapFidelity.Exact } + : { position: group.atEnd ? segment.virtualEnd : segment.virtualStart, fidelity: SpanMapFidelity.Atom }; + if (!results.some(result => result.position === mapped.position && result.fidelity === mapped.fidelity)) { + results.push(mapped); + } + } + } + return results.sort((left, right) => left.position - right.position); + } + + /** + * Returns every feature-compatible virtual projection of an original range. + * A range contained by one or more segments produces one exact or atom result per matching segment. + * + * A range that starts in one group and ends in another can have several possible virtual ranges. For + * example, suppose two original segments are each copied twice into the virtual text: + * + * ```text + * original: [ A ][ B ] + * [---) range from inside A to inside B + * + * virtual: [ A ][ B ] [ A ][ B ] + * ^ ^ ^ ^ + * start end start end + * 1 3 11 13 + * ``` + * + * The map says that the range may start at 1 or 11 and end at 3 or 13, but it does not say which copy of A + * belongs with which copy of B. We choose the smallest range around each possible location, producing [1,3) + * and [11,13). We do not return [1,13), because it contains both smaller candidates and would include code + * that may be unrelated to the original range. These cross-group results have approximate fidelity. + */ + originalToVirtualSpans(range: ReadonlyTextRange, feature: SpanMapFeature): readonly MappedRange[] { + const start = range.pos; + const end = Math.max(range.end, start); + if (start === end) { + return this.originalToVirtualPositions(start, feature).map(({ position, fidelity }) => ({ + range: { pos: position, end: position }, + fidelity, + })); + } + const lastCharacter = end - 1; + const originalIndex = this.getOriginalIndex(); + const startSegments = segmentsAtOriginalPosition(originalIndex, start); + const endSegments = segmentsAtOriginalPosition(originalIndex, lastCharacter); + if (!startSegments || !endSegments) return []; + const containing = startSegments.filter(segment => end <= segment.originalEnd); + if (containing.length > 0) { + const results = [...originalToVirtualSpansInSegments(containing, start, end, feature)]; + if (results.length > 0) return results.sort((left, right) => left.range.pos - right.range.pos); + } + const starts = [...originalStartProjections(startSegments, start, feature)].sort((left, right) => left - right); + const ends = [...originalEndProjections(endSegments, end, feature)].sort((left, right) => left - right); + if (starts.length === 0 || ends.length === 0) return []; + return starts.flatMap((virtualStart, index) => { + const virtualEnd = ends.find(end => end >= virtualStart); + return virtualEnd === undefined || index + 1 < starts.length && starts[index + 1] <= virtualEnd + ? [] + : [{ range: { pos: virtualStart, end: virtualEnd }, fidelity: SpanMapFidelity.Approximate }]; + }); + } + + /** Maps one range through an ordered segment index in the direction selected by `reverse`. */ + private mapRange(range: ReadonlyTextRange, segments: readonly SpanMapSegment[], reverse: boolean): MappedRange { + const start = range.pos; + const end = Math.max(range.end, start); + if (start === end) { + const { position, fidelity } = this.mapPoint(start, segments, reverse); + return { range: { pos: position, end: position }, fidelity }; + } + const [startIndex, startInside] = segmentIndexAt(segments, start, reverse); + const endProbe = end - 1; + const [endIndex, endInside] = segmentIndexAt(segments, endProbe, reverse); + + if (startIndex === endIndex && startInside === endInside) { + if (startInside) { + const segment = segments[startIndex]; + if (segment.kind === SpanMapKind.Verbatim) { + const mappedStart = mapVerbatimPosition(segment, start, reverse); + const mappedEnd = Math.max(mappedStart, mapVerbatimPosition(segment, end, reverse)); + return { range: { pos: mappedStart, end: mappedEnd }, fidelity: SpanMapFidelity.Exact }; + } + return { range: targetRange(segment, reverse), fidelity: SpanMapFidelity.Atom }; + } + const position = insertionPoint(segments, startIndex, reverse); + return { range: { pos: position, end: position }, fidelity: SpanMapFidelity.None }; + } + + const mappedStart = mapBoundary(segments, start, startIndex, startInside, reverse, false); + const mappedEnd = Math.max(mappedStart, mapBoundary(segments, end, endIndex, endInside, reverse, true)); + return { range: { pos: mappedStart, end: mappedEnd }, fidelity: SpanMapFidelity.Approximate }; + } + + /** Maps one position through an ordered segment index in the direction selected by `reverse`. */ + private mapPoint(position: number, segments: readonly SpanMapSegment[], reverse: boolean): MappedPosition { + const [index, inside] = segmentIndexAt(segments, position, reverse); + if (!inside) { + return { position: insertionPoint(segments, index, reverse), fidelity: SpanMapFidelity.None }; + } + const segment = segments[index]; + if (segment.kind === SpanMapKind.Verbatim) { + return { position: mapVerbatimPosition(segment, position, reverse), fidelity: SpanMapFidelity.Exact }; + } + return { + position: reverse ? segment.virtualStart : segment.originalStart, + fidelity: SpanMapFidelity.Atom, + }; + } + + /** Returns the lazily built original-text interval index. */ + private getOriginalIndex(): OriginalIndex { + if (this.originalIndex) return this.originalIndex; + const segments = [...this.segments].sort((left, right) => + left.originalStart - right.originalStart + || left.originalEnd - right.originalEnd + || left.virtualStart - right.virtualStart + ); + let leafCount = 1; + while (leafCount < segments.length) leafCount *= 2; + const maxEnds = new Array(2 * leafCount).fill(0); + for (let i = 0; i < segments.length; i++) maxEnds[leafCount + i] = segments[i].originalEnd; + for (let i = leafCount - 1; i > 0; i--) maxEnds[i] = Math.max(maxEnds[2 * i], maxEnds[2 * i + 1]); + return this.originalIndex = { segments, leafCount, maxEnds }; + } + + private virtualRangeSupportsFeature(range: ReadonlyTextRange, feature: SpanMapFeature): boolean { + const start = range.pos; + const end = Math.max(range.end, start); + if (start === end) { + const [index, inside] = segmentIndexAt(this.segments, start, false); + return inside && supportsFeature(this.segments[index], feature); + } + let [index, inside] = segmentIndexAt(this.segments, start, false); + if (!inside) return false; + let coveredThrough = start; + while (index < this.segments.length && coveredThrough < end) { + const segment = this.segments[index]; + if (segment.virtualStart > coveredThrough || segment.virtualEnd <= coveredThrough || !supportsFeature(segment, feature)) return false; + coveredThrough = segment.virtualEnd; + index++; + } + return coveredThrough >= end; + } +} + +/** + * Maps the inclusive start of an original range through every matching segment. Verbatim segments preserve + * the offset within the segment; atoms map to their virtual start. + * + * ```text + * original: [---------) + * ^ start + * + * virtual: [---------) [---------) + * ^ ^ + * result result + * ``` + */ +function originalStartProjections(segments: readonly NormalizedSpanMapSegment[], start: number, feature: SpanMapFeature): readonly number[] { + return segments + .filter(segment => supportsFeature(segment, feature)) + .map(segment => + segment.kind === SpanMapKind.Verbatim + ? mapVerbatimPosition(segment, start, true) + : segment.virtualStart + ); +} + +/** + * Maps the exclusive end of an original range through every matching segment. The caller uses `end - 1` + * to find the segment containing the final character, while this helper maps the `end` boundary itself. + * + * ```text + * original: [---------)[ next segment ) + * ^`-- end + * `--- end - 1 + * + * virtual: [---------) [---------) + * ^ ^ + * result result + * ``` + */ +function originalEndProjections(segments: readonly NormalizedSpanMapSegment[], end: number, feature: SpanMapFeature): readonly number[] { + return segments + .filter(segment => supportsFeature(segment, feature)) + .map(segment => + segment.kind === SpanMapKind.Verbatim + ? mapVerbatimPosition(segment, end, true) + : segment.virtualEnd + ); +} + +/** Maps a range fully contained by each segment. */ +function originalToVirtualSpansInSegments(segments: readonly NormalizedSpanMapSegment[], start: number, end: number, feature: SpanMapFeature): readonly MappedRange[] { + return segments + .filter(segment => supportsFeature(segment, feature)) + .map(segment => { + if (segment.kind === SpanMapKind.Verbatim) { + const mappedStart = mapVerbatimPosition(segment, start, true); + const mappedEnd = Math.max(mappedStart, mapVerbatimPosition(segment, end, true)); + return { range: { pos: mappedStart, end: mappedEnd }, fidelity: SpanMapFidelity.Exact }; + } + return { range: { pos: segment.virtualStart, end: segment.virtualEnd }, fidelity: SpanMapFidelity.Atom }; + }); +} + +/** Reports whether two segments belong to the same duplicate group. */ +function sameOriginalRange(left: SpanMapSegment, right: SpanMapSegment): boolean { + return left.originalStart === right.originalStart && left.originalEnd === right.originalEnd; +} + +/** + * Returns every mapping segment containing the original-text `position`. + * Segment ends are exclusive; starts, including zero-length segment starts, are included. + */ +function segmentsAtOriginalPosition(index: OriginalIndex, position: number): readonly NormalizedSpanMapSegment[] | undefined { + // Query intervals that contain position strictly before their exclusive end. Segments starting exactly at + // position are appended separately so zero-length segments are included while maxEnd <= position is pruned. + const start = firstOriginalSegmentAtOrAfter(index.segments, position); + const results = segmentsEndingAtOrAfter(index, start, position, false); + const end = firstOriginalSegmentAfter(index.segments, position); + results.push(...index.segments.slice(start, end)); + return results.length > 0 ? results : undefined; +} + +/** Returns segments among `[0, limit)` whose original end reaches `position`. */ +function segmentsEndingAtOrAfter(index: OriginalIndex, limit: number, position: number, includeEnd: boolean): NormalizedSpanMapSegment[] { + const results: NormalizedSpanMapSegment[] = []; + collectSegmentsEndingAtOrAfter(index, 1, 0, index.leafCount, limit, position, includeEnd, results); + return results; +} + +/** Walks the flat max-end tree left-to-right, preserving original-text order. */ +function collectSegmentsEndingAtOrAfter( + index: OriginalIndex, + node: number, + start: number, + end: number, + limit: number, + position: number, + includeEnd: boolean, + results: NormalizedSpanMapSegment[], +): void { + const maxEnd = index.maxEnds[node]; + if (start >= limit || maxEnd < position || !includeEnd && maxEnd === position) return; + if (end - start === 1) { + results.push(index.segments[start]); + return; + } + const middle = start + ((end - start) >>> 1); + collectSegmentsEndingAtOrAfter(index, 2 * node, start, middle, limit, position, includeEnd, results); + collectSegmentsEndingAtOrAfter(index, 2 * node + 1, middle, end, limit, position, includeEnd, results); +} + +/** Returns the first original-ordered segment whose start is greater than or equal to `position`. */ +function firstOriginalSegmentAtOrAfter(segments: readonly NormalizedSpanMapSegment[], position: number): number { + let low = 0; + let high = segments.length; + while (low < high) { + const middle = (low + high) >>> 1; + if (segments[middle].originalStart < position) low = middle + 1; + else high = middle; + } + return low; +} + +/** Returns the first original-ordered segment whose start is greater than `position`. */ +function firstOriginalSegmentAfter(segments: readonly NormalizedSpanMapSegment[], position: number): number { + let low = 0; + let high = segments.length; + while (low < high) { + const middle = (low + high) >>> 1; + if (segments[middle].originalStart <= position) low = middle + 1; + else high = middle; + } + return low; +} + +interface SegmentGroupAtOriginalPosition { + readonly segments: readonly NormalizedSpanMapSegment[]; + readonly atEnd: boolean; +} + +/** + * Returns every group of equal-range mapping segments containing or touching the original-text `position`. + * Segment ends are included for point mapping: + * + * ```text + * original: [--- A ---)[--- B ---) + * ^ position + * + * virtual: [ A1 ) [ A2 ) [ B1 ) [ B2 ) + * left group right group + * atEnd: true atEnd: false + * ``` + */ +function segmentGroupsAtOriginalPosition(index: OriginalIndex, position: number): readonly SegmentGroupAtOriginalPosition[] { + const limit = firstOriginalSegmentAfter(index.segments, position); + const segments = segmentsEndingAtOrAfter(index, limit, position, true); + const groups: SegmentGroupAtOriginalPosition[] = []; + for (let start = 0; start < segments.length;) { + let end = start + 1; + while (end < segments.length && sameOriginalRange(segments[start], segments[end])) end++; + const segment = segments[start]; + if (position <= segment.originalEnd) { + groups.push({ + segments: segments.slice(start, end), + atEnd: position === segment.originalEnd && position !== segment.originalStart, + }); + } + start = end; + } + return groups; +} + +/** Reports whether a segment participates in an original-to-virtual query for `features`. */ +function supportsFeature(segment: NormalizedSpanMapSegment, feature: SpanMapFeature): boolean { + return (segment.features & feature) !== 0; +} + +/** + * Finds the segment containing `position`, or the preceding segment when `position` is in a gap. + * The boolean distinguishes containment from a gap; `reverse` selects original rather than virtual coordinates. + */ +function segmentIndexAt(segments: readonly SpanMapSegment[], position: number, reverse: boolean): [number, boolean] { + let low = 0; + let high = segments.length; + while (low < high) { + const middle = (low + high) >>> 1; + const start = reverse ? segments[middle].originalStart : segments[middle].virtualStart; + if (start < position) low = middle + 1; + else high = middle; + } + if (low < segments.length && (reverse ? segments[low].originalStart : segments[low].virtualStart) === position) { + return [low, true]; + } + const previous = low - 1; + if (previous >= 0) { + const end = reverse ? segments[previous].originalEnd : segments[previous].virtualEnd; + if (position < end || previous === segments.length - 1 && position === end) return [previous, true]; + } + return [previous, false]; +} + +/** Returns the target insertion point for a gap following `previous`, or zero before the first segment. */ +function insertionPoint(segments: readonly SpanMapSegment[], previous: number, reverse: boolean): number { + if (previous < 0) return 0; + return reverse ? segments[previous].virtualEnd : segments[previous].originalEnd; +} + +/** Linearly maps and clamps a position within a length-preserving verbatim segment. */ +function mapVerbatimPosition(segment: SpanMapSegment, position: number, reverse: boolean): number { + const sourceStart = reverse ? segment.originalStart : segment.virtualStart; + const targetStart = reverse ? segment.virtualStart : segment.originalStart; + const targetEnd = reverse ? segment.virtualEnd : segment.originalEnd; + return clamp(targetStart + position - sourceStart, targetStart, targetEnd); +} + +/** Maps a range boundary, using insertion points for gaps and the selected endpoint for atoms. */ +function mapBoundary(segments: readonly SpanMapSegment[], position: number, index: number, inside: boolean, reverse: boolean, high: boolean): number { + if (!inside) return insertionPoint(segments, index, reverse); + const segment = segments[index]; + if (segment.kind === SpanMapKind.Verbatim) return mapVerbatimPosition(segment, position, reverse); + if (reverse) return high ? segment.virtualEnd : segment.virtualStart; + return high ? segment.originalEnd : segment.originalStart; +} + +/** Returns the complete target range of a segment in the selected direction. */ +function targetRange(segment: SpanMapSegment, reverse: boolean): ReadonlyTextRange { + return reverse + ? { pos: segment.virtualStart, end: segment.virtualEnd } + : { pos: segment.originalStart, end: segment.originalEnd }; +} + +/** Confines `value` to the inclusive interval [`low`, `high`]. */ +function clamp(value: number, low: number, high: number): number { + return Math.max(low, Math.min(value, high)); +} diff --git a/packages/typescript/src/ast/utils.ts b/packages/typescript/src/ast/utils.ts index f3018566aa4a4..1d919e983a53e 100644 --- a/packages/typescript/src/ast/utils.ts +++ b/packages/typescript/src/ast/utils.ts @@ -1,100 +1,100 @@ -import { CharacterCodes } from "#enums/characterCodes"; -import { SyntaxKind } from "#enums/syntaxKind"; -import type { - __String, - HasExpression, - HasInitializer, - ObjectAssignmentInitializer, - SourceFile, -} from "./ast.ts"; -import type { Node } from "./ast.ts"; - -let syntaxKindNames: Map | undefined; -function getSyntaxKindNames(): Map { - if (!syntaxKindNames) { - syntaxKindNames = new Map(); - for (const name of Object.keys(SyntaxKind)) { - const val = SyntaxKind[name as keyof typeof SyntaxKind]; - if (typeof val === "number" && !syntaxKindNames.has(val)) { - syntaxKindNames.set(val, name); - } - } - syntaxKindNames.set(SyntaxKind.EndOfFile, "EndOfFileToken"); - } - return syntaxKindNames; -} - -export function formatSyntaxKind(kind: SyntaxKind): string { - return getSyntaxKindNames().get(kind) ?? `Unknown(${kind})`; -} - -/** - * Remove one extra leading underscore from an identifier name, recovering the - * display form from its escaped {@link __String} key. - */ -export function unescapeLeadingUnderscores(identifier: __String): string { - const id = identifier as string; - return id.length >= 3 && id.charCodeAt(0) === CharacterCodes._ && id.charCodeAt(1) === CharacterCodes._ && id.charCodeAt(2) === CharacterCodes._ - ? id.slice(1) - : id; -} - -/** - * Add an extra leading underscore to a display name that already begins with - * `__`, producing its escaped {@link __String} key. - */ -export function escapeLeadingUnderscores(identifier: string): __String { - return (identifier.length >= 2 && identifier.charCodeAt(0) === CharacterCodes._ && identifier.charCodeAt(1) === CharacterCodes._ - ? "_" + identifier - : identifier) as __String; -} - -export function tryCast(value: TIn | undefined, test: (value: TIn) => value is TOut): TOut | undefined { - return value !== undefined && test(value) ? value : undefined; -} - -export function cast(value: TIn | undefined, test: (value: TIn) => value is TOut): TOut { - if (value !== undefined && test(value)) return value; - - throw new Error(`Invalid cast. The supplied value ${value} did not pass the test '${test.name}'.`); -} - -export function hasExpression(node: Node): node is HasExpression { - return "expression" in node; -} - -export function hasInitializer(node: Node): node is HasInitializer { - return "initializer" in node; -} - -export function hasObjectAssignmentInitializer(node: Node): node is ObjectAssignmentInitializer { - return "objectAssignmentInitializer" in node; -} - -export function cloneSourceFileData(sourceFile: SourceFile): Record { - return { - statements: sourceFile.statements, - endOfFileToken: sourceFile.endOfFileToken, - text: sourceFile.text, - originalText: sourceFile.originalText, - spanMap: sourceFile.spanMap, - contentMapper: sourceFile.contentMapper, - virtualFileName: sourceFile.virtualFileName, - diagnosticDirectives: sourceFile.diagnosticDirectives, - supplementalSourceFileNames: sourceFile.supplementalSourceFileNames, - canonicalSourceFileName: sourceFile.canonicalSourceFileName, - fileName: sourceFile.fileName, - path: sourceFile.path, - languageVariant: sourceFile.languageVariant, - scriptKind: sourceFile.scriptKind, - isDeclarationFile: sourceFile.isDeclarationFile, - referencedFiles: sourceFile.referencedFiles, - typeReferenceDirectives: sourceFile.typeReferenceDirectives, - libReferenceDirectives: sourceFile.libReferenceDirectives, - imports: sourceFile.imports, - moduleAugmentations: sourceFile.moduleAugmentations, - ambientModuleNames: sourceFile.ambientModuleNames, - externalModuleIndicator: sourceFile.externalModuleIndicator, - tokenCache: undefined, - }; -} +import { CharacterCodes } from "#enums/characterCodes"; +import { SyntaxKind } from "#enums/syntaxKind"; +import type { + __String, + HasExpression, + HasInitializer, + ObjectAssignmentInitializer, + SourceFile, +} from "./ast.ts"; +import type { Node } from "./ast.ts"; + +let syntaxKindNames: Map | undefined; +function getSyntaxKindNames(): Map { + if (!syntaxKindNames) { + syntaxKindNames = new Map(); + for (const name of Object.keys(SyntaxKind)) { + const val = SyntaxKind[name as keyof typeof SyntaxKind]; + if (typeof val === "number" && !syntaxKindNames.has(val)) { + syntaxKindNames.set(val, name); + } + } + syntaxKindNames.set(SyntaxKind.EndOfFile, "EndOfFileToken"); + } + return syntaxKindNames; +} + +export function formatSyntaxKind(kind: SyntaxKind): string { + return getSyntaxKindNames().get(kind) ?? `Unknown(${kind})`; +} + +/** + * Remove one extra leading underscore from an identifier name, recovering the + * display form from its escaped {@link __String} key. + */ +export function unescapeLeadingUnderscores(identifier: __String): string { + const id = identifier as string; + return id.length >= 3 && id.charCodeAt(0) === CharacterCodes._ && id.charCodeAt(1) === CharacterCodes._ && id.charCodeAt(2) === CharacterCodes._ + ? id.slice(1) + : id; +} + +/** + * Add an extra leading underscore to a display name that already begins with + * `__`, producing its escaped {@link __String} key. + */ +export function escapeLeadingUnderscores(identifier: string): __String { + return (identifier.length >= 2 && identifier.charCodeAt(0) === CharacterCodes._ && identifier.charCodeAt(1) === CharacterCodes._ + ? "_" + identifier + : identifier) as __String; +} + +export function tryCast(value: TIn | undefined, test: (value: TIn) => value is TOut): TOut | undefined { + return value !== undefined && test(value) ? value : undefined; +} + +export function cast(value: TIn | undefined, test: (value: TIn) => value is TOut): TOut { + if (value !== undefined && test(value)) return value; + + throw new Error(`Invalid cast. The supplied value ${value} did not pass the test '${test.name}'.`); +} + +export function hasExpression(node: Node): node is HasExpression { + return "expression" in node; +} + +export function hasInitializer(node: Node): node is HasInitializer { + return "initializer" in node; +} + +export function hasObjectAssignmentInitializer(node: Node): node is ObjectAssignmentInitializer { + return "objectAssignmentInitializer" in node; +} + +export function cloneSourceFileData(sourceFile: SourceFile): Record { + return { + statements: sourceFile.statements, + endOfFileToken: sourceFile.endOfFileToken, + text: sourceFile.text, + originalText: sourceFile.originalText, + spanMap: sourceFile.spanMap, + contentMapper: sourceFile.contentMapper, + virtualFileName: sourceFile.virtualFileName, + diagnosticDirectives: sourceFile.diagnosticDirectives, + supplementalSourceFileNames: sourceFile.supplementalSourceFileNames, + canonicalSourceFileName: sourceFile.canonicalSourceFileName, + fileName: sourceFile.fileName, + path: sourceFile.path, + languageVariant: sourceFile.languageVariant, + scriptKind: sourceFile.scriptKind, + isDeclarationFile: sourceFile.isDeclarationFile, + referencedFiles: sourceFile.referencedFiles, + typeReferenceDirectives: sourceFile.typeReferenceDirectives, + libReferenceDirectives: sourceFile.libReferenceDirectives, + imports: sourceFile.imports, + moduleAugmentations: sourceFile.moduleAugmentations, + ambientModuleNames: sourceFile.ambientModuleNames, + externalModuleIndicator: sourceFile.externalModuleIndicator, + tokenCache: undefined, + }; +} diff --git a/packages/typescript/src/ast/visitor.generated.ts b/packages/typescript/src/ast/visitor.generated.ts index 00625a299ae8a..77a4b3b6653e6 100644 --- a/packages/typescript/src/ast/visitor.generated.ts +++ b/packages/typescript/src/ast/visitor.generated.ts @@ -1,1441 +1,1441 @@ -// Code generated by tools/scripts/tsc/generate-ts-ast.ts. DO NOT EDIT. - -import { SyntaxKind } from "#enums/syntaxKind"; -import type { - ArrayBindingPattern, - ArrayLiteralExpression, - ArrayTypeNode, - ArrowFunction, - AsExpression, - AwaitExpression, - BinaryExpression, - BindingElement, - Block, - BreakStatement, - CallExpression, - CallSignatureDeclaration, - CaseBlock, - CaseClause, - CatchClause, - ClassDeclaration, - ClassExpression, - ClassStaticBlockDeclaration, - ComputedPropertyName, - ConditionalExpression, - ConditionalTypeNode, - ConstructorDeclaration, - ConstructorTypeNode, - ConstructSignatureDeclaration, - ContinueStatement, - Decorator, - DefaultClause, - DeleteExpression, - DoStatement, - ElementAccessExpression, - EnumDeclaration, - EnumMember, - ExportAssignment, - ExportDeclaration, - ExportSpecifier, - ExpressionStatement, - ExpressionWithTypeArguments, - ExternalModuleReference, - ForInStatement, - ForOfStatement, - ForStatement, - FunctionDeclaration, - FunctionExpression, - FunctionTypeNode, - GetAccessorDeclaration, - HeritageClause, - IfStatement, - ImportAttribute, - ImportAttributes, - ImportClause, - ImportDeclaration, - ImportEqualsDeclaration, - ImportSpecifier, - ImportTypeNode, - IndexedAccessTypeNode, - IndexSignatureDeclaration, - InferTypeNode, - InterfaceDeclaration, - IntersectionTypeNode, - JSDoc, - JSDocAugmentsTag, - JSDocCallbackTag, - JSDocDeprecatedTag, - JSDocImplementsTag, - JSDocImportTag, - JSDocLink, - JSDocLinkCode, - JSDocLinkPlain, - JSDocNameReference, - JSDocNonNullableType, - JSDocNullableType, - JSDocOptionalType, - JSDocOverloadTag, - JSDocOverrideTag, - JSDocParameterTag, - JSDocPrivateTag, - JSDocPropertyTag, - JSDocProtectedTag, - JSDocPublicTag, - JSDocReadonlyTag, - JSDocReturnTag, - JSDocSatisfiesTag, - JSDocSeeTag, - JSDocSignature, - JSDocTemplateTag, - JSDocThisTag, - JSDocThrowsTag, - JSDocTypedefTag, - JSDocTypeExpression, - JSDocTypeLiteral, - JSDocTypeTag, - JSDocUnknownTag, - JSDocVariadicType, - JsxAttribute, - JsxAttributes, - JsxClosingElement, - JsxElement, - JsxExpression, - JsxFragment, - JsxNamespacedName, - JsxOpeningElement, - JsxSelfClosingElement, - JsxSpreadAttribute, - LabeledStatement, - LiteralTypeNode, - MappedTypeNode, - MetaProperty, - MethodDeclaration, - MethodSignatureDeclaration, - MissingDeclaration, - ModuleBlock, - ModuleDeclaration, - NamedExports, - NamedImports, - NamedTupleMember, - NamespaceExport, - NamespaceExportDeclaration, - NamespaceImport, - NewExpression, - Node, - NodeArray, - NonNullExpression, - ObjectBindingPattern, - ObjectLiteralExpression, - OptionalTypeNode, - ParameterDeclaration, - ParenthesizedExpression, - ParenthesizedTypeNode, - PartiallyEmittedExpression, - PostfixUnaryExpression, - PrefixUnaryExpression, - PropertyAccessExpression, - PropertyAssignment, - PropertyDeclaration, - PropertySignatureDeclaration, - QualifiedName, - RestTypeNode, - ReturnStatement, - SatisfiesExpression, - SetAccessorDeclaration, - ShorthandPropertyAssignment, - SourceFile, - SpreadAssignment, - SpreadElement, - SwitchStatement, - SyntaxList, - SyntheticExpression, - SyntheticReferenceExpression, - TaggedTemplateExpression, - TemplateExpression, - TemplateLiteralTypeNode, - TemplateLiteralTypeSpan, - TemplateSpan, - ThrowStatement, - TryStatement, - TupleTypeNode, - TypeAliasDeclaration, - TypeAssertion, - TypeLiteralNode, - TypeOfExpression, - TypeOperatorNode, - TypeParameterDeclaration, - TypePredicateNode, - TypeQueryNode, - TypeReferenceNode, - UnionTypeNode, - VariableDeclaration, - VariableDeclarationList, - VariableStatement, - VoidExpression, - WhileStatement, - WithStatement, - YieldExpression, -} from "./ast.ts"; -import { - createNodeArray, - updateArrayBindingPattern, - updateArrayLiteralExpression, - updateArrayTypeNode, - updateArrowFunction, - updateAsExpression, - updateAwaitExpression, - updateBinaryExpression, - updateBindingElement, - updateBlock, - updateBreakStatement, - updateCallExpression, - updateCallSignatureDeclaration, - updateCaseBlock, - updateCaseClause, - updateCatchClause, - updateClassDeclaration, - updateClassExpression, - updateClassStaticBlockDeclaration, - updateComputedPropertyName, - updateConditionalExpression, - updateConditionalTypeNode, - updateConstructorDeclaration, - updateConstructorTypeNode, - updateConstructSignatureDeclaration, - updateContinueStatement, - updateDecorator, - updateDefaultClause, - updateDeleteExpression, - updateDoStatement, - updateElementAccessExpression, - updateEnumDeclaration, - updateEnumMember, - updateExportAssignment, - updateExportDeclaration, - updateExportSpecifier, - updateExpressionStatement, - updateExpressionWithTypeArguments, - updateExternalModuleReference, - updateForInStatement, - updateForOfStatement, - updateForStatement, - updateFunctionDeclaration, - updateFunctionExpression, - updateFunctionTypeNode, - updateGetAccessorDeclaration, - updateHeritageClause, - updateIfStatement, - updateImportAttribute, - updateImportAttributes, - updateImportClause, - updateImportDeclaration, - updateImportEqualsDeclaration, - updateImportSpecifier, - updateImportTypeNode, - updateIndexedAccessTypeNode, - updateIndexSignatureDeclaration, - updateInferTypeNode, - updateInterfaceDeclaration, - updateIntersectionTypeNode, - updateJSDoc, - updateJSDocAugmentsTag, - updateJSDocCallbackTag, - updateJSDocDeprecatedTag, - updateJSDocImplementsTag, - updateJSDocImportTag, - updateJSDocLink, - updateJSDocLinkCode, - updateJSDocLinkPlain, - updateJSDocNameReference, - updateJSDocNonNullableType, - updateJSDocNullableType, - updateJSDocOptionalType, - updateJSDocOverloadTag, - updateJSDocOverrideTag, - updateJSDocPrivateTag, - updateJSDocProtectedTag, - updateJSDocPublicTag, - updateJSDocReadonlyTag, - updateJSDocReturnTag, - updateJSDocSatisfiesTag, - updateJSDocSeeTag, - updateJSDocSignature, - updateJSDocTemplateTag, - updateJSDocThisTag, - updateJSDocThrowsTag, - updateJSDocTypedefTag, - updateJSDocTypeExpression, - updateJSDocTypeLiteral, - updateJSDocTypeTag, - updateJSDocUnknownTag, - updateJSDocVariadicType, - updateJsxAttribute, - updateJsxAttributes, - updateJsxClosingElement, - updateJsxElement, - updateJsxExpression, - updateJsxFragment, - updateJsxNamespacedName, - updateJsxOpeningElement, - updateJsxSelfClosingElement, - updateJsxSpreadAttribute, - updateLabeledStatement, - updateLiteralTypeNode, - updateMappedTypeNode, - updateMetaProperty, - updateMethodDeclaration, - updateMethodSignatureDeclaration, - updateMissingDeclaration, - updateModuleBlock, - updateModuleDeclaration, - updateNamedExports, - updateNamedImports, - updateNamedTupleMember, - updateNamespaceExport, - updateNamespaceExportDeclaration, - updateNamespaceImport, - updateNewExpression, - updateNonNullExpression, - updateObjectBindingPattern, - updateObjectLiteralExpression, - updateOptionalTypeNode, - updateParameterDeclaration, - updateParenthesizedExpression, - updateParenthesizedTypeNode, - updatePartiallyEmittedExpression, - updatePostfixUnaryExpression, - updatePrefixUnaryExpression, - updatePropertyAccessExpression, - updatePropertyAssignment, - updatePropertyDeclaration, - updatePropertySignatureDeclaration, - updateQualifiedName, - updateRestTypeNode, - updateReturnStatement, - updateSatisfiesExpression, - updateSetAccessorDeclaration, - updateShorthandPropertyAssignment, - updateSourceFile, - updateSpreadAssignment, - updateSpreadElement, - updateSwitchStatement, - updateSyntaxList, - updateSyntheticExpression, - updateSyntheticReferenceExpression, - updateTaggedTemplateExpression, - updateTemplateExpression, - updateTemplateLiteralTypeNode, - updateTemplateLiteralTypeSpan, - updateTemplateSpan, - updateThrowStatement, - updateTryStatement, - updateTupleTypeNode, - updateTypeAliasDeclaration, - updateTypeAssertion, - updateTypeLiteralNode, - updateTypeOfExpression, - updateTypeOperatorNode, - updateTypeParameterDeclaration, - updateTypePredicateNode, - updateTypeQueryNode, - updateTypeReferenceNode, - updateUnionTypeNode, - updateVariableDeclaration, - updateVariableDeclarationList, - updateVariableStatement, - updateVoidExpression, - updateWhileStatement, - updateWithStatement, - updateYieldExpression, -} from "./factory.generated.ts"; -import { - isAssertsKeyword, - isAsteriskToken, - isAwaitKeyword, - isBinaryOperatorToken, - isBindingName, - isBlock, - isCaseBlock, - isCatchClause, - isColonToken, - isConciseBody, - isDotDotDotToken, - isEndOfFile, - isEntityName, - isEqualsGreaterThanToken, - isEqualsToken, - isExclamationToken, - isExpression, - isExpressionWithTypeArguments, - isForInitializer, - isFunctionBody, - isIdentifier, - isImportAttributeName, - isImportAttributes, - isImportClause, - isJSDocFullName, - isJsxAttributeName, - isJsxAttributes, - isJsxAttributeValue, - isJsxClosingElement, - isJsxClosingFragment, - isJsxOpeningElement, - isJsxOpeningFragment, - isJsxTagNameExpression, - isLeftHandSideExpression, - isMemberName, - isModuleBody, - isModuleExportName, - isModuleName, - isModuleReference, - isNamedExportBindings, - isNamedImportBindings, - isPropertyName, - isQuestionDotToken, - isQuestionOrExclamationToken, - isQuestionOrPlusOrMinusToken, - isQuestionToken, - isReadonlyKeywordOrPlusOrMinusToken, - isStatement, - isTemplateHead, - isTemplateLiteral, - isTemplateMiddleOrTail, - isTypeNode, - isTypeParameterDeclaration, - isTypePredicateParameterName, - isVariableDeclaration, - isVariableDeclarationList, -} from "./is.ts"; -import { - visitEachChildOfJSDocParameterTag, - visitEachChildOfJSDocPropertyTag, -} from "./visitor.ts"; - -/** - * A callback that receives a node and returns a visited node (or undefined to remove it). - */ -export type Visitor = (node: Node) => Node | undefined; - -/** - * Visits a Node using the supplied visitor, possibly returning a new Node in its place. - * - * - If the input node is undefined, then the output is undefined. - * - If the visitor returns undefined, then the output is undefined. - * - If the output node is not undefined, then it will satisfy the test function. - * - In order to obtain a return type that is more specific than `Node`, a test - * function must be provided, and that function must be a type predicate. - * - * @param node The Node to visit. - * @param visitor The callback used to visit the Node. - * @param test A callback to execute to verify the Node is valid. - */ -export function visitNode( - node: TIn, - visitor: Visitor, - test: (node: Node) => node is TOut, -): TOut | (TIn & undefined); -/** - * Visits a Node using the supplied visitor, possibly returning a new Node in its place. - * - * - If the input node is undefined, then the output is undefined. - * - If the visitor returns undefined, then the output is undefined. - * - * @param node The Node to visit. - * @param visitor The callback used to visit the Node. - * @param test An optional callback to execute to verify the Node is valid. - */ -export function visitNode( - node: TIn, - visitor: Visitor, - test?: (node: Node) => boolean, -): Node | (TIn & undefined); -export function visitNode(node: Node | undefined, visitor: Visitor, test?: (node: Node) => boolean): Node | undefined { - if (node === undefined) return undefined; - const visited = visitor(node); - if (visited !== undefined && test !== undefined && !test(visited)) { - throw new Error("Visited node failed test assertion."); - } - return visited; -} - -/** - * Visits a NodeArray using the supplied visitor, possibly returning a new NodeArray in its place. - * - * - If the input node array is undefined, the output is undefined. - * - If the visitor returns undefined for a node, that node is dropped from the result. - */ -export function visitNodes(nodes: NodeArray, visitor: Visitor): NodeArray; -export function visitNodes(nodes: NodeArray | undefined, visitor: Visitor): NodeArray | undefined; -export function visitNodes(nodes: NodeArray | undefined, visitor: Visitor): NodeArray | undefined { - if (nodes === undefined) return undefined; - const updated = visitNodesArray(nodes, visitor); - if (updated === nodes) { - return nodes; - } - return createNodeArray(updated, nodes.pos, nodes.end); -} - -export function visitNodesArray(nodes: readonly T[], visitor: Visitor): readonly T[]; -export function visitNodesArray(nodes: readonly T[] | undefined, visitor: Visitor): readonly T[] | undefined; -export function visitNodesArray(nodes: readonly Node[] | undefined, visitor: Visitor): readonly Node[] | undefined { - if (nodes === undefined) return undefined; - let updated: Node[] | undefined; - for (let i = 0; i < nodes.length; i++) { - const node = nodes[i]; - const visited = visitor(node); - if (updated) { - if (visited) updated.push(visited); - } - else if (visited !== node) { - updated = []; - for (let j = 0; j < i; j++) updated.push(nodes[j]); - if (visited) updated.push(visited); - } - } - return updated ?? nodes; -} - -/** - * Visits each child of a Node using the supplied visitor, possibly returning a new Node of the same kind in its place. - * - * @param node The Node whose children will be visited. - * @param visitor The callback used to visit each child. - * @returns The original node if no children changed, or a new node with visited children. - */ -export function visitEachChild(node: T, visitor: Visitor): T; -export function visitEachChild(node: T | undefined, visitor: Visitor): T | undefined; -export function visitEachChild(node: Node | undefined, visitor: Visitor): Node | undefined { - if (node === undefined) return undefined; - const fn = visitEachChildTable[node.kind]; - return fn ? fn(node, visitor) : node; -} - -type VisitEachChildFunction = (node: any, visitor: Visitor) => Node; - -const visitEachChildTable: Record = { - [SyntaxKind.QualifiedName]: (node: QualifiedName, visitor: Visitor): QualifiedName => { - const _left = visitNode(node.left, visitor, isEntityName); - const _right = visitNode(node.right, visitor, isMemberName); - return updateQualifiedName(node, _left, _right); - }, - [SyntaxKind.ComputedPropertyName]: (node: ComputedPropertyName, visitor: Visitor): ComputedPropertyName => { - const _expression = visitNode(node.expression, visitor, isExpression); - return updateComputedPropertyName(node, _expression); - }, - [SyntaxKind.Decorator]: (node: Decorator, visitor: Visitor): Decorator => { - const _expression = visitNode(node.expression, visitor, isLeftHandSideExpression); - return updateDecorator(node, _expression); - }, - [SyntaxKind.IfStatement]: (node: IfStatement, visitor: Visitor): IfStatement => { - const _expression = visitNode(node.expression, visitor, isExpression); - const _thenStatement = visitNode(node.thenStatement, visitor, isStatement); - const _elseStatement = visitNode(node.elseStatement, visitor, isStatement); - return updateIfStatement(node, _expression, _thenStatement, _elseStatement); - }, - [SyntaxKind.DoStatement]: (node: DoStatement, visitor: Visitor): DoStatement => { - const _statement = visitNode(node.statement, visitor, isStatement); - const _expression = visitNode(node.expression, visitor, isExpression); - return updateDoStatement(node, _statement, _expression); - }, - [SyntaxKind.WhileStatement]: (node: WhileStatement, visitor: Visitor): WhileStatement => { - const _expression = visitNode(node.expression, visitor, isExpression); - const _statement = visitNode(node.statement, visitor, isStatement); - return updateWhileStatement(node, _expression, _statement); - }, - [SyntaxKind.ForStatement]: (node: ForStatement, visitor: Visitor): ForStatement => { - const _initializer = visitNode(node.initializer, visitor, isForInitializer); - const _condition = visitNode(node.condition, visitor, isExpression); - const _incrementor = visitNode(node.incrementor, visitor, isExpression); - const _statement = visitNode(node.statement, visitor, isStatement); - return updateForStatement(node, _initializer, _condition, _incrementor, _statement); - }, - [SyntaxKind.BreakStatement]: (node: BreakStatement, visitor: Visitor): BreakStatement => { - const _label = visitNode(node.label, visitor, isIdentifier); - return updateBreakStatement(node, _label); - }, - [SyntaxKind.ContinueStatement]: (node: ContinueStatement, visitor: Visitor): ContinueStatement => { - const _label = visitNode(node.label, visitor, isIdentifier); - return updateContinueStatement(node, _label); - }, - [SyntaxKind.ReturnStatement]: (node: ReturnStatement, visitor: Visitor): ReturnStatement => { - const _expression = visitNode(node.expression, visitor, isExpression); - return updateReturnStatement(node, _expression); - }, - [SyntaxKind.WithStatement]: (node: WithStatement, visitor: Visitor): WithStatement => { - const _expression = visitNode(node.expression, visitor, isExpression); - const _statement = visitNode(node.statement, visitor, isStatement); - return updateWithStatement(node, _expression, _statement); - }, - [SyntaxKind.SwitchStatement]: (node: SwitchStatement, visitor: Visitor): SwitchStatement => { - const _expression = visitNode(node.expression, visitor, isExpression); - const _caseBlock = visitNode(node.caseBlock, visitor, isCaseBlock); - return updateSwitchStatement(node, _expression, _caseBlock); - }, - [SyntaxKind.CaseBlock]: (node: CaseBlock, visitor: Visitor): CaseBlock => { - const _clauses = visitNodes(node.clauses, visitor); - return updateCaseBlock(node, _clauses); - }, - [SyntaxKind.ThrowStatement]: (node: ThrowStatement, visitor: Visitor): ThrowStatement => { - const _expression = visitNode(node.expression, visitor, isExpression); - return updateThrowStatement(node, _expression); - }, - [SyntaxKind.TryStatement]: (node: TryStatement, visitor: Visitor): TryStatement => { - const _tryBlock = visitNode(node.tryBlock, visitor, isBlock); - const _catchClause = visitNode(node.catchClause, visitor, isCatchClause); - const _finallyBlock = visitNode(node.finallyBlock, visitor, isBlock); - return updateTryStatement(node, _tryBlock, _catchClause, _finallyBlock); - }, - [SyntaxKind.CatchClause]: (node: CatchClause, visitor: Visitor): CatchClause => { - const _variableDeclaration = visitNode(node.variableDeclaration, visitor, isVariableDeclaration); - const _block = visitNode(node.block, visitor, isBlock); - return updateCatchClause(node, _variableDeclaration, _block); - }, - [SyntaxKind.LabeledStatement]: (node: LabeledStatement, visitor: Visitor): LabeledStatement => { - const _label = visitNode(node.label, visitor, isIdentifier); - const _statement = visitNode(node.statement, visitor, isStatement); - return updateLabeledStatement(node, _label, _statement); - }, - [SyntaxKind.ExpressionStatement]: (node: ExpressionStatement, visitor: Visitor): ExpressionStatement => { - const _expression = visitNode(node.expression, visitor, isExpression); - return updateExpressionStatement(node, _expression); - }, - [SyntaxKind.Block]: (node: Block, visitor: Visitor): Block => { - const _statements = visitNodes(node.statements, visitor); - return updateBlock(node, _statements); - }, - [SyntaxKind.VariableStatement]: (node: VariableStatement, visitor: Visitor): VariableStatement => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _declarationList = visitNode(node.declarationList, visitor, isVariableDeclarationList); - return updateVariableStatement(node, _modifiers, _declarationList); - }, - [SyntaxKind.VariableDeclaration]: (node: VariableDeclaration, visitor: Visitor): VariableDeclaration => { - const _name = visitNode(node.name, visitor, isBindingName); - const _exclamationToken = visitNode(node.exclamationToken, visitor, isExclamationToken); - const _type = visitNode(node.type, visitor, isTypeNode); - const _initializer = visitNode(node.initializer, visitor, isExpression); - return updateVariableDeclaration(node, _name, _exclamationToken, _type, _initializer); - }, - [SyntaxKind.VariableDeclarationList]: (node: VariableDeclarationList, visitor: Visitor): VariableDeclarationList => { - const _declarations = visitNodes(node.declarations, visitor); - return updateVariableDeclarationList(node, _declarations); - }, - [SyntaxKind.Parameter]: (node: ParameterDeclaration, visitor: Visitor): ParameterDeclaration => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _dotDotDotToken = visitNode(node.dotDotDotToken, visitor, isDotDotDotToken); - const _name = visitNode(node.name, visitor, isBindingName); - const _questionToken = visitNode(node.questionToken, visitor, isQuestionToken); - const _type = visitNode(node.type, visitor, isTypeNode); - const _initializer = visitNode(node.initializer, visitor, isExpression); - return updateParameterDeclaration(node, _modifiers, _dotDotDotToken, _name, _questionToken, _type, _initializer); - }, - [SyntaxKind.BindingElement]: (node: BindingElement, visitor: Visitor): BindingElement => { - const _dotDotDotToken = visitNode(node.dotDotDotToken, visitor, isDotDotDotToken); - const _propertyName = visitNode(node.propertyName, visitor, isPropertyName); - const _name = visitNode(node.name, visitor, isBindingName); - const _initializer = visitNode(node.initializer, visitor, isExpression); - return updateBindingElement(node, _dotDotDotToken, _propertyName, _name, _initializer); - }, - [SyntaxKind.MissingDeclaration]: (node: MissingDeclaration, visitor: Visitor): MissingDeclaration => { - const _modifiers = visitNodes(node.modifiers, visitor); - return updateMissingDeclaration(node, _modifiers); - }, - [SyntaxKind.FunctionDeclaration]: (node: FunctionDeclaration, visitor: Visitor): FunctionDeclaration => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _asteriskToken = visitNode(node.asteriskToken, visitor, isAsteriskToken); - const _name = visitNode(node.name, visitor, isIdentifier); - const _typeParameters = visitNodes(node.typeParameters, visitor); - const _parameters = visitNodes(node.parameters, visitor); - const _type = visitNode(node.type, visitor, isTypeNode); - const _body = visitNode(node.body, visitor, isFunctionBody); - return updateFunctionDeclaration(node, _modifiers, _asteriskToken, _name, _typeParameters, _parameters, _type, _body); - }, - [SyntaxKind.ClassDeclaration]: (node: ClassDeclaration, visitor: Visitor): ClassDeclaration => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _name = visitNode(node.name, visitor, isIdentifier); - const _typeParameters = visitNodes(node.typeParameters, visitor); - const _heritageClauses = visitNodes(node.heritageClauses, visitor); - const _members = visitNodes(node.members, visitor); - return updateClassDeclaration(node, _modifiers, _name, _typeParameters, _heritageClauses, _members); - }, - [SyntaxKind.ClassExpression]: (node: ClassExpression, visitor: Visitor): ClassExpression => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _name = visitNode(node.name, visitor, isIdentifier); - const _typeParameters = visitNodes(node.typeParameters, visitor); - const _heritageClauses = visitNodes(node.heritageClauses, visitor); - const _members = visitNodes(node.members, visitor); - return updateClassExpression(node, _modifiers, _name, _typeParameters, _heritageClauses, _members); - }, - [SyntaxKind.HeritageClause]: (node: HeritageClause, visitor: Visitor): HeritageClause => { - const _types = visitNodes(node.types, visitor); - return updateHeritageClause(node, _types); - }, - [SyntaxKind.InterfaceDeclaration]: (node: InterfaceDeclaration, visitor: Visitor): InterfaceDeclaration => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _name = visitNode(node.name, visitor, isIdentifier); - const _typeParameters = visitNodes(node.typeParameters, visitor); - const _heritageClauses = visitNodes(node.heritageClauses, visitor); - const _members = visitNodes(node.members, visitor); - return updateInterfaceDeclaration(node, _modifiers, _name, _typeParameters, _heritageClauses, _members); - }, - [SyntaxKind.TypeAliasDeclaration]: (node: TypeAliasDeclaration, visitor: Visitor): TypeAliasDeclaration => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _name = visitNode(node.name, visitor, isIdentifier); - const _typeParameters = visitNodes(node.typeParameters, visitor); - const _type = visitNode(node.type, visitor, isTypeNode); - return updateTypeAliasDeclaration(node, _modifiers, _name, _typeParameters, _type); - }, - [SyntaxKind.EnumMember]: (node: EnumMember, visitor: Visitor): EnumMember => { - const _name = visitNode(node.name, visitor, isPropertyName); - const _initializer = visitNode(node.initializer, visitor, isExpression); - return updateEnumMember(node, _name, _initializer); - }, - [SyntaxKind.EnumDeclaration]: (node: EnumDeclaration, visitor: Visitor): EnumDeclaration => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _name = visitNode(node.name, visitor, isIdentifier); - const _members = visitNodes(node.members, visitor); - return updateEnumDeclaration(node, _modifiers, _name, _members); - }, - [SyntaxKind.ModuleBlock]: (node: ModuleBlock, visitor: Visitor): ModuleBlock => { - const _statements = visitNodes(node.statements, visitor); - return updateModuleBlock(node, _statements); - }, - [SyntaxKind.ImportDeclaration]: (node: ImportDeclaration, visitor: Visitor): ImportDeclaration => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _importClause = visitNode(node.importClause, visitor, isImportClause); - const _moduleSpecifier = visitNode(node.moduleSpecifier, visitor, isExpression); - const _attributes = visitNode(node.attributes, visitor, isImportAttributes); - return updateImportDeclaration(node, _modifiers, _importClause, _moduleSpecifier, _attributes); - }, - [SyntaxKind.ExternalModuleReference]: (node: ExternalModuleReference, visitor: Visitor): ExternalModuleReference => { - const _expression = visitNode(node.expression, visitor, isExpression); - return updateExternalModuleReference(node, _expression); - }, - [SyntaxKind.NamespaceImport]: (node: NamespaceImport, visitor: Visitor): NamespaceImport => { - const _name = visitNode(node.name, visitor, isIdentifier); - return updateNamespaceImport(node, _name); - }, - [SyntaxKind.NamedImports]: (node: NamedImports, visitor: Visitor): NamedImports => { - const _elements = visitNodes(node.elements, visitor); - return updateNamedImports(node, _elements); - }, - [SyntaxKind.ExportAssignment]: (node: ExportAssignment, visitor: Visitor): ExportAssignment => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _type = visitNode(node.type, visitor, isTypeNode); - const _expression = visitNode(node.expression, visitor, isExpression); - return updateExportAssignment(node, _modifiers, _type, _expression); - }, - [SyntaxKind.NamespaceExportDeclaration]: (node: NamespaceExportDeclaration, visitor: Visitor): NamespaceExportDeclaration => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _name = visitNode(node.name, visitor, isIdentifier); - return updateNamespaceExportDeclaration(node, _modifiers, _name); - }, - [SyntaxKind.NamespaceExport]: (node: NamespaceExport, visitor: Visitor): NamespaceExport => { - const _name = visitNode(node.name, visitor, isModuleExportName); - return updateNamespaceExport(node, _name); - }, - [SyntaxKind.NamedExports]: (node: NamedExports, visitor: Visitor): NamedExports => { - const _elements = visitNodes(node.elements, visitor); - return updateNamedExports(node, _elements); - }, - [SyntaxKind.ExportSpecifier]: (node: ExportSpecifier, visitor: Visitor): ExportSpecifier => { - const _propertyName = visitNode(node.propertyName, visitor, isModuleExportName); - const _name = visitNode(node.name, visitor, isModuleExportName); - return updateExportSpecifier(node, _propertyName, _name); - }, - [SyntaxKind.CallSignature]: (node: CallSignatureDeclaration, visitor: Visitor): CallSignatureDeclaration => { - const _typeParameters = visitNodes(node.typeParameters, visitor); - const _parameters = visitNodes(node.parameters, visitor); - const _type = visitNode(node.type, visitor, isTypeNode); - return updateCallSignatureDeclaration(node, _typeParameters, _parameters, _type); - }, - [SyntaxKind.ConstructSignature]: (node: ConstructSignatureDeclaration, visitor: Visitor): ConstructSignatureDeclaration => { - const _typeParameters = visitNodes(node.typeParameters, visitor); - const _parameters = visitNodes(node.parameters, visitor); - const _type = visitNode(node.type, visitor, isTypeNode); - return updateConstructSignatureDeclaration(node, _typeParameters, _parameters, _type); - }, - [SyntaxKind.Constructor]: (node: ConstructorDeclaration, visitor: Visitor): ConstructorDeclaration => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _typeParameters = visitNodes(node.typeParameters, visitor); - const _parameters = visitNodes(node.parameters, visitor); - const _type = visitNode(node.type, visitor, isTypeNode); - const _body = visitNode(node.body, visitor, isFunctionBody); - return updateConstructorDeclaration(node, _modifiers, _typeParameters, _parameters, _type, _body); - }, - [SyntaxKind.GetAccessor]: (node: GetAccessorDeclaration, visitor: Visitor): GetAccessorDeclaration => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _name = visitNode(node.name, visitor, isPropertyName); - const _typeParameters = visitNodes(node.typeParameters, visitor); - const _parameters = visitNodes(node.parameters, visitor); - const _type = visitNode(node.type, visitor, isTypeNode); - const _body = visitNode(node.body, visitor, isFunctionBody); - return updateGetAccessorDeclaration(node, _modifiers, _name, _typeParameters, _parameters, _type, _body); - }, - [SyntaxKind.SetAccessor]: (node: SetAccessorDeclaration, visitor: Visitor): SetAccessorDeclaration => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _name = visitNode(node.name, visitor, isPropertyName); - const _typeParameters = visitNodes(node.typeParameters, visitor); - const _parameters = visitNodes(node.parameters, visitor); - const _type = visitNode(node.type, visitor, isTypeNode); - const _body = visitNode(node.body, visitor, isFunctionBody); - return updateSetAccessorDeclaration(node, _modifiers, _name, _typeParameters, _parameters, _type, _body); - }, - [SyntaxKind.IndexSignature]: (node: IndexSignatureDeclaration, visitor: Visitor): IndexSignatureDeclaration => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _parameters = visitNodes(node.parameters, visitor); - const _type = visitNode(node.type, visitor, isTypeNode); - return updateIndexSignatureDeclaration(node, _modifiers, _parameters, _type); - }, - [SyntaxKind.MethodSignature]: (node: MethodSignatureDeclaration, visitor: Visitor): MethodSignatureDeclaration => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _name = visitNode(node.name, visitor, isPropertyName); - const _postfixToken = visitNode(node.postfixToken, visitor, isQuestionOrExclamationToken); - const _typeParameters = visitNodes(node.typeParameters, visitor); - const _parameters = visitNodes(node.parameters, visitor); - const _type = visitNode(node.type, visitor, isTypeNode); - return updateMethodSignatureDeclaration(node, _modifiers, _name, _postfixToken, _typeParameters, _parameters, _type); - }, - [SyntaxKind.MethodDeclaration]: (node: MethodDeclaration, visitor: Visitor): MethodDeclaration => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _asteriskToken = visitNode(node.asteriskToken, visitor, isAsteriskToken); - const _name = visitNode(node.name, visitor, isPropertyName); - const _postfixToken = visitNode(node.postfixToken, visitor, isQuestionOrExclamationToken); - const _typeParameters = visitNodes(node.typeParameters, visitor); - const _parameters = visitNodes(node.parameters, visitor); - const _type = visitNode(node.type, visitor, isTypeNode); - const _body = visitNode(node.body, visitor, isFunctionBody); - return updateMethodDeclaration(node, _modifiers, _asteriskToken, _name, _postfixToken, _typeParameters, _parameters, _type, _body); - }, - [SyntaxKind.PropertySignature]: (node: PropertySignatureDeclaration, visitor: Visitor): PropertySignatureDeclaration => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _name = visitNode(node.name, visitor, isPropertyName); - const _postfixToken = visitNode(node.postfixToken, visitor, isQuestionOrExclamationToken); - const _type = visitNode(node.type, visitor, isTypeNode); - const _initializer = visitNode(node.initializer, visitor, isExpression); - return updatePropertySignatureDeclaration(node, _modifiers, _name, _postfixToken, _type, _initializer); - }, - [SyntaxKind.PropertyDeclaration]: (node: PropertyDeclaration, visitor: Visitor): PropertyDeclaration => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _name = visitNode(node.name, visitor, isPropertyName); - const _postfixToken = visitNode(node.postfixToken, visitor, isQuestionOrExclamationToken); - const _type = visitNode(node.type, visitor, isTypeNode); - const _initializer = visitNode(node.initializer, visitor, isExpression); - return updatePropertyDeclaration(node, _modifiers, _name, _postfixToken, _type, _initializer); - }, - [SyntaxKind.ClassStaticBlockDeclaration]: (node: ClassStaticBlockDeclaration, visitor: Visitor): ClassStaticBlockDeclaration => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _body = visitNode(node.body, visitor, isBlock); - return updateClassStaticBlockDeclaration(node, _modifiers, _body); - }, - [SyntaxKind.BinaryExpression]: (node: BinaryExpression, visitor: Visitor): BinaryExpression => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _left = visitNode(node.left, visitor, isExpression); - const _type = visitNode(node.type, visitor, isTypeNode); - const _operatorToken = visitNode(node.operatorToken, visitor, isBinaryOperatorToken); - const _right = visitNode(node.right, visitor, isExpression); - return updateBinaryExpression(node, _modifiers, _left, _type, _operatorToken, _right); - }, - [SyntaxKind.PrefixUnaryExpression]: (node: PrefixUnaryExpression, visitor: Visitor): PrefixUnaryExpression => { - const _operand = visitNode(node.operand, visitor, isExpression); - return updatePrefixUnaryExpression(node, _operand); - }, - [SyntaxKind.PostfixUnaryExpression]: (node: PostfixUnaryExpression, visitor: Visitor): PostfixUnaryExpression => { - const _operand = visitNode(node.operand, visitor, isExpression); - return updatePostfixUnaryExpression(node, _operand); - }, - [SyntaxKind.YieldExpression]: (node: YieldExpression, visitor: Visitor): YieldExpression => { - const _asteriskToken = visitNode(node.asteriskToken, visitor, isAsteriskToken); - const _expression = visitNode(node.expression, visitor, isExpression); - return updateYieldExpression(node, _asteriskToken, _expression); - }, - [SyntaxKind.ArrowFunction]: (node: ArrowFunction, visitor: Visitor): ArrowFunction => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _typeParameters = visitNodes(node.typeParameters, visitor); - const _parameters = visitNodes(node.parameters, visitor); - const _type = visitNode(node.type, visitor, isTypeNode); - const _equalsGreaterThanToken = visitNode(node.equalsGreaterThanToken, visitor, isEqualsGreaterThanToken); - const _body = visitNode(node.body, visitor, isConciseBody); - return updateArrowFunction(node, _modifiers, _typeParameters, _parameters, _type, _equalsGreaterThanToken, _body); - }, - [SyntaxKind.FunctionExpression]: (node: FunctionExpression, visitor: Visitor): FunctionExpression => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _asteriskToken = visitNode(node.asteriskToken, visitor, isAsteriskToken); - const _name = visitNode(node.name, visitor, isIdentifier); - const _typeParameters = visitNodes(node.typeParameters, visitor); - const _parameters = visitNodes(node.parameters, visitor); - const _type = visitNode(node.type, visitor, isTypeNode); - const _body = visitNode(node.body, visitor, isFunctionBody); - return updateFunctionExpression(node, _modifiers, _asteriskToken, _name, _typeParameters, _parameters, _type, _body); - }, - [SyntaxKind.AsExpression]: (node: AsExpression, visitor: Visitor): AsExpression => { - const _expression = visitNode(node.expression, visitor, isExpression); - const _type = visitNode(node.type, visitor, isTypeNode); - return updateAsExpression(node, _expression, _type); - }, - [SyntaxKind.SatisfiesExpression]: (node: SatisfiesExpression, visitor: Visitor): SatisfiesExpression => { - const _expression = visitNode(node.expression, visitor, isExpression); - const _type = visitNode(node.type, visitor, isTypeNode); - return updateSatisfiesExpression(node, _expression, _type); - }, - [SyntaxKind.ConditionalExpression]: (node: ConditionalExpression, visitor: Visitor): ConditionalExpression => { - const _condition = visitNode(node.condition, visitor, isExpression); - const _questionToken = visitNode(node.questionToken, visitor, isQuestionToken); - const _whenTrue = visitNode(node.whenTrue, visitor, isExpression); - const _colonToken = visitNode(node.colonToken, visitor, isColonToken); - const _whenFalse = visitNode(node.whenFalse, visitor, isExpression); - return updateConditionalExpression(node, _condition, _questionToken, _whenTrue, _colonToken, _whenFalse); - }, - [SyntaxKind.PropertyAccessExpression]: (node: PropertyAccessExpression, visitor: Visitor): PropertyAccessExpression => { - const _expression = visitNode(node.expression, visitor, isExpression); - const _questionDotToken = visitNode(node.questionDotToken, visitor, isQuestionDotToken); - const _name = visitNode(node.name, visitor, isMemberName); - return updatePropertyAccessExpression(node, _expression, _questionDotToken, _name); - }, - [SyntaxKind.ElementAccessExpression]: (node: ElementAccessExpression, visitor: Visitor): ElementAccessExpression => { - const _expression = visitNode(node.expression, visitor, isExpression); - const _questionDotToken = visitNode(node.questionDotToken, visitor, isQuestionDotToken); - const _argumentExpression = visitNode(node.argumentExpression, visitor, isExpression); - return updateElementAccessExpression(node, _expression, _questionDotToken, _argumentExpression); - }, - [SyntaxKind.CallExpression]: (node: CallExpression, visitor: Visitor): CallExpression => { - const _expression = visitNode(node.expression, visitor, isExpression); - const _questionDotToken = visitNode(node.questionDotToken, visitor, isQuestionDotToken); - const _typeArguments = visitNodes(node.typeArguments, visitor); - const _arguments = visitNodes(node.arguments, visitor); - return updateCallExpression(node, _expression, _questionDotToken, _typeArguments, _arguments); - }, - [SyntaxKind.NewExpression]: (node: NewExpression, visitor: Visitor): NewExpression => { - const _expression = visitNode(node.expression, visitor, isExpression); - const _typeArguments = visitNodes(node.typeArguments, visitor); - const _arguments = visitNodes(node.arguments, visitor); - return updateNewExpression(node, _expression, _typeArguments, _arguments); - }, - [SyntaxKind.MetaProperty]: (node: MetaProperty, visitor: Visitor): MetaProperty => { - const _name = visitNode(node.name, visitor, isIdentifier); - return updateMetaProperty(node, _name); - }, - [SyntaxKind.NonNullExpression]: (node: NonNullExpression, visitor: Visitor): NonNullExpression => { - const _expression = visitNode(node.expression, visitor, isExpression); - return updateNonNullExpression(node, _expression); - }, - [SyntaxKind.SpreadElement]: (node: SpreadElement, visitor: Visitor): SpreadElement => { - const _expression = visitNode(node.expression, visitor, isExpression); - return updateSpreadElement(node, _expression); - }, - [SyntaxKind.TemplateExpression]: (node: TemplateExpression, visitor: Visitor): TemplateExpression => { - const _head = visitNode(node.head, visitor, isTemplateHead); - const _templateSpans = visitNodes(node.templateSpans, visitor); - return updateTemplateExpression(node, _head, _templateSpans); - }, - [SyntaxKind.TemplateSpan]: (node: TemplateSpan, visitor: Visitor): TemplateSpan => { - const _expression = visitNode(node.expression, visitor, isExpression); - const _literal = visitNode(node.literal, visitor, isTemplateMiddleOrTail); - return updateTemplateSpan(node, _expression, _literal); - }, - [SyntaxKind.TaggedTemplateExpression]: (node: TaggedTemplateExpression, visitor: Visitor): TaggedTemplateExpression => { - const _tag = visitNode(node.tag, visitor, isExpression); - const _questionDotToken = visitNode(node.questionDotToken, visitor, isQuestionDotToken); - const _typeArguments = visitNodes(node.typeArguments, visitor); - const _template = visitNode(node.template, visitor, isTemplateLiteral); - return updateTaggedTemplateExpression(node, _tag, _questionDotToken, _typeArguments, _template); - }, - [SyntaxKind.ParenthesizedExpression]: (node: ParenthesizedExpression, visitor: Visitor): ParenthesizedExpression => { - const _expression = visitNode(node.expression, visitor, isExpression); - return updateParenthesizedExpression(node, _expression); - }, - [SyntaxKind.ArrayLiteralExpression]: (node: ArrayLiteralExpression, visitor: Visitor): ArrayLiteralExpression => { - const _elements = visitNodes(node.elements, visitor); - return updateArrayLiteralExpression(node, _elements); - }, - [SyntaxKind.ObjectLiteralExpression]: (node: ObjectLiteralExpression, visitor: Visitor): ObjectLiteralExpression => { - const _properties = visitNodes(node.properties, visitor); - return updateObjectLiteralExpression(node, _properties); - }, - [SyntaxKind.SpreadAssignment]: (node: SpreadAssignment, visitor: Visitor): SpreadAssignment => { - const _expression = visitNode(node.expression, visitor, isExpression); - return updateSpreadAssignment(node, _expression); - }, - [SyntaxKind.PropertyAssignment]: (node: PropertyAssignment, visitor: Visitor): PropertyAssignment => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _name = visitNode(node.name, visitor, isPropertyName); - const _postfixToken = visitNode(node.postfixToken, visitor, isQuestionOrExclamationToken); - const _type = visitNode(node.type, visitor, isTypeNode); - const _initializer = visitNode(node.initializer, visitor, isExpression); - return updatePropertyAssignment(node, _modifiers, _name, _postfixToken, _type, _initializer); - }, - [SyntaxKind.ShorthandPropertyAssignment]: (node: ShorthandPropertyAssignment, visitor: Visitor): ShorthandPropertyAssignment => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _name = visitNode(node.name, visitor, isPropertyName); - const _postfixToken = visitNode(node.postfixToken, visitor, isQuestionOrExclamationToken); - const _type = visitNode(node.type, visitor, isTypeNode); - const _equalsToken = visitNode(node.equalsToken, visitor, isEqualsToken); - const _objectAssignmentInitializer = visitNode(node.objectAssignmentInitializer, visitor, isExpression); - return updateShorthandPropertyAssignment(node, _modifiers, _name, _postfixToken, _type, _equalsToken, _objectAssignmentInitializer); - }, - [SyntaxKind.DeleteExpression]: (node: DeleteExpression, visitor: Visitor): DeleteExpression => { - const _expression = visitNode(node.expression, visitor, isExpression); - return updateDeleteExpression(node, _expression); - }, - [SyntaxKind.TypeOfExpression]: (node: TypeOfExpression, visitor: Visitor): TypeOfExpression => { - const _expression = visitNode(node.expression, visitor, isExpression); - return updateTypeOfExpression(node, _expression); - }, - [SyntaxKind.VoidExpression]: (node: VoidExpression, visitor: Visitor): VoidExpression => { - const _expression = visitNode(node.expression, visitor, isExpression); - return updateVoidExpression(node, _expression); - }, - [SyntaxKind.AwaitExpression]: (node: AwaitExpression, visitor: Visitor): AwaitExpression => { - const _expression = visitNode(node.expression, visitor, isExpression); - return updateAwaitExpression(node, _expression); - }, - [SyntaxKind.TypeAssertionExpression]: (node: TypeAssertion, visitor: Visitor): TypeAssertion => { - const _type = visitNode(node.type, visitor, isTypeNode); - const _expression = visitNode(node.expression, visitor, isExpression); - return updateTypeAssertion(node, _type, _expression); - }, - [SyntaxKind.UnionType]: (node: UnionTypeNode, visitor: Visitor): UnionTypeNode => { - const _types = visitNodes(node.types, visitor); - return updateUnionTypeNode(node, _types); - }, - [SyntaxKind.IntersectionType]: (node: IntersectionTypeNode, visitor: Visitor): IntersectionTypeNode => { - const _types = visitNodes(node.types, visitor); - return updateIntersectionTypeNode(node, _types); - }, - [SyntaxKind.ConditionalType]: (node: ConditionalTypeNode, visitor: Visitor): ConditionalTypeNode => { - const _checkType = visitNode(node.checkType, visitor, isTypeNode); - const _extendsType = visitNode(node.extendsType, visitor, isTypeNode); - const _trueType = visitNode(node.trueType, visitor, isTypeNode); - const _falseType = visitNode(node.falseType, visitor, isTypeNode); - return updateConditionalTypeNode(node, _checkType, _extendsType, _trueType, _falseType); - }, - [SyntaxKind.TypeOperator]: (node: TypeOperatorNode, visitor: Visitor): TypeOperatorNode => { - const _type = visitNode(node.type, visitor, isTypeNode); - return updateTypeOperatorNode(node, _type); - }, - [SyntaxKind.InferType]: (node: InferTypeNode, visitor: Visitor): InferTypeNode => { - const _typeParameter = visitNode(node.typeParameter, visitor, isTypeParameterDeclaration); - return updateInferTypeNode(node, _typeParameter); - }, - [SyntaxKind.ArrayType]: (node: ArrayTypeNode, visitor: Visitor): ArrayTypeNode => { - const _elementType = visitNode(node.elementType, visitor, isTypeNode); - return updateArrayTypeNode(node, _elementType); - }, - [SyntaxKind.IndexedAccessType]: (node: IndexedAccessTypeNode, visitor: Visitor): IndexedAccessTypeNode => { - const _objectType = visitNode(node.objectType, visitor, isTypeNode); - const _indexType = visitNode(node.indexType, visitor, isTypeNode); - return updateIndexedAccessTypeNode(node, _objectType, _indexType); - }, - [SyntaxKind.TypeReference]: (node: TypeReferenceNode, visitor: Visitor): TypeReferenceNode => { - const _typeName = visitNode(node.typeName, visitor, isEntityName); - const _typeArguments = visitNodes(node.typeArguments, visitor); - return updateTypeReferenceNode(node, _typeName, _typeArguments); - }, - [SyntaxKind.ExpressionWithTypeArguments]: (node: ExpressionWithTypeArguments, visitor: Visitor): ExpressionWithTypeArguments => { - const _expression = visitNode(node.expression, visitor, isExpression); - const _typeArguments = visitNodes(node.typeArguments, visitor); - return updateExpressionWithTypeArguments(node, _expression, _typeArguments); - }, - [SyntaxKind.LiteralType]: (node: LiteralTypeNode, visitor: Visitor): LiteralTypeNode => { - const _literal = visitNode(node.literal, visitor); - return updateLiteralTypeNode(node, _literal); - }, - [SyntaxKind.TypePredicate]: (node: TypePredicateNode, visitor: Visitor): TypePredicateNode => { - const _assertsModifier = visitNode(node.assertsModifier, visitor, isAssertsKeyword); - const _parameterName = visitNode(node.parameterName, visitor, isTypePredicateParameterName); - const _type = visitNode(node.type, visitor, isTypeNode); - return updateTypePredicateNode(node, _assertsModifier, _parameterName, _type); - }, - [SyntaxKind.ImportAttribute]: (node: ImportAttribute, visitor: Visitor): ImportAttribute => { - const _name = visitNode(node.name, visitor, isImportAttributeName); - const _value = visitNode(node.value, visitor, isExpression); - return updateImportAttribute(node, _name, _value); - }, - [SyntaxKind.ImportAttributes]: (node: ImportAttributes, visitor: Visitor): ImportAttributes => { - const _attributes = visitNodes(node.attributes, visitor); - return updateImportAttributes(node, _attributes); - }, - [SyntaxKind.TypeQuery]: (node: TypeQueryNode, visitor: Visitor): TypeQueryNode => { - const _exprName = visitNode(node.exprName, visitor, isEntityName); - const _typeArguments = visitNodes(node.typeArguments, visitor); - return updateTypeQueryNode(node, _exprName, _typeArguments); - }, - [SyntaxKind.MappedType]: (node: MappedTypeNode, visitor: Visitor): MappedTypeNode => { - const _readonlyToken = visitNode(node.readonlyToken, visitor, isReadonlyKeywordOrPlusOrMinusToken); - const _typeParameter = visitNode(node.typeParameter, visitor, isTypeParameterDeclaration); - const _nameType = visitNode(node.nameType, visitor, isTypeNode); - const _questionToken = visitNode(node.questionToken, visitor, isQuestionOrPlusOrMinusToken); - const _type = visitNode(node.type, visitor, isTypeNode); - const _members = visitNodes(node.members, visitor); - return updateMappedTypeNode(node, _readonlyToken, _typeParameter, _nameType, _questionToken, _type, _members); - }, - [SyntaxKind.TypeLiteral]: (node: TypeLiteralNode, visitor: Visitor): TypeLiteralNode => { - const _members = visitNodes(node.members, visitor); - return updateTypeLiteralNode(node, _members); - }, - [SyntaxKind.TupleType]: (node: TupleTypeNode, visitor: Visitor): TupleTypeNode => { - const _elements = visitNodes(node.elements, visitor); - return updateTupleTypeNode(node, _elements); - }, - [SyntaxKind.NamedTupleMember]: (node: NamedTupleMember, visitor: Visitor): NamedTupleMember => { - const _dotDotDotToken = visitNode(node.dotDotDotToken, visitor, isDotDotDotToken); - const _name = visitNode(node.name, visitor, isIdentifier); - const _questionToken = visitNode(node.questionToken, visitor, isQuestionToken); - const _type = visitNode(node.type, visitor, isTypeNode); - return updateNamedTupleMember(node, _dotDotDotToken, _name, _questionToken, _type); - }, - [SyntaxKind.OptionalType]: (node: OptionalTypeNode, visitor: Visitor): OptionalTypeNode => { - const _type = visitNode(node.type, visitor, isTypeNode); - return updateOptionalTypeNode(node, _type); - }, - [SyntaxKind.RestType]: (node: RestTypeNode, visitor: Visitor): RestTypeNode => { - const _type = visitNode(node.type, visitor, isTypeNode); - return updateRestTypeNode(node, _type); - }, - [SyntaxKind.ParenthesizedType]: (node: ParenthesizedTypeNode, visitor: Visitor): ParenthesizedTypeNode => { - const _type = visitNode(node.type, visitor, isTypeNode); - return updateParenthesizedTypeNode(node, _type); - }, - [SyntaxKind.FunctionType]: (node: FunctionTypeNode, visitor: Visitor): FunctionTypeNode => { - const _typeParameters = visitNodes(node.typeParameters, visitor); - const _parameters = visitNodes(node.parameters, visitor); - const _type = visitNode(node.type, visitor, isTypeNode); - return updateFunctionTypeNode(node, _typeParameters, _parameters, _type); - }, - [SyntaxKind.ConstructorType]: (node: ConstructorTypeNode, visitor: Visitor): ConstructorTypeNode => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _typeParameters = visitNodes(node.typeParameters, visitor); - const _parameters = visitNodes(node.parameters, visitor); - const _type = visitNode(node.type, visitor, isTypeNode); - return updateConstructorTypeNode(node, _modifiers, _typeParameters, _parameters, _type); - }, - [SyntaxKind.TemplateLiteralType]: (node: TemplateLiteralTypeNode, visitor: Visitor): TemplateLiteralTypeNode => { - const _head = visitNode(node.head, visitor, isTemplateHead); - const _templateSpans = visitNodes(node.templateSpans, visitor); - return updateTemplateLiteralTypeNode(node, _head, _templateSpans); - }, - [SyntaxKind.TemplateLiteralTypeSpan]: (node: TemplateLiteralTypeSpan, visitor: Visitor): TemplateLiteralTypeSpan => { - const _type = visitNode(node.type, visitor, isTypeNode); - const _literal = visitNode(node.literal, visitor, isTemplateMiddleOrTail); - return updateTemplateLiteralTypeSpan(node, _type, _literal); - }, - [SyntaxKind.SyntheticExpression]: (node: SyntheticExpression, visitor: Visitor): SyntheticExpression => { - const _tupleNameSource = visitNode(node.tupleNameSource, visitor); - return updateSyntheticExpression(node, _tupleNameSource); - }, - [SyntaxKind.PartiallyEmittedExpression]: (node: PartiallyEmittedExpression, visitor: Visitor): PartiallyEmittedExpression => { - const _expression = visitNode(node.expression, visitor, isExpression); - return updatePartiallyEmittedExpression(node, _expression); - }, - [SyntaxKind.JsxElement]: (node: JsxElement, visitor: Visitor): JsxElement => { - const _openingElement = visitNode(node.openingElement, visitor, isJsxOpeningElement); - const _children = visitNodes(node.children, visitor); - const _closingElement = visitNode(node.closingElement, visitor, isJsxClosingElement); - return updateJsxElement(node, _openingElement, _children, _closingElement); - }, - [SyntaxKind.JsxAttributes]: (node: JsxAttributes, visitor: Visitor): JsxAttributes => { - const _properties = visitNodes(node.properties, visitor); - return updateJsxAttributes(node, _properties); - }, - [SyntaxKind.JsxNamespacedName]: (node: JsxNamespacedName, visitor: Visitor): JsxNamespacedName => { - const _namespace = visitNode(node.namespace, visitor, isIdentifier); - const _name = visitNode(node.name, visitor, isIdentifier); - return updateJsxNamespacedName(node, _namespace, _name); - }, - [SyntaxKind.JsxOpeningElement]: (node: JsxOpeningElement, visitor: Visitor): JsxOpeningElement => { - const _tagName = visitNode(node.tagName, visitor, isJsxTagNameExpression); - const _typeArguments = visitNodes(node.typeArguments, visitor); - const _attributes = visitNode(node.attributes, visitor, isJsxAttributes); - return updateJsxOpeningElement(node, _tagName, _typeArguments, _attributes); - }, - [SyntaxKind.JsxSelfClosingElement]: (node: JsxSelfClosingElement, visitor: Visitor): JsxSelfClosingElement => { - const _tagName = visitNode(node.tagName, visitor, isJsxTagNameExpression); - const _typeArguments = visitNodes(node.typeArguments, visitor); - const _attributes = visitNode(node.attributes, visitor, isJsxAttributes); - return updateJsxSelfClosingElement(node, _tagName, _typeArguments, _attributes); - }, - [SyntaxKind.JsxFragment]: (node: JsxFragment, visitor: Visitor): JsxFragment => { - const _openingFragment = visitNode(node.openingFragment, visitor, isJsxOpeningFragment); - const _children = visitNodes(node.children, visitor); - const _closingFragment = visitNode(node.closingFragment, visitor, isJsxClosingFragment); - return updateJsxFragment(node, _openingFragment, _children, _closingFragment); - }, - [SyntaxKind.JsxAttribute]: (node: JsxAttribute, visitor: Visitor): JsxAttribute => { - const _name = visitNode(node.name, visitor, isJsxAttributeName); - const _initializer = visitNode(node.initializer, visitor, isJsxAttributeValue); - return updateJsxAttribute(node, _name, _initializer); - }, - [SyntaxKind.JsxSpreadAttribute]: (node: JsxSpreadAttribute, visitor: Visitor): JsxSpreadAttribute => { - const _expression = visitNode(node.expression, visitor, isExpression); - return updateJsxSpreadAttribute(node, _expression); - }, - [SyntaxKind.JsxClosingElement]: (node: JsxClosingElement, visitor: Visitor): JsxClosingElement => { - const _tagName = visitNode(node.tagName, visitor, isJsxTagNameExpression); - return updateJsxClosingElement(node, _tagName); - }, - [SyntaxKind.JsxExpression]: (node: JsxExpression, visitor: Visitor): JsxExpression => { - const _dotDotDotToken = visitNode(node.dotDotDotToken, visitor, isDotDotDotToken); - const _expression = visitNode(node.expression, visitor, isExpression); - return updateJsxExpression(node, _dotDotDotToken, _expression); - }, - [SyntaxKind.SyntaxList]: (node: SyntaxList, visitor: Visitor): SyntaxList => { - const _children = visitNodesArray(node.children, visitor); - return updateSyntaxList(node, _children); - }, - [SyntaxKind.JSDoc]: (node: JSDoc, visitor: Visitor): JSDoc => { - const _comment = visitNodes(node.comment, visitor); - const _tags = visitNodes(node.tags, visitor); - return updateJSDoc(node, _comment, _tags); - }, - [SyntaxKind.JSDocTypeExpression]: (node: JSDocTypeExpression, visitor: Visitor): JSDocTypeExpression => { - const _type = visitNode(node.type, visitor, isTypeNode); - return updateJSDocTypeExpression(node, _type); - }, - [SyntaxKind.JSDocNonNullableType]: (node: JSDocNonNullableType, visitor: Visitor): JSDocNonNullableType => { - const _type = visitNode(node.type, visitor, isTypeNode); - return updateJSDocNonNullableType(node, _type); - }, - [SyntaxKind.JSDocNullableType]: (node: JSDocNullableType, visitor: Visitor): JSDocNullableType => { - const _type = visitNode(node.type, visitor, isTypeNode); - return updateJSDocNullableType(node, _type); - }, - [SyntaxKind.JSDocVariadicType]: (node: JSDocVariadicType, visitor: Visitor): JSDocVariadicType => { - const _type = visitNode(node.type, visitor, isTypeNode); - return updateJSDocVariadicType(node, _type); - }, - [SyntaxKind.JSDocOptionalType]: (node: JSDocOptionalType, visitor: Visitor): JSDocOptionalType => { - const _type = visitNode(node.type, visitor, isTypeNode); - return updateJSDocOptionalType(node, _type); - }, - [SyntaxKind.JSDocTypeTag]: (node: JSDocTypeTag, visitor: Visitor): JSDocTypeTag => { - const _tagName = visitNode(node.tagName, visitor, isIdentifier); - const _typeExpression = visitNode(node.typeExpression, visitor); - const _comment = visitNodes(node.comment, visitor); - return updateJSDocTypeTag(node, _tagName, _typeExpression, _comment); - }, - [SyntaxKind.JSDocUnknownTag]: (node: JSDocUnknownTag, visitor: Visitor): JSDocUnknownTag => { - const _tagName = visitNode(node.tagName, visitor, isIdentifier); - const _comment = visitNodes(node.comment, visitor); - return updateJSDocUnknownTag(node, _tagName, _comment); - }, - [SyntaxKind.JSDocTemplateTag]: (node: JSDocTemplateTag, visitor: Visitor): JSDocTemplateTag => { - const _tagName = visitNode(node.tagName, visitor, isIdentifier); - const _constraint = visitNode(node.constraint, visitor); - const _typeParameters = visitNodes(node.typeParameters, visitor); - const _comment = visitNodes(node.comment, visitor); - return updateJSDocTemplateTag(node, _tagName, _constraint, _typeParameters, _comment); - }, - [SyntaxKind.JSDocReturnTag]: (node: JSDocReturnTag, visitor: Visitor): JSDocReturnTag => { - const _tagName = visitNode(node.tagName, visitor, isIdentifier); - const _typeExpression = visitNode(node.typeExpression, visitor, isTypeNode); - const _comment = visitNodes(node.comment, visitor); - return updateJSDocReturnTag(node, _tagName, _typeExpression, _comment); - }, - [SyntaxKind.JSDocPublicTag]: (node: JSDocPublicTag, visitor: Visitor): JSDocPublicTag => { - const _tagName = visitNode(node.tagName, visitor, isIdentifier); - const _comment = visitNodes(node.comment, visitor); - return updateJSDocPublicTag(node, _tagName, _comment); - }, - [SyntaxKind.JSDocPrivateTag]: (node: JSDocPrivateTag, visitor: Visitor): JSDocPrivateTag => { - const _tagName = visitNode(node.tagName, visitor, isIdentifier); - const _comment = visitNodes(node.comment, visitor); - return updateJSDocPrivateTag(node, _tagName, _comment); - }, - [SyntaxKind.JSDocProtectedTag]: (node: JSDocProtectedTag, visitor: Visitor): JSDocProtectedTag => { - const _tagName = visitNode(node.tagName, visitor, isIdentifier); - const _comment = visitNodes(node.comment, visitor); - return updateJSDocProtectedTag(node, _tagName, _comment); - }, - [SyntaxKind.JSDocReadonlyTag]: (node: JSDocReadonlyTag, visitor: Visitor): JSDocReadonlyTag => { - const _tagName = visitNode(node.tagName, visitor, isIdentifier); - const _comment = visitNodes(node.comment, visitor); - return updateJSDocReadonlyTag(node, _tagName, _comment); - }, - [SyntaxKind.JSDocOverrideTag]: (node: JSDocOverrideTag, visitor: Visitor): JSDocOverrideTag => { - const _tagName = visitNode(node.tagName, visitor, isIdentifier); - const _comment = visitNodes(node.comment, visitor); - return updateJSDocOverrideTag(node, _tagName, _comment); - }, - [SyntaxKind.JSDocDeprecatedTag]: (node: JSDocDeprecatedTag, visitor: Visitor): JSDocDeprecatedTag => { - const _tagName = visitNode(node.tagName, visitor, isIdentifier); - const _comment = visitNodes(node.comment, visitor); - return updateJSDocDeprecatedTag(node, _tagName, _comment); - }, - [SyntaxKind.JSDocSeeTag]: (node: JSDocSeeTag, visitor: Visitor): JSDocSeeTag => { - const _tagName = visitNode(node.tagName, visitor, isIdentifier); - const _nameExpression = visitNode(node.nameExpression, visitor, isTypeNode); - const _comment = visitNodes(node.comment, visitor); - return updateJSDocSeeTag(node, _tagName, _nameExpression, _comment); - }, - [SyntaxKind.JSDocImplementsTag]: (node: JSDocImplementsTag, visitor: Visitor): JSDocImplementsTag => { - const _tagName = visitNode(node.tagName, visitor, isIdentifier); - const _className = visitNode(node.className, visitor, isExpressionWithTypeArguments); - const _comment = visitNodes(node.comment, visitor); - return updateJSDocImplementsTag(node, _tagName, _className, _comment); - }, - [SyntaxKind.JSDocAugmentsTag]: (node: JSDocAugmentsTag, visitor: Visitor): JSDocAugmentsTag => { - const _tagName = visitNode(node.tagName, visitor, isIdentifier); - const _className = visitNode(node.className, visitor, isExpressionWithTypeArguments); - const _comment = visitNodes(node.comment, visitor); - return updateJSDocAugmentsTag(node, _tagName, _className, _comment); - }, - [SyntaxKind.JSDocSatisfiesTag]: (node: JSDocSatisfiesTag, visitor: Visitor): JSDocSatisfiesTag => { - const _tagName = visitNode(node.tagName, visitor, isIdentifier); - const _typeExpression = visitNode(node.typeExpression, visitor, isTypeNode); - const _comment = visitNodes(node.comment, visitor); - return updateJSDocSatisfiesTag(node, _tagName, _typeExpression, _comment); - }, - [SyntaxKind.JSDocThrowsTag]: (node: JSDocThrowsTag, visitor: Visitor): JSDocThrowsTag => { - const _tagName = visitNode(node.tagName, visitor, isIdentifier); - const _typeExpression = visitNode(node.typeExpression, visitor, isTypeNode); - const _comment = visitNodes(node.comment, visitor); - return updateJSDocThrowsTag(node, _tagName, _typeExpression, _comment); - }, - [SyntaxKind.JSDocThisTag]: (node: JSDocThisTag, visitor: Visitor): JSDocThisTag => { - const _tagName = visitNode(node.tagName, visitor, isIdentifier); - const _typeExpression = visitNode(node.typeExpression, visitor, isTypeNode); - const _comment = visitNodes(node.comment, visitor); - return updateJSDocThisTag(node, _tagName, _typeExpression, _comment); - }, - [SyntaxKind.JSDocImportTag]: (node: JSDocImportTag, visitor: Visitor): JSDocImportTag => { - const _tagName = visitNode(node.tagName, visitor, isIdentifier); - const _importClause = visitNode(node.importClause, visitor, isImportClause); - const _moduleSpecifier = visitNode(node.moduleSpecifier, visitor, isExpression); - const _attributes = visitNode(node.attributes, visitor, isImportAttributes); - const _comment = visitNodes(node.comment, visitor); - return updateJSDocImportTag(node, _tagName, _importClause, _moduleSpecifier, _attributes, _comment); - }, - [SyntaxKind.JSDocCallbackTag]: (node: JSDocCallbackTag, visitor: Visitor): JSDocCallbackTag => { - const _tagName = visitNode(node.tagName, visitor, isIdentifier); - const _typeExpression = visitNode(node.typeExpression, visitor, isTypeNode); - const _name = visitNode(node.name, visitor, isJSDocFullName); - const _comment = visitNodes(node.comment, visitor); - return updateJSDocCallbackTag(node, _tagName, _typeExpression, _name, _comment); - }, - [SyntaxKind.JSDocOverloadTag]: (node: JSDocOverloadTag, visitor: Visitor): JSDocOverloadTag => { - const _tagName = visitNode(node.tagName, visitor, isIdentifier); - const _typeExpression = visitNode(node.typeExpression, visitor, isTypeNode); - const _comment = visitNodes(node.comment, visitor); - return updateJSDocOverloadTag(node, _tagName, _typeExpression, _comment); - }, - [SyntaxKind.JSDocTypedefTag]: (node: JSDocTypedefTag, visitor: Visitor): JSDocTypedefTag => { - const _tagName = visitNode(node.tagName, visitor, isIdentifier); - const _typeExpression = visitNode(node.typeExpression, visitor); - const _name = visitNode(node.name, visitor, isJSDocFullName); - const _comment = visitNodes(node.comment, visitor); - return updateJSDocTypedefTag(node, _tagName, _typeExpression, _name, _comment); - }, - [SyntaxKind.JSDocSignature]: (node: JSDocSignature, visitor: Visitor): JSDocSignature => { - const _typeParameters = visitNodes(node.typeParameters, visitor); - const _parameters = visitNodes(node.parameters, visitor); - const _type = visitNode(node.type, visitor, isTypeNode); - return updateJSDocSignature(node, _typeParameters, _parameters, _type); - }, - [SyntaxKind.JSDocNameReference]: (node: JSDocNameReference, visitor: Visitor): JSDocNameReference => { - const _name = visitNode(node.name, visitor, isEntityName); - return updateJSDocNameReference(node, _name); - }, - [SyntaxKind.SourceFile]: (node: SourceFile, visitor: Visitor): SourceFile => { - const _statements = visitNodes(node.statements, visitor); - const _endOfFileToken = visitNode(node.endOfFileToken, visitor, isEndOfFile); - return updateSourceFile(node, _statements, _endOfFileToken); - }, - [SyntaxKind.ModuleDeclaration]: (node: ModuleDeclaration, visitor: Visitor): ModuleDeclaration => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _name = visitNode(node.name, visitor, isModuleName); - const _body = visitNode(node.body, visitor, isModuleBody); - return updateModuleDeclaration(node, _modifiers, _name, _body); - }, - [SyntaxKind.ImportEqualsDeclaration]: (node: ImportEqualsDeclaration, visitor: Visitor): ImportEqualsDeclaration => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _name = visitNode(node.name, visitor, isIdentifier); - const _moduleReference = visitNode(node.moduleReference, visitor, isModuleReference); - return updateImportEqualsDeclaration(node, _modifiers, _name, _moduleReference); - }, - [SyntaxKind.ExportDeclaration]: (node: ExportDeclaration, visitor: Visitor): ExportDeclaration => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _exportClause = visitNode(node.exportClause, visitor, isNamedExportBindings); - const _moduleSpecifier = visitNode(node.moduleSpecifier, visitor, isExpression); - const _attributes = visitNode(node.attributes, visitor, isImportAttributes); - return updateExportDeclaration(node, _modifiers, _exportClause, _moduleSpecifier, _attributes); - }, - [SyntaxKind.ImportType]: (node: ImportTypeNode, visitor: Visitor): ImportTypeNode => { - const _argument = visitNode(node.argument, visitor, isTypeNode); - const _attributes = visitNode(node.attributes, visitor, isImportAttributes); - const _qualifier = visitNode(node.qualifier, visitor, isEntityName); - const _typeArguments = visitNodes(node.typeArguments, visitor); - return updateImportTypeNode(node, _argument, _attributes, _qualifier, _typeArguments); - }, - [SyntaxKind.ImportClause]: (node: ImportClause, visitor: Visitor): ImportClause => { - const _name = visitNode(node.name, visitor, isIdentifier); - const _namedBindings = visitNode(node.namedBindings, visitor, isNamedImportBindings); - return updateImportClause(node, _name, _namedBindings); - }, - [SyntaxKind.ImportSpecifier]: (node: ImportSpecifier, visitor: Visitor): ImportSpecifier => { - const _propertyName = visitNode(node.propertyName, visitor, isModuleExportName); - const _name = visitNode(node.name, visitor, isIdentifier); - return updateImportSpecifier(node, _propertyName, _name); - }, - [SyntaxKind.JSDocLink]: (node: JSDocLink, visitor: Visitor): JSDocLink => { - const _name = visitNode(node.name, visitor, isEntityName); - return updateJSDocLink(node, _name); - }, - [SyntaxKind.JSDocLinkPlain]: (node: JSDocLinkPlain, visitor: Visitor): JSDocLinkPlain => { - const _name = visitNode(node.name, visitor, isEntityName); - return updateJSDocLinkPlain(node, _name); - }, - [SyntaxKind.JSDocLinkCode]: (node: JSDocLinkCode, visitor: Visitor): JSDocLinkCode => { - const _name = visitNode(node.name, visitor, isEntityName); - return updateJSDocLinkCode(node, _name); - }, - [SyntaxKind.TypeParameter]: (node: TypeParameterDeclaration, visitor: Visitor): TypeParameterDeclaration => { - const _modifiers = visitNodes(node.modifiers, visitor); - const _name = visitNode(node.name, visitor, isIdentifier); - const _constraint = visitNode(node.constraint, visitor, isTypeNode); - const _expression = visitNode(node.expression, visitor, isExpression); - const _defaultType = visitNode(node.defaultType, visitor, isTypeNode); - return updateTypeParameterDeclaration(node, _modifiers, _name, _constraint, _expression, _defaultType); - }, - [SyntaxKind.SyntheticReferenceExpression]: (node: SyntheticReferenceExpression, visitor: Visitor): SyntheticReferenceExpression => { - const _expression = visitNode(node.expression, visitor, isExpression); - const _thisArg = visitNode(node.thisArg, visitor, isExpression); - return updateSyntheticReferenceExpression(node, _expression, _thisArg); - }, - [SyntaxKind.JSDocTypeLiteral]: (node: JSDocTypeLiteral, visitor: Visitor): JSDocTypeLiteral => { - const _jsdocPropertyTags = visitNodesArray(node.jsdocPropertyTags, visitor); - return updateJSDocTypeLiteral(node, _jsdocPropertyTags); - }, - [SyntaxKind.ForInStatement]: (node: ForInStatement, visitor: Visitor): ForInStatement => { - const _awaitModifier = visitNode(node.awaitModifier, visitor, isAwaitKeyword); - const _initializer = visitNode(node.initializer, visitor, isForInitializer); - const _expression = visitNode(node.expression, visitor, isExpression); - const _statement = visitNode(node.statement, visitor, isStatement); - return updateForInStatement(node, _awaitModifier, _initializer, _expression, _statement); - }, - [SyntaxKind.ForOfStatement]: (node: ForOfStatement, visitor: Visitor): ForOfStatement => { - const _awaitModifier = visitNode(node.awaitModifier, visitor, isAwaitKeyword); - const _initializer = visitNode(node.initializer, visitor, isForInitializer); - const _expression = visitNode(node.expression, visitor, isExpression); - const _statement = visitNode(node.statement, visitor, isStatement); - return updateForOfStatement(node, _awaitModifier, _initializer, _expression, _statement); - }, - [SyntaxKind.CaseClause]: (node: CaseClause, visitor: Visitor): CaseClause => { - const _expression = visitNode(node.expression, visitor, isExpression); - const _statements = visitNodes(node.statements, visitor); - return updateCaseClause(node, _expression, _statements); - }, - [SyntaxKind.DefaultClause]: (node: DefaultClause, visitor: Visitor): DefaultClause => { - const _expression = visitNode(node.expression, visitor, isExpression); - const _statements = visitNodes(node.statements, visitor); - return updateDefaultClause(node, _expression, _statements); - }, - [SyntaxKind.ObjectBindingPattern]: (node: ObjectBindingPattern, visitor: Visitor): ObjectBindingPattern => { - const _elements = visitNodes(node.elements, visitor); - return updateObjectBindingPattern(node, _elements); - }, - [SyntaxKind.ArrayBindingPattern]: (node: ArrayBindingPattern, visitor: Visitor): ArrayBindingPattern => { - const _elements = visitNodes(node.elements, visitor); - return updateArrayBindingPattern(node, _elements); - }, - [SyntaxKind.JSDocParameterTag]: visitEachChildOfJSDocParameterTag, - [SyntaxKind.JSDocPropertyTag]: visitEachChildOfJSDocPropertyTag, -}; +// Code generated by tools/scripts/tsc/generate-ts-ast.ts. DO NOT EDIT. + +import { SyntaxKind } from "#enums/syntaxKind"; +import type { + ArrayBindingPattern, + ArrayLiteralExpression, + ArrayTypeNode, + ArrowFunction, + AsExpression, + AwaitExpression, + BinaryExpression, + BindingElement, + Block, + BreakStatement, + CallExpression, + CallSignatureDeclaration, + CaseBlock, + CaseClause, + CatchClause, + ClassDeclaration, + ClassExpression, + ClassStaticBlockDeclaration, + ComputedPropertyName, + ConditionalExpression, + ConditionalTypeNode, + ConstructorDeclaration, + ConstructorTypeNode, + ConstructSignatureDeclaration, + ContinueStatement, + Decorator, + DefaultClause, + DeleteExpression, + DoStatement, + ElementAccessExpression, + EnumDeclaration, + EnumMember, + ExportAssignment, + ExportDeclaration, + ExportSpecifier, + ExpressionStatement, + ExpressionWithTypeArguments, + ExternalModuleReference, + ForInStatement, + ForOfStatement, + ForStatement, + FunctionDeclaration, + FunctionExpression, + FunctionTypeNode, + GetAccessorDeclaration, + HeritageClause, + IfStatement, + ImportAttribute, + ImportAttributes, + ImportClause, + ImportDeclaration, + ImportEqualsDeclaration, + ImportSpecifier, + ImportTypeNode, + IndexedAccessTypeNode, + IndexSignatureDeclaration, + InferTypeNode, + InterfaceDeclaration, + IntersectionTypeNode, + JSDoc, + JSDocAugmentsTag, + JSDocCallbackTag, + JSDocDeprecatedTag, + JSDocImplementsTag, + JSDocImportTag, + JSDocLink, + JSDocLinkCode, + JSDocLinkPlain, + JSDocNameReference, + JSDocNonNullableType, + JSDocNullableType, + JSDocOptionalType, + JSDocOverloadTag, + JSDocOverrideTag, + JSDocParameterTag, + JSDocPrivateTag, + JSDocPropertyTag, + JSDocProtectedTag, + JSDocPublicTag, + JSDocReadonlyTag, + JSDocReturnTag, + JSDocSatisfiesTag, + JSDocSeeTag, + JSDocSignature, + JSDocTemplateTag, + JSDocThisTag, + JSDocThrowsTag, + JSDocTypedefTag, + JSDocTypeExpression, + JSDocTypeLiteral, + JSDocTypeTag, + JSDocUnknownTag, + JSDocVariadicType, + JsxAttribute, + JsxAttributes, + JsxClosingElement, + JsxElement, + JsxExpression, + JsxFragment, + JsxNamespacedName, + JsxOpeningElement, + JsxSelfClosingElement, + JsxSpreadAttribute, + LabeledStatement, + LiteralTypeNode, + MappedTypeNode, + MetaProperty, + MethodDeclaration, + MethodSignatureDeclaration, + MissingDeclaration, + ModuleBlock, + ModuleDeclaration, + NamedExports, + NamedImports, + NamedTupleMember, + NamespaceExport, + NamespaceExportDeclaration, + NamespaceImport, + NewExpression, + Node, + NodeArray, + NonNullExpression, + ObjectBindingPattern, + ObjectLiteralExpression, + OptionalTypeNode, + ParameterDeclaration, + ParenthesizedExpression, + ParenthesizedTypeNode, + PartiallyEmittedExpression, + PostfixUnaryExpression, + PrefixUnaryExpression, + PropertyAccessExpression, + PropertyAssignment, + PropertyDeclaration, + PropertySignatureDeclaration, + QualifiedName, + RestTypeNode, + ReturnStatement, + SatisfiesExpression, + SetAccessorDeclaration, + ShorthandPropertyAssignment, + SourceFile, + SpreadAssignment, + SpreadElement, + SwitchStatement, + SyntaxList, + SyntheticExpression, + SyntheticReferenceExpression, + TaggedTemplateExpression, + TemplateExpression, + TemplateLiteralTypeNode, + TemplateLiteralTypeSpan, + TemplateSpan, + ThrowStatement, + TryStatement, + TupleTypeNode, + TypeAliasDeclaration, + TypeAssertion, + TypeLiteralNode, + TypeOfExpression, + TypeOperatorNode, + TypeParameterDeclaration, + TypePredicateNode, + TypeQueryNode, + TypeReferenceNode, + UnionTypeNode, + VariableDeclaration, + VariableDeclarationList, + VariableStatement, + VoidExpression, + WhileStatement, + WithStatement, + YieldExpression, +} from "./ast.ts"; +import { + createNodeArray, + updateArrayBindingPattern, + updateArrayLiteralExpression, + updateArrayTypeNode, + updateArrowFunction, + updateAsExpression, + updateAwaitExpression, + updateBinaryExpression, + updateBindingElement, + updateBlock, + updateBreakStatement, + updateCallExpression, + updateCallSignatureDeclaration, + updateCaseBlock, + updateCaseClause, + updateCatchClause, + updateClassDeclaration, + updateClassExpression, + updateClassStaticBlockDeclaration, + updateComputedPropertyName, + updateConditionalExpression, + updateConditionalTypeNode, + updateConstructorDeclaration, + updateConstructorTypeNode, + updateConstructSignatureDeclaration, + updateContinueStatement, + updateDecorator, + updateDefaultClause, + updateDeleteExpression, + updateDoStatement, + updateElementAccessExpression, + updateEnumDeclaration, + updateEnumMember, + updateExportAssignment, + updateExportDeclaration, + updateExportSpecifier, + updateExpressionStatement, + updateExpressionWithTypeArguments, + updateExternalModuleReference, + updateForInStatement, + updateForOfStatement, + updateForStatement, + updateFunctionDeclaration, + updateFunctionExpression, + updateFunctionTypeNode, + updateGetAccessorDeclaration, + updateHeritageClause, + updateIfStatement, + updateImportAttribute, + updateImportAttributes, + updateImportClause, + updateImportDeclaration, + updateImportEqualsDeclaration, + updateImportSpecifier, + updateImportTypeNode, + updateIndexedAccessTypeNode, + updateIndexSignatureDeclaration, + updateInferTypeNode, + updateInterfaceDeclaration, + updateIntersectionTypeNode, + updateJSDoc, + updateJSDocAugmentsTag, + updateJSDocCallbackTag, + updateJSDocDeprecatedTag, + updateJSDocImplementsTag, + updateJSDocImportTag, + updateJSDocLink, + updateJSDocLinkCode, + updateJSDocLinkPlain, + updateJSDocNameReference, + updateJSDocNonNullableType, + updateJSDocNullableType, + updateJSDocOptionalType, + updateJSDocOverloadTag, + updateJSDocOverrideTag, + updateJSDocPrivateTag, + updateJSDocProtectedTag, + updateJSDocPublicTag, + updateJSDocReadonlyTag, + updateJSDocReturnTag, + updateJSDocSatisfiesTag, + updateJSDocSeeTag, + updateJSDocSignature, + updateJSDocTemplateTag, + updateJSDocThisTag, + updateJSDocThrowsTag, + updateJSDocTypedefTag, + updateJSDocTypeExpression, + updateJSDocTypeLiteral, + updateJSDocTypeTag, + updateJSDocUnknownTag, + updateJSDocVariadicType, + updateJsxAttribute, + updateJsxAttributes, + updateJsxClosingElement, + updateJsxElement, + updateJsxExpression, + updateJsxFragment, + updateJsxNamespacedName, + updateJsxOpeningElement, + updateJsxSelfClosingElement, + updateJsxSpreadAttribute, + updateLabeledStatement, + updateLiteralTypeNode, + updateMappedTypeNode, + updateMetaProperty, + updateMethodDeclaration, + updateMethodSignatureDeclaration, + updateMissingDeclaration, + updateModuleBlock, + updateModuleDeclaration, + updateNamedExports, + updateNamedImports, + updateNamedTupleMember, + updateNamespaceExport, + updateNamespaceExportDeclaration, + updateNamespaceImport, + updateNewExpression, + updateNonNullExpression, + updateObjectBindingPattern, + updateObjectLiteralExpression, + updateOptionalTypeNode, + updateParameterDeclaration, + updateParenthesizedExpression, + updateParenthesizedTypeNode, + updatePartiallyEmittedExpression, + updatePostfixUnaryExpression, + updatePrefixUnaryExpression, + updatePropertyAccessExpression, + updatePropertyAssignment, + updatePropertyDeclaration, + updatePropertySignatureDeclaration, + updateQualifiedName, + updateRestTypeNode, + updateReturnStatement, + updateSatisfiesExpression, + updateSetAccessorDeclaration, + updateShorthandPropertyAssignment, + updateSourceFile, + updateSpreadAssignment, + updateSpreadElement, + updateSwitchStatement, + updateSyntaxList, + updateSyntheticExpression, + updateSyntheticReferenceExpression, + updateTaggedTemplateExpression, + updateTemplateExpression, + updateTemplateLiteralTypeNode, + updateTemplateLiteralTypeSpan, + updateTemplateSpan, + updateThrowStatement, + updateTryStatement, + updateTupleTypeNode, + updateTypeAliasDeclaration, + updateTypeAssertion, + updateTypeLiteralNode, + updateTypeOfExpression, + updateTypeOperatorNode, + updateTypeParameterDeclaration, + updateTypePredicateNode, + updateTypeQueryNode, + updateTypeReferenceNode, + updateUnionTypeNode, + updateVariableDeclaration, + updateVariableDeclarationList, + updateVariableStatement, + updateVoidExpression, + updateWhileStatement, + updateWithStatement, + updateYieldExpression, +} from "./factory.generated.ts"; +import { + isAssertsKeyword, + isAsteriskToken, + isAwaitKeyword, + isBinaryOperatorToken, + isBindingName, + isBlock, + isCaseBlock, + isCatchClause, + isColonToken, + isConciseBody, + isDotDotDotToken, + isEndOfFile, + isEntityName, + isEqualsGreaterThanToken, + isEqualsToken, + isExclamationToken, + isExpression, + isExpressionWithTypeArguments, + isForInitializer, + isFunctionBody, + isIdentifier, + isImportAttributeName, + isImportAttributes, + isImportClause, + isJSDocFullName, + isJsxAttributeName, + isJsxAttributes, + isJsxAttributeValue, + isJsxClosingElement, + isJsxClosingFragment, + isJsxOpeningElement, + isJsxOpeningFragment, + isJsxTagNameExpression, + isLeftHandSideExpression, + isMemberName, + isModuleBody, + isModuleExportName, + isModuleName, + isModuleReference, + isNamedExportBindings, + isNamedImportBindings, + isPropertyName, + isQuestionDotToken, + isQuestionOrExclamationToken, + isQuestionOrPlusOrMinusToken, + isQuestionToken, + isReadonlyKeywordOrPlusOrMinusToken, + isStatement, + isTemplateHead, + isTemplateLiteral, + isTemplateMiddleOrTail, + isTypeNode, + isTypeParameterDeclaration, + isTypePredicateParameterName, + isVariableDeclaration, + isVariableDeclarationList, +} from "./is.ts"; +import { + visitEachChildOfJSDocParameterTag, + visitEachChildOfJSDocPropertyTag, +} from "./visitor.ts"; + +/** + * A callback that receives a node and returns a visited node (or undefined to remove it). + */ +export type Visitor = (node: Node) => Node | undefined; + +/** + * Visits a Node using the supplied visitor, possibly returning a new Node in its place. + * + * - If the input node is undefined, then the output is undefined. + * - If the visitor returns undefined, then the output is undefined. + * - If the output node is not undefined, then it will satisfy the test function. + * - In order to obtain a return type that is more specific than `Node`, a test + * function must be provided, and that function must be a type predicate. + * + * @param node The Node to visit. + * @param visitor The callback used to visit the Node. + * @param test A callback to execute to verify the Node is valid. + */ +export function visitNode( + node: TIn, + visitor: Visitor, + test: (node: Node) => node is TOut, +): TOut | (TIn & undefined); +/** + * Visits a Node using the supplied visitor, possibly returning a new Node in its place. + * + * - If the input node is undefined, then the output is undefined. + * - If the visitor returns undefined, then the output is undefined. + * + * @param node The Node to visit. + * @param visitor The callback used to visit the Node. + * @param test An optional callback to execute to verify the Node is valid. + */ +export function visitNode( + node: TIn, + visitor: Visitor, + test?: (node: Node) => boolean, +): Node | (TIn & undefined); +export function visitNode(node: Node | undefined, visitor: Visitor, test?: (node: Node) => boolean): Node | undefined { + if (node === undefined) return undefined; + const visited = visitor(node); + if (visited !== undefined && test !== undefined && !test(visited)) { + throw new Error("Visited node failed test assertion."); + } + return visited; +} + +/** + * Visits a NodeArray using the supplied visitor, possibly returning a new NodeArray in its place. + * + * - If the input node array is undefined, the output is undefined. + * - If the visitor returns undefined for a node, that node is dropped from the result. + */ +export function visitNodes(nodes: NodeArray, visitor: Visitor): NodeArray; +export function visitNodes(nodes: NodeArray | undefined, visitor: Visitor): NodeArray | undefined; +export function visitNodes(nodes: NodeArray | undefined, visitor: Visitor): NodeArray | undefined { + if (nodes === undefined) return undefined; + const updated = visitNodesArray(nodes, visitor); + if (updated === nodes) { + return nodes; + } + return createNodeArray(updated, nodes.pos, nodes.end); +} + +export function visitNodesArray(nodes: readonly T[], visitor: Visitor): readonly T[]; +export function visitNodesArray(nodes: readonly T[] | undefined, visitor: Visitor): readonly T[] | undefined; +export function visitNodesArray(nodes: readonly Node[] | undefined, visitor: Visitor): readonly Node[] | undefined { + if (nodes === undefined) return undefined; + let updated: Node[] | undefined; + for (let i = 0; i < nodes.length; i++) { + const node = nodes[i]; + const visited = visitor(node); + if (updated) { + if (visited) updated.push(visited); + } + else if (visited !== node) { + updated = []; + for (let j = 0; j < i; j++) updated.push(nodes[j]); + if (visited) updated.push(visited); + } + } + return updated ?? nodes; +} + +/** + * Visits each child of a Node using the supplied visitor, possibly returning a new Node of the same kind in its place. + * + * @param node The Node whose children will be visited. + * @param visitor The callback used to visit each child. + * @returns The original node if no children changed, or a new node with visited children. + */ +export function visitEachChild(node: T, visitor: Visitor): T; +export function visitEachChild(node: T | undefined, visitor: Visitor): T | undefined; +export function visitEachChild(node: Node | undefined, visitor: Visitor): Node | undefined { + if (node === undefined) return undefined; + const fn = visitEachChildTable[node.kind]; + return fn ? fn(node, visitor) : node; +} + +type VisitEachChildFunction = (node: any, visitor: Visitor) => Node; + +const visitEachChildTable: Record = { + [SyntaxKind.QualifiedName]: (node: QualifiedName, visitor: Visitor): QualifiedName => { + const _left = visitNode(node.left, visitor, isEntityName); + const _right = visitNode(node.right, visitor, isMemberName); + return updateQualifiedName(node, _left, _right); + }, + [SyntaxKind.ComputedPropertyName]: (node: ComputedPropertyName, visitor: Visitor): ComputedPropertyName => { + const _expression = visitNode(node.expression, visitor, isExpression); + return updateComputedPropertyName(node, _expression); + }, + [SyntaxKind.Decorator]: (node: Decorator, visitor: Visitor): Decorator => { + const _expression = visitNode(node.expression, visitor, isLeftHandSideExpression); + return updateDecorator(node, _expression); + }, + [SyntaxKind.IfStatement]: (node: IfStatement, visitor: Visitor): IfStatement => { + const _expression = visitNode(node.expression, visitor, isExpression); + const _thenStatement = visitNode(node.thenStatement, visitor, isStatement); + const _elseStatement = visitNode(node.elseStatement, visitor, isStatement); + return updateIfStatement(node, _expression, _thenStatement, _elseStatement); + }, + [SyntaxKind.DoStatement]: (node: DoStatement, visitor: Visitor): DoStatement => { + const _statement = visitNode(node.statement, visitor, isStatement); + const _expression = visitNode(node.expression, visitor, isExpression); + return updateDoStatement(node, _statement, _expression); + }, + [SyntaxKind.WhileStatement]: (node: WhileStatement, visitor: Visitor): WhileStatement => { + const _expression = visitNode(node.expression, visitor, isExpression); + const _statement = visitNode(node.statement, visitor, isStatement); + return updateWhileStatement(node, _expression, _statement); + }, + [SyntaxKind.ForStatement]: (node: ForStatement, visitor: Visitor): ForStatement => { + const _initializer = visitNode(node.initializer, visitor, isForInitializer); + const _condition = visitNode(node.condition, visitor, isExpression); + const _incrementor = visitNode(node.incrementor, visitor, isExpression); + const _statement = visitNode(node.statement, visitor, isStatement); + return updateForStatement(node, _initializer, _condition, _incrementor, _statement); + }, + [SyntaxKind.BreakStatement]: (node: BreakStatement, visitor: Visitor): BreakStatement => { + const _label = visitNode(node.label, visitor, isIdentifier); + return updateBreakStatement(node, _label); + }, + [SyntaxKind.ContinueStatement]: (node: ContinueStatement, visitor: Visitor): ContinueStatement => { + const _label = visitNode(node.label, visitor, isIdentifier); + return updateContinueStatement(node, _label); + }, + [SyntaxKind.ReturnStatement]: (node: ReturnStatement, visitor: Visitor): ReturnStatement => { + const _expression = visitNode(node.expression, visitor, isExpression); + return updateReturnStatement(node, _expression); + }, + [SyntaxKind.WithStatement]: (node: WithStatement, visitor: Visitor): WithStatement => { + const _expression = visitNode(node.expression, visitor, isExpression); + const _statement = visitNode(node.statement, visitor, isStatement); + return updateWithStatement(node, _expression, _statement); + }, + [SyntaxKind.SwitchStatement]: (node: SwitchStatement, visitor: Visitor): SwitchStatement => { + const _expression = visitNode(node.expression, visitor, isExpression); + const _caseBlock = visitNode(node.caseBlock, visitor, isCaseBlock); + return updateSwitchStatement(node, _expression, _caseBlock); + }, + [SyntaxKind.CaseBlock]: (node: CaseBlock, visitor: Visitor): CaseBlock => { + const _clauses = visitNodes(node.clauses, visitor); + return updateCaseBlock(node, _clauses); + }, + [SyntaxKind.ThrowStatement]: (node: ThrowStatement, visitor: Visitor): ThrowStatement => { + const _expression = visitNode(node.expression, visitor, isExpression); + return updateThrowStatement(node, _expression); + }, + [SyntaxKind.TryStatement]: (node: TryStatement, visitor: Visitor): TryStatement => { + const _tryBlock = visitNode(node.tryBlock, visitor, isBlock); + const _catchClause = visitNode(node.catchClause, visitor, isCatchClause); + const _finallyBlock = visitNode(node.finallyBlock, visitor, isBlock); + return updateTryStatement(node, _tryBlock, _catchClause, _finallyBlock); + }, + [SyntaxKind.CatchClause]: (node: CatchClause, visitor: Visitor): CatchClause => { + const _variableDeclaration = visitNode(node.variableDeclaration, visitor, isVariableDeclaration); + const _block = visitNode(node.block, visitor, isBlock); + return updateCatchClause(node, _variableDeclaration, _block); + }, + [SyntaxKind.LabeledStatement]: (node: LabeledStatement, visitor: Visitor): LabeledStatement => { + const _label = visitNode(node.label, visitor, isIdentifier); + const _statement = visitNode(node.statement, visitor, isStatement); + return updateLabeledStatement(node, _label, _statement); + }, + [SyntaxKind.ExpressionStatement]: (node: ExpressionStatement, visitor: Visitor): ExpressionStatement => { + const _expression = visitNode(node.expression, visitor, isExpression); + return updateExpressionStatement(node, _expression); + }, + [SyntaxKind.Block]: (node: Block, visitor: Visitor): Block => { + const _statements = visitNodes(node.statements, visitor); + return updateBlock(node, _statements); + }, + [SyntaxKind.VariableStatement]: (node: VariableStatement, visitor: Visitor): VariableStatement => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _declarationList = visitNode(node.declarationList, visitor, isVariableDeclarationList); + return updateVariableStatement(node, _modifiers, _declarationList); + }, + [SyntaxKind.VariableDeclaration]: (node: VariableDeclaration, visitor: Visitor): VariableDeclaration => { + const _name = visitNode(node.name, visitor, isBindingName); + const _exclamationToken = visitNode(node.exclamationToken, visitor, isExclamationToken); + const _type = visitNode(node.type, visitor, isTypeNode); + const _initializer = visitNode(node.initializer, visitor, isExpression); + return updateVariableDeclaration(node, _name, _exclamationToken, _type, _initializer); + }, + [SyntaxKind.VariableDeclarationList]: (node: VariableDeclarationList, visitor: Visitor): VariableDeclarationList => { + const _declarations = visitNodes(node.declarations, visitor); + return updateVariableDeclarationList(node, _declarations); + }, + [SyntaxKind.Parameter]: (node: ParameterDeclaration, visitor: Visitor): ParameterDeclaration => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _dotDotDotToken = visitNode(node.dotDotDotToken, visitor, isDotDotDotToken); + const _name = visitNode(node.name, visitor, isBindingName); + const _questionToken = visitNode(node.questionToken, visitor, isQuestionToken); + const _type = visitNode(node.type, visitor, isTypeNode); + const _initializer = visitNode(node.initializer, visitor, isExpression); + return updateParameterDeclaration(node, _modifiers, _dotDotDotToken, _name, _questionToken, _type, _initializer); + }, + [SyntaxKind.BindingElement]: (node: BindingElement, visitor: Visitor): BindingElement => { + const _dotDotDotToken = visitNode(node.dotDotDotToken, visitor, isDotDotDotToken); + const _propertyName = visitNode(node.propertyName, visitor, isPropertyName); + const _name = visitNode(node.name, visitor, isBindingName); + const _initializer = visitNode(node.initializer, visitor, isExpression); + return updateBindingElement(node, _dotDotDotToken, _propertyName, _name, _initializer); + }, + [SyntaxKind.MissingDeclaration]: (node: MissingDeclaration, visitor: Visitor): MissingDeclaration => { + const _modifiers = visitNodes(node.modifiers, visitor); + return updateMissingDeclaration(node, _modifiers); + }, + [SyntaxKind.FunctionDeclaration]: (node: FunctionDeclaration, visitor: Visitor): FunctionDeclaration => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _asteriskToken = visitNode(node.asteriskToken, visitor, isAsteriskToken); + const _name = visitNode(node.name, visitor, isIdentifier); + const _typeParameters = visitNodes(node.typeParameters, visitor); + const _parameters = visitNodes(node.parameters, visitor); + const _type = visitNode(node.type, visitor, isTypeNode); + const _body = visitNode(node.body, visitor, isFunctionBody); + return updateFunctionDeclaration(node, _modifiers, _asteriskToken, _name, _typeParameters, _parameters, _type, _body); + }, + [SyntaxKind.ClassDeclaration]: (node: ClassDeclaration, visitor: Visitor): ClassDeclaration => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _name = visitNode(node.name, visitor, isIdentifier); + const _typeParameters = visitNodes(node.typeParameters, visitor); + const _heritageClauses = visitNodes(node.heritageClauses, visitor); + const _members = visitNodes(node.members, visitor); + return updateClassDeclaration(node, _modifiers, _name, _typeParameters, _heritageClauses, _members); + }, + [SyntaxKind.ClassExpression]: (node: ClassExpression, visitor: Visitor): ClassExpression => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _name = visitNode(node.name, visitor, isIdentifier); + const _typeParameters = visitNodes(node.typeParameters, visitor); + const _heritageClauses = visitNodes(node.heritageClauses, visitor); + const _members = visitNodes(node.members, visitor); + return updateClassExpression(node, _modifiers, _name, _typeParameters, _heritageClauses, _members); + }, + [SyntaxKind.HeritageClause]: (node: HeritageClause, visitor: Visitor): HeritageClause => { + const _types = visitNodes(node.types, visitor); + return updateHeritageClause(node, _types); + }, + [SyntaxKind.InterfaceDeclaration]: (node: InterfaceDeclaration, visitor: Visitor): InterfaceDeclaration => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _name = visitNode(node.name, visitor, isIdentifier); + const _typeParameters = visitNodes(node.typeParameters, visitor); + const _heritageClauses = visitNodes(node.heritageClauses, visitor); + const _members = visitNodes(node.members, visitor); + return updateInterfaceDeclaration(node, _modifiers, _name, _typeParameters, _heritageClauses, _members); + }, + [SyntaxKind.TypeAliasDeclaration]: (node: TypeAliasDeclaration, visitor: Visitor): TypeAliasDeclaration => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _name = visitNode(node.name, visitor, isIdentifier); + const _typeParameters = visitNodes(node.typeParameters, visitor); + const _type = visitNode(node.type, visitor, isTypeNode); + return updateTypeAliasDeclaration(node, _modifiers, _name, _typeParameters, _type); + }, + [SyntaxKind.EnumMember]: (node: EnumMember, visitor: Visitor): EnumMember => { + const _name = visitNode(node.name, visitor, isPropertyName); + const _initializer = visitNode(node.initializer, visitor, isExpression); + return updateEnumMember(node, _name, _initializer); + }, + [SyntaxKind.EnumDeclaration]: (node: EnumDeclaration, visitor: Visitor): EnumDeclaration => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _name = visitNode(node.name, visitor, isIdentifier); + const _members = visitNodes(node.members, visitor); + return updateEnumDeclaration(node, _modifiers, _name, _members); + }, + [SyntaxKind.ModuleBlock]: (node: ModuleBlock, visitor: Visitor): ModuleBlock => { + const _statements = visitNodes(node.statements, visitor); + return updateModuleBlock(node, _statements); + }, + [SyntaxKind.ImportDeclaration]: (node: ImportDeclaration, visitor: Visitor): ImportDeclaration => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _importClause = visitNode(node.importClause, visitor, isImportClause); + const _moduleSpecifier = visitNode(node.moduleSpecifier, visitor, isExpression); + const _attributes = visitNode(node.attributes, visitor, isImportAttributes); + return updateImportDeclaration(node, _modifiers, _importClause, _moduleSpecifier, _attributes); + }, + [SyntaxKind.ExternalModuleReference]: (node: ExternalModuleReference, visitor: Visitor): ExternalModuleReference => { + const _expression = visitNode(node.expression, visitor, isExpression); + return updateExternalModuleReference(node, _expression); + }, + [SyntaxKind.NamespaceImport]: (node: NamespaceImport, visitor: Visitor): NamespaceImport => { + const _name = visitNode(node.name, visitor, isIdentifier); + return updateNamespaceImport(node, _name); + }, + [SyntaxKind.NamedImports]: (node: NamedImports, visitor: Visitor): NamedImports => { + const _elements = visitNodes(node.elements, visitor); + return updateNamedImports(node, _elements); + }, + [SyntaxKind.ExportAssignment]: (node: ExportAssignment, visitor: Visitor): ExportAssignment => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _type = visitNode(node.type, visitor, isTypeNode); + const _expression = visitNode(node.expression, visitor, isExpression); + return updateExportAssignment(node, _modifiers, _type, _expression); + }, + [SyntaxKind.NamespaceExportDeclaration]: (node: NamespaceExportDeclaration, visitor: Visitor): NamespaceExportDeclaration => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _name = visitNode(node.name, visitor, isIdentifier); + return updateNamespaceExportDeclaration(node, _modifiers, _name); + }, + [SyntaxKind.NamespaceExport]: (node: NamespaceExport, visitor: Visitor): NamespaceExport => { + const _name = visitNode(node.name, visitor, isModuleExportName); + return updateNamespaceExport(node, _name); + }, + [SyntaxKind.NamedExports]: (node: NamedExports, visitor: Visitor): NamedExports => { + const _elements = visitNodes(node.elements, visitor); + return updateNamedExports(node, _elements); + }, + [SyntaxKind.ExportSpecifier]: (node: ExportSpecifier, visitor: Visitor): ExportSpecifier => { + const _propertyName = visitNode(node.propertyName, visitor, isModuleExportName); + const _name = visitNode(node.name, visitor, isModuleExportName); + return updateExportSpecifier(node, _propertyName, _name); + }, + [SyntaxKind.CallSignature]: (node: CallSignatureDeclaration, visitor: Visitor): CallSignatureDeclaration => { + const _typeParameters = visitNodes(node.typeParameters, visitor); + const _parameters = visitNodes(node.parameters, visitor); + const _type = visitNode(node.type, visitor, isTypeNode); + return updateCallSignatureDeclaration(node, _typeParameters, _parameters, _type); + }, + [SyntaxKind.ConstructSignature]: (node: ConstructSignatureDeclaration, visitor: Visitor): ConstructSignatureDeclaration => { + const _typeParameters = visitNodes(node.typeParameters, visitor); + const _parameters = visitNodes(node.parameters, visitor); + const _type = visitNode(node.type, visitor, isTypeNode); + return updateConstructSignatureDeclaration(node, _typeParameters, _parameters, _type); + }, + [SyntaxKind.Constructor]: (node: ConstructorDeclaration, visitor: Visitor): ConstructorDeclaration => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _typeParameters = visitNodes(node.typeParameters, visitor); + const _parameters = visitNodes(node.parameters, visitor); + const _type = visitNode(node.type, visitor, isTypeNode); + const _body = visitNode(node.body, visitor, isFunctionBody); + return updateConstructorDeclaration(node, _modifiers, _typeParameters, _parameters, _type, _body); + }, + [SyntaxKind.GetAccessor]: (node: GetAccessorDeclaration, visitor: Visitor): GetAccessorDeclaration => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _name = visitNode(node.name, visitor, isPropertyName); + const _typeParameters = visitNodes(node.typeParameters, visitor); + const _parameters = visitNodes(node.parameters, visitor); + const _type = visitNode(node.type, visitor, isTypeNode); + const _body = visitNode(node.body, visitor, isFunctionBody); + return updateGetAccessorDeclaration(node, _modifiers, _name, _typeParameters, _parameters, _type, _body); + }, + [SyntaxKind.SetAccessor]: (node: SetAccessorDeclaration, visitor: Visitor): SetAccessorDeclaration => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _name = visitNode(node.name, visitor, isPropertyName); + const _typeParameters = visitNodes(node.typeParameters, visitor); + const _parameters = visitNodes(node.parameters, visitor); + const _type = visitNode(node.type, visitor, isTypeNode); + const _body = visitNode(node.body, visitor, isFunctionBody); + return updateSetAccessorDeclaration(node, _modifiers, _name, _typeParameters, _parameters, _type, _body); + }, + [SyntaxKind.IndexSignature]: (node: IndexSignatureDeclaration, visitor: Visitor): IndexSignatureDeclaration => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _parameters = visitNodes(node.parameters, visitor); + const _type = visitNode(node.type, visitor, isTypeNode); + return updateIndexSignatureDeclaration(node, _modifiers, _parameters, _type); + }, + [SyntaxKind.MethodSignature]: (node: MethodSignatureDeclaration, visitor: Visitor): MethodSignatureDeclaration => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _name = visitNode(node.name, visitor, isPropertyName); + const _postfixToken = visitNode(node.postfixToken, visitor, isQuestionOrExclamationToken); + const _typeParameters = visitNodes(node.typeParameters, visitor); + const _parameters = visitNodes(node.parameters, visitor); + const _type = visitNode(node.type, visitor, isTypeNode); + return updateMethodSignatureDeclaration(node, _modifiers, _name, _postfixToken, _typeParameters, _parameters, _type); + }, + [SyntaxKind.MethodDeclaration]: (node: MethodDeclaration, visitor: Visitor): MethodDeclaration => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _asteriskToken = visitNode(node.asteriskToken, visitor, isAsteriskToken); + const _name = visitNode(node.name, visitor, isPropertyName); + const _postfixToken = visitNode(node.postfixToken, visitor, isQuestionOrExclamationToken); + const _typeParameters = visitNodes(node.typeParameters, visitor); + const _parameters = visitNodes(node.parameters, visitor); + const _type = visitNode(node.type, visitor, isTypeNode); + const _body = visitNode(node.body, visitor, isFunctionBody); + return updateMethodDeclaration(node, _modifiers, _asteriskToken, _name, _postfixToken, _typeParameters, _parameters, _type, _body); + }, + [SyntaxKind.PropertySignature]: (node: PropertySignatureDeclaration, visitor: Visitor): PropertySignatureDeclaration => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _name = visitNode(node.name, visitor, isPropertyName); + const _postfixToken = visitNode(node.postfixToken, visitor, isQuestionOrExclamationToken); + const _type = visitNode(node.type, visitor, isTypeNode); + const _initializer = visitNode(node.initializer, visitor, isExpression); + return updatePropertySignatureDeclaration(node, _modifiers, _name, _postfixToken, _type, _initializer); + }, + [SyntaxKind.PropertyDeclaration]: (node: PropertyDeclaration, visitor: Visitor): PropertyDeclaration => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _name = visitNode(node.name, visitor, isPropertyName); + const _postfixToken = visitNode(node.postfixToken, visitor, isQuestionOrExclamationToken); + const _type = visitNode(node.type, visitor, isTypeNode); + const _initializer = visitNode(node.initializer, visitor, isExpression); + return updatePropertyDeclaration(node, _modifiers, _name, _postfixToken, _type, _initializer); + }, + [SyntaxKind.ClassStaticBlockDeclaration]: (node: ClassStaticBlockDeclaration, visitor: Visitor): ClassStaticBlockDeclaration => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _body = visitNode(node.body, visitor, isBlock); + return updateClassStaticBlockDeclaration(node, _modifiers, _body); + }, + [SyntaxKind.BinaryExpression]: (node: BinaryExpression, visitor: Visitor): BinaryExpression => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _left = visitNode(node.left, visitor, isExpression); + const _type = visitNode(node.type, visitor, isTypeNode); + const _operatorToken = visitNode(node.operatorToken, visitor, isBinaryOperatorToken); + const _right = visitNode(node.right, visitor, isExpression); + return updateBinaryExpression(node, _modifiers, _left, _type, _operatorToken, _right); + }, + [SyntaxKind.PrefixUnaryExpression]: (node: PrefixUnaryExpression, visitor: Visitor): PrefixUnaryExpression => { + const _operand = visitNode(node.operand, visitor, isExpression); + return updatePrefixUnaryExpression(node, _operand); + }, + [SyntaxKind.PostfixUnaryExpression]: (node: PostfixUnaryExpression, visitor: Visitor): PostfixUnaryExpression => { + const _operand = visitNode(node.operand, visitor, isExpression); + return updatePostfixUnaryExpression(node, _operand); + }, + [SyntaxKind.YieldExpression]: (node: YieldExpression, visitor: Visitor): YieldExpression => { + const _asteriskToken = visitNode(node.asteriskToken, visitor, isAsteriskToken); + const _expression = visitNode(node.expression, visitor, isExpression); + return updateYieldExpression(node, _asteriskToken, _expression); + }, + [SyntaxKind.ArrowFunction]: (node: ArrowFunction, visitor: Visitor): ArrowFunction => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _typeParameters = visitNodes(node.typeParameters, visitor); + const _parameters = visitNodes(node.parameters, visitor); + const _type = visitNode(node.type, visitor, isTypeNode); + const _equalsGreaterThanToken = visitNode(node.equalsGreaterThanToken, visitor, isEqualsGreaterThanToken); + const _body = visitNode(node.body, visitor, isConciseBody); + return updateArrowFunction(node, _modifiers, _typeParameters, _parameters, _type, _equalsGreaterThanToken, _body); + }, + [SyntaxKind.FunctionExpression]: (node: FunctionExpression, visitor: Visitor): FunctionExpression => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _asteriskToken = visitNode(node.asteriskToken, visitor, isAsteriskToken); + const _name = visitNode(node.name, visitor, isIdentifier); + const _typeParameters = visitNodes(node.typeParameters, visitor); + const _parameters = visitNodes(node.parameters, visitor); + const _type = visitNode(node.type, visitor, isTypeNode); + const _body = visitNode(node.body, visitor, isFunctionBody); + return updateFunctionExpression(node, _modifiers, _asteriskToken, _name, _typeParameters, _parameters, _type, _body); + }, + [SyntaxKind.AsExpression]: (node: AsExpression, visitor: Visitor): AsExpression => { + const _expression = visitNode(node.expression, visitor, isExpression); + const _type = visitNode(node.type, visitor, isTypeNode); + return updateAsExpression(node, _expression, _type); + }, + [SyntaxKind.SatisfiesExpression]: (node: SatisfiesExpression, visitor: Visitor): SatisfiesExpression => { + const _expression = visitNode(node.expression, visitor, isExpression); + const _type = visitNode(node.type, visitor, isTypeNode); + return updateSatisfiesExpression(node, _expression, _type); + }, + [SyntaxKind.ConditionalExpression]: (node: ConditionalExpression, visitor: Visitor): ConditionalExpression => { + const _condition = visitNode(node.condition, visitor, isExpression); + const _questionToken = visitNode(node.questionToken, visitor, isQuestionToken); + const _whenTrue = visitNode(node.whenTrue, visitor, isExpression); + const _colonToken = visitNode(node.colonToken, visitor, isColonToken); + const _whenFalse = visitNode(node.whenFalse, visitor, isExpression); + return updateConditionalExpression(node, _condition, _questionToken, _whenTrue, _colonToken, _whenFalse); + }, + [SyntaxKind.PropertyAccessExpression]: (node: PropertyAccessExpression, visitor: Visitor): PropertyAccessExpression => { + const _expression = visitNode(node.expression, visitor, isExpression); + const _questionDotToken = visitNode(node.questionDotToken, visitor, isQuestionDotToken); + const _name = visitNode(node.name, visitor, isMemberName); + return updatePropertyAccessExpression(node, _expression, _questionDotToken, _name); + }, + [SyntaxKind.ElementAccessExpression]: (node: ElementAccessExpression, visitor: Visitor): ElementAccessExpression => { + const _expression = visitNode(node.expression, visitor, isExpression); + const _questionDotToken = visitNode(node.questionDotToken, visitor, isQuestionDotToken); + const _argumentExpression = visitNode(node.argumentExpression, visitor, isExpression); + return updateElementAccessExpression(node, _expression, _questionDotToken, _argumentExpression); + }, + [SyntaxKind.CallExpression]: (node: CallExpression, visitor: Visitor): CallExpression => { + const _expression = visitNode(node.expression, visitor, isExpression); + const _questionDotToken = visitNode(node.questionDotToken, visitor, isQuestionDotToken); + const _typeArguments = visitNodes(node.typeArguments, visitor); + const _arguments = visitNodes(node.arguments, visitor); + return updateCallExpression(node, _expression, _questionDotToken, _typeArguments, _arguments); + }, + [SyntaxKind.NewExpression]: (node: NewExpression, visitor: Visitor): NewExpression => { + const _expression = visitNode(node.expression, visitor, isExpression); + const _typeArguments = visitNodes(node.typeArguments, visitor); + const _arguments = visitNodes(node.arguments, visitor); + return updateNewExpression(node, _expression, _typeArguments, _arguments); + }, + [SyntaxKind.MetaProperty]: (node: MetaProperty, visitor: Visitor): MetaProperty => { + const _name = visitNode(node.name, visitor, isIdentifier); + return updateMetaProperty(node, _name); + }, + [SyntaxKind.NonNullExpression]: (node: NonNullExpression, visitor: Visitor): NonNullExpression => { + const _expression = visitNode(node.expression, visitor, isExpression); + return updateNonNullExpression(node, _expression); + }, + [SyntaxKind.SpreadElement]: (node: SpreadElement, visitor: Visitor): SpreadElement => { + const _expression = visitNode(node.expression, visitor, isExpression); + return updateSpreadElement(node, _expression); + }, + [SyntaxKind.TemplateExpression]: (node: TemplateExpression, visitor: Visitor): TemplateExpression => { + const _head = visitNode(node.head, visitor, isTemplateHead); + const _templateSpans = visitNodes(node.templateSpans, visitor); + return updateTemplateExpression(node, _head, _templateSpans); + }, + [SyntaxKind.TemplateSpan]: (node: TemplateSpan, visitor: Visitor): TemplateSpan => { + const _expression = visitNode(node.expression, visitor, isExpression); + const _literal = visitNode(node.literal, visitor, isTemplateMiddleOrTail); + return updateTemplateSpan(node, _expression, _literal); + }, + [SyntaxKind.TaggedTemplateExpression]: (node: TaggedTemplateExpression, visitor: Visitor): TaggedTemplateExpression => { + const _tag = visitNode(node.tag, visitor, isExpression); + const _questionDotToken = visitNode(node.questionDotToken, visitor, isQuestionDotToken); + const _typeArguments = visitNodes(node.typeArguments, visitor); + const _template = visitNode(node.template, visitor, isTemplateLiteral); + return updateTaggedTemplateExpression(node, _tag, _questionDotToken, _typeArguments, _template); + }, + [SyntaxKind.ParenthesizedExpression]: (node: ParenthesizedExpression, visitor: Visitor): ParenthesizedExpression => { + const _expression = visitNode(node.expression, visitor, isExpression); + return updateParenthesizedExpression(node, _expression); + }, + [SyntaxKind.ArrayLiteralExpression]: (node: ArrayLiteralExpression, visitor: Visitor): ArrayLiteralExpression => { + const _elements = visitNodes(node.elements, visitor); + return updateArrayLiteralExpression(node, _elements); + }, + [SyntaxKind.ObjectLiteralExpression]: (node: ObjectLiteralExpression, visitor: Visitor): ObjectLiteralExpression => { + const _properties = visitNodes(node.properties, visitor); + return updateObjectLiteralExpression(node, _properties); + }, + [SyntaxKind.SpreadAssignment]: (node: SpreadAssignment, visitor: Visitor): SpreadAssignment => { + const _expression = visitNode(node.expression, visitor, isExpression); + return updateSpreadAssignment(node, _expression); + }, + [SyntaxKind.PropertyAssignment]: (node: PropertyAssignment, visitor: Visitor): PropertyAssignment => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _name = visitNode(node.name, visitor, isPropertyName); + const _postfixToken = visitNode(node.postfixToken, visitor, isQuestionOrExclamationToken); + const _type = visitNode(node.type, visitor, isTypeNode); + const _initializer = visitNode(node.initializer, visitor, isExpression); + return updatePropertyAssignment(node, _modifiers, _name, _postfixToken, _type, _initializer); + }, + [SyntaxKind.ShorthandPropertyAssignment]: (node: ShorthandPropertyAssignment, visitor: Visitor): ShorthandPropertyAssignment => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _name = visitNode(node.name, visitor, isPropertyName); + const _postfixToken = visitNode(node.postfixToken, visitor, isQuestionOrExclamationToken); + const _type = visitNode(node.type, visitor, isTypeNode); + const _equalsToken = visitNode(node.equalsToken, visitor, isEqualsToken); + const _objectAssignmentInitializer = visitNode(node.objectAssignmentInitializer, visitor, isExpression); + return updateShorthandPropertyAssignment(node, _modifiers, _name, _postfixToken, _type, _equalsToken, _objectAssignmentInitializer); + }, + [SyntaxKind.DeleteExpression]: (node: DeleteExpression, visitor: Visitor): DeleteExpression => { + const _expression = visitNode(node.expression, visitor, isExpression); + return updateDeleteExpression(node, _expression); + }, + [SyntaxKind.TypeOfExpression]: (node: TypeOfExpression, visitor: Visitor): TypeOfExpression => { + const _expression = visitNode(node.expression, visitor, isExpression); + return updateTypeOfExpression(node, _expression); + }, + [SyntaxKind.VoidExpression]: (node: VoidExpression, visitor: Visitor): VoidExpression => { + const _expression = visitNode(node.expression, visitor, isExpression); + return updateVoidExpression(node, _expression); + }, + [SyntaxKind.AwaitExpression]: (node: AwaitExpression, visitor: Visitor): AwaitExpression => { + const _expression = visitNode(node.expression, visitor, isExpression); + return updateAwaitExpression(node, _expression); + }, + [SyntaxKind.TypeAssertionExpression]: (node: TypeAssertion, visitor: Visitor): TypeAssertion => { + const _type = visitNode(node.type, visitor, isTypeNode); + const _expression = visitNode(node.expression, visitor, isExpression); + return updateTypeAssertion(node, _type, _expression); + }, + [SyntaxKind.UnionType]: (node: UnionTypeNode, visitor: Visitor): UnionTypeNode => { + const _types = visitNodes(node.types, visitor); + return updateUnionTypeNode(node, _types); + }, + [SyntaxKind.IntersectionType]: (node: IntersectionTypeNode, visitor: Visitor): IntersectionTypeNode => { + const _types = visitNodes(node.types, visitor); + return updateIntersectionTypeNode(node, _types); + }, + [SyntaxKind.ConditionalType]: (node: ConditionalTypeNode, visitor: Visitor): ConditionalTypeNode => { + const _checkType = visitNode(node.checkType, visitor, isTypeNode); + const _extendsType = visitNode(node.extendsType, visitor, isTypeNode); + const _trueType = visitNode(node.trueType, visitor, isTypeNode); + const _falseType = visitNode(node.falseType, visitor, isTypeNode); + return updateConditionalTypeNode(node, _checkType, _extendsType, _trueType, _falseType); + }, + [SyntaxKind.TypeOperator]: (node: TypeOperatorNode, visitor: Visitor): TypeOperatorNode => { + const _type = visitNode(node.type, visitor, isTypeNode); + return updateTypeOperatorNode(node, _type); + }, + [SyntaxKind.InferType]: (node: InferTypeNode, visitor: Visitor): InferTypeNode => { + const _typeParameter = visitNode(node.typeParameter, visitor, isTypeParameterDeclaration); + return updateInferTypeNode(node, _typeParameter); + }, + [SyntaxKind.ArrayType]: (node: ArrayTypeNode, visitor: Visitor): ArrayTypeNode => { + const _elementType = visitNode(node.elementType, visitor, isTypeNode); + return updateArrayTypeNode(node, _elementType); + }, + [SyntaxKind.IndexedAccessType]: (node: IndexedAccessTypeNode, visitor: Visitor): IndexedAccessTypeNode => { + const _objectType = visitNode(node.objectType, visitor, isTypeNode); + const _indexType = visitNode(node.indexType, visitor, isTypeNode); + return updateIndexedAccessTypeNode(node, _objectType, _indexType); + }, + [SyntaxKind.TypeReference]: (node: TypeReferenceNode, visitor: Visitor): TypeReferenceNode => { + const _typeName = visitNode(node.typeName, visitor, isEntityName); + const _typeArguments = visitNodes(node.typeArguments, visitor); + return updateTypeReferenceNode(node, _typeName, _typeArguments); + }, + [SyntaxKind.ExpressionWithTypeArguments]: (node: ExpressionWithTypeArguments, visitor: Visitor): ExpressionWithTypeArguments => { + const _expression = visitNode(node.expression, visitor, isExpression); + const _typeArguments = visitNodes(node.typeArguments, visitor); + return updateExpressionWithTypeArguments(node, _expression, _typeArguments); + }, + [SyntaxKind.LiteralType]: (node: LiteralTypeNode, visitor: Visitor): LiteralTypeNode => { + const _literal = visitNode(node.literal, visitor); + return updateLiteralTypeNode(node, _literal); + }, + [SyntaxKind.TypePredicate]: (node: TypePredicateNode, visitor: Visitor): TypePredicateNode => { + const _assertsModifier = visitNode(node.assertsModifier, visitor, isAssertsKeyword); + const _parameterName = visitNode(node.parameterName, visitor, isTypePredicateParameterName); + const _type = visitNode(node.type, visitor, isTypeNode); + return updateTypePredicateNode(node, _assertsModifier, _parameterName, _type); + }, + [SyntaxKind.ImportAttribute]: (node: ImportAttribute, visitor: Visitor): ImportAttribute => { + const _name = visitNode(node.name, visitor, isImportAttributeName); + const _value = visitNode(node.value, visitor, isExpression); + return updateImportAttribute(node, _name, _value); + }, + [SyntaxKind.ImportAttributes]: (node: ImportAttributes, visitor: Visitor): ImportAttributes => { + const _attributes = visitNodes(node.attributes, visitor); + return updateImportAttributes(node, _attributes); + }, + [SyntaxKind.TypeQuery]: (node: TypeQueryNode, visitor: Visitor): TypeQueryNode => { + const _exprName = visitNode(node.exprName, visitor, isEntityName); + const _typeArguments = visitNodes(node.typeArguments, visitor); + return updateTypeQueryNode(node, _exprName, _typeArguments); + }, + [SyntaxKind.MappedType]: (node: MappedTypeNode, visitor: Visitor): MappedTypeNode => { + const _readonlyToken = visitNode(node.readonlyToken, visitor, isReadonlyKeywordOrPlusOrMinusToken); + const _typeParameter = visitNode(node.typeParameter, visitor, isTypeParameterDeclaration); + const _nameType = visitNode(node.nameType, visitor, isTypeNode); + const _questionToken = visitNode(node.questionToken, visitor, isQuestionOrPlusOrMinusToken); + const _type = visitNode(node.type, visitor, isTypeNode); + const _members = visitNodes(node.members, visitor); + return updateMappedTypeNode(node, _readonlyToken, _typeParameter, _nameType, _questionToken, _type, _members); + }, + [SyntaxKind.TypeLiteral]: (node: TypeLiteralNode, visitor: Visitor): TypeLiteralNode => { + const _members = visitNodes(node.members, visitor); + return updateTypeLiteralNode(node, _members); + }, + [SyntaxKind.TupleType]: (node: TupleTypeNode, visitor: Visitor): TupleTypeNode => { + const _elements = visitNodes(node.elements, visitor); + return updateTupleTypeNode(node, _elements); + }, + [SyntaxKind.NamedTupleMember]: (node: NamedTupleMember, visitor: Visitor): NamedTupleMember => { + const _dotDotDotToken = visitNode(node.dotDotDotToken, visitor, isDotDotDotToken); + const _name = visitNode(node.name, visitor, isIdentifier); + const _questionToken = visitNode(node.questionToken, visitor, isQuestionToken); + const _type = visitNode(node.type, visitor, isTypeNode); + return updateNamedTupleMember(node, _dotDotDotToken, _name, _questionToken, _type); + }, + [SyntaxKind.OptionalType]: (node: OptionalTypeNode, visitor: Visitor): OptionalTypeNode => { + const _type = visitNode(node.type, visitor, isTypeNode); + return updateOptionalTypeNode(node, _type); + }, + [SyntaxKind.RestType]: (node: RestTypeNode, visitor: Visitor): RestTypeNode => { + const _type = visitNode(node.type, visitor, isTypeNode); + return updateRestTypeNode(node, _type); + }, + [SyntaxKind.ParenthesizedType]: (node: ParenthesizedTypeNode, visitor: Visitor): ParenthesizedTypeNode => { + const _type = visitNode(node.type, visitor, isTypeNode); + return updateParenthesizedTypeNode(node, _type); + }, + [SyntaxKind.FunctionType]: (node: FunctionTypeNode, visitor: Visitor): FunctionTypeNode => { + const _typeParameters = visitNodes(node.typeParameters, visitor); + const _parameters = visitNodes(node.parameters, visitor); + const _type = visitNode(node.type, visitor, isTypeNode); + return updateFunctionTypeNode(node, _typeParameters, _parameters, _type); + }, + [SyntaxKind.ConstructorType]: (node: ConstructorTypeNode, visitor: Visitor): ConstructorTypeNode => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _typeParameters = visitNodes(node.typeParameters, visitor); + const _parameters = visitNodes(node.parameters, visitor); + const _type = visitNode(node.type, visitor, isTypeNode); + return updateConstructorTypeNode(node, _modifiers, _typeParameters, _parameters, _type); + }, + [SyntaxKind.TemplateLiteralType]: (node: TemplateLiteralTypeNode, visitor: Visitor): TemplateLiteralTypeNode => { + const _head = visitNode(node.head, visitor, isTemplateHead); + const _templateSpans = visitNodes(node.templateSpans, visitor); + return updateTemplateLiteralTypeNode(node, _head, _templateSpans); + }, + [SyntaxKind.TemplateLiteralTypeSpan]: (node: TemplateLiteralTypeSpan, visitor: Visitor): TemplateLiteralTypeSpan => { + const _type = visitNode(node.type, visitor, isTypeNode); + const _literal = visitNode(node.literal, visitor, isTemplateMiddleOrTail); + return updateTemplateLiteralTypeSpan(node, _type, _literal); + }, + [SyntaxKind.SyntheticExpression]: (node: SyntheticExpression, visitor: Visitor): SyntheticExpression => { + const _tupleNameSource = visitNode(node.tupleNameSource, visitor); + return updateSyntheticExpression(node, _tupleNameSource); + }, + [SyntaxKind.PartiallyEmittedExpression]: (node: PartiallyEmittedExpression, visitor: Visitor): PartiallyEmittedExpression => { + const _expression = visitNode(node.expression, visitor, isExpression); + return updatePartiallyEmittedExpression(node, _expression); + }, + [SyntaxKind.JsxElement]: (node: JsxElement, visitor: Visitor): JsxElement => { + const _openingElement = visitNode(node.openingElement, visitor, isJsxOpeningElement); + const _children = visitNodes(node.children, visitor); + const _closingElement = visitNode(node.closingElement, visitor, isJsxClosingElement); + return updateJsxElement(node, _openingElement, _children, _closingElement); + }, + [SyntaxKind.JsxAttributes]: (node: JsxAttributes, visitor: Visitor): JsxAttributes => { + const _properties = visitNodes(node.properties, visitor); + return updateJsxAttributes(node, _properties); + }, + [SyntaxKind.JsxNamespacedName]: (node: JsxNamespacedName, visitor: Visitor): JsxNamespacedName => { + const _namespace = visitNode(node.namespace, visitor, isIdentifier); + const _name = visitNode(node.name, visitor, isIdentifier); + return updateJsxNamespacedName(node, _namespace, _name); + }, + [SyntaxKind.JsxOpeningElement]: (node: JsxOpeningElement, visitor: Visitor): JsxOpeningElement => { + const _tagName = visitNode(node.tagName, visitor, isJsxTagNameExpression); + const _typeArguments = visitNodes(node.typeArguments, visitor); + const _attributes = visitNode(node.attributes, visitor, isJsxAttributes); + return updateJsxOpeningElement(node, _tagName, _typeArguments, _attributes); + }, + [SyntaxKind.JsxSelfClosingElement]: (node: JsxSelfClosingElement, visitor: Visitor): JsxSelfClosingElement => { + const _tagName = visitNode(node.tagName, visitor, isJsxTagNameExpression); + const _typeArguments = visitNodes(node.typeArguments, visitor); + const _attributes = visitNode(node.attributes, visitor, isJsxAttributes); + return updateJsxSelfClosingElement(node, _tagName, _typeArguments, _attributes); + }, + [SyntaxKind.JsxFragment]: (node: JsxFragment, visitor: Visitor): JsxFragment => { + const _openingFragment = visitNode(node.openingFragment, visitor, isJsxOpeningFragment); + const _children = visitNodes(node.children, visitor); + const _closingFragment = visitNode(node.closingFragment, visitor, isJsxClosingFragment); + return updateJsxFragment(node, _openingFragment, _children, _closingFragment); + }, + [SyntaxKind.JsxAttribute]: (node: JsxAttribute, visitor: Visitor): JsxAttribute => { + const _name = visitNode(node.name, visitor, isJsxAttributeName); + const _initializer = visitNode(node.initializer, visitor, isJsxAttributeValue); + return updateJsxAttribute(node, _name, _initializer); + }, + [SyntaxKind.JsxSpreadAttribute]: (node: JsxSpreadAttribute, visitor: Visitor): JsxSpreadAttribute => { + const _expression = visitNode(node.expression, visitor, isExpression); + return updateJsxSpreadAttribute(node, _expression); + }, + [SyntaxKind.JsxClosingElement]: (node: JsxClosingElement, visitor: Visitor): JsxClosingElement => { + const _tagName = visitNode(node.tagName, visitor, isJsxTagNameExpression); + return updateJsxClosingElement(node, _tagName); + }, + [SyntaxKind.JsxExpression]: (node: JsxExpression, visitor: Visitor): JsxExpression => { + const _dotDotDotToken = visitNode(node.dotDotDotToken, visitor, isDotDotDotToken); + const _expression = visitNode(node.expression, visitor, isExpression); + return updateJsxExpression(node, _dotDotDotToken, _expression); + }, + [SyntaxKind.SyntaxList]: (node: SyntaxList, visitor: Visitor): SyntaxList => { + const _children = visitNodesArray(node.children, visitor); + return updateSyntaxList(node, _children); + }, + [SyntaxKind.JSDoc]: (node: JSDoc, visitor: Visitor): JSDoc => { + const _comment = visitNodes(node.comment, visitor); + const _tags = visitNodes(node.tags, visitor); + return updateJSDoc(node, _comment, _tags); + }, + [SyntaxKind.JSDocTypeExpression]: (node: JSDocTypeExpression, visitor: Visitor): JSDocTypeExpression => { + const _type = visitNode(node.type, visitor, isTypeNode); + return updateJSDocTypeExpression(node, _type); + }, + [SyntaxKind.JSDocNonNullableType]: (node: JSDocNonNullableType, visitor: Visitor): JSDocNonNullableType => { + const _type = visitNode(node.type, visitor, isTypeNode); + return updateJSDocNonNullableType(node, _type); + }, + [SyntaxKind.JSDocNullableType]: (node: JSDocNullableType, visitor: Visitor): JSDocNullableType => { + const _type = visitNode(node.type, visitor, isTypeNode); + return updateJSDocNullableType(node, _type); + }, + [SyntaxKind.JSDocVariadicType]: (node: JSDocVariadicType, visitor: Visitor): JSDocVariadicType => { + const _type = visitNode(node.type, visitor, isTypeNode); + return updateJSDocVariadicType(node, _type); + }, + [SyntaxKind.JSDocOptionalType]: (node: JSDocOptionalType, visitor: Visitor): JSDocOptionalType => { + const _type = visitNode(node.type, visitor, isTypeNode); + return updateJSDocOptionalType(node, _type); + }, + [SyntaxKind.JSDocTypeTag]: (node: JSDocTypeTag, visitor: Visitor): JSDocTypeTag => { + const _tagName = visitNode(node.tagName, visitor, isIdentifier); + const _typeExpression = visitNode(node.typeExpression, visitor); + const _comment = visitNodes(node.comment, visitor); + return updateJSDocTypeTag(node, _tagName, _typeExpression, _comment); + }, + [SyntaxKind.JSDocUnknownTag]: (node: JSDocUnknownTag, visitor: Visitor): JSDocUnknownTag => { + const _tagName = visitNode(node.tagName, visitor, isIdentifier); + const _comment = visitNodes(node.comment, visitor); + return updateJSDocUnknownTag(node, _tagName, _comment); + }, + [SyntaxKind.JSDocTemplateTag]: (node: JSDocTemplateTag, visitor: Visitor): JSDocTemplateTag => { + const _tagName = visitNode(node.tagName, visitor, isIdentifier); + const _constraint = visitNode(node.constraint, visitor); + const _typeParameters = visitNodes(node.typeParameters, visitor); + const _comment = visitNodes(node.comment, visitor); + return updateJSDocTemplateTag(node, _tagName, _constraint, _typeParameters, _comment); + }, + [SyntaxKind.JSDocReturnTag]: (node: JSDocReturnTag, visitor: Visitor): JSDocReturnTag => { + const _tagName = visitNode(node.tagName, visitor, isIdentifier); + const _typeExpression = visitNode(node.typeExpression, visitor, isTypeNode); + const _comment = visitNodes(node.comment, visitor); + return updateJSDocReturnTag(node, _tagName, _typeExpression, _comment); + }, + [SyntaxKind.JSDocPublicTag]: (node: JSDocPublicTag, visitor: Visitor): JSDocPublicTag => { + const _tagName = visitNode(node.tagName, visitor, isIdentifier); + const _comment = visitNodes(node.comment, visitor); + return updateJSDocPublicTag(node, _tagName, _comment); + }, + [SyntaxKind.JSDocPrivateTag]: (node: JSDocPrivateTag, visitor: Visitor): JSDocPrivateTag => { + const _tagName = visitNode(node.tagName, visitor, isIdentifier); + const _comment = visitNodes(node.comment, visitor); + return updateJSDocPrivateTag(node, _tagName, _comment); + }, + [SyntaxKind.JSDocProtectedTag]: (node: JSDocProtectedTag, visitor: Visitor): JSDocProtectedTag => { + const _tagName = visitNode(node.tagName, visitor, isIdentifier); + const _comment = visitNodes(node.comment, visitor); + return updateJSDocProtectedTag(node, _tagName, _comment); + }, + [SyntaxKind.JSDocReadonlyTag]: (node: JSDocReadonlyTag, visitor: Visitor): JSDocReadonlyTag => { + const _tagName = visitNode(node.tagName, visitor, isIdentifier); + const _comment = visitNodes(node.comment, visitor); + return updateJSDocReadonlyTag(node, _tagName, _comment); + }, + [SyntaxKind.JSDocOverrideTag]: (node: JSDocOverrideTag, visitor: Visitor): JSDocOverrideTag => { + const _tagName = visitNode(node.tagName, visitor, isIdentifier); + const _comment = visitNodes(node.comment, visitor); + return updateJSDocOverrideTag(node, _tagName, _comment); + }, + [SyntaxKind.JSDocDeprecatedTag]: (node: JSDocDeprecatedTag, visitor: Visitor): JSDocDeprecatedTag => { + const _tagName = visitNode(node.tagName, visitor, isIdentifier); + const _comment = visitNodes(node.comment, visitor); + return updateJSDocDeprecatedTag(node, _tagName, _comment); + }, + [SyntaxKind.JSDocSeeTag]: (node: JSDocSeeTag, visitor: Visitor): JSDocSeeTag => { + const _tagName = visitNode(node.tagName, visitor, isIdentifier); + const _nameExpression = visitNode(node.nameExpression, visitor, isTypeNode); + const _comment = visitNodes(node.comment, visitor); + return updateJSDocSeeTag(node, _tagName, _nameExpression, _comment); + }, + [SyntaxKind.JSDocImplementsTag]: (node: JSDocImplementsTag, visitor: Visitor): JSDocImplementsTag => { + const _tagName = visitNode(node.tagName, visitor, isIdentifier); + const _className = visitNode(node.className, visitor, isExpressionWithTypeArguments); + const _comment = visitNodes(node.comment, visitor); + return updateJSDocImplementsTag(node, _tagName, _className, _comment); + }, + [SyntaxKind.JSDocAugmentsTag]: (node: JSDocAugmentsTag, visitor: Visitor): JSDocAugmentsTag => { + const _tagName = visitNode(node.tagName, visitor, isIdentifier); + const _className = visitNode(node.className, visitor, isExpressionWithTypeArguments); + const _comment = visitNodes(node.comment, visitor); + return updateJSDocAugmentsTag(node, _tagName, _className, _comment); + }, + [SyntaxKind.JSDocSatisfiesTag]: (node: JSDocSatisfiesTag, visitor: Visitor): JSDocSatisfiesTag => { + const _tagName = visitNode(node.tagName, visitor, isIdentifier); + const _typeExpression = visitNode(node.typeExpression, visitor, isTypeNode); + const _comment = visitNodes(node.comment, visitor); + return updateJSDocSatisfiesTag(node, _tagName, _typeExpression, _comment); + }, + [SyntaxKind.JSDocThrowsTag]: (node: JSDocThrowsTag, visitor: Visitor): JSDocThrowsTag => { + const _tagName = visitNode(node.tagName, visitor, isIdentifier); + const _typeExpression = visitNode(node.typeExpression, visitor, isTypeNode); + const _comment = visitNodes(node.comment, visitor); + return updateJSDocThrowsTag(node, _tagName, _typeExpression, _comment); + }, + [SyntaxKind.JSDocThisTag]: (node: JSDocThisTag, visitor: Visitor): JSDocThisTag => { + const _tagName = visitNode(node.tagName, visitor, isIdentifier); + const _typeExpression = visitNode(node.typeExpression, visitor, isTypeNode); + const _comment = visitNodes(node.comment, visitor); + return updateJSDocThisTag(node, _tagName, _typeExpression, _comment); + }, + [SyntaxKind.JSDocImportTag]: (node: JSDocImportTag, visitor: Visitor): JSDocImportTag => { + const _tagName = visitNode(node.tagName, visitor, isIdentifier); + const _importClause = visitNode(node.importClause, visitor, isImportClause); + const _moduleSpecifier = visitNode(node.moduleSpecifier, visitor, isExpression); + const _attributes = visitNode(node.attributes, visitor, isImportAttributes); + const _comment = visitNodes(node.comment, visitor); + return updateJSDocImportTag(node, _tagName, _importClause, _moduleSpecifier, _attributes, _comment); + }, + [SyntaxKind.JSDocCallbackTag]: (node: JSDocCallbackTag, visitor: Visitor): JSDocCallbackTag => { + const _tagName = visitNode(node.tagName, visitor, isIdentifier); + const _typeExpression = visitNode(node.typeExpression, visitor, isTypeNode); + const _name = visitNode(node.name, visitor, isJSDocFullName); + const _comment = visitNodes(node.comment, visitor); + return updateJSDocCallbackTag(node, _tagName, _typeExpression, _name, _comment); + }, + [SyntaxKind.JSDocOverloadTag]: (node: JSDocOverloadTag, visitor: Visitor): JSDocOverloadTag => { + const _tagName = visitNode(node.tagName, visitor, isIdentifier); + const _typeExpression = visitNode(node.typeExpression, visitor, isTypeNode); + const _comment = visitNodes(node.comment, visitor); + return updateJSDocOverloadTag(node, _tagName, _typeExpression, _comment); + }, + [SyntaxKind.JSDocTypedefTag]: (node: JSDocTypedefTag, visitor: Visitor): JSDocTypedefTag => { + const _tagName = visitNode(node.tagName, visitor, isIdentifier); + const _typeExpression = visitNode(node.typeExpression, visitor); + const _name = visitNode(node.name, visitor, isJSDocFullName); + const _comment = visitNodes(node.comment, visitor); + return updateJSDocTypedefTag(node, _tagName, _typeExpression, _name, _comment); + }, + [SyntaxKind.JSDocSignature]: (node: JSDocSignature, visitor: Visitor): JSDocSignature => { + const _typeParameters = visitNodes(node.typeParameters, visitor); + const _parameters = visitNodes(node.parameters, visitor); + const _type = visitNode(node.type, visitor, isTypeNode); + return updateJSDocSignature(node, _typeParameters, _parameters, _type); + }, + [SyntaxKind.JSDocNameReference]: (node: JSDocNameReference, visitor: Visitor): JSDocNameReference => { + const _name = visitNode(node.name, visitor, isEntityName); + return updateJSDocNameReference(node, _name); + }, + [SyntaxKind.SourceFile]: (node: SourceFile, visitor: Visitor): SourceFile => { + const _statements = visitNodes(node.statements, visitor); + const _endOfFileToken = visitNode(node.endOfFileToken, visitor, isEndOfFile); + return updateSourceFile(node, _statements, _endOfFileToken); + }, + [SyntaxKind.ModuleDeclaration]: (node: ModuleDeclaration, visitor: Visitor): ModuleDeclaration => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _name = visitNode(node.name, visitor, isModuleName); + const _body = visitNode(node.body, visitor, isModuleBody); + return updateModuleDeclaration(node, _modifiers, _name, _body); + }, + [SyntaxKind.ImportEqualsDeclaration]: (node: ImportEqualsDeclaration, visitor: Visitor): ImportEqualsDeclaration => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _name = visitNode(node.name, visitor, isIdentifier); + const _moduleReference = visitNode(node.moduleReference, visitor, isModuleReference); + return updateImportEqualsDeclaration(node, _modifiers, _name, _moduleReference); + }, + [SyntaxKind.ExportDeclaration]: (node: ExportDeclaration, visitor: Visitor): ExportDeclaration => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _exportClause = visitNode(node.exportClause, visitor, isNamedExportBindings); + const _moduleSpecifier = visitNode(node.moduleSpecifier, visitor, isExpression); + const _attributes = visitNode(node.attributes, visitor, isImportAttributes); + return updateExportDeclaration(node, _modifiers, _exportClause, _moduleSpecifier, _attributes); + }, + [SyntaxKind.ImportType]: (node: ImportTypeNode, visitor: Visitor): ImportTypeNode => { + const _argument = visitNode(node.argument, visitor, isTypeNode); + const _attributes = visitNode(node.attributes, visitor, isImportAttributes); + const _qualifier = visitNode(node.qualifier, visitor, isEntityName); + const _typeArguments = visitNodes(node.typeArguments, visitor); + return updateImportTypeNode(node, _argument, _attributes, _qualifier, _typeArguments); + }, + [SyntaxKind.ImportClause]: (node: ImportClause, visitor: Visitor): ImportClause => { + const _name = visitNode(node.name, visitor, isIdentifier); + const _namedBindings = visitNode(node.namedBindings, visitor, isNamedImportBindings); + return updateImportClause(node, _name, _namedBindings); + }, + [SyntaxKind.ImportSpecifier]: (node: ImportSpecifier, visitor: Visitor): ImportSpecifier => { + const _propertyName = visitNode(node.propertyName, visitor, isModuleExportName); + const _name = visitNode(node.name, visitor, isIdentifier); + return updateImportSpecifier(node, _propertyName, _name); + }, + [SyntaxKind.JSDocLink]: (node: JSDocLink, visitor: Visitor): JSDocLink => { + const _name = visitNode(node.name, visitor, isEntityName); + return updateJSDocLink(node, _name); + }, + [SyntaxKind.JSDocLinkPlain]: (node: JSDocLinkPlain, visitor: Visitor): JSDocLinkPlain => { + const _name = visitNode(node.name, visitor, isEntityName); + return updateJSDocLinkPlain(node, _name); + }, + [SyntaxKind.JSDocLinkCode]: (node: JSDocLinkCode, visitor: Visitor): JSDocLinkCode => { + const _name = visitNode(node.name, visitor, isEntityName); + return updateJSDocLinkCode(node, _name); + }, + [SyntaxKind.TypeParameter]: (node: TypeParameterDeclaration, visitor: Visitor): TypeParameterDeclaration => { + const _modifiers = visitNodes(node.modifiers, visitor); + const _name = visitNode(node.name, visitor, isIdentifier); + const _constraint = visitNode(node.constraint, visitor, isTypeNode); + const _expression = visitNode(node.expression, visitor, isExpression); + const _defaultType = visitNode(node.defaultType, visitor, isTypeNode); + return updateTypeParameterDeclaration(node, _modifiers, _name, _constraint, _expression, _defaultType); + }, + [SyntaxKind.SyntheticReferenceExpression]: (node: SyntheticReferenceExpression, visitor: Visitor): SyntheticReferenceExpression => { + const _expression = visitNode(node.expression, visitor, isExpression); + const _thisArg = visitNode(node.thisArg, visitor, isExpression); + return updateSyntheticReferenceExpression(node, _expression, _thisArg); + }, + [SyntaxKind.JSDocTypeLiteral]: (node: JSDocTypeLiteral, visitor: Visitor): JSDocTypeLiteral => { + const _jsdocPropertyTags = visitNodesArray(node.jsdocPropertyTags, visitor); + return updateJSDocTypeLiteral(node, _jsdocPropertyTags); + }, + [SyntaxKind.ForInStatement]: (node: ForInStatement, visitor: Visitor): ForInStatement => { + const _awaitModifier = visitNode(node.awaitModifier, visitor, isAwaitKeyword); + const _initializer = visitNode(node.initializer, visitor, isForInitializer); + const _expression = visitNode(node.expression, visitor, isExpression); + const _statement = visitNode(node.statement, visitor, isStatement); + return updateForInStatement(node, _awaitModifier, _initializer, _expression, _statement); + }, + [SyntaxKind.ForOfStatement]: (node: ForOfStatement, visitor: Visitor): ForOfStatement => { + const _awaitModifier = visitNode(node.awaitModifier, visitor, isAwaitKeyword); + const _initializer = visitNode(node.initializer, visitor, isForInitializer); + const _expression = visitNode(node.expression, visitor, isExpression); + const _statement = visitNode(node.statement, visitor, isStatement); + return updateForOfStatement(node, _awaitModifier, _initializer, _expression, _statement); + }, + [SyntaxKind.CaseClause]: (node: CaseClause, visitor: Visitor): CaseClause => { + const _expression = visitNode(node.expression, visitor, isExpression); + const _statements = visitNodes(node.statements, visitor); + return updateCaseClause(node, _expression, _statements); + }, + [SyntaxKind.DefaultClause]: (node: DefaultClause, visitor: Visitor): DefaultClause => { + const _expression = visitNode(node.expression, visitor, isExpression); + const _statements = visitNodes(node.statements, visitor); + return updateDefaultClause(node, _expression, _statements); + }, + [SyntaxKind.ObjectBindingPattern]: (node: ObjectBindingPattern, visitor: Visitor): ObjectBindingPattern => { + const _elements = visitNodes(node.elements, visitor); + return updateObjectBindingPattern(node, _elements); + }, + [SyntaxKind.ArrayBindingPattern]: (node: ArrayBindingPattern, visitor: Visitor): ArrayBindingPattern => { + const _elements = visitNodes(node.elements, visitor); + return updateArrayBindingPattern(node, _elements); + }, + [SyntaxKind.JSDocParameterTag]: visitEachChildOfJSDocParameterTag, + [SyntaxKind.JSDocPropertyTag]: visitEachChildOfJSDocPropertyTag, +}; diff --git a/packages/typescript/src/ast/visitor.ts b/packages/typescript/src/ast/visitor.ts index 09438ab524a19..8f25d56a18cfa 100644 --- a/packages/typescript/src/ast/visitor.ts +++ b/packages/typescript/src/ast/visitor.ts @@ -1,74 +1,74 @@ -/** - * Hand-written visitor implementations for nodes with runtime-dependent - * child ordering. Generated code in visitor.generated.ts and factory.generated.ts - * delegates to these functions. - */ - -import { SyntaxKind } from "#enums/syntaxKind"; -import type { - JSDocComment, - JSDocParameterOrPropertyTag, - JSDocParameterTag, - JSDocPropertyTag, - Node, - NodeArray, -} from "./ast.ts"; -import { - updateJSDocParameterTag, - updateJSDocPropertyTag, -} from "./factory.generated.ts"; -import { - isEntityName, - isIdentifier, - isTypeNode, -} from "./is.ts"; -import type { Visitor } from "./visitor.generated.ts"; -import { - visitNode, - visitNodes, -} from "./visitor.generated.ts"; - -export type { Visitor }; -export { visitEachChild, visitNode, visitNodes, visitNodesArray } from "./visitor.generated.ts"; - -// ── forEachChild helpers (same signature as forEachChildTable entries) ── - -function visitNodeForEachChild(cbNode: (node: Node) => T, node: Node | undefined): T | undefined { - return node ? cbNode(node) : undefined; -} - -function visitNodesForEachChild(cbNode: (node: Node) => T, cbNodes: ((nodes: NodeArray) => T) | undefined, nodes: NodeArray | undefined): T | undefined { - if (!nodes) return undefined; - if (cbNodes) return cbNodes(nodes); - for (const node of nodes) { - const result = cbNode(node); - if (result) return result; - } - return undefined; -} - -// ── forEachChild implementations ── - -function forEachChildOfJSDocParameterOrPropertyTag(data: any, cbNode: (node: Node) => T, cbNodes: ((nodes: NodeArray) => T) | undefined): T | undefined { - return visitNodeForEachChild(cbNode, data.tagName) || - (data.isNameFirst - ? visitNodeForEachChild(cbNode, data.name) || visitNodeForEachChild(cbNode, data.typeExpression) - : visitNodeForEachChild(cbNode, data.typeExpression) || visitNodeForEachChild(cbNode, data.name)) || - visitNodesForEachChild(cbNode, cbNodes, data.comment); -} - -export { forEachChildOfJSDocParameterOrPropertyTag as forEachChildOfJSDocParameterTag, forEachChildOfJSDocParameterOrPropertyTag as forEachChildOfJSDocPropertyTag }; - -// ── visitEachChild implementations ── - -function visitEachChildOfJSDocParameterOrPropertyTag(node: JSDocParameterOrPropertyTag, visitor: Visitor): JSDocParameterOrPropertyTag { - const _tagName = visitNode(node.tagName, visitor, isIdentifier); - const _name = visitNode(node.name, visitor, isEntityName); - const _typeExpression = visitNode(node.typeExpression, visitor, isTypeNode); - const _comment = visitNodes(node.comment, visitor); - return node.kind === SyntaxKind.JSDocParameterTag - ? updateJSDocParameterTag(node, _tagName, _name, _typeExpression, _comment) - : updateJSDocPropertyTag(node, _tagName, _name, _typeExpression, _comment); -} - -export { visitEachChildOfJSDocParameterOrPropertyTag as visitEachChildOfJSDocParameterTag, visitEachChildOfJSDocParameterOrPropertyTag as visitEachChildOfJSDocPropertyTag }; +/** + * Hand-written visitor implementations for nodes with runtime-dependent + * child ordering. Generated code in visitor.generated.ts and factory.generated.ts + * delegates to these functions. + */ + +import { SyntaxKind } from "#enums/syntaxKind"; +import type { + JSDocComment, + JSDocParameterOrPropertyTag, + JSDocParameterTag, + JSDocPropertyTag, + Node, + NodeArray, +} from "./ast.ts"; +import { + updateJSDocParameterTag, + updateJSDocPropertyTag, +} from "./factory.generated.ts"; +import { + isEntityName, + isIdentifier, + isTypeNode, +} from "./is.ts"; +import type { Visitor } from "./visitor.generated.ts"; +import { + visitNode, + visitNodes, +} from "./visitor.generated.ts"; + +export type { Visitor }; +export { visitEachChild, visitNode, visitNodes, visitNodesArray } from "./visitor.generated.ts"; + +// ── forEachChild helpers (same signature as forEachChildTable entries) ── + +function visitNodeForEachChild(cbNode: (node: Node) => T, node: Node | undefined): T | undefined { + return node ? cbNode(node) : undefined; +} + +function visitNodesForEachChild(cbNode: (node: Node) => T, cbNodes: ((nodes: NodeArray) => T) | undefined, nodes: NodeArray | undefined): T | undefined { + if (!nodes) return undefined; + if (cbNodes) return cbNodes(nodes); + for (const node of nodes) { + const result = cbNode(node); + if (result) return result; + } + return undefined; +} + +// ── forEachChild implementations ── + +function forEachChildOfJSDocParameterOrPropertyTag(data: any, cbNode: (node: Node) => T, cbNodes: ((nodes: NodeArray) => T) | undefined): T | undefined { + return visitNodeForEachChild(cbNode, data.tagName) || + (data.isNameFirst + ? visitNodeForEachChild(cbNode, data.name) || visitNodeForEachChild(cbNode, data.typeExpression) + : visitNodeForEachChild(cbNode, data.typeExpression) || visitNodeForEachChild(cbNode, data.name)) || + visitNodesForEachChild(cbNode, cbNodes, data.comment); +} + +export { forEachChildOfJSDocParameterOrPropertyTag as forEachChildOfJSDocParameterTag, forEachChildOfJSDocParameterOrPropertyTag as forEachChildOfJSDocPropertyTag }; + +// ── visitEachChild implementations ── + +function visitEachChildOfJSDocParameterOrPropertyTag(node: JSDocParameterOrPropertyTag, visitor: Visitor): JSDocParameterOrPropertyTag { + const _tagName = visitNode(node.tagName, visitor, isIdentifier); + const _name = visitNode(node.name, visitor, isEntityName); + const _typeExpression = visitNode(node.typeExpression, visitor, isTypeNode); + const _comment = visitNodes(node.comment, visitor); + return node.kind === SyntaxKind.JSDocParameterTag + ? updateJSDocParameterTag(node, _tagName, _name, _typeExpression, _comment) + : updateJSDocPropertyTag(node, _tagName, _name, _typeExpression, _comment); +} + +export { visitEachChildOfJSDocParameterOrPropertyTag as visitEachChildOfJSDocParameterTag, visitEachChildOfJSDocParameterOrPropertyTag as visitEachChildOfJSDocPropertyTag }; diff --git a/packages/typescript/src/enums/characterCodes.enum.ts b/packages/typescript/src/enums/characterCodes.enum.ts index 052c7e8feb896..0e7fe5a8c8323 100644 --- a/packages/typescript/src/enums/characterCodes.enum.ts +++ b/packages/typescript/src/enums/characterCodes.enum.ts @@ -1,139 +1,139 @@ -export enum CharacterCodes { - EOF = -1, - nullCharacter = 0, - maxAsciiCharacter = 0x7F, - - lineFeed = 0x0A, // \n - carriageReturn = 0x0D, // \r - lineSeparator = 0x2028, - paragraphSeparator = 0x2029, - nextLine = 0x0085, - - // Unicode 3.0 space characters - space = 0x0020, // " " - nonBreakingSpace = 0x00A0, // - enQuad = 0x2000, - emQuad = 0x2001, - enSpace = 0x2002, - emSpace = 0x2003, - threePerEmSpace = 0x2004, - fourPerEmSpace = 0x2005, - sixPerEmSpace = 0x2006, - figureSpace = 0x2007, - punctuationSpace = 0x2008, - thinSpace = 0x2009, - hairSpace = 0x200A, - zeroWidthSpace = 0x200B, - narrowNoBreakSpace = 0x202F, - ideographicSpace = 0x3000, - mathematicalSpace = 0x205F, - ogham = 0x1680, - - // Unicode replacement character produced when a byte sequence is invalid - replacementCharacter = 0xFFFD, - - _ = 0x5F, - $ = 0x24, - - _0 = 0x30, - _1 = 0x31, - _2 = 0x32, - _3 = 0x33, - _4 = 0x34, - _5 = 0x35, - _6 = 0x36, - _7 = 0x37, - _8 = 0x38, - _9 = 0x39, - - a = 0x61, - b = 0x62, - c = 0x63, - d = 0x64, - e = 0x65, - f = 0x66, - g = 0x67, - h = 0x68, - i = 0x69, - j = 0x6A, - k = 0x6B, - l = 0x6C, - m = 0x6D, - n = 0x6E, - o = 0x6F, - p = 0x70, - q = 0x71, - r = 0x72, - s = 0x73, - t = 0x74, - u = 0x75, - v = 0x76, - w = 0x77, - x = 0x78, - y = 0x79, - z = 0x7A, - - A = 0x41, - B = 0x42, - C = 0x43, - D = 0x44, - E = 0x45, - F = 0x46, - G = 0x47, - H = 0x48, - I = 0x49, - J = 0x4A, - K = 0x4B, - L = 0x4C, - M = 0x4D, - N = 0x4E, - O = 0x4F, - P = 0x50, - Q = 0x51, - R = 0x52, - S = 0x53, - T = 0x54, - U = 0x55, - V = 0x56, - W = 0x57, - X = 0x58, - Y = 0x59, - Z = 0x5a, - - ampersand = 0x26, // & - asterisk = 0x2A, // * - at = 0x40, // @ - backslash = 0x5C, // \ - backtick = 0x60, // ` - bar = 0x7C, // | - caret = 0x5E, // ^ - closeBrace = 0x7D, // } - closeBracket = 0x5D, // ] - closeParen = 0x29, // ) - colon = 0x3A, // : - comma = 0x2C, // , - dot = 0x2E, // . - doubleQuote = 0x22, // " - equals = 0x3D, // = - exclamation = 0x21, // ! - greaterThan = 0x3E, // > - hash = 0x23, // # - lessThan = 0x3C, // < - minus = 0x2D, // - - openBrace = 0x7B, // { - openBracket = 0x5B, // [ - openParen = 0x28, // ( - percent = 0x25, // % - plus = 0x2B, // + - question = 0x3F, // ? - semicolon = 0x3B, // ; - singleQuote = 0x27, // ' - slash = 0x2F, // / - tilde = 0x7E, // ~ - - backspace = 0x08, // \b - formFeed = 0x0C, // \f - byteOrderMark = 0xFEFF, - tab = 0x09, // \t - verticalTab = 0x0B, // \v -} +export enum CharacterCodes { + EOF = -1, + nullCharacter = 0, + maxAsciiCharacter = 0x7F, + + lineFeed = 0x0A, // \n + carriageReturn = 0x0D, // \r + lineSeparator = 0x2028, + paragraphSeparator = 0x2029, + nextLine = 0x0085, + + // Unicode 3.0 space characters + space = 0x0020, // " " + nonBreakingSpace = 0x00A0, // + enQuad = 0x2000, + emQuad = 0x2001, + enSpace = 0x2002, + emSpace = 0x2003, + threePerEmSpace = 0x2004, + fourPerEmSpace = 0x2005, + sixPerEmSpace = 0x2006, + figureSpace = 0x2007, + punctuationSpace = 0x2008, + thinSpace = 0x2009, + hairSpace = 0x200A, + zeroWidthSpace = 0x200B, + narrowNoBreakSpace = 0x202F, + ideographicSpace = 0x3000, + mathematicalSpace = 0x205F, + ogham = 0x1680, + + // Unicode replacement character produced when a byte sequence is invalid + replacementCharacter = 0xFFFD, + + _ = 0x5F, + $ = 0x24, + + _0 = 0x30, + _1 = 0x31, + _2 = 0x32, + _3 = 0x33, + _4 = 0x34, + _5 = 0x35, + _6 = 0x36, + _7 = 0x37, + _8 = 0x38, + _9 = 0x39, + + a = 0x61, + b = 0x62, + c = 0x63, + d = 0x64, + e = 0x65, + f = 0x66, + g = 0x67, + h = 0x68, + i = 0x69, + j = 0x6A, + k = 0x6B, + l = 0x6C, + m = 0x6D, + n = 0x6E, + o = 0x6F, + p = 0x70, + q = 0x71, + r = 0x72, + s = 0x73, + t = 0x74, + u = 0x75, + v = 0x76, + w = 0x77, + x = 0x78, + y = 0x79, + z = 0x7A, + + A = 0x41, + B = 0x42, + C = 0x43, + D = 0x44, + E = 0x45, + F = 0x46, + G = 0x47, + H = 0x48, + I = 0x49, + J = 0x4A, + K = 0x4B, + L = 0x4C, + M = 0x4D, + N = 0x4E, + O = 0x4F, + P = 0x50, + Q = 0x51, + R = 0x52, + S = 0x53, + T = 0x54, + U = 0x55, + V = 0x56, + W = 0x57, + X = 0x58, + Y = 0x59, + Z = 0x5a, + + ampersand = 0x26, // & + asterisk = 0x2A, // * + at = 0x40, // @ + backslash = 0x5C, // \ + backtick = 0x60, // ` + bar = 0x7C, // | + caret = 0x5E, // ^ + closeBrace = 0x7D, // } + closeBracket = 0x5D, // ] + closeParen = 0x29, // ) + colon = 0x3A, // : + comma = 0x2C, // , + dot = 0x2E, // . + doubleQuote = 0x22, // " + equals = 0x3D, // = + exclamation = 0x21, // ! + greaterThan = 0x3E, // > + hash = 0x23, // # + lessThan = 0x3C, // < + minus = 0x2D, // - + openBrace = 0x7B, // { + openBracket = 0x5B, // [ + openParen = 0x28, // ( + percent = 0x25, // % + plus = 0x2B, // + + question = 0x3F, // ? + semicolon = 0x3B, // ; + singleQuote = 0x27, // ' + slash = 0x2F, // / + tilde = 0x7E, // ~ + + backspace = 0x08, // \b + formFeed = 0x0C, // \f + byteOrderMark = 0xFEFF, + tab = 0x09, // \t + verticalTab = 0x0B, // \v +} diff --git a/packages/typescript/src/enums/characterCodes.ts b/packages/typescript/src/enums/characterCodes.ts index 232a292da0cd0..5981512833131 100644 --- a/packages/typescript/src/enums/characterCodes.ts +++ b/packages/typescript/src/enums/characterCodes.ts @@ -1,129 +1,129 @@ -export var CharacterCodes: any; -(function (CharacterCodes) { - CharacterCodes[CharacterCodes["EOF"] = -1] = "EOF"; - CharacterCodes[CharacterCodes["nullCharacter"] = 0] = "nullCharacter"; - CharacterCodes[CharacterCodes["maxAsciiCharacter"] = 127] = "maxAsciiCharacter"; - CharacterCodes[CharacterCodes["lineFeed"] = 10] = "lineFeed"; - CharacterCodes[CharacterCodes["carriageReturn"] = 13] = "carriageReturn"; - CharacterCodes[CharacterCodes["lineSeparator"] = 8232] = "lineSeparator"; - CharacterCodes[CharacterCodes["paragraphSeparator"] = 8233] = "paragraphSeparator"; - CharacterCodes[CharacterCodes["nextLine"] = 133] = "nextLine"; - CharacterCodes[CharacterCodes["space"] = 32] = "space"; - CharacterCodes[CharacterCodes["nonBreakingSpace"] = 160] = "nonBreakingSpace"; - CharacterCodes[CharacterCodes["enQuad"] = 8192] = "enQuad"; - CharacterCodes[CharacterCodes["emQuad"] = 8193] = "emQuad"; - CharacterCodes[CharacterCodes["enSpace"] = 8194] = "enSpace"; - CharacterCodes[CharacterCodes["emSpace"] = 8195] = "emSpace"; - CharacterCodes[CharacterCodes["threePerEmSpace"] = 8196] = "threePerEmSpace"; - CharacterCodes[CharacterCodes["fourPerEmSpace"] = 8197] = "fourPerEmSpace"; - CharacterCodes[CharacterCodes["sixPerEmSpace"] = 8198] = "sixPerEmSpace"; - CharacterCodes[CharacterCodes["figureSpace"] = 8199] = "figureSpace"; - CharacterCodes[CharacterCodes["punctuationSpace"] = 8200] = "punctuationSpace"; - CharacterCodes[CharacterCodes["thinSpace"] = 8201] = "thinSpace"; - CharacterCodes[CharacterCodes["hairSpace"] = 8202] = "hairSpace"; - CharacterCodes[CharacterCodes["zeroWidthSpace"] = 8203] = "zeroWidthSpace"; - CharacterCodes[CharacterCodes["narrowNoBreakSpace"] = 8239] = "narrowNoBreakSpace"; - CharacterCodes[CharacterCodes["ideographicSpace"] = 12288] = "ideographicSpace"; - CharacterCodes[CharacterCodes["mathematicalSpace"] = 8287] = "mathematicalSpace"; - CharacterCodes[CharacterCodes["ogham"] = 5765] = "ogham"; - CharacterCodes[CharacterCodes["replacementCharacter"] = 65533] = "replacementCharacter"; - CharacterCodes[CharacterCodes["_"] = 95] = "_"; - CharacterCodes[CharacterCodes["$"] = 36] = "$"; - CharacterCodes[CharacterCodes["_0"] = 48] = "_0"; - CharacterCodes[CharacterCodes["_1"] = 49] = "_1"; - CharacterCodes[CharacterCodes["_2"] = 50] = "_2"; - CharacterCodes[CharacterCodes["_3"] = 51] = "_3"; - CharacterCodes[CharacterCodes["_4"] = 52] = "_4"; - CharacterCodes[CharacterCodes["_5"] = 53] = "_5"; - CharacterCodes[CharacterCodes["_6"] = 54] = "_6"; - CharacterCodes[CharacterCodes["_7"] = 55] = "_7"; - CharacterCodes[CharacterCodes["_8"] = 56] = "_8"; - CharacterCodes[CharacterCodes["_9"] = 57] = "_9"; - CharacterCodes[CharacterCodes["a"] = 97] = "a"; - CharacterCodes[CharacterCodes["b"] = 98] = "b"; - CharacterCodes[CharacterCodes["c"] = 99] = "c"; - CharacterCodes[CharacterCodes["d"] = 100] = "d"; - CharacterCodes[CharacterCodes["e"] = 101] = "e"; - CharacterCodes[CharacterCodes["f"] = 102] = "f"; - CharacterCodes[CharacterCodes["g"] = 103] = "g"; - CharacterCodes[CharacterCodes["h"] = 104] = "h"; - CharacterCodes[CharacterCodes["i"] = 105] = "i"; - CharacterCodes[CharacterCodes["j"] = 106] = "j"; - CharacterCodes[CharacterCodes["k"] = 107] = "k"; - CharacterCodes[CharacterCodes["l"] = 108] = "l"; - CharacterCodes[CharacterCodes["m"] = 109] = "m"; - CharacterCodes[CharacterCodes["n"] = 110] = "n"; - CharacterCodes[CharacterCodes["o"] = 111] = "o"; - CharacterCodes[CharacterCodes["p"] = 112] = "p"; - CharacterCodes[CharacterCodes["q"] = 113] = "q"; - CharacterCodes[CharacterCodes["r"] = 114] = "r"; - CharacterCodes[CharacterCodes["s"] = 115] = "s"; - CharacterCodes[CharacterCodes["t"] = 116] = "t"; - CharacterCodes[CharacterCodes["u"] = 117] = "u"; - CharacterCodes[CharacterCodes["v"] = 118] = "v"; - CharacterCodes[CharacterCodes["w"] = 119] = "w"; - CharacterCodes[CharacterCodes["x"] = 120] = "x"; - CharacterCodes[CharacterCodes["y"] = 121] = "y"; - CharacterCodes[CharacterCodes["z"] = 122] = "z"; - CharacterCodes[CharacterCodes["A"] = 65] = "A"; - CharacterCodes[CharacterCodes["B"] = 66] = "B"; - CharacterCodes[CharacterCodes["C"] = 67] = "C"; - CharacterCodes[CharacterCodes["D"] = 68] = "D"; - CharacterCodes[CharacterCodes["E"] = 69] = "E"; - CharacterCodes[CharacterCodes["F"] = 70] = "F"; - CharacterCodes[CharacterCodes["G"] = 71] = "G"; - CharacterCodes[CharacterCodes["H"] = 72] = "H"; - CharacterCodes[CharacterCodes["I"] = 73] = "I"; - CharacterCodes[CharacterCodes["J"] = 74] = "J"; - CharacterCodes[CharacterCodes["K"] = 75] = "K"; - CharacterCodes[CharacterCodes["L"] = 76] = "L"; - CharacterCodes[CharacterCodes["M"] = 77] = "M"; - CharacterCodes[CharacterCodes["N"] = 78] = "N"; - CharacterCodes[CharacterCodes["O"] = 79] = "O"; - CharacterCodes[CharacterCodes["P"] = 80] = "P"; - CharacterCodes[CharacterCodes["Q"] = 81] = "Q"; - CharacterCodes[CharacterCodes["R"] = 82] = "R"; - CharacterCodes[CharacterCodes["S"] = 83] = "S"; - CharacterCodes[CharacterCodes["T"] = 84] = "T"; - CharacterCodes[CharacterCodes["U"] = 85] = "U"; - CharacterCodes[CharacterCodes["V"] = 86] = "V"; - CharacterCodes[CharacterCodes["W"] = 87] = "W"; - CharacterCodes[CharacterCodes["X"] = 88] = "X"; - CharacterCodes[CharacterCodes["Y"] = 89] = "Y"; - CharacterCodes[CharacterCodes["Z"] = 90] = "Z"; - CharacterCodes[CharacterCodes["ampersand"] = 38] = "ampersand"; - CharacterCodes[CharacterCodes["asterisk"] = 42] = "asterisk"; - CharacterCodes[CharacterCodes["at"] = 64] = "at"; - CharacterCodes[CharacterCodes["backslash"] = 92] = "backslash"; - CharacterCodes[CharacterCodes["backtick"] = 96] = "backtick"; - CharacterCodes[CharacterCodes["bar"] = 124] = "bar"; - CharacterCodes[CharacterCodes["caret"] = 94] = "caret"; - CharacterCodes[CharacterCodes["closeBrace"] = 125] = "closeBrace"; - CharacterCodes[CharacterCodes["closeBracket"] = 93] = "closeBracket"; - CharacterCodes[CharacterCodes["closeParen"] = 41] = "closeParen"; - CharacterCodes[CharacterCodes["colon"] = 58] = "colon"; - CharacterCodes[CharacterCodes["comma"] = 44] = "comma"; - CharacterCodes[CharacterCodes["dot"] = 46] = "dot"; - CharacterCodes[CharacterCodes["doubleQuote"] = 34] = "doubleQuote"; - CharacterCodes[CharacterCodes["equals"] = 61] = "equals"; - CharacterCodes[CharacterCodes["exclamation"] = 33] = "exclamation"; - CharacterCodes[CharacterCodes["greaterThan"] = 62] = "greaterThan"; - CharacterCodes[CharacterCodes["hash"] = 35] = "hash"; - CharacterCodes[CharacterCodes["lessThan"] = 60] = "lessThan"; - CharacterCodes[CharacterCodes["minus"] = 45] = "minus"; - CharacterCodes[CharacterCodes["openBrace"] = 123] = "openBrace"; - CharacterCodes[CharacterCodes["openBracket"] = 91] = "openBracket"; - CharacterCodes[CharacterCodes["openParen"] = 40] = "openParen"; - CharacterCodes[CharacterCodes["percent"] = 37] = "percent"; - CharacterCodes[CharacterCodes["plus"] = 43] = "plus"; - CharacterCodes[CharacterCodes["question"] = 63] = "question"; - CharacterCodes[CharacterCodes["semicolon"] = 59] = "semicolon"; - CharacterCodes[CharacterCodes["singleQuote"] = 39] = "singleQuote"; - CharacterCodes[CharacterCodes["slash"] = 47] = "slash"; - CharacterCodes[CharacterCodes["tilde"] = 126] = "tilde"; - CharacterCodes[CharacterCodes["backspace"] = 8] = "backspace"; - CharacterCodes[CharacterCodes["formFeed"] = 12] = "formFeed"; - CharacterCodes[CharacterCodes["byteOrderMark"] = 65279] = "byteOrderMark"; - CharacterCodes[CharacterCodes["tab"] = 9] = "tab"; - CharacterCodes[CharacterCodes["verticalTab"] = 11] = "verticalTab"; -})(CharacterCodes || (CharacterCodes = {})); +export var CharacterCodes: any; +(function (CharacterCodes) { + CharacterCodes[CharacterCodes["EOF"] = -1] = "EOF"; + CharacterCodes[CharacterCodes["nullCharacter"] = 0] = "nullCharacter"; + CharacterCodes[CharacterCodes["maxAsciiCharacter"] = 127] = "maxAsciiCharacter"; + CharacterCodes[CharacterCodes["lineFeed"] = 10] = "lineFeed"; + CharacterCodes[CharacterCodes["carriageReturn"] = 13] = "carriageReturn"; + CharacterCodes[CharacterCodes["lineSeparator"] = 8232] = "lineSeparator"; + CharacterCodes[CharacterCodes["paragraphSeparator"] = 8233] = "paragraphSeparator"; + CharacterCodes[CharacterCodes["nextLine"] = 133] = "nextLine"; + CharacterCodes[CharacterCodes["space"] = 32] = "space"; + CharacterCodes[CharacterCodes["nonBreakingSpace"] = 160] = "nonBreakingSpace"; + CharacterCodes[CharacterCodes["enQuad"] = 8192] = "enQuad"; + CharacterCodes[CharacterCodes["emQuad"] = 8193] = "emQuad"; + CharacterCodes[CharacterCodes["enSpace"] = 8194] = "enSpace"; + CharacterCodes[CharacterCodes["emSpace"] = 8195] = "emSpace"; + CharacterCodes[CharacterCodes["threePerEmSpace"] = 8196] = "threePerEmSpace"; + CharacterCodes[CharacterCodes["fourPerEmSpace"] = 8197] = "fourPerEmSpace"; + CharacterCodes[CharacterCodes["sixPerEmSpace"] = 8198] = "sixPerEmSpace"; + CharacterCodes[CharacterCodes["figureSpace"] = 8199] = "figureSpace"; + CharacterCodes[CharacterCodes["punctuationSpace"] = 8200] = "punctuationSpace"; + CharacterCodes[CharacterCodes["thinSpace"] = 8201] = "thinSpace"; + CharacterCodes[CharacterCodes["hairSpace"] = 8202] = "hairSpace"; + CharacterCodes[CharacterCodes["zeroWidthSpace"] = 8203] = "zeroWidthSpace"; + CharacterCodes[CharacterCodes["narrowNoBreakSpace"] = 8239] = "narrowNoBreakSpace"; + CharacterCodes[CharacterCodes["ideographicSpace"] = 12288] = "ideographicSpace"; + CharacterCodes[CharacterCodes["mathematicalSpace"] = 8287] = "mathematicalSpace"; + CharacterCodes[CharacterCodes["ogham"] = 5765] = "ogham"; + CharacterCodes[CharacterCodes["replacementCharacter"] = 65533] = "replacementCharacter"; + CharacterCodes[CharacterCodes["_"] = 95] = "_"; + CharacterCodes[CharacterCodes["$"] = 36] = "$"; + CharacterCodes[CharacterCodes["_0"] = 48] = "_0"; + CharacterCodes[CharacterCodes["_1"] = 49] = "_1"; + CharacterCodes[CharacterCodes["_2"] = 50] = "_2"; + CharacterCodes[CharacterCodes["_3"] = 51] = "_3"; + CharacterCodes[CharacterCodes["_4"] = 52] = "_4"; + CharacterCodes[CharacterCodes["_5"] = 53] = "_5"; + CharacterCodes[CharacterCodes["_6"] = 54] = "_6"; + CharacterCodes[CharacterCodes["_7"] = 55] = "_7"; + CharacterCodes[CharacterCodes["_8"] = 56] = "_8"; + CharacterCodes[CharacterCodes["_9"] = 57] = "_9"; + CharacterCodes[CharacterCodes["a"] = 97] = "a"; + CharacterCodes[CharacterCodes["b"] = 98] = "b"; + CharacterCodes[CharacterCodes["c"] = 99] = "c"; + CharacterCodes[CharacterCodes["d"] = 100] = "d"; + CharacterCodes[CharacterCodes["e"] = 101] = "e"; + CharacterCodes[CharacterCodes["f"] = 102] = "f"; + CharacterCodes[CharacterCodes["g"] = 103] = "g"; + CharacterCodes[CharacterCodes["h"] = 104] = "h"; + CharacterCodes[CharacterCodes["i"] = 105] = "i"; + CharacterCodes[CharacterCodes["j"] = 106] = "j"; + CharacterCodes[CharacterCodes["k"] = 107] = "k"; + CharacterCodes[CharacterCodes["l"] = 108] = "l"; + CharacterCodes[CharacterCodes["m"] = 109] = "m"; + CharacterCodes[CharacterCodes["n"] = 110] = "n"; + CharacterCodes[CharacterCodes["o"] = 111] = "o"; + CharacterCodes[CharacterCodes["p"] = 112] = "p"; + CharacterCodes[CharacterCodes["q"] = 113] = "q"; + CharacterCodes[CharacterCodes["r"] = 114] = "r"; + CharacterCodes[CharacterCodes["s"] = 115] = "s"; + CharacterCodes[CharacterCodes["t"] = 116] = "t"; + CharacterCodes[CharacterCodes["u"] = 117] = "u"; + CharacterCodes[CharacterCodes["v"] = 118] = "v"; + CharacterCodes[CharacterCodes["w"] = 119] = "w"; + CharacterCodes[CharacterCodes["x"] = 120] = "x"; + CharacterCodes[CharacterCodes["y"] = 121] = "y"; + CharacterCodes[CharacterCodes["z"] = 122] = "z"; + CharacterCodes[CharacterCodes["A"] = 65] = "A"; + CharacterCodes[CharacterCodes["B"] = 66] = "B"; + CharacterCodes[CharacterCodes["C"] = 67] = "C"; + CharacterCodes[CharacterCodes["D"] = 68] = "D"; + CharacterCodes[CharacterCodes["E"] = 69] = "E"; + CharacterCodes[CharacterCodes["F"] = 70] = "F"; + CharacterCodes[CharacterCodes["G"] = 71] = "G"; + CharacterCodes[CharacterCodes["H"] = 72] = "H"; + CharacterCodes[CharacterCodes["I"] = 73] = "I"; + CharacterCodes[CharacterCodes["J"] = 74] = "J"; + CharacterCodes[CharacterCodes["K"] = 75] = "K"; + CharacterCodes[CharacterCodes["L"] = 76] = "L"; + CharacterCodes[CharacterCodes["M"] = 77] = "M"; + CharacterCodes[CharacterCodes["N"] = 78] = "N"; + CharacterCodes[CharacterCodes["O"] = 79] = "O"; + CharacterCodes[CharacterCodes["P"] = 80] = "P"; + CharacterCodes[CharacterCodes["Q"] = 81] = "Q"; + CharacterCodes[CharacterCodes["R"] = 82] = "R"; + CharacterCodes[CharacterCodes["S"] = 83] = "S"; + CharacterCodes[CharacterCodes["T"] = 84] = "T"; + CharacterCodes[CharacterCodes["U"] = 85] = "U"; + CharacterCodes[CharacterCodes["V"] = 86] = "V"; + CharacterCodes[CharacterCodes["W"] = 87] = "W"; + CharacterCodes[CharacterCodes["X"] = 88] = "X"; + CharacterCodes[CharacterCodes["Y"] = 89] = "Y"; + CharacterCodes[CharacterCodes["Z"] = 90] = "Z"; + CharacterCodes[CharacterCodes["ampersand"] = 38] = "ampersand"; + CharacterCodes[CharacterCodes["asterisk"] = 42] = "asterisk"; + CharacterCodes[CharacterCodes["at"] = 64] = "at"; + CharacterCodes[CharacterCodes["backslash"] = 92] = "backslash"; + CharacterCodes[CharacterCodes["backtick"] = 96] = "backtick"; + CharacterCodes[CharacterCodes["bar"] = 124] = "bar"; + CharacterCodes[CharacterCodes["caret"] = 94] = "caret"; + CharacterCodes[CharacterCodes["closeBrace"] = 125] = "closeBrace"; + CharacterCodes[CharacterCodes["closeBracket"] = 93] = "closeBracket"; + CharacterCodes[CharacterCodes["closeParen"] = 41] = "closeParen"; + CharacterCodes[CharacterCodes["colon"] = 58] = "colon"; + CharacterCodes[CharacterCodes["comma"] = 44] = "comma"; + CharacterCodes[CharacterCodes["dot"] = 46] = "dot"; + CharacterCodes[CharacterCodes["doubleQuote"] = 34] = "doubleQuote"; + CharacterCodes[CharacterCodes["equals"] = 61] = "equals"; + CharacterCodes[CharacterCodes["exclamation"] = 33] = "exclamation"; + CharacterCodes[CharacterCodes["greaterThan"] = 62] = "greaterThan"; + CharacterCodes[CharacterCodes["hash"] = 35] = "hash"; + CharacterCodes[CharacterCodes["lessThan"] = 60] = "lessThan"; + CharacterCodes[CharacterCodes["minus"] = 45] = "minus"; + CharacterCodes[CharacterCodes["openBrace"] = 123] = "openBrace"; + CharacterCodes[CharacterCodes["openBracket"] = 91] = "openBracket"; + CharacterCodes[CharacterCodes["openParen"] = 40] = "openParen"; + CharacterCodes[CharacterCodes["percent"] = 37] = "percent"; + CharacterCodes[CharacterCodes["plus"] = 43] = "plus"; + CharacterCodes[CharacterCodes["question"] = 63] = "question"; + CharacterCodes[CharacterCodes["semicolon"] = 59] = "semicolon"; + CharacterCodes[CharacterCodes["singleQuote"] = 39] = "singleQuote"; + CharacterCodes[CharacterCodes["slash"] = 47] = "slash"; + CharacterCodes[CharacterCodes["tilde"] = 126] = "tilde"; + CharacterCodes[CharacterCodes["backspace"] = 8] = "backspace"; + CharacterCodes[CharacterCodes["formFeed"] = 12] = "formFeed"; + CharacterCodes[CharacterCodes["byteOrderMark"] = 65279] = "byteOrderMark"; + CharacterCodes[CharacterCodes["tab"] = 9] = "tab"; + CharacterCodes[CharacterCodes["verticalTab"] = 11] = "verticalTab"; +})(CharacterCodes || (CharacterCodes = {})); diff --git a/packages/typescript/src/enums/checkFlags.enum.ts b/packages/typescript/src/enums/checkFlags.enum.ts index 7aab3deb385bf..80f35c9ce7048 100644 --- a/packages/typescript/src/enums/checkFlags.enum.ts +++ b/packages/typescript/src/enums/checkFlags.enum.ts @@ -1,35 +1,35 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/checkflags.go. DO NOT EDIT. - -export enum CheckFlags { - None = 0, - Instantiated = 1 << 0, - SyntheticProperty = 1 << 1, - SyntheticMethod = 1 << 2, - Readonly = 1 << 3, - ReadPartial = 1 << 4, - WritePartial = 1 << 5, - HasNonUniformType = 1 << 6, - HasLiteralType = 1 << 7, - ContainsPublic = 1 << 8, - ContainsProtected = 1 << 9, - ContainsPrivate = 1 << 10, - ContainsWritePublic = 1 << 11, - ContainsWriteProtected = 1 << 12, - ContainsWritePrivate = 1 << 13, - ContainsStatic = 1 << 14, - Late = 1 << 15, - ReverseMapped = 1 << 16, - OptionalParameter = 1 << 17, - RestParameter = 1 << 18, - DeferredType = 1 << 19, - HasNeverType = 1 << 20, - Mapped = 1 << 21, - StripOptional = 1 << 22, - Unresolved = 1 << 23, - IsDiscriminantComputed = 1 << 24, - IsDiscriminant = 1 << 25, - IndexSymbol = 1 << 26, - Synthetic = SyntheticProperty | SyntheticMethod, - NonUniformAndLiteral = HasNonUniformType | HasLiteralType, - Partial = ReadPartial | WritePartial, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/checkflags.go. DO NOT EDIT. + +export enum CheckFlags { + None = 0, + Instantiated = 1 << 0, + SyntheticProperty = 1 << 1, + SyntheticMethod = 1 << 2, + Readonly = 1 << 3, + ReadPartial = 1 << 4, + WritePartial = 1 << 5, + HasNonUniformType = 1 << 6, + HasLiteralType = 1 << 7, + ContainsPublic = 1 << 8, + ContainsProtected = 1 << 9, + ContainsPrivate = 1 << 10, + ContainsWritePublic = 1 << 11, + ContainsWriteProtected = 1 << 12, + ContainsWritePrivate = 1 << 13, + ContainsStatic = 1 << 14, + Late = 1 << 15, + ReverseMapped = 1 << 16, + OptionalParameter = 1 << 17, + RestParameter = 1 << 18, + DeferredType = 1 << 19, + HasNeverType = 1 << 20, + Mapped = 1 << 21, + StripOptional = 1 << 22, + Unresolved = 1 << 23, + IsDiscriminantComputed = 1 << 24, + IsDiscriminant = 1 << 25, + IndexSymbol = 1 << 26, + Synthetic = SyntheticProperty | SyntheticMethod, + NonUniformAndLiteral = HasNonUniformType | HasLiteralType, + Partial = ReadPartial | WritePartial, +} diff --git a/packages/typescript/src/enums/checkFlags.ts b/packages/typescript/src/enums/checkFlags.ts index 8dfcbdb384974..4e5872757e6ec 100644 --- a/packages/typescript/src/enums/checkFlags.ts +++ b/packages/typescript/src/enums/checkFlags.ts @@ -1,35 +1,35 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/checkflags.go. DO NOT EDIT. -export var CheckFlags: any; -(function (CheckFlags) { - CheckFlags[CheckFlags["None"] = 0] = "None"; - CheckFlags[CheckFlags["Instantiated"] = 1] = "Instantiated"; - CheckFlags[CheckFlags["SyntheticProperty"] = 2] = "SyntheticProperty"; - CheckFlags[CheckFlags["SyntheticMethod"] = 4] = "SyntheticMethod"; - CheckFlags[CheckFlags["Readonly"] = 8] = "Readonly"; - CheckFlags[CheckFlags["ReadPartial"] = 16] = "ReadPartial"; - CheckFlags[CheckFlags["WritePartial"] = 32] = "WritePartial"; - CheckFlags[CheckFlags["HasNonUniformType"] = 64] = "HasNonUniformType"; - CheckFlags[CheckFlags["HasLiteralType"] = 128] = "HasLiteralType"; - CheckFlags[CheckFlags["ContainsPublic"] = 256] = "ContainsPublic"; - CheckFlags[CheckFlags["ContainsProtected"] = 512] = "ContainsProtected"; - CheckFlags[CheckFlags["ContainsPrivate"] = 1024] = "ContainsPrivate"; - CheckFlags[CheckFlags["ContainsWritePublic"] = 2048] = "ContainsWritePublic"; - CheckFlags[CheckFlags["ContainsWriteProtected"] = 4096] = "ContainsWriteProtected"; - CheckFlags[CheckFlags["ContainsWritePrivate"] = 8192] = "ContainsWritePrivate"; - CheckFlags[CheckFlags["ContainsStatic"] = 16384] = "ContainsStatic"; - CheckFlags[CheckFlags["Late"] = 32768] = "Late"; - CheckFlags[CheckFlags["ReverseMapped"] = 65536] = "ReverseMapped"; - CheckFlags[CheckFlags["OptionalParameter"] = 131072] = "OptionalParameter"; - CheckFlags[CheckFlags["RestParameter"] = 262144] = "RestParameter"; - CheckFlags[CheckFlags["DeferredType"] = 524288] = "DeferredType"; - CheckFlags[CheckFlags["HasNeverType"] = 1048576] = "HasNeverType"; - CheckFlags[CheckFlags["Mapped"] = 2097152] = "Mapped"; - CheckFlags[CheckFlags["StripOptional"] = 4194304] = "StripOptional"; - CheckFlags[CheckFlags["Unresolved"] = 8388608] = "Unresolved"; - CheckFlags[CheckFlags["IsDiscriminantComputed"] = 16777216] = "IsDiscriminantComputed"; - CheckFlags[CheckFlags["IsDiscriminant"] = 33554432] = "IsDiscriminant"; - CheckFlags[CheckFlags["IndexSymbol"] = 67108864] = "IndexSymbol"; - CheckFlags[CheckFlags["Synthetic"] = 6] = "Synthetic"; - CheckFlags[CheckFlags["NonUniformAndLiteral"] = 192] = "NonUniformAndLiteral"; - CheckFlags[CheckFlags["Partial"] = 48] = "Partial"; -})(CheckFlags || (CheckFlags = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/checkflags.go. DO NOT EDIT. +export var CheckFlags: any; +(function (CheckFlags) { + CheckFlags[CheckFlags["None"] = 0] = "None"; + CheckFlags[CheckFlags["Instantiated"] = 1] = "Instantiated"; + CheckFlags[CheckFlags["SyntheticProperty"] = 2] = "SyntheticProperty"; + CheckFlags[CheckFlags["SyntheticMethod"] = 4] = "SyntheticMethod"; + CheckFlags[CheckFlags["Readonly"] = 8] = "Readonly"; + CheckFlags[CheckFlags["ReadPartial"] = 16] = "ReadPartial"; + CheckFlags[CheckFlags["WritePartial"] = 32] = "WritePartial"; + CheckFlags[CheckFlags["HasNonUniformType"] = 64] = "HasNonUniformType"; + CheckFlags[CheckFlags["HasLiteralType"] = 128] = "HasLiteralType"; + CheckFlags[CheckFlags["ContainsPublic"] = 256] = "ContainsPublic"; + CheckFlags[CheckFlags["ContainsProtected"] = 512] = "ContainsProtected"; + CheckFlags[CheckFlags["ContainsPrivate"] = 1024] = "ContainsPrivate"; + CheckFlags[CheckFlags["ContainsWritePublic"] = 2048] = "ContainsWritePublic"; + CheckFlags[CheckFlags["ContainsWriteProtected"] = 4096] = "ContainsWriteProtected"; + CheckFlags[CheckFlags["ContainsWritePrivate"] = 8192] = "ContainsWritePrivate"; + CheckFlags[CheckFlags["ContainsStatic"] = 16384] = "ContainsStatic"; + CheckFlags[CheckFlags["Late"] = 32768] = "Late"; + CheckFlags[CheckFlags["ReverseMapped"] = 65536] = "ReverseMapped"; + CheckFlags[CheckFlags["OptionalParameter"] = 131072] = "OptionalParameter"; + CheckFlags[CheckFlags["RestParameter"] = 262144] = "RestParameter"; + CheckFlags[CheckFlags["DeferredType"] = 524288] = "DeferredType"; + CheckFlags[CheckFlags["HasNeverType"] = 1048576] = "HasNeverType"; + CheckFlags[CheckFlags["Mapped"] = 2097152] = "Mapped"; + CheckFlags[CheckFlags["StripOptional"] = 4194304] = "StripOptional"; + CheckFlags[CheckFlags["Unresolved"] = 8388608] = "Unresolved"; + CheckFlags[CheckFlags["IsDiscriminantComputed"] = 16777216] = "IsDiscriminantComputed"; + CheckFlags[CheckFlags["IsDiscriminant"] = 33554432] = "IsDiscriminant"; + CheckFlags[CheckFlags["IndexSymbol"] = 67108864] = "IndexSymbol"; + CheckFlags[CheckFlags["Synthetic"] = 6] = "Synthetic"; + CheckFlags[CheckFlags["NonUniformAndLiteral"] = 192] = "NonUniformAndLiteral"; + CheckFlags[CheckFlags["Partial"] = 48] = "Partial"; +})(CheckFlags || (CheckFlags = {})); diff --git a/packages/typescript/src/enums/commentDirectiveType.enum.ts b/packages/typescript/src/enums/commentDirectiveType.enum.ts index dc75790bcd812..9fa5012aac561 100644 --- a/packages/typescript/src/enums/commentDirectiveType.enum.ts +++ b/packages/typescript/src/enums/commentDirectiveType.enum.ts @@ -1,4 +1,4 @@ -export enum CommentDirectiveType { - ExpectError = 0, - Ignore = 1, -} +export enum CommentDirectiveType { + ExpectError = 0, + Ignore = 1, +} diff --git a/packages/typescript/src/enums/commentDirectiveType.ts b/packages/typescript/src/enums/commentDirectiveType.ts index 2c59ede17483c..e249db7b71fb1 100644 --- a/packages/typescript/src/enums/commentDirectiveType.ts +++ b/packages/typescript/src/enums/commentDirectiveType.ts @@ -1,5 +1,5 @@ -export var CommentDirectiveType: any; -(function (CommentDirectiveType) { - CommentDirectiveType[CommentDirectiveType["ExpectError"] = 0] = "ExpectError"; - CommentDirectiveType[CommentDirectiveType["Ignore"] = 1] = "Ignore"; -})(CommentDirectiveType || (CommentDirectiveType = {})); +export var CommentDirectiveType: any; +(function (CommentDirectiveType) { + CommentDirectiveType[CommentDirectiveType["ExpectError"] = 0] = "ExpectError"; + CommentDirectiveType[CommentDirectiveType["Ignore"] = 1] = "Ignore"; +})(CommentDirectiveType || (CommentDirectiveType = {})); diff --git a/packages/typescript/src/enums/completionItemKind.enum.ts b/packages/typescript/src/enums/completionItemKind.enum.ts index 3118b0d02e4be..62cc74a51f34d 100644 --- a/packages/typescript/src/enums/completionItemKind.enum.ts +++ b/packages/typescript/src/enums/completionItemKind.enum.ts @@ -1,29 +1,29 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/lsp/lsproto/lsp_generated.go. DO NOT EDIT. - -export enum CompletionItemKind { - Text = 1, - Method = 2, - Function = 3, - Constructor = 4, - Field = 5, - Variable = 6, - Class = 7, - Interface = 8, - Module = 9, - Property = 10, - Unit = 11, - Value = 12, - Enum = 13, - Keyword = 14, - Snippet = 15, - Color = 16, - File = 17, - Reference = 18, - Folder = 19, - EnumMember = 20, - Constant = 21, - Struct = 22, - Event = 23, - Operator = 24, - TypeParameter = 25, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/lsp/lsproto/lsp_generated.go. DO NOT EDIT. + +export enum CompletionItemKind { + Text = 1, + Method = 2, + Function = 3, + Constructor = 4, + Field = 5, + Variable = 6, + Class = 7, + Interface = 8, + Module = 9, + Property = 10, + Unit = 11, + Value = 12, + Enum = 13, + Keyword = 14, + Snippet = 15, + Color = 16, + File = 17, + Reference = 18, + Folder = 19, + EnumMember = 20, + Constant = 21, + Struct = 22, + Event = 23, + Operator = 24, + TypeParameter = 25, +} diff --git a/packages/typescript/src/enums/completionItemKind.ts b/packages/typescript/src/enums/completionItemKind.ts index 3c946ea6af203..04360559a89e0 100644 --- a/packages/typescript/src/enums/completionItemKind.ts +++ b/packages/typescript/src/enums/completionItemKind.ts @@ -1,29 +1,29 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/lsp/lsproto/lsp_generated.go. DO NOT EDIT. -export var CompletionItemKind: any; -(function (CompletionItemKind) { - CompletionItemKind[CompletionItemKind["Text"] = 1] = "Text"; - CompletionItemKind[CompletionItemKind["Method"] = 2] = "Method"; - CompletionItemKind[CompletionItemKind["Function"] = 3] = "Function"; - CompletionItemKind[CompletionItemKind["Constructor"] = 4] = "Constructor"; - CompletionItemKind[CompletionItemKind["Field"] = 5] = "Field"; - CompletionItemKind[CompletionItemKind["Variable"] = 6] = "Variable"; - CompletionItemKind[CompletionItemKind["Class"] = 7] = "Class"; - CompletionItemKind[CompletionItemKind["Interface"] = 8] = "Interface"; - CompletionItemKind[CompletionItemKind["Module"] = 9] = "Module"; - CompletionItemKind[CompletionItemKind["Property"] = 10] = "Property"; - CompletionItemKind[CompletionItemKind["Unit"] = 11] = "Unit"; - CompletionItemKind[CompletionItemKind["Value"] = 12] = "Value"; - CompletionItemKind[CompletionItemKind["Enum"] = 13] = "Enum"; - CompletionItemKind[CompletionItemKind["Keyword"] = 14] = "Keyword"; - CompletionItemKind[CompletionItemKind["Snippet"] = 15] = "Snippet"; - CompletionItemKind[CompletionItemKind["Color"] = 16] = "Color"; - CompletionItemKind[CompletionItemKind["File"] = 17] = "File"; - CompletionItemKind[CompletionItemKind["Reference"] = 18] = "Reference"; - CompletionItemKind[CompletionItemKind["Folder"] = 19] = "Folder"; - CompletionItemKind[CompletionItemKind["EnumMember"] = 20] = "EnumMember"; - CompletionItemKind[CompletionItemKind["Constant"] = 21] = "Constant"; - CompletionItemKind[CompletionItemKind["Struct"] = 22] = "Struct"; - CompletionItemKind[CompletionItemKind["Event"] = 23] = "Event"; - CompletionItemKind[CompletionItemKind["Operator"] = 24] = "Operator"; - CompletionItemKind[CompletionItemKind["TypeParameter"] = 25] = "TypeParameter"; -})(CompletionItemKind || (CompletionItemKind = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/lsp/lsproto/lsp_generated.go. DO NOT EDIT. +export var CompletionItemKind: any; +(function (CompletionItemKind) { + CompletionItemKind[CompletionItemKind["Text"] = 1] = "Text"; + CompletionItemKind[CompletionItemKind["Method"] = 2] = "Method"; + CompletionItemKind[CompletionItemKind["Function"] = 3] = "Function"; + CompletionItemKind[CompletionItemKind["Constructor"] = 4] = "Constructor"; + CompletionItemKind[CompletionItemKind["Field"] = 5] = "Field"; + CompletionItemKind[CompletionItemKind["Variable"] = 6] = "Variable"; + CompletionItemKind[CompletionItemKind["Class"] = 7] = "Class"; + CompletionItemKind[CompletionItemKind["Interface"] = 8] = "Interface"; + CompletionItemKind[CompletionItemKind["Module"] = 9] = "Module"; + CompletionItemKind[CompletionItemKind["Property"] = 10] = "Property"; + CompletionItemKind[CompletionItemKind["Unit"] = 11] = "Unit"; + CompletionItemKind[CompletionItemKind["Value"] = 12] = "Value"; + CompletionItemKind[CompletionItemKind["Enum"] = 13] = "Enum"; + CompletionItemKind[CompletionItemKind["Keyword"] = 14] = "Keyword"; + CompletionItemKind[CompletionItemKind["Snippet"] = 15] = "Snippet"; + CompletionItemKind[CompletionItemKind["Color"] = 16] = "Color"; + CompletionItemKind[CompletionItemKind["File"] = 17] = "File"; + CompletionItemKind[CompletionItemKind["Reference"] = 18] = "Reference"; + CompletionItemKind[CompletionItemKind["Folder"] = 19] = "Folder"; + CompletionItemKind[CompletionItemKind["EnumMember"] = 20] = "EnumMember"; + CompletionItemKind[CompletionItemKind["Constant"] = 21] = "Constant"; + CompletionItemKind[CompletionItemKind["Struct"] = 22] = "Struct"; + CompletionItemKind[CompletionItemKind["Event"] = 23] = "Event"; + CompletionItemKind[CompletionItemKind["Operator"] = 24] = "Operator"; + CompletionItemKind[CompletionItemKind["TypeParameter"] = 25] = "TypeParameter"; +})(CompletionItemKind || (CompletionItemKind = {})); diff --git a/packages/typescript/src/enums/diagnosticCategory.enum.ts b/packages/typescript/src/enums/diagnosticCategory.enum.ts index 1014d1e6a4698..14917c47088f5 100644 --- a/packages/typescript/src/enums/diagnosticCategory.enum.ts +++ b/packages/typescript/src/enums/diagnosticCategory.enum.ts @@ -1,8 +1,8 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/diagnostics/diagnostics.go. DO NOT EDIT. - -export enum DiagnosticCategory { - Warning = 0, - Error = 1, - Suggestion = 2, - Message = 3, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/diagnostics/diagnostics.go. DO NOT EDIT. + +export enum DiagnosticCategory { + Warning = 0, + Error = 1, + Suggestion = 2, + Message = 3, +} diff --git a/packages/typescript/src/enums/diagnosticCategory.ts b/packages/typescript/src/enums/diagnosticCategory.ts index 75c588c41918a..cc58925125e6d 100644 --- a/packages/typescript/src/enums/diagnosticCategory.ts +++ b/packages/typescript/src/enums/diagnosticCategory.ts @@ -1,8 +1,8 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/diagnostics/diagnostics.go. DO NOT EDIT. -export var DiagnosticCategory: any; -(function (DiagnosticCategory) { - DiagnosticCategory[DiagnosticCategory["Warning"] = 0] = "Warning"; - DiagnosticCategory[DiagnosticCategory["Error"] = 1] = "Error"; - DiagnosticCategory[DiagnosticCategory["Suggestion"] = 2] = "Suggestion"; - DiagnosticCategory[DiagnosticCategory["Message"] = 3] = "Message"; -})(DiagnosticCategory || (DiagnosticCategory = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/diagnostics/diagnostics.go. DO NOT EDIT. +export var DiagnosticCategory: any; +(function (DiagnosticCategory) { + DiagnosticCategory[DiagnosticCategory["Warning"] = 0] = "Warning"; + DiagnosticCategory[DiagnosticCategory["Error"] = 1] = "Error"; + DiagnosticCategory[DiagnosticCategory["Suggestion"] = 2] = "Suggestion"; + DiagnosticCategory[DiagnosticCategory["Message"] = 3] = "Message"; +})(DiagnosticCategory || (DiagnosticCategory = {})); diff --git a/packages/typescript/src/enums/diagnosticDirectivePolicy.enum.ts b/packages/typescript/src/enums/diagnosticDirectivePolicy.enum.ts index 74ea6f1dcf25d..e131f13535980 100644 --- a/packages/typescript/src/enums/diagnosticDirectivePolicy.enum.ts +++ b/packages/typescript/src/enums/diagnosticDirectivePolicy.enum.ts @@ -1,6 +1,6 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/ast.go. DO NOT EDIT. - -export enum DiagnosticDirectivePolicy { - Ignore = 0, - Expect = 1, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/ast.go. DO NOT EDIT. + +export enum DiagnosticDirectivePolicy { + Ignore = 0, + Expect = 1, +} diff --git a/packages/typescript/src/enums/diagnosticDirectivePolicy.ts b/packages/typescript/src/enums/diagnosticDirectivePolicy.ts index 46d7a7c539d1d..2340c51fa9b8c 100644 --- a/packages/typescript/src/enums/diagnosticDirectivePolicy.ts +++ b/packages/typescript/src/enums/diagnosticDirectivePolicy.ts @@ -1,6 +1,6 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/ast.go. DO NOT EDIT. -export var DiagnosticDirectivePolicy: any; -(function (DiagnosticDirectivePolicy) { - DiagnosticDirectivePolicy[DiagnosticDirectivePolicy["Ignore"] = 0] = "Ignore"; - DiagnosticDirectivePolicy[DiagnosticDirectivePolicy["Expect"] = 1] = "Expect"; -})(DiagnosticDirectivePolicy || (DiagnosticDirectivePolicy = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/ast.go. DO NOT EDIT. +export var DiagnosticDirectivePolicy: any; +(function (DiagnosticDirectivePolicy) { + DiagnosticDirectivePolicy[DiagnosticDirectivePolicy["Ignore"] = 0] = "Ignore"; + DiagnosticDirectivePolicy[DiagnosticDirectivePolicy["Expect"] = 1] = "Expect"; +})(DiagnosticDirectivePolicy || (DiagnosticDirectivePolicy = {})); diff --git a/packages/typescript/src/enums/elementFlags.enum.ts b/packages/typescript/src/enums/elementFlags.enum.ts index 5905d808a2e85..82343b1e3adbe 100644 --- a/packages/typescript/src/enums/elementFlags.enum.ts +++ b/packages/typescript/src/enums/elementFlags.enum.ts @@ -1,13 +1,13 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. - -export enum ElementFlags { - None = 0, - Required = 1 << 0, - Optional = 1 << 1, - Rest = 1 << 2, - Variadic = 1 << 3, - Fixed = Required | Optional, - Variable = Rest | Variadic, - NonRequired = Optional | Rest | Variadic, - NonRest = Required | Optional | Variadic, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. + +export enum ElementFlags { + None = 0, + Required = 1 << 0, + Optional = 1 << 1, + Rest = 1 << 2, + Variadic = 1 << 3, + Fixed = Required | Optional, + Variable = Rest | Variadic, + NonRequired = Optional | Rest | Variadic, + NonRest = Required | Optional | Variadic, +} diff --git a/packages/typescript/src/enums/elementFlags.ts b/packages/typescript/src/enums/elementFlags.ts index fa0ff45cd2380..f5c1573500ef9 100644 --- a/packages/typescript/src/enums/elementFlags.ts +++ b/packages/typescript/src/enums/elementFlags.ts @@ -1,13 +1,13 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. -export var ElementFlags: any; -(function (ElementFlags) { - ElementFlags[ElementFlags["None"] = 0] = "None"; - ElementFlags[ElementFlags["Required"] = 1] = "Required"; - ElementFlags[ElementFlags["Optional"] = 2] = "Optional"; - ElementFlags[ElementFlags["Rest"] = 4] = "Rest"; - ElementFlags[ElementFlags["Variadic"] = 8] = "Variadic"; - ElementFlags[ElementFlags["Fixed"] = 3] = "Fixed"; - ElementFlags[ElementFlags["Variable"] = 12] = "Variable"; - ElementFlags[ElementFlags["NonRequired"] = 14] = "NonRequired"; - ElementFlags[ElementFlags["NonRest"] = 11] = "NonRest"; -})(ElementFlags || (ElementFlags = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. +export var ElementFlags: any; +(function (ElementFlags) { + ElementFlags[ElementFlags["None"] = 0] = "None"; + ElementFlags[ElementFlags["Required"] = 1] = "Required"; + ElementFlags[ElementFlags["Optional"] = 2] = "Optional"; + ElementFlags[ElementFlags["Rest"] = 4] = "Rest"; + ElementFlags[ElementFlags["Variadic"] = 8] = "Variadic"; + ElementFlags[ElementFlags["Fixed"] = 3] = "Fixed"; + ElementFlags[ElementFlags["Variable"] = 12] = "Variable"; + ElementFlags[ElementFlags["NonRequired"] = 14] = "NonRequired"; + ElementFlags[ElementFlags["NonRest"] = 11] = "NonRest"; +})(ElementFlags || (ElementFlags = {})); diff --git a/packages/typescript/src/enums/emitOnly.enum.ts b/packages/typescript/src/enums/emitOnly.enum.ts index 4d29dd3c0849f..5ab104bde57be 100644 --- a/packages/typescript/src/enums/emitOnly.enum.ts +++ b/packages/typescript/src/enums/emitOnly.enum.ts @@ -1,7 +1,7 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/compiler/emitter.go. DO NOT EDIT. - -export enum EmitOnly { - All = 0, - OnlyJs = 1, - OnlyDts = 2, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/compiler/emitter.go. DO NOT EDIT. + +export enum EmitOnly { + All = 0, + OnlyJs = 1, + OnlyDts = 2, +} diff --git a/packages/typescript/src/enums/emitOnly.ts b/packages/typescript/src/enums/emitOnly.ts index 44ee14f68b248..f903c6ff40c85 100644 --- a/packages/typescript/src/enums/emitOnly.ts +++ b/packages/typescript/src/enums/emitOnly.ts @@ -1,7 +1,7 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/compiler/emitter.go. DO NOT EDIT. -export var EmitOnly: any; -(function (EmitOnly) { - EmitOnly[EmitOnly["All"] = 0] = "All"; - EmitOnly[EmitOnly["OnlyJs"] = 1] = "OnlyJs"; - EmitOnly[EmitOnly["OnlyDts"] = 2] = "OnlyDts"; -})(EmitOnly || (EmitOnly = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/compiler/emitter.go. DO NOT EDIT. +export var EmitOnly: any; +(function (EmitOnly) { + EmitOnly[EmitOnly["All"] = 0] = "All"; + EmitOnly[EmitOnly["OnlyJs"] = 1] = "OnlyJs"; + EmitOnly[EmitOnly["OnlyDts"] = 2] = "OnlyDts"; +})(EmitOnly || (EmitOnly = {})); diff --git a/packages/typescript/src/enums/internalSymbolName.enum.ts b/packages/typescript/src/enums/internalSymbolName.enum.ts index 1660591cc69ef..2a70fe813cd83 100644 --- a/packages/typescript/src/enums/internalSymbolName.enum.ts +++ b/packages/typescript/src/enums/internalSymbolName.enum.ts @@ -1,24 +1,24 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/symbol.go. DO NOT EDIT. - -export enum InternalSymbolName { - Call = "__call", - Constructor = "__constructor", - New = "__new", - Index = "__index", - ExportStar = "__export", - Global = "__global", - Missing = "__missing", - Type = "__type", - Object = "__object", - JSXAttributes = "__jsxAttributes", - Class = "__class", - Function = "__function", - Computed = "__computed", - AssignmentDeclaration = "__assignment", - InstantiationExpression = "__instantiationExpression", - ImportAttributes = "__importAttributes", - ExportEquals = "export=", - Default = "default", - This = "this", - ModuleExports = "module.exports", -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/symbol.go. DO NOT EDIT. + +export enum InternalSymbolName { + Call = "__call", + Constructor = "__constructor", + New = "__new", + Index = "__index", + ExportStar = "__export", + Global = "__global", + Missing = "__missing", + Type = "__type", + Object = "__object", + JSXAttributes = "__jsxAttributes", + Class = "__class", + Function = "__function", + Computed = "__computed", + AssignmentDeclaration = "__assignment", + InstantiationExpression = "__instantiationExpression", + ImportAttributes = "__importAttributes", + ExportEquals = "export=", + Default = "default", + This = "this", + ModuleExports = "module.exports", +} diff --git a/packages/typescript/src/enums/internalSymbolName.ts b/packages/typescript/src/enums/internalSymbolName.ts index 1352b8690bd95..a3b87c2fe7704 100644 --- a/packages/typescript/src/enums/internalSymbolName.ts +++ b/packages/typescript/src/enums/internalSymbolName.ts @@ -1,24 +1,24 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/symbol.go. DO NOT EDIT. -export var InternalSymbolName: any; -(function (InternalSymbolName) { - InternalSymbolName["Call"] = "__call"; - InternalSymbolName["Constructor"] = "__constructor"; - InternalSymbolName["New"] = "__new"; - InternalSymbolName["Index"] = "__index"; - InternalSymbolName["ExportStar"] = "__export"; - InternalSymbolName["Global"] = "__global"; - InternalSymbolName["Missing"] = "__missing"; - InternalSymbolName["Type"] = "__type"; - InternalSymbolName["Object"] = "__object"; - InternalSymbolName["JSXAttributes"] = "__jsxAttributes"; - InternalSymbolName["Class"] = "__class"; - InternalSymbolName["Function"] = "__function"; - InternalSymbolName["Computed"] = "__computed"; - InternalSymbolName["AssignmentDeclaration"] = "__assignment"; - InternalSymbolName["InstantiationExpression"] = "__instantiationExpression"; - InternalSymbolName["ImportAttributes"] = "__importAttributes"; - InternalSymbolName["ExportEquals"] = "export="; - InternalSymbolName["Default"] = "default"; - InternalSymbolName["This"] = "this"; - InternalSymbolName["ModuleExports"] = "module.exports"; -})(InternalSymbolName || (InternalSymbolName = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/symbol.go. DO NOT EDIT. +export var InternalSymbolName: any; +(function (InternalSymbolName) { + InternalSymbolName["Call"] = "__call"; + InternalSymbolName["Constructor"] = "__constructor"; + InternalSymbolName["New"] = "__new"; + InternalSymbolName["Index"] = "__index"; + InternalSymbolName["ExportStar"] = "__export"; + InternalSymbolName["Global"] = "__global"; + InternalSymbolName["Missing"] = "__missing"; + InternalSymbolName["Type"] = "__type"; + InternalSymbolName["Object"] = "__object"; + InternalSymbolName["JSXAttributes"] = "__jsxAttributes"; + InternalSymbolName["Class"] = "__class"; + InternalSymbolName["Function"] = "__function"; + InternalSymbolName["Computed"] = "__computed"; + InternalSymbolName["AssignmentDeclaration"] = "__assignment"; + InternalSymbolName["InstantiationExpression"] = "__instantiationExpression"; + InternalSymbolName["ImportAttributes"] = "__importAttributes"; + InternalSymbolName["ExportEquals"] = "export="; + InternalSymbolName["Default"] = "default"; + InternalSymbolName["This"] = "this"; + InternalSymbolName["ModuleExports"] = "module.exports"; +})(InternalSymbolName || (InternalSymbolName = {})); diff --git a/packages/typescript/src/enums/jsxEmit.enum.ts b/packages/typescript/src/enums/jsxEmit.enum.ts index 7270c60ee0611..4a3b46cf0671c 100644 --- a/packages/typescript/src/enums/jsxEmit.enum.ts +++ b/packages/typescript/src/enums/jsxEmit.enum.ts @@ -1,10 +1,10 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/core/compileroptions.go. DO NOT EDIT. - -export enum JsxEmit { - None = 0, - Preserve = 1, - ReactNative = 2, - React = 3, - ReactJSX = 4, - ReactJSXDev = 5, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/core/compileroptions.go. DO NOT EDIT. + +export enum JsxEmit { + None = 0, + Preserve = 1, + ReactNative = 2, + React = 3, + ReactJSX = 4, + ReactJSXDev = 5, +} diff --git a/packages/typescript/src/enums/jsxEmit.ts b/packages/typescript/src/enums/jsxEmit.ts index 2d82471d099ad..f6e457285d8c9 100644 --- a/packages/typescript/src/enums/jsxEmit.ts +++ b/packages/typescript/src/enums/jsxEmit.ts @@ -1,10 +1,10 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/core/compileroptions.go. DO NOT EDIT. -export var JsxEmit: any; -(function (JsxEmit) { - JsxEmit[JsxEmit["None"] = 0] = "None"; - JsxEmit[JsxEmit["Preserve"] = 1] = "Preserve"; - JsxEmit[JsxEmit["ReactNative"] = 2] = "ReactNative"; - JsxEmit[JsxEmit["React"] = 3] = "React"; - JsxEmit[JsxEmit["ReactJSX"] = 4] = "ReactJSX"; - JsxEmit[JsxEmit["ReactJSXDev"] = 5] = "ReactJSXDev"; -})(JsxEmit || (JsxEmit = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/core/compileroptions.go. DO NOT EDIT. +export var JsxEmit: any; +(function (JsxEmit) { + JsxEmit[JsxEmit["None"] = 0] = "None"; + JsxEmit[JsxEmit["Preserve"] = 1] = "Preserve"; + JsxEmit[JsxEmit["ReactNative"] = 2] = "ReactNative"; + JsxEmit[JsxEmit["React"] = 3] = "React"; + JsxEmit[JsxEmit["ReactJSX"] = 4] = "ReactJSX"; + JsxEmit[JsxEmit["ReactJSXDev"] = 5] = "ReactJSXDev"; +})(JsxEmit || (JsxEmit = {})); diff --git a/packages/typescript/src/enums/languageVariant.enum.ts b/packages/typescript/src/enums/languageVariant.enum.ts index c471ab69ee7a5..d93d5658f20c2 100644 --- a/packages/typescript/src/enums/languageVariant.enum.ts +++ b/packages/typescript/src/enums/languageVariant.enum.ts @@ -1,4 +1,4 @@ -export enum LanguageVariant { - Standard = 0, - JSX = 1, -} +export enum LanguageVariant { + Standard = 0, + JSX = 1, +} diff --git a/packages/typescript/src/enums/languageVariant.ts b/packages/typescript/src/enums/languageVariant.ts index 831ca135c8f4d..3d736070f876b 100644 --- a/packages/typescript/src/enums/languageVariant.ts +++ b/packages/typescript/src/enums/languageVariant.ts @@ -1,5 +1,5 @@ -export var LanguageVariant: any; -(function (LanguageVariant) { - LanguageVariant[LanguageVariant["Standard"] = 0] = "Standard"; - LanguageVariant[LanguageVariant["JSX"] = 1] = "JSX"; -})(LanguageVariant || (LanguageVariant = {})); +export var LanguageVariant: any; +(function (LanguageVariant) { + LanguageVariant[LanguageVariant["Standard"] = 0] = "Standard"; + LanguageVariant[LanguageVariant["JSX"] = 1] = "JSX"; +})(LanguageVariant || (LanguageVariant = {})); diff --git a/packages/typescript/src/enums/modifierFlags.enum.ts b/packages/typescript/src/enums/modifierFlags.enum.ts index 3d7923ff86d4d..06c2465c6d037 100644 --- a/packages/typescript/src/enums/modifierFlags.enum.ts +++ b/packages/typescript/src/enums/modifierFlags.enum.ts @@ -1,43 +1,43 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/modifierflags.go. DO NOT EDIT. - -export enum ModifierFlags { - None = 0, - Public = 1 << 0, - Private = 1 << 1, - Protected = 1 << 2, - Readonly = 1 << 3, - Override = 1 << 4, - Export = 1 << 5, - Abstract = 1 << 6, - Ambient = 1 << 7, - Static = 1 << 8, - Accessor = 1 << 9, - Async = 1 << 10, - Default = 1 << 11, - Const = 1 << 12, - In = 1 << 13, - Out = 1 << 14, - Decorator = 1 << 15, - Deprecated = 1 << 16, - JSDocPublic = 1 << 23, - JSDocPrivate = 1 << 24, - JSDocProtected = 1 << 25, - JSDocReadonly = 1 << 26, - JSDocOverride = 1 << 27, - HasComputedJSDocModifiers = 1 << 28, - HasComputedFlags = 1 << 29, - SyntacticOrJSDocModifiers = Public | Private | Protected | Readonly | Override, - SyntacticOnlyModifiers = Export | Ambient | Abstract | Static | Accessor | Async | Default | Const | In | Out | Decorator, - SyntacticModifiers = SyntacticOrJSDocModifiers | SyntacticOnlyModifiers, - JSDocCacheOnlyModifiers = JSDocPublic | JSDocPrivate | JSDocProtected | JSDocReadonly | JSDocOverride, - JSDocOnlyModifiers = Deprecated, - NonCacheOnlyModifiers = SyntacticOrJSDocModifiers | SyntacticOnlyModifiers | JSDocOnlyModifiers, - AccessibilityModifier = Public | Private | Protected, - ParameterPropertyModifier = AccessibilityModifier | Readonly | Override, - NonPublicAccessibilityModifier = Private | Protected, - TypeScriptModifier = Ambient | Public | Private | Protected | Readonly | Abstract | Const | Override | In | Out, - ExportDefault = Export | Default, - All = Export | Ambient | Public | Private | Protected | Static | Readonly | Abstract | Accessor | Async | Default | Const | Deprecated | Override | In | Out | Decorator, - Modifier = All & ~Decorator, - JavaScript = Export | Static | Accessor | Async | Default, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/modifierflags.go. DO NOT EDIT. + +export enum ModifierFlags { + None = 0, + Public = 1 << 0, + Private = 1 << 1, + Protected = 1 << 2, + Readonly = 1 << 3, + Override = 1 << 4, + Export = 1 << 5, + Abstract = 1 << 6, + Ambient = 1 << 7, + Static = 1 << 8, + Accessor = 1 << 9, + Async = 1 << 10, + Default = 1 << 11, + Const = 1 << 12, + In = 1 << 13, + Out = 1 << 14, + Decorator = 1 << 15, + Deprecated = 1 << 16, + JSDocPublic = 1 << 23, + JSDocPrivate = 1 << 24, + JSDocProtected = 1 << 25, + JSDocReadonly = 1 << 26, + JSDocOverride = 1 << 27, + HasComputedJSDocModifiers = 1 << 28, + HasComputedFlags = 1 << 29, + SyntacticOrJSDocModifiers = Public | Private | Protected | Readonly | Override, + SyntacticOnlyModifiers = Export | Ambient | Abstract | Static | Accessor | Async | Default | Const | In | Out | Decorator, + SyntacticModifiers = SyntacticOrJSDocModifiers | SyntacticOnlyModifiers, + JSDocCacheOnlyModifiers = JSDocPublic | JSDocPrivate | JSDocProtected | JSDocReadonly | JSDocOverride, + JSDocOnlyModifiers = Deprecated, + NonCacheOnlyModifiers = SyntacticOrJSDocModifiers | SyntacticOnlyModifiers | JSDocOnlyModifiers, + AccessibilityModifier = Public | Private | Protected, + ParameterPropertyModifier = AccessibilityModifier | Readonly | Override, + NonPublicAccessibilityModifier = Private | Protected, + TypeScriptModifier = Ambient | Public | Private | Protected | Readonly | Abstract | Const | Override | In | Out, + ExportDefault = Export | Default, + All = Export | Ambient | Public | Private | Protected | Static | Readonly | Abstract | Accessor | Async | Default | Const | Deprecated | Override | In | Out | Decorator, + Modifier = All & ~Decorator, + JavaScript = Export | Static | Accessor | Async | Default, +} diff --git a/packages/typescript/src/enums/modifierFlags.ts b/packages/typescript/src/enums/modifierFlags.ts index 43ec0b39076fc..c6c64750a0f46 100644 --- a/packages/typescript/src/enums/modifierFlags.ts +++ b/packages/typescript/src/enums/modifierFlags.ts @@ -1,43 +1,43 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/modifierflags.go. DO NOT EDIT. -export var ModifierFlags: any; -(function (ModifierFlags) { - ModifierFlags[ModifierFlags["None"] = 0] = "None"; - ModifierFlags[ModifierFlags["Public"] = 1] = "Public"; - ModifierFlags[ModifierFlags["Private"] = 2] = "Private"; - ModifierFlags[ModifierFlags["Protected"] = 4] = "Protected"; - ModifierFlags[ModifierFlags["Readonly"] = 8] = "Readonly"; - ModifierFlags[ModifierFlags["Override"] = 16] = "Override"; - ModifierFlags[ModifierFlags["Export"] = 32] = "Export"; - ModifierFlags[ModifierFlags["Abstract"] = 64] = "Abstract"; - ModifierFlags[ModifierFlags["Ambient"] = 128] = "Ambient"; - ModifierFlags[ModifierFlags["Static"] = 256] = "Static"; - ModifierFlags[ModifierFlags["Accessor"] = 512] = "Accessor"; - ModifierFlags[ModifierFlags["Async"] = 1024] = "Async"; - ModifierFlags[ModifierFlags["Default"] = 2048] = "Default"; - ModifierFlags[ModifierFlags["Const"] = 4096] = "Const"; - ModifierFlags[ModifierFlags["In"] = 8192] = "In"; - ModifierFlags[ModifierFlags["Out"] = 16384] = "Out"; - ModifierFlags[ModifierFlags["Decorator"] = 32768] = "Decorator"; - ModifierFlags[ModifierFlags["Deprecated"] = 65536] = "Deprecated"; - ModifierFlags[ModifierFlags["JSDocPublic"] = 8388608] = "JSDocPublic"; - ModifierFlags[ModifierFlags["JSDocPrivate"] = 16777216] = "JSDocPrivate"; - ModifierFlags[ModifierFlags["JSDocProtected"] = 33554432] = "JSDocProtected"; - ModifierFlags[ModifierFlags["JSDocReadonly"] = 67108864] = "JSDocReadonly"; - ModifierFlags[ModifierFlags["JSDocOverride"] = 134217728] = "JSDocOverride"; - ModifierFlags[ModifierFlags["HasComputedJSDocModifiers"] = 268435456] = "HasComputedJSDocModifiers"; - ModifierFlags[ModifierFlags["HasComputedFlags"] = 536870912] = "HasComputedFlags"; - ModifierFlags[ModifierFlags["SyntacticOrJSDocModifiers"] = 31] = "SyntacticOrJSDocModifiers"; - ModifierFlags[ModifierFlags["SyntacticOnlyModifiers"] = 65504] = "SyntacticOnlyModifiers"; - ModifierFlags[ModifierFlags["SyntacticModifiers"] = 65535] = "SyntacticModifiers"; - ModifierFlags[ModifierFlags["JSDocCacheOnlyModifiers"] = 260046848] = "JSDocCacheOnlyModifiers"; - ModifierFlags[ModifierFlags["JSDocOnlyModifiers"] = 65536] = "JSDocOnlyModifiers"; - ModifierFlags[ModifierFlags["NonCacheOnlyModifiers"] = 131071] = "NonCacheOnlyModifiers"; - ModifierFlags[ModifierFlags["AccessibilityModifier"] = 7] = "AccessibilityModifier"; - ModifierFlags[ModifierFlags["ParameterPropertyModifier"] = 31] = "ParameterPropertyModifier"; - ModifierFlags[ModifierFlags["NonPublicAccessibilityModifier"] = 6] = "NonPublicAccessibilityModifier"; - ModifierFlags[ModifierFlags["TypeScriptModifier"] = 28895] = "TypeScriptModifier"; - ModifierFlags[ModifierFlags["ExportDefault"] = 2080] = "ExportDefault"; - ModifierFlags[ModifierFlags["All"] = 131071] = "All"; - ModifierFlags[ModifierFlags["Modifier"] = 98303] = "Modifier"; - ModifierFlags[ModifierFlags["JavaScript"] = 3872] = "JavaScript"; -})(ModifierFlags || (ModifierFlags = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/modifierflags.go. DO NOT EDIT. +export var ModifierFlags: any; +(function (ModifierFlags) { + ModifierFlags[ModifierFlags["None"] = 0] = "None"; + ModifierFlags[ModifierFlags["Public"] = 1] = "Public"; + ModifierFlags[ModifierFlags["Private"] = 2] = "Private"; + ModifierFlags[ModifierFlags["Protected"] = 4] = "Protected"; + ModifierFlags[ModifierFlags["Readonly"] = 8] = "Readonly"; + ModifierFlags[ModifierFlags["Override"] = 16] = "Override"; + ModifierFlags[ModifierFlags["Export"] = 32] = "Export"; + ModifierFlags[ModifierFlags["Abstract"] = 64] = "Abstract"; + ModifierFlags[ModifierFlags["Ambient"] = 128] = "Ambient"; + ModifierFlags[ModifierFlags["Static"] = 256] = "Static"; + ModifierFlags[ModifierFlags["Accessor"] = 512] = "Accessor"; + ModifierFlags[ModifierFlags["Async"] = 1024] = "Async"; + ModifierFlags[ModifierFlags["Default"] = 2048] = "Default"; + ModifierFlags[ModifierFlags["Const"] = 4096] = "Const"; + ModifierFlags[ModifierFlags["In"] = 8192] = "In"; + ModifierFlags[ModifierFlags["Out"] = 16384] = "Out"; + ModifierFlags[ModifierFlags["Decorator"] = 32768] = "Decorator"; + ModifierFlags[ModifierFlags["Deprecated"] = 65536] = "Deprecated"; + ModifierFlags[ModifierFlags["JSDocPublic"] = 8388608] = "JSDocPublic"; + ModifierFlags[ModifierFlags["JSDocPrivate"] = 16777216] = "JSDocPrivate"; + ModifierFlags[ModifierFlags["JSDocProtected"] = 33554432] = "JSDocProtected"; + ModifierFlags[ModifierFlags["JSDocReadonly"] = 67108864] = "JSDocReadonly"; + ModifierFlags[ModifierFlags["JSDocOverride"] = 134217728] = "JSDocOverride"; + ModifierFlags[ModifierFlags["HasComputedJSDocModifiers"] = 268435456] = "HasComputedJSDocModifiers"; + ModifierFlags[ModifierFlags["HasComputedFlags"] = 536870912] = "HasComputedFlags"; + ModifierFlags[ModifierFlags["SyntacticOrJSDocModifiers"] = 31] = "SyntacticOrJSDocModifiers"; + ModifierFlags[ModifierFlags["SyntacticOnlyModifiers"] = 65504] = "SyntacticOnlyModifiers"; + ModifierFlags[ModifierFlags["SyntacticModifiers"] = 65535] = "SyntacticModifiers"; + ModifierFlags[ModifierFlags["JSDocCacheOnlyModifiers"] = 260046848] = "JSDocCacheOnlyModifiers"; + ModifierFlags[ModifierFlags["JSDocOnlyModifiers"] = 65536] = "JSDocOnlyModifiers"; + ModifierFlags[ModifierFlags["NonCacheOnlyModifiers"] = 131071] = "NonCacheOnlyModifiers"; + ModifierFlags[ModifierFlags["AccessibilityModifier"] = 7] = "AccessibilityModifier"; + ModifierFlags[ModifierFlags["ParameterPropertyModifier"] = 31] = "ParameterPropertyModifier"; + ModifierFlags[ModifierFlags["NonPublicAccessibilityModifier"] = 6] = "NonPublicAccessibilityModifier"; + ModifierFlags[ModifierFlags["TypeScriptModifier"] = 28895] = "TypeScriptModifier"; + ModifierFlags[ModifierFlags["ExportDefault"] = 2080] = "ExportDefault"; + ModifierFlags[ModifierFlags["All"] = 131071] = "All"; + ModifierFlags[ModifierFlags["Modifier"] = 98303] = "Modifier"; + ModifierFlags[ModifierFlags["JavaScript"] = 3872] = "JavaScript"; +})(ModifierFlags || (ModifierFlags = {})); diff --git a/packages/typescript/src/enums/moduleDetectionKind.enum.ts b/packages/typescript/src/enums/moduleDetectionKind.enum.ts index 3550ff58ad43d..41fce6cbccce3 100644 --- a/packages/typescript/src/enums/moduleDetectionKind.enum.ts +++ b/packages/typescript/src/enums/moduleDetectionKind.enum.ts @@ -1,8 +1,8 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/core/compileroptions.go. DO NOT EDIT. - -export enum ModuleDetectionKind { - None = 0, - Auto = 1, - Legacy = 2, - Force = 3, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/core/compileroptions.go. DO NOT EDIT. + +export enum ModuleDetectionKind { + None = 0, + Auto = 1, + Legacy = 2, + Force = 3, +} diff --git a/packages/typescript/src/enums/moduleDetectionKind.ts b/packages/typescript/src/enums/moduleDetectionKind.ts index 4f9e7ed54a7c4..43b564e4423ff 100644 --- a/packages/typescript/src/enums/moduleDetectionKind.ts +++ b/packages/typescript/src/enums/moduleDetectionKind.ts @@ -1,8 +1,8 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/core/compileroptions.go. DO NOT EDIT. -export var ModuleDetectionKind: any; -(function (ModuleDetectionKind) { - ModuleDetectionKind[ModuleDetectionKind["None"] = 0] = "None"; - ModuleDetectionKind[ModuleDetectionKind["Auto"] = 1] = "Auto"; - ModuleDetectionKind[ModuleDetectionKind["Legacy"] = 2] = "Legacy"; - ModuleDetectionKind[ModuleDetectionKind["Force"] = 3] = "Force"; -})(ModuleDetectionKind || (ModuleDetectionKind = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/core/compileroptions.go. DO NOT EDIT. +export var ModuleDetectionKind: any; +(function (ModuleDetectionKind) { + ModuleDetectionKind[ModuleDetectionKind["None"] = 0] = "None"; + ModuleDetectionKind[ModuleDetectionKind["Auto"] = 1] = "Auto"; + ModuleDetectionKind[ModuleDetectionKind["Legacy"] = 2] = "Legacy"; + ModuleDetectionKind[ModuleDetectionKind["Force"] = 3] = "Force"; +})(ModuleDetectionKind || (ModuleDetectionKind = {})); diff --git a/packages/typescript/src/enums/moduleKind.enum.ts b/packages/typescript/src/enums/moduleKind.enum.ts index 6a9362631e2c7..11178dd01e637 100644 --- a/packages/typescript/src/enums/moduleKind.enum.ts +++ b/packages/typescript/src/enums/moduleKind.enum.ts @@ -1,18 +1,18 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/core/compileroptions.go. DO NOT EDIT. - -export enum ModuleKind { - None = 0, - CommonJS = 1, - AMD = 2, - UMD = 3, - System = 4, - ES2015 = 5, - ES2020 = 6, - ES2022 = 7, - ESNext = 99, - Node16 = 100, - Node18 = 101, - Node20 = 102, - NodeNext = 199, - Preserve = 200, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/core/compileroptions.go. DO NOT EDIT. + +export enum ModuleKind { + None = 0, + CommonJS = 1, + AMD = 2, + UMD = 3, + System = 4, + ES2015 = 5, + ES2020 = 6, + ES2022 = 7, + ESNext = 99, + Node16 = 100, + Node18 = 101, + Node20 = 102, + NodeNext = 199, + Preserve = 200, +} diff --git a/packages/typescript/src/enums/moduleKind.ts b/packages/typescript/src/enums/moduleKind.ts index 0158c88a172e8..4cdcad7cbbf7d 100644 --- a/packages/typescript/src/enums/moduleKind.ts +++ b/packages/typescript/src/enums/moduleKind.ts @@ -1,18 +1,18 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/core/compileroptions.go. DO NOT EDIT. -export var ModuleKind: any; -(function (ModuleKind) { - ModuleKind[ModuleKind["None"] = 0] = "None"; - ModuleKind[ModuleKind["CommonJS"] = 1] = "CommonJS"; - ModuleKind[ModuleKind["AMD"] = 2] = "AMD"; - ModuleKind[ModuleKind["UMD"] = 3] = "UMD"; - ModuleKind[ModuleKind["System"] = 4] = "System"; - ModuleKind[ModuleKind["ES2015"] = 5] = "ES2015"; - ModuleKind[ModuleKind["ES2020"] = 6] = "ES2020"; - ModuleKind[ModuleKind["ES2022"] = 7] = "ES2022"; - ModuleKind[ModuleKind["ESNext"] = 99] = "ESNext"; - ModuleKind[ModuleKind["Node16"] = 100] = "Node16"; - ModuleKind[ModuleKind["Node18"] = 101] = "Node18"; - ModuleKind[ModuleKind["Node20"] = 102] = "Node20"; - ModuleKind[ModuleKind["NodeNext"] = 199] = "NodeNext"; - ModuleKind[ModuleKind["Preserve"] = 200] = "Preserve"; -})(ModuleKind || (ModuleKind = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/core/compileroptions.go. DO NOT EDIT. +export var ModuleKind: any; +(function (ModuleKind) { + ModuleKind[ModuleKind["None"] = 0] = "None"; + ModuleKind[ModuleKind["CommonJS"] = 1] = "CommonJS"; + ModuleKind[ModuleKind["AMD"] = 2] = "AMD"; + ModuleKind[ModuleKind["UMD"] = 3] = "UMD"; + ModuleKind[ModuleKind["System"] = 4] = "System"; + ModuleKind[ModuleKind["ES2015"] = 5] = "ES2015"; + ModuleKind[ModuleKind["ES2020"] = 6] = "ES2020"; + ModuleKind[ModuleKind["ES2022"] = 7] = "ES2022"; + ModuleKind[ModuleKind["ESNext"] = 99] = "ESNext"; + ModuleKind[ModuleKind["Node16"] = 100] = "Node16"; + ModuleKind[ModuleKind["Node18"] = 101] = "Node18"; + ModuleKind[ModuleKind["Node20"] = 102] = "Node20"; + ModuleKind[ModuleKind["NodeNext"] = 199] = "NodeNext"; + ModuleKind[ModuleKind["Preserve"] = 200] = "Preserve"; +})(ModuleKind || (ModuleKind = {})); diff --git a/packages/typescript/src/enums/moduleResolutionKind.enum.ts b/packages/typescript/src/enums/moduleResolutionKind.enum.ts index d1229a6dcaf38..0c3c16a72a738 100644 --- a/packages/typescript/src/enums/moduleResolutionKind.enum.ts +++ b/packages/typescript/src/enums/moduleResolutionKind.enum.ts @@ -1,10 +1,10 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/core/compileroptions.go. DO NOT EDIT. - -export enum ModuleResolutionKind { - Unknown = 0, - Classic = 1, - Node10 = 2, - Node16 = 3, - NodeNext = 99, - Bundler = 100, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/core/compileroptions.go. DO NOT EDIT. + +export enum ModuleResolutionKind { + Unknown = 0, + Classic = 1, + Node10 = 2, + Node16 = 3, + NodeNext = 99, + Bundler = 100, +} diff --git a/packages/typescript/src/enums/moduleResolutionKind.ts b/packages/typescript/src/enums/moduleResolutionKind.ts index 2f99ddc4e0c55..a62ed3a3585f0 100644 --- a/packages/typescript/src/enums/moduleResolutionKind.ts +++ b/packages/typescript/src/enums/moduleResolutionKind.ts @@ -1,10 +1,10 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/core/compileroptions.go. DO NOT EDIT. -export var ModuleResolutionKind: any; -(function (ModuleResolutionKind) { - ModuleResolutionKind[ModuleResolutionKind["Unknown"] = 0] = "Unknown"; - ModuleResolutionKind[ModuleResolutionKind["Classic"] = 1] = "Classic"; - ModuleResolutionKind[ModuleResolutionKind["Node10"] = 2] = "Node10"; - ModuleResolutionKind[ModuleResolutionKind["Node16"] = 3] = "Node16"; - ModuleResolutionKind[ModuleResolutionKind["NodeNext"] = 99] = "NodeNext"; - ModuleResolutionKind[ModuleResolutionKind["Bundler"] = 100] = "Bundler"; -})(ModuleResolutionKind || (ModuleResolutionKind = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/core/compileroptions.go. DO NOT EDIT. +export var ModuleResolutionKind: any; +(function (ModuleResolutionKind) { + ModuleResolutionKind[ModuleResolutionKind["Unknown"] = 0] = "Unknown"; + ModuleResolutionKind[ModuleResolutionKind["Classic"] = 1] = "Classic"; + ModuleResolutionKind[ModuleResolutionKind["Node10"] = 2] = "Node10"; + ModuleResolutionKind[ModuleResolutionKind["Node16"] = 3] = "Node16"; + ModuleResolutionKind[ModuleResolutionKind["NodeNext"] = 99] = "NodeNext"; + ModuleResolutionKind[ModuleResolutionKind["Bundler"] = 100] = "Bundler"; +})(ModuleResolutionKind || (ModuleResolutionKind = {})); diff --git a/packages/typescript/src/enums/newLineKind.enum.ts b/packages/typescript/src/enums/newLineKind.enum.ts index 1aba40b360aba..c2cb515b9881c 100644 --- a/packages/typescript/src/enums/newLineKind.enum.ts +++ b/packages/typescript/src/enums/newLineKind.enum.ts @@ -1,7 +1,7 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/core/compileroptions.go. DO NOT EDIT. - -export enum NewLineKind { - None = 0, - CRLF = 1, - LF = 2, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/core/compileroptions.go. DO NOT EDIT. + +export enum NewLineKind { + None = 0, + CRLF = 1, + LF = 2, +} diff --git a/packages/typescript/src/enums/newLineKind.ts b/packages/typescript/src/enums/newLineKind.ts index 43dce5e02005a..98adf7c0f689a 100644 --- a/packages/typescript/src/enums/newLineKind.ts +++ b/packages/typescript/src/enums/newLineKind.ts @@ -1,7 +1,7 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/core/compileroptions.go. DO NOT EDIT. -export var NewLineKind: any; -(function (NewLineKind) { - NewLineKind[NewLineKind["None"] = 0] = "None"; - NewLineKind[NewLineKind["CRLF"] = 1] = "CRLF"; - NewLineKind[NewLineKind["LF"] = 2] = "LF"; -})(NewLineKind || (NewLineKind = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/core/compileroptions.go. DO NOT EDIT. +export var NewLineKind: any; +(function (NewLineKind) { + NewLineKind[NewLineKind["None"] = 0] = "None"; + NewLineKind[NewLineKind["CRLF"] = 1] = "CRLF"; + NewLineKind[NewLineKind["LF"] = 2] = "LF"; +})(NewLineKind || (NewLineKind = {})); diff --git a/packages/typescript/src/enums/nodeBuilderFlags.enum.ts b/packages/typescript/src/enums/nodeBuilderFlags.enum.ts index f7ecf188b1908..4c6fa7d46d56b 100644 --- a/packages/typescript/src/enums/nodeBuilderFlags.enum.ts +++ b/packages/typescript/src/enums/nodeBuilderFlags.enum.ts @@ -1,37 +1,37 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/nodebuilder/types.go. DO NOT EDIT. - -export enum NodeBuilderFlags { - None = 0, - NoTruncation = 1 << 0, - WriteArrayAsGenericType = 1 << 1, - GenerateNamesForShadowedTypeParams = 1 << 2, - UseStructuralFallback = 1 << 3, - ForbidIndexedAccessSymbolReferences = 1 << 4, - WriteTypeArgumentsOfSignature = 1 << 5, - UseFullyQualifiedType = 1 << 6, - UseOnlyExternalAliasing = 1 << 7, - SuppressAnyReturnType = 1 << 8, - WriteTypeParametersInQualifiedName = 1 << 9, - MultilineObjectLiterals = 1 << 10, - WriteClassExpressionAsTypeLiteral = 1 << 11, - UseTypeOfFunction = 1 << 12, - OmitParameterModifiers = 1 << 13, - UseAliasDefinedOutsideCurrentScope = 1 << 14, - UseSingleQuotesForStringLiteralType = 1 << 28, - NoTypeReduction = 1 << 29, - UseInstantiationExpressions = 1 << 30, - OmitThisParameter = 1 << 25, - WriteCallStyleSignature = 1 << 27, - AllowThisInObjectLiteral = 1 << 15, - AllowQualifiedNameInPlaceOfIdentifier = 1 << 16, - AllowAnonymousIdentifier = 1 << 17, - AllowEmptyUnionOrIntersection = 1 << 18, - AllowEmptyTuple = 1 << 19, - AllowUniqueESSymbolType = 1 << 20, - AllowEmptyIndexInfoType = 1 << 21, - AllowNodeModulesRelativePaths = 1 << 26, - IgnoreErrors = AllowThisInObjectLiteral | AllowQualifiedNameInPlaceOfIdentifier | AllowAnonymousIdentifier | AllowEmptyUnionOrIntersection | AllowEmptyTuple | AllowEmptyIndexInfoType | AllowNodeModulesRelativePaths, - InObjectTypeLiteral = 1 << 22, - InTypeAlias = 1 << 23, - InInitialEntityName = 1 << 24, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/nodebuilder/types.go. DO NOT EDIT. + +export enum NodeBuilderFlags { + None = 0, + NoTruncation = 1 << 0, + WriteArrayAsGenericType = 1 << 1, + GenerateNamesForShadowedTypeParams = 1 << 2, + UseStructuralFallback = 1 << 3, + ForbidIndexedAccessSymbolReferences = 1 << 4, + WriteTypeArgumentsOfSignature = 1 << 5, + UseFullyQualifiedType = 1 << 6, + UseOnlyExternalAliasing = 1 << 7, + SuppressAnyReturnType = 1 << 8, + WriteTypeParametersInQualifiedName = 1 << 9, + MultilineObjectLiterals = 1 << 10, + WriteClassExpressionAsTypeLiteral = 1 << 11, + UseTypeOfFunction = 1 << 12, + OmitParameterModifiers = 1 << 13, + UseAliasDefinedOutsideCurrentScope = 1 << 14, + UseSingleQuotesForStringLiteralType = 1 << 28, + NoTypeReduction = 1 << 29, + UseInstantiationExpressions = 1 << 30, + OmitThisParameter = 1 << 25, + WriteCallStyleSignature = 1 << 27, + AllowThisInObjectLiteral = 1 << 15, + AllowQualifiedNameInPlaceOfIdentifier = 1 << 16, + AllowAnonymousIdentifier = 1 << 17, + AllowEmptyUnionOrIntersection = 1 << 18, + AllowEmptyTuple = 1 << 19, + AllowUniqueESSymbolType = 1 << 20, + AllowEmptyIndexInfoType = 1 << 21, + AllowNodeModulesRelativePaths = 1 << 26, + IgnoreErrors = AllowThisInObjectLiteral | AllowQualifiedNameInPlaceOfIdentifier | AllowAnonymousIdentifier | AllowEmptyUnionOrIntersection | AllowEmptyTuple | AllowEmptyIndexInfoType | AllowNodeModulesRelativePaths, + InObjectTypeLiteral = 1 << 22, + InTypeAlias = 1 << 23, + InInitialEntityName = 1 << 24, +} diff --git a/packages/typescript/src/enums/nodeBuilderFlags.ts b/packages/typescript/src/enums/nodeBuilderFlags.ts index 020e1af5136db..4f33ae4b60b80 100644 --- a/packages/typescript/src/enums/nodeBuilderFlags.ts +++ b/packages/typescript/src/enums/nodeBuilderFlags.ts @@ -1,37 +1,37 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/nodebuilder/types.go. DO NOT EDIT. -export var NodeBuilderFlags: any; -(function (NodeBuilderFlags) { - NodeBuilderFlags[NodeBuilderFlags["None"] = 0] = "None"; - NodeBuilderFlags[NodeBuilderFlags["NoTruncation"] = 1] = "NoTruncation"; - NodeBuilderFlags[NodeBuilderFlags["WriteArrayAsGenericType"] = 2] = "WriteArrayAsGenericType"; - NodeBuilderFlags[NodeBuilderFlags["GenerateNamesForShadowedTypeParams"] = 4] = "GenerateNamesForShadowedTypeParams"; - NodeBuilderFlags[NodeBuilderFlags["UseStructuralFallback"] = 8] = "UseStructuralFallback"; - NodeBuilderFlags[NodeBuilderFlags["ForbidIndexedAccessSymbolReferences"] = 16] = "ForbidIndexedAccessSymbolReferences"; - NodeBuilderFlags[NodeBuilderFlags["WriteTypeArgumentsOfSignature"] = 32] = "WriteTypeArgumentsOfSignature"; - NodeBuilderFlags[NodeBuilderFlags["UseFullyQualifiedType"] = 64] = "UseFullyQualifiedType"; - NodeBuilderFlags[NodeBuilderFlags["UseOnlyExternalAliasing"] = 128] = "UseOnlyExternalAliasing"; - NodeBuilderFlags[NodeBuilderFlags["SuppressAnyReturnType"] = 256] = "SuppressAnyReturnType"; - NodeBuilderFlags[NodeBuilderFlags["WriteTypeParametersInQualifiedName"] = 512] = "WriteTypeParametersInQualifiedName"; - NodeBuilderFlags[NodeBuilderFlags["MultilineObjectLiterals"] = 1024] = "MultilineObjectLiterals"; - NodeBuilderFlags[NodeBuilderFlags["WriteClassExpressionAsTypeLiteral"] = 2048] = "WriteClassExpressionAsTypeLiteral"; - NodeBuilderFlags[NodeBuilderFlags["UseTypeOfFunction"] = 4096] = "UseTypeOfFunction"; - NodeBuilderFlags[NodeBuilderFlags["OmitParameterModifiers"] = 8192] = "OmitParameterModifiers"; - NodeBuilderFlags[NodeBuilderFlags["UseAliasDefinedOutsideCurrentScope"] = 16384] = "UseAliasDefinedOutsideCurrentScope"; - NodeBuilderFlags[NodeBuilderFlags["UseSingleQuotesForStringLiteralType"] = 268435456] = "UseSingleQuotesForStringLiteralType"; - NodeBuilderFlags[NodeBuilderFlags["NoTypeReduction"] = 536870912] = "NoTypeReduction"; - NodeBuilderFlags[NodeBuilderFlags["UseInstantiationExpressions"] = 1073741824] = "UseInstantiationExpressions"; - NodeBuilderFlags[NodeBuilderFlags["OmitThisParameter"] = 33554432] = "OmitThisParameter"; - NodeBuilderFlags[NodeBuilderFlags["WriteCallStyleSignature"] = 134217728] = "WriteCallStyleSignature"; - NodeBuilderFlags[NodeBuilderFlags["AllowThisInObjectLiteral"] = 32768] = "AllowThisInObjectLiteral"; - NodeBuilderFlags[NodeBuilderFlags["AllowQualifiedNameInPlaceOfIdentifier"] = 65536] = "AllowQualifiedNameInPlaceOfIdentifier"; - NodeBuilderFlags[NodeBuilderFlags["AllowAnonymousIdentifier"] = 131072] = "AllowAnonymousIdentifier"; - NodeBuilderFlags[NodeBuilderFlags["AllowEmptyUnionOrIntersection"] = 262144] = "AllowEmptyUnionOrIntersection"; - NodeBuilderFlags[NodeBuilderFlags["AllowEmptyTuple"] = 524288] = "AllowEmptyTuple"; - NodeBuilderFlags[NodeBuilderFlags["AllowUniqueESSymbolType"] = 1048576] = "AllowUniqueESSymbolType"; - NodeBuilderFlags[NodeBuilderFlags["AllowEmptyIndexInfoType"] = 2097152] = "AllowEmptyIndexInfoType"; - NodeBuilderFlags[NodeBuilderFlags["AllowNodeModulesRelativePaths"] = 67108864] = "AllowNodeModulesRelativePaths"; - NodeBuilderFlags[NodeBuilderFlags["IgnoreErrors"] = 70221824] = "IgnoreErrors"; - NodeBuilderFlags[NodeBuilderFlags["InObjectTypeLiteral"] = 4194304] = "InObjectTypeLiteral"; - NodeBuilderFlags[NodeBuilderFlags["InTypeAlias"] = 8388608] = "InTypeAlias"; - NodeBuilderFlags[NodeBuilderFlags["InInitialEntityName"] = 16777216] = "InInitialEntityName"; -})(NodeBuilderFlags || (NodeBuilderFlags = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/nodebuilder/types.go. DO NOT EDIT. +export var NodeBuilderFlags: any; +(function (NodeBuilderFlags) { + NodeBuilderFlags[NodeBuilderFlags["None"] = 0] = "None"; + NodeBuilderFlags[NodeBuilderFlags["NoTruncation"] = 1] = "NoTruncation"; + NodeBuilderFlags[NodeBuilderFlags["WriteArrayAsGenericType"] = 2] = "WriteArrayAsGenericType"; + NodeBuilderFlags[NodeBuilderFlags["GenerateNamesForShadowedTypeParams"] = 4] = "GenerateNamesForShadowedTypeParams"; + NodeBuilderFlags[NodeBuilderFlags["UseStructuralFallback"] = 8] = "UseStructuralFallback"; + NodeBuilderFlags[NodeBuilderFlags["ForbidIndexedAccessSymbolReferences"] = 16] = "ForbidIndexedAccessSymbolReferences"; + NodeBuilderFlags[NodeBuilderFlags["WriteTypeArgumentsOfSignature"] = 32] = "WriteTypeArgumentsOfSignature"; + NodeBuilderFlags[NodeBuilderFlags["UseFullyQualifiedType"] = 64] = "UseFullyQualifiedType"; + NodeBuilderFlags[NodeBuilderFlags["UseOnlyExternalAliasing"] = 128] = "UseOnlyExternalAliasing"; + NodeBuilderFlags[NodeBuilderFlags["SuppressAnyReturnType"] = 256] = "SuppressAnyReturnType"; + NodeBuilderFlags[NodeBuilderFlags["WriteTypeParametersInQualifiedName"] = 512] = "WriteTypeParametersInQualifiedName"; + NodeBuilderFlags[NodeBuilderFlags["MultilineObjectLiterals"] = 1024] = "MultilineObjectLiterals"; + NodeBuilderFlags[NodeBuilderFlags["WriteClassExpressionAsTypeLiteral"] = 2048] = "WriteClassExpressionAsTypeLiteral"; + NodeBuilderFlags[NodeBuilderFlags["UseTypeOfFunction"] = 4096] = "UseTypeOfFunction"; + NodeBuilderFlags[NodeBuilderFlags["OmitParameterModifiers"] = 8192] = "OmitParameterModifiers"; + NodeBuilderFlags[NodeBuilderFlags["UseAliasDefinedOutsideCurrentScope"] = 16384] = "UseAliasDefinedOutsideCurrentScope"; + NodeBuilderFlags[NodeBuilderFlags["UseSingleQuotesForStringLiteralType"] = 268435456] = "UseSingleQuotesForStringLiteralType"; + NodeBuilderFlags[NodeBuilderFlags["NoTypeReduction"] = 536870912] = "NoTypeReduction"; + NodeBuilderFlags[NodeBuilderFlags["UseInstantiationExpressions"] = 1073741824] = "UseInstantiationExpressions"; + NodeBuilderFlags[NodeBuilderFlags["OmitThisParameter"] = 33554432] = "OmitThisParameter"; + NodeBuilderFlags[NodeBuilderFlags["WriteCallStyleSignature"] = 134217728] = "WriteCallStyleSignature"; + NodeBuilderFlags[NodeBuilderFlags["AllowThisInObjectLiteral"] = 32768] = "AllowThisInObjectLiteral"; + NodeBuilderFlags[NodeBuilderFlags["AllowQualifiedNameInPlaceOfIdentifier"] = 65536] = "AllowQualifiedNameInPlaceOfIdentifier"; + NodeBuilderFlags[NodeBuilderFlags["AllowAnonymousIdentifier"] = 131072] = "AllowAnonymousIdentifier"; + NodeBuilderFlags[NodeBuilderFlags["AllowEmptyUnionOrIntersection"] = 262144] = "AllowEmptyUnionOrIntersection"; + NodeBuilderFlags[NodeBuilderFlags["AllowEmptyTuple"] = 524288] = "AllowEmptyTuple"; + NodeBuilderFlags[NodeBuilderFlags["AllowUniqueESSymbolType"] = 1048576] = "AllowUniqueESSymbolType"; + NodeBuilderFlags[NodeBuilderFlags["AllowEmptyIndexInfoType"] = 2097152] = "AllowEmptyIndexInfoType"; + NodeBuilderFlags[NodeBuilderFlags["AllowNodeModulesRelativePaths"] = 67108864] = "AllowNodeModulesRelativePaths"; + NodeBuilderFlags[NodeBuilderFlags["IgnoreErrors"] = 70221824] = "IgnoreErrors"; + NodeBuilderFlags[NodeBuilderFlags["InObjectTypeLiteral"] = 4194304] = "InObjectTypeLiteral"; + NodeBuilderFlags[NodeBuilderFlags["InTypeAlias"] = 8388608] = "InTypeAlias"; + NodeBuilderFlags[NodeBuilderFlags["InInitialEntityName"] = 16777216] = "InInitialEntityName"; +})(NodeBuilderFlags || (NodeBuilderFlags = {})); diff --git a/packages/typescript/src/enums/nodeFlags.enum.ts b/packages/typescript/src/enums/nodeFlags.enum.ts index 614736d0e38bb..232748bbad9c2 100644 --- a/packages/typescript/src/enums/nodeFlags.enum.ts +++ b/packages/typescript/src/enums/nodeFlags.enum.ts @@ -1,45 +1,45 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/nodeflags.go. DO NOT EDIT. - -export enum NodeFlags { - None = 0, - Let = 1 << 0, - Const = 1 << 1, - Using = 1 << 2, - Reparsed = 1 << 3, - Synthesized = 1 << 4, - OptionalChain = 1 << 5, - ExportContext = 1 << 6, - ContainsThis = 1 << 7, - HasImplicitReturn = 1 << 8, - HasExplicitReturn = 1 << 9, - DisallowInContext = 1 << 10, - YieldContext = 1 << 11, - DecoratorContext = 1 << 12, - AwaitContext = 1 << 13, - DisallowConditionalTypesContext = 1 << 14, - ThisNodeHasError = 1 << 15, - JavaScriptFile = 1 << 16, - ThisNodeOrAnySubNodesHasError = 1 << 17, - HasAsyncFunctions = 1 << 18, - PossiblyContainsDynamicImport = 1 << 19, - PossiblyContainsImportMeta = 1 << 20, - HasJSDoc = 1 << 21, - JSDoc = 1 << 22, - Ambient = 1 << 23, - InWithStatement = 1 << 24, - JsonFile = 1 << 25, - PossiblyContainsDeprecatedTag = 1 << 26, - Unreachable = 1 << 27, - ReparserTransformedLiteral = 1 << 28, - BlockScoped = Let | Const | Using, - Constant = Const | Using, - AwaitUsing = Const | Using, - ReachabilityCheckFlags = HasImplicitReturn | HasExplicitReturn, - ReachabilityAndEmitFlags = ReachabilityCheckFlags | HasAsyncFunctions, - ContextFlags = DisallowInContext | DisallowConditionalTypesContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile | InWithStatement | Ambient, - TypeExcludesFlags = YieldContext | AwaitContext, - PermanentlySetIncrementalFlags = PossiblyContainsDynamicImport | PossiblyContainsImportMeta, - IdentifierHasExtendedUnicodeEscape = ContainsThis, - IdentifierIsInJSDocNamespace = HasAsyncFunctions, - NestedNamespace = OptionalChain, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/nodeflags.go. DO NOT EDIT. + +export enum NodeFlags { + None = 0, + Let = 1 << 0, + Const = 1 << 1, + Using = 1 << 2, + Reparsed = 1 << 3, + Synthesized = 1 << 4, + OptionalChain = 1 << 5, + ExportContext = 1 << 6, + ContainsThis = 1 << 7, + HasImplicitReturn = 1 << 8, + HasExplicitReturn = 1 << 9, + DisallowInContext = 1 << 10, + YieldContext = 1 << 11, + DecoratorContext = 1 << 12, + AwaitContext = 1 << 13, + DisallowConditionalTypesContext = 1 << 14, + ThisNodeHasError = 1 << 15, + JavaScriptFile = 1 << 16, + ThisNodeOrAnySubNodesHasError = 1 << 17, + HasAsyncFunctions = 1 << 18, + PossiblyContainsDynamicImport = 1 << 19, + PossiblyContainsImportMeta = 1 << 20, + HasJSDoc = 1 << 21, + JSDoc = 1 << 22, + Ambient = 1 << 23, + InWithStatement = 1 << 24, + JsonFile = 1 << 25, + PossiblyContainsDeprecatedTag = 1 << 26, + Unreachable = 1 << 27, + ReparserTransformedLiteral = 1 << 28, + BlockScoped = Let | Const | Using, + Constant = Const | Using, + AwaitUsing = Const | Using, + ReachabilityCheckFlags = HasImplicitReturn | HasExplicitReturn, + ReachabilityAndEmitFlags = ReachabilityCheckFlags | HasAsyncFunctions, + ContextFlags = DisallowInContext | DisallowConditionalTypesContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile | InWithStatement | Ambient, + TypeExcludesFlags = YieldContext | AwaitContext, + PermanentlySetIncrementalFlags = PossiblyContainsDynamicImport | PossiblyContainsImportMeta, + IdentifierHasExtendedUnicodeEscape = ContainsThis, + IdentifierIsInJSDocNamespace = HasAsyncFunctions, + NestedNamespace = OptionalChain, +} diff --git a/packages/typescript/src/enums/nodeFlags.ts b/packages/typescript/src/enums/nodeFlags.ts index e1a41bc27b6f7..653d30d714999 100644 --- a/packages/typescript/src/enums/nodeFlags.ts +++ b/packages/typescript/src/enums/nodeFlags.ts @@ -1,45 +1,45 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/nodeflags.go. DO NOT EDIT. -export var NodeFlags: any; -(function (NodeFlags) { - NodeFlags[NodeFlags["None"] = 0] = "None"; - NodeFlags[NodeFlags["Let"] = 1] = "Let"; - NodeFlags[NodeFlags["Const"] = 2] = "Const"; - NodeFlags[NodeFlags["Using"] = 4] = "Using"; - NodeFlags[NodeFlags["Reparsed"] = 8] = "Reparsed"; - NodeFlags[NodeFlags["Synthesized"] = 16] = "Synthesized"; - NodeFlags[NodeFlags["OptionalChain"] = 32] = "OptionalChain"; - NodeFlags[NodeFlags["ExportContext"] = 64] = "ExportContext"; - NodeFlags[NodeFlags["ContainsThis"] = 128] = "ContainsThis"; - NodeFlags[NodeFlags["HasImplicitReturn"] = 256] = "HasImplicitReturn"; - NodeFlags[NodeFlags["HasExplicitReturn"] = 512] = "HasExplicitReturn"; - NodeFlags[NodeFlags["DisallowInContext"] = 1024] = "DisallowInContext"; - NodeFlags[NodeFlags["YieldContext"] = 2048] = "YieldContext"; - NodeFlags[NodeFlags["DecoratorContext"] = 4096] = "DecoratorContext"; - NodeFlags[NodeFlags["AwaitContext"] = 8192] = "AwaitContext"; - NodeFlags[NodeFlags["DisallowConditionalTypesContext"] = 16384] = "DisallowConditionalTypesContext"; - NodeFlags[NodeFlags["ThisNodeHasError"] = 32768] = "ThisNodeHasError"; - NodeFlags[NodeFlags["JavaScriptFile"] = 65536] = "JavaScriptFile"; - NodeFlags[NodeFlags["ThisNodeOrAnySubNodesHasError"] = 131072] = "ThisNodeOrAnySubNodesHasError"; - NodeFlags[NodeFlags["HasAsyncFunctions"] = 262144] = "HasAsyncFunctions"; - NodeFlags[NodeFlags["PossiblyContainsDynamicImport"] = 524288] = "PossiblyContainsDynamicImport"; - NodeFlags[NodeFlags["PossiblyContainsImportMeta"] = 1048576] = "PossiblyContainsImportMeta"; - NodeFlags[NodeFlags["HasJSDoc"] = 2097152] = "HasJSDoc"; - NodeFlags[NodeFlags["JSDoc"] = 4194304] = "JSDoc"; - NodeFlags[NodeFlags["Ambient"] = 8388608] = "Ambient"; - NodeFlags[NodeFlags["InWithStatement"] = 16777216] = "InWithStatement"; - NodeFlags[NodeFlags["JsonFile"] = 33554432] = "JsonFile"; - NodeFlags[NodeFlags["PossiblyContainsDeprecatedTag"] = 67108864] = "PossiblyContainsDeprecatedTag"; - NodeFlags[NodeFlags["Unreachable"] = 134217728] = "Unreachable"; - NodeFlags[NodeFlags["ReparserTransformedLiteral"] = 268435456] = "ReparserTransformedLiteral"; - NodeFlags[NodeFlags["BlockScoped"] = 7] = "BlockScoped"; - NodeFlags[NodeFlags["Constant"] = 6] = "Constant"; - NodeFlags[NodeFlags["AwaitUsing"] = 6] = "AwaitUsing"; - NodeFlags[NodeFlags["ReachabilityCheckFlags"] = 768] = "ReachabilityCheckFlags"; - NodeFlags[NodeFlags["ReachabilityAndEmitFlags"] = 262912] = "ReachabilityAndEmitFlags"; - NodeFlags[NodeFlags["ContextFlags"] = 25263104] = "ContextFlags"; - NodeFlags[NodeFlags["TypeExcludesFlags"] = 10240] = "TypeExcludesFlags"; - NodeFlags[NodeFlags["PermanentlySetIncrementalFlags"] = 1572864] = "PermanentlySetIncrementalFlags"; - NodeFlags[NodeFlags["IdentifierHasExtendedUnicodeEscape"] = 128] = "IdentifierHasExtendedUnicodeEscape"; - NodeFlags[NodeFlags["IdentifierIsInJSDocNamespace"] = 262144] = "IdentifierIsInJSDocNamespace"; - NodeFlags[NodeFlags["NestedNamespace"] = 32] = "NestedNamespace"; -})(NodeFlags || (NodeFlags = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/nodeflags.go. DO NOT EDIT. +export var NodeFlags: any; +(function (NodeFlags) { + NodeFlags[NodeFlags["None"] = 0] = "None"; + NodeFlags[NodeFlags["Let"] = 1] = "Let"; + NodeFlags[NodeFlags["Const"] = 2] = "Const"; + NodeFlags[NodeFlags["Using"] = 4] = "Using"; + NodeFlags[NodeFlags["Reparsed"] = 8] = "Reparsed"; + NodeFlags[NodeFlags["Synthesized"] = 16] = "Synthesized"; + NodeFlags[NodeFlags["OptionalChain"] = 32] = "OptionalChain"; + NodeFlags[NodeFlags["ExportContext"] = 64] = "ExportContext"; + NodeFlags[NodeFlags["ContainsThis"] = 128] = "ContainsThis"; + NodeFlags[NodeFlags["HasImplicitReturn"] = 256] = "HasImplicitReturn"; + NodeFlags[NodeFlags["HasExplicitReturn"] = 512] = "HasExplicitReturn"; + NodeFlags[NodeFlags["DisallowInContext"] = 1024] = "DisallowInContext"; + NodeFlags[NodeFlags["YieldContext"] = 2048] = "YieldContext"; + NodeFlags[NodeFlags["DecoratorContext"] = 4096] = "DecoratorContext"; + NodeFlags[NodeFlags["AwaitContext"] = 8192] = "AwaitContext"; + NodeFlags[NodeFlags["DisallowConditionalTypesContext"] = 16384] = "DisallowConditionalTypesContext"; + NodeFlags[NodeFlags["ThisNodeHasError"] = 32768] = "ThisNodeHasError"; + NodeFlags[NodeFlags["JavaScriptFile"] = 65536] = "JavaScriptFile"; + NodeFlags[NodeFlags["ThisNodeOrAnySubNodesHasError"] = 131072] = "ThisNodeOrAnySubNodesHasError"; + NodeFlags[NodeFlags["HasAsyncFunctions"] = 262144] = "HasAsyncFunctions"; + NodeFlags[NodeFlags["PossiblyContainsDynamicImport"] = 524288] = "PossiblyContainsDynamicImport"; + NodeFlags[NodeFlags["PossiblyContainsImportMeta"] = 1048576] = "PossiblyContainsImportMeta"; + NodeFlags[NodeFlags["HasJSDoc"] = 2097152] = "HasJSDoc"; + NodeFlags[NodeFlags["JSDoc"] = 4194304] = "JSDoc"; + NodeFlags[NodeFlags["Ambient"] = 8388608] = "Ambient"; + NodeFlags[NodeFlags["InWithStatement"] = 16777216] = "InWithStatement"; + NodeFlags[NodeFlags["JsonFile"] = 33554432] = "JsonFile"; + NodeFlags[NodeFlags["PossiblyContainsDeprecatedTag"] = 67108864] = "PossiblyContainsDeprecatedTag"; + NodeFlags[NodeFlags["Unreachable"] = 134217728] = "Unreachable"; + NodeFlags[NodeFlags["ReparserTransformedLiteral"] = 268435456] = "ReparserTransformedLiteral"; + NodeFlags[NodeFlags["BlockScoped"] = 7] = "BlockScoped"; + NodeFlags[NodeFlags["Constant"] = 6] = "Constant"; + NodeFlags[NodeFlags["AwaitUsing"] = 6] = "AwaitUsing"; + NodeFlags[NodeFlags["ReachabilityCheckFlags"] = 768] = "ReachabilityCheckFlags"; + NodeFlags[NodeFlags["ReachabilityAndEmitFlags"] = 262912] = "ReachabilityAndEmitFlags"; + NodeFlags[NodeFlags["ContextFlags"] = 25263104] = "ContextFlags"; + NodeFlags[NodeFlags["TypeExcludesFlags"] = 10240] = "TypeExcludesFlags"; + NodeFlags[NodeFlags["PermanentlySetIncrementalFlags"] = 1572864] = "PermanentlySetIncrementalFlags"; + NodeFlags[NodeFlags["IdentifierHasExtendedUnicodeEscape"] = 128] = "IdentifierHasExtendedUnicodeEscape"; + NodeFlags[NodeFlags["IdentifierIsInJSDocNamespace"] = 262144] = "IdentifierIsInJSDocNamespace"; + NodeFlags[NodeFlags["NestedNamespace"] = 32] = "NestedNamespace"; +})(NodeFlags || (NodeFlags = {})); diff --git a/packages/typescript/src/enums/objectFlags.enum.ts b/packages/typescript/src/enums/objectFlags.enum.ts index 63ed4e4d3c93f..3636d4b418303 100644 --- a/packages/typescript/src/enums/objectFlags.enum.ts +++ b/packages/typescript/src/enums/objectFlags.enum.ts @@ -1,53 +1,53 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. - -export enum ObjectFlags { - None = 0, - Class = 1 << 0, - Interface = 1 << 1, - Reference = 1 << 2, - Tuple = 1 << 3, - Anonymous = 1 << 4, - Mapped = 1 << 5, - Instantiated = 1 << 6, - ObjectLiteral = 1 << 7, - EvolvingArray = 1 << 8, - ObjectLiteralPatternWithComputedProperties = 1 << 9, - ReverseMapped = 1 << 10, - JsxAttributes = 1 << 11, - JSLiteral = 1 << 12, - FreshLiteral = 1 << 13, - ArrayLiteral = 1 << 14, - PrimitiveUnion = 1 << 15, - ContainsWideningType = 1 << 16, - ContainsObjectOrArrayLiteral = 1 << 17, - NonInferrableType = 1 << 18, - CouldContainTypeVariablesComputed = 1 << 19, - CouldContainTypeVariables = 1 << 20, - MembersResolved = 1 << 21, - ClassOrInterface = Class | Interface, - RequiresWidening = ContainsWideningType | ContainsObjectOrArrayLiteral, - PropagatingFlags = ContainsWideningType | ContainsObjectOrArrayLiteral | NonInferrableType, - InstantiatedMapped = Mapped | Instantiated, - InstantiationExpressionType = 1 << 24, - SingleSignatureType = 1 << 25, - ObjectTypeKindMask = ClassOrInterface | Reference | Tuple | Anonymous | Mapped | ReverseMapped | EvolvingArray | InstantiationExpressionType | SingleSignatureType, - ContainsSpread = 1 << 22, - ObjectRestType = 1 << 23, - IsClassInstanceClone = 1 << 26, - IdenticalBaseTypeCalculated = 1 << 27, - IdenticalBaseTypeExists = 1 << 28, - UnresolvedMembers = 1 << 29, - FromTypeNode = 1 << 30, - IsGenericTypeComputed = 1 << 22, - IsGenericObjectType = 1 << 23, - IsGenericIndexType = 1 << 24, - IsGenericType = IsGenericObjectType | IsGenericIndexType, - ContainsIntersections = 1 << 25, - IsUnknownLikeUnionComputed = 1 << 26, - IsUnknownLikeUnion = 1 << 27, - IsUniformEnumComputed = 1 << 28, - IsUniformEnum = 1 << 29, - IsNeverIntersectionComputed = 1 << 25, - IsNeverIntersection = 1 << 26, - IsConstrainedTypeVariable = 1 << 27, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. + +export enum ObjectFlags { + None = 0, + Class = 1 << 0, + Interface = 1 << 1, + Reference = 1 << 2, + Tuple = 1 << 3, + Anonymous = 1 << 4, + Mapped = 1 << 5, + Instantiated = 1 << 6, + ObjectLiteral = 1 << 7, + EvolvingArray = 1 << 8, + ObjectLiteralPatternWithComputedProperties = 1 << 9, + ReverseMapped = 1 << 10, + JsxAttributes = 1 << 11, + JSLiteral = 1 << 12, + FreshLiteral = 1 << 13, + ArrayLiteral = 1 << 14, + PrimitiveUnion = 1 << 15, + ContainsWideningType = 1 << 16, + ContainsObjectOrArrayLiteral = 1 << 17, + NonInferrableType = 1 << 18, + CouldContainTypeVariablesComputed = 1 << 19, + CouldContainTypeVariables = 1 << 20, + MembersResolved = 1 << 21, + ClassOrInterface = Class | Interface, + RequiresWidening = ContainsWideningType | ContainsObjectOrArrayLiteral, + PropagatingFlags = ContainsWideningType | ContainsObjectOrArrayLiteral | NonInferrableType, + InstantiatedMapped = Mapped | Instantiated, + InstantiationExpressionType = 1 << 24, + SingleSignatureType = 1 << 25, + ObjectTypeKindMask = ClassOrInterface | Reference | Tuple | Anonymous | Mapped | ReverseMapped | EvolvingArray | InstantiationExpressionType | SingleSignatureType, + ContainsSpread = 1 << 22, + ObjectRestType = 1 << 23, + IsClassInstanceClone = 1 << 26, + IdenticalBaseTypeCalculated = 1 << 27, + IdenticalBaseTypeExists = 1 << 28, + UnresolvedMembers = 1 << 29, + FromTypeNode = 1 << 30, + IsGenericTypeComputed = 1 << 22, + IsGenericObjectType = 1 << 23, + IsGenericIndexType = 1 << 24, + IsGenericType = IsGenericObjectType | IsGenericIndexType, + ContainsIntersections = 1 << 25, + IsUnknownLikeUnionComputed = 1 << 26, + IsUnknownLikeUnion = 1 << 27, + IsUniformEnumComputed = 1 << 28, + IsUniformEnum = 1 << 29, + IsNeverIntersectionComputed = 1 << 25, + IsNeverIntersection = 1 << 26, + IsConstrainedTypeVariable = 1 << 27, +} diff --git a/packages/typescript/src/enums/objectFlags.ts b/packages/typescript/src/enums/objectFlags.ts index 1c17a70d5f797..e25ec35f7aa39 100644 --- a/packages/typescript/src/enums/objectFlags.ts +++ b/packages/typescript/src/enums/objectFlags.ts @@ -1,53 +1,53 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. -export var ObjectFlags: any; -(function (ObjectFlags) { - ObjectFlags[ObjectFlags["None"] = 0] = "None"; - ObjectFlags[ObjectFlags["Class"] = 1] = "Class"; - ObjectFlags[ObjectFlags["Interface"] = 2] = "Interface"; - ObjectFlags[ObjectFlags["Reference"] = 4] = "Reference"; - ObjectFlags[ObjectFlags["Tuple"] = 8] = "Tuple"; - ObjectFlags[ObjectFlags["Anonymous"] = 16] = "Anonymous"; - ObjectFlags[ObjectFlags["Mapped"] = 32] = "Mapped"; - ObjectFlags[ObjectFlags["Instantiated"] = 64] = "Instantiated"; - ObjectFlags[ObjectFlags["ObjectLiteral"] = 128] = "ObjectLiteral"; - ObjectFlags[ObjectFlags["EvolvingArray"] = 256] = "EvolvingArray"; - ObjectFlags[ObjectFlags["ObjectLiteralPatternWithComputedProperties"] = 512] = "ObjectLiteralPatternWithComputedProperties"; - ObjectFlags[ObjectFlags["ReverseMapped"] = 1024] = "ReverseMapped"; - ObjectFlags[ObjectFlags["JsxAttributes"] = 2048] = "JsxAttributes"; - ObjectFlags[ObjectFlags["JSLiteral"] = 4096] = "JSLiteral"; - ObjectFlags[ObjectFlags["FreshLiteral"] = 8192] = "FreshLiteral"; - ObjectFlags[ObjectFlags["ArrayLiteral"] = 16384] = "ArrayLiteral"; - ObjectFlags[ObjectFlags["PrimitiveUnion"] = 32768] = "PrimitiveUnion"; - ObjectFlags[ObjectFlags["ContainsWideningType"] = 65536] = "ContainsWideningType"; - ObjectFlags[ObjectFlags["ContainsObjectOrArrayLiteral"] = 131072] = "ContainsObjectOrArrayLiteral"; - ObjectFlags[ObjectFlags["NonInferrableType"] = 262144] = "NonInferrableType"; - ObjectFlags[ObjectFlags["CouldContainTypeVariablesComputed"] = 524288] = "CouldContainTypeVariablesComputed"; - ObjectFlags[ObjectFlags["CouldContainTypeVariables"] = 1048576] = "CouldContainTypeVariables"; - ObjectFlags[ObjectFlags["MembersResolved"] = 2097152] = "MembersResolved"; - ObjectFlags[ObjectFlags["ClassOrInterface"] = 3] = "ClassOrInterface"; - ObjectFlags[ObjectFlags["RequiresWidening"] = 196608] = "RequiresWidening"; - ObjectFlags[ObjectFlags["PropagatingFlags"] = 458752] = "PropagatingFlags"; - ObjectFlags[ObjectFlags["InstantiatedMapped"] = 96] = "InstantiatedMapped"; - ObjectFlags[ObjectFlags["InstantiationExpressionType"] = 16777216] = "InstantiationExpressionType"; - ObjectFlags[ObjectFlags["SingleSignatureType"] = 33554432] = "SingleSignatureType"; - ObjectFlags[ObjectFlags["ObjectTypeKindMask"] = 50332991] = "ObjectTypeKindMask"; - ObjectFlags[ObjectFlags["ContainsSpread"] = 4194304] = "ContainsSpread"; - ObjectFlags[ObjectFlags["ObjectRestType"] = 8388608] = "ObjectRestType"; - ObjectFlags[ObjectFlags["IsClassInstanceClone"] = 67108864] = "IsClassInstanceClone"; - ObjectFlags[ObjectFlags["IdenticalBaseTypeCalculated"] = 134217728] = "IdenticalBaseTypeCalculated"; - ObjectFlags[ObjectFlags["IdenticalBaseTypeExists"] = 268435456] = "IdenticalBaseTypeExists"; - ObjectFlags[ObjectFlags["UnresolvedMembers"] = 536870912] = "UnresolvedMembers"; - ObjectFlags[ObjectFlags["FromTypeNode"] = 1073741824] = "FromTypeNode"; - ObjectFlags[ObjectFlags["IsGenericTypeComputed"] = 4194304] = "IsGenericTypeComputed"; - ObjectFlags[ObjectFlags["IsGenericObjectType"] = 8388608] = "IsGenericObjectType"; - ObjectFlags[ObjectFlags["IsGenericIndexType"] = 16777216] = "IsGenericIndexType"; - ObjectFlags[ObjectFlags["IsGenericType"] = 25165824] = "IsGenericType"; - ObjectFlags[ObjectFlags["ContainsIntersections"] = 33554432] = "ContainsIntersections"; - ObjectFlags[ObjectFlags["IsUnknownLikeUnionComputed"] = 67108864] = "IsUnknownLikeUnionComputed"; - ObjectFlags[ObjectFlags["IsUnknownLikeUnion"] = 134217728] = "IsUnknownLikeUnion"; - ObjectFlags[ObjectFlags["IsUniformEnumComputed"] = 268435456] = "IsUniformEnumComputed"; - ObjectFlags[ObjectFlags["IsUniformEnum"] = 536870912] = "IsUniformEnum"; - ObjectFlags[ObjectFlags["IsNeverIntersectionComputed"] = 33554432] = "IsNeverIntersectionComputed"; - ObjectFlags[ObjectFlags["IsNeverIntersection"] = 67108864] = "IsNeverIntersection"; - ObjectFlags[ObjectFlags["IsConstrainedTypeVariable"] = 134217728] = "IsConstrainedTypeVariable"; -})(ObjectFlags || (ObjectFlags = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. +export var ObjectFlags: any; +(function (ObjectFlags) { + ObjectFlags[ObjectFlags["None"] = 0] = "None"; + ObjectFlags[ObjectFlags["Class"] = 1] = "Class"; + ObjectFlags[ObjectFlags["Interface"] = 2] = "Interface"; + ObjectFlags[ObjectFlags["Reference"] = 4] = "Reference"; + ObjectFlags[ObjectFlags["Tuple"] = 8] = "Tuple"; + ObjectFlags[ObjectFlags["Anonymous"] = 16] = "Anonymous"; + ObjectFlags[ObjectFlags["Mapped"] = 32] = "Mapped"; + ObjectFlags[ObjectFlags["Instantiated"] = 64] = "Instantiated"; + ObjectFlags[ObjectFlags["ObjectLiteral"] = 128] = "ObjectLiteral"; + ObjectFlags[ObjectFlags["EvolvingArray"] = 256] = "EvolvingArray"; + ObjectFlags[ObjectFlags["ObjectLiteralPatternWithComputedProperties"] = 512] = "ObjectLiteralPatternWithComputedProperties"; + ObjectFlags[ObjectFlags["ReverseMapped"] = 1024] = "ReverseMapped"; + ObjectFlags[ObjectFlags["JsxAttributes"] = 2048] = "JsxAttributes"; + ObjectFlags[ObjectFlags["JSLiteral"] = 4096] = "JSLiteral"; + ObjectFlags[ObjectFlags["FreshLiteral"] = 8192] = "FreshLiteral"; + ObjectFlags[ObjectFlags["ArrayLiteral"] = 16384] = "ArrayLiteral"; + ObjectFlags[ObjectFlags["PrimitiveUnion"] = 32768] = "PrimitiveUnion"; + ObjectFlags[ObjectFlags["ContainsWideningType"] = 65536] = "ContainsWideningType"; + ObjectFlags[ObjectFlags["ContainsObjectOrArrayLiteral"] = 131072] = "ContainsObjectOrArrayLiteral"; + ObjectFlags[ObjectFlags["NonInferrableType"] = 262144] = "NonInferrableType"; + ObjectFlags[ObjectFlags["CouldContainTypeVariablesComputed"] = 524288] = "CouldContainTypeVariablesComputed"; + ObjectFlags[ObjectFlags["CouldContainTypeVariables"] = 1048576] = "CouldContainTypeVariables"; + ObjectFlags[ObjectFlags["MembersResolved"] = 2097152] = "MembersResolved"; + ObjectFlags[ObjectFlags["ClassOrInterface"] = 3] = "ClassOrInterface"; + ObjectFlags[ObjectFlags["RequiresWidening"] = 196608] = "RequiresWidening"; + ObjectFlags[ObjectFlags["PropagatingFlags"] = 458752] = "PropagatingFlags"; + ObjectFlags[ObjectFlags["InstantiatedMapped"] = 96] = "InstantiatedMapped"; + ObjectFlags[ObjectFlags["InstantiationExpressionType"] = 16777216] = "InstantiationExpressionType"; + ObjectFlags[ObjectFlags["SingleSignatureType"] = 33554432] = "SingleSignatureType"; + ObjectFlags[ObjectFlags["ObjectTypeKindMask"] = 50332991] = "ObjectTypeKindMask"; + ObjectFlags[ObjectFlags["ContainsSpread"] = 4194304] = "ContainsSpread"; + ObjectFlags[ObjectFlags["ObjectRestType"] = 8388608] = "ObjectRestType"; + ObjectFlags[ObjectFlags["IsClassInstanceClone"] = 67108864] = "IsClassInstanceClone"; + ObjectFlags[ObjectFlags["IdenticalBaseTypeCalculated"] = 134217728] = "IdenticalBaseTypeCalculated"; + ObjectFlags[ObjectFlags["IdenticalBaseTypeExists"] = 268435456] = "IdenticalBaseTypeExists"; + ObjectFlags[ObjectFlags["UnresolvedMembers"] = 536870912] = "UnresolvedMembers"; + ObjectFlags[ObjectFlags["FromTypeNode"] = 1073741824] = "FromTypeNode"; + ObjectFlags[ObjectFlags["IsGenericTypeComputed"] = 4194304] = "IsGenericTypeComputed"; + ObjectFlags[ObjectFlags["IsGenericObjectType"] = 8388608] = "IsGenericObjectType"; + ObjectFlags[ObjectFlags["IsGenericIndexType"] = 16777216] = "IsGenericIndexType"; + ObjectFlags[ObjectFlags["IsGenericType"] = 25165824] = "IsGenericType"; + ObjectFlags[ObjectFlags["ContainsIntersections"] = 33554432] = "ContainsIntersections"; + ObjectFlags[ObjectFlags["IsUnknownLikeUnionComputed"] = 67108864] = "IsUnknownLikeUnionComputed"; + ObjectFlags[ObjectFlags["IsUnknownLikeUnion"] = 134217728] = "IsUnknownLikeUnion"; + ObjectFlags[ObjectFlags["IsUniformEnumComputed"] = 268435456] = "IsUniformEnumComputed"; + ObjectFlags[ObjectFlags["IsUniformEnum"] = 536870912] = "IsUniformEnum"; + ObjectFlags[ObjectFlags["IsNeverIntersectionComputed"] = 33554432] = "IsNeverIntersectionComputed"; + ObjectFlags[ObjectFlags["IsNeverIntersection"] = 67108864] = "IsNeverIntersection"; + ObjectFlags[ObjectFlags["IsConstrainedTypeVariable"] = 134217728] = "IsConstrainedTypeVariable"; +})(ObjectFlags || (ObjectFlags = {})); diff --git a/packages/typescript/src/enums/outerExpressionKinds.enum.ts b/packages/typescript/src/enums/outerExpressionKinds.enum.ts index 5cf9d3a2b8440..f55fdf4eafbc2 100644 --- a/packages/typescript/src/enums/outerExpressionKinds.enum.ts +++ b/packages/typescript/src/enums/outerExpressionKinds.enum.ts @@ -1,17 +1,17 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/utilities.go. DO NOT EDIT. - -export enum OuterExpressionKinds { - Parentheses = 1 << 0, - TypeAssertions = 1 << 1, - NonNullAssertions = 1 << 2, - PartiallyEmittedExpressions = 1 << 3, - ExpressionsWithTypeArguments = 1 << 4, - Satisfies = 1 << 5, - ExcludeJSDocTypeAssertion = 1 << 6, - Assignments = 1 << 7, - Comma = 1 << 8, - Assertions = TypeAssertions | NonNullAssertions | Satisfies, - All = Parentheses | Assertions | PartiallyEmittedExpressions | ExpressionsWithTypeArguments, - AllExceptAssertionsOrExpressionsWithTypeArguments = All & ~Assertions & ~ExpressionsWithTypeArguments, - ExpressionTypePassthrough = Parentheses | Assignments | Comma, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/utilities.go. DO NOT EDIT. + +export enum OuterExpressionKinds { + Parentheses = 1 << 0, + TypeAssertions = 1 << 1, + NonNullAssertions = 1 << 2, + PartiallyEmittedExpressions = 1 << 3, + ExpressionsWithTypeArguments = 1 << 4, + Satisfies = 1 << 5, + ExcludeJSDocTypeAssertion = 1 << 6, + Assignments = 1 << 7, + Comma = 1 << 8, + Assertions = TypeAssertions | NonNullAssertions | Satisfies, + All = Parentheses | Assertions | PartiallyEmittedExpressions | ExpressionsWithTypeArguments, + AllExceptAssertionsOrExpressionsWithTypeArguments = All & ~Assertions & ~ExpressionsWithTypeArguments, + ExpressionTypePassthrough = Parentheses | Assignments | Comma, +} diff --git a/packages/typescript/src/enums/outerExpressionKinds.ts b/packages/typescript/src/enums/outerExpressionKinds.ts index 83a9ec9c75c33..f0e9791b59cf0 100644 --- a/packages/typescript/src/enums/outerExpressionKinds.ts +++ b/packages/typescript/src/enums/outerExpressionKinds.ts @@ -1,17 +1,17 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/utilities.go. DO NOT EDIT. -export var OuterExpressionKinds: any; -(function (OuterExpressionKinds) { - OuterExpressionKinds[OuterExpressionKinds["Parentheses"] = 1] = "Parentheses"; - OuterExpressionKinds[OuterExpressionKinds["TypeAssertions"] = 2] = "TypeAssertions"; - OuterExpressionKinds[OuterExpressionKinds["NonNullAssertions"] = 4] = "NonNullAssertions"; - OuterExpressionKinds[OuterExpressionKinds["PartiallyEmittedExpressions"] = 8] = "PartiallyEmittedExpressions"; - OuterExpressionKinds[OuterExpressionKinds["ExpressionsWithTypeArguments"] = 16] = "ExpressionsWithTypeArguments"; - OuterExpressionKinds[OuterExpressionKinds["Satisfies"] = 32] = "Satisfies"; - OuterExpressionKinds[OuterExpressionKinds["ExcludeJSDocTypeAssertion"] = 64] = "ExcludeJSDocTypeAssertion"; - OuterExpressionKinds[OuterExpressionKinds["Assignments"] = 128] = "Assignments"; - OuterExpressionKinds[OuterExpressionKinds["Comma"] = 256] = "Comma"; - OuterExpressionKinds[OuterExpressionKinds["Assertions"] = 38] = "Assertions"; - OuterExpressionKinds[OuterExpressionKinds["All"] = 63] = "All"; - OuterExpressionKinds[OuterExpressionKinds["AllExceptAssertionsOrExpressionsWithTypeArguments"] = 9] = "AllExceptAssertionsOrExpressionsWithTypeArguments"; - OuterExpressionKinds[OuterExpressionKinds["ExpressionTypePassthrough"] = 385] = "ExpressionTypePassthrough"; -})(OuterExpressionKinds || (OuterExpressionKinds = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/utilities.go. DO NOT EDIT. +export var OuterExpressionKinds: any; +(function (OuterExpressionKinds) { + OuterExpressionKinds[OuterExpressionKinds["Parentheses"] = 1] = "Parentheses"; + OuterExpressionKinds[OuterExpressionKinds["TypeAssertions"] = 2] = "TypeAssertions"; + OuterExpressionKinds[OuterExpressionKinds["NonNullAssertions"] = 4] = "NonNullAssertions"; + OuterExpressionKinds[OuterExpressionKinds["PartiallyEmittedExpressions"] = 8] = "PartiallyEmittedExpressions"; + OuterExpressionKinds[OuterExpressionKinds["ExpressionsWithTypeArguments"] = 16] = "ExpressionsWithTypeArguments"; + OuterExpressionKinds[OuterExpressionKinds["Satisfies"] = 32] = "Satisfies"; + OuterExpressionKinds[OuterExpressionKinds["ExcludeJSDocTypeAssertion"] = 64] = "ExcludeJSDocTypeAssertion"; + OuterExpressionKinds[OuterExpressionKinds["Assignments"] = 128] = "Assignments"; + OuterExpressionKinds[OuterExpressionKinds["Comma"] = 256] = "Comma"; + OuterExpressionKinds[OuterExpressionKinds["Assertions"] = 38] = "Assertions"; + OuterExpressionKinds[OuterExpressionKinds["All"] = 63] = "All"; + OuterExpressionKinds[OuterExpressionKinds["AllExceptAssertionsOrExpressionsWithTypeArguments"] = 9] = "AllExceptAssertionsOrExpressionsWithTypeArguments"; + OuterExpressionKinds[OuterExpressionKinds["ExpressionTypePassthrough"] = 385] = "ExpressionTypePassthrough"; +})(OuterExpressionKinds || (OuterExpressionKinds = {})); diff --git a/packages/typescript/src/enums/regularExpressionFlags.enum.ts b/packages/typescript/src/enums/regularExpressionFlags.enum.ts index 2f85b60897d60..7385fd13a53ab 100644 --- a/packages/typescript/src/enums/regularExpressionFlags.enum.ts +++ b/packages/typescript/src/enums/regularExpressionFlags.enum.ts @@ -1,12 +1,12 @@ -export enum RegularExpressionFlags { - None = 0, - HasIndices = 1 << 0, - Global = 1 << 1, - IgnoreCase = 1 << 2, - Multiline = 1 << 3, - DotAll = 1 << 4, - Unicode = 1 << 5, - UnicodeSets = 1 << 6, - Sticky = 1 << 7, - AnyUnicodeMode = Unicode | UnicodeSets, -} +export enum RegularExpressionFlags { + None = 0, + HasIndices = 1 << 0, + Global = 1 << 1, + IgnoreCase = 1 << 2, + Multiline = 1 << 3, + DotAll = 1 << 4, + Unicode = 1 << 5, + UnicodeSets = 1 << 6, + Sticky = 1 << 7, + AnyUnicodeMode = Unicode | UnicodeSets, +} diff --git a/packages/typescript/src/enums/regularExpressionFlags.ts b/packages/typescript/src/enums/regularExpressionFlags.ts index ddd797fc1f971..465cf540d564f 100644 --- a/packages/typescript/src/enums/regularExpressionFlags.ts +++ b/packages/typescript/src/enums/regularExpressionFlags.ts @@ -1,13 +1,13 @@ -export var RegularExpressionFlags: any; -(function (RegularExpressionFlags) { - RegularExpressionFlags[RegularExpressionFlags["None"] = 0] = "None"; - RegularExpressionFlags[RegularExpressionFlags["HasIndices"] = 1] = "HasIndices"; - RegularExpressionFlags[RegularExpressionFlags["Global"] = 2] = "Global"; - RegularExpressionFlags[RegularExpressionFlags["IgnoreCase"] = 4] = "IgnoreCase"; - RegularExpressionFlags[RegularExpressionFlags["Multiline"] = 8] = "Multiline"; - RegularExpressionFlags[RegularExpressionFlags["DotAll"] = 16] = "DotAll"; - RegularExpressionFlags[RegularExpressionFlags["Unicode"] = 32] = "Unicode"; - RegularExpressionFlags[RegularExpressionFlags["UnicodeSets"] = 64] = "UnicodeSets"; - RegularExpressionFlags[RegularExpressionFlags["Sticky"] = 128] = "Sticky"; - RegularExpressionFlags[RegularExpressionFlags["AnyUnicodeMode"] = 96] = "AnyUnicodeMode"; -})(RegularExpressionFlags || (RegularExpressionFlags = {})); +export var RegularExpressionFlags: any; +(function (RegularExpressionFlags) { + RegularExpressionFlags[RegularExpressionFlags["None"] = 0] = "None"; + RegularExpressionFlags[RegularExpressionFlags["HasIndices"] = 1] = "HasIndices"; + RegularExpressionFlags[RegularExpressionFlags["Global"] = 2] = "Global"; + RegularExpressionFlags[RegularExpressionFlags["IgnoreCase"] = 4] = "IgnoreCase"; + RegularExpressionFlags[RegularExpressionFlags["Multiline"] = 8] = "Multiline"; + RegularExpressionFlags[RegularExpressionFlags["DotAll"] = 16] = "DotAll"; + RegularExpressionFlags[RegularExpressionFlags["Unicode"] = 32] = "Unicode"; + RegularExpressionFlags[RegularExpressionFlags["UnicodeSets"] = 64] = "UnicodeSets"; + RegularExpressionFlags[RegularExpressionFlags["Sticky"] = 128] = "Sticky"; + RegularExpressionFlags[RegularExpressionFlags["AnyUnicodeMode"] = 96] = "AnyUnicodeMode"; +})(RegularExpressionFlags || (RegularExpressionFlags = {})); diff --git a/packages/typescript/src/enums/scriptKind.enum.ts b/packages/typescript/src/enums/scriptKind.enum.ts index 66b181d5b837c..c3374a651da3d 100644 --- a/packages/typescript/src/enums/scriptKind.enum.ts +++ b/packages/typescript/src/enums/scriptKind.enum.ts @@ -1,10 +1,10 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/core/scriptkind.go. DO NOT EDIT. - -export enum ScriptKind { - Unknown = 0, - JS = 1, - JSX = 2, - TS = 3, - TSX = 4, - JSON = 6, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/core/scriptkind.go. DO NOT EDIT. + +export enum ScriptKind { + Unknown = 0, + JS = 1, + JSX = 2, + TS = 3, + TSX = 4, + JSON = 6, +} diff --git a/packages/typescript/src/enums/scriptKind.ts b/packages/typescript/src/enums/scriptKind.ts index 5d9812af851db..bfba4a9cd235b 100644 --- a/packages/typescript/src/enums/scriptKind.ts +++ b/packages/typescript/src/enums/scriptKind.ts @@ -1,10 +1,10 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/core/scriptkind.go. DO NOT EDIT. -export var ScriptKind: any; -(function (ScriptKind) { - ScriptKind[ScriptKind["Unknown"] = 0] = "Unknown"; - ScriptKind[ScriptKind["JS"] = 1] = "JS"; - ScriptKind[ScriptKind["JSX"] = 2] = "JSX"; - ScriptKind[ScriptKind["TS"] = 3] = "TS"; - ScriptKind[ScriptKind["TSX"] = 4] = "TSX"; - ScriptKind[ScriptKind["JSON"] = 6] = "JSON"; -})(ScriptKind || (ScriptKind = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/core/scriptkind.go. DO NOT EDIT. +export var ScriptKind: any; +(function (ScriptKind) { + ScriptKind[ScriptKind["Unknown"] = 0] = "Unknown"; + ScriptKind[ScriptKind["JS"] = 1] = "JS"; + ScriptKind[ScriptKind["JSX"] = 2] = "JSX"; + ScriptKind[ScriptKind["TS"] = 3] = "TS"; + ScriptKind[ScriptKind["TSX"] = 4] = "TSX"; + ScriptKind[ScriptKind["JSON"] = 6] = "JSON"; +})(ScriptKind || (ScriptKind = {})); diff --git a/packages/typescript/src/enums/scriptTarget.enum.ts b/packages/typescript/src/enums/scriptTarget.enum.ts index beef0f0eeab76..e98b1fe1b1e30 100644 --- a/packages/typescript/src/enums/scriptTarget.enum.ts +++ b/packages/typescript/src/enums/scriptTarget.enum.ts @@ -1,16 +1,16 @@ -export enum ScriptTarget { - ES2015 = 2, - ES2016 = 3, - ES2017 = 4, - ES2018 = 5, - ES2019 = 6, - ES2020 = 7, - ES2021 = 8, - ES2022 = 9, - ES2023 = 10, - ES2024 = 11, - ES2025 = 12, - ESNext = 99, - JSON = 100, - Latest = ESNext, -} +export enum ScriptTarget { + ES2015 = 2, + ES2016 = 3, + ES2017 = 4, + ES2018 = 5, + ES2019 = 6, + ES2020 = 7, + ES2021 = 8, + ES2022 = 9, + ES2023 = 10, + ES2024 = 11, + ES2025 = 12, + ESNext = 99, + JSON = 100, + Latest = ESNext, +} diff --git a/packages/typescript/src/enums/scriptTarget.ts b/packages/typescript/src/enums/scriptTarget.ts index d750cba77a4bb..c6cfe76d6dc1b 100644 --- a/packages/typescript/src/enums/scriptTarget.ts +++ b/packages/typescript/src/enums/scriptTarget.ts @@ -1,17 +1,17 @@ -export var ScriptTarget: any; -(function (ScriptTarget) { - ScriptTarget[ScriptTarget["ES2015"] = 2] = "ES2015"; - ScriptTarget[ScriptTarget["ES2016"] = 3] = "ES2016"; - ScriptTarget[ScriptTarget["ES2017"] = 4] = "ES2017"; - ScriptTarget[ScriptTarget["ES2018"] = 5] = "ES2018"; - ScriptTarget[ScriptTarget["ES2019"] = 6] = "ES2019"; - ScriptTarget[ScriptTarget["ES2020"] = 7] = "ES2020"; - ScriptTarget[ScriptTarget["ES2021"] = 8] = "ES2021"; - ScriptTarget[ScriptTarget["ES2022"] = 9] = "ES2022"; - ScriptTarget[ScriptTarget["ES2023"] = 10] = "ES2023"; - ScriptTarget[ScriptTarget["ES2024"] = 11] = "ES2024"; - ScriptTarget[ScriptTarget["ES2025"] = 12] = "ES2025"; - ScriptTarget[ScriptTarget["ESNext"] = 99] = "ESNext"; - ScriptTarget[ScriptTarget["JSON"] = 100] = "JSON"; - ScriptTarget[ScriptTarget["Latest"] = 99] = "Latest"; -})(ScriptTarget || (ScriptTarget = {})); +export var ScriptTarget: any; +(function (ScriptTarget) { + ScriptTarget[ScriptTarget["ES2015"] = 2] = "ES2015"; + ScriptTarget[ScriptTarget["ES2016"] = 3] = "ES2016"; + ScriptTarget[ScriptTarget["ES2017"] = 4] = "ES2017"; + ScriptTarget[ScriptTarget["ES2018"] = 5] = "ES2018"; + ScriptTarget[ScriptTarget["ES2019"] = 6] = "ES2019"; + ScriptTarget[ScriptTarget["ES2020"] = 7] = "ES2020"; + ScriptTarget[ScriptTarget["ES2021"] = 8] = "ES2021"; + ScriptTarget[ScriptTarget["ES2022"] = 9] = "ES2022"; + ScriptTarget[ScriptTarget["ES2023"] = 10] = "ES2023"; + ScriptTarget[ScriptTarget["ES2024"] = 11] = "ES2024"; + ScriptTarget[ScriptTarget["ES2025"] = 12] = "ES2025"; + ScriptTarget[ScriptTarget["ESNext"] = 99] = "ESNext"; + ScriptTarget[ScriptTarget["JSON"] = 100] = "JSON"; + ScriptTarget[ScriptTarget["Latest"] = 99] = "Latest"; +})(ScriptTarget || (ScriptTarget = {})); diff --git a/packages/typescript/src/enums/signatureFlags.enum.ts b/packages/typescript/src/enums/signatureFlags.enum.ts index a3d7e51b6f923..8a96f85ffea30 100644 --- a/packages/typescript/src/enums/signatureFlags.enum.ts +++ b/packages/typescript/src/enums/signatureFlags.enum.ts @@ -1,16 +1,16 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. - -export enum SignatureFlags { - None = 0, - HasRestParameter = 1 << 0, - HasLiteralTypes = 1 << 1, - Construct = 1 << 2, - Abstract = 1 << 3, - IsInnerCallChain = 1 << 4, - IsOuterCallChain = 1 << 5, - IsUntypedSignatureInJSFile = 1 << 6, - IsNonInferrable = 1 << 7, - IsSignatureCandidateForOverloadFailure = 1 << 8, - PropagatingFlags = HasRestParameter | HasLiteralTypes | Construct | Abstract | IsUntypedSignatureInJSFile | IsSignatureCandidateForOverloadFailure, - CallChainFlags = IsInnerCallChain | IsOuterCallChain, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. + +export enum SignatureFlags { + None = 0, + HasRestParameter = 1 << 0, + HasLiteralTypes = 1 << 1, + Construct = 1 << 2, + Abstract = 1 << 3, + IsInnerCallChain = 1 << 4, + IsOuterCallChain = 1 << 5, + IsUntypedSignatureInJSFile = 1 << 6, + IsNonInferrable = 1 << 7, + IsSignatureCandidateForOverloadFailure = 1 << 8, + PropagatingFlags = HasRestParameter | HasLiteralTypes | Construct | Abstract | IsUntypedSignatureInJSFile | IsSignatureCandidateForOverloadFailure, + CallChainFlags = IsInnerCallChain | IsOuterCallChain, +} diff --git a/packages/typescript/src/enums/signatureFlags.ts b/packages/typescript/src/enums/signatureFlags.ts index aefca0a1055f8..372f662c3f310 100644 --- a/packages/typescript/src/enums/signatureFlags.ts +++ b/packages/typescript/src/enums/signatureFlags.ts @@ -1,16 +1,16 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. -export var SignatureFlags: any; -(function (SignatureFlags) { - SignatureFlags[SignatureFlags["None"] = 0] = "None"; - SignatureFlags[SignatureFlags["HasRestParameter"] = 1] = "HasRestParameter"; - SignatureFlags[SignatureFlags["HasLiteralTypes"] = 2] = "HasLiteralTypes"; - SignatureFlags[SignatureFlags["Construct"] = 4] = "Construct"; - SignatureFlags[SignatureFlags["Abstract"] = 8] = "Abstract"; - SignatureFlags[SignatureFlags["IsInnerCallChain"] = 16] = "IsInnerCallChain"; - SignatureFlags[SignatureFlags["IsOuterCallChain"] = 32] = "IsOuterCallChain"; - SignatureFlags[SignatureFlags["IsUntypedSignatureInJSFile"] = 64] = "IsUntypedSignatureInJSFile"; - SignatureFlags[SignatureFlags["IsNonInferrable"] = 128] = "IsNonInferrable"; - SignatureFlags[SignatureFlags["IsSignatureCandidateForOverloadFailure"] = 256] = "IsSignatureCandidateForOverloadFailure"; - SignatureFlags[SignatureFlags["PropagatingFlags"] = 335] = "PropagatingFlags"; - SignatureFlags[SignatureFlags["CallChainFlags"] = 48] = "CallChainFlags"; -})(SignatureFlags || (SignatureFlags = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. +export var SignatureFlags: any; +(function (SignatureFlags) { + SignatureFlags[SignatureFlags["None"] = 0] = "None"; + SignatureFlags[SignatureFlags["HasRestParameter"] = 1] = "HasRestParameter"; + SignatureFlags[SignatureFlags["HasLiteralTypes"] = 2] = "HasLiteralTypes"; + SignatureFlags[SignatureFlags["Construct"] = 4] = "Construct"; + SignatureFlags[SignatureFlags["Abstract"] = 8] = "Abstract"; + SignatureFlags[SignatureFlags["IsInnerCallChain"] = 16] = "IsInnerCallChain"; + SignatureFlags[SignatureFlags["IsOuterCallChain"] = 32] = "IsOuterCallChain"; + SignatureFlags[SignatureFlags["IsUntypedSignatureInJSFile"] = 64] = "IsUntypedSignatureInJSFile"; + SignatureFlags[SignatureFlags["IsNonInferrable"] = 128] = "IsNonInferrable"; + SignatureFlags[SignatureFlags["IsSignatureCandidateForOverloadFailure"] = 256] = "IsSignatureCandidateForOverloadFailure"; + SignatureFlags[SignatureFlags["PropagatingFlags"] = 335] = "PropagatingFlags"; + SignatureFlags[SignatureFlags["CallChainFlags"] = 48] = "CallChainFlags"; +})(SignatureFlags || (SignatureFlags = {})); diff --git a/packages/typescript/src/enums/signatureKind.enum.ts b/packages/typescript/src/enums/signatureKind.enum.ts index fe354479d617d..f748a6f744303 100644 --- a/packages/typescript/src/enums/signatureKind.enum.ts +++ b/packages/typescript/src/enums/signatureKind.enum.ts @@ -1,6 +1,6 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. - -export enum SignatureKind { - Call = 0, - Construct = 1, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. + +export enum SignatureKind { + Call = 0, + Construct = 1, +} diff --git a/packages/typescript/src/enums/signatureKind.ts b/packages/typescript/src/enums/signatureKind.ts index 9177cdd4a067d..150a82af54501 100644 --- a/packages/typescript/src/enums/signatureKind.ts +++ b/packages/typescript/src/enums/signatureKind.ts @@ -1,6 +1,6 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. -export var SignatureKind: any; -(function (SignatureKind) { - SignatureKind[SignatureKind["Call"] = 0] = "Call"; - SignatureKind[SignatureKind["Construct"] = 1] = "Construct"; -})(SignatureKind || (SignatureKind = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. +export var SignatureKind: any; +(function (SignatureKind) { + SignatureKind[SignatureKind["Call"] = 0] = "Call"; + SignatureKind[SignatureKind["Construct"] = 1] = "Construct"; +})(SignatureKind || (SignatureKind = {})); diff --git a/packages/typescript/src/enums/spanMapFeature.enum.ts b/packages/typescript/src/enums/spanMapFeature.enum.ts index 4bdd07a49e1c9..465e9ac7d3056 100644 --- a/packages/typescript/src/enums/spanMapFeature.enum.ts +++ b/packages/typescript/src/enums/spanMapFeature.enum.ts @@ -1,26 +1,26 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/spanmap/spanmap.go. DO NOT EDIT. - -export enum SpanMapFeature { - Hover = 1 << 0, - SignatureHelp = 1 << 1, - Completion = 1 << 2, - Definition = 1 << 3, - TypeDefinition = 1 << 4, - Implementation = 1 << 5, - References = 1 << 6, - DocumentHighlights = 1 << 7, - Rename = 1 << 8, - CallHierarchy = 1 << 9, - CodeActions = 1 << 10, - Formatting = 1 << 11, - InlayHints = 1 << 12, - SemanticTokens = 1 << 13, - FoldingRanges = 1 << 14, - SelectionRanges = 1 << 15, - LinkedEditing = 1 << 16, - AutoInsert = 1 << 17, - DocumentSymbols = 1 << 18, - CodeLens = 1 << 19, - None = 0, - All = (CodeLens << 1) - 1, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/spanmap/spanmap.go. DO NOT EDIT. + +export enum SpanMapFeature { + Hover = 1 << 0, + SignatureHelp = 1 << 1, + Completion = 1 << 2, + Definition = 1 << 3, + TypeDefinition = 1 << 4, + Implementation = 1 << 5, + References = 1 << 6, + DocumentHighlights = 1 << 7, + Rename = 1 << 8, + CallHierarchy = 1 << 9, + CodeActions = 1 << 10, + Formatting = 1 << 11, + InlayHints = 1 << 12, + SemanticTokens = 1 << 13, + FoldingRanges = 1 << 14, + SelectionRanges = 1 << 15, + LinkedEditing = 1 << 16, + AutoInsert = 1 << 17, + DocumentSymbols = 1 << 18, + CodeLens = 1 << 19, + None = 0, + All = (CodeLens << 1) - 1, +} diff --git a/packages/typescript/src/enums/spanMapFeature.ts b/packages/typescript/src/enums/spanMapFeature.ts index 625ff58ae10b3..35f474d5e9f2f 100644 --- a/packages/typescript/src/enums/spanMapFeature.ts +++ b/packages/typescript/src/enums/spanMapFeature.ts @@ -1,26 +1,26 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/spanmap/spanmap.go. DO NOT EDIT. -export var SpanMapFeature: any; -(function (SpanMapFeature) { - SpanMapFeature[SpanMapFeature["Hover"] = 1] = "Hover"; - SpanMapFeature[SpanMapFeature["SignatureHelp"] = 2] = "SignatureHelp"; - SpanMapFeature[SpanMapFeature["Completion"] = 4] = "Completion"; - SpanMapFeature[SpanMapFeature["Definition"] = 8] = "Definition"; - SpanMapFeature[SpanMapFeature["TypeDefinition"] = 16] = "TypeDefinition"; - SpanMapFeature[SpanMapFeature["Implementation"] = 32] = "Implementation"; - SpanMapFeature[SpanMapFeature["References"] = 64] = "References"; - SpanMapFeature[SpanMapFeature["DocumentHighlights"] = 128] = "DocumentHighlights"; - SpanMapFeature[SpanMapFeature["Rename"] = 256] = "Rename"; - SpanMapFeature[SpanMapFeature["CallHierarchy"] = 512] = "CallHierarchy"; - SpanMapFeature[SpanMapFeature["CodeActions"] = 1024] = "CodeActions"; - SpanMapFeature[SpanMapFeature["Formatting"] = 2048] = "Formatting"; - SpanMapFeature[SpanMapFeature["InlayHints"] = 4096] = "InlayHints"; - SpanMapFeature[SpanMapFeature["SemanticTokens"] = 8192] = "SemanticTokens"; - SpanMapFeature[SpanMapFeature["FoldingRanges"] = 16384] = "FoldingRanges"; - SpanMapFeature[SpanMapFeature["SelectionRanges"] = 32768] = "SelectionRanges"; - SpanMapFeature[SpanMapFeature["LinkedEditing"] = 65536] = "LinkedEditing"; - SpanMapFeature[SpanMapFeature["AutoInsert"] = 131072] = "AutoInsert"; - SpanMapFeature[SpanMapFeature["DocumentSymbols"] = 262144] = "DocumentSymbols"; - SpanMapFeature[SpanMapFeature["CodeLens"] = 524288] = "CodeLens"; - SpanMapFeature[SpanMapFeature["None"] = 0] = "None"; - SpanMapFeature[SpanMapFeature["All"] = 1048575] = "All"; -})(SpanMapFeature || (SpanMapFeature = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/spanmap/spanmap.go. DO NOT EDIT. +export var SpanMapFeature: any; +(function (SpanMapFeature) { + SpanMapFeature[SpanMapFeature["Hover"] = 1] = "Hover"; + SpanMapFeature[SpanMapFeature["SignatureHelp"] = 2] = "SignatureHelp"; + SpanMapFeature[SpanMapFeature["Completion"] = 4] = "Completion"; + SpanMapFeature[SpanMapFeature["Definition"] = 8] = "Definition"; + SpanMapFeature[SpanMapFeature["TypeDefinition"] = 16] = "TypeDefinition"; + SpanMapFeature[SpanMapFeature["Implementation"] = 32] = "Implementation"; + SpanMapFeature[SpanMapFeature["References"] = 64] = "References"; + SpanMapFeature[SpanMapFeature["DocumentHighlights"] = 128] = "DocumentHighlights"; + SpanMapFeature[SpanMapFeature["Rename"] = 256] = "Rename"; + SpanMapFeature[SpanMapFeature["CallHierarchy"] = 512] = "CallHierarchy"; + SpanMapFeature[SpanMapFeature["CodeActions"] = 1024] = "CodeActions"; + SpanMapFeature[SpanMapFeature["Formatting"] = 2048] = "Formatting"; + SpanMapFeature[SpanMapFeature["InlayHints"] = 4096] = "InlayHints"; + SpanMapFeature[SpanMapFeature["SemanticTokens"] = 8192] = "SemanticTokens"; + SpanMapFeature[SpanMapFeature["FoldingRanges"] = 16384] = "FoldingRanges"; + SpanMapFeature[SpanMapFeature["SelectionRanges"] = 32768] = "SelectionRanges"; + SpanMapFeature[SpanMapFeature["LinkedEditing"] = 65536] = "LinkedEditing"; + SpanMapFeature[SpanMapFeature["AutoInsert"] = 131072] = "AutoInsert"; + SpanMapFeature[SpanMapFeature["DocumentSymbols"] = 262144] = "DocumentSymbols"; + SpanMapFeature[SpanMapFeature["CodeLens"] = 524288] = "CodeLens"; + SpanMapFeature[SpanMapFeature["None"] = 0] = "None"; + SpanMapFeature[SpanMapFeature["All"] = 1048575] = "All"; +})(SpanMapFeature || (SpanMapFeature = {})); diff --git a/packages/typescript/src/enums/spanMapFidelity.enum.ts b/packages/typescript/src/enums/spanMapFidelity.enum.ts index 00c9a498c74dd..2bbafd378daeb 100644 --- a/packages/typescript/src/enums/spanMapFidelity.enum.ts +++ b/packages/typescript/src/enums/spanMapFidelity.enum.ts @@ -1,8 +1,8 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/spanmap/spanmap.go. DO NOT EDIT. - -export enum SpanMapFidelity { - Exact = 0, - Atom = 1, - Approximate = 2, - None = 3, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/spanmap/spanmap.go. DO NOT EDIT. + +export enum SpanMapFidelity { + Exact = 0, + Atom = 1, + Approximate = 2, + None = 3, +} diff --git a/packages/typescript/src/enums/spanMapFidelity.ts b/packages/typescript/src/enums/spanMapFidelity.ts index 2a4f92ecb0178..70227ee9ffdbf 100644 --- a/packages/typescript/src/enums/spanMapFidelity.ts +++ b/packages/typescript/src/enums/spanMapFidelity.ts @@ -1,8 +1,8 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/spanmap/spanmap.go. DO NOT EDIT. -export var SpanMapFidelity: any; -(function (SpanMapFidelity) { - SpanMapFidelity[SpanMapFidelity["Exact"] = 0] = "Exact"; - SpanMapFidelity[SpanMapFidelity["Atom"] = 1] = "Atom"; - SpanMapFidelity[SpanMapFidelity["Approximate"] = 2] = "Approximate"; - SpanMapFidelity[SpanMapFidelity["None"] = 3] = "None"; -})(SpanMapFidelity || (SpanMapFidelity = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/spanmap/spanmap.go. DO NOT EDIT. +export var SpanMapFidelity: any; +(function (SpanMapFidelity) { + SpanMapFidelity[SpanMapFidelity["Exact"] = 0] = "Exact"; + SpanMapFidelity[SpanMapFidelity["Atom"] = 1] = "Atom"; + SpanMapFidelity[SpanMapFidelity["Approximate"] = 2] = "Approximate"; + SpanMapFidelity[SpanMapFidelity["None"] = 3] = "None"; +})(SpanMapFidelity || (SpanMapFidelity = {})); diff --git a/packages/typescript/src/enums/spanMapKind.enum.ts b/packages/typescript/src/enums/spanMapKind.enum.ts index db71ed3b2f015..f8c1588596077 100644 --- a/packages/typescript/src/enums/spanMapKind.enum.ts +++ b/packages/typescript/src/enums/spanMapKind.enum.ts @@ -1,7 +1,7 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/spanmap/spanmap.go. DO NOT EDIT. - -export enum SpanMapKind { - Verbatim = 0, - Atom = 1, - Alias = 2, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/spanmap/spanmap.go. DO NOT EDIT. + +export enum SpanMapKind { + Verbatim = 0, + Atom = 1, + Alias = 2, +} diff --git a/packages/typescript/src/enums/spanMapKind.ts b/packages/typescript/src/enums/spanMapKind.ts index e514215da849c..c74f4293b32ff 100644 --- a/packages/typescript/src/enums/spanMapKind.ts +++ b/packages/typescript/src/enums/spanMapKind.ts @@ -1,7 +1,7 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/spanmap/spanmap.go. DO NOT EDIT. -export var SpanMapKind: any; -(function (SpanMapKind) { - SpanMapKind[SpanMapKind["Verbatim"] = 0] = "Verbatim"; - SpanMapKind[SpanMapKind["Atom"] = 1] = "Atom"; - SpanMapKind[SpanMapKind["Alias"] = 2] = "Alias"; -})(SpanMapKind || (SpanMapKind = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/spanmap/spanmap.go. DO NOT EDIT. +export var SpanMapKind: any; +(function (SpanMapKind) { + SpanMapKind[SpanMapKind["Verbatim"] = 0] = "Verbatim"; + SpanMapKind[SpanMapKind["Atom"] = 1] = "Atom"; + SpanMapKind[SpanMapKind["Alias"] = 2] = "Alias"; +})(SpanMapKind || (SpanMapKind = {})); diff --git a/packages/typescript/src/enums/symbolFlags.enum.ts b/packages/typescript/src/enums/symbolFlags.enum.ts index 96401cdd4a4e4..1a9da398fe53b 100644 --- a/packages/typescript/src/enums/symbolFlags.enum.ts +++ b/packages/typescript/src/enums/symbolFlags.enum.ts @@ -1,71 +1,71 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/symbolflags.go. DO NOT EDIT. - -export enum SymbolFlags { - None = 0, - FunctionScopedVariable = 1 << 0, - BlockScopedVariable = 1 << 1, - Property = 1 << 2, - EnumMember = 1 << 3, - Function = 1 << 4, - Class = 1 << 5, - Interface = 1 << 6, - ConstEnum = 1 << 7, - RegularEnum = 1 << 8, - ValueModule = 1 << 9, - NamespaceModule = 1 << 10, - TypeLiteral = 1 << 11, - ObjectLiteral = 1 << 12, - Method = 1 << 13, - Constructor = 1 << 14, - GetAccessor = 1 << 15, - SetAccessor = 1 << 16, - Signature = 1 << 17, - TypeParameter = 1 << 18, - TypeAlias = 1 << 19, - ExportValue = 1 << 20, - Alias = 1 << 21, - Prototype = 1 << 22, - ExportStar = 1 << 23, - Optional = 1 << 24, - Transient = 1 << 25, - Assignment = 1 << 26, - ModuleExports = 1 << 27, - ConstEnumOnlyModule = 1 << 28, - ReplaceableByMethod = 1 << 29, - GlobalLookup = 1 << 30, - All = 1 << 30 - 1, - Enum = RegularEnum | ConstEnum, - Variable = FunctionScopedVariable | BlockScopedVariable, - Value = Variable | Property | EnumMember | ObjectLiteral | Function | Class | Enum | ValueModule | Method | GetAccessor | SetAccessor, - Type = Class | Interface | Enum | EnumMember | TypeLiteral | TypeParameter | TypeAlias, - Namespace = ValueModule | NamespaceModule | Enum, - Module = ValueModule | NamespaceModule, - Accessor = GetAccessor | SetAccessor, - FunctionScopedVariableExcludes = Value & ~FunctionScopedVariable, - BlockScopedVariableExcludes = Value, - ParameterExcludes = Value, - PropertyExcludes = Value & ~(Property | Accessor), - EnumMemberExcludes = Value | Type, - FunctionExcludes = Value & ~(Function | ValueModule | Class), - ClassExcludes = (Value | Type) & ~(ValueModule | Interface | Function), - InterfaceExcludes = Type & ~(Interface | Class), - RegularEnumExcludes = (Value | Type) & ~(RegularEnum | ValueModule), - ConstEnumExcludes = (Value | Type) & ~ConstEnum, - ValueModuleExcludes = Value & ~(Function | Class | RegularEnum | ValueModule), - NamespaceModuleExcludes = None, - MethodExcludes = Value & ~Method, - GetAccessorExcludes = Value & ~(SetAccessor | Property), - SetAccessorExcludes = Value & ~(GetAccessor | Property), - AccessorExcludes = Value & ~Property, - TypeParameterExcludes = Type & ~TypeParameter, - TypeAliasExcludes = Type, - AliasExcludes = Alias, - ModuleMember = Variable | Function | Class | Interface | Enum | Module | TypeAlias | Alias, - ExportHasLocal = Function | Class | Enum | ValueModule, - BlockScoped = BlockScopedVariable | Class | Enum, - PropertyOrAccessor = Property | Accessor, - ClassMember = Method | Accessor | Property, - ExportSupportsDefaultModifier = Class | Function | Interface, - ExportDoesNotSupportDefaultModifier = ~ExportSupportsDefaultModifier, - LateBindingContainer = Class | Interface | TypeLiteral | ObjectLiteral | Function, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/symbolflags.go. DO NOT EDIT. + +export enum SymbolFlags { + None = 0, + FunctionScopedVariable = 1 << 0, + BlockScopedVariable = 1 << 1, + Property = 1 << 2, + EnumMember = 1 << 3, + Function = 1 << 4, + Class = 1 << 5, + Interface = 1 << 6, + ConstEnum = 1 << 7, + RegularEnum = 1 << 8, + ValueModule = 1 << 9, + NamespaceModule = 1 << 10, + TypeLiteral = 1 << 11, + ObjectLiteral = 1 << 12, + Method = 1 << 13, + Constructor = 1 << 14, + GetAccessor = 1 << 15, + SetAccessor = 1 << 16, + Signature = 1 << 17, + TypeParameter = 1 << 18, + TypeAlias = 1 << 19, + ExportValue = 1 << 20, + Alias = 1 << 21, + Prototype = 1 << 22, + ExportStar = 1 << 23, + Optional = 1 << 24, + Transient = 1 << 25, + Assignment = 1 << 26, + ModuleExports = 1 << 27, + ConstEnumOnlyModule = 1 << 28, + ReplaceableByMethod = 1 << 29, + GlobalLookup = 1 << 30, + All = 1 << 30 - 1, + Enum = RegularEnum | ConstEnum, + Variable = FunctionScopedVariable | BlockScopedVariable, + Value = Variable | Property | EnumMember | ObjectLiteral | Function | Class | Enum | ValueModule | Method | GetAccessor | SetAccessor, + Type = Class | Interface | Enum | EnumMember | TypeLiteral | TypeParameter | TypeAlias, + Namespace = ValueModule | NamespaceModule | Enum, + Module = ValueModule | NamespaceModule, + Accessor = GetAccessor | SetAccessor, + FunctionScopedVariableExcludes = Value & ~FunctionScopedVariable, + BlockScopedVariableExcludes = Value, + ParameterExcludes = Value, + PropertyExcludes = Value & ~(Property | Accessor), + EnumMemberExcludes = Value | Type, + FunctionExcludes = Value & ~(Function | ValueModule | Class), + ClassExcludes = (Value | Type) & ~(ValueModule | Interface | Function), + InterfaceExcludes = Type & ~(Interface | Class), + RegularEnumExcludes = (Value | Type) & ~(RegularEnum | ValueModule), + ConstEnumExcludes = (Value | Type) & ~ConstEnum, + ValueModuleExcludes = Value & ~(Function | Class | RegularEnum | ValueModule), + NamespaceModuleExcludes = None, + MethodExcludes = Value & ~Method, + GetAccessorExcludes = Value & ~(SetAccessor | Property), + SetAccessorExcludes = Value & ~(GetAccessor | Property), + AccessorExcludes = Value & ~Property, + TypeParameterExcludes = Type & ~TypeParameter, + TypeAliasExcludes = Type, + AliasExcludes = Alias, + ModuleMember = Variable | Function | Class | Interface | Enum | Module | TypeAlias | Alias, + ExportHasLocal = Function | Class | Enum | ValueModule, + BlockScoped = BlockScopedVariable | Class | Enum, + PropertyOrAccessor = Property | Accessor, + ClassMember = Method | Accessor | Property, + ExportSupportsDefaultModifier = Class | Function | Interface, + ExportDoesNotSupportDefaultModifier = ~ExportSupportsDefaultModifier, + LateBindingContainer = Class | Interface | TypeLiteral | ObjectLiteral | Function, +} diff --git a/packages/typescript/src/enums/symbolFlags.ts b/packages/typescript/src/enums/symbolFlags.ts index 85e3c29ee08d2..d657716693a1f 100644 --- a/packages/typescript/src/enums/symbolFlags.ts +++ b/packages/typescript/src/enums/symbolFlags.ts @@ -1,71 +1,71 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/symbolflags.go. DO NOT EDIT. -export var SymbolFlags: any; -(function (SymbolFlags) { - SymbolFlags[SymbolFlags["None"] = 0] = "None"; - SymbolFlags[SymbolFlags["FunctionScopedVariable"] = 1] = "FunctionScopedVariable"; - SymbolFlags[SymbolFlags["BlockScopedVariable"] = 2] = "BlockScopedVariable"; - SymbolFlags[SymbolFlags["Property"] = 4] = "Property"; - SymbolFlags[SymbolFlags["EnumMember"] = 8] = "EnumMember"; - SymbolFlags[SymbolFlags["Function"] = 16] = "Function"; - SymbolFlags[SymbolFlags["Class"] = 32] = "Class"; - SymbolFlags[SymbolFlags["Interface"] = 64] = "Interface"; - SymbolFlags[SymbolFlags["ConstEnum"] = 128] = "ConstEnum"; - SymbolFlags[SymbolFlags["RegularEnum"] = 256] = "RegularEnum"; - SymbolFlags[SymbolFlags["ValueModule"] = 512] = "ValueModule"; - SymbolFlags[SymbolFlags["NamespaceModule"] = 1024] = "NamespaceModule"; - SymbolFlags[SymbolFlags["TypeLiteral"] = 2048] = "TypeLiteral"; - SymbolFlags[SymbolFlags["ObjectLiteral"] = 4096] = "ObjectLiteral"; - SymbolFlags[SymbolFlags["Method"] = 8192] = "Method"; - SymbolFlags[SymbolFlags["Constructor"] = 16384] = "Constructor"; - SymbolFlags[SymbolFlags["GetAccessor"] = 32768] = "GetAccessor"; - SymbolFlags[SymbolFlags["SetAccessor"] = 65536] = "SetAccessor"; - SymbolFlags[SymbolFlags["Signature"] = 131072] = "Signature"; - SymbolFlags[SymbolFlags["TypeParameter"] = 262144] = "TypeParameter"; - SymbolFlags[SymbolFlags["TypeAlias"] = 524288] = "TypeAlias"; - SymbolFlags[SymbolFlags["ExportValue"] = 1048576] = "ExportValue"; - SymbolFlags[SymbolFlags["Alias"] = 2097152] = "Alias"; - SymbolFlags[SymbolFlags["Prototype"] = 4194304] = "Prototype"; - SymbolFlags[SymbolFlags["ExportStar"] = 8388608] = "ExportStar"; - SymbolFlags[SymbolFlags["Optional"] = 16777216] = "Optional"; - SymbolFlags[SymbolFlags["Transient"] = 33554432] = "Transient"; - SymbolFlags[SymbolFlags["Assignment"] = 67108864] = "Assignment"; - SymbolFlags[SymbolFlags["ModuleExports"] = 134217728] = "ModuleExports"; - SymbolFlags[SymbolFlags["ConstEnumOnlyModule"] = 268435456] = "ConstEnumOnlyModule"; - SymbolFlags[SymbolFlags["ReplaceableByMethod"] = 536870912] = "ReplaceableByMethod"; - SymbolFlags[SymbolFlags["GlobalLookup"] = 1073741824] = "GlobalLookup"; - SymbolFlags[SymbolFlags["All"] = 536870912] = "All"; - SymbolFlags[SymbolFlags["Enum"] = 384] = "Enum"; - SymbolFlags[SymbolFlags["Variable"] = 3] = "Variable"; - SymbolFlags[SymbolFlags["Value"] = 111551] = "Value"; - SymbolFlags[SymbolFlags["Type"] = 788968] = "Type"; - SymbolFlags[SymbolFlags["Namespace"] = 1920] = "Namespace"; - SymbolFlags[SymbolFlags["Module"] = 1536] = "Module"; - SymbolFlags[SymbolFlags["Accessor"] = 98304] = "Accessor"; - SymbolFlags[SymbolFlags["FunctionScopedVariableExcludes"] = 111550] = "FunctionScopedVariableExcludes"; - SymbolFlags[SymbolFlags["BlockScopedVariableExcludes"] = 111551] = "BlockScopedVariableExcludes"; - SymbolFlags[SymbolFlags["ParameterExcludes"] = 111551] = "ParameterExcludes"; - SymbolFlags[SymbolFlags["PropertyExcludes"] = 13243] = "PropertyExcludes"; - SymbolFlags[SymbolFlags["EnumMemberExcludes"] = 900095] = "EnumMemberExcludes"; - SymbolFlags[SymbolFlags["FunctionExcludes"] = 110991] = "FunctionExcludes"; - SymbolFlags[SymbolFlags["ClassExcludes"] = 899503] = "ClassExcludes"; - SymbolFlags[SymbolFlags["InterfaceExcludes"] = 788872] = "InterfaceExcludes"; - SymbolFlags[SymbolFlags["RegularEnumExcludes"] = 899327] = "RegularEnumExcludes"; - SymbolFlags[SymbolFlags["ConstEnumExcludes"] = 899967] = "ConstEnumExcludes"; - SymbolFlags[SymbolFlags["ValueModuleExcludes"] = 110735] = "ValueModuleExcludes"; - SymbolFlags[SymbolFlags["NamespaceModuleExcludes"] = 0] = "NamespaceModuleExcludes"; - SymbolFlags[SymbolFlags["MethodExcludes"] = 103359] = "MethodExcludes"; - SymbolFlags[SymbolFlags["GetAccessorExcludes"] = 46011] = "GetAccessorExcludes"; - SymbolFlags[SymbolFlags["SetAccessorExcludes"] = 78779] = "SetAccessorExcludes"; - SymbolFlags[SymbolFlags["AccessorExcludes"] = 111547] = "AccessorExcludes"; - SymbolFlags[SymbolFlags["TypeParameterExcludes"] = 526824] = "TypeParameterExcludes"; - SymbolFlags[SymbolFlags["TypeAliasExcludes"] = 788968] = "TypeAliasExcludes"; - SymbolFlags[SymbolFlags["AliasExcludes"] = 2097152] = "AliasExcludes"; - SymbolFlags[SymbolFlags["ModuleMember"] = 2623475] = "ModuleMember"; - SymbolFlags[SymbolFlags["ExportHasLocal"] = 944] = "ExportHasLocal"; - SymbolFlags[SymbolFlags["BlockScoped"] = 418] = "BlockScoped"; - SymbolFlags[SymbolFlags["PropertyOrAccessor"] = 98308] = "PropertyOrAccessor"; - SymbolFlags[SymbolFlags["ClassMember"] = 106500] = "ClassMember"; - SymbolFlags[SymbolFlags["ExportSupportsDefaultModifier"] = 112] = "ExportSupportsDefaultModifier"; - SymbolFlags[SymbolFlags["ExportDoesNotSupportDefaultModifier"] = -113] = "ExportDoesNotSupportDefaultModifier"; - SymbolFlags[SymbolFlags["LateBindingContainer"] = 6256] = "LateBindingContainer"; -})(SymbolFlags || (SymbolFlags = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/symbolflags.go. DO NOT EDIT. +export var SymbolFlags: any; +(function (SymbolFlags) { + SymbolFlags[SymbolFlags["None"] = 0] = "None"; + SymbolFlags[SymbolFlags["FunctionScopedVariable"] = 1] = "FunctionScopedVariable"; + SymbolFlags[SymbolFlags["BlockScopedVariable"] = 2] = "BlockScopedVariable"; + SymbolFlags[SymbolFlags["Property"] = 4] = "Property"; + SymbolFlags[SymbolFlags["EnumMember"] = 8] = "EnumMember"; + SymbolFlags[SymbolFlags["Function"] = 16] = "Function"; + SymbolFlags[SymbolFlags["Class"] = 32] = "Class"; + SymbolFlags[SymbolFlags["Interface"] = 64] = "Interface"; + SymbolFlags[SymbolFlags["ConstEnum"] = 128] = "ConstEnum"; + SymbolFlags[SymbolFlags["RegularEnum"] = 256] = "RegularEnum"; + SymbolFlags[SymbolFlags["ValueModule"] = 512] = "ValueModule"; + SymbolFlags[SymbolFlags["NamespaceModule"] = 1024] = "NamespaceModule"; + SymbolFlags[SymbolFlags["TypeLiteral"] = 2048] = "TypeLiteral"; + SymbolFlags[SymbolFlags["ObjectLiteral"] = 4096] = "ObjectLiteral"; + SymbolFlags[SymbolFlags["Method"] = 8192] = "Method"; + SymbolFlags[SymbolFlags["Constructor"] = 16384] = "Constructor"; + SymbolFlags[SymbolFlags["GetAccessor"] = 32768] = "GetAccessor"; + SymbolFlags[SymbolFlags["SetAccessor"] = 65536] = "SetAccessor"; + SymbolFlags[SymbolFlags["Signature"] = 131072] = "Signature"; + SymbolFlags[SymbolFlags["TypeParameter"] = 262144] = "TypeParameter"; + SymbolFlags[SymbolFlags["TypeAlias"] = 524288] = "TypeAlias"; + SymbolFlags[SymbolFlags["ExportValue"] = 1048576] = "ExportValue"; + SymbolFlags[SymbolFlags["Alias"] = 2097152] = "Alias"; + SymbolFlags[SymbolFlags["Prototype"] = 4194304] = "Prototype"; + SymbolFlags[SymbolFlags["ExportStar"] = 8388608] = "ExportStar"; + SymbolFlags[SymbolFlags["Optional"] = 16777216] = "Optional"; + SymbolFlags[SymbolFlags["Transient"] = 33554432] = "Transient"; + SymbolFlags[SymbolFlags["Assignment"] = 67108864] = "Assignment"; + SymbolFlags[SymbolFlags["ModuleExports"] = 134217728] = "ModuleExports"; + SymbolFlags[SymbolFlags["ConstEnumOnlyModule"] = 268435456] = "ConstEnumOnlyModule"; + SymbolFlags[SymbolFlags["ReplaceableByMethod"] = 536870912] = "ReplaceableByMethod"; + SymbolFlags[SymbolFlags["GlobalLookup"] = 1073741824] = "GlobalLookup"; + SymbolFlags[SymbolFlags["All"] = 536870912] = "All"; + SymbolFlags[SymbolFlags["Enum"] = 384] = "Enum"; + SymbolFlags[SymbolFlags["Variable"] = 3] = "Variable"; + SymbolFlags[SymbolFlags["Value"] = 111551] = "Value"; + SymbolFlags[SymbolFlags["Type"] = 788968] = "Type"; + SymbolFlags[SymbolFlags["Namespace"] = 1920] = "Namespace"; + SymbolFlags[SymbolFlags["Module"] = 1536] = "Module"; + SymbolFlags[SymbolFlags["Accessor"] = 98304] = "Accessor"; + SymbolFlags[SymbolFlags["FunctionScopedVariableExcludes"] = 111550] = "FunctionScopedVariableExcludes"; + SymbolFlags[SymbolFlags["BlockScopedVariableExcludes"] = 111551] = "BlockScopedVariableExcludes"; + SymbolFlags[SymbolFlags["ParameterExcludes"] = 111551] = "ParameterExcludes"; + SymbolFlags[SymbolFlags["PropertyExcludes"] = 13243] = "PropertyExcludes"; + SymbolFlags[SymbolFlags["EnumMemberExcludes"] = 900095] = "EnumMemberExcludes"; + SymbolFlags[SymbolFlags["FunctionExcludes"] = 110991] = "FunctionExcludes"; + SymbolFlags[SymbolFlags["ClassExcludes"] = 899503] = "ClassExcludes"; + SymbolFlags[SymbolFlags["InterfaceExcludes"] = 788872] = "InterfaceExcludes"; + SymbolFlags[SymbolFlags["RegularEnumExcludes"] = 899327] = "RegularEnumExcludes"; + SymbolFlags[SymbolFlags["ConstEnumExcludes"] = 899967] = "ConstEnumExcludes"; + SymbolFlags[SymbolFlags["ValueModuleExcludes"] = 110735] = "ValueModuleExcludes"; + SymbolFlags[SymbolFlags["NamespaceModuleExcludes"] = 0] = "NamespaceModuleExcludes"; + SymbolFlags[SymbolFlags["MethodExcludes"] = 103359] = "MethodExcludes"; + SymbolFlags[SymbolFlags["GetAccessorExcludes"] = 46011] = "GetAccessorExcludes"; + SymbolFlags[SymbolFlags["SetAccessorExcludes"] = 78779] = "SetAccessorExcludes"; + SymbolFlags[SymbolFlags["AccessorExcludes"] = 111547] = "AccessorExcludes"; + SymbolFlags[SymbolFlags["TypeParameterExcludes"] = 526824] = "TypeParameterExcludes"; + SymbolFlags[SymbolFlags["TypeAliasExcludes"] = 788968] = "TypeAliasExcludes"; + SymbolFlags[SymbolFlags["AliasExcludes"] = 2097152] = "AliasExcludes"; + SymbolFlags[SymbolFlags["ModuleMember"] = 2623475] = "ModuleMember"; + SymbolFlags[SymbolFlags["ExportHasLocal"] = 944] = "ExportHasLocal"; + SymbolFlags[SymbolFlags["BlockScoped"] = 418] = "BlockScoped"; + SymbolFlags[SymbolFlags["PropertyOrAccessor"] = 98308] = "PropertyOrAccessor"; + SymbolFlags[SymbolFlags["ClassMember"] = 106500] = "ClassMember"; + SymbolFlags[SymbolFlags["ExportSupportsDefaultModifier"] = 112] = "ExportSupportsDefaultModifier"; + SymbolFlags[SymbolFlags["ExportDoesNotSupportDefaultModifier"] = -113] = "ExportDoesNotSupportDefaultModifier"; + SymbolFlags[SymbolFlags["LateBindingContainer"] = 6256] = "LateBindingContainer"; +})(SymbolFlags || (SymbolFlags = {})); diff --git a/packages/typescript/src/enums/syntaxKind.enum.ts b/packages/typescript/src/enums/syntaxKind.enum.ts index 400c16806abf5..cc85b21f63721 100644 --- a/packages/typescript/src/enums/syntaxKind.enum.ts +++ b/packages/typescript/src/enums/syntaxKind.enum.ts @@ -1,390 +1,390 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/kind_generated.go. DO NOT EDIT. - -export enum SyntaxKind { - Unknown = 0, - EndOfFile = 1, - SingleLineCommentTrivia = 2, - MultiLineCommentTrivia = 3, - NewLineTrivia = 4, - WhitespaceTrivia = 5, - ConflictMarkerTrivia = 6, - NonTextFileMarkerTrivia = 7, - NumericLiteral = 8, - BigIntLiteral = 9, - StringLiteral = 10, - JsxText = 11, - JsxTextAllWhiteSpaces = 12, - RegularExpressionLiteral = 13, - NoSubstitutionTemplateLiteral = 14, - TemplateHead = 15, - TemplateMiddle = 16, - TemplateTail = 17, - OpenBraceToken = 18, - CloseBraceToken = 19, - OpenParenToken = 20, - CloseParenToken = 21, - OpenBracketToken = 22, - CloseBracketToken = 23, - DotToken = 24, - DotDotDotToken = 25, - SemicolonToken = 26, - CommaToken = 27, - QuestionDotToken = 28, - LessThanToken = 29, - LessThanSlashToken = 30, - GreaterThanToken = 31, - LessThanEqualsToken = 32, - GreaterThanEqualsToken = 33, - EqualsEqualsToken = 34, - ExclamationEqualsToken = 35, - EqualsEqualsEqualsToken = 36, - ExclamationEqualsEqualsToken = 37, - EqualsGreaterThanToken = 38, - PlusToken = 39, - MinusToken = 40, - AsteriskToken = 41, - AsteriskAsteriskToken = 42, - SlashToken = 43, - PercentToken = 44, - PlusPlusToken = 45, - MinusMinusToken = 46, - LessThanLessThanToken = 47, - GreaterThanGreaterThanToken = 48, - GreaterThanGreaterThanGreaterThanToken = 49, - AmpersandToken = 50, - BarToken = 51, - CaretToken = 52, - ExclamationToken = 53, - TildeToken = 54, - AmpersandAmpersandToken = 55, - BarBarToken = 56, - QuestionToken = 57, - ColonToken = 58, - AtToken = 59, - QuestionQuestionToken = 60, - BacktickToken = 61, - HashToken = 62, - EqualsToken = 63, - PlusEqualsToken = 64, - MinusEqualsToken = 65, - AsteriskEqualsToken = 66, - AsteriskAsteriskEqualsToken = 67, - SlashEqualsToken = 68, - PercentEqualsToken = 69, - LessThanLessThanEqualsToken = 70, - GreaterThanGreaterThanEqualsToken = 71, - GreaterThanGreaterThanGreaterThanEqualsToken = 72, - AmpersandEqualsToken = 73, - BarEqualsToken = 74, - BarBarEqualsToken = 75, - AmpersandAmpersandEqualsToken = 76, - QuestionQuestionEqualsToken = 77, - CaretEqualsToken = 78, - Identifier = 79, - PrivateIdentifier = 80, - JSDocCommentTextToken = 81, - BreakKeyword = 82, - CaseKeyword = 83, - CatchKeyword = 84, - ClassKeyword = 85, - ConstKeyword = 86, - ContinueKeyword = 87, - DebuggerKeyword = 88, - DefaultKeyword = 89, - DeleteKeyword = 90, - DoKeyword = 91, - ElseKeyword = 92, - EnumKeyword = 93, - ExportKeyword = 94, - ExtendsKeyword = 95, - FalseKeyword = 96, - FinallyKeyword = 97, - ForKeyword = 98, - FunctionKeyword = 99, - IfKeyword = 100, - ImportKeyword = 101, - InKeyword = 102, - InstanceOfKeyword = 103, - NewKeyword = 104, - NullKeyword = 105, - ReturnKeyword = 106, - SuperKeyword = 107, - SwitchKeyword = 108, - ThisKeyword = 109, - ThrowKeyword = 110, - TrueKeyword = 111, - TryKeyword = 112, - TypeOfKeyword = 113, - VarKeyword = 114, - VoidKeyword = 115, - WhileKeyword = 116, - WithKeyword = 117, - ImplementsKeyword = 118, - InterfaceKeyword = 119, - LetKeyword = 120, - PackageKeyword = 121, - PrivateKeyword = 122, - ProtectedKeyword = 123, - PublicKeyword = 124, - StaticKeyword = 125, - YieldKeyword = 126, - AbstractKeyword = 127, - AccessorKeyword = 128, - AsKeyword = 129, - AssertsKeyword = 130, - AssertKeyword = 131, - AnyKeyword = 132, - AsyncKeyword = 133, - AwaitKeyword = 134, - BooleanKeyword = 135, - ConstructorKeyword = 136, - DeclareKeyword = 137, - GetKeyword = 138, - ImmediateKeyword = 139, - InferKeyword = 140, - IntrinsicKeyword = 141, - IsKeyword = 142, - KeyOfKeyword = 143, - ModuleKeyword = 144, - NamespaceKeyword = 145, - NeverKeyword = 146, - OutKeyword = 147, - ReadonlyKeyword = 148, - RequireKeyword = 149, - NumberKeyword = 150, - ObjectKeyword = 151, - SatisfiesKeyword = 152, - SetKeyword = 153, - StringKeyword = 154, - SymbolKeyword = 155, - TypeKeyword = 156, - UndefinedKeyword = 157, - UniqueKeyword = 158, - UnknownKeyword = 159, - UsingKeyword = 160, - FromKeyword = 161, - GlobalKeyword = 162, - BigIntKeyword = 163, - OverrideKeyword = 164, - OfKeyword = 165, - DeferKeyword = 166, - QualifiedName = 167, - ComputedPropertyName = 168, - TypeParameter = 169, - Parameter = 170, - Decorator = 171, - PropertySignature = 172, - PropertyDeclaration = 173, - MethodSignature = 174, - MethodDeclaration = 175, - ClassStaticBlockDeclaration = 176, - Constructor = 177, - GetAccessor = 178, - SetAccessor = 179, - CallSignature = 180, - ConstructSignature = 181, - IndexSignature = 182, - TypePredicate = 183, - TypeReference = 184, - FunctionType = 185, - ConstructorType = 186, - TypeQuery = 187, - TypeLiteral = 188, - ArrayType = 189, - TupleType = 190, - OptionalType = 191, - RestType = 192, - UnionType = 193, - IntersectionType = 194, - ConditionalType = 195, - InferType = 196, - ParenthesizedType = 197, - ThisType = 198, - TypeOperator = 199, - IndexedAccessType = 200, - MappedType = 201, - LiteralType = 202, - NamedTupleMember = 203, - TemplateLiteralType = 204, - TemplateLiteralTypeSpan = 205, - ImportType = 206, - ObjectBindingPattern = 207, - ArrayBindingPattern = 208, - BindingElement = 209, - ArrayLiteralExpression = 210, - ObjectLiteralExpression = 211, - PropertyAccessExpression = 212, - ElementAccessExpression = 213, - CallExpression = 214, - NewExpression = 215, - TaggedTemplateExpression = 216, - TypeAssertionExpression = 217, - ParenthesizedExpression = 218, - FunctionExpression = 219, - ArrowFunction = 220, - DeleteExpression = 221, - TypeOfExpression = 222, - VoidExpression = 223, - AwaitExpression = 224, - PrefixUnaryExpression = 225, - PostfixUnaryExpression = 226, - BinaryExpression = 227, - ConditionalExpression = 228, - TemplateExpression = 229, - YieldExpression = 230, - SpreadElement = 231, - ClassExpression = 232, - OmittedExpression = 233, - ExpressionWithTypeArguments = 234, - AsExpression = 235, - NonNullExpression = 236, - MetaProperty = 237, - SyntheticExpression = 238, - SatisfiesExpression = 239, - TemplateSpan = 240, - SemicolonClassElement = 241, - Block = 242, - EmptyStatement = 243, - VariableStatement = 244, - ExpressionStatement = 245, - IfStatement = 246, - DoStatement = 247, - WhileStatement = 248, - ForStatement = 249, - ForInStatement = 250, - ForOfStatement = 251, - ContinueStatement = 252, - BreakStatement = 253, - ReturnStatement = 254, - WithStatement = 255, - SwitchStatement = 256, - LabeledStatement = 257, - ThrowStatement = 258, - TryStatement = 259, - DebuggerStatement = 260, - VariableDeclaration = 261, - VariableDeclarationList = 262, - FunctionDeclaration = 263, - ClassDeclaration = 264, - InterfaceDeclaration = 265, - TypeAliasDeclaration = 266, - EnumDeclaration = 267, - ModuleDeclaration = 268, - ModuleBlock = 269, - CaseBlock = 270, - NamespaceExportDeclaration = 271, - ImportEqualsDeclaration = 272, - ImportDeclaration = 273, - ImportClause = 274, - NamespaceImport = 275, - NamedImports = 276, - ImportSpecifier = 277, - ExportAssignment = 278, - ExportDeclaration = 279, - NamedExports = 280, - NamespaceExport = 281, - ExportSpecifier = 282, - MissingDeclaration = 283, - ExternalModuleReference = 284, - JsxElement = 285, - JsxSelfClosingElement = 286, - JsxOpeningElement = 287, - JsxClosingElement = 288, - JsxFragment = 289, - JsxOpeningFragment = 290, - JsxClosingFragment = 291, - JsxAttribute = 292, - JsxAttributes = 293, - JsxSpreadAttribute = 294, - JsxExpression = 295, - JsxNamespacedName = 296, - CaseClause = 297, - DefaultClause = 298, - HeritageClause = 299, - CatchClause = 300, - ImportAttributes = 301, - ImportAttribute = 302, - PropertyAssignment = 303, - ShorthandPropertyAssignment = 304, - SpreadAssignment = 305, - EnumMember = 306, - SourceFile = 307, - JSDocTypeExpression = 308, - JSDocNameReference = 309, - JSDocAllType = 310, - JSDocNullableType = 311, - JSDocNonNullableType = 312, - JSDocOptionalType = 313, - JSDocVariadicType = 314, - JSDoc = 315, - JSDocText = 316, - JSDocTypeLiteral = 317, - JSDocSignature = 318, - JSDocLink = 319, - JSDocLinkCode = 320, - JSDocLinkPlain = 321, - JSDocUnknownTag = 322, - JSDocAugmentsTag = 323, - JSDocImplementsTag = 324, - JSDocDeprecatedTag = 325, - JSDocPublicTag = 326, - JSDocPrivateTag = 327, - JSDocProtectedTag = 328, - JSDocReadonlyTag = 329, - JSDocOverrideTag = 330, - JSDocCallbackTag = 331, - JSDocOverloadTag = 332, - JSDocParameterTag = 333, - JSDocReturnTag = 334, - JSDocThisTag = 335, - JSDocTypeTag = 336, - JSDocTemplateTag = 337, - JSDocTypedefTag = 338, - JSDocSeeTag = 339, - JSDocPropertyTag = 340, - JSDocThrowsTag = 341, - JSDocSatisfiesTag = 342, - JSDocImportTag = 343, - SyntaxList = 344, - JSTypeAliasDeclaration = 345, - JSImportDeclaration = 346, - NotEmittedStatement = 347, - PartiallyEmittedExpression = 348, - SyntheticReferenceExpression = 349, - NotEmittedTypeElement = 350, - Count = 351, - FirstAssignment = EqualsToken, - LastAssignment = CaretEqualsToken, - FirstCompoundAssignment = PlusEqualsToken, - LastCompoundAssignment = CaretEqualsToken, - FirstReservedWord = BreakKeyword, - LastReservedWord = WithKeyword, - FirstKeyword = BreakKeyword, - LastKeyword = DeferKeyword, - FirstFutureReservedWord = ImplementsKeyword, - LastFutureReservedWord = YieldKeyword, - FirstTypeNode = TypePredicate, - LastTypeNode = ImportType, - FirstPunctuation = OpenBraceToken, - LastPunctuation = CaretEqualsToken, - FirstToken = Unknown, - LastToken = LastKeyword, - FirstLiteralToken = NumericLiteral, - LastLiteralToken = NoSubstitutionTemplateLiteral, - FirstTemplateToken = NoSubstitutionTemplateLiteral, - LastTemplateToken = TemplateTail, - FirstBinaryOperator = LessThanToken, - LastBinaryOperator = CaretEqualsToken, - FirstStatement = VariableStatement, - LastStatement = DebuggerStatement, - FirstNode = QualifiedName, - FirstJSDocNode = JSDocTypeExpression, - LastJSDocNode = JSDocImportTag, - FirstJSDocTagNode = JSDocUnknownTag, - LastJSDocTagNode = JSDocImportTag, - FirstContextualKeyword = AbstractKeyword, - LastContextualKeyword = DeferKeyword, - LastUnaryOperator = TildeToken, - FirstTriviaToken = SingleLineCommentTrivia, - LastTriviaToken = ConflictMarkerTrivia, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/kind_generated.go. DO NOT EDIT. + +export enum SyntaxKind { + Unknown = 0, + EndOfFile = 1, + SingleLineCommentTrivia = 2, + MultiLineCommentTrivia = 3, + NewLineTrivia = 4, + WhitespaceTrivia = 5, + ConflictMarkerTrivia = 6, + NonTextFileMarkerTrivia = 7, + NumericLiteral = 8, + BigIntLiteral = 9, + StringLiteral = 10, + JsxText = 11, + JsxTextAllWhiteSpaces = 12, + RegularExpressionLiteral = 13, + NoSubstitutionTemplateLiteral = 14, + TemplateHead = 15, + TemplateMiddle = 16, + TemplateTail = 17, + OpenBraceToken = 18, + CloseBraceToken = 19, + OpenParenToken = 20, + CloseParenToken = 21, + OpenBracketToken = 22, + CloseBracketToken = 23, + DotToken = 24, + DotDotDotToken = 25, + SemicolonToken = 26, + CommaToken = 27, + QuestionDotToken = 28, + LessThanToken = 29, + LessThanSlashToken = 30, + GreaterThanToken = 31, + LessThanEqualsToken = 32, + GreaterThanEqualsToken = 33, + EqualsEqualsToken = 34, + ExclamationEqualsToken = 35, + EqualsEqualsEqualsToken = 36, + ExclamationEqualsEqualsToken = 37, + EqualsGreaterThanToken = 38, + PlusToken = 39, + MinusToken = 40, + AsteriskToken = 41, + AsteriskAsteriskToken = 42, + SlashToken = 43, + PercentToken = 44, + PlusPlusToken = 45, + MinusMinusToken = 46, + LessThanLessThanToken = 47, + GreaterThanGreaterThanToken = 48, + GreaterThanGreaterThanGreaterThanToken = 49, + AmpersandToken = 50, + BarToken = 51, + CaretToken = 52, + ExclamationToken = 53, + TildeToken = 54, + AmpersandAmpersandToken = 55, + BarBarToken = 56, + QuestionToken = 57, + ColonToken = 58, + AtToken = 59, + QuestionQuestionToken = 60, + BacktickToken = 61, + HashToken = 62, + EqualsToken = 63, + PlusEqualsToken = 64, + MinusEqualsToken = 65, + AsteriskEqualsToken = 66, + AsteriskAsteriskEqualsToken = 67, + SlashEqualsToken = 68, + PercentEqualsToken = 69, + LessThanLessThanEqualsToken = 70, + GreaterThanGreaterThanEqualsToken = 71, + GreaterThanGreaterThanGreaterThanEqualsToken = 72, + AmpersandEqualsToken = 73, + BarEqualsToken = 74, + BarBarEqualsToken = 75, + AmpersandAmpersandEqualsToken = 76, + QuestionQuestionEqualsToken = 77, + CaretEqualsToken = 78, + Identifier = 79, + PrivateIdentifier = 80, + JSDocCommentTextToken = 81, + BreakKeyword = 82, + CaseKeyword = 83, + CatchKeyword = 84, + ClassKeyword = 85, + ConstKeyword = 86, + ContinueKeyword = 87, + DebuggerKeyword = 88, + DefaultKeyword = 89, + DeleteKeyword = 90, + DoKeyword = 91, + ElseKeyword = 92, + EnumKeyword = 93, + ExportKeyword = 94, + ExtendsKeyword = 95, + FalseKeyword = 96, + FinallyKeyword = 97, + ForKeyword = 98, + FunctionKeyword = 99, + IfKeyword = 100, + ImportKeyword = 101, + InKeyword = 102, + InstanceOfKeyword = 103, + NewKeyword = 104, + NullKeyword = 105, + ReturnKeyword = 106, + SuperKeyword = 107, + SwitchKeyword = 108, + ThisKeyword = 109, + ThrowKeyword = 110, + TrueKeyword = 111, + TryKeyword = 112, + TypeOfKeyword = 113, + VarKeyword = 114, + VoidKeyword = 115, + WhileKeyword = 116, + WithKeyword = 117, + ImplementsKeyword = 118, + InterfaceKeyword = 119, + LetKeyword = 120, + PackageKeyword = 121, + PrivateKeyword = 122, + ProtectedKeyword = 123, + PublicKeyword = 124, + StaticKeyword = 125, + YieldKeyword = 126, + AbstractKeyword = 127, + AccessorKeyword = 128, + AsKeyword = 129, + AssertsKeyword = 130, + AssertKeyword = 131, + AnyKeyword = 132, + AsyncKeyword = 133, + AwaitKeyword = 134, + BooleanKeyword = 135, + ConstructorKeyword = 136, + DeclareKeyword = 137, + GetKeyword = 138, + ImmediateKeyword = 139, + InferKeyword = 140, + IntrinsicKeyword = 141, + IsKeyword = 142, + KeyOfKeyword = 143, + ModuleKeyword = 144, + NamespaceKeyword = 145, + NeverKeyword = 146, + OutKeyword = 147, + ReadonlyKeyword = 148, + RequireKeyword = 149, + NumberKeyword = 150, + ObjectKeyword = 151, + SatisfiesKeyword = 152, + SetKeyword = 153, + StringKeyword = 154, + SymbolKeyword = 155, + TypeKeyword = 156, + UndefinedKeyword = 157, + UniqueKeyword = 158, + UnknownKeyword = 159, + UsingKeyword = 160, + FromKeyword = 161, + GlobalKeyword = 162, + BigIntKeyword = 163, + OverrideKeyword = 164, + OfKeyword = 165, + DeferKeyword = 166, + QualifiedName = 167, + ComputedPropertyName = 168, + TypeParameter = 169, + Parameter = 170, + Decorator = 171, + PropertySignature = 172, + PropertyDeclaration = 173, + MethodSignature = 174, + MethodDeclaration = 175, + ClassStaticBlockDeclaration = 176, + Constructor = 177, + GetAccessor = 178, + SetAccessor = 179, + CallSignature = 180, + ConstructSignature = 181, + IndexSignature = 182, + TypePredicate = 183, + TypeReference = 184, + FunctionType = 185, + ConstructorType = 186, + TypeQuery = 187, + TypeLiteral = 188, + ArrayType = 189, + TupleType = 190, + OptionalType = 191, + RestType = 192, + UnionType = 193, + IntersectionType = 194, + ConditionalType = 195, + InferType = 196, + ParenthesizedType = 197, + ThisType = 198, + TypeOperator = 199, + IndexedAccessType = 200, + MappedType = 201, + LiteralType = 202, + NamedTupleMember = 203, + TemplateLiteralType = 204, + TemplateLiteralTypeSpan = 205, + ImportType = 206, + ObjectBindingPattern = 207, + ArrayBindingPattern = 208, + BindingElement = 209, + ArrayLiteralExpression = 210, + ObjectLiteralExpression = 211, + PropertyAccessExpression = 212, + ElementAccessExpression = 213, + CallExpression = 214, + NewExpression = 215, + TaggedTemplateExpression = 216, + TypeAssertionExpression = 217, + ParenthesizedExpression = 218, + FunctionExpression = 219, + ArrowFunction = 220, + DeleteExpression = 221, + TypeOfExpression = 222, + VoidExpression = 223, + AwaitExpression = 224, + PrefixUnaryExpression = 225, + PostfixUnaryExpression = 226, + BinaryExpression = 227, + ConditionalExpression = 228, + TemplateExpression = 229, + YieldExpression = 230, + SpreadElement = 231, + ClassExpression = 232, + OmittedExpression = 233, + ExpressionWithTypeArguments = 234, + AsExpression = 235, + NonNullExpression = 236, + MetaProperty = 237, + SyntheticExpression = 238, + SatisfiesExpression = 239, + TemplateSpan = 240, + SemicolonClassElement = 241, + Block = 242, + EmptyStatement = 243, + VariableStatement = 244, + ExpressionStatement = 245, + IfStatement = 246, + DoStatement = 247, + WhileStatement = 248, + ForStatement = 249, + ForInStatement = 250, + ForOfStatement = 251, + ContinueStatement = 252, + BreakStatement = 253, + ReturnStatement = 254, + WithStatement = 255, + SwitchStatement = 256, + LabeledStatement = 257, + ThrowStatement = 258, + TryStatement = 259, + DebuggerStatement = 260, + VariableDeclaration = 261, + VariableDeclarationList = 262, + FunctionDeclaration = 263, + ClassDeclaration = 264, + InterfaceDeclaration = 265, + TypeAliasDeclaration = 266, + EnumDeclaration = 267, + ModuleDeclaration = 268, + ModuleBlock = 269, + CaseBlock = 270, + NamespaceExportDeclaration = 271, + ImportEqualsDeclaration = 272, + ImportDeclaration = 273, + ImportClause = 274, + NamespaceImport = 275, + NamedImports = 276, + ImportSpecifier = 277, + ExportAssignment = 278, + ExportDeclaration = 279, + NamedExports = 280, + NamespaceExport = 281, + ExportSpecifier = 282, + MissingDeclaration = 283, + ExternalModuleReference = 284, + JsxElement = 285, + JsxSelfClosingElement = 286, + JsxOpeningElement = 287, + JsxClosingElement = 288, + JsxFragment = 289, + JsxOpeningFragment = 290, + JsxClosingFragment = 291, + JsxAttribute = 292, + JsxAttributes = 293, + JsxSpreadAttribute = 294, + JsxExpression = 295, + JsxNamespacedName = 296, + CaseClause = 297, + DefaultClause = 298, + HeritageClause = 299, + CatchClause = 300, + ImportAttributes = 301, + ImportAttribute = 302, + PropertyAssignment = 303, + ShorthandPropertyAssignment = 304, + SpreadAssignment = 305, + EnumMember = 306, + SourceFile = 307, + JSDocTypeExpression = 308, + JSDocNameReference = 309, + JSDocAllType = 310, + JSDocNullableType = 311, + JSDocNonNullableType = 312, + JSDocOptionalType = 313, + JSDocVariadicType = 314, + JSDoc = 315, + JSDocText = 316, + JSDocTypeLiteral = 317, + JSDocSignature = 318, + JSDocLink = 319, + JSDocLinkCode = 320, + JSDocLinkPlain = 321, + JSDocUnknownTag = 322, + JSDocAugmentsTag = 323, + JSDocImplementsTag = 324, + JSDocDeprecatedTag = 325, + JSDocPublicTag = 326, + JSDocPrivateTag = 327, + JSDocProtectedTag = 328, + JSDocReadonlyTag = 329, + JSDocOverrideTag = 330, + JSDocCallbackTag = 331, + JSDocOverloadTag = 332, + JSDocParameterTag = 333, + JSDocReturnTag = 334, + JSDocThisTag = 335, + JSDocTypeTag = 336, + JSDocTemplateTag = 337, + JSDocTypedefTag = 338, + JSDocSeeTag = 339, + JSDocPropertyTag = 340, + JSDocThrowsTag = 341, + JSDocSatisfiesTag = 342, + JSDocImportTag = 343, + SyntaxList = 344, + JSTypeAliasDeclaration = 345, + JSImportDeclaration = 346, + NotEmittedStatement = 347, + PartiallyEmittedExpression = 348, + SyntheticReferenceExpression = 349, + NotEmittedTypeElement = 350, + Count = 351, + FirstAssignment = EqualsToken, + LastAssignment = CaretEqualsToken, + FirstCompoundAssignment = PlusEqualsToken, + LastCompoundAssignment = CaretEqualsToken, + FirstReservedWord = BreakKeyword, + LastReservedWord = WithKeyword, + FirstKeyword = BreakKeyword, + LastKeyword = DeferKeyword, + FirstFutureReservedWord = ImplementsKeyword, + LastFutureReservedWord = YieldKeyword, + FirstTypeNode = TypePredicate, + LastTypeNode = ImportType, + FirstPunctuation = OpenBraceToken, + LastPunctuation = CaretEqualsToken, + FirstToken = Unknown, + LastToken = LastKeyword, + FirstLiteralToken = NumericLiteral, + LastLiteralToken = NoSubstitutionTemplateLiteral, + FirstTemplateToken = NoSubstitutionTemplateLiteral, + LastTemplateToken = TemplateTail, + FirstBinaryOperator = LessThanToken, + LastBinaryOperator = CaretEqualsToken, + FirstStatement = VariableStatement, + LastStatement = DebuggerStatement, + FirstNode = QualifiedName, + FirstJSDocNode = JSDocTypeExpression, + LastJSDocNode = JSDocImportTag, + FirstJSDocTagNode = JSDocUnknownTag, + LastJSDocTagNode = JSDocImportTag, + FirstContextualKeyword = AbstractKeyword, + LastContextualKeyword = DeferKeyword, + LastUnaryOperator = TildeToken, + FirstTriviaToken = SingleLineCommentTrivia, + LastTriviaToken = ConflictMarkerTrivia, +} diff --git a/packages/typescript/src/enums/syntaxKind.ts b/packages/typescript/src/enums/syntaxKind.ts index 87c2385c7cd47..367b79fe9b82b 100644 --- a/packages/typescript/src/enums/syntaxKind.ts +++ b/packages/typescript/src/enums/syntaxKind.ts @@ -1,390 +1,390 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/kind_generated.go. DO NOT EDIT. -export var SyntaxKind: any; -(function (SyntaxKind) { - SyntaxKind[SyntaxKind["Unknown"] = 0] = "Unknown"; - SyntaxKind[SyntaxKind["EndOfFile"] = 1] = "EndOfFile"; - SyntaxKind[SyntaxKind["SingleLineCommentTrivia"] = 2] = "SingleLineCommentTrivia"; - SyntaxKind[SyntaxKind["MultiLineCommentTrivia"] = 3] = "MultiLineCommentTrivia"; - SyntaxKind[SyntaxKind["NewLineTrivia"] = 4] = "NewLineTrivia"; - SyntaxKind[SyntaxKind["WhitespaceTrivia"] = 5] = "WhitespaceTrivia"; - SyntaxKind[SyntaxKind["ConflictMarkerTrivia"] = 6] = "ConflictMarkerTrivia"; - SyntaxKind[SyntaxKind["NonTextFileMarkerTrivia"] = 7] = "NonTextFileMarkerTrivia"; - SyntaxKind[SyntaxKind["NumericLiteral"] = 8] = "NumericLiteral"; - SyntaxKind[SyntaxKind["BigIntLiteral"] = 9] = "BigIntLiteral"; - SyntaxKind[SyntaxKind["StringLiteral"] = 10] = "StringLiteral"; - SyntaxKind[SyntaxKind["JsxText"] = 11] = "JsxText"; - SyntaxKind[SyntaxKind["JsxTextAllWhiteSpaces"] = 12] = "JsxTextAllWhiteSpaces"; - SyntaxKind[SyntaxKind["RegularExpressionLiteral"] = 13] = "RegularExpressionLiteral"; - SyntaxKind[SyntaxKind["NoSubstitutionTemplateLiteral"] = 14] = "NoSubstitutionTemplateLiteral"; - SyntaxKind[SyntaxKind["TemplateHead"] = 15] = "TemplateHead"; - SyntaxKind[SyntaxKind["TemplateMiddle"] = 16] = "TemplateMiddle"; - SyntaxKind[SyntaxKind["TemplateTail"] = 17] = "TemplateTail"; - SyntaxKind[SyntaxKind["OpenBraceToken"] = 18] = "OpenBraceToken"; - SyntaxKind[SyntaxKind["CloseBraceToken"] = 19] = "CloseBraceToken"; - SyntaxKind[SyntaxKind["OpenParenToken"] = 20] = "OpenParenToken"; - SyntaxKind[SyntaxKind["CloseParenToken"] = 21] = "CloseParenToken"; - SyntaxKind[SyntaxKind["OpenBracketToken"] = 22] = "OpenBracketToken"; - SyntaxKind[SyntaxKind["CloseBracketToken"] = 23] = "CloseBracketToken"; - SyntaxKind[SyntaxKind["DotToken"] = 24] = "DotToken"; - SyntaxKind[SyntaxKind["DotDotDotToken"] = 25] = "DotDotDotToken"; - SyntaxKind[SyntaxKind["SemicolonToken"] = 26] = "SemicolonToken"; - SyntaxKind[SyntaxKind["CommaToken"] = 27] = "CommaToken"; - SyntaxKind[SyntaxKind["QuestionDotToken"] = 28] = "QuestionDotToken"; - SyntaxKind[SyntaxKind["LessThanToken"] = 29] = "LessThanToken"; - SyntaxKind[SyntaxKind["LessThanSlashToken"] = 30] = "LessThanSlashToken"; - SyntaxKind[SyntaxKind["GreaterThanToken"] = 31] = "GreaterThanToken"; - SyntaxKind[SyntaxKind["LessThanEqualsToken"] = 32] = "LessThanEqualsToken"; - SyntaxKind[SyntaxKind["GreaterThanEqualsToken"] = 33] = "GreaterThanEqualsToken"; - SyntaxKind[SyntaxKind["EqualsEqualsToken"] = 34] = "EqualsEqualsToken"; - SyntaxKind[SyntaxKind["ExclamationEqualsToken"] = 35] = "ExclamationEqualsToken"; - SyntaxKind[SyntaxKind["EqualsEqualsEqualsToken"] = 36] = "EqualsEqualsEqualsToken"; - SyntaxKind[SyntaxKind["ExclamationEqualsEqualsToken"] = 37] = "ExclamationEqualsEqualsToken"; - SyntaxKind[SyntaxKind["EqualsGreaterThanToken"] = 38] = "EqualsGreaterThanToken"; - SyntaxKind[SyntaxKind["PlusToken"] = 39] = "PlusToken"; - SyntaxKind[SyntaxKind["MinusToken"] = 40] = "MinusToken"; - SyntaxKind[SyntaxKind["AsteriskToken"] = 41] = "AsteriskToken"; - SyntaxKind[SyntaxKind["AsteriskAsteriskToken"] = 42] = "AsteriskAsteriskToken"; - SyntaxKind[SyntaxKind["SlashToken"] = 43] = "SlashToken"; - SyntaxKind[SyntaxKind["PercentToken"] = 44] = "PercentToken"; - SyntaxKind[SyntaxKind["PlusPlusToken"] = 45] = "PlusPlusToken"; - SyntaxKind[SyntaxKind["MinusMinusToken"] = 46] = "MinusMinusToken"; - SyntaxKind[SyntaxKind["LessThanLessThanToken"] = 47] = "LessThanLessThanToken"; - SyntaxKind[SyntaxKind["GreaterThanGreaterThanToken"] = 48] = "GreaterThanGreaterThanToken"; - SyntaxKind[SyntaxKind["GreaterThanGreaterThanGreaterThanToken"] = 49] = "GreaterThanGreaterThanGreaterThanToken"; - SyntaxKind[SyntaxKind["AmpersandToken"] = 50] = "AmpersandToken"; - SyntaxKind[SyntaxKind["BarToken"] = 51] = "BarToken"; - SyntaxKind[SyntaxKind["CaretToken"] = 52] = "CaretToken"; - SyntaxKind[SyntaxKind["ExclamationToken"] = 53] = "ExclamationToken"; - SyntaxKind[SyntaxKind["TildeToken"] = 54] = "TildeToken"; - SyntaxKind[SyntaxKind["AmpersandAmpersandToken"] = 55] = "AmpersandAmpersandToken"; - SyntaxKind[SyntaxKind["BarBarToken"] = 56] = "BarBarToken"; - SyntaxKind[SyntaxKind["QuestionToken"] = 57] = "QuestionToken"; - SyntaxKind[SyntaxKind["ColonToken"] = 58] = "ColonToken"; - SyntaxKind[SyntaxKind["AtToken"] = 59] = "AtToken"; - SyntaxKind[SyntaxKind["QuestionQuestionToken"] = 60] = "QuestionQuestionToken"; - SyntaxKind[SyntaxKind["BacktickToken"] = 61] = "BacktickToken"; - SyntaxKind[SyntaxKind["HashToken"] = 62] = "HashToken"; - SyntaxKind[SyntaxKind["EqualsToken"] = 63] = "EqualsToken"; - SyntaxKind[SyntaxKind["PlusEqualsToken"] = 64] = "PlusEqualsToken"; - SyntaxKind[SyntaxKind["MinusEqualsToken"] = 65] = "MinusEqualsToken"; - SyntaxKind[SyntaxKind["AsteriskEqualsToken"] = 66] = "AsteriskEqualsToken"; - SyntaxKind[SyntaxKind["AsteriskAsteriskEqualsToken"] = 67] = "AsteriskAsteriskEqualsToken"; - SyntaxKind[SyntaxKind["SlashEqualsToken"] = 68] = "SlashEqualsToken"; - SyntaxKind[SyntaxKind["PercentEqualsToken"] = 69] = "PercentEqualsToken"; - SyntaxKind[SyntaxKind["LessThanLessThanEqualsToken"] = 70] = "LessThanLessThanEqualsToken"; - SyntaxKind[SyntaxKind["GreaterThanGreaterThanEqualsToken"] = 71] = "GreaterThanGreaterThanEqualsToken"; - SyntaxKind[SyntaxKind["GreaterThanGreaterThanGreaterThanEqualsToken"] = 72] = "GreaterThanGreaterThanGreaterThanEqualsToken"; - SyntaxKind[SyntaxKind["AmpersandEqualsToken"] = 73] = "AmpersandEqualsToken"; - SyntaxKind[SyntaxKind["BarEqualsToken"] = 74] = "BarEqualsToken"; - SyntaxKind[SyntaxKind["BarBarEqualsToken"] = 75] = "BarBarEqualsToken"; - SyntaxKind[SyntaxKind["AmpersandAmpersandEqualsToken"] = 76] = "AmpersandAmpersandEqualsToken"; - SyntaxKind[SyntaxKind["QuestionQuestionEqualsToken"] = 77] = "QuestionQuestionEqualsToken"; - SyntaxKind[SyntaxKind["CaretEqualsToken"] = 78] = "CaretEqualsToken"; - SyntaxKind[SyntaxKind["Identifier"] = 79] = "Identifier"; - SyntaxKind[SyntaxKind["PrivateIdentifier"] = 80] = "PrivateIdentifier"; - SyntaxKind[SyntaxKind["JSDocCommentTextToken"] = 81] = "JSDocCommentTextToken"; - SyntaxKind[SyntaxKind["BreakKeyword"] = 82] = "BreakKeyword"; - SyntaxKind[SyntaxKind["CaseKeyword"] = 83] = "CaseKeyword"; - SyntaxKind[SyntaxKind["CatchKeyword"] = 84] = "CatchKeyword"; - SyntaxKind[SyntaxKind["ClassKeyword"] = 85] = "ClassKeyword"; - SyntaxKind[SyntaxKind["ConstKeyword"] = 86] = "ConstKeyword"; - SyntaxKind[SyntaxKind["ContinueKeyword"] = 87] = "ContinueKeyword"; - SyntaxKind[SyntaxKind["DebuggerKeyword"] = 88] = "DebuggerKeyword"; - SyntaxKind[SyntaxKind["DefaultKeyword"] = 89] = "DefaultKeyword"; - SyntaxKind[SyntaxKind["DeleteKeyword"] = 90] = "DeleteKeyword"; - SyntaxKind[SyntaxKind["DoKeyword"] = 91] = "DoKeyword"; - SyntaxKind[SyntaxKind["ElseKeyword"] = 92] = "ElseKeyword"; - SyntaxKind[SyntaxKind["EnumKeyword"] = 93] = "EnumKeyword"; - SyntaxKind[SyntaxKind["ExportKeyword"] = 94] = "ExportKeyword"; - SyntaxKind[SyntaxKind["ExtendsKeyword"] = 95] = "ExtendsKeyword"; - SyntaxKind[SyntaxKind["FalseKeyword"] = 96] = "FalseKeyword"; - SyntaxKind[SyntaxKind["FinallyKeyword"] = 97] = "FinallyKeyword"; - SyntaxKind[SyntaxKind["ForKeyword"] = 98] = "ForKeyword"; - SyntaxKind[SyntaxKind["FunctionKeyword"] = 99] = "FunctionKeyword"; - SyntaxKind[SyntaxKind["IfKeyword"] = 100] = "IfKeyword"; - SyntaxKind[SyntaxKind["ImportKeyword"] = 101] = "ImportKeyword"; - SyntaxKind[SyntaxKind["InKeyword"] = 102] = "InKeyword"; - SyntaxKind[SyntaxKind["InstanceOfKeyword"] = 103] = "InstanceOfKeyword"; - SyntaxKind[SyntaxKind["NewKeyword"] = 104] = "NewKeyword"; - SyntaxKind[SyntaxKind["NullKeyword"] = 105] = "NullKeyword"; - SyntaxKind[SyntaxKind["ReturnKeyword"] = 106] = "ReturnKeyword"; - SyntaxKind[SyntaxKind["SuperKeyword"] = 107] = "SuperKeyword"; - SyntaxKind[SyntaxKind["SwitchKeyword"] = 108] = "SwitchKeyword"; - SyntaxKind[SyntaxKind["ThisKeyword"] = 109] = "ThisKeyword"; - SyntaxKind[SyntaxKind["ThrowKeyword"] = 110] = "ThrowKeyword"; - SyntaxKind[SyntaxKind["TrueKeyword"] = 111] = "TrueKeyword"; - SyntaxKind[SyntaxKind["TryKeyword"] = 112] = "TryKeyword"; - SyntaxKind[SyntaxKind["TypeOfKeyword"] = 113] = "TypeOfKeyword"; - SyntaxKind[SyntaxKind["VarKeyword"] = 114] = "VarKeyword"; - SyntaxKind[SyntaxKind["VoidKeyword"] = 115] = "VoidKeyword"; - SyntaxKind[SyntaxKind["WhileKeyword"] = 116] = "WhileKeyword"; - SyntaxKind[SyntaxKind["WithKeyword"] = 117] = "WithKeyword"; - SyntaxKind[SyntaxKind["ImplementsKeyword"] = 118] = "ImplementsKeyword"; - SyntaxKind[SyntaxKind["InterfaceKeyword"] = 119] = "InterfaceKeyword"; - SyntaxKind[SyntaxKind["LetKeyword"] = 120] = "LetKeyword"; - SyntaxKind[SyntaxKind["PackageKeyword"] = 121] = "PackageKeyword"; - SyntaxKind[SyntaxKind["PrivateKeyword"] = 122] = "PrivateKeyword"; - SyntaxKind[SyntaxKind["ProtectedKeyword"] = 123] = "ProtectedKeyword"; - SyntaxKind[SyntaxKind["PublicKeyword"] = 124] = "PublicKeyword"; - SyntaxKind[SyntaxKind["StaticKeyword"] = 125] = "StaticKeyword"; - SyntaxKind[SyntaxKind["YieldKeyword"] = 126] = "YieldKeyword"; - SyntaxKind[SyntaxKind["AbstractKeyword"] = 127] = "AbstractKeyword"; - SyntaxKind[SyntaxKind["AccessorKeyword"] = 128] = "AccessorKeyword"; - SyntaxKind[SyntaxKind["AsKeyword"] = 129] = "AsKeyword"; - SyntaxKind[SyntaxKind["AssertsKeyword"] = 130] = "AssertsKeyword"; - SyntaxKind[SyntaxKind["AssertKeyword"] = 131] = "AssertKeyword"; - SyntaxKind[SyntaxKind["AnyKeyword"] = 132] = "AnyKeyword"; - SyntaxKind[SyntaxKind["AsyncKeyword"] = 133] = "AsyncKeyword"; - SyntaxKind[SyntaxKind["AwaitKeyword"] = 134] = "AwaitKeyword"; - SyntaxKind[SyntaxKind["BooleanKeyword"] = 135] = "BooleanKeyword"; - SyntaxKind[SyntaxKind["ConstructorKeyword"] = 136] = "ConstructorKeyword"; - SyntaxKind[SyntaxKind["DeclareKeyword"] = 137] = "DeclareKeyword"; - SyntaxKind[SyntaxKind["GetKeyword"] = 138] = "GetKeyword"; - SyntaxKind[SyntaxKind["ImmediateKeyword"] = 139] = "ImmediateKeyword"; - SyntaxKind[SyntaxKind["InferKeyword"] = 140] = "InferKeyword"; - SyntaxKind[SyntaxKind["IntrinsicKeyword"] = 141] = "IntrinsicKeyword"; - SyntaxKind[SyntaxKind["IsKeyword"] = 142] = "IsKeyword"; - SyntaxKind[SyntaxKind["KeyOfKeyword"] = 143] = "KeyOfKeyword"; - SyntaxKind[SyntaxKind["ModuleKeyword"] = 144] = "ModuleKeyword"; - SyntaxKind[SyntaxKind["NamespaceKeyword"] = 145] = "NamespaceKeyword"; - SyntaxKind[SyntaxKind["NeverKeyword"] = 146] = "NeverKeyword"; - SyntaxKind[SyntaxKind["OutKeyword"] = 147] = "OutKeyword"; - SyntaxKind[SyntaxKind["ReadonlyKeyword"] = 148] = "ReadonlyKeyword"; - SyntaxKind[SyntaxKind["RequireKeyword"] = 149] = "RequireKeyword"; - SyntaxKind[SyntaxKind["NumberKeyword"] = 150] = "NumberKeyword"; - SyntaxKind[SyntaxKind["ObjectKeyword"] = 151] = "ObjectKeyword"; - SyntaxKind[SyntaxKind["SatisfiesKeyword"] = 152] = "SatisfiesKeyword"; - SyntaxKind[SyntaxKind["SetKeyword"] = 153] = "SetKeyword"; - SyntaxKind[SyntaxKind["StringKeyword"] = 154] = "StringKeyword"; - SyntaxKind[SyntaxKind["SymbolKeyword"] = 155] = "SymbolKeyword"; - SyntaxKind[SyntaxKind["TypeKeyword"] = 156] = "TypeKeyword"; - SyntaxKind[SyntaxKind["UndefinedKeyword"] = 157] = "UndefinedKeyword"; - SyntaxKind[SyntaxKind["UniqueKeyword"] = 158] = "UniqueKeyword"; - SyntaxKind[SyntaxKind["UnknownKeyword"] = 159] = "UnknownKeyword"; - SyntaxKind[SyntaxKind["UsingKeyword"] = 160] = "UsingKeyword"; - SyntaxKind[SyntaxKind["FromKeyword"] = 161] = "FromKeyword"; - SyntaxKind[SyntaxKind["GlobalKeyword"] = 162] = "GlobalKeyword"; - SyntaxKind[SyntaxKind["BigIntKeyword"] = 163] = "BigIntKeyword"; - SyntaxKind[SyntaxKind["OverrideKeyword"] = 164] = "OverrideKeyword"; - SyntaxKind[SyntaxKind["OfKeyword"] = 165] = "OfKeyword"; - SyntaxKind[SyntaxKind["DeferKeyword"] = 166] = "DeferKeyword"; - SyntaxKind[SyntaxKind["QualifiedName"] = 167] = "QualifiedName"; - SyntaxKind[SyntaxKind["ComputedPropertyName"] = 168] = "ComputedPropertyName"; - SyntaxKind[SyntaxKind["TypeParameter"] = 169] = "TypeParameter"; - SyntaxKind[SyntaxKind["Parameter"] = 170] = "Parameter"; - SyntaxKind[SyntaxKind["Decorator"] = 171] = "Decorator"; - SyntaxKind[SyntaxKind["PropertySignature"] = 172] = "PropertySignature"; - SyntaxKind[SyntaxKind["PropertyDeclaration"] = 173] = "PropertyDeclaration"; - SyntaxKind[SyntaxKind["MethodSignature"] = 174] = "MethodSignature"; - SyntaxKind[SyntaxKind["MethodDeclaration"] = 175] = "MethodDeclaration"; - SyntaxKind[SyntaxKind["ClassStaticBlockDeclaration"] = 176] = "ClassStaticBlockDeclaration"; - SyntaxKind[SyntaxKind["Constructor"] = 177] = "Constructor"; - SyntaxKind[SyntaxKind["GetAccessor"] = 178] = "GetAccessor"; - SyntaxKind[SyntaxKind["SetAccessor"] = 179] = "SetAccessor"; - SyntaxKind[SyntaxKind["CallSignature"] = 180] = "CallSignature"; - SyntaxKind[SyntaxKind["ConstructSignature"] = 181] = "ConstructSignature"; - SyntaxKind[SyntaxKind["IndexSignature"] = 182] = "IndexSignature"; - SyntaxKind[SyntaxKind["TypePredicate"] = 183] = "TypePredicate"; - SyntaxKind[SyntaxKind["TypeReference"] = 184] = "TypeReference"; - SyntaxKind[SyntaxKind["FunctionType"] = 185] = "FunctionType"; - SyntaxKind[SyntaxKind["ConstructorType"] = 186] = "ConstructorType"; - SyntaxKind[SyntaxKind["TypeQuery"] = 187] = "TypeQuery"; - SyntaxKind[SyntaxKind["TypeLiteral"] = 188] = "TypeLiteral"; - SyntaxKind[SyntaxKind["ArrayType"] = 189] = "ArrayType"; - SyntaxKind[SyntaxKind["TupleType"] = 190] = "TupleType"; - SyntaxKind[SyntaxKind["OptionalType"] = 191] = "OptionalType"; - SyntaxKind[SyntaxKind["RestType"] = 192] = "RestType"; - SyntaxKind[SyntaxKind["UnionType"] = 193] = "UnionType"; - SyntaxKind[SyntaxKind["IntersectionType"] = 194] = "IntersectionType"; - SyntaxKind[SyntaxKind["ConditionalType"] = 195] = "ConditionalType"; - SyntaxKind[SyntaxKind["InferType"] = 196] = "InferType"; - SyntaxKind[SyntaxKind["ParenthesizedType"] = 197] = "ParenthesizedType"; - SyntaxKind[SyntaxKind["ThisType"] = 198] = "ThisType"; - SyntaxKind[SyntaxKind["TypeOperator"] = 199] = "TypeOperator"; - SyntaxKind[SyntaxKind["IndexedAccessType"] = 200] = "IndexedAccessType"; - SyntaxKind[SyntaxKind["MappedType"] = 201] = "MappedType"; - SyntaxKind[SyntaxKind["LiteralType"] = 202] = "LiteralType"; - SyntaxKind[SyntaxKind["NamedTupleMember"] = 203] = "NamedTupleMember"; - SyntaxKind[SyntaxKind["TemplateLiteralType"] = 204] = "TemplateLiteralType"; - SyntaxKind[SyntaxKind["TemplateLiteralTypeSpan"] = 205] = "TemplateLiteralTypeSpan"; - SyntaxKind[SyntaxKind["ImportType"] = 206] = "ImportType"; - SyntaxKind[SyntaxKind["ObjectBindingPattern"] = 207] = "ObjectBindingPattern"; - SyntaxKind[SyntaxKind["ArrayBindingPattern"] = 208] = "ArrayBindingPattern"; - SyntaxKind[SyntaxKind["BindingElement"] = 209] = "BindingElement"; - SyntaxKind[SyntaxKind["ArrayLiteralExpression"] = 210] = "ArrayLiteralExpression"; - SyntaxKind[SyntaxKind["ObjectLiteralExpression"] = 211] = "ObjectLiteralExpression"; - SyntaxKind[SyntaxKind["PropertyAccessExpression"] = 212] = "PropertyAccessExpression"; - SyntaxKind[SyntaxKind["ElementAccessExpression"] = 213] = "ElementAccessExpression"; - SyntaxKind[SyntaxKind["CallExpression"] = 214] = "CallExpression"; - SyntaxKind[SyntaxKind["NewExpression"] = 215] = "NewExpression"; - SyntaxKind[SyntaxKind["TaggedTemplateExpression"] = 216] = "TaggedTemplateExpression"; - SyntaxKind[SyntaxKind["TypeAssertionExpression"] = 217] = "TypeAssertionExpression"; - SyntaxKind[SyntaxKind["ParenthesizedExpression"] = 218] = "ParenthesizedExpression"; - SyntaxKind[SyntaxKind["FunctionExpression"] = 219] = "FunctionExpression"; - SyntaxKind[SyntaxKind["ArrowFunction"] = 220] = "ArrowFunction"; - SyntaxKind[SyntaxKind["DeleteExpression"] = 221] = "DeleteExpression"; - SyntaxKind[SyntaxKind["TypeOfExpression"] = 222] = "TypeOfExpression"; - SyntaxKind[SyntaxKind["VoidExpression"] = 223] = "VoidExpression"; - SyntaxKind[SyntaxKind["AwaitExpression"] = 224] = "AwaitExpression"; - SyntaxKind[SyntaxKind["PrefixUnaryExpression"] = 225] = "PrefixUnaryExpression"; - SyntaxKind[SyntaxKind["PostfixUnaryExpression"] = 226] = "PostfixUnaryExpression"; - SyntaxKind[SyntaxKind["BinaryExpression"] = 227] = "BinaryExpression"; - SyntaxKind[SyntaxKind["ConditionalExpression"] = 228] = "ConditionalExpression"; - SyntaxKind[SyntaxKind["TemplateExpression"] = 229] = "TemplateExpression"; - SyntaxKind[SyntaxKind["YieldExpression"] = 230] = "YieldExpression"; - SyntaxKind[SyntaxKind["SpreadElement"] = 231] = "SpreadElement"; - SyntaxKind[SyntaxKind["ClassExpression"] = 232] = "ClassExpression"; - SyntaxKind[SyntaxKind["OmittedExpression"] = 233] = "OmittedExpression"; - SyntaxKind[SyntaxKind["ExpressionWithTypeArguments"] = 234] = "ExpressionWithTypeArguments"; - SyntaxKind[SyntaxKind["AsExpression"] = 235] = "AsExpression"; - SyntaxKind[SyntaxKind["NonNullExpression"] = 236] = "NonNullExpression"; - SyntaxKind[SyntaxKind["MetaProperty"] = 237] = "MetaProperty"; - SyntaxKind[SyntaxKind["SyntheticExpression"] = 238] = "SyntheticExpression"; - SyntaxKind[SyntaxKind["SatisfiesExpression"] = 239] = "SatisfiesExpression"; - SyntaxKind[SyntaxKind["TemplateSpan"] = 240] = "TemplateSpan"; - SyntaxKind[SyntaxKind["SemicolonClassElement"] = 241] = "SemicolonClassElement"; - SyntaxKind[SyntaxKind["Block"] = 242] = "Block"; - SyntaxKind[SyntaxKind["EmptyStatement"] = 243] = "EmptyStatement"; - SyntaxKind[SyntaxKind["VariableStatement"] = 244] = "VariableStatement"; - SyntaxKind[SyntaxKind["ExpressionStatement"] = 245] = "ExpressionStatement"; - SyntaxKind[SyntaxKind["IfStatement"] = 246] = "IfStatement"; - SyntaxKind[SyntaxKind["DoStatement"] = 247] = "DoStatement"; - SyntaxKind[SyntaxKind["WhileStatement"] = 248] = "WhileStatement"; - SyntaxKind[SyntaxKind["ForStatement"] = 249] = "ForStatement"; - SyntaxKind[SyntaxKind["ForInStatement"] = 250] = "ForInStatement"; - SyntaxKind[SyntaxKind["ForOfStatement"] = 251] = "ForOfStatement"; - SyntaxKind[SyntaxKind["ContinueStatement"] = 252] = "ContinueStatement"; - SyntaxKind[SyntaxKind["BreakStatement"] = 253] = "BreakStatement"; - SyntaxKind[SyntaxKind["ReturnStatement"] = 254] = "ReturnStatement"; - SyntaxKind[SyntaxKind["WithStatement"] = 255] = "WithStatement"; - SyntaxKind[SyntaxKind["SwitchStatement"] = 256] = "SwitchStatement"; - SyntaxKind[SyntaxKind["LabeledStatement"] = 257] = "LabeledStatement"; - SyntaxKind[SyntaxKind["ThrowStatement"] = 258] = "ThrowStatement"; - SyntaxKind[SyntaxKind["TryStatement"] = 259] = "TryStatement"; - SyntaxKind[SyntaxKind["DebuggerStatement"] = 260] = "DebuggerStatement"; - SyntaxKind[SyntaxKind["VariableDeclaration"] = 261] = "VariableDeclaration"; - SyntaxKind[SyntaxKind["VariableDeclarationList"] = 262] = "VariableDeclarationList"; - SyntaxKind[SyntaxKind["FunctionDeclaration"] = 263] = "FunctionDeclaration"; - SyntaxKind[SyntaxKind["ClassDeclaration"] = 264] = "ClassDeclaration"; - SyntaxKind[SyntaxKind["InterfaceDeclaration"] = 265] = "InterfaceDeclaration"; - SyntaxKind[SyntaxKind["TypeAliasDeclaration"] = 266] = "TypeAliasDeclaration"; - SyntaxKind[SyntaxKind["EnumDeclaration"] = 267] = "EnumDeclaration"; - SyntaxKind[SyntaxKind["ModuleDeclaration"] = 268] = "ModuleDeclaration"; - SyntaxKind[SyntaxKind["ModuleBlock"] = 269] = "ModuleBlock"; - SyntaxKind[SyntaxKind["CaseBlock"] = 270] = "CaseBlock"; - SyntaxKind[SyntaxKind["NamespaceExportDeclaration"] = 271] = "NamespaceExportDeclaration"; - SyntaxKind[SyntaxKind["ImportEqualsDeclaration"] = 272] = "ImportEqualsDeclaration"; - SyntaxKind[SyntaxKind["ImportDeclaration"] = 273] = "ImportDeclaration"; - SyntaxKind[SyntaxKind["ImportClause"] = 274] = "ImportClause"; - SyntaxKind[SyntaxKind["NamespaceImport"] = 275] = "NamespaceImport"; - SyntaxKind[SyntaxKind["NamedImports"] = 276] = "NamedImports"; - SyntaxKind[SyntaxKind["ImportSpecifier"] = 277] = "ImportSpecifier"; - SyntaxKind[SyntaxKind["ExportAssignment"] = 278] = "ExportAssignment"; - SyntaxKind[SyntaxKind["ExportDeclaration"] = 279] = "ExportDeclaration"; - SyntaxKind[SyntaxKind["NamedExports"] = 280] = "NamedExports"; - SyntaxKind[SyntaxKind["NamespaceExport"] = 281] = "NamespaceExport"; - SyntaxKind[SyntaxKind["ExportSpecifier"] = 282] = "ExportSpecifier"; - SyntaxKind[SyntaxKind["MissingDeclaration"] = 283] = "MissingDeclaration"; - SyntaxKind[SyntaxKind["ExternalModuleReference"] = 284] = "ExternalModuleReference"; - SyntaxKind[SyntaxKind["JsxElement"] = 285] = "JsxElement"; - SyntaxKind[SyntaxKind["JsxSelfClosingElement"] = 286] = "JsxSelfClosingElement"; - SyntaxKind[SyntaxKind["JsxOpeningElement"] = 287] = "JsxOpeningElement"; - SyntaxKind[SyntaxKind["JsxClosingElement"] = 288] = "JsxClosingElement"; - SyntaxKind[SyntaxKind["JsxFragment"] = 289] = "JsxFragment"; - SyntaxKind[SyntaxKind["JsxOpeningFragment"] = 290] = "JsxOpeningFragment"; - SyntaxKind[SyntaxKind["JsxClosingFragment"] = 291] = "JsxClosingFragment"; - SyntaxKind[SyntaxKind["JsxAttribute"] = 292] = "JsxAttribute"; - SyntaxKind[SyntaxKind["JsxAttributes"] = 293] = "JsxAttributes"; - SyntaxKind[SyntaxKind["JsxSpreadAttribute"] = 294] = "JsxSpreadAttribute"; - SyntaxKind[SyntaxKind["JsxExpression"] = 295] = "JsxExpression"; - SyntaxKind[SyntaxKind["JsxNamespacedName"] = 296] = "JsxNamespacedName"; - SyntaxKind[SyntaxKind["CaseClause"] = 297] = "CaseClause"; - SyntaxKind[SyntaxKind["DefaultClause"] = 298] = "DefaultClause"; - SyntaxKind[SyntaxKind["HeritageClause"] = 299] = "HeritageClause"; - SyntaxKind[SyntaxKind["CatchClause"] = 300] = "CatchClause"; - SyntaxKind[SyntaxKind["ImportAttributes"] = 301] = "ImportAttributes"; - SyntaxKind[SyntaxKind["ImportAttribute"] = 302] = "ImportAttribute"; - SyntaxKind[SyntaxKind["PropertyAssignment"] = 303] = "PropertyAssignment"; - SyntaxKind[SyntaxKind["ShorthandPropertyAssignment"] = 304] = "ShorthandPropertyAssignment"; - SyntaxKind[SyntaxKind["SpreadAssignment"] = 305] = "SpreadAssignment"; - SyntaxKind[SyntaxKind["EnumMember"] = 306] = "EnumMember"; - SyntaxKind[SyntaxKind["SourceFile"] = 307] = "SourceFile"; - SyntaxKind[SyntaxKind["JSDocTypeExpression"] = 308] = "JSDocTypeExpression"; - SyntaxKind[SyntaxKind["JSDocNameReference"] = 309] = "JSDocNameReference"; - SyntaxKind[SyntaxKind["JSDocAllType"] = 310] = "JSDocAllType"; - SyntaxKind[SyntaxKind["JSDocNullableType"] = 311] = "JSDocNullableType"; - SyntaxKind[SyntaxKind["JSDocNonNullableType"] = 312] = "JSDocNonNullableType"; - SyntaxKind[SyntaxKind["JSDocOptionalType"] = 313] = "JSDocOptionalType"; - SyntaxKind[SyntaxKind["JSDocVariadicType"] = 314] = "JSDocVariadicType"; - SyntaxKind[SyntaxKind["JSDoc"] = 315] = "JSDoc"; - SyntaxKind[SyntaxKind["JSDocText"] = 316] = "JSDocText"; - SyntaxKind[SyntaxKind["JSDocTypeLiteral"] = 317] = "JSDocTypeLiteral"; - SyntaxKind[SyntaxKind["JSDocSignature"] = 318] = "JSDocSignature"; - SyntaxKind[SyntaxKind["JSDocLink"] = 319] = "JSDocLink"; - SyntaxKind[SyntaxKind["JSDocLinkCode"] = 320] = "JSDocLinkCode"; - SyntaxKind[SyntaxKind["JSDocLinkPlain"] = 321] = "JSDocLinkPlain"; - SyntaxKind[SyntaxKind["JSDocUnknownTag"] = 322] = "JSDocUnknownTag"; - SyntaxKind[SyntaxKind["JSDocAugmentsTag"] = 323] = "JSDocAugmentsTag"; - SyntaxKind[SyntaxKind["JSDocImplementsTag"] = 324] = "JSDocImplementsTag"; - SyntaxKind[SyntaxKind["JSDocDeprecatedTag"] = 325] = "JSDocDeprecatedTag"; - SyntaxKind[SyntaxKind["JSDocPublicTag"] = 326] = "JSDocPublicTag"; - SyntaxKind[SyntaxKind["JSDocPrivateTag"] = 327] = "JSDocPrivateTag"; - SyntaxKind[SyntaxKind["JSDocProtectedTag"] = 328] = "JSDocProtectedTag"; - SyntaxKind[SyntaxKind["JSDocReadonlyTag"] = 329] = "JSDocReadonlyTag"; - SyntaxKind[SyntaxKind["JSDocOverrideTag"] = 330] = "JSDocOverrideTag"; - SyntaxKind[SyntaxKind["JSDocCallbackTag"] = 331] = "JSDocCallbackTag"; - SyntaxKind[SyntaxKind["JSDocOverloadTag"] = 332] = "JSDocOverloadTag"; - SyntaxKind[SyntaxKind["JSDocParameterTag"] = 333] = "JSDocParameterTag"; - SyntaxKind[SyntaxKind["JSDocReturnTag"] = 334] = "JSDocReturnTag"; - SyntaxKind[SyntaxKind["JSDocThisTag"] = 335] = "JSDocThisTag"; - SyntaxKind[SyntaxKind["JSDocTypeTag"] = 336] = "JSDocTypeTag"; - SyntaxKind[SyntaxKind["JSDocTemplateTag"] = 337] = "JSDocTemplateTag"; - SyntaxKind[SyntaxKind["JSDocTypedefTag"] = 338] = "JSDocTypedefTag"; - SyntaxKind[SyntaxKind["JSDocSeeTag"] = 339] = "JSDocSeeTag"; - SyntaxKind[SyntaxKind["JSDocPropertyTag"] = 340] = "JSDocPropertyTag"; - SyntaxKind[SyntaxKind["JSDocThrowsTag"] = 341] = "JSDocThrowsTag"; - SyntaxKind[SyntaxKind["JSDocSatisfiesTag"] = 342] = "JSDocSatisfiesTag"; - SyntaxKind[SyntaxKind["JSDocImportTag"] = 343] = "JSDocImportTag"; - SyntaxKind[SyntaxKind["SyntaxList"] = 344] = "SyntaxList"; - SyntaxKind[SyntaxKind["JSTypeAliasDeclaration"] = 345] = "JSTypeAliasDeclaration"; - SyntaxKind[SyntaxKind["JSImportDeclaration"] = 346] = "JSImportDeclaration"; - SyntaxKind[SyntaxKind["NotEmittedStatement"] = 347] = "NotEmittedStatement"; - SyntaxKind[SyntaxKind["PartiallyEmittedExpression"] = 348] = "PartiallyEmittedExpression"; - SyntaxKind[SyntaxKind["SyntheticReferenceExpression"] = 349] = "SyntheticReferenceExpression"; - SyntaxKind[SyntaxKind["NotEmittedTypeElement"] = 350] = "NotEmittedTypeElement"; - SyntaxKind[SyntaxKind["Count"] = 351] = "Count"; - SyntaxKind[SyntaxKind["FirstAssignment"] = 63] = "FirstAssignment"; - SyntaxKind[SyntaxKind["LastAssignment"] = 78] = "LastAssignment"; - SyntaxKind[SyntaxKind["FirstCompoundAssignment"] = 64] = "FirstCompoundAssignment"; - SyntaxKind[SyntaxKind["LastCompoundAssignment"] = 78] = "LastCompoundAssignment"; - SyntaxKind[SyntaxKind["FirstReservedWord"] = 82] = "FirstReservedWord"; - SyntaxKind[SyntaxKind["LastReservedWord"] = 117] = "LastReservedWord"; - SyntaxKind[SyntaxKind["FirstKeyword"] = 82] = "FirstKeyword"; - SyntaxKind[SyntaxKind["LastKeyword"] = 166] = "LastKeyword"; - SyntaxKind[SyntaxKind["FirstFutureReservedWord"] = 118] = "FirstFutureReservedWord"; - SyntaxKind[SyntaxKind["LastFutureReservedWord"] = 126] = "LastFutureReservedWord"; - SyntaxKind[SyntaxKind["FirstTypeNode"] = 183] = "FirstTypeNode"; - SyntaxKind[SyntaxKind["LastTypeNode"] = 206] = "LastTypeNode"; - SyntaxKind[SyntaxKind["FirstPunctuation"] = 18] = "FirstPunctuation"; - SyntaxKind[SyntaxKind["LastPunctuation"] = 78] = "LastPunctuation"; - SyntaxKind[SyntaxKind["FirstToken"] = 0] = "FirstToken"; - SyntaxKind[SyntaxKind["LastToken"] = 166] = "LastToken"; - SyntaxKind[SyntaxKind["FirstLiteralToken"] = 8] = "FirstLiteralToken"; - SyntaxKind[SyntaxKind["LastLiteralToken"] = 14] = "LastLiteralToken"; - SyntaxKind[SyntaxKind["FirstTemplateToken"] = 14] = "FirstTemplateToken"; - SyntaxKind[SyntaxKind["LastTemplateToken"] = 17] = "LastTemplateToken"; - SyntaxKind[SyntaxKind["FirstBinaryOperator"] = 29] = "FirstBinaryOperator"; - SyntaxKind[SyntaxKind["LastBinaryOperator"] = 78] = "LastBinaryOperator"; - SyntaxKind[SyntaxKind["FirstStatement"] = 244] = "FirstStatement"; - SyntaxKind[SyntaxKind["LastStatement"] = 260] = "LastStatement"; - SyntaxKind[SyntaxKind["FirstNode"] = 167] = "FirstNode"; - SyntaxKind[SyntaxKind["FirstJSDocNode"] = 308] = "FirstJSDocNode"; - SyntaxKind[SyntaxKind["LastJSDocNode"] = 343] = "LastJSDocNode"; - SyntaxKind[SyntaxKind["FirstJSDocTagNode"] = 322] = "FirstJSDocTagNode"; - SyntaxKind[SyntaxKind["LastJSDocTagNode"] = 343] = "LastJSDocTagNode"; - SyntaxKind[SyntaxKind["FirstContextualKeyword"] = 127] = "FirstContextualKeyword"; - SyntaxKind[SyntaxKind["LastContextualKeyword"] = 166] = "LastContextualKeyword"; - SyntaxKind[SyntaxKind["LastUnaryOperator"] = 54] = "LastUnaryOperator"; - SyntaxKind[SyntaxKind["FirstTriviaToken"] = 2] = "FirstTriviaToken"; - SyntaxKind[SyntaxKind["LastTriviaToken"] = 6] = "LastTriviaToken"; -})(SyntaxKind || (SyntaxKind = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/kind_generated.go. DO NOT EDIT. +export var SyntaxKind: any; +(function (SyntaxKind) { + SyntaxKind[SyntaxKind["Unknown"] = 0] = "Unknown"; + SyntaxKind[SyntaxKind["EndOfFile"] = 1] = "EndOfFile"; + SyntaxKind[SyntaxKind["SingleLineCommentTrivia"] = 2] = "SingleLineCommentTrivia"; + SyntaxKind[SyntaxKind["MultiLineCommentTrivia"] = 3] = "MultiLineCommentTrivia"; + SyntaxKind[SyntaxKind["NewLineTrivia"] = 4] = "NewLineTrivia"; + SyntaxKind[SyntaxKind["WhitespaceTrivia"] = 5] = "WhitespaceTrivia"; + SyntaxKind[SyntaxKind["ConflictMarkerTrivia"] = 6] = "ConflictMarkerTrivia"; + SyntaxKind[SyntaxKind["NonTextFileMarkerTrivia"] = 7] = "NonTextFileMarkerTrivia"; + SyntaxKind[SyntaxKind["NumericLiteral"] = 8] = "NumericLiteral"; + SyntaxKind[SyntaxKind["BigIntLiteral"] = 9] = "BigIntLiteral"; + SyntaxKind[SyntaxKind["StringLiteral"] = 10] = "StringLiteral"; + SyntaxKind[SyntaxKind["JsxText"] = 11] = "JsxText"; + SyntaxKind[SyntaxKind["JsxTextAllWhiteSpaces"] = 12] = "JsxTextAllWhiteSpaces"; + SyntaxKind[SyntaxKind["RegularExpressionLiteral"] = 13] = "RegularExpressionLiteral"; + SyntaxKind[SyntaxKind["NoSubstitutionTemplateLiteral"] = 14] = "NoSubstitutionTemplateLiteral"; + SyntaxKind[SyntaxKind["TemplateHead"] = 15] = "TemplateHead"; + SyntaxKind[SyntaxKind["TemplateMiddle"] = 16] = "TemplateMiddle"; + SyntaxKind[SyntaxKind["TemplateTail"] = 17] = "TemplateTail"; + SyntaxKind[SyntaxKind["OpenBraceToken"] = 18] = "OpenBraceToken"; + SyntaxKind[SyntaxKind["CloseBraceToken"] = 19] = "CloseBraceToken"; + SyntaxKind[SyntaxKind["OpenParenToken"] = 20] = "OpenParenToken"; + SyntaxKind[SyntaxKind["CloseParenToken"] = 21] = "CloseParenToken"; + SyntaxKind[SyntaxKind["OpenBracketToken"] = 22] = "OpenBracketToken"; + SyntaxKind[SyntaxKind["CloseBracketToken"] = 23] = "CloseBracketToken"; + SyntaxKind[SyntaxKind["DotToken"] = 24] = "DotToken"; + SyntaxKind[SyntaxKind["DotDotDotToken"] = 25] = "DotDotDotToken"; + SyntaxKind[SyntaxKind["SemicolonToken"] = 26] = "SemicolonToken"; + SyntaxKind[SyntaxKind["CommaToken"] = 27] = "CommaToken"; + SyntaxKind[SyntaxKind["QuestionDotToken"] = 28] = "QuestionDotToken"; + SyntaxKind[SyntaxKind["LessThanToken"] = 29] = "LessThanToken"; + SyntaxKind[SyntaxKind["LessThanSlashToken"] = 30] = "LessThanSlashToken"; + SyntaxKind[SyntaxKind["GreaterThanToken"] = 31] = "GreaterThanToken"; + SyntaxKind[SyntaxKind["LessThanEqualsToken"] = 32] = "LessThanEqualsToken"; + SyntaxKind[SyntaxKind["GreaterThanEqualsToken"] = 33] = "GreaterThanEqualsToken"; + SyntaxKind[SyntaxKind["EqualsEqualsToken"] = 34] = "EqualsEqualsToken"; + SyntaxKind[SyntaxKind["ExclamationEqualsToken"] = 35] = "ExclamationEqualsToken"; + SyntaxKind[SyntaxKind["EqualsEqualsEqualsToken"] = 36] = "EqualsEqualsEqualsToken"; + SyntaxKind[SyntaxKind["ExclamationEqualsEqualsToken"] = 37] = "ExclamationEqualsEqualsToken"; + SyntaxKind[SyntaxKind["EqualsGreaterThanToken"] = 38] = "EqualsGreaterThanToken"; + SyntaxKind[SyntaxKind["PlusToken"] = 39] = "PlusToken"; + SyntaxKind[SyntaxKind["MinusToken"] = 40] = "MinusToken"; + SyntaxKind[SyntaxKind["AsteriskToken"] = 41] = "AsteriskToken"; + SyntaxKind[SyntaxKind["AsteriskAsteriskToken"] = 42] = "AsteriskAsteriskToken"; + SyntaxKind[SyntaxKind["SlashToken"] = 43] = "SlashToken"; + SyntaxKind[SyntaxKind["PercentToken"] = 44] = "PercentToken"; + SyntaxKind[SyntaxKind["PlusPlusToken"] = 45] = "PlusPlusToken"; + SyntaxKind[SyntaxKind["MinusMinusToken"] = 46] = "MinusMinusToken"; + SyntaxKind[SyntaxKind["LessThanLessThanToken"] = 47] = "LessThanLessThanToken"; + SyntaxKind[SyntaxKind["GreaterThanGreaterThanToken"] = 48] = "GreaterThanGreaterThanToken"; + SyntaxKind[SyntaxKind["GreaterThanGreaterThanGreaterThanToken"] = 49] = "GreaterThanGreaterThanGreaterThanToken"; + SyntaxKind[SyntaxKind["AmpersandToken"] = 50] = "AmpersandToken"; + SyntaxKind[SyntaxKind["BarToken"] = 51] = "BarToken"; + SyntaxKind[SyntaxKind["CaretToken"] = 52] = "CaretToken"; + SyntaxKind[SyntaxKind["ExclamationToken"] = 53] = "ExclamationToken"; + SyntaxKind[SyntaxKind["TildeToken"] = 54] = "TildeToken"; + SyntaxKind[SyntaxKind["AmpersandAmpersandToken"] = 55] = "AmpersandAmpersandToken"; + SyntaxKind[SyntaxKind["BarBarToken"] = 56] = "BarBarToken"; + SyntaxKind[SyntaxKind["QuestionToken"] = 57] = "QuestionToken"; + SyntaxKind[SyntaxKind["ColonToken"] = 58] = "ColonToken"; + SyntaxKind[SyntaxKind["AtToken"] = 59] = "AtToken"; + SyntaxKind[SyntaxKind["QuestionQuestionToken"] = 60] = "QuestionQuestionToken"; + SyntaxKind[SyntaxKind["BacktickToken"] = 61] = "BacktickToken"; + SyntaxKind[SyntaxKind["HashToken"] = 62] = "HashToken"; + SyntaxKind[SyntaxKind["EqualsToken"] = 63] = "EqualsToken"; + SyntaxKind[SyntaxKind["PlusEqualsToken"] = 64] = "PlusEqualsToken"; + SyntaxKind[SyntaxKind["MinusEqualsToken"] = 65] = "MinusEqualsToken"; + SyntaxKind[SyntaxKind["AsteriskEqualsToken"] = 66] = "AsteriskEqualsToken"; + SyntaxKind[SyntaxKind["AsteriskAsteriskEqualsToken"] = 67] = "AsteriskAsteriskEqualsToken"; + SyntaxKind[SyntaxKind["SlashEqualsToken"] = 68] = "SlashEqualsToken"; + SyntaxKind[SyntaxKind["PercentEqualsToken"] = 69] = "PercentEqualsToken"; + SyntaxKind[SyntaxKind["LessThanLessThanEqualsToken"] = 70] = "LessThanLessThanEqualsToken"; + SyntaxKind[SyntaxKind["GreaterThanGreaterThanEqualsToken"] = 71] = "GreaterThanGreaterThanEqualsToken"; + SyntaxKind[SyntaxKind["GreaterThanGreaterThanGreaterThanEqualsToken"] = 72] = "GreaterThanGreaterThanGreaterThanEqualsToken"; + SyntaxKind[SyntaxKind["AmpersandEqualsToken"] = 73] = "AmpersandEqualsToken"; + SyntaxKind[SyntaxKind["BarEqualsToken"] = 74] = "BarEqualsToken"; + SyntaxKind[SyntaxKind["BarBarEqualsToken"] = 75] = "BarBarEqualsToken"; + SyntaxKind[SyntaxKind["AmpersandAmpersandEqualsToken"] = 76] = "AmpersandAmpersandEqualsToken"; + SyntaxKind[SyntaxKind["QuestionQuestionEqualsToken"] = 77] = "QuestionQuestionEqualsToken"; + SyntaxKind[SyntaxKind["CaretEqualsToken"] = 78] = "CaretEqualsToken"; + SyntaxKind[SyntaxKind["Identifier"] = 79] = "Identifier"; + SyntaxKind[SyntaxKind["PrivateIdentifier"] = 80] = "PrivateIdentifier"; + SyntaxKind[SyntaxKind["JSDocCommentTextToken"] = 81] = "JSDocCommentTextToken"; + SyntaxKind[SyntaxKind["BreakKeyword"] = 82] = "BreakKeyword"; + SyntaxKind[SyntaxKind["CaseKeyword"] = 83] = "CaseKeyword"; + SyntaxKind[SyntaxKind["CatchKeyword"] = 84] = "CatchKeyword"; + SyntaxKind[SyntaxKind["ClassKeyword"] = 85] = "ClassKeyword"; + SyntaxKind[SyntaxKind["ConstKeyword"] = 86] = "ConstKeyword"; + SyntaxKind[SyntaxKind["ContinueKeyword"] = 87] = "ContinueKeyword"; + SyntaxKind[SyntaxKind["DebuggerKeyword"] = 88] = "DebuggerKeyword"; + SyntaxKind[SyntaxKind["DefaultKeyword"] = 89] = "DefaultKeyword"; + SyntaxKind[SyntaxKind["DeleteKeyword"] = 90] = "DeleteKeyword"; + SyntaxKind[SyntaxKind["DoKeyword"] = 91] = "DoKeyword"; + SyntaxKind[SyntaxKind["ElseKeyword"] = 92] = "ElseKeyword"; + SyntaxKind[SyntaxKind["EnumKeyword"] = 93] = "EnumKeyword"; + SyntaxKind[SyntaxKind["ExportKeyword"] = 94] = "ExportKeyword"; + SyntaxKind[SyntaxKind["ExtendsKeyword"] = 95] = "ExtendsKeyword"; + SyntaxKind[SyntaxKind["FalseKeyword"] = 96] = "FalseKeyword"; + SyntaxKind[SyntaxKind["FinallyKeyword"] = 97] = "FinallyKeyword"; + SyntaxKind[SyntaxKind["ForKeyword"] = 98] = "ForKeyword"; + SyntaxKind[SyntaxKind["FunctionKeyword"] = 99] = "FunctionKeyword"; + SyntaxKind[SyntaxKind["IfKeyword"] = 100] = "IfKeyword"; + SyntaxKind[SyntaxKind["ImportKeyword"] = 101] = "ImportKeyword"; + SyntaxKind[SyntaxKind["InKeyword"] = 102] = "InKeyword"; + SyntaxKind[SyntaxKind["InstanceOfKeyword"] = 103] = "InstanceOfKeyword"; + SyntaxKind[SyntaxKind["NewKeyword"] = 104] = "NewKeyword"; + SyntaxKind[SyntaxKind["NullKeyword"] = 105] = "NullKeyword"; + SyntaxKind[SyntaxKind["ReturnKeyword"] = 106] = "ReturnKeyword"; + SyntaxKind[SyntaxKind["SuperKeyword"] = 107] = "SuperKeyword"; + SyntaxKind[SyntaxKind["SwitchKeyword"] = 108] = "SwitchKeyword"; + SyntaxKind[SyntaxKind["ThisKeyword"] = 109] = "ThisKeyword"; + SyntaxKind[SyntaxKind["ThrowKeyword"] = 110] = "ThrowKeyword"; + SyntaxKind[SyntaxKind["TrueKeyword"] = 111] = "TrueKeyword"; + SyntaxKind[SyntaxKind["TryKeyword"] = 112] = "TryKeyword"; + SyntaxKind[SyntaxKind["TypeOfKeyword"] = 113] = "TypeOfKeyword"; + SyntaxKind[SyntaxKind["VarKeyword"] = 114] = "VarKeyword"; + SyntaxKind[SyntaxKind["VoidKeyword"] = 115] = "VoidKeyword"; + SyntaxKind[SyntaxKind["WhileKeyword"] = 116] = "WhileKeyword"; + SyntaxKind[SyntaxKind["WithKeyword"] = 117] = "WithKeyword"; + SyntaxKind[SyntaxKind["ImplementsKeyword"] = 118] = "ImplementsKeyword"; + SyntaxKind[SyntaxKind["InterfaceKeyword"] = 119] = "InterfaceKeyword"; + SyntaxKind[SyntaxKind["LetKeyword"] = 120] = "LetKeyword"; + SyntaxKind[SyntaxKind["PackageKeyword"] = 121] = "PackageKeyword"; + SyntaxKind[SyntaxKind["PrivateKeyword"] = 122] = "PrivateKeyword"; + SyntaxKind[SyntaxKind["ProtectedKeyword"] = 123] = "ProtectedKeyword"; + SyntaxKind[SyntaxKind["PublicKeyword"] = 124] = "PublicKeyword"; + SyntaxKind[SyntaxKind["StaticKeyword"] = 125] = "StaticKeyword"; + SyntaxKind[SyntaxKind["YieldKeyword"] = 126] = "YieldKeyword"; + SyntaxKind[SyntaxKind["AbstractKeyword"] = 127] = "AbstractKeyword"; + SyntaxKind[SyntaxKind["AccessorKeyword"] = 128] = "AccessorKeyword"; + SyntaxKind[SyntaxKind["AsKeyword"] = 129] = "AsKeyword"; + SyntaxKind[SyntaxKind["AssertsKeyword"] = 130] = "AssertsKeyword"; + SyntaxKind[SyntaxKind["AssertKeyword"] = 131] = "AssertKeyword"; + SyntaxKind[SyntaxKind["AnyKeyword"] = 132] = "AnyKeyword"; + SyntaxKind[SyntaxKind["AsyncKeyword"] = 133] = "AsyncKeyword"; + SyntaxKind[SyntaxKind["AwaitKeyword"] = 134] = "AwaitKeyword"; + SyntaxKind[SyntaxKind["BooleanKeyword"] = 135] = "BooleanKeyword"; + SyntaxKind[SyntaxKind["ConstructorKeyword"] = 136] = "ConstructorKeyword"; + SyntaxKind[SyntaxKind["DeclareKeyword"] = 137] = "DeclareKeyword"; + SyntaxKind[SyntaxKind["GetKeyword"] = 138] = "GetKeyword"; + SyntaxKind[SyntaxKind["ImmediateKeyword"] = 139] = "ImmediateKeyword"; + SyntaxKind[SyntaxKind["InferKeyword"] = 140] = "InferKeyword"; + SyntaxKind[SyntaxKind["IntrinsicKeyword"] = 141] = "IntrinsicKeyword"; + SyntaxKind[SyntaxKind["IsKeyword"] = 142] = "IsKeyword"; + SyntaxKind[SyntaxKind["KeyOfKeyword"] = 143] = "KeyOfKeyword"; + SyntaxKind[SyntaxKind["ModuleKeyword"] = 144] = "ModuleKeyword"; + SyntaxKind[SyntaxKind["NamespaceKeyword"] = 145] = "NamespaceKeyword"; + SyntaxKind[SyntaxKind["NeverKeyword"] = 146] = "NeverKeyword"; + SyntaxKind[SyntaxKind["OutKeyword"] = 147] = "OutKeyword"; + SyntaxKind[SyntaxKind["ReadonlyKeyword"] = 148] = "ReadonlyKeyword"; + SyntaxKind[SyntaxKind["RequireKeyword"] = 149] = "RequireKeyword"; + SyntaxKind[SyntaxKind["NumberKeyword"] = 150] = "NumberKeyword"; + SyntaxKind[SyntaxKind["ObjectKeyword"] = 151] = "ObjectKeyword"; + SyntaxKind[SyntaxKind["SatisfiesKeyword"] = 152] = "SatisfiesKeyword"; + SyntaxKind[SyntaxKind["SetKeyword"] = 153] = "SetKeyword"; + SyntaxKind[SyntaxKind["StringKeyword"] = 154] = "StringKeyword"; + SyntaxKind[SyntaxKind["SymbolKeyword"] = 155] = "SymbolKeyword"; + SyntaxKind[SyntaxKind["TypeKeyword"] = 156] = "TypeKeyword"; + SyntaxKind[SyntaxKind["UndefinedKeyword"] = 157] = "UndefinedKeyword"; + SyntaxKind[SyntaxKind["UniqueKeyword"] = 158] = "UniqueKeyword"; + SyntaxKind[SyntaxKind["UnknownKeyword"] = 159] = "UnknownKeyword"; + SyntaxKind[SyntaxKind["UsingKeyword"] = 160] = "UsingKeyword"; + SyntaxKind[SyntaxKind["FromKeyword"] = 161] = "FromKeyword"; + SyntaxKind[SyntaxKind["GlobalKeyword"] = 162] = "GlobalKeyword"; + SyntaxKind[SyntaxKind["BigIntKeyword"] = 163] = "BigIntKeyword"; + SyntaxKind[SyntaxKind["OverrideKeyword"] = 164] = "OverrideKeyword"; + SyntaxKind[SyntaxKind["OfKeyword"] = 165] = "OfKeyword"; + SyntaxKind[SyntaxKind["DeferKeyword"] = 166] = "DeferKeyword"; + SyntaxKind[SyntaxKind["QualifiedName"] = 167] = "QualifiedName"; + SyntaxKind[SyntaxKind["ComputedPropertyName"] = 168] = "ComputedPropertyName"; + SyntaxKind[SyntaxKind["TypeParameter"] = 169] = "TypeParameter"; + SyntaxKind[SyntaxKind["Parameter"] = 170] = "Parameter"; + SyntaxKind[SyntaxKind["Decorator"] = 171] = "Decorator"; + SyntaxKind[SyntaxKind["PropertySignature"] = 172] = "PropertySignature"; + SyntaxKind[SyntaxKind["PropertyDeclaration"] = 173] = "PropertyDeclaration"; + SyntaxKind[SyntaxKind["MethodSignature"] = 174] = "MethodSignature"; + SyntaxKind[SyntaxKind["MethodDeclaration"] = 175] = "MethodDeclaration"; + SyntaxKind[SyntaxKind["ClassStaticBlockDeclaration"] = 176] = "ClassStaticBlockDeclaration"; + SyntaxKind[SyntaxKind["Constructor"] = 177] = "Constructor"; + SyntaxKind[SyntaxKind["GetAccessor"] = 178] = "GetAccessor"; + SyntaxKind[SyntaxKind["SetAccessor"] = 179] = "SetAccessor"; + SyntaxKind[SyntaxKind["CallSignature"] = 180] = "CallSignature"; + SyntaxKind[SyntaxKind["ConstructSignature"] = 181] = "ConstructSignature"; + SyntaxKind[SyntaxKind["IndexSignature"] = 182] = "IndexSignature"; + SyntaxKind[SyntaxKind["TypePredicate"] = 183] = "TypePredicate"; + SyntaxKind[SyntaxKind["TypeReference"] = 184] = "TypeReference"; + SyntaxKind[SyntaxKind["FunctionType"] = 185] = "FunctionType"; + SyntaxKind[SyntaxKind["ConstructorType"] = 186] = "ConstructorType"; + SyntaxKind[SyntaxKind["TypeQuery"] = 187] = "TypeQuery"; + SyntaxKind[SyntaxKind["TypeLiteral"] = 188] = "TypeLiteral"; + SyntaxKind[SyntaxKind["ArrayType"] = 189] = "ArrayType"; + SyntaxKind[SyntaxKind["TupleType"] = 190] = "TupleType"; + SyntaxKind[SyntaxKind["OptionalType"] = 191] = "OptionalType"; + SyntaxKind[SyntaxKind["RestType"] = 192] = "RestType"; + SyntaxKind[SyntaxKind["UnionType"] = 193] = "UnionType"; + SyntaxKind[SyntaxKind["IntersectionType"] = 194] = "IntersectionType"; + SyntaxKind[SyntaxKind["ConditionalType"] = 195] = "ConditionalType"; + SyntaxKind[SyntaxKind["InferType"] = 196] = "InferType"; + SyntaxKind[SyntaxKind["ParenthesizedType"] = 197] = "ParenthesizedType"; + SyntaxKind[SyntaxKind["ThisType"] = 198] = "ThisType"; + SyntaxKind[SyntaxKind["TypeOperator"] = 199] = "TypeOperator"; + SyntaxKind[SyntaxKind["IndexedAccessType"] = 200] = "IndexedAccessType"; + SyntaxKind[SyntaxKind["MappedType"] = 201] = "MappedType"; + SyntaxKind[SyntaxKind["LiteralType"] = 202] = "LiteralType"; + SyntaxKind[SyntaxKind["NamedTupleMember"] = 203] = "NamedTupleMember"; + SyntaxKind[SyntaxKind["TemplateLiteralType"] = 204] = "TemplateLiteralType"; + SyntaxKind[SyntaxKind["TemplateLiteralTypeSpan"] = 205] = "TemplateLiteralTypeSpan"; + SyntaxKind[SyntaxKind["ImportType"] = 206] = "ImportType"; + SyntaxKind[SyntaxKind["ObjectBindingPattern"] = 207] = "ObjectBindingPattern"; + SyntaxKind[SyntaxKind["ArrayBindingPattern"] = 208] = "ArrayBindingPattern"; + SyntaxKind[SyntaxKind["BindingElement"] = 209] = "BindingElement"; + SyntaxKind[SyntaxKind["ArrayLiteralExpression"] = 210] = "ArrayLiteralExpression"; + SyntaxKind[SyntaxKind["ObjectLiteralExpression"] = 211] = "ObjectLiteralExpression"; + SyntaxKind[SyntaxKind["PropertyAccessExpression"] = 212] = "PropertyAccessExpression"; + SyntaxKind[SyntaxKind["ElementAccessExpression"] = 213] = "ElementAccessExpression"; + SyntaxKind[SyntaxKind["CallExpression"] = 214] = "CallExpression"; + SyntaxKind[SyntaxKind["NewExpression"] = 215] = "NewExpression"; + SyntaxKind[SyntaxKind["TaggedTemplateExpression"] = 216] = "TaggedTemplateExpression"; + SyntaxKind[SyntaxKind["TypeAssertionExpression"] = 217] = "TypeAssertionExpression"; + SyntaxKind[SyntaxKind["ParenthesizedExpression"] = 218] = "ParenthesizedExpression"; + SyntaxKind[SyntaxKind["FunctionExpression"] = 219] = "FunctionExpression"; + SyntaxKind[SyntaxKind["ArrowFunction"] = 220] = "ArrowFunction"; + SyntaxKind[SyntaxKind["DeleteExpression"] = 221] = "DeleteExpression"; + SyntaxKind[SyntaxKind["TypeOfExpression"] = 222] = "TypeOfExpression"; + SyntaxKind[SyntaxKind["VoidExpression"] = 223] = "VoidExpression"; + SyntaxKind[SyntaxKind["AwaitExpression"] = 224] = "AwaitExpression"; + SyntaxKind[SyntaxKind["PrefixUnaryExpression"] = 225] = "PrefixUnaryExpression"; + SyntaxKind[SyntaxKind["PostfixUnaryExpression"] = 226] = "PostfixUnaryExpression"; + SyntaxKind[SyntaxKind["BinaryExpression"] = 227] = "BinaryExpression"; + SyntaxKind[SyntaxKind["ConditionalExpression"] = 228] = "ConditionalExpression"; + SyntaxKind[SyntaxKind["TemplateExpression"] = 229] = "TemplateExpression"; + SyntaxKind[SyntaxKind["YieldExpression"] = 230] = "YieldExpression"; + SyntaxKind[SyntaxKind["SpreadElement"] = 231] = "SpreadElement"; + SyntaxKind[SyntaxKind["ClassExpression"] = 232] = "ClassExpression"; + SyntaxKind[SyntaxKind["OmittedExpression"] = 233] = "OmittedExpression"; + SyntaxKind[SyntaxKind["ExpressionWithTypeArguments"] = 234] = "ExpressionWithTypeArguments"; + SyntaxKind[SyntaxKind["AsExpression"] = 235] = "AsExpression"; + SyntaxKind[SyntaxKind["NonNullExpression"] = 236] = "NonNullExpression"; + SyntaxKind[SyntaxKind["MetaProperty"] = 237] = "MetaProperty"; + SyntaxKind[SyntaxKind["SyntheticExpression"] = 238] = "SyntheticExpression"; + SyntaxKind[SyntaxKind["SatisfiesExpression"] = 239] = "SatisfiesExpression"; + SyntaxKind[SyntaxKind["TemplateSpan"] = 240] = "TemplateSpan"; + SyntaxKind[SyntaxKind["SemicolonClassElement"] = 241] = "SemicolonClassElement"; + SyntaxKind[SyntaxKind["Block"] = 242] = "Block"; + SyntaxKind[SyntaxKind["EmptyStatement"] = 243] = "EmptyStatement"; + SyntaxKind[SyntaxKind["VariableStatement"] = 244] = "VariableStatement"; + SyntaxKind[SyntaxKind["ExpressionStatement"] = 245] = "ExpressionStatement"; + SyntaxKind[SyntaxKind["IfStatement"] = 246] = "IfStatement"; + SyntaxKind[SyntaxKind["DoStatement"] = 247] = "DoStatement"; + SyntaxKind[SyntaxKind["WhileStatement"] = 248] = "WhileStatement"; + SyntaxKind[SyntaxKind["ForStatement"] = 249] = "ForStatement"; + SyntaxKind[SyntaxKind["ForInStatement"] = 250] = "ForInStatement"; + SyntaxKind[SyntaxKind["ForOfStatement"] = 251] = "ForOfStatement"; + SyntaxKind[SyntaxKind["ContinueStatement"] = 252] = "ContinueStatement"; + SyntaxKind[SyntaxKind["BreakStatement"] = 253] = "BreakStatement"; + SyntaxKind[SyntaxKind["ReturnStatement"] = 254] = "ReturnStatement"; + SyntaxKind[SyntaxKind["WithStatement"] = 255] = "WithStatement"; + SyntaxKind[SyntaxKind["SwitchStatement"] = 256] = "SwitchStatement"; + SyntaxKind[SyntaxKind["LabeledStatement"] = 257] = "LabeledStatement"; + SyntaxKind[SyntaxKind["ThrowStatement"] = 258] = "ThrowStatement"; + SyntaxKind[SyntaxKind["TryStatement"] = 259] = "TryStatement"; + SyntaxKind[SyntaxKind["DebuggerStatement"] = 260] = "DebuggerStatement"; + SyntaxKind[SyntaxKind["VariableDeclaration"] = 261] = "VariableDeclaration"; + SyntaxKind[SyntaxKind["VariableDeclarationList"] = 262] = "VariableDeclarationList"; + SyntaxKind[SyntaxKind["FunctionDeclaration"] = 263] = "FunctionDeclaration"; + SyntaxKind[SyntaxKind["ClassDeclaration"] = 264] = "ClassDeclaration"; + SyntaxKind[SyntaxKind["InterfaceDeclaration"] = 265] = "InterfaceDeclaration"; + SyntaxKind[SyntaxKind["TypeAliasDeclaration"] = 266] = "TypeAliasDeclaration"; + SyntaxKind[SyntaxKind["EnumDeclaration"] = 267] = "EnumDeclaration"; + SyntaxKind[SyntaxKind["ModuleDeclaration"] = 268] = "ModuleDeclaration"; + SyntaxKind[SyntaxKind["ModuleBlock"] = 269] = "ModuleBlock"; + SyntaxKind[SyntaxKind["CaseBlock"] = 270] = "CaseBlock"; + SyntaxKind[SyntaxKind["NamespaceExportDeclaration"] = 271] = "NamespaceExportDeclaration"; + SyntaxKind[SyntaxKind["ImportEqualsDeclaration"] = 272] = "ImportEqualsDeclaration"; + SyntaxKind[SyntaxKind["ImportDeclaration"] = 273] = "ImportDeclaration"; + SyntaxKind[SyntaxKind["ImportClause"] = 274] = "ImportClause"; + SyntaxKind[SyntaxKind["NamespaceImport"] = 275] = "NamespaceImport"; + SyntaxKind[SyntaxKind["NamedImports"] = 276] = "NamedImports"; + SyntaxKind[SyntaxKind["ImportSpecifier"] = 277] = "ImportSpecifier"; + SyntaxKind[SyntaxKind["ExportAssignment"] = 278] = "ExportAssignment"; + SyntaxKind[SyntaxKind["ExportDeclaration"] = 279] = "ExportDeclaration"; + SyntaxKind[SyntaxKind["NamedExports"] = 280] = "NamedExports"; + SyntaxKind[SyntaxKind["NamespaceExport"] = 281] = "NamespaceExport"; + SyntaxKind[SyntaxKind["ExportSpecifier"] = 282] = "ExportSpecifier"; + SyntaxKind[SyntaxKind["MissingDeclaration"] = 283] = "MissingDeclaration"; + SyntaxKind[SyntaxKind["ExternalModuleReference"] = 284] = "ExternalModuleReference"; + SyntaxKind[SyntaxKind["JsxElement"] = 285] = "JsxElement"; + SyntaxKind[SyntaxKind["JsxSelfClosingElement"] = 286] = "JsxSelfClosingElement"; + SyntaxKind[SyntaxKind["JsxOpeningElement"] = 287] = "JsxOpeningElement"; + SyntaxKind[SyntaxKind["JsxClosingElement"] = 288] = "JsxClosingElement"; + SyntaxKind[SyntaxKind["JsxFragment"] = 289] = "JsxFragment"; + SyntaxKind[SyntaxKind["JsxOpeningFragment"] = 290] = "JsxOpeningFragment"; + SyntaxKind[SyntaxKind["JsxClosingFragment"] = 291] = "JsxClosingFragment"; + SyntaxKind[SyntaxKind["JsxAttribute"] = 292] = "JsxAttribute"; + SyntaxKind[SyntaxKind["JsxAttributes"] = 293] = "JsxAttributes"; + SyntaxKind[SyntaxKind["JsxSpreadAttribute"] = 294] = "JsxSpreadAttribute"; + SyntaxKind[SyntaxKind["JsxExpression"] = 295] = "JsxExpression"; + SyntaxKind[SyntaxKind["JsxNamespacedName"] = 296] = "JsxNamespacedName"; + SyntaxKind[SyntaxKind["CaseClause"] = 297] = "CaseClause"; + SyntaxKind[SyntaxKind["DefaultClause"] = 298] = "DefaultClause"; + SyntaxKind[SyntaxKind["HeritageClause"] = 299] = "HeritageClause"; + SyntaxKind[SyntaxKind["CatchClause"] = 300] = "CatchClause"; + SyntaxKind[SyntaxKind["ImportAttributes"] = 301] = "ImportAttributes"; + SyntaxKind[SyntaxKind["ImportAttribute"] = 302] = "ImportAttribute"; + SyntaxKind[SyntaxKind["PropertyAssignment"] = 303] = "PropertyAssignment"; + SyntaxKind[SyntaxKind["ShorthandPropertyAssignment"] = 304] = "ShorthandPropertyAssignment"; + SyntaxKind[SyntaxKind["SpreadAssignment"] = 305] = "SpreadAssignment"; + SyntaxKind[SyntaxKind["EnumMember"] = 306] = "EnumMember"; + SyntaxKind[SyntaxKind["SourceFile"] = 307] = "SourceFile"; + SyntaxKind[SyntaxKind["JSDocTypeExpression"] = 308] = "JSDocTypeExpression"; + SyntaxKind[SyntaxKind["JSDocNameReference"] = 309] = "JSDocNameReference"; + SyntaxKind[SyntaxKind["JSDocAllType"] = 310] = "JSDocAllType"; + SyntaxKind[SyntaxKind["JSDocNullableType"] = 311] = "JSDocNullableType"; + SyntaxKind[SyntaxKind["JSDocNonNullableType"] = 312] = "JSDocNonNullableType"; + SyntaxKind[SyntaxKind["JSDocOptionalType"] = 313] = "JSDocOptionalType"; + SyntaxKind[SyntaxKind["JSDocVariadicType"] = 314] = "JSDocVariadicType"; + SyntaxKind[SyntaxKind["JSDoc"] = 315] = "JSDoc"; + SyntaxKind[SyntaxKind["JSDocText"] = 316] = "JSDocText"; + SyntaxKind[SyntaxKind["JSDocTypeLiteral"] = 317] = "JSDocTypeLiteral"; + SyntaxKind[SyntaxKind["JSDocSignature"] = 318] = "JSDocSignature"; + SyntaxKind[SyntaxKind["JSDocLink"] = 319] = "JSDocLink"; + SyntaxKind[SyntaxKind["JSDocLinkCode"] = 320] = "JSDocLinkCode"; + SyntaxKind[SyntaxKind["JSDocLinkPlain"] = 321] = "JSDocLinkPlain"; + SyntaxKind[SyntaxKind["JSDocUnknownTag"] = 322] = "JSDocUnknownTag"; + SyntaxKind[SyntaxKind["JSDocAugmentsTag"] = 323] = "JSDocAugmentsTag"; + SyntaxKind[SyntaxKind["JSDocImplementsTag"] = 324] = "JSDocImplementsTag"; + SyntaxKind[SyntaxKind["JSDocDeprecatedTag"] = 325] = "JSDocDeprecatedTag"; + SyntaxKind[SyntaxKind["JSDocPublicTag"] = 326] = "JSDocPublicTag"; + SyntaxKind[SyntaxKind["JSDocPrivateTag"] = 327] = "JSDocPrivateTag"; + SyntaxKind[SyntaxKind["JSDocProtectedTag"] = 328] = "JSDocProtectedTag"; + SyntaxKind[SyntaxKind["JSDocReadonlyTag"] = 329] = "JSDocReadonlyTag"; + SyntaxKind[SyntaxKind["JSDocOverrideTag"] = 330] = "JSDocOverrideTag"; + SyntaxKind[SyntaxKind["JSDocCallbackTag"] = 331] = "JSDocCallbackTag"; + SyntaxKind[SyntaxKind["JSDocOverloadTag"] = 332] = "JSDocOverloadTag"; + SyntaxKind[SyntaxKind["JSDocParameterTag"] = 333] = "JSDocParameterTag"; + SyntaxKind[SyntaxKind["JSDocReturnTag"] = 334] = "JSDocReturnTag"; + SyntaxKind[SyntaxKind["JSDocThisTag"] = 335] = "JSDocThisTag"; + SyntaxKind[SyntaxKind["JSDocTypeTag"] = 336] = "JSDocTypeTag"; + SyntaxKind[SyntaxKind["JSDocTemplateTag"] = 337] = "JSDocTemplateTag"; + SyntaxKind[SyntaxKind["JSDocTypedefTag"] = 338] = "JSDocTypedefTag"; + SyntaxKind[SyntaxKind["JSDocSeeTag"] = 339] = "JSDocSeeTag"; + SyntaxKind[SyntaxKind["JSDocPropertyTag"] = 340] = "JSDocPropertyTag"; + SyntaxKind[SyntaxKind["JSDocThrowsTag"] = 341] = "JSDocThrowsTag"; + SyntaxKind[SyntaxKind["JSDocSatisfiesTag"] = 342] = "JSDocSatisfiesTag"; + SyntaxKind[SyntaxKind["JSDocImportTag"] = 343] = "JSDocImportTag"; + SyntaxKind[SyntaxKind["SyntaxList"] = 344] = "SyntaxList"; + SyntaxKind[SyntaxKind["JSTypeAliasDeclaration"] = 345] = "JSTypeAliasDeclaration"; + SyntaxKind[SyntaxKind["JSImportDeclaration"] = 346] = "JSImportDeclaration"; + SyntaxKind[SyntaxKind["NotEmittedStatement"] = 347] = "NotEmittedStatement"; + SyntaxKind[SyntaxKind["PartiallyEmittedExpression"] = 348] = "PartiallyEmittedExpression"; + SyntaxKind[SyntaxKind["SyntheticReferenceExpression"] = 349] = "SyntheticReferenceExpression"; + SyntaxKind[SyntaxKind["NotEmittedTypeElement"] = 350] = "NotEmittedTypeElement"; + SyntaxKind[SyntaxKind["Count"] = 351] = "Count"; + SyntaxKind[SyntaxKind["FirstAssignment"] = 63] = "FirstAssignment"; + SyntaxKind[SyntaxKind["LastAssignment"] = 78] = "LastAssignment"; + SyntaxKind[SyntaxKind["FirstCompoundAssignment"] = 64] = "FirstCompoundAssignment"; + SyntaxKind[SyntaxKind["LastCompoundAssignment"] = 78] = "LastCompoundAssignment"; + SyntaxKind[SyntaxKind["FirstReservedWord"] = 82] = "FirstReservedWord"; + SyntaxKind[SyntaxKind["LastReservedWord"] = 117] = "LastReservedWord"; + SyntaxKind[SyntaxKind["FirstKeyword"] = 82] = "FirstKeyword"; + SyntaxKind[SyntaxKind["LastKeyword"] = 166] = "LastKeyword"; + SyntaxKind[SyntaxKind["FirstFutureReservedWord"] = 118] = "FirstFutureReservedWord"; + SyntaxKind[SyntaxKind["LastFutureReservedWord"] = 126] = "LastFutureReservedWord"; + SyntaxKind[SyntaxKind["FirstTypeNode"] = 183] = "FirstTypeNode"; + SyntaxKind[SyntaxKind["LastTypeNode"] = 206] = "LastTypeNode"; + SyntaxKind[SyntaxKind["FirstPunctuation"] = 18] = "FirstPunctuation"; + SyntaxKind[SyntaxKind["LastPunctuation"] = 78] = "LastPunctuation"; + SyntaxKind[SyntaxKind["FirstToken"] = 0] = "FirstToken"; + SyntaxKind[SyntaxKind["LastToken"] = 166] = "LastToken"; + SyntaxKind[SyntaxKind["FirstLiteralToken"] = 8] = "FirstLiteralToken"; + SyntaxKind[SyntaxKind["LastLiteralToken"] = 14] = "LastLiteralToken"; + SyntaxKind[SyntaxKind["FirstTemplateToken"] = 14] = "FirstTemplateToken"; + SyntaxKind[SyntaxKind["LastTemplateToken"] = 17] = "LastTemplateToken"; + SyntaxKind[SyntaxKind["FirstBinaryOperator"] = 29] = "FirstBinaryOperator"; + SyntaxKind[SyntaxKind["LastBinaryOperator"] = 78] = "LastBinaryOperator"; + SyntaxKind[SyntaxKind["FirstStatement"] = 244] = "FirstStatement"; + SyntaxKind[SyntaxKind["LastStatement"] = 260] = "LastStatement"; + SyntaxKind[SyntaxKind["FirstNode"] = 167] = "FirstNode"; + SyntaxKind[SyntaxKind["FirstJSDocNode"] = 308] = "FirstJSDocNode"; + SyntaxKind[SyntaxKind["LastJSDocNode"] = 343] = "LastJSDocNode"; + SyntaxKind[SyntaxKind["FirstJSDocTagNode"] = 322] = "FirstJSDocTagNode"; + SyntaxKind[SyntaxKind["LastJSDocTagNode"] = 343] = "LastJSDocTagNode"; + SyntaxKind[SyntaxKind["FirstContextualKeyword"] = 127] = "FirstContextualKeyword"; + SyntaxKind[SyntaxKind["LastContextualKeyword"] = 166] = "LastContextualKeyword"; + SyntaxKind[SyntaxKind["LastUnaryOperator"] = 54] = "LastUnaryOperator"; + SyntaxKind[SyntaxKind["FirstTriviaToken"] = 2] = "FirstTriviaToken"; + SyntaxKind[SyntaxKind["LastTriviaToken"] = 6] = "LastTriviaToken"; +})(SyntaxKind || (SyntaxKind = {})); diff --git a/packages/typescript/src/enums/tokenFlags.enum.ts b/packages/typescript/src/enums/tokenFlags.enum.ts index 8459f360d196e..75ade8ce1860c 100644 --- a/packages/typescript/src/enums/tokenFlags.enum.ts +++ b/packages/typescript/src/enums/tokenFlags.enum.ts @@ -1,31 +1,31 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/tokenflags.go. DO NOT EDIT. - -export enum TokenFlags { - None = 0, - PrecedingLineBreak = 1 << 0, - PrecedingJSDocComment = 1 << 1, - Unterminated = 1 << 2, - ExtendedUnicodeEscape = 1 << 3, - Scientific = 1 << 4, - Octal = 1 << 5, - HexSpecifier = 1 << 6, - BinarySpecifier = 1 << 7, - OctalSpecifier = 1 << 8, - ContainsSeparator = 1 << 9, - UnicodeEscape = 1 << 10, - ContainsInvalidEscape = 1 << 11, - HexEscape = 1 << 12, - ContainsLeadingZero = 1 << 13, - ContainsInvalidSeparator = 1 << 14, - PrecedingJSDocLeadingAsterisks = 1 << 15, - SingleQuote = 1 << 16, - PrecedingJSDocWithDeprecated = 1 << 17, - PrecedingJSDocWithSeeOrLink = 1 << 18, - BinaryOrOctalSpecifier = BinarySpecifier | OctalSpecifier, - WithSpecifier = HexSpecifier | BinaryOrOctalSpecifier, - StringLiteralFlags = Unterminated | HexEscape | UnicodeEscape | ExtendedUnicodeEscape | ContainsInvalidEscape | SingleQuote, - NumericLiteralFlags = Scientific | Octal | ContainsLeadingZero | WithSpecifier | ContainsSeparator | ContainsInvalidSeparator, - TemplateLiteralLikeFlags = Unterminated | HexEscape | UnicodeEscape | ExtendedUnicodeEscape | ContainsInvalidEscape, - RegularExpressionLiteralFlags = Unterminated, - IsInvalid = Octal | ContainsLeadingZero | ContainsInvalidSeparator | ContainsInvalidEscape, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/tokenflags.go. DO NOT EDIT. + +export enum TokenFlags { + None = 0, + PrecedingLineBreak = 1 << 0, + PrecedingJSDocComment = 1 << 1, + Unterminated = 1 << 2, + ExtendedUnicodeEscape = 1 << 3, + Scientific = 1 << 4, + Octal = 1 << 5, + HexSpecifier = 1 << 6, + BinarySpecifier = 1 << 7, + OctalSpecifier = 1 << 8, + ContainsSeparator = 1 << 9, + UnicodeEscape = 1 << 10, + ContainsInvalidEscape = 1 << 11, + HexEscape = 1 << 12, + ContainsLeadingZero = 1 << 13, + ContainsInvalidSeparator = 1 << 14, + PrecedingJSDocLeadingAsterisks = 1 << 15, + SingleQuote = 1 << 16, + PrecedingJSDocWithDeprecated = 1 << 17, + PrecedingJSDocWithSeeOrLink = 1 << 18, + BinaryOrOctalSpecifier = BinarySpecifier | OctalSpecifier, + WithSpecifier = HexSpecifier | BinaryOrOctalSpecifier, + StringLiteralFlags = Unterminated | HexEscape | UnicodeEscape | ExtendedUnicodeEscape | ContainsInvalidEscape | SingleQuote, + NumericLiteralFlags = Scientific | Octal | ContainsLeadingZero | WithSpecifier | ContainsSeparator | ContainsInvalidSeparator, + TemplateLiteralLikeFlags = Unterminated | HexEscape | UnicodeEscape | ExtendedUnicodeEscape | ContainsInvalidEscape, + RegularExpressionLiteralFlags = Unterminated, + IsInvalid = Octal | ContainsLeadingZero | ContainsInvalidSeparator | ContainsInvalidEscape, +} diff --git a/packages/typescript/src/enums/tokenFlags.ts b/packages/typescript/src/enums/tokenFlags.ts index eece2c2eefecc..878747f181f30 100644 --- a/packages/typescript/src/enums/tokenFlags.ts +++ b/packages/typescript/src/enums/tokenFlags.ts @@ -1,31 +1,31 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/tokenflags.go. DO NOT EDIT. -export var TokenFlags: any; -(function (TokenFlags) { - TokenFlags[TokenFlags["None"] = 0] = "None"; - TokenFlags[TokenFlags["PrecedingLineBreak"] = 1] = "PrecedingLineBreak"; - TokenFlags[TokenFlags["PrecedingJSDocComment"] = 2] = "PrecedingJSDocComment"; - TokenFlags[TokenFlags["Unterminated"] = 4] = "Unterminated"; - TokenFlags[TokenFlags["ExtendedUnicodeEscape"] = 8] = "ExtendedUnicodeEscape"; - TokenFlags[TokenFlags["Scientific"] = 16] = "Scientific"; - TokenFlags[TokenFlags["Octal"] = 32] = "Octal"; - TokenFlags[TokenFlags["HexSpecifier"] = 64] = "HexSpecifier"; - TokenFlags[TokenFlags["BinarySpecifier"] = 128] = "BinarySpecifier"; - TokenFlags[TokenFlags["OctalSpecifier"] = 256] = "OctalSpecifier"; - TokenFlags[TokenFlags["ContainsSeparator"] = 512] = "ContainsSeparator"; - TokenFlags[TokenFlags["UnicodeEscape"] = 1024] = "UnicodeEscape"; - TokenFlags[TokenFlags["ContainsInvalidEscape"] = 2048] = "ContainsInvalidEscape"; - TokenFlags[TokenFlags["HexEscape"] = 4096] = "HexEscape"; - TokenFlags[TokenFlags["ContainsLeadingZero"] = 8192] = "ContainsLeadingZero"; - TokenFlags[TokenFlags["ContainsInvalidSeparator"] = 16384] = "ContainsInvalidSeparator"; - TokenFlags[TokenFlags["PrecedingJSDocLeadingAsterisks"] = 32768] = "PrecedingJSDocLeadingAsterisks"; - TokenFlags[TokenFlags["SingleQuote"] = 65536] = "SingleQuote"; - TokenFlags[TokenFlags["PrecedingJSDocWithDeprecated"] = 131072] = "PrecedingJSDocWithDeprecated"; - TokenFlags[TokenFlags["PrecedingJSDocWithSeeOrLink"] = 262144] = "PrecedingJSDocWithSeeOrLink"; - TokenFlags[TokenFlags["BinaryOrOctalSpecifier"] = 384] = "BinaryOrOctalSpecifier"; - TokenFlags[TokenFlags["WithSpecifier"] = 448] = "WithSpecifier"; - TokenFlags[TokenFlags["StringLiteralFlags"] = 72716] = "StringLiteralFlags"; - TokenFlags[TokenFlags["NumericLiteralFlags"] = 25584] = "NumericLiteralFlags"; - TokenFlags[TokenFlags["TemplateLiteralLikeFlags"] = 7180] = "TemplateLiteralLikeFlags"; - TokenFlags[TokenFlags["RegularExpressionLiteralFlags"] = 4] = "RegularExpressionLiteralFlags"; - TokenFlags[TokenFlags["IsInvalid"] = 26656] = "IsInvalid"; -})(TokenFlags || (TokenFlags = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/ast/tokenflags.go. DO NOT EDIT. +export var TokenFlags: any; +(function (TokenFlags) { + TokenFlags[TokenFlags["None"] = 0] = "None"; + TokenFlags[TokenFlags["PrecedingLineBreak"] = 1] = "PrecedingLineBreak"; + TokenFlags[TokenFlags["PrecedingJSDocComment"] = 2] = "PrecedingJSDocComment"; + TokenFlags[TokenFlags["Unterminated"] = 4] = "Unterminated"; + TokenFlags[TokenFlags["ExtendedUnicodeEscape"] = 8] = "ExtendedUnicodeEscape"; + TokenFlags[TokenFlags["Scientific"] = 16] = "Scientific"; + TokenFlags[TokenFlags["Octal"] = 32] = "Octal"; + TokenFlags[TokenFlags["HexSpecifier"] = 64] = "HexSpecifier"; + TokenFlags[TokenFlags["BinarySpecifier"] = 128] = "BinarySpecifier"; + TokenFlags[TokenFlags["OctalSpecifier"] = 256] = "OctalSpecifier"; + TokenFlags[TokenFlags["ContainsSeparator"] = 512] = "ContainsSeparator"; + TokenFlags[TokenFlags["UnicodeEscape"] = 1024] = "UnicodeEscape"; + TokenFlags[TokenFlags["ContainsInvalidEscape"] = 2048] = "ContainsInvalidEscape"; + TokenFlags[TokenFlags["HexEscape"] = 4096] = "HexEscape"; + TokenFlags[TokenFlags["ContainsLeadingZero"] = 8192] = "ContainsLeadingZero"; + TokenFlags[TokenFlags["ContainsInvalidSeparator"] = 16384] = "ContainsInvalidSeparator"; + TokenFlags[TokenFlags["PrecedingJSDocLeadingAsterisks"] = 32768] = "PrecedingJSDocLeadingAsterisks"; + TokenFlags[TokenFlags["SingleQuote"] = 65536] = "SingleQuote"; + TokenFlags[TokenFlags["PrecedingJSDocWithDeprecated"] = 131072] = "PrecedingJSDocWithDeprecated"; + TokenFlags[TokenFlags["PrecedingJSDocWithSeeOrLink"] = 262144] = "PrecedingJSDocWithSeeOrLink"; + TokenFlags[TokenFlags["BinaryOrOctalSpecifier"] = 384] = "BinaryOrOctalSpecifier"; + TokenFlags[TokenFlags["WithSpecifier"] = 448] = "WithSpecifier"; + TokenFlags[TokenFlags["StringLiteralFlags"] = 72716] = "StringLiteralFlags"; + TokenFlags[TokenFlags["NumericLiteralFlags"] = 25584] = "NumericLiteralFlags"; + TokenFlags[TokenFlags["TemplateLiteralLikeFlags"] = 7180] = "TemplateLiteralLikeFlags"; + TokenFlags[TokenFlags["RegularExpressionLiteralFlags"] = 4] = "RegularExpressionLiteralFlags"; + TokenFlags[TokenFlags["IsInvalid"] = 26656] = "IsInvalid"; +})(TokenFlags || (TokenFlags = {})); diff --git a/packages/typescript/src/enums/typeFlags.enum.ts b/packages/typescript/src/enums/typeFlags.enum.ts index 88701e2174f55..5e17cef5e9662 100644 --- a/packages/typescript/src/enums/typeFlags.enum.ts +++ b/packages/typescript/src/enums/typeFlags.enum.ts @@ -1,77 +1,77 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. - -export enum TypeFlags { - None = 0, - Any = 1 << 0, - Unknown = 1 << 1, - Undefined = 1 << 2, - Null = 1 << 3, - Void = 1 << 4, - String = 1 << 5, - Number = 1 << 6, - BigInt = 1 << 7, - Boolean = 1 << 8, - ESSymbol = 1 << 9, - StringLiteral = 1 << 10, - NumberLiteral = 1 << 11, - BigIntLiteral = 1 << 12, - BooleanLiteral = 1 << 13, - UniqueESSymbol = 1 << 14, - EnumLiteral = 1 << 15, - Enum = 1 << 16, - NonPrimitive = 1 << 17, - Never = 1 << 18, - TypeParameter = 1 << 19, - Object = 1 << 20, - Index = 1 << 21, - TemplateLiteral = 1 << 22, - StringMapping = 1 << 23, - Substitution = 1 << 24, - IndexedAccess = 1 << 25, - Conditional = 1 << 26, - Union = 1 << 27, - Intersection = 1 << 28, - Reserved1 = 1 << 29, - Reserved2 = 1 << 30, - Reserved3 = 1 << 31, - AnyOrUnknown = Any | Unknown, - Nullable = Undefined | Null, - Literal = StringLiteral | NumberLiteral | BigIntLiteral | BooleanLiteral, - Unit = Enum | Literal | UniqueESSymbol | Nullable, - Freshable = Enum | Literal, - StringOrNumberLiteral = StringLiteral | NumberLiteral, - StringOrNumberLiteralOrUnique = StringLiteral | NumberLiteral | UniqueESSymbol, - DefinitelyFalsy = StringLiteral | NumberLiteral | BigIntLiteral | BooleanLiteral | Void | Undefined | Null, - PossiblyFalsy = DefinitelyFalsy | String | Number | BigInt | Boolean, - Intrinsic = Any | Unknown | String | Number | BigInt | ESSymbol | Void | Undefined | Null | Never | NonPrimitive, - StringLike = String | StringLiteral | TemplateLiteral | StringMapping, - NumberLike = Number | NumberLiteral | Enum, - BigIntLike = BigInt | BigIntLiteral, - BooleanLike = Boolean | BooleanLiteral, - EnumLike = Enum | EnumLiteral, - ESSymbolLike = ESSymbol | UniqueESSymbol, - VoidLike = Void | Undefined, - Primitive = StringLike | NumberLike | BigIntLike | BooleanLike | EnumLike | ESSymbolLike | VoidLike | Null, - DefinitelyNonNullable = StringLike | NumberLike | BigIntLike | BooleanLike | EnumLike | ESSymbolLike | Object | NonPrimitive, - DisjointDomains = NonPrimitive | StringLike | NumberLike | BigIntLike | BooleanLike | ESSymbolLike | VoidLike | Null, - UnionOrIntersection = Union | Intersection, - StructuredType = Object | Union | Intersection, - TypeVariable = TypeParameter | IndexedAccess, - InstantiableNonPrimitive = TypeVariable | Conditional | Substitution, - InstantiablePrimitive = Index | TemplateLiteral | StringMapping, - Instantiable = InstantiableNonPrimitive | InstantiablePrimitive, - StructuredOrInstantiable = StructuredType | Instantiable, - ObjectFlagsType = Any | Nullable | Never | Object | Union | Intersection, - Simplifiable = IndexedAccess | Conditional | Index, - Singleton = Any | Unknown | String | Number | Boolean | BigInt | ESSymbol | Void | Undefined | Null | Never | NonPrimitive, - Narrowable = Any | Unknown | StructuredOrInstantiable | StringLike | NumberLike | BigIntLike | BooleanLike | ESSymbol | UniqueESSymbol | NonPrimitive, - IncludesMask = Any | Unknown | Primitive | Never | Object | Union | Intersection | NonPrimitive | TemplateLiteral | StringMapping, - IncludesMissingType = TypeParameter, - IncludesNonWideningType = Index, - IncludesWildcard = IndexedAccess, - IncludesEmptyObject = Conditional, - IncludesInstantiable = Substitution, - IncludesConstrainedTypeVariable = Reserved1, - IncludesError = Reserved2, - NotPrimitiveUnion = Any | Unknown | Void | Never | Object | Intersection | IncludesInstantiable, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. + +export enum TypeFlags { + None = 0, + Any = 1 << 0, + Unknown = 1 << 1, + Undefined = 1 << 2, + Null = 1 << 3, + Void = 1 << 4, + String = 1 << 5, + Number = 1 << 6, + BigInt = 1 << 7, + Boolean = 1 << 8, + ESSymbol = 1 << 9, + StringLiteral = 1 << 10, + NumberLiteral = 1 << 11, + BigIntLiteral = 1 << 12, + BooleanLiteral = 1 << 13, + UniqueESSymbol = 1 << 14, + EnumLiteral = 1 << 15, + Enum = 1 << 16, + NonPrimitive = 1 << 17, + Never = 1 << 18, + TypeParameter = 1 << 19, + Object = 1 << 20, + Index = 1 << 21, + TemplateLiteral = 1 << 22, + StringMapping = 1 << 23, + Substitution = 1 << 24, + IndexedAccess = 1 << 25, + Conditional = 1 << 26, + Union = 1 << 27, + Intersection = 1 << 28, + Reserved1 = 1 << 29, + Reserved2 = 1 << 30, + Reserved3 = 1 << 31, + AnyOrUnknown = Any | Unknown, + Nullable = Undefined | Null, + Literal = StringLiteral | NumberLiteral | BigIntLiteral | BooleanLiteral, + Unit = Enum | Literal | UniqueESSymbol | Nullable, + Freshable = Enum | Literal, + StringOrNumberLiteral = StringLiteral | NumberLiteral, + StringOrNumberLiteralOrUnique = StringLiteral | NumberLiteral | UniqueESSymbol, + DefinitelyFalsy = StringLiteral | NumberLiteral | BigIntLiteral | BooleanLiteral | Void | Undefined | Null, + PossiblyFalsy = DefinitelyFalsy | String | Number | BigInt | Boolean, + Intrinsic = Any | Unknown | String | Number | BigInt | ESSymbol | Void | Undefined | Null | Never | NonPrimitive, + StringLike = String | StringLiteral | TemplateLiteral | StringMapping, + NumberLike = Number | NumberLiteral | Enum, + BigIntLike = BigInt | BigIntLiteral, + BooleanLike = Boolean | BooleanLiteral, + EnumLike = Enum | EnumLiteral, + ESSymbolLike = ESSymbol | UniqueESSymbol, + VoidLike = Void | Undefined, + Primitive = StringLike | NumberLike | BigIntLike | BooleanLike | EnumLike | ESSymbolLike | VoidLike | Null, + DefinitelyNonNullable = StringLike | NumberLike | BigIntLike | BooleanLike | EnumLike | ESSymbolLike | Object | NonPrimitive, + DisjointDomains = NonPrimitive | StringLike | NumberLike | BigIntLike | BooleanLike | ESSymbolLike | VoidLike | Null, + UnionOrIntersection = Union | Intersection, + StructuredType = Object | Union | Intersection, + TypeVariable = TypeParameter | IndexedAccess, + InstantiableNonPrimitive = TypeVariable | Conditional | Substitution, + InstantiablePrimitive = Index | TemplateLiteral | StringMapping, + Instantiable = InstantiableNonPrimitive | InstantiablePrimitive, + StructuredOrInstantiable = StructuredType | Instantiable, + ObjectFlagsType = Any | Nullable | Never | Object | Union | Intersection, + Simplifiable = IndexedAccess | Conditional | Index, + Singleton = Any | Unknown | String | Number | Boolean | BigInt | ESSymbol | Void | Undefined | Null | Never | NonPrimitive, + Narrowable = Any | Unknown | StructuredOrInstantiable | StringLike | NumberLike | BigIntLike | BooleanLike | ESSymbol | UniqueESSymbol | NonPrimitive, + IncludesMask = Any | Unknown | Primitive | Never | Object | Union | Intersection | NonPrimitive | TemplateLiteral | StringMapping, + IncludesMissingType = TypeParameter, + IncludesNonWideningType = Index, + IncludesWildcard = IndexedAccess, + IncludesEmptyObject = Conditional, + IncludesInstantiable = Substitution, + IncludesConstrainedTypeVariable = Reserved1, + IncludesError = Reserved2, + NotPrimitiveUnion = Any | Unknown | Void | Never | Object | Intersection | IncludesInstantiable, +} diff --git a/packages/typescript/src/enums/typeFlags.ts b/packages/typescript/src/enums/typeFlags.ts index 01fbbcbdeaca5..06b7ce90f50f0 100644 --- a/packages/typescript/src/enums/typeFlags.ts +++ b/packages/typescript/src/enums/typeFlags.ts @@ -1,77 +1,77 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. -export var TypeFlags: any; -(function (TypeFlags) { - TypeFlags[TypeFlags["None"] = 0] = "None"; - TypeFlags[TypeFlags["Any"] = 1] = "Any"; - TypeFlags[TypeFlags["Unknown"] = 2] = "Unknown"; - TypeFlags[TypeFlags["Undefined"] = 4] = "Undefined"; - TypeFlags[TypeFlags["Null"] = 8] = "Null"; - TypeFlags[TypeFlags["Void"] = 16] = "Void"; - TypeFlags[TypeFlags["String"] = 32] = "String"; - TypeFlags[TypeFlags["Number"] = 64] = "Number"; - TypeFlags[TypeFlags["BigInt"] = 128] = "BigInt"; - TypeFlags[TypeFlags["Boolean"] = 256] = "Boolean"; - TypeFlags[TypeFlags["ESSymbol"] = 512] = "ESSymbol"; - TypeFlags[TypeFlags["StringLiteral"] = 1024] = "StringLiteral"; - TypeFlags[TypeFlags["NumberLiteral"] = 2048] = "NumberLiteral"; - TypeFlags[TypeFlags["BigIntLiteral"] = 4096] = "BigIntLiteral"; - TypeFlags[TypeFlags["BooleanLiteral"] = 8192] = "BooleanLiteral"; - TypeFlags[TypeFlags["UniqueESSymbol"] = 16384] = "UniqueESSymbol"; - TypeFlags[TypeFlags["EnumLiteral"] = 32768] = "EnumLiteral"; - TypeFlags[TypeFlags["Enum"] = 65536] = "Enum"; - TypeFlags[TypeFlags["NonPrimitive"] = 131072] = "NonPrimitive"; - TypeFlags[TypeFlags["Never"] = 262144] = "Never"; - TypeFlags[TypeFlags["TypeParameter"] = 524288] = "TypeParameter"; - TypeFlags[TypeFlags["Object"] = 1048576] = "Object"; - TypeFlags[TypeFlags["Index"] = 2097152] = "Index"; - TypeFlags[TypeFlags["TemplateLiteral"] = 4194304] = "TemplateLiteral"; - TypeFlags[TypeFlags["StringMapping"] = 8388608] = "StringMapping"; - TypeFlags[TypeFlags["Substitution"] = 16777216] = "Substitution"; - TypeFlags[TypeFlags["IndexedAccess"] = 33554432] = "IndexedAccess"; - TypeFlags[TypeFlags["Conditional"] = 67108864] = "Conditional"; - TypeFlags[TypeFlags["Union"] = 134217728] = "Union"; - TypeFlags[TypeFlags["Intersection"] = 268435456] = "Intersection"; - TypeFlags[TypeFlags["Reserved1"] = 536870912] = "Reserved1"; - TypeFlags[TypeFlags["Reserved2"] = 1073741824] = "Reserved2"; - TypeFlags[TypeFlags["Reserved3"] = -2147483648] = "Reserved3"; - TypeFlags[TypeFlags["AnyOrUnknown"] = 3] = "AnyOrUnknown"; - TypeFlags[TypeFlags["Nullable"] = 12] = "Nullable"; - TypeFlags[TypeFlags["Literal"] = 15360] = "Literal"; - TypeFlags[TypeFlags["Unit"] = 97292] = "Unit"; - TypeFlags[TypeFlags["Freshable"] = 80896] = "Freshable"; - TypeFlags[TypeFlags["StringOrNumberLiteral"] = 3072] = "StringOrNumberLiteral"; - TypeFlags[TypeFlags["StringOrNumberLiteralOrUnique"] = 19456] = "StringOrNumberLiteralOrUnique"; - TypeFlags[TypeFlags["DefinitelyFalsy"] = 15388] = "DefinitelyFalsy"; - TypeFlags[TypeFlags["PossiblyFalsy"] = 15868] = "PossiblyFalsy"; - TypeFlags[TypeFlags["Intrinsic"] = 393983] = "Intrinsic"; - TypeFlags[TypeFlags["StringLike"] = 12583968] = "StringLike"; - TypeFlags[TypeFlags["NumberLike"] = 67648] = "NumberLike"; - TypeFlags[TypeFlags["BigIntLike"] = 4224] = "BigIntLike"; - TypeFlags[TypeFlags["BooleanLike"] = 8448] = "BooleanLike"; - TypeFlags[TypeFlags["EnumLike"] = 98304] = "EnumLike"; - TypeFlags[TypeFlags["ESSymbolLike"] = 16896] = "ESSymbolLike"; - TypeFlags[TypeFlags["VoidLike"] = 20] = "VoidLike"; - TypeFlags[TypeFlags["Primitive"] = 12713980] = "Primitive"; - TypeFlags[TypeFlags["DefinitelyNonNullable"] = 13893600] = "DefinitelyNonNullable"; - TypeFlags[TypeFlags["DisjointDomains"] = 12812284] = "DisjointDomains"; - TypeFlags[TypeFlags["UnionOrIntersection"] = 402653184] = "UnionOrIntersection"; - TypeFlags[TypeFlags["StructuredType"] = 403701760] = "StructuredType"; - TypeFlags[TypeFlags["TypeVariable"] = 34078720] = "TypeVariable"; - TypeFlags[TypeFlags["InstantiableNonPrimitive"] = 117964800] = "InstantiableNonPrimitive"; - TypeFlags[TypeFlags["InstantiablePrimitive"] = 14680064] = "InstantiablePrimitive"; - TypeFlags[TypeFlags["Instantiable"] = 132644864] = "Instantiable"; - TypeFlags[TypeFlags["StructuredOrInstantiable"] = 536346624] = "StructuredOrInstantiable"; - TypeFlags[TypeFlags["ObjectFlagsType"] = 403963917] = "ObjectFlagsType"; - TypeFlags[TypeFlags["Simplifiable"] = 102760448] = "Simplifiable"; - TypeFlags[TypeFlags["Singleton"] = 394239] = "Singleton"; - TypeFlags[TypeFlags["Narrowable"] = 536575971] = "Narrowable"; - TypeFlags[TypeFlags["IncludesMask"] = 416808959] = "IncludesMask"; - TypeFlags[TypeFlags["IncludesMissingType"] = 524288] = "IncludesMissingType"; - TypeFlags[TypeFlags["IncludesNonWideningType"] = 2097152] = "IncludesNonWideningType"; - TypeFlags[TypeFlags["IncludesWildcard"] = 33554432] = "IncludesWildcard"; - TypeFlags[TypeFlags["IncludesEmptyObject"] = 67108864] = "IncludesEmptyObject"; - TypeFlags[TypeFlags["IncludesInstantiable"] = 16777216] = "IncludesInstantiable"; - TypeFlags[TypeFlags["IncludesConstrainedTypeVariable"] = 536870912] = "IncludesConstrainedTypeVariable"; - TypeFlags[TypeFlags["IncludesError"] = 1073741824] = "IncludesError"; - TypeFlags[TypeFlags["NotPrimitiveUnion"] = 286523411] = "NotPrimitiveUnion"; -})(TypeFlags || (TypeFlags = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. +export var TypeFlags: any; +(function (TypeFlags) { + TypeFlags[TypeFlags["None"] = 0] = "None"; + TypeFlags[TypeFlags["Any"] = 1] = "Any"; + TypeFlags[TypeFlags["Unknown"] = 2] = "Unknown"; + TypeFlags[TypeFlags["Undefined"] = 4] = "Undefined"; + TypeFlags[TypeFlags["Null"] = 8] = "Null"; + TypeFlags[TypeFlags["Void"] = 16] = "Void"; + TypeFlags[TypeFlags["String"] = 32] = "String"; + TypeFlags[TypeFlags["Number"] = 64] = "Number"; + TypeFlags[TypeFlags["BigInt"] = 128] = "BigInt"; + TypeFlags[TypeFlags["Boolean"] = 256] = "Boolean"; + TypeFlags[TypeFlags["ESSymbol"] = 512] = "ESSymbol"; + TypeFlags[TypeFlags["StringLiteral"] = 1024] = "StringLiteral"; + TypeFlags[TypeFlags["NumberLiteral"] = 2048] = "NumberLiteral"; + TypeFlags[TypeFlags["BigIntLiteral"] = 4096] = "BigIntLiteral"; + TypeFlags[TypeFlags["BooleanLiteral"] = 8192] = "BooleanLiteral"; + TypeFlags[TypeFlags["UniqueESSymbol"] = 16384] = "UniqueESSymbol"; + TypeFlags[TypeFlags["EnumLiteral"] = 32768] = "EnumLiteral"; + TypeFlags[TypeFlags["Enum"] = 65536] = "Enum"; + TypeFlags[TypeFlags["NonPrimitive"] = 131072] = "NonPrimitive"; + TypeFlags[TypeFlags["Never"] = 262144] = "Never"; + TypeFlags[TypeFlags["TypeParameter"] = 524288] = "TypeParameter"; + TypeFlags[TypeFlags["Object"] = 1048576] = "Object"; + TypeFlags[TypeFlags["Index"] = 2097152] = "Index"; + TypeFlags[TypeFlags["TemplateLiteral"] = 4194304] = "TemplateLiteral"; + TypeFlags[TypeFlags["StringMapping"] = 8388608] = "StringMapping"; + TypeFlags[TypeFlags["Substitution"] = 16777216] = "Substitution"; + TypeFlags[TypeFlags["IndexedAccess"] = 33554432] = "IndexedAccess"; + TypeFlags[TypeFlags["Conditional"] = 67108864] = "Conditional"; + TypeFlags[TypeFlags["Union"] = 134217728] = "Union"; + TypeFlags[TypeFlags["Intersection"] = 268435456] = "Intersection"; + TypeFlags[TypeFlags["Reserved1"] = 536870912] = "Reserved1"; + TypeFlags[TypeFlags["Reserved2"] = 1073741824] = "Reserved2"; + TypeFlags[TypeFlags["Reserved3"] = -2147483648] = "Reserved3"; + TypeFlags[TypeFlags["AnyOrUnknown"] = 3] = "AnyOrUnknown"; + TypeFlags[TypeFlags["Nullable"] = 12] = "Nullable"; + TypeFlags[TypeFlags["Literal"] = 15360] = "Literal"; + TypeFlags[TypeFlags["Unit"] = 97292] = "Unit"; + TypeFlags[TypeFlags["Freshable"] = 80896] = "Freshable"; + TypeFlags[TypeFlags["StringOrNumberLiteral"] = 3072] = "StringOrNumberLiteral"; + TypeFlags[TypeFlags["StringOrNumberLiteralOrUnique"] = 19456] = "StringOrNumberLiteralOrUnique"; + TypeFlags[TypeFlags["DefinitelyFalsy"] = 15388] = "DefinitelyFalsy"; + TypeFlags[TypeFlags["PossiblyFalsy"] = 15868] = "PossiblyFalsy"; + TypeFlags[TypeFlags["Intrinsic"] = 393983] = "Intrinsic"; + TypeFlags[TypeFlags["StringLike"] = 12583968] = "StringLike"; + TypeFlags[TypeFlags["NumberLike"] = 67648] = "NumberLike"; + TypeFlags[TypeFlags["BigIntLike"] = 4224] = "BigIntLike"; + TypeFlags[TypeFlags["BooleanLike"] = 8448] = "BooleanLike"; + TypeFlags[TypeFlags["EnumLike"] = 98304] = "EnumLike"; + TypeFlags[TypeFlags["ESSymbolLike"] = 16896] = "ESSymbolLike"; + TypeFlags[TypeFlags["VoidLike"] = 20] = "VoidLike"; + TypeFlags[TypeFlags["Primitive"] = 12713980] = "Primitive"; + TypeFlags[TypeFlags["DefinitelyNonNullable"] = 13893600] = "DefinitelyNonNullable"; + TypeFlags[TypeFlags["DisjointDomains"] = 12812284] = "DisjointDomains"; + TypeFlags[TypeFlags["UnionOrIntersection"] = 402653184] = "UnionOrIntersection"; + TypeFlags[TypeFlags["StructuredType"] = 403701760] = "StructuredType"; + TypeFlags[TypeFlags["TypeVariable"] = 34078720] = "TypeVariable"; + TypeFlags[TypeFlags["InstantiableNonPrimitive"] = 117964800] = "InstantiableNonPrimitive"; + TypeFlags[TypeFlags["InstantiablePrimitive"] = 14680064] = "InstantiablePrimitive"; + TypeFlags[TypeFlags["Instantiable"] = 132644864] = "Instantiable"; + TypeFlags[TypeFlags["StructuredOrInstantiable"] = 536346624] = "StructuredOrInstantiable"; + TypeFlags[TypeFlags["ObjectFlagsType"] = 403963917] = "ObjectFlagsType"; + TypeFlags[TypeFlags["Simplifiable"] = 102760448] = "Simplifiable"; + TypeFlags[TypeFlags["Singleton"] = 394239] = "Singleton"; + TypeFlags[TypeFlags["Narrowable"] = 536575971] = "Narrowable"; + TypeFlags[TypeFlags["IncludesMask"] = 416808959] = "IncludesMask"; + TypeFlags[TypeFlags["IncludesMissingType"] = 524288] = "IncludesMissingType"; + TypeFlags[TypeFlags["IncludesNonWideningType"] = 2097152] = "IncludesNonWideningType"; + TypeFlags[TypeFlags["IncludesWildcard"] = 33554432] = "IncludesWildcard"; + TypeFlags[TypeFlags["IncludesEmptyObject"] = 67108864] = "IncludesEmptyObject"; + TypeFlags[TypeFlags["IncludesInstantiable"] = 16777216] = "IncludesInstantiable"; + TypeFlags[TypeFlags["IncludesConstrainedTypeVariable"] = 536870912] = "IncludesConstrainedTypeVariable"; + TypeFlags[TypeFlags["IncludesError"] = 1073741824] = "IncludesError"; + TypeFlags[TypeFlags["NotPrimitiveUnion"] = 286523411] = "NotPrimitiveUnion"; +})(TypeFlags || (TypeFlags = {})); diff --git a/packages/typescript/src/enums/typeFormatFlags.enum.ts b/packages/typescript/src/enums/typeFormatFlags.enum.ts index dbf5ce42a1c0a..2e2cb439a90bb 100644 --- a/packages/typescript/src/enums/typeFormatFlags.enum.ts +++ b/packages/typescript/src/enums/typeFormatFlags.enum.ts @@ -1,30 +1,30 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. - -export enum TypeFormatFlags { - None = 0, - NoTruncation = 1 << 0, - WriteArrayAsGenericType = 1 << 1, - GenerateNamesForShadowedTypeParams = 1 << 2, - UseStructuralFallback = 1 << 3, - WriteTypeArgumentsOfSignature = 1 << 5, - UseFullyQualifiedType = 1 << 6, - SuppressAnyReturnType = 1 << 8, - MultilineObjectLiterals = 1 << 10, - WriteClassExpressionAsTypeLiteral = 1 << 11, - UseTypeOfFunction = 1 << 12, - OmitParameterModifiers = 1 << 13, - UseAliasDefinedOutsideCurrentScope = 1 << 14, - UseSingleQuotesForStringLiteralType = 1 << 28, - NoTypeReduction = 1 << 29, - UseInstantiationExpressions = 1 << 30, - OmitThisParameter = 1 << 25, - WriteCallStyleSignature = 1 << 27, - AllowUniqueESSymbolType = 1 << 20, - AddUndefined = 1 << 17, - WriteArrowStyleSignature = 1 << 18, - InArrayType = 1 << 19, - InElementType = 1 << 21, - InFirstTypeArgument = 1 << 22, - InTypeAlias = 1 << 23, - NodeBuilderFlagsMask = NoTruncation | WriteArrayAsGenericType | GenerateNamesForShadowedTypeParams | UseStructuralFallback | WriteTypeArgumentsOfSignature | UseFullyQualifiedType | SuppressAnyReturnType | MultilineObjectLiterals | WriteClassExpressionAsTypeLiteral | UseTypeOfFunction | OmitParameterModifiers | UseAliasDefinedOutsideCurrentScope | AllowUniqueESSymbolType | InTypeAlias | UseInstantiationExpressions | UseSingleQuotesForStringLiteralType | NoTypeReduction | OmitThisParameter, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. + +export enum TypeFormatFlags { + None = 0, + NoTruncation = 1 << 0, + WriteArrayAsGenericType = 1 << 1, + GenerateNamesForShadowedTypeParams = 1 << 2, + UseStructuralFallback = 1 << 3, + WriteTypeArgumentsOfSignature = 1 << 5, + UseFullyQualifiedType = 1 << 6, + SuppressAnyReturnType = 1 << 8, + MultilineObjectLiterals = 1 << 10, + WriteClassExpressionAsTypeLiteral = 1 << 11, + UseTypeOfFunction = 1 << 12, + OmitParameterModifiers = 1 << 13, + UseAliasDefinedOutsideCurrentScope = 1 << 14, + UseSingleQuotesForStringLiteralType = 1 << 28, + NoTypeReduction = 1 << 29, + UseInstantiationExpressions = 1 << 30, + OmitThisParameter = 1 << 25, + WriteCallStyleSignature = 1 << 27, + AllowUniqueESSymbolType = 1 << 20, + AddUndefined = 1 << 17, + WriteArrowStyleSignature = 1 << 18, + InArrayType = 1 << 19, + InElementType = 1 << 21, + InFirstTypeArgument = 1 << 22, + InTypeAlias = 1 << 23, + NodeBuilderFlagsMask = NoTruncation | WriteArrayAsGenericType | GenerateNamesForShadowedTypeParams | UseStructuralFallback | WriteTypeArgumentsOfSignature | UseFullyQualifiedType | SuppressAnyReturnType | MultilineObjectLiterals | WriteClassExpressionAsTypeLiteral | UseTypeOfFunction | OmitParameterModifiers | UseAliasDefinedOutsideCurrentScope | AllowUniqueESSymbolType | InTypeAlias | UseInstantiationExpressions | UseSingleQuotesForStringLiteralType | NoTypeReduction | OmitThisParameter, +} diff --git a/packages/typescript/src/enums/typeFormatFlags.ts b/packages/typescript/src/enums/typeFormatFlags.ts index 406656d44b3c9..65cd1024d88ff 100644 --- a/packages/typescript/src/enums/typeFormatFlags.ts +++ b/packages/typescript/src/enums/typeFormatFlags.ts @@ -1,30 +1,30 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. -export var TypeFormatFlags: any; -(function (TypeFormatFlags) { - TypeFormatFlags[TypeFormatFlags["None"] = 0] = "None"; - TypeFormatFlags[TypeFormatFlags["NoTruncation"] = 1] = "NoTruncation"; - TypeFormatFlags[TypeFormatFlags["WriteArrayAsGenericType"] = 2] = "WriteArrayAsGenericType"; - TypeFormatFlags[TypeFormatFlags["GenerateNamesForShadowedTypeParams"] = 4] = "GenerateNamesForShadowedTypeParams"; - TypeFormatFlags[TypeFormatFlags["UseStructuralFallback"] = 8] = "UseStructuralFallback"; - TypeFormatFlags[TypeFormatFlags["WriteTypeArgumentsOfSignature"] = 32] = "WriteTypeArgumentsOfSignature"; - TypeFormatFlags[TypeFormatFlags["UseFullyQualifiedType"] = 64] = "UseFullyQualifiedType"; - TypeFormatFlags[TypeFormatFlags["SuppressAnyReturnType"] = 256] = "SuppressAnyReturnType"; - TypeFormatFlags[TypeFormatFlags["MultilineObjectLiterals"] = 1024] = "MultilineObjectLiterals"; - TypeFormatFlags[TypeFormatFlags["WriteClassExpressionAsTypeLiteral"] = 2048] = "WriteClassExpressionAsTypeLiteral"; - TypeFormatFlags[TypeFormatFlags["UseTypeOfFunction"] = 4096] = "UseTypeOfFunction"; - TypeFormatFlags[TypeFormatFlags["OmitParameterModifiers"] = 8192] = "OmitParameterModifiers"; - TypeFormatFlags[TypeFormatFlags["UseAliasDefinedOutsideCurrentScope"] = 16384] = "UseAliasDefinedOutsideCurrentScope"; - TypeFormatFlags[TypeFormatFlags["UseSingleQuotesForStringLiteralType"] = 268435456] = "UseSingleQuotesForStringLiteralType"; - TypeFormatFlags[TypeFormatFlags["NoTypeReduction"] = 536870912] = "NoTypeReduction"; - TypeFormatFlags[TypeFormatFlags["UseInstantiationExpressions"] = 1073741824] = "UseInstantiationExpressions"; - TypeFormatFlags[TypeFormatFlags["OmitThisParameter"] = 33554432] = "OmitThisParameter"; - TypeFormatFlags[TypeFormatFlags["WriteCallStyleSignature"] = 134217728] = "WriteCallStyleSignature"; - TypeFormatFlags[TypeFormatFlags["AllowUniqueESSymbolType"] = 1048576] = "AllowUniqueESSymbolType"; - TypeFormatFlags[TypeFormatFlags["AddUndefined"] = 131072] = "AddUndefined"; - TypeFormatFlags[TypeFormatFlags["WriteArrowStyleSignature"] = 262144] = "WriteArrowStyleSignature"; - TypeFormatFlags[TypeFormatFlags["InArrayType"] = 524288] = "InArrayType"; - TypeFormatFlags[TypeFormatFlags["InElementType"] = 2097152] = "InElementType"; - TypeFormatFlags[TypeFormatFlags["InFirstTypeArgument"] = 4194304] = "InFirstTypeArgument"; - TypeFormatFlags[TypeFormatFlags["InTypeAlias"] = 8388608] = "InTypeAlias"; - TypeFormatFlags[TypeFormatFlags["NodeBuilderFlagsMask"] = 1922071919] = "NodeBuilderFlagsMask"; -})(TypeFormatFlags || (TypeFormatFlags = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. +export var TypeFormatFlags: any; +(function (TypeFormatFlags) { + TypeFormatFlags[TypeFormatFlags["None"] = 0] = "None"; + TypeFormatFlags[TypeFormatFlags["NoTruncation"] = 1] = "NoTruncation"; + TypeFormatFlags[TypeFormatFlags["WriteArrayAsGenericType"] = 2] = "WriteArrayAsGenericType"; + TypeFormatFlags[TypeFormatFlags["GenerateNamesForShadowedTypeParams"] = 4] = "GenerateNamesForShadowedTypeParams"; + TypeFormatFlags[TypeFormatFlags["UseStructuralFallback"] = 8] = "UseStructuralFallback"; + TypeFormatFlags[TypeFormatFlags["WriteTypeArgumentsOfSignature"] = 32] = "WriteTypeArgumentsOfSignature"; + TypeFormatFlags[TypeFormatFlags["UseFullyQualifiedType"] = 64] = "UseFullyQualifiedType"; + TypeFormatFlags[TypeFormatFlags["SuppressAnyReturnType"] = 256] = "SuppressAnyReturnType"; + TypeFormatFlags[TypeFormatFlags["MultilineObjectLiterals"] = 1024] = "MultilineObjectLiterals"; + TypeFormatFlags[TypeFormatFlags["WriteClassExpressionAsTypeLiteral"] = 2048] = "WriteClassExpressionAsTypeLiteral"; + TypeFormatFlags[TypeFormatFlags["UseTypeOfFunction"] = 4096] = "UseTypeOfFunction"; + TypeFormatFlags[TypeFormatFlags["OmitParameterModifiers"] = 8192] = "OmitParameterModifiers"; + TypeFormatFlags[TypeFormatFlags["UseAliasDefinedOutsideCurrentScope"] = 16384] = "UseAliasDefinedOutsideCurrentScope"; + TypeFormatFlags[TypeFormatFlags["UseSingleQuotesForStringLiteralType"] = 268435456] = "UseSingleQuotesForStringLiteralType"; + TypeFormatFlags[TypeFormatFlags["NoTypeReduction"] = 536870912] = "NoTypeReduction"; + TypeFormatFlags[TypeFormatFlags["UseInstantiationExpressions"] = 1073741824] = "UseInstantiationExpressions"; + TypeFormatFlags[TypeFormatFlags["OmitThisParameter"] = 33554432] = "OmitThisParameter"; + TypeFormatFlags[TypeFormatFlags["WriteCallStyleSignature"] = 134217728] = "WriteCallStyleSignature"; + TypeFormatFlags[TypeFormatFlags["AllowUniqueESSymbolType"] = 1048576] = "AllowUniqueESSymbolType"; + TypeFormatFlags[TypeFormatFlags["AddUndefined"] = 131072] = "AddUndefined"; + TypeFormatFlags[TypeFormatFlags["WriteArrowStyleSignature"] = 262144] = "WriteArrowStyleSignature"; + TypeFormatFlags[TypeFormatFlags["InArrayType"] = 524288] = "InArrayType"; + TypeFormatFlags[TypeFormatFlags["InElementType"] = 2097152] = "InElementType"; + TypeFormatFlags[TypeFormatFlags["InFirstTypeArgument"] = 4194304] = "InFirstTypeArgument"; + TypeFormatFlags[TypeFormatFlags["InTypeAlias"] = 8388608] = "InTypeAlias"; + TypeFormatFlags[TypeFormatFlags["NodeBuilderFlagsMask"] = 1922071919] = "NodeBuilderFlagsMask"; +})(TypeFormatFlags || (TypeFormatFlags = {})); diff --git a/packages/typescript/src/enums/typePredicateKind.enum.ts b/packages/typescript/src/enums/typePredicateKind.enum.ts index e79af96776168..798305f11a41b 100644 --- a/packages/typescript/src/enums/typePredicateKind.enum.ts +++ b/packages/typescript/src/enums/typePredicateKind.enum.ts @@ -1,8 +1,8 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. - -export enum TypePredicateKind { - This = 0, - Identifier = 1, - AssertsThis = 2, - AssertsIdentifier = 3, -} +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. + +export enum TypePredicateKind { + This = 0, + Identifier = 1, + AssertsThis = 2, + AssertsIdentifier = 3, +} diff --git a/packages/typescript/src/enums/typePredicateKind.ts b/packages/typescript/src/enums/typePredicateKind.ts index 5e0a61ff9c1e2..6fb805d9cb58c 100644 --- a/packages/typescript/src/enums/typePredicateKind.ts +++ b/packages/typescript/src/enums/typePredicateKind.ts @@ -1,8 +1,8 @@ -// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. -export var TypePredicateKind: any; -(function (TypePredicateKind) { - TypePredicateKind[TypePredicateKind["This"] = 0] = "This"; - TypePredicateKind[TypePredicateKind["Identifier"] = 1] = "Identifier"; - TypePredicateKind[TypePredicateKind["AssertsThis"] = 2] = "AssertsThis"; - TypePredicateKind[TypePredicateKind["AssertsIdentifier"] = 3] = "AssertsIdentifier"; -})(TypePredicateKind || (TypePredicateKind = {})); +// Code generated by Herebyfile.mjs generate:enums from tsc/internal/checker/types.go. DO NOT EDIT. +export var TypePredicateKind: any; +(function (TypePredicateKind) { + TypePredicateKind[TypePredicateKind["This"] = 0] = "This"; + TypePredicateKind[TypePredicateKind["Identifier"] = 1] = "Identifier"; + TypePredicateKind[TypePredicateKind["AssertsThis"] = 2] = "AssertsThis"; + TypePredicateKind[TypePredicateKind["AssertsIdentifier"] = 3] = "AssertsIdentifier"; +})(TypePredicateKind || (TypePredicateKind = {})); diff --git a/packages/typescript/src/internal/utils.ts b/packages/typescript/src/internal/utils.ts index 84b5bb7e94f97..f04a0462035d2 100644 --- a/packages/typescript/src/internal/utils.ts +++ b/packages/typescript/src/internal/utils.ts @@ -1,44 +1,44 @@ -import type { Node } from "../ast/ast.ts"; -import { formatSyntaxKind } from "../ast/utils.ts"; - -const hasOwnProperty = Object.prototype.hasOwnProperty; - -/** - * Safer version of `Function` which should not be called. - * Every function should be assignable to this, but this should not be assignable to every function. - */ -export type AnyFunction = (...args: never[]) => void; - -/** - * Type of objects whose values are all of the same type. - * The `in` and `for-in` operators can *not* be safely used, - * since `Object.prototype` may be modified by outside code. - */ -export interface MapLike { - [index: string]: T; -} - -/** - * Indicates whether a map-like contains an own property with the specified key. - * - * @param map A map-like. - * @param key A property key. - */ -export function hasProperty(map: MapLike, key: string): boolean { - return hasOwnProperty.call(map, key); -} - -export function assertNever(member: never, message = "Illegal value:", stackCrawlMark?: AnyFunction): never { - const detail = typeof member === "object" && hasProperty(member, "kind") && hasProperty(member, "pos") ? "SyntaxKind: " + formatSyntaxKind((member as Node).kind) : JSON.stringify(member); - return fail(`${message} ${detail}`, stackCrawlMark || assertNever); -} - -export function fail(message?: string, stackCrawlMark?: AnyFunction): never { - // eslint-disable-next-line no-debugger - debugger; - const e = new Error(message ? `Debug Failure. ${message}` : "Debug Failure."); - if ((Error as any).captureStackTrace) { - (Error as any).captureStackTrace(e, stackCrawlMark || fail); - } - throw e; -} +import type { Node } from "../ast/ast.ts"; +import { formatSyntaxKind } from "../ast/utils.ts"; + +const hasOwnProperty = Object.prototype.hasOwnProperty; + +/** + * Safer version of `Function` which should not be called. + * Every function should be assignable to this, but this should not be assignable to every function. + */ +export type AnyFunction = (...args: never[]) => void; + +/** + * Type of objects whose values are all of the same type. + * The `in` and `for-in` operators can *not* be safely used, + * since `Object.prototype` may be modified by outside code. + */ +export interface MapLike { + [index: string]: T; +} + +/** + * Indicates whether a map-like contains an own property with the specified key. + * + * @param map A map-like. + * @param key A property key. + */ +export function hasProperty(map: MapLike, key: string): boolean { + return hasOwnProperty.call(map, key); +} + +export function assertNever(member: never, message = "Illegal value:", stackCrawlMark?: AnyFunction): never { + const detail = typeof member === "object" && hasProperty(member, "kind") && hasProperty(member, "pos") ? "SyntaxKind: " + formatSyntaxKind((member as Node).kind) : JSON.stringify(member); + return fail(`${message} ${detail}`, stackCrawlMark || assertNever); +} + +export function fail(message?: string, stackCrawlMark?: AnyFunction): never { + // eslint-disable-next-line no-debugger + debugger; + const e = new Error(message ? `Debug Failure. ${message}` : "Debug Failure."); + if ((Error as any).captureStackTrace) { + (Error as any).captureStackTrace(e, stackCrawlMark || fail); + } + throw e; +} diff --git a/packages/typescript/test/api.bench.ts b/packages/typescript/test/api.bench.ts index 53a159b54b02c..2cce5c02bfe3c 100644 --- a/packages/typescript/test/api.bench.ts +++ b/packages/typescript/test/api.bench.ts @@ -1,9 +1,9 @@ -import { fileURLToPath } from "node:url"; -import { runBenchmarks as runAsyncBenchmarks } from "./async/api.bench.ts"; -import { runBenchmarks as runSyncBenchmarks } from "./sync/api.bench.ts"; - -const isMain = process.argv[1] === fileURLToPath(import.meta.url); -if (isMain) { - await runAsyncBenchmarks(); - runSyncBenchmarks(); -} +import { fileURLToPath } from "node:url"; +import { runBenchmarks as runAsyncBenchmarks } from "./async/api.bench.ts"; +import { runBenchmarks as runSyncBenchmarks } from "./sync/api.bench.ts"; + +const isMain = process.argv[1] === fileURLToPath(import.meta.url); +if (isMain) { + await runAsyncBenchmarks(); + runSyncBenchmarks(); +} diff --git a/packages/typescript/test/async/api.bench.ts b/packages/typescript/test/async/api.bench.ts index f77d037714297..67404adbcd43e 100644 --- a/packages/typescript/test/async/api.bench.ts +++ b/packages/typescript/test/async/api.bench.ts @@ -1,261 +1,261 @@ -import { - type Node, - type SourceFile, - SyntaxKind, -} from "@typescript/typescript/unstable/ast"; -import { - API, - type Project, - type Snapshot, -} from "@typescript/typescript/unstable/async"; // @sync: } from "@typescript/typescript/unstable/sync"; -import { writeFileSync } from "node:fs"; -import inspector from "node:inspector"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; -import { parseArgs } from "node:util"; -import { Bench } from "tinybench"; -import ts from "typescript"; -import { RemoteSourceFile } from "../../src/api/node/node.ts"; - -const isMain = process.argv[1] === fileURLToPath(import.meta.url); -if (isMain) { - const { values } = parseArgs({ - options: { - filter: { type: "string" }, - singleIteration: { type: "boolean", default: false }, - cpuprofile: { type: "boolean", default: false }, - }, - }); - await runBenchmarks(values); -} - -export async function runBenchmarks(options?: { filter?: string; singleIteration?: boolean; cpuprofile?: boolean; }) { - const { filter, singleIteration, cpuprofile } = options ?? {}; - const repoRoot = fileURLToPath(new URL("../../../../", import.meta.url).toString()); - - const bench = new Bench({ - name: "Async API", // @sync: name: "Sync API", - teardown, - // Reduce iterations from the default 64 to 10. Slow tasks - // are dominated by the iteration minimum, not the time limit. - // 10 iterations still gives stable medians while cutting total - // bench time by ~5x. - iterations: 10, - warmupIterations: 4, - ...singleIteration ? { - iterations: 1, - warmup: false, - time: 0, - } : undefined, - }); - - let api: API; - let snapshot: Snapshot; - let project: Project; - let tsProgram: ts.Program; - let file: SourceFile; - let tsFile: ts.SourceFile; - - const programIdentifierCount = await (async () => { - await spawnAPI(); - await loadSnapshot(); - await getProgramTS(); - let count = 0; - file!.forEachChild(function visit(node) { - if (node.kind === SyntaxKind.Identifier) { - count++; - } - node.forEachChild(visit); - }); - await teardown(); - return count; - })(); - - // Tinybench's `isFnAsyncResource` probes each task function by *calling* - // it once during `.add()` to detect whether it returns a Promise. - // In sync mode every task function is a plain (non-async) function, so - // the probe actually executes the benchmarked code (spawning processes, - // creating TS programs, etc.) wasting 30+ seconds. Passing an explicit - // `async` flag on every task skips the probe entirely. - const isAsync = true; // @sync: const isAsync = false; - - bench - .add("spawn API", async () => { - await spawnAPI(); - }, { async: isAsync }) - .add("load snapshot", async () => { - await loadSnapshot(); - }, { async: isAsync, beforeAll: spawnAPI }) - .add("TS - load project", () => { - tsCreateProgram(); - }, { async: isAsync }) - .add("transfer debug.ts", async () => { - await getDebugTS(); - }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot), beforeEach: clearSourceFileCache }) - .add("transfer program.ts", async () => { - await getProgramTS(); - }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot), beforeEach: clearSourceFileCache }) - .add("transfer checker.ts", async () => { - await getCheckerTS(); - }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot), beforeEach: clearSourceFileCache }) - .add("materialize program.ts", async () => { - const { view, _decoder } = file as unknown as RemoteSourceFile; - new RemoteSourceFile(new Uint8Array(view.buffer, view.byteOffset, view.byteLength), _decoder).forEachChild(function visit(node) { - node.forEachChild(visit); - }); - }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, getProgramTS) }) - .add("materialize checker.ts", async () => { - const { view, _decoder } = file as unknown as RemoteSourceFile; - new RemoteSourceFile(new Uint8Array(view.buffer, view.byteOffset, view.byteLength), _decoder).forEachChild(function visit(node) { - node.forEachChild(visit); - }); - }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, getCheckerTS) }) - .add("getSymbolAtPosition - one location", async () => { - await project.checker.getSymbolAtPosition("program.ts", 8895); - }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, createChecker) }) - .add("TS - getSymbolAtPosition - one location", () => { - tsProgram.getTypeChecker().getSymbolAtLocation( - // @ts-ignore internal API - ts.getTokenAtPosition(tsFile, 8895), - ); - }, { async: isAsync, beforeAll: all(tsCreateProgram, tsCreateChecker, tsGetProgramTS) }) - .add(`getSymbolAtPosition - ${programIdentifierCount} identifiers`, async () => { - for (const node of collectIdentifiers(file)) { - await project.checker.getSymbolAtPosition("program.ts", node.pos); - } - }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, createChecker, getProgramTS) }) - .add(`getSymbolAtPosition - ${programIdentifierCount} identifiers (batched)`, async () => { - const positions = collectIdentifiers(file).map(node => node.pos); - await project.checker.getSymbolAtPosition("program.ts", positions); - }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, createChecker, getProgramTS) }) - .add(`getSymbolAtLocation - ${programIdentifierCount} identifiers`, async () => { - for (const node of collectIdentifiers(file)) { - await project.checker.getSymbolAtLocation(node); - } - }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, createChecker, getProgramTS) }) - .add(`getSymbolAtLocation - ${programIdentifierCount} identifiers (batched)`, async () => { - const nodes = collectIdentifiers(file); - await project.checker.getSymbolAtLocation(nodes); - }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, createChecker, getProgramTS) }) - .add(`TS - getSymbolAtLocation - ${programIdentifierCount} identifiers`, () => { - const checker = tsProgram.getTypeChecker(); - tsFile.forEachChild(function visit(node) { - if (node.kind === ts.SyntaxKind.Identifier) { - checker.getSymbolAtLocation(node); - } - node.forEachChild(visit); - }); - }, { async: isAsync, beforeAll: all(tsCreateProgram, tsCreateChecker, tsGetProgramTS) }); - - if (filter) { - const pattern = filter.toLowerCase(); - for (const task of [...bench.tasks]) { - if (!task.name.toLowerCase().includes(pattern)) { - bench.remove(task.name); - } - } - } - - let session: inspector.Session | undefined; - if (cpuprofile) { - session = new inspector.Session(); - session.connect(); - session.post("Profiler.enable"); - session.post("Profiler.start"); - } - - await bench.run(); // @sync: bench.runSync(); - - if (session) { - session.post("Profiler.stop", (err, { profile }) => { - if (err) throw err; - const outPath = `bench-${Date.now()}.cpuprofile`; - writeFileSync(outPath, JSON.stringify(profile)); - console.log(`CPU profile written to ${outPath}`); - }); - session.disconnect(); - } - console.table(bench.table()); - - function collectIdentifiers(sourceFile: SourceFile): Node[] { - const nodes: Node[] = []; - sourceFile.forEachChild(function visit(node) { - if (node.kind === SyntaxKind.Identifier) { - nodes.push(node); - } - node.forEachChild(visit); - }); - return nodes; - } - - async function spawnAPI() { - api = new API({ - cwd: repoRoot, - }); - } - - async function loadSnapshot() { - snapshot = await api.updateSnapshot({ openProject: "tsc/testdata/fixtures/compiler/tsconfig.json" }); - project = snapshot.getProjects()[0]; - } - - function tsCreateProgram() { - const configFileName = fileURLToPath(new URL("../../../../tsc/testdata/fixtures/compiler/tsconfig.json", import.meta.url).toString()); - const configFile = ts.readConfigFile(configFileName, ts.sys.readFile); - const parsedCommandLine = ts.parseJsonConfigFileContent(configFile.config, ts.sys, path.dirname(configFileName)); - const host = ts.createCompilerHost(parsedCommandLine.options); - tsProgram = ts.createProgram({ - rootNames: parsedCommandLine.fileNames, - options: parsedCommandLine.options, - host, - }); - } - - async function createChecker() { - // checker is created lazily, for measuring symbol time in a loop - // we need to create it first. - await project.checker.getSymbolAtPosition("core.ts", 0); - } - - function tsCreateChecker() { - tsProgram.getTypeChecker(); - } - - async function getDebugTS() { - file = (await project.program.getSourceFile("debug.ts"))!; - } - - async function getProgramTS() { - file = (await project.program.getSourceFile("program.ts"))!; - } - - function tsGetProgramTS() { - tsFile = tsProgram.getSourceFile(fileURLToPath(new URL("../../../../tsc/testdata/fixtures/compiler/program.ts", import.meta.url).toString()))!; - } - - async function getCheckerTS() { - file = (await project.program.getSourceFile("checker.ts"))!; - } - - function clearSourceFileCache() { - api.clearSourceFileCache(); - } - - async function teardown() { - await api?.close(); - api = undefined!; - snapshot = undefined!; - project = undefined!; - file = undefined!; - tsProgram = undefined!; - tsFile = undefined!; - } - - function all(...fns: (() => void | Promise)[]) { - return async () => { - for (const fn of fns) { - await fn(); - } - }; - } -} +import { + type Node, + type SourceFile, + SyntaxKind, +} from "@typescript/typescript/unstable/ast"; +import { + API, + type Project, + type Snapshot, +} from "@typescript/typescript/unstable/async"; // @sync: } from "@typescript/typescript/unstable/sync"; +import { writeFileSync } from "node:fs"; +import inspector from "node:inspector"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { parseArgs } from "node:util"; +import { Bench } from "tinybench"; +import ts from "typescript"; +import { RemoteSourceFile } from "../../src/api/node/node.ts"; + +const isMain = process.argv[1] === fileURLToPath(import.meta.url); +if (isMain) { + const { values } = parseArgs({ + options: { + filter: { type: "string" }, + singleIteration: { type: "boolean", default: false }, + cpuprofile: { type: "boolean", default: false }, + }, + }); + await runBenchmarks(values); +} + +export async function runBenchmarks(options?: { filter?: string; singleIteration?: boolean; cpuprofile?: boolean; }) { + const { filter, singleIteration, cpuprofile } = options ?? {}; + const repoRoot = fileURLToPath(new URL("../../../../", import.meta.url).toString()); + + const bench = new Bench({ + name: "Async API", // @sync: name: "Sync API", + teardown, + // Reduce iterations from the default 64 to 10. Slow tasks + // are dominated by the iteration minimum, not the time limit. + // 10 iterations still gives stable medians while cutting total + // bench time by ~5x. + iterations: 10, + warmupIterations: 4, + ...singleIteration ? { + iterations: 1, + warmup: false, + time: 0, + } : undefined, + }); + + let api: API; + let snapshot: Snapshot; + let project: Project; + let tsProgram: ts.Program; + let file: SourceFile; + let tsFile: ts.SourceFile; + + const programIdentifierCount = await (async () => { + await spawnAPI(); + await loadSnapshot(); + await getProgramTS(); + let count = 0; + file!.forEachChild(function visit(node) { + if (node.kind === SyntaxKind.Identifier) { + count++; + } + node.forEachChild(visit); + }); + await teardown(); + return count; + })(); + + // Tinybench's `isFnAsyncResource` probes each task function by *calling* + // it once during `.add()` to detect whether it returns a Promise. + // In sync mode every task function is a plain (non-async) function, so + // the probe actually executes the benchmarked code (spawning processes, + // creating TS programs, etc.) wasting 30+ seconds. Passing an explicit + // `async` flag on every task skips the probe entirely. + const isAsync = true; // @sync: const isAsync = false; + + bench + .add("spawn API", async () => { + await spawnAPI(); + }, { async: isAsync }) + .add("load snapshot", async () => { + await loadSnapshot(); + }, { async: isAsync, beforeAll: spawnAPI }) + .add("TS - load project", () => { + tsCreateProgram(); + }, { async: isAsync }) + .add("transfer debug.ts", async () => { + await getDebugTS(); + }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot), beforeEach: clearSourceFileCache }) + .add("transfer program.ts", async () => { + await getProgramTS(); + }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot), beforeEach: clearSourceFileCache }) + .add("transfer checker.ts", async () => { + await getCheckerTS(); + }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot), beforeEach: clearSourceFileCache }) + .add("materialize program.ts", async () => { + const { view, _decoder } = file as unknown as RemoteSourceFile; + new RemoteSourceFile(new Uint8Array(view.buffer, view.byteOffset, view.byteLength), _decoder).forEachChild(function visit(node) { + node.forEachChild(visit); + }); + }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, getProgramTS) }) + .add("materialize checker.ts", async () => { + const { view, _decoder } = file as unknown as RemoteSourceFile; + new RemoteSourceFile(new Uint8Array(view.buffer, view.byteOffset, view.byteLength), _decoder).forEachChild(function visit(node) { + node.forEachChild(visit); + }); + }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, getCheckerTS) }) + .add("getSymbolAtPosition - one location", async () => { + await project.checker.getSymbolAtPosition("program.ts", 8895); + }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, createChecker) }) + .add("TS - getSymbolAtPosition - one location", () => { + tsProgram.getTypeChecker().getSymbolAtLocation( + // @ts-ignore internal API + ts.getTokenAtPosition(tsFile, 8895), + ); + }, { async: isAsync, beforeAll: all(tsCreateProgram, tsCreateChecker, tsGetProgramTS) }) + .add(`getSymbolAtPosition - ${programIdentifierCount} identifiers`, async () => { + for (const node of collectIdentifiers(file)) { + await project.checker.getSymbolAtPosition("program.ts", node.pos); + } + }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, createChecker, getProgramTS) }) + .add(`getSymbolAtPosition - ${programIdentifierCount} identifiers (batched)`, async () => { + const positions = collectIdentifiers(file).map(node => node.pos); + await project.checker.getSymbolAtPosition("program.ts", positions); + }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, createChecker, getProgramTS) }) + .add(`getSymbolAtLocation - ${programIdentifierCount} identifiers`, async () => { + for (const node of collectIdentifiers(file)) { + await project.checker.getSymbolAtLocation(node); + } + }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, createChecker, getProgramTS) }) + .add(`getSymbolAtLocation - ${programIdentifierCount} identifiers (batched)`, async () => { + const nodes = collectIdentifiers(file); + await project.checker.getSymbolAtLocation(nodes); + }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, createChecker, getProgramTS) }) + .add(`TS - getSymbolAtLocation - ${programIdentifierCount} identifiers`, () => { + const checker = tsProgram.getTypeChecker(); + tsFile.forEachChild(function visit(node) { + if (node.kind === ts.SyntaxKind.Identifier) { + checker.getSymbolAtLocation(node); + } + node.forEachChild(visit); + }); + }, { async: isAsync, beforeAll: all(tsCreateProgram, tsCreateChecker, tsGetProgramTS) }); + + if (filter) { + const pattern = filter.toLowerCase(); + for (const task of [...bench.tasks]) { + if (!task.name.toLowerCase().includes(pattern)) { + bench.remove(task.name); + } + } + } + + let session: inspector.Session | undefined; + if (cpuprofile) { + session = new inspector.Session(); + session.connect(); + session.post("Profiler.enable"); + session.post("Profiler.start"); + } + + await bench.run(); // @sync: bench.runSync(); + + if (session) { + session.post("Profiler.stop", (err, { profile }) => { + if (err) throw err; + const outPath = `bench-${Date.now()}.cpuprofile`; + writeFileSync(outPath, JSON.stringify(profile)); + console.log(`CPU profile written to ${outPath}`); + }); + session.disconnect(); + } + console.table(bench.table()); + + function collectIdentifiers(sourceFile: SourceFile): Node[] { + const nodes: Node[] = []; + sourceFile.forEachChild(function visit(node) { + if (node.kind === SyntaxKind.Identifier) { + nodes.push(node); + } + node.forEachChild(visit); + }); + return nodes; + } + + async function spawnAPI() { + api = new API({ + cwd: repoRoot, + }); + } + + async function loadSnapshot() { + snapshot = await api.updateSnapshot({ openProject: "tsc/testdata/fixtures/compiler/tsconfig.json" }); + project = snapshot.getProjects()[0]; + } + + function tsCreateProgram() { + const configFileName = fileURLToPath(new URL("../../../../tsc/testdata/fixtures/compiler/tsconfig.json", import.meta.url).toString()); + const configFile = ts.readConfigFile(configFileName, ts.sys.readFile); + const parsedCommandLine = ts.parseJsonConfigFileContent(configFile.config, ts.sys, path.dirname(configFileName)); + const host = ts.createCompilerHost(parsedCommandLine.options); + tsProgram = ts.createProgram({ + rootNames: parsedCommandLine.fileNames, + options: parsedCommandLine.options, + host, + }); + } + + async function createChecker() { + // checker is created lazily, for measuring symbol time in a loop + // we need to create it first. + await project.checker.getSymbolAtPosition("core.ts", 0); + } + + function tsCreateChecker() { + tsProgram.getTypeChecker(); + } + + async function getDebugTS() { + file = (await project.program.getSourceFile("debug.ts"))!; + } + + async function getProgramTS() { + file = (await project.program.getSourceFile("program.ts"))!; + } + + function tsGetProgramTS() { + tsFile = tsProgram.getSourceFile(fileURLToPath(new URL("../../../../tsc/testdata/fixtures/compiler/program.ts", import.meta.url).toString()))!; + } + + async function getCheckerTS() { + file = (await project.program.getSourceFile("checker.ts"))!; + } + + function clearSourceFileCache() { + api.clearSourceFileCache(); + } + + async function teardown() { + await api?.close(); + api = undefined!; + snapshot = undefined!; + project = undefined!; + file = undefined!; + tsProgram = undefined!; + tsFile = undefined!; + } + + function all(...fns: (() => void | Promise)[]) { + return async () => { + for (const fn of fns) { + await fn(); + } + }; + } +} diff --git a/packages/typescript/test/async/api.test.ts b/packages/typescript/test/async/api.test.ts index b40091c90d6c9..81abd11cca003 100644 --- a/packages/typescript/test/async/api.test.ts +++ b/packages/typescript/test/async/api.test.ts @@ -1,7076 +1,7076 @@ -import { - type __String, - cast, - escapeLeadingUnderscores, - type Expression, - getJSDocTags, - getSynthesizedDeepClone, - InternalSymbolName, - isCallExpression, - isExpressionStatement, - isFunctionDeclaration, - isIdentifier, - isImportDeclaration, - isInterfaceDeclaration, - isJSDocParameterTag, - isNamedImports, - isReturnStatement, - isShorthandPropertyAssignment, - isStringLiteral, - isTemplateHead, - isTemplateMiddle, - isTemplateTail, - isTypeAliasDeclaration, - isTypeNode, - isVariableDeclarationList, - isVariableStatement, - type Node, - type NodeArray, - NodeFlags, - SyntaxKind, - unescapeLeadingUnderscores, -} from "@typescript/typescript/unstable/ast"; -import { - createArrayTypeNode, - createFunctionTypeNode, - createIdentifier, - createKeywordTypeNode, - createNumericLiteral, - createParameterDeclaration, - createToken, - createTypeAliasDeclaration, - createTypeReferenceNode, - createUnionTypeNode, - createVariableDeclaration, - createVariableDeclarationList, - createVariableStatement, -} from "@typescript/typescript/unstable/ast/factory"; -import { visitEachChild } from "@typescript/typescript/unstable/ast/visitor"; -import { - API, - type BigIntLiteralType, - CheckFlags, - type ConditionalType, - DiagnosticCategory, - EmitOnly, - type FreshableType, - type ImportAdderAction, - type IndexedAccessType, - type IndexType, - type InterfaceType, - type IntrinsicType, - isErrorType, - type LiteralType, - ModifierFlags, - ModuleKind, - ObjectFlags, - type Signature, - SignatureKind, - type StringMappingType, - SymbolFlags, - type TemplateLiteralType, - type TextEdit, - TypeFlags, - TypeFormatFlags, - type TypeParameter, - TypePredicateKind, - type TypeReference, - type UnionOrIntersectionType, -} from "@typescript/typescript/unstable/async"; // @sync: } from "@typescript/typescript/unstable/sync"; -import { createVirtualFileSystem } from "@typescript/typescript/unstable/fs"; -import type { FileSystem } from "@typescript/typescript/unstable/fs"; -import assert from "node:assert"; -import { globSync } from "node:fs"; -import { resolve } from "node:path"; -import { - describe, - test, -} from "node:test"; -import { fileURLToPath } from "node:url"; -import { isSignatureDeclaration } from "../../src/ast/is.ts"; -import { runBenchmarks } from "./api.bench.ts"; -import { - defaultFiles, - spawnAPI, -} from "./api.testUtils.ts"; - -describe("API", () => { - test("parseCommandLine", async () => { - const api = spawnAPI(); - try { - const commandLine = await api.parseCommandLine([ - "--strict", - "--outDir", - "dist", - "/src/index.ts", - ]); - assert.deepEqual(commandLine.fileNames, ["/src/index.ts"]); - assert.equal(commandLine.options.strict, true); - assert.equal( - resolve(commandLine.options.outDir!), - resolve(fileURLToPath(new URL("../../../../", import.meta.url)), "dist"), - ); - assert.deepEqual(commandLine.raw, { - strict: true, - outDir: "dist", - }); - assert.deepEqual(commandLine.errors, []); - } - finally { - await api.close(); - } - }); - - test("parseCommandLine reports diagnostics", async () => { - const api = spawnAPI(); - try { - const commandLine = await api.parseCommandLine(["--notAnOption"]); - assert.deepEqual(commandLine.fileNames, []); - assert.equal(commandLine.errors.length, 1); - assert.equal(commandLine.errors[0].code, 5023); - } - finally { - await api.close(); - } - }); - - test("readConfigFile", async () => { - const api = spawnAPI({ - "/tsconfig.json": `{ - // Comments and trailing commas are supported. - "compilerOptions": { "strict": true, }, - }`, - }); - try { - const result = await api.readConfigFile("/tsconfig.json"); - assert.deepEqual(result, { - config: { compilerOptions: { strict: true } }, - }); - } - finally { - await api.close(); - } - }); - - test("readConfigFile reports read and parse errors", async () => { - const api = spawnAPI({ - "/invalid.json": `{ "compilerOptions": { "strict": true,, } }`, - }); - try { - const invalid = await api.readConfigFile("/invalid.json"); - assert.deepEqual(invalid.config, { compilerOptions: { strict: true } }); - assert.ok(invalid.error); - - const missing = await api.readConfigFile("/missing.json"); - assert.deepEqual(missing.config, {}); - assert.equal(missing.error?.code, 5083); - } - finally { - await api.close(); - } - }); - - test("parseJsonConfigFileContent with configDirectory", async () => { - const api = spawnAPI(); - try { - const config = await api.parseJsonConfigFileContent( - { - compilerOptions: { strict: true }, - files: ["index.ts"], - }, - { configDirectory: "/src" }, - ); - assert.deepEqual(config.fileNames, ["/src/index.ts"]); - assert.equal(config.options.strict, true); - assert.equal("configFilePath" in config.options, false); - assert.equal(config.compileOnSave, false); - assert.deepEqual(config.errors, []); - } - finally { - await api.close(); - } - }); - - test("parseJsonConfigFileContent accepts non-object JSON", async () => { - const api = spawnAPI(); - try { - const config = await api.parseJsonConfigFileContent(null, { configDirectory: "/src" }); - assert.deepEqual(config.fileNames, ["/src/index.ts", "/src/foo.ts"]); - assert.deepEqual(config.errors, []); - } - finally { - await api.close(); - } - }); - - test("parseJsonConfigFileContent with configFileName", async () => { - const api = spawnAPI(); - try { - const config = await api.parseJsonConfigFileContent( - { - compilerOptions: { strict: true }, - files: ["index.ts"], - }, - { configFileName: "/src/tsconfig.json" }, - ); - assert.deepEqual(config.fileNames, ["/src/index.ts"]); - assert.equal(config.options.strict, true); - assert.equal(config.options.configFilePath, "/src/tsconfig.json"); - assert.deepEqual(config.errors, []); - } - finally { - await api.close(); - } - }); - - test("parseJsonConfigFileContent preserves raw config", async () => { - const api = spawnAPI(); - try { - const input = { - compileOnSave: true, - customSetting: { enabled: true }, - files: ["index.ts"], - }; - const config = await api.parseJsonConfigFileContent(input, { configDirectory: "/src" }); - assert.deepEqual(config.raw, input); - assert.equal(config.compileOnSave, true); - } - finally { - await api.close(); - } - }); - - test("parseJsonConfigFileContent preserves an empty files list", async () => { - const api = spawnAPI(); - try { - const config = await api.parseJsonConfigFileContent( - { files: [] }, - { configDirectory: "/src" }, - ); - assert.deepEqual(config.fileNames, []); - assert.equal(config.errors.length, 1); - assert.equal(config.errors[0].code, 18002); - } - finally { - await api.close(); - } - }); - - test("parseJsonConfigFileContent reports null array elements", async () => { - const api = spawnAPI(); - try { - const config = await api.parseJsonConfigFileContent( - { files: [null], include: [null], exclude: [null] }, - { configDirectory: "/src" }, - ); - assert.equal(config.errors.length, 3); - assert.ok(config.errors.every(diagnostic => diagnostic.code === 5024)); - } - finally { - await api.close(); - } - }); - - test("transpile", async () => { - const api = spawnAPI({ - "/input.ts": "export const x: number = 1;", - }); - try { - const moduleOutput = await api.transpileModule("export const x: number = 1;", { - compilerOptions: { module: ModuleKind.CommonJS }, - }); - assert.match(moduleOutput.outputText, /exports\.x = 1/); - - const isolatedDeclarationModuleOutput = await api.transpileModule("export const x: number = 1;", { - compilerOptions: { declaration: true, isolatedDeclarations: true }, - reportDiagnostics: true, - }); - assert.equal(isolatedDeclarationModuleOutput.diagnostics?.length ?? 0, 0); - - const moduleFileOutput = await api.transpileModuleFromFile("/input.ts", { - compilerOptions: { module: ModuleKind.CommonJS }, - }); - assert.match(moduleFileOutput.outputText, /exports\.x = 1/); - - const declarationOutput = await api.transpileDeclaration("export const x: number = 1;"); - assert.equal(declarationOutput.outputText, "export declare const x: number;\n"); - - const windowsDeclarationOutput = await api.transpileDeclaration("export const x: number = 1;", { - fileName: "C:/Users/me/project/input.ts", - }); - assert.equal(windowsDeclarationOutput.outputText, "export declare const x: number;\n"); - - const declarationFileOutput = await api.transpileDeclarationFromFile("/input.ts"); - assert.equal(declarationFileOutput.outputText, "export declare const x: number;\n"); - } - finally { - await api.close(); - } - }); - - test("parseConfigFile", async () => { - const api = spawnAPI(); - try { - const config = await api.parseConfigFile("/tsconfig.json"); - assert.deepEqual(config.fileNames, ["/src/index.ts", "/src/foo.ts"]); - assert.deepEqual(config.options, { configFilePath: "/tsconfig.json" }); - assert.equal(config.compileOnSave, false); - assert.equal(config.typeAcquisition, undefined); - assert.equal(config.projectReferences, undefined); - } - finally { - await api.close(); - } - }); - - test("parseConfigFile includes projectReferences", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ references: [{ path: "./harness" }, { path: "./server" }] }), - }); - try { - const config = await api.parseConfigFile("/tsconfig.json"); - assert.deepEqual(config.fileNames, []); - assert.deepEqual(config.options, { configFilePath: "/tsconfig.json" }); - assert.deepEqual(config.projectReferences, [ - { circular: false, originalPath: "./harness", path: "/harness" }, - { circular: false, originalPath: "./server", path: "/server" }, - ]); - } - finally { - await api.close(); - } - }); - - test("parseConfigFile preserves raw config", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ - compileOnSave: true, - customSetting: { enabled: true }, - files: ["/src/index.ts"], - }), - }); - try { - const config = await api.parseConfigFile("/tsconfig.json"); - assert.equal(config.compileOnSave, true); - assert.deepEqual(config.raw, { - compileOnSave: true, - customSetting: { enabled: true }, - files: ["/src/index.ts"], - }); - } - finally { - await api.close(); - } - }); - - test("parseConfigFile includes compileOnSave", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compileOnSave: true }), - }); - try { - const config = await api.parseConfigFile("/tsconfig.json"); - assert.equal(config.compileOnSave, true); - } - finally { - await api.close(); - } - }); - - test("parseConfigFile includes typeAcquisition", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ typeAcquisition: { enable: true, include: ["jquery"] } }), - }); - try { - const config = await api.parseConfigFile("/tsconfig.json"); - assert.equal(config.typeAcquisition?.enable, true); - assert.deepEqual(config.typeAcquisition?.include, ["jquery"]); - } - finally { - await api.close(); - } - }); -}); - -// @sync-skip-block-start -describe("API - automatic batching", () => { - test("batches multiple concurrent requests into one automatically", async () => { - const api = new API({ - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), - fs: createVirtualFileSystem(defaultFiles), - collectTiming: true, - }); - try { - await api.parseCommandLine([]); // initialize API - await api.resetTimingInfo(); - let { totals: { requestCount } } = await api.getTimingInfo(); - assert.equal(requestCount, 0); - const [parseCommandLine, readConfigFile] = await Promise.all([ - api.parseCommandLine(["--strict"]), - api.readConfigFile("/tsconfig.json"), - ]); - ({ totals: { requestCount } } = await api.getTimingInfo()); - assert.equal(requestCount, 1); - - assert.equal(parseCommandLine.options.strict, true); - assert.deepStrictEqual(readConfigFile.config, {}); - } - finally { - api.close(); - } - }); -}); - -describe("API - batchContext", () => { - test("holds requests until disposal", async () => { - const api = spawnAPI(); - try { - await api.parseCommandLine([]); - - const requests = await (async () => { - using _ = api.batchContext(); - const requests = [ - api.parseCommandLine(["--strict"]), - api.readConfigFile("/tsconfig.json"), - ] as const; - let settled = false; - void Promise.all(requests).then(() => { - settled = true; - }); - - await new Promise(resolve => setImmediate(resolve)); - assert.equal(settled, false, "requests should remain pending inside the batch context"); - return requests; - })(); - - const [commandLine, config] = await Promise.all(requests); - assert.equal(commandLine.options.strict, true); - assert.deepEqual(config.config, {}); - } - finally { - await api.close(); - } - }); - - test("settles an item error without dropping a sibling result", async () => { - const src = `export const value: string = "";`; - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("value:")); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - - const requests = await (async () => { - using _ = api.batchContext(); - return [ - project.checker.getTypeArguments(type as unknown as TypeReference), - project.checker.getStringType(), - ] as const; - })(); - - await assert.rejects(requests[0], /panic:/); - assert.ok((await requests[1]).flags & TypeFlags.String); - } - finally { - await api.close(); - } - }); -}); -// @sync-skip-block-end - -describe("Checker - getImmediateAliasedSymbol", () => { - test("resolves one level of alias indirection", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/foo.ts": `export const foo = 42;`, - "/src/main.ts": `import { foo } from "./foo";\nexport const usage = foo;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = `import { foo } from "./foo";`.indexOf("foo }"); - const aliasSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(aliasSymbol); - const aliased = await project.checker.getImmediateAliasedSymbol(aliasSymbol); - assert.ok(aliased, "Should resolve the immediate aliased symbol"); - assert.equal(aliased.name, "foo"); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getTargetSymbol", () => { - test("gets the target symbol of instantiated symbol", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -class Base { - private value!: T; -} -class Alpha extends Base {} -class Bravo extends Base {} - -declare function test(): void; -test(); -test(); -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - const nodes: Array = []; - sourceFile.forEachChild(node => { - if (isExpressionStatement(node) && isCallExpression(node.expression) && node.expression.typeArguments) { - nodes.push(node.expression.typeArguments[0]); - } - }); - const aType = await project.checker.getTypeAtLocation(nodes[0]); - const bType = await project.checker.getTypeAtLocation(nodes[1]); - const aProperty = (await project.checker.getPropertiesOfType(aType))[0]; - const bProperty = (await project.checker.getPropertiesOfType(bType))[0]; - assert.ok(aProperty); - assert.ok(bProperty); - assert.equal(aProperty === bProperty, false); - assert.equal(await project.checker.getTargetSymbol(aProperty) === await project.checker.getTargetSymbol(bProperty), true); - } - finally { - await api.close(); - } - }); -}); - -describe("Snapshot", () => { - test("updateSnapshot returns snapshot with projects", async () => { - const api = spawnAPI(); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - assert.ok(snapshot); - assert.ok(snapshot.id); - assert.ok(snapshot.getProjects().length > 0); - assert.ok(snapshot.getProject("/tsconfig.json")); - } - finally { - await api.close(); - } - }); - - test("project exposes parsedCommandLine", async () => { - const api = spawnAPI({ - ...defaultFiles, - "/tsconfig.json": JSON.stringify({ compileOnSave: true }), - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - assert.deepEqual(project.parsedCommandLine.fileNames, ["/src/index.ts", "/src/foo.ts"]); - assert.deepEqual(project.parsedCommandLine.options, { configFilePath: "/tsconfig.json" }); - assert.equal(project.parsedCommandLine.compileOnSave, true); - assert.deepEqual(project.rootFiles, project.parsedCommandLine.fileNames); - assert.deepEqual(project.compilerOptions, project.parsedCommandLine.options); - } - finally { - await api.close(); - } - }); - - test("getSymbolAtPosition", async () => { - const api = spawnAPI(); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = await project.checker.getSymbolAtPosition("/src/index.ts", 9); - assert.ok(symbol); - assert.equal(symbol.name, "foo"); - assert.ok(symbol.flags & SymbolFlags.Alias); - } - finally { - await api.close(); - } - }); - - test("getSymbolAtLocation", async () => { - const api = spawnAPI(); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - const node = cast( - cast(sourceFile.statements[0], isImportDeclaration).importClause?.namedBindings, - isNamedImports, - ).elements[0].name; - assert.ok(node); - const symbol = await project.checker.getSymbolAtLocation(node); - assert.ok(symbol); - assert.equal(symbol.name, "foo"); - assert.ok(symbol.flags & SymbolFlags.Alias); - } - finally { - await api.close(); - } - }); - - test("getSymbolOfSourceFile", async () => { - const api = spawnAPI(); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const moduleSymbol = await project.checker.getSymbolOfSourceFile("/src/foo.ts"); - assert.ok(moduleSymbol); - const exports = await moduleSymbol.getExports(); - assert.ok(exports.has(escapeLeadingUnderscores("foo"))); - } - finally { - await api.close(); - } - }); - - test("getSymbolOfSourceFile returns undefined for a non-module file", async () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/script.ts": `const x = 1;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = await project.checker.getSymbolOfSourceFile("/src/script.ts"); - assert.equal(symbol, undefined); - } - finally { - await api.close(); - } - }); - - test("getSymbolOfSourceFile batched", async () => { - const api = spawnAPI(); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbols = await project.checker.getSymbolOfSourceFile(["/src/index.ts", "/src/foo.ts"]); - assert.equal(symbols.length, 2); - assert.ok(symbols[0]); - assert.ok(symbols[1]); - assert.strictEqual(symbols[1], await project.checker.getSymbolOfSourceFile("/src/foo.ts")); - } - finally { - await api.close(); - } - }); - - test("getTypeOfSymbol", async () => { - const api = spawnAPI(); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = await project.checker.getSymbolAtPosition("/src/index.ts", 9); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.ok(type.flags & TypeFlags.NumberLiteral); - } - finally { - await api.close(); - } - }); - - test("getNonMissingTypeOfSymbol", async () => { - const api = spawnAPI({ - "/tsconfig-one.json": JSON.stringify({ - compilerOptions: { - exactOptionalPropertyTypes: true, - strict: true, - }, - }), - "/tsconfig-two.json": JSON.stringify({ - compilerOptions: { - exactOptionalPropertyTypes: false, - strict: true, - }, - }), - "/src/index.ts": "const x: Partial<{ a: string }> = {};", - }); - - try { - // when `"exactOptionalPropertyTypes": true` - const snapshot1 = await api.updateSnapshot({ openProject: "/tsconfig-one.json" }); - const project1 = snapshot1.getProject("/tsconfig-one.json")!; - const type1 = await project1.checker.getTypeAtPosition("/src/index.ts", 7); - assert.ok(type1); - const symbol1 = await project1.checker.getPropertyOfType(type1, "a"); - assert.ok(symbol1); - const propertyType1 = await project1.checker.getTypeOfSymbol(symbol1); - assert.ok(propertyType1); - // getTypeOfSymbol returns 'string | undefined' - assert.ok(propertyType1.isUnionType()); - const propertyType2 = await project1.checker.getNonMissingTypeOfSymbol(symbol1); - assert.ok(propertyType2); - // getNonMissingTypeOfSymbol returns 'string' - assert.ok(!propertyType2.isUnionType()); - assert.ok(propertyType2.flags & TypeFlags.String); - - // when `"exactOptionalPropertyTypes": false` - const snapshot2 = await api.updateSnapshot({ openProject: "/tsconfig-two.json" }); - const project2 = snapshot2.getProject("/tsconfig-two.json")!; - const type2 = await project2.checker.getTypeAtPosition("/src/index.ts", 7); - assert.ok(type2); - const symbol2 = await project2.checker.getPropertyOfType(type2, "a"); - assert.ok(symbol2); - const propertyType3 = await project2.checker.getTypeOfSymbol(symbol2); - assert.ok(propertyType3); - // getTypeOfSymbol returns 'string | undefined' - assert.ok(propertyType3.isUnionType()); - const propertyType4 = await project2.checker.getNonMissingTypeOfSymbol(symbol2); - assert.ok(propertyType4); - // getNonMissingTypeOfSymbol returns 'string | undefined' - assert.ok(propertyType4.isUnionType()); - } - finally { - await api.close(); - } - }); -}); - -describe("LanguageService - imports", () => { - test("getImportEditsForSymbols adds a named import", async () => { - const source = `const value = foo;\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": source, - "/src/foo.ts": `export const foo = 1;\n`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = await project.checker.getSymbolAtPosition("/src/foo.ts", "export const ".length); - assert.ok(symbol); - - const edits = await project.languageService.getImportEditsForSymbols("/src/index.ts", [await symbol.getExportSymbol()]); - - assert.equal(applyTextEdits(source, edits), `import { foo } from "./foo";\n\nconst value = foo;\n`); - } - finally { - await api.close(); - } - }); - - test("getImportAdderEdits coalesces multiple importSymbol actions", async () => { - const source = `const value = foo + bar;\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": source, - "/src/foo.ts": `export const foo = 1;\nexport const bar = 2;\n`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const foo = await project.checker.getSymbolAtPosition("/src/foo.ts", "export const ".length); - const bar = await project.checker.getSymbolAtPosition("/src/foo.ts", "export const foo = 1;\nexport const ".length); - assert.ok(foo); - assert.ok(bar); - - const edits = await project.languageService.getImportAdderEdits("/src/index.ts", [ - { kind: "importSymbol", symbol: await foo.getExportSymbol() }, - { kind: "importSymbol", symbol: await bar.getExportSymbol() }, - ]); - - assert.equal(applyTextEdits(source, edits), `import { bar, foo } from "./foo";\n\nconst value = foo + bar;\n`); - } - finally { - await api.close(); - } - }); - - test("getImportAdderEdits adds to an existing import", async () => { - const source = `import { foo } from "./foo";\nconst value = foo + bar;\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": source, - "/src/foo.ts": `export const foo = 1;\nexport const bar = 2;\n`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const bar = await project.checker.getSymbolAtPosition("/src/foo.ts", "export const foo = 1;\nexport const ".length); - assert.ok(bar); - - const edits = await project.languageService.getImportAdderEdits("/src/index.ts", [ - { kind: "importSymbol", symbol: await bar.getExportSymbol() }, - ]); - - assert.equal(applyTextEdits(source, edits), `import { bar, foo } from "./foo";\nconst value = foo + bar;\n`); - } - finally { - await api.close(); - } - }); - - test("getImportAdderEdits returns no edits for non-exported symbols", async () => { - const source = `const value = local;\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": source, - "/src/foo.ts": `const local = 1;\n`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = await project.checker.getSymbolAtPosition("/src/foo.ts", "const ".length); - assert.ok(symbol); - - const edits = await project.languageService.getImportAdderEdits("/src/index.ts", [ - { kind: "importSymbol", symbol }, - ]); - - assert.deepEqual(edits, []); - } - finally { - await api.close(); - } - }); - - test("getImportAdderEdits rejects invalid actions", async () => { - const api = spawnAPI(); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = await project.checker.getSymbolAtPosition("/src/foo.ts", 13); - assert.ok(symbol); - - await assert.rejects( // @sync: assert.throws( - () => project.languageService.getImportAdderEdits("/src/index.ts", [{ kind: "unknown", symbol: symbol.id } as unknown as ImportAdderAction]), - /Debug Failure\. Illegal value: "unknown"/, - ); - await assert.rejects( // @sync: assert.throws( - () => project.languageService.getImportAdderEdits("/src/index.ts", [{ kind: "importSymbol", symbol: { ...symbol, id: 999_999_999 } } as unknown as ImportAdderAction]), - /symbol handle \d+ not found/, - ); - } - finally { - await api.close(); - } - }); -}); - -describe("LanguageService - getCompletionsAtPosition", () => { - test("returns member completions after a dot", async () => { - const src = `\nconst obj = { name: "hello", age: 42 };\nobj.\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - // Position right after "obj." — member completion trigger - const pos = src.indexOf("obj.") + "obj.".length; - const completions = await project.languageService.getCompletionsAtPosition("/src/main.ts", pos, { triggerCharacter: "." }); - assert.ok(completions, "Expected completions to be returned"); - assert.ok(completions.entries.length > 0, "Expected at least one completion entry"); - assert.ok(completions.entries.some(e => e.name === "name"), "Expected 'name' property in completions"); - assert.ok(completions.entries.some(e => e.name === "age"), "Expected 'age' property in completions"); - assert.ok(completions.entries.every(e => e.symbol === undefined), "Expected no symbol information"); - } - finally { - await api.close(); - } - }); - - test("completion entries include sortText", async () => { - const src = `\nconst obj = { value: 1 };\nobj.\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("obj.") + "obj.".length; - const completions = await project.languageService.getCompletionsAtPosition("/src/main.ts", pos, { triggerCharacter: "." }); - assert.ok(completions); - assert.ok(completions.entries.length > 0); - assert.ok(completions.entries.some(e => e.sortText !== undefined), "Expected sortText on all entries"); - } - finally { - await api.close(); - } - }); - - test("returns undefined for a non-existent file", async () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": `export {};`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const completions = await project.languageService.getCompletionsAtPosition("/src/does-not-exist.ts", 0); - assert.equal(completions, undefined, "Expected undefined for non-existent file"); - } - finally { - await api.close(); - } - }); - - test("includeSymbol: true populates symbol on property completions", async () => { - const src = `\nconst obj = { name: "hello", age: 42 };\nobj.\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("obj.") + "obj.".length; - const completions = await project.languageService.getCompletionsAtPosition("/src/main.ts", pos, { triggerCharacter: ".", includeSymbol: true }); - assert.ok(completions, "Expected completions"); - const nameEntry = completions.entries.find(e => e.name === "name"); - assert.ok(nameEntry, "Expected 'name' entry"); - assert.ok(nameEntry.symbol, "Expected symbol to be set on 'name' entry when includeSymbol: true"); - assert.equal(nameEntry.symbol.name, "name", "Symbol name should match completion name"); - } - finally { - await api.close(); - } - }); -}); - -describe("LanguageService - getReferencedSymbolsForNode", () => { - test("getReferencedSymbolsForNode", async () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `function greet(name: string) { return name; }\ngreet("world");`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - const funcDecl = cast(sourceFile.statements[0], isFunctionDeclaration); - const funcName = funcDecl.name!; - const refs = await project.languageService.getReferencedSymbolsForNode(funcName, funcName.pos); - assert.ok(refs.length > 0); - // Each entry should have a definition and references - const entry = refs[0]; - assert.ok(entry.definition); - assert.ok(entry.references.length > 0); - } - finally { - await api.close(); - } - }); -}); - -describe("LanguageService - getSignatureUsage", () => { - test("getSignatureUsage", async () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `function greet(name: string) { return name; }\ngreet("world");`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - const funcDecl = cast(sourceFile.statements[0], isFunctionDeclaration); - const usages = await project.languageService.getSignatureUsage(funcDecl); - assert.ok(usages.length > 0); - // The call site should have a call expression - const usage = usages.find(u => u.call !== undefined); - assert.ok(usage, "Expected at least one usage with a call expression"); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getApparentType", () => { - test("returns the apparent type of a literal type", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const x = "hello" as const;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = `export const x = "hello" as const;`.indexOf("x ="); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.equal(type.isLiteralType(), true); - assert.equal(type.isStringLiteralType(), true); - assert.equal(type.isIntrinsicType(), false); - const apparent = await project.checker.getApparentType(type); - assert.ok(apparent); - assert.ok(apparent.id > 0); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getReducedType", () => { - test("returns the reduced type", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -declare const TaggedError: ( - tag: Tag, -) => new = {}>( - args: { readonly [P in keyof A]: A[P] }, -) => { readonly _tag: Tag } & Readonly; - -class RateLimitError extends TaggedError("RateLimitError")<{ - readonly retryAfter: number; -}> {} - -class QuotaExceededError extends TaggedError("QuotaExceededError")<{ - readonly limit: number; -}> {} - -export type Result = RateLimitError | (RateLimitError & QuotaExceededError);`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - const typeAlias = sourceFile.statements.find(isTypeAliasDeclaration); - assert.ok(typeAlias); - const type = await project.checker.getTypeAtLocation(typeAlias); - assert.equal(type.isUnionType(), true); - assert.equal(type.isObjectType(), false); - const reducedType = await project.checker.getReducedType(type); - assert.equal(reducedType.isUnionType(), false); - assert.equal(reducedType.isObjectType(), true); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getMemberInModuleExports", () => { - test("returns a named export when present", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/index.ts": `export const direct = 1;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - const moduleSymbol = await project.checker.getSymbolAtLocation(sourceFile); - assert.ok(moduleSymbol); - const found = await project.checker.getMemberInModuleExports(moduleSymbol, "direct"); - assert.ok(found); - assert.equal(found.name, "direct"); - const missing = await project.checker.getMemberInModuleExports(moduleSymbol, "missing"); - assert.equal(missing, undefined); - } - finally { - await api.close(); - } - }); -}); - -describe("SourceFile", () => { - test("getSourceFileNames returns all program files, not just root files", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ - compilerOptions: { - moduleResolution: "node10", - noLib: true, - }, - }), - "/src/index.ts": `import { foo } from "./foo";\nimport { bar } from "my-lib";\nexport const result = foo + bar;`, - "/src/foo.ts": `export const foo = 42;`, - "/node_modules/my-lib/package.json": JSON.stringify({ name: "my-lib", types: "./index.d.ts" }), - "/node_modules/my-lib/index.d.ts": `export declare const bar: number;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const fileNames = await project.program.getSourceFileNames(); - assert.deepEqual(fileNames, [ - "/src/foo.ts", - "/node_modules/my-lib/index.d.ts", - "/src/index.ts", - ]); - } - finally { - await api.close(); - } - }); - - test("source file metadata identifies external library and default library files", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ - compilerOptions: { - moduleResolution: "node10", - }, - }), - "/src/index.ts": `import { bar } from "my-lib";\nexport const result = bar;`, - "/node_modules/my-lib/package.json": JSON.stringify({ name: "my-lib", types: "./index.d.ts" }), - "/node_modules/my-lib/index.d.ts": `export declare const bar: number;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const program = project.program; - - const index = await program.getSourceFile("/src/index.ts"); - assert.ok(index); - assert.equal(await program.isSourceFileFromExternalLibrary(index), false); - assert.equal(await program.isSourceFileDefaultLibrary(index), false); - - const lib = await program.getSourceFile("/node_modules/my-lib/index.d.ts"); - assert.ok(lib); - assert.equal(await program.isSourceFileFromExternalLibrary(lib), true); - assert.equal(await program.isSourceFileDefaultLibrary(lib), false); - - const fileNames = await program.getSourceFileNames(); - const defaultLibName = fileNames.find(name => name.endsWith("lib.d.ts") || name.includes("/lib.")); - assert.ok(defaultLibName, "expected a default library file in the program"); - const defaultLib = await program.getSourceFile(defaultLibName); - assert.ok(defaultLib); - assert.equal(await program.isSourceFileDefaultLibrary(defaultLib), true); - } - finally { - await api.close(); - } - }); - - test("source file metadata reports implied node format", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ - compilerOptions: { - module: "nodenext", - moduleResolution: "nodenext", - }, - }), - "/src/index.ts": `export const x = 1;`, - "/src/esm.mts": `export const e = 1;`, - "/src/cjs.cts": `export const c = 1;`, - "/esm/package.json": JSON.stringify({ type: "module" }), - "/esm/index.ts": `export const m = 1;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const program = snapshot.getProject("/tsconfig.json")!.program; - - const mts = await program.getSourceFile("/src/esm.mts"); - assert.ok(mts); - assert.equal((await program.getSourceFileMetadata(mts.fileName))?.impliedNodeFormat, ModuleKind.ESNext); - assert.equal((await program.getSourceFileMetadataByPath(mts.path))?.impliedNodeFormat, ModuleKind.ESNext); - - const cts = await program.getSourceFile("/src/cjs.cts"); - assert.ok(cts); - assert.equal((await program.getSourceFileMetadata(cts.fileName))?.impliedNodeFormat, ModuleKind.CommonJS); - - // A plain .ts file with no nearby `"type": "module"` is CommonJS. - const index = await program.getSourceFile("/src/index.ts"); - assert.ok(index); - assert.equal((await program.getSourceFileMetadata(index.fileName))?.impliedNodeFormat, ModuleKind.CommonJS); - - // A plain .ts file under a `"type": "module"` package is ESM. - const esmIndex = await program.getSourceFile("/esm/index.ts"); - assert.ok(esmIndex); - assert.equal((await program.getSourceFileMetadata(esmIndex.fileName))?.impliedNodeFormat, ModuleKind.ESNext); - } - finally { - await api.close(); - } - }); - - test("file properties", async () => { - const api = spawnAPI(); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - - assert.ok(sourceFile); - assert.equal(sourceFile.text, defaultFiles["/src/index.ts"]); - assert.equal(sourceFile.fileName, "/src/index.ts"); - } - finally { - await api.close(); - } - }); - - test("extended data", async () => { - const api = spawnAPI(); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - - assert.ok(sourceFile); - let nodeCount = 1; - sourceFile.forEachChild(function visit(node) { - if (isTemplateHead(node)) { - assert.equal(node.text, "head "); - assert.equal(node.rawText, "head "); - assert.equal(node.templateFlags, 0); - } - else if (isTemplateMiddle(node)) { - assert.equal(node.text, "middle"); - assert.equal(node.rawText, "middle"); - assert.equal(node.templateFlags, 0); - } - else if (isTemplateTail(node)) { - assert.equal(node.text, " tail"); - assert.equal(node.rawText, " tail"); - assert.equal(node.templateFlags, 0); - } - nodeCount++; - node.forEachChild(visit); - }); - assert.equal(nodeCount, 8); - } - finally { - await api.close(); - } - }); - - test("forEachChild with visitList does not visit array children twice", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ files: ["/input.ts"] }), - "/input.ts": `let arrow = () => {}`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/input.ts"); - - assert.ok(sourceFile); - - const visited: { kind: SyntaxKind; pos: number; end: number; }[] = []; - (function walk(node: Node): void { - visited.push({ kind: node.kind, pos: node.pos, end: node.end }); - node.forEachChild(walk, (nodes: NodeArray) => { - for (let i = 0; i < nodes.length; i++) { - walk(nodes[i]); - } - return undefined; - }); - })(sourceFile); - - // Each node should be visited exactly once, even when a visitList callback - // is supplied. Previously array children were visited twice. - const seen = new Set(); - for (const { kind, pos, end } of visited) { - const key = `${kind}.${pos}.${end}`; - assert.ok(!seen.has(key), `Node ${key} was visited more than once`); - seen.add(key); - } - } - finally { - await api.close(); - } - }); -}); - -describe("NodeArray", () => { - test("hasTrailingComma", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `declare function foo(...args: any): void;\nfoo("a", "b",);\nfoo("a", "b");`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - const statements = sourceFile.statements.filter(isExpressionStatement); - assert.ok(isCallExpression(statements[0].expression)); - assert.equal(statements[0].expression.arguments.hasTrailingComma, true); - assert.ok(isCallExpression(statements[1].expression)); - assert.equal(statements[1].expression.arguments.hasTrailingComma, false); - } - finally { - await api.close(); - } - }); -}); - -test("unicode escapes", async () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/1.ts": `"😃"`, - "/src/2.ts": `"\\ud83d\\ude03"`, - "/src/3.ts": `"\\ud800a\\udc00"`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const expectedTexts = new Map([ - ["/src/1.ts", "😃"], - ["/src/2.ts", "😃"], - ["/src/3.ts", "\ud800a\udc00"], - ]); - - for (const file of expectedTexts.keys()) { - const sourceFile = await project.program.getSourceFile(file); - assert.ok(sourceFile); - - sourceFile.forEachChild(function visit(node) { - if (isStringLiteral(node)) { - assert.equal(node.text, expectedTexts.get(file)); - } - node.forEachChild(visit); - }); - } - } - finally { - await api.close(); - } -}); - -test("template unicode escapes", async () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": "`\\ud800${0}\\udc00`", - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - - let sawHead = false; - let sawTail = false; - sourceFile.forEachChild(function visit(node) { - if (isTemplateHead(node)) { - assert.equal(node.text, "\ud800"); - sawHead = true; - } - else if (isTemplateTail(node)) { - assert.equal(node.text, "\udc00"); - sawTail = true; - } - node.forEachChild(visit); - }); - assert.ok(sawHead); - assert.ok(sawTail); - } - finally { - await api.close(); - } -}); - -test("Object equality", async () => { - const api = spawnAPI(); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - // Same symbol returned from same snapshot's checker - assert.strictEqual( - await project.checker.getSymbolAtPosition("/src/index.ts", 9), - await project.checker.getSymbolAtPosition("/src/index.ts", 10), - ); - } - finally { - await api.close(); - } -}); - -test("Snapshot dispose", async () => { - const api = spawnAPI(); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = await project.checker.getSymbolAtPosition("/src/index.ts", 9); - assert.ok(symbol); - - // Snapshot dispose should release server-side resources - assert.ok(snapshot.isDisposed() === false); - await snapshot.dispose(); - assert.ok(snapshot.isDisposed() === true); - - // After dispose, snapshot methods should throw - assert.throws(() => { - snapshot.getProject("/tsconfig.json"); - }, { - name: "Error", - message: "Snapshot is disposed", - }); - } - finally { - await api.close(); - } -}); - -describe("Multiple snapshots", () => { - test("two snapshots work independently", async () => { - const api = spawnAPI(); - try { - const snap1 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const snap2 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - - // Both can fetch source files - const sf1 = await snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); - const sf2 = await snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); - assert.ok(sf1); - assert.ok(sf2); - - // Disposing one doesn't break the other - await snap1.dispose(); - assert.ok(snap1.isDisposed()); - assert.ok(!snap2.isDisposed()); - - // snap2 still works after snap1 is disposed - const symbol = await snap2.getProject("/tsconfig.json")!.checker.getSymbolAtPosition("/src/index.ts", 9); - assert.ok(symbol); - assert.equal(symbol.name, "foo"); - } - finally { - await api.close(); - } - }); - - test("each snapshot has its own server-side lifecycle", async () => { - const { api, fs } = spawnAPIWithFS(); - try { - const snap1 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - - // Verify initial state - const sf1 = await snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); - assert.ok(sf1); - assert.equal(sf1.text, `export const foo = 42;`); - - // Mutate the file and create a new snapshot with the change - fs.writeFile!("/src/foo.ts", `export const foo = "changed";`); - const snap2 = await api.updateSnapshot({ - fileChanges: { changed: ["/src/foo.ts"] }, - }); - - // snap2 should reflect the updated content - const sf2 = await snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); - assert.ok(sf2); - assert.equal(sf2.text, `export const foo = "changed";`); - - // snap1's source file should still have the original content - assert.equal(sf1.text, `export const foo = 42;`); - - await snap1.dispose(); - - // snap2 still works independently after snap1 is disposed - const symbol = await snap2.getProject("/tsconfig.json")!.checker.getSymbolAtPosition("/src/index.ts", 9); - assert.ok(symbol); - - await snap2.dispose(); - - // Both are disposed, new snapshot works fine with latest content - const snap3 = await api.updateSnapshot(); - const sf3 = await snap3.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); - assert.ok(sf3); - assert.equal(sf3.text, `export const foo = "changed";`); - } - finally { - await api.close(); - } - }); - - test("adding a new file is reflected in the next snapshot", async () => { - const { api, fs } = spawnAPIWithFS(); - try { - const snap1 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - - // Add a brand new file - fs.writeFile!("/src/bar.ts", `export const bar = true;`); - const snap2 = await api.updateSnapshot({ - fileChanges: { created: ["/src/bar.ts"] }, - }); - - const sf = await snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/bar.ts"); - assert.ok(sf); - assert.equal(sf.text, `export const bar = true;`); - - // Original snapshot shouldn't have the new file - const sfOld = await snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/bar.ts"); - assert.equal(sfOld, undefined); - } - finally { - await api.close(); - } - }); - - test("multiple sequential edits produce correct snapshots", async () => { - const { api, fs } = spawnAPIWithFS(); - try { - await api.updateSnapshot({ openProject: "/tsconfig.json" }); - - const versions = [ - `export const foo = 1;`, - `export const foo = 2;`, - `export const foo = 3;`, - ]; - - for (const version of versions) { - fs.writeFile!("/src/foo.ts", version); - const snap = await api.updateSnapshot({ - fileChanges: { changed: ["/src/foo.ts"] }, - }); - const sf = await snap.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); - assert.ok(sf); - assert.equal(sf.text, version); - } - } - finally { - await api.close(); - } - }); -}); - -describe("Source file caching", () => { - test("same file from same snapshot returns cached object", async () => { - const api = spawnAPI(); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sf1 = await project.program.getSourceFile("/src/index.ts"); - const sf2 = await project.program.getSourceFile("/src/index.ts"); - assert.ok(sf1); - assert.strictEqual(sf1, sf2, "Same source file should be returned from cache"); - } - finally { - await api.close(); - } - }); - - test("same file from two snapshots (same content) returns cached object", async () => { - const api = spawnAPI(); - try { - const snap1 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const snap2 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - // Fetch from snap1 first (populates cache), then snap2 (cache hit via hash) - const sf1 = await snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); - const sf2 = await snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); - assert.ok(sf1); - assert.ok(sf2); - // Same content hash → cache hit → same object - assert.strictEqual(sf1, sf2, "Same file with same content should share cached object"); - } - finally { - await api.close(); - } - }); - - test("modified file returns a different source file object", async () => { - const { api, fs } = spawnAPIWithFS(); - try { - const snap1 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const sf1 = await snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); - assert.ok(sf1); - assert.equal(sf1.text, `export const foo = 42;`); - - // Mutate the file in the VFS - fs.writeFile!("/src/foo.ts", `export const foo = 100;`); - - // Notify the server about the change - const snap2 = await api.updateSnapshot({ - fileChanges: { changed: ["/src/foo.ts"] }, - }); - const sf2 = await snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); - assert.ok(sf2); - assert.equal(sf2.text, `export const foo = 100;`); - - // Different content → different object - assert.notStrictEqual(sf1, sf2, "Modified file should return a new source file object"); - } - finally { - await api.close(); - } - }); - - test("unmodified file retains cached object across file change notification", async () => { - const { api, fs } = spawnAPIWithFS(); - try { - const snap1 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const sf1 = await snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); - assert.ok(sf1); - - // Mutate a different file - fs.writeFile!("/src/foo.ts", `export const foo = 999;`); - - // Notify the server about the change to foo.ts only - const snap2 = await api.updateSnapshot({ - fileChanges: { changed: ["/src/foo.ts"] }, - }); - const sf2 = await snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); - assert.ok(sf2); - - // index.ts wasn't changed — should still get cached object - assert.strictEqual(sf1, sf2, "Unchanged file should return cached object across snapshots"); - } - finally { - await api.close(); - } - }); - - test("cache entries survive when one of two snapshots is disposed", async () => { - const api = spawnAPI(); - try { - const snap1 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - // Fetch from snap1 to populate cache - const sf1 = await snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); - assert.ok(sf1); - - // snap2 retains snap1's cache refs for unchanged files via snapshot changes - const snap2 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - - // Dispose snap1 — snap2 still holds a ref, so the entry survives - await snap1.dispose(); - - // Fetching from snap2 should still return the cached object - const sf2 = await snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); - assert.ok(sf2); - assert.strictEqual(sf1, sf2, "Cache entry should survive when retained by the next snapshot"); - } - finally { - await api.close(); - } - }); - - test("invalidateAll causes all files to be re-fetched", async () => { - const { api, fs } = spawnAPIWithFS(); - try { - const snap1 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const sf1 = await snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); - assert.ok(sf1); - assert.equal(sf1.text, `export const foo = 42;`); - - // Mutate the file - fs.writeFile!("/src/foo.ts", `export const foo = "hello";`); - - // Use invalidateAll to force re-fetch - const snap2 = await api.updateSnapshot({ - fileChanges: { invalidateAll: true }, - }); - const sf2 = await snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); - assert.ok(sf2); - assert.equal(sf2.text, `export const foo = "hello";`); - assert.notStrictEqual(sf1, sf2, "invalidateAll should produce new source file objects"); - } - finally { - await api.close(); - } - }); - - test("node handles from a cached source file should be valid in a new snapshot", async () => { - const { api, fs } = spawnAPIWithFS({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `function foo(x: number) {}\nfoo(42);`, - "/src/other.ts": `export const x = 1;`, - }); - try { - // Snapshot 1: get a node and verify getContextualType works - const snap1 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const proj1 = snap1.getProject("/tsconfig.json")!; - - const sf1 = await proj1.program.getSourceFile("/src/main.ts"); - assert.ok(sf1); - - let numLiteral: Expression | undefined; - sf1.forEachChild(function visit(node) { - if (isCallExpression(node)) numLiteral = node.arguments[0]; - node.forEachChild(visit); - }); - assert.ok(numLiteral, "should find the 42 argument"); - - const type1 = await proj1.checker.getContextualType(numLiteral); - assert.ok(type1); - assert.ok(type1.flags & TypeFlags.Number); - - // Snapshot 2: change a different file - fs.writeFile!("/src/other.ts", `export const x = 2;`); - const snap2 = await api.updateSnapshot({ - fileChanges: { changed: ["/src/other.ts"] }, - }); - const proj2 = snap2.getProject("/tsconfig.json")!; - - // main.ts is unchanged — client returns the cached SourceFile (same object) - const sf2 = await proj2.program.getSourceFile("/src/main.ts"); - assert.ok(sf2); - assert.strictEqual(sf1, sf2, "unchanged file should be served from client cache"); - - let numLiteral2: Expression | undefined; - sf2.forEachChild(function visit(node) { - if (isCallExpression(node)) numLiteral2 = node.arguments[0]; - node.forEachChild(visit); - }); - assert.ok(numLiteral2, "should find the 42 argument"); - assert.strictEqual(numLiteral, numLiteral2, "unchanged file should be served from client cache"); - - // A type from new snapshot should be resolved - const type2 = await proj2.checker.getContextualType(numLiteral); - assert.ok(type2); - assert.ok(type2.flags & TypeFlags.Number); - } - finally { - await api.close(); - } - }); -}); - -describe("Snapshot disposal", () => { - test("dispose is idempotent", async () => { - const api = spawnAPI(); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - await snapshot.dispose(); - assert.ok(snapshot.isDisposed()); - // Second dispose should not throw - await snapshot.dispose(); - assert.ok(snapshot.isDisposed()); - } - finally { - await api.close(); - } - }); - - test("api.close disposes all active snapshots", async () => { - const api = spawnAPI(); - const snap1 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const snap2 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - assert.ok(!snap1.isDisposed()); - assert.ok(!snap2.isDisposed()); - await api.close(); - assert.ok(snap1.isDisposed()); - assert.ok(snap2.isDisposed()); - }); -}); - -describe("Source file cache keying across projects", () => { - // Three projects share the same file (/src/shared.ts). - // The file sits inside a package.json scope with "type": "module". - // - // Project A: moduleResolution: bundler (auto detection, bundler doesn't - // trigger isFileForcedToBeModuleByFormat → file parsed as script) - // Project B: moduleResolution: bundler, moduleDetection: force - // (force → file parsed as module) - // Project C: moduleResolution: nodenext - // (nodenext + type:module → impliedNodeFormat ESNext → - // isFileForcedToBeModuleByFormat → file parsed as module) - // - // Expected: exactly two distinct source file objects are stored: - // - A gets one (script parse) - // - B and C share another (module parse) - const multiProjectFiles: Record = { - "/package.json": JSON.stringify({ type: "module" }), - "/src/shared.ts": `export const x = 1;`, - // Project A – bundler, auto detection (default) - "/projectA/tsconfig.json": JSON.stringify({ - compilerOptions: { - moduleResolution: "bundler", - module: "esnext", - strict: true, - }, - files: ["../src/shared.ts"], - }), - // Project B – bundler, force module detection - "/projectB/tsconfig.json": JSON.stringify({ - compilerOptions: { - moduleResolution: "bundler", - module: "esnext", - moduleDetection: "force", - strict: true, - }, - files: ["../src/shared.ts"], - }), - // Project C – nodenext (type:module → module) - "/projectC/tsconfig.json": JSON.stringify({ - compilerOptions: { - moduleResolution: "nodenext", - module: "nodenext", - strict: true, - }, - files: ["../src/shared.ts"], - }), - }; - - test("different parse modes produce separate cached objects; same parse modes share", async () => { - const api = spawnAPI(multiProjectFiles); - try { - // Open all three projects - await api.updateSnapshot({ openProject: "/projectA/tsconfig.json" }); - await api.updateSnapshot({ openProject: "/projectB/tsconfig.json" }); - const snapshot = await api.updateSnapshot({ openProject: "/projectC/tsconfig.json" }); - - const projectA = snapshot.getProject("/projectA/tsconfig.json")!; - const projectB = snapshot.getProject("/projectB/tsconfig.json")!; - const projectC = snapshot.getProject("/projectC/tsconfig.json")!; - assert.ok(projectA, "projectA should exist"); - assert.ok(projectB, "projectB should exist"); - assert.ok(projectC, "projectC should exist"); - - // Fetch the shared file from each project - const sfA = await projectA.program.getSourceFile("/src/shared.ts"); - const sfB = await projectB.program.getSourceFile("/src/shared.ts"); - const sfC = await projectC.program.getSourceFile("/src/shared.ts"); - assert.ok(sfA, "sfA should exist"); - assert.ok(sfB, "sfB should exist"); - assert.ok(sfC, "sfC should exist"); - - // A should differ from B and C (script vs module parse) - assert.notStrictEqual(sfA, sfB, "projectA (script) and projectB (module) should have different cached source files"); - assert.notStrictEqual(sfA, sfC, "projectA (script) and projectC (module) should have different cached source files"); - - // B and C should share the same cached object (both module parse, same content hash) - assert.strictEqual(sfB, sfC, "projectB and projectC (both module parse) should share the same cached source file"); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - symbol identity across projects", () => { - const sharedSymbolFiles = { - "/projectA/tsconfig.json": JSON.stringify({ files: ["../src/shared.ts"] }), - "/projectB/tsconfig.json": JSON.stringify({ files: ["../src/shared.ts"] }), - "/src/shared.ts": `export const sharedVar = 42;`, - }; - - test("getSymbolAtPosition returns same Symbol instance across projects", async () => { - const api = spawnAPI(sharedSymbolFiles); - try { - await api.updateSnapshot({ openProject: "/projectA/tsconfig.json" }); - const snapshot = await api.updateSnapshot({ openProject: "/projectB/tsconfig.json" }); - - const projectA = snapshot.getProject("/projectA/tsconfig.json")!; - const projectB = snapshot.getProject("/projectB/tsconfig.json")!; - assert.ok(projectA, "projectA should exist"); - assert.ok(projectB, "projectB should exist"); - - const src = sharedSymbolFiles["/src/shared.ts"]; - const varPos = src.indexOf("sharedVar"); - - const symbolA = await projectA.checker.getSymbolAtPosition("/src/shared.ts", varPos); - const symbolB = await projectB.checker.getSymbolAtPosition("/src/shared.ts", varPos); - - assert.ok(symbolA, "symbolA should exist"); - assert.ok(symbolB, "symbolB should exist"); - assert.equal(symbolA.name, "sharedVar"); - assert.equal(symbolB.name, "sharedVar"); - - assert.strictEqual(symbolA, symbolB, "Same source symbol queried from two projects should be the same object"); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - types and signatures", () => { - const checkerFiles = { - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export const x = 42; -export function add(a: number, b: number, ...rest: number[]): number { return a + b; } -export class MyClass { - value: string = ""; - getValue(): string { return this.value; } -} -`, - }; - - test("getTypeAtPosition", async () => { - const api = spawnAPI(checkerFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = checkerFiles["/src/main.ts"]; - const xPos = src.indexOf("x = 42"); - const type = await project.checker.getTypeAtPosition("/src/main.ts", xPos); - assert.ok(type); - assert.ok(type.flags & TypeFlags.NumberLiteral); - } - finally { - await api.close(); - } - }); - - test("getTypeAtPosition batched", async () => { - const api = spawnAPI(checkerFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = checkerFiles["/src/main.ts"]; - const xPos = src.indexOf("x = 42"); - const addPos = src.indexOf("add("); - const types = await project.checker.getTypeAtPosition("/src/main.ts", [xPos, addPos]); - assert.equal(types.length, 2); - assert.ok(types[0]); - assert.ok(types[1]); - } - finally { - await api.close(); - } - }); - - test("getTypeAtLocation", async () => { - const api = spawnAPI(checkerFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - const firstVarDecl = sourceFile.statements[2]; // "export const x" - assert.ok(firstVarDecl); - const type = await project.checker.getTypeAtLocation(firstVarDecl); - assert.ok(type); - } - finally { - await api.close(); - } - }); - - test("getTypeAtLocation returns property type for parenthesized and chained access (issue #3938)", async () => { - const files = { - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -interface A { - a: number; - b: { c: number }; -} -const obj: A = { a: 1, b: { c: 2 } }; -const a1 = obj.a; -const a2 = (obj).a; -const c = obj.b.c; -`, - }; - - const api = spawnAPI(files); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - - const targetNodes = new Map(); - sourceFile.forEachChild(function visit(node) { - if (node.kind === SyntaxKind.PropertyAccessExpression) { - const text = sourceFile.text.slice(node.pos, node.end).trim(); - if (text === "obj.a" || text === "(obj).a" || text === "obj.b.c") { - targetNodes.set(text, node); - } - } - node.forEachChild(visit); - }); - - assert.equal(targetNodes.size, 3, "Should find all target property access expressions"); - for (const expr of ["obj.a", "(obj).a", "obj.b.c"] as const) { - const node = targetNodes.get(expr); - assert.ok(node, `Should find expression '${expr}'`); - const type = await project.checker.getTypeAtLocation(node); - assert.ok(type, `Should get a type for '${expr}'`); - assert.ok(type.flags & TypeFlags.Number, `Expected '${expr}' to have number type flags, got ${type.flags}`); - } - } - finally { - await api.close(); - } - }); - - test("getTypeAtLocation returns call result type for private method call (issue #4041)", async () => { - const files = { - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true, target: "esnext" } }), - "/src/main.ts": ` -type Result = { readonly value: string }; - -export class Cache { - run(): Result { - return this.#buildCapabilities(); - } - - #buildCapabilities(): Result { - return { value: "ok" }; - } -} -`, - }; - - const api = spawnAPI(files); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - - let callNode: import("@typescript/typescript/unstable/ast").CallExpression | undefined; - sourceFile.forEachChild(function visit(node) { - if (isCallExpression(node)) { - const text = sourceFile.text.slice(node.pos, node.end).trim(); - if (text === "this.#buildCapabilities()") { - callNode = node; - } - } - node.forEachChild(visit); - }); - - assert.ok(callNode, "Should find private method call expression"); - const callType = await project.checker.getTypeAtLocation(callNode); - const calleeType = await project.checker.getTypeAtLocation(callNode.expression); - assert.ok(callType, "Should get type for call expression"); - assert.ok(calleeType, "Should get type for callee expression"); - - const callSignatures = await project.checker.getSignaturesOfType(calleeType, SignatureKind.Call); - assert.ok(callSignatures.length > 0, "Callee should be callable"); - const returnType = await project.checker.getReturnTypeOfSignature(callSignatures[0]); - assert.ok(returnType, "Should get return type for private method call"); - const callExprSignatures = await project.checker.getSignaturesOfType(callType, SignatureKind.Call); - - assert.ok(callType.flags & TypeFlags.Object, `Expected call expression type to be object-like, got ${callType.flags}`); - assert.ok(returnType.flags & TypeFlags.Object, `Expected return type to be object-like, got ${returnType.flags}`); - assert.equal(callType.flags, returnType.flags, "Call expression type should have same flags as method return type"); - assert.equal(callExprSignatures.length, 0, "Call expression result type should not itself be callable"); - } - finally { - await api.close(); - } - }); - - test("getSignaturesOfType - call signatures", async () => { - const api = spawnAPI(checkerFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = checkerFiles["/src/main.ts"]; - const addPos = src.indexOf("add("); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", addPos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const callSigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(callSigs.length > 0); - const sig = callSigs[0]; - const typeCallSigs = await type.getCallSignatures(); - assert.deepEqual(typeCallSigs, callSigs); - assert.ok((await sig.getReturnType()).flags & TypeFlags.Number); - assert.ok((await sig.getTypeParameterAtPosition(0)).flags & TypeFlags.Number); - assert.ok(sig.id); - assert.ok(sig.parameters.length >= 2); - assert.ok(sig.hasRestParameter); - assert.ok(!sig.isConstruct); - assert.ok(!sig.isAbstract); - } - finally { - await api.close(); - } - }); - - test("getApparentProperties includes CallableFunction members", async () => { - const api = spawnAPI(checkerFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = checkerFiles["/src/main.ts"]; - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("add(")); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - const propertyNames = (await type.getApparentProperties()).map(property => property.name); - assert.ok(propertyNames.includes("apply")); - assert.ok(propertyNames.includes("call")); - assert.ok(propertyNames.includes("bind")); - } - finally { - await api.close(); - } - }); - - test("checker and object methods share cached results", async () => { - const api = new API({ - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), - fs: createVirtualFileSystem(checkerFiles), - collectTiming: true, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = checkerFiles["/src/main.ts"]; - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("add(")); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - let signature: Signature | undefined; - - async function assertOneRequest(callback: () => Promise) { - await api.resetTimingInfo(); - await callback(); - assert.equal((await api.getTimingInfo()).totals.requestCount, 1); - } - - await assertOneRequest(async () => { - const properties = await type.getProperties(); - assert.strictEqual(await project.checker.getPropertiesOfType(type), properties); - }); - await assertOneRequest(async () => { - const signatures = await type.getCallSignatures(); - assert.strictEqual(await project.checker.getSignaturesOfType(type, SignatureKind.Call), signatures); - signature = signatures[0]; - }); - await assertOneRequest(async () => { - const nonNullable = await project.checker.getNonNullableType(type); - assert.strictEqual(await type.getNonNullableType(), nonNullable); - }); - await assertOneRequest(async () => { - const apparentType = await type.getApparentType(); - assert.strictEqual(await project.checker.getApparentType(type), apparentType); - }); - await assertOneRequest(async () => { - const indexInfos = await type.getIndexInfos(); - assert.strictEqual(await project.checker.getIndexInfosOfType(type), indexInfos); - }); - await assertOneRequest(async () => { - assert.ok(signature); - const returnType = await project.checker.getReturnTypeOfSignature(signature); - assert.strictEqual(await signature.getReturnType(), returnType); - }); - } - finally { - await api.close(); - } - }); - - test("getSignaturesOfType - construct signatures", async () => { - const api = spawnAPI(checkerFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = checkerFiles["/src/main.ts"]; - const classPos = src.indexOf("MyClass"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", classPos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const constructSigs = await project.checker.getSignaturesOfType(type, SignatureKind.Construct); - assert.ok(constructSigs.length > 0); - const sig = constructSigs[0]; - assert.ok(sig.isConstruct); - } - finally { - await api.close(); - } - }); - - test("Signature declaration can be resolved", async () => { - const api = spawnAPI(checkerFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = checkerFiles["/src/main.ts"]; - const addPos = src.indexOf("add("); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", addPos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const callSigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(callSigs.length > 0); - const sig = callSigs[0]; - assert.ok(sig.declaration); - assert.ok(isSignatureDeclaration.Handle(sig.declaration)); - const node = await sig.declaration.resolve(project); - assert.ok(node); - assert.ok(node.parameters); - assert.ok(isSignatureDeclaration(node)); - // The handle remembers its canonical project, so resolve() works without an argument. - const nodeFromCanonical = await sig.declaration.resolve(); - assert.ok(nodeFromCanonical); - assert.ok(nodeFromCanonical.parameters); - assert.ok(isSignatureDeclaration(nodeFromCanonical)); - assert.strictEqual(nodeFromCanonical.kind, node.kind); - - const methodPos = src.indexOf("getValue"); - const methodSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", methodPos); - assert.ok(methodSymbol); - assert.ok(methodSymbol.valueDeclaration); - const methodNode = await methodSymbol.valueDeclaration.resolve(project); - assert.ok(methodNode); - assert.strictEqual(methodNode.parent.kind, SyntaxKind.ClassDeclaration); - assert.strictEqual(methodNode.parent.parent.kind, SyntaxKind.SourceFile); - // A symbol's declaration handles default to the symbol's canonical project. - const methodNodeFromCanonical = await methodSymbol.valueDeclaration.resolve(); - assert.ok(methodNodeFromCanonical); - assert.strictEqual(methodNodeFromCanonical.kind, methodNode.kind); - } - finally { - await api.close(); - } - }); - - test("getSignaturesOfType - signature type parameters", async () => { - const mainFile = ` - interface Operator { - } - export declare class Observable { - lift(operator: Operator): Observable; - } - `; - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": mainFile, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const liftPos = mainFile.indexOf("lift"); - const type = await project.checker.getTypeAtPosition("/src/main.ts", liftPos); - assert.ok(type); - const callSigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(callSigs.length === 1, "should have exactly one call signature, found: " + callSigs.length); - const sig = callSigs[0]; - assert.ok(sig.typeParameters?.length === 1, "should have exactly one type parameter, found: " + sig.typeParameters?.length); - const typeParams = await sig.getTypeParameters(); - const typeParam = typeParams[0]; - assert.ok(typeParam, "should have type parameter"); - const name = (await typeParam.getSymbol())?.name; - assert.ok(name === "R", "should be named R, instead: " + name); - assert.ok(typeParam.flags & TypeFlags.TypeParameter, "should be a type parameter, instead flags: " + typeParam.flags); - } - finally { - await api.close(); - } - }); - - test("Signature.getParameters() returns parameter symbols with correct names", async () => { - const api = spawnAPI(checkerFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = checkerFiles["/src/main.ts"]; - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("add(")); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const params = await sigs[0].getParameters(); - assert.equal(params.length, 3); - assert.equal(params[0].name, "a"); - assert.equal(params[1].name, "b"); - assert.equal(params[2].name, "rest"); - assert.ok(params[0].flags & SymbolFlags.FunctionScopedVariable, `expected FunctionScopedVariable on 'a', got ${params[0].flags}`); - } - finally { - await api.close(); - } - }); - - test("Signature.getThisParameter() returns undefined when no explicit this parameter", async () => { - const api = spawnAPI(checkerFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = checkerFiles["/src/main.ts"]; - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("add(")); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const thisParam = await sigs[0].getThisParameter(); - assert.strictEqual(thisParam, undefined, "add() has no explicit this parameter"); - } - finally { - await api.close(); - } - }); - - test("Signature.getThisParameter() returns symbol for explicit this parameter", async () => { - const src = `export function foo(this: { n: number }, x: string): void {}`; - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("foo(")); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const thisParam = await sigs[0].getThisParameter(); - assert.ok(thisParam, "foo has an explicit this parameter"); - assert.equal(thisParam.name, "this"); - assert.ok(thisParam.flags & SymbolFlags.FunctionScopedVariable, `expected FunctionScopedVariable, got ${thisParam.flags}`); - } - finally { - await api.close(); - } - }); - - test("Signature.getTarget() returns undefined for a non-instantiated signature", async () => { - const api = spawnAPI(checkerFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = checkerFiles["/src/main.ts"]; - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("add(")); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const target = await sigs[0].getTarget(); - assert.strictEqual(target, undefined, "add() is not an instantiated signature"); - } - finally { - await api.close(); - } - }); - - test("Signature.getTarget() returns the generic source signature for an instantiated call", async () => { - const src = ` - function identity(x: T): T { return x; } - identity("hello"); - `; - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - let callNode: Node | undefined; - sourceFile.forEachChild(function visit(node) { - if (isCallExpression(node)) callNode = node; - node.forEachChild(visit); - }); - assert.ok(callNode, "should find a call expression"); - const sig = await project.checker.getResolvedSignature(callNode); - assert.ok(sig, "should resolve a signature for the call"); - assert.ok(sig.target !== undefined, "instantiated call should have a target ID"); - const target = await sig.getTarget(); - assert.ok(target, "getTarget() should return the generic signature"); - assert.ok(target.typeParameters && target.typeParameters.length > 0, "target should have type parameters"); - } - finally { - await api.close(); - } - }); -}); - -describe("Symbol - parent, members, exports", () => { - const symbolFiles = { - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/mod.ts": ` -export class Animal { - name: string = ""; - speak(): void {} -} -export const value = 1; -`, - }; - - test("getMembers returns class members", async () => { - const api = spawnAPI(symbolFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = symbolFiles["/src/mod.ts"]; - const animalPos = src.indexOf("Animal"); - const symbol = await project.checker.getSymbolAtPosition("/src/mod.ts", animalPos); - assert.ok(symbol); - const members = await symbol.getMembers(); - assert.ok(members.size > 0); - const memberNames = [...members.values()].map(m => m.name); - assert.ok(memberNames.includes("name"), "should have 'name' member"); - assert.ok(memberNames.includes("speak"), "should have 'speak' member"); - } - finally { - await api.close(); - } - }); - - test("getExports returns module exports via sourceFile symbol", async () => { - const api = spawnAPI(symbolFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/mod.ts"); - assert.ok(sourceFile); - const moduleSymbol = await project.checker.getSymbolAtLocation(sourceFile); - assert.ok(moduleSymbol); - const exports = await moduleSymbol.getExports(); - assert.ok(exports.size > 0); - const exportNames = [...exports.values()].map(e => e.name); - assert.ok(exportNames.includes("Animal"), "should export Animal"); - assert.ok(exportNames.includes("value"), "should export value"); - } - finally { - await api.close(); - } - }); - - test("getParent returns containing symbol", async () => { - const api = spawnAPI(symbolFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = symbolFiles["/src/mod.ts"]; - const namePos = src.indexOf("name:"); - const nameSymbol = await project.checker.getSymbolAtPosition("/src/mod.ts", namePos); - assert.ok(nameSymbol); - assert.equal(nameSymbol.name, "name"); - const parent = await nameSymbol.getParent(); - assert.ok(parent); - assert.equal(parent.name, "Animal"); - } - finally { - await api.close(); - } - }); - - test("checkFlags is typed as CheckFlags", async () => { - const api = spawnAPI(symbolFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = await project.checker.getSymbolAtPosition("/src/mod.ts", symbolFiles["/src/mod.ts"].indexOf("Animal")); - assert.ok(symbol); - const checkFlags: CheckFlags = symbol.checkFlags; - assert.equal(checkFlags & CheckFlags.Readonly, 0); - } - finally { - await api.close(); - } - }); -}); - -describe("Type - getSymbol", () => { - test("getSymbol returns the symbol of a type", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/types.ts": ` -export class Foo { - x: number = 0; -} -export const instance: Foo = new Foo(); -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\nexport class Foo {\n x: number = 0;\n}\nexport const instance: Foo = new Foo();\n`; - const instancePos = src.indexOf("instance"); - const symbol = await project.checker.getSymbolAtPosition("/src/types.ts", instancePos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const typeSymbol = await type.getSymbol(); - assert.ok(typeSymbol); - assert.equal(typeSymbol.name, "Foo"); - } - finally { - await api.close(); - } - }); -}); - -describe("Type - sub-property fetchers", () => { - const typeFiles = { - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true, target: "esnext" } }), - "/src/types.ts": ` -export const arr: Array = [1, 2, 3]; -export const union: string | number = "hello"; -export const intersection: { a: number } & { b: string } = { a: 1, b: "hi" }; -export type KeyOf = keyof T; -export type Lookup = T[K]; -export type Cond = T extends string ? "yes" : "no"; -export const tpl: \`hello \${string}\` = "hello world"; -export type Upper = Uppercase<"hello">; -export const tuple: readonly [number, string?, ...boolean[]] = [1]; -`, - }; - - async function getTypeAtName(api: API, name: string) { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = typeFiles["/src/types.ts"]; - const pos = src.indexOf(name); - assert.ok(pos >= 0, `Could not find "${name}" in source`); - const symbol = await project.checker.getSymbolAtPosition("/src/types.ts", pos); - assert.ok(symbol, `No symbol found at "${name}"`); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type, `No type found for symbol "${name}"`); - return { type, project, snapshot, api }; - } - - test("TypeReference.getTarget() returns the generic target", async () => { - const { type, api } = await getTypeAtName(spawnAPI(typeFiles), "arr:"); - try { - assert.ok(type.flags & TypeFlags.Object); - const ref = type as TypeReference; - assert.ok(ref.objectFlags & ObjectFlags.Reference); - assert.equal(type.isObjectType(), true); - assert.equal(type.isTypeReference(), true); - assert.equal(type.isLiteralType(), false); - const target = await ref.getTarget(); - assert.ok(target); - assert.ok(target.flags & TypeFlags.Object); - const properties = await type.getProperties(); - assert.ok(properties.some(property => property.name === "length")); - assert.equal((await type.getProperty("length"))?.name, "length"); - assert.ok((await type.getApparentProperties()).length >= properties.length); - assert.strictEqual(await type.getNonNullableType(), type); - } - finally { - await api.close(); - } - }); - - test("UnionOrIntersectionType.getTypes() returns union members", async () => { - const { type, api } = await getTypeAtName(spawnAPI(typeFiles), "union:"); - try { - assert.ok(type.flags & TypeFlags.Union); - const union = type as UnionOrIntersectionType; - const types = await union.getTypes(); - assert.ok(types.length >= 2); - assert.equal(type.isUnionType(), true); - assert.equal(type.isIntersectionType(), false); - } - finally { - await api.close(); - } - }); - - test("UnionOrIntersectionType.getTypes() returns intersection members", async () => { - const { type, api } = await getTypeAtName(spawnAPI(typeFiles), "intersection:"); - try { - assert.ok(type.flags & TypeFlags.Intersection); - const inter = type as UnionOrIntersectionType; - const types = await inter.getTypes(); - assert.ok(types.length >= 2); - } - finally { - await api.close(); - } - }); - - test("UnionOrIntersectionType.getTypes() on a wrongly-cast type returns undefined without hitting the server", async () => { - const src = `export const s: string = ""; export const u: string | number = "";`; - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - - // `string` is neither a union/intersection nor a template literal type, - // so it has no constituent types. The client guards on the type's flags - // and returns undefined without ever sending a request the server cannot satisfy. - const sSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("s:")); - assert.ok(sSymbol); - const sType = await project.checker.getTypeOfSymbol(sSymbol); - assert.ok(sType); - assert.equal(await (sType as unknown as UnionOrIntersectionType).getTypes(), undefined); - - // A real union still returns its constituents. - const uSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("u:")); - assert.ok(uSymbol); - const uType = await project.checker.getTypeOfSymbol(uSymbol); - assert.ok(uType); - assert.equal((await (uType as UnionOrIntersectionType).getTypes()).length, 2); - } - finally { - await api.close(); - } - }); - - test("IndexType.getTarget() returns the target type", async () => { - const api = spawnAPI(typeFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = await project.checker.resolveName("KeyOf", SymbolFlags.TypeAlias, { document: "/src/types.ts", position: 0 }); - assert.ok(symbol); - const type = await project.checker.getDeclaredTypeOfSymbol(symbol); - assert.ok(type); - // KeyOf = keyof T — this is an IndexType - assert.ok(type.flags & TypeFlags.Index, `Expected IndexType, got flags ${type.flags}`); - const indexType = type as IndexType; - const target = await indexType.getTarget(); - assert.ok(target); - } - finally { - await api.close(); - } - }); - - test("IndexedAccessType.getObjectType() and getIndexType()", async () => { - const api = spawnAPI(typeFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = await project.checker.resolveName("Lookup", SymbolFlags.TypeAlias, { document: "/src/types.ts", position: 0 }); - assert.ok(symbol); - const type = await project.checker.getDeclaredTypeOfSymbol(symbol); - assert.ok(type); - assert.ok(type.flags & TypeFlags.IndexedAccess, `Expected IndexedAccessType, got flags ${type.flags}`); - const ia = type as IndexedAccessType; - assert.equal(type.isIndexedAccessType(), true); - const objectType = await ia.getObjectType(); - assert.ok(objectType); - const indexType = await ia.getIndexType(); - assert.ok(indexType); - } - finally { - await api.close(); - } - }); - - test("ConditionalType.getCheckType() and getExtendsType()", async () => { - const api = spawnAPI(typeFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = await project.checker.resolveName("Cond", SymbolFlags.TypeAlias, { document: "/src/types.ts", position: 0 }); - assert.ok(symbol); - const type = await project.checker.getDeclaredTypeOfSymbol(symbol); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Conditional, `Expected ConditionalType, got flags ${type.flags}`); - const cond = type as ConditionalType; - assert.equal(type.isConditionalType(), true); - const checkType = await cond.getCheckType(); - assert.ok(checkType); - const extendsType = await cond.getExtendsType(); - assert.ok(extendsType); - } - finally { - await api.close(); - } - }); - - test("ConditionalType.getTrueType() and getFalseType()", async () => { - const api = spawnAPI(typeFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = await project.checker.resolveName("Cond", SymbolFlags.TypeAlias, { document: "/src/types.ts", position: 0 }); - assert.ok(symbol); - const type = await project.checker.getDeclaredTypeOfSymbol(symbol); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Conditional, `Expected ConditionalType, got flags ${type.flags}`); - - const trueType = await (type as ConditionalType).getTrueType(); - assert.ok(trueType, "should return the true-branch type"); - assert.ok(trueType.flags & TypeFlags.StringLiteral, `Expected StringLiteral for true branch, got flags ${trueType.flags}`); - assert.equal((trueType as LiteralType).value, "yes"); - - const falseType = await (type as ConditionalType).getFalseType(); - assert.ok(falseType, "should return the false-branch type"); - assert.ok(falseType.flags & TypeFlags.StringLiteral, `Expected StringLiteral for false branch, got flags ${falseType.flags}`); - assert.equal((falseType as LiteralType).value, "no"); - } - finally { - await api.close(); - } - }); - - test("TemplateLiteralType.texts and getTypes()", async () => { - const { type, api } = await getTypeAtName(spawnAPI(typeFiles), "tpl:"); - try { - assert.ok(type.flags & TypeFlags.TemplateLiteral, `Expected TemplateLiteralType, got flags ${type.flags}`); - const tpl = type as TemplateLiteralType; - assert.ok(tpl.texts); - assert.ok(tpl.texts.length >= 2); - assert.equal(tpl.texts[0], "hello "); - const types = await tpl.getTypes(); - assert.ok(types.length >= 1); - } - finally { - await api.close(); - } - }); - - test("StringMappingType.getTarget() returns the mapped type", async () => { - const api = spawnAPI(typeFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = typeFiles["/src/types.ts"]; - const pos = src.indexOf("Upper"); - const symbol = await project.checker.getSymbolAtPosition("/src/types.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - // Uppercase<"hello"> may resolve to a StringMappingType or a string literal - if (type.flags & TypeFlags.StringMapping) { - const sm = type as StringMappingType; - const target = await sm.getTarget(); - assert.ok(target); - } - // If it resolved to "HELLO" literal, that's fine too — it means eager evaluation - } - finally { - await api.close(); - } - }); - - test("TupleType properties", async () => { - const { type, api } = await getTypeAtName(spawnAPI(typeFiles), "tuple:"); - try { - assert.ok(type.flags & TypeFlags.Object); - const ref = type as TypeReference; - assert.ok(ref.objectFlags & ObjectFlags.Reference); - const target = await ref.getTarget(); - assert.ok(target); - assert.ok(target.flags & TypeFlags.Object); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - intrinsic type getters", () => { - const intrinsicFiles = { - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const x = 1;`, - }; - - test("getAnyType returns a type with Any flag", async () => { - const api = spawnAPI(intrinsicFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const type = await project.checker.getAnyType(); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Any); - } - finally { - await api.close(); - } - }); - - test("getStringType returns a type with String flag", async () => { - const api = spawnAPI(intrinsicFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const type = await project.checker.getStringType(); - assert.ok(type); - assert.ok(type.flags & TypeFlags.String); - } - finally { - await api.close(); - } - }); - - test("getNumberType returns a type with Number flag", async () => { - const api = spawnAPI(intrinsicFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const type = await project.checker.getNumberType(); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Number); - } - finally { - await api.close(); - } - }); - - test("getBooleanType returns a type with Boolean flag", async () => { - const api = spawnAPI(intrinsicFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const type = await project.checker.getBooleanType(); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Boolean); - } - finally { - await api.close(); - } - }); - - test("getVoidType returns a type with Void flag", async () => { - const api = spawnAPI(intrinsicFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const type = await project.checker.getVoidType(); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Void); - } - finally { - await api.close(); - } - }); - - test("getUndefinedType returns a type with Undefined flag", async () => { - const api = spawnAPI(intrinsicFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const type = await project.checker.getUndefinedType(); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Undefined); - } - finally { - await api.close(); - } - }); - - test("getNullType returns a type with Null flag", async () => { - const api = spawnAPI(intrinsicFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const type = await project.checker.getNullType(); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Null); - } - finally { - await api.close(); - } - }); - - test("getNeverType returns a type with Never flag", async () => { - const api = spawnAPI(intrinsicFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const type = await project.checker.getNeverType(); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Never); - } - finally { - await api.close(); - } - }); - - test("getUnknownType returns a type with Unknown flag", async () => { - const api = spawnAPI(intrinsicFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const type = await project.checker.getUnknownType(); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Unknown); - } - finally { - await api.close(); - } - }); - - test("getBigIntType returns a type with BigInt flag", async () => { - const api = spawnAPI(intrinsicFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const type = await project.checker.getBigIntType(); - assert.ok(type); - assert.ok(type.flags & TypeFlags.BigInt); - } - finally { - await api.close(); - } - }); - - test("getESSymbolType returns a type with ESSymbol flag", async () => { - const api = spawnAPI(intrinsicFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const type = await project.checker.getESSymbolType(); - assert.ok(type); - assert.ok(type.flags & TypeFlags.ESSymbol); - } - finally { - await api.close(); - } - }); - - test("getNonPrimitiveType returns a type with NonPrimitive flag", async () => { - const api = spawnAPI(intrinsicFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const type = await project.checker.getNonPrimitiveType(); - assert.ok(type); - assert.ok(type.flags & TypeFlags.NonPrimitive); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - multi-project type ID uniqueness", () => { - test("intrinsic types from 3 projects in the same snapshot have non-colliding IDs", async () => { - const api = spawnAPI({ - "/proj1/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/proj1/src/index.ts": `export const x = 1;`, - "/proj2/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/proj2/src/index.ts": `export const y = "hello";`, - "/proj3/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/proj3/src/index.ts": `export const z = true;`, - }); - try { - // Open all 3 projects — each updateSnapshot accumulates open projects - await api.updateSnapshot({ openProject: "/proj1/tsconfig.json" }); - await api.updateSnapshot({ openProject: "/proj2/tsconfig.json" }); - const snapshot = await api.updateSnapshot({ openProject: "/proj3/tsconfig.json" }); - - const proj1 = snapshot.getProject("/proj1/tsconfig.json")!; - const proj2 = snapshot.getProject("/proj2/tsconfig.json")!; - const proj3 = snapshot.getProject("/proj3/tsconfig.json")!; - assert.ok(proj1, "proj1 should be in final snapshot"); - assert.ok(proj2, "proj2 should be in final snapshot"); - assert.ok(proj3, "proj3 should be in final snapshot"); - - // Fetch several intrinsic types from each checker. - // If type IDs collide across checkers, registerType panics → API error. - const num1 = await proj1.checker.getNumberType(); - const str1 = await proj1.checker.getStringType(); - const bool1 = await proj1.checker.getBooleanType(); - const any1 = await proj1.checker.getAnyType(); - const num2 = await proj2.checker.getNumberType(); - const str2 = await proj2.checker.getStringType(); - const bool2 = await proj2.checker.getBooleanType(); - const any2 = await proj2.checker.getAnyType(); - const num3 = await proj3.checker.getNumberType(); - const str3 = await proj3.checker.getStringType(); - const bool3 = await proj3.checker.getBooleanType(); - const any3 = await proj3.checker.getAnyType(); - - assert.ok(num1.flags & TypeFlags.Number, "proj1 number type"); - assert.ok(str1.flags & TypeFlags.String, "proj1 string type"); - assert.ok(bool1.flags & TypeFlags.Boolean, "proj1 boolean type"); - assert.ok(any1.flags & TypeFlags.Any, "proj1 any type"); - - assert.ok(num2.flags & TypeFlags.Number, "proj2 number type"); - assert.ok(str2.flags & TypeFlags.String, "proj2 string type"); - assert.ok(bool2.flags & TypeFlags.Boolean, "proj2 boolean type"); - assert.ok(any2.flags & TypeFlags.Any, "proj2 any type"); - - assert.ok(num3.flags & TypeFlags.Number, "proj3 number type"); - assert.ok(str3.flags & TypeFlags.String, "proj3 string type"); - assert.ok(bool3.flags & TypeFlags.Boolean, "proj3 boolean type"); - assert.ok(any3.flags & TypeFlags.Any, "proj3 any type"); - } - finally { - await api.close(); - } - }); - - test("symbol and signature handles from 3 projects in the same snapshot have non-colliding IDs", async () => { - const api = spawnAPI({ - "/proj1/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/proj1/src/index.ts": `export function add(a: number, b: number): number { return a + b; }`, - "/proj2/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/proj2/src/index.ts": `export function greet(name: string): string { return "hello " + name; }`, - "/proj3/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/proj3/src/index.ts": `export function toggle(b: boolean): boolean { return !b; }`, - }); - try { - await api.updateSnapshot({ openProject: "/proj1/tsconfig.json" }); - await api.updateSnapshot({ openProject: "/proj2/tsconfig.json" }); - const snapshot = await api.updateSnapshot({ openProject: "/proj3/tsconfig.json" }); - - const proj1 = snapshot.getProject("/proj1/tsconfig.json")!; - const proj2 = snapshot.getProject("/proj2/tsconfig.json")!; - const proj3 = snapshot.getProject("/proj3/tsconfig.json")!; - - // Get a symbol from each project (exercises symbol registry) - const src1 = `export function add(a: number, b: number): number { return a + b; }`; - const src2 = `export function greet(name: string): string { return "hello " + name; }`; - const src3 = `export function toggle(b: boolean): boolean { return !b; }`; - - const sym1 = await proj1.checker.getSymbolAtPosition("/proj1/src/index.ts", src1.indexOf("add")); - const sym2 = await proj2.checker.getSymbolAtPosition("/proj2/src/index.ts", src2.indexOf("greet")); - const sym3 = await proj3.checker.getSymbolAtPosition("/proj3/src/index.ts", src3.indexOf("toggle")); - assert.ok(sym1, "proj1 symbol"); - assert.ok(sym2, "proj2 symbol"); - assert.ok(sym3, "proj3 symbol"); - assert.equal(sym1.name, "add", "proj1 symbol name"); - assert.equal(sym2.name, "greet", "proj2 symbol name"); - assert.equal(sym3.name, "toggle", "proj3 symbol name"); - - // Get type of each symbol, then signatures (exercises type + signature registries) - const type1 = await proj1.checker.getTypeOfSymbol(sym1); - const type2 = await proj2.checker.getTypeOfSymbol(sym2); - const type3 = await proj3.checker.getTypeOfSymbol(sym3); - assert.ok(type1, "proj1 function type"); - assert.ok(type2, "proj2 function type"); - assert.ok(type3, "proj3 function type"); - - const sigs1 = await proj1.checker.getSignaturesOfType(type1, SignatureKind.Call); - const sigs2 = await proj2.checker.getSignaturesOfType(type2, SignatureKind.Call); - const sigs3 = await proj3.checker.getSignaturesOfType(type3, SignatureKind.Call); - assert.equal(sigs1.length, 1, "proj1 has 1 call signature"); - assert.equal(sigs2.length, 1, "proj2 has 1 call signature"); - assert.equal(sigs3.length, 1, "proj3 has 1 call signature"); - assert.equal(sigs1[0].parameters.length, 2, "proj1 add() has 2 params"); - assert.equal(sigs2[0].parameters.length, 1, "proj2 greet() has 1 param"); - assert.equal(sigs3[0].parameters.length, 1, "proj3 toggle() has 1 param"); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getBaseTypeOfLiteralType", () => { - test("number literal widens to number", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const x = 42;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export const x = 42;`; - const pos = src.indexOf("x ="); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const literalType = await project.checker.getTypeOfSymbol(symbol); - assert.ok(literalType); - assert.ok(literalType.flags & TypeFlags.NumberLiteral); - const baseType = await project.checker.getBaseTypeOfLiteralType(literalType); - assert.ok(baseType); - assert.ok(baseType.flags & TypeFlags.Number); - } - finally { - await api.close(); - } - }); - - test("string literal widens to string", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const s = "hello";`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export const s = "hello";`; - const pos = src.indexOf("s "); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const literalType = await project.checker.getTypeOfSymbol(symbol); - assert.ok(literalType); - assert.ok(literalType.flags & TypeFlags.StringLiteral); - const baseType = await project.checker.getBaseTypeOfLiteralType(literalType); - assert.ok(baseType); - assert.ok(baseType.flags & TypeFlags.String); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getContextualType", () => { - test("contextual type from function parameter", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -function foo(x: number) {} -foo(42); -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - - const sourceFile = await project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - - // Find the argument "42" in foo(42) - // statement[1] = foo(42); which is an ExpressionStatement -> CallExpression - const callStmt = sourceFile.statements[1]; - assert.ok(callStmt); - let numLiteral: import("@typescript/typescript/unstable/ast").Expression | undefined; - callStmt.forEachChild(function visit(node) { - if (isCallExpression(node)) { - // First argument - numLiteral = node.arguments[0]; - } - node.forEachChild(visit); - }); - assert.ok(numLiteral); - const contextualType = await project.checker.getContextualType(numLiteral); - assert.ok(contextualType); - assert.ok(contextualType.flags & TypeFlags.Number); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getTypeOfSymbolAtLocation", () => { - test("narrowed type via typeof check", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export function check(x: string | number) { - if (typeof x === "string") { - return x; - } - return x; -} -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\nexport function check(x: string | number) {\n if (typeof x === "string") {\n return x;\n }\n return x;\n}\n`; - - // Get the symbol for parameter "x" - const paramPos = src.indexOf("x:"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", paramPos); - assert.ok(symbol); - assert.equal(symbol.name, "x"); - - // Get the type of "x" at the wider function scope — should be string | number - const wideType = await project.checker.getTypeOfSymbol(symbol); - assert.ok(wideType); - assert.ok(wideType.flags & TypeFlags.Union, `Expected union type, got flags ${wideType.flags}`); - - // Get the narrowed return x inside the if block - const sourceFile = await project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - - // statement[0] is the function declaration - const funcDecl = sourceFile.statements[0]; - assert.ok(funcDecl); - // Walk to find the first "return x" — inside the if, x should be narrowed to string - let firstReturnX: import("@typescript/typescript/unstable/ast").Node | undefined; - funcDecl.forEachChild(function visit(node) { - if (isReturnStatement(node) && !firstReturnX) { - // The expression of the return statement is the identifier "x" - if (node.expression) { - firstReturnX = node.expression; - } - } - node.forEachChild(visit); - }); - assert.ok(firstReturnX); - const narrowedType = await project.checker.getTypeOfSymbolAtLocation(symbol, firstReturnX); - assert.ok(narrowedType); - // Inside the if (typeof x === "string") branch, x should be narrowed to string - assert.ok(narrowedType.flags & TypeFlags.String, `Expected string type, got flags ${narrowedType.flags}`); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getShorthandAssignmentValueSymbol", () => { - test("shorthand property symbol resolves to variable", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -const name = "Alice"; -export const obj = { name }; -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - - const sourceFile = await project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - - // Find the shorthand property assignment { name } - // statement[1] = export const obj = { name }; - let shorthandNode: import("@typescript/typescript/unstable/ast").Node | undefined; - sourceFile.forEachChild(function visit(node) { - if (isShorthandPropertyAssignment(node)) { - shorthandNode = node; - } - node.forEachChild(visit); - }); - assert.ok(shorthandNode, "Should find a shorthand property assignment"); - const valueSymbol = await project.checker.getShorthandAssignmentValueSymbol(shorthandNode); - assert.ok(valueSymbol); - assert.equal(valueSymbol.name, "name"); - } - finally { - await api.close(); - } - }); -}); - -describe("readFile callback semantics", () => { - test("readFile: string returns content, null blocks fallback, undefined falls through to real FS", async () => { - const virtualFiles: Record = { - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/index.ts": `export const x: number = 1;`, - }; - const vfs = createVirtualFileSystem(virtualFiles); - const blockedPath = "/src/blocked.ts"; - - const fs: FileSystem = { - ...vfs, - readFile: (fileName: string) => { - if (fileName === blockedPath) { - // null = file not found, don't fall back to real FS - return null; - } - // Try the VFS first; if it has the file, return its content (string). - // Otherwise return undefined to fall through to the real FS. - return vfs.readFile!(fileName); - }, - }; - - const api = new API({ - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), - fs, - }); - - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - - // 1. String content: virtual file is found - const sf = await project.program.getSourceFile("/src/index.ts"); - assert.ok(sf, "Virtual file should be found"); - assert.equal(sf.text, virtualFiles["/src/index.ts"]); - - // 2. undefined fallback: lib files from the real FS should be present. - // If readFile returned null for unknowns, lib files would be missing - // and `number` would not resolve — this was the original async bug. - // Verify by checking that `number` resolves to a proper type (not error). - const pos = virtualFiles["/src/index.ts"].indexOf("x:"); - const type = await project.checker.getTypeAtPosition("/src/index.ts", pos); - assert.ok(type, "Type should resolve"); - assert.ok(type.flags & TypeFlags.Number, `Expected number type, got flags ${type.flags}`); - - // 3. null blocks fallback: blocked file should not be found - const blockedSf = await project.program.getSourceFile(blockedPath); - assert.equal(blockedSf, undefined, "Blocked file should not be found (null prevents fallback)"); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - isArrayType / isTupleType", () => { - test("number[] is array, not tuple", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const xs: number[] = [];`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export const xs: number[] = [];`; - const pos = src.indexOf("xs"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.equal(await project.checker.isArrayType(type), true); - assert.equal(await project.checker.isTupleType(type), false); - } - finally { - await api.close(); - } - }); - - test("readonly number[] is array", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const xs: readonly number[] = [];`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export const xs: readonly number[] = [];`; - const pos = src.indexOf("xs"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.equal(await project.checker.isArrayType(type), true); - assert.equal(await project.checker.isTupleType(type), false); - } - finally { - await api.close(); - } - }); - - test("Array is array, not tuple", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const xs: Array = [];`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export const xs: Array = [];`; - const pos = src.indexOf("xs"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.equal(await project.checker.isArrayType(type), true); - assert.equal(await project.checker.isTupleType(type), false); - } - finally { - await api.close(); - } - }); - - test("[number, string] is tuple, not array", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const tup: [number, string] = [1, "a"];`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export const tup: [number, string] = [1, "a"];`; - const pos = src.indexOf("tup"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.equal(await project.checker.isArrayType(type), false); - assert.equal(await project.checker.isTupleType(type), true); - } - finally { - await api.close(); - } - }); - - test("readonly [number, string] is tuple, not array", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const tup: readonly [number, string] = [1, "a"];`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export const tup: readonly [number, string] = [1, "a"];`; - const pos = src.indexOf("tup"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.equal(await project.checker.isArrayType(type), false); - assert.equal(await project.checker.isTupleType(type), true); - } - finally { - await api.close(); - } - }); - - test("string is neither array nor tuple", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const str: string = "";`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export const str: string = "";`; - const pos = src.indexOf("str"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.equal(await project.checker.isArrayType(type), false); - assert.equal(await project.checker.isTupleType(type), false); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - isReadonlySymbol", () => { - test("properties with a 'readonly' modifier", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export interface User { - readonly name: string; - age: number; -} - -export type ReadonlyUser = Readonly; -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - const user = sourceFile.statements.find(isInterfaceDeclaration); - assert.ok(user); - const userProperties = await project.checker.getPropertiesOfType( - await project.checker.getTypeAtLocation(user), - ); - assert.equal(await project.checker.isReadonlySymbol(userProperties[0]), true); - assert.equal(await project.checker.isReadonlySymbol(userProperties[1]), false); - const readonlyUser = sourceFile.statements.find(isTypeAliasDeclaration); - assert.ok(readonlyUser); - const readonlyUserProperties = await project.checker.getPropertiesOfType( - await project.checker.getTypeAtLocation(readonlyUser), - ); - assert.equal(await project.checker.isReadonlySymbol(readonlyUserProperties[0]), true); - assert.equal(await project.checker.isReadonlySymbol(readonlyUserProperties[1]), true); - } - finally { - await api.close(); - } - }); - - test("variables declared with 'const'", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const a = 1; export let b = 2;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const { checker } = snapshot.getProject("/tsconfig.json")!; - const a = await checker.getSymbolAtPosition("/src/main.ts", "export const ".length); - const b = await checker.getSymbolAtPosition("/src/main.ts", "export const a = 1; export let ".length); - assert.ok(a); - assert.ok(b); - assert.equal(await checker.isReadonlySymbol(a), true); - assert.equal(await checker.isReadonlySymbol(b), false); - } - finally { - await api.close(); - } - }); - - test("get accessors without matching set accessors", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -class Alpha { - private _value!: number; - get value(): number { - return this._value; - } -} -class Bravo { - private _value!: number; - get value(): number { - return this._value; - } - set value(newValue: number) { - this._value = newValue; - } -} -export type A = InstanceType; -export type B = InstanceType; -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - const typeAliases = sourceFile.statements.filter(isTypeAliasDeclaration); - assert.equal(typeAliases.length, 2); - const aProperties = await project.checker.getPropertiesOfType( - await project.checker.getTypeAtLocation(typeAliases[0]), - ); - assert.equal(await project.checker.isReadonlySymbol(aProperties[1]), true); - const bProperties = await project.checker.getPropertiesOfType( - await project.checker.getTypeAtLocation(typeAliases[1]), - ); - assert.equal(await project.checker.isReadonlySymbol(bProperties[1]), false); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getReturnTypeOfSignature", () => { - test("returns the return type of a function signature", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export function add(a: number, b: number): number { return a + b; }`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export function add(a: number, b: number): number { return a + b; }`; - const pos = src.indexOf("add("); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const returnType = await project.checker.getReturnTypeOfSignature(sigs[0]); - assert.ok(returnType); - assert.ok(returnType.flags & TypeFlags.Number, `Expected number, got flags ${returnType.flags}`); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getRestTypeOfSignature", () => { - test("returns the rest type of a signature with rest parameter", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export function sum(...nums: number[]): number { return nums.reduce((a, b) => a + b, 0); }`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export function sum(...nums: number[]): number { return nums.reduce((a, b) => a + b, 0); }`; - const pos = src.indexOf("sum("); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const restType = await project.checker.getRestTypeOfSignature(sigs[0]); - assert.ok(restType); - assert.ok(restType.flags & TypeFlags.Number, `Expected number type, got flags ${restType.flags}`); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getTypePredicateOfSignature", () => { - test("returns type predicate for 'x is T' guard", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export function isString(x: unknown): x is string { return typeof x === "string"; }`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export function isString(x: unknown): x is string { return typeof x === "string"; }`; - const pos = src.indexOf("isString("); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const predicate = await project.checker.getTypePredicateOfSignature(sigs[0]); - assert.ok(predicate); - assert.equal(predicate.kind, TypePredicateKind.Identifier); - assert.equal(predicate.parameterName, "x"); - assert.equal(predicate.parameterIndex, 0); - assert.ok(predicate.type); - assert.ok(predicate.type.flags & TypeFlags.String); - } - finally { - await api.close(); - } - }); - - test("returns type predicate for 'this is T' guard", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export class Animal { - isdog(): this is Dog { return this instanceof Dog; } -} -export class Dog extends Animal { - bark() {} -} -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\nexport class Animal {\n isdog(): this is Dog { return this instanceof Dog; }\n}\nexport class Dog extends Animal {\n bark() {}\n}\n`; - const pos = src.indexOf("isdog("); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const predicate = await project.checker.getTypePredicateOfSignature(sigs[0]); - assert.ok(predicate); - assert.equal(predicate.kind, TypePredicateKind.This); - } - finally { - await api.close(); - } - }); - - test("returns type predicate for 'asserts x is T'", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export function assertIsString(x: unknown): asserts x is string { if (typeof x !== "string") throw new Error(); }`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export function assertIsString(x: unknown): asserts x is string { if (typeof x !== "string") throw new Error(); }`; - const pos = src.indexOf("assertIsString("); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const predicate = await project.checker.getTypePredicateOfSignature(sigs[0]); - assert.ok(predicate); - assert.equal(predicate.kind, TypePredicateKind.AssertsIdentifier); - assert.equal(predicate.parameterName, "x"); - assert.equal(predicate.parameterIndex, 0); - assert.ok(predicate.type); - assert.ok(predicate.type.flags & TypeFlags.String); - } - finally { - await api.close(); - } - }); - - test("returns undefined for signature without type predicate", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export function add(a: number, b: number): number { return a + b; }`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export function add(a: number, b: number): number { return a + b; }`; - const pos = src.indexOf("add("); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const predicate = await project.checker.getTypePredicateOfSignature(sigs[0]); - assert.equal(predicate, undefined); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getBaseTypes", () => { - test("returns base types of a class", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export class Base { - x: number = 0; -} -export class Derived extends Base { - y: string = ""; -} -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\nexport class Base {\n x: number = 0;\n}\nexport class Derived extends Base {\n y: string = "";\n}\n`; - const pos = src.indexOf("Derived"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getDeclaredTypeOfSymbol(symbol); - assert.ok(type); - const baseTypes = await project.checker.getBaseTypes(type as InterfaceType); - assert.ok(baseTypes.length > 0, "Should have at least one base type"); - const baseSymbol = await baseTypes[0].getSymbol(); - assert.ok(baseSymbol); - assert.equal(baseSymbol.name, "Base"); - } - finally { - await api.close(); - } - }); - - test("returns base types of an interface", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export interface Animal { - name: string; -} -export interface Dog extends Animal { - bark(): void; -} -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\nexport interface Animal {\n name: string;\n}\nexport interface Dog extends Animal {\n bark(): void;\n}\n`; - const pos = src.indexOf("Dog"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getDeclaredTypeOfSymbol(symbol); - assert.ok(type); - const baseTypes = await project.checker.getBaseTypes(type as InterfaceType); - assert.ok(baseTypes.length > 0, "Should have at least one base type"); - const baseSymbol = await baseTypes[0].getSymbol(); - assert.ok(baseSymbol); - assert.equal(baseSymbol.name, "Animal"); - } - finally { - await api.close(); - } - }); - - test("does not panic for a type alias to a generic interface instantiation", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export interface Box { - value: T; -} -export type BoxOfString = Box; -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - const typeAlias = sourceFile.statements.find(isTypeAliasDeclaration); - assert.ok(typeAlias); - const type = await project.checker.getTypeAtLocation(typeAlias); - assert.ok(type); - // A generic interface instantiation produces a type reference, not an - // interface type, so it has no base types and yields []. - const baseTypes = await project.checker.getBaseTypes(type as InterfaceType); - assert.deepEqual(baseTypes, []); - } - finally { - await api.close(); - } - }); -}); - -describe("Type - getBaseTypes", () => { - test("returns base types for a class type and undefined for a non-class/interface", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export class Base { - x: number = 0; -} -export class Derived extends Base { - y: string = ""; -} -export const n: number = 0; -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\nexport class Base {\n x: number = 0;\n}\nexport class Derived extends Base {\n y: string = "";\n}\nexport const n: number = 0;\n`; - - const derivedSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("Derived")); - assert.ok(derivedSymbol); - const derivedType = await project.checker.getDeclaredTypeOfSymbol(derivedSymbol); - assert.ok(derivedType.isClassOrInterface(), "Derived should be a class/interface type"); - const baseTypes = await derivedType.getBaseTypes(); - assert.ok(baseTypes && baseTypes.length > 0, "class type should have base types"); - const baseSymbol = await baseTypes![0].getSymbol(); - assert.equal(baseSymbol?.name, "Base"); - - // A primitive type is not a class/interface, so getBaseTypes() is undefined. - const numberSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("n:")); - assert.ok(numberSymbol); - const numberType = await project.checker.getTypeOfSymbol(numberSymbol); - assert.ok(numberType); - assert.equal(numberType.isClassOrInterface(), false); - assert.equal(await numberType.getBaseTypes(), undefined); - } - finally { - await api.close(); - } - }); -}); - -describe("Type - isErrorType", () => { - test("identifies the error type from an unresolvable annotation", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -declare const good: string; -declare const bad: ThisTypeDoesNotExist; -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\ndeclare const good: string;\ndeclare const bad: ThisTypeDoesNotExist;\n`; - - const badSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("bad")); - assert.ok(badSymbol); - const badType = await project.checker.getTypeOfSymbol(badSymbol); - assert.ok(badType); - assert.ok(badType.isErrorType(), "unresolved annotation should yield the error type"); - assert.ok(isErrorType(badType)); - - const goodSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("good")); - assert.ok(goodSymbol); - const goodType = await project.checker.getTypeOfSymbol(goodSymbol); - assert.ok(goodType); - assert.equal(goodType.isErrorType(), false, "string type is not the error type"); - assert.equal(isErrorType(goodType), false); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - well-known symbols", () => { - test("isUnknownSymbol identifies the aliased unknown symbol", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export const value = 1; -export type Alias = typeof value; -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\nexport const value = 1;\nexport type Alias = typeof value;\n`; - - // A real symbol is not the unknown/undefined symbol. - const valueSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("value")); - assert.ok(valueSymbol); - assert.equal(await project.checker.isUnknownSymbol(valueSymbol), false); - assert.equal(await project.checker.isUndefinedSymbol(valueSymbol), false); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - well-known signatures", () => { - test("isUnknownSignature identifies an unresolvable call", async () => { - const src = ` -const ok = (x: number) => x; -ok(1); -const notCallable = 1; -notCallable(); -`; - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - const calls: Node[] = []; - sourceFile.forEachChild(function visit(node) { - if (isCallExpression(node)) calls.push(node); - node.forEachChild(visit); - }); - assert.equal(calls.length, 2, "should find two call expressions"); - - // The valid call resolves to a real signature, not the unknown signature. - const okSig = await project.checker.getResolvedSignature(calls[0]); - assert.equal(await project.checker.isUnknownSignature(okSig), false); - - // The call to a non-callable value yields the unknown signature. - const badSig = await project.checker.getResolvedSignature(calls[1]); - assert.equal(await project.checker.isUnknownSignature(badSig), true); - } - finally { - await api.close(); - } - }); -}); - -describe("Symbol - escaped names and tables", () => { - test("getExports/getMembers return a cached Map keyed by escaped name", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export class Animal { - name: string = ""; - speak(): void {} -} -export const value = 1; -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - const moduleSymbol = await project.checker.getSymbolAtLocation(sourceFile); - assert.ok(moduleSymbol); - - const exports = await moduleSymbol.getExports(); - assert.ok(exports.has(escapeLeadingUnderscores("Animal"))); - assert.ok(exports.get(escapeLeadingUnderscores("value"))); - // Calling again returns the same cached Map instance. - assert.strictEqual(await moduleSymbol.getExports(), exports); - - const animal = exports.get(escapeLeadingUnderscores("Animal"))!; - const members = await animal.getMembers(); - assert.ok(members.get(escapeLeadingUnderscores("name"))); - assert.ok(members.get(escapeLeadingUnderscores("speak"))); - assert.strictEqual(await animal.getMembers(), members); - } - finally { - await api.close(); - } - }); - - test("anonymous type symbol has a __-escaped name, never the \\xFE sentinel", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export const obj: { a: number } = { a: 1 }; -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\nexport const obj: { a: number } = { a: 1 };\n`; - const objSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("obj")); - assert.ok(objSymbol); - const objType = await project.checker.getTypeOfSymbol(objSymbol); - assert.ok(objType); - const typeSymbol = await objType.getSymbol(); - assert.ok(typeSymbol); - // The anonymous type literal symbol is internally named; over the wire - // it must be the "__type" escaped form, not the raw "\xFE" sentinel. - assert.equal(typeSymbol.escapedName, InternalSymbolName.Type); - assert.ok(!typeSymbol.escapedName.includes("\xFE")); - } - finally { - await api.close(); - } - }); -}); - -describe("ast - escapeLeadingUnderscores", () => { - test("round-trips display and escaped names", () => { - assert.equal(escapeLeadingUnderscores("foo"), "foo"); - assert.equal(escapeLeadingUnderscores("_foo"), "_foo"); - assert.equal(escapeLeadingUnderscores("__foo"), "___foo"); - assert.equal(unescapeLeadingUnderscores("foo" as __String), "foo"); - assert.equal(unescapeLeadingUnderscores("__type" as __String), "__type"); - assert.equal(unescapeLeadingUnderscores("___foo" as __String), "__foo"); - }); -}); - -describe("ast - getJSDocTags", () => { - test("returns a node's own tags, and inherited @param / @template tags", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -/** - * Adds two numbers. - * @param a the first number - * @param b the second number - * @returns the sum - */ -export function add(a: number, b: number): number { - return a + b; -} - -/** - * @template T the element type - * @param x a value - */ -export function identity(x: T): T { - return x; -} - -/** @deprecated use add */ -export const total = add(1, 2); -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - const functions = [...sourceFile.statements].filter(isFunctionDeclaration); - const add = functions[0]; - const identity = functions[1]; - assert.ok(add); - assert.ok(identity); - - // A function reports its own JSDoc tags. - assert.deepEqual(getJSDocTags(add).map(t => t.tagName.text), ["param", "param", "returns"]); - - // A parameter inherits the matching @param tag from its containing - // signature. - const paramA = add.parameters[0]; - const paramATags = getJSDocTags(paramA); - assert.deepEqual(paramATags.map(t => t.tagName.text), ["param"]); - const paramATag = paramATags[0]; - assert.ok(isJSDocParameterTag(paramATag)); - assert.ok(isIdentifier(paramATag.name)); - assert.equal(paramATag.name.text, "a"); - - const paramB = add.parameters[1]; - const paramBTags = getJSDocTags(paramB); - assert.deepEqual(paramBTags.map(t => t.tagName.text), ["param"]); - const paramBTag = paramBTags[0]; - assert.ok(isJSDocParameterTag(paramBTag)); - assert.ok(isIdentifier(paramBTag.name)); - assert.equal(paramBTag.name.text, "b"); - - // A type parameter inherits the matching @template tag. - const typeParam = identity.typeParameters![0]; - assert.deepEqual(getJSDocTags(typeParam).map(t => t.tagName.text), ["template"]); - - // The value parameter of `identity` inherits its @param tag. - const identityParam = identity.parameters[0]; - assert.deepEqual(getJSDocTags(identityParam).map(t => t.tagName.text), ["param"]); - - // A variable declaration walks up its comment-location chain - // (declaration -> declaration list -> statement) to the JSDoc on the - // containing variable statement. - const variable = sourceFile.statements.find(isVariableStatement); - assert.ok(variable); - const declaration = variable.declarationList.declarations[0]; - assert.deepEqual(getJSDocTags(declaration).map(t => t.tagName.text), ["deprecated"]); - } - finally { - await api.close(); - } - }); - - test("a function-expression parameter inherits @param tags from the variable statement", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { allowJs: true, checkJs: true } }), - "/src/main.js": ` -/** - * @param {string} name a name - * @returns {number} the length - */ -var measure = function (name) { - return name.length; -}; -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/main.js"); - assert.ok(sourceFile); - const variable = sourceFile.statements.find(isVariableStatement); - assert.ok(variable); - const declaration = variable.declarationList.declarations[0]; - const funcExpr = declaration.initializer; - assert.ok(funcExpr); - - // The variable statement carries the @param and @returns tags. - assert.deepEqual(getJSDocTags(declaration).map(t => t.tagName.text), ["param", "returns"]); - - // The function expression's parameter walks declaration -> list -> - // statement and inherits the matching @param tag. - const param = (funcExpr as unknown as { parameters: NodeArray; }).parameters[0]; - const paramTags = getJSDocTags(param); - assert.deepEqual(paramTags.map(t => t.tagName.text), ["param"]); - const paramTag = paramTags[0]; - assert.ok(isJSDocParameterTag(paramTag)); - assert.ok(isIdentifier(paramTag.name)); - assert.equal(paramTag.name.text, "name"); - } - finally { - await api.close(); - } - }); - - test("a @type cast tag is owned by its parenthesized expression, not the declaration", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { allowJs: true, checkJs: true } }), - "/src/main.js": ` -/** @type {string} */ -const value = "hello"; - -const cast = /** @type {number} */ (someValue); -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/main.js"); - assert.ok(sourceFile); - const statements = [...sourceFile.statements].filter(isVariableStatement); - - // A @type tag directly on a declaration is reported for that declaration. - const valueDecl = statements[0].declarationList.declarations[0]; - assert.deepEqual(getJSDocTags(valueDecl).map(t => t.tagName.text), ["type"]); - - // A @type cast tag attached to a parenthesized expression is owned by - // that expression and is not reported for the enclosing declaration. - const castDecl = statements[1].declarationList.declarations[0]; - assert.deepEqual(getJSDocTags(castDecl).map(t => t.tagName.text), []); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getPropertiesOfType", () => { - test("returns properties of an object type", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export interface Person { - name: string; - age: number; - greet(): void; -} -export declare const p: Person; -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\nexport interface Person {\n name: string;\n age: number;\n greet(): void;\n}\nexport declare const p: Person;\n`; - const pos = src.indexOf("p: Person"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const props = await project.checker.getPropertiesOfType(type); - assert.ok(props.length >= 3, `Expected at least 3 properties, got ${props.length}`); - const names = props.map(p => p.name); - assert.ok(names.includes("name"), "should have 'name' property"); - assert.ok(names.includes("age"), "should have 'age' property"); - assert.ok(names.includes("greet"), "should have 'greet' property"); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getIndexInfosOfType", () => { - test("returns index signatures of an indexed type", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export interface StringMap { - [key: string]: number; -} -export declare const m: StringMap; -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\nexport interface StringMap {\n [key: string]: number;\n}\nexport declare const m: StringMap;\n`; - const pos = src.indexOf("m: StringMap"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const indexInfos = await project.checker.getIndexInfosOfType(type); - assert.ok(indexInfos.length > 0, "Should have at least one index info"); - const info = indexInfos[0]; - assert.ok(info.keyType); - assert.ok(info.keyType.flags & TypeFlags.String, `Expected string key type, got flags ${info.keyType.flags}`); - assert.ok(info.valueType); - assert.ok(info.valueType.flags & TypeFlags.Number, `Expected number value type, got flags ${info.valueType.flags}`); - assert.equal(info.isReadonly, false); - } - finally { - await api.close(); - } - }); - - test("readonly index signature reports isReadonly true", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export interface ReadonlyMap { - readonly [key: string]: number; -} -export declare const m: ReadonlyMap; -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\nexport interface ReadonlyMap {\n readonly [key: string]: number;\n}\nexport declare const m: ReadonlyMap;\n`; - const pos = src.indexOf("m: ReadonlyMap"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const indexInfos = await project.checker.getIndexInfosOfType(type); - assert.ok(indexInfos.length > 0); - assert.equal(indexInfos[0].isReadonly, true); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getConstraintOfTypeParameter", () => { - test("returns constraint of a type parameter", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export function identity(x: T): T { return x; }`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export function identity(x: T): T { return x; }`; - const pos = src.indexOf("identity<"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - assert.ok(sigs[0].typeParameters && sigs[0].typeParameters.length > 0, "Should have type parameters"); - const typeParams = await sigs[0].getTypeParameters(); - const constraint = await project.checker.getConstraintOfTypeParameter(typeParams[0]); - assert.ok(constraint); - assert.ok(constraint.flags & TypeFlags.String, `Expected string constraint, got flags ${constraint.flags}`); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - TypeParameter getters", () => { - test("getConstraint() and getDefault() return the constraint and default types", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export function f(x: T): T { return x; }`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export function f(x: T): T { return x; }`; - const pos = src.indexOf("f<"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const typeParams = await sigs[0].getTypeParameters(); - assert.ok(typeParams.length > 0, "Should have type parameters"); - const typeParam = typeParams[0] as TypeParameter; - - const constraint = await typeParam.getConstraint(); - assert.ok(constraint); - assert.ok(constraint.flags & TypeFlags.String, `Expected string constraint, got flags ${constraint.flags}`); - - const defaultType = await typeParam.getDefault(); - assert.ok(defaultType); - assert.ok(defaultType.flags & TypeFlags.StringLiteral, `Expected string literal default, got flags ${defaultType.flags}`); - } - finally { - await api.close(); - } - }); - - test("getConstraint() and getDefault() return undefined when there is none", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export function f(x: T): T { return x; }`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export function f(x: T): T { return x; }`; - const pos = src.indexOf("f<"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const typeParams = await sigs[0].getTypeParameters(); - assert.ok(typeParams.length > 0, "Should have type parameters"); - const typeParam = typeParams[0] as TypeParameter; - - assert.equal(await typeParam.getConstraint(), undefined); - assert.equal(await typeParam.getDefault(), undefined); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getTypeArguments", () => { - test("returns type arguments of a generic instantiation", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const arr: Array = [1, 2, 3];`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export const arr: Array = [1, 2, 3];`; - const pos = src.indexOf("arr:"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const typeArgs = await project.checker.getTypeArguments(type as TypeReference); - assert.ok(typeArgs.length > 0, "Should have type arguments"); - assert.ok(typeArgs[0].flags & TypeFlags.Number, `Expected number type argument, got flags ${typeArgs[0].flags}`); - } - finally { - await api.close(); - } - }); - - test("a wrongly-typed call throws on the client without taking down the server", async () => { - const src = `export const s: string = ""; export const arr: Array = [1];`; - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - - // `string` is not a type reference. When getTypeArguments is reached - // with one, the server panics, but the per-request panic recovery - // converts that into an error response rather than crashing the process. - const sSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("s:")); - assert.ok(sSymbol); - const sType = await project.checker.getTypeOfSymbol(sSymbol); - assert.ok(sType); - await assert.rejects(() => project.checker.getTypeArguments(sType as unknown as TypeReference)); // @sync: assert.throws(() => project.checker.getTypeArguments(sType as unknown as TypeReference)); - - // The server survived: a subsequent valid request still succeeds. - const arrSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("arr:")); - assert.ok(arrSymbol); - const arrType = await project.checker.getTypeOfSymbol(arrSymbol); - assert.ok(arrType); - const typeArgs = await project.checker.getTypeArguments(arrType as TypeReference); - assert.ok(typeArgs.length > 0, "Server should still serve valid requests"); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getBaseConstraintOfType", () => { - test("returns the base constraint of a type parameter", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export function identity(x: T): T { return x; }`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export function identity(x: T): T { return x; }`; - const pos = src.indexOf("identity<"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const typeParams = await sigs[0].getTypeParameters(); - const constraint = await project.checker.getBaseConstraintOfType(typeParams[0]); - assert.ok(constraint, "Should resolve a base constraint"); - assert.ok(constraint.flags & TypeFlags.String, `Expected string constraint, got flags ${constraint.flags}`); - } - finally { - await api.close(); - } - }); - - test("returns undefined for a non-instantiable type", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const x: number = 1;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = `export const x: number = 1;`.indexOf("x:"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const constraint = await project.checker.getBaseConstraintOfType(type); - assert.equal(constraint, undefined); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getPropertyOfType", () => { - test("returns a named property symbol of a type", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export interface Person { - name: string; - age: number; -} -export declare const p: Person; -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\nexport interface Person {\n name: string;\n age: number;\n}\nexport declare const p: Person;\n`; - const pos = src.indexOf("p: Person"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const nameProp = await project.checker.getPropertyOfType(type, "name"); - assert.ok(nameProp, "Should find 'name' property"); - assert.equal(nameProp.name, "name"); - const missing = await project.checker.getPropertyOfType(type, "doesNotExist"); - assert.equal(missing, undefined); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getConstantValue", () => { - test("returns numeric value of an enum member", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export enum E { A = 1, B = 2 }`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - let memberB: Node | undefined; - sourceFile.forEachChild(function visit(node) { - if (node.kind === SyntaxKind.EnumMember) { - const text = sourceFile.text.slice(node.pos, node.end).trim(); - if (text.startsWith("B")) memberB = node; - } - node.forEachChild(visit); - }); - assert.ok(memberB, "Should find enum member B"); - const value = await project.checker.getConstantValue(memberB); - assert.equal(value, 2); - } - finally { - await api.close(); - } - }); - - test("returns string value of a string-initialized enum member", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export enum Color { Red = "red" }`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - let member: Node | undefined; - sourceFile.forEachChild(function visit(node) { - if (node.kind === SyntaxKind.EnumMember) member = node; - node.forEachChild(visit); - }); - assert.ok(member); - const value = await project.checker.getConstantValue(member); - assert.equal(value, "red"); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getSignatureFromDeclaration", () => { - test("returns the signature of a function declaration", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export function add(a: number, b: number): number { return a + b; }`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - let funcDecl: Node | undefined; - sourceFile.forEachChild(function visit(node) { - if (isFunctionDeclaration(node)) funcDecl = node; - node.forEachChild(visit); - }); - assert.ok(funcDecl, "Should find the function declaration"); - const sig = await project.checker.getSignatureFromDeclaration(funcDecl); - assert.ok(sig, "Should resolve a signature"); - assert.equal(sig.parameters.length, 2); - const returnType = await project.checker.getReturnTypeOfSignature(sig); - assert.ok(returnType); - assert.ok(returnType.flags & TypeFlags.Number); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getExportSpecifierLocalTargetSymbol", () => { - test("resolves the local target of an export specifier", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -const value = 42; -export { value as renamed }; -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - let exportSpecifier: Node | undefined; - sourceFile.forEachChild(function visit(node) { - if (node.kind === SyntaxKind.ExportSpecifier) exportSpecifier = node; - node.forEachChild(visit); - }); - assert.ok(exportSpecifier, "Should find the export specifier"); - const target = await project.checker.getExportSpecifierLocalTargetSymbol(exportSpecifier); - assert.ok(target, "Should resolve a local target symbol"); - assert.equal(target.name, "value"); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getAliasedSymbol", () => { - test("resolves an import alias to its target symbol", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/foo.ts": `export const foo = 42;`, - "/src/main.ts": `import { foo } from "./foo";\nexport const usage = foo;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = `import { foo } from "./foo";`.indexOf("foo }"); - const aliasSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(aliasSymbol); - assert.ok(aliasSymbol.flags & SymbolFlags.Alias, "Import binding should be an alias"); - const aliased = await project.checker.getAliasedSymbol(aliasSymbol); - assert.ok(aliased, "Should resolve the aliased symbol"); - assert.equal(aliased.name, "foo"); - assert.ok(!(aliased.flags & SymbolFlags.Alias), "Target should not be an alias"); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getFullyQualifiedName", () => { - test("returns module-qualified names for exported symbols and dotted names for members", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/index.ts": ` -export namespace Outer { - export class Inner {} -} -export class Standalone {} -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - const moduleSymbol = await project.checker.getSymbolAtLocation(sourceFile); - assert.ok(moduleSymbol); - const exports = await project.checker.getExportsOfModule(moduleSymbol); - const standalone = exports.find(e => e.name === "Standalone"); - assert.ok(standalone); - assert.equal(await project.checker.getFullyQualifiedName(standalone), `"/src/index".Standalone`); - const outer = exports.find(e => e.name === "Outer"); - assert.ok(outer); - const inner = (await outer.getExports()).get("Inner" as __String); - assert.ok(inner); - assert.equal(await project.checker.getFullyQualifiedName(inner), `"/src/index".Outer.Inner`); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getExportsOfModule", () => { - test("returns all exports including re-exports via 'export *'", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/inner.ts": `export const innerValue = 1;`, - "/src/index.ts": ` -export const direct = 1; -export * from "./inner"; -`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - const moduleSymbol = await project.checker.getSymbolAtLocation(sourceFile); - assert.ok(moduleSymbol, "Source file should have a module symbol"); - const exports = await project.checker.getExportsOfModule(moduleSymbol); - const names = exports.map(e => e.name); - assert.ok(names.includes("direct"), "should include directly-declared export"); - assert.ok(names.includes("innerValue"), "should include 'export *' re-export"); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getSymbolsInScope", () => { - const scopeFiles = { - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -const outerValue = 1; -interface OuterType { x: number; } -function f() { - const innerValue = 2; - return innerValue; -} -`, - }; - - test("returns symbols visible at a position", async () => { - const api = spawnAPI(scopeFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = scopeFiles["/src/main.ts"].indexOf("return innerValue"); - const symbols = await project.checker.getSymbolsInScope( - { document: "/src/main.ts", position: pos }, - SymbolFlags.Value, - ); - const names = symbols.map(s => s.name); - assert.ok(names.includes("innerValue"), "should include local variable"); - assert.ok(names.includes("outerValue"), "should include outer variable"); - assert.ok(names.includes("f"), "should include enclosing function"); - assert.ok(!names.includes("OuterType"), "should not include type-only meanings"); - } - finally { - await api.close(); - } - }); - - test("returns type symbols when asked for type meaning at a node", async () => { - const api = spawnAPI(scopeFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - const symbols = await project.checker.getSymbolsInScope(sourceFile, SymbolFlags.Type); - const names = symbols.map(s => s.name); - assert.ok(names.includes("OuterType"), "should include interface declared in file"); - assert.ok(names.includes("Array"), "should include global type symbols"); - } - finally { - await api.close(); - } - }); -}); - -describe("Symbol - getDocumentationComment and getJsDocTags", () => { - const docFiles = { - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -/** - * Adds two numbers together. - * @param a the first number - * @returns the sum - */ -export function add(a: number, b: number): number { return a + b; } -`, - }; - - test("getDocumentationComment returns the leading comment text", async () => { - const api = spawnAPI(docFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = docFiles["/src/main.ts"].indexOf("add(a"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const doc = await symbol.getDocumentationComment(project.checker); - assert.ok(doc.includes("Adds two numbers together"), `Expected documentation, got: ${doc}`); - assert.ok(!doc.includes("@param"), "Documentation comment should not include tags"); - } - finally { - await api.close(); - } - }); - - test("getJsDocTags returns structured tag name/text pairs", async () => { - const api = spawnAPI(docFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = docFiles["/src/main.ts"].indexOf("add(a"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const tags = await symbol.getJsDocTags(project.checker); - const param = tags.find(t => t.name === "param"); - assert.ok(param, `Expected a @param tag, got: ${JSON.stringify(tags)}`); - assert.equal(param.text, "a the first number"); - const returns = tags.find(t => t.name === "returns"); - assert.ok(returns, `Expected a @returns tag, got: ${JSON.stringify(tags)}`); - assert.equal(returns.text, "the sum"); - } - finally { - await api.close(); - } - }); -}); - -describe("TypeParameter - isThisType", () => { - test("isThisType is true for the polymorphic 'this' type in a class method", async () => { - const src = `\nexport class Builder {\n setName(name: string): this { return this; }\n}\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - // ": this {" — offset 2 past ': ' lands on 't' in the return-type 'this' - const pos = src.indexOf(": this {") + 2; - const type = await project.checker.getTypeAtPosition("/src/main.ts", pos); - assert.ok(type, "Expected a type at the 'this' position"); - assert.ok(type.flags & TypeFlags.TypeParameter, "Expected TypeParameter"); - const typeParam = type as TypeParameter; - assert.equal(typeParam.isThisType, true); - } - finally { - await api.close(); - } - }); - - test("isThisType is absent for a regular generic type parameter", async () => { - const src = `\nexport function identity(x: T): T { return x; }\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - // Point to 'T' in the type parameter declaration '' — getTypeAtPosition - // on a type annotation reference doesn't resolve to TypeParameter, but - // the declaration position does. - const pos = src.indexOf("") + 1; - const type = await project.checker.getTypeAtPosition("/src/main.ts", pos); - assert.ok(type, "Expected a type at the 'T' position"); - assert.ok(type.flags & TypeFlags.TypeParameter, "Expected TypeParameter"); - const typeParam = type as TypeParameter; - assert.ok(!typeParam.isThisType, "Expected isThisType to be absent/false for a regular type parameter"); - } - finally { - await api.close(); - } - }); -}); - -describe("Type - getAliasTypeArguments", () => { - test("returns the type arguments of a single-param generic type alias", async () => { - const src = `\ntype Box = { value: T };\nexport const x: Box = { value: "hi" };\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("x:"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const aliasArgs = await type.getAliasTypeArguments(); - assert.equal(aliasArgs.length, 1, "Expected 1 alias type argument"); - assert.ok(aliasArgs[0].flags & TypeFlags.String, `Expected string, got flags ${aliasArgs[0].flags}`); - } - finally { - await api.close(); - } - }); - - test("returns multiple type arguments for a multi-param generic type alias", async () => { - const src = `\ntype Pair = [A, B];\nexport const p: Pair = ["hello", 42];\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("p:"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const aliasArgs = await type.getAliasTypeArguments(); - assert.equal(aliasArgs.length, 2, "Expected 2 alias type arguments"); - assert.ok(aliasArgs[0].flags & TypeFlags.String, `Expected first arg to be string, got flags ${aliasArgs[0].flags}`); - assert.ok(aliasArgs[1].flags & TypeFlags.Number, `Expected second arg to be number, got flags ${aliasArgs[1].flags}`); - } - finally { - await api.close(); - } - }); - - test("returns empty array for a non-alias generic type", async () => { - const src = `\nexport const arr: Array = ["hello"];\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("arr:"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const aliasArgs = await type.getAliasTypeArguments(); - assert.equal(aliasArgs.length, 0, "Expected no alias type arguments for a direct generic reference"); - } - finally { - await api.close(); - } - }); -}); - -describe("Type - getAliasSymbol", () => { - test("returns the symbol for a non-generic type alias", async () => { - // Object-type aliases preserve aliasSymbol; primitive aliases (type Foo = string) do not. - const src = `\ntype Point = { x: number; y: number };\nexport const p: Point = { x: 1, y: 2 };\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("p:"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const aliasSymbol = await type.getAliasSymbol(); - assert.ok(aliasSymbol, "Expected alias symbol to exist"); - assert.equal(aliasSymbol.name, "Point"); - } - finally { - await api.close(); - } - }); - - test("returns the symbol for a generic type alias", async () => { - const src = `\ntype Container = { item: T };\nexport const c: Container = { item: 42 };\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("c:"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const aliasSymbol = await type.getAliasSymbol(); - assert.ok(aliasSymbol, "Expected alias symbol for generic alias"); - assert.equal(aliasSymbol.name, "Container"); - } - finally { - await api.close(); - } - }); - - test("returns undefined for a non-alias type", async () => { - const src = `\nexport const str: string = "test";\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("str:"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const aliasSymbol = await type.getAliasSymbol(); - assert.equal(aliasSymbol, undefined, "Expected no alias symbol for primitive type"); - } - finally { - await api.close(); - } - }); -}); - -describe("IntrinsicType - intrinsicName", () => { - test("intrinsicName matches the primitive type name", async () => { - const src = `\nexport const x: string = "hello";\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const stringType = await project.checker.getStringType(); - assert.equal((stringType as IntrinsicType).intrinsicName, "string"); - const anyType = await project.checker.getAnyType(); - assert.equal((anyType as IntrinsicType).intrinsicName, "any"); - const neverType = await project.checker.getNeverType(); - assert.equal((neverType as IntrinsicType).intrinsicName, "never"); - const pos = src.indexOf("x:"); - const sym = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(sym); - const litType = await project.checker.getTypeOfSymbol(sym); - assert.ok(litType); - assert.ok(litType.flags & TypeFlags.Intrinsic); - assert.equal((litType as IntrinsicType).intrinsicName, "string"); - } - finally { - await api.close(); - } - }); -}); - -describe("FreshableType - getFreshType and getRegularType", () => { - test("LiteralType.value is empty string for the empty-string literal type", async () => { - const src = `\nexport const empty: "" = "";\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("empty:")); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.ok(type.flags & TypeFlags.StringLiteral, "Expected StringLiteral"); - const literal = type as LiteralType; - assert.equal(literal.value, "", "value should be empty string, not undefined"); - } - finally { - await api.close(); - } - }); - - test("LiteralType.value is accessible via the FreshableType hierarchy", async () => { - const src = `\nexport const greeting: "hello" = "hello";\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("greeting:"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.ok(type.flags & TypeFlags.StringLiteral, "Expected StringLiteral"); - const literal = type as LiteralType; - assert.equal(literal.value, "hello"); - } - finally { - await api.close(); - } - }); - - test("BigIntLiteralType.value is a bigint (positive and negative)", async () => { - const src = `\nexport const pos = 123n;\nexport const neg = -123n;\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - - const posSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("pos =")); - assert.ok(posSymbol); - const posType = await project.checker.getTypeOfSymbol(posSymbol); - assert.ok(posType); - assert.ok(posType.flags & TypeFlags.BigIntLiteral, "Expected BigIntLiteral"); - const posLiteral = posType as BigIntLiteralType; - assert.equal(typeof posLiteral.value, "bigint"); - assert.equal(posLiteral.value, 123n); - - const negSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("neg =")); - assert.ok(negSymbol); - const negType = await project.checker.getTypeOfSymbol(negSymbol); - assert.ok(negType); - assert.ok(negType.flags & TypeFlags.BigIntLiteral, "Expected BigIntLiteral"); - const negLiteral = negType as BigIntLiteralType; - assert.equal(typeof negLiteral.value, "bigint"); - assert.equal(negLiteral.value, -123n); - } - finally { - await api.close(); - } - }); - - test("getFreshType() returns a fresh twin with matching value", async () => { - const src = `\nexport const greeting: "hello" = "hello";\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("greeting:"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Freshable, "Type should be a freshable type"); - const fresh = await (type as FreshableType).getFreshType(); - assert.ok(fresh, "Expected getFreshType() to return non-undefined for a literal type"); - assert.ok(fresh.flags & TypeFlags.StringLiteral, "Fresh type should be a StringLiteral"); - assert.equal((fresh as LiteralType).value, "hello", "Fresh type should carry the same value"); - assert.notEqual(fresh.id, type.id, "Fresh type should not be the original type"); - const freshFresh = await fresh.getFreshType(); - assert.ok(freshFresh, "Expected getFreshType() to return non-undefined for a fresh type"); - assert.equal(freshFresh.id, fresh.id, "Fresh type of a fresh type should be the fresh type"); - } - finally { - await api.close(); - } - }); - - test("getRegularType() on a fresh literal returns the regular twin", async () => { - // The initial type response from getTypeOfSymbol does not always include the - // regularType handle, so getRegularType() is tested via the fresh twin which - // always includes its regularType in its own response. - const src = `\nexport const greeting: "hello" = "hello";\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("greeting:"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Freshable, "Type should be a freshable type"); - const fresh = await (type as FreshableType).getFreshType(); - assert.ok(fresh, "Need fresh type for this test"); - assert.ok(fresh.flags & TypeFlags.Freshable, "Fresh type should be a freshable type"); - const regular = await fresh.getRegularType(); - assert.ok(regular, "Expected getRegularType() on the fresh twin to return non-undefined"); - assert.ok(regular.flags & TypeFlags.StringLiteral, "Regular type should be a StringLiteral"); - assert.equal((regular as LiteralType).value, "hello", "Regular type should carry the same value"); - assert.equal(regular.id, type.id, "Regular type should be the original type"); - } - finally { - await api.close(); - } - }); - - test("getFreshType() and getRegularType() work for computed enum types (TypeFlags.Enum)", async () => { - // getTypeOfSymbol on an ambient enum member returns the FRESH computed enum type. - // For fresh types: getFreshType() returns self, getRegularType() returns the regular twin. - const src = `\ndeclare enum Status { Pending }\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("Pending"); - const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = await project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Enum, `Expected TypeFlags.Enum, got ${type.flags}`); - assert.ok(type.flags & TypeFlags.Freshable, "Enum type should be freshable"); - // The returned type IS the fresh type: getFreshType() returns itself - const fresh = await (type as FreshableType).getFreshType(); - assert.ok(fresh, "Expected getFreshType() to return non-undefined"); - assert.equal(fresh.id, type.id, "getFreshType() on a fresh enum type returns itself"); - // getRegularType() returns the regular twin (a different type) - const regular = await (type as FreshableType).getRegularType(); - assert.ok(regular, "Expected getRegularType() to return non-undefined"); - assert.ok(regular.flags & TypeFlags.Enum, "Regular enum type should also have TypeFlags.Enum"); - assert.notEqual(regular.id, type.id, "Regular type should be distinct from the fresh type"); - // Round-trip: regular → getFreshType() → back to the original fresh type - const backToFresh = await (regular as FreshableType).getFreshType(); - assert.ok(backToFresh); - assert.equal(backToFresh.id, type.id, "Round-trip through regular/fresh returns the original type"); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - isContextSensitive", () => { - test("arrow function with no type annotation is context sensitive", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const fn = (x) => x;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - // Find the arrow function node - let arrowFn: import("@typescript/typescript/unstable/ast").Node | undefined; - sourceFile.forEachChild(function visit(node) { - if (node.kind === SyntaxKind.ArrowFunction) { - arrowFn = node; - } - node.forEachChild(visit); - }); - assert.ok(arrowFn, "Should find an arrow function"); - const result = await project.checker.isContextSensitive(arrowFn); - assert.equal(result, true); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - isTypeAssignableTo", () => { - test("returns true when source is assignable to target", async () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": `export {};`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const stringType = await project.checker.getStringType(); - const anyType = await project.checker.getAnyType(); - const neverType = await project.checker.getNeverType(); - assert.ok(await project.checker.isTypeAssignableTo(stringType, stringType), "string assignable to string"); - assert.ok(await project.checker.isTypeAssignableTo(stringType, anyType), "string assignable to any"); - assert.ok(await project.checker.isTypeAssignableTo(neverType, stringType), "never assignable to string (bottom type)"); - } - finally { - await api.close(); - } - }); - - test("returns false when source is not assignable to target", async () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": `export {};`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const stringType = await project.checker.getStringType(); - const numberType = await project.checker.getNumberType(); - assert.ok(!await project.checker.isTypeAssignableTo(numberType, stringType), "number not assignable to string"); - assert.ok(!await project.checker.isTypeAssignableTo(stringType, numberType), "string not assignable to number"); - } - finally { - await api.close(); - } - }); - - test("a string literal type is assignable to string but not number", async () => { - const src = `\nexport const x: "hello" = "hello";\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("x:"); - const sym = await project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(sym); - const litType = await project.checker.getTypeOfSymbol(sym); - assert.ok(litType); - assert.ok(litType.flags & TypeFlags.StringLiteral); - const stringType = await project.checker.getStringType(); - const numberType = await project.checker.getNumberType(); - assert.ok(await project.checker.isTypeAssignableTo(litType, stringType)); - assert.ok(!await project.checker.isTypeAssignableTo(litType, numberType)); - } - finally { - await api.close(); - } - }); -}); - -describe("Emitter - printNode", () => { - const emitterFiles = { - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export const x = 42; -export function greet(name: string): string { return name; } -export type Pair = [string, number]; -export const obj = { m: 1, s: "hi", b: true }; -`, - }; - - test("printNode with factory-created keyword type", async () => { - const api = spawnAPI(emitterFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const node = createKeywordTypeNode(SyntaxKind.StringKeyword); - const text = await project.emitter.printNode(node); - assert.strictEqual(text, "string"); - } - finally { - await api.close(); - } - }); - - test("printNode with factory-created union type", async () => { - const api = spawnAPI(emitterFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const node = createUnionTypeNode([ - createKeywordTypeNode(SyntaxKind.StringKeyword), - createKeywordTypeNode(SyntaxKind.NumberKeyword), - ]); - const text = await project.emitter.printNode(node); - assert.strictEqual(text, "string | number"); - } - finally { - await api.close(); - } - }); - - test("printNode with factory-created function type", async () => { - const api = spawnAPI(emitterFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const param = createParameterDeclaration( - undefined, - undefined, - createIdentifier("x"), - undefined, - createKeywordTypeNode(SyntaxKind.StringKeyword), - undefined, - ); - const node = createFunctionTypeNode( - undefined, - [param], - createKeywordTypeNode(SyntaxKind.NumberKeyword), - ); - const text = await project.emitter.printNode(node); - assert.strictEqual(text, "(x: string) => number"); - } - finally { - await api.close(); - } - }); - - test("printNode with factory-created type reference", async () => { - const api = spawnAPI(emitterFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const node = createTypeReferenceNode(createIdentifier("Array"), [ - createKeywordTypeNode(SyntaxKind.StringKeyword), - ]); - const text = await project.emitter.printNode(node); - assert.strictEqual(text, "Array"); - } - finally { - await api.close(); - } - }); - - test("printNode with factory-created array type", async () => { - const api = spawnAPI(emitterFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const node = createArrayTypeNode(createKeywordTypeNode(SyntaxKind.NumberKeyword)); - const text = await project.emitter.printNode(node); - assert.strictEqual(text, "number[]"); - } - finally { - await api.close(); - } - }); - - test("typeToTypeNode + printNode round-trip", async () => { - const api = spawnAPI(emitterFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const { checker, emitter } = snapshot.getProject("/tsconfig.json")!; - const src = emitterFiles["/src/main.ts"]; - - const greetPos = src.indexOf("greet("); - const symbol = await checker.getSymbolAtPosition("/src/main.ts", greetPos); - assert.ok(symbol); - const type = await checker.getTypeOfSymbol(symbol); - assert.ok(type); - const typeNode = await checker.typeToTypeNode(type); - assert.ok(typeNode); - const text = await emitter.printNode(typeNode); - assert.ok(text); - assert.strictEqual(text, "(name: string) => string"); - } - finally { - await api.close(); - } - }); - - test("visitEachChild on typeToTypeNode result with keyword types", async () => { - const api = spawnAPI(emitterFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const { checker } = snapshot.getProject("/tsconfig.json")!; - const src = emitterFiles["/src/main.ts"]; - const objPos = src.indexOf("obj"); - const symbol = await checker.getSymbolAtPosition("/src/main.ts", objPos); - assert.ok(symbol, "should find symbol for obj"); - const type = await checker.getTypeOfSymbol(symbol); - assert.ok(type); - const typeNode = await checker.typeToTypeNode(type); - assert.ok(typeNode, "typeToTypeNode should return a type node"); - - // Recursively visit to reach PropertySignature.type where isTypeNode is checked. - const visited = (function visit(node: Node): Node { - return visitEachChild(node, visit); - })(typeNode); - assert.ok(visited, "visitEachChild should not throw"); - - const kinds = [ - SyntaxKind.NumberKeyword, - SyntaxKind.StringKeyword, - SyntaxKind.BooleanKeyword, - SyntaxKind.AnyKeyword, - SyntaxKind.VoidKeyword, - SyntaxKind.UndefinedKeyword, - SyntaxKind.NeverKeyword, - SyntaxKind.UnknownKeyword, - SyntaxKind.BigIntKeyword, - SyntaxKind.ObjectKeyword, - SyntaxKind.SymbolKeyword, - SyntaxKind.IntrinsicKeyword, - SyntaxKind.ExpressionWithTypeArguments, - SyntaxKind.JSDocAllType, - SyntaxKind.JSDocNullableType, - SyntaxKind.JSDocNonNullableType, - SyntaxKind.JSDocOptionalType, - SyntaxKind.JSDocVariadicType, - SyntaxKind.JSDocTypeExpression, - SyntaxKind.JSDocTypeLiteral, - SyntaxKind.JSDocSignature, - ]; - for (const kind of kinds) { - assert.ok(isTypeNode({ kind } as any), `isTypeNode should accept ${SyntaxKind[kind]}`); - } - } - finally { - await api.close(); - } - }); - - test("typeToString", async () => { - const api = spawnAPI(emitterFiles); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const { checker } = snapshot.getProject("/tsconfig.json")!; - const src = emitterFiles["/src/main.ts"]; - - const greetPos = src.indexOf("greet("); - const symbol = await checker.getSymbolAtPosition("/src/main.ts", greetPos); - assert.ok(symbol); - const type = await checker.getTypeOfSymbol(symbol); - assert.ok(type); - const text = await checker.typeToString(type); - assert.strictEqual(text, "(name: string) => string"); - } - finally { - await api.close(); - } - }); - - test("typeToString with TypeFormatFlags", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export function greet(name: string): string[] { return [name]; }`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const { checker } = snapshot.getProject("/tsconfig.json")!; - const greetPos = "export function greet".indexOf("greet"); - const symbol = await checker.getSymbolAtPosition("/src/main.ts", greetPos); - assert.ok(symbol); - const type = await checker.getTypeOfSymbol(symbol); - assert.ok(type); - const text = await checker.typeToString(type, undefined, TypeFormatFlags.WriteArrayAsGenericType); - assert.strictEqual(text, "(name: string) => Array"); - } - finally { - await api.close(); - } - }); - - test("printNode with terminateUnterminatedLiterals option", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `const foo = /asdfasf;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - - // Find the regex literal node - let regexNode: import("@typescript/typescript/unstable/ast").Node | undefined; - sourceFile.forEachChild(function visit(node) { - if (node.kind === SyntaxKind.RegularExpressionLiteral) { - regexNode = node; - return; - } - node.forEachChild(visit); - }); - assert.ok(regexNode, "Should find a regex literal"); - - // Without the option, regex is printed as-is - const textWithout = await project.emitter.printNode(regexNode); - assert.strictEqual(textWithout, "/asdfasf"); - - // With the option, the closing slash is added - const textWith = await project.emitter.printNode(regexNode, { terminateUnterminatedLiterals: true }); - assert.strictEqual(textWith, "/asdfasf/"); - } - finally { - await api.close(); - } - }); -}); - -describe("Program - selected file emit", () => { - const files = { - "/tsconfig.json": JSON.stringify({ - compilerOptions: { - declarationMap: true, - emitDeclarationOnly: true, - noEmit: true, - noEmitOnError: true, - sourceMap: true, - }, - }), - "/src/a.ts": `export const a: string = 1;`, - "/src/b.ts": `export const b = 2;`, - }; - - test("getJavaScriptEmit forces JavaScript and source maps", async () => { - const { api, fs } = spawnAPIWithFS({ ...files }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const result = await project.program.getJavaScriptEmit(["/src/a.ts", "/src/b.ts"]); - assert.equal(result.emitSkipped, false); - assert.deepEqual([...result.outputFiles.keys()], [ - "/src/a.js", - "/src/a.js.map", - "/src/b.js", - "/src/b.js.map", - ]); - assert.equal(result.outputFiles.get("/src/a.js")?.sourceFileName, "/src/a.ts"); - assert.match(result.outputFiles.get("/src/a.js")!.text, /export const a = 1/); - assert.equal(fs.readFile?.("/src/a.js"), undefined); - } - finally { - await api.close(); - } - }); - - test("getDeclarationEmit forces declarations and declaration maps", async () => { - const { api, fs } = spawnAPIWithFS({ ...files }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const result = await project.program.getDeclarationEmit(["/src/a.ts", "/src/b.ts"]); - assert.equal(result.emitSkipped, false); - assert.deepEqual([...result.outputFiles.keys()], [ - "/src/a.d.ts", - "/src/a.d.ts.map", - "/src/b.d.ts", - "/src/b.d.ts.map", - ]); - assert.equal(result.outputFiles.get("/src/a.d.ts")?.sourceFileName, "/src/a.ts"); - assert.equal(fs.readFile?.("/src/a.d.ts"), undefined); - } - finally { - await api.close(); - } - }); - - test("selected file emit accepts empty arrays", async () => { - const api = spawnAPI({ ...files }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - assert.deepEqual((await project.program.getJavaScriptEmit([])).outputFiles, new Map()); - assert.deepEqual((await project.program.getDeclarationEmit([])).outputFiles, new Map()); - } - finally { - await api.close(); - } - }); -}); - -describe("SnapshotInternalAPI - formatNodeForInsertion", () => { - test("formats a synthesized statement with correct indentation for insertion inside a function body", async () => { - const files = { - "/tsconfig.json": "{}", - "/src/index.ts": `function greet(name: string) {\n console.log(name);\n}\n`, - }; - const api = spawnAPI(files); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - - const node = createVariableStatement( - undefined, - createVariableDeclarationList( - [createVariableDeclaration(createIdentifier("x"), undefined, undefined, createNumericLiteral("1", 0))], - NodeFlags.Const, - ), - ); - - const sourceText = files["/src/index.ts"]; - const insertionPos = sourceText.indexOf("\n console.log") + 1; - - const formatted = await snapshot.internal.formatNodeForInsertion(node, "/src/index.ts", insertionPos); - assert.ok(formatted.includes("const x = 1;"), `Expected 'const x = 1;' in formatted output, got: ${JSON.stringify(formatted)}`); - assert.ok(formatted.startsWith(" "), `Expected 4 spaces of indentation, got: ${JSON.stringify(formatted)}`); - } - finally { - await api.close(); - } - }); - - test("formats a node at top-level with no indentation", async () => { - const files = { - "/tsconfig.json": "{}", - "/src/index.ts": `const a = 1;\nconst b = 2;\n`, - }; - const api = spawnAPI(files); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - - const node = createVariableStatement( - undefined, - createVariableDeclarationList( - [createVariableDeclaration(createIdentifier("y"), undefined, undefined, createNumericLiteral("42", 0))], - NodeFlags.Const, - ), - ); - - const formatted = await snapshot.internal.formatNodeForInsertion(node, "/src/index.ts", 0); - assert.ok(formatted.includes("const y = 42;"), `Expected 'const y = 42;' in formatted output, got: ${JSON.stringify(formatted)}`); - assert.ok(!formatted.startsWith(" "), `Expected no leading spaces at top level, got: ${JSON.stringify(formatted)}`); - } - finally { - await api.close(); - } - }); - - test("formats a synthesized statement with correct indentation when non-ASCII characters precede the insertion position", async () => { - // 'é' is 1 UTF-16 code unit but 2 UTF-8 bytes, so UTF-16 and UTF-8 offsets diverge after it. - // This test verifies the position is correctly converted from UTF-16 to UTF-8 before use. - const files = { - "/tsconfig.json": "{}", - "/src/index.ts": `// \u00e9\nfunction greet(name: string) {\n console.log(name);\n}\n`, - }; - const api = spawnAPI(files); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - - const node = createVariableStatement( - undefined, - createVariableDeclarationList( - [createVariableDeclaration(createIdentifier("x"), undefined, undefined, createNumericLiteral("1", 0))], - NodeFlags.Const, - ), - ); - - // insertionPos is the UTF-16 code-unit offset of the start of the indented line inside the function body. - const sourceText = files["/src/index.ts"]; - const insertionPos = sourceText.indexOf("\n console.log") + 1; - - const formatted = await snapshot.internal.formatNodeForInsertion(node, "/src/index.ts", insertionPos); - // The node should be indented to match the function body (4 spaces) - assert.ok(formatted.includes("const x = 1;"), `Expected 'const x = 1;' in formatted output, got: ${JSON.stringify(formatted)}`); - assert.ok(formatted.startsWith(" "), `Expected 4 spaces of indentation (UTF-16 offset correctly converted), got: ${JSON.stringify(formatted)}`); - } - finally { - await api.close(); - } - }); -}); - -describe("modifierFlags", () => { - test("export async function has Export | Async flags", async () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `export async function foo() {}`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - - let fnNode: import("@typescript/typescript/unstable/ast").FunctionDeclaration | undefined; - sourceFile.forEachChild(function visit(node) { - if (isFunctionDeclaration(node)) { - fnNode = node; - } - node.forEachChild(visit); - }); - assert.ok(fnNode, "Should find a function declaration"); - assert.ok(fnNode.modifierFlags & ModifierFlags.Export, "Should have Export flag"); - assert.ok(fnNode.modifierFlags & ModifierFlags.Async, "Should have Async flag"); - assert.strictEqual(fnNode.modifierFlags, ModifierFlags.Export | ModifierFlags.Async); - } - finally { - await api.close(); - } - }); - - test("node without modifiers has ModifierFlags.None", async () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `function bar() {}`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - - let fnNode: import("@typescript/typescript/unstable/ast").FunctionDeclaration | undefined; - sourceFile.forEachChild(function visit(node) { - if (isFunctionDeclaration(node)) { - fnNode = node; - } - node.forEachChild(visit); - }); - assert.ok(fnNode, "Should find a function declaration"); - assert.strictEqual(fnNode.modifierFlags, ModifierFlags.None); - } - finally { - await api.close(); - } - }); -}); - -describe("Checker - getResolvedSymbol", () => { - test("resolves variable reference to its declaration symbol", async () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `const x = 1;\nconst y = x;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - - // Find the 'x' identifier in `const y = x` - let refNode: import("@typescript/typescript/unstable/ast").Identifier | undefined; - sourceFile.forEachChild(function visit(node) { - if (isIdentifier(node) && node.text === "x") { - // We want the reference, not the declaration - take the last one - refNode = node; - } - node.forEachChild(visit); - }); - assert.ok(refNode, "Should find identifier 'x'"); - - const symbol = await project.checker.getResolvedSymbol(refNode); - assert.ok(symbol, "Should resolve symbol for 'x'"); - assert.equal(symbol.name, "x"); - } - finally { - await api.close(); - } - }); -}); - -describe("VariableDeclarationList - BlockScoped flags", () => { - test("let declaration has Let flag", async () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `let x = 1;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - - let declList: import("@typescript/typescript/unstable/ast").Node | undefined; - sourceFile.forEachChild(function visit(node) { - if (isVariableDeclarationList(node)) { - declList = node; - } - node.forEachChild(visit); - }); - assert.ok(declList, "Should find VariableDeclarationList"); - assert.ok(declList.flags & NodeFlags.Let, "Should have Let flag"); - } - finally { - await api.close(); - } - }); - - test("const declaration has Const flag", async () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `const x = 1;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - - let declList: import("@typescript/typescript/unstable/ast").Node | undefined; - sourceFile.forEachChild(function visit(node) { - if (isVariableDeclarationList(node)) { - declList = node; - } - node.forEachChild(visit); - }); - assert.ok(declList, "Should find VariableDeclarationList"); - assert.ok(declList.flags & NodeFlags.Const, "Should have Const flag"); - } - finally { - await api.close(); - } - }); -}); - -test("TypeOperator operator kind", async () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `function test(arg: readonly number[]) { }\n`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - assert(sourceFile); - const param = (sourceFile.statements[0] as import("@typescript/typescript/unstable/ast").FunctionDeclaration).parameters[0]; - assert(param); - const type = param.type as import("@typescript/typescript/unstable/ast").TypeOperatorNode; - assert(type); - assert.equal(type.kind, SyntaxKind.TypeOperator); - assert.equal(type.operator, SyntaxKind.ReadonlyKeyword); - const printed = await project.emitter.printNode(sourceFile); - assert.equal(sourceFile.text, printed); - } - finally { - await api.close(); - } -}); - -test("SpreadAssignment roundtrip", async () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `var thing = { ...other };\n`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - assert(sourceFile); - const stmt = sourceFile.statements[0] as import("@typescript/typescript/unstable/ast").VariableStatement; - const object = stmt.declarationList.declarations[0].initializer as import("@typescript/typescript/unstable/ast").ObjectLiteralExpression; - const assignment = object.properties[0] as import("@typescript/typescript/unstable/ast").SpreadAssignment; - assert(assignment); - assert.equal(assignment.kind, SyntaxKind.SpreadAssignment); - const expr = assignment.expression; - assert(expr); - assert.equal(expr.kind, SyntaxKind.Identifier); - const printed = await project.emitter.printNode(sourceFile); - assert.equal(sourceFile.text, printed); - } - finally { - await api.close(); - } -}); - -test("VariableDeclarationList const flag clone", async () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `const thing = 123;\n`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - assert(sourceFile); - { - const stmt = sourceFile.statements[0] as import("@typescript/typescript/unstable/ast").VariableStatement; - const list = stmt.declarationList; - assert(list.flags & NodeFlags.Const); - } - const cloned = getSynthesizedDeepClone(sourceFile); - { - const stmt = cloned.statements[0] as import("@typescript/typescript/unstable/ast").VariableStatement; - const list = stmt.declarationList; - assert(list.flags & NodeFlags.Const); - } - const printed = await project.emitter.printNode(cloned); - assert.equal(sourceFile.text, printed); - } - finally { - await api.close(); - } -}); - -test("JSDoc before ExpressionStatement allowed", async () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": ` -/** - * A doc. - */ -doThing(); - `, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - assert(sourceFile); - const printed = await project.emitter.printNode(sourceFile); - assert.equal(sourceFile.text.trim(), printed.trim()); - } - finally { - await api.close(); - } -}); - -test("Factory ModifierList auto-conversion", async () => { - const api = spawnAPI(); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const node = createTypeAliasDeclaration( - [createToken(SyntaxKind.ExportKeyword)], - createIdentifier("Test"), - undefined, - createKeywordTypeNode(SyntaxKind.AnyKeyword), - ); - - assert.equal(await project.emitter.printNode(node), "export type Test = any;"); - - const cloned = getSynthesizedDeepClone(node); - assert.equal(await project.emitter.printNode(cloned), "export type Test = any;"); - } - finally { - await api.close(); - } -}); - -test("Parse-clone-emit roundtrip", async () => { - const tsSource = fileURLToPath(new URL("../../../../tsc/testdata/fixtures", import.meta.url).toString()); - const api = new API({ - cwd: tsSource, - }); - const target = { - cloneCrashed: 0, - printCrashed: 0, - clonePrintCrashed: 0, - }; - const errors = { ...target }; - try { - for (const tsconfig of globSync("**/tsconfig.json", { cwd: tsSource })) { - const snapshot = await api.updateSnapshot({ openProject: resolve(tsSource, tsconfig) }); - const project = snapshot.getProject(tsconfig); - assert(project); - for (const file of project.rootFiles) { - const source = await project.program.getSourceFile(file); - assert(source); - let clone: typeof source; - - try { - await project.emitter.printNode(source); - } - catch { - errors.printCrashed++; - continue; - } - - try { - clone = getSynthesizedDeepClone(source); - } - catch { - errors.cloneCrashed++; - continue; - } - - try { - await project.emitter.printNode(clone); - } - catch { - errors.clonePrintCrashed++; - continue; - } - } - } - } - finally { - await api.close(); - } - assert.deepEqual(errors, target); -}); - -describe("Program - diagnostics", () => { - test("getSyntacticDiagnostics", async () => { - const source = `const x: = 1;`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": source, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = await project.program.getSyntacticDiagnostics("/src/index.ts"); - assert.deepEqual(diags[0].startPosition, { line: 0, character: 9 }); - assert.deepEqual(diags[0].endPosition, { line: 0, character: 10 }); - assert.deepEqual(diags[0].sourceLines, [{ line: 0, text: source }]); - assert.deepEqual(withoutFormattingContext(diags), [{ - fileName: "/src/index.ts", - ...rangeOf(source, "="), - code: 1110, - category: DiagnosticCategory.Error, - text: "Type expected.", - }]); - } - finally { - await api.close(); - } - }); - - test("getSemanticDiagnostics with messageChain and relatedInformation", async () => { - const source = `interface Props { callback: (x: string) => void }\nconst p: Props = { callback: (x: number) => {} };`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": source, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = await project.program.getSemanticDiagnostics("/src/index.ts"); - const declRange = rangeOf(source, "callback", 0); - const assignRange = rangeOf(source, "callback", 1); - assert.deepEqual(withoutFormattingContext(diags), [{ - fileName: "/src/index.ts", - ...assignRange, - code: 2322, - category: DiagnosticCategory.Error, - text: "Type '(x: number) => void' is not assignable to type '(x: string) => void'.", - messageChain: [{ - fileName: "/src/index.ts", - ...assignRange, - code: 2328, - category: DiagnosticCategory.Error, - text: "Types of parameters 'x' and 'x' are incompatible.", - messageChain: [{ - fileName: "/src/index.ts", - ...assignRange, - code: 2322, - category: DiagnosticCategory.Error, - text: "Type 'string' is not assignable to type 'number'.", - }], - }], - relatedInformation: [{ - fileName: "/src/index.ts", - ...declRange, - code: 6500, - category: DiagnosticCategory.Message, - text: "The expected type comes from property 'callback' which is declared here on type 'Props'", - }], - }]); - } - finally { - await api.close(); - } - }); - - test("getSuggestionDiagnostics", async () => { - const source = `export function f() { const x = 1; return x; }\nconst _unused = 1;\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": source, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = await project.program.getSuggestionDiagnostics("/src/index.ts"); - assert.deepEqual(withoutFormattingContext(diags), [{ - fileName: "/src/index.ts", - ...rangeOf(source, "_unused"), - code: 6133, - category: DiagnosticCategory.Suggestion, - text: "'_unused' is declared but its value is never read.", - reportsUnnecessary: true, - }]); - } - finally { - await api.close(); - } - }); - - test("getConfigFileParsingDiagnostics", async () => { - const config = `{ "compilerOptions": { "target": "invalid" } }`; - const api = spawnAPI({ - "/tsconfig.json": config, - "/src/index.ts": `export const x = 1;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = await project.program.getConfigFileParsingDiagnostics(); - assert.deepEqual(withoutFormattingContext(diags), [{ - fileName: "/tsconfig.json", - ...rangeOf(config, `"invalid"`), - code: 6046, - category: DiagnosticCategory.Error, - text: "Argument for '--target' option must be: 'es6', 'es2015', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'es2021', 'es2022', 'es2023', 'es2024', 'es2025', 'esnext'.", - }]); - } - finally { - await api.close(); - } - }); - - test("getConfigFileNames and getConfigSourceFile", async () => { - const baseConfigText = `{ "compilerOptions": { "strict": true } }`; - const { api, fs } = spawnAPIWithFS({ - "/tsconfig.base.json": baseConfigText, - "/tsconfig.json": `{ "extends": "./tsconfig.base.json", "files": ["./src/index.ts"] }`, - "/src/index.ts": `export const x = 1;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const names = await project.program.getConfigFileNames(); - assert.deepEqual(names, ["/tsconfig.json", "/tsconfig.base.json"]); - - const rootConfig = await project.program.getConfigSourceFile("/tsconfig.json"); - assert.ok(rootConfig); - assert.equal(rootConfig.fileName, "/tsconfig.json"); - assert.equal(await project.program.getSourceFile("/tsconfig.json"), undefined); - - fs.writeFile!("/tsconfig.base.json", `{ "compilerOptions": { "strict": false } }`); - const extendedConfig = await project.program.getConfigSourceFile("/tsconfig.base.json"); - assert.ok(extendedConfig); - assert.equal(extendedConfig.fileName, "/tsconfig.base.json"); - assert.equal(extendedConfig.getFullText(), baseConfigText); - - const nonConfig = await project.program.getConfigSourceFile("/src/index.ts"); - assert.equal(nonConfig, undefined); - } - finally { - await api.close(); - } - }); - - test("getDeclarationDiagnostics", async () => { - const api = spawnAPI({ - "/tsconfig.json": `{ "compilerOptions": { "declaration": true } }`, - "/src/index.ts": `export const x: number = 1;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = await project.program.getDeclarationDiagnostics("/src/index.ts"); - assert.deepEqual(diags, []); - } - finally { - await api.close(); - } - }); - - test("getBindDiagnostics", async () => { - const source = `let x = 1;\nlet x = 2;`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": source, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = await project.program.getBindDiagnostics("/src/index.ts"); - assert.deepEqual(withoutFormattingContext(diags), [ - { - fileName: "/src/index.ts", - ...rangeOf(source, "x", 0), - code: 2451, - category: DiagnosticCategory.Error, - text: "Cannot redeclare block-scoped variable 'x'.", - }, - { - fileName: "/src/index.ts", - ...rangeOf(source, "x", 1), - code: 2451, - category: DiagnosticCategory.Error, - text: "Cannot redeclare block-scoped variable 'x'.", - }, - ]); - } - finally { - await api.close(); - } - }); - - test("getProgramDiagnostics", async () => { - const config = `{ "compilerOptions": { "moduleResolution": "bundler", "module": "nodenext" } }`; - const api = spawnAPI({ - "/tsconfig.json": config, - "/src/index.ts": `export const x = 1;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = await project.program.getProgramDiagnostics(); - assert.deepEqual(withoutFormattingContext(diags), [ - { - fileName: "/tsconfig.json", - ...rangeOf(config, `"bundler"`), - code: 5095, - category: DiagnosticCategory.Error, - text: "Option 'bundler' can only be used when 'module' is set to 'preserve', 'commonjs', or 'es2015' or later.", - }, - { - fileName: "/tsconfig.json", - ...rangeOf(config, `"bundler"`), - code: 5109, - category: DiagnosticCategory.Error, - text: "Option 'moduleResolution' must be set to 'NodeNext' (or left unspecified) when option 'module' is set to 'NodeNext'.", - }, - ]); - } - finally { - await api.close(); - } - }); - - test("getGlobalDiagnostics", async () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `export const x = 1;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = await project.program.getGlobalDiagnostics(); - assert.deepEqual(diags, []); - } - finally { - await api.close(); - } - }); - - test("getGlobalDiagnostics returns file-less diagnostics from the checker", async () => { - const api = spawnAPI({ - "/tsconfig.json": `{ "compilerOptions": { "noLib": true } }`, - "/src/index.ts": `export const x = [1, 2, 3];`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = await project.program.getGlobalDiagnostics(); - // With noLib, the checker reports "Cannot find global type" diagnostics that - // are not associated with any source file. - assert.ok(diags.length > 0, "expected global diagnostics to be reported"); - for (const diag of diags) { - assert.equal(diag.fileName, undefined); - assert.equal(diag.code, 2318); - assert.equal(diag.category, DiagnosticCategory.Error); - } - assert.ok( - diags.some(d => d.text === "Cannot find global type 'Array'."), - "expected a global diagnostic for the 'Array' type", - ); - } - finally { - await api.close(); - } - }); - - test("getSyntacticDiagnostics with multiple files", async () => { - const sourceA = `const a: = 1;`; - const sourceB = `const b: = 2;`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/a.ts": sourceA, - "/src/b.ts": sourceB, - "/src/clean.ts": `const c = 3;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = await project.program.getSyntacticDiagnostics(["/src/a.ts", "/src/b.ts"]); - assert.deepEqual(withoutFormattingContext(diags), [ - { - fileName: "/src/a.ts", - ...rangeOf(sourceA, "="), - code: 1110, - category: DiagnosticCategory.Error, - text: "Type expected.", - }, - { - fileName: "/src/b.ts", - ...rangeOf(sourceB, "="), - code: 1110, - category: DiagnosticCategory.Error, - text: "Type expected.", - }, - ]); - } - finally { - await api.close(); - } - }); - - test("getSemanticDiagnostics with multiple files", async () => { - const sourceA = `export const a: number = "hello";`; - const sourceB = `export const b: string = 42;`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/a.ts": sourceA, - "/src/b.ts": sourceB, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = await project.program.getSemanticDiagnostics(["/src/a.ts", "/src/b.ts"]); - assert.equal(diags.length, 2); - assert.equal(diags[0].fileName, "/src/a.ts"); - assert.equal(diags[0].code, 2322); - assert.equal(diags[1].fileName, "/src/b.ts"); - assert.equal(diags[1].code, 2322); - } - finally { - await api.close(); - } - }); - - test("getBindDiagnostics with multiple files", async () => { - const sourceA = `let x = 1;\nlet x = 2;`; - const sourceB = `let y = 1;\nlet y = 2;`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/a.ts": sourceA, - "/src/b.ts": sourceB, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = await project.program.getBindDiagnostics(["/src/a.ts", "/src/b.ts"]); - assert.equal(diags.length, 4); - assert.equal(diags.filter(d => d.fileName === "/src/a.ts").length, 2); - assert.equal(diags.filter(d => d.fileName === "/src/b.ts").length, 2); - } - finally { - await api.close(); - } - }); - - test("diagnostics with no files argument returns all", async () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/a.ts": `const a: = 1;`, - "/src/b.ts": `const b: = 2;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = await project.program.getSyntacticDiagnostics(); - assert.equal(diags.length, 2); - } - finally { - await api.close(); - } - }); - - test("diagnostics with empty files array returns empty", async () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/a.ts": `const a: = 1;`, - "/src/b.ts": `const b: = 2;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = await project.program.getSyntacticDiagnostics([]); - assert.deepEqual(diags, []); - } - finally { - await api.close(); - } - }); -}); - -describe("getDefaultProjectForFile", () => { - test("finds inferred project for d.ts in node_modules after openFiles", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/index.ts": `export const x = 1;`, - "/node_modules/my-lib/package.json": JSON.stringify({ name: "my-lib", types: "./index.d.ts" }), - "/node_modules/my-lib/index.d.ts": `export declare const foo: string;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - - // The d.ts is not imported, so it is not in the project's program - const dtsSf = await project.program.getSourceFile("/node_modules/my-lib/index.d.ts"); - assert.equal(dtsSf, undefined, "d.ts not in import graph should not be found via project.program.getSourceFile"); - - // Before opening the file, getDefaultProjectForFile returns undefined (no error) - const noProject = await snapshot.getDefaultProjectForFile("/node_modules/my-lib/index.d.ts"); - assert.equal(noProject, undefined, "getDefaultProjectForFile returns undefined for unloaded file"); - - // Load the file into the inferred project via updateSnapshot openFiles - const snapshot2 = await api.updateSnapshot({ openFiles: ["/node_modules/my-lib/index.d.ts"] }); - const defaultProject = await snapshot2.getDefaultProjectForFile("/node_modules/my-lib/index.d.ts"); - assert.ok(defaultProject, "getDefaultProjectForFile should find inferred project after openFiles"); - - const fooPos = `export declare const foo: string;`.indexOf("foo"); - const fooType = await defaultProject.checker.getTypeAtPosition("/node_modules/my-lib/index.d.ts", fooPos); - assert.ok(fooType); - assert.ok(fooType.flags & TypeFlags.String); - } - finally { - await api.close(); - } - }); - - test("inferred project reflects file changes without a follow-up open/close request", async () => { - const { api, fs } = spawnAPIWithFS({ - "/loose.ts": `export const foo = 1;`, - }); - try { - const snapshot1 = await api.updateSnapshot({ openFiles: ["/loose.ts"] }); - const project1 = await snapshot1.getDefaultProjectForFile("/loose.ts"); - assert.ok(project1, "file with no config file in its ancestry should load into the inferred project"); - const sf1 = await project1.program.getSourceFile("/loose.ts"); - assert.ok(sf1); - assert.equal(sf1.text, `export const foo = 1;`); - - // Mutate the file and notify only via fileChanges — no follow-up openFiles/closeFiles. - fs.writeFile!("/loose.ts", `export const foo = 2;`); - const snapshot2 = await api.updateSnapshot({ - fileChanges: { changed: ["/loose.ts"] }, - }); - - const project2 = await snapshot2.getDefaultProjectForFile("/loose.ts"); - assert.ok(project2); - const sf2 = await project2.program.getSourceFile("/loose.ts"); - assert.ok(sf2); - assert.equal( - sf2.text, - `export const foo = 2;`, - "inferred project's program should reflect the file change even without a follow-up open/close request", - ); - } - finally { - await api.close(); - } - }); - - test("keeps previously opened files open across subsequent openFiles calls", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/index.ts": `export const x = 1;`, - "/node_modules/my-lib/package.json": JSON.stringify({ name: "my-lib", types: "./index.d.ts" }), - "/node_modules/my-lib/index.d.ts": `export declare const foo: string;`, - "/node_modules/other-lib/package.json": JSON.stringify({ name: "other-lib", types: "./index.d.ts" }), - "/node_modules/other-lib/index.d.ts": `export declare const bar: number;`, - }); - try { - await api.updateSnapshot({ openProject: "/tsconfig.json" }); - await api.updateSnapshot({ openFiles: ["/node_modules/my-lib/index.d.ts"] }); - - // Opening a second file in a later snapshot must not close the first one. - const snapshot = await api.updateSnapshot({ openFiles: ["/node_modules/other-lib/index.d.ts"] }); - - const firstProject = await snapshot.getDefaultProjectForFile("/node_modules/my-lib/index.d.ts"); - assert.ok(firstProject, "previously opened file should remain in the inferred project"); - const secondProject = await snapshot.getDefaultProjectForFile("/node_modules/other-lib/index.d.ts"); - assert.ok(secondProject, "newly opened file should be in the inferred project"); - } - finally { - await api.close(); - } - }); - - test("opening a file resolves to a configured project via ancestor search", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/index.ts": `export const x = 1;`, - }); - try { - // Open the file without first opening the project. Like LSP's didOpen, this - // should search ancestor directories for a tsconfig that contains the file. - const snapshot = await api.updateSnapshot({ openFiles: ["/src/index.ts"] }); - const defaultProject = await snapshot.getDefaultProjectForFile("/src/index.ts"); - assert.ok(defaultProject, "should find a project for the opened file"); - assert.equal( - defaultProject.configFileName, - "/tsconfig.json", - "opened file should resolve to the containing configured project, not the inferred project", - ); - } - finally { - await api.close(); - } - }); - - test("closeProjects releases a project opened via openProjects", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/index.ts": `export const x = 1;`, - }); - try { - const opened = await api.updateSnapshot({ openProjects: ["/tsconfig.json"] }); - assert.ok(opened.getProject("/tsconfig.json"), "project should be open after openProjects"); - - const closed = await api.updateSnapshot({ closeProjects: ["/tsconfig.json"] }); - assert.equal( - closed.getProject("/tsconfig.json"), - undefined, - "project should be unloaded after closeProjects", - ); - } - finally { - await api.close(); - } - }); - - test("closeFiles releases a file opened via openFiles", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/index.ts": `export const x = 1;`, - "/node_modules/my-lib/package.json": JSON.stringify({ name: "my-lib", types: "./index.d.ts" }), - "/node_modules/my-lib/index.d.ts": `export declare const foo: string;`, - }); - try { - await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const opened = await api.updateSnapshot({ openFiles: ["/node_modules/my-lib/index.d.ts"] }); - assert.ok( - await opened.getDefaultProjectForFile("/node_modules/my-lib/index.d.ts"), - "file should resolve to a project after openFiles", - ); - - const closed = await api.updateSnapshot({ closeFiles: ["/node_modules/my-lib/index.d.ts"] }); - assert.equal( - await closed.getDefaultProjectForFile("/node_modules/my-lib/index.d.ts"), - undefined, - "file should no longer resolve to a project after closeFiles", - ); - } - finally { - await api.close(); - } - }); -}); - -describe("Program - emit", () => { - const files = { - "/tsconfig.json": `{ - "compilerOptions": { - "outDir": "dist", - "declaration": true, - } - }`, - "/src/index.ts": "export const x: number = 1;", - "/src/testing.ts": "export const y: string = 'typescript';", - }; - - test("emit all files", async () => { - const fs = createVirtualFileSystem({ ...files }); - - const api = new API({ - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), - fs: fs, - }); - - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const result = await project.program.emit(); - assert.deepEqual(result, { - diagnostics: [], - emitSkipped: false, - emittedFiles: [ - "/dist/src/index.js", - "/dist/src/index.d.ts", - "/dist/src/testing.js", - "/dist/src/testing.d.ts", - ], - }); - - const js = fs.readFile?.("/dist/src/index.js"); - const dts = fs.readFile?.("/dist/src/index.d.ts"); - const js2 = fs.readFile?.("/dist/src/testing.js"); - const dts2 = fs.readFile?.("/dist/src/testing.d.ts"); - assert.strictEqual(js, `export const x = 1;\n`); - assert.strictEqual(dts, `export declare const x: number;\n`); - assert.strictEqual(js2, `export const y = 'typescript';\n`); - assert.strictEqual(dts2, `export declare const y: string;\n`); - } - finally { - await api.close(); - } - }); - - test("emit only dts", async () => { - const fs = createVirtualFileSystem({ ...files }); - - const api = new API({ - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), - fs: fs, - }); - - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const result = await project.program.emit(EmitOnly.OnlyDts); - assert.deepEqual(result, { - diagnostics: [], - emitSkipped: false, - emittedFiles: [ - "/dist/src/index.d.ts", - "/dist/src/testing.d.ts", - ], - }); - - const js = fs.readFile?.("/dist/src/index.js"); - const dts = fs.readFile?.("/dist/src/index.d.ts"); - const js2 = fs.readFile?.("/dist/src/testing.js"); - const dts2 = fs.readFile?.("/dist/src/testing.d.ts"); - assert.strictEqual(js, undefined); - assert.strictEqual(dts, `export declare const x: number;\n`); - assert.strictEqual(js2, undefined); - assert.strictEqual(dts2, `export declare const y: string;\n`); - } - finally { - await api.close(); - } - }); - - test("emit only js", async () => { - const fs = createVirtualFileSystem({ ...files }); - - const api = new API({ - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), - fs: fs, - }); - - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const result = await project.program.emit(EmitOnly.OnlyJs); - assert.deepEqual(result, { - diagnostics: [], - emitSkipped: false, - emittedFiles: [ - "/dist/src/index.js", - "/dist/src/testing.js", - ], - }); - - const js = fs.readFile?.("/dist/src/index.js"); - const dts = fs.readFile?.("/dist/src/index.d.ts"); - const js2 = fs.readFile?.("/dist/src/testing.js"); - const dts2 = fs.readFile?.("/dist/src/testing.d.ts"); - assert.strictEqual(js, `export const x = 1;\n`); - assert.strictEqual(dts, undefined); - assert.strictEqual(js2, `export const y = 'typescript';\n`); - assert.strictEqual(dts2, undefined); - } - finally { - await api.close(); - } - }); - - test("emitToString emits the whole program and respects emitOnly", async () => { - const { api, fs } = spawnAPIWithFS({ ...files }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const result = await project.program.emitToString(EmitOnly.OnlyDts); - assert.deepEqual([...result.outputFiles.keys()], [ - "/dist/src/index.d.ts", - "/dist/src/testing.d.ts", - ]); - assert.equal(fs.readFile?.("/dist/src/index.js"), undefined); - } - finally { - await api.close(); - } - }); - - test("whole-program emit includes option-controlled maps", async () => { - const { api, fs } = spawnAPIWithFS({ - "/tsconfig.json": JSON.stringify({ - compilerOptions: { - declaration: true, - declarationMap: true, - outDir: "dist", - sourceMap: true, - }, - }), - "/src/index.ts": "export const value: number = 1;", - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - - const result = await project.program.emit(); - assert.deepEqual( - new Set(result.emittedFiles), - new Set([ - "/dist/src/index.js", - "/dist/src/index.js.map", - "/dist/src/index.d.ts", - "/dist/src/index.d.ts.map", - ]), - ); - assert.ok(fs.fileExists?.("/dist/src/index.js.map")); - assert.ok(fs.fileExists?.("/dist/src/index.d.ts.map")); - - const js = await project.program.emitToString(EmitOnly.OnlyJs); - assert.deepEqual([...js.outputFiles.keys()], [ - "/dist/src/index.js", - "/dist/src/index.js.map", - ]); - - const dts = await project.program.emitToString(EmitOnly.OnlyDts); - assert.deepEqual([...dts.outputFiles.keys()], [ - "/dist/src/index.d.ts", - "/dist/src/index.d.ts.map", - ]); - } - finally { - await api.close(); - } - }); - - test("noEmitOnError reports diagnostics without writing output", async () => { - const { api, fs } = spawnAPIWithFS({ - "/tsconfig.json": JSON.stringify({ - compilerOptions: { - noEmitOnError: true, - outDir: "dist", - }, - }), - "/src/bad.ts": "export const bad = ;", - "/src/good.ts": "export const value = 1;", - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const result = await project.program.emit(); - assert.equal(result.emitSkipped, true); - assert.ok(result.diagnostics.some(d => d.code === 1109)); - assert.deepEqual(result.emittedFiles, []); - assert.equal(fs.readFile?.("/dist/src/bad.js"), undefined); - assert.equal(fs.readFile?.("/dist/src/good.js"), undefined); - - const stringResult = await project.program.emitToString(); - assert.equal(stringResult.emitSkipped, true); - assert.ok(stringResult.diagnostics.some(d => d.code === 1109)); - assert.deepEqual(stringResult.outputFiles, new Map()); - } - finally { - await api.close(); - } - }); - - test("emit and emitToString respect noEmit", async () => { - const { api, fs } = spawnAPIWithFS({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { noEmit: true } }), - "/src/index.ts": "export const value = 1;", - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - assert.deepEqual(await project.program.emit(), { - diagnostics: [], - emitSkipped: false, - emittedFiles: [], - }); - assert.deepEqual(await project.program.emitToString(), { - diagnostics: [], - emitSkipped: false, - outputFiles: new Map(), - }); - assert.equal(fs.readFile?.("/src/index.js"), undefined); - } - finally { - await api.close(); - } - }); - - test("emit rejects unknown files and invalid emitOnly values", async () => { - const api = spawnAPI({ ...files }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - - let error: unknown; - try { - await project.program.getJavaScriptEmit(["/src/missing.ts"]); - } - catch (e) { - error = e; - } - assert.match(String(error), /source file not found/); - - error = undefined; - try { - await project.program.emitToString(3 as EmitOnly); - } - catch (e) { - error = e; - } - assert.match(String(error), /invalid emitOnly value/); - } - finally { - await api.close(); - } - }); - - // @sync-skip-block-start - test("emit reports async VFS write failures as diagnostics", async () => { - const fs = createVirtualFileSystem({ ...files }); - fs.writeFile = () => { - throw new Error("write failed"); - }; - const api = new API({ - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), - fs, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const result = await project.program.emit(); - assert.deepEqual(result.emittedFiles, []); - assert.ok(result.diagnostics.some(d => d.text.includes("write failed"))); - } - finally { - await api.close(); - } - }); - // @sync-skip-block-end -}); - -test("Benchmarks", async () => { - await runBenchmarks({ singleIteration: true }); -}); - -describe("Timing", () => { - test("collects combined client, server, and transport timing info", async () => { - const api = new API({ - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), - fs: createVirtualFileSystem({ ...defaultFiles }), - collectTiming: true, - }); - try { - // Baseline: enabled, but nothing measured yet. - let info = await api.getTimingInfo(); - assert.equal(info.enabled, true); - assert.equal(info.totals.requestCount, 0); - assert.equal(info.recentRequests.length, 0); - - // Exercise a JSON request and a binary source-file request. - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - - // Client-side timing: round-trip latency and byte counts. - info = await api.getTimingInfo(); - assert.ok(info.totals.requestCount >= 2, "expected at least two measured requests"); - assert.ok(info.totals.bytesSent > 0); - assert.ok(info.totals.bytesReceived > 0); - assert.ok(info.totals.roundTripMs >= 0); - - // Server-side timing is folded into the same snapshot. - assert.ok(info.totals.serverTimeMs >= 0); - assert.ok(info.totals.transportOverheadMs >= 0); - assert.equal( - info.totals.transportOverheadMs, - Math.max(0, info.totals.roundTripMs - info.totals.serverTimeMs), - ); - - // The ring buffer retains at most the 5 most recent requests. - assert.ok(info.recentRequests.length > 0); - assert.ok(info.recentRequests.length <= 5); - assert.ok(info.recentRequests.length <= info.totals.requestCount); - - for (const r of info.recentRequests) { - assert.ok(r.roundTripMs >= 0); - assert.ok(r.bytesSent >= 0); - assert.ok(r.bytesReceived >= 0); - assert.ok(typeof r.method === "string" && r.method.length > 0); - assert.equal(typeof r.timestamp, "number"); - // Transport overhead is present exactly when server time is. - assert.equal(r.transportOverheadMs === undefined, r.serverTimeMs === undefined); - if (r.serverTimeMs !== undefined) { - assert.ok(r.serverTimeMs >= 0); - assert.equal(r.transportOverheadMs, Math.max(0, r.roundTripMs - r.serverTimeMs)); - } - } - - // Server processing time is folded in for the recent requests. - assert.ok(info.recentRequests.every(r => r.serverTimeMs !== undefined), "server time should be reported"); - - // Reset clears totals and history on both client and server. - await api.resetTimingInfo(); - info = await api.getTimingInfo(); - assert.equal(info.enabled, true); - assert.equal(info.totals.requestCount, 0); - assert.equal(info.totals.serverTimeMs, 0); - assert.equal(info.totals.transportOverheadMs, 0); - assert.equal(info.recentRequests.length, 0); - } - finally { - await api.close(); - } - }); - - test("tracks source-file node materialization", async () => { - const api = new API({ - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), - fs: createVirtualFileSystem({ ...defaultFiles }), - collectTiming: true, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = await project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - - // The source file node itself is pre-cached, so before walking the - // tree no descendant nodes have been materialized. - let info = await api.getTimingInfo(); - const before = info.totals.nodesMaterialized; - assert.equal(info.totals.sourceFilesFetched, 1, "one source file was fetched"); - assert.ok(info.totals.nodesFetched > 0, "the fetched file contributes materializable nodes"); - assert.equal(info.totals.nodesMaterialized, 0, "nothing walked yet, so no nodes materialized"); - - // Walk the whole tree to force lazy materialization of every node. - let visited = 0; - sourceFile.forEachChild(function visit(node) { - visited++; - node.forEachChild(visit); - }); - assert.ok(visited > 0, "expected to visit at least one node"); - - info = await api.getTimingInfo(); - assert.ok( - info.totals.nodesMaterialized > before, - "walking the tree should materialize nodes", - ); - assert.ok( - info.totals.nodesMaterialized >= visited, - "every visited node should have been materialized", - ); - - // A full walk materializes (nearly) every fetched node, so the share - // of fetched nodes materialized should be substantial. - assert.equal(info.totals.sourceFilesFetched, 1); - assert.ok( - info.totals.nodesMaterialized > 0 - && info.totals.nodesMaterialized <= info.totals.nodesFetched, - "materialized nodes should be in (0, nodesFetched]", - ); - - // Reset clears materialization totals along with the rest. - await api.resetTimingInfo(); - info = await api.getTimingInfo(); - assert.equal(info.totals.nodesMaterialized, 0); - assert.equal(info.totals.sourceFilesFetched, 0); - assert.equal(info.totals.nodesFetched, 0); - } - finally { - await api.close(); - } - }); - - test("is disabled by default", async () => { - const api = spawnAPI(); - try { - await api.parseConfigFile("/tsconfig.json"); - const info = await api.getTimingInfo(); - assert.equal(info.enabled, false); - assert.equal(info.totals.requestCount, 0); - assert.equal(info.totals.nodesMaterialized, 0); - assert.equal(info.totals.sourceFilesFetched, 0); - assert.equal(info.totals.nodesFetched, 0); - assert.equal(info.recentRequests.length, 0); - } - finally { - await api.close(); - } - }); -}); - -describe("runWithTemporaryFileUpdate", () => { - test("temporary file update is visible in the callback and reverted afterward", async () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/index.ts": `export const x: number = 1;`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - - // The original content type-checks cleanly. - const baseDiags = await project.program.getSemanticDiagnostics("/src/index.ts"); - assert.equal(baseDiags.length, 0); - - // Keep a newer snapshot active to verify any active snapshot can be the base. - const latestSnapshot = await api.updateSnapshot(); - assert.notEqual(latestSnapshot.id, snapshot.id); - - // Inside the callback, the file has the temporary (erroneous) content. - let errorCount = -1; - await api.runWithTemporaryFileUpdate(snapshot, "/src/index.ts", `export const x: string = 1;`, async tempSnapshot => { - const tempProject = tempSnapshot.getProject("/tsconfig.json")!; - const diags = await tempProject.program.getSemanticDiagnostics("/src/index.ts"); - errorCount = diags.length; - }); - assert.ok(errorCount > 0, "temporary content should produce a semantic error inside the callback"); - - // The original snapshot is unaffected by the temporary update. - const afterDiags = await project.program.getSemanticDiagnostics("/src/index.ts"); - assert.equal(afterDiags.length, 0); - - // Subsequent regular updates still work and diff against the real latest snapshot. - const snapshot2 = await api.updateSnapshot(); - const project2 = snapshot2.getProject("/tsconfig.json")!; - const diags2 = await project2.program.getSemanticDiagnostics("/src/index.ts"); - assert.equal(diags2.length, 0); - } - finally { - await api.close(); - } - }); -}); - -function spawnAPIWithFS(files: Record = { ...defaultFiles }): { api: API; fs: FileSystem; } { - const fs = createVirtualFileSystem(files); - const api = new API({ - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), - fs, - }); - return { api, fs }; -} - -/** Returns `{ pos, end }` for the nth (0-based, default 0) occurrence of `searchString` in `source`. */ -function rangeOf(source: string, searchString: string, occurrence: number = 0): { pos: number; end: number; } { - let index = -1; - for (let i = 0; i <= occurrence; i++) { - index = source.indexOf(searchString, index + 1); - if (index === -1) { - throw new Error(`Occurrence ${occurrence} of "${searchString}" not found in source`); - } - } - return { pos: index, end: index + searchString.length }; -} - -function withoutFormattingContext(value: T): T { - const formattingKeys = new Set(["startPosition", "endPosition", "sourceLines"]); - return JSON.parse(JSON.stringify(value, (key, item) => formattingKeys.has(key) ? undefined : item)) as T; -} - -function applyTextEdits(source: string, edits: readonly TextEdit[]): string { - const sorted = [...edits].sort((a, b) => b.pos - a.pos); - let result = source; - for (const edit of sorted) { - result = result.slice(0, edit.pos) + edit.newText + result.slice(edit.end); - } - return result; -} +import { + type __String, + cast, + escapeLeadingUnderscores, + type Expression, + getJSDocTags, + getSynthesizedDeepClone, + InternalSymbolName, + isCallExpression, + isExpressionStatement, + isFunctionDeclaration, + isIdentifier, + isImportDeclaration, + isInterfaceDeclaration, + isJSDocParameterTag, + isNamedImports, + isReturnStatement, + isShorthandPropertyAssignment, + isStringLiteral, + isTemplateHead, + isTemplateMiddle, + isTemplateTail, + isTypeAliasDeclaration, + isTypeNode, + isVariableDeclarationList, + isVariableStatement, + type Node, + type NodeArray, + NodeFlags, + SyntaxKind, + unescapeLeadingUnderscores, +} from "@typescript/typescript/unstable/ast"; +import { + createArrayTypeNode, + createFunctionTypeNode, + createIdentifier, + createKeywordTypeNode, + createNumericLiteral, + createParameterDeclaration, + createToken, + createTypeAliasDeclaration, + createTypeReferenceNode, + createUnionTypeNode, + createVariableDeclaration, + createVariableDeclarationList, + createVariableStatement, +} from "@typescript/typescript/unstable/ast/factory"; +import { visitEachChild } from "@typescript/typescript/unstable/ast/visitor"; +import { + API, + type BigIntLiteralType, + CheckFlags, + type ConditionalType, + DiagnosticCategory, + EmitOnly, + type FreshableType, + type ImportAdderAction, + type IndexedAccessType, + type IndexType, + type InterfaceType, + type IntrinsicType, + isErrorType, + type LiteralType, + ModifierFlags, + ModuleKind, + ObjectFlags, + type Signature, + SignatureKind, + type StringMappingType, + SymbolFlags, + type TemplateLiteralType, + type TextEdit, + TypeFlags, + TypeFormatFlags, + type TypeParameter, + TypePredicateKind, + type TypeReference, + type UnionOrIntersectionType, +} from "@typescript/typescript/unstable/async"; // @sync: } from "@typescript/typescript/unstable/sync"; +import { createVirtualFileSystem } from "@typescript/typescript/unstable/fs"; +import type { FileSystem } from "@typescript/typescript/unstable/fs"; +import assert from "node:assert"; +import { globSync } from "node:fs"; +import { resolve } from "node:path"; +import { + describe, + test, +} from "node:test"; +import { fileURLToPath } from "node:url"; +import { isSignatureDeclaration } from "../../src/ast/is.ts"; +import { runBenchmarks } from "./api.bench.ts"; +import { + defaultFiles, + spawnAPI, +} from "./api.testUtils.ts"; + +describe("API", () => { + test("parseCommandLine", async () => { + const api = spawnAPI(); + try { + const commandLine = await api.parseCommandLine([ + "--strict", + "--outDir", + "dist", + "/src/index.ts", + ]); + assert.deepEqual(commandLine.fileNames, ["/src/index.ts"]); + assert.equal(commandLine.options.strict, true); + assert.equal( + resolve(commandLine.options.outDir!), + resolve(fileURLToPath(new URL("../../../../", import.meta.url)), "dist"), + ); + assert.deepEqual(commandLine.raw, { + strict: true, + outDir: "dist", + }); + assert.deepEqual(commandLine.errors, []); + } + finally { + await api.close(); + } + }); + + test("parseCommandLine reports diagnostics", async () => { + const api = spawnAPI(); + try { + const commandLine = await api.parseCommandLine(["--notAnOption"]); + assert.deepEqual(commandLine.fileNames, []); + assert.equal(commandLine.errors.length, 1); + assert.equal(commandLine.errors[0].code, 5023); + } + finally { + await api.close(); + } + }); + + test("readConfigFile", async () => { + const api = spawnAPI({ + "/tsconfig.json": `{ + // Comments and trailing commas are supported. + "compilerOptions": { "strict": true, }, + }`, + }); + try { + const result = await api.readConfigFile("/tsconfig.json"); + assert.deepEqual(result, { + config: { compilerOptions: { strict: true } }, + }); + } + finally { + await api.close(); + } + }); + + test("readConfigFile reports read and parse errors", async () => { + const api = spawnAPI({ + "/invalid.json": `{ "compilerOptions": { "strict": true,, } }`, + }); + try { + const invalid = await api.readConfigFile("/invalid.json"); + assert.deepEqual(invalid.config, { compilerOptions: { strict: true } }); + assert.ok(invalid.error); + + const missing = await api.readConfigFile("/missing.json"); + assert.deepEqual(missing.config, {}); + assert.equal(missing.error?.code, 5083); + } + finally { + await api.close(); + } + }); + + test("parseJsonConfigFileContent with configDirectory", async () => { + const api = spawnAPI(); + try { + const config = await api.parseJsonConfigFileContent( + { + compilerOptions: { strict: true }, + files: ["index.ts"], + }, + { configDirectory: "/src" }, + ); + assert.deepEqual(config.fileNames, ["/src/index.ts"]); + assert.equal(config.options.strict, true); + assert.equal("configFilePath" in config.options, false); + assert.equal(config.compileOnSave, false); + assert.deepEqual(config.errors, []); + } + finally { + await api.close(); + } + }); + + test("parseJsonConfigFileContent accepts non-object JSON", async () => { + const api = spawnAPI(); + try { + const config = await api.parseJsonConfigFileContent(null, { configDirectory: "/src" }); + assert.deepEqual(config.fileNames, ["/src/index.ts", "/src/foo.ts"]); + assert.deepEqual(config.errors, []); + } + finally { + await api.close(); + } + }); + + test("parseJsonConfigFileContent with configFileName", async () => { + const api = spawnAPI(); + try { + const config = await api.parseJsonConfigFileContent( + { + compilerOptions: { strict: true }, + files: ["index.ts"], + }, + { configFileName: "/src/tsconfig.json" }, + ); + assert.deepEqual(config.fileNames, ["/src/index.ts"]); + assert.equal(config.options.strict, true); + assert.equal(config.options.configFilePath, "/src/tsconfig.json"); + assert.deepEqual(config.errors, []); + } + finally { + await api.close(); + } + }); + + test("parseJsonConfigFileContent preserves raw config", async () => { + const api = spawnAPI(); + try { + const input = { + compileOnSave: true, + customSetting: { enabled: true }, + files: ["index.ts"], + }; + const config = await api.parseJsonConfigFileContent(input, { configDirectory: "/src" }); + assert.deepEqual(config.raw, input); + assert.equal(config.compileOnSave, true); + } + finally { + await api.close(); + } + }); + + test("parseJsonConfigFileContent preserves an empty files list", async () => { + const api = spawnAPI(); + try { + const config = await api.parseJsonConfigFileContent( + { files: [] }, + { configDirectory: "/src" }, + ); + assert.deepEqual(config.fileNames, []); + assert.equal(config.errors.length, 1); + assert.equal(config.errors[0].code, 18002); + } + finally { + await api.close(); + } + }); + + test("parseJsonConfigFileContent reports null array elements", async () => { + const api = spawnAPI(); + try { + const config = await api.parseJsonConfigFileContent( + { files: [null], include: [null], exclude: [null] }, + { configDirectory: "/src" }, + ); + assert.equal(config.errors.length, 3); + assert.ok(config.errors.every(diagnostic => diagnostic.code === 5024)); + } + finally { + await api.close(); + } + }); + + test("transpile", async () => { + const api = spawnAPI({ + "/input.ts": "export const x: number = 1;", + }); + try { + const moduleOutput = await api.transpileModule("export const x: number = 1;", { + compilerOptions: { module: ModuleKind.CommonJS }, + }); + assert.match(moduleOutput.outputText, /exports\.x = 1/); + + const isolatedDeclarationModuleOutput = await api.transpileModule("export const x: number = 1;", { + compilerOptions: { declaration: true, isolatedDeclarations: true }, + reportDiagnostics: true, + }); + assert.equal(isolatedDeclarationModuleOutput.diagnostics?.length ?? 0, 0); + + const moduleFileOutput = await api.transpileModuleFromFile("/input.ts", { + compilerOptions: { module: ModuleKind.CommonJS }, + }); + assert.match(moduleFileOutput.outputText, /exports\.x = 1/); + + const declarationOutput = await api.transpileDeclaration("export const x: number = 1;"); + assert.equal(declarationOutput.outputText, "export declare const x: number;\n"); + + const windowsDeclarationOutput = await api.transpileDeclaration("export const x: number = 1;", { + fileName: "C:/Users/me/project/input.ts", + }); + assert.equal(windowsDeclarationOutput.outputText, "export declare const x: number;\n"); + + const declarationFileOutput = await api.transpileDeclarationFromFile("/input.ts"); + assert.equal(declarationFileOutput.outputText, "export declare const x: number;\n"); + } + finally { + await api.close(); + } + }); + + test("parseConfigFile", async () => { + const api = spawnAPI(); + try { + const config = await api.parseConfigFile("/tsconfig.json"); + assert.deepEqual(config.fileNames, ["/src/index.ts", "/src/foo.ts"]); + assert.deepEqual(config.options, { configFilePath: "/tsconfig.json" }); + assert.equal(config.compileOnSave, false); + assert.equal(config.typeAcquisition, undefined); + assert.equal(config.projectReferences, undefined); + } + finally { + await api.close(); + } + }); + + test("parseConfigFile includes projectReferences", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ references: [{ path: "./harness" }, { path: "./server" }] }), + }); + try { + const config = await api.parseConfigFile("/tsconfig.json"); + assert.deepEqual(config.fileNames, []); + assert.deepEqual(config.options, { configFilePath: "/tsconfig.json" }); + assert.deepEqual(config.projectReferences, [ + { circular: false, originalPath: "./harness", path: "/harness" }, + { circular: false, originalPath: "./server", path: "/server" }, + ]); + } + finally { + await api.close(); + } + }); + + test("parseConfigFile preserves raw config", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ + compileOnSave: true, + customSetting: { enabled: true }, + files: ["/src/index.ts"], + }), + }); + try { + const config = await api.parseConfigFile("/tsconfig.json"); + assert.equal(config.compileOnSave, true); + assert.deepEqual(config.raw, { + compileOnSave: true, + customSetting: { enabled: true }, + files: ["/src/index.ts"], + }); + } + finally { + await api.close(); + } + }); + + test("parseConfigFile includes compileOnSave", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compileOnSave: true }), + }); + try { + const config = await api.parseConfigFile("/tsconfig.json"); + assert.equal(config.compileOnSave, true); + } + finally { + await api.close(); + } + }); + + test("parseConfigFile includes typeAcquisition", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ typeAcquisition: { enable: true, include: ["jquery"] } }), + }); + try { + const config = await api.parseConfigFile("/tsconfig.json"); + assert.equal(config.typeAcquisition?.enable, true); + assert.deepEqual(config.typeAcquisition?.include, ["jquery"]); + } + finally { + await api.close(); + } + }); +}); + +// @sync-skip-block-start +describe("API - automatic batching", () => { + test("batches multiple concurrent requests into one automatically", async () => { + const api = new API({ + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + fs: createVirtualFileSystem(defaultFiles), + collectTiming: true, + }); + try { + await api.parseCommandLine([]); // initialize API + await api.resetTimingInfo(); + let { totals: { requestCount } } = await api.getTimingInfo(); + assert.equal(requestCount, 0); + const [parseCommandLine, readConfigFile] = await Promise.all([ + api.parseCommandLine(["--strict"]), + api.readConfigFile("/tsconfig.json"), + ]); + ({ totals: { requestCount } } = await api.getTimingInfo()); + assert.equal(requestCount, 1); + + assert.equal(parseCommandLine.options.strict, true); + assert.deepStrictEqual(readConfigFile.config, {}); + } + finally { + api.close(); + } + }); +}); + +describe("API - batchContext", () => { + test("holds requests until disposal", async () => { + const api = spawnAPI(); + try { + await api.parseCommandLine([]); + + const requests = await (async () => { + using _ = api.batchContext(); + const requests = [ + api.parseCommandLine(["--strict"]), + api.readConfigFile("/tsconfig.json"), + ] as const; + let settled = false; + void Promise.all(requests).then(() => { + settled = true; + }); + + await new Promise(resolve => setImmediate(resolve)); + assert.equal(settled, false, "requests should remain pending inside the batch context"); + return requests; + })(); + + const [commandLine, config] = await Promise.all(requests); + assert.equal(commandLine.options.strict, true); + assert.deepEqual(config.config, {}); + } + finally { + await api.close(); + } + }); + + test("settles an item error without dropping a sibling result", async () => { + const src = `export const value: string = "";`; + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("value:")); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + + const requests = await (async () => { + using _ = api.batchContext(); + return [ + project.checker.getTypeArguments(type as unknown as TypeReference), + project.checker.getStringType(), + ] as const; + })(); + + await assert.rejects(requests[0], /panic:/); + assert.ok((await requests[1]).flags & TypeFlags.String); + } + finally { + await api.close(); + } + }); +}); +// @sync-skip-block-end + +describe("Checker - getImmediateAliasedSymbol", () => { + test("resolves one level of alias indirection", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/foo.ts": `export const foo = 42;`, + "/src/main.ts": `import { foo } from "./foo";\nexport const usage = foo;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = `import { foo } from "./foo";`.indexOf("foo }"); + const aliasSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(aliasSymbol); + const aliased = await project.checker.getImmediateAliasedSymbol(aliasSymbol); + assert.ok(aliased, "Should resolve the immediate aliased symbol"); + assert.equal(aliased.name, "foo"); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getTargetSymbol", () => { + test("gets the target symbol of instantiated symbol", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +class Base { + private value!: T; +} +class Alpha extends Base {} +class Bravo extends Base {} + +declare function test(): void; +test(); +test(); +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const nodes: Array = []; + sourceFile.forEachChild(node => { + if (isExpressionStatement(node) && isCallExpression(node.expression) && node.expression.typeArguments) { + nodes.push(node.expression.typeArguments[0]); + } + }); + const aType = await project.checker.getTypeAtLocation(nodes[0]); + const bType = await project.checker.getTypeAtLocation(nodes[1]); + const aProperty = (await project.checker.getPropertiesOfType(aType))[0]; + const bProperty = (await project.checker.getPropertiesOfType(bType))[0]; + assert.ok(aProperty); + assert.ok(bProperty); + assert.equal(aProperty === bProperty, false); + assert.equal(await project.checker.getTargetSymbol(aProperty) === await project.checker.getTargetSymbol(bProperty), true); + } + finally { + await api.close(); + } + }); +}); + +describe("Snapshot", () => { + test("updateSnapshot returns snapshot with projects", async () => { + const api = spawnAPI(); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + assert.ok(snapshot); + assert.ok(snapshot.id); + assert.ok(snapshot.getProjects().length > 0); + assert.ok(snapshot.getProject("/tsconfig.json")); + } + finally { + await api.close(); + } + }); + + test("project exposes parsedCommandLine", async () => { + const api = spawnAPI({ + ...defaultFiles, + "/tsconfig.json": JSON.stringify({ compileOnSave: true }), + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + assert.deepEqual(project.parsedCommandLine.fileNames, ["/src/index.ts", "/src/foo.ts"]); + assert.deepEqual(project.parsedCommandLine.options, { configFilePath: "/tsconfig.json" }); + assert.equal(project.parsedCommandLine.compileOnSave, true); + assert.deepEqual(project.rootFiles, project.parsedCommandLine.fileNames); + assert.deepEqual(project.compilerOptions, project.parsedCommandLine.options); + } + finally { + await api.close(); + } + }); + + test("getSymbolAtPosition", async () => { + const api = spawnAPI(); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = await project.checker.getSymbolAtPosition("/src/index.ts", 9); + assert.ok(symbol); + assert.equal(symbol.name, "foo"); + assert.ok(symbol.flags & SymbolFlags.Alias); + } + finally { + await api.close(); + } + }); + + test("getSymbolAtLocation", async () => { + const api = spawnAPI(); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + const node = cast( + cast(sourceFile.statements[0], isImportDeclaration).importClause?.namedBindings, + isNamedImports, + ).elements[0].name; + assert.ok(node); + const symbol = await project.checker.getSymbolAtLocation(node); + assert.ok(symbol); + assert.equal(symbol.name, "foo"); + assert.ok(symbol.flags & SymbolFlags.Alias); + } + finally { + await api.close(); + } + }); + + test("getSymbolOfSourceFile", async () => { + const api = spawnAPI(); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const moduleSymbol = await project.checker.getSymbolOfSourceFile("/src/foo.ts"); + assert.ok(moduleSymbol); + const exports = await moduleSymbol.getExports(); + assert.ok(exports.has(escapeLeadingUnderscores("foo"))); + } + finally { + await api.close(); + } + }); + + test("getSymbolOfSourceFile returns undefined for a non-module file", async () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/script.ts": `const x = 1;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = await project.checker.getSymbolOfSourceFile("/src/script.ts"); + assert.equal(symbol, undefined); + } + finally { + await api.close(); + } + }); + + test("getSymbolOfSourceFile batched", async () => { + const api = spawnAPI(); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbols = await project.checker.getSymbolOfSourceFile(["/src/index.ts", "/src/foo.ts"]); + assert.equal(symbols.length, 2); + assert.ok(symbols[0]); + assert.ok(symbols[1]); + assert.strictEqual(symbols[1], await project.checker.getSymbolOfSourceFile("/src/foo.ts")); + } + finally { + await api.close(); + } + }); + + test("getTypeOfSymbol", async () => { + const api = spawnAPI(); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = await project.checker.getSymbolAtPosition("/src/index.ts", 9); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.ok(type.flags & TypeFlags.NumberLiteral); + } + finally { + await api.close(); + } + }); + + test("getNonMissingTypeOfSymbol", async () => { + const api = spawnAPI({ + "/tsconfig-one.json": JSON.stringify({ + compilerOptions: { + exactOptionalPropertyTypes: true, + strict: true, + }, + }), + "/tsconfig-two.json": JSON.stringify({ + compilerOptions: { + exactOptionalPropertyTypes: false, + strict: true, + }, + }), + "/src/index.ts": "const x: Partial<{ a: string }> = {};", + }); + + try { + // when `"exactOptionalPropertyTypes": true` + const snapshot1 = await api.updateSnapshot({ openProject: "/tsconfig-one.json" }); + const project1 = snapshot1.getProject("/tsconfig-one.json")!; + const type1 = await project1.checker.getTypeAtPosition("/src/index.ts", 7); + assert.ok(type1); + const symbol1 = await project1.checker.getPropertyOfType(type1, "a"); + assert.ok(symbol1); + const propertyType1 = await project1.checker.getTypeOfSymbol(symbol1); + assert.ok(propertyType1); + // getTypeOfSymbol returns 'string | undefined' + assert.ok(propertyType1.isUnionType()); + const propertyType2 = await project1.checker.getNonMissingTypeOfSymbol(symbol1); + assert.ok(propertyType2); + // getNonMissingTypeOfSymbol returns 'string' + assert.ok(!propertyType2.isUnionType()); + assert.ok(propertyType2.flags & TypeFlags.String); + + // when `"exactOptionalPropertyTypes": false` + const snapshot2 = await api.updateSnapshot({ openProject: "/tsconfig-two.json" }); + const project2 = snapshot2.getProject("/tsconfig-two.json")!; + const type2 = await project2.checker.getTypeAtPosition("/src/index.ts", 7); + assert.ok(type2); + const symbol2 = await project2.checker.getPropertyOfType(type2, "a"); + assert.ok(symbol2); + const propertyType3 = await project2.checker.getTypeOfSymbol(symbol2); + assert.ok(propertyType3); + // getTypeOfSymbol returns 'string | undefined' + assert.ok(propertyType3.isUnionType()); + const propertyType4 = await project2.checker.getNonMissingTypeOfSymbol(symbol2); + assert.ok(propertyType4); + // getNonMissingTypeOfSymbol returns 'string | undefined' + assert.ok(propertyType4.isUnionType()); + } + finally { + await api.close(); + } + }); +}); + +describe("LanguageService - imports", () => { + test("getImportEditsForSymbols adds a named import", async () => { + const source = `const value = foo;\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": source, + "/src/foo.ts": `export const foo = 1;\n`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = await project.checker.getSymbolAtPosition("/src/foo.ts", "export const ".length); + assert.ok(symbol); + + const edits = await project.languageService.getImportEditsForSymbols("/src/index.ts", [await symbol.getExportSymbol()]); + + assert.equal(applyTextEdits(source, edits), `import { foo } from "./foo";\n\nconst value = foo;\n`); + } + finally { + await api.close(); + } + }); + + test("getImportAdderEdits coalesces multiple importSymbol actions", async () => { + const source = `const value = foo + bar;\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": source, + "/src/foo.ts": `export const foo = 1;\nexport const bar = 2;\n`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const foo = await project.checker.getSymbolAtPosition("/src/foo.ts", "export const ".length); + const bar = await project.checker.getSymbolAtPosition("/src/foo.ts", "export const foo = 1;\nexport const ".length); + assert.ok(foo); + assert.ok(bar); + + const edits = await project.languageService.getImportAdderEdits("/src/index.ts", [ + { kind: "importSymbol", symbol: await foo.getExportSymbol() }, + { kind: "importSymbol", symbol: await bar.getExportSymbol() }, + ]); + + assert.equal(applyTextEdits(source, edits), `import { bar, foo } from "./foo";\n\nconst value = foo + bar;\n`); + } + finally { + await api.close(); + } + }); + + test("getImportAdderEdits adds to an existing import", async () => { + const source = `import { foo } from "./foo";\nconst value = foo + bar;\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": source, + "/src/foo.ts": `export const foo = 1;\nexport const bar = 2;\n`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const bar = await project.checker.getSymbolAtPosition("/src/foo.ts", "export const foo = 1;\nexport const ".length); + assert.ok(bar); + + const edits = await project.languageService.getImportAdderEdits("/src/index.ts", [ + { kind: "importSymbol", symbol: await bar.getExportSymbol() }, + ]); + + assert.equal(applyTextEdits(source, edits), `import { bar, foo } from "./foo";\nconst value = foo + bar;\n`); + } + finally { + await api.close(); + } + }); + + test("getImportAdderEdits returns no edits for non-exported symbols", async () => { + const source = `const value = local;\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": source, + "/src/foo.ts": `const local = 1;\n`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = await project.checker.getSymbolAtPosition("/src/foo.ts", "const ".length); + assert.ok(symbol); + + const edits = await project.languageService.getImportAdderEdits("/src/index.ts", [ + { kind: "importSymbol", symbol }, + ]); + + assert.deepEqual(edits, []); + } + finally { + await api.close(); + } + }); + + test("getImportAdderEdits rejects invalid actions", async () => { + const api = spawnAPI(); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = await project.checker.getSymbolAtPosition("/src/foo.ts", 13); + assert.ok(symbol); + + await assert.rejects( // @sync: assert.throws( + () => project.languageService.getImportAdderEdits("/src/index.ts", [{ kind: "unknown", symbol: symbol.id } as unknown as ImportAdderAction]), + /Debug Failure\. Illegal value: "unknown"/, + ); + await assert.rejects( // @sync: assert.throws( + () => project.languageService.getImportAdderEdits("/src/index.ts", [{ kind: "importSymbol", symbol: { ...symbol, id: 999_999_999 } } as unknown as ImportAdderAction]), + /symbol handle \d+ not found/, + ); + } + finally { + await api.close(); + } + }); +}); + +describe("LanguageService - getCompletionsAtPosition", () => { + test("returns member completions after a dot", async () => { + const src = `\nconst obj = { name: "hello", age: 42 };\nobj.\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + // Position right after "obj." — member completion trigger + const pos = src.indexOf("obj.") + "obj.".length; + const completions = await project.languageService.getCompletionsAtPosition("/src/main.ts", pos, { triggerCharacter: "." }); + assert.ok(completions, "Expected completions to be returned"); + assert.ok(completions.entries.length > 0, "Expected at least one completion entry"); + assert.ok(completions.entries.some(e => e.name === "name"), "Expected 'name' property in completions"); + assert.ok(completions.entries.some(e => e.name === "age"), "Expected 'age' property in completions"); + assert.ok(completions.entries.every(e => e.symbol === undefined), "Expected no symbol information"); + } + finally { + await api.close(); + } + }); + + test("completion entries include sortText", async () => { + const src = `\nconst obj = { value: 1 };\nobj.\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("obj.") + "obj.".length; + const completions = await project.languageService.getCompletionsAtPosition("/src/main.ts", pos, { triggerCharacter: "." }); + assert.ok(completions); + assert.ok(completions.entries.length > 0); + assert.ok(completions.entries.some(e => e.sortText !== undefined), "Expected sortText on all entries"); + } + finally { + await api.close(); + } + }); + + test("returns undefined for a non-existent file", async () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": `export {};`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const completions = await project.languageService.getCompletionsAtPosition("/src/does-not-exist.ts", 0); + assert.equal(completions, undefined, "Expected undefined for non-existent file"); + } + finally { + await api.close(); + } + }); + + test("includeSymbol: true populates symbol on property completions", async () => { + const src = `\nconst obj = { name: "hello", age: 42 };\nobj.\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("obj.") + "obj.".length; + const completions = await project.languageService.getCompletionsAtPosition("/src/main.ts", pos, { triggerCharacter: ".", includeSymbol: true }); + assert.ok(completions, "Expected completions"); + const nameEntry = completions.entries.find(e => e.name === "name"); + assert.ok(nameEntry, "Expected 'name' entry"); + assert.ok(nameEntry.symbol, "Expected symbol to be set on 'name' entry when includeSymbol: true"); + assert.equal(nameEntry.symbol.name, "name", "Symbol name should match completion name"); + } + finally { + await api.close(); + } + }); +}); + +describe("LanguageService - getReferencedSymbolsForNode", () => { + test("getReferencedSymbolsForNode", async () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `function greet(name: string) { return name; }\ngreet("world");`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + const funcDecl = cast(sourceFile.statements[0], isFunctionDeclaration); + const funcName = funcDecl.name!; + const refs = await project.languageService.getReferencedSymbolsForNode(funcName, funcName.pos); + assert.ok(refs.length > 0); + // Each entry should have a definition and references + const entry = refs[0]; + assert.ok(entry.definition); + assert.ok(entry.references.length > 0); + } + finally { + await api.close(); + } + }); +}); + +describe("LanguageService - getSignatureUsage", () => { + test("getSignatureUsage", async () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `function greet(name: string) { return name; }\ngreet("world");`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + const funcDecl = cast(sourceFile.statements[0], isFunctionDeclaration); + const usages = await project.languageService.getSignatureUsage(funcDecl); + assert.ok(usages.length > 0); + // The call site should have a call expression + const usage = usages.find(u => u.call !== undefined); + assert.ok(usage, "Expected at least one usage with a call expression"); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getApparentType", () => { + test("returns the apparent type of a literal type", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const x = "hello" as const;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = `export const x = "hello" as const;`.indexOf("x ="); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.equal(type.isLiteralType(), true); + assert.equal(type.isStringLiteralType(), true); + assert.equal(type.isIntrinsicType(), false); + const apparent = await project.checker.getApparentType(type); + assert.ok(apparent); + assert.ok(apparent.id > 0); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getReducedType", () => { + test("returns the reduced type", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +declare const TaggedError: ( + tag: Tag, +) => new = {}>( + args: { readonly [P in keyof A]: A[P] }, +) => { readonly _tag: Tag } & Readonly; + +class RateLimitError extends TaggedError("RateLimitError")<{ + readonly retryAfter: number; +}> {} + +class QuotaExceededError extends TaggedError("QuotaExceededError")<{ + readonly limit: number; +}> {} + +export type Result = RateLimitError | (RateLimitError & QuotaExceededError);`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const typeAlias = sourceFile.statements.find(isTypeAliasDeclaration); + assert.ok(typeAlias); + const type = await project.checker.getTypeAtLocation(typeAlias); + assert.equal(type.isUnionType(), true); + assert.equal(type.isObjectType(), false); + const reducedType = await project.checker.getReducedType(type); + assert.equal(reducedType.isUnionType(), false); + assert.equal(reducedType.isObjectType(), true); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getMemberInModuleExports", () => { + test("returns a named export when present", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/index.ts": `export const direct = 1;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + const moduleSymbol = await project.checker.getSymbolAtLocation(sourceFile); + assert.ok(moduleSymbol); + const found = await project.checker.getMemberInModuleExports(moduleSymbol, "direct"); + assert.ok(found); + assert.equal(found.name, "direct"); + const missing = await project.checker.getMemberInModuleExports(moduleSymbol, "missing"); + assert.equal(missing, undefined); + } + finally { + await api.close(); + } + }); +}); + +describe("SourceFile", () => { + test("getSourceFileNames returns all program files, not just root files", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ + compilerOptions: { + moduleResolution: "node10", + noLib: true, + }, + }), + "/src/index.ts": `import { foo } from "./foo";\nimport { bar } from "my-lib";\nexport const result = foo + bar;`, + "/src/foo.ts": `export const foo = 42;`, + "/node_modules/my-lib/package.json": JSON.stringify({ name: "my-lib", types: "./index.d.ts" }), + "/node_modules/my-lib/index.d.ts": `export declare const bar: number;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const fileNames = await project.program.getSourceFileNames(); + assert.deepEqual(fileNames, [ + "/src/foo.ts", + "/node_modules/my-lib/index.d.ts", + "/src/index.ts", + ]); + } + finally { + await api.close(); + } + }); + + test("source file metadata identifies external library and default library files", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ + compilerOptions: { + moduleResolution: "node10", + }, + }), + "/src/index.ts": `import { bar } from "my-lib";\nexport const result = bar;`, + "/node_modules/my-lib/package.json": JSON.stringify({ name: "my-lib", types: "./index.d.ts" }), + "/node_modules/my-lib/index.d.ts": `export declare const bar: number;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const program = project.program; + + const index = await program.getSourceFile("/src/index.ts"); + assert.ok(index); + assert.equal(await program.isSourceFileFromExternalLibrary(index), false); + assert.equal(await program.isSourceFileDefaultLibrary(index), false); + + const lib = await program.getSourceFile("/node_modules/my-lib/index.d.ts"); + assert.ok(lib); + assert.equal(await program.isSourceFileFromExternalLibrary(lib), true); + assert.equal(await program.isSourceFileDefaultLibrary(lib), false); + + const fileNames = await program.getSourceFileNames(); + const defaultLibName = fileNames.find(name => name.endsWith("lib.d.ts") || name.includes("/lib.")); + assert.ok(defaultLibName, "expected a default library file in the program"); + const defaultLib = await program.getSourceFile(defaultLibName); + assert.ok(defaultLib); + assert.equal(await program.isSourceFileDefaultLibrary(defaultLib), true); + } + finally { + await api.close(); + } + }); + + test("source file metadata reports implied node format", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ + compilerOptions: { + module: "nodenext", + moduleResolution: "nodenext", + }, + }), + "/src/index.ts": `export const x = 1;`, + "/src/esm.mts": `export const e = 1;`, + "/src/cjs.cts": `export const c = 1;`, + "/esm/package.json": JSON.stringify({ type: "module" }), + "/esm/index.ts": `export const m = 1;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const program = snapshot.getProject("/tsconfig.json")!.program; + + const mts = await program.getSourceFile("/src/esm.mts"); + assert.ok(mts); + assert.equal((await program.getSourceFileMetadata(mts.fileName))?.impliedNodeFormat, ModuleKind.ESNext); + assert.equal((await program.getSourceFileMetadataByPath(mts.path))?.impliedNodeFormat, ModuleKind.ESNext); + + const cts = await program.getSourceFile("/src/cjs.cts"); + assert.ok(cts); + assert.equal((await program.getSourceFileMetadata(cts.fileName))?.impliedNodeFormat, ModuleKind.CommonJS); + + // A plain .ts file with no nearby `"type": "module"` is CommonJS. + const index = await program.getSourceFile("/src/index.ts"); + assert.ok(index); + assert.equal((await program.getSourceFileMetadata(index.fileName))?.impliedNodeFormat, ModuleKind.CommonJS); + + // A plain .ts file under a `"type": "module"` package is ESM. + const esmIndex = await program.getSourceFile("/esm/index.ts"); + assert.ok(esmIndex); + assert.equal((await program.getSourceFileMetadata(esmIndex.fileName))?.impliedNodeFormat, ModuleKind.ESNext); + } + finally { + await api.close(); + } + }); + + test("file properties", async () => { + const api = spawnAPI(); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); + + assert.ok(sourceFile); + assert.equal(sourceFile.text, defaultFiles["/src/index.ts"]); + assert.equal(sourceFile.fileName, "/src/index.ts"); + } + finally { + await api.close(); + } + }); + + test("extended data", async () => { + const api = spawnAPI(); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); + + assert.ok(sourceFile); + let nodeCount = 1; + sourceFile.forEachChild(function visit(node) { + if (isTemplateHead(node)) { + assert.equal(node.text, "head "); + assert.equal(node.rawText, "head "); + assert.equal(node.templateFlags, 0); + } + else if (isTemplateMiddle(node)) { + assert.equal(node.text, "middle"); + assert.equal(node.rawText, "middle"); + assert.equal(node.templateFlags, 0); + } + else if (isTemplateTail(node)) { + assert.equal(node.text, " tail"); + assert.equal(node.rawText, " tail"); + assert.equal(node.templateFlags, 0); + } + nodeCount++; + node.forEachChild(visit); + }); + assert.equal(nodeCount, 8); + } + finally { + await api.close(); + } + }); + + test("forEachChild with visitList does not visit array children twice", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ files: ["/input.ts"] }), + "/input.ts": `let arrow = () => {}`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/input.ts"); + + assert.ok(sourceFile); + + const visited: { kind: SyntaxKind; pos: number; end: number; }[] = []; + (function walk(node: Node): void { + visited.push({ kind: node.kind, pos: node.pos, end: node.end }); + node.forEachChild(walk, (nodes: NodeArray) => { + for (let i = 0; i < nodes.length; i++) { + walk(nodes[i]); + } + return undefined; + }); + })(sourceFile); + + // Each node should be visited exactly once, even when a visitList callback + // is supplied. Previously array children were visited twice. + const seen = new Set(); + for (const { kind, pos, end } of visited) { + const key = `${kind}.${pos}.${end}`; + assert.ok(!seen.has(key), `Node ${key} was visited more than once`); + seen.add(key); + } + } + finally { + await api.close(); + } + }); +}); + +describe("NodeArray", () => { + test("hasTrailingComma", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `declare function foo(...args: any): void;\nfoo("a", "b",);\nfoo("a", "b");`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const statements = sourceFile.statements.filter(isExpressionStatement); + assert.ok(isCallExpression(statements[0].expression)); + assert.equal(statements[0].expression.arguments.hasTrailingComma, true); + assert.ok(isCallExpression(statements[1].expression)); + assert.equal(statements[1].expression.arguments.hasTrailingComma, false); + } + finally { + await api.close(); + } + }); +}); + +test("unicode escapes", async () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/1.ts": `"😃"`, + "/src/2.ts": `"\\ud83d\\ude03"`, + "/src/3.ts": `"\\ud800a\\udc00"`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const expectedTexts = new Map([ + ["/src/1.ts", "😃"], + ["/src/2.ts", "😃"], + ["/src/3.ts", "\ud800a\udc00"], + ]); + + for (const file of expectedTexts.keys()) { + const sourceFile = await project.program.getSourceFile(file); + assert.ok(sourceFile); + + sourceFile.forEachChild(function visit(node) { + if (isStringLiteral(node)) { + assert.equal(node.text, expectedTexts.get(file)); + } + node.forEachChild(visit); + }); + } + } + finally { + await api.close(); + } +}); + +test("template unicode escapes", async () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": "`\\ud800${0}\\udc00`", + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + + let sawHead = false; + let sawTail = false; + sourceFile.forEachChild(function visit(node) { + if (isTemplateHead(node)) { + assert.equal(node.text, "\ud800"); + sawHead = true; + } + else if (isTemplateTail(node)) { + assert.equal(node.text, "\udc00"); + sawTail = true; + } + node.forEachChild(visit); + }); + assert.ok(sawHead); + assert.ok(sawTail); + } + finally { + await api.close(); + } +}); + +test("Object equality", async () => { + const api = spawnAPI(); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + // Same symbol returned from same snapshot's checker + assert.strictEqual( + await project.checker.getSymbolAtPosition("/src/index.ts", 9), + await project.checker.getSymbolAtPosition("/src/index.ts", 10), + ); + } + finally { + await api.close(); + } +}); + +test("Snapshot dispose", async () => { + const api = spawnAPI(); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = await project.checker.getSymbolAtPosition("/src/index.ts", 9); + assert.ok(symbol); + + // Snapshot dispose should release server-side resources + assert.ok(snapshot.isDisposed() === false); + await snapshot.dispose(); + assert.ok(snapshot.isDisposed() === true); + + // After dispose, snapshot methods should throw + assert.throws(() => { + snapshot.getProject("/tsconfig.json"); + }, { + name: "Error", + message: "Snapshot is disposed", + }); + } + finally { + await api.close(); + } +}); + +describe("Multiple snapshots", () => { + test("two snapshots work independently", async () => { + const api = spawnAPI(); + try { + const snap1 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const snap2 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + + // Both can fetch source files + const sf1 = await snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); + const sf2 = await snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); + assert.ok(sf1); + assert.ok(sf2); + + // Disposing one doesn't break the other + await snap1.dispose(); + assert.ok(snap1.isDisposed()); + assert.ok(!snap2.isDisposed()); + + // snap2 still works after snap1 is disposed + const symbol = await snap2.getProject("/tsconfig.json")!.checker.getSymbolAtPosition("/src/index.ts", 9); + assert.ok(symbol); + assert.equal(symbol.name, "foo"); + } + finally { + await api.close(); + } + }); + + test("each snapshot has its own server-side lifecycle", async () => { + const { api, fs } = spawnAPIWithFS(); + try { + const snap1 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + + // Verify initial state + const sf1 = await snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); + assert.ok(sf1); + assert.equal(sf1.text, `export const foo = 42;`); + + // Mutate the file and create a new snapshot with the change + fs.writeFile!("/src/foo.ts", `export const foo = "changed";`); + const snap2 = await api.updateSnapshot({ + fileChanges: { changed: ["/src/foo.ts"] }, + }); + + // snap2 should reflect the updated content + const sf2 = await snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); + assert.ok(sf2); + assert.equal(sf2.text, `export const foo = "changed";`); + + // snap1's source file should still have the original content + assert.equal(sf1.text, `export const foo = 42;`); + + await snap1.dispose(); + + // snap2 still works independently after snap1 is disposed + const symbol = await snap2.getProject("/tsconfig.json")!.checker.getSymbolAtPosition("/src/index.ts", 9); + assert.ok(symbol); + + await snap2.dispose(); + + // Both are disposed, new snapshot works fine with latest content + const snap3 = await api.updateSnapshot(); + const sf3 = await snap3.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); + assert.ok(sf3); + assert.equal(sf3.text, `export const foo = "changed";`); + } + finally { + await api.close(); + } + }); + + test("adding a new file is reflected in the next snapshot", async () => { + const { api, fs } = spawnAPIWithFS(); + try { + const snap1 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + + // Add a brand new file + fs.writeFile!("/src/bar.ts", `export const bar = true;`); + const snap2 = await api.updateSnapshot({ + fileChanges: { created: ["/src/bar.ts"] }, + }); + + const sf = await snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/bar.ts"); + assert.ok(sf); + assert.equal(sf.text, `export const bar = true;`); + + // Original snapshot shouldn't have the new file + const sfOld = await snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/bar.ts"); + assert.equal(sfOld, undefined); + } + finally { + await api.close(); + } + }); + + test("multiple sequential edits produce correct snapshots", async () => { + const { api, fs } = spawnAPIWithFS(); + try { + await api.updateSnapshot({ openProject: "/tsconfig.json" }); + + const versions = [ + `export const foo = 1;`, + `export const foo = 2;`, + `export const foo = 3;`, + ]; + + for (const version of versions) { + fs.writeFile!("/src/foo.ts", version); + const snap = await api.updateSnapshot({ + fileChanges: { changed: ["/src/foo.ts"] }, + }); + const sf = await snap.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); + assert.ok(sf); + assert.equal(sf.text, version); + } + } + finally { + await api.close(); + } + }); +}); + +describe("Source file caching", () => { + test("same file from same snapshot returns cached object", async () => { + const api = spawnAPI(); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sf1 = await project.program.getSourceFile("/src/index.ts"); + const sf2 = await project.program.getSourceFile("/src/index.ts"); + assert.ok(sf1); + assert.strictEqual(sf1, sf2, "Same source file should be returned from cache"); + } + finally { + await api.close(); + } + }); + + test("same file from two snapshots (same content) returns cached object", async () => { + const api = spawnAPI(); + try { + const snap1 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const snap2 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + // Fetch from snap1 first (populates cache), then snap2 (cache hit via hash) + const sf1 = await snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); + const sf2 = await snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); + assert.ok(sf1); + assert.ok(sf2); + // Same content hash → cache hit → same object + assert.strictEqual(sf1, sf2, "Same file with same content should share cached object"); + } + finally { + await api.close(); + } + }); + + test("modified file returns a different source file object", async () => { + const { api, fs } = spawnAPIWithFS(); + try { + const snap1 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const sf1 = await snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); + assert.ok(sf1); + assert.equal(sf1.text, `export const foo = 42;`); + + // Mutate the file in the VFS + fs.writeFile!("/src/foo.ts", `export const foo = 100;`); + + // Notify the server about the change + const snap2 = await api.updateSnapshot({ + fileChanges: { changed: ["/src/foo.ts"] }, + }); + const sf2 = await snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); + assert.ok(sf2); + assert.equal(sf2.text, `export const foo = 100;`); + + // Different content → different object + assert.notStrictEqual(sf1, sf2, "Modified file should return a new source file object"); + } + finally { + await api.close(); + } + }); + + test("unmodified file retains cached object across file change notification", async () => { + const { api, fs } = spawnAPIWithFS(); + try { + const snap1 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const sf1 = await snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); + assert.ok(sf1); + + // Mutate a different file + fs.writeFile!("/src/foo.ts", `export const foo = 999;`); + + // Notify the server about the change to foo.ts only + const snap2 = await api.updateSnapshot({ + fileChanges: { changed: ["/src/foo.ts"] }, + }); + const sf2 = await snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); + assert.ok(sf2); + + // index.ts wasn't changed — should still get cached object + assert.strictEqual(sf1, sf2, "Unchanged file should return cached object across snapshots"); + } + finally { + await api.close(); + } + }); + + test("cache entries survive when one of two snapshots is disposed", async () => { + const api = spawnAPI(); + try { + const snap1 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + // Fetch from snap1 to populate cache + const sf1 = await snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); + assert.ok(sf1); + + // snap2 retains snap1's cache refs for unchanged files via snapshot changes + const snap2 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + + // Dispose snap1 — snap2 still holds a ref, so the entry survives + await snap1.dispose(); + + // Fetching from snap2 should still return the cached object + const sf2 = await snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); + assert.ok(sf2); + assert.strictEqual(sf1, sf2, "Cache entry should survive when retained by the next snapshot"); + } + finally { + await api.close(); + } + }); + + test("invalidateAll causes all files to be re-fetched", async () => { + const { api, fs } = spawnAPIWithFS(); + try { + const snap1 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const sf1 = await snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); + assert.ok(sf1); + assert.equal(sf1.text, `export const foo = 42;`); + + // Mutate the file + fs.writeFile!("/src/foo.ts", `export const foo = "hello";`); + + // Use invalidateAll to force re-fetch + const snap2 = await api.updateSnapshot({ + fileChanges: { invalidateAll: true }, + }); + const sf2 = await snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); + assert.ok(sf2); + assert.equal(sf2.text, `export const foo = "hello";`); + assert.notStrictEqual(sf1, sf2, "invalidateAll should produce new source file objects"); + } + finally { + await api.close(); + } + }); + + test("node handles from a cached source file should be valid in a new snapshot", async () => { + const { api, fs } = spawnAPIWithFS({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `function foo(x: number) {}\nfoo(42);`, + "/src/other.ts": `export const x = 1;`, + }); + try { + // Snapshot 1: get a node and verify getContextualType works + const snap1 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const proj1 = snap1.getProject("/tsconfig.json")!; + + const sf1 = await proj1.program.getSourceFile("/src/main.ts"); + assert.ok(sf1); + + let numLiteral: Expression | undefined; + sf1.forEachChild(function visit(node) { + if (isCallExpression(node)) numLiteral = node.arguments[0]; + node.forEachChild(visit); + }); + assert.ok(numLiteral, "should find the 42 argument"); + + const type1 = await proj1.checker.getContextualType(numLiteral); + assert.ok(type1); + assert.ok(type1.flags & TypeFlags.Number); + + // Snapshot 2: change a different file + fs.writeFile!("/src/other.ts", `export const x = 2;`); + const snap2 = await api.updateSnapshot({ + fileChanges: { changed: ["/src/other.ts"] }, + }); + const proj2 = snap2.getProject("/tsconfig.json")!; + + // main.ts is unchanged — client returns the cached SourceFile (same object) + const sf2 = await proj2.program.getSourceFile("/src/main.ts"); + assert.ok(sf2); + assert.strictEqual(sf1, sf2, "unchanged file should be served from client cache"); + + let numLiteral2: Expression | undefined; + sf2.forEachChild(function visit(node) { + if (isCallExpression(node)) numLiteral2 = node.arguments[0]; + node.forEachChild(visit); + }); + assert.ok(numLiteral2, "should find the 42 argument"); + assert.strictEqual(numLiteral, numLiteral2, "unchanged file should be served from client cache"); + + // A type from new snapshot should be resolved + const type2 = await proj2.checker.getContextualType(numLiteral); + assert.ok(type2); + assert.ok(type2.flags & TypeFlags.Number); + } + finally { + await api.close(); + } + }); +}); + +describe("Snapshot disposal", () => { + test("dispose is idempotent", async () => { + const api = spawnAPI(); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + await snapshot.dispose(); + assert.ok(snapshot.isDisposed()); + // Second dispose should not throw + await snapshot.dispose(); + assert.ok(snapshot.isDisposed()); + } + finally { + await api.close(); + } + }); + + test("api.close disposes all active snapshots", async () => { + const api = spawnAPI(); + const snap1 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const snap2 = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + assert.ok(!snap1.isDisposed()); + assert.ok(!snap2.isDisposed()); + await api.close(); + assert.ok(snap1.isDisposed()); + assert.ok(snap2.isDisposed()); + }); +}); + +describe("Source file cache keying across projects", () => { + // Three projects share the same file (/src/shared.ts). + // The file sits inside a package.json scope with "type": "module". + // + // Project A: moduleResolution: bundler (auto detection, bundler doesn't + // trigger isFileForcedToBeModuleByFormat → file parsed as script) + // Project B: moduleResolution: bundler, moduleDetection: force + // (force → file parsed as module) + // Project C: moduleResolution: nodenext + // (nodenext + type:module → impliedNodeFormat ESNext → + // isFileForcedToBeModuleByFormat → file parsed as module) + // + // Expected: exactly two distinct source file objects are stored: + // - A gets one (script parse) + // - B and C share another (module parse) + const multiProjectFiles: Record = { + "/package.json": JSON.stringify({ type: "module" }), + "/src/shared.ts": `export const x = 1;`, + // Project A – bundler, auto detection (default) + "/projectA/tsconfig.json": JSON.stringify({ + compilerOptions: { + moduleResolution: "bundler", + module: "esnext", + strict: true, + }, + files: ["../src/shared.ts"], + }), + // Project B – bundler, force module detection + "/projectB/tsconfig.json": JSON.stringify({ + compilerOptions: { + moduleResolution: "bundler", + module: "esnext", + moduleDetection: "force", + strict: true, + }, + files: ["../src/shared.ts"], + }), + // Project C – nodenext (type:module → module) + "/projectC/tsconfig.json": JSON.stringify({ + compilerOptions: { + moduleResolution: "nodenext", + module: "nodenext", + strict: true, + }, + files: ["../src/shared.ts"], + }), + }; + + test("different parse modes produce separate cached objects; same parse modes share", async () => { + const api = spawnAPI(multiProjectFiles); + try { + // Open all three projects + await api.updateSnapshot({ openProject: "/projectA/tsconfig.json" }); + await api.updateSnapshot({ openProject: "/projectB/tsconfig.json" }); + const snapshot = await api.updateSnapshot({ openProject: "/projectC/tsconfig.json" }); + + const projectA = snapshot.getProject("/projectA/tsconfig.json")!; + const projectB = snapshot.getProject("/projectB/tsconfig.json")!; + const projectC = snapshot.getProject("/projectC/tsconfig.json")!; + assert.ok(projectA, "projectA should exist"); + assert.ok(projectB, "projectB should exist"); + assert.ok(projectC, "projectC should exist"); + + // Fetch the shared file from each project + const sfA = await projectA.program.getSourceFile("/src/shared.ts"); + const sfB = await projectB.program.getSourceFile("/src/shared.ts"); + const sfC = await projectC.program.getSourceFile("/src/shared.ts"); + assert.ok(sfA, "sfA should exist"); + assert.ok(sfB, "sfB should exist"); + assert.ok(sfC, "sfC should exist"); + + // A should differ from B and C (script vs module parse) + assert.notStrictEqual(sfA, sfB, "projectA (script) and projectB (module) should have different cached source files"); + assert.notStrictEqual(sfA, sfC, "projectA (script) and projectC (module) should have different cached source files"); + + // B and C should share the same cached object (both module parse, same content hash) + assert.strictEqual(sfB, sfC, "projectB and projectC (both module parse) should share the same cached source file"); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - symbol identity across projects", () => { + const sharedSymbolFiles = { + "/projectA/tsconfig.json": JSON.stringify({ files: ["../src/shared.ts"] }), + "/projectB/tsconfig.json": JSON.stringify({ files: ["../src/shared.ts"] }), + "/src/shared.ts": `export const sharedVar = 42;`, + }; + + test("getSymbolAtPosition returns same Symbol instance across projects", async () => { + const api = spawnAPI(sharedSymbolFiles); + try { + await api.updateSnapshot({ openProject: "/projectA/tsconfig.json" }); + const snapshot = await api.updateSnapshot({ openProject: "/projectB/tsconfig.json" }); + + const projectA = snapshot.getProject("/projectA/tsconfig.json")!; + const projectB = snapshot.getProject("/projectB/tsconfig.json")!; + assert.ok(projectA, "projectA should exist"); + assert.ok(projectB, "projectB should exist"); + + const src = sharedSymbolFiles["/src/shared.ts"]; + const varPos = src.indexOf("sharedVar"); + + const symbolA = await projectA.checker.getSymbolAtPosition("/src/shared.ts", varPos); + const symbolB = await projectB.checker.getSymbolAtPosition("/src/shared.ts", varPos); + + assert.ok(symbolA, "symbolA should exist"); + assert.ok(symbolB, "symbolB should exist"); + assert.equal(symbolA.name, "sharedVar"); + assert.equal(symbolB.name, "sharedVar"); + + assert.strictEqual(symbolA, symbolB, "Same source symbol queried from two projects should be the same object"); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - types and signatures", () => { + const checkerFiles = { + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export const x = 42; +export function add(a: number, b: number, ...rest: number[]): number { return a + b; } +export class MyClass { + value: string = ""; + getValue(): string { return this.value; } +} +`, + }; + + test("getTypeAtPosition", async () => { + const api = spawnAPI(checkerFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = checkerFiles["/src/main.ts"]; + const xPos = src.indexOf("x = 42"); + const type = await project.checker.getTypeAtPosition("/src/main.ts", xPos); + assert.ok(type); + assert.ok(type.flags & TypeFlags.NumberLiteral); + } + finally { + await api.close(); + } + }); + + test("getTypeAtPosition batched", async () => { + const api = spawnAPI(checkerFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = checkerFiles["/src/main.ts"]; + const xPos = src.indexOf("x = 42"); + const addPos = src.indexOf("add("); + const types = await project.checker.getTypeAtPosition("/src/main.ts", [xPos, addPos]); + assert.equal(types.length, 2); + assert.ok(types[0]); + assert.ok(types[1]); + } + finally { + await api.close(); + } + }); + + test("getTypeAtLocation", async () => { + const api = spawnAPI(checkerFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const firstVarDecl = sourceFile.statements[2]; // "export const x" + assert.ok(firstVarDecl); + const type = await project.checker.getTypeAtLocation(firstVarDecl); + assert.ok(type); + } + finally { + await api.close(); + } + }); + + test("getTypeAtLocation returns property type for parenthesized and chained access (issue #3938)", async () => { + const files = { + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +interface A { + a: number; + b: { c: number }; +} +const obj: A = { a: 1, b: { c: 2 } }; +const a1 = obj.a; +const a2 = (obj).a; +const c = obj.b.c; +`, + }; + + const api = spawnAPI(files); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + + const targetNodes = new Map(); + sourceFile.forEachChild(function visit(node) { + if (node.kind === SyntaxKind.PropertyAccessExpression) { + const text = sourceFile.text.slice(node.pos, node.end).trim(); + if (text === "obj.a" || text === "(obj).a" || text === "obj.b.c") { + targetNodes.set(text, node); + } + } + node.forEachChild(visit); + }); + + assert.equal(targetNodes.size, 3, "Should find all target property access expressions"); + for (const expr of ["obj.a", "(obj).a", "obj.b.c"] as const) { + const node = targetNodes.get(expr); + assert.ok(node, `Should find expression '${expr}'`); + const type = await project.checker.getTypeAtLocation(node); + assert.ok(type, `Should get a type for '${expr}'`); + assert.ok(type.flags & TypeFlags.Number, `Expected '${expr}' to have number type flags, got ${type.flags}`); + } + } + finally { + await api.close(); + } + }); + + test("getTypeAtLocation returns call result type for private method call (issue #4041)", async () => { + const files = { + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true, target: "esnext" } }), + "/src/main.ts": ` +type Result = { readonly value: string }; + +export class Cache { + run(): Result { + return this.#buildCapabilities(); + } + + #buildCapabilities(): Result { + return { value: "ok" }; + } +} +`, + }; + + const api = spawnAPI(files); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + + let callNode: import("@typescript/typescript/unstable/ast").CallExpression | undefined; + sourceFile.forEachChild(function visit(node) { + if (isCallExpression(node)) { + const text = sourceFile.text.slice(node.pos, node.end).trim(); + if (text === "this.#buildCapabilities()") { + callNode = node; + } + } + node.forEachChild(visit); + }); + + assert.ok(callNode, "Should find private method call expression"); + const callType = await project.checker.getTypeAtLocation(callNode); + const calleeType = await project.checker.getTypeAtLocation(callNode.expression); + assert.ok(callType, "Should get type for call expression"); + assert.ok(calleeType, "Should get type for callee expression"); + + const callSignatures = await project.checker.getSignaturesOfType(calleeType, SignatureKind.Call); + assert.ok(callSignatures.length > 0, "Callee should be callable"); + const returnType = await project.checker.getReturnTypeOfSignature(callSignatures[0]); + assert.ok(returnType, "Should get return type for private method call"); + const callExprSignatures = await project.checker.getSignaturesOfType(callType, SignatureKind.Call); + + assert.ok(callType.flags & TypeFlags.Object, `Expected call expression type to be object-like, got ${callType.flags}`); + assert.ok(returnType.flags & TypeFlags.Object, `Expected return type to be object-like, got ${returnType.flags}`); + assert.equal(callType.flags, returnType.flags, "Call expression type should have same flags as method return type"); + assert.equal(callExprSignatures.length, 0, "Call expression result type should not itself be callable"); + } + finally { + await api.close(); + } + }); + + test("getSignaturesOfType - call signatures", async () => { + const api = spawnAPI(checkerFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = checkerFiles["/src/main.ts"]; + const addPos = src.indexOf("add("); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", addPos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const callSigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(callSigs.length > 0); + const sig = callSigs[0]; + const typeCallSigs = await type.getCallSignatures(); + assert.deepEqual(typeCallSigs, callSigs); + assert.ok((await sig.getReturnType()).flags & TypeFlags.Number); + assert.ok((await sig.getTypeParameterAtPosition(0)).flags & TypeFlags.Number); + assert.ok(sig.id); + assert.ok(sig.parameters.length >= 2); + assert.ok(sig.hasRestParameter); + assert.ok(!sig.isConstruct); + assert.ok(!sig.isAbstract); + } + finally { + await api.close(); + } + }); + + test("getApparentProperties includes CallableFunction members", async () => { + const api = spawnAPI(checkerFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = checkerFiles["/src/main.ts"]; + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("add(")); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + const propertyNames = (await type.getApparentProperties()).map(property => property.name); + assert.ok(propertyNames.includes("apply")); + assert.ok(propertyNames.includes("call")); + assert.ok(propertyNames.includes("bind")); + } + finally { + await api.close(); + } + }); + + test("checker and object methods share cached results", async () => { + const api = new API({ + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + fs: createVirtualFileSystem(checkerFiles), + collectTiming: true, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = checkerFiles["/src/main.ts"]; + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("add(")); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + let signature: Signature | undefined; + + async function assertOneRequest(callback: () => Promise) { + await api.resetTimingInfo(); + await callback(); + assert.equal((await api.getTimingInfo()).totals.requestCount, 1); + } + + await assertOneRequest(async () => { + const properties = await type.getProperties(); + assert.strictEqual(await project.checker.getPropertiesOfType(type), properties); + }); + await assertOneRequest(async () => { + const signatures = await type.getCallSignatures(); + assert.strictEqual(await project.checker.getSignaturesOfType(type, SignatureKind.Call), signatures); + signature = signatures[0]; + }); + await assertOneRequest(async () => { + const nonNullable = await project.checker.getNonNullableType(type); + assert.strictEqual(await type.getNonNullableType(), nonNullable); + }); + await assertOneRequest(async () => { + const apparentType = await type.getApparentType(); + assert.strictEqual(await project.checker.getApparentType(type), apparentType); + }); + await assertOneRequest(async () => { + const indexInfos = await type.getIndexInfos(); + assert.strictEqual(await project.checker.getIndexInfosOfType(type), indexInfos); + }); + await assertOneRequest(async () => { + assert.ok(signature); + const returnType = await project.checker.getReturnTypeOfSignature(signature); + assert.strictEqual(await signature.getReturnType(), returnType); + }); + } + finally { + await api.close(); + } + }); + + test("getSignaturesOfType - construct signatures", async () => { + const api = spawnAPI(checkerFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = checkerFiles["/src/main.ts"]; + const classPos = src.indexOf("MyClass"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", classPos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const constructSigs = await project.checker.getSignaturesOfType(type, SignatureKind.Construct); + assert.ok(constructSigs.length > 0); + const sig = constructSigs[0]; + assert.ok(sig.isConstruct); + } + finally { + await api.close(); + } + }); + + test("Signature declaration can be resolved", async () => { + const api = spawnAPI(checkerFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = checkerFiles["/src/main.ts"]; + const addPos = src.indexOf("add("); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", addPos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const callSigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(callSigs.length > 0); + const sig = callSigs[0]; + assert.ok(sig.declaration); + assert.ok(isSignatureDeclaration.Handle(sig.declaration)); + const node = await sig.declaration.resolve(project); + assert.ok(node); + assert.ok(node.parameters); + assert.ok(isSignatureDeclaration(node)); + // The handle remembers its canonical project, so resolve() works without an argument. + const nodeFromCanonical = await sig.declaration.resolve(); + assert.ok(nodeFromCanonical); + assert.ok(nodeFromCanonical.parameters); + assert.ok(isSignatureDeclaration(nodeFromCanonical)); + assert.strictEqual(nodeFromCanonical.kind, node.kind); + + const methodPos = src.indexOf("getValue"); + const methodSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", methodPos); + assert.ok(methodSymbol); + assert.ok(methodSymbol.valueDeclaration); + const methodNode = await methodSymbol.valueDeclaration.resolve(project); + assert.ok(methodNode); + assert.strictEqual(methodNode.parent.kind, SyntaxKind.ClassDeclaration); + assert.strictEqual(methodNode.parent.parent.kind, SyntaxKind.SourceFile); + // A symbol's declaration handles default to the symbol's canonical project. + const methodNodeFromCanonical = await methodSymbol.valueDeclaration.resolve(); + assert.ok(methodNodeFromCanonical); + assert.strictEqual(methodNodeFromCanonical.kind, methodNode.kind); + } + finally { + await api.close(); + } + }); + + test("getSignaturesOfType - signature type parameters", async () => { + const mainFile = ` + interface Operator { + } + export declare class Observable { + lift(operator: Operator): Observable; + } + `; + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": mainFile, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const liftPos = mainFile.indexOf("lift"); + const type = await project.checker.getTypeAtPosition("/src/main.ts", liftPos); + assert.ok(type); + const callSigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(callSigs.length === 1, "should have exactly one call signature, found: " + callSigs.length); + const sig = callSigs[0]; + assert.ok(sig.typeParameters?.length === 1, "should have exactly one type parameter, found: " + sig.typeParameters?.length); + const typeParams = await sig.getTypeParameters(); + const typeParam = typeParams[0]; + assert.ok(typeParam, "should have type parameter"); + const name = (await typeParam.getSymbol())?.name; + assert.ok(name === "R", "should be named R, instead: " + name); + assert.ok(typeParam.flags & TypeFlags.TypeParameter, "should be a type parameter, instead flags: " + typeParam.flags); + } + finally { + await api.close(); + } + }); + + test("Signature.getParameters() returns parameter symbols with correct names", async () => { + const api = spawnAPI(checkerFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = checkerFiles["/src/main.ts"]; + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("add(")); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const params = await sigs[0].getParameters(); + assert.equal(params.length, 3); + assert.equal(params[0].name, "a"); + assert.equal(params[1].name, "b"); + assert.equal(params[2].name, "rest"); + assert.ok(params[0].flags & SymbolFlags.FunctionScopedVariable, `expected FunctionScopedVariable on 'a', got ${params[0].flags}`); + } + finally { + await api.close(); + } + }); + + test("Signature.getThisParameter() returns undefined when no explicit this parameter", async () => { + const api = spawnAPI(checkerFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = checkerFiles["/src/main.ts"]; + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("add(")); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const thisParam = await sigs[0].getThisParameter(); + assert.strictEqual(thisParam, undefined, "add() has no explicit this parameter"); + } + finally { + await api.close(); + } + }); + + test("Signature.getThisParameter() returns symbol for explicit this parameter", async () => { + const src = `export function foo(this: { n: number }, x: string): void {}`; + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("foo(")); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const thisParam = await sigs[0].getThisParameter(); + assert.ok(thisParam, "foo has an explicit this parameter"); + assert.equal(thisParam.name, "this"); + assert.ok(thisParam.flags & SymbolFlags.FunctionScopedVariable, `expected FunctionScopedVariable, got ${thisParam.flags}`); + } + finally { + await api.close(); + } + }); + + test("Signature.getTarget() returns undefined for a non-instantiated signature", async () => { + const api = spawnAPI(checkerFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = checkerFiles["/src/main.ts"]; + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("add(")); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const target = await sigs[0].getTarget(); + assert.strictEqual(target, undefined, "add() is not an instantiated signature"); + } + finally { + await api.close(); + } + }); + + test("Signature.getTarget() returns the generic source signature for an instantiated call", async () => { + const src = ` + function identity(x: T): T { return x; } + identity("hello"); + `; + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + let callNode: Node | undefined; + sourceFile.forEachChild(function visit(node) { + if (isCallExpression(node)) callNode = node; + node.forEachChild(visit); + }); + assert.ok(callNode, "should find a call expression"); + const sig = await project.checker.getResolvedSignature(callNode); + assert.ok(sig, "should resolve a signature for the call"); + assert.ok(sig.target !== undefined, "instantiated call should have a target ID"); + const target = await sig.getTarget(); + assert.ok(target, "getTarget() should return the generic signature"); + assert.ok(target.typeParameters && target.typeParameters.length > 0, "target should have type parameters"); + } + finally { + await api.close(); + } + }); +}); + +describe("Symbol - parent, members, exports", () => { + const symbolFiles = { + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/mod.ts": ` +export class Animal { + name: string = ""; + speak(): void {} +} +export const value = 1; +`, + }; + + test("getMembers returns class members", async () => { + const api = spawnAPI(symbolFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = symbolFiles["/src/mod.ts"]; + const animalPos = src.indexOf("Animal"); + const symbol = await project.checker.getSymbolAtPosition("/src/mod.ts", animalPos); + assert.ok(symbol); + const members = await symbol.getMembers(); + assert.ok(members.size > 0); + const memberNames = [...members.values()].map(m => m.name); + assert.ok(memberNames.includes("name"), "should have 'name' member"); + assert.ok(memberNames.includes("speak"), "should have 'speak' member"); + } + finally { + await api.close(); + } + }); + + test("getExports returns module exports via sourceFile symbol", async () => { + const api = spawnAPI(symbolFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/mod.ts"); + assert.ok(sourceFile); + const moduleSymbol = await project.checker.getSymbolAtLocation(sourceFile); + assert.ok(moduleSymbol); + const exports = await moduleSymbol.getExports(); + assert.ok(exports.size > 0); + const exportNames = [...exports.values()].map(e => e.name); + assert.ok(exportNames.includes("Animal"), "should export Animal"); + assert.ok(exportNames.includes("value"), "should export value"); + } + finally { + await api.close(); + } + }); + + test("getParent returns containing symbol", async () => { + const api = spawnAPI(symbolFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = symbolFiles["/src/mod.ts"]; + const namePos = src.indexOf("name:"); + const nameSymbol = await project.checker.getSymbolAtPosition("/src/mod.ts", namePos); + assert.ok(nameSymbol); + assert.equal(nameSymbol.name, "name"); + const parent = await nameSymbol.getParent(); + assert.ok(parent); + assert.equal(parent.name, "Animal"); + } + finally { + await api.close(); + } + }); + + test("checkFlags is typed as CheckFlags", async () => { + const api = spawnAPI(symbolFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = await project.checker.getSymbolAtPosition("/src/mod.ts", symbolFiles["/src/mod.ts"].indexOf("Animal")); + assert.ok(symbol); + const checkFlags: CheckFlags = symbol.checkFlags; + assert.equal(checkFlags & CheckFlags.Readonly, 0); + } + finally { + await api.close(); + } + }); +}); + +describe("Type - getSymbol", () => { + test("getSymbol returns the symbol of a type", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/types.ts": ` +export class Foo { + x: number = 0; +} +export const instance: Foo = new Foo(); +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\nexport class Foo {\n x: number = 0;\n}\nexport const instance: Foo = new Foo();\n`; + const instancePos = src.indexOf("instance"); + const symbol = await project.checker.getSymbolAtPosition("/src/types.ts", instancePos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const typeSymbol = await type.getSymbol(); + assert.ok(typeSymbol); + assert.equal(typeSymbol.name, "Foo"); + } + finally { + await api.close(); + } + }); +}); + +describe("Type - sub-property fetchers", () => { + const typeFiles = { + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true, target: "esnext" } }), + "/src/types.ts": ` +export const arr: Array = [1, 2, 3]; +export const union: string | number = "hello"; +export const intersection: { a: number } & { b: string } = { a: 1, b: "hi" }; +export type KeyOf = keyof T; +export type Lookup = T[K]; +export type Cond = T extends string ? "yes" : "no"; +export const tpl: \`hello \${string}\` = "hello world"; +export type Upper = Uppercase<"hello">; +export const tuple: readonly [number, string?, ...boolean[]] = [1]; +`, + }; + + async function getTypeAtName(api: API, name: string) { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = typeFiles["/src/types.ts"]; + const pos = src.indexOf(name); + assert.ok(pos >= 0, `Could not find "${name}" in source`); + const symbol = await project.checker.getSymbolAtPosition("/src/types.ts", pos); + assert.ok(symbol, `No symbol found at "${name}"`); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type, `No type found for symbol "${name}"`); + return { type, project, snapshot, api }; + } + + test("TypeReference.getTarget() returns the generic target", async () => { + const { type, api } = await getTypeAtName(spawnAPI(typeFiles), "arr:"); + try { + assert.ok(type.flags & TypeFlags.Object); + const ref = type as TypeReference; + assert.ok(ref.objectFlags & ObjectFlags.Reference); + assert.equal(type.isObjectType(), true); + assert.equal(type.isTypeReference(), true); + assert.equal(type.isLiteralType(), false); + const target = await ref.getTarget(); + assert.ok(target); + assert.ok(target.flags & TypeFlags.Object); + const properties = await type.getProperties(); + assert.ok(properties.some(property => property.name === "length")); + assert.equal((await type.getProperty("length"))?.name, "length"); + assert.ok((await type.getApparentProperties()).length >= properties.length); + assert.strictEqual(await type.getNonNullableType(), type); + } + finally { + await api.close(); + } + }); + + test("UnionOrIntersectionType.getTypes() returns union members", async () => { + const { type, api } = await getTypeAtName(spawnAPI(typeFiles), "union:"); + try { + assert.ok(type.flags & TypeFlags.Union); + const union = type as UnionOrIntersectionType; + const types = await union.getTypes(); + assert.ok(types.length >= 2); + assert.equal(type.isUnionType(), true); + assert.equal(type.isIntersectionType(), false); + } + finally { + await api.close(); + } + }); + + test("UnionOrIntersectionType.getTypes() returns intersection members", async () => { + const { type, api } = await getTypeAtName(spawnAPI(typeFiles), "intersection:"); + try { + assert.ok(type.flags & TypeFlags.Intersection); + const inter = type as UnionOrIntersectionType; + const types = await inter.getTypes(); + assert.ok(types.length >= 2); + } + finally { + await api.close(); + } + }); + + test("UnionOrIntersectionType.getTypes() on a wrongly-cast type returns undefined without hitting the server", async () => { + const src = `export const s: string = ""; export const u: string | number = "";`; + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + + // `string` is neither a union/intersection nor a template literal type, + // so it has no constituent types. The client guards on the type's flags + // and returns undefined without ever sending a request the server cannot satisfy. + const sSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("s:")); + assert.ok(sSymbol); + const sType = await project.checker.getTypeOfSymbol(sSymbol); + assert.ok(sType); + assert.equal(await (sType as unknown as UnionOrIntersectionType).getTypes(), undefined); + + // A real union still returns its constituents. + const uSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("u:")); + assert.ok(uSymbol); + const uType = await project.checker.getTypeOfSymbol(uSymbol); + assert.ok(uType); + assert.equal((await (uType as UnionOrIntersectionType).getTypes()).length, 2); + } + finally { + await api.close(); + } + }); + + test("IndexType.getTarget() returns the target type", async () => { + const api = spawnAPI(typeFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = await project.checker.resolveName("KeyOf", SymbolFlags.TypeAlias, { document: "/src/types.ts", position: 0 }); + assert.ok(symbol); + const type = await project.checker.getDeclaredTypeOfSymbol(symbol); + assert.ok(type); + // KeyOf = keyof T — this is an IndexType + assert.ok(type.flags & TypeFlags.Index, `Expected IndexType, got flags ${type.flags}`); + const indexType = type as IndexType; + const target = await indexType.getTarget(); + assert.ok(target); + } + finally { + await api.close(); + } + }); + + test("IndexedAccessType.getObjectType() and getIndexType()", async () => { + const api = spawnAPI(typeFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = await project.checker.resolveName("Lookup", SymbolFlags.TypeAlias, { document: "/src/types.ts", position: 0 }); + assert.ok(symbol); + const type = await project.checker.getDeclaredTypeOfSymbol(symbol); + assert.ok(type); + assert.ok(type.flags & TypeFlags.IndexedAccess, `Expected IndexedAccessType, got flags ${type.flags}`); + const ia = type as IndexedAccessType; + assert.equal(type.isIndexedAccessType(), true); + const objectType = await ia.getObjectType(); + assert.ok(objectType); + const indexType = await ia.getIndexType(); + assert.ok(indexType); + } + finally { + await api.close(); + } + }); + + test("ConditionalType.getCheckType() and getExtendsType()", async () => { + const api = spawnAPI(typeFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = await project.checker.resolveName("Cond", SymbolFlags.TypeAlias, { document: "/src/types.ts", position: 0 }); + assert.ok(symbol); + const type = await project.checker.getDeclaredTypeOfSymbol(symbol); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Conditional, `Expected ConditionalType, got flags ${type.flags}`); + const cond = type as ConditionalType; + assert.equal(type.isConditionalType(), true); + const checkType = await cond.getCheckType(); + assert.ok(checkType); + const extendsType = await cond.getExtendsType(); + assert.ok(extendsType); + } + finally { + await api.close(); + } + }); + + test("ConditionalType.getTrueType() and getFalseType()", async () => { + const api = spawnAPI(typeFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = await project.checker.resolveName("Cond", SymbolFlags.TypeAlias, { document: "/src/types.ts", position: 0 }); + assert.ok(symbol); + const type = await project.checker.getDeclaredTypeOfSymbol(symbol); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Conditional, `Expected ConditionalType, got flags ${type.flags}`); + + const trueType = await (type as ConditionalType).getTrueType(); + assert.ok(trueType, "should return the true-branch type"); + assert.ok(trueType.flags & TypeFlags.StringLiteral, `Expected StringLiteral for true branch, got flags ${trueType.flags}`); + assert.equal((trueType as LiteralType).value, "yes"); + + const falseType = await (type as ConditionalType).getFalseType(); + assert.ok(falseType, "should return the false-branch type"); + assert.ok(falseType.flags & TypeFlags.StringLiteral, `Expected StringLiteral for false branch, got flags ${falseType.flags}`); + assert.equal((falseType as LiteralType).value, "no"); + } + finally { + await api.close(); + } + }); + + test("TemplateLiteralType.texts and getTypes()", async () => { + const { type, api } = await getTypeAtName(spawnAPI(typeFiles), "tpl:"); + try { + assert.ok(type.flags & TypeFlags.TemplateLiteral, `Expected TemplateLiteralType, got flags ${type.flags}`); + const tpl = type as TemplateLiteralType; + assert.ok(tpl.texts); + assert.ok(tpl.texts.length >= 2); + assert.equal(tpl.texts[0], "hello "); + const types = await tpl.getTypes(); + assert.ok(types.length >= 1); + } + finally { + await api.close(); + } + }); + + test("StringMappingType.getTarget() returns the mapped type", async () => { + const api = spawnAPI(typeFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = typeFiles["/src/types.ts"]; + const pos = src.indexOf("Upper"); + const symbol = await project.checker.getSymbolAtPosition("/src/types.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + // Uppercase<"hello"> may resolve to a StringMappingType or a string literal + if (type.flags & TypeFlags.StringMapping) { + const sm = type as StringMappingType; + const target = await sm.getTarget(); + assert.ok(target); + } + // If it resolved to "HELLO" literal, that's fine too — it means eager evaluation + } + finally { + await api.close(); + } + }); + + test("TupleType properties", async () => { + const { type, api } = await getTypeAtName(spawnAPI(typeFiles), "tuple:"); + try { + assert.ok(type.flags & TypeFlags.Object); + const ref = type as TypeReference; + assert.ok(ref.objectFlags & ObjectFlags.Reference); + const target = await ref.getTarget(); + assert.ok(target); + assert.ok(target.flags & TypeFlags.Object); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - intrinsic type getters", () => { + const intrinsicFiles = { + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const x = 1;`, + }; + + test("getAnyType returns a type with Any flag", async () => { + const api = spawnAPI(intrinsicFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const type = await project.checker.getAnyType(); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Any); + } + finally { + await api.close(); + } + }); + + test("getStringType returns a type with String flag", async () => { + const api = spawnAPI(intrinsicFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const type = await project.checker.getStringType(); + assert.ok(type); + assert.ok(type.flags & TypeFlags.String); + } + finally { + await api.close(); + } + }); + + test("getNumberType returns a type with Number flag", async () => { + const api = spawnAPI(intrinsicFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const type = await project.checker.getNumberType(); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Number); + } + finally { + await api.close(); + } + }); + + test("getBooleanType returns a type with Boolean flag", async () => { + const api = spawnAPI(intrinsicFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const type = await project.checker.getBooleanType(); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Boolean); + } + finally { + await api.close(); + } + }); + + test("getVoidType returns a type with Void flag", async () => { + const api = spawnAPI(intrinsicFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const type = await project.checker.getVoidType(); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Void); + } + finally { + await api.close(); + } + }); + + test("getUndefinedType returns a type with Undefined flag", async () => { + const api = spawnAPI(intrinsicFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const type = await project.checker.getUndefinedType(); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Undefined); + } + finally { + await api.close(); + } + }); + + test("getNullType returns a type with Null flag", async () => { + const api = spawnAPI(intrinsicFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const type = await project.checker.getNullType(); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Null); + } + finally { + await api.close(); + } + }); + + test("getNeverType returns a type with Never flag", async () => { + const api = spawnAPI(intrinsicFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const type = await project.checker.getNeverType(); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Never); + } + finally { + await api.close(); + } + }); + + test("getUnknownType returns a type with Unknown flag", async () => { + const api = spawnAPI(intrinsicFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const type = await project.checker.getUnknownType(); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Unknown); + } + finally { + await api.close(); + } + }); + + test("getBigIntType returns a type with BigInt flag", async () => { + const api = spawnAPI(intrinsicFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const type = await project.checker.getBigIntType(); + assert.ok(type); + assert.ok(type.flags & TypeFlags.BigInt); + } + finally { + await api.close(); + } + }); + + test("getESSymbolType returns a type with ESSymbol flag", async () => { + const api = spawnAPI(intrinsicFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const type = await project.checker.getESSymbolType(); + assert.ok(type); + assert.ok(type.flags & TypeFlags.ESSymbol); + } + finally { + await api.close(); + } + }); + + test("getNonPrimitiveType returns a type with NonPrimitive flag", async () => { + const api = spawnAPI(intrinsicFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const type = await project.checker.getNonPrimitiveType(); + assert.ok(type); + assert.ok(type.flags & TypeFlags.NonPrimitive); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - multi-project type ID uniqueness", () => { + test("intrinsic types from 3 projects in the same snapshot have non-colliding IDs", async () => { + const api = spawnAPI({ + "/proj1/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/proj1/src/index.ts": `export const x = 1;`, + "/proj2/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/proj2/src/index.ts": `export const y = "hello";`, + "/proj3/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/proj3/src/index.ts": `export const z = true;`, + }); + try { + // Open all 3 projects — each updateSnapshot accumulates open projects + await api.updateSnapshot({ openProject: "/proj1/tsconfig.json" }); + await api.updateSnapshot({ openProject: "/proj2/tsconfig.json" }); + const snapshot = await api.updateSnapshot({ openProject: "/proj3/tsconfig.json" }); + + const proj1 = snapshot.getProject("/proj1/tsconfig.json")!; + const proj2 = snapshot.getProject("/proj2/tsconfig.json")!; + const proj3 = snapshot.getProject("/proj3/tsconfig.json")!; + assert.ok(proj1, "proj1 should be in final snapshot"); + assert.ok(proj2, "proj2 should be in final snapshot"); + assert.ok(proj3, "proj3 should be in final snapshot"); + + // Fetch several intrinsic types from each checker. + // If type IDs collide across checkers, registerType panics → API error. + const num1 = await proj1.checker.getNumberType(); + const str1 = await proj1.checker.getStringType(); + const bool1 = await proj1.checker.getBooleanType(); + const any1 = await proj1.checker.getAnyType(); + const num2 = await proj2.checker.getNumberType(); + const str2 = await proj2.checker.getStringType(); + const bool2 = await proj2.checker.getBooleanType(); + const any2 = await proj2.checker.getAnyType(); + const num3 = await proj3.checker.getNumberType(); + const str3 = await proj3.checker.getStringType(); + const bool3 = await proj3.checker.getBooleanType(); + const any3 = await proj3.checker.getAnyType(); + + assert.ok(num1.flags & TypeFlags.Number, "proj1 number type"); + assert.ok(str1.flags & TypeFlags.String, "proj1 string type"); + assert.ok(bool1.flags & TypeFlags.Boolean, "proj1 boolean type"); + assert.ok(any1.flags & TypeFlags.Any, "proj1 any type"); + + assert.ok(num2.flags & TypeFlags.Number, "proj2 number type"); + assert.ok(str2.flags & TypeFlags.String, "proj2 string type"); + assert.ok(bool2.flags & TypeFlags.Boolean, "proj2 boolean type"); + assert.ok(any2.flags & TypeFlags.Any, "proj2 any type"); + + assert.ok(num3.flags & TypeFlags.Number, "proj3 number type"); + assert.ok(str3.flags & TypeFlags.String, "proj3 string type"); + assert.ok(bool3.flags & TypeFlags.Boolean, "proj3 boolean type"); + assert.ok(any3.flags & TypeFlags.Any, "proj3 any type"); + } + finally { + await api.close(); + } + }); + + test("symbol and signature handles from 3 projects in the same snapshot have non-colliding IDs", async () => { + const api = spawnAPI({ + "/proj1/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/proj1/src/index.ts": `export function add(a: number, b: number): number { return a + b; }`, + "/proj2/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/proj2/src/index.ts": `export function greet(name: string): string { return "hello " + name; }`, + "/proj3/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/proj3/src/index.ts": `export function toggle(b: boolean): boolean { return !b; }`, + }); + try { + await api.updateSnapshot({ openProject: "/proj1/tsconfig.json" }); + await api.updateSnapshot({ openProject: "/proj2/tsconfig.json" }); + const snapshot = await api.updateSnapshot({ openProject: "/proj3/tsconfig.json" }); + + const proj1 = snapshot.getProject("/proj1/tsconfig.json")!; + const proj2 = snapshot.getProject("/proj2/tsconfig.json")!; + const proj3 = snapshot.getProject("/proj3/tsconfig.json")!; + + // Get a symbol from each project (exercises symbol registry) + const src1 = `export function add(a: number, b: number): number { return a + b; }`; + const src2 = `export function greet(name: string): string { return "hello " + name; }`; + const src3 = `export function toggle(b: boolean): boolean { return !b; }`; + + const sym1 = await proj1.checker.getSymbolAtPosition("/proj1/src/index.ts", src1.indexOf("add")); + const sym2 = await proj2.checker.getSymbolAtPosition("/proj2/src/index.ts", src2.indexOf("greet")); + const sym3 = await proj3.checker.getSymbolAtPosition("/proj3/src/index.ts", src3.indexOf("toggle")); + assert.ok(sym1, "proj1 symbol"); + assert.ok(sym2, "proj2 symbol"); + assert.ok(sym3, "proj3 symbol"); + assert.equal(sym1.name, "add", "proj1 symbol name"); + assert.equal(sym2.name, "greet", "proj2 symbol name"); + assert.equal(sym3.name, "toggle", "proj3 symbol name"); + + // Get type of each symbol, then signatures (exercises type + signature registries) + const type1 = await proj1.checker.getTypeOfSymbol(sym1); + const type2 = await proj2.checker.getTypeOfSymbol(sym2); + const type3 = await proj3.checker.getTypeOfSymbol(sym3); + assert.ok(type1, "proj1 function type"); + assert.ok(type2, "proj2 function type"); + assert.ok(type3, "proj3 function type"); + + const sigs1 = await proj1.checker.getSignaturesOfType(type1, SignatureKind.Call); + const sigs2 = await proj2.checker.getSignaturesOfType(type2, SignatureKind.Call); + const sigs3 = await proj3.checker.getSignaturesOfType(type3, SignatureKind.Call); + assert.equal(sigs1.length, 1, "proj1 has 1 call signature"); + assert.equal(sigs2.length, 1, "proj2 has 1 call signature"); + assert.equal(sigs3.length, 1, "proj3 has 1 call signature"); + assert.equal(sigs1[0].parameters.length, 2, "proj1 add() has 2 params"); + assert.equal(sigs2[0].parameters.length, 1, "proj2 greet() has 1 param"); + assert.equal(sigs3[0].parameters.length, 1, "proj3 toggle() has 1 param"); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getBaseTypeOfLiteralType", () => { + test("number literal widens to number", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const x = 42;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export const x = 42;`; + const pos = src.indexOf("x ="); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const literalType = await project.checker.getTypeOfSymbol(symbol); + assert.ok(literalType); + assert.ok(literalType.flags & TypeFlags.NumberLiteral); + const baseType = await project.checker.getBaseTypeOfLiteralType(literalType); + assert.ok(baseType); + assert.ok(baseType.flags & TypeFlags.Number); + } + finally { + await api.close(); + } + }); + + test("string literal widens to string", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const s = "hello";`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export const s = "hello";`; + const pos = src.indexOf("s "); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const literalType = await project.checker.getTypeOfSymbol(symbol); + assert.ok(literalType); + assert.ok(literalType.flags & TypeFlags.StringLiteral); + const baseType = await project.checker.getBaseTypeOfLiteralType(literalType); + assert.ok(baseType); + assert.ok(baseType.flags & TypeFlags.String); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getContextualType", () => { + test("contextual type from function parameter", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +function foo(x: number) {} +foo(42); +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + + // Find the argument "42" in foo(42) + // statement[1] = foo(42); which is an ExpressionStatement -> CallExpression + const callStmt = sourceFile.statements[1]; + assert.ok(callStmt); + let numLiteral: import("@typescript/typescript/unstable/ast").Expression | undefined; + callStmt.forEachChild(function visit(node) { + if (isCallExpression(node)) { + // First argument + numLiteral = node.arguments[0]; + } + node.forEachChild(visit); + }); + assert.ok(numLiteral); + const contextualType = await project.checker.getContextualType(numLiteral); + assert.ok(contextualType); + assert.ok(contextualType.flags & TypeFlags.Number); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getTypeOfSymbolAtLocation", () => { + test("narrowed type via typeof check", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export function check(x: string | number) { + if (typeof x === "string") { + return x; + } + return x; +} +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\nexport function check(x: string | number) {\n if (typeof x === "string") {\n return x;\n }\n return x;\n}\n`; + + // Get the symbol for parameter "x" + const paramPos = src.indexOf("x:"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", paramPos); + assert.ok(symbol); + assert.equal(symbol.name, "x"); + + // Get the type of "x" at the wider function scope — should be string | number + const wideType = await project.checker.getTypeOfSymbol(symbol); + assert.ok(wideType); + assert.ok(wideType.flags & TypeFlags.Union, `Expected union type, got flags ${wideType.flags}`); + + // Get the narrowed return x inside the if block + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + + // statement[0] is the function declaration + const funcDecl = sourceFile.statements[0]; + assert.ok(funcDecl); + // Walk to find the first "return x" — inside the if, x should be narrowed to string + let firstReturnX: import("@typescript/typescript/unstable/ast").Node | undefined; + funcDecl.forEachChild(function visit(node) { + if (isReturnStatement(node) && !firstReturnX) { + // The expression of the return statement is the identifier "x" + if (node.expression) { + firstReturnX = node.expression; + } + } + node.forEachChild(visit); + }); + assert.ok(firstReturnX); + const narrowedType = await project.checker.getTypeOfSymbolAtLocation(symbol, firstReturnX); + assert.ok(narrowedType); + // Inside the if (typeof x === "string") branch, x should be narrowed to string + assert.ok(narrowedType.flags & TypeFlags.String, `Expected string type, got flags ${narrowedType.flags}`); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getShorthandAssignmentValueSymbol", () => { + test("shorthand property symbol resolves to variable", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +const name = "Alice"; +export const obj = { name }; +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + + // Find the shorthand property assignment { name } + // statement[1] = export const obj = { name }; + let shorthandNode: import("@typescript/typescript/unstable/ast").Node | undefined; + sourceFile.forEachChild(function visit(node) { + if (isShorthandPropertyAssignment(node)) { + shorthandNode = node; + } + node.forEachChild(visit); + }); + assert.ok(shorthandNode, "Should find a shorthand property assignment"); + const valueSymbol = await project.checker.getShorthandAssignmentValueSymbol(shorthandNode); + assert.ok(valueSymbol); + assert.equal(valueSymbol.name, "name"); + } + finally { + await api.close(); + } + }); +}); + +describe("readFile callback semantics", () => { + test("readFile: string returns content, null blocks fallback, undefined falls through to real FS", async () => { + const virtualFiles: Record = { + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/index.ts": `export const x: number = 1;`, + }; + const vfs = createVirtualFileSystem(virtualFiles); + const blockedPath = "/src/blocked.ts"; + + const fs: FileSystem = { + ...vfs, + readFile: (fileName: string) => { + if (fileName === blockedPath) { + // null = file not found, don't fall back to real FS + return null; + } + // Try the VFS first; if it has the file, return its content (string). + // Otherwise return undefined to fall through to the real FS. + return vfs.readFile!(fileName); + }, + }; + + const api = new API({ + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + fs, + }); + + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + + // 1. String content: virtual file is found + const sf = await project.program.getSourceFile("/src/index.ts"); + assert.ok(sf, "Virtual file should be found"); + assert.equal(sf.text, virtualFiles["/src/index.ts"]); + + // 2. undefined fallback: lib files from the real FS should be present. + // If readFile returned null for unknowns, lib files would be missing + // and `number` would not resolve — this was the original async bug. + // Verify by checking that `number` resolves to a proper type (not error). + const pos = virtualFiles["/src/index.ts"].indexOf("x:"); + const type = await project.checker.getTypeAtPosition("/src/index.ts", pos); + assert.ok(type, "Type should resolve"); + assert.ok(type.flags & TypeFlags.Number, `Expected number type, got flags ${type.flags}`); + + // 3. null blocks fallback: blocked file should not be found + const blockedSf = await project.program.getSourceFile(blockedPath); + assert.equal(blockedSf, undefined, "Blocked file should not be found (null prevents fallback)"); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - isArrayType / isTupleType", () => { + test("number[] is array, not tuple", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const xs: number[] = [];`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export const xs: number[] = [];`; + const pos = src.indexOf("xs"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.equal(await project.checker.isArrayType(type), true); + assert.equal(await project.checker.isTupleType(type), false); + } + finally { + await api.close(); + } + }); + + test("readonly number[] is array", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const xs: readonly number[] = [];`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export const xs: readonly number[] = [];`; + const pos = src.indexOf("xs"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.equal(await project.checker.isArrayType(type), true); + assert.equal(await project.checker.isTupleType(type), false); + } + finally { + await api.close(); + } + }); + + test("Array is array, not tuple", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const xs: Array = [];`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export const xs: Array = [];`; + const pos = src.indexOf("xs"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.equal(await project.checker.isArrayType(type), true); + assert.equal(await project.checker.isTupleType(type), false); + } + finally { + await api.close(); + } + }); + + test("[number, string] is tuple, not array", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const tup: [number, string] = [1, "a"];`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export const tup: [number, string] = [1, "a"];`; + const pos = src.indexOf("tup"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.equal(await project.checker.isArrayType(type), false); + assert.equal(await project.checker.isTupleType(type), true); + } + finally { + await api.close(); + } + }); + + test("readonly [number, string] is tuple, not array", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const tup: readonly [number, string] = [1, "a"];`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export const tup: readonly [number, string] = [1, "a"];`; + const pos = src.indexOf("tup"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.equal(await project.checker.isArrayType(type), false); + assert.equal(await project.checker.isTupleType(type), true); + } + finally { + await api.close(); + } + }); + + test("string is neither array nor tuple", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const str: string = "";`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export const str: string = "";`; + const pos = src.indexOf("str"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.equal(await project.checker.isArrayType(type), false); + assert.equal(await project.checker.isTupleType(type), false); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - isReadonlySymbol", () => { + test("properties with a 'readonly' modifier", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export interface User { + readonly name: string; + age: number; +} + +export type ReadonlyUser = Readonly; +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const user = sourceFile.statements.find(isInterfaceDeclaration); + assert.ok(user); + const userProperties = await project.checker.getPropertiesOfType( + await project.checker.getTypeAtLocation(user), + ); + assert.equal(await project.checker.isReadonlySymbol(userProperties[0]), true); + assert.equal(await project.checker.isReadonlySymbol(userProperties[1]), false); + const readonlyUser = sourceFile.statements.find(isTypeAliasDeclaration); + assert.ok(readonlyUser); + const readonlyUserProperties = await project.checker.getPropertiesOfType( + await project.checker.getTypeAtLocation(readonlyUser), + ); + assert.equal(await project.checker.isReadonlySymbol(readonlyUserProperties[0]), true); + assert.equal(await project.checker.isReadonlySymbol(readonlyUserProperties[1]), true); + } + finally { + await api.close(); + } + }); + + test("variables declared with 'const'", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const a = 1; export let b = 2;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const { checker } = snapshot.getProject("/tsconfig.json")!; + const a = await checker.getSymbolAtPosition("/src/main.ts", "export const ".length); + const b = await checker.getSymbolAtPosition("/src/main.ts", "export const a = 1; export let ".length); + assert.ok(a); + assert.ok(b); + assert.equal(await checker.isReadonlySymbol(a), true); + assert.equal(await checker.isReadonlySymbol(b), false); + } + finally { + await api.close(); + } + }); + + test("get accessors without matching set accessors", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +class Alpha { + private _value!: number; + get value(): number { + return this._value; + } +} +class Bravo { + private _value!: number; + get value(): number { + return this._value; + } + set value(newValue: number) { + this._value = newValue; + } +} +export type A = InstanceType; +export type B = InstanceType; +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const typeAliases = sourceFile.statements.filter(isTypeAliasDeclaration); + assert.equal(typeAliases.length, 2); + const aProperties = await project.checker.getPropertiesOfType( + await project.checker.getTypeAtLocation(typeAliases[0]), + ); + assert.equal(await project.checker.isReadonlySymbol(aProperties[1]), true); + const bProperties = await project.checker.getPropertiesOfType( + await project.checker.getTypeAtLocation(typeAliases[1]), + ); + assert.equal(await project.checker.isReadonlySymbol(bProperties[1]), false); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getReturnTypeOfSignature", () => { + test("returns the return type of a function signature", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export function add(a: number, b: number): number { return a + b; }`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export function add(a: number, b: number): number { return a + b; }`; + const pos = src.indexOf("add("); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const returnType = await project.checker.getReturnTypeOfSignature(sigs[0]); + assert.ok(returnType); + assert.ok(returnType.flags & TypeFlags.Number, `Expected number, got flags ${returnType.flags}`); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getRestTypeOfSignature", () => { + test("returns the rest type of a signature with rest parameter", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export function sum(...nums: number[]): number { return nums.reduce((a, b) => a + b, 0); }`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export function sum(...nums: number[]): number { return nums.reduce((a, b) => a + b, 0); }`; + const pos = src.indexOf("sum("); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const restType = await project.checker.getRestTypeOfSignature(sigs[0]); + assert.ok(restType); + assert.ok(restType.flags & TypeFlags.Number, `Expected number type, got flags ${restType.flags}`); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getTypePredicateOfSignature", () => { + test("returns type predicate for 'x is T' guard", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export function isString(x: unknown): x is string { return typeof x === "string"; }`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export function isString(x: unknown): x is string { return typeof x === "string"; }`; + const pos = src.indexOf("isString("); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const predicate = await project.checker.getTypePredicateOfSignature(sigs[0]); + assert.ok(predicate); + assert.equal(predicate.kind, TypePredicateKind.Identifier); + assert.equal(predicate.parameterName, "x"); + assert.equal(predicate.parameterIndex, 0); + assert.ok(predicate.type); + assert.ok(predicate.type.flags & TypeFlags.String); + } + finally { + await api.close(); + } + }); + + test("returns type predicate for 'this is T' guard", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export class Animal { + isdog(): this is Dog { return this instanceof Dog; } +} +export class Dog extends Animal { + bark() {} +} +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\nexport class Animal {\n isdog(): this is Dog { return this instanceof Dog; }\n}\nexport class Dog extends Animal {\n bark() {}\n}\n`; + const pos = src.indexOf("isdog("); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const predicate = await project.checker.getTypePredicateOfSignature(sigs[0]); + assert.ok(predicate); + assert.equal(predicate.kind, TypePredicateKind.This); + } + finally { + await api.close(); + } + }); + + test("returns type predicate for 'asserts x is T'", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export function assertIsString(x: unknown): asserts x is string { if (typeof x !== "string") throw new Error(); }`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export function assertIsString(x: unknown): asserts x is string { if (typeof x !== "string") throw new Error(); }`; + const pos = src.indexOf("assertIsString("); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const predicate = await project.checker.getTypePredicateOfSignature(sigs[0]); + assert.ok(predicate); + assert.equal(predicate.kind, TypePredicateKind.AssertsIdentifier); + assert.equal(predicate.parameterName, "x"); + assert.equal(predicate.parameterIndex, 0); + assert.ok(predicate.type); + assert.ok(predicate.type.flags & TypeFlags.String); + } + finally { + await api.close(); + } + }); + + test("returns undefined for signature without type predicate", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export function add(a: number, b: number): number { return a + b; }`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export function add(a: number, b: number): number { return a + b; }`; + const pos = src.indexOf("add("); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const predicate = await project.checker.getTypePredicateOfSignature(sigs[0]); + assert.equal(predicate, undefined); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getBaseTypes", () => { + test("returns base types of a class", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export class Base { + x: number = 0; +} +export class Derived extends Base { + y: string = ""; +} +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\nexport class Base {\n x: number = 0;\n}\nexport class Derived extends Base {\n y: string = "";\n}\n`; + const pos = src.indexOf("Derived"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getDeclaredTypeOfSymbol(symbol); + assert.ok(type); + const baseTypes = await project.checker.getBaseTypes(type as InterfaceType); + assert.ok(baseTypes.length > 0, "Should have at least one base type"); + const baseSymbol = await baseTypes[0].getSymbol(); + assert.ok(baseSymbol); + assert.equal(baseSymbol.name, "Base"); + } + finally { + await api.close(); + } + }); + + test("returns base types of an interface", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export interface Animal { + name: string; +} +export interface Dog extends Animal { + bark(): void; +} +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\nexport interface Animal {\n name: string;\n}\nexport interface Dog extends Animal {\n bark(): void;\n}\n`; + const pos = src.indexOf("Dog"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getDeclaredTypeOfSymbol(symbol); + assert.ok(type); + const baseTypes = await project.checker.getBaseTypes(type as InterfaceType); + assert.ok(baseTypes.length > 0, "Should have at least one base type"); + const baseSymbol = await baseTypes[0].getSymbol(); + assert.ok(baseSymbol); + assert.equal(baseSymbol.name, "Animal"); + } + finally { + await api.close(); + } + }); + + test("does not panic for a type alias to a generic interface instantiation", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export interface Box { + value: T; +} +export type BoxOfString = Box; +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const typeAlias = sourceFile.statements.find(isTypeAliasDeclaration); + assert.ok(typeAlias); + const type = await project.checker.getTypeAtLocation(typeAlias); + assert.ok(type); + // A generic interface instantiation produces a type reference, not an + // interface type, so it has no base types and yields []. + const baseTypes = await project.checker.getBaseTypes(type as InterfaceType); + assert.deepEqual(baseTypes, []); + } + finally { + await api.close(); + } + }); +}); + +describe("Type - getBaseTypes", () => { + test("returns base types for a class type and undefined for a non-class/interface", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export class Base { + x: number = 0; +} +export class Derived extends Base { + y: string = ""; +} +export const n: number = 0; +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\nexport class Base {\n x: number = 0;\n}\nexport class Derived extends Base {\n y: string = "";\n}\nexport const n: number = 0;\n`; + + const derivedSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("Derived")); + assert.ok(derivedSymbol); + const derivedType = await project.checker.getDeclaredTypeOfSymbol(derivedSymbol); + assert.ok(derivedType.isClassOrInterface(), "Derived should be a class/interface type"); + const baseTypes = await derivedType.getBaseTypes(); + assert.ok(baseTypes && baseTypes.length > 0, "class type should have base types"); + const baseSymbol = await baseTypes![0].getSymbol(); + assert.equal(baseSymbol?.name, "Base"); + + // A primitive type is not a class/interface, so getBaseTypes() is undefined. + const numberSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("n:")); + assert.ok(numberSymbol); + const numberType = await project.checker.getTypeOfSymbol(numberSymbol); + assert.ok(numberType); + assert.equal(numberType.isClassOrInterface(), false); + assert.equal(await numberType.getBaseTypes(), undefined); + } + finally { + await api.close(); + } + }); +}); + +describe("Type - isErrorType", () => { + test("identifies the error type from an unresolvable annotation", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +declare const good: string; +declare const bad: ThisTypeDoesNotExist; +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\ndeclare const good: string;\ndeclare const bad: ThisTypeDoesNotExist;\n`; + + const badSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("bad")); + assert.ok(badSymbol); + const badType = await project.checker.getTypeOfSymbol(badSymbol); + assert.ok(badType); + assert.ok(badType.isErrorType(), "unresolved annotation should yield the error type"); + assert.ok(isErrorType(badType)); + + const goodSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("good")); + assert.ok(goodSymbol); + const goodType = await project.checker.getTypeOfSymbol(goodSymbol); + assert.ok(goodType); + assert.equal(goodType.isErrorType(), false, "string type is not the error type"); + assert.equal(isErrorType(goodType), false); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - well-known symbols", () => { + test("isUnknownSymbol identifies the aliased unknown symbol", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export const value = 1; +export type Alias = typeof value; +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\nexport const value = 1;\nexport type Alias = typeof value;\n`; + + // A real symbol is not the unknown/undefined symbol. + const valueSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("value")); + assert.ok(valueSymbol); + assert.equal(await project.checker.isUnknownSymbol(valueSymbol), false); + assert.equal(await project.checker.isUndefinedSymbol(valueSymbol), false); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - well-known signatures", () => { + test("isUnknownSignature identifies an unresolvable call", async () => { + const src = ` +const ok = (x: number) => x; +ok(1); +const notCallable = 1; +notCallable(); +`; + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const calls: Node[] = []; + sourceFile.forEachChild(function visit(node) { + if (isCallExpression(node)) calls.push(node); + node.forEachChild(visit); + }); + assert.equal(calls.length, 2, "should find two call expressions"); + + // The valid call resolves to a real signature, not the unknown signature. + const okSig = await project.checker.getResolvedSignature(calls[0]); + assert.equal(await project.checker.isUnknownSignature(okSig), false); + + // The call to a non-callable value yields the unknown signature. + const badSig = await project.checker.getResolvedSignature(calls[1]); + assert.equal(await project.checker.isUnknownSignature(badSig), true); + } + finally { + await api.close(); + } + }); +}); + +describe("Symbol - escaped names and tables", () => { + test("getExports/getMembers return a cached Map keyed by escaped name", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export class Animal { + name: string = ""; + speak(): void {} +} +export const value = 1; +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const moduleSymbol = await project.checker.getSymbolAtLocation(sourceFile); + assert.ok(moduleSymbol); + + const exports = await moduleSymbol.getExports(); + assert.ok(exports.has(escapeLeadingUnderscores("Animal"))); + assert.ok(exports.get(escapeLeadingUnderscores("value"))); + // Calling again returns the same cached Map instance. + assert.strictEqual(await moduleSymbol.getExports(), exports); + + const animal = exports.get(escapeLeadingUnderscores("Animal"))!; + const members = await animal.getMembers(); + assert.ok(members.get(escapeLeadingUnderscores("name"))); + assert.ok(members.get(escapeLeadingUnderscores("speak"))); + assert.strictEqual(await animal.getMembers(), members); + } + finally { + await api.close(); + } + }); + + test("anonymous type symbol has a __-escaped name, never the \\xFE sentinel", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export const obj: { a: number } = { a: 1 }; +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\nexport const obj: { a: number } = { a: 1 };\n`; + const objSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("obj")); + assert.ok(objSymbol); + const objType = await project.checker.getTypeOfSymbol(objSymbol); + assert.ok(objType); + const typeSymbol = await objType.getSymbol(); + assert.ok(typeSymbol); + // The anonymous type literal symbol is internally named; over the wire + // it must be the "__type" escaped form, not the raw "\xFE" sentinel. + assert.equal(typeSymbol.escapedName, InternalSymbolName.Type); + assert.ok(!typeSymbol.escapedName.includes("\xFE")); + } + finally { + await api.close(); + } + }); +}); + +describe("ast - escapeLeadingUnderscores", () => { + test("round-trips display and escaped names", () => { + assert.equal(escapeLeadingUnderscores("foo"), "foo"); + assert.equal(escapeLeadingUnderscores("_foo"), "_foo"); + assert.equal(escapeLeadingUnderscores("__foo"), "___foo"); + assert.equal(unescapeLeadingUnderscores("foo" as __String), "foo"); + assert.equal(unescapeLeadingUnderscores("__type" as __String), "__type"); + assert.equal(unescapeLeadingUnderscores("___foo" as __String), "__foo"); + }); +}); + +describe("ast - getJSDocTags", () => { + test("returns a node's own tags, and inherited @param / @template tags", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +/** + * Adds two numbers. + * @param a the first number + * @param b the second number + * @returns the sum + */ +export function add(a: number, b: number): number { + return a + b; +} + +/** + * @template T the element type + * @param x a value + */ +export function identity(x: T): T { + return x; +} + +/** @deprecated use add */ +export const total = add(1, 2); +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const functions = [...sourceFile.statements].filter(isFunctionDeclaration); + const add = functions[0]; + const identity = functions[1]; + assert.ok(add); + assert.ok(identity); + + // A function reports its own JSDoc tags. + assert.deepEqual(getJSDocTags(add).map(t => t.tagName.text), ["param", "param", "returns"]); + + // A parameter inherits the matching @param tag from its containing + // signature. + const paramA = add.parameters[0]; + const paramATags = getJSDocTags(paramA); + assert.deepEqual(paramATags.map(t => t.tagName.text), ["param"]); + const paramATag = paramATags[0]; + assert.ok(isJSDocParameterTag(paramATag)); + assert.ok(isIdentifier(paramATag.name)); + assert.equal(paramATag.name.text, "a"); + + const paramB = add.parameters[1]; + const paramBTags = getJSDocTags(paramB); + assert.deepEqual(paramBTags.map(t => t.tagName.text), ["param"]); + const paramBTag = paramBTags[0]; + assert.ok(isJSDocParameterTag(paramBTag)); + assert.ok(isIdentifier(paramBTag.name)); + assert.equal(paramBTag.name.text, "b"); + + // A type parameter inherits the matching @template tag. + const typeParam = identity.typeParameters![0]; + assert.deepEqual(getJSDocTags(typeParam).map(t => t.tagName.text), ["template"]); + + // The value parameter of `identity` inherits its @param tag. + const identityParam = identity.parameters[0]; + assert.deepEqual(getJSDocTags(identityParam).map(t => t.tagName.text), ["param"]); + + // A variable declaration walks up its comment-location chain + // (declaration -> declaration list -> statement) to the JSDoc on the + // containing variable statement. + const variable = sourceFile.statements.find(isVariableStatement); + assert.ok(variable); + const declaration = variable.declarationList.declarations[0]; + assert.deepEqual(getJSDocTags(declaration).map(t => t.tagName.text), ["deprecated"]); + } + finally { + await api.close(); + } + }); + + test("a function-expression parameter inherits @param tags from the variable statement", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { allowJs: true, checkJs: true } }), + "/src/main.js": ` +/** + * @param {string} name a name + * @returns {number} the length + */ +var measure = function (name) { + return name.length; +}; +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.js"); + assert.ok(sourceFile); + const variable = sourceFile.statements.find(isVariableStatement); + assert.ok(variable); + const declaration = variable.declarationList.declarations[0]; + const funcExpr = declaration.initializer; + assert.ok(funcExpr); + + // The variable statement carries the @param and @returns tags. + assert.deepEqual(getJSDocTags(declaration).map(t => t.tagName.text), ["param", "returns"]); + + // The function expression's parameter walks declaration -> list -> + // statement and inherits the matching @param tag. + const param = (funcExpr as unknown as { parameters: NodeArray; }).parameters[0]; + const paramTags = getJSDocTags(param); + assert.deepEqual(paramTags.map(t => t.tagName.text), ["param"]); + const paramTag = paramTags[0]; + assert.ok(isJSDocParameterTag(paramTag)); + assert.ok(isIdentifier(paramTag.name)); + assert.equal(paramTag.name.text, "name"); + } + finally { + await api.close(); + } + }); + + test("a @type cast tag is owned by its parenthesized expression, not the declaration", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { allowJs: true, checkJs: true } }), + "/src/main.js": ` +/** @type {string} */ +const value = "hello"; + +const cast = /** @type {number} */ (someValue); +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.js"); + assert.ok(sourceFile); + const statements = [...sourceFile.statements].filter(isVariableStatement); + + // A @type tag directly on a declaration is reported for that declaration. + const valueDecl = statements[0].declarationList.declarations[0]; + assert.deepEqual(getJSDocTags(valueDecl).map(t => t.tagName.text), ["type"]); + + // A @type cast tag attached to a parenthesized expression is owned by + // that expression and is not reported for the enclosing declaration. + const castDecl = statements[1].declarationList.declarations[0]; + assert.deepEqual(getJSDocTags(castDecl).map(t => t.tagName.text), []); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getPropertiesOfType", () => { + test("returns properties of an object type", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export interface Person { + name: string; + age: number; + greet(): void; +} +export declare const p: Person; +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\nexport interface Person {\n name: string;\n age: number;\n greet(): void;\n}\nexport declare const p: Person;\n`; + const pos = src.indexOf("p: Person"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const props = await project.checker.getPropertiesOfType(type); + assert.ok(props.length >= 3, `Expected at least 3 properties, got ${props.length}`); + const names = props.map(p => p.name); + assert.ok(names.includes("name"), "should have 'name' property"); + assert.ok(names.includes("age"), "should have 'age' property"); + assert.ok(names.includes("greet"), "should have 'greet' property"); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getIndexInfosOfType", () => { + test("returns index signatures of an indexed type", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export interface StringMap { + [key: string]: number; +} +export declare const m: StringMap; +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\nexport interface StringMap {\n [key: string]: number;\n}\nexport declare const m: StringMap;\n`; + const pos = src.indexOf("m: StringMap"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const indexInfos = await project.checker.getIndexInfosOfType(type); + assert.ok(indexInfos.length > 0, "Should have at least one index info"); + const info = indexInfos[0]; + assert.ok(info.keyType); + assert.ok(info.keyType.flags & TypeFlags.String, `Expected string key type, got flags ${info.keyType.flags}`); + assert.ok(info.valueType); + assert.ok(info.valueType.flags & TypeFlags.Number, `Expected number value type, got flags ${info.valueType.flags}`); + assert.equal(info.isReadonly, false); + } + finally { + await api.close(); + } + }); + + test("readonly index signature reports isReadonly true", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export interface ReadonlyMap { + readonly [key: string]: number; +} +export declare const m: ReadonlyMap; +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\nexport interface ReadonlyMap {\n readonly [key: string]: number;\n}\nexport declare const m: ReadonlyMap;\n`; + const pos = src.indexOf("m: ReadonlyMap"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const indexInfos = await project.checker.getIndexInfosOfType(type); + assert.ok(indexInfos.length > 0); + assert.equal(indexInfos[0].isReadonly, true); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getConstraintOfTypeParameter", () => { + test("returns constraint of a type parameter", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export function identity(x: T): T { return x; }`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export function identity(x: T): T { return x; }`; + const pos = src.indexOf("identity<"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + assert.ok(sigs[0].typeParameters && sigs[0].typeParameters.length > 0, "Should have type parameters"); + const typeParams = await sigs[0].getTypeParameters(); + const constraint = await project.checker.getConstraintOfTypeParameter(typeParams[0]); + assert.ok(constraint); + assert.ok(constraint.flags & TypeFlags.String, `Expected string constraint, got flags ${constraint.flags}`); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - TypeParameter getters", () => { + test("getConstraint() and getDefault() return the constraint and default types", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export function f(x: T): T { return x; }`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export function f(x: T): T { return x; }`; + const pos = src.indexOf("f<"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const typeParams = await sigs[0].getTypeParameters(); + assert.ok(typeParams.length > 0, "Should have type parameters"); + const typeParam = typeParams[0] as TypeParameter; + + const constraint = await typeParam.getConstraint(); + assert.ok(constraint); + assert.ok(constraint.flags & TypeFlags.String, `Expected string constraint, got flags ${constraint.flags}`); + + const defaultType = await typeParam.getDefault(); + assert.ok(defaultType); + assert.ok(defaultType.flags & TypeFlags.StringLiteral, `Expected string literal default, got flags ${defaultType.flags}`); + } + finally { + await api.close(); + } + }); + + test("getConstraint() and getDefault() return undefined when there is none", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export function f(x: T): T { return x; }`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export function f(x: T): T { return x; }`; + const pos = src.indexOf("f<"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const typeParams = await sigs[0].getTypeParameters(); + assert.ok(typeParams.length > 0, "Should have type parameters"); + const typeParam = typeParams[0] as TypeParameter; + + assert.equal(await typeParam.getConstraint(), undefined); + assert.equal(await typeParam.getDefault(), undefined); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getTypeArguments", () => { + test("returns type arguments of a generic instantiation", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const arr: Array = [1, 2, 3];`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export const arr: Array = [1, 2, 3];`; + const pos = src.indexOf("arr:"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const typeArgs = await project.checker.getTypeArguments(type as TypeReference); + assert.ok(typeArgs.length > 0, "Should have type arguments"); + assert.ok(typeArgs[0].flags & TypeFlags.Number, `Expected number type argument, got flags ${typeArgs[0].flags}`); + } + finally { + await api.close(); + } + }); + + test("a wrongly-typed call throws on the client without taking down the server", async () => { + const src = `export const s: string = ""; export const arr: Array = [1];`; + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + + // `string` is not a type reference. When getTypeArguments is reached + // with one, the server panics, but the per-request panic recovery + // converts that into an error response rather than crashing the process. + const sSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("s:")); + assert.ok(sSymbol); + const sType = await project.checker.getTypeOfSymbol(sSymbol); + assert.ok(sType); + await assert.rejects(() => project.checker.getTypeArguments(sType as unknown as TypeReference)); // @sync: assert.throws(() => project.checker.getTypeArguments(sType as unknown as TypeReference)); + + // The server survived: a subsequent valid request still succeeds. + const arrSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("arr:")); + assert.ok(arrSymbol); + const arrType = await project.checker.getTypeOfSymbol(arrSymbol); + assert.ok(arrType); + const typeArgs = await project.checker.getTypeArguments(arrType as TypeReference); + assert.ok(typeArgs.length > 0, "Server should still serve valid requests"); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getBaseConstraintOfType", () => { + test("returns the base constraint of a type parameter", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export function identity(x: T): T { return x; }`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export function identity(x: T): T { return x; }`; + const pos = src.indexOf("identity<"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = await project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const typeParams = await sigs[0].getTypeParameters(); + const constraint = await project.checker.getBaseConstraintOfType(typeParams[0]); + assert.ok(constraint, "Should resolve a base constraint"); + assert.ok(constraint.flags & TypeFlags.String, `Expected string constraint, got flags ${constraint.flags}`); + } + finally { + await api.close(); + } + }); + + test("returns undefined for a non-instantiable type", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const x: number = 1;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = `export const x: number = 1;`.indexOf("x:"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const constraint = await project.checker.getBaseConstraintOfType(type); + assert.equal(constraint, undefined); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getPropertyOfType", () => { + test("returns a named property symbol of a type", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export interface Person { + name: string; + age: number; +} +export declare const p: Person; +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\nexport interface Person {\n name: string;\n age: number;\n}\nexport declare const p: Person;\n`; + const pos = src.indexOf("p: Person"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const nameProp = await project.checker.getPropertyOfType(type, "name"); + assert.ok(nameProp, "Should find 'name' property"); + assert.equal(nameProp.name, "name"); + const missing = await project.checker.getPropertyOfType(type, "doesNotExist"); + assert.equal(missing, undefined); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getConstantValue", () => { + test("returns numeric value of an enum member", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export enum E { A = 1, B = 2 }`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + let memberB: Node | undefined; + sourceFile.forEachChild(function visit(node) { + if (node.kind === SyntaxKind.EnumMember) { + const text = sourceFile.text.slice(node.pos, node.end).trim(); + if (text.startsWith("B")) memberB = node; + } + node.forEachChild(visit); + }); + assert.ok(memberB, "Should find enum member B"); + const value = await project.checker.getConstantValue(memberB); + assert.equal(value, 2); + } + finally { + await api.close(); + } + }); + + test("returns string value of a string-initialized enum member", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export enum Color { Red = "red" }`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + let member: Node | undefined; + sourceFile.forEachChild(function visit(node) { + if (node.kind === SyntaxKind.EnumMember) member = node; + node.forEachChild(visit); + }); + assert.ok(member); + const value = await project.checker.getConstantValue(member); + assert.equal(value, "red"); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getSignatureFromDeclaration", () => { + test("returns the signature of a function declaration", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export function add(a: number, b: number): number { return a + b; }`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + let funcDecl: Node | undefined; + sourceFile.forEachChild(function visit(node) { + if (isFunctionDeclaration(node)) funcDecl = node; + node.forEachChild(visit); + }); + assert.ok(funcDecl, "Should find the function declaration"); + const sig = await project.checker.getSignatureFromDeclaration(funcDecl); + assert.ok(sig, "Should resolve a signature"); + assert.equal(sig.parameters.length, 2); + const returnType = await project.checker.getReturnTypeOfSignature(sig); + assert.ok(returnType); + assert.ok(returnType.flags & TypeFlags.Number); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getExportSpecifierLocalTargetSymbol", () => { + test("resolves the local target of an export specifier", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +const value = 42; +export { value as renamed }; +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + let exportSpecifier: Node | undefined; + sourceFile.forEachChild(function visit(node) { + if (node.kind === SyntaxKind.ExportSpecifier) exportSpecifier = node; + node.forEachChild(visit); + }); + assert.ok(exportSpecifier, "Should find the export specifier"); + const target = await project.checker.getExportSpecifierLocalTargetSymbol(exportSpecifier); + assert.ok(target, "Should resolve a local target symbol"); + assert.equal(target.name, "value"); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getAliasedSymbol", () => { + test("resolves an import alias to its target symbol", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/foo.ts": `export const foo = 42;`, + "/src/main.ts": `import { foo } from "./foo";\nexport const usage = foo;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = `import { foo } from "./foo";`.indexOf("foo }"); + const aliasSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(aliasSymbol); + assert.ok(aliasSymbol.flags & SymbolFlags.Alias, "Import binding should be an alias"); + const aliased = await project.checker.getAliasedSymbol(aliasSymbol); + assert.ok(aliased, "Should resolve the aliased symbol"); + assert.equal(aliased.name, "foo"); + assert.ok(!(aliased.flags & SymbolFlags.Alias), "Target should not be an alias"); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getFullyQualifiedName", () => { + test("returns module-qualified names for exported symbols and dotted names for members", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/index.ts": ` +export namespace Outer { + export class Inner {} +} +export class Standalone {} +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + const moduleSymbol = await project.checker.getSymbolAtLocation(sourceFile); + assert.ok(moduleSymbol); + const exports = await project.checker.getExportsOfModule(moduleSymbol); + const standalone = exports.find(e => e.name === "Standalone"); + assert.ok(standalone); + assert.equal(await project.checker.getFullyQualifiedName(standalone), `"/src/index".Standalone`); + const outer = exports.find(e => e.name === "Outer"); + assert.ok(outer); + const inner = (await outer.getExports()).get("Inner" as __String); + assert.ok(inner); + assert.equal(await project.checker.getFullyQualifiedName(inner), `"/src/index".Outer.Inner`); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getExportsOfModule", () => { + test("returns all exports including re-exports via 'export *'", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/inner.ts": `export const innerValue = 1;`, + "/src/index.ts": ` +export const direct = 1; +export * from "./inner"; +`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + const moduleSymbol = await project.checker.getSymbolAtLocation(sourceFile); + assert.ok(moduleSymbol, "Source file should have a module symbol"); + const exports = await project.checker.getExportsOfModule(moduleSymbol); + const names = exports.map(e => e.name); + assert.ok(names.includes("direct"), "should include directly-declared export"); + assert.ok(names.includes("innerValue"), "should include 'export *' re-export"); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getSymbolsInScope", () => { + const scopeFiles = { + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +const outerValue = 1; +interface OuterType { x: number; } +function f() { + const innerValue = 2; + return innerValue; +} +`, + }; + + test("returns symbols visible at a position", async () => { + const api = spawnAPI(scopeFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = scopeFiles["/src/main.ts"].indexOf("return innerValue"); + const symbols = await project.checker.getSymbolsInScope( + { document: "/src/main.ts", position: pos }, + SymbolFlags.Value, + ); + const names = symbols.map(s => s.name); + assert.ok(names.includes("innerValue"), "should include local variable"); + assert.ok(names.includes("outerValue"), "should include outer variable"); + assert.ok(names.includes("f"), "should include enclosing function"); + assert.ok(!names.includes("OuterType"), "should not include type-only meanings"); + } + finally { + await api.close(); + } + }); + + test("returns type symbols when asked for type meaning at a node", async () => { + const api = spawnAPI(scopeFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const symbols = await project.checker.getSymbolsInScope(sourceFile, SymbolFlags.Type); + const names = symbols.map(s => s.name); + assert.ok(names.includes("OuterType"), "should include interface declared in file"); + assert.ok(names.includes("Array"), "should include global type symbols"); + } + finally { + await api.close(); + } + }); +}); + +describe("Symbol - getDocumentationComment and getJsDocTags", () => { + const docFiles = { + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +/** + * Adds two numbers together. + * @param a the first number + * @returns the sum + */ +export function add(a: number, b: number): number { return a + b; } +`, + }; + + test("getDocumentationComment returns the leading comment text", async () => { + const api = spawnAPI(docFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = docFiles["/src/main.ts"].indexOf("add(a"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const doc = await symbol.getDocumentationComment(project.checker); + assert.ok(doc.includes("Adds two numbers together"), `Expected documentation, got: ${doc}`); + assert.ok(!doc.includes("@param"), "Documentation comment should not include tags"); + } + finally { + await api.close(); + } + }); + + test("getJsDocTags returns structured tag name/text pairs", async () => { + const api = spawnAPI(docFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = docFiles["/src/main.ts"].indexOf("add(a"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const tags = await symbol.getJsDocTags(project.checker); + const param = tags.find(t => t.name === "param"); + assert.ok(param, `Expected a @param tag, got: ${JSON.stringify(tags)}`); + assert.equal(param.text, "a the first number"); + const returns = tags.find(t => t.name === "returns"); + assert.ok(returns, `Expected a @returns tag, got: ${JSON.stringify(tags)}`); + assert.equal(returns.text, "the sum"); + } + finally { + await api.close(); + } + }); +}); + +describe("TypeParameter - isThisType", () => { + test("isThisType is true for the polymorphic 'this' type in a class method", async () => { + const src = `\nexport class Builder {\n setName(name: string): this { return this; }\n}\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + // ": this {" — offset 2 past ': ' lands on 't' in the return-type 'this' + const pos = src.indexOf(": this {") + 2; + const type = await project.checker.getTypeAtPosition("/src/main.ts", pos); + assert.ok(type, "Expected a type at the 'this' position"); + assert.ok(type.flags & TypeFlags.TypeParameter, "Expected TypeParameter"); + const typeParam = type as TypeParameter; + assert.equal(typeParam.isThisType, true); + } + finally { + await api.close(); + } + }); + + test("isThisType is absent for a regular generic type parameter", async () => { + const src = `\nexport function identity(x: T): T { return x; }\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + // Point to 'T' in the type parameter declaration '' — getTypeAtPosition + // on a type annotation reference doesn't resolve to TypeParameter, but + // the declaration position does. + const pos = src.indexOf("") + 1; + const type = await project.checker.getTypeAtPosition("/src/main.ts", pos); + assert.ok(type, "Expected a type at the 'T' position"); + assert.ok(type.flags & TypeFlags.TypeParameter, "Expected TypeParameter"); + const typeParam = type as TypeParameter; + assert.ok(!typeParam.isThisType, "Expected isThisType to be absent/false for a regular type parameter"); + } + finally { + await api.close(); + } + }); +}); + +describe("Type - getAliasTypeArguments", () => { + test("returns the type arguments of a single-param generic type alias", async () => { + const src = `\ntype Box = { value: T };\nexport const x: Box = { value: "hi" };\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("x:"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const aliasArgs = await type.getAliasTypeArguments(); + assert.equal(aliasArgs.length, 1, "Expected 1 alias type argument"); + assert.ok(aliasArgs[0].flags & TypeFlags.String, `Expected string, got flags ${aliasArgs[0].flags}`); + } + finally { + await api.close(); + } + }); + + test("returns multiple type arguments for a multi-param generic type alias", async () => { + const src = `\ntype Pair = [A, B];\nexport const p: Pair = ["hello", 42];\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("p:"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const aliasArgs = await type.getAliasTypeArguments(); + assert.equal(aliasArgs.length, 2, "Expected 2 alias type arguments"); + assert.ok(aliasArgs[0].flags & TypeFlags.String, `Expected first arg to be string, got flags ${aliasArgs[0].flags}`); + assert.ok(aliasArgs[1].flags & TypeFlags.Number, `Expected second arg to be number, got flags ${aliasArgs[1].flags}`); + } + finally { + await api.close(); + } + }); + + test("returns empty array for a non-alias generic type", async () => { + const src = `\nexport const arr: Array = ["hello"];\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("arr:"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const aliasArgs = await type.getAliasTypeArguments(); + assert.equal(aliasArgs.length, 0, "Expected no alias type arguments for a direct generic reference"); + } + finally { + await api.close(); + } + }); +}); + +describe("Type - getAliasSymbol", () => { + test("returns the symbol for a non-generic type alias", async () => { + // Object-type aliases preserve aliasSymbol; primitive aliases (type Foo = string) do not. + const src = `\ntype Point = { x: number; y: number };\nexport const p: Point = { x: 1, y: 2 };\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("p:"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const aliasSymbol = await type.getAliasSymbol(); + assert.ok(aliasSymbol, "Expected alias symbol to exist"); + assert.equal(aliasSymbol.name, "Point"); + } + finally { + await api.close(); + } + }); + + test("returns the symbol for a generic type alias", async () => { + const src = `\ntype Container = { item: T };\nexport const c: Container = { item: 42 };\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("c:"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const aliasSymbol = await type.getAliasSymbol(); + assert.ok(aliasSymbol, "Expected alias symbol for generic alias"); + assert.equal(aliasSymbol.name, "Container"); + } + finally { + await api.close(); + } + }); + + test("returns undefined for a non-alias type", async () => { + const src = `\nexport const str: string = "test";\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("str:"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const aliasSymbol = await type.getAliasSymbol(); + assert.equal(aliasSymbol, undefined, "Expected no alias symbol for primitive type"); + } + finally { + await api.close(); + } + }); +}); + +describe("IntrinsicType - intrinsicName", () => { + test("intrinsicName matches the primitive type name", async () => { + const src = `\nexport const x: string = "hello";\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const stringType = await project.checker.getStringType(); + assert.equal((stringType as IntrinsicType).intrinsicName, "string"); + const anyType = await project.checker.getAnyType(); + assert.equal((anyType as IntrinsicType).intrinsicName, "any"); + const neverType = await project.checker.getNeverType(); + assert.equal((neverType as IntrinsicType).intrinsicName, "never"); + const pos = src.indexOf("x:"); + const sym = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(sym); + const litType = await project.checker.getTypeOfSymbol(sym); + assert.ok(litType); + assert.ok(litType.flags & TypeFlags.Intrinsic); + assert.equal((litType as IntrinsicType).intrinsicName, "string"); + } + finally { + await api.close(); + } + }); +}); + +describe("FreshableType - getFreshType and getRegularType", () => { + test("LiteralType.value is empty string for the empty-string literal type", async () => { + const src = `\nexport const empty: "" = "";\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("empty:")); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.ok(type.flags & TypeFlags.StringLiteral, "Expected StringLiteral"); + const literal = type as LiteralType; + assert.equal(literal.value, "", "value should be empty string, not undefined"); + } + finally { + await api.close(); + } + }); + + test("LiteralType.value is accessible via the FreshableType hierarchy", async () => { + const src = `\nexport const greeting: "hello" = "hello";\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("greeting:"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.ok(type.flags & TypeFlags.StringLiteral, "Expected StringLiteral"); + const literal = type as LiteralType; + assert.equal(literal.value, "hello"); + } + finally { + await api.close(); + } + }); + + test("BigIntLiteralType.value is a bigint (positive and negative)", async () => { + const src = `\nexport const pos = 123n;\nexport const neg = -123n;\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + + const posSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("pos =")); + assert.ok(posSymbol); + const posType = await project.checker.getTypeOfSymbol(posSymbol); + assert.ok(posType); + assert.ok(posType.flags & TypeFlags.BigIntLiteral, "Expected BigIntLiteral"); + const posLiteral = posType as BigIntLiteralType; + assert.equal(typeof posLiteral.value, "bigint"); + assert.equal(posLiteral.value, 123n); + + const negSymbol = await project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("neg =")); + assert.ok(negSymbol); + const negType = await project.checker.getTypeOfSymbol(negSymbol); + assert.ok(negType); + assert.ok(negType.flags & TypeFlags.BigIntLiteral, "Expected BigIntLiteral"); + const negLiteral = negType as BigIntLiteralType; + assert.equal(typeof negLiteral.value, "bigint"); + assert.equal(negLiteral.value, -123n); + } + finally { + await api.close(); + } + }); + + test("getFreshType() returns a fresh twin with matching value", async () => { + const src = `\nexport const greeting: "hello" = "hello";\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("greeting:"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Freshable, "Type should be a freshable type"); + const fresh = await (type as FreshableType).getFreshType(); + assert.ok(fresh, "Expected getFreshType() to return non-undefined for a literal type"); + assert.ok(fresh.flags & TypeFlags.StringLiteral, "Fresh type should be a StringLiteral"); + assert.equal((fresh as LiteralType).value, "hello", "Fresh type should carry the same value"); + assert.notEqual(fresh.id, type.id, "Fresh type should not be the original type"); + const freshFresh = await fresh.getFreshType(); + assert.ok(freshFresh, "Expected getFreshType() to return non-undefined for a fresh type"); + assert.equal(freshFresh.id, fresh.id, "Fresh type of a fresh type should be the fresh type"); + } + finally { + await api.close(); + } + }); + + test("getRegularType() on a fresh literal returns the regular twin", async () => { + // The initial type response from getTypeOfSymbol does not always include the + // regularType handle, so getRegularType() is tested via the fresh twin which + // always includes its regularType in its own response. + const src = `\nexport const greeting: "hello" = "hello";\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("greeting:"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Freshable, "Type should be a freshable type"); + const fresh = await (type as FreshableType).getFreshType(); + assert.ok(fresh, "Need fresh type for this test"); + assert.ok(fresh.flags & TypeFlags.Freshable, "Fresh type should be a freshable type"); + const regular = await fresh.getRegularType(); + assert.ok(regular, "Expected getRegularType() on the fresh twin to return non-undefined"); + assert.ok(regular.flags & TypeFlags.StringLiteral, "Regular type should be a StringLiteral"); + assert.equal((regular as LiteralType).value, "hello", "Regular type should carry the same value"); + assert.equal(regular.id, type.id, "Regular type should be the original type"); + } + finally { + await api.close(); + } + }); + + test("getFreshType() and getRegularType() work for computed enum types (TypeFlags.Enum)", async () => { + // getTypeOfSymbol on an ambient enum member returns the FRESH computed enum type. + // For fresh types: getFreshType() returns self, getRegularType() returns the regular twin. + const src = `\ndeclare enum Status { Pending }\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("Pending"); + const symbol = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = await project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Enum, `Expected TypeFlags.Enum, got ${type.flags}`); + assert.ok(type.flags & TypeFlags.Freshable, "Enum type should be freshable"); + // The returned type IS the fresh type: getFreshType() returns itself + const fresh = await (type as FreshableType).getFreshType(); + assert.ok(fresh, "Expected getFreshType() to return non-undefined"); + assert.equal(fresh.id, type.id, "getFreshType() on a fresh enum type returns itself"); + // getRegularType() returns the regular twin (a different type) + const regular = await (type as FreshableType).getRegularType(); + assert.ok(regular, "Expected getRegularType() to return non-undefined"); + assert.ok(regular.flags & TypeFlags.Enum, "Regular enum type should also have TypeFlags.Enum"); + assert.notEqual(regular.id, type.id, "Regular type should be distinct from the fresh type"); + // Round-trip: regular → getFreshType() → back to the original fresh type + const backToFresh = await (regular as FreshableType).getFreshType(); + assert.ok(backToFresh); + assert.equal(backToFresh.id, type.id, "Round-trip through regular/fresh returns the original type"); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - isContextSensitive", () => { + test("arrow function with no type annotation is context sensitive", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const fn = (x) => x;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + // Find the arrow function node + let arrowFn: import("@typescript/typescript/unstable/ast").Node | undefined; + sourceFile.forEachChild(function visit(node) { + if (node.kind === SyntaxKind.ArrowFunction) { + arrowFn = node; + } + node.forEachChild(visit); + }); + assert.ok(arrowFn, "Should find an arrow function"); + const result = await project.checker.isContextSensitive(arrowFn); + assert.equal(result, true); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - isTypeAssignableTo", () => { + test("returns true when source is assignable to target", async () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": `export {};`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const stringType = await project.checker.getStringType(); + const anyType = await project.checker.getAnyType(); + const neverType = await project.checker.getNeverType(); + assert.ok(await project.checker.isTypeAssignableTo(stringType, stringType), "string assignable to string"); + assert.ok(await project.checker.isTypeAssignableTo(stringType, anyType), "string assignable to any"); + assert.ok(await project.checker.isTypeAssignableTo(neverType, stringType), "never assignable to string (bottom type)"); + } + finally { + await api.close(); + } + }); + + test("returns false when source is not assignable to target", async () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": `export {};`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const stringType = await project.checker.getStringType(); + const numberType = await project.checker.getNumberType(); + assert.ok(!await project.checker.isTypeAssignableTo(numberType, stringType), "number not assignable to string"); + assert.ok(!await project.checker.isTypeAssignableTo(stringType, numberType), "string not assignable to number"); + } + finally { + await api.close(); + } + }); + + test("a string literal type is assignable to string but not number", async () => { + const src = `\nexport const x: "hello" = "hello";\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("x:"); + const sym = await project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(sym); + const litType = await project.checker.getTypeOfSymbol(sym); + assert.ok(litType); + assert.ok(litType.flags & TypeFlags.StringLiteral); + const stringType = await project.checker.getStringType(); + const numberType = await project.checker.getNumberType(); + assert.ok(await project.checker.isTypeAssignableTo(litType, stringType)); + assert.ok(!await project.checker.isTypeAssignableTo(litType, numberType)); + } + finally { + await api.close(); + } + }); +}); + +describe("Emitter - printNode", () => { + const emitterFiles = { + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export const x = 42; +export function greet(name: string): string { return name; } +export type Pair = [string, number]; +export const obj = { m: 1, s: "hi", b: true }; +`, + }; + + test("printNode with factory-created keyword type", async () => { + const api = spawnAPI(emitterFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const node = createKeywordTypeNode(SyntaxKind.StringKeyword); + const text = await project.emitter.printNode(node); + assert.strictEqual(text, "string"); + } + finally { + await api.close(); + } + }); + + test("printNode with factory-created union type", async () => { + const api = spawnAPI(emitterFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const node = createUnionTypeNode([ + createKeywordTypeNode(SyntaxKind.StringKeyword), + createKeywordTypeNode(SyntaxKind.NumberKeyword), + ]); + const text = await project.emitter.printNode(node); + assert.strictEqual(text, "string | number"); + } + finally { + await api.close(); + } + }); + + test("printNode with factory-created function type", async () => { + const api = spawnAPI(emitterFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const param = createParameterDeclaration( + undefined, + undefined, + createIdentifier("x"), + undefined, + createKeywordTypeNode(SyntaxKind.StringKeyword), + undefined, + ); + const node = createFunctionTypeNode( + undefined, + [param], + createKeywordTypeNode(SyntaxKind.NumberKeyword), + ); + const text = await project.emitter.printNode(node); + assert.strictEqual(text, "(x: string) => number"); + } + finally { + await api.close(); + } + }); + + test("printNode with factory-created type reference", async () => { + const api = spawnAPI(emitterFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const node = createTypeReferenceNode(createIdentifier("Array"), [ + createKeywordTypeNode(SyntaxKind.StringKeyword), + ]); + const text = await project.emitter.printNode(node); + assert.strictEqual(text, "Array"); + } + finally { + await api.close(); + } + }); + + test("printNode with factory-created array type", async () => { + const api = spawnAPI(emitterFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const node = createArrayTypeNode(createKeywordTypeNode(SyntaxKind.NumberKeyword)); + const text = await project.emitter.printNode(node); + assert.strictEqual(text, "number[]"); + } + finally { + await api.close(); + } + }); + + test("typeToTypeNode + printNode round-trip", async () => { + const api = spawnAPI(emitterFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const { checker, emitter } = snapshot.getProject("/tsconfig.json")!; + const src = emitterFiles["/src/main.ts"]; + + const greetPos = src.indexOf("greet("); + const symbol = await checker.getSymbolAtPosition("/src/main.ts", greetPos); + assert.ok(symbol); + const type = await checker.getTypeOfSymbol(symbol); + assert.ok(type); + const typeNode = await checker.typeToTypeNode(type); + assert.ok(typeNode); + const text = await emitter.printNode(typeNode); + assert.ok(text); + assert.strictEqual(text, "(name: string) => string"); + } + finally { + await api.close(); + } + }); + + test("visitEachChild on typeToTypeNode result with keyword types", async () => { + const api = spawnAPI(emitterFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const { checker } = snapshot.getProject("/tsconfig.json")!; + const src = emitterFiles["/src/main.ts"]; + const objPos = src.indexOf("obj"); + const symbol = await checker.getSymbolAtPosition("/src/main.ts", objPos); + assert.ok(symbol, "should find symbol for obj"); + const type = await checker.getTypeOfSymbol(symbol); + assert.ok(type); + const typeNode = await checker.typeToTypeNode(type); + assert.ok(typeNode, "typeToTypeNode should return a type node"); + + // Recursively visit to reach PropertySignature.type where isTypeNode is checked. + const visited = (function visit(node: Node): Node { + return visitEachChild(node, visit); + })(typeNode); + assert.ok(visited, "visitEachChild should not throw"); + + const kinds = [ + SyntaxKind.NumberKeyword, + SyntaxKind.StringKeyword, + SyntaxKind.BooleanKeyword, + SyntaxKind.AnyKeyword, + SyntaxKind.VoidKeyword, + SyntaxKind.UndefinedKeyword, + SyntaxKind.NeverKeyword, + SyntaxKind.UnknownKeyword, + SyntaxKind.BigIntKeyword, + SyntaxKind.ObjectKeyword, + SyntaxKind.SymbolKeyword, + SyntaxKind.IntrinsicKeyword, + SyntaxKind.ExpressionWithTypeArguments, + SyntaxKind.JSDocAllType, + SyntaxKind.JSDocNullableType, + SyntaxKind.JSDocNonNullableType, + SyntaxKind.JSDocOptionalType, + SyntaxKind.JSDocVariadicType, + SyntaxKind.JSDocTypeExpression, + SyntaxKind.JSDocTypeLiteral, + SyntaxKind.JSDocSignature, + ]; + for (const kind of kinds) { + assert.ok(isTypeNode({ kind } as any), `isTypeNode should accept ${SyntaxKind[kind]}`); + } + } + finally { + await api.close(); + } + }); + + test("typeToString", async () => { + const api = spawnAPI(emitterFiles); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const { checker } = snapshot.getProject("/tsconfig.json")!; + const src = emitterFiles["/src/main.ts"]; + + const greetPos = src.indexOf("greet("); + const symbol = await checker.getSymbolAtPosition("/src/main.ts", greetPos); + assert.ok(symbol); + const type = await checker.getTypeOfSymbol(symbol); + assert.ok(type); + const text = await checker.typeToString(type); + assert.strictEqual(text, "(name: string) => string"); + } + finally { + await api.close(); + } + }); + + test("typeToString with TypeFormatFlags", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export function greet(name: string): string[] { return [name]; }`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const { checker } = snapshot.getProject("/tsconfig.json")!; + const greetPos = "export function greet".indexOf("greet"); + const symbol = await checker.getSymbolAtPosition("/src/main.ts", greetPos); + assert.ok(symbol); + const type = await checker.getTypeOfSymbol(symbol); + assert.ok(type); + const text = await checker.typeToString(type, undefined, TypeFormatFlags.WriteArrayAsGenericType); + assert.strictEqual(text, "(name: string) => Array"); + } + finally { + await api.close(); + } + }); + + test("printNode with terminateUnterminatedLiterals option", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `const foo = /asdfasf;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + + // Find the regex literal node + let regexNode: import("@typescript/typescript/unstable/ast").Node | undefined; + sourceFile.forEachChild(function visit(node) { + if (node.kind === SyntaxKind.RegularExpressionLiteral) { + regexNode = node; + return; + } + node.forEachChild(visit); + }); + assert.ok(regexNode, "Should find a regex literal"); + + // Without the option, regex is printed as-is + const textWithout = await project.emitter.printNode(regexNode); + assert.strictEqual(textWithout, "/asdfasf"); + + // With the option, the closing slash is added + const textWith = await project.emitter.printNode(regexNode, { terminateUnterminatedLiterals: true }); + assert.strictEqual(textWith, "/asdfasf/"); + } + finally { + await api.close(); + } + }); +}); + +describe("Program - selected file emit", () => { + const files = { + "/tsconfig.json": JSON.stringify({ + compilerOptions: { + declarationMap: true, + emitDeclarationOnly: true, + noEmit: true, + noEmitOnError: true, + sourceMap: true, + }, + }), + "/src/a.ts": `export const a: string = 1;`, + "/src/b.ts": `export const b = 2;`, + }; + + test("getJavaScriptEmit forces JavaScript and source maps", async () => { + const { api, fs } = spawnAPIWithFS({ ...files }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const result = await project.program.getJavaScriptEmit(["/src/a.ts", "/src/b.ts"]); + assert.equal(result.emitSkipped, false); + assert.deepEqual([...result.outputFiles.keys()], [ + "/src/a.js", + "/src/a.js.map", + "/src/b.js", + "/src/b.js.map", + ]); + assert.equal(result.outputFiles.get("/src/a.js")?.sourceFileName, "/src/a.ts"); + assert.match(result.outputFiles.get("/src/a.js")!.text, /export const a = 1/); + assert.equal(fs.readFile?.("/src/a.js"), undefined); + } + finally { + await api.close(); + } + }); + + test("getDeclarationEmit forces declarations and declaration maps", async () => { + const { api, fs } = spawnAPIWithFS({ ...files }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const result = await project.program.getDeclarationEmit(["/src/a.ts", "/src/b.ts"]); + assert.equal(result.emitSkipped, false); + assert.deepEqual([...result.outputFiles.keys()], [ + "/src/a.d.ts", + "/src/a.d.ts.map", + "/src/b.d.ts", + "/src/b.d.ts.map", + ]); + assert.equal(result.outputFiles.get("/src/a.d.ts")?.sourceFileName, "/src/a.ts"); + assert.equal(fs.readFile?.("/src/a.d.ts"), undefined); + } + finally { + await api.close(); + } + }); + + test("selected file emit accepts empty arrays", async () => { + const api = spawnAPI({ ...files }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + assert.deepEqual((await project.program.getJavaScriptEmit([])).outputFiles, new Map()); + assert.deepEqual((await project.program.getDeclarationEmit([])).outputFiles, new Map()); + } + finally { + await api.close(); + } + }); +}); + +describe("SnapshotInternalAPI - formatNodeForInsertion", () => { + test("formats a synthesized statement with correct indentation for insertion inside a function body", async () => { + const files = { + "/tsconfig.json": "{}", + "/src/index.ts": `function greet(name: string) {\n console.log(name);\n}\n`, + }; + const api = spawnAPI(files); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + + const node = createVariableStatement( + undefined, + createVariableDeclarationList( + [createVariableDeclaration(createIdentifier("x"), undefined, undefined, createNumericLiteral("1", 0))], + NodeFlags.Const, + ), + ); + + const sourceText = files["/src/index.ts"]; + const insertionPos = sourceText.indexOf("\n console.log") + 1; + + const formatted = await snapshot.internal.formatNodeForInsertion(node, "/src/index.ts", insertionPos); + assert.ok(formatted.includes("const x = 1;"), `Expected 'const x = 1;' in formatted output, got: ${JSON.stringify(formatted)}`); + assert.ok(formatted.startsWith(" "), `Expected 4 spaces of indentation, got: ${JSON.stringify(formatted)}`); + } + finally { + await api.close(); + } + }); + + test("formats a node at top-level with no indentation", async () => { + const files = { + "/tsconfig.json": "{}", + "/src/index.ts": `const a = 1;\nconst b = 2;\n`, + }; + const api = spawnAPI(files); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + + const node = createVariableStatement( + undefined, + createVariableDeclarationList( + [createVariableDeclaration(createIdentifier("y"), undefined, undefined, createNumericLiteral("42", 0))], + NodeFlags.Const, + ), + ); + + const formatted = await snapshot.internal.formatNodeForInsertion(node, "/src/index.ts", 0); + assert.ok(formatted.includes("const y = 42;"), `Expected 'const y = 42;' in formatted output, got: ${JSON.stringify(formatted)}`); + assert.ok(!formatted.startsWith(" "), `Expected no leading spaces at top level, got: ${JSON.stringify(formatted)}`); + } + finally { + await api.close(); + } + }); + + test("formats a synthesized statement with correct indentation when non-ASCII characters precede the insertion position", async () => { + // 'é' is 1 UTF-16 code unit but 2 UTF-8 bytes, so UTF-16 and UTF-8 offsets diverge after it. + // This test verifies the position is correctly converted from UTF-16 to UTF-8 before use. + const files = { + "/tsconfig.json": "{}", + "/src/index.ts": `// \u00e9\nfunction greet(name: string) {\n console.log(name);\n}\n`, + }; + const api = spawnAPI(files); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + + const node = createVariableStatement( + undefined, + createVariableDeclarationList( + [createVariableDeclaration(createIdentifier("x"), undefined, undefined, createNumericLiteral("1", 0))], + NodeFlags.Const, + ), + ); + + // insertionPos is the UTF-16 code-unit offset of the start of the indented line inside the function body. + const sourceText = files["/src/index.ts"]; + const insertionPos = sourceText.indexOf("\n console.log") + 1; + + const formatted = await snapshot.internal.formatNodeForInsertion(node, "/src/index.ts", insertionPos); + // The node should be indented to match the function body (4 spaces) + assert.ok(formatted.includes("const x = 1;"), `Expected 'const x = 1;' in formatted output, got: ${JSON.stringify(formatted)}`); + assert.ok(formatted.startsWith(" "), `Expected 4 spaces of indentation (UTF-16 offset correctly converted), got: ${JSON.stringify(formatted)}`); + } + finally { + await api.close(); + } + }); +}); + +describe("modifierFlags", () => { + test("export async function has Export | Async flags", async () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `export async function foo() {}`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + + let fnNode: import("@typescript/typescript/unstable/ast").FunctionDeclaration | undefined; + sourceFile.forEachChild(function visit(node) { + if (isFunctionDeclaration(node)) { + fnNode = node; + } + node.forEachChild(visit); + }); + assert.ok(fnNode, "Should find a function declaration"); + assert.ok(fnNode.modifierFlags & ModifierFlags.Export, "Should have Export flag"); + assert.ok(fnNode.modifierFlags & ModifierFlags.Async, "Should have Async flag"); + assert.strictEqual(fnNode.modifierFlags, ModifierFlags.Export | ModifierFlags.Async); + } + finally { + await api.close(); + } + }); + + test("node without modifiers has ModifierFlags.None", async () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `function bar() {}`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + + let fnNode: import("@typescript/typescript/unstable/ast").FunctionDeclaration | undefined; + sourceFile.forEachChild(function visit(node) { + if (isFunctionDeclaration(node)) { + fnNode = node; + } + node.forEachChild(visit); + }); + assert.ok(fnNode, "Should find a function declaration"); + assert.strictEqual(fnNode.modifierFlags, ModifierFlags.None); + } + finally { + await api.close(); + } + }); +}); + +describe("Checker - getResolvedSymbol", () => { + test("resolves variable reference to its declaration symbol", async () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `const x = 1;\nconst y = x;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + + // Find the 'x' identifier in `const y = x` + let refNode: import("@typescript/typescript/unstable/ast").Identifier | undefined; + sourceFile.forEachChild(function visit(node) { + if (isIdentifier(node) && node.text === "x") { + // We want the reference, not the declaration - take the last one + refNode = node; + } + node.forEachChild(visit); + }); + assert.ok(refNode, "Should find identifier 'x'"); + + const symbol = await project.checker.getResolvedSymbol(refNode); + assert.ok(symbol, "Should resolve symbol for 'x'"); + assert.equal(symbol.name, "x"); + } + finally { + await api.close(); + } + }); +}); + +describe("VariableDeclarationList - BlockScoped flags", () => { + test("let declaration has Let flag", async () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `let x = 1;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + + let declList: import("@typescript/typescript/unstable/ast").Node | undefined; + sourceFile.forEachChild(function visit(node) { + if (isVariableDeclarationList(node)) { + declList = node; + } + node.forEachChild(visit); + }); + assert.ok(declList, "Should find VariableDeclarationList"); + assert.ok(declList.flags & NodeFlags.Let, "Should have Let flag"); + } + finally { + await api.close(); + } + }); + + test("const declaration has Const flag", async () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `const x = 1;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + + let declList: import("@typescript/typescript/unstable/ast").Node | undefined; + sourceFile.forEachChild(function visit(node) { + if (isVariableDeclarationList(node)) { + declList = node; + } + node.forEachChild(visit); + }); + assert.ok(declList, "Should find VariableDeclarationList"); + assert.ok(declList.flags & NodeFlags.Const, "Should have Const flag"); + } + finally { + await api.close(); + } + }); +}); + +test("TypeOperator operator kind", async () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `function test(arg: readonly number[]) { }\n`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); + assert(sourceFile); + const param = (sourceFile.statements[0] as import("@typescript/typescript/unstable/ast").FunctionDeclaration).parameters[0]; + assert(param); + const type = param.type as import("@typescript/typescript/unstable/ast").TypeOperatorNode; + assert(type); + assert.equal(type.kind, SyntaxKind.TypeOperator); + assert.equal(type.operator, SyntaxKind.ReadonlyKeyword); + const printed = await project.emitter.printNode(sourceFile); + assert.equal(sourceFile.text, printed); + } + finally { + await api.close(); + } +}); + +test("SpreadAssignment roundtrip", async () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `var thing = { ...other };\n`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); + assert(sourceFile); + const stmt = sourceFile.statements[0] as import("@typescript/typescript/unstable/ast").VariableStatement; + const object = stmt.declarationList.declarations[0].initializer as import("@typescript/typescript/unstable/ast").ObjectLiteralExpression; + const assignment = object.properties[0] as import("@typescript/typescript/unstable/ast").SpreadAssignment; + assert(assignment); + assert.equal(assignment.kind, SyntaxKind.SpreadAssignment); + const expr = assignment.expression; + assert(expr); + assert.equal(expr.kind, SyntaxKind.Identifier); + const printed = await project.emitter.printNode(sourceFile); + assert.equal(sourceFile.text, printed); + } + finally { + await api.close(); + } +}); + +test("VariableDeclarationList const flag clone", async () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `const thing = 123;\n`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); + assert(sourceFile); + { + const stmt = sourceFile.statements[0] as import("@typescript/typescript/unstable/ast").VariableStatement; + const list = stmt.declarationList; + assert(list.flags & NodeFlags.Const); + } + const cloned = getSynthesizedDeepClone(sourceFile); + { + const stmt = cloned.statements[0] as import("@typescript/typescript/unstable/ast").VariableStatement; + const list = stmt.declarationList; + assert(list.flags & NodeFlags.Const); + } + const printed = await project.emitter.printNode(cloned); + assert.equal(sourceFile.text, printed); + } + finally { + await api.close(); + } +}); + +test("JSDoc before ExpressionStatement allowed", async () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": ` +/** + * A doc. + */ +doThing(); + `, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); + assert(sourceFile); + const printed = await project.emitter.printNode(sourceFile); + assert.equal(sourceFile.text.trim(), printed.trim()); + } + finally { + await api.close(); + } +}); + +test("Factory ModifierList auto-conversion", async () => { + const api = spawnAPI(); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const node = createTypeAliasDeclaration( + [createToken(SyntaxKind.ExportKeyword)], + createIdentifier("Test"), + undefined, + createKeywordTypeNode(SyntaxKind.AnyKeyword), + ); + + assert.equal(await project.emitter.printNode(node), "export type Test = any;"); + + const cloned = getSynthesizedDeepClone(node); + assert.equal(await project.emitter.printNode(cloned), "export type Test = any;"); + } + finally { + await api.close(); + } +}); + +test("Parse-clone-emit roundtrip", async () => { + const tsSource = fileURLToPath(new URL("../../../../tsc/testdata/fixtures", import.meta.url).toString()); + const api = new API({ + cwd: tsSource, + }); + const target = { + cloneCrashed: 0, + printCrashed: 0, + clonePrintCrashed: 0, + }; + const errors = { ...target }; + try { + for (const tsconfig of globSync("**/tsconfig.json", { cwd: tsSource })) { + const snapshot = await api.updateSnapshot({ openProject: resolve(tsSource, tsconfig) }); + const project = snapshot.getProject(tsconfig); + assert(project); + for (const file of project.rootFiles) { + const source = await project.program.getSourceFile(file); + assert(source); + let clone: typeof source; + + try { + await project.emitter.printNode(source); + } + catch { + errors.printCrashed++; + continue; + } + + try { + clone = getSynthesizedDeepClone(source); + } + catch { + errors.cloneCrashed++; + continue; + } + + try { + await project.emitter.printNode(clone); + } + catch { + errors.clonePrintCrashed++; + continue; + } + } + } + } + finally { + await api.close(); + } + assert.deepEqual(errors, target); +}); + +describe("Program - diagnostics", () => { + test("getSyntacticDiagnostics", async () => { + const source = `const x: = 1;`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": source, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = await project.program.getSyntacticDiagnostics("/src/index.ts"); + assert.deepEqual(diags[0].startPosition, { line: 0, character: 9 }); + assert.deepEqual(diags[0].endPosition, { line: 0, character: 10 }); + assert.deepEqual(diags[0].sourceLines, [{ line: 0, text: source }]); + assert.deepEqual(withoutFormattingContext(diags), [{ + fileName: "/src/index.ts", + ...rangeOf(source, "="), + code: 1110, + category: DiagnosticCategory.Error, + text: "Type expected.", + }]); + } + finally { + await api.close(); + } + }); + + test("getSemanticDiagnostics with messageChain and relatedInformation", async () => { + const source = `interface Props { callback: (x: string) => void }\nconst p: Props = { callback: (x: number) => {} };`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": source, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = await project.program.getSemanticDiagnostics("/src/index.ts"); + const declRange = rangeOf(source, "callback", 0); + const assignRange = rangeOf(source, "callback", 1); + assert.deepEqual(withoutFormattingContext(diags), [{ + fileName: "/src/index.ts", + ...assignRange, + code: 2322, + category: DiagnosticCategory.Error, + text: "Type '(x: number) => void' is not assignable to type '(x: string) => void'.", + messageChain: [{ + fileName: "/src/index.ts", + ...assignRange, + code: 2328, + category: DiagnosticCategory.Error, + text: "Types of parameters 'x' and 'x' are incompatible.", + messageChain: [{ + fileName: "/src/index.ts", + ...assignRange, + code: 2322, + category: DiagnosticCategory.Error, + text: "Type 'string' is not assignable to type 'number'.", + }], + }], + relatedInformation: [{ + fileName: "/src/index.ts", + ...declRange, + code: 6500, + category: DiagnosticCategory.Message, + text: "The expected type comes from property 'callback' which is declared here on type 'Props'", + }], + }]); + } + finally { + await api.close(); + } + }); + + test("getSuggestionDiagnostics", async () => { + const source = `export function f() { const x = 1; return x; }\nconst _unused = 1;\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": source, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = await project.program.getSuggestionDiagnostics("/src/index.ts"); + assert.deepEqual(withoutFormattingContext(diags), [{ + fileName: "/src/index.ts", + ...rangeOf(source, "_unused"), + code: 6133, + category: DiagnosticCategory.Suggestion, + text: "'_unused' is declared but its value is never read.", + reportsUnnecessary: true, + }]); + } + finally { + await api.close(); + } + }); + + test("getConfigFileParsingDiagnostics", async () => { + const config = `{ "compilerOptions": { "target": "invalid" } }`; + const api = spawnAPI({ + "/tsconfig.json": config, + "/src/index.ts": `export const x = 1;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = await project.program.getConfigFileParsingDiagnostics(); + assert.deepEqual(withoutFormattingContext(diags), [{ + fileName: "/tsconfig.json", + ...rangeOf(config, `"invalid"`), + code: 6046, + category: DiagnosticCategory.Error, + text: "Argument for '--target' option must be: 'es6', 'es2015', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'es2021', 'es2022', 'es2023', 'es2024', 'es2025', 'esnext'.", + }]); + } + finally { + await api.close(); + } + }); + + test("getConfigFileNames and getConfigSourceFile", async () => { + const baseConfigText = `{ "compilerOptions": { "strict": true } }`; + const { api, fs } = spawnAPIWithFS({ + "/tsconfig.base.json": baseConfigText, + "/tsconfig.json": `{ "extends": "./tsconfig.base.json", "files": ["./src/index.ts"] }`, + "/src/index.ts": `export const x = 1;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const names = await project.program.getConfigFileNames(); + assert.deepEqual(names, ["/tsconfig.json", "/tsconfig.base.json"]); + + const rootConfig = await project.program.getConfigSourceFile("/tsconfig.json"); + assert.ok(rootConfig); + assert.equal(rootConfig.fileName, "/tsconfig.json"); + assert.equal(await project.program.getSourceFile("/tsconfig.json"), undefined); + + fs.writeFile!("/tsconfig.base.json", `{ "compilerOptions": { "strict": false } }`); + const extendedConfig = await project.program.getConfigSourceFile("/tsconfig.base.json"); + assert.ok(extendedConfig); + assert.equal(extendedConfig.fileName, "/tsconfig.base.json"); + assert.equal(extendedConfig.getFullText(), baseConfigText); + + const nonConfig = await project.program.getConfigSourceFile("/src/index.ts"); + assert.equal(nonConfig, undefined); + } + finally { + await api.close(); + } + }); + + test("getDeclarationDiagnostics", async () => { + const api = spawnAPI({ + "/tsconfig.json": `{ "compilerOptions": { "declaration": true } }`, + "/src/index.ts": `export const x: number = 1;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = await project.program.getDeclarationDiagnostics("/src/index.ts"); + assert.deepEqual(diags, []); + } + finally { + await api.close(); + } + }); + + test("getBindDiagnostics", async () => { + const source = `let x = 1;\nlet x = 2;`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": source, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = await project.program.getBindDiagnostics("/src/index.ts"); + assert.deepEqual(withoutFormattingContext(diags), [ + { + fileName: "/src/index.ts", + ...rangeOf(source, "x", 0), + code: 2451, + category: DiagnosticCategory.Error, + text: "Cannot redeclare block-scoped variable 'x'.", + }, + { + fileName: "/src/index.ts", + ...rangeOf(source, "x", 1), + code: 2451, + category: DiagnosticCategory.Error, + text: "Cannot redeclare block-scoped variable 'x'.", + }, + ]); + } + finally { + await api.close(); + } + }); + + test("getProgramDiagnostics", async () => { + const config = `{ "compilerOptions": { "moduleResolution": "bundler", "module": "nodenext" } }`; + const api = spawnAPI({ + "/tsconfig.json": config, + "/src/index.ts": `export const x = 1;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = await project.program.getProgramDiagnostics(); + assert.deepEqual(withoutFormattingContext(diags), [ + { + fileName: "/tsconfig.json", + ...rangeOf(config, `"bundler"`), + code: 5095, + category: DiagnosticCategory.Error, + text: "Option 'bundler' can only be used when 'module' is set to 'preserve', 'commonjs', or 'es2015' or later.", + }, + { + fileName: "/tsconfig.json", + ...rangeOf(config, `"bundler"`), + code: 5109, + category: DiagnosticCategory.Error, + text: "Option 'moduleResolution' must be set to 'NodeNext' (or left unspecified) when option 'module' is set to 'NodeNext'.", + }, + ]); + } + finally { + await api.close(); + } + }); + + test("getGlobalDiagnostics", async () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `export const x = 1;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = await project.program.getGlobalDiagnostics(); + assert.deepEqual(diags, []); + } + finally { + await api.close(); + } + }); + + test("getGlobalDiagnostics returns file-less diagnostics from the checker", async () => { + const api = spawnAPI({ + "/tsconfig.json": `{ "compilerOptions": { "noLib": true } }`, + "/src/index.ts": `export const x = [1, 2, 3];`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = await project.program.getGlobalDiagnostics(); + // With noLib, the checker reports "Cannot find global type" diagnostics that + // are not associated with any source file. + assert.ok(diags.length > 0, "expected global diagnostics to be reported"); + for (const diag of diags) { + assert.equal(diag.fileName, undefined); + assert.equal(diag.code, 2318); + assert.equal(diag.category, DiagnosticCategory.Error); + } + assert.ok( + diags.some(d => d.text === "Cannot find global type 'Array'."), + "expected a global diagnostic for the 'Array' type", + ); + } + finally { + await api.close(); + } + }); + + test("getSyntacticDiagnostics with multiple files", async () => { + const sourceA = `const a: = 1;`; + const sourceB = `const b: = 2;`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/a.ts": sourceA, + "/src/b.ts": sourceB, + "/src/clean.ts": `const c = 3;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = await project.program.getSyntacticDiagnostics(["/src/a.ts", "/src/b.ts"]); + assert.deepEqual(withoutFormattingContext(diags), [ + { + fileName: "/src/a.ts", + ...rangeOf(sourceA, "="), + code: 1110, + category: DiagnosticCategory.Error, + text: "Type expected.", + }, + { + fileName: "/src/b.ts", + ...rangeOf(sourceB, "="), + code: 1110, + category: DiagnosticCategory.Error, + text: "Type expected.", + }, + ]); + } + finally { + await api.close(); + } + }); + + test("getSemanticDiagnostics with multiple files", async () => { + const sourceA = `export const a: number = "hello";`; + const sourceB = `export const b: string = 42;`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/a.ts": sourceA, + "/src/b.ts": sourceB, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = await project.program.getSemanticDiagnostics(["/src/a.ts", "/src/b.ts"]); + assert.equal(diags.length, 2); + assert.equal(diags[0].fileName, "/src/a.ts"); + assert.equal(diags[0].code, 2322); + assert.equal(diags[1].fileName, "/src/b.ts"); + assert.equal(diags[1].code, 2322); + } + finally { + await api.close(); + } + }); + + test("getBindDiagnostics with multiple files", async () => { + const sourceA = `let x = 1;\nlet x = 2;`; + const sourceB = `let y = 1;\nlet y = 2;`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/a.ts": sourceA, + "/src/b.ts": sourceB, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = await project.program.getBindDiagnostics(["/src/a.ts", "/src/b.ts"]); + assert.equal(diags.length, 4); + assert.equal(diags.filter(d => d.fileName === "/src/a.ts").length, 2); + assert.equal(diags.filter(d => d.fileName === "/src/b.ts").length, 2); + } + finally { + await api.close(); + } + }); + + test("diagnostics with no files argument returns all", async () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/a.ts": `const a: = 1;`, + "/src/b.ts": `const b: = 2;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = await project.program.getSyntacticDiagnostics(); + assert.equal(diags.length, 2); + } + finally { + await api.close(); + } + }); + + test("diagnostics with empty files array returns empty", async () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/a.ts": `const a: = 1;`, + "/src/b.ts": `const b: = 2;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = await project.program.getSyntacticDiagnostics([]); + assert.deepEqual(diags, []); + } + finally { + await api.close(); + } + }); +}); + +describe("getDefaultProjectForFile", () => { + test("finds inferred project for d.ts in node_modules after openFiles", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/index.ts": `export const x = 1;`, + "/node_modules/my-lib/package.json": JSON.stringify({ name: "my-lib", types: "./index.d.ts" }), + "/node_modules/my-lib/index.d.ts": `export declare const foo: string;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + + // The d.ts is not imported, so it is not in the project's program + const dtsSf = await project.program.getSourceFile("/node_modules/my-lib/index.d.ts"); + assert.equal(dtsSf, undefined, "d.ts not in import graph should not be found via project.program.getSourceFile"); + + // Before opening the file, getDefaultProjectForFile returns undefined (no error) + const noProject = await snapshot.getDefaultProjectForFile("/node_modules/my-lib/index.d.ts"); + assert.equal(noProject, undefined, "getDefaultProjectForFile returns undefined for unloaded file"); + + // Load the file into the inferred project via updateSnapshot openFiles + const snapshot2 = await api.updateSnapshot({ openFiles: ["/node_modules/my-lib/index.d.ts"] }); + const defaultProject = await snapshot2.getDefaultProjectForFile("/node_modules/my-lib/index.d.ts"); + assert.ok(defaultProject, "getDefaultProjectForFile should find inferred project after openFiles"); + + const fooPos = `export declare const foo: string;`.indexOf("foo"); + const fooType = await defaultProject.checker.getTypeAtPosition("/node_modules/my-lib/index.d.ts", fooPos); + assert.ok(fooType); + assert.ok(fooType.flags & TypeFlags.String); + } + finally { + await api.close(); + } + }); + + test("inferred project reflects file changes without a follow-up open/close request", async () => { + const { api, fs } = spawnAPIWithFS({ + "/loose.ts": `export const foo = 1;`, + }); + try { + const snapshot1 = await api.updateSnapshot({ openFiles: ["/loose.ts"] }); + const project1 = await snapshot1.getDefaultProjectForFile("/loose.ts"); + assert.ok(project1, "file with no config file in its ancestry should load into the inferred project"); + const sf1 = await project1.program.getSourceFile("/loose.ts"); + assert.ok(sf1); + assert.equal(sf1.text, `export const foo = 1;`); + + // Mutate the file and notify only via fileChanges — no follow-up openFiles/closeFiles. + fs.writeFile!("/loose.ts", `export const foo = 2;`); + const snapshot2 = await api.updateSnapshot({ + fileChanges: { changed: ["/loose.ts"] }, + }); + + const project2 = await snapshot2.getDefaultProjectForFile("/loose.ts"); + assert.ok(project2); + const sf2 = await project2.program.getSourceFile("/loose.ts"); + assert.ok(sf2); + assert.equal( + sf2.text, + `export const foo = 2;`, + "inferred project's program should reflect the file change even without a follow-up open/close request", + ); + } + finally { + await api.close(); + } + }); + + test("keeps previously opened files open across subsequent openFiles calls", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/index.ts": `export const x = 1;`, + "/node_modules/my-lib/package.json": JSON.stringify({ name: "my-lib", types: "./index.d.ts" }), + "/node_modules/my-lib/index.d.ts": `export declare const foo: string;`, + "/node_modules/other-lib/package.json": JSON.stringify({ name: "other-lib", types: "./index.d.ts" }), + "/node_modules/other-lib/index.d.ts": `export declare const bar: number;`, + }); + try { + await api.updateSnapshot({ openProject: "/tsconfig.json" }); + await api.updateSnapshot({ openFiles: ["/node_modules/my-lib/index.d.ts"] }); + + // Opening a second file in a later snapshot must not close the first one. + const snapshot = await api.updateSnapshot({ openFiles: ["/node_modules/other-lib/index.d.ts"] }); + + const firstProject = await snapshot.getDefaultProjectForFile("/node_modules/my-lib/index.d.ts"); + assert.ok(firstProject, "previously opened file should remain in the inferred project"); + const secondProject = await snapshot.getDefaultProjectForFile("/node_modules/other-lib/index.d.ts"); + assert.ok(secondProject, "newly opened file should be in the inferred project"); + } + finally { + await api.close(); + } + }); + + test("opening a file resolves to a configured project via ancestor search", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/index.ts": `export const x = 1;`, + }); + try { + // Open the file without first opening the project. Like LSP's didOpen, this + // should search ancestor directories for a tsconfig that contains the file. + const snapshot = await api.updateSnapshot({ openFiles: ["/src/index.ts"] }); + const defaultProject = await snapshot.getDefaultProjectForFile("/src/index.ts"); + assert.ok(defaultProject, "should find a project for the opened file"); + assert.equal( + defaultProject.configFileName, + "/tsconfig.json", + "opened file should resolve to the containing configured project, not the inferred project", + ); + } + finally { + await api.close(); + } + }); + + test("closeProjects releases a project opened via openProjects", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/index.ts": `export const x = 1;`, + }); + try { + const opened = await api.updateSnapshot({ openProjects: ["/tsconfig.json"] }); + assert.ok(opened.getProject("/tsconfig.json"), "project should be open after openProjects"); + + const closed = await api.updateSnapshot({ closeProjects: ["/tsconfig.json"] }); + assert.equal( + closed.getProject("/tsconfig.json"), + undefined, + "project should be unloaded after closeProjects", + ); + } + finally { + await api.close(); + } + }); + + test("closeFiles releases a file opened via openFiles", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/index.ts": `export const x = 1;`, + "/node_modules/my-lib/package.json": JSON.stringify({ name: "my-lib", types: "./index.d.ts" }), + "/node_modules/my-lib/index.d.ts": `export declare const foo: string;`, + }); + try { + await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const opened = await api.updateSnapshot({ openFiles: ["/node_modules/my-lib/index.d.ts"] }); + assert.ok( + await opened.getDefaultProjectForFile("/node_modules/my-lib/index.d.ts"), + "file should resolve to a project after openFiles", + ); + + const closed = await api.updateSnapshot({ closeFiles: ["/node_modules/my-lib/index.d.ts"] }); + assert.equal( + await closed.getDefaultProjectForFile("/node_modules/my-lib/index.d.ts"), + undefined, + "file should no longer resolve to a project after closeFiles", + ); + } + finally { + await api.close(); + } + }); +}); + +describe("Program - emit", () => { + const files = { + "/tsconfig.json": `{ + "compilerOptions": { + "outDir": "dist", + "declaration": true, + } + }`, + "/src/index.ts": "export const x: number = 1;", + "/src/testing.ts": "export const y: string = 'typescript';", + }; + + test("emit all files", async () => { + const fs = createVirtualFileSystem({ ...files }); + + const api = new API({ + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + fs: fs, + }); + + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const result = await project.program.emit(); + assert.deepEqual(result, { + diagnostics: [], + emitSkipped: false, + emittedFiles: [ + "/dist/src/index.js", + "/dist/src/index.d.ts", + "/dist/src/testing.js", + "/dist/src/testing.d.ts", + ], + }); + + const js = fs.readFile?.("/dist/src/index.js"); + const dts = fs.readFile?.("/dist/src/index.d.ts"); + const js2 = fs.readFile?.("/dist/src/testing.js"); + const dts2 = fs.readFile?.("/dist/src/testing.d.ts"); + assert.strictEqual(js, `export const x = 1;\n`); + assert.strictEqual(dts, `export declare const x: number;\n`); + assert.strictEqual(js2, `export const y = 'typescript';\n`); + assert.strictEqual(dts2, `export declare const y: string;\n`); + } + finally { + await api.close(); + } + }); + + test("emit only dts", async () => { + const fs = createVirtualFileSystem({ ...files }); + + const api = new API({ + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + fs: fs, + }); + + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const result = await project.program.emit(EmitOnly.OnlyDts); + assert.deepEqual(result, { + diagnostics: [], + emitSkipped: false, + emittedFiles: [ + "/dist/src/index.d.ts", + "/dist/src/testing.d.ts", + ], + }); + + const js = fs.readFile?.("/dist/src/index.js"); + const dts = fs.readFile?.("/dist/src/index.d.ts"); + const js2 = fs.readFile?.("/dist/src/testing.js"); + const dts2 = fs.readFile?.("/dist/src/testing.d.ts"); + assert.strictEqual(js, undefined); + assert.strictEqual(dts, `export declare const x: number;\n`); + assert.strictEqual(js2, undefined); + assert.strictEqual(dts2, `export declare const y: string;\n`); + } + finally { + await api.close(); + } + }); + + test("emit only js", async () => { + const fs = createVirtualFileSystem({ ...files }); + + const api = new API({ + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + fs: fs, + }); + + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const result = await project.program.emit(EmitOnly.OnlyJs); + assert.deepEqual(result, { + diagnostics: [], + emitSkipped: false, + emittedFiles: [ + "/dist/src/index.js", + "/dist/src/testing.js", + ], + }); + + const js = fs.readFile?.("/dist/src/index.js"); + const dts = fs.readFile?.("/dist/src/index.d.ts"); + const js2 = fs.readFile?.("/dist/src/testing.js"); + const dts2 = fs.readFile?.("/dist/src/testing.d.ts"); + assert.strictEqual(js, `export const x = 1;\n`); + assert.strictEqual(dts, undefined); + assert.strictEqual(js2, `export const y = 'typescript';\n`); + assert.strictEqual(dts2, undefined); + } + finally { + await api.close(); + } + }); + + test("emitToString emits the whole program and respects emitOnly", async () => { + const { api, fs } = spawnAPIWithFS({ ...files }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const result = await project.program.emitToString(EmitOnly.OnlyDts); + assert.deepEqual([...result.outputFiles.keys()], [ + "/dist/src/index.d.ts", + "/dist/src/testing.d.ts", + ]); + assert.equal(fs.readFile?.("/dist/src/index.js"), undefined); + } + finally { + await api.close(); + } + }); + + test("whole-program emit includes option-controlled maps", async () => { + const { api, fs } = spawnAPIWithFS({ + "/tsconfig.json": JSON.stringify({ + compilerOptions: { + declaration: true, + declarationMap: true, + outDir: "dist", + sourceMap: true, + }, + }), + "/src/index.ts": "export const value: number = 1;", + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + + const result = await project.program.emit(); + assert.deepEqual( + new Set(result.emittedFiles), + new Set([ + "/dist/src/index.js", + "/dist/src/index.js.map", + "/dist/src/index.d.ts", + "/dist/src/index.d.ts.map", + ]), + ); + assert.ok(fs.fileExists?.("/dist/src/index.js.map")); + assert.ok(fs.fileExists?.("/dist/src/index.d.ts.map")); + + const js = await project.program.emitToString(EmitOnly.OnlyJs); + assert.deepEqual([...js.outputFiles.keys()], [ + "/dist/src/index.js", + "/dist/src/index.js.map", + ]); + + const dts = await project.program.emitToString(EmitOnly.OnlyDts); + assert.deepEqual([...dts.outputFiles.keys()], [ + "/dist/src/index.d.ts", + "/dist/src/index.d.ts.map", + ]); + } + finally { + await api.close(); + } + }); + + test("noEmitOnError reports diagnostics without writing output", async () => { + const { api, fs } = spawnAPIWithFS({ + "/tsconfig.json": JSON.stringify({ + compilerOptions: { + noEmitOnError: true, + outDir: "dist", + }, + }), + "/src/bad.ts": "export const bad = ;", + "/src/good.ts": "export const value = 1;", + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const result = await project.program.emit(); + assert.equal(result.emitSkipped, true); + assert.ok(result.diagnostics.some(d => d.code === 1109)); + assert.deepEqual(result.emittedFiles, []); + assert.equal(fs.readFile?.("/dist/src/bad.js"), undefined); + assert.equal(fs.readFile?.("/dist/src/good.js"), undefined); + + const stringResult = await project.program.emitToString(); + assert.equal(stringResult.emitSkipped, true); + assert.ok(stringResult.diagnostics.some(d => d.code === 1109)); + assert.deepEqual(stringResult.outputFiles, new Map()); + } + finally { + await api.close(); + } + }); + + test("emit and emitToString respect noEmit", async () => { + const { api, fs } = spawnAPIWithFS({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { noEmit: true } }), + "/src/index.ts": "export const value = 1;", + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + assert.deepEqual(await project.program.emit(), { + diagnostics: [], + emitSkipped: false, + emittedFiles: [], + }); + assert.deepEqual(await project.program.emitToString(), { + diagnostics: [], + emitSkipped: false, + outputFiles: new Map(), + }); + assert.equal(fs.readFile?.("/src/index.js"), undefined); + } + finally { + await api.close(); + } + }); + + test("emit rejects unknown files and invalid emitOnly values", async () => { + const api = spawnAPI({ ...files }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + + let error: unknown; + try { + await project.program.getJavaScriptEmit(["/src/missing.ts"]); + } + catch (e) { + error = e; + } + assert.match(String(error), /source file not found/); + + error = undefined; + try { + await project.program.emitToString(3 as EmitOnly); + } + catch (e) { + error = e; + } + assert.match(String(error), /invalid emitOnly value/); + } + finally { + await api.close(); + } + }); + + // @sync-skip-block-start + test("emit reports async VFS write failures as diagnostics", async () => { + const fs = createVirtualFileSystem({ ...files }); + fs.writeFile = () => { + throw new Error("write failed"); + }; + const api = new API({ + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + fs, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const result = await project.program.emit(); + assert.deepEqual(result.emittedFiles, []); + assert.ok(result.diagnostics.some(d => d.text.includes("write failed"))); + } + finally { + await api.close(); + } + }); + // @sync-skip-block-end +}); + +test("Benchmarks", async () => { + await runBenchmarks({ singleIteration: true }); +}); + +describe("Timing", () => { + test("collects combined client, server, and transport timing info", async () => { + const api = new API({ + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + fs: createVirtualFileSystem({ ...defaultFiles }), + collectTiming: true, + }); + try { + // Baseline: enabled, but nothing measured yet. + let info = await api.getTimingInfo(); + assert.equal(info.enabled, true); + assert.equal(info.totals.requestCount, 0); + assert.equal(info.recentRequests.length, 0); + + // Exercise a JSON request and a binary source-file request. + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + + // Client-side timing: round-trip latency and byte counts. + info = await api.getTimingInfo(); + assert.ok(info.totals.requestCount >= 2, "expected at least two measured requests"); + assert.ok(info.totals.bytesSent > 0); + assert.ok(info.totals.bytesReceived > 0); + assert.ok(info.totals.roundTripMs >= 0); + + // Server-side timing is folded into the same snapshot. + assert.ok(info.totals.serverTimeMs >= 0); + assert.ok(info.totals.transportOverheadMs >= 0); + assert.equal( + info.totals.transportOverheadMs, + Math.max(0, info.totals.roundTripMs - info.totals.serverTimeMs), + ); + + // The ring buffer retains at most the 5 most recent requests. + assert.ok(info.recentRequests.length > 0); + assert.ok(info.recentRequests.length <= 5); + assert.ok(info.recentRequests.length <= info.totals.requestCount); + + for (const r of info.recentRequests) { + assert.ok(r.roundTripMs >= 0); + assert.ok(r.bytesSent >= 0); + assert.ok(r.bytesReceived >= 0); + assert.ok(typeof r.method === "string" && r.method.length > 0); + assert.equal(typeof r.timestamp, "number"); + // Transport overhead is present exactly when server time is. + assert.equal(r.transportOverheadMs === undefined, r.serverTimeMs === undefined); + if (r.serverTimeMs !== undefined) { + assert.ok(r.serverTimeMs >= 0); + assert.equal(r.transportOverheadMs, Math.max(0, r.roundTripMs - r.serverTimeMs)); + } + } + + // Server processing time is folded in for the recent requests. + assert.ok(info.recentRequests.every(r => r.serverTimeMs !== undefined), "server time should be reported"); + + // Reset clears totals and history on both client and server. + await api.resetTimingInfo(); + info = await api.getTimingInfo(); + assert.equal(info.enabled, true); + assert.equal(info.totals.requestCount, 0); + assert.equal(info.totals.serverTimeMs, 0); + assert.equal(info.totals.transportOverheadMs, 0); + assert.equal(info.recentRequests.length, 0); + } + finally { + await api.close(); + } + }); + + test("tracks source-file node materialization", async () => { + const api = new API({ + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + fs: createVirtualFileSystem({ ...defaultFiles }), + collectTiming: true, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = await project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + + // The source file node itself is pre-cached, so before walking the + // tree no descendant nodes have been materialized. + let info = await api.getTimingInfo(); + const before = info.totals.nodesMaterialized; + assert.equal(info.totals.sourceFilesFetched, 1, "one source file was fetched"); + assert.ok(info.totals.nodesFetched > 0, "the fetched file contributes materializable nodes"); + assert.equal(info.totals.nodesMaterialized, 0, "nothing walked yet, so no nodes materialized"); + + // Walk the whole tree to force lazy materialization of every node. + let visited = 0; + sourceFile.forEachChild(function visit(node) { + visited++; + node.forEachChild(visit); + }); + assert.ok(visited > 0, "expected to visit at least one node"); + + info = await api.getTimingInfo(); + assert.ok( + info.totals.nodesMaterialized > before, + "walking the tree should materialize nodes", + ); + assert.ok( + info.totals.nodesMaterialized >= visited, + "every visited node should have been materialized", + ); + + // A full walk materializes (nearly) every fetched node, so the share + // of fetched nodes materialized should be substantial. + assert.equal(info.totals.sourceFilesFetched, 1); + assert.ok( + info.totals.nodesMaterialized > 0 + && info.totals.nodesMaterialized <= info.totals.nodesFetched, + "materialized nodes should be in (0, nodesFetched]", + ); + + // Reset clears materialization totals along with the rest. + await api.resetTimingInfo(); + info = await api.getTimingInfo(); + assert.equal(info.totals.nodesMaterialized, 0); + assert.equal(info.totals.sourceFilesFetched, 0); + assert.equal(info.totals.nodesFetched, 0); + } + finally { + await api.close(); + } + }); + + test("is disabled by default", async () => { + const api = spawnAPI(); + try { + await api.parseConfigFile("/tsconfig.json"); + const info = await api.getTimingInfo(); + assert.equal(info.enabled, false); + assert.equal(info.totals.requestCount, 0); + assert.equal(info.totals.nodesMaterialized, 0); + assert.equal(info.totals.sourceFilesFetched, 0); + assert.equal(info.totals.nodesFetched, 0); + assert.equal(info.recentRequests.length, 0); + } + finally { + await api.close(); + } + }); +}); + +describe("runWithTemporaryFileUpdate", () => { + test("temporary file update is visible in the callback and reverted afterward", async () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/index.ts": `export const x: number = 1;`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + + // The original content type-checks cleanly. + const baseDiags = await project.program.getSemanticDiagnostics("/src/index.ts"); + assert.equal(baseDiags.length, 0); + + // Keep a newer snapshot active to verify any active snapshot can be the base. + const latestSnapshot = await api.updateSnapshot(); + assert.notEqual(latestSnapshot.id, snapshot.id); + + // Inside the callback, the file has the temporary (erroneous) content. + let errorCount = -1; + await api.runWithTemporaryFileUpdate(snapshot, "/src/index.ts", `export const x: string = 1;`, async tempSnapshot => { + const tempProject = tempSnapshot.getProject("/tsconfig.json")!; + const diags = await tempProject.program.getSemanticDiagnostics("/src/index.ts"); + errorCount = diags.length; + }); + assert.ok(errorCount > 0, "temporary content should produce a semantic error inside the callback"); + + // The original snapshot is unaffected by the temporary update. + const afterDiags = await project.program.getSemanticDiagnostics("/src/index.ts"); + assert.equal(afterDiags.length, 0); + + // Subsequent regular updates still work and diff against the real latest snapshot. + const snapshot2 = await api.updateSnapshot(); + const project2 = snapshot2.getProject("/tsconfig.json")!; + const diags2 = await project2.program.getSemanticDiagnostics("/src/index.ts"); + assert.equal(diags2.length, 0); + } + finally { + await api.close(); + } + }); +}); + +function spawnAPIWithFS(files: Record = { ...defaultFiles }): { api: API; fs: FileSystem; } { + const fs = createVirtualFileSystem(files); + const api = new API({ + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + fs, + }); + return { api, fs }; +} + +/** Returns `{ pos, end }` for the nth (0-based, default 0) occurrence of `searchString` in `source`. */ +function rangeOf(source: string, searchString: string, occurrence: number = 0): { pos: number; end: number; } { + let index = -1; + for (let i = 0; i <= occurrence; i++) { + index = source.indexOf(searchString, index + 1); + if (index === -1) { + throw new Error(`Occurrence ${occurrence} of "${searchString}" not found in source`); + } + } + return { pos: index, end: index + searchString.length }; +} + +function withoutFormattingContext(value: T): T { + const formattingKeys = new Set(["startPosition", "endPosition", "sourceLines"]); + return JSON.parse(JSON.stringify(value, (key, item) => formattingKeys.has(key) ? undefined : item)) as T; +} + +function applyTextEdits(source: string, edits: readonly TextEdit[]): string { + const sorted = [...edits].sort((a, b) => b.pos - a.pos); + let result = source; + for (const edit of sorted) { + result = result.slice(0, edit.pos) + edit.newText + result.slice(edit.end); + } + return result; +} diff --git a/packages/typescript/test/async/api.testUtils.ts b/packages/typescript/test/async/api.testUtils.ts index c3ad21541b429..4b8600abcf295 100644 --- a/packages/typescript/test/async/api.testUtils.ts +++ b/packages/typescript/test/async/api.testUtils.ts @@ -1,20 +1,20 @@ -import { - API, - type APIOptions, -} from "@typescript/typescript/unstable/async"; -import { createVirtualFileSystem } from "@typescript/typescript/unstable/fs"; -import { fileURLToPath } from "node:url"; - -export const defaultFiles = { - "/tsconfig.json": "{}", - "/src/index.ts": `import { foo } from './foo';`, - "/src/foo.ts": `export const foo = 42;`, -}; - -export function spawnAPI(files: Record = { ...defaultFiles }, options: Omit = {}) { - return new API({ - ...options, - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), - fs: createVirtualFileSystem(files), - }); -} +import { + API, + type APIOptions, +} from "@typescript/typescript/unstable/async"; +import { createVirtualFileSystem } from "@typescript/typescript/unstable/fs"; +import { fileURLToPath } from "node:url"; + +export const defaultFiles = { + "/tsconfig.json": "{}", + "/src/index.ts": `import { foo } from './foo';`, + "/src/foo.ts": `export const foo = 42;`, +}; + +export function spawnAPI(files: Record = { ...defaultFiles }, options: Omit = {}) { + return new API({ + ...options, + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + fs: createVirtualFileSystem(files), + }); +} diff --git a/packages/typescript/test/async/astnav.test.ts b/packages/typescript/test/async/astnav.test.ts index d4285c7fd53e1..eae5a55613079 100644 --- a/packages/typescript/test/async/astnav.test.ts +++ b/packages/typescript/test/async/astnav.test.ts @@ -1,188 +1,188 @@ -import { API } from "@typescript/typescript/unstable/async"; // @sync-skip -// @sync-only-start -// import { API } from "@typescript/typescript/unstable/sync"; -// @sync-only-end -import { - findNextToken, - findPrecedingToken, - formatSyntaxKind, - getTokenAtPosition, - getTouchingPropertyName, -} from "@typescript/typescript/unstable/ast"; -import type { - Node, - SourceFile, -} from "@typescript/typescript/unstable/ast"; -import { createVirtualFileSystem } from "@typescript/typescript/unstable/fs"; -import assert from "node:assert"; -import { readFileSync } from "node:fs"; -import { resolve } from "node:path"; -import { - after, - before, - describe, - test, -} from "node:test"; -import { fileURLToPath } from "node:url"; - -// --------------------------------------------------------------------------- -// Go JSON baseline format -// --------------------------------------------------------------------------- - -interface TokenRun { - startPos: number; - endPos: number; - kind: string; - nodePos: number; - nodeEnd: number; -} - -/** - * Expand run-length encoded Go baseline into a per-position map. - */ -function expandBaseline(runs: TokenRun[]): Map { - const map = new Map(); - for (const run of runs) { - const entry = { kind: run.kind, pos: run.nodePos, end: run.nodeEnd }; - for (let p = run.startPos; p <= run.endPos; p++) { - map.set(p, entry); - } - } - return map; -} - -// --------------------------------------------------------------------------- -// Helpers -// --------------------------------------------------------------------------- - -/** Convert an astnav result node to a string-based token info for comparison. */ -function toTokenInfo(node: Node): { kind: string; pos: number; end: number; } { - return { - kind: formatSyntaxKind(node.kind), - pos: node.pos, - end: node.end, - }; -} - -// --------------------------------------------------------------------------- -// Test configuration -// --------------------------------------------------------------------------- - -const repoRoot = resolve(import.meta.dirname!, "..", "..", "..", ".."); -const testFile = resolve(repoRoot, "tsc/testdata/fixtures/services/mapCode.ts"); -const baselineDir = resolve(repoRoot, "tsc/testdata/baselines/reference/astnav"); - -// --------------------------------------------------------------------------- -// Tests -// --------------------------------------------------------------------------- - -describe("astnav", () => { - let fileText: string; - - try { - fileText = readFileSync(testFile, "utf-8"); - } - catch { - console.log("Skipping astnav tests: compiler fixtures not available"); - fileText = ""; - } - - if (!fileText) return; - - // Use the Go API to parse the file — the resulting SourceFile is already - // in our SyntaxKind/NodeFlags enum space with correct JSDoc structure. - let api: API; - let sourceFile: SourceFile; - - before(async () => { - api = new API({ - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), - fs: createVirtualFileSystem({ - "/tsconfig.json": JSON.stringify({ files: ["/src/testFile.ts"] }), - "/src/testFile.ts": fileText, - }), - }); - - const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sf = await project.program.getSourceFile("/src/testFile.ts"); - assert.ok(sf, "Failed to get source file from API"); - sourceFile = sf; - }); - - after(async () => { - await api.close(); - }); - - const testCases = [ - { - name: "getTokenAtPosition", - baselineFile: "GetTokenAtPosition.mapCode.ts.baseline.json", - fn: (sf: SourceFile, pos: number) => getTokenAtPosition(sf, pos) as Node | undefined, - }, - { - name: "getTouchingPropertyName", - baselineFile: "GetTouchingPropertyName.mapCode.ts.baseline.json", - fn: (sf: SourceFile, pos: number) => getTouchingPropertyName(sf, pos) as Node | undefined, - }, - { - name: "findPrecedingToken", - baselineFile: "FindPrecedingToken.mapCode.ts.baseline.json", - fn: (sf: SourceFile, pos: number) => findPrecedingToken(sf, pos), - }, - { - name: "findNextToken", - baselineFile: "FindNextToken.mapCode.ts.baseline.json", - fn: (sf: SourceFile, pos: number) => findNextToken(getTokenAtPosition(sf, pos), sf, sf), - }, - ]; - - for (const tc of testCases) { - test(tc.name, () => { - const baselinePath = resolve(baselineDir, tc.baselineFile); - const runs: TokenRun[] = JSON.parse(readFileSync(baselinePath, "utf-8")); - const expected = expandBaseline(runs); - - const failures: string[] = []; - - for (let pos = 0; pos < fileText.length; pos++) { - const node = tc.fn(sourceFile, pos); - const goExpected = expected.get(pos); - - if (!goExpected) continue; - - if (node === undefined) { - failures.push( - ` pos ${pos}: expected ${goExpected.kind} [${goExpected.pos}, ${goExpected.end}), got undefined`, - ); - if (failures.length >= 50) { - failures.push(" ... (truncated, too many failures)"); - break; - } - continue; - } - - const result = toTokenInfo(node); - if (result.kind !== goExpected.kind || result.pos !== goExpected.pos || result.end !== goExpected.end) { - failures.push( - ` pos ${pos}: expected ${goExpected.kind} [${goExpected.pos}, ${goExpected.end}), ` + - `got ${result.kind} [${result.pos}, ${result.end})`, - ); - if (failures.length >= 50) { - failures.push(" ... (truncated, too many failures)"); - break; - } - } - } - - console.log(` ${tc.name}: checked ${fileText.length} positions`); - - if (failures.length > 0) { - assert.fail( - `${tc.name}: ${failures.length} position(s) differ from Go baseline:\n` + - failures.join("\n"), - ); - } - }); - } -}); +import { API } from "@typescript/typescript/unstable/async"; // @sync-skip +// @sync-only-start +// import { API } from "@typescript/typescript/unstable/sync"; +// @sync-only-end +import { + findNextToken, + findPrecedingToken, + formatSyntaxKind, + getTokenAtPosition, + getTouchingPropertyName, +} from "@typescript/typescript/unstable/ast"; +import type { + Node, + SourceFile, +} from "@typescript/typescript/unstable/ast"; +import { createVirtualFileSystem } from "@typescript/typescript/unstable/fs"; +import assert from "node:assert"; +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { + after, + before, + describe, + test, +} from "node:test"; +import { fileURLToPath } from "node:url"; + +// --------------------------------------------------------------------------- +// Go JSON baseline format +// --------------------------------------------------------------------------- + +interface TokenRun { + startPos: number; + endPos: number; + kind: string; + nodePos: number; + nodeEnd: number; +} + +/** + * Expand run-length encoded Go baseline into a per-position map. + */ +function expandBaseline(runs: TokenRun[]): Map { + const map = new Map(); + for (const run of runs) { + const entry = { kind: run.kind, pos: run.nodePos, end: run.nodeEnd }; + for (let p = run.startPos; p <= run.endPos; p++) { + map.set(p, entry); + } + } + return map; +} + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +/** Convert an astnav result node to a string-based token info for comparison. */ +function toTokenInfo(node: Node): { kind: string; pos: number; end: number; } { + return { + kind: formatSyntaxKind(node.kind), + pos: node.pos, + end: node.end, + }; +} + +// --------------------------------------------------------------------------- +// Test configuration +// --------------------------------------------------------------------------- + +const repoRoot = resolve(import.meta.dirname!, "..", "..", "..", ".."); +const testFile = resolve(repoRoot, "tsc/testdata/fixtures/services/mapCode.ts"); +const baselineDir = resolve(repoRoot, "tsc/testdata/baselines/reference/astnav"); + +// --------------------------------------------------------------------------- +// Tests +// --------------------------------------------------------------------------- + +describe("astnav", () => { + let fileText: string; + + try { + fileText = readFileSync(testFile, "utf-8"); + } + catch { + console.log("Skipping astnav tests: compiler fixtures not available"); + fileText = ""; + } + + if (!fileText) return; + + // Use the Go API to parse the file — the resulting SourceFile is already + // in our SyntaxKind/NodeFlags enum space with correct JSDoc structure. + let api: API; + let sourceFile: SourceFile; + + before(async () => { + api = new API({ + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + fs: createVirtualFileSystem({ + "/tsconfig.json": JSON.stringify({ files: ["/src/testFile.ts"] }), + "/src/testFile.ts": fileText, + }), + }); + + const snapshot = await api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sf = await project.program.getSourceFile("/src/testFile.ts"); + assert.ok(sf, "Failed to get source file from API"); + sourceFile = sf; + }); + + after(async () => { + await api.close(); + }); + + const testCases = [ + { + name: "getTokenAtPosition", + baselineFile: "GetTokenAtPosition.mapCode.ts.baseline.json", + fn: (sf: SourceFile, pos: number) => getTokenAtPosition(sf, pos) as Node | undefined, + }, + { + name: "getTouchingPropertyName", + baselineFile: "GetTouchingPropertyName.mapCode.ts.baseline.json", + fn: (sf: SourceFile, pos: number) => getTouchingPropertyName(sf, pos) as Node | undefined, + }, + { + name: "findPrecedingToken", + baselineFile: "FindPrecedingToken.mapCode.ts.baseline.json", + fn: (sf: SourceFile, pos: number) => findPrecedingToken(sf, pos), + }, + { + name: "findNextToken", + baselineFile: "FindNextToken.mapCode.ts.baseline.json", + fn: (sf: SourceFile, pos: number) => findNextToken(getTokenAtPosition(sf, pos), sf, sf), + }, + ]; + + for (const tc of testCases) { + test(tc.name, () => { + const baselinePath = resolve(baselineDir, tc.baselineFile); + const runs: TokenRun[] = JSON.parse(readFileSync(baselinePath, "utf-8")); + const expected = expandBaseline(runs); + + const failures: string[] = []; + + for (let pos = 0; pos < fileText.length; pos++) { + const node = tc.fn(sourceFile, pos); + const goExpected = expected.get(pos); + + if (!goExpected) continue; + + if (node === undefined) { + failures.push( + ` pos ${pos}: expected ${goExpected.kind} [${goExpected.pos}, ${goExpected.end}), got undefined`, + ); + if (failures.length >= 50) { + failures.push(" ... (truncated, too many failures)"); + break; + } + continue; + } + + const result = toTokenInfo(node); + if (result.kind !== goExpected.kind || result.pos !== goExpected.pos || result.end !== goExpected.end) { + failures.push( + ` pos ${pos}: expected ${goExpected.kind} [${goExpected.pos}, ${goExpected.end}), ` + + `got ${result.kind} [${result.pos}, ${result.end})`, + ); + if (failures.length >= 50) { + failures.push(" ... (truncated, too many failures)"); + break; + } + } + } + + console.log(` ${tc.name}: checked ${fileText.length} positions`); + + if (failures.length > 0) { + assert.fail( + `${tc.name}: ${failures.length} position(s) differ from Go baseline:\n` + + failures.join("\n"), + ); + } + }); + } +}); diff --git a/packages/typescript/test/diagnosticFormatter.test.ts b/packages/typescript/test/diagnosticFormatter.test.ts index bce9f0f98371a..31d69efdd9d32 100644 --- a/packages/typescript/test/diagnosticFormatter.test.ts +++ b/packages/typescript/test/diagnosticFormatter.test.ts @@ -1,133 +1,133 @@ -import { - API, - formatDiagnostics, - formatDiagnosticsWithColorAndContext, -} from "@typescript/typescript/unstable/async"; -import { createVirtualFileSystem } from "@typescript/typescript/unstable/fs"; -import assert from "node:assert"; -import { - describe, - test, -} from "node:test"; - -describe("diagnosticFormatter", () => { - test("formats diagnostics with a configured program host", async () => { - const source = `const x: number = "oops";\n`; - const api = spawnAPI({ - "/project/tsconfig.json": `{ "compilerOptions": { "strict": true, "newLine": "crlf" } }`, - "/project/index.ts": source, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/project/tsconfig.json" }); - const program = snapshot.getProject("/project/tsconfig.json")!.program; - const diagnostics = await program.getSemanticDiagnostics("/project/index.ts"); - assert.equal(diagnostics.length, 1); - assert.equal(api.getCurrentDirectory(), "/workspace"); - assert.equal(api.getNewLine(), "\n"); - assert.equal(program.getCurrentDirectory(), "/project"); - assert.equal(program.getNewLine(), "\r\n"); - - const plain = formatDiagnostics(diagnostics, program); - assert.equal( - plain, - "index.ts(1,7): error TS2322: Type 'string' is not assignable to type 'number'.\r\n", - ); - - const color = formatDiagnosticsWithColorAndContext(diagnostics, program); - assert.ok(color.includes("TS2322: "), color); - assert.ok(color.includes(source.trim()), color); - assert.ok(color.includes("~"), color); - assert.ok(color.includes("\x1b["), color); - assert.ok(color.endsWith("\r\n"), color); - const doubled = formatDiagnosticsWithColorAndContext([diagnostics[0], diagnostics[0]], program); - assert.equal(doubled, color + "\r\n" + color); - - const zeroWidth = { - ...diagnostics[0], - end: diagnostics[0].pos, - endPosition: diagnostics[0].startPosition!, - }; - const zeroWidthColor = formatDiagnosticsWithColorAndContext([zeroWidth], program); - assert.match(zeroWidthColor, /\x1b\[91m +~\x1b\[0m/); - - const relatedText = "Related information"; - const withRelated = { - ...diagnostics[0], - relatedInformation: [{ ...diagnostics[0], text: relatedText }], - }; - const relatedColor = formatDiagnosticsWithColorAndContext([withRelated], program); - const relatedMessage = relatedColor.indexOf(` - ${relatedText}`); - assert.notEqual(relatedMessage, -1, relatedColor); - assert.ok(relatedColor.indexOf(source.trim(), relatedMessage) > relatedMessage, relatedColor); - - const multiline = { - ...diagnostics[0], - startPosition: { line: 0, character: 0 }, - endPosition: { line: 6, character: 5 }, - sourceLines: [ - { line: 0, text: "one\n" }, - { line: 1, text: "two\n" }, - { line: 5, text: "six\n" }, - { line: 6, text: "seven" }, - ], - }; - const multilineColor = formatDiagnosticsWithColorAndContext([multiline], program); - assert.ok(multilineColor.includes("..."), multilineColor); - assert.ok(multilineColor.includes("seven"), multilineColor); - } - finally { - await api.close(); - } - }); - - test("uses the API host for standalone diagnostics", async () => { - const configText = `{ "compilerOptions": { "target": "invalid" } }`; - const api = spawnAPI({ - "/workspace/tsconfig.json": configText, - "/workspace/index.ts": `const x: number = "oops";`, - }); - try { - const snapshot = await api.updateSnapshot({ openProject: "/workspace/tsconfig.json" }); - const program = snapshot.getProject("/workspace/tsconfig.json")!.program; - const diagnostics = await program.getSemanticDiagnostics("/workspace/index.ts"); - const configDiagnostics = (await api.parseConfigFile("/workspace/tsconfig.json")).errors; - const clonedDiagnostics = [ - [{ ...diagnostics[0] }], - structuredClone(diagnostics), - JSON.parse(JSON.stringify(diagnostics)), - ]; - - for (const cloned of clonedDiagnostics) { - assert.ok(formatDiagnostics(cloned, api).includes("TS2322")); - } - assert.ok(formatDiagnostics(configDiagnostics, api).includes("tsconfig.json(1,34): error TS6046: ")); - assert.ok(formatDiagnosticsWithColorAndContext(configDiagnostics, api).includes(configText)); - } - finally { - await api.close(); - } - }); - - test("uses the API directory and LF defaults for inferred projects", async () => { - const api = spawnAPI({ - "/workspace/index.ts": `const x: number = "oops";`, - }); - try { - const snapshot = await api.updateSnapshot({ openFiles: ["/workspace/index.ts"] }); - const project = await snapshot.getDefaultProjectForFile("/workspace/index.ts"); - assert.ok(project); - assert.equal(project.program.getCurrentDirectory(), api.getCurrentDirectory()); - assert.equal(project.program.getNewLine(), "\n"); - } - finally { - await api.close(); - } - }); -}); - -function spawnAPI(files: Record): API { - return new API({ - cwd: "/workspace", - fs: createVirtualFileSystem(files), - }); -} +import { + API, + formatDiagnostics, + formatDiagnosticsWithColorAndContext, +} from "@typescript/typescript/unstable/async"; +import { createVirtualFileSystem } from "@typescript/typescript/unstable/fs"; +import assert from "node:assert"; +import { + describe, + test, +} from "node:test"; + +describe("diagnosticFormatter", () => { + test("formats diagnostics with a configured program host", async () => { + const source = `const x: number = "oops";\n`; + const api = spawnAPI({ + "/project/tsconfig.json": `{ "compilerOptions": { "strict": true, "newLine": "crlf" } }`, + "/project/index.ts": source, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/project/tsconfig.json" }); + const program = snapshot.getProject("/project/tsconfig.json")!.program; + const diagnostics = await program.getSemanticDiagnostics("/project/index.ts"); + assert.equal(diagnostics.length, 1); + assert.equal(api.getCurrentDirectory(), "/workspace"); + assert.equal(api.getNewLine(), "\n"); + assert.equal(program.getCurrentDirectory(), "/project"); + assert.equal(program.getNewLine(), "\r\n"); + + const plain = formatDiagnostics(diagnostics, program); + assert.equal( + plain, + "index.ts(1,7): error TS2322: Type 'string' is not assignable to type 'number'.\r\n", + ); + + const color = formatDiagnosticsWithColorAndContext(diagnostics, program); + assert.ok(color.includes("TS2322: "), color); + assert.ok(color.includes(source.trim()), color); + assert.ok(color.includes("~"), color); + assert.ok(color.includes("\x1b["), color); + assert.ok(color.endsWith("\r\n"), color); + const doubled = formatDiagnosticsWithColorAndContext([diagnostics[0], diagnostics[0]], program); + assert.equal(doubled, color + "\r\n" + color); + + const zeroWidth = { + ...diagnostics[0], + end: diagnostics[0].pos, + endPosition: diagnostics[0].startPosition!, + }; + const zeroWidthColor = formatDiagnosticsWithColorAndContext([zeroWidth], program); + assert.match(zeroWidthColor, /\x1b\[91m +~\x1b\[0m/); + + const relatedText = "Related information"; + const withRelated = { + ...diagnostics[0], + relatedInformation: [{ ...diagnostics[0], text: relatedText }], + }; + const relatedColor = formatDiagnosticsWithColorAndContext([withRelated], program); + const relatedMessage = relatedColor.indexOf(` - ${relatedText}`); + assert.notEqual(relatedMessage, -1, relatedColor); + assert.ok(relatedColor.indexOf(source.trim(), relatedMessage) > relatedMessage, relatedColor); + + const multiline = { + ...diagnostics[0], + startPosition: { line: 0, character: 0 }, + endPosition: { line: 6, character: 5 }, + sourceLines: [ + { line: 0, text: "one\n" }, + { line: 1, text: "two\n" }, + { line: 5, text: "six\n" }, + { line: 6, text: "seven" }, + ], + }; + const multilineColor = formatDiagnosticsWithColorAndContext([multiline], program); + assert.ok(multilineColor.includes("..."), multilineColor); + assert.ok(multilineColor.includes("seven"), multilineColor); + } + finally { + await api.close(); + } + }); + + test("uses the API host for standalone diagnostics", async () => { + const configText = `{ "compilerOptions": { "target": "invalid" } }`; + const api = spawnAPI({ + "/workspace/tsconfig.json": configText, + "/workspace/index.ts": `const x: number = "oops";`, + }); + try { + const snapshot = await api.updateSnapshot({ openProject: "/workspace/tsconfig.json" }); + const program = snapshot.getProject("/workspace/tsconfig.json")!.program; + const diagnostics = await program.getSemanticDiagnostics("/workspace/index.ts"); + const configDiagnostics = (await api.parseConfigFile("/workspace/tsconfig.json")).errors; + const clonedDiagnostics = [ + [{ ...diagnostics[0] }], + structuredClone(diagnostics), + JSON.parse(JSON.stringify(diagnostics)), + ]; + + for (const cloned of clonedDiagnostics) { + assert.ok(formatDiagnostics(cloned, api).includes("TS2322")); + } + assert.ok(formatDiagnostics(configDiagnostics, api).includes("tsconfig.json(1,34): error TS6046: ")); + assert.ok(formatDiagnosticsWithColorAndContext(configDiagnostics, api).includes(configText)); + } + finally { + await api.close(); + } + }); + + test("uses the API directory and LF defaults for inferred projects", async () => { + const api = spawnAPI({ + "/workspace/index.ts": `const x: number = "oops";`, + }); + try { + const snapshot = await api.updateSnapshot({ openFiles: ["/workspace/index.ts"] }); + const project = await snapshot.getDefaultProjectForFile("/workspace/index.ts"); + assert.ok(project); + assert.equal(project.program.getCurrentDirectory(), api.getCurrentDirectory()); + assert.equal(project.program.getNewLine(), "\n"); + } + finally { + await api.close(); + } + }); +}); + +function spawnAPI(files: Record): API { + return new API({ + cwd: "/workspace", + fs: createVirtualFileSystem(files), + }); +} diff --git a/packages/typescript/test/encoder.test.ts b/packages/typescript/test/encoder.test.ts index c214e0675f56e..e1cb6840320b7 100644 --- a/packages/typescript/test/encoder.test.ts +++ b/packages/typescript/test/encoder.test.ts @@ -1,459 +1,459 @@ -import type { - Path, - SourceFile, - Statement, -} from "@typescript/typescript/unstable/ast"; -import { - SyntaxKind, - TokenFlags, -} from "@typescript/typescript/unstable/ast"; -import { - createArrayLiteralExpression, - createBlock, - createExpressionStatement, - createFunctionDeclaration, - createIdentifier, - createIfStatement, - createJsxAttributes, - createJsxClosingElement, - createJsxElement, - createJsxOpeningElement, - createNumericLiteral, - createPostfixUnaryExpression, - createPrefixUnaryExpression, - createSourceFile, - createToken, - createVariableDeclaration, - createVariableDeclarationList, - createVariableStatement, -} from "@typescript/typescript/unstable/ast/factory"; -import assert from "node:assert"; -import { - describe, - test, -} from "node:test"; -import { - encodeNode, - encodeSourceFile, -} from "../src/api/node/encoder.ts"; -import { - RemoteNode, - RemoteNodeList, - RemoteSourceFile, -} from "../src/api/node/node.ts"; -import { - HEADER_OFFSET_NODES, - NODE_LEN, - NODE_OFFSET_DATA, -} from "../src/api/node/protocol.ts"; - -function makeSF(text: string, fileName: string, statements: readonly Statement[]): SourceFile { - const endOfFileToken = createToken(SyntaxKind.EndOfFile); - return createSourceFile(statements, endOfFileToken, text, fileName, fileName as Path); -} - -function decode(data: Uint8Array): RemoteSourceFile { - return new RemoteSourceFile(data, new TextDecoder()); -} - -describe("Encoder", () => { - test("encodes empty source file", () => { - const sf = makeSF("", "/test.ts", []); - - const encoded = encodeSourceFile(sf); - assert.ok(encoded instanceof Uint8Array); - assert.ok(encoded.length > 0); - - // Verify header - const view = new DataView(encoded.buffer, encoded.byteOffset, encoded.byteLength); - const metadata = view.getUint32(0, true); - assert.strictEqual(metadata >>> 24, 8, "protocol version should be 8"); - - // Verify we can decode it - const decoded = decode(encoded); - assert.strictEqual(decoded.kind, SyntaxKind.SourceFile); - assert.strictEqual(decoded.fileName, "/test.ts"); - assert.strictEqual(decoded.path, "/test.ts"); - assert.strictEqual(decoded.text, ""); - assert.strictEqual(decoded.contentMapper, undefined); - assert.strictEqual(decoded.virtualFileName, undefined); - assert.strictEqual(decoded.diagnosticDirectives, undefined); - assert.strictEqual(decoded.supplementalSourceFileNames, undefined); - assert.strictEqual(decoded.canonicalSourceFileName, undefined); - }); - - test("encodes source file with identifier", () => { - const id = createIdentifier("hello"); - const decl = createVariableDeclaration(id, undefined, undefined, undefined); - const declList = createVariableDeclarationList([decl], 0); - const stmt = createVariableStatement(undefined, declList); - const sf = makeSF("var hello = 42;", "/test.ts", [stmt]); - - const encoded = encodeSourceFile(sf); - const decoded = decode(encoded); - - assert.strictEqual(decoded.kind, SyntaxKind.SourceFile); - assert.strictEqual(decoded.text, "var hello = 42;"); - assert.strictEqual(decoded.fileName, "/test.ts"); - - const stmts = decoded.statements; - assert.ok(stmts); - assert.strictEqual(stmts.length, 1); - const decodedStmt = stmts.at(0)!; - assert.strictEqual(decodedStmt.kind, SyntaxKind.VariableStatement); - }); - - test("encodes if statement with optional else", () => { - const condition = createIdentifier("ok"); - const thenBlock = createBlock([]); - const ifStmt = createIfStatement(condition, thenBlock, undefined); - const sf = makeSF("if (ok) {}", "/test.ts", [ifStmt]); - - const encoded = encodeSourceFile(sf); - const decoded = decode(encoded); - const stmts = decoded.statements; - assert.ok(stmts); - assert.strictEqual(stmts.length, 1); - const decodedIf = stmts.at(0)!; - assert.strictEqual(decodedIf.kind, SyntaxKind.IfStatement); - assert.ok(decodedIf.expression); - assert.strictEqual(decodedIf.expression.kind, SyntaxKind.Identifier); - assert.strictEqual(decodedIf.expression.text, "ok"); - assert.ok(decodedIf.thenStatement); - assert.strictEqual(decodedIf.thenStatement.kind, SyntaxKind.Block); - // No else - assert.strictEqual(decodedIf.elseStatement, undefined); - }); - - test("encodes if statement with else", () => { - const condition = createIdentifier("x"); - const thenBlock = createBlock([]); - const elseBlock = createBlock([]); - const ifStmt = createIfStatement(condition, thenBlock, elseBlock); - const sf = makeSF("if (x) {} else {}", "/test.ts", [ifStmt]); - - const encoded = encodeSourceFile(sf); - const decoded = decode(encoded); - const stmts = decoded.statements; - assert.ok(stmts); - const decodedIf = stmts.at(0)!; - assert.strictEqual(decodedIf.kind, SyntaxKind.IfStatement); - assert.ok(decodedIf.expression); - assert.ok(decodedIf.thenStatement); - assert.ok(decodedIf.elseStatement); - assert.strictEqual(decodedIf.elseStatement.kind, SyntaxKind.Block); - }); - - test("encodes function declaration", () => { - const name = createIdentifier("foo"); - const body = createBlock([]); - const fn = createFunctionDeclaration(undefined, undefined, name, undefined, [], undefined, body); - const sf = makeSF("function foo() {}", "/test.ts", [fn]); - - const encoded = encodeSourceFile(sf); - const decoded = decode(encoded); - const stmts = decoded.statements; - assert.ok(stmts); - assert.strictEqual(stmts.length, 1); - const decodedFn = stmts.at(0)!; - assert.strictEqual(decodedFn.kind, SyntaxKind.FunctionDeclaration); - assert.ok(decodedFn.name); - assert.strictEqual(decodedFn.name.kind, SyntaxKind.Identifier); - assert.strictEqual(decodedFn.name.text, "foo"); - assert.ok(decodedFn.body); - assert.strictEqual(decodedFn.body.kind, SyntaxKind.Block); - }); - - test("encodes arbitrary node (not source file)", () => { - const condition = createIdentifier("x"); - const thenBlock = createBlock([]); - const ifStmt = createIfStatement(condition, thenBlock, undefined); - - const encoded = encodeNode(ifStmt); - assert.ok(encoded instanceof Uint8Array); - - // Root node at index 1 should be IfStatement - const view = new DataView(encoded.buffer, encoded.byteOffset, encoded.byteLength); - const offsetNodes = view.getUint32(HEADER_OFFSET_NODES, true); - const rootKind = view.getUint32(offsetNodes + NODE_LEN, true); - assert.strictEqual(rootKind, SyntaxKind.IfStatement); - }); - - test("protocol version is 8", () => { - const sf = makeSF("", "/test.ts", []); - const encoded = encodeSourceFile(sf); - const view = new DataView(encoded.buffer, encoded.byteOffset, encoded.byteLength); - assert.strictEqual(view.getUint32(0, true) >>> 24, 8); - }); - - test("encodes source files without content mapping metadata", () => { - const ordinary = decode(encodeSourceFile(makeSF("text", "/test.ts", []))); - assert.equal(ordinary.originalText, ordinary.text); - assert.equal(ordinary.spanMap, undefined); - assert.equal(ordinary.contentMapper, undefined); - assert.equal(ordinary.virtualFileName, undefined); - assert.equal(ordinary.diagnosticDirectives, undefined); - }); - - test("boolean properties are encoded", () => { - // Block with multiLine=true - const block = createBlock([], true); - const encoded = encodeNode(block); - const view = new DataView(encoded.buffer, encoded.byteOffset, encoded.byteLength); - const offsetNodes = view.getUint32(HEADER_OFFSET_NODES, true); - const data = view.getUint32(offsetNodes + NODE_LEN + NODE_OFFSET_DATA, true); - // Bit 24 should be 1 (multiLine) - assert.strictEqual((data >>> 24) & 1, 1); - }); - - test("postfix unary operator is preserved through encode/decode", () => { - const operand = createIdentifier("i"); - const postfix = createPostfixUnaryExpression(operand, SyntaxKind.PlusPlusToken); - const stmt = createExpressionStatement(postfix); - const sf = makeSF("i++;", "/test.ts", [stmt]); - - const encoded = encodeSourceFile(sf); - const decoded = decode(encoded); - const stmts = decoded.statements; - assert.ok(stmts); - const decodedStmt = stmts.at(0)!; - assert.strictEqual(decodedStmt.kind, SyntaxKind.ExpressionStatement); - const expr = decodedStmt.expression!; - assert.strictEqual(expr.kind, SyntaxKind.PostfixUnaryExpression); - assert.strictEqual(expr.operator, SyntaxKind.PlusPlusToken); - assert.ok(expr.operand); - assert.strictEqual(expr.operand.kind, SyntaxKind.Identifier); - }); - - test("prefix unary operator is preserved through encode/decode", () => { - const operand = createIdentifier("x"); - const prefix = createPrefixUnaryExpression(SyntaxKind.ExclamationToken, operand); - const stmt = createExpressionStatement(prefix); - const sf = makeSF("!x;", "/test.ts", [stmt]); - - const encoded = encodeSourceFile(sf); - const decoded = decode(encoded); - const stmts = decoded.statements; - assert.ok(stmts); - const expr = stmts.at(0)!.expression!; - assert.strictEqual(expr.kind, SyntaxKind.PrefixUnaryExpression); - assert.strictEqual(expr.operator, SyntaxKind.ExclamationToken); - }); - - test("postfix decrement operator is preserved", () => { - const operand = createIdentifier("n"); - const postfix = createPostfixUnaryExpression(operand, SyntaxKind.MinusMinusToken); - const stmt = createExpressionStatement(postfix); - const sf = makeSF("n--;", "/test.ts", [stmt]); - - const encoded = encodeSourceFile(sf); - const decoded = decode(encoded); - const expr = decoded.statements!.at(0)!.expression!; - assert.strictEqual(expr.kind, SyntaxKind.PostfixUnaryExpression); - assert.strictEqual(expr.operator, SyntaxKind.MinusMinusToken); - }); - - test("single-child node with no children returns undefined", () => { - // JsxAttributes is a single-child node (property: "properties"). - // When the properties NodeList is empty, the encoder skips it, - // so JsxAttributes has zero encoded children. Accessing .properties - // must return undefined, not throw "Expected only one child". - const tagName = createIdentifier("div"); - const emptyAttrs = createJsxAttributes([]); - const opening = createJsxOpeningElement(tagName, undefined, emptyAttrs); - const closing = createJsxClosingElement(createIdentifier("div")); - const jsx = createJsxElement(opening, [], closing); - const stmt = createVariableStatement( - undefined, - createVariableDeclarationList([createVariableDeclaration(createIdentifier("x"), undefined, undefined, jsx)], 0), - ); - const sf = makeSF("const x =
;", "/test.tsx", [stmt]); - - const encoded = encodeSourceFile(sf); - const decoded = decode(encoded); - - // Walk to JsxOpeningElement → attributes (JsxAttributes) - const varStmt = decoded.statements!.at(0)!; - const declList = varStmt.declarationList!; - const declarationsNode = declList.declarations!; - assert.ok(declarationsNode instanceof RemoteNodeList); - const varDecl = declarationsNode.at(0)!; - const jsxElem = varDecl.initializer!; - assert.strictEqual(jsxElem.kind, SyntaxKind.JsxElement); - const openingElem = jsxElem.openingElement!; - assert.strictEqual(openingElem.kind, SyntaxKind.JsxOpeningElement); - const attrs = openingElem.attributes! as RemoteNode; - assert.strictEqual(attrs.kind, SyntaxKind.JsxAttributes); - // Empty properties should return undefined, not throw - assert.strictEqual(jsxElem.typeArguments, undefined); - }); - - test("NodeList element access is consistent across forward, backward, and random order", () => { - // Build `[0, 1, 2, ..., 9]` and access the NodeList in several orders to - // ensure the memoized cursor in `at()` never returns a stale element. - const count = 10; - const elements = []; - let text = "["; - for (let i = 0; i < count; i++) { - elements.push(createNumericLiteral(String(i), TokenFlags.None)); - text += (i === 0 ? "" : ", ") + i; - } - text += "]"; - const stmt = createExpressionStatement(createArrayLiteralExpression(elements)); - const sf = makeSF(text, "/test.ts", [stmt]); - - const decoded = decode(encodeSourceFile(sf)); - const arrayExpr = (decoded.statements!.at(0)! as RemoteNode).expression! as RemoteNode; - const list = arrayExpr.elements! as unknown as RemoteNodeList; - assert.strictEqual(list.length, count); - - // Independent decode used as an oracle: fresh cursor per lookup. - const posOf = (i: number) => { - const freshExpr = (decode(encodeSourceFile(sf)).statements!.at(0)! as RemoteNode).expression! as RemoteNode; - return (freshExpr.elements! as unknown as RemoteNodeList).at(i)!.pos; - }; - const expected = Array.from({ length: count }, (_, i) => posOf(i)); - - // Forward sequential (exercises cursor resume). - for (let i = 0; i < count; i++) { - assert.strictEqual(list.at(i)!.pos, expected[i], `forward at(${i})`); - } - // Backward sequential (cursor must reset when index < cursor). - for (let i = count - 1; i >= 0; i--) { - assert.strictEqual(list.at(i)!.pos, expected[i], `backward at(${i})`); - } - // Random order, with repeats. - for (const i of [3, 7, 1, 7, 0, 9, 4, 4, 2, 8, 5, 6, 0, 9]) { - assert.strictEqual(list.at(i)!.pos, expected[i], `random at(${i})`); - } - // Negative indices. - assert.strictEqual(list.at(-1)!.pos, expected[count - 1], "at(-1)"); - assert.strictEqual(list.at(-count)!.pos, expected[0], `at(-${count})`); - - // Indexed getters (0..15) and spread must agree with at(). - assert.strictEqual((list[0] as RemoteNode).pos, expected[0]); - assert.strictEqual((list[9] as RemoteNode).pos, expected[9]); - assert.deepStrictEqual([...list].map(n => n.pos), expected); - }); -}); - -describe("UTF-8 vs UTF-16 position encoding", () => { - // Positions in the encoded AST must be UTF-16 code unit offsets so that - // file.text.slice(node.pos, node.end) works correctly on JS strings. - // This is the same convention TypeScript uses. - - function utf8ByteLength(s: string): number { - return new TextEncoder().encode(s).length; - } - - // Returns the UTF-16 code unit position of a substring in a source string - function utf16Pos(source: string, substring: string): { pos: number; end: number; } { - const pos = source.indexOf(substring); - return { pos, end: pos + substring.length }; - } - - test("ASCII: text.slice(pos, end) works", () => { - const source = "const x = 1;"; - assert.strictEqual(source.length, utf8ByteLength(source)); - - const { pos, end } = utf16Pos(source, "x"); - assert.strictEqual(source.slice(pos, end), "x"); - }); - - test("2-byte char: text.slice(pos, end) works", () => { - // é (U+00E9) is 1 UTF-16 code unit but 2 UTF-8 bytes - const source = "const café = 1;\nconst x = 2;"; - assert.strictEqual(source.length, 28); // UTF-16 code units - assert.strictEqual(utf8ByteLength(source), 29); // UTF-8 bytes (1 extra from é) - - const { pos, end } = utf16Pos(source, "x"); - assert.strictEqual(source.slice(pos, end), "x"); - - // Positions after multi-byte characters must be UTF-16 code unit offsets, not UTF-8 byte offsets - assert.strictEqual(pos, 22); // UTF-16 position - assert.notStrictEqual(pos, 23); // NOT the UTF-8 byte offset - }); - - test("4-byte char (supplementary plane): text.slice(pos, end) works", () => { - // 🎉 (U+1F389) is 2 UTF-16 code units (surrogate pair) but 4 UTF-8 bytes - const source = 'const a = "🎉";\nconst b = 2;'; - assert.strictEqual(source.length, 28); // UTF-16 code units - assert.strictEqual(utf8ByteLength(source), 30); // 2 extra bytes from 🎉 - - const { pos, end } = utf16Pos(source, "b"); - assert.strictEqual(source.slice(pos, end), "b"); - - // Must use UTF-16 offset, not UTF-8 byte offset - assert.strictEqual(pos, 22); // UTF-16 position - assert.notStrictEqual(pos, 24); // NOT the UTF-8 byte offset (shifted by 2) - }); - - test("multi-byte identifier: text.slice(pos, end) works", () => { - // Identifier "café" has 4 UTF-16 code units but 5 UTF-8 bytes - const source = "let café = 1;"; - - const { pos, end } = utf16Pos(source, "café"); - assert.strictEqual(source.slice(pos, end), "café"); - assert.strictEqual(end - pos, 4); // UTF-16 length, not UTF-8 byte length (5) - }); -}); - -describe("Line and character mapping", () => { - function makeSourceFile(text: string): RemoteSourceFile { - return decode(encodeSourceFile(makeSF(text, "/test.ts", []))); - } - - test("getLineStarts for empty file", () => { - const sf = makeSourceFile(""); - assert.deepStrictEqual(sf.getLineStarts(), [0]); - }); - - test("getLineStarts for LF-separated lines", () => { - const sf = makeSourceFile("a\nb\nc"); - assert.deepStrictEqual(sf.getLineStarts(), [0, 2, 4]); - }); - - test("getLineStarts for CRLF-separated lines", () => { - const sf = makeSourceFile("a\r\nbb\r\nc"); - assert.deepStrictEqual(sf.getLineStarts(), [0, 3, 7]); - }); - - test("getLineStarts is cached", () => { - const sf = makeSourceFile("a\nb"); - assert.strictEqual(sf.getLineStarts(), sf.getLineStarts()); - }); - - test("getLineAndCharacterOfPosition", () => { - const sf = makeSourceFile("ab\ncde\nf"); - assert.deepStrictEqual(sf.getLineAndCharacterOfPosition(0), { line: 0, character: 0 }); - assert.deepStrictEqual(sf.getLineAndCharacterOfPosition(2), { line: 0, character: 2 }); - assert.deepStrictEqual(sf.getLineAndCharacterOfPosition(3), { line: 1, character: 0 }); - assert.deepStrictEqual(sf.getLineAndCharacterOfPosition(6), { line: 1, character: 3 }); - assert.deepStrictEqual(sf.getLineAndCharacterOfPosition(7), { line: 2, character: 0 }); - }); - - test("getLineAndCharacterOfPosition uses UTF-16 code units", () => { - // 🎉 (U+1F389) is a surrogate pair: 2 UTF-16 code units. - const source = "🎉a\nb"; - const sf = makeSourceFile(source); - // "a" is at UTF-16 offset 2, on line 0 - assert.deepStrictEqual(sf.getLineAndCharacterOfPosition(2), { line: 0, character: 2 }); - // "b" is at UTF-16 offset 4 (after "🎉a\n"), on line 1 - assert.strictEqual(source.indexOf("b"), 4); - assert.deepStrictEqual(sf.getLineAndCharacterOfPosition(4), { line: 1, character: 0 }); - }); - - test("getPositionOfLineAndCharacter round-trips", () => { - const sf = makeSourceFile("ab\ncde\nf"); - for (const pos of [0, 1, 2, 3, 5, 6, 7]) { - const lc = sf.getLineAndCharacterOfPosition(pos); - assert.strictEqual(sf.getPositionOfLineAndCharacter(lc.line, lc.character), pos); - } - }); - - test("getPositionOfLineAndCharacter throws for out-of-range line", () => { - const sf = makeSourceFile("a\nb"); - assert.throws(() => sf.getPositionOfLineAndCharacter(5, 0), /Bad line number/); - assert.throws(() => sf.getPositionOfLineAndCharacter(-1, 0), /Bad line number/); - }); -}); +import type { + Path, + SourceFile, + Statement, +} from "@typescript/typescript/unstable/ast"; +import { + SyntaxKind, + TokenFlags, +} from "@typescript/typescript/unstable/ast"; +import { + createArrayLiteralExpression, + createBlock, + createExpressionStatement, + createFunctionDeclaration, + createIdentifier, + createIfStatement, + createJsxAttributes, + createJsxClosingElement, + createJsxElement, + createJsxOpeningElement, + createNumericLiteral, + createPostfixUnaryExpression, + createPrefixUnaryExpression, + createSourceFile, + createToken, + createVariableDeclaration, + createVariableDeclarationList, + createVariableStatement, +} from "@typescript/typescript/unstable/ast/factory"; +import assert from "node:assert"; +import { + describe, + test, +} from "node:test"; +import { + encodeNode, + encodeSourceFile, +} from "../src/api/node/encoder.ts"; +import { + RemoteNode, + RemoteNodeList, + RemoteSourceFile, +} from "../src/api/node/node.ts"; +import { + HEADER_OFFSET_NODES, + NODE_LEN, + NODE_OFFSET_DATA, +} from "../src/api/node/protocol.ts"; + +function makeSF(text: string, fileName: string, statements: readonly Statement[]): SourceFile { + const endOfFileToken = createToken(SyntaxKind.EndOfFile); + return createSourceFile(statements, endOfFileToken, text, fileName, fileName as Path); +} + +function decode(data: Uint8Array): RemoteSourceFile { + return new RemoteSourceFile(data, new TextDecoder()); +} + +describe("Encoder", () => { + test("encodes empty source file", () => { + const sf = makeSF("", "/test.ts", []); + + const encoded = encodeSourceFile(sf); + assert.ok(encoded instanceof Uint8Array); + assert.ok(encoded.length > 0); + + // Verify header + const view = new DataView(encoded.buffer, encoded.byteOffset, encoded.byteLength); + const metadata = view.getUint32(0, true); + assert.strictEqual(metadata >>> 24, 8, "protocol version should be 8"); + + // Verify we can decode it + const decoded = decode(encoded); + assert.strictEqual(decoded.kind, SyntaxKind.SourceFile); + assert.strictEqual(decoded.fileName, "/test.ts"); + assert.strictEqual(decoded.path, "/test.ts"); + assert.strictEqual(decoded.text, ""); + assert.strictEqual(decoded.contentMapper, undefined); + assert.strictEqual(decoded.virtualFileName, undefined); + assert.strictEqual(decoded.diagnosticDirectives, undefined); + assert.strictEqual(decoded.supplementalSourceFileNames, undefined); + assert.strictEqual(decoded.canonicalSourceFileName, undefined); + }); + + test("encodes source file with identifier", () => { + const id = createIdentifier("hello"); + const decl = createVariableDeclaration(id, undefined, undefined, undefined); + const declList = createVariableDeclarationList([decl], 0); + const stmt = createVariableStatement(undefined, declList); + const sf = makeSF("var hello = 42;", "/test.ts", [stmt]); + + const encoded = encodeSourceFile(sf); + const decoded = decode(encoded); + + assert.strictEqual(decoded.kind, SyntaxKind.SourceFile); + assert.strictEqual(decoded.text, "var hello = 42;"); + assert.strictEqual(decoded.fileName, "/test.ts"); + + const stmts = decoded.statements; + assert.ok(stmts); + assert.strictEqual(stmts.length, 1); + const decodedStmt = stmts.at(0)!; + assert.strictEqual(decodedStmt.kind, SyntaxKind.VariableStatement); + }); + + test("encodes if statement with optional else", () => { + const condition = createIdentifier("ok"); + const thenBlock = createBlock([]); + const ifStmt = createIfStatement(condition, thenBlock, undefined); + const sf = makeSF("if (ok) {}", "/test.ts", [ifStmt]); + + const encoded = encodeSourceFile(sf); + const decoded = decode(encoded); + const stmts = decoded.statements; + assert.ok(stmts); + assert.strictEqual(stmts.length, 1); + const decodedIf = stmts.at(0)!; + assert.strictEqual(decodedIf.kind, SyntaxKind.IfStatement); + assert.ok(decodedIf.expression); + assert.strictEqual(decodedIf.expression.kind, SyntaxKind.Identifier); + assert.strictEqual(decodedIf.expression.text, "ok"); + assert.ok(decodedIf.thenStatement); + assert.strictEqual(decodedIf.thenStatement.kind, SyntaxKind.Block); + // No else + assert.strictEqual(decodedIf.elseStatement, undefined); + }); + + test("encodes if statement with else", () => { + const condition = createIdentifier("x"); + const thenBlock = createBlock([]); + const elseBlock = createBlock([]); + const ifStmt = createIfStatement(condition, thenBlock, elseBlock); + const sf = makeSF("if (x) {} else {}", "/test.ts", [ifStmt]); + + const encoded = encodeSourceFile(sf); + const decoded = decode(encoded); + const stmts = decoded.statements; + assert.ok(stmts); + const decodedIf = stmts.at(0)!; + assert.strictEqual(decodedIf.kind, SyntaxKind.IfStatement); + assert.ok(decodedIf.expression); + assert.ok(decodedIf.thenStatement); + assert.ok(decodedIf.elseStatement); + assert.strictEqual(decodedIf.elseStatement.kind, SyntaxKind.Block); + }); + + test("encodes function declaration", () => { + const name = createIdentifier("foo"); + const body = createBlock([]); + const fn = createFunctionDeclaration(undefined, undefined, name, undefined, [], undefined, body); + const sf = makeSF("function foo() {}", "/test.ts", [fn]); + + const encoded = encodeSourceFile(sf); + const decoded = decode(encoded); + const stmts = decoded.statements; + assert.ok(stmts); + assert.strictEqual(stmts.length, 1); + const decodedFn = stmts.at(0)!; + assert.strictEqual(decodedFn.kind, SyntaxKind.FunctionDeclaration); + assert.ok(decodedFn.name); + assert.strictEqual(decodedFn.name.kind, SyntaxKind.Identifier); + assert.strictEqual(decodedFn.name.text, "foo"); + assert.ok(decodedFn.body); + assert.strictEqual(decodedFn.body.kind, SyntaxKind.Block); + }); + + test("encodes arbitrary node (not source file)", () => { + const condition = createIdentifier("x"); + const thenBlock = createBlock([]); + const ifStmt = createIfStatement(condition, thenBlock, undefined); + + const encoded = encodeNode(ifStmt); + assert.ok(encoded instanceof Uint8Array); + + // Root node at index 1 should be IfStatement + const view = new DataView(encoded.buffer, encoded.byteOffset, encoded.byteLength); + const offsetNodes = view.getUint32(HEADER_OFFSET_NODES, true); + const rootKind = view.getUint32(offsetNodes + NODE_LEN, true); + assert.strictEqual(rootKind, SyntaxKind.IfStatement); + }); + + test("protocol version is 8", () => { + const sf = makeSF("", "/test.ts", []); + const encoded = encodeSourceFile(sf); + const view = new DataView(encoded.buffer, encoded.byteOffset, encoded.byteLength); + assert.strictEqual(view.getUint32(0, true) >>> 24, 8); + }); + + test("encodes source files without content mapping metadata", () => { + const ordinary = decode(encodeSourceFile(makeSF("text", "/test.ts", []))); + assert.equal(ordinary.originalText, ordinary.text); + assert.equal(ordinary.spanMap, undefined); + assert.equal(ordinary.contentMapper, undefined); + assert.equal(ordinary.virtualFileName, undefined); + assert.equal(ordinary.diagnosticDirectives, undefined); + }); + + test("boolean properties are encoded", () => { + // Block with multiLine=true + const block = createBlock([], true); + const encoded = encodeNode(block); + const view = new DataView(encoded.buffer, encoded.byteOffset, encoded.byteLength); + const offsetNodes = view.getUint32(HEADER_OFFSET_NODES, true); + const data = view.getUint32(offsetNodes + NODE_LEN + NODE_OFFSET_DATA, true); + // Bit 24 should be 1 (multiLine) + assert.strictEqual((data >>> 24) & 1, 1); + }); + + test("postfix unary operator is preserved through encode/decode", () => { + const operand = createIdentifier("i"); + const postfix = createPostfixUnaryExpression(operand, SyntaxKind.PlusPlusToken); + const stmt = createExpressionStatement(postfix); + const sf = makeSF("i++;", "/test.ts", [stmt]); + + const encoded = encodeSourceFile(sf); + const decoded = decode(encoded); + const stmts = decoded.statements; + assert.ok(stmts); + const decodedStmt = stmts.at(0)!; + assert.strictEqual(decodedStmt.kind, SyntaxKind.ExpressionStatement); + const expr = decodedStmt.expression!; + assert.strictEqual(expr.kind, SyntaxKind.PostfixUnaryExpression); + assert.strictEqual(expr.operator, SyntaxKind.PlusPlusToken); + assert.ok(expr.operand); + assert.strictEqual(expr.operand.kind, SyntaxKind.Identifier); + }); + + test("prefix unary operator is preserved through encode/decode", () => { + const operand = createIdentifier("x"); + const prefix = createPrefixUnaryExpression(SyntaxKind.ExclamationToken, operand); + const stmt = createExpressionStatement(prefix); + const sf = makeSF("!x;", "/test.ts", [stmt]); + + const encoded = encodeSourceFile(sf); + const decoded = decode(encoded); + const stmts = decoded.statements; + assert.ok(stmts); + const expr = stmts.at(0)!.expression!; + assert.strictEqual(expr.kind, SyntaxKind.PrefixUnaryExpression); + assert.strictEqual(expr.operator, SyntaxKind.ExclamationToken); + }); + + test("postfix decrement operator is preserved", () => { + const operand = createIdentifier("n"); + const postfix = createPostfixUnaryExpression(operand, SyntaxKind.MinusMinusToken); + const stmt = createExpressionStatement(postfix); + const sf = makeSF("n--;", "/test.ts", [stmt]); + + const encoded = encodeSourceFile(sf); + const decoded = decode(encoded); + const expr = decoded.statements!.at(0)!.expression!; + assert.strictEqual(expr.kind, SyntaxKind.PostfixUnaryExpression); + assert.strictEqual(expr.operator, SyntaxKind.MinusMinusToken); + }); + + test("single-child node with no children returns undefined", () => { + // JsxAttributes is a single-child node (property: "properties"). + // When the properties NodeList is empty, the encoder skips it, + // so JsxAttributes has zero encoded children. Accessing .properties + // must return undefined, not throw "Expected only one child". + const tagName = createIdentifier("div"); + const emptyAttrs = createJsxAttributes([]); + const opening = createJsxOpeningElement(tagName, undefined, emptyAttrs); + const closing = createJsxClosingElement(createIdentifier("div")); + const jsx = createJsxElement(opening, [], closing); + const stmt = createVariableStatement( + undefined, + createVariableDeclarationList([createVariableDeclaration(createIdentifier("x"), undefined, undefined, jsx)], 0), + ); + const sf = makeSF("const x =
;", "/test.tsx", [stmt]); + + const encoded = encodeSourceFile(sf); + const decoded = decode(encoded); + + // Walk to JsxOpeningElement → attributes (JsxAttributes) + const varStmt = decoded.statements!.at(0)!; + const declList = varStmt.declarationList!; + const declarationsNode = declList.declarations!; + assert.ok(declarationsNode instanceof RemoteNodeList); + const varDecl = declarationsNode.at(0)!; + const jsxElem = varDecl.initializer!; + assert.strictEqual(jsxElem.kind, SyntaxKind.JsxElement); + const openingElem = jsxElem.openingElement!; + assert.strictEqual(openingElem.kind, SyntaxKind.JsxOpeningElement); + const attrs = openingElem.attributes! as RemoteNode; + assert.strictEqual(attrs.kind, SyntaxKind.JsxAttributes); + // Empty properties should return undefined, not throw + assert.strictEqual(jsxElem.typeArguments, undefined); + }); + + test("NodeList element access is consistent across forward, backward, and random order", () => { + // Build `[0, 1, 2, ..., 9]` and access the NodeList in several orders to + // ensure the memoized cursor in `at()` never returns a stale element. + const count = 10; + const elements = []; + let text = "["; + for (let i = 0; i < count; i++) { + elements.push(createNumericLiteral(String(i), TokenFlags.None)); + text += (i === 0 ? "" : ", ") + i; + } + text += "]"; + const stmt = createExpressionStatement(createArrayLiteralExpression(elements)); + const sf = makeSF(text, "/test.ts", [stmt]); + + const decoded = decode(encodeSourceFile(sf)); + const arrayExpr = (decoded.statements!.at(0)! as RemoteNode).expression! as RemoteNode; + const list = arrayExpr.elements! as unknown as RemoteNodeList; + assert.strictEqual(list.length, count); + + // Independent decode used as an oracle: fresh cursor per lookup. + const posOf = (i: number) => { + const freshExpr = (decode(encodeSourceFile(sf)).statements!.at(0)! as RemoteNode).expression! as RemoteNode; + return (freshExpr.elements! as unknown as RemoteNodeList).at(i)!.pos; + }; + const expected = Array.from({ length: count }, (_, i) => posOf(i)); + + // Forward sequential (exercises cursor resume). + for (let i = 0; i < count; i++) { + assert.strictEqual(list.at(i)!.pos, expected[i], `forward at(${i})`); + } + // Backward sequential (cursor must reset when index < cursor). + for (let i = count - 1; i >= 0; i--) { + assert.strictEqual(list.at(i)!.pos, expected[i], `backward at(${i})`); + } + // Random order, with repeats. + for (const i of [3, 7, 1, 7, 0, 9, 4, 4, 2, 8, 5, 6, 0, 9]) { + assert.strictEqual(list.at(i)!.pos, expected[i], `random at(${i})`); + } + // Negative indices. + assert.strictEqual(list.at(-1)!.pos, expected[count - 1], "at(-1)"); + assert.strictEqual(list.at(-count)!.pos, expected[0], `at(-${count})`); + + // Indexed getters (0..15) and spread must agree with at(). + assert.strictEqual((list[0] as RemoteNode).pos, expected[0]); + assert.strictEqual((list[9] as RemoteNode).pos, expected[9]); + assert.deepStrictEqual([...list].map(n => n.pos), expected); + }); +}); + +describe("UTF-8 vs UTF-16 position encoding", () => { + // Positions in the encoded AST must be UTF-16 code unit offsets so that + // file.text.slice(node.pos, node.end) works correctly on JS strings. + // This is the same convention TypeScript uses. + + function utf8ByteLength(s: string): number { + return new TextEncoder().encode(s).length; + } + + // Returns the UTF-16 code unit position of a substring in a source string + function utf16Pos(source: string, substring: string): { pos: number; end: number; } { + const pos = source.indexOf(substring); + return { pos, end: pos + substring.length }; + } + + test("ASCII: text.slice(pos, end) works", () => { + const source = "const x = 1;"; + assert.strictEqual(source.length, utf8ByteLength(source)); + + const { pos, end } = utf16Pos(source, "x"); + assert.strictEqual(source.slice(pos, end), "x"); + }); + + test("2-byte char: text.slice(pos, end) works", () => { + // é (U+00E9) is 1 UTF-16 code unit but 2 UTF-8 bytes + const source = "const café = 1;\nconst x = 2;"; + assert.strictEqual(source.length, 28); // UTF-16 code units + assert.strictEqual(utf8ByteLength(source), 29); // UTF-8 bytes (1 extra from é) + + const { pos, end } = utf16Pos(source, "x"); + assert.strictEqual(source.slice(pos, end), "x"); + + // Positions after multi-byte characters must be UTF-16 code unit offsets, not UTF-8 byte offsets + assert.strictEqual(pos, 22); // UTF-16 position + assert.notStrictEqual(pos, 23); // NOT the UTF-8 byte offset + }); + + test("4-byte char (supplementary plane): text.slice(pos, end) works", () => { + // 🎉 (U+1F389) is 2 UTF-16 code units (surrogate pair) but 4 UTF-8 bytes + const source = 'const a = "🎉";\nconst b = 2;'; + assert.strictEqual(source.length, 28); // UTF-16 code units + assert.strictEqual(utf8ByteLength(source), 30); // 2 extra bytes from 🎉 + + const { pos, end } = utf16Pos(source, "b"); + assert.strictEqual(source.slice(pos, end), "b"); + + // Must use UTF-16 offset, not UTF-8 byte offset + assert.strictEqual(pos, 22); // UTF-16 position + assert.notStrictEqual(pos, 24); // NOT the UTF-8 byte offset (shifted by 2) + }); + + test("multi-byte identifier: text.slice(pos, end) works", () => { + // Identifier "café" has 4 UTF-16 code units but 5 UTF-8 bytes + const source = "let café = 1;"; + + const { pos, end } = utf16Pos(source, "café"); + assert.strictEqual(source.slice(pos, end), "café"); + assert.strictEqual(end - pos, 4); // UTF-16 length, not UTF-8 byte length (5) + }); +}); + +describe("Line and character mapping", () => { + function makeSourceFile(text: string): RemoteSourceFile { + return decode(encodeSourceFile(makeSF(text, "/test.ts", []))); + } + + test("getLineStarts for empty file", () => { + const sf = makeSourceFile(""); + assert.deepStrictEqual(sf.getLineStarts(), [0]); + }); + + test("getLineStarts for LF-separated lines", () => { + const sf = makeSourceFile("a\nb\nc"); + assert.deepStrictEqual(sf.getLineStarts(), [0, 2, 4]); + }); + + test("getLineStarts for CRLF-separated lines", () => { + const sf = makeSourceFile("a\r\nbb\r\nc"); + assert.deepStrictEqual(sf.getLineStarts(), [0, 3, 7]); + }); + + test("getLineStarts is cached", () => { + const sf = makeSourceFile("a\nb"); + assert.strictEqual(sf.getLineStarts(), sf.getLineStarts()); + }); + + test("getLineAndCharacterOfPosition", () => { + const sf = makeSourceFile("ab\ncde\nf"); + assert.deepStrictEqual(sf.getLineAndCharacterOfPosition(0), { line: 0, character: 0 }); + assert.deepStrictEqual(sf.getLineAndCharacterOfPosition(2), { line: 0, character: 2 }); + assert.deepStrictEqual(sf.getLineAndCharacterOfPosition(3), { line: 1, character: 0 }); + assert.deepStrictEqual(sf.getLineAndCharacterOfPosition(6), { line: 1, character: 3 }); + assert.deepStrictEqual(sf.getLineAndCharacterOfPosition(7), { line: 2, character: 0 }); + }); + + test("getLineAndCharacterOfPosition uses UTF-16 code units", () => { + // 🎉 (U+1F389) is a surrogate pair: 2 UTF-16 code units. + const source = "🎉a\nb"; + const sf = makeSourceFile(source); + // "a" is at UTF-16 offset 2, on line 0 + assert.deepStrictEqual(sf.getLineAndCharacterOfPosition(2), { line: 0, character: 2 }); + // "b" is at UTF-16 offset 4 (after "🎉a\n"), on line 1 + assert.strictEqual(source.indexOf("b"), 4); + assert.deepStrictEqual(sf.getLineAndCharacterOfPosition(4), { line: 1, character: 0 }); + }); + + test("getPositionOfLineAndCharacter round-trips", () => { + const sf = makeSourceFile("ab\ncde\nf"); + for (const pos of [0, 1, 2, 3, 5, 6, 7]) { + const lc = sf.getLineAndCharacterOfPosition(pos); + assert.strictEqual(sf.getPositionOfLineAndCharacter(lc.line, lc.character), pos); + } + }); + + test("getPositionOfLineAndCharacter throws for out-of-range line", () => { + const sf = makeSourceFile("a\nb"); + assert.throws(() => sf.getPositionOfLineAndCharacter(5, 0), /Bad line number/); + assert.throws(() => sf.getPositionOfLineAndCharacter(-1, 0), /Bad line number/); + }); +}); diff --git a/packages/typescript/test/nodelist.bench.ts b/packages/typescript/test/nodelist.bench.ts index 33ffb167de0f2..9dae5903ee8b2 100644 --- a/packages/typescript/test/nodelist.bench.ts +++ b/packages/typescript/test/nodelist.bench.ts @@ -1,157 +1,157 @@ -// Micro-benchmark for RemoteNodeList access patterns. -// -// Focuses on the cost of the element accessors (`list[i]`), the `.at()` -// method, and the inherited Array iteration methods (map/forEach/reduce), -// all of which walk the encoded linked list of children. -// -// Run with: -// npm run node -- test/nodelist.bench.ts -// npm run node -- test/nodelist.bench.ts --filter map - -import { - type Expression, - type Path, - type SourceFile, - type Statement, - SyntaxKind, - TokenFlags, -} from "@typescript/typescript/unstable/ast"; -import { - createArrayLiteralExpression, - createExpressionStatement, - createNumericLiteral, - createSourceFile, - createToken, -} from "@typescript/typescript/unstable/ast/factory"; -import { fileURLToPath } from "node:url"; -import { parseArgs } from "node:util"; -import { Bench } from "tinybench"; -import { encodeSourceFile } from "../src/api/node/encoder.ts"; -import type { RemoteNodeList } from "../src/api/node/node.generated.ts"; -import { RemoteSourceFile } from "../src/api/node/node.ts"; - -const isMain = process.argv[1] === fileURLToPath(import.meta.url); -if (isMain) { - const { values } = parseArgs({ - options: { - filter: { type: "string" }, - size: { type: "string", default: "500" }, - }, - }); - runBenchmarks(values); -} - -function makeSourceFileWithLargeList(elementCount: number): Uint8Array { - const elements: Expression[] = []; - let text = "["; - for (let i = 0; i < elementCount; i++) { - elements.push(createNumericLiteral(String(i), TokenFlags.None)); - text += (i === 0 ? "" : ",") + i; - } - text += "]"; - const array = createArrayLiteralExpression(elements); - const stmt: Statement = createExpressionStatement(array); - const eof = createToken(SyntaxKind.EndOfFile); - const sf: SourceFile = createSourceFile([stmt], eof, text, "/bench.ts", "/bench.ts" as Path); - return encodeSourceFile(sf); -} - -function getElementsList(data: Uint8Array): RemoteNodeList { - const sf = new RemoteSourceFile(data, new TextDecoder()); - const stmt = (sf.statements as any).at(0); - return stmt.expression.elements as RemoteNodeList; -} - -export function runBenchmarks(options?: { filter?: string; size?: string; }): void { - const { filter, size } = options ?? {}; - const elementCount = size ? parseInt(size, 10) : 500; - - const encoded = makeSourceFileWithLargeList(elementCount); - - // Sanity check that access works and produces the expected length. - { - const list = getElementsList(encoded); - if (list.length !== elementCount) { - throw new Error(`Expected ${elementCount} elements, got ${list.length}`); - } - } - - const bench = new Bench({ - name: `RemoteNodeList (${elementCount} elements)`, - iterations: 30, - warmupIterations: 10, - }); - - // Each task re-decodes the source file so node materialization caching - // inside `sourceFile.nodes` does not carry over between iterations, which - // would otherwise mask the cost of walking the list. - bench - .add("index access (list[i])", () => { - const list = getElementsList(encoded); - let sum = 0; - for (let i = 0; i < list.length; i++) { - sum += list[i].pos; - } - if (sum < 0) throw new Error("unreachable"); - }) - .add("at(i)", () => { - const list = getElementsList(encoded); - let sum = 0; - for (let i = 0; i < list.length; i++) { - sum += list.at(i)!.pos; - } - if (sum < 0) throw new Error("unreachable"); - }) - .add("for..of (iterator)", () => { - const list = getElementsList(encoded); - let sum = 0; - for (const node of list) { - sum += node.pos; - } - if (sum < 0) throw new Error("unreachable"); - }) - .add("map", () => { - const list = getElementsList(encoded); - const positions = list.map(node => node.pos); - if (positions.length < 0) throw new Error("unreachable"); - }) - .add("filter", () => { - const list = getElementsList(encoded); - const filtered = list.filter(node => node.pos >= 0); - if (filtered.length < 0) throw new Error("unreachable"); - }) - .add("forEach", () => { - const list = getElementsList(encoded); - let sum = 0; - list.forEach(node => { - sum += node.pos; - }); - if (sum < 0) throw new Error("unreachable"); - }) - .add("reduce", () => { - const list = getElementsList(encoded); - const sum = list.reduce((acc, node) => acc + node.pos, 0); - if (sum < 0) throw new Error("unreachable"); - }) - .add("forEachNode", () => { - const list = getElementsList(encoded); - let sum = 0; - list.forEachNode(node => { - sum += node.pos; - return undefined; - }); - if (sum < 0) throw new Error("unreachable"); - }); - - if (filter) { - const pattern = filter.toLowerCase(); - for (const task of [...bench.tasks]) { - if (!task.name.toLowerCase().includes(pattern)) { - bench.remove(task.name); - } - } - } - - bench.runSync(); - console.table(bench.table()); -} +// Micro-benchmark for RemoteNodeList access patterns. +// +// Focuses on the cost of the element accessors (`list[i]`), the `.at()` +// method, and the inherited Array iteration methods (map/forEach/reduce), +// all of which walk the encoded linked list of children. +// +// Run with: +// npm run node -- test/nodelist.bench.ts +// npm run node -- test/nodelist.bench.ts --filter map + +import { + type Expression, + type Path, + type SourceFile, + type Statement, + SyntaxKind, + TokenFlags, +} from "@typescript/typescript/unstable/ast"; +import { + createArrayLiteralExpression, + createExpressionStatement, + createNumericLiteral, + createSourceFile, + createToken, +} from "@typescript/typescript/unstable/ast/factory"; +import { fileURLToPath } from "node:url"; +import { parseArgs } from "node:util"; +import { Bench } from "tinybench"; +import { encodeSourceFile } from "../src/api/node/encoder.ts"; +import type { RemoteNodeList } from "../src/api/node/node.generated.ts"; +import { RemoteSourceFile } from "../src/api/node/node.ts"; + +const isMain = process.argv[1] === fileURLToPath(import.meta.url); +if (isMain) { + const { values } = parseArgs({ + options: { + filter: { type: "string" }, + size: { type: "string", default: "500" }, + }, + }); + runBenchmarks(values); +} + +function makeSourceFileWithLargeList(elementCount: number): Uint8Array { + const elements: Expression[] = []; + let text = "["; + for (let i = 0; i < elementCount; i++) { + elements.push(createNumericLiteral(String(i), TokenFlags.None)); + text += (i === 0 ? "" : ",") + i; + } + text += "]"; + const array = createArrayLiteralExpression(elements); + const stmt: Statement = createExpressionStatement(array); + const eof = createToken(SyntaxKind.EndOfFile); + const sf: SourceFile = createSourceFile([stmt], eof, text, "/bench.ts", "/bench.ts" as Path); + return encodeSourceFile(sf); +} + +function getElementsList(data: Uint8Array): RemoteNodeList { + const sf = new RemoteSourceFile(data, new TextDecoder()); + const stmt = (sf.statements as any).at(0); + return stmt.expression.elements as RemoteNodeList; +} + +export function runBenchmarks(options?: { filter?: string; size?: string; }): void { + const { filter, size } = options ?? {}; + const elementCount = size ? parseInt(size, 10) : 500; + + const encoded = makeSourceFileWithLargeList(elementCount); + + // Sanity check that access works and produces the expected length. + { + const list = getElementsList(encoded); + if (list.length !== elementCount) { + throw new Error(`Expected ${elementCount} elements, got ${list.length}`); + } + } + + const bench = new Bench({ + name: `RemoteNodeList (${elementCount} elements)`, + iterations: 30, + warmupIterations: 10, + }); + + // Each task re-decodes the source file so node materialization caching + // inside `sourceFile.nodes` does not carry over between iterations, which + // would otherwise mask the cost of walking the list. + bench + .add("index access (list[i])", () => { + const list = getElementsList(encoded); + let sum = 0; + for (let i = 0; i < list.length; i++) { + sum += list[i].pos; + } + if (sum < 0) throw new Error("unreachable"); + }) + .add("at(i)", () => { + const list = getElementsList(encoded); + let sum = 0; + for (let i = 0; i < list.length; i++) { + sum += list.at(i)!.pos; + } + if (sum < 0) throw new Error("unreachable"); + }) + .add("for..of (iterator)", () => { + const list = getElementsList(encoded); + let sum = 0; + for (const node of list) { + sum += node.pos; + } + if (sum < 0) throw new Error("unreachable"); + }) + .add("map", () => { + const list = getElementsList(encoded); + const positions = list.map(node => node.pos); + if (positions.length < 0) throw new Error("unreachable"); + }) + .add("filter", () => { + const list = getElementsList(encoded); + const filtered = list.filter(node => node.pos >= 0); + if (filtered.length < 0) throw new Error("unreachable"); + }) + .add("forEach", () => { + const list = getElementsList(encoded); + let sum = 0; + list.forEach(node => { + sum += node.pos; + }); + if (sum < 0) throw new Error("unreachable"); + }) + .add("reduce", () => { + const list = getElementsList(encoded); + const sum = list.reduce((acc, node) => acc + node.pos, 0); + if (sum < 0) throw new Error("unreachable"); + }) + .add("forEachNode", () => { + const list = getElementsList(encoded); + let sum = 0; + list.forEachNode(node => { + sum += node.pos; + return undefined; + }); + if (sum < 0) throw new Error("unreachable"); + }); + + if (filter) { + const pattern = filter.toLowerCase(); + for (const task of [...bench.tasks]) { + if (!task.name.toLowerCase().includes(pattern)) { + bench.remove(task.name); + } + } + } + + bench.runSync(); + console.table(bench.table()); +} diff --git a/packages/typescript/test/spanMap.test.ts b/packages/typescript/test/spanMap.test.ts index c0dc8314185c7..86cb8a1d86355 100644 --- a/packages/typescript/test/spanMap.test.ts +++ b/packages/typescript/test/spanMap.test.ts @@ -1,218 +1,218 @@ -import { - SpanMap, - SpanMapFeature, - SpanMapFidelity, - SpanMapKind, -} from "@typescript/typescript/unstable/ast"; -import assert from "node:assert"; -import { - describe, - test, -} from "node:test"; - -describe("SpanMap", () => { - const map = new SpanMap([ - { virtualStart: 2, virtualEnd: 6, originalStart: 10, originalEnd: 14, kind: SpanMapKind.Verbatim }, - { virtualStart: 8, virtualEnd: 11, originalStart: 20, originalEnd: 27, kind: SpanMapKind.Atom }, - { virtualStart: 14, virtualEnd: 18, originalStart: 30, originalEnd: 34, kind: SpanMapKind.Verbatim }, - ]); - - test("maps virtual positions and ranges to original", () => { - assert.equal(map.segments[0].features, SpanMapFeature.All); - assert.deepEqual(map.virtualToOriginalPosition(4), { position: 12, fidelity: SpanMapFidelity.Exact }); - assert.deepEqual(map.virtualToOriginalSpan({ pos: 3, end: 5 }), { range: { pos: 11, end: 13 }, fidelity: SpanMapFidelity.Exact }); - assert.deepEqual(map.virtualToOriginalPosition(9), { position: 20, fidelity: SpanMapFidelity.Atom }); - assert.deepEqual(map.virtualToOriginalSpan({ pos: 8, end: 10 }), { range: { pos: 20, end: 27 }, fidelity: SpanMapFidelity.Atom }); - assert.deepEqual(map.virtualToOriginalSpan({ pos: 5, end: 15 }), { range: { pos: 13, end: 31 }, fidelity: SpanMapFidelity.Approximate }); - }); - - test("maps aliases with atom geometry", () => { - const alias = new SpanMap([ - { virtualStart: 0, virtualEnd: 3, originalStart: 0, originalEnd: 1, kind: SpanMapKind.Alias }, - ]); - assert.deepEqual(alias.virtualToOriginalSpan({ pos: 0, end: 3 }), { - range: { pos: 0, end: 1 }, - fidelity: SpanMapFidelity.Atom, - }); - }); - - test("maps synthesized gaps to insertion points", () => { - assert.deepEqual(map.virtualToOriginalPosition(0), { position: 0, fidelity: SpanMapFidelity.None }); - assert.deepEqual(map.virtualToOriginalSpan({ pos: 6, end: 8 }), { range: { pos: 14, end: 14 }, fidelity: SpanMapFidelity.None }); - assert.deepEqual(map.virtualToOriginalPosition(19), { position: 34, fidelity: SpanMapFidelity.None }); - }); - - test("maps original positions and ranges to virtual", () => { - assert.deepEqual(map.originalToVirtualPositions(12, SpanMapFeature.All), [{ position: 4, fidelity: SpanMapFidelity.Exact }]); - assert.deepEqual(map.originalToVirtualSpans({ pos: 21, end: 25 }, SpanMapFeature.All), [{ range: { pos: 8, end: 11 }, fidelity: SpanMapFidelity.Atom }]); - assert.deepEqual(map.originalToVirtualSpans({ pos: 13, end: 31 }, SpanMapFeature.All), [{ range: { pos: 5, end: 15 }, fidelity: SpanMapFidelity.Approximate }]); - assert.deepEqual(map.originalToVirtualPositions(15, SpanMapFeature.All), []); - }); - - test("maps segment endpoints", () => { - assert.deepEqual(map.virtualToOriginalPosition(18), { position: 34, fidelity: SpanMapFidelity.Exact }); - assert.deepEqual(map.originalToVirtualPositions(34, SpanMapFeature.All), [{ position: 18, fidelity: SpanMapFidelity.Exact }]); - assert.deepEqual(map.virtualToOriginalPosition(6), { position: 14, fidelity: SpanMapFidelity.None }); - assert.deepEqual(map.originalToVirtualPositions(14, SpanMapFeature.All), [{ position: 6, fidelity: SpanMapFidelity.Exact }]); - - const adjacent = new SpanMap([ - { virtualStart: 20, virtualEnd: 23, originalStart: 10, originalEnd: 13, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, - { virtualStart: 2, virtualEnd: 5, originalStart: 13, originalEnd: 16, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Completion }, - { virtualStart: 30, virtualEnd: 33, originalStart: 20, originalEnd: 25, kind: SpanMapKind.Atom }, - ]); - assert.deepEqual(adjacent.originalToVirtualPositions(13, SpanMapFeature.All), [ - { position: 2, fidelity: SpanMapFidelity.Exact }, - { position: 23, fidelity: SpanMapFidelity.Exact }, - ]); - assert.deepEqual(adjacent.originalToVirtualPositions(13, SpanMapFeature.Completion), [ - { position: 2, fidelity: SpanMapFidelity.Exact }, - ]); - assert.deepEqual(adjacent.originalToVirtualPositions(25, SpanMapFeature.All), [ - { position: 33, fidelity: SpanMapFidelity.Atom }, - ]); - }); - - test("maps zero-length spans at segment ends", () => { - assert.deepEqual(map.virtualToOriginalSpan({ pos: 18, end: 18 }), { - range: { pos: 34, end: 34 }, - fidelity: SpanMapFidelity.Exact, - }); - for (const originalEnd of [14, 34]) { - const positions = map.originalToVirtualPositions(originalEnd, SpanMapFeature.All); - assert.equal(positions.length, 1); - assert.deepEqual(map.originalToVirtualSpans({ pos: originalEnd, end: originalEnd }, SpanMapFeature.All), [{ - range: { pos: positions[0].position, end: positions[0].position }, - fidelity: positions[0].fidelity, - }]); - } - }); - - test("sorts virtual and original indexes independently", () => { - const reordered = new SpanMap([ - { virtualStart: 0, virtualEnd: 2, originalStart: 10, originalEnd: 12, kind: SpanMapKind.Verbatim }, - { virtualStart: 2, virtualEnd: 4, originalStart: 0, originalEnd: 2, kind: SpanMapKind.Verbatim }, - ]); - assert.deepEqual(reordered.virtualToOriginalPosition(3), { position: 1, fidelity: SpanMapFidelity.Exact }); - assert.deepEqual(reordered.originalToVirtualPositions(1, SpanMapFeature.All), [{ position: 3, fidelity: SpanMapFidelity.Exact }]); - }); - - test("an empty map describes fully synthesized output", () => { - const empty = new SpanMap([]); - assert.deepEqual(empty.virtualToOriginalPosition(5), { position: 0, fidelity: SpanMapFidelity.None }); - assert.deepEqual(empty.virtualToOriginalSpan({ pos: 2, end: 7 }), { range: { pos: 0, end: 0 }, fidelity: SpanMapFidelity.None }); - assert.deepEqual(empty.originalToVirtualPositions(5, SpanMapFeature.All), []); - }); - - test("maps duplicate groups by features", () => { - const duplicates = new SpanMap([ - { virtualStart: 0, virtualEnd: 3, originalStart: 10, originalEnd: 13, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Definition }, - { virtualStart: 10, virtualEnd: 13, originalStart: 10, originalEnd: 13, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, - { virtualStart: 14, virtualEnd: 17, originalStart: 10, originalEnd: 13, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, - { virtualStart: 20, virtualEnd: 25, originalStart: 10, originalEnd: 13, kind: SpanMapKind.Atom, features: SpanMapFeature.Definition }, - ]); - - assert.deepEqual(duplicates.originalToVirtualPositions(11, SpanMapFeature.Hover), [ - { position: 11, fidelity: SpanMapFidelity.Exact }, - { position: 15, fidelity: SpanMapFidelity.Exact }, - ]); - assert.deepEqual(duplicates.originalToVirtualPositions(11, SpanMapFeature.Definition), [ - { position: 1, fidelity: SpanMapFidelity.Exact }, - { position: 20, fidelity: SpanMapFidelity.Atom }, - ]); - assert.deepEqual(duplicates.originalToVirtualPositions(13, SpanMapFeature.Hover), [ - { position: 13, fidelity: SpanMapFidelity.Exact }, - { position: 17, fidelity: SpanMapFidelity.Exact }, - ]); - }); - - test("maps every covering overlapping span", () => { - const overlapping = new SpanMap([ - { virtualStart: 0, virtualEnd: 6, originalStart: 0, originalEnd: 6, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, - { virtualStart: 10, virtualEnd: 12, originalStart: 2, originalEnd: 4, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, - { virtualStart: 20, virtualEnd: 24, originalStart: 3, originalEnd: 7, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, - ]); - - assert.deepEqual(overlapping.originalToVirtualPositions(3, SpanMapFeature.Hover), [ - { position: 3, fidelity: SpanMapFidelity.Exact }, - { position: 11, fidelity: SpanMapFidelity.Exact }, - { position: 20, fidelity: SpanMapFidelity.Exact }, - ]); - assert.deepEqual(overlapping.originalToVirtualSpans({ pos: 3, end: 4 }, SpanMapFeature.Hover), [ - { range: { pos: 3, end: 4 }, fidelity: SpanMapFidelity.Exact }, - { range: { pos: 11, end: 12 }, fidelity: SpanMapFidelity.Exact }, - { range: { pos: 20, end: 21 }, fidelity: SpanMapFidelity.Exact }, - ]); - }); - - test("finds an early covering segment through the original index", () => { - // Binary search lands near [90,95), which does not contain 97. The interval index must still find the - // earlier [0,100) segment without scanning every segment whose start precedes the query. - const overlapping = new SpanMap([ - { virtualStart: 0, virtualEnd: 100, originalStart: 0, originalEnd: 100, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, - { virtualStart: 100, virtualEnd: 105, originalStart: 80, originalEnd: 85, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, - { virtualStart: 105, virtualEnd: 110, originalStart: 90, originalEnd: 95, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, - { virtualStart: 110, virtualEnd: 113, originalStart: 100, originalEnd: 103, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, - ]); - - assert.deepEqual(overlapping.originalToVirtualPositions(97, SpanMapFeature.Hover), [ - { position: 97, fidelity: SpanMapFidelity.Exact }, - ]); - assert.deepEqual(overlapping.originalToVirtualSpans({ pos: 97, end: 98 }, SpanMapFeature.Hover), [ - { range: { pos: 97, end: 98 }, fidelity: SpanMapFidelity.Exact }, - ]); - - // Point lookup includes both sides of a shared endpoint. Nonempty span lookup treats segment ends as - // exclusive and uses only the segment beginning at the endpoint. - assert.deepEqual(overlapping.originalToVirtualPositions(100, SpanMapFeature.Hover), [ - { position: 100, fidelity: SpanMapFidelity.Exact }, - { position: 110, fidelity: SpanMapFidelity.Exact }, - ]); - assert.deepEqual(overlapping.originalToVirtualSpans({ pos: 100, end: 101 }, SpanMapFeature.Hover), [ - { range: { pos: 110, end: 111 }, fidelity: SpanMapFidelity.Exact }, - ]); - }); - - test("falls back from a disabled containing span", () => { - const overlapping = new SpanMap([ - { virtualStart: 0, virtualEnd: 6, originalStart: 0, originalEnd: 6, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Definition }, - { virtualStart: 10, virtualEnd: 13, originalStart: 0, originalEnd: 3, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, - { virtualStart: 13, virtualEnd: 16, originalStart: 3, originalEnd: 6, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, - ]); - - assert.deepEqual(overlapping.originalToVirtualSpans({ pos: 1, end: 5 }, SpanMapFeature.Hover), [ - { range: { pos: 11, end: 15 }, fidelity: SpanMapFidelity.Approximate }, - ]); - }); - - test("maps minimal cross-group projections", () => { - const projections = new SpanMap([ - { virtualStart: 0, virtualEnd: 2, originalStart: 0, originalEnd: 2, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, - { virtualStart: 2, virtualEnd: 4, originalStart: 2, originalEnd: 4, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, - { virtualStart: 10, virtualEnd: 12, originalStart: 0, originalEnd: 2, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, - { virtualStart: 12, virtualEnd: 14, originalStart: 2, originalEnd: 4, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, - ]); - - assert.deepEqual(projections.originalToVirtualSpans({ pos: 1, end: 3 }, SpanMapFeature.Hover), [ - { range: { pos: 1, end: 3 }, fidelity: SpanMapFidelity.Approximate }, - { range: { pos: 11, end: 13 }, fidelity: SpanMapFidelity.Approximate }, - ]); - }); - - test("explicit zero features disables original-to-virtual mapping", () => { - const disabled = new SpanMap([ - { virtualStart: 0, virtualEnd: 3, originalStart: 10, originalEnd: 13, kind: SpanMapKind.Verbatim, features: SpanMapFeature.None }, - ]); - - assert.deepEqual(disabled.originalToVirtualPositions(11, SpanMapFeature.Hover), []); - assert.deepEqual(disabled.originalToVirtualPositions(11, SpanMapFeature.Definition), []); - assert.deepEqual(disabled.originalToVirtualSpans({ pos: 10, end: 13 }, SpanMapFeature.Hover), []); - }); - - test("exposes fidelity predicates", () => { - assert.equal(SpanMap.isExact(SpanMapFidelity.Exact), true); - assert.equal(SpanMap.isSingleSegment(SpanMapFidelity.Exact), true); - assert.equal(SpanMap.isSingleSegment(SpanMapFidelity.Atom), true); - assert.equal(SpanMap.isSingleSegment(SpanMapFidelity.Approximate), false); - assert.equal(SpanMap.isNone(SpanMapFidelity.None), true); - }); -}); +import { + SpanMap, + SpanMapFeature, + SpanMapFidelity, + SpanMapKind, +} from "@typescript/typescript/unstable/ast"; +import assert from "node:assert"; +import { + describe, + test, +} from "node:test"; + +describe("SpanMap", () => { + const map = new SpanMap([ + { virtualStart: 2, virtualEnd: 6, originalStart: 10, originalEnd: 14, kind: SpanMapKind.Verbatim }, + { virtualStart: 8, virtualEnd: 11, originalStart: 20, originalEnd: 27, kind: SpanMapKind.Atom }, + { virtualStart: 14, virtualEnd: 18, originalStart: 30, originalEnd: 34, kind: SpanMapKind.Verbatim }, + ]); + + test("maps virtual positions and ranges to original", () => { + assert.equal(map.segments[0].features, SpanMapFeature.All); + assert.deepEqual(map.virtualToOriginalPosition(4), { position: 12, fidelity: SpanMapFidelity.Exact }); + assert.deepEqual(map.virtualToOriginalSpan({ pos: 3, end: 5 }), { range: { pos: 11, end: 13 }, fidelity: SpanMapFidelity.Exact }); + assert.deepEqual(map.virtualToOriginalPosition(9), { position: 20, fidelity: SpanMapFidelity.Atom }); + assert.deepEqual(map.virtualToOriginalSpan({ pos: 8, end: 10 }), { range: { pos: 20, end: 27 }, fidelity: SpanMapFidelity.Atom }); + assert.deepEqual(map.virtualToOriginalSpan({ pos: 5, end: 15 }), { range: { pos: 13, end: 31 }, fidelity: SpanMapFidelity.Approximate }); + }); + + test("maps aliases with atom geometry", () => { + const alias = new SpanMap([ + { virtualStart: 0, virtualEnd: 3, originalStart: 0, originalEnd: 1, kind: SpanMapKind.Alias }, + ]); + assert.deepEqual(alias.virtualToOriginalSpan({ pos: 0, end: 3 }), { + range: { pos: 0, end: 1 }, + fidelity: SpanMapFidelity.Atom, + }); + }); + + test("maps synthesized gaps to insertion points", () => { + assert.deepEqual(map.virtualToOriginalPosition(0), { position: 0, fidelity: SpanMapFidelity.None }); + assert.deepEqual(map.virtualToOriginalSpan({ pos: 6, end: 8 }), { range: { pos: 14, end: 14 }, fidelity: SpanMapFidelity.None }); + assert.deepEqual(map.virtualToOriginalPosition(19), { position: 34, fidelity: SpanMapFidelity.None }); + }); + + test("maps original positions and ranges to virtual", () => { + assert.deepEqual(map.originalToVirtualPositions(12, SpanMapFeature.All), [{ position: 4, fidelity: SpanMapFidelity.Exact }]); + assert.deepEqual(map.originalToVirtualSpans({ pos: 21, end: 25 }, SpanMapFeature.All), [{ range: { pos: 8, end: 11 }, fidelity: SpanMapFidelity.Atom }]); + assert.deepEqual(map.originalToVirtualSpans({ pos: 13, end: 31 }, SpanMapFeature.All), [{ range: { pos: 5, end: 15 }, fidelity: SpanMapFidelity.Approximate }]); + assert.deepEqual(map.originalToVirtualPositions(15, SpanMapFeature.All), []); + }); + + test("maps segment endpoints", () => { + assert.deepEqual(map.virtualToOriginalPosition(18), { position: 34, fidelity: SpanMapFidelity.Exact }); + assert.deepEqual(map.originalToVirtualPositions(34, SpanMapFeature.All), [{ position: 18, fidelity: SpanMapFidelity.Exact }]); + assert.deepEqual(map.virtualToOriginalPosition(6), { position: 14, fidelity: SpanMapFidelity.None }); + assert.deepEqual(map.originalToVirtualPositions(14, SpanMapFeature.All), [{ position: 6, fidelity: SpanMapFidelity.Exact }]); + + const adjacent = new SpanMap([ + { virtualStart: 20, virtualEnd: 23, originalStart: 10, originalEnd: 13, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, + { virtualStart: 2, virtualEnd: 5, originalStart: 13, originalEnd: 16, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Completion }, + { virtualStart: 30, virtualEnd: 33, originalStart: 20, originalEnd: 25, kind: SpanMapKind.Atom }, + ]); + assert.deepEqual(adjacent.originalToVirtualPositions(13, SpanMapFeature.All), [ + { position: 2, fidelity: SpanMapFidelity.Exact }, + { position: 23, fidelity: SpanMapFidelity.Exact }, + ]); + assert.deepEqual(adjacent.originalToVirtualPositions(13, SpanMapFeature.Completion), [ + { position: 2, fidelity: SpanMapFidelity.Exact }, + ]); + assert.deepEqual(adjacent.originalToVirtualPositions(25, SpanMapFeature.All), [ + { position: 33, fidelity: SpanMapFidelity.Atom }, + ]); + }); + + test("maps zero-length spans at segment ends", () => { + assert.deepEqual(map.virtualToOriginalSpan({ pos: 18, end: 18 }), { + range: { pos: 34, end: 34 }, + fidelity: SpanMapFidelity.Exact, + }); + for (const originalEnd of [14, 34]) { + const positions = map.originalToVirtualPositions(originalEnd, SpanMapFeature.All); + assert.equal(positions.length, 1); + assert.deepEqual(map.originalToVirtualSpans({ pos: originalEnd, end: originalEnd }, SpanMapFeature.All), [{ + range: { pos: positions[0].position, end: positions[0].position }, + fidelity: positions[0].fidelity, + }]); + } + }); + + test("sorts virtual and original indexes independently", () => { + const reordered = new SpanMap([ + { virtualStart: 0, virtualEnd: 2, originalStart: 10, originalEnd: 12, kind: SpanMapKind.Verbatim }, + { virtualStart: 2, virtualEnd: 4, originalStart: 0, originalEnd: 2, kind: SpanMapKind.Verbatim }, + ]); + assert.deepEqual(reordered.virtualToOriginalPosition(3), { position: 1, fidelity: SpanMapFidelity.Exact }); + assert.deepEqual(reordered.originalToVirtualPositions(1, SpanMapFeature.All), [{ position: 3, fidelity: SpanMapFidelity.Exact }]); + }); + + test("an empty map describes fully synthesized output", () => { + const empty = new SpanMap([]); + assert.deepEqual(empty.virtualToOriginalPosition(5), { position: 0, fidelity: SpanMapFidelity.None }); + assert.deepEqual(empty.virtualToOriginalSpan({ pos: 2, end: 7 }), { range: { pos: 0, end: 0 }, fidelity: SpanMapFidelity.None }); + assert.deepEqual(empty.originalToVirtualPositions(5, SpanMapFeature.All), []); + }); + + test("maps duplicate groups by features", () => { + const duplicates = new SpanMap([ + { virtualStart: 0, virtualEnd: 3, originalStart: 10, originalEnd: 13, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Definition }, + { virtualStart: 10, virtualEnd: 13, originalStart: 10, originalEnd: 13, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, + { virtualStart: 14, virtualEnd: 17, originalStart: 10, originalEnd: 13, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, + { virtualStart: 20, virtualEnd: 25, originalStart: 10, originalEnd: 13, kind: SpanMapKind.Atom, features: SpanMapFeature.Definition }, + ]); + + assert.deepEqual(duplicates.originalToVirtualPositions(11, SpanMapFeature.Hover), [ + { position: 11, fidelity: SpanMapFidelity.Exact }, + { position: 15, fidelity: SpanMapFidelity.Exact }, + ]); + assert.deepEqual(duplicates.originalToVirtualPositions(11, SpanMapFeature.Definition), [ + { position: 1, fidelity: SpanMapFidelity.Exact }, + { position: 20, fidelity: SpanMapFidelity.Atom }, + ]); + assert.deepEqual(duplicates.originalToVirtualPositions(13, SpanMapFeature.Hover), [ + { position: 13, fidelity: SpanMapFidelity.Exact }, + { position: 17, fidelity: SpanMapFidelity.Exact }, + ]); + }); + + test("maps every covering overlapping span", () => { + const overlapping = new SpanMap([ + { virtualStart: 0, virtualEnd: 6, originalStart: 0, originalEnd: 6, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, + { virtualStart: 10, virtualEnd: 12, originalStart: 2, originalEnd: 4, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, + { virtualStart: 20, virtualEnd: 24, originalStart: 3, originalEnd: 7, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, + ]); + + assert.deepEqual(overlapping.originalToVirtualPositions(3, SpanMapFeature.Hover), [ + { position: 3, fidelity: SpanMapFidelity.Exact }, + { position: 11, fidelity: SpanMapFidelity.Exact }, + { position: 20, fidelity: SpanMapFidelity.Exact }, + ]); + assert.deepEqual(overlapping.originalToVirtualSpans({ pos: 3, end: 4 }, SpanMapFeature.Hover), [ + { range: { pos: 3, end: 4 }, fidelity: SpanMapFidelity.Exact }, + { range: { pos: 11, end: 12 }, fidelity: SpanMapFidelity.Exact }, + { range: { pos: 20, end: 21 }, fidelity: SpanMapFidelity.Exact }, + ]); + }); + + test("finds an early covering segment through the original index", () => { + // Binary search lands near [90,95), which does not contain 97. The interval index must still find the + // earlier [0,100) segment without scanning every segment whose start precedes the query. + const overlapping = new SpanMap([ + { virtualStart: 0, virtualEnd: 100, originalStart: 0, originalEnd: 100, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, + { virtualStart: 100, virtualEnd: 105, originalStart: 80, originalEnd: 85, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, + { virtualStart: 105, virtualEnd: 110, originalStart: 90, originalEnd: 95, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, + { virtualStart: 110, virtualEnd: 113, originalStart: 100, originalEnd: 103, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, + ]); + + assert.deepEqual(overlapping.originalToVirtualPositions(97, SpanMapFeature.Hover), [ + { position: 97, fidelity: SpanMapFidelity.Exact }, + ]); + assert.deepEqual(overlapping.originalToVirtualSpans({ pos: 97, end: 98 }, SpanMapFeature.Hover), [ + { range: { pos: 97, end: 98 }, fidelity: SpanMapFidelity.Exact }, + ]); + + // Point lookup includes both sides of a shared endpoint. Nonempty span lookup treats segment ends as + // exclusive and uses only the segment beginning at the endpoint. + assert.deepEqual(overlapping.originalToVirtualPositions(100, SpanMapFeature.Hover), [ + { position: 100, fidelity: SpanMapFidelity.Exact }, + { position: 110, fidelity: SpanMapFidelity.Exact }, + ]); + assert.deepEqual(overlapping.originalToVirtualSpans({ pos: 100, end: 101 }, SpanMapFeature.Hover), [ + { range: { pos: 110, end: 111 }, fidelity: SpanMapFidelity.Exact }, + ]); + }); + + test("falls back from a disabled containing span", () => { + const overlapping = new SpanMap([ + { virtualStart: 0, virtualEnd: 6, originalStart: 0, originalEnd: 6, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Definition }, + { virtualStart: 10, virtualEnd: 13, originalStart: 0, originalEnd: 3, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, + { virtualStart: 13, virtualEnd: 16, originalStart: 3, originalEnd: 6, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, + ]); + + assert.deepEqual(overlapping.originalToVirtualSpans({ pos: 1, end: 5 }, SpanMapFeature.Hover), [ + { range: { pos: 11, end: 15 }, fidelity: SpanMapFidelity.Approximate }, + ]); + }); + + test("maps minimal cross-group projections", () => { + const projections = new SpanMap([ + { virtualStart: 0, virtualEnd: 2, originalStart: 0, originalEnd: 2, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, + { virtualStart: 2, virtualEnd: 4, originalStart: 2, originalEnd: 4, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, + { virtualStart: 10, virtualEnd: 12, originalStart: 0, originalEnd: 2, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, + { virtualStart: 12, virtualEnd: 14, originalStart: 2, originalEnd: 4, kind: SpanMapKind.Verbatim, features: SpanMapFeature.Hover }, + ]); + + assert.deepEqual(projections.originalToVirtualSpans({ pos: 1, end: 3 }, SpanMapFeature.Hover), [ + { range: { pos: 1, end: 3 }, fidelity: SpanMapFidelity.Approximate }, + { range: { pos: 11, end: 13 }, fidelity: SpanMapFidelity.Approximate }, + ]); + }); + + test("explicit zero features disables original-to-virtual mapping", () => { + const disabled = new SpanMap([ + { virtualStart: 0, virtualEnd: 3, originalStart: 10, originalEnd: 13, kind: SpanMapKind.Verbatim, features: SpanMapFeature.None }, + ]); + + assert.deepEqual(disabled.originalToVirtualPositions(11, SpanMapFeature.Hover), []); + assert.deepEqual(disabled.originalToVirtualPositions(11, SpanMapFeature.Definition), []); + assert.deepEqual(disabled.originalToVirtualSpans({ pos: 10, end: 13 }, SpanMapFeature.Hover), []); + }); + + test("exposes fidelity predicates", () => { + assert.equal(SpanMap.isExact(SpanMapFidelity.Exact), true); + assert.equal(SpanMap.isSingleSegment(SpanMapFidelity.Exact), true); + assert.equal(SpanMap.isSingleSegment(SpanMapFidelity.Atom), true); + assert.equal(SpanMap.isSingleSegment(SpanMapFidelity.Approximate), false); + assert.equal(SpanMap.isNone(SpanMapFidelity.None), true); + }); +}); diff --git a/packages/typescript/test/sync/api-generators.test.ts b/packages/typescript/test/sync/api-generators.test.ts index 9016e6c75e7de..721bad9bb1cff 100644 --- a/packages/typescript/test/sync/api-generators.test.ts +++ b/packages/typescript/test/sync/api-generators.test.ts @@ -1,934 +1,934 @@ -import { - cast, - type Identifier, - isCallExpression, - isClassDeclaration, - isEnumDeclaration, - isExportDeclaration, - isFunctionDeclaration, - isIdentifier, - isImportDeclaration, - isInterfaceDeclaration, - isNamedExports, - isNamedImports, - isObjectLiteralExpression, - isShorthandPropertyAssignment, - isTypeAliasDeclaration, - isVariableDeclaration, - isVariableStatement, - type Node, - type SourceFile, - SyntaxKind, -} from "@typescript/typescript/unstable/ast"; -import type { - APIRequest, - APIResponse, -} from "@typescript/typescript/unstable/proto"; -import { - all, - type API, - type ConditionalType, - type IndexedAccessType, - type IndexInfo, - type InterfaceType, - type LiteralType, - type NodeHandle, - type Project, - type Signature, - SignatureKind, - type Snapshot, - type SubstitutionType, - type Symbol, - SymbolFlags, - type TimingInfo, - type Type, - TypeFlags, - type TypeParameter, - type TypePredicate, - type TypeReference, - type UnionOrIntersectionType, -} from "@typescript/typescript/unstable/sync"; -import assert from "node:assert"; -import { - describe, - test, -} from "node:test"; -import { spawnAPI } from "./api.testUtils.ts"; - -const parityFiles = { - "/base.json": JSON.stringify({ - compilerOptions: { - declaration: true, - module: "nodenext", - moduleResolution: "nodenext", - strict: true, - target: "esnext", - exactOptionalPropertyTypes: true, - }, - }), - "/tsconfig.json": JSON.stringify({ - extends: "./base.json", - include: ["src/**/*.ts"], - }), - "/src/models.ts": ` -export interface Box { - value: T; - tuple: readonly [T, ...T[]]; - [key: string]: unknown; - [index: number]: T; - readonly opt?: true; -} -export class Base { base = true; } -export class Derived extends Base { derived = 1; } -export type Boxed = Box; -export type TupleAlias = readonly [string, ...number[]]; -export type ArrayAlias = Derived[]; -export type Conditional = T extends Base ? T : never; -export type Indexed> = T["value"]; -export type Keys = keyof Box; -export type Union = Derived | string; -export enum Choice { First = 1, Second = "second" } -export class Unimported { value = "extra"; } -`, - "/src/index.ts": ` -import { Base, Box, Choice, Derived } from "./models.js"; -export { Derived as RenamedDerived } from "./models.js"; - -/** Combine a first value with the rest. @deprecated parity fixture */ -export function combine(this: Box, first: T, ...rest: T[]): readonly [T, ...T[]] { - return [first, ...rest]; -} - -export const derived = new Derived(); -export const box: Box = { value: derived, tuple: [derived], derived }; -export const shorthand = { derived }; -export const called = combine.call(box, derived, derived); -export const semanticIssue: string = 123; -export const choice = Choice.First; -export function isDerived(value: Base): value is Derived { return value instanceof Derived; } -export const badCall = derived(); -export function getArguments() { return arguments; } -import { absent } from "./absent.js"; -export const missingValue = absent; -`, - "/src/syntax.ts": `export const broken: = 1;`, - "/src/bind.ts": `let duplicate = 1; let duplicate = 2;`, - "/src/suggestions.ts": `export function suggestion() { const unused = 1; return 1; }`, -}; - -type GeneratorMethod = ((...args: Args) => Result) & { - gen(...args: Args): Generator; -}; - -type EquivalenceAssertion = (actual: Result, expected: Result, message?: string) => void; - -interface ParityCase { - readonly ownerName: string; - readonly methodName: string; - readonly runGenerator: () => Generator; - readonly assertEquivalent: (message?: string) => void; -} - -const exercisedMethods = new Set(); -const publicGeneratorExemptions = new Map([ - ["API.fromLSPConnection", "requires an existing LSP API session"], - ["InternalAPI.startCPUProfile", "writes a CPU profile and changes process-global profiling state"], - ["InternalAPI.stopCPUProfile", "requires a matching active CPU profile"], - ["InternalAPI.saveHeapProfile", "writes a potentially large heap profile to disk"], -]); -const privateGeneratorGetters = new Set([ - "API.ensureInitialized", - "Checker.getIntrinsicType", - "Checker.getWellKnownSignatures", - "Checker.getWellKnownSymbols", - "Program.fetchSourceFileMetadata", - "Symbol.fetchSymbolTable", - "Type.getNumberIndexTypeWorker", - "Type.getStringIndexTypeWorker", -]); - -function methodKey(ownerName: string, methodName: string): string { - return `${ownerName}.${methodName}`; -} - -function parityCase( - ownerName: string, - methodName: string, - method: GeneratorMethod, - assertEquivalent: EquivalenceAssertion>, - ...args: Args -): ParityCase { - let generatedResult: { readonly value: Result; } | undefined; - return { - ownerName, - methodName, - runGenerator: function* () { - generatedResult = { value: yield* method.gen(...args) }; - }, - assertEquivalent: message => { - assert.ok(generatedResult, `${message ?? methodName}: generator did not complete`); - assertEquivalent(generatedResult.value, method(...args), message); - }, - }; -} - -function selectGeneratorMethod( - method: GeneratorMethod, -): GeneratorMethod { - return method; -} - -function assertDeepEquivalent(actual: T, expected: T, message?: string): void { - assert.deepEqual(actual, expected, message); -} - -function assertOptionalEquivalent(actual: T | undefined, expected: T | undefined, assertPresentEquivalent: EquivalenceAssertion, message?: string): void { - if (actual === undefined || expected === undefined) { - assert.equal(actual, expected, message); - return; - } - assertPresentEquivalent(actual, expected, message); -} - -function assertArrayElementsEquivalent(actual: readonly T[], expected: readonly T[], assertElementEquivalent: EquivalenceAssertion, message?: string): void { - assert.equal(actual.length, expected.length, message); - for (let index = 0; index < actual.length; index++) { - assertElementEquivalent(actual[index], expected[index], `${message ?? "value"}[${index}]`); - } -} - -function assertSymbolsEquivalent(actual: Symbol, expected: Symbol, message?: string): void { - assert.strictEqual(actual, expected, message); - assert.equal(actual.id, expected.id, message); - assert.equal(actual.name, expected.name, message); - assert.equal(actual.flags, expected.flags, message); -} - -function assertOptionalSymbolsEquivalent(actual: Symbol | undefined, expected: Symbol | undefined, message?: string): void { - assertOptionalEquivalent(actual, expected, assertSymbolsEquivalent, message); -} - -function assertSymbolArraysEquivalent(actual: readonly Symbol[], expected: readonly Symbol[], message?: string): void { - assertArrayElementsEquivalent(actual, expected, assertSymbolsEquivalent, message); -} - -function assertUnorderedSymbolArraysEquivalent(actual: readonly Symbol[], expected: readonly Symbol[], message?: string): void { - const byIdentity = (left: Symbol, right: Symbol) => left.id - right.id || left.name.localeCompare(right.name); - const actualSymbols = [...actual].sort(byIdentity); - const expectedSymbols = [...expected].sort(byIdentity); - assertArrayElementsEquivalent(actualSymbols, expectedSymbols, assertSymbolsEquivalent, message); -} - -function assertOptionalSymbolArraysEquivalent(actual: readonly (Symbol | undefined)[], expected: readonly (Symbol | undefined)[], message?: string): void { - assertArrayElementsEquivalent(actual, expected, assertOptionalSymbolsEquivalent, message); -} - -function assertTypesEquivalent(actual: Type, expected: Type, message?: string): void { - assert.strictEqual(actual, expected, message); - assert.equal(actual.id, expected.id, message); - assert.equal(actual.flags, expected.flags, message); -} - -function assertOptionalTypesEquivalent(actual: Type | undefined, expected: Type | undefined, message?: string): void { - assertOptionalEquivalent(actual, expected, assertTypesEquivalent, message); -} - -function assertTypeArraysEquivalent(actual: readonly Type[], expected: readonly Type[], message?: string): void { - assertArrayElementsEquivalent(actual, expected, assertTypesEquivalent, message); -} - -function assertOptionalTypeArraysEquivalent(actual: readonly (Type | undefined)[], expected: readonly (Type | undefined)[], message?: string): void { - assertArrayElementsEquivalent(actual, expected, assertOptionalTypesEquivalent, message); -} - -function assertOptionalTypeArrayEquivalent(actual: readonly Type[] | undefined, expected: readonly Type[] | undefined, message?: string): void { - assertOptionalEquivalent(actual, expected, assertTypeArraysEquivalent, message); -} - -function assertSignaturesEquivalent(actual: Signature, expected: Signature, message?: string): void { - assert.strictEqual(actual, expected, message); - assert.equal(actual.id, expected.id, message); - assert.equal(actual.hasRestParameter, expected.hasRestParameter, message); - assert.equal(actual.isConstruct, expected.isConstruct, message); -} - -function assertOptionalSignaturesEquivalent(actual: Signature | undefined, expected: Signature | undefined, message?: string): void { - assertOptionalEquivalent(actual, expected, assertSignaturesEquivalent, message); -} - -function assertSignatureArraysEquivalent(actual: readonly Signature[], expected: readonly Signature[], message?: string): void { - assertArrayElementsEquivalent(actual, expected, assertSignaturesEquivalent, message); -} - -function assertNodeHandlesEquivalent(actual: NodeHandle, expected: NodeHandle, message?: string): void { - assert.equal(actual.index, expected.index, message); - assert.equal(actual.kind, expected.kind, message); - assert.equal(actual.path, expected.path, message); -} - -function assertNodeHandleArraysEquivalent(actual: readonly NodeHandle[], expected: readonly NodeHandle[], message?: string): void { - assertArrayElementsEquivalent(actual, expected, assertNodeHandlesEquivalent, message); -} - -function assertOptionalNodeHandlesEquivalent(actual: NodeHandle | undefined, expected: NodeHandle | undefined, message?: string): void { - assertOptionalEquivalent(actual, expected, assertNodeHandlesEquivalent, message); -} - -function assertNodesEquivalent(actual: Node, expected: Node, message?: string): void { - assert.equal(actual.kind, expected.kind, message); - assert.equal(actual.pos, expected.pos, message); - assert.equal(actual.end, expected.end, message); -} - -function assertOptionalNodesEquivalent(actual: Node | undefined, expected: Node | undefined, message?: string): void { - assertOptionalEquivalent(actual, expected, assertNodesEquivalent, message); -} - -function assertSourceFilesEquivalent(actual: SourceFile, expected: SourceFile, message?: string): void { - assertNodesEquivalent(actual, expected, message); - assert.equal(actual.fileName, expected.fileName, message); - assert.equal(actual.text, expected.text, message); -} - -function assertOptionalSourceFilesEquivalent(actual: SourceFile | undefined, expected: SourceFile | undefined, message?: string): void { - assertOptionalEquivalent(actual, expected, assertSourceFilesEquivalent, message); -} - -function assertProjectsEquivalent(actual: Project, expected: Project, message?: string): void { - assert.equal(actual.configFileName, expected.configFileName, message); - assert.deepEqual(actual.rootFiles, expected.rootFiles, message); -} - -function assertOptionalProjectsEquivalent(actual: Project | undefined, expected: Project | undefined, message?: string): void { - assertOptionalEquivalent(actual, expected, assertProjectsEquivalent, message); -} - -function assertSnapshotsEquivalent(actual: Snapshot, expected: Snapshot, message?: string): void { - const actualProjects = actual.getProjects(); - const expectedProjects = expected.getProjects(); - assertArrayElementsEquivalent(actualProjects, expectedProjects, assertProjectsEquivalent, message); -} - -function assertSymbolMapsEquivalent(actual: ReadonlyMap, expected: ReadonlyMap, message?: string): void { - assert.deepEqual([...actual.keys()], [...expected.keys()], message); - for (const key of actual.keys()) { - assertOptionalSymbolsEquivalent(actual.get(key), expected.get(key), `${message ?? "symbol map"}[${key}]`); - } -} - -function assertIndexInfosEquivalent(actual: readonly IndexInfo[], expected: readonly IndexInfo[], message?: string): void { - assertArrayElementsEquivalent(actual, expected, (actualInfo, expectedInfo, elementMessage) => { - assertTypesEquivalent(actualInfo.keyType, expectedInfo.keyType, elementMessage); - assertTypesEquivalent(actualInfo.valueType, expectedInfo.valueType, elementMessage); - assert.equal(actualInfo.isReadonly, expectedInfo.isReadonly, elementMessage); - assertOptionalNodeHandlesEquivalent(actualInfo.declaration, expectedInfo.declaration, elementMessage); - }, message); -} - -function assertOptionalTypePredicatesEquivalent(actual: TypePredicate | undefined, expected: TypePredicate | undefined, message?: string): void { - assertOptionalEquivalent(actual, expected, (actualPredicate, expectedPredicate, predicateMessage) => { - assert.equal(actualPredicate.kind, expectedPredicate.kind, predicateMessage); - assert.equal(actualPredicate.parameterName, expectedPredicate.parameterName, predicateMessage); - assert.equal(actualPredicate.parameterIndex, expectedPredicate.parameterIndex, predicateMessage); - assertOptionalTypesEquivalent(actualPredicate.type, expectedPredicate.type, predicateMessage); - }, message); -} - -function assertDeepArraysEquivalent(actual: readonly T[], expected: readonly T[], message?: string): void { - assertArrayElementsEquivalent(actual, expected, assertDeepEquivalent, message); -} - -function assertTimingInfoEquivalent(actual: TimingInfo, expected: TimingInfo, message?: string): void { - assert.equal(actual.enabled, expected.enabled, message); - assert.equal(actual.totals.requestCount, expected.totals.requestCount, message); - assert.equal(actual.totals.bytesSent, expected.totals.bytesSent, message); - assert.equal(actual.totals.bytesReceived, expected.totals.bytesReceived, message); - assert.deepEqual(actual.recentRequests.map(request => request.method), expected.recentRequests.map(request => request.method), message); -} - -function runParityBatch(api: API, cases: readonly ParityCase[]): void { - api.batch(...cases.map(parity => parity.runGenerator())); - - for (const { assertEquivalent, methodName, ownerName } of cases) { - assertEquivalent(methodKey(ownerName, methodName)); - exercisedMethods.add(methodKey(ownerName, methodName)); - } -} - -function* observeRequestBatches( - requestGenerator: Generator, - requestBatches: string[][], -): Generator { - let state = requestGenerator.next(); - while (!state.done) { - const request = state.value; - const requests = Array.isArray(request) ? request : [request as APIRequest]; - requestBatches.push(requests.map(current => current.method)); - state = requestGenerator.next(yield request); - } - return state.value; -} - -function assertPublicGeneratorCoverage(owners: readonly { readonly name: string; readonly value: object; readonly own?: boolean; }[]): void { - const missing: string[] = []; - for (const { name, own, value } of owners) { - const descriptorOwner = own ? value : Object.getPrototypeOf(value) as object; - for (const [methodName, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(descriptorOwner))) { - if (!descriptor.get) continue; - const key = methodKey(name, methodName); - const result: { gen?: unknown; } | undefined = descriptor.get.call(value); - if (typeof result?.gen !== "function") continue; - if (!exercisedMethods.has(key) && !publicGeneratorExemptions.has(key) && !privateGeneratorGetters.has(key)) { - missing.push(key); - } - } - } - assert.deepEqual(missing, [], `Uncovered public generator getters: ${missing.join(", ")}`); -} - -describe("API - generator batching", () => { - test("composes request generators with all", () => { - const api = spawnAPI(parityFiles); - const requestBatches: string[][] = []; - function* getStrictOption() { - const commandLine = yield* api.parseCommandLine.gen(["--strict"]); - const config = yield* api.readConfigFile.gen("/tsconfig.json"); - const parsed = yield* api.parseJsonConfigFileContent.gen(config.config, { configFileName: "/tsconfig.json" }); - return { strict: commandLine.options.strict, fileNames: parsed.fileNames }; - } - function* getTranspiledText() { - const config = yield* api.readConfigFile.gen("/tsconfig.json"); - const parsed = yield* api.parseJsonConfigFileContent.gen(config.config, { configFileName: "/tsconfig.json" }); - const output = yield* api.transpileModule.gen("const value: string = 'ok';", { compilerOptions: parsed.options }); - return output.outputText; - } - - try { - const [[config, outputText]] = api.batch(observeRequestBatches(all(getStrictOption(), getTranspiledText()), requestBatches)); - assert.equal(config.strict, true); - assert.deepEqual([...config.fileNames].sort(), ["/src/bind.ts", "/src/index.ts", "/src/models.ts", "/src/suggestions.ts", "/src/syntax.ts"]); - assert.match(outputText, /const value = ['"]ok['"]/); - assert.deepEqual(requestBatches, [ - ["initialize"], - ["parseCommandLine", "readConfigFile"], - ["readConfigFile", "parseJsonConfigFileContent"], - ["parseJsonConfigFileContent", "transpileModule"], - ]); - } - finally { - api.close(); - } - }); - - test("throws request errors into generators", () => { - const api = spawnAPI(); - const events: string[] = []; - function* requestWithCleanup(): Generator { - try { - yield { method: "unknown", params: null } as unknown as APIRequest; - return "unexpected"; - } - catch { - events.push("caught"); - return "recovered"; - } - finally { - events.push("finally"); - } - } - - try { - assert.deepEqual(api.batch(requestWithCleanup()), ["recovered"]); - assert.deepEqual(events, ["caught", "finally"]); - } - finally { - api.close(); - } - }); - - test("lazily caches generator-enabled methods", () => { - const api = spawnAPI(); - const prototype = Object.getPrototypeOf(api); - try { - assert.equal(Object.hasOwn(api, "parseCommandLine"), false); - assert.equal(typeof Object.getOwnPropertyDescriptor(prototype, "parseCommandLine")?.get, "function"); - - const method = api.parseCommandLine; - assert.strictEqual(api.parseCommandLine, method); - assert.equal(Object.hasOwn(api, "parseCommandLine"), true); - assert.equal(Object.keys(api).includes("parseCommandLine"), false); - assert.equal(typeof method.gen, "function"); - } - finally { - api.close(); - } - }); - - test("advances generators through multiple request rounds", () => { - const api = spawnAPI(); - try { - const [commandLine, config] = api.batch( - api.parseCommandLine.gen(["--strict"]), - api.readConfigFile.gen("/tsconfig.json"), - ); - - assert.equal(commandLine.options.strict, true); - assert.deepEqual(config.config, {}); - } - finally { - api.batch(api.close.gen()); - api.close(); - } - }); - - test("all deduplicates only initialize requests within a batch round", () => { - const api = spawnAPI(); - const requestBatches: string[][] = []; - - try { - const [[firstCommandLine, secondCommandLine, config]] = api.batch(observeRequestBatches( - all( - api.parseCommandLine.gen(["--strict"]), - api.parseCommandLine.gen(["--strict"]), - api.readConfigFile.gen("/tsconfig.json"), - ), - requestBatches, - )); - - assert.deepEqual(requestBatches, [ - ["initialize"], - ["parseCommandLine", "parseCommandLine", "readConfigFile"], - ]); - assert.equal(firstCommandLine.options.strict, true); - assert.equal(secondCommandLine.options.strict, true); - assert.deepEqual(config.config, {}); - } - finally { - api.close(); - } - }); - - test("yields source file metadata requests on cache misses", () => { - const api = spawnAPI(); - try { - using snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const program = snapshot.getProject("/tsconfig.json")!.program; - const sourceFile = program.getSourceFile("/src/index.ts")!; - const state = program.getSourceFileMetadataByPath.gen(sourceFile.path).next(); - - if (state.done) assert.fail("Expected getSourceFileMetadataByPath.gen() to yield a request"); - assert.equal(state.value.method, "getSourceFileMetadata"); - } - finally { - api.close(); - } - }); - - test("uses generators attached to sync API methods", () => { - const api = spawnAPI(); - try { - using snapshot = api.batch(api.updateSnapshot.gen({ openProject: "/tsconfig.json" }))[0]; - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - const node = cast( - cast(sourceFile.statements[0], isImportDeclaration).importClause?.namedBindings, - isNamedImports, - ).elements[0].name; - - const [defaultProject, sourceFileNames] = api.batch( - snapshot.getDefaultProjectForFile.gen("/src/index.ts"), - project.program.getSourceFileNames.gen(), - ); - assert.strictEqual(defaultProject, project); - assert.ok(sourceFileNames.includes("/src/foo.ts")); - assert.ok(sourceFileNames.includes("/src/index.ts")); - - const [symbol, type] = api.batch( - project.checker.getSymbolAtLocation.gen(node), - project.checker.getTypeAtLocation.gen(node), - ); - const syncSymbol = project.checker.getSymbolAtLocation(node); - const syncType = project.checker.getTypeAtLocation(node); - - assert.ok(symbol); - assert.ok(syncSymbol); - assert.equal(symbol.name, "foo"); - assert.strictEqual(symbol, syncSymbol); - assert.strictEqual(type, syncType); - assert.ok(symbol.flags & SymbolFlags.Alias); - assert.ok(type.flags & TypeFlags.NumberLiteral); - assert.equal(Object.hasOwn(syncType, "getProperties"), false); - assert.equal(typeof Object.getOwnPropertyDescriptor(Object.getPrototypeOf(syncType), "getProperties")?.get, "function"); - - const [parent, properties] = api.batch( - syncSymbol.getParent.gen(), - syncType.getProperties.gen(), - ); - assert.equal(parent, undefined); - assert.ok(properties.some(property => property.name === "toString")); - assert.equal(Object.hasOwn(syncType, "getProperties"), true); - assert.equal(Object.keys(syncType).includes("getProperties"), false); - } - finally { - api.batch(api.close.gen()); - api.close(); - } - }); - - test("keeps every publicly reachable generator-backed method in sync", () => { - const api = spawnAPI(parityFiles); - try { - using snapshot = api.batch(api.updateSnapshot.gen({ openProject: "/tsconfig.json" }))[0]; - const project = snapshot.getProject("/tsconfig.json")!; - const { checker, emitter, languageService, program } = project; - const indexFile = program.getSourceFile("/src/index.ts")!; - const modelsFile = program.getSourceFile("/src/models.ts")!; - - const importDeclaration = cast(indexFile.statements[0], isImportDeclaration); - const importedNames = cast(importDeclaration.importClause?.namedBindings, isNamedImports); - const importedDerived = importedNames.elements.find(element => element.name.text === "Derived")!.name; - const exportDeclaration = cast(indexFile.statements[1], isExportDeclaration); - const exportSpecifier = cast(exportDeclaration.exportClause, isNamedExports).elements[0]; - const combineDeclaration = cast(indexFile.statements[2], isFunctionDeclaration); - const derivedDeclaration = cast( - cast(indexFile.statements[3], isVariableStatement).declarationList.declarations[0], - isVariableDeclaration, - ); - const boxDeclaration = cast( - cast(indexFile.statements[4], isVariableStatement).declarationList.declarations[0], - isVariableDeclaration, - ); - const shorthandDeclaration = cast( - cast(indexFile.statements[5], isVariableStatement).declarationList.declarations[0], - isVariableDeclaration, - ); - const shorthand = cast( - cast(shorthandDeclaration.initializer, isObjectLiteralExpression).properties[0], - isShorthandPropertyAssignment, - ); - const calledDeclaration = cast( - cast(indexFile.statements[6], isVariableStatement).declarationList.declarations[0], - isVariableDeclaration, - ); - const callExpression = cast(calledDeclaration.initializer, isCallExpression); - const interfaceDeclaration = cast(modelsFile.statements[0], isInterfaceDeclaration); - const derivedClassDeclaration = cast(modelsFile.statements[2], isClassDeclaration); - const boxedAlias = cast(modelsFile.statements[3], isTypeAliasDeclaration); - const tupleAlias = cast(modelsFile.statements[4], isTypeAliasDeclaration); - const arrayAlias = cast(modelsFile.statements[5], isTypeAliasDeclaration); - const conditionalAlias = cast(modelsFile.statements[6], isTypeAliasDeclaration); - const indexedAlias = cast(modelsFile.statements[7], isTypeAliasDeclaration); - const indexAlias = cast(modelsFile.statements[8], isTypeAliasDeclaration); - const unionAlias = cast(modelsFile.statements[9], isTypeAliasDeclaration); - const enumDeclaration = cast(modelsFile.statements[10], isEnumDeclaration); - - const importedDerivedSymbol = checker.getSymbolAtLocation(importedDerived)!; - const combineSymbol = checker.getSymbolAtLocation(combineDeclaration.name!)!; - const derivedSymbol = checker.getSymbolAtLocation(cast(derivedDeclaration.name, isIdentifier))!; - const interfaceSymbol = checker.getSymbolAtLocation(interfaceDeclaration.name)!; - const derivedClassSymbol = checker.getSymbolAtLocation(derivedClassDeclaration.name!)!; - const moduleSymbol = checker.getSymbolOfSourceFile("/src/models.ts")!; - const unimportedSymbol = checker.getMemberInModuleExports(moduleSymbol, "Unimported")!; - const interfaceType = checker.getDeclaredTypeOfSymbol(interfaceSymbol) as InterfaceType; - const derivedType = checker.getDeclaredTypeOfSymbol(derivedClassSymbol) as InterfaceType; - const derivedConstructorType = checker.getTypeOfSymbol(derivedClassSymbol); - const boxedType = checker.getTypeFromTypeNode(boxedAlias.type) as TypeReference; - const boxedOptSymbol = checker.getPropertyOfType(boxedType, "opt"); - assert.ok(boxedOptSymbol); - const tupleType = checker.getTypeFromTypeNode(tupleAlias.type); - const arrayType = checker.getTypeFromTypeNode(arrayAlias.type); - const conditionalType = checker.getTypeFromTypeNode(conditionalAlias.type) as ConditionalType; - const indexedType = checker.getTypeFromTypeNode(indexedAlias.type) as IndexedAccessType; - checker.getTypeFromTypeNode(indexAlias.type); - const unionType = checker.getTypeFromTypeNode(unionAlias.type); - const typeParameter = checker.getTypeAtLocation(combineDeclaration.typeParameters![0].name) as TypeParameter; - const literalType = checker.getTypeAtLocation(enumDeclaration.members[0].name) as LiteralType; - const substitutionType = conditionalType.getTrueType() as SubstitutionType; - const signature = checker.getSignatureFromDeclaration(combineDeclaration); - const predicateDeclaration = indexFile.statements.find(statement => isFunctionDeclaration(statement) && statement.name?.text === "isDerived")!; - const predicateSignature = checker.getSignatureFromDeclaration(predicateDeclaration); - const badCallDeclaration = indexFile.statements - .filter(isVariableStatement) - .flatMap(statement => [...statement.declarationList.declarations]) - .find(declaration => isIdentifier(declaration.name) && declaration.name.text === "badCall")!; - const unknownSignature = checker.getResolvedSignature(cast(badCallDeclaration.initializer, isCallExpression)); - let argumentsIdentifier: Identifier | undefined; - let absentIdentifier: Identifier | undefined; - indexFile.forEachChild(function visit(node) { - if (isIdentifier(node)) { - if (node.text === "arguments") argumentsIdentifier = node; - if (node.text === "absent" && !absentIdentifier) absentIdentifier = node; - } - node.forEachChild(visit); - }); - assert.ok(argumentsIdentifier); - assert.ok(absentIdentifier); - const absentAlias = checker.getSymbolAtLocation(absentIdentifier); - assert.ok(absentAlias); - const unknownSymbol = checker.getAliasedSymbol(absentAlias); - const undefinedSymbol = checker.resolveName("undefined", SymbolFlags.Value, absentIdentifier); - const argumentsSymbol = checker.getResolvedSymbol(argumentsIdentifier); - assert.ok(unknownSymbol); - assert.ok(undefinedSymbol); - assert.ok(argumentsSymbol); - const constructSignature = derivedConstructorType.getConstructSignatures()[0]; - const derivedMemberSymbol = derivedType.getProperty("derived")!; - const nodeHandle = combineSymbol.valueDeclaration ?? combineSymbol.declarations[0]; - const completionPosition = parityFiles["/src/index.ts"].indexOf("semanticIssue"); - const temporaryProjects: string[] = []; - - const orderedArguments = Array.from({ length: 128 }, (_, index) => ["--strict", `--outDir=out-${index}`] as const); - const orderedGenerated = api.batch(...orderedArguments.map(args => api.parseCommandLine.gen(args))); - const orderedSync = orderedArguments.map(args => api.parseCommandLine(args)); - assertDeepArraysEquivalent(orderedGenerated, orderedSync, "large parseCommandLine batch"); - exercisedMethods.add("API.parseCommandLine"); - - const timingGeneratorAPI = spawnAPI(parityFiles, { collectTiming: true }); - const timingSyncAPI = spawnAPI(parityFiles, { collectTiming: true }); - timingGeneratorAPI.parseCommandLine(["--strict"]); - timingSyncAPI.parseCommandLine(["--strict"]); - assertTimingInfoEquivalent( - timingGeneratorAPI.batch(timingGeneratorAPI.getTimingInfo.gen())[0], - timingSyncAPI.getTimingInfo(), - "API.getTimingInfo", - ); - timingGeneratorAPI.batch(timingGeneratorAPI.resetTimingInfo.gen()); - timingSyncAPI.resetTimingInfo(); - assertTimingInfoEquivalent(timingGeneratorAPI.getTimingInfo(), timingSyncAPI.getTimingInfo(), "API.resetTimingInfo"); - exercisedMethods.add("API.getTimingInfo"); - exercisedMethods.add("API.resetTimingInfo"); - timingGeneratorAPI.close(); - timingSyncAPI.close(); - - assert.ok(project.getImportAdderEdits("/src/index.ts", [{ kind: "importSymbol", symbol: unimportedSymbol }]).length > 0); - assert.ok(program.getSyntacticDiagnostics("/src/syntax.ts").length > 0); - assert.ok(program.getBindDiagnostics("/src/bind.ts").length > 0); - assert.ok(program.getSuggestionDiagnostics("/src/suggestions.ts").length > 0); - assert.strictEqual(derivedMemberSymbol.getParent(), derivedClassSymbol); - assert.ok(checker.getTypePredicateOfSignature(predicateSignature)); - assert.equal(checker.isUnknownSymbol(unknownSymbol), true); - assert.equal(checker.isUndefinedSymbol(undefinedSymbol), true); - assert.equal(checker.isArgumentsSymbol(argumentsSymbol), true); - assert.equal(checker.isUnknownSignature(unknownSignature), true); - - const cases: ParityCase[] = [ - parityCase("API", "parseConfigFile", api.parseConfigFile, assertDeepEquivalent, "/tsconfig.json"), - parityCase("API", "parseCommandLine", api.parseCommandLine, assertDeepEquivalent, ["--strict", "--noEmit"]), - parityCase("API", "readConfigFile", api.readConfigFile, assertDeepEquivalent, "/tsconfig.json"), - parityCase("API", "parseJsonConfigFileContent", api.parseJsonConfigFileContent, assertDeepEquivalent, { compilerOptions: { strict: true } }, { configDirectory: "/" }), - parityCase("API", "parseJsonConfigFileContent", api.parseJsonConfigFileContent, assertDeepEquivalent, { extends: "./base.json" }, { configFileName: "/tsconfig.json" }), - parityCase("API", "transpileModule", api.transpileModule, assertDeepEquivalent, "export const value: number = 1;", { compilerOptions: { module: 99 } }), - parityCase("API", "transpileModuleFromFile", api.transpileModuleFromFile, assertDeepEquivalent, "/src/index.ts"), - parityCase("API", "transpileDeclaration", api.transpileDeclaration, assertDeepEquivalent, "export function declared(value: string): number { return value.length; }"), - parityCase("API", "transpileDeclarationFromFile", api.transpileDeclarationFromFile, assertDeepEquivalent, "/src/index.ts"), - parityCase("API", "updateSnapshot", api.updateSnapshot, assertSnapshotsEquivalent, { openProject: "/tsconfig.json" }), - parityCase("API", "runWithTemporaryFileUpdate", api.runWithTemporaryFileUpdate, assertDeepEquivalent, snapshot, "/src/index.ts", parityFiles["/src/index.ts"].replace("123", '"fixed"'), (temporarySnapshot: Snapshot) => { - temporaryProjects.push(temporarySnapshot.getProjects()[0].configFileName); - }), - parityCase("Snapshot", "getDefaultProjectForFile", snapshot.getDefaultProjectForFile, assertOptionalProjectsEquivalent, "/src/index.ts"), - - parityCase("Project", "getImportAdderEdits", project.getImportAdderEdits, assertDeepEquivalent, "/src/index.ts", [{ kind: "importSymbol", symbol: unimportedSymbol }]), - parityCase("Project", "getImportEditsForSymbols", project.getImportEditsForSymbols, assertDeepEquivalent, "/src/index.ts", [unimportedSymbol]), - - parityCase("LanguageService", "getImportAdderEdits", languageService.getImportAdderEdits, assertDeepEquivalent, "/src/index.ts", [{ kind: "importSymbol", symbol: unimportedSymbol }]), - parityCase("LanguageService", "getImportEditsForSymbols", languageService.getImportEditsForSymbols, assertDeepEquivalent, "/src/index.ts", [unimportedSymbol], { isValidTypeOnlyUseSite: true }), - parityCase("LanguageService", "getReferencedSymbolsForNode", languageService.getReferencedSymbolsForNode, assertDeepEquivalent, combineDeclaration.name!, combineDeclaration.name!.end), - parityCase("LanguageService", "getSignatureUsage", languageService.getSignatureUsage, assertDeepEquivalent, combineDeclaration), - parityCase("LanguageService", "getCompletionsAtPosition", languageService.getCompletionsAtPosition, assertDeepEquivalent, "/src/index.ts", completionPosition, { includeSymbol: true }), - - parityCase("Program", "getSourceFile", program.getSourceFile, assertOptionalSourceFilesEquivalent, "/src/index.ts"), - parityCase("Program", "getSourceFileNames", program.getSourceFileNames, assertDeepEquivalent), - parityCase("Program", "getSourceFileMetadata", program.getSourceFileMetadata, assertDeepEquivalent, "/src/index.ts"), - parityCase("Program", "getSourceFileMetadataByPath", program.getSourceFileMetadataByPath, assertDeepEquivalent, indexFile.path), - parityCase("Program", "isSourceFileFromExternalLibrary", program.isSourceFileFromExternalLibrary, assertDeepEquivalent, indexFile), - parityCase("Program", "isSourceFileDefaultLibrary", program.isSourceFileDefaultLibrary, assertDeepEquivalent, indexFile), - parityCase("Program", "getConfigFileNames", program.getConfigFileNames, assertDeepEquivalent), - parityCase("Program", "getConfigSourceFile", program.getConfigSourceFile, assertOptionalSourceFilesEquivalent, "/base.json"), - parityCase("Program", "getSyntacticDiagnostics", program.getSyntacticDiagnostics, assertDeepEquivalent), - parityCase("Program", "getSyntacticDiagnostics", program.getSyntacticDiagnostics, assertDeepEquivalent, "/src/syntax.ts"), - parityCase("Program", "getSyntacticDiagnostics", program.getSyntacticDiagnostics, assertDeepEquivalent, ["/src/syntax.ts", "/src/index.ts"]), - parityCase("Program", "getBindDiagnostics", program.getBindDiagnostics, assertDeepEquivalent, "/src/bind.ts"), - parityCase("Program", "getSemanticDiagnostics", program.getSemanticDiagnostics, assertDeepEquivalent), - parityCase("Program", "getSemanticDiagnostics", program.getSemanticDiagnostics, assertDeepEquivalent, "/src/index.ts"), - parityCase("Program", "getSemanticDiagnostics", program.getSemanticDiagnostics, assertDeepEquivalent, ["/src/index.ts", "/src/models.ts"]), - parityCase("Program", "getSuggestionDiagnostics", program.getSuggestionDiagnostics, assertDeepEquivalent, "/src/suggestions.ts"), - parityCase("Program", "getDeclarationDiagnostics", program.getDeclarationDiagnostics, assertDeepEquivalent, "/src/index.ts"), - parityCase("Program", "getProgramDiagnostics", program.getProgramDiagnostics, assertDeepEquivalent), - parityCase("Program", "getGlobalDiagnostics", program.getGlobalDiagnostics, assertDeepEquivalent), - parityCase("Program", "getConfigFileParsingDiagnostics", program.getConfigFileParsingDiagnostics, assertDeepEquivalent), - parityCase("Program", "emit", program.emit, assertDeepEquivalent), - parityCase("Program", "emitToString", program.emitToString, assertDeepEquivalent), - parityCase("Program", "getJavaScriptEmit", program.getJavaScriptEmit, assertDeepEquivalent, ["/src/index.ts"]), - parityCase("Program", "getDeclarationEmit", program.getDeclarationEmit, assertDeepEquivalent, ["/src/index.ts"]), - - parityCase("Checker", "getSymbolAtLocation", selectGeneratorMethod<[node: Node], Symbol | undefined>(checker.getSymbolAtLocation), assertOptionalSymbolsEquivalent, importedDerived), - parityCase("Checker", "getSymbolAtLocation", checker.getSymbolAtLocation, assertOptionalSymbolArraysEquivalent, [importedDerived, combineDeclaration.name!]), - parityCase("Checker", "getSymbolAtPosition", selectGeneratorMethod<[file: string, position: number], Symbol | undefined>(checker.getSymbolAtPosition), assertOptionalSymbolsEquivalent, "/src/index.ts", importedDerived.pos), - parityCase("Checker", "getSymbolAtPosition", checker.getSymbolAtPosition, assertOptionalSymbolArraysEquivalent, "/src/index.ts", [importedDerived.pos, combineDeclaration.name!.pos]), - parityCase("Checker", "getSymbolOfSourceFile", selectGeneratorMethod<[file: string], Symbol | undefined>(checker.getSymbolOfSourceFile), assertOptionalSymbolsEquivalent, "/src/models.ts"), - parityCase("Checker", "getSymbolOfSourceFile", checker.getSymbolOfSourceFile, assertOptionalSymbolArraysEquivalent, ["/src/index.ts", "/src/models.ts"]), - parityCase("Checker", "getTypeOfSymbol", selectGeneratorMethod<[symbol: Symbol], Type>(checker.getTypeOfSymbol), assertTypesEquivalent, derivedClassSymbol), - parityCase("Checker", "getTypeOfSymbol", checker.getTypeOfSymbol, assertTypeArraysEquivalent, [derivedClassSymbol, combineSymbol]), - parityCase("Checker", "getDeclaredTypeOfSymbol", checker.getDeclaredTypeOfSymbol, assertTypesEquivalent, interfaceSymbol), - parityCase("Checker", "getReferencesToSymbolInFile", checker.getReferencesToSymbolInFile, assertNodeHandleArraysEquivalent, "/src/index.ts", derivedSymbol), - parityCase("Checker", "getReferencedSymbolsForNode", checker.getReferencedSymbolsForNode, assertDeepEquivalent, combineDeclaration.name!, combineDeclaration.name!.end), - parityCase("Checker", "getSignatureUsage", checker.getSignatureUsage, assertDeepEquivalent, combineDeclaration), - parityCase("Checker", "getCompletionsAtPosition", checker.getCompletionsAtPosition, assertDeepEquivalent, "/src/index.ts", completionPosition, { includeSymbol: true }), - parityCase("Checker", "getTypeAtLocation", selectGeneratorMethod<[node: Node], Type>(checker.getTypeAtLocation), assertTypesEquivalent, boxDeclaration.name), - parityCase("Checker", "getTypeAtLocation", checker.getTypeAtLocation, assertTypeArraysEquivalent, [boxDeclaration.name, combineDeclaration.name!]), - parityCase("Checker", "getSignaturesOfType", checker.getSignaturesOfType, assertSignatureArraysEquivalent, checker.getTypeAtLocation(combineDeclaration.name!), SignatureKind.Call), - parityCase("Checker", "getResolvedSignature", checker.getResolvedSignature, assertOptionalSignaturesEquivalent, callExpression), - parityCase("Checker", "getTypeAtPosition", selectGeneratorMethod<[file: string, position: number], Type | undefined>(checker.getTypeAtPosition), assertOptionalTypesEquivalent, "/src/index.ts", boxDeclaration.name.pos), - parityCase("Checker", "getTypeAtPosition", checker.getTypeAtPosition, assertOptionalTypeArraysEquivalent, "/src/index.ts", [boxDeclaration.name.pos, calledDeclaration.name.pos]), - parityCase("Checker", "resolveName", checker.resolveName, assertOptionalSymbolsEquivalent, "Derived", SymbolFlags.Type | SymbolFlags.Value, importedDerived), - parityCase("Checker", "resolveName", checker.resolveName, assertOptionalSymbolsEquivalent, "Derived", SymbolFlags.Type | SymbolFlags.Value, { document: "/src/index.ts", position: importedDerived.pos }), - parityCase("Checker", "getSymbolsInScope", checker.getSymbolsInScope, assertUnorderedSymbolArraysEquivalent, combineDeclaration, SymbolFlags.Value | SymbolFlags.Type), - parityCase("Checker", "getSymbolsInScope", checker.getSymbolsInScope, assertUnorderedSymbolArraysEquivalent, { document: "/src/index.ts", position: combineDeclaration.pos }, SymbolFlags.Value), - parityCase("Checker", "getResolvedSymbol", checker.getResolvedSymbol, assertOptionalSymbolsEquivalent, importedDerived), - parityCase("Checker", "getContextualType", checker.getContextualType, assertOptionalTypesEquivalent, boxDeclaration.initializer!), - parityCase("Checker", "getBaseTypeOfLiteralType", checker.getBaseTypeOfLiteralType, assertTypesEquivalent, literalType), - parityCase("Checker", "getNonNullableType", checker.getNonNullableType, assertTypesEquivalent, interfaceType), - parityCase("Checker", "getTypeFromTypeNode", checker.getTypeFromTypeNode, assertTypesEquivalent, boxedAlias.type), - parityCase("Checker", "getWidenedType", checker.getWidenedType, assertTypesEquivalent, literalType), - parityCase("Checker", "getParameterType", checker.getParameterType, assertTypesEquivalent, signature, 0), - parityCase("Checker", "isArrayLikeType", checker.isArrayLikeType, assertDeepEquivalent, arrayType), - parityCase("Checker", "isTypeAssignableTo", checker.isTypeAssignableTo, assertDeepEquivalent, derivedType, interfaceType), - parityCase("Checker", "getShorthandAssignmentValueSymbol", checker.getShorthandAssignmentValueSymbol, assertOptionalSymbolsEquivalent, shorthand), - parityCase("Checker", "getTypeOfSymbolAtLocation", checker.getTypeOfSymbolAtLocation, assertTypesEquivalent, derivedSymbol, shorthand), - parityCase("Checker", "getAnyType", checker.getAnyType, assertTypesEquivalent), - parityCase("Checker", "getStringType", checker.getStringType, assertTypesEquivalent), - parityCase("Checker", "getNumberType", checker.getNumberType, assertTypesEquivalent), - parityCase("Checker", "getBooleanType", checker.getBooleanType, assertTypesEquivalent), - parityCase("Checker", "getVoidType", checker.getVoidType, assertTypesEquivalent), - parityCase("Checker", "getUndefinedType", checker.getUndefinedType, assertTypesEquivalent), - parityCase("Checker", "getNullType", checker.getNullType, assertTypesEquivalent), - parityCase("Checker", "getNeverType", checker.getNeverType, assertTypesEquivalent), - parityCase("Checker", "getUnknownType", checker.getUnknownType, assertTypesEquivalent), - parityCase("Checker", "getBigIntType", checker.getBigIntType, assertTypesEquivalent), - parityCase("Checker", "getESSymbolType", checker.getESSymbolType, assertTypesEquivalent), - parityCase("Checker", "getNonPrimitiveType", checker.getNonPrimitiveType, assertTypesEquivalent), - parityCase("Checker", "typeToTypeNode", checker.typeToTypeNode, assertOptionalNodesEquivalent, interfaceType, interfaceDeclaration), - parityCase("Checker", "signatureToSignatureDeclaration", checker.signatureToSignatureDeclaration, assertOptionalNodesEquivalent, signature, SyntaxKind.FunctionDeclaration, combineDeclaration), - parityCase("Checker", "typeToString", checker.typeToString, assertDeepEquivalent, interfaceType, interfaceDeclaration), - parityCase("Checker", "isContextSensitive", checker.isContextSensitive, assertDeepEquivalent, boxDeclaration.initializer!), - parityCase("Checker", "isArrayType", checker.isArrayType, assertDeepEquivalent, arrayType), - parityCase("Checker", "isTupleType", checker.isTupleType, assertDeepEquivalent, tupleType), - parityCase("Checker", "getReturnTypeOfSignature", checker.getReturnTypeOfSignature, assertTypesEquivalent, signature), - parityCase("Checker", "getRestTypeOfSignature", checker.getRestTypeOfSignature, assertOptionalTypesEquivalent, signature), - parityCase("Checker", "getTypePredicateOfSignature", checker.getTypePredicateOfSignature, assertOptionalTypePredicatesEquivalent, predicateSignature), - parityCase("Checker", "getBaseTypes", checker.getBaseTypes, assertTypeArraysEquivalent, derivedType), - parityCase("Checker", "getApparentType", checker.getApparentType, assertTypesEquivalent, interfaceType), - parityCase("Checker", "getReducedType", checker.getReducedType, assertTypesEquivalent, unionType), - parityCase("Checker", "getPropertiesOfType", checker.getPropertiesOfType, assertSymbolArraysEquivalent, interfaceType), - parityCase("Checker", "getIndexInfosOfType", checker.getIndexInfosOfType, assertIndexInfosEquivalent, interfaceType), - parityCase("Checker", "getConstraintOfTypeParameter", checker.getConstraintOfTypeParameter, assertOptionalTypesEquivalent, typeParameter), - parityCase("Checker", "getDefaultFromTypeParameter", checker.getDefaultFromTypeParameter, assertOptionalTypesEquivalent, typeParameter), - parityCase("Checker", "getBaseConstraintOfType", checker.getBaseConstraintOfType, assertOptionalTypesEquivalent, typeParameter), - parityCase("Checker", "getPropertyOfType", checker.getPropertyOfType, assertOptionalSymbolsEquivalent, interfaceType, "value"), - parityCase("Checker", "getConstantValue", checker.getConstantValue, assertDeepEquivalent, enumDeclaration.members[0]), - parityCase("Checker", "getSignatureFromDeclaration", checker.getSignatureFromDeclaration, assertOptionalSignaturesEquivalent, combineDeclaration), - parityCase("Checker", "getExportSpecifierLocalTargetSymbol", checker.getExportSpecifierLocalTargetSymbol, assertOptionalSymbolsEquivalent, exportSpecifier), - parityCase("Checker", "getAliasedSymbol", checker.getAliasedSymbol, assertSymbolsEquivalent, importedDerivedSymbol), - parityCase("Checker", "getFullyQualifiedName", checker.getFullyQualifiedName, assertDeepEquivalent, combineSymbol), - parityCase("Checker", "getImmediateAliasedSymbol", checker.getImmediateAliasedSymbol, assertOptionalSymbolsEquivalent, importedDerivedSymbol), - parityCase("Checker", "isUnknownSymbol", checker.isUnknownSymbol, assertDeepEquivalent, unknownSymbol), - parityCase("Checker", "isUndefinedSymbol", checker.isUndefinedSymbol, assertDeepEquivalent, undefinedSymbol), - parityCase("Checker", "isArgumentsSymbol", checker.isArgumentsSymbol, assertDeepEquivalent, argumentsSymbol), - parityCase("Checker", "isUnknownSignature", checker.isUnknownSignature, assertDeepEquivalent, unknownSignature), - parityCase("Checker", "getExportsOfModule", checker.getExportsOfModule, assertSymbolArraysEquivalent, moduleSymbol), - parityCase("Checker", "getMemberInModuleExports", checker.getMemberInModuleExports, assertOptionalSymbolsEquivalent, moduleSymbol, "Derived"), - parityCase("Checker", "getJsDocTagsOfSymbol", checker.getJsDocTagsOfSymbol, assertDeepEquivalent, combineSymbol), - parityCase("Checker", "getDocumentationCommentOfSymbol", checker.getDocumentationCommentOfSymbol, assertDeepEquivalent, combineSymbol), - parityCase("Checker", "getTypeArguments", checker.getTypeArguments, assertTypeArraysEquivalent, boxedType), - parityCase("Checker", "getNonMissingTypeOfSymbol", checker.getNonMissingTypeOfSymbol, assertTypesEquivalent, boxedOptSymbol), - parityCase("Checker", "isReadonlySymbol", checker.isReadonlySymbol, assertDeepEquivalent, boxedOptSymbol), - parityCase("Checker", "getTargetSymbol", checker.getTargetSymbol, assertOptionalSymbolsEquivalent, boxedOptSymbol), - - parityCase("Emitter", "printNode", emitter.printNode, assertDeepEquivalent, combineDeclaration, { preserveSourceNewlines: true }), - parityCase("SnapshotInternalAPI", "formatNodeForInsertion", snapshot.internal.formatNodeForInsertion, assertDeepEquivalent, combineDeclaration, "/src/index.ts", combineDeclaration.pos), - parityCase("NodeHandle", "resolve", nodeHandle.resolve, assertOptionalNodesEquivalent), - parityCase("NodeHandle", "resolve", nodeHandle.resolve, assertOptionalNodesEquivalent, project), - - parityCase("Symbol", "getParent", derivedMemberSymbol.getParent, assertOptionalSymbolsEquivalent), - parityCase("Symbol", "getMembers", derivedClassSymbol.getMembers, assertSymbolMapsEquivalent), - parityCase("Symbol", "getExports", moduleSymbol.getExports, assertSymbolMapsEquivalent), - parityCase("Symbol", "getExportSymbol", combineSymbol.getExportSymbol, assertSymbolsEquivalent), - parityCase("Symbol", "getJsDocTags", combineSymbol.getJsDocTags, assertDeepEquivalent, checker), - parityCase("Symbol", "getDocumentationComment", combineSymbol.getDocumentationComment, assertDeepEquivalent, checker), - - parityCase("Type", "getSymbol", interfaceType.getSymbol, assertOptionalSymbolsEquivalent), - parityCase("Type", "getProperties", interfaceType.getProperties, assertSymbolArraysEquivalent), - parityCase("Type", "getProperty", interfaceType.getProperty, assertOptionalSymbolsEquivalent, "value"), - parityCase("Type", "getApparentProperties", interfaceType.getApparentProperties, assertSymbolArraysEquivalent), - parityCase("Type", "getCallSignatures", checker.getTypeAtLocation(combineDeclaration.name!).getCallSignatures, assertSignatureArraysEquivalent), - parityCase("Type", "getConstructSignatures", derivedConstructorType.getConstructSignatures, assertSignatureArraysEquivalent), - parityCase("Type", "getNonNullableType", interfaceType.getNonNullableType, assertTypesEquivalent), - parityCase("Type", "getStringIndexType", interfaceType.getStringIndexType, assertOptionalTypesEquivalent), - parityCase("Type", "getNumberIndexType", interfaceType.getNumberIndexType, assertOptionalTypesEquivalent), - parityCase("Type", "getApparentType", interfaceType.getApparentType, assertTypesEquivalent), - parityCase("Type", "getReducedType", unionType.getReducedType, assertTypesEquivalent), - parityCase("Type", "getIndexInfos", interfaceType.getIndexInfos, assertIndexInfosEquivalent), - parityCase("Type", "getAliasSymbol", boxedType.getAliasSymbol, assertOptionalSymbolsEquivalent), - parityCase("Type", "getTarget", boxedType.getTarget, assertTypesEquivalent), - parityCase("Type", "getFreshType", literalType.getFreshType, assertOptionalTypesEquivalent), - parityCase("Type", "getRegularType", literalType.getRegularType, assertOptionalTypesEquivalent), - parityCase("Type", "getTypes", (unionType as UnionOrIntersectionType).getTypes, assertOptionalTypeArrayEquivalent), - parityCase("Type", "getTypeParameters", interfaceType.getTypeParameters, assertTypeArraysEquivalent), - parityCase("Type", "getOuterTypeParameters", interfaceType.getOuterTypeParameters, assertTypeArraysEquivalent), - parityCase("Type", "getLocalTypeParameters", interfaceType.getLocalTypeParameters, assertTypeArraysEquivalent), - parityCase("Type", "getAliasTypeArguments", boxedType.getAliasTypeArguments, assertTypeArraysEquivalent), - parityCase("Type", "getObjectType", indexedType.getObjectType, assertTypesEquivalent), - parityCase("Type", "getIndexType", indexedType.getIndexType, assertTypesEquivalent), - parityCase("Type", "getCheckType", conditionalType.getCheckType, assertTypesEquivalent), - parityCase("Type", "getExtendsType", conditionalType.getExtendsType, assertTypesEquivalent), - parityCase("Type", "getBaseType", substitutionType.getBaseType, assertTypesEquivalent), - parityCase("Type", "getConstraint", typeParameter.getConstraint, assertOptionalTypesEquivalent), - parityCase("Type", "getDefault", typeParameter.getDefault, assertOptionalTypesEquivalent), - parityCase("Type", "getTrueType", conditionalType.getTrueType, assertTypesEquivalent), - parityCase("Type", "getFalseType", conditionalType.getFalseType, assertTypesEquivalent), - parityCase("Type", "getBaseTypes", derivedType.getBaseTypes, assertOptionalTypeArrayEquivalent), - - parityCase("Signature", "getTypeParameters", signature.getTypeParameters, assertTypeArraysEquivalent), - parityCase("Signature", "getParameters", signature.getParameters, assertSymbolArraysEquivalent), - parityCase("Signature", "getThisParameter", signature.getThisParameter, assertOptionalSymbolsEquivalent), - parityCase("Signature", "getTarget", constructSignature.getTarget, assertOptionalSignaturesEquivalent), - parityCase("Signature", "getReturnType", signature.getReturnType, assertTypesEquivalent), - parityCase("Signature", "getTypeParameterAtPosition", signature.getTypeParameterAtPosition, assertTypesEquivalent, 0), - ]; - - runParityBatch(api, cases); - assert.deepEqual(temporaryProjects, ["/tsconfig.json", "/tsconfig.json"]); - - const destructiveAPI = spawnAPI(parityFiles); - const disposableSnapshot = destructiveAPI.batch(destructiveAPI.updateSnapshot.gen({ openProject: "/tsconfig.json" }))[0]; - destructiveAPI.batch(disposableSnapshot.dispose.gen()); - assert.equal(disposableSnapshot.isDisposed(), true); - assert.equal(disposableSnapshot.dispose(), undefined); - exercisedMethods.add("Snapshot.dispose"); - destructiveAPI.batch(destructiveAPI.close.gen()); - assert.equal(destructiveAPI.close(), undefined); - exercisedMethods.add("API.close"); - - assertPublicGeneratorCoverage([ - { name: "API", value: api }, - { name: "API", value: api.constructor as object, own: true }, - { name: "InternalAPI", value: api.internal }, - { name: "Snapshot", value: snapshot }, - { name: "Project", value: project }, - { name: "LanguageService", value: languageService }, - { name: "Program", value: program }, - { name: "Checker", value: checker }, - { name: "Emitter", value: emitter }, - { name: "SnapshotInternalAPI", value: snapshot.internal }, - { name: "NodeHandle", value: nodeHandle }, - { name: "Symbol", value: combineSymbol }, - { name: "Type", value: interfaceType }, - { name: "Signature", value: signature }, - ]); - } - finally { - api.close(); - } - }); -}); +import { + cast, + type Identifier, + isCallExpression, + isClassDeclaration, + isEnumDeclaration, + isExportDeclaration, + isFunctionDeclaration, + isIdentifier, + isImportDeclaration, + isInterfaceDeclaration, + isNamedExports, + isNamedImports, + isObjectLiteralExpression, + isShorthandPropertyAssignment, + isTypeAliasDeclaration, + isVariableDeclaration, + isVariableStatement, + type Node, + type SourceFile, + SyntaxKind, +} from "@typescript/typescript/unstable/ast"; +import type { + APIRequest, + APIResponse, +} from "@typescript/typescript/unstable/proto"; +import { + all, + type API, + type ConditionalType, + type IndexedAccessType, + type IndexInfo, + type InterfaceType, + type LiteralType, + type NodeHandle, + type Project, + type Signature, + SignatureKind, + type Snapshot, + type SubstitutionType, + type Symbol, + SymbolFlags, + type TimingInfo, + type Type, + TypeFlags, + type TypeParameter, + type TypePredicate, + type TypeReference, + type UnionOrIntersectionType, +} from "@typescript/typescript/unstable/sync"; +import assert from "node:assert"; +import { + describe, + test, +} from "node:test"; +import { spawnAPI } from "./api.testUtils.ts"; + +const parityFiles = { + "/base.json": JSON.stringify({ + compilerOptions: { + declaration: true, + module: "nodenext", + moduleResolution: "nodenext", + strict: true, + target: "esnext", + exactOptionalPropertyTypes: true, + }, + }), + "/tsconfig.json": JSON.stringify({ + extends: "./base.json", + include: ["src/**/*.ts"], + }), + "/src/models.ts": ` +export interface Box { + value: T; + tuple: readonly [T, ...T[]]; + [key: string]: unknown; + [index: number]: T; + readonly opt?: true; +} +export class Base { base = true; } +export class Derived extends Base { derived = 1; } +export type Boxed = Box; +export type TupleAlias = readonly [string, ...number[]]; +export type ArrayAlias = Derived[]; +export type Conditional = T extends Base ? T : never; +export type Indexed> = T["value"]; +export type Keys = keyof Box; +export type Union = Derived | string; +export enum Choice { First = 1, Second = "second" } +export class Unimported { value = "extra"; } +`, + "/src/index.ts": ` +import { Base, Box, Choice, Derived } from "./models.js"; +export { Derived as RenamedDerived } from "./models.js"; + +/** Combine a first value with the rest. @deprecated parity fixture */ +export function combine(this: Box, first: T, ...rest: T[]): readonly [T, ...T[]] { + return [first, ...rest]; +} + +export const derived = new Derived(); +export const box: Box = { value: derived, tuple: [derived], derived }; +export const shorthand = { derived }; +export const called = combine.call(box, derived, derived); +export const semanticIssue: string = 123; +export const choice = Choice.First; +export function isDerived(value: Base): value is Derived { return value instanceof Derived; } +export const badCall = derived(); +export function getArguments() { return arguments; } +import { absent } from "./absent.js"; +export const missingValue = absent; +`, + "/src/syntax.ts": `export const broken: = 1;`, + "/src/bind.ts": `let duplicate = 1; let duplicate = 2;`, + "/src/suggestions.ts": `export function suggestion() { const unused = 1; return 1; }`, +}; + +type GeneratorMethod = ((...args: Args) => Result) & { + gen(...args: Args): Generator; +}; + +type EquivalenceAssertion = (actual: Result, expected: Result, message?: string) => void; + +interface ParityCase { + readonly ownerName: string; + readonly methodName: string; + readonly runGenerator: () => Generator; + readonly assertEquivalent: (message?: string) => void; +} + +const exercisedMethods = new Set(); +const publicGeneratorExemptions = new Map([ + ["API.fromLSPConnection", "requires an existing LSP API session"], + ["InternalAPI.startCPUProfile", "writes a CPU profile and changes process-global profiling state"], + ["InternalAPI.stopCPUProfile", "requires a matching active CPU profile"], + ["InternalAPI.saveHeapProfile", "writes a potentially large heap profile to disk"], +]); +const privateGeneratorGetters = new Set([ + "API.ensureInitialized", + "Checker.getIntrinsicType", + "Checker.getWellKnownSignatures", + "Checker.getWellKnownSymbols", + "Program.fetchSourceFileMetadata", + "Symbol.fetchSymbolTable", + "Type.getNumberIndexTypeWorker", + "Type.getStringIndexTypeWorker", +]); + +function methodKey(ownerName: string, methodName: string): string { + return `${ownerName}.${methodName}`; +} + +function parityCase( + ownerName: string, + methodName: string, + method: GeneratorMethod, + assertEquivalent: EquivalenceAssertion>, + ...args: Args +): ParityCase { + let generatedResult: { readonly value: Result; } | undefined; + return { + ownerName, + methodName, + runGenerator: function* () { + generatedResult = { value: yield* method.gen(...args) }; + }, + assertEquivalent: message => { + assert.ok(generatedResult, `${message ?? methodName}: generator did not complete`); + assertEquivalent(generatedResult.value, method(...args), message); + }, + }; +} + +function selectGeneratorMethod( + method: GeneratorMethod, +): GeneratorMethod { + return method; +} + +function assertDeepEquivalent(actual: T, expected: T, message?: string): void { + assert.deepEqual(actual, expected, message); +} + +function assertOptionalEquivalent(actual: T | undefined, expected: T | undefined, assertPresentEquivalent: EquivalenceAssertion, message?: string): void { + if (actual === undefined || expected === undefined) { + assert.equal(actual, expected, message); + return; + } + assertPresentEquivalent(actual, expected, message); +} + +function assertArrayElementsEquivalent(actual: readonly T[], expected: readonly T[], assertElementEquivalent: EquivalenceAssertion, message?: string): void { + assert.equal(actual.length, expected.length, message); + for (let index = 0; index < actual.length; index++) { + assertElementEquivalent(actual[index], expected[index], `${message ?? "value"}[${index}]`); + } +} + +function assertSymbolsEquivalent(actual: Symbol, expected: Symbol, message?: string): void { + assert.strictEqual(actual, expected, message); + assert.equal(actual.id, expected.id, message); + assert.equal(actual.name, expected.name, message); + assert.equal(actual.flags, expected.flags, message); +} + +function assertOptionalSymbolsEquivalent(actual: Symbol | undefined, expected: Symbol | undefined, message?: string): void { + assertOptionalEquivalent(actual, expected, assertSymbolsEquivalent, message); +} + +function assertSymbolArraysEquivalent(actual: readonly Symbol[], expected: readonly Symbol[], message?: string): void { + assertArrayElementsEquivalent(actual, expected, assertSymbolsEquivalent, message); +} + +function assertUnorderedSymbolArraysEquivalent(actual: readonly Symbol[], expected: readonly Symbol[], message?: string): void { + const byIdentity = (left: Symbol, right: Symbol) => left.id - right.id || left.name.localeCompare(right.name); + const actualSymbols = [...actual].sort(byIdentity); + const expectedSymbols = [...expected].sort(byIdentity); + assertArrayElementsEquivalent(actualSymbols, expectedSymbols, assertSymbolsEquivalent, message); +} + +function assertOptionalSymbolArraysEquivalent(actual: readonly (Symbol | undefined)[], expected: readonly (Symbol | undefined)[], message?: string): void { + assertArrayElementsEquivalent(actual, expected, assertOptionalSymbolsEquivalent, message); +} + +function assertTypesEquivalent(actual: Type, expected: Type, message?: string): void { + assert.strictEqual(actual, expected, message); + assert.equal(actual.id, expected.id, message); + assert.equal(actual.flags, expected.flags, message); +} + +function assertOptionalTypesEquivalent(actual: Type | undefined, expected: Type | undefined, message?: string): void { + assertOptionalEquivalent(actual, expected, assertTypesEquivalent, message); +} + +function assertTypeArraysEquivalent(actual: readonly Type[], expected: readonly Type[], message?: string): void { + assertArrayElementsEquivalent(actual, expected, assertTypesEquivalent, message); +} + +function assertOptionalTypeArraysEquivalent(actual: readonly (Type | undefined)[], expected: readonly (Type | undefined)[], message?: string): void { + assertArrayElementsEquivalent(actual, expected, assertOptionalTypesEquivalent, message); +} + +function assertOptionalTypeArrayEquivalent(actual: readonly Type[] | undefined, expected: readonly Type[] | undefined, message?: string): void { + assertOptionalEquivalent(actual, expected, assertTypeArraysEquivalent, message); +} + +function assertSignaturesEquivalent(actual: Signature, expected: Signature, message?: string): void { + assert.strictEqual(actual, expected, message); + assert.equal(actual.id, expected.id, message); + assert.equal(actual.hasRestParameter, expected.hasRestParameter, message); + assert.equal(actual.isConstruct, expected.isConstruct, message); +} + +function assertOptionalSignaturesEquivalent(actual: Signature | undefined, expected: Signature | undefined, message?: string): void { + assertOptionalEquivalent(actual, expected, assertSignaturesEquivalent, message); +} + +function assertSignatureArraysEquivalent(actual: readonly Signature[], expected: readonly Signature[], message?: string): void { + assertArrayElementsEquivalent(actual, expected, assertSignaturesEquivalent, message); +} + +function assertNodeHandlesEquivalent(actual: NodeHandle, expected: NodeHandle, message?: string): void { + assert.equal(actual.index, expected.index, message); + assert.equal(actual.kind, expected.kind, message); + assert.equal(actual.path, expected.path, message); +} + +function assertNodeHandleArraysEquivalent(actual: readonly NodeHandle[], expected: readonly NodeHandle[], message?: string): void { + assertArrayElementsEquivalent(actual, expected, assertNodeHandlesEquivalent, message); +} + +function assertOptionalNodeHandlesEquivalent(actual: NodeHandle | undefined, expected: NodeHandle | undefined, message?: string): void { + assertOptionalEquivalent(actual, expected, assertNodeHandlesEquivalent, message); +} + +function assertNodesEquivalent(actual: Node, expected: Node, message?: string): void { + assert.equal(actual.kind, expected.kind, message); + assert.equal(actual.pos, expected.pos, message); + assert.equal(actual.end, expected.end, message); +} + +function assertOptionalNodesEquivalent(actual: Node | undefined, expected: Node | undefined, message?: string): void { + assertOptionalEquivalent(actual, expected, assertNodesEquivalent, message); +} + +function assertSourceFilesEquivalent(actual: SourceFile, expected: SourceFile, message?: string): void { + assertNodesEquivalent(actual, expected, message); + assert.equal(actual.fileName, expected.fileName, message); + assert.equal(actual.text, expected.text, message); +} + +function assertOptionalSourceFilesEquivalent(actual: SourceFile | undefined, expected: SourceFile | undefined, message?: string): void { + assertOptionalEquivalent(actual, expected, assertSourceFilesEquivalent, message); +} + +function assertProjectsEquivalent(actual: Project, expected: Project, message?: string): void { + assert.equal(actual.configFileName, expected.configFileName, message); + assert.deepEqual(actual.rootFiles, expected.rootFiles, message); +} + +function assertOptionalProjectsEquivalent(actual: Project | undefined, expected: Project | undefined, message?: string): void { + assertOptionalEquivalent(actual, expected, assertProjectsEquivalent, message); +} + +function assertSnapshotsEquivalent(actual: Snapshot, expected: Snapshot, message?: string): void { + const actualProjects = actual.getProjects(); + const expectedProjects = expected.getProjects(); + assertArrayElementsEquivalent(actualProjects, expectedProjects, assertProjectsEquivalent, message); +} + +function assertSymbolMapsEquivalent(actual: ReadonlyMap, expected: ReadonlyMap, message?: string): void { + assert.deepEqual([...actual.keys()], [...expected.keys()], message); + for (const key of actual.keys()) { + assertOptionalSymbolsEquivalent(actual.get(key), expected.get(key), `${message ?? "symbol map"}[${key}]`); + } +} + +function assertIndexInfosEquivalent(actual: readonly IndexInfo[], expected: readonly IndexInfo[], message?: string): void { + assertArrayElementsEquivalent(actual, expected, (actualInfo, expectedInfo, elementMessage) => { + assertTypesEquivalent(actualInfo.keyType, expectedInfo.keyType, elementMessage); + assertTypesEquivalent(actualInfo.valueType, expectedInfo.valueType, elementMessage); + assert.equal(actualInfo.isReadonly, expectedInfo.isReadonly, elementMessage); + assertOptionalNodeHandlesEquivalent(actualInfo.declaration, expectedInfo.declaration, elementMessage); + }, message); +} + +function assertOptionalTypePredicatesEquivalent(actual: TypePredicate | undefined, expected: TypePredicate | undefined, message?: string): void { + assertOptionalEquivalent(actual, expected, (actualPredicate, expectedPredicate, predicateMessage) => { + assert.equal(actualPredicate.kind, expectedPredicate.kind, predicateMessage); + assert.equal(actualPredicate.parameterName, expectedPredicate.parameterName, predicateMessage); + assert.equal(actualPredicate.parameterIndex, expectedPredicate.parameterIndex, predicateMessage); + assertOptionalTypesEquivalent(actualPredicate.type, expectedPredicate.type, predicateMessage); + }, message); +} + +function assertDeepArraysEquivalent(actual: readonly T[], expected: readonly T[], message?: string): void { + assertArrayElementsEquivalent(actual, expected, assertDeepEquivalent, message); +} + +function assertTimingInfoEquivalent(actual: TimingInfo, expected: TimingInfo, message?: string): void { + assert.equal(actual.enabled, expected.enabled, message); + assert.equal(actual.totals.requestCount, expected.totals.requestCount, message); + assert.equal(actual.totals.bytesSent, expected.totals.bytesSent, message); + assert.equal(actual.totals.bytesReceived, expected.totals.bytesReceived, message); + assert.deepEqual(actual.recentRequests.map(request => request.method), expected.recentRequests.map(request => request.method), message); +} + +function runParityBatch(api: API, cases: readonly ParityCase[]): void { + api.batch(...cases.map(parity => parity.runGenerator())); + + for (const { assertEquivalent, methodName, ownerName } of cases) { + assertEquivalent(methodKey(ownerName, methodName)); + exercisedMethods.add(methodKey(ownerName, methodName)); + } +} + +function* observeRequestBatches( + requestGenerator: Generator, + requestBatches: string[][], +): Generator { + let state = requestGenerator.next(); + while (!state.done) { + const request = state.value; + const requests = Array.isArray(request) ? request : [request as APIRequest]; + requestBatches.push(requests.map(current => current.method)); + state = requestGenerator.next(yield request); + } + return state.value; +} + +function assertPublicGeneratorCoverage(owners: readonly { readonly name: string; readonly value: object; readonly own?: boolean; }[]): void { + const missing: string[] = []; + for (const { name, own, value } of owners) { + const descriptorOwner = own ? value : Object.getPrototypeOf(value) as object; + for (const [methodName, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(descriptorOwner))) { + if (!descriptor.get) continue; + const key = methodKey(name, methodName); + const result: { gen?: unknown; } | undefined = descriptor.get.call(value); + if (typeof result?.gen !== "function") continue; + if (!exercisedMethods.has(key) && !publicGeneratorExemptions.has(key) && !privateGeneratorGetters.has(key)) { + missing.push(key); + } + } + } + assert.deepEqual(missing, [], `Uncovered public generator getters: ${missing.join(", ")}`); +} + +describe("API - generator batching", () => { + test("composes request generators with all", () => { + const api = spawnAPI(parityFiles); + const requestBatches: string[][] = []; + function* getStrictOption() { + const commandLine = yield* api.parseCommandLine.gen(["--strict"]); + const config = yield* api.readConfigFile.gen("/tsconfig.json"); + const parsed = yield* api.parseJsonConfigFileContent.gen(config.config, { configFileName: "/tsconfig.json" }); + return { strict: commandLine.options.strict, fileNames: parsed.fileNames }; + } + function* getTranspiledText() { + const config = yield* api.readConfigFile.gen("/tsconfig.json"); + const parsed = yield* api.parseJsonConfigFileContent.gen(config.config, { configFileName: "/tsconfig.json" }); + const output = yield* api.transpileModule.gen("const value: string = 'ok';", { compilerOptions: parsed.options }); + return output.outputText; + } + + try { + const [[config, outputText]] = api.batch(observeRequestBatches(all(getStrictOption(), getTranspiledText()), requestBatches)); + assert.equal(config.strict, true); + assert.deepEqual([...config.fileNames].sort(), ["/src/bind.ts", "/src/index.ts", "/src/models.ts", "/src/suggestions.ts", "/src/syntax.ts"]); + assert.match(outputText, /const value = ['"]ok['"]/); + assert.deepEqual(requestBatches, [ + ["initialize"], + ["parseCommandLine", "readConfigFile"], + ["readConfigFile", "parseJsonConfigFileContent"], + ["parseJsonConfigFileContent", "transpileModule"], + ]); + } + finally { + api.close(); + } + }); + + test("throws request errors into generators", () => { + const api = spawnAPI(); + const events: string[] = []; + function* requestWithCleanup(): Generator { + try { + yield { method: "unknown", params: null } as unknown as APIRequest; + return "unexpected"; + } + catch { + events.push("caught"); + return "recovered"; + } + finally { + events.push("finally"); + } + } + + try { + assert.deepEqual(api.batch(requestWithCleanup()), ["recovered"]); + assert.deepEqual(events, ["caught", "finally"]); + } + finally { + api.close(); + } + }); + + test("lazily caches generator-enabled methods", () => { + const api = spawnAPI(); + const prototype = Object.getPrototypeOf(api); + try { + assert.equal(Object.hasOwn(api, "parseCommandLine"), false); + assert.equal(typeof Object.getOwnPropertyDescriptor(prototype, "parseCommandLine")?.get, "function"); + + const method = api.parseCommandLine; + assert.strictEqual(api.parseCommandLine, method); + assert.equal(Object.hasOwn(api, "parseCommandLine"), true); + assert.equal(Object.keys(api).includes("parseCommandLine"), false); + assert.equal(typeof method.gen, "function"); + } + finally { + api.close(); + } + }); + + test("advances generators through multiple request rounds", () => { + const api = spawnAPI(); + try { + const [commandLine, config] = api.batch( + api.parseCommandLine.gen(["--strict"]), + api.readConfigFile.gen("/tsconfig.json"), + ); + + assert.equal(commandLine.options.strict, true); + assert.deepEqual(config.config, {}); + } + finally { + api.batch(api.close.gen()); + api.close(); + } + }); + + test("all deduplicates only initialize requests within a batch round", () => { + const api = spawnAPI(); + const requestBatches: string[][] = []; + + try { + const [[firstCommandLine, secondCommandLine, config]] = api.batch(observeRequestBatches( + all( + api.parseCommandLine.gen(["--strict"]), + api.parseCommandLine.gen(["--strict"]), + api.readConfigFile.gen("/tsconfig.json"), + ), + requestBatches, + )); + + assert.deepEqual(requestBatches, [ + ["initialize"], + ["parseCommandLine", "parseCommandLine", "readConfigFile"], + ]); + assert.equal(firstCommandLine.options.strict, true); + assert.equal(secondCommandLine.options.strict, true); + assert.deepEqual(config.config, {}); + } + finally { + api.close(); + } + }); + + test("yields source file metadata requests on cache misses", () => { + const api = spawnAPI(); + try { + using snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const program = snapshot.getProject("/tsconfig.json")!.program; + const sourceFile = program.getSourceFile("/src/index.ts")!; + const state = program.getSourceFileMetadataByPath.gen(sourceFile.path).next(); + + if (state.done) assert.fail("Expected getSourceFileMetadataByPath.gen() to yield a request"); + assert.equal(state.value.method, "getSourceFileMetadata"); + } + finally { + api.close(); + } + }); + + test("uses generators attached to sync API methods", () => { + const api = spawnAPI(); + try { + using snapshot = api.batch(api.updateSnapshot.gen({ openProject: "/tsconfig.json" }))[0]; + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + const node = cast( + cast(sourceFile.statements[0], isImportDeclaration).importClause?.namedBindings, + isNamedImports, + ).elements[0].name; + + const [defaultProject, sourceFileNames] = api.batch( + snapshot.getDefaultProjectForFile.gen("/src/index.ts"), + project.program.getSourceFileNames.gen(), + ); + assert.strictEqual(defaultProject, project); + assert.ok(sourceFileNames.includes("/src/foo.ts")); + assert.ok(sourceFileNames.includes("/src/index.ts")); + + const [symbol, type] = api.batch( + project.checker.getSymbolAtLocation.gen(node), + project.checker.getTypeAtLocation.gen(node), + ); + const syncSymbol = project.checker.getSymbolAtLocation(node); + const syncType = project.checker.getTypeAtLocation(node); + + assert.ok(symbol); + assert.ok(syncSymbol); + assert.equal(symbol.name, "foo"); + assert.strictEqual(symbol, syncSymbol); + assert.strictEqual(type, syncType); + assert.ok(symbol.flags & SymbolFlags.Alias); + assert.ok(type.flags & TypeFlags.NumberLiteral); + assert.equal(Object.hasOwn(syncType, "getProperties"), false); + assert.equal(typeof Object.getOwnPropertyDescriptor(Object.getPrototypeOf(syncType), "getProperties")?.get, "function"); + + const [parent, properties] = api.batch( + syncSymbol.getParent.gen(), + syncType.getProperties.gen(), + ); + assert.equal(parent, undefined); + assert.ok(properties.some(property => property.name === "toString")); + assert.equal(Object.hasOwn(syncType, "getProperties"), true); + assert.equal(Object.keys(syncType).includes("getProperties"), false); + } + finally { + api.batch(api.close.gen()); + api.close(); + } + }); + + test("keeps every publicly reachable generator-backed method in sync", () => { + const api = spawnAPI(parityFiles); + try { + using snapshot = api.batch(api.updateSnapshot.gen({ openProject: "/tsconfig.json" }))[0]; + const project = snapshot.getProject("/tsconfig.json")!; + const { checker, emitter, languageService, program } = project; + const indexFile = program.getSourceFile("/src/index.ts")!; + const modelsFile = program.getSourceFile("/src/models.ts")!; + + const importDeclaration = cast(indexFile.statements[0], isImportDeclaration); + const importedNames = cast(importDeclaration.importClause?.namedBindings, isNamedImports); + const importedDerived = importedNames.elements.find(element => element.name.text === "Derived")!.name; + const exportDeclaration = cast(indexFile.statements[1], isExportDeclaration); + const exportSpecifier = cast(exportDeclaration.exportClause, isNamedExports).elements[0]; + const combineDeclaration = cast(indexFile.statements[2], isFunctionDeclaration); + const derivedDeclaration = cast( + cast(indexFile.statements[3], isVariableStatement).declarationList.declarations[0], + isVariableDeclaration, + ); + const boxDeclaration = cast( + cast(indexFile.statements[4], isVariableStatement).declarationList.declarations[0], + isVariableDeclaration, + ); + const shorthandDeclaration = cast( + cast(indexFile.statements[5], isVariableStatement).declarationList.declarations[0], + isVariableDeclaration, + ); + const shorthand = cast( + cast(shorthandDeclaration.initializer, isObjectLiteralExpression).properties[0], + isShorthandPropertyAssignment, + ); + const calledDeclaration = cast( + cast(indexFile.statements[6], isVariableStatement).declarationList.declarations[0], + isVariableDeclaration, + ); + const callExpression = cast(calledDeclaration.initializer, isCallExpression); + const interfaceDeclaration = cast(modelsFile.statements[0], isInterfaceDeclaration); + const derivedClassDeclaration = cast(modelsFile.statements[2], isClassDeclaration); + const boxedAlias = cast(modelsFile.statements[3], isTypeAliasDeclaration); + const tupleAlias = cast(modelsFile.statements[4], isTypeAliasDeclaration); + const arrayAlias = cast(modelsFile.statements[5], isTypeAliasDeclaration); + const conditionalAlias = cast(modelsFile.statements[6], isTypeAliasDeclaration); + const indexedAlias = cast(modelsFile.statements[7], isTypeAliasDeclaration); + const indexAlias = cast(modelsFile.statements[8], isTypeAliasDeclaration); + const unionAlias = cast(modelsFile.statements[9], isTypeAliasDeclaration); + const enumDeclaration = cast(modelsFile.statements[10], isEnumDeclaration); + + const importedDerivedSymbol = checker.getSymbolAtLocation(importedDerived)!; + const combineSymbol = checker.getSymbolAtLocation(combineDeclaration.name!)!; + const derivedSymbol = checker.getSymbolAtLocation(cast(derivedDeclaration.name, isIdentifier))!; + const interfaceSymbol = checker.getSymbolAtLocation(interfaceDeclaration.name)!; + const derivedClassSymbol = checker.getSymbolAtLocation(derivedClassDeclaration.name!)!; + const moduleSymbol = checker.getSymbolOfSourceFile("/src/models.ts")!; + const unimportedSymbol = checker.getMemberInModuleExports(moduleSymbol, "Unimported")!; + const interfaceType = checker.getDeclaredTypeOfSymbol(interfaceSymbol) as InterfaceType; + const derivedType = checker.getDeclaredTypeOfSymbol(derivedClassSymbol) as InterfaceType; + const derivedConstructorType = checker.getTypeOfSymbol(derivedClassSymbol); + const boxedType = checker.getTypeFromTypeNode(boxedAlias.type) as TypeReference; + const boxedOptSymbol = checker.getPropertyOfType(boxedType, "opt"); + assert.ok(boxedOptSymbol); + const tupleType = checker.getTypeFromTypeNode(tupleAlias.type); + const arrayType = checker.getTypeFromTypeNode(arrayAlias.type); + const conditionalType = checker.getTypeFromTypeNode(conditionalAlias.type) as ConditionalType; + const indexedType = checker.getTypeFromTypeNode(indexedAlias.type) as IndexedAccessType; + checker.getTypeFromTypeNode(indexAlias.type); + const unionType = checker.getTypeFromTypeNode(unionAlias.type); + const typeParameter = checker.getTypeAtLocation(combineDeclaration.typeParameters![0].name) as TypeParameter; + const literalType = checker.getTypeAtLocation(enumDeclaration.members[0].name) as LiteralType; + const substitutionType = conditionalType.getTrueType() as SubstitutionType; + const signature = checker.getSignatureFromDeclaration(combineDeclaration); + const predicateDeclaration = indexFile.statements.find(statement => isFunctionDeclaration(statement) && statement.name?.text === "isDerived")!; + const predicateSignature = checker.getSignatureFromDeclaration(predicateDeclaration); + const badCallDeclaration = indexFile.statements + .filter(isVariableStatement) + .flatMap(statement => [...statement.declarationList.declarations]) + .find(declaration => isIdentifier(declaration.name) && declaration.name.text === "badCall")!; + const unknownSignature = checker.getResolvedSignature(cast(badCallDeclaration.initializer, isCallExpression)); + let argumentsIdentifier: Identifier | undefined; + let absentIdentifier: Identifier | undefined; + indexFile.forEachChild(function visit(node) { + if (isIdentifier(node)) { + if (node.text === "arguments") argumentsIdentifier = node; + if (node.text === "absent" && !absentIdentifier) absentIdentifier = node; + } + node.forEachChild(visit); + }); + assert.ok(argumentsIdentifier); + assert.ok(absentIdentifier); + const absentAlias = checker.getSymbolAtLocation(absentIdentifier); + assert.ok(absentAlias); + const unknownSymbol = checker.getAliasedSymbol(absentAlias); + const undefinedSymbol = checker.resolveName("undefined", SymbolFlags.Value, absentIdentifier); + const argumentsSymbol = checker.getResolvedSymbol(argumentsIdentifier); + assert.ok(unknownSymbol); + assert.ok(undefinedSymbol); + assert.ok(argumentsSymbol); + const constructSignature = derivedConstructorType.getConstructSignatures()[0]; + const derivedMemberSymbol = derivedType.getProperty("derived")!; + const nodeHandle = combineSymbol.valueDeclaration ?? combineSymbol.declarations[0]; + const completionPosition = parityFiles["/src/index.ts"].indexOf("semanticIssue"); + const temporaryProjects: string[] = []; + + const orderedArguments = Array.from({ length: 128 }, (_, index) => ["--strict", `--outDir=out-${index}`] as const); + const orderedGenerated = api.batch(...orderedArguments.map(args => api.parseCommandLine.gen(args))); + const orderedSync = orderedArguments.map(args => api.parseCommandLine(args)); + assertDeepArraysEquivalent(orderedGenerated, orderedSync, "large parseCommandLine batch"); + exercisedMethods.add("API.parseCommandLine"); + + const timingGeneratorAPI = spawnAPI(parityFiles, { collectTiming: true }); + const timingSyncAPI = spawnAPI(parityFiles, { collectTiming: true }); + timingGeneratorAPI.parseCommandLine(["--strict"]); + timingSyncAPI.parseCommandLine(["--strict"]); + assertTimingInfoEquivalent( + timingGeneratorAPI.batch(timingGeneratorAPI.getTimingInfo.gen())[0], + timingSyncAPI.getTimingInfo(), + "API.getTimingInfo", + ); + timingGeneratorAPI.batch(timingGeneratorAPI.resetTimingInfo.gen()); + timingSyncAPI.resetTimingInfo(); + assertTimingInfoEquivalent(timingGeneratorAPI.getTimingInfo(), timingSyncAPI.getTimingInfo(), "API.resetTimingInfo"); + exercisedMethods.add("API.getTimingInfo"); + exercisedMethods.add("API.resetTimingInfo"); + timingGeneratorAPI.close(); + timingSyncAPI.close(); + + assert.ok(project.getImportAdderEdits("/src/index.ts", [{ kind: "importSymbol", symbol: unimportedSymbol }]).length > 0); + assert.ok(program.getSyntacticDiagnostics("/src/syntax.ts").length > 0); + assert.ok(program.getBindDiagnostics("/src/bind.ts").length > 0); + assert.ok(program.getSuggestionDiagnostics("/src/suggestions.ts").length > 0); + assert.strictEqual(derivedMemberSymbol.getParent(), derivedClassSymbol); + assert.ok(checker.getTypePredicateOfSignature(predicateSignature)); + assert.equal(checker.isUnknownSymbol(unknownSymbol), true); + assert.equal(checker.isUndefinedSymbol(undefinedSymbol), true); + assert.equal(checker.isArgumentsSymbol(argumentsSymbol), true); + assert.equal(checker.isUnknownSignature(unknownSignature), true); + + const cases: ParityCase[] = [ + parityCase("API", "parseConfigFile", api.parseConfigFile, assertDeepEquivalent, "/tsconfig.json"), + parityCase("API", "parseCommandLine", api.parseCommandLine, assertDeepEquivalent, ["--strict", "--noEmit"]), + parityCase("API", "readConfigFile", api.readConfigFile, assertDeepEquivalent, "/tsconfig.json"), + parityCase("API", "parseJsonConfigFileContent", api.parseJsonConfigFileContent, assertDeepEquivalent, { compilerOptions: { strict: true } }, { configDirectory: "/" }), + parityCase("API", "parseJsonConfigFileContent", api.parseJsonConfigFileContent, assertDeepEquivalent, { extends: "./base.json" }, { configFileName: "/tsconfig.json" }), + parityCase("API", "transpileModule", api.transpileModule, assertDeepEquivalent, "export const value: number = 1;", { compilerOptions: { module: 99 } }), + parityCase("API", "transpileModuleFromFile", api.transpileModuleFromFile, assertDeepEquivalent, "/src/index.ts"), + parityCase("API", "transpileDeclaration", api.transpileDeclaration, assertDeepEquivalent, "export function declared(value: string): number { return value.length; }"), + parityCase("API", "transpileDeclarationFromFile", api.transpileDeclarationFromFile, assertDeepEquivalent, "/src/index.ts"), + parityCase("API", "updateSnapshot", api.updateSnapshot, assertSnapshotsEquivalent, { openProject: "/tsconfig.json" }), + parityCase("API", "runWithTemporaryFileUpdate", api.runWithTemporaryFileUpdate, assertDeepEquivalent, snapshot, "/src/index.ts", parityFiles["/src/index.ts"].replace("123", '"fixed"'), (temporarySnapshot: Snapshot) => { + temporaryProjects.push(temporarySnapshot.getProjects()[0].configFileName); + }), + parityCase("Snapshot", "getDefaultProjectForFile", snapshot.getDefaultProjectForFile, assertOptionalProjectsEquivalent, "/src/index.ts"), + + parityCase("Project", "getImportAdderEdits", project.getImportAdderEdits, assertDeepEquivalent, "/src/index.ts", [{ kind: "importSymbol", symbol: unimportedSymbol }]), + parityCase("Project", "getImportEditsForSymbols", project.getImportEditsForSymbols, assertDeepEquivalent, "/src/index.ts", [unimportedSymbol]), + + parityCase("LanguageService", "getImportAdderEdits", languageService.getImportAdderEdits, assertDeepEquivalent, "/src/index.ts", [{ kind: "importSymbol", symbol: unimportedSymbol }]), + parityCase("LanguageService", "getImportEditsForSymbols", languageService.getImportEditsForSymbols, assertDeepEquivalent, "/src/index.ts", [unimportedSymbol], { isValidTypeOnlyUseSite: true }), + parityCase("LanguageService", "getReferencedSymbolsForNode", languageService.getReferencedSymbolsForNode, assertDeepEquivalent, combineDeclaration.name!, combineDeclaration.name!.end), + parityCase("LanguageService", "getSignatureUsage", languageService.getSignatureUsage, assertDeepEquivalent, combineDeclaration), + parityCase("LanguageService", "getCompletionsAtPosition", languageService.getCompletionsAtPosition, assertDeepEquivalent, "/src/index.ts", completionPosition, { includeSymbol: true }), + + parityCase("Program", "getSourceFile", program.getSourceFile, assertOptionalSourceFilesEquivalent, "/src/index.ts"), + parityCase("Program", "getSourceFileNames", program.getSourceFileNames, assertDeepEquivalent), + parityCase("Program", "getSourceFileMetadata", program.getSourceFileMetadata, assertDeepEquivalent, "/src/index.ts"), + parityCase("Program", "getSourceFileMetadataByPath", program.getSourceFileMetadataByPath, assertDeepEquivalent, indexFile.path), + parityCase("Program", "isSourceFileFromExternalLibrary", program.isSourceFileFromExternalLibrary, assertDeepEquivalent, indexFile), + parityCase("Program", "isSourceFileDefaultLibrary", program.isSourceFileDefaultLibrary, assertDeepEquivalent, indexFile), + parityCase("Program", "getConfigFileNames", program.getConfigFileNames, assertDeepEquivalent), + parityCase("Program", "getConfigSourceFile", program.getConfigSourceFile, assertOptionalSourceFilesEquivalent, "/base.json"), + parityCase("Program", "getSyntacticDiagnostics", program.getSyntacticDiagnostics, assertDeepEquivalent), + parityCase("Program", "getSyntacticDiagnostics", program.getSyntacticDiagnostics, assertDeepEquivalent, "/src/syntax.ts"), + parityCase("Program", "getSyntacticDiagnostics", program.getSyntacticDiagnostics, assertDeepEquivalent, ["/src/syntax.ts", "/src/index.ts"]), + parityCase("Program", "getBindDiagnostics", program.getBindDiagnostics, assertDeepEquivalent, "/src/bind.ts"), + parityCase("Program", "getSemanticDiagnostics", program.getSemanticDiagnostics, assertDeepEquivalent), + parityCase("Program", "getSemanticDiagnostics", program.getSemanticDiagnostics, assertDeepEquivalent, "/src/index.ts"), + parityCase("Program", "getSemanticDiagnostics", program.getSemanticDiagnostics, assertDeepEquivalent, ["/src/index.ts", "/src/models.ts"]), + parityCase("Program", "getSuggestionDiagnostics", program.getSuggestionDiagnostics, assertDeepEquivalent, "/src/suggestions.ts"), + parityCase("Program", "getDeclarationDiagnostics", program.getDeclarationDiagnostics, assertDeepEquivalent, "/src/index.ts"), + parityCase("Program", "getProgramDiagnostics", program.getProgramDiagnostics, assertDeepEquivalent), + parityCase("Program", "getGlobalDiagnostics", program.getGlobalDiagnostics, assertDeepEquivalent), + parityCase("Program", "getConfigFileParsingDiagnostics", program.getConfigFileParsingDiagnostics, assertDeepEquivalent), + parityCase("Program", "emit", program.emit, assertDeepEquivalent), + parityCase("Program", "emitToString", program.emitToString, assertDeepEquivalent), + parityCase("Program", "getJavaScriptEmit", program.getJavaScriptEmit, assertDeepEquivalent, ["/src/index.ts"]), + parityCase("Program", "getDeclarationEmit", program.getDeclarationEmit, assertDeepEquivalent, ["/src/index.ts"]), + + parityCase("Checker", "getSymbolAtLocation", selectGeneratorMethod<[node: Node], Symbol | undefined>(checker.getSymbolAtLocation), assertOptionalSymbolsEquivalent, importedDerived), + parityCase("Checker", "getSymbolAtLocation", checker.getSymbolAtLocation, assertOptionalSymbolArraysEquivalent, [importedDerived, combineDeclaration.name!]), + parityCase("Checker", "getSymbolAtPosition", selectGeneratorMethod<[file: string, position: number], Symbol | undefined>(checker.getSymbolAtPosition), assertOptionalSymbolsEquivalent, "/src/index.ts", importedDerived.pos), + parityCase("Checker", "getSymbolAtPosition", checker.getSymbolAtPosition, assertOptionalSymbolArraysEquivalent, "/src/index.ts", [importedDerived.pos, combineDeclaration.name!.pos]), + parityCase("Checker", "getSymbolOfSourceFile", selectGeneratorMethod<[file: string], Symbol | undefined>(checker.getSymbolOfSourceFile), assertOptionalSymbolsEquivalent, "/src/models.ts"), + parityCase("Checker", "getSymbolOfSourceFile", checker.getSymbolOfSourceFile, assertOptionalSymbolArraysEquivalent, ["/src/index.ts", "/src/models.ts"]), + parityCase("Checker", "getTypeOfSymbol", selectGeneratorMethod<[symbol: Symbol], Type>(checker.getTypeOfSymbol), assertTypesEquivalent, derivedClassSymbol), + parityCase("Checker", "getTypeOfSymbol", checker.getTypeOfSymbol, assertTypeArraysEquivalent, [derivedClassSymbol, combineSymbol]), + parityCase("Checker", "getDeclaredTypeOfSymbol", checker.getDeclaredTypeOfSymbol, assertTypesEquivalent, interfaceSymbol), + parityCase("Checker", "getReferencesToSymbolInFile", checker.getReferencesToSymbolInFile, assertNodeHandleArraysEquivalent, "/src/index.ts", derivedSymbol), + parityCase("Checker", "getReferencedSymbolsForNode", checker.getReferencedSymbolsForNode, assertDeepEquivalent, combineDeclaration.name!, combineDeclaration.name!.end), + parityCase("Checker", "getSignatureUsage", checker.getSignatureUsage, assertDeepEquivalent, combineDeclaration), + parityCase("Checker", "getCompletionsAtPosition", checker.getCompletionsAtPosition, assertDeepEquivalent, "/src/index.ts", completionPosition, { includeSymbol: true }), + parityCase("Checker", "getTypeAtLocation", selectGeneratorMethod<[node: Node], Type>(checker.getTypeAtLocation), assertTypesEquivalent, boxDeclaration.name), + parityCase("Checker", "getTypeAtLocation", checker.getTypeAtLocation, assertTypeArraysEquivalent, [boxDeclaration.name, combineDeclaration.name!]), + parityCase("Checker", "getSignaturesOfType", checker.getSignaturesOfType, assertSignatureArraysEquivalent, checker.getTypeAtLocation(combineDeclaration.name!), SignatureKind.Call), + parityCase("Checker", "getResolvedSignature", checker.getResolvedSignature, assertOptionalSignaturesEquivalent, callExpression), + parityCase("Checker", "getTypeAtPosition", selectGeneratorMethod<[file: string, position: number], Type | undefined>(checker.getTypeAtPosition), assertOptionalTypesEquivalent, "/src/index.ts", boxDeclaration.name.pos), + parityCase("Checker", "getTypeAtPosition", checker.getTypeAtPosition, assertOptionalTypeArraysEquivalent, "/src/index.ts", [boxDeclaration.name.pos, calledDeclaration.name.pos]), + parityCase("Checker", "resolveName", checker.resolveName, assertOptionalSymbolsEquivalent, "Derived", SymbolFlags.Type | SymbolFlags.Value, importedDerived), + parityCase("Checker", "resolveName", checker.resolveName, assertOptionalSymbolsEquivalent, "Derived", SymbolFlags.Type | SymbolFlags.Value, { document: "/src/index.ts", position: importedDerived.pos }), + parityCase("Checker", "getSymbolsInScope", checker.getSymbolsInScope, assertUnorderedSymbolArraysEquivalent, combineDeclaration, SymbolFlags.Value | SymbolFlags.Type), + parityCase("Checker", "getSymbolsInScope", checker.getSymbolsInScope, assertUnorderedSymbolArraysEquivalent, { document: "/src/index.ts", position: combineDeclaration.pos }, SymbolFlags.Value), + parityCase("Checker", "getResolvedSymbol", checker.getResolvedSymbol, assertOptionalSymbolsEquivalent, importedDerived), + parityCase("Checker", "getContextualType", checker.getContextualType, assertOptionalTypesEquivalent, boxDeclaration.initializer!), + parityCase("Checker", "getBaseTypeOfLiteralType", checker.getBaseTypeOfLiteralType, assertTypesEquivalent, literalType), + parityCase("Checker", "getNonNullableType", checker.getNonNullableType, assertTypesEquivalent, interfaceType), + parityCase("Checker", "getTypeFromTypeNode", checker.getTypeFromTypeNode, assertTypesEquivalent, boxedAlias.type), + parityCase("Checker", "getWidenedType", checker.getWidenedType, assertTypesEquivalent, literalType), + parityCase("Checker", "getParameterType", checker.getParameterType, assertTypesEquivalent, signature, 0), + parityCase("Checker", "isArrayLikeType", checker.isArrayLikeType, assertDeepEquivalent, arrayType), + parityCase("Checker", "isTypeAssignableTo", checker.isTypeAssignableTo, assertDeepEquivalent, derivedType, interfaceType), + parityCase("Checker", "getShorthandAssignmentValueSymbol", checker.getShorthandAssignmentValueSymbol, assertOptionalSymbolsEquivalent, shorthand), + parityCase("Checker", "getTypeOfSymbolAtLocation", checker.getTypeOfSymbolAtLocation, assertTypesEquivalent, derivedSymbol, shorthand), + parityCase("Checker", "getAnyType", checker.getAnyType, assertTypesEquivalent), + parityCase("Checker", "getStringType", checker.getStringType, assertTypesEquivalent), + parityCase("Checker", "getNumberType", checker.getNumberType, assertTypesEquivalent), + parityCase("Checker", "getBooleanType", checker.getBooleanType, assertTypesEquivalent), + parityCase("Checker", "getVoidType", checker.getVoidType, assertTypesEquivalent), + parityCase("Checker", "getUndefinedType", checker.getUndefinedType, assertTypesEquivalent), + parityCase("Checker", "getNullType", checker.getNullType, assertTypesEquivalent), + parityCase("Checker", "getNeverType", checker.getNeverType, assertTypesEquivalent), + parityCase("Checker", "getUnknownType", checker.getUnknownType, assertTypesEquivalent), + parityCase("Checker", "getBigIntType", checker.getBigIntType, assertTypesEquivalent), + parityCase("Checker", "getESSymbolType", checker.getESSymbolType, assertTypesEquivalent), + parityCase("Checker", "getNonPrimitiveType", checker.getNonPrimitiveType, assertTypesEquivalent), + parityCase("Checker", "typeToTypeNode", checker.typeToTypeNode, assertOptionalNodesEquivalent, interfaceType, interfaceDeclaration), + parityCase("Checker", "signatureToSignatureDeclaration", checker.signatureToSignatureDeclaration, assertOptionalNodesEquivalent, signature, SyntaxKind.FunctionDeclaration, combineDeclaration), + parityCase("Checker", "typeToString", checker.typeToString, assertDeepEquivalent, interfaceType, interfaceDeclaration), + parityCase("Checker", "isContextSensitive", checker.isContextSensitive, assertDeepEquivalent, boxDeclaration.initializer!), + parityCase("Checker", "isArrayType", checker.isArrayType, assertDeepEquivalent, arrayType), + parityCase("Checker", "isTupleType", checker.isTupleType, assertDeepEquivalent, tupleType), + parityCase("Checker", "getReturnTypeOfSignature", checker.getReturnTypeOfSignature, assertTypesEquivalent, signature), + parityCase("Checker", "getRestTypeOfSignature", checker.getRestTypeOfSignature, assertOptionalTypesEquivalent, signature), + parityCase("Checker", "getTypePredicateOfSignature", checker.getTypePredicateOfSignature, assertOptionalTypePredicatesEquivalent, predicateSignature), + parityCase("Checker", "getBaseTypes", checker.getBaseTypes, assertTypeArraysEquivalent, derivedType), + parityCase("Checker", "getApparentType", checker.getApparentType, assertTypesEquivalent, interfaceType), + parityCase("Checker", "getReducedType", checker.getReducedType, assertTypesEquivalent, unionType), + parityCase("Checker", "getPropertiesOfType", checker.getPropertiesOfType, assertSymbolArraysEquivalent, interfaceType), + parityCase("Checker", "getIndexInfosOfType", checker.getIndexInfosOfType, assertIndexInfosEquivalent, interfaceType), + parityCase("Checker", "getConstraintOfTypeParameter", checker.getConstraintOfTypeParameter, assertOptionalTypesEquivalent, typeParameter), + parityCase("Checker", "getDefaultFromTypeParameter", checker.getDefaultFromTypeParameter, assertOptionalTypesEquivalent, typeParameter), + parityCase("Checker", "getBaseConstraintOfType", checker.getBaseConstraintOfType, assertOptionalTypesEquivalent, typeParameter), + parityCase("Checker", "getPropertyOfType", checker.getPropertyOfType, assertOptionalSymbolsEquivalent, interfaceType, "value"), + parityCase("Checker", "getConstantValue", checker.getConstantValue, assertDeepEquivalent, enumDeclaration.members[0]), + parityCase("Checker", "getSignatureFromDeclaration", checker.getSignatureFromDeclaration, assertOptionalSignaturesEquivalent, combineDeclaration), + parityCase("Checker", "getExportSpecifierLocalTargetSymbol", checker.getExportSpecifierLocalTargetSymbol, assertOptionalSymbolsEquivalent, exportSpecifier), + parityCase("Checker", "getAliasedSymbol", checker.getAliasedSymbol, assertSymbolsEquivalent, importedDerivedSymbol), + parityCase("Checker", "getFullyQualifiedName", checker.getFullyQualifiedName, assertDeepEquivalent, combineSymbol), + parityCase("Checker", "getImmediateAliasedSymbol", checker.getImmediateAliasedSymbol, assertOptionalSymbolsEquivalent, importedDerivedSymbol), + parityCase("Checker", "isUnknownSymbol", checker.isUnknownSymbol, assertDeepEquivalent, unknownSymbol), + parityCase("Checker", "isUndefinedSymbol", checker.isUndefinedSymbol, assertDeepEquivalent, undefinedSymbol), + parityCase("Checker", "isArgumentsSymbol", checker.isArgumentsSymbol, assertDeepEquivalent, argumentsSymbol), + parityCase("Checker", "isUnknownSignature", checker.isUnknownSignature, assertDeepEquivalent, unknownSignature), + parityCase("Checker", "getExportsOfModule", checker.getExportsOfModule, assertSymbolArraysEquivalent, moduleSymbol), + parityCase("Checker", "getMemberInModuleExports", checker.getMemberInModuleExports, assertOptionalSymbolsEquivalent, moduleSymbol, "Derived"), + parityCase("Checker", "getJsDocTagsOfSymbol", checker.getJsDocTagsOfSymbol, assertDeepEquivalent, combineSymbol), + parityCase("Checker", "getDocumentationCommentOfSymbol", checker.getDocumentationCommentOfSymbol, assertDeepEquivalent, combineSymbol), + parityCase("Checker", "getTypeArguments", checker.getTypeArguments, assertTypeArraysEquivalent, boxedType), + parityCase("Checker", "getNonMissingTypeOfSymbol", checker.getNonMissingTypeOfSymbol, assertTypesEquivalent, boxedOptSymbol), + parityCase("Checker", "isReadonlySymbol", checker.isReadonlySymbol, assertDeepEquivalent, boxedOptSymbol), + parityCase("Checker", "getTargetSymbol", checker.getTargetSymbol, assertOptionalSymbolsEquivalent, boxedOptSymbol), + + parityCase("Emitter", "printNode", emitter.printNode, assertDeepEquivalent, combineDeclaration, { preserveSourceNewlines: true }), + parityCase("SnapshotInternalAPI", "formatNodeForInsertion", snapshot.internal.formatNodeForInsertion, assertDeepEquivalent, combineDeclaration, "/src/index.ts", combineDeclaration.pos), + parityCase("NodeHandle", "resolve", nodeHandle.resolve, assertOptionalNodesEquivalent), + parityCase("NodeHandle", "resolve", nodeHandle.resolve, assertOptionalNodesEquivalent, project), + + parityCase("Symbol", "getParent", derivedMemberSymbol.getParent, assertOptionalSymbolsEquivalent), + parityCase("Symbol", "getMembers", derivedClassSymbol.getMembers, assertSymbolMapsEquivalent), + parityCase("Symbol", "getExports", moduleSymbol.getExports, assertSymbolMapsEquivalent), + parityCase("Symbol", "getExportSymbol", combineSymbol.getExportSymbol, assertSymbolsEquivalent), + parityCase("Symbol", "getJsDocTags", combineSymbol.getJsDocTags, assertDeepEquivalent, checker), + parityCase("Symbol", "getDocumentationComment", combineSymbol.getDocumentationComment, assertDeepEquivalent, checker), + + parityCase("Type", "getSymbol", interfaceType.getSymbol, assertOptionalSymbolsEquivalent), + parityCase("Type", "getProperties", interfaceType.getProperties, assertSymbolArraysEquivalent), + parityCase("Type", "getProperty", interfaceType.getProperty, assertOptionalSymbolsEquivalent, "value"), + parityCase("Type", "getApparentProperties", interfaceType.getApparentProperties, assertSymbolArraysEquivalent), + parityCase("Type", "getCallSignatures", checker.getTypeAtLocation(combineDeclaration.name!).getCallSignatures, assertSignatureArraysEquivalent), + parityCase("Type", "getConstructSignatures", derivedConstructorType.getConstructSignatures, assertSignatureArraysEquivalent), + parityCase("Type", "getNonNullableType", interfaceType.getNonNullableType, assertTypesEquivalent), + parityCase("Type", "getStringIndexType", interfaceType.getStringIndexType, assertOptionalTypesEquivalent), + parityCase("Type", "getNumberIndexType", interfaceType.getNumberIndexType, assertOptionalTypesEquivalent), + parityCase("Type", "getApparentType", interfaceType.getApparentType, assertTypesEquivalent), + parityCase("Type", "getReducedType", unionType.getReducedType, assertTypesEquivalent), + parityCase("Type", "getIndexInfos", interfaceType.getIndexInfos, assertIndexInfosEquivalent), + parityCase("Type", "getAliasSymbol", boxedType.getAliasSymbol, assertOptionalSymbolsEquivalent), + parityCase("Type", "getTarget", boxedType.getTarget, assertTypesEquivalent), + parityCase("Type", "getFreshType", literalType.getFreshType, assertOptionalTypesEquivalent), + parityCase("Type", "getRegularType", literalType.getRegularType, assertOptionalTypesEquivalent), + parityCase("Type", "getTypes", (unionType as UnionOrIntersectionType).getTypes, assertOptionalTypeArrayEquivalent), + parityCase("Type", "getTypeParameters", interfaceType.getTypeParameters, assertTypeArraysEquivalent), + parityCase("Type", "getOuterTypeParameters", interfaceType.getOuterTypeParameters, assertTypeArraysEquivalent), + parityCase("Type", "getLocalTypeParameters", interfaceType.getLocalTypeParameters, assertTypeArraysEquivalent), + parityCase("Type", "getAliasTypeArguments", boxedType.getAliasTypeArguments, assertTypeArraysEquivalent), + parityCase("Type", "getObjectType", indexedType.getObjectType, assertTypesEquivalent), + parityCase("Type", "getIndexType", indexedType.getIndexType, assertTypesEquivalent), + parityCase("Type", "getCheckType", conditionalType.getCheckType, assertTypesEquivalent), + parityCase("Type", "getExtendsType", conditionalType.getExtendsType, assertTypesEquivalent), + parityCase("Type", "getBaseType", substitutionType.getBaseType, assertTypesEquivalent), + parityCase("Type", "getConstraint", typeParameter.getConstraint, assertOptionalTypesEquivalent), + parityCase("Type", "getDefault", typeParameter.getDefault, assertOptionalTypesEquivalent), + parityCase("Type", "getTrueType", conditionalType.getTrueType, assertTypesEquivalent), + parityCase("Type", "getFalseType", conditionalType.getFalseType, assertTypesEquivalent), + parityCase("Type", "getBaseTypes", derivedType.getBaseTypes, assertOptionalTypeArrayEquivalent), + + parityCase("Signature", "getTypeParameters", signature.getTypeParameters, assertTypeArraysEquivalent), + parityCase("Signature", "getParameters", signature.getParameters, assertSymbolArraysEquivalent), + parityCase("Signature", "getThisParameter", signature.getThisParameter, assertOptionalSymbolsEquivalent), + parityCase("Signature", "getTarget", constructSignature.getTarget, assertOptionalSignaturesEquivalent), + parityCase("Signature", "getReturnType", signature.getReturnType, assertTypesEquivalent), + parityCase("Signature", "getTypeParameterAtPosition", signature.getTypeParameterAtPosition, assertTypesEquivalent, 0), + ]; + + runParityBatch(api, cases); + assert.deepEqual(temporaryProjects, ["/tsconfig.json", "/tsconfig.json"]); + + const destructiveAPI = spawnAPI(parityFiles); + const disposableSnapshot = destructiveAPI.batch(destructiveAPI.updateSnapshot.gen({ openProject: "/tsconfig.json" }))[0]; + destructiveAPI.batch(disposableSnapshot.dispose.gen()); + assert.equal(disposableSnapshot.isDisposed(), true); + assert.equal(disposableSnapshot.dispose(), undefined); + exercisedMethods.add("Snapshot.dispose"); + destructiveAPI.batch(destructiveAPI.close.gen()); + assert.equal(destructiveAPI.close(), undefined); + exercisedMethods.add("API.close"); + + assertPublicGeneratorCoverage([ + { name: "API", value: api }, + { name: "API", value: api.constructor as object, own: true }, + { name: "InternalAPI", value: api.internal }, + { name: "Snapshot", value: snapshot }, + { name: "Project", value: project }, + { name: "LanguageService", value: languageService }, + { name: "Program", value: program }, + { name: "Checker", value: checker }, + { name: "Emitter", value: emitter }, + { name: "SnapshotInternalAPI", value: snapshot.internal }, + { name: "NodeHandle", value: nodeHandle }, + { name: "Symbol", value: combineSymbol }, + { name: "Type", value: interfaceType }, + { name: "Signature", value: signature }, + ]); + } + finally { + api.close(); + } + }); +}); diff --git a/packages/typescript/test/sync/api.bench.ts b/packages/typescript/test/sync/api.bench.ts index e3c40be190f28..0b57aed549823 100644 --- a/packages/typescript/test/sync/api.bench.ts +++ b/packages/typescript/test/sync/api.bench.ts @@ -1,269 +1,269 @@ -// -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// !!! THIS FILE IS AUTO-GENERATED - DO NOT EDIT !!! -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// -// Source: test/async/api.bench.ts -// Regenerate: npm run generate (from packages/typescript) -// -import { - type Node, - type SourceFile, - SyntaxKind, -} from "@typescript/typescript/unstable/ast"; -import { - API, - type Project, - type Snapshot, -} from "@typescript/typescript/unstable/sync"; -import { writeFileSync } from "node:fs"; -import inspector from "node:inspector"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; -import { parseArgs } from "node:util"; -import { Bench } from "tinybench"; -import ts from "typescript"; -import { RemoteSourceFile } from "../../src/api/node/node.ts"; - -const isMain = process.argv[1] === fileURLToPath(import.meta.url); -if (isMain) { - const { values } = parseArgs({ - options: { - filter: { type: "string" }, - singleIteration: { type: "boolean", default: false }, - cpuprofile: { type: "boolean", default: false }, - }, - }); - runBenchmarks(values); -} - -export function runBenchmarks(options?: { filter?: string; singleIteration?: boolean; cpuprofile?: boolean; }) { - const { filter, singleIteration, cpuprofile } = options ?? {}; - const repoRoot = fileURLToPath(new URL("../../../../", import.meta.url).toString()); - - const bench = new Bench({ - name: "Sync API", - teardown, - // Reduce iterations from the default 64 to 10. Slow tasks - // are dominated by the iteration minimum, not the time limit. - // 10 iterations still gives stable medians while cutting total - // bench time by ~5x. - iterations: 10, - warmupIterations: 4, - ...singleIteration ? { - iterations: 1, - warmup: false, - time: 0, - } : undefined, - }); - - let api: API; - let snapshot: Snapshot; - let project: Project; - let tsProgram: ts.Program; - let file: SourceFile; - let tsFile: ts.SourceFile; - - const programIdentifierCount = (() => { - spawnAPI(); - loadSnapshot(); - getProgramTS(); - let count = 0; - file!.forEachChild(function visit(node) { - if (node.kind === SyntaxKind.Identifier) { - count++; - } - node.forEachChild(visit); - }); - teardown(); - return count; - })(); - - // Tinybench's `isFnAsyncResource` probes each task function by *calling* - // it once during `.add()` to detect whether it returns a Promise. - // In sync mode every task function is a plain (non-async) function, so - // the probe actually executes the benchmarked code (spawning processes, - // creating TS programs, etc.) wasting 30+ seconds. Passing an explicit - // `async` flag on every task skips the probe entirely. - const isAsync = false; - - bench - .add("spawn API", () => { - spawnAPI(); - }, { async: isAsync }) - .add("load snapshot", () => { - loadSnapshot(); - }, { async: isAsync, beforeAll: spawnAPI }) - .add("TS - load project", () => { - tsCreateProgram(); - }, { async: isAsync }) - .add("transfer debug.ts", () => { - getDebugTS(); - }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot), beforeEach: clearSourceFileCache }) - .add("transfer program.ts", () => { - getProgramTS(); - }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot), beforeEach: clearSourceFileCache }) - .add("transfer checker.ts", () => { - getCheckerTS(); - }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot), beforeEach: clearSourceFileCache }) - .add("materialize program.ts", () => { - const { view, _decoder } = file as unknown as RemoteSourceFile; - new RemoteSourceFile(new Uint8Array(view.buffer, view.byteOffset, view.byteLength), _decoder).forEachChild(function visit(node) { - node.forEachChild(visit); - }); - }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, getProgramTS) }) - .add("materialize checker.ts", () => { - const { view, _decoder } = file as unknown as RemoteSourceFile; - new RemoteSourceFile(new Uint8Array(view.buffer, view.byteOffset, view.byteLength), _decoder).forEachChild(function visit(node) { - node.forEachChild(visit); - }); - }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, getCheckerTS) }) - .add("getSymbolAtPosition - one location", () => { - project.checker.getSymbolAtPosition("program.ts", 8895); - }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, createChecker) }) - .add("TS - getSymbolAtPosition - one location", () => { - tsProgram.getTypeChecker().getSymbolAtLocation( - // @ts-ignore internal API - ts.getTokenAtPosition(tsFile, 8895), - ); - }, { async: isAsync, beforeAll: all(tsCreateProgram, tsCreateChecker, tsGetProgramTS) }) - .add(`getSymbolAtPosition - ${programIdentifierCount} identifiers`, () => { - for (const node of collectIdentifiers(file)) { - project.checker.getSymbolAtPosition("program.ts", node.pos); - } - }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, createChecker, getProgramTS) }) - .add(`getSymbolAtPosition - ${programIdentifierCount} identifiers (batched)`, () => { - const positions = collectIdentifiers(file).map(node => node.pos); - project.checker.getSymbolAtPosition("program.ts", positions); - }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, createChecker, getProgramTS) }) - .add(`getSymbolAtLocation - ${programIdentifierCount} identifiers`, () => { - for (const node of collectIdentifiers(file)) { - project.checker.getSymbolAtLocation(node); - } - }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, createChecker, getProgramTS) }) - .add(`getSymbolAtLocation - ${programIdentifierCount} identifiers (batched)`, () => { - const nodes = collectIdentifiers(file); - project.checker.getSymbolAtLocation(nodes); - }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, createChecker, getProgramTS) }) - .add(`TS - getSymbolAtLocation - ${programIdentifierCount} identifiers`, () => { - const checker = tsProgram.getTypeChecker(); - tsFile.forEachChild(function visit(node) { - if (node.kind === ts.SyntaxKind.Identifier) { - checker.getSymbolAtLocation(node); - } - node.forEachChild(visit); - }); - }, { async: isAsync, beforeAll: all(tsCreateProgram, tsCreateChecker, tsGetProgramTS) }); - - if (filter) { - const pattern = filter.toLowerCase(); - for (const task of [...bench.tasks]) { - if (!task.name.toLowerCase().includes(pattern)) { - bench.remove(task.name); - } - } - } - - let session: inspector.Session | undefined; - if (cpuprofile) { - session = new inspector.Session(); - session.connect(); - session.post("Profiler.enable"); - session.post("Profiler.start"); - } - - bench.runSync(); - - if (session) { - session.post("Profiler.stop", (err, { profile }) => { - if (err) throw err; - const outPath = `bench-${Date.now()}.cpuprofile`; - writeFileSync(outPath, JSON.stringify(profile)); - console.log(`CPU profile written to ${outPath}`); - }); - session.disconnect(); - } - console.table(bench.table()); - - function collectIdentifiers(sourceFile: SourceFile): Node[] { - const nodes: Node[] = []; - sourceFile.forEachChild(function visit(node) { - if (node.kind === SyntaxKind.Identifier) { - nodes.push(node); - } - node.forEachChild(visit); - }); - return nodes; - } - - function spawnAPI() { - api = new API({ - cwd: repoRoot, - }); - } - - function loadSnapshot() { - snapshot = api.updateSnapshot({ openProject: "tsc/testdata/fixtures/compiler/tsconfig.json" }); - project = snapshot.getProjects()[0]; - } - - function tsCreateProgram() { - const configFileName = fileURLToPath(new URL("../../../../tsc/testdata/fixtures/compiler/tsconfig.json", import.meta.url).toString()); - const configFile = ts.readConfigFile(configFileName, ts.sys.readFile); - const parsedCommandLine = ts.parseJsonConfigFileContent(configFile.config, ts.sys, path.dirname(configFileName)); - const host = ts.createCompilerHost(parsedCommandLine.options); - tsProgram = ts.createProgram({ - rootNames: parsedCommandLine.fileNames, - options: parsedCommandLine.options, - host, - }); - } - - function createChecker() { - // checker is created lazily, for measuring symbol time in a loop - // we need to create it first. - project.checker.getSymbolAtPosition("core.ts", 0); - } - - function tsCreateChecker() { - tsProgram.getTypeChecker(); - } - - function getDebugTS() { - file = (project.program.getSourceFile("debug.ts"))!; - } - - function getProgramTS() { - file = (project.program.getSourceFile("program.ts"))!; - } - - function tsGetProgramTS() { - tsFile = tsProgram.getSourceFile(fileURLToPath(new URL("../../../../tsc/testdata/fixtures/compiler/program.ts", import.meta.url).toString()))!; - } - - function getCheckerTS() { - file = (project.program.getSourceFile("checker.ts"))!; - } - - function clearSourceFileCache() { - api.clearSourceFileCache(); - } - - function teardown() { - api?.close(); - api = undefined!; - snapshot = undefined!; - project = undefined!; - file = undefined!; - tsProgram = undefined!; - tsFile = undefined!; - } - - function all(...fns: (() => void)[]) { - return () => { - for (const fn of fns) { - fn(); - } - }; - } -} +// +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// !!! THIS FILE IS AUTO-GENERATED - DO NOT EDIT !!! +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// +// Source: test/async/api.bench.ts +// Regenerate: npm run generate (from packages/typescript) +// +import { + type Node, + type SourceFile, + SyntaxKind, +} from "@typescript/typescript/unstable/ast"; +import { + API, + type Project, + type Snapshot, +} from "@typescript/typescript/unstable/sync"; +import { writeFileSync } from "node:fs"; +import inspector from "node:inspector"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { parseArgs } from "node:util"; +import { Bench } from "tinybench"; +import ts from "typescript"; +import { RemoteSourceFile } from "../../src/api/node/node.ts"; + +const isMain = process.argv[1] === fileURLToPath(import.meta.url); +if (isMain) { + const { values } = parseArgs({ + options: { + filter: { type: "string" }, + singleIteration: { type: "boolean", default: false }, + cpuprofile: { type: "boolean", default: false }, + }, + }); + runBenchmarks(values); +} + +export function runBenchmarks(options?: { filter?: string; singleIteration?: boolean; cpuprofile?: boolean; }) { + const { filter, singleIteration, cpuprofile } = options ?? {}; + const repoRoot = fileURLToPath(new URL("../../../../", import.meta.url).toString()); + + const bench = new Bench({ + name: "Sync API", + teardown, + // Reduce iterations from the default 64 to 10. Slow tasks + // are dominated by the iteration minimum, not the time limit. + // 10 iterations still gives stable medians while cutting total + // bench time by ~5x. + iterations: 10, + warmupIterations: 4, + ...singleIteration ? { + iterations: 1, + warmup: false, + time: 0, + } : undefined, + }); + + let api: API; + let snapshot: Snapshot; + let project: Project; + let tsProgram: ts.Program; + let file: SourceFile; + let tsFile: ts.SourceFile; + + const programIdentifierCount = (() => { + spawnAPI(); + loadSnapshot(); + getProgramTS(); + let count = 0; + file!.forEachChild(function visit(node) { + if (node.kind === SyntaxKind.Identifier) { + count++; + } + node.forEachChild(visit); + }); + teardown(); + return count; + })(); + + // Tinybench's `isFnAsyncResource` probes each task function by *calling* + // it once during `.add()` to detect whether it returns a Promise. + // In sync mode every task function is a plain (non-async) function, so + // the probe actually executes the benchmarked code (spawning processes, + // creating TS programs, etc.) wasting 30+ seconds. Passing an explicit + // `async` flag on every task skips the probe entirely. + const isAsync = false; + + bench + .add("spawn API", () => { + spawnAPI(); + }, { async: isAsync }) + .add("load snapshot", () => { + loadSnapshot(); + }, { async: isAsync, beforeAll: spawnAPI }) + .add("TS - load project", () => { + tsCreateProgram(); + }, { async: isAsync }) + .add("transfer debug.ts", () => { + getDebugTS(); + }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot), beforeEach: clearSourceFileCache }) + .add("transfer program.ts", () => { + getProgramTS(); + }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot), beforeEach: clearSourceFileCache }) + .add("transfer checker.ts", () => { + getCheckerTS(); + }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot), beforeEach: clearSourceFileCache }) + .add("materialize program.ts", () => { + const { view, _decoder } = file as unknown as RemoteSourceFile; + new RemoteSourceFile(new Uint8Array(view.buffer, view.byteOffset, view.byteLength), _decoder).forEachChild(function visit(node) { + node.forEachChild(visit); + }); + }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, getProgramTS) }) + .add("materialize checker.ts", () => { + const { view, _decoder } = file as unknown as RemoteSourceFile; + new RemoteSourceFile(new Uint8Array(view.buffer, view.byteOffset, view.byteLength), _decoder).forEachChild(function visit(node) { + node.forEachChild(visit); + }); + }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, getCheckerTS) }) + .add("getSymbolAtPosition - one location", () => { + project.checker.getSymbolAtPosition("program.ts", 8895); + }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, createChecker) }) + .add("TS - getSymbolAtPosition - one location", () => { + tsProgram.getTypeChecker().getSymbolAtLocation( + // @ts-ignore internal API + ts.getTokenAtPosition(tsFile, 8895), + ); + }, { async: isAsync, beforeAll: all(tsCreateProgram, tsCreateChecker, tsGetProgramTS) }) + .add(`getSymbolAtPosition - ${programIdentifierCount} identifiers`, () => { + for (const node of collectIdentifiers(file)) { + project.checker.getSymbolAtPosition("program.ts", node.pos); + } + }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, createChecker, getProgramTS) }) + .add(`getSymbolAtPosition - ${programIdentifierCount} identifiers (batched)`, () => { + const positions = collectIdentifiers(file).map(node => node.pos); + project.checker.getSymbolAtPosition("program.ts", positions); + }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, createChecker, getProgramTS) }) + .add(`getSymbolAtLocation - ${programIdentifierCount} identifiers`, () => { + for (const node of collectIdentifiers(file)) { + project.checker.getSymbolAtLocation(node); + } + }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, createChecker, getProgramTS) }) + .add(`getSymbolAtLocation - ${programIdentifierCount} identifiers (batched)`, () => { + const nodes = collectIdentifiers(file); + project.checker.getSymbolAtLocation(nodes); + }, { async: isAsync, beforeAll: all(spawnAPI, loadSnapshot, createChecker, getProgramTS) }) + .add(`TS - getSymbolAtLocation - ${programIdentifierCount} identifiers`, () => { + const checker = tsProgram.getTypeChecker(); + tsFile.forEachChild(function visit(node) { + if (node.kind === ts.SyntaxKind.Identifier) { + checker.getSymbolAtLocation(node); + } + node.forEachChild(visit); + }); + }, { async: isAsync, beforeAll: all(tsCreateProgram, tsCreateChecker, tsGetProgramTS) }); + + if (filter) { + const pattern = filter.toLowerCase(); + for (const task of [...bench.tasks]) { + if (!task.name.toLowerCase().includes(pattern)) { + bench.remove(task.name); + } + } + } + + let session: inspector.Session | undefined; + if (cpuprofile) { + session = new inspector.Session(); + session.connect(); + session.post("Profiler.enable"); + session.post("Profiler.start"); + } + + bench.runSync(); + + if (session) { + session.post("Profiler.stop", (err, { profile }) => { + if (err) throw err; + const outPath = `bench-${Date.now()}.cpuprofile`; + writeFileSync(outPath, JSON.stringify(profile)); + console.log(`CPU profile written to ${outPath}`); + }); + session.disconnect(); + } + console.table(bench.table()); + + function collectIdentifiers(sourceFile: SourceFile): Node[] { + const nodes: Node[] = []; + sourceFile.forEachChild(function visit(node) { + if (node.kind === SyntaxKind.Identifier) { + nodes.push(node); + } + node.forEachChild(visit); + }); + return nodes; + } + + function spawnAPI() { + api = new API({ + cwd: repoRoot, + }); + } + + function loadSnapshot() { + snapshot = api.updateSnapshot({ openProject: "tsc/testdata/fixtures/compiler/tsconfig.json" }); + project = snapshot.getProjects()[0]; + } + + function tsCreateProgram() { + const configFileName = fileURLToPath(new URL("../../../../tsc/testdata/fixtures/compiler/tsconfig.json", import.meta.url).toString()); + const configFile = ts.readConfigFile(configFileName, ts.sys.readFile); + const parsedCommandLine = ts.parseJsonConfigFileContent(configFile.config, ts.sys, path.dirname(configFileName)); + const host = ts.createCompilerHost(parsedCommandLine.options); + tsProgram = ts.createProgram({ + rootNames: parsedCommandLine.fileNames, + options: parsedCommandLine.options, + host, + }); + } + + function createChecker() { + // checker is created lazily, for measuring symbol time in a loop + // we need to create it first. + project.checker.getSymbolAtPosition("core.ts", 0); + } + + function tsCreateChecker() { + tsProgram.getTypeChecker(); + } + + function getDebugTS() { + file = (project.program.getSourceFile("debug.ts"))!; + } + + function getProgramTS() { + file = (project.program.getSourceFile("program.ts"))!; + } + + function tsGetProgramTS() { + tsFile = tsProgram.getSourceFile(fileURLToPath(new URL("../../../../tsc/testdata/fixtures/compiler/program.ts", import.meta.url).toString()))!; + } + + function getCheckerTS() { + file = (project.program.getSourceFile("checker.ts"))!; + } + + function clearSourceFileCache() { + api.clearSourceFileCache(); + } + + function teardown() { + api?.close(); + api = undefined!; + snapshot = undefined!; + project = undefined!; + file = undefined!; + tsProgram = undefined!; + tsFile = undefined!; + } + + function all(...fns: (() => void)[]) { + return () => { + for (const fn of fns) { + fn(); + } + }; + } +} diff --git a/packages/typescript/test/sync/api.test.ts b/packages/typescript/test/sync/api.test.ts index d534bc20a6e4e..b4e0ce85e40a2 100644 --- a/packages/typescript/test/sync/api.test.ts +++ b/packages/typescript/test/sync/api.test.ts @@ -1,6969 +1,6969 @@ -// -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// !!! THIS FILE IS AUTO-GENERATED - DO NOT EDIT !!! -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// -// Source: test/async/api.test.ts -// Regenerate: npm run generate (from packages/typescript) -// -import { - type __String, - cast, - escapeLeadingUnderscores, - type Expression, - getJSDocTags, - getSynthesizedDeepClone, - InternalSymbolName, - isCallExpression, - isExpressionStatement, - isFunctionDeclaration, - isIdentifier, - isImportDeclaration, - isInterfaceDeclaration, - isJSDocParameterTag, - isNamedImports, - isReturnStatement, - isShorthandPropertyAssignment, - isStringLiteral, - isTemplateHead, - isTemplateMiddle, - isTemplateTail, - isTypeAliasDeclaration, - isTypeNode, - isVariableDeclarationList, - isVariableStatement, - type Node, - type NodeArray, - NodeFlags, - SyntaxKind, - unescapeLeadingUnderscores, -} from "@typescript/typescript/unstable/ast"; -import { - createArrayTypeNode, - createFunctionTypeNode, - createIdentifier, - createKeywordTypeNode, - createNumericLiteral, - createParameterDeclaration, - createToken, - createTypeAliasDeclaration, - createTypeReferenceNode, - createUnionTypeNode, - createVariableDeclaration, - createVariableDeclarationList, - createVariableStatement, -} from "@typescript/typescript/unstable/ast/factory"; -import { visitEachChild } from "@typescript/typescript/unstable/ast/visitor"; -import { createVirtualFileSystem } from "@typescript/typescript/unstable/fs"; -import type { FileSystem } from "@typescript/typescript/unstable/fs"; -import { - API, - type BigIntLiteralType, - CheckFlags, - type ConditionalType, - DiagnosticCategory, - EmitOnly, - type FreshableType, - type ImportAdderAction, - type IndexedAccessType, - type IndexType, - type InterfaceType, - type IntrinsicType, - isErrorType, - type LiteralType, - ModifierFlags, - ModuleKind, - ObjectFlags, - type Signature, - SignatureKind, - type StringMappingType, - SymbolFlags, - type TemplateLiteralType, - type TextEdit, - TypeFlags, - TypeFormatFlags, - type TypeParameter, - TypePredicateKind, - type TypeReference, - type UnionOrIntersectionType, -} from "@typescript/typescript/unstable/sync"; -import assert from "node:assert"; -import { globSync } from "node:fs"; -import { resolve } from "node:path"; -import { - describe, - test, -} from "node:test"; -import { fileURLToPath } from "node:url"; -import { isSignatureDeclaration } from "../../src/ast/is.ts"; -import { runBenchmarks } from "./api.bench.ts"; -import { - defaultFiles, - spawnAPI, -} from "./api.testUtils.ts"; - -describe("API", () => { - test("parseCommandLine", () => { - const api = spawnAPI(); - try { - const commandLine = api.parseCommandLine([ - "--strict", - "--outDir", - "dist", - "/src/index.ts", - ]); - assert.deepEqual(commandLine.fileNames, ["/src/index.ts"]); - assert.equal(commandLine.options.strict, true); - assert.equal( - resolve(commandLine.options.outDir!), - resolve(fileURLToPath(new URL("../../../../", import.meta.url)), "dist"), - ); - assert.deepEqual(commandLine.raw, { - strict: true, - outDir: "dist", - }); - assert.deepEqual(commandLine.errors, []); - } - finally { - api.close(); - } - }); - - test("parseCommandLine reports diagnostics", () => { - const api = spawnAPI(); - try { - const commandLine = api.parseCommandLine(["--notAnOption"]); - assert.deepEqual(commandLine.fileNames, []); - assert.equal(commandLine.errors.length, 1); - assert.equal(commandLine.errors[0].code, 5023); - } - finally { - api.close(); - } - }); - - test("readConfigFile", () => { - const api = spawnAPI({ - "/tsconfig.json": `{ - // Comments and trailing commas are supported. - "compilerOptions": { "strict": true, }, - }`, - }); - try { - const result = api.readConfigFile("/tsconfig.json"); - assert.deepEqual(result, { - config: { compilerOptions: { strict: true } }, - }); - } - finally { - api.close(); - } - }); - - test("readConfigFile reports read and parse errors", () => { - const api = spawnAPI({ - "/invalid.json": `{ "compilerOptions": { "strict": true,, } }`, - }); - try { - const invalid = api.readConfigFile("/invalid.json"); - assert.deepEqual(invalid.config, { compilerOptions: { strict: true } }); - assert.ok(invalid.error); - - const missing = api.readConfigFile("/missing.json"); - assert.deepEqual(missing.config, {}); - assert.equal(missing.error?.code, 5083); - } - finally { - api.close(); - } - }); - - test("parseJsonConfigFileContent with configDirectory", () => { - const api = spawnAPI(); - try { - const config = api.parseJsonConfigFileContent( - { - compilerOptions: { strict: true }, - files: ["index.ts"], - }, - { configDirectory: "/src" }, - ); - assert.deepEqual(config.fileNames, ["/src/index.ts"]); - assert.equal(config.options.strict, true); - assert.equal("configFilePath" in config.options, false); - assert.equal(config.compileOnSave, false); - assert.deepEqual(config.errors, []); - } - finally { - api.close(); - } - }); - - test("parseJsonConfigFileContent accepts non-object JSON", () => { - const api = spawnAPI(); - try { - const config = api.parseJsonConfigFileContent(null, { configDirectory: "/src" }); - assert.deepEqual(config.fileNames, ["/src/index.ts", "/src/foo.ts"]); - assert.deepEqual(config.errors, []); - } - finally { - api.close(); - } - }); - - test("parseJsonConfigFileContent with configFileName", () => { - const api = spawnAPI(); - try { - const config = api.parseJsonConfigFileContent( - { - compilerOptions: { strict: true }, - files: ["index.ts"], - }, - { configFileName: "/src/tsconfig.json" }, - ); - assert.deepEqual(config.fileNames, ["/src/index.ts"]); - assert.equal(config.options.strict, true); - assert.equal(config.options.configFilePath, "/src/tsconfig.json"); - assert.deepEqual(config.errors, []); - } - finally { - api.close(); - } - }); - - test("parseJsonConfigFileContent preserves raw config", () => { - const api = spawnAPI(); - try { - const input = { - compileOnSave: true, - customSetting: { enabled: true }, - files: ["index.ts"], - }; - const config = api.parseJsonConfigFileContent(input, { configDirectory: "/src" }); - assert.deepEqual(config.raw, input); - assert.equal(config.compileOnSave, true); - } - finally { - api.close(); - } - }); - - test("parseJsonConfigFileContent preserves an empty files list", () => { - const api = spawnAPI(); - try { - const config = api.parseJsonConfigFileContent( - { files: [] }, - { configDirectory: "/src" }, - ); - assert.deepEqual(config.fileNames, []); - assert.equal(config.errors.length, 1); - assert.equal(config.errors[0].code, 18002); - } - finally { - api.close(); - } - }); - - test("parseJsonConfigFileContent reports null array elements", () => { - const api = spawnAPI(); - try { - const config = api.parseJsonConfigFileContent( - { files: [null], include: [null], exclude: [null] }, - { configDirectory: "/src" }, - ); - assert.equal(config.errors.length, 3); - assert.ok(config.errors.every(diagnostic => diagnostic.code === 5024)); - } - finally { - api.close(); - } - }); - - test("transpile", () => { - const api = spawnAPI({ - "/input.ts": "export const x: number = 1;", - }); - try { - const moduleOutput = api.transpileModule("export const x: number = 1;", { - compilerOptions: { module: ModuleKind.CommonJS }, - }); - assert.match(moduleOutput.outputText, /exports\.x = 1/); - - const isolatedDeclarationModuleOutput = api.transpileModule("export const x: number = 1;", { - compilerOptions: { declaration: true, isolatedDeclarations: true }, - reportDiagnostics: true, - }); - assert.equal(isolatedDeclarationModuleOutput.diagnostics?.length ?? 0, 0); - - const moduleFileOutput = api.transpileModuleFromFile("/input.ts", { - compilerOptions: { module: ModuleKind.CommonJS }, - }); - assert.match(moduleFileOutput.outputText, /exports\.x = 1/); - - const declarationOutput = api.transpileDeclaration("export const x: number = 1;"); - assert.equal(declarationOutput.outputText, "export declare const x: number;\n"); - - const windowsDeclarationOutput = api.transpileDeclaration("export const x: number = 1;", { - fileName: "C:/Users/me/project/input.ts", - }); - assert.equal(windowsDeclarationOutput.outputText, "export declare const x: number;\n"); - - const declarationFileOutput = api.transpileDeclarationFromFile("/input.ts"); - assert.equal(declarationFileOutput.outputText, "export declare const x: number;\n"); - } - finally { - api.close(); - } - }); - - test("parseConfigFile", () => { - const api = spawnAPI(); - try { - const config = api.parseConfigFile("/tsconfig.json"); - assert.deepEqual(config.fileNames, ["/src/index.ts", "/src/foo.ts"]); - assert.deepEqual(config.options, { configFilePath: "/tsconfig.json" }); - assert.equal(config.compileOnSave, false); - assert.equal(config.typeAcquisition, undefined); - assert.equal(config.projectReferences, undefined); - } - finally { - api.close(); - } - }); - - test("parseConfigFile includes projectReferences", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ references: [{ path: "./harness" }, { path: "./server" }] }), - }); - try { - const config = api.parseConfigFile("/tsconfig.json"); - assert.deepEqual(config.fileNames, []); - assert.deepEqual(config.options, { configFilePath: "/tsconfig.json" }); - assert.deepEqual(config.projectReferences, [ - { circular: false, originalPath: "./harness", path: "/harness" }, - { circular: false, originalPath: "./server", path: "/server" }, - ]); - } - finally { - api.close(); - } - }); - - test("parseConfigFile preserves raw config", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ - compileOnSave: true, - customSetting: { enabled: true }, - files: ["/src/index.ts"], - }), - }); - try { - const config = api.parseConfigFile("/tsconfig.json"); - assert.equal(config.compileOnSave, true); - assert.deepEqual(config.raw, { - compileOnSave: true, - customSetting: { enabled: true }, - files: ["/src/index.ts"], - }); - } - finally { - api.close(); - } - }); - - test("parseConfigFile includes compileOnSave", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compileOnSave: true }), - }); - try { - const config = api.parseConfigFile("/tsconfig.json"); - assert.equal(config.compileOnSave, true); - } - finally { - api.close(); - } - }); - - test("parseConfigFile includes typeAcquisition", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ typeAcquisition: { enable: true, include: ["jquery"] } }), - }); - try { - const config = api.parseConfigFile("/tsconfig.json"); - assert.equal(config.typeAcquisition?.enable, true); - assert.deepEqual(config.typeAcquisition?.include, ["jquery"]); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getImmediateAliasedSymbol", () => { - test("resolves one level of alias indirection", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/foo.ts": `export const foo = 42;`, - "/src/main.ts": `import { foo } from "./foo";\nexport const usage = foo;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = `import { foo } from "./foo";`.indexOf("foo }"); - const aliasSymbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(aliasSymbol); - const aliased = project.checker.getImmediateAliasedSymbol(aliasSymbol); - assert.ok(aliased, "Should resolve the immediate aliased symbol"); - assert.equal(aliased.name, "foo"); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getTargetSymbol", () => { - test("gets the target symbol of instantiated symbol", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -class Base { - private value!: T; -} -class Alpha extends Base {} -class Bravo extends Base {} - -declare function test(): void; -test(); -test(); -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - const nodes: Array = []; - sourceFile.forEachChild(node => { - if (isExpressionStatement(node) && isCallExpression(node.expression) && node.expression.typeArguments) { - nodes.push(node.expression.typeArguments[0]); - } - }); - const aType = project.checker.getTypeAtLocation(nodes[0]); - const bType = project.checker.getTypeAtLocation(nodes[1]); - const aProperty = (project.checker.getPropertiesOfType(aType))[0]; - const bProperty = (project.checker.getPropertiesOfType(bType))[0]; - assert.ok(aProperty); - assert.ok(bProperty); - assert.equal(aProperty === bProperty, false); - assert.equal(project.checker.getTargetSymbol(aProperty) === project.checker.getTargetSymbol(bProperty), true); - } - finally { - api.close(); - } - }); -}); - -describe("Snapshot", () => { - test("updateSnapshot returns snapshot with projects", () => { - const api = spawnAPI(); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - assert.ok(snapshot); - assert.ok(snapshot.id); - assert.ok(snapshot.getProjects().length > 0); - assert.ok(snapshot.getProject("/tsconfig.json")); - } - finally { - api.close(); - } - }); - - test("project exposes parsedCommandLine", () => { - const api = spawnAPI({ - ...defaultFiles, - "/tsconfig.json": JSON.stringify({ compileOnSave: true }), - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - assert.deepEqual(project.parsedCommandLine.fileNames, ["/src/index.ts", "/src/foo.ts"]); - assert.deepEqual(project.parsedCommandLine.options, { configFilePath: "/tsconfig.json" }); - assert.equal(project.parsedCommandLine.compileOnSave, true); - assert.deepEqual(project.rootFiles, project.parsedCommandLine.fileNames); - assert.deepEqual(project.compilerOptions, project.parsedCommandLine.options); - } - finally { - api.close(); - } - }); - - test("getSymbolAtPosition", () => { - const api = spawnAPI(); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = project.checker.getSymbolAtPosition("/src/index.ts", 9); - assert.ok(symbol); - assert.equal(symbol.name, "foo"); - assert.ok(symbol.flags & SymbolFlags.Alias); - } - finally { - api.close(); - } - }); - - test("getSymbolAtLocation", () => { - const api = spawnAPI(); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - const node = cast( - cast(sourceFile.statements[0], isImportDeclaration).importClause?.namedBindings, - isNamedImports, - ).elements[0].name; - assert.ok(node); - const symbol = project.checker.getSymbolAtLocation(node); - assert.ok(symbol); - assert.equal(symbol.name, "foo"); - assert.ok(symbol.flags & SymbolFlags.Alias); - } - finally { - api.close(); - } - }); - - test("getSymbolOfSourceFile", () => { - const api = spawnAPI(); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const moduleSymbol = project.checker.getSymbolOfSourceFile("/src/foo.ts"); - assert.ok(moduleSymbol); - const exports = moduleSymbol.getExports(); - assert.ok(exports.has(escapeLeadingUnderscores("foo"))); - } - finally { - api.close(); - } - }); - - test("getSymbolOfSourceFile returns undefined for a non-module file", () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/script.ts": `const x = 1;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = project.checker.getSymbolOfSourceFile("/src/script.ts"); - assert.equal(symbol, undefined); - } - finally { - api.close(); - } - }); - - test("getSymbolOfSourceFile batched", () => { - const api = spawnAPI(); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbols = project.checker.getSymbolOfSourceFile(["/src/index.ts", "/src/foo.ts"]); - assert.equal(symbols.length, 2); - assert.ok(symbols[0]); - assert.ok(symbols[1]); - assert.strictEqual(symbols[1], project.checker.getSymbolOfSourceFile("/src/foo.ts")); - } - finally { - api.close(); - } - }); - - test("getTypeOfSymbol", () => { - const api = spawnAPI(); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = project.checker.getSymbolAtPosition("/src/index.ts", 9); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.ok(type.flags & TypeFlags.NumberLiteral); - } - finally { - api.close(); - } - }); - - test("getNonMissingTypeOfSymbol", () => { - const api = spawnAPI({ - "/tsconfig-one.json": JSON.stringify({ - compilerOptions: { - exactOptionalPropertyTypes: true, - strict: true, - }, - }), - "/tsconfig-two.json": JSON.stringify({ - compilerOptions: { - exactOptionalPropertyTypes: false, - strict: true, - }, - }), - "/src/index.ts": "const x: Partial<{ a: string }> = {};", - }); - - try { - // when `"exactOptionalPropertyTypes": true` - const snapshot1 = api.updateSnapshot({ openProject: "/tsconfig-one.json" }); - const project1 = snapshot1.getProject("/tsconfig-one.json")!; - const type1 = project1.checker.getTypeAtPosition("/src/index.ts", 7); - assert.ok(type1); - const symbol1 = project1.checker.getPropertyOfType(type1, "a"); - assert.ok(symbol1); - const propertyType1 = project1.checker.getTypeOfSymbol(symbol1); - assert.ok(propertyType1); - // getTypeOfSymbol returns 'string | undefined' - assert.ok(propertyType1.isUnionType()); - const propertyType2 = project1.checker.getNonMissingTypeOfSymbol(symbol1); - assert.ok(propertyType2); - // getNonMissingTypeOfSymbol returns 'string' - assert.ok(!propertyType2.isUnionType()); - assert.ok(propertyType2.flags & TypeFlags.String); - - // when `"exactOptionalPropertyTypes": false` - const snapshot2 = api.updateSnapshot({ openProject: "/tsconfig-two.json" }); - const project2 = snapshot2.getProject("/tsconfig-two.json")!; - const type2 = project2.checker.getTypeAtPosition("/src/index.ts", 7); - assert.ok(type2); - const symbol2 = project2.checker.getPropertyOfType(type2, "a"); - assert.ok(symbol2); - const propertyType3 = project2.checker.getTypeOfSymbol(symbol2); - assert.ok(propertyType3); - // getTypeOfSymbol returns 'string | undefined' - assert.ok(propertyType3.isUnionType()); - const propertyType4 = project2.checker.getNonMissingTypeOfSymbol(symbol2); - assert.ok(propertyType4); - // getNonMissingTypeOfSymbol returns 'string | undefined' - assert.ok(propertyType4.isUnionType()); - } - finally { - api.close(); - } - }); -}); - -describe("LanguageService - imports", () => { - test("getImportEditsForSymbols adds a named import", () => { - const source = `const value = foo;\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": source, - "/src/foo.ts": `export const foo = 1;\n`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = project.checker.getSymbolAtPosition("/src/foo.ts", "export const ".length); - assert.ok(symbol); - - const edits = project.languageService.getImportEditsForSymbols("/src/index.ts", [symbol.getExportSymbol()]); - - assert.equal(applyTextEdits(source, edits), `import { foo } from "./foo";\n\nconst value = foo;\n`); - } - finally { - api.close(); - } - }); - - test("getImportAdderEdits coalesces multiple importSymbol actions", () => { - const source = `const value = foo + bar;\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": source, - "/src/foo.ts": `export const foo = 1;\nexport const bar = 2;\n`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const foo = project.checker.getSymbolAtPosition("/src/foo.ts", "export const ".length); - const bar = project.checker.getSymbolAtPosition("/src/foo.ts", "export const foo = 1;\nexport const ".length); - assert.ok(foo); - assert.ok(bar); - - const edits = project.languageService.getImportAdderEdits("/src/index.ts", [ - { kind: "importSymbol", symbol: foo.getExportSymbol() }, - { kind: "importSymbol", symbol: bar.getExportSymbol() }, - ]); - - assert.equal(applyTextEdits(source, edits), `import { bar, foo } from "./foo";\n\nconst value = foo + bar;\n`); - } - finally { - api.close(); - } - }); - - test("getImportAdderEdits adds to an existing import", () => { - const source = `import { foo } from "./foo";\nconst value = foo + bar;\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": source, - "/src/foo.ts": `export const foo = 1;\nexport const bar = 2;\n`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const bar = project.checker.getSymbolAtPosition("/src/foo.ts", "export const foo = 1;\nexport const ".length); - assert.ok(bar); - - const edits = project.languageService.getImportAdderEdits("/src/index.ts", [ - { kind: "importSymbol", symbol: bar.getExportSymbol() }, - ]); - - assert.equal(applyTextEdits(source, edits), `import { bar, foo } from "./foo";\nconst value = foo + bar;\n`); - } - finally { - api.close(); - } - }); - - test("getImportAdderEdits returns no edits for non-exported symbols", () => { - const source = `const value = local;\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": source, - "/src/foo.ts": `const local = 1;\n`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = project.checker.getSymbolAtPosition("/src/foo.ts", "const ".length); - assert.ok(symbol); - - const edits = project.languageService.getImportAdderEdits("/src/index.ts", [ - { kind: "importSymbol", symbol }, - ]); - - assert.deepEqual(edits, []); - } - finally { - api.close(); - } - }); - - test("getImportAdderEdits rejects invalid actions", () => { - const api = spawnAPI(); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = project.checker.getSymbolAtPosition("/src/foo.ts", 13); - assert.ok(symbol); - - assert.throws( - () => project.languageService.getImportAdderEdits("/src/index.ts", [{ kind: "unknown", symbol: symbol.id } as unknown as ImportAdderAction]), - /Debug Failure\. Illegal value: "unknown"/, - ); - assert.throws( - () => project.languageService.getImportAdderEdits("/src/index.ts", [{ kind: "importSymbol", symbol: { ...symbol, id: 999_999_999 } } as unknown as ImportAdderAction]), - /symbol handle \d+ not found/, - ); - } - finally { - api.close(); - } - }); -}); - -describe("LanguageService - getCompletionsAtPosition", () => { - test("returns member completions after a dot", () => { - const src = `\nconst obj = { name: "hello", age: 42 };\nobj.\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - // Position right after "obj." — member completion trigger - const pos = src.indexOf("obj.") + "obj.".length; - const completions = project.languageService.getCompletionsAtPosition("/src/main.ts", pos, { triggerCharacter: "." }); - assert.ok(completions, "Expected completions to be returned"); - assert.ok(completions.entries.length > 0, "Expected at least one completion entry"); - assert.ok(completions.entries.some(e => e.name === "name"), "Expected 'name' property in completions"); - assert.ok(completions.entries.some(e => e.name === "age"), "Expected 'age' property in completions"); - assert.ok(completions.entries.every(e => e.symbol === undefined), "Expected no symbol information"); - } - finally { - api.close(); - } - }); - - test("completion entries include sortText", () => { - const src = `\nconst obj = { value: 1 };\nobj.\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("obj.") + "obj.".length; - const completions = project.languageService.getCompletionsAtPosition("/src/main.ts", pos, { triggerCharacter: "." }); - assert.ok(completions); - assert.ok(completions.entries.length > 0); - assert.ok(completions.entries.some(e => e.sortText !== undefined), "Expected sortText on all entries"); - } - finally { - api.close(); - } - }); - - test("returns undefined for a non-existent file", () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": `export {};`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const completions = project.languageService.getCompletionsAtPosition("/src/does-not-exist.ts", 0); - assert.equal(completions, undefined, "Expected undefined for non-existent file"); - } - finally { - api.close(); - } - }); - - test("includeSymbol: true populates symbol on property completions", () => { - const src = `\nconst obj = { name: "hello", age: 42 };\nobj.\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("obj.") + "obj.".length; - const completions = project.languageService.getCompletionsAtPosition("/src/main.ts", pos, { triggerCharacter: ".", includeSymbol: true }); - assert.ok(completions, "Expected completions"); - const nameEntry = completions.entries.find(e => e.name === "name"); - assert.ok(nameEntry, "Expected 'name' entry"); - assert.ok(nameEntry.symbol, "Expected symbol to be set on 'name' entry when includeSymbol: true"); - assert.equal(nameEntry.symbol.name, "name", "Symbol name should match completion name"); - } - finally { - api.close(); - } - }); -}); - -describe("LanguageService - getReferencedSymbolsForNode", () => { - test("getReferencedSymbolsForNode", () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `function greet(name: string) { return name; }\ngreet("world");`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - const funcDecl = cast(sourceFile.statements[0], isFunctionDeclaration); - const funcName = funcDecl.name!; - const refs = project.languageService.getReferencedSymbolsForNode(funcName, funcName.pos); - assert.ok(refs.length > 0); - // Each entry should have a definition and references - const entry = refs[0]; - assert.ok(entry.definition); - assert.ok(entry.references.length > 0); - } - finally { - api.close(); - } - }); -}); - -describe("LanguageService - getSignatureUsage", () => { - test("getSignatureUsage", () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `function greet(name: string) { return name; }\ngreet("world");`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - const funcDecl = cast(sourceFile.statements[0], isFunctionDeclaration); - const usages = project.languageService.getSignatureUsage(funcDecl); - assert.ok(usages.length > 0); - // The call site should have a call expression - const usage = usages.find(u => u.call !== undefined); - assert.ok(usage, "Expected at least one usage with a call expression"); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getApparentType", () => { - test("returns the apparent type of a literal type", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const x = "hello" as const;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = `export const x = "hello" as const;`.indexOf("x ="); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.equal(type.isLiteralType(), true); - assert.equal(type.isStringLiteralType(), true); - assert.equal(type.isIntrinsicType(), false); - const apparent = project.checker.getApparentType(type); - assert.ok(apparent); - assert.ok(apparent.id > 0); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getReducedType", () => { - test("returns the reduced type", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -declare const TaggedError: ( - tag: Tag, -) => new
= {}>( - args: { readonly [P in keyof A]: A[P] }, -) => { readonly _tag: Tag } & Readonly; - -class RateLimitError extends TaggedError("RateLimitError")<{ - readonly retryAfter: number; -}> {} - -class QuotaExceededError extends TaggedError("QuotaExceededError")<{ - readonly limit: number; -}> {} - -export type Result = RateLimitError | (RateLimitError & QuotaExceededError);`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - const typeAlias = sourceFile.statements.find(isTypeAliasDeclaration); - assert.ok(typeAlias); - const type = project.checker.getTypeAtLocation(typeAlias); - assert.equal(type.isUnionType(), true); - assert.equal(type.isObjectType(), false); - const reducedType = project.checker.getReducedType(type); - assert.equal(reducedType.isUnionType(), false); - assert.equal(reducedType.isObjectType(), true); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getMemberInModuleExports", () => { - test("returns a named export when present", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/index.ts": `export const direct = 1;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - const moduleSymbol = project.checker.getSymbolAtLocation(sourceFile); - assert.ok(moduleSymbol); - const found = project.checker.getMemberInModuleExports(moduleSymbol, "direct"); - assert.ok(found); - assert.equal(found.name, "direct"); - const missing = project.checker.getMemberInModuleExports(moduleSymbol, "missing"); - assert.equal(missing, undefined); - } - finally { - api.close(); - } - }); -}); - -describe("SourceFile", () => { - test("getSourceFileNames returns all program files, not just root files", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ - compilerOptions: { - moduleResolution: "node10", - noLib: true, - }, - }), - "/src/index.ts": `import { foo } from "./foo";\nimport { bar } from "my-lib";\nexport const result = foo + bar;`, - "/src/foo.ts": `export const foo = 42;`, - "/node_modules/my-lib/package.json": JSON.stringify({ name: "my-lib", types: "./index.d.ts" }), - "/node_modules/my-lib/index.d.ts": `export declare const bar: number;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const fileNames = project.program.getSourceFileNames(); - assert.deepEqual(fileNames, [ - "/src/foo.ts", - "/node_modules/my-lib/index.d.ts", - "/src/index.ts", - ]); - } - finally { - api.close(); - } - }); - - test("source file metadata identifies external library and default library files", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ - compilerOptions: { - moduleResolution: "node10", - }, - }), - "/src/index.ts": `import { bar } from "my-lib";\nexport const result = bar;`, - "/node_modules/my-lib/package.json": JSON.stringify({ name: "my-lib", types: "./index.d.ts" }), - "/node_modules/my-lib/index.d.ts": `export declare const bar: number;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const program = project.program; - - const index = program.getSourceFile("/src/index.ts"); - assert.ok(index); - assert.equal(program.isSourceFileFromExternalLibrary(index), false); - assert.equal(program.isSourceFileDefaultLibrary(index), false); - - const lib = program.getSourceFile("/node_modules/my-lib/index.d.ts"); - assert.ok(lib); - assert.equal(program.isSourceFileFromExternalLibrary(lib), true); - assert.equal(program.isSourceFileDefaultLibrary(lib), false); - - const fileNames = program.getSourceFileNames(); - const defaultLibName = fileNames.find(name => name.endsWith("lib.d.ts") || name.includes("/lib.")); - assert.ok(defaultLibName, "expected a default library file in the program"); - const defaultLib = program.getSourceFile(defaultLibName); - assert.ok(defaultLib); - assert.equal(program.isSourceFileDefaultLibrary(defaultLib), true); - } - finally { - api.close(); - } - }); - - test("source file metadata reports implied node format", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ - compilerOptions: { - module: "nodenext", - moduleResolution: "nodenext", - }, - }), - "/src/index.ts": `export const x = 1;`, - "/src/esm.mts": `export const e = 1;`, - "/src/cjs.cts": `export const c = 1;`, - "/esm/package.json": JSON.stringify({ type: "module" }), - "/esm/index.ts": `export const m = 1;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const program = snapshot.getProject("/tsconfig.json")!.program; - - const mts = program.getSourceFile("/src/esm.mts"); - assert.ok(mts); - assert.equal((program.getSourceFileMetadata(mts.fileName))?.impliedNodeFormat, ModuleKind.ESNext); - assert.equal((program.getSourceFileMetadataByPath(mts.path))?.impliedNodeFormat, ModuleKind.ESNext); - - const cts = program.getSourceFile("/src/cjs.cts"); - assert.ok(cts); - assert.equal((program.getSourceFileMetadata(cts.fileName))?.impliedNodeFormat, ModuleKind.CommonJS); - - // A plain .ts file with no nearby `"type": "module"` is CommonJS. - const index = program.getSourceFile("/src/index.ts"); - assert.ok(index); - assert.equal((program.getSourceFileMetadata(index.fileName))?.impliedNodeFormat, ModuleKind.CommonJS); - - // A plain .ts file under a `"type": "module"` package is ESM. - const esmIndex = program.getSourceFile("/esm/index.ts"); - assert.ok(esmIndex); - assert.equal((program.getSourceFileMetadata(esmIndex.fileName))?.impliedNodeFormat, ModuleKind.ESNext); - } - finally { - api.close(); - } - }); - - test("file properties", () => { - const api = spawnAPI(); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - - assert.ok(sourceFile); - assert.equal(sourceFile.text, defaultFiles["/src/index.ts"]); - assert.equal(sourceFile.fileName, "/src/index.ts"); - } - finally { - api.close(); - } - }); - - test("extended data", () => { - const api = spawnAPI(); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - - assert.ok(sourceFile); - let nodeCount = 1; - sourceFile.forEachChild(function visit(node) { - if (isTemplateHead(node)) { - assert.equal(node.text, "head "); - assert.equal(node.rawText, "head "); - assert.equal(node.templateFlags, 0); - } - else if (isTemplateMiddle(node)) { - assert.equal(node.text, "middle"); - assert.equal(node.rawText, "middle"); - assert.equal(node.templateFlags, 0); - } - else if (isTemplateTail(node)) { - assert.equal(node.text, " tail"); - assert.equal(node.rawText, " tail"); - assert.equal(node.templateFlags, 0); - } - nodeCount++; - node.forEachChild(visit); - }); - assert.equal(nodeCount, 8); - } - finally { - api.close(); - } - }); - - test("forEachChild with visitList does not visit array children twice", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ files: ["/input.ts"] }), - "/input.ts": `let arrow = () => {}`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/input.ts"); - - assert.ok(sourceFile); - - const visited: { kind: SyntaxKind; pos: number; end: number; }[] = []; - (function walk(node: Node): void { - visited.push({ kind: node.kind, pos: node.pos, end: node.end }); - node.forEachChild(walk, (nodes: NodeArray) => { - for (let i = 0; i < nodes.length; i++) { - walk(nodes[i]); - } - return undefined; - }); - })(sourceFile); - - // Each node should be visited exactly once, even when a visitList callback - // is supplied. Previously array children were visited twice. - const seen = new Set(); - for (const { kind, pos, end } of visited) { - const key = `${kind}.${pos}.${end}`; - assert.ok(!seen.has(key), `Node ${key} was visited more than once`); - seen.add(key); - } - } - finally { - api.close(); - } - }); -}); - -describe("NodeArray", () => { - test("hasTrailingComma", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `declare function foo(...args: any): void;\nfoo("a", "b",);\nfoo("a", "b");`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - const statements = sourceFile.statements.filter(isExpressionStatement); - assert.ok(isCallExpression(statements[0].expression)); - assert.equal(statements[0].expression.arguments.hasTrailingComma, true); - assert.ok(isCallExpression(statements[1].expression)); - assert.equal(statements[1].expression.arguments.hasTrailingComma, false); - } - finally { - api.close(); - } - }); -}); - -test("unicode escapes", () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/1.ts": `"😃"`, - "/src/2.ts": `"\\ud83d\\ude03"`, - "/src/3.ts": `"\\ud800a\\udc00"`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const expectedTexts = new Map([ - ["/src/1.ts", "😃"], - ["/src/2.ts", "😃"], - ["/src/3.ts", "\ud800a\udc00"], - ]); - - for (const file of expectedTexts.keys()) { - const sourceFile = project.program.getSourceFile(file); - assert.ok(sourceFile); - - sourceFile.forEachChild(function visit(node) { - if (isStringLiteral(node)) { - assert.equal(node.text, expectedTexts.get(file)); - } - node.forEachChild(visit); - }); - } - } - finally { - api.close(); - } -}); - -test("template unicode escapes", () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": "`\\ud800${0}\\udc00`", - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - - let sawHead = false; - let sawTail = false; - sourceFile.forEachChild(function visit(node) { - if (isTemplateHead(node)) { - assert.equal(node.text, "\ud800"); - sawHead = true; - } - else if (isTemplateTail(node)) { - assert.equal(node.text, "\udc00"); - sawTail = true; - } - node.forEachChild(visit); - }); - assert.ok(sawHead); - assert.ok(sawTail); - } - finally { - api.close(); - } -}); - -test("Object equality", () => { - const api = spawnAPI(); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - // Same symbol returned from same snapshot's checker - assert.strictEqual( - project.checker.getSymbolAtPosition("/src/index.ts", 9), - project.checker.getSymbolAtPosition("/src/index.ts", 10), - ); - } - finally { - api.close(); - } -}); - -test("Snapshot dispose", () => { - const api = spawnAPI(); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = project.checker.getSymbolAtPosition("/src/index.ts", 9); - assert.ok(symbol); - - // Snapshot dispose should release server-side resources - assert.ok(snapshot.isDisposed() === false); - snapshot.dispose(); - assert.ok(snapshot.isDisposed() === true); - - // After dispose, snapshot methods should throw - assert.throws(() => { - snapshot.getProject("/tsconfig.json"); - }, { - name: "Error", - message: "Snapshot is disposed", - }); - } - finally { - api.close(); - } -}); - -describe("Multiple snapshots", () => { - test("two snapshots work independently", () => { - const api = spawnAPI(); - try { - const snap1 = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const snap2 = api.updateSnapshot({ openProject: "/tsconfig.json" }); - - // Both can fetch source files - const sf1 = snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); - const sf2 = snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); - assert.ok(sf1); - assert.ok(sf2); - - // Disposing one doesn't break the other - snap1.dispose(); - assert.ok(snap1.isDisposed()); - assert.ok(!snap2.isDisposed()); - - // snap2 still works after snap1 is disposed - const symbol = snap2.getProject("/tsconfig.json")!.checker.getSymbolAtPosition("/src/index.ts", 9); - assert.ok(symbol); - assert.equal(symbol.name, "foo"); - } - finally { - api.close(); - } - }); - - test("each snapshot has its own server-side lifecycle", () => { - const { api, fs } = spawnAPIWithFS(); - try { - const snap1 = api.updateSnapshot({ openProject: "/tsconfig.json" }); - - // Verify initial state - const sf1 = snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); - assert.ok(sf1); - assert.equal(sf1.text, `export const foo = 42;`); - - // Mutate the file and create a new snapshot with the change - fs.writeFile!("/src/foo.ts", `export const foo = "changed";`); - const snap2 = api.updateSnapshot({ - fileChanges: { changed: ["/src/foo.ts"] }, - }); - - // snap2 should reflect the updated content - const sf2 = snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); - assert.ok(sf2); - assert.equal(sf2.text, `export const foo = "changed";`); - - // snap1's source file should still have the original content - assert.equal(sf1.text, `export const foo = 42;`); - - snap1.dispose(); - - // snap2 still works independently after snap1 is disposed - const symbol = snap2.getProject("/tsconfig.json")!.checker.getSymbolAtPosition("/src/index.ts", 9); - assert.ok(symbol); - - snap2.dispose(); - - // Both are disposed, new snapshot works fine with latest content - const snap3 = api.updateSnapshot(); - const sf3 = snap3.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); - assert.ok(sf3); - assert.equal(sf3.text, `export const foo = "changed";`); - } - finally { - api.close(); - } - }); - - test("adding a new file is reflected in the next snapshot", () => { - const { api, fs } = spawnAPIWithFS(); - try { - const snap1 = api.updateSnapshot({ openProject: "/tsconfig.json" }); - - // Add a brand new file - fs.writeFile!("/src/bar.ts", `export const bar = true;`); - const snap2 = api.updateSnapshot({ - fileChanges: { created: ["/src/bar.ts"] }, - }); - - const sf = snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/bar.ts"); - assert.ok(sf); - assert.equal(sf.text, `export const bar = true;`); - - // Original snapshot shouldn't have the new file - const sfOld = snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/bar.ts"); - assert.equal(sfOld, undefined); - } - finally { - api.close(); - } - }); - - test("multiple sequential edits produce correct snapshots", () => { - const { api, fs } = spawnAPIWithFS(); - try { - api.updateSnapshot({ openProject: "/tsconfig.json" }); - - const versions = [ - `export const foo = 1;`, - `export const foo = 2;`, - `export const foo = 3;`, - ]; - - for (const version of versions) { - fs.writeFile!("/src/foo.ts", version); - const snap = api.updateSnapshot({ - fileChanges: { changed: ["/src/foo.ts"] }, - }); - const sf = snap.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); - assert.ok(sf); - assert.equal(sf.text, version); - } - } - finally { - api.close(); - } - }); -}); - -describe("Source file caching", () => { - test("same file from same snapshot returns cached object", () => { - const api = spawnAPI(); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sf1 = project.program.getSourceFile("/src/index.ts"); - const sf2 = project.program.getSourceFile("/src/index.ts"); - assert.ok(sf1); - assert.strictEqual(sf1, sf2, "Same source file should be returned from cache"); - } - finally { - api.close(); - } - }); - - test("same file from two snapshots (same content) returns cached object", () => { - const api = spawnAPI(); - try { - const snap1 = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const snap2 = api.updateSnapshot({ openProject: "/tsconfig.json" }); - // Fetch from snap1 first (populates cache), then snap2 (cache hit via hash) - const sf1 = snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); - const sf2 = snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); - assert.ok(sf1); - assert.ok(sf2); - // Same content hash → cache hit → same object - assert.strictEqual(sf1, sf2, "Same file with same content should share cached object"); - } - finally { - api.close(); - } - }); - - test("modified file returns a different source file object", () => { - const { api, fs } = spawnAPIWithFS(); - try { - const snap1 = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const sf1 = snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); - assert.ok(sf1); - assert.equal(sf1.text, `export const foo = 42;`); - - // Mutate the file in the VFS - fs.writeFile!("/src/foo.ts", `export const foo = 100;`); - - // Notify the server about the change - const snap2 = api.updateSnapshot({ - fileChanges: { changed: ["/src/foo.ts"] }, - }); - const sf2 = snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); - assert.ok(sf2); - assert.equal(sf2.text, `export const foo = 100;`); - - // Different content → different object - assert.notStrictEqual(sf1, sf2, "Modified file should return a new source file object"); - } - finally { - api.close(); - } - }); - - test("unmodified file retains cached object across file change notification", () => { - const { api, fs } = spawnAPIWithFS(); - try { - const snap1 = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const sf1 = snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); - assert.ok(sf1); - - // Mutate a different file - fs.writeFile!("/src/foo.ts", `export const foo = 999;`); - - // Notify the server about the change to foo.ts only - const snap2 = api.updateSnapshot({ - fileChanges: { changed: ["/src/foo.ts"] }, - }); - const sf2 = snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); - assert.ok(sf2); - - // index.ts wasn't changed — should still get cached object - assert.strictEqual(sf1, sf2, "Unchanged file should return cached object across snapshots"); - } - finally { - api.close(); - } - }); - - test("cache entries survive when one of two snapshots is disposed", () => { - const api = spawnAPI(); - try { - const snap1 = api.updateSnapshot({ openProject: "/tsconfig.json" }); - // Fetch from snap1 to populate cache - const sf1 = snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); - assert.ok(sf1); - - // snap2 retains snap1's cache refs for unchanged files via snapshot changes - const snap2 = api.updateSnapshot({ openProject: "/tsconfig.json" }); - - // Dispose snap1 — snap2 still holds a ref, so the entry survives - snap1.dispose(); - - // Fetching from snap2 should still return the cached object - const sf2 = snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); - assert.ok(sf2); - assert.strictEqual(sf1, sf2, "Cache entry should survive when retained by the next snapshot"); - } - finally { - api.close(); - } - }); - - test("invalidateAll causes all files to be re-fetched", () => { - const { api, fs } = spawnAPIWithFS(); - try { - const snap1 = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const sf1 = snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); - assert.ok(sf1); - assert.equal(sf1.text, `export const foo = 42;`); - - // Mutate the file - fs.writeFile!("/src/foo.ts", `export const foo = "hello";`); - - // Use invalidateAll to force re-fetch - const snap2 = api.updateSnapshot({ - fileChanges: { invalidateAll: true }, - }); - const sf2 = snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); - assert.ok(sf2); - assert.equal(sf2.text, `export const foo = "hello";`); - assert.notStrictEqual(sf1, sf2, "invalidateAll should produce new source file objects"); - } - finally { - api.close(); - } - }); - - test("node handles from a cached source file should be valid in a new snapshot", () => { - const { api, fs } = spawnAPIWithFS({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `function foo(x: number) {}\nfoo(42);`, - "/src/other.ts": `export const x = 1;`, - }); - try { - // Snapshot 1: get a node and verify getContextualType works - const snap1 = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const proj1 = snap1.getProject("/tsconfig.json")!; - - const sf1 = proj1.program.getSourceFile("/src/main.ts"); - assert.ok(sf1); - - let numLiteral: Expression | undefined; - sf1.forEachChild(function visit(node) { - if (isCallExpression(node)) numLiteral = node.arguments[0]; - node.forEachChild(visit); - }); - assert.ok(numLiteral, "should find the 42 argument"); - - const type1 = proj1.checker.getContextualType(numLiteral); - assert.ok(type1); - assert.ok(type1.flags & TypeFlags.Number); - - // Snapshot 2: change a different file - fs.writeFile!("/src/other.ts", `export const x = 2;`); - const snap2 = api.updateSnapshot({ - fileChanges: { changed: ["/src/other.ts"] }, - }); - const proj2 = snap2.getProject("/tsconfig.json")!; - - // main.ts is unchanged — client returns the cached SourceFile (same object) - const sf2 = proj2.program.getSourceFile("/src/main.ts"); - assert.ok(sf2); - assert.strictEqual(sf1, sf2, "unchanged file should be served from client cache"); - - let numLiteral2: Expression | undefined; - sf2.forEachChild(function visit(node) { - if (isCallExpression(node)) numLiteral2 = node.arguments[0]; - node.forEachChild(visit); - }); - assert.ok(numLiteral2, "should find the 42 argument"); - assert.strictEqual(numLiteral, numLiteral2, "unchanged file should be served from client cache"); - - // A type from new snapshot should be resolved - const type2 = proj2.checker.getContextualType(numLiteral); - assert.ok(type2); - assert.ok(type2.flags & TypeFlags.Number); - } - finally { - api.close(); - } - }); -}); - -describe("Snapshot disposal", () => { - test("dispose is idempotent", () => { - const api = spawnAPI(); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - snapshot.dispose(); - assert.ok(snapshot.isDisposed()); - // Second dispose should not throw - snapshot.dispose(); - assert.ok(snapshot.isDisposed()); - } - finally { - api.close(); - } - }); - - test("api.close disposes all active snapshots", () => { - const api = spawnAPI(); - const snap1 = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const snap2 = api.updateSnapshot({ openProject: "/tsconfig.json" }); - assert.ok(!snap1.isDisposed()); - assert.ok(!snap2.isDisposed()); - api.close(); - assert.ok(snap1.isDisposed()); - assert.ok(snap2.isDisposed()); - }); -}); - -describe("Source file cache keying across projects", () => { - // Three projects share the same file (/src/shared.ts). - // The file sits inside a package.json scope with "type": "module". - // - // Project A: moduleResolution: bundler (auto detection, bundler doesn't - // trigger isFileForcedToBeModuleByFormat → file parsed as script) - // Project B: moduleResolution: bundler, moduleDetection: force - // (force → file parsed as module) - // Project C: moduleResolution: nodenext - // (nodenext + type:module → impliedNodeFormat ESNext → - // isFileForcedToBeModuleByFormat → file parsed as module) - // - // Expected: exactly two distinct source file objects are stored: - // - A gets one (script parse) - // - B and C share another (module parse) - const multiProjectFiles: Record = { - "/package.json": JSON.stringify({ type: "module" }), - "/src/shared.ts": `export const x = 1;`, - // Project A – bundler, auto detection (default) - "/projectA/tsconfig.json": JSON.stringify({ - compilerOptions: { - moduleResolution: "bundler", - module: "esnext", - strict: true, - }, - files: ["../src/shared.ts"], - }), - // Project B – bundler, force module detection - "/projectB/tsconfig.json": JSON.stringify({ - compilerOptions: { - moduleResolution: "bundler", - module: "esnext", - moduleDetection: "force", - strict: true, - }, - files: ["../src/shared.ts"], - }), - // Project C – nodenext (type:module → module) - "/projectC/tsconfig.json": JSON.stringify({ - compilerOptions: { - moduleResolution: "nodenext", - module: "nodenext", - strict: true, - }, - files: ["../src/shared.ts"], - }), - }; - - test("different parse modes produce separate cached objects; same parse modes share", () => { - const api = spawnAPI(multiProjectFiles); - try { - // Open all three projects - api.updateSnapshot({ openProject: "/projectA/tsconfig.json" }); - api.updateSnapshot({ openProject: "/projectB/tsconfig.json" }); - const snapshot = api.updateSnapshot({ openProject: "/projectC/tsconfig.json" }); - - const projectA = snapshot.getProject("/projectA/tsconfig.json")!; - const projectB = snapshot.getProject("/projectB/tsconfig.json")!; - const projectC = snapshot.getProject("/projectC/tsconfig.json")!; - assert.ok(projectA, "projectA should exist"); - assert.ok(projectB, "projectB should exist"); - assert.ok(projectC, "projectC should exist"); - - // Fetch the shared file from each project - const sfA = projectA.program.getSourceFile("/src/shared.ts"); - const sfB = projectB.program.getSourceFile("/src/shared.ts"); - const sfC = projectC.program.getSourceFile("/src/shared.ts"); - assert.ok(sfA, "sfA should exist"); - assert.ok(sfB, "sfB should exist"); - assert.ok(sfC, "sfC should exist"); - - // A should differ from B and C (script vs module parse) - assert.notStrictEqual(sfA, sfB, "projectA (script) and projectB (module) should have different cached source files"); - assert.notStrictEqual(sfA, sfC, "projectA (script) and projectC (module) should have different cached source files"); - - // B and C should share the same cached object (both module parse, same content hash) - assert.strictEqual(sfB, sfC, "projectB and projectC (both module parse) should share the same cached source file"); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - symbol identity across projects", () => { - const sharedSymbolFiles = { - "/projectA/tsconfig.json": JSON.stringify({ files: ["../src/shared.ts"] }), - "/projectB/tsconfig.json": JSON.stringify({ files: ["../src/shared.ts"] }), - "/src/shared.ts": `export const sharedVar = 42;`, - }; - - test("getSymbolAtPosition returns same Symbol instance across projects", () => { - const api = spawnAPI(sharedSymbolFiles); - try { - api.updateSnapshot({ openProject: "/projectA/tsconfig.json" }); - const snapshot = api.updateSnapshot({ openProject: "/projectB/tsconfig.json" }); - - const projectA = snapshot.getProject("/projectA/tsconfig.json")!; - const projectB = snapshot.getProject("/projectB/tsconfig.json")!; - assert.ok(projectA, "projectA should exist"); - assert.ok(projectB, "projectB should exist"); - - const src = sharedSymbolFiles["/src/shared.ts"]; - const varPos = src.indexOf("sharedVar"); - - const symbolA = projectA.checker.getSymbolAtPosition("/src/shared.ts", varPos); - const symbolB = projectB.checker.getSymbolAtPosition("/src/shared.ts", varPos); - - assert.ok(symbolA, "symbolA should exist"); - assert.ok(symbolB, "symbolB should exist"); - assert.equal(symbolA.name, "sharedVar"); - assert.equal(symbolB.name, "sharedVar"); - - assert.strictEqual(symbolA, symbolB, "Same source symbol queried from two projects should be the same object"); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - types and signatures", () => { - const checkerFiles = { - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export const x = 42; -export function add(a: number, b: number, ...rest: number[]): number { return a + b; } -export class MyClass { - value: string = ""; - getValue(): string { return this.value; } -} -`, - }; - - test("getTypeAtPosition", () => { - const api = spawnAPI(checkerFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = checkerFiles["/src/main.ts"]; - const xPos = src.indexOf("x = 42"); - const type = project.checker.getTypeAtPosition("/src/main.ts", xPos); - assert.ok(type); - assert.ok(type.flags & TypeFlags.NumberLiteral); - } - finally { - api.close(); - } - }); - - test("getTypeAtPosition batched", () => { - const api = spawnAPI(checkerFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = checkerFiles["/src/main.ts"]; - const xPos = src.indexOf("x = 42"); - const addPos = src.indexOf("add("); - const types = project.checker.getTypeAtPosition("/src/main.ts", [xPos, addPos]); - assert.equal(types.length, 2); - assert.ok(types[0]); - assert.ok(types[1]); - } - finally { - api.close(); - } - }); - - test("getTypeAtLocation", () => { - const api = spawnAPI(checkerFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - const firstVarDecl = sourceFile.statements[2]; // "export const x" - assert.ok(firstVarDecl); - const type = project.checker.getTypeAtLocation(firstVarDecl); - assert.ok(type); - } - finally { - api.close(); - } - }); - - test("getTypeAtLocation returns property type for parenthesized and chained access (issue #3938)", () => { - const files = { - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -interface A { - a: number; - b: { c: number }; -} -const obj: A = { a: 1, b: { c: 2 } }; -const a1 = obj.a; -const a2 = (obj).a; -const c = obj.b.c; -`, - }; - - const api = spawnAPI(files); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - - const targetNodes = new Map(); - sourceFile.forEachChild(function visit(node) { - if (node.kind === SyntaxKind.PropertyAccessExpression) { - const text = sourceFile.text.slice(node.pos, node.end).trim(); - if (text === "obj.a" || text === "(obj).a" || text === "obj.b.c") { - targetNodes.set(text, node); - } - } - node.forEachChild(visit); - }); - - assert.equal(targetNodes.size, 3, "Should find all target property access expressions"); - for (const expr of ["obj.a", "(obj).a", "obj.b.c"] as const) { - const node = targetNodes.get(expr); - assert.ok(node, `Should find expression '${expr}'`); - const type = project.checker.getTypeAtLocation(node); - assert.ok(type, `Should get a type for '${expr}'`); - assert.ok(type.flags & TypeFlags.Number, `Expected '${expr}' to have number type flags, got ${type.flags}`); - } - } - finally { - api.close(); - } - }); - - test("getTypeAtLocation returns call result type for private method call (issue #4041)", () => { - const files = { - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true, target: "esnext" } }), - "/src/main.ts": ` -type Result = { readonly value: string }; - -export class Cache { - run(): Result { - return this.#buildCapabilities(); - } - - #buildCapabilities(): Result { - return { value: "ok" }; - } -} -`, - }; - - const api = spawnAPI(files); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - - let callNode: import("@typescript/typescript/unstable/ast").CallExpression | undefined; - sourceFile.forEachChild(function visit(node) { - if (isCallExpression(node)) { - const text = sourceFile.text.slice(node.pos, node.end).trim(); - if (text === "this.#buildCapabilities()") { - callNode = node; - } - } - node.forEachChild(visit); - }); - - assert.ok(callNode, "Should find private method call expression"); - const callType = project.checker.getTypeAtLocation(callNode); - const calleeType = project.checker.getTypeAtLocation(callNode.expression); - assert.ok(callType, "Should get type for call expression"); - assert.ok(calleeType, "Should get type for callee expression"); - - const callSignatures = project.checker.getSignaturesOfType(calleeType, SignatureKind.Call); - assert.ok(callSignatures.length > 0, "Callee should be callable"); - const returnType = project.checker.getReturnTypeOfSignature(callSignatures[0]); - assert.ok(returnType, "Should get return type for private method call"); - const callExprSignatures = project.checker.getSignaturesOfType(callType, SignatureKind.Call); - - assert.ok(callType.flags & TypeFlags.Object, `Expected call expression type to be object-like, got ${callType.flags}`); - assert.ok(returnType.flags & TypeFlags.Object, `Expected return type to be object-like, got ${returnType.flags}`); - assert.equal(callType.flags, returnType.flags, "Call expression type should have same flags as method return type"); - assert.equal(callExprSignatures.length, 0, "Call expression result type should not itself be callable"); - } - finally { - api.close(); - } - }); - - test("getSignaturesOfType - call signatures", () => { - const api = spawnAPI(checkerFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = checkerFiles["/src/main.ts"]; - const addPos = src.indexOf("add("); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", addPos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const callSigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(callSigs.length > 0); - const sig = callSigs[0]; - const typeCallSigs = type.getCallSignatures(); - assert.deepEqual(typeCallSigs, callSigs); - assert.ok((sig.getReturnType()).flags & TypeFlags.Number); - assert.ok((sig.getTypeParameterAtPosition(0)).flags & TypeFlags.Number); - assert.ok(sig.id); - assert.ok(sig.parameters.length >= 2); - assert.ok(sig.hasRestParameter); - assert.ok(!sig.isConstruct); - assert.ok(!sig.isAbstract); - } - finally { - api.close(); - } - }); - - test("getApparentProperties includes CallableFunction members", () => { - const api = spawnAPI(checkerFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = checkerFiles["/src/main.ts"]; - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("add(")); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - const propertyNames = (type.getApparentProperties()).map(property => property.name); - assert.ok(propertyNames.includes("apply")); - assert.ok(propertyNames.includes("call")); - assert.ok(propertyNames.includes("bind")); - } - finally { - api.close(); - } - }); - - test("checker and object methods share cached results", () => { - const api = new API({ - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), - fs: createVirtualFileSystem(checkerFiles), - collectTiming: true, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = checkerFiles["/src/main.ts"]; - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("add(")); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - let signature: Signature | undefined; - - function assertOneRequest(callback: () => void) { - api.resetTimingInfo(); - callback(); - assert.equal((api.getTimingInfo()).totals.requestCount, 1); - } - - assertOneRequest(() => { - const properties = type.getProperties(); - assert.strictEqual(project.checker.getPropertiesOfType(type), properties); - }); - assertOneRequest(() => { - const signatures = type.getCallSignatures(); - assert.strictEqual(project.checker.getSignaturesOfType(type, SignatureKind.Call), signatures); - signature = signatures[0]; - }); - assertOneRequest(() => { - const nonNullable = project.checker.getNonNullableType(type); - assert.strictEqual(type.getNonNullableType(), nonNullable); - }); - assertOneRequest(() => { - const apparentType = type.getApparentType(); - assert.strictEqual(project.checker.getApparentType(type), apparentType); - }); - assertOneRequest(() => { - const indexInfos = type.getIndexInfos(); - assert.strictEqual(project.checker.getIndexInfosOfType(type), indexInfos); - }); - assertOneRequest(() => { - assert.ok(signature); - const returnType = project.checker.getReturnTypeOfSignature(signature); - assert.strictEqual(signature.getReturnType(), returnType); - }); - } - finally { - api.close(); - } - }); - - test("getSignaturesOfType - construct signatures", () => { - const api = spawnAPI(checkerFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = checkerFiles["/src/main.ts"]; - const classPos = src.indexOf("MyClass"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", classPos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const constructSigs = project.checker.getSignaturesOfType(type, SignatureKind.Construct); - assert.ok(constructSigs.length > 0); - const sig = constructSigs[0]; - assert.ok(sig.isConstruct); - } - finally { - api.close(); - } - }); - - test("Signature declaration can be resolved", () => { - const api = spawnAPI(checkerFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = checkerFiles["/src/main.ts"]; - const addPos = src.indexOf("add("); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", addPos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const callSigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(callSigs.length > 0); - const sig = callSigs[0]; - assert.ok(sig.declaration); - assert.ok(isSignatureDeclaration.Handle(sig.declaration)); - const node = sig.declaration.resolve(project); - assert.ok(node); - assert.ok(node.parameters); - assert.ok(isSignatureDeclaration(node)); - // The handle remembers its canonical project, so resolve() works without an argument. - const nodeFromCanonical = sig.declaration.resolve(); - assert.ok(nodeFromCanonical); - assert.ok(nodeFromCanonical.parameters); - assert.ok(isSignatureDeclaration(nodeFromCanonical)); - assert.strictEqual(nodeFromCanonical.kind, node.kind); - - const methodPos = src.indexOf("getValue"); - const methodSymbol = project.checker.getSymbolAtPosition("/src/main.ts", methodPos); - assert.ok(methodSymbol); - assert.ok(methodSymbol.valueDeclaration); - const methodNode = methodSymbol.valueDeclaration.resolve(project); - assert.ok(methodNode); - assert.strictEqual(methodNode.parent.kind, SyntaxKind.ClassDeclaration); - assert.strictEqual(methodNode.parent.parent.kind, SyntaxKind.SourceFile); - // A symbol's declaration handles default to the symbol's canonical project. - const methodNodeFromCanonical = methodSymbol.valueDeclaration.resolve(); - assert.ok(methodNodeFromCanonical); - assert.strictEqual(methodNodeFromCanonical.kind, methodNode.kind); - } - finally { - api.close(); - } - }); - - test("getSignaturesOfType - signature type parameters", () => { - const mainFile = ` - interface Operator { - } - export declare class Observable { - lift(operator: Operator): Observable; - } - `; - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": mainFile, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const liftPos = mainFile.indexOf("lift"); - const type = project.checker.getTypeAtPosition("/src/main.ts", liftPos); - assert.ok(type); - const callSigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(callSigs.length === 1, "should have exactly one call signature, found: " + callSigs.length); - const sig = callSigs[0]; - assert.ok(sig.typeParameters?.length === 1, "should have exactly one type parameter, found: " + sig.typeParameters?.length); - const typeParams = sig.getTypeParameters(); - const typeParam = typeParams[0]; - assert.ok(typeParam, "should have type parameter"); - const name = (typeParam.getSymbol())?.name; - assert.ok(name === "R", "should be named R, instead: " + name); - assert.ok(typeParam.flags & TypeFlags.TypeParameter, "should be a type parameter, instead flags: " + typeParam.flags); - } - finally { - api.close(); - } - }); - - test("Signature.getParameters() returns parameter symbols with correct names", () => { - const api = spawnAPI(checkerFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = checkerFiles["/src/main.ts"]; - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("add(")); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const params = sigs[0].getParameters(); - assert.equal(params.length, 3); - assert.equal(params[0].name, "a"); - assert.equal(params[1].name, "b"); - assert.equal(params[2].name, "rest"); - assert.ok(params[0].flags & SymbolFlags.FunctionScopedVariable, `expected FunctionScopedVariable on 'a', got ${params[0].flags}`); - } - finally { - api.close(); - } - }); - - test("Signature.getThisParameter() returns undefined when no explicit this parameter", () => { - const api = spawnAPI(checkerFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = checkerFiles["/src/main.ts"]; - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("add(")); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const thisParam = sigs[0].getThisParameter(); - assert.strictEqual(thisParam, undefined, "add() has no explicit this parameter"); - } - finally { - api.close(); - } - }); - - test("Signature.getThisParameter() returns symbol for explicit this parameter", () => { - const src = `export function foo(this: { n: number }, x: string): void {}`; - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": src, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("foo(")); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const thisParam = sigs[0].getThisParameter(); - assert.ok(thisParam, "foo has an explicit this parameter"); - assert.equal(thisParam.name, "this"); - assert.ok(thisParam.flags & SymbolFlags.FunctionScopedVariable, `expected FunctionScopedVariable, got ${thisParam.flags}`); - } - finally { - api.close(); - } - }); - - test("Signature.getTarget() returns undefined for a non-instantiated signature", () => { - const api = spawnAPI(checkerFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = checkerFiles["/src/main.ts"]; - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("add(")); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const target = sigs[0].getTarget(); - assert.strictEqual(target, undefined, "add() is not an instantiated signature"); - } - finally { - api.close(); - } - }); - - test("Signature.getTarget() returns the generic source signature for an instantiated call", () => { - const src = ` - function identity(x: T): T { return x; } - identity("hello"); - `; - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": src, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - let callNode: Node | undefined; - sourceFile.forEachChild(function visit(node) { - if (isCallExpression(node)) callNode = node; - node.forEachChild(visit); - }); - assert.ok(callNode, "should find a call expression"); - const sig = project.checker.getResolvedSignature(callNode); - assert.ok(sig, "should resolve a signature for the call"); - assert.ok(sig.target !== undefined, "instantiated call should have a target ID"); - const target = sig.getTarget(); - assert.ok(target, "getTarget() should return the generic signature"); - assert.ok(target.typeParameters && target.typeParameters.length > 0, "target should have type parameters"); - } - finally { - api.close(); - } - }); -}); - -describe("Symbol - parent, members, exports", () => { - const symbolFiles = { - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/mod.ts": ` -export class Animal { - name: string = ""; - speak(): void {} -} -export const value = 1; -`, - }; - - test("getMembers returns class members", () => { - const api = spawnAPI(symbolFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = symbolFiles["/src/mod.ts"]; - const animalPos = src.indexOf("Animal"); - const symbol = project.checker.getSymbolAtPosition("/src/mod.ts", animalPos); - assert.ok(symbol); - const members = symbol.getMembers(); - assert.ok(members.size > 0); - const memberNames = [...members.values()].map(m => m.name); - assert.ok(memberNames.includes("name"), "should have 'name' member"); - assert.ok(memberNames.includes("speak"), "should have 'speak' member"); - } - finally { - api.close(); - } - }); - - test("getExports returns module exports via sourceFile symbol", () => { - const api = spawnAPI(symbolFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/mod.ts"); - assert.ok(sourceFile); - const moduleSymbol = project.checker.getSymbolAtLocation(sourceFile); - assert.ok(moduleSymbol); - const exports = moduleSymbol.getExports(); - assert.ok(exports.size > 0); - const exportNames = [...exports.values()].map(e => e.name); - assert.ok(exportNames.includes("Animal"), "should export Animal"); - assert.ok(exportNames.includes("value"), "should export value"); - } - finally { - api.close(); - } - }); - - test("getParent returns containing symbol", () => { - const api = spawnAPI(symbolFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = symbolFiles["/src/mod.ts"]; - const namePos = src.indexOf("name:"); - const nameSymbol = project.checker.getSymbolAtPosition("/src/mod.ts", namePos); - assert.ok(nameSymbol); - assert.equal(nameSymbol.name, "name"); - const parent = nameSymbol.getParent(); - assert.ok(parent); - assert.equal(parent.name, "Animal"); - } - finally { - api.close(); - } - }); - - test("checkFlags is typed as CheckFlags", () => { - const api = spawnAPI(symbolFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = project.checker.getSymbolAtPosition("/src/mod.ts", symbolFiles["/src/mod.ts"].indexOf("Animal")); - assert.ok(symbol); - const checkFlags: CheckFlags = symbol.checkFlags; - assert.equal(checkFlags & CheckFlags.Readonly, 0); - } - finally { - api.close(); - } - }); -}); - -describe("Type - getSymbol", () => { - test("getSymbol returns the symbol of a type", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/types.ts": ` -export class Foo { - x: number = 0; -} -export const instance: Foo = new Foo(); -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\nexport class Foo {\n x: number = 0;\n}\nexport const instance: Foo = new Foo();\n`; - const instancePos = src.indexOf("instance"); - const symbol = project.checker.getSymbolAtPosition("/src/types.ts", instancePos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const typeSymbol = type.getSymbol(); - assert.ok(typeSymbol); - assert.equal(typeSymbol.name, "Foo"); - } - finally { - api.close(); - } - }); -}); - -describe("Type - sub-property fetchers", () => { - const typeFiles = { - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true, target: "esnext" } }), - "/src/types.ts": ` -export const arr: Array = [1, 2, 3]; -export const union: string | number = "hello"; -export const intersection: { a: number } & { b: string } = { a: 1, b: "hi" }; -export type KeyOf = keyof T; -export type Lookup = T[K]; -export type Cond = T extends string ? "yes" : "no"; -export const tpl: \`hello \${string}\` = "hello world"; -export type Upper = Uppercase<"hello">; -export const tuple: readonly [number, string?, ...boolean[]] = [1]; -`, - }; - - function getTypeAtName(api: API, name: string) { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = typeFiles["/src/types.ts"]; - const pos = src.indexOf(name); - assert.ok(pos >= 0, `Could not find "${name}" in source`); - const symbol = project.checker.getSymbolAtPosition("/src/types.ts", pos); - assert.ok(symbol, `No symbol found at "${name}"`); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type, `No type found for symbol "${name}"`); - return { type, project, snapshot, api }; - } - - test("TypeReference.getTarget() returns the generic target", () => { - const { type, api } = getTypeAtName(spawnAPI(typeFiles), "arr:"); - try { - assert.ok(type.flags & TypeFlags.Object); - const ref = type as TypeReference; - assert.ok(ref.objectFlags & ObjectFlags.Reference); - assert.equal(type.isObjectType(), true); - assert.equal(type.isTypeReference(), true); - assert.equal(type.isLiteralType(), false); - const target = ref.getTarget(); - assert.ok(target); - assert.ok(target.flags & TypeFlags.Object); - const properties = type.getProperties(); - assert.ok(properties.some(property => property.name === "length")); - assert.equal((type.getProperty("length"))?.name, "length"); - assert.ok((type.getApparentProperties()).length >= properties.length); - assert.strictEqual(type.getNonNullableType(), type); - } - finally { - api.close(); - } - }); - - test("UnionOrIntersectionType.getTypes() returns union members", () => { - const { type, api } = getTypeAtName(spawnAPI(typeFiles), "union:"); - try { - assert.ok(type.flags & TypeFlags.Union); - const union = type as UnionOrIntersectionType; - const types = union.getTypes(); - assert.ok(types.length >= 2); - assert.equal(type.isUnionType(), true); - assert.equal(type.isIntersectionType(), false); - } - finally { - api.close(); - } - }); - - test("UnionOrIntersectionType.getTypes() returns intersection members", () => { - const { type, api } = getTypeAtName(spawnAPI(typeFiles), "intersection:"); - try { - assert.ok(type.flags & TypeFlags.Intersection); - const inter = type as UnionOrIntersectionType; - const types = inter.getTypes(); - assert.ok(types.length >= 2); - } - finally { - api.close(); - } - }); - - test("UnionOrIntersectionType.getTypes() on a wrongly-cast type returns undefined without hitting the server", () => { - const src = `export const s: string = ""; export const u: string | number = "";`; - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": src, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - - // `string` is neither a union/intersection nor a template literal type, - // so it has no constituent types. The client guards on the type's flags - // and returns undefined without ever sending a request the server cannot satisfy. - const sSymbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("s:")); - assert.ok(sSymbol); - const sType = project.checker.getTypeOfSymbol(sSymbol); - assert.ok(sType); - assert.equal((sType as unknown as UnionOrIntersectionType).getTypes(), undefined); - - // A real union still returns its constituents. - const uSymbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("u:")); - assert.ok(uSymbol); - const uType = project.checker.getTypeOfSymbol(uSymbol); - assert.ok(uType); - assert.equal(((uType as UnionOrIntersectionType).getTypes()).length, 2); - } - finally { - api.close(); - } - }); - - test("IndexType.getTarget() returns the target type", () => { - const api = spawnAPI(typeFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = project.checker.resolveName("KeyOf", SymbolFlags.TypeAlias, { document: "/src/types.ts", position: 0 }); - assert.ok(symbol); - const type = project.checker.getDeclaredTypeOfSymbol(symbol); - assert.ok(type); - // KeyOf = keyof T — this is an IndexType - assert.ok(type.flags & TypeFlags.Index, `Expected IndexType, got flags ${type.flags}`); - const indexType = type as IndexType; - const target = indexType.getTarget(); - assert.ok(target); - } - finally { - api.close(); - } - }); - - test("IndexedAccessType.getObjectType() and getIndexType()", () => { - const api = spawnAPI(typeFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = project.checker.resolveName("Lookup", SymbolFlags.TypeAlias, { document: "/src/types.ts", position: 0 }); - assert.ok(symbol); - const type = project.checker.getDeclaredTypeOfSymbol(symbol); - assert.ok(type); - assert.ok(type.flags & TypeFlags.IndexedAccess, `Expected IndexedAccessType, got flags ${type.flags}`); - const ia = type as IndexedAccessType; - assert.equal(type.isIndexedAccessType(), true); - const objectType = ia.getObjectType(); - assert.ok(objectType); - const indexType = ia.getIndexType(); - assert.ok(indexType); - } - finally { - api.close(); - } - }); - - test("ConditionalType.getCheckType() and getExtendsType()", () => { - const api = spawnAPI(typeFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = project.checker.resolveName("Cond", SymbolFlags.TypeAlias, { document: "/src/types.ts", position: 0 }); - assert.ok(symbol); - const type = project.checker.getDeclaredTypeOfSymbol(symbol); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Conditional, `Expected ConditionalType, got flags ${type.flags}`); - const cond = type as ConditionalType; - assert.equal(type.isConditionalType(), true); - const checkType = cond.getCheckType(); - assert.ok(checkType); - const extendsType = cond.getExtendsType(); - assert.ok(extendsType); - } - finally { - api.close(); - } - }); - - test("ConditionalType.getTrueType() and getFalseType()", () => { - const api = spawnAPI(typeFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = project.checker.resolveName("Cond", SymbolFlags.TypeAlias, { document: "/src/types.ts", position: 0 }); - assert.ok(symbol); - const type = project.checker.getDeclaredTypeOfSymbol(symbol); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Conditional, `Expected ConditionalType, got flags ${type.flags}`); - - const trueType = (type as ConditionalType).getTrueType(); - assert.ok(trueType, "should return the true-branch type"); - assert.ok(trueType.flags & TypeFlags.StringLiteral, `Expected StringLiteral for true branch, got flags ${trueType.flags}`); - assert.equal((trueType as LiteralType).value, "yes"); - - const falseType = (type as ConditionalType).getFalseType(); - assert.ok(falseType, "should return the false-branch type"); - assert.ok(falseType.flags & TypeFlags.StringLiteral, `Expected StringLiteral for false branch, got flags ${falseType.flags}`); - assert.equal((falseType as LiteralType).value, "no"); - } - finally { - api.close(); - } - }); - - test("TemplateLiteralType.texts and getTypes()", () => { - const { type, api } = getTypeAtName(spawnAPI(typeFiles), "tpl:"); - try { - assert.ok(type.flags & TypeFlags.TemplateLiteral, `Expected TemplateLiteralType, got flags ${type.flags}`); - const tpl = type as TemplateLiteralType; - assert.ok(tpl.texts); - assert.ok(tpl.texts.length >= 2); - assert.equal(tpl.texts[0], "hello "); - const types = tpl.getTypes(); - assert.ok(types.length >= 1); - } - finally { - api.close(); - } - }); - - test("StringMappingType.getTarget() returns the mapped type", () => { - const api = spawnAPI(typeFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = typeFiles["/src/types.ts"]; - const pos = src.indexOf("Upper"); - const symbol = project.checker.getSymbolAtPosition("/src/types.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - // Uppercase<"hello"> may resolve to a StringMappingType or a string literal - if (type.flags & TypeFlags.StringMapping) { - const sm = type as StringMappingType; - const target = sm.getTarget(); - assert.ok(target); - } - // If it resolved to "HELLO" literal, that's fine too — it means eager evaluation - } - finally { - api.close(); - } - }); - - test("TupleType properties", () => { - const { type, api } = getTypeAtName(spawnAPI(typeFiles), "tuple:"); - try { - assert.ok(type.flags & TypeFlags.Object); - const ref = type as TypeReference; - assert.ok(ref.objectFlags & ObjectFlags.Reference); - const target = ref.getTarget(); - assert.ok(target); - assert.ok(target.flags & TypeFlags.Object); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - intrinsic type getters", () => { - const intrinsicFiles = { - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const x = 1;`, - }; - - test("getAnyType returns a type with Any flag", () => { - const api = spawnAPI(intrinsicFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const type = project.checker.getAnyType(); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Any); - } - finally { - api.close(); - } - }); - - test("getStringType returns a type with String flag", () => { - const api = spawnAPI(intrinsicFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const type = project.checker.getStringType(); - assert.ok(type); - assert.ok(type.flags & TypeFlags.String); - } - finally { - api.close(); - } - }); - - test("getNumberType returns a type with Number flag", () => { - const api = spawnAPI(intrinsicFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const type = project.checker.getNumberType(); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Number); - } - finally { - api.close(); - } - }); - - test("getBooleanType returns a type with Boolean flag", () => { - const api = spawnAPI(intrinsicFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const type = project.checker.getBooleanType(); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Boolean); - } - finally { - api.close(); - } - }); - - test("getVoidType returns a type with Void flag", () => { - const api = spawnAPI(intrinsicFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const type = project.checker.getVoidType(); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Void); - } - finally { - api.close(); - } - }); - - test("getUndefinedType returns a type with Undefined flag", () => { - const api = spawnAPI(intrinsicFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const type = project.checker.getUndefinedType(); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Undefined); - } - finally { - api.close(); - } - }); - - test("getNullType returns a type with Null flag", () => { - const api = spawnAPI(intrinsicFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const type = project.checker.getNullType(); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Null); - } - finally { - api.close(); - } - }); - - test("getNeverType returns a type with Never flag", () => { - const api = spawnAPI(intrinsicFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const type = project.checker.getNeverType(); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Never); - } - finally { - api.close(); - } - }); - - test("getUnknownType returns a type with Unknown flag", () => { - const api = spawnAPI(intrinsicFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const type = project.checker.getUnknownType(); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Unknown); - } - finally { - api.close(); - } - }); - - test("getBigIntType returns a type with BigInt flag", () => { - const api = spawnAPI(intrinsicFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const type = project.checker.getBigIntType(); - assert.ok(type); - assert.ok(type.flags & TypeFlags.BigInt); - } - finally { - api.close(); - } - }); - - test("getESSymbolType returns a type with ESSymbol flag", () => { - const api = spawnAPI(intrinsicFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const type = project.checker.getESSymbolType(); - assert.ok(type); - assert.ok(type.flags & TypeFlags.ESSymbol); - } - finally { - api.close(); - } - }); - - test("getNonPrimitiveType returns a type with NonPrimitive flag", () => { - const api = spawnAPI(intrinsicFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const type = project.checker.getNonPrimitiveType(); - assert.ok(type); - assert.ok(type.flags & TypeFlags.NonPrimitive); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - multi-project type ID uniqueness", () => { - test("intrinsic types from 3 projects in the same snapshot have non-colliding IDs", () => { - const api = spawnAPI({ - "/proj1/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/proj1/src/index.ts": `export const x = 1;`, - "/proj2/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/proj2/src/index.ts": `export const y = "hello";`, - "/proj3/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/proj3/src/index.ts": `export const z = true;`, - }); - try { - // Open all 3 projects — each updateSnapshot accumulates open projects - api.updateSnapshot({ openProject: "/proj1/tsconfig.json" }); - api.updateSnapshot({ openProject: "/proj2/tsconfig.json" }); - const snapshot = api.updateSnapshot({ openProject: "/proj3/tsconfig.json" }); - - const proj1 = snapshot.getProject("/proj1/tsconfig.json")!; - const proj2 = snapshot.getProject("/proj2/tsconfig.json")!; - const proj3 = snapshot.getProject("/proj3/tsconfig.json")!; - assert.ok(proj1, "proj1 should be in final snapshot"); - assert.ok(proj2, "proj2 should be in final snapshot"); - assert.ok(proj3, "proj3 should be in final snapshot"); - - // Fetch several intrinsic types from each checker. - // If type IDs collide across checkers, registerType panics → API error. - const num1 = proj1.checker.getNumberType(); - const str1 = proj1.checker.getStringType(); - const bool1 = proj1.checker.getBooleanType(); - const any1 = proj1.checker.getAnyType(); - const num2 = proj2.checker.getNumberType(); - const str2 = proj2.checker.getStringType(); - const bool2 = proj2.checker.getBooleanType(); - const any2 = proj2.checker.getAnyType(); - const num3 = proj3.checker.getNumberType(); - const str3 = proj3.checker.getStringType(); - const bool3 = proj3.checker.getBooleanType(); - const any3 = proj3.checker.getAnyType(); - - assert.ok(num1.flags & TypeFlags.Number, "proj1 number type"); - assert.ok(str1.flags & TypeFlags.String, "proj1 string type"); - assert.ok(bool1.flags & TypeFlags.Boolean, "proj1 boolean type"); - assert.ok(any1.flags & TypeFlags.Any, "proj1 any type"); - - assert.ok(num2.flags & TypeFlags.Number, "proj2 number type"); - assert.ok(str2.flags & TypeFlags.String, "proj2 string type"); - assert.ok(bool2.flags & TypeFlags.Boolean, "proj2 boolean type"); - assert.ok(any2.flags & TypeFlags.Any, "proj2 any type"); - - assert.ok(num3.flags & TypeFlags.Number, "proj3 number type"); - assert.ok(str3.flags & TypeFlags.String, "proj3 string type"); - assert.ok(bool3.flags & TypeFlags.Boolean, "proj3 boolean type"); - assert.ok(any3.flags & TypeFlags.Any, "proj3 any type"); - } - finally { - api.close(); - } - }); - - test("symbol and signature handles from 3 projects in the same snapshot have non-colliding IDs", () => { - const api = spawnAPI({ - "/proj1/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/proj1/src/index.ts": `export function add(a: number, b: number): number { return a + b; }`, - "/proj2/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/proj2/src/index.ts": `export function greet(name: string): string { return "hello " + name; }`, - "/proj3/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/proj3/src/index.ts": `export function toggle(b: boolean): boolean { return !b; }`, - }); - try { - api.updateSnapshot({ openProject: "/proj1/tsconfig.json" }); - api.updateSnapshot({ openProject: "/proj2/tsconfig.json" }); - const snapshot = api.updateSnapshot({ openProject: "/proj3/tsconfig.json" }); - - const proj1 = snapshot.getProject("/proj1/tsconfig.json")!; - const proj2 = snapshot.getProject("/proj2/tsconfig.json")!; - const proj3 = snapshot.getProject("/proj3/tsconfig.json")!; - - // Get a symbol from each project (exercises symbol registry) - const src1 = `export function add(a: number, b: number): number { return a + b; }`; - const src2 = `export function greet(name: string): string { return "hello " + name; }`; - const src3 = `export function toggle(b: boolean): boolean { return !b; }`; - - const sym1 = proj1.checker.getSymbolAtPosition("/proj1/src/index.ts", src1.indexOf("add")); - const sym2 = proj2.checker.getSymbolAtPosition("/proj2/src/index.ts", src2.indexOf("greet")); - const sym3 = proj3.checker.getSymbolAtPosition("/proj3/src/index.ts", src3.indexOf("toggle")); - assert.ok(sym1, "proj1 symbol"); - assert.ok(sym2, "proj2 symbol"); - assert.ok(sym3, "proj3 symbol"); - assert.equal(sym1.name, "add", "proj1 symbol name"); - assert.equal(sym2.name, "greet", "proj2 symbol name"); - assert.equal(sym3.name, "toggle", "proj3 symbol name"); - - // Get type of each symbol, then signatures (exercises type + signature registries) - const type1 = proj1.checker.getTypeOfSymbol(sym1); - const type2 = proj2.checker.getTypeOfSymbol(sym2); - const type3 = proj3.checker.getTypeOfSymbol(sym3); - assert.ok(type1, "proj1 function type"); - assert.ok(type2, "proj2 function type"); - assert.ok(type3, "proj3 function type"); - - const sigs1 = proj1.checker.getSignaturesOfType(type1, SignatureKind.Call); - const sigs2 = proj2.checker.getSignaturesOfType(type2, SignatureKind.Call); - const sigs3 = proj3.checker.getSignaturesOfType(type3, SignatureKind.Call); - assert.equal(sigs1.length, 1, "proj1 has 1 call signature"); - assert.equal(sigs2.length, 1, "proj2 has 1 call signature"); - assert.equal(sigs3.length, 1, "proj3 has 1 call signature"); - assert.equal(sigs1[0].parameters.length, 2, "proj1 add() has 2 params"); - assert.equal(sigs2[0].parameters.length, 1, "proj2 greet() has 1 param"); - assert.equal(sigs3[0].parameters.length, 1, "proj3 toggle() has 1 param"); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getBaseTypeOfLiteralType", () => { - test("number literal widens to number", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const x = 42;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export const x = 42;`; - const pos = src.indexOf("x ="); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const literalType = project.checker.getTypeOfSymbol(symbol); - assert.ok(literalType); - assert.ok(literalType.flags & TypeFlags.NumberLiteral); - const baseType = project.checker.getBaseTypeOfLiteralType(literalType); - assert.ok(baseType); - assert.ok(baseType.flags & TypeFlags.Number); - } - finally { - api.close(); - } - }); - - test("string literal widens to string", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const s = "hello";`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export const s = "hello";`; - const pos = src.indexOf("s "); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const literalType = project.checker.getTypeOfSymbol(symbol); - assert.ok(literalType); - assert.ok(literalType.flags & TypeFlags.StringLiteral); - const baseType = project.checker.getBaseTypeOfLiteralType(literalType); - assert.ok(baseType); - assert.ok(baseType.flags & TypeFlags.String); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getContextualType", () => { - test("contextual type from function parameter", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -function foo(x: number) {} -foo(42); -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - - const sourceFile = project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - - // Find the argument "42" in foo(42) - // statement[1] = foo(42); which is an ExpressionStatement -> CallExpression - const callStmt = sourceFile.statements[1]; - assert.ok(callStmt); - let numLiteral: import("@typescript/typescript/unstable/ast").Expression | undefined; - callStmt.forEachChild(function visit(node) { - if (isCallExpression(node)) { - // First argument - numLiteral = node.arguments[0]; - } - node.forEachChild(visit); - }); - assert.ok(numLiteral); - const contextualType = project.checker.getContextualType(numLiteral); - assert.ok(contextualType); - assert.ok(contextualType.flags & TypeFlags.Number); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getTypeOfSymbolAtLocation", () => { - test("narrowed type via typeof check", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export function check(x: string | number) { - if (typeof x === "string") { - return x; - } - return x; -} -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\nexport function check(x: string | number) {\n if (typeof x === "string") {\n return x;\n }\n return x;\n}\n`; - - // Get the symbol for parameter "x" - const paramPos = src.indexOf("x:"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", paramPos); - assert.ok(symbol); - assert.equal(symbol.name, "x"); - - // Get the type of "x" at the wider function scope — should be string | number - const wideType = project.checker.getTypeOfSymbol(symbol); - assert.ok(wideType); - assert.ok(wideType.flags & TypeFlags.Union, `Expected union type, got flags ${wideType.flags}`); - - // Get the narrowed return x inside the if block - const sourceFile = project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - - // statement[0] is the function declaration - const funcDecl = sourceFile.statements[0]; - assert.ok(funcDecl); - // Walk to find the first "return x" — inside the if, x should be narrowed to string - let firstReturnX: import("@typescript/typescript/unstable/ast").Node | undefined; - funcDecl.forEachChild(function visit(node) { - if (isReturnStatement(node) && !firstReturnX) { - // The expression of the return statement is the identifier "x" - if (node.expression) { - firstReturnX = node.expression; - } - } - node.forEachChild(visit); - }); - assert.ok(firstReturnX); - const narrowedType = project.checker.getTypeOfSymbolAtLocation(symbol, firstReturnX); - assert.ok(narrowedType); - // Inside the if (typeof x === "string") branch, x should be narrowed to string - assert.ok(narrowedType.flags & TypeFlags.String, `Expected string type, got flags ${narrowedType.flags}`); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getShorthandAssignmentValueSymbol", () => { - test("shorthand property symbol resolves to variable", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -const name = "Alice"; -export const obj = { name }; -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - - const sourceFile = project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - - // Find the shorthand property assignment { name } - // statement[1] = export const obj = { name }; - let shorthandNode: import("@typescript/typescript/unstable/ast").Node | undefined; - sourceFile.forEachChild(function visit(node) { - if (isShorthandPropertyAssignment(node)) { - shorthandNode = node; - } - node.forEachChild(visit); - }); - assert.ok(shorthandNode, "Should find a shorthand property assignment"); - const valueSymbol = project.checker.getShorthandAssignmentValueSymbol(shorthandNode); - assert.ok(valueSymbol); - assert.equal(valueSymbol.name, "name"); - } - finally { - api.close(); - } - }); -}); - -describe("readFile callback semantics", () => { - test("readFile: string returns content, null blocks fallback, undefined falls through to real FS", () => { - const virtualFiles: Record = { - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/index.ts": `export const x: number = 1;`, - }; - const vfs = createVirtualFileSystem(virtualFiles); - const blockedPath = "/src/blocked.ts"; - - const fs: FileSystem = { - ...vfs, - readFile: (fileName: string) => { - if (fileName === blockedPath) { - // null = file not found, don't fall back to real FS - return null; - } - // Try the VFS first; if it has the file, return its content (string). - // Otherwise return undefined to fall through to the real FS. - return vfs.readFile!(fileName); - }, - }; - - const api = new API({ - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), - fs, - }); - - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - - // 1. String content: virtual file is found - const sf = project.program.getSourceFile("/src/index.ts"); - assert.ok(sf, "Virtual file should be found"); - assert.equal(sf.text, virtualFiles["/src/index.ts"]); - - // 2. undefined fallback: lib files from the real FS should be present. - // If readFile returned null for unknowns, lib files would be missing - // and `number` would not resolve — this was the original async bug. - // Verify by checking that `number` resolves to a proper type (not error). - const pos = virtualFiles["/src/index.ts"].indexOf("x:"); - const type = project.checker.getTypeAtPosition("/src/index.ts", pos); - assert.ok(type, "Type should resolve"); - assert.ok(type.flags & TypeFlags.Number, `Expected number type, got flags ${type.flags}`); - - // 3. null blocks fallback: blocked file should not be found - const blockedSf = project.program.getSourceFile(blockedPath); - assert.equal(blockedSf, undefined, "Blocked file should not be found (null prevents fallback)"); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - isArrayType / isTupleType", () => { - test("number[] is array, not tuple", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const xs: number[] = [];`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export const xs: number[] = [];`; - const pos = src.indexOf("xs"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.equal(project.checker.isArrayType(type), true); - assert.equal(project.checker.isTupleType(type), false); - } - finally { - api.close(); - } - }); - - test("readonly number[] is array", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const xs: readonly number[] = [];`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export const xs: readonly number[] = [];`; - const pos = src.indexOf("xs"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.equal(project.checker.isArrayType(type), true); - assert.equal(project.checker.isTupleType(type), false); - } - finally { - api.close(); - } - }); - - test("Array is array, not tuple", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const xs: Array = [];`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export const xs: Array = [];`; - const pos = src.indexOf("xs"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.equal(project.checker.isArrayType(type), true); - assert.equal(project.checker.isTupleType(type), false); - } - finally { - api.close(); - } - }); - - test("[number, string] is tuple, not array", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const tup: [number, string] = [1, "a"];`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export const tup: [number, string] = [1, "a"];`; - const pos = src.indexOf("tup"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.equal(project.checker.isArrayType(type), false); - assert.equal(project.checker.isTupleType(type), true); - } - finally { - api.close(); - } - }); - - test("readonly [number, string] is tuple, not array", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const tup: readonly [number, string] = [1, "a"];`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export const tup: readonly [number, string] = [1, "a"];`; - const pos = src.indexOf("tup"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.equal(project.checker.isArrayType(type), false); - assert.equal(project.checker.isTupleType(type), true); - } - finally { - api.close(); - } - }); - - test("string is neither array nor tuple", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const str: string = "";`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export const str: string = "";`; - const pos = src.indexOf("str"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.equal(project.checker.isArrayType(type), false); - assert.equal(project.checker.isTupleType(type), false); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - isReadonlySymbol", () => { - test("properties with a 'readonly' modifier", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export interface User { - readonly name: string; - age: number; -} - -export type ReadonlyUser = Readonly; -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - const user = sourceFile.statements.find(isInterfaceDeclaration); - assert.ok(user); - const userProperties = project.checker.getPropertiesOfType( - project.checker.getTypeAtLocation(user), - ); - assert.equal(project.checker.isReadonlySymbol(userProperties[0]), true); - assert.equal(project.checker.isReadonlySymbol(userProperties[1]), false); - const readonlyUser = sourceFile.statements.find(isTypeAliasDeclaration); - assert.ok(readonlyUser); - const readonlyUserProperties = project.checker.getPropertiesOfType( - project.checker.getTypeAtLocation(readonlyUser), - ); - assert.equal(project.checker.isReadonlySymbol(readonlyUserProperties[0]), true); - assert.equal(project.checker.isReadonlySymbol(readonlyUserProperties[1]), true); - } - finally { - api.close(); - } - }); - - test("variables declared with 'const'", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const a = 1; export let b = 2;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const { checker } = snapshot.getProject("/tsconfig.json")!; - const a = checker.getSymbolAtPosition("/src/main.ts", "export const ".length); - const b = checker.getSymbolAtPosition("/src/main.ts", "export const a = 1; export let ".length); - assert.ok(a); - assert.ok(b); - assert.equal(checker.isReadonlySymbol(a), true); - assert.equal(checker.isReadonlySymbol(b), false); - } - finally { - api.close(); - } - }); - - test("get accessors without matching set accessors", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -class Alpha { - private _value!: number; - get value(): number { - return this._value; - } -} -class Bravo { - private _value!: number; - get value(): number { - return this._value; - } - set value(newValue: number) { - this._value = newValue; - } -} -export type A = InstanceType; -export type B = InstanceType; -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - const typeAliases = sourceFile.statements.filter(isTypeAliasDeclaration); - assert.equal(typeAliases.length, 2); - const aProperties = project.checker.getPropertiesOfType( - project.checker.getTypeAtLocation(typeAliases[0]), - ); - assert.equal(project.checker.isReadonlySymbol(aProperties[1]), true); - const bProperties = project.checker.getPropertiesOfType( - project.checker.getTypeAtLocation(typeAliases[1]), - ); - assert.equal(project.checker.isReadonlySymbol(bProperties[1]), false); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getReturnTypeOfSignature", () => { - test("returns the return type of a function signature", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export function add(a: number, b: number): number { return a + b; }`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export function add(a: number, b: number): number { return a + b; }`; - const pos = src.indexOf("add("); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const returnType = project.checker.getReturnTypeOfSignature(sigs[0]); - assert.ok(returnType); - assert.ok(returnType.flags & TypeFlags.Number, `Expected number, got flags ${returnType.flags}`); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getRestTypeOfSignature", () => { - test("returns the rest type of a signature with rest parameter", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export function sum(...nums: number[]): number { return nums.reduce((a, b) => a + b, 0); }`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export function sum(...nums: number[]): number { return nums.reduce((a, b) => a + b, 0); }`; - const pos = src.indexOf("sum("); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const restType = project.checker.getRestTypeOfSignature(sigs[0]); - assert.ok(restType); - assert.ok(restType.flags & TypeFlags.Number, `Expected number type, got flags ${restType.flags}`); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getTypePredicateOfSignature", () => { - test("returns type predicate for 'x is T' guard", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export function isString(x: unknown): x is string { return typeof x === "string"; }`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export function isString(x: unknown): x is string { return typeof x === "string"; }`; - const pos = src.indexOf("isString("); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const predicate = project.checker.getTypePredicateOfSignature(sigs[0]); - assert.ok(predicate); - assert.equal(predicate.kind, TypePredicateKind.Identifier); - assert.equal(predicate.parameterName, "x"); - assert.equal(predicate.parameterIndex, 0); - assert.ok(predicate.type); - assert.ok(predicate.type.flags & TypeFlags.String); - } - finally { - api.close(); - } - }); - - test("returns type predicate for 'this is T' guard", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export class Animal { - isdog(): this is Dog { return this instanceof Dog; } -} -export class Dog extends Animal { - bark() {} -} -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\nexport class Animal {\n isdog(): this is Dog { return this instanceof Dog; }\n}\nexport class Dog extends Animal {\n bark() {}\n}\n`; - const pos = src.indexOf("isdog("); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const predicate = project.checker.getTypePredicateOfSignature(sigs[0]); - assert.ok(predicate); - assert.equal(predicate.kind, TypePredicateKind.This); - } - finally { - api.close(); - } - }); - - test("returns type predicate for 'asserts x is T'", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export function assertIsString(x: unknown): asserts x is string { if (typeof x !== "string") throw new Error(); }`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export function assertIsString(x: unknown): asserts x is string { if (typeof x !== "string") throw new Error(); }`; - const pos = src.indexOf("assertIsString("); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const predicate = project.checker.getTypePredicateOfSignature(sigs[0]); - assert.ok(predicate); - assert.equal(predicate.kind, TypePredicateKind.AssertsIdentifier); - assert.equal(predicate.parameterName, "x"); - assert.equal(predicate.parameterIndex, 0); - assert.ok(predicate.type); - assert.ok(predicate.type.flags & TypeFlags.String); - } - finally { - api.close(); - } - }); - - test("returns undefined for signature without type predicate", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export function add(a: number, b: number): number { return a + b; }`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export function add(a: number, b: number): number { return a + b; }`; - const pos = src.indexOf("add("); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const predicate = project.checker.getTypePredicateOfSignature(sigs[0]); - assert.equal(predicate, undefined); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getBaseTypes", () => { - test("returns base types of a class", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export class Base { - x: number = 0; -} -export class Derived extends Base { - y: string = ""; -} -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\nexport class Base {\n x: number = 0;\n}\nexport class Derived extends Base {\n y: string = "";\n}\n`; - const pos = src.indexOf("Derived"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getDeclaredTypeOfSymbol(symbol); - assert.ok(type); - const baseTypes = project.checker.getBaseTypes(type as InterfaceType); - assert.ok(baseTypes.length > 0, "Should have at least one base type"); - const baseSymbol = baseTypes[0].getSymbol(); - assert.ok(baseSymbol); - assert.equal(baseSymbol.name, "Base"); - } - finally { - api.close(); - } - }); - - test("returns base types of an interface", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export interface Animal { - name: string; -} -export interface Dog extends Animal { - bark(): void; -} -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\nexport interface Animal {\n name: string;\n}\nexport interface Dog extends Animal {\n bark(): void;\n}\n`; - const pos = src.indexOf("Dog"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getDeclaredTypeOfSymbol(symbol); - assert.ok(type); - const baseTypes = project.checker.getBaseTypes(type as InterfaceType); - assert.ok(baseTypes.length > 0, "Should have at least one base type"); - const baseSymbol = baseTypes[0].getSymbol(); - assert.ok(baseSymbol); - assert.equal(baseSymbol.name, "Animal"); - } - finally { - api.close(); - } - }); - - test("does not panic for a type alias to a generic interface instantiation", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export interface Box { - value: T; -} -export type BoxOfString = Box; -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - const typeAlias = sourceFile.statements.find(isTypeAliasDeclaration); - assert.ok(typeAlias); - const type = project.checker.getTypeAtLocation(typeAlias); - assert.ok(type); - // A generic interface instantiation produces a type reference, not an - // interface type, so it has no base types and yields []. - const baseTypes = project.checker.getBaseTypes(type as InterfaceType); - assert.deepEqual(baseTypes, []); - } - finally { - api.close(); - } - }); -}); - -describe("Type - getBaseTypes", () => { - test("returns base types for a class type and undefined for a non-class/interface", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export class Base { - x: number = 0; -} -export class Derived extends Base { - y: string = ""; -} -export const n: number = 0; -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\nexport class Base {\n x: number = 0;\n}\nexport class Derived extends Base {\n y: string = "";\n}\nexport const n: number = 0;\n`; - - const derivedSymbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("Derived")); - assert.ok(derivedSymbol); - const derivedType = project.checker.getDeclaredTypeOfSymbol(derivedSymbol); - assert.ok(derivedType.isClassOrInterface(), "Derived should be a class/interface type"); - const baseTypes = derivedType.getBaseTypes(); - assert.ok(baseTypes && baseTypes.length > 0, "class type should have base types"); - const baseSymbol = baseTypes![0].getSymbol(); - assert.equal(baseSymbol?.name, "Base"); - - // A primitive type is not a class/interface, so getBaseTypes() is undefined. - const numberSymbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("n:")); - assert.ok(numberSymbol); - const numberType = project.checker.getTypeOfSymbol(numberSymbol); - assert.ok(numberType); - assert.equal(numberType.isClassOrInterface(), false); - assert.equal(numberType.getBaseTypes(), undefined); - } - finally { - api.close(); - } - }); -}); - -describe("Type - isErrorType", () => { - test("identifies the error type from an unresolvable annotation", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -declare const good: string; -declare const bad: ThisTypeDoesNotExist; -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\ndeclare const good: string;\ndeclare const bad: ThisTypeDoesNotExist;\n`; - - const badSymbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("bad")); - assert.ok(badSymbol); - const badType = project.checker.getTypeOfSymbol(badSymbol); - assert.ok(badType); - assert.ok(badType.isErrorType(), "unresolved annotation should yield the error type"); - assert.ok(isErrorType(badType)); - - const goodSymbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("good")); - assert.ok(goodSymbol); - const goodType = project.checker.getTypeOfSymbol(goodSymbol); - assert.ok(goodType); - assert.equal(goodType.isErrorType(), false, "string type is not the error type"); - assert.equal(isErrorType(goodType), false); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - well-known symbols", () => { - test("isUnknownSymbol identifies the aliased unknown symbol", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export const value = 1; -export type Alias = typeof value; -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\nexport const value = 1;\nexport type Alias = typeof value;\n`; - - // A real symbol is not the unknown/undefined symbol. - const valueSymbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("value")); - assert.ok(valueSymbol); - assert.equal(project.checker.isUnknownSymbol(valueSymbol), false); - assert.equal(project.checker.isUndefinedSymbol(valueSymbol), false); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - well-known signatures", () => { - test("isUnknownSignature identifies an unresolvable call", () => { - const src = ` -const ok = (x: number) => x; -ok(1); -const notCallable = 1; -notCallable(); -`; - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": src, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - const calls: Node[] = []; - sourceFile.forEachChild(function visit(node) { - if (isCallExpression(node)) calls.push(node); - node.forEachChild(visit); - }); - assert.equal(calls.length, 2, "should find two call expressions"); - - // The valid call resolves to a real signature, not the unknown signature. - const okSig = project.checker.getResolvedSignature(calls[0]); - assert.equal(project.checker.isUnknownSignature(okSig), false); - - // The call to a non-callable value yields the unknown signature. - const badSig = project.checker.getResolvedSignature(calls[1]); - assert.equal(project.checker.isUnknownSignature(badSig), true); - } - finally { - api.close(); - } - }); -}); - -describe("Symbol - escaped names and tables", () => { - test("getExports/getMembers return a cached Map keyed by escaped name", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export class Animal { - name: string = ""; - speak(): void {} -} -export const value = 1; -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - const moduleSymbol = project.checker.getSymbolAtLocation(sourceFile); - assert.ok(moduleSymbol); - - const exports = moduleSymbol.getExports(); - assert.ok(exports.has(escapeLeadingUnderscores("Animal"))); - assert.ok(exports.get(escapeLeadingUnderscores("value"))); - // Calling again returns the same cached Map instance. - assert.strictEqual(moduleSymbol.getExports(), exports); - - const animal = exports.get(escapeLeadingUnderscores("Animal"))!; - const members = animal.getMembers(); - assert.ok(members.get(escapeLeadingUnderscores("name"))); - assert.ok(members.get(escapeLeadingUnderscores("speak"))); - assert.strictEqual(animal.getMembers(), members); - } - finally { - api.close(); - } - }); - - test("anonymous type symbol has a __-escaped name, never the \\xFE sentinel", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export const obj: { a: number } = { a: 1 }; -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\nexport const obj: { a: number } = { a: 1 };\n`; - const objSymbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("obj")); - assert.ok(objSymbol); - const objType = project.checker.getTypeOfSymbol(objSymbol); - assert.ok(objType); - const typeSymbol = objType.getSymbol(); - assert.ok(typeSymbol); - // The anonymous type literal symbol is internally named; over the wire - // it must be the "__type" escaped form, not the raw "\xFE" sentinel. - assert.equal(typeSymbol.escapedName, InternalSymbolName.Type); - assert.ok(!typeSymbol.escapedName.includes("\xFE")); - } - finally { - api.close(); - } - }); -}); - -describe("ast - escapeLeadingUnderscores", () => { - test("round-trips display and escaped names", () => { - assert.equal(escapeLeadingUnderscores("foo"), "foo"); - assert.equal(escapeLeadingUnderscores("_foo"), "_foo"); - assert.equal(escapeLeadingUnderscores("__foo"), "___foo"); - assert.equal(unescapeLeadingUnderscores("foo" as __String), "foo"); - assert.equal(unescapeLeadingUnderscores("__type" as __String), "__type"); - assert.equal(unescapeLeadingUnderscores("___foo" as __String), "__foo"); - }); -}); - -describe("ast - getJSDocTags", () => { - test("returns a node's own tags, and inherited @param / @template tags", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -/** - * Adds two numbers. - * @param a the first number - * @param b the second number - * @returns the sum - */ -export function add(a: number, b: number): number { - return a + b; -} - -/** - * @template T the element type - * @param x a value - */ -export function identity(x: T): T { - return x; -} - -/** @deprecated use add */ -export const total = add(1, 2); -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - const functions = [...sourceFile.statements].filter(isFunctionDeclaration); - const add = functions[0]; - const identity = functions[1]; - assert.ok(add); - assert.ok(identity); - - // A function reports its own JSDoc tags. - assert.deepEqual(getJSDocTags(add).map(t => t.tagName.text), ["param", "param", "returns"]); - - // A parameter inherits the matching @param tag from its containing - // signature. - const paramA = add.parameters[0]; - const paramATags = getJSDocTags(paramA); - assert.deepEqual(paramATags.map(t => t.tagName.text), ["param"]); - const paramATag = paramATags[0]; - assert.ok(isJSDocParameterTag(paramATag)); - assert.ok(isIdentifier(paramATag.name)); - assert.equal(paramATag.name.text, "a"); - - const paramB = add.parameters[1]; - const paramBTags = getJSDocTags(paramB); - assert.deepEqual(paramBTags.map(t => t.tagName.text), ["param"]); - const paramBTag = paramBTags[0]; - assert.ok(isJSDocParameterTag(paramBTag)); - assert.ok(isIdentifier(paramBTag.name)); - assert.equal(paramBTag.name.text, "b"); - - // A type parameter inherits the matching @template tag. - const typeParam = identity.typeParameters![0]; - assert.deepEqual(getJSDocTags(typeParam).map(t => t.tagName.text), ["template"]); - - // The value parameter of `identity` inherits its @param tag. - const identityParam = identity.parameters[0]; - assert.deepEqual(getJSDocTags(identityParam).map(t => t.tagName.text), ["param"]); - - // A variable declaration walks up its comment-location chain - // (declaration -> declaration list -> statement) to the JSDoc on the - // containing variable statement. - const variable = sourceFile.statements.find(isVariableStatement); - assert.ok(variable); - const declaration = variable.declarationList.declarations[0]; - assert.deepEqual(getJSDocTags(declaration).map(t => t.tagName.text), ["deprecated"]); - } - finally { - api.close(); - } - }); - - test("a function-expression parameter inherits @param tags from the variable statement", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { allowJs: true, checkJs: true } }), - "/src/main.js": ` -/** - * @param {string} name a name - * @returns {number} the length - */ -var measure = function (name) { - return name.length; -}; -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/main.js"); - assert.ok(sourceFile); - const variable = sourceFile.statements.find(isVariableStatement); - assert.ok(variable); - const declaration = variable.declarationList.declarations[0]; - const funcExpr = declaration.initializer; - assert.ok(funcExpr); - - // The variable statement carries the @param and @returns tags. - assert.deepEqual(getJSDocTags(declaration).map(t => t.tagName.text), ["param", "returns"]); - - // The function expression's parameter walks declaration -> list -> - // statement and inherits the matching @param tag. - const param = (funcExpr as unknown as { parameters: NodeArray; }).parameters[0]; - const paramTags = getJSDocTags(param); - assert.deepEqual(paramTags.map(t => t.tagName.text), ["param"]); - const paramTag = paramTags[0]; - assert.ok(isJSDocParameterTag(paramTag)); - assert.ok(isIdentifier(paramTag.name)); - assert.equal(paramTag.name.text, "name"); - } - finally { - api.close(); - } - }); - - test("a @type cast tag is owned by its parenthesized expression, not the declaration", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { allowJs: true, checkJs: true } }), - "/src/main.js": ` -/** @type {string} */ -const value = "hello"; - -const cast = /** @type {number} */ (someValue); -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/main.js"); - assert.ok(sourceFile); - const statements = [...sourceFile.statements].filter(isVariableStatement); - - // A @type tag directly on a declaration is reported for that declaration. - const valueDecl = statements[0].declarationList.declarations[0]; - assert.deepEqual(getJSDocTags(valueDecl).map(t => t.tagName.text), ["type"]); - - // A @type cast tag attached to a parenthesized expression is owned by - // that expression and is not reported for the enclosing declaration. - const castDecl = statements[1].declarationList.declarations[0]; - assert.deepEqual(getJSDocTags(castDecl).map(t => t.tagName.text), []); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getPropertiesOfType", () => { - test("returns properties of an object type", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export interface Person { - name: string; - age: number; - greet(): void; -} -export declare const p: Person; -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\nexport interface Person {\n name: string;\n age: number;\n greet(): void;\n}\nexport declare const p: Person;\n`; - const pos = src.indexOf("p: Person"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const props = project.checker.getPropertiesOfType(type); - assert.ok(props.length >= 3, `Expected at least 3 properties, got ${props.length}`); - const names = props.map(p => p.name); - assert.ok(names.includes("name"), "should have 'name' property"); - assert.ok(names.includes("age"), "should have 'age' property"); - assert.ok(names.includes("greet"), "should have 'greet' property"); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getIndexInfosOfType", () => { - test("returns index signatures of an indexed type", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export interface StringMap { - [key: string]: number; -} -export declare const m: StringMap; -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\nexport interface StringMap {\n [key: string]: number;\n}\nexport declare const m: StringMap;\n`; - const pos = src.indexOf("m: StringMap"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const indexInfos = project.checker.getIndexInfosOfType(type); - assert.ok(indexInfos.length > 0, "Should have at least one index info"); - const info = indexInfos[0]; - assert.ok(info.keyType); - assert.ok(info.keyType.flags & TypeFlags.String, `Expected string key type, got flags ${info.keyType.flags}`); - assert.ok(info.valueType); - assert.ok(info.valueType.flags & TypeFlags.Number, `Expected number value type, got flags ${info.valueType.flags}`); - assert.equal(info.isReadonly, false); - } - finally { - api.close(); - } - }); - - test("readonly index signature reports isReadonly true", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export interface ReadonlyMap { - readonly [key: string]: number; -} -export declare const m: ReadonlyMap; -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\nexport interface ReadonlyMap {\n readonly [key: string]: number;\n}\nexport declare const m: ReadonlyMap;\n`; - const pos = src.indexOf("m: ReadonlyMap"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const indexInfos = project.checker.getIndexInfosOfType(type); - assert.ok(indexInfos.length > 0); - assert.equal(indexInfos[0].isReadonly, true); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getConstraintOfTypeParameter", () => { - test("returns constraint of a type parameter", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export function identity(x: T): T { return x; }`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export function identity(x: T): T { return x; }`; - const pos = src.indexOf("identity<"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - assert.ok(sigs[0].typeParameters && sigs[0].typeParameters.length > 0, "Should have type parameters"); - const typeParams = sigs[0].getTypeParameters(); - const constraint = project.checker.getConstraintOfTypeParameter(typeParams[0]); - assert.ok(constraint); - assert.ok(constraint.flags & TypeFlags.String, `Expected string constraint, got flags ${constraint.flags}`); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - TypeParameter getters", () => { - test("getConstraint() and getDefault() return the constraint and default types", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export function f(x: T): T { return x; }`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export function f(x: T): T { return x; }`; - const pos = src.indexOf("f<"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const typeParams = sigs[0].getTypeParameters(); - assert.ok(typeParams.length > 0, "Should have type parameters"); - const typeParam = typeParams[0] as TypeParameter; - - const constraint = typeParam.getConstraint(); - assert.ok(constraint); - assert.ok(constraint.flags & TypeFlags.String, `Expected string constraint, got flags ${constraint.flags}`); - - const defaultType = typeParam.getDefault(); - assert.ok(defaultType); - assert.ok(defaultType.flags & TypeFlags.StringLiteral, `Expected string literal default, got flags ${defaultType.flags}`); - } - finally { - api.close(); - } - }); - - test("getConstraint() and getDefault() return undefined when there is none", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export function f(x: T): T { return x; }`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export function f(x: T): T { return x; }`; - const pos = src.indexOf("f<"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const typeParams = sigs[0].getTypeParameters(); - assert.ok(typeParams.length > 0, "Should have type parameters"); - const typeParam = typeParams[0] as TypeParameter; - - assert.equal(typeParam.getConstraint(), undefined); - assert.equal(typeParam.getDefault(), undefined); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getTypeArguments", () => { - test("returns type arguments of a generic instantiation", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const arr: Array = [1, 2, 3];`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export const arr: Array = [1, 2, 3];`; - const pos = src.indexOf("arr:"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const typeArgs = project.checker.getTypeArguments(type as TypeReference); - assert.ok(typeArgs.length > 0, "Should have type arguments"); - assert.ok(typeArgs[0].flags & TypeFlags.Number, `Expected number type argument, got flags ${typeArgs[0].flags}`); - } - finally { - api.close(); - } - }); - - test("a wrongly-typed call throws on the client without taking down the server", () => { - const src = `export const s: string = ""; export const arr: Array = [1];`; - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": src, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - - // `string` is not a type reference. When getTypeArguments is reached - // with one, the server panics, but the per-request panic recovery - // converts that into an error response rather than crashing the process. - const sSymbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("s:")); - assert.ok(sSymbol); - const sType = project.checker.getTypeOfSymbol(sSymbol); - assert.ok(sType); - assert.throws(() => project.checker.getTypeArguments(sType as unknown as TypeReference)); - - // The server survived: a subsequent valid request still succeeds. - const arrSymbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("arr:")); - assert.ok(arrSymbol); - const arrType = project.checker.getTypeOfSymbol(arrSymbol); - assert.ok(arrType); - const typeArgs = project.checker.getTypeArguments(arrType as TypeReference); - assert.ok(typeArgs.length > 0, "Server should still serve valid requests"); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getBaseConstraintOfType", () => { - test("returns the base constraint of a type parameter", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export function identity(x: T): T { return x; }`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `export function identity(x: T): T { return x; }`; - const pos = src.indexOf("identity<"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); - assert.ok(sigs.length > 0); - const typeParams = sigs[0].getTypeParameters(); - const constraint = project.checker.getBaseConstraintOfType(typeParams[0]); - assert.ok(constraint, "Should resolve a base constraint"); - assert.ok(constraint.flags & TypeFlags.String, `Expected string constraint, got flags ${constraint.flags}`); - } - finally { - api.close(); - } - }); - - test("returns undefined for a non-instantiable type", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const x: number = 1;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = `export const x: number = 1;`.indexOf("x:"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const constraint = project.checker.getBaseConstraintOfType(type); - assert.equal(constraint, undefined); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getPropertyOfType", () => { - test("returns a named property symbol of a type", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export interface Person { - name: string; - age: number; -} -export declare const p: Person; -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const src = `\nexport interface Person {\n name: string;\n age: number;\n}\nexport declare const p: Person;\n`; - const pos = src.indexOf("p: Person"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const nameProp = project.checker.getPropertyOfType(type, "name"); - assert.ok(nameProp, "Should find 'name' property"); - assert.equal(nameProp.name, "name"); - const missing = project.checker.getPropertyOfType(type, "doesNotExist"); - assert.equal(missing, undefined); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getConstantValue", () => { - test("returns numeric value of an enum member", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export enum E { A = 1, B = 2 }`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - let memberB: Node | undefined; - sourceFile.forEachChild(function visit(node) { - if (node.kind === SyntaxKind.EnumMember) { - const text = sourceFile.text.slice(node.pos, node.end).trim(); - if (text.startsWith("B")) memberB = node; - } - node.forEachChild(visit); - }); - assert.ok(memberB, "Should find enum member B"); - const value = project.checker.getConstantValue(memberB); - assert.equal(value, 2); - } - finally { - api.close(); - } - }); - - test("returns string value of a string-initialized enum member", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export enum Color { Red = "red" }`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - let member: Node | undefined; - sourceFile.forEachChild(function visit(node) { - if (node.kind === SyntaxKind.EnumMember) member = node; - node.forEachChild(visit); - }); - assert.ok(member); - const value = project.checker.getConstantValue(member); - assert.equal(value, "red"); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getSignatureFromDeclaration", () => { - test("returns the signature of a function declaration", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export function add(a: number, b: number): number { return a + b; }`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - let funcDecl: Node | undefined; - sourceFile.forEachChild(function visit(node) { - if (isFunctionDeclaration(node)) funcDecl = node; - node.forEachChild(visit); - }); - assert.ok(funcDecl, "Should find the function declaration"); - const sig = project.checker.getSignatureFromDeclaration(funcDecl); - assert.ok(sig, "Should resolve a signature"); - assert.equal(sig.parameters.length, 2); - const returnType = project.checker.getReturnTypeOfSignature(sig); - assert.ok(returnType); - assert.ok(returnType.flags & TypeFlags.Number); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getExportSpecifierLocalTargetSymbol", () => { - test("resolves the local target of an export specifier", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -const value = 42; -export { value as renamed }; -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - let exportSpecifier: Node | undefined; - sourceFile.forEachChild(function visit(node) { - if (node.kind === SyntaxKind.ExportSpecifier) exportSpecifier = node; - node.forEachChild(visit); - }); - assert.ok(exportSpecifier, "Should find the export specifier"); - const target = project.checker.getExportSpecifierLocalTargetSymbol(exportSpecifier); - assert.ok(target, "Should resolve a local target symbol"); - assert.equal(target.name, "value"); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getAliasedSymbol", () => { - test("resolves an import alias to its target symbol", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/foo.ts": `export const foo = 42;`, - "/src/main.ts": `import { foo } from "./foo";\nexport const usage = foo;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = `import { foo } from "./foo";`.indexOf("foo }"); - const aliasSymbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(aliasSymbol); - assert.ok(aliasSymbol.flags & SymbolFlags.Alias, "Import binding should be an alias"); - const aliased = project.checker.getAliasedSymbol(aliasSymbol); - assert.ok(aliased, "Should resolve the aliased symbol"); - assert.equal(aliased.name, "foo"); - assert.ok(!(aliased.flags & SymbolFlags.Alias), "Target should not be an alias"); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getFullyQualifiedName", () => { - test("returns module-qualified names for exported symbols and dotted names for members", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/index.ts": ` -export namespace Outer { - export class Inner {} -} -export class Standalone {} -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - const moduleSymbol = project.checker.getSymbolAtLocation(sourceFile); - assert.ok(moduleSymbol); - const exports = project.checker.getExportsOfModule(moduleSymbol); - const standalone = exports.find(e => e.name === "Standalone"); - assert.ok(standalone); - assert.equal(project.checker.getFullyQualifiedName(standalone), `"/src/index".Standalone`); - const outer = exports.find(e => e.name === "Outer"); - assert.ok(outer); - const inner = (outer.getExports()).get("Inner" as __String); - assert.ok(inner); - assert.equal(project.checker.getFullyQualifiedName(inner), `"/src/index".Outer.Inner`); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getExportsOfModule", () => { - test("returns all exports including re-exports via 'export *'", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/inner.ts": `export const innerValue = 1;`, - "/src/index.ts": ` -export const direct = 1; -export * from "./inner"; -`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - const moduleSymbol = project.checker.getSymbolAtLocation(sourceFile); - assert.ok(moduleSymbol, "Source file should have a module symbol"); - const exports = project.checker.getExportsOfModule(moduleSymbol); - const names = exports.map(e => e.name); - assert.ok(names.includes("direct"), "should include directly-declared export"); - assert.ok(names.includes("innerValue"), "should include 'export *' re-export"); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getSymbolsInScope", () => { - const scopeFiles = { - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -const outerValue = 1; -interface OuterType { x: number; } -function f() { - const innerValue = 2; - return innerValue; -} -`, - }; - - test("returns symbols visible at a position", () => { - const api = spawnAPI(scopeFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = scopeFiles["/src/main.ts"].indexOf("return innerValue"); - const symbols = project.checker.getSymbolsInScope( - { document: "/src/main.ts", position: pos }, - SymbolFlags.Value, - ); - const names = symbols.map(s => s.name); - assert.ok(names.includes("innerValue"), "should include local variable"); - assert.ok(names.includes("outerValue"), "should include outer variable"); - assert.ok(names.includes("f"), "should include enclosing function"); - assert.ok(!names.includes("OuterType"), "should not include type-only meanings"); - } - finally { - api.close(); - } - }); - - test("returns type symbols when asked for type meaning at a node", () => { - const api = spawnAPI(scopeFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - const symbols = project.checker.getSymbolsInScope(sourceFile, SymbolFlags.Type); - const names = symbols.map(s => s.name); - assert.ok(names.includes("OuterType"), "should include interface declared in file"); - assert.ok(names.includes("Array"), "should include global type symbols"); - } - finally { - api.close(); - } - }); -}); - -describe("Symbol - getDocumentationComment and getJsDocTags", () => { - const docFiles = { - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -/** - * Adds two numbers together. - * @param a the first number - * @returns the sum - */ -export function add(a: number, b: number): number { return a + b; } -`, - }; - - test("getDocumentationComment returns the leading comment text", () => { - const api = spawnAPI(docFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = docFiles["/src/main.ts"].indexOf("add(a"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const doc = symbol.getDocumentationComment(project.checker); - assert.ok(doc.includes("Adds two numbers together"), `Expected documentation, got: ${doc}`); - assert.ok(!doc.includes("@param"), "Documentation comment should not include tags"); - } - finally { - api.close(); - } - }); - - test("getJsDocTags returns structured tag name/text pairs", () => { - const api = spawnAPI(docFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = docFiles["/src/main.ts"].indexOf("add(a"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const tags = symbol.getJsDocTags(project.checker); - const param = tags.find(t => t.name === "param"); - assert.ok(param, `Expected a @param tag, got: ${JSON.stringify(tags)}`); - assert.equal(param.text, "a the first number"); - const returns = tags.find(t => t.name === "returns"); - assert.ok(returns, `Expected a @returns tag, got: ${JSON.stringify(tags)}`); - assert.equal(returns.text, "the sum"); - } - finally { - api.close(); - } - }); -}); - -describe("TypeParameter - isThisType", () => { - test("isThisType is true for the polymorphic 'this' type in a class method", () => { - const src = `\nexport class Builder {\n setName(name: string): this { return this; }\n}\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - // ": this {" — offset 2 past ': ' lands on 't' in the return-type 'this' - const pos = src.indexOf(": this {") + 2; - const type = project.checker.getTypeAtPosition("/src/main.ts", pos); - assert.ok(type, "Expected a type at the 'this' position"); - assert.ok(type.flags & TypeFlags.TypeParameter, "Expected TypeParameter"); - const typeParam = type as TypeParameter; - assert.equal(typeParam.isThisType, true); - } - finally { - api.close(); - } - }); - - test("isThisType is absent for a regular generic type parameter", () => { - const src = `\nexport function identity(x: T): T { return x; }\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - // Point to 'T' in the type parameter declaration '' — getTypeAtPosition - // on a type annotation reference doesn't resolve to TypeParameter, but - // the declaration position does. - const pos = src.indexOf("") + 1; - const type = project.checker.getTypeAtPosition("/src/main.ts", pos); - assert.ok(type, "Expected a type at the 'T' position"); - assert.ok(type.flags & TypeFlags.TypeParameter, "Expected TypeParameter"); - const typeParam = type as TypeParameter; - assert.ok(!typeParam.isThisType, "Expected isThisType to be absent/false for a regular type parameter"); - } - finally { - api.close(); - } - }); -}); - -describe("Type - getAliasTypeArguments", () => { - test("returns the type arguments of a single-param generic type alias", () => { - const src = `\ntype Box = { value: T };\nexport const x: Box = { value: "hi" };\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("x:"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const aliasArgs = type.getAliasTypeArguments(); - assert.equal(aliasArgs.length, 1, "Expected 1 alias type argument"); - assert.ok(aliasArgs[0].flags & TypeFlags.String, `Expected string, got flags ${aliasArgs[0].flags}`); - } - finally { - api.close(); - } - }); - - test("returns multiple type arguments for a multi-param generic type alias", () => { - const src = `\ntype Pair = [A, B];\nexport const p: Pair = ["hello", 42];\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("p:"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const aliasArgs = type.getAliasTypeArguments(); - assert.equal(aliasArgs.length, 2, "Expected 2 alias type arguments"); - assert.ok(aliasArgs[0].flags & TypeFlags.String, `Expected first arg to be string, got flags ${aliasArgs[0].flags}`); - assert.ok(aliasArgs[1].flags & TypeFlags.Number, `Expected second arg to be number, got flags ${aliasArgs[1].flags}`); - } - finally { - api.close(); - } - }); - - test("returns empty array for a non-alias generic type", () => { - const src = `\nexport const arr: Array = ["hello"];\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("arr:"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const aliasArgs = type.getAliasTypeArguments(); - assert.equal(aliasArgs.length, 0, "Expected no alias type arguments for a direct generic reference"); - } - finally { - api.close(); - } - }); -}); - -describe("Type - getAliasSymbol", () => { - test("returns the symbol for a non-generic type alias", () => { - // Object-type aliases preserve aliasSymbol; primitive aliases (type Foo = string) do not. - const src = `\ntype Point = { x: number; y: number };\nexport const p: Point = { x: 1, y: 2 };\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("p:"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const aliasSymbol = type.getAliasSymbol(); - assert.ok(aliasSymbol, "Expected alias symbol to exist"); - assert.equal(aliasSymbol.name, "Point"); - } - finally { - api.close(); - } - }); - - test("returns the symbol for a generic type alias", () => { - const src = `\ntype Container = { item: T };\nexport const c: Container = { item: 42 };\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("c:"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const aliasSymbol = type.getAliasSymbol(); - assert.ok(aliasSymbol, "Expected alias symbol for generic alias"); - assert.equal(aliasSymbol.name, "Container"); - } - finally { - api.close(); - } - }); - - test("returns undefined for a non-alias type", () => { - const src = `\nexport const str: string = "test";\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("str:"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - const aliasSymbol = type.getAliasSymbol(); - assert.equal(aliasSymbol, undefined, "Expected no alias symbol for primitive type"); - } - finally { - api.close(); - } - }); -}); - -describe("IntrinsicType - intrinsicName", () => { - test("intrinsicName matches the primitive type name", () => { - const src = `\nexport const x: string = "hello";\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const stringType = project.checker.getStringType(); - assert.equal((stringType as IntrinsicType).intrinsicName, "string"); - const anyType = project.checker.getAnyType(); - assert.equal((anyType as IntrinsicType).intrinsicName, "any"); - const neverType = project.checker.getNeverType(); - assert.equal((neverType as IntrinsicType).intrinsicName, "never"); - const pos = src.indexOf("x:"); - const sym = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(sym); - const litType = project.checker.getTypeOfSymbol(sym); - assert.ok(litType); - assert.ok(litType.flags & TypeFlags.Intrinsic); - assert.equal((litType as IntrinsicType).intrinsicName, "string"); - } - finally { - api.close(); - } - }); -}); - -describe("FreshableType - getFreshType and getRegularType", () => { - test("LiteralType.value is empty string for the empty-string literal type", () => { - const src = `\nexport const empty: "" = "";\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("empty:")); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.ok(type.flags & TypeFlags.StringLiteral, "Expected StringLiteral"); - const literal = type as LiteralType; - assert.equal(literal.value, "", "value should be empty string, not undefined"); - } - finally { - api.close(); - } - }); - - test("LiteralType.value is accessible via the FreshableType hierarchy", () => { - const src = `\nexport const greeting: "hello" = "hello";\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("greeting:"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.ok(type.flags & TypeFlags.StringLiteral, "Expected StringLiteral"); - const literal = type as LiteralType; - assert.equal(literal.value, "hello"); - } - finally { - api.close(); - } - }); - - test("BigIntLiteralType.value is a bigint (positive and negative)", () => { - const src = `\nexport const pos = 123n;\nexport const neg = -123n;\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - - const posSymbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("pos =")); - assert.ok(posSymbol); - const posType = project.checker.getTypeOfSymbol(posSymbol); - assert.ok(posType); - assert.ok(posType.flags & TypeFlags.BigIntLiteral, "Expected BigIntLiteral"); - const posLiteral = posType as BigIntLiteralType; - assert.equal(typeof posLiteral.value, "bigint"); - assert.equal(posLiteral.value, 123n); - - const negSymbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("neg =")); - assert.ok(negSymbol); - const negType = project.checker.getTypeOfSymbol(negSymbol); - assert.ok(negType); - assert.ok(negType.flags & TypeFlags.BigIntLiteral, "Expected BigIntLiteral"); - const negLiteral = negType as BigIntLiteralType; - assert.equal(typeof negLiteral.value, "bigint"); - assert.equal(negLiteral.value, -123n); - } - finally { - api.close(); - } - }); - - test("getFreshType() returns a fresh twin with matching value", () => { - const src = `\nexport const greeting: "hello" = "hello";\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("greeting:"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Freshable, "Type should be a freshable type"); - const fresh = (type as FreshableType).getFreshType(); - assert.ok(fresh, "Expected getFreshType() to return non-undefined for a literal type"); - assert.ok(fresh.flags & TypeFlags.StringLiteral, "Fresh type should be a StringLiteral"); - assert.equal((fresh as LiteralType).value, "hello", "Fresh type should carry the same value"); - assert.notEqual(fresh.id, type.id, "Fresh type should not be the original type"); - const freshFresh = fresh.getFreshType(); - assert.ok(freshFresh, "Expected getFreshType() to return non-undefined for a fresh type"); - assert.equal(freshFresh.id, fresh.id, "Fresh type of a fresh type should be the fresh type"); - } - finally { - api.close(); - } - }); - - test("getRegularType() on a fresh literal returns the regular twin", () => { - // The initial type response from getTypeOfSymbol does not always include the - // regularType handle, so getRegularType() is tested via the fresh twin which - // always includes its regularType in its own response. - const src = `\nexport const greeting: "hello" = "hello";\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("greeting:"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Freshable, "Type should be a freshable type"); - const fresh = (type as FreshableType).getFreshType(); - assert.ok(fresh, "Need fresh type for this test"); - assert.ok(fresh.flags & TypeFlags.Freshable, "Fresh type should be a freshable type"); - const regular = fresh.getRegularType(); - assert.ok(regular, "Expected getRegularType() on the fresh twin to return non-undefined"); - assert.ok(regular.flags & TypeFlags.StringLiteral, "Regular type should be a StringLiteral"); - assert.equal((regular as LiteralType).value, "hello", "Regular type should carry the same value"); - assert.equal(regular.id, type.id, "Regular type should be the original type"); - } - finally { - api.close(); - } - }); - - test("getFreshType() and getRegularType() work for computed enum types (TypeFlags.Enum)", () => { - // getTypeOfSymbol on an ambient enum member returns the FRESH computed enum type. - // For fresh types: getFreshType() returns self, getRegularType() returns the regular twin. - const src = `\ndeclare enum Status { Pending }\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("Pending"); - const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(symbol); - const type = project.checker.getTypeOfSymbol(symbol); - assert.ok(type); - assert.ok(type.flags & TypeFlags.Enum, `Expected TypeFlags.Enum, got ${type.flags}`); - assert.ok(type.flags & TypeFlags.Freshable, "Enum type should be freshable"); - // The returned type IS the fresh type: getFreshType() returns itself - const fresh = (type as FreshableType).getFreshType(); - assert.ok(fresh, "Expected getFreshType() to return non-undefined"); - assert.equal(fresh.id, type.id, "getFreshType() on a fresh enum type returns itself"); - // getRegularType() returns the regular twin (a different type) - const regular = (type as FreshableType).getRegularType(); - assert.ok(regular, "Expected getRegularType() to return non-undefined"); - assert.ok(regular.flags & TypeFlags.Enum, "Regular enum type should also have TypeFlags.Enum"); - assert.notEqual(regular.id, type.id, "Regular type should be distinct from the fresh type"); - // Round-trip: regular → getFreshType() → back to the original fresh type - const backToFresh = (regular as FreshableType).getFreshType(); - assert.ok(backToFresh); - assert.equal(backToFresh.id, type.id, "Round-trip through regular/fresh returns the original type"); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - isContextSensitive", () => { - test("arrow function with no type annotation is context sensitive", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export const fn = (x) => x;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - // Find the arrow function node - let arrowFn: import("@typescript/typescript/unstable/ast").Node | undefined; - sourceFile.forEachChild(function visit(node) { - if (node.kind === SyntaxKind.ArrowFunction) { - arrowFn = node; - } - node.forEachChild(visit); - }); - assert.ok(arrowFn, "Should find an arrow function"); - const result = project.checker.isContextSensitive(arrowFn); - assert.equal(result, true); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - isTypeAssignableTo", () => { - test("returns true when source is assignable to target", () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": `export {};`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const stringType = project.checker.getStringType(); - const anyType = project.checker.getAnyType(); - const neverType = project.checker.getNeverType(); - assert.ok(project.checker.isTypeAssignableTo(stringType, stringType), "string assignable to string"); - assert.ok(project.checker.isTypeAssignableTo(stringType, anyType), "string assignable to any"); - assert.ok(project.checker.isTypeAssignableTo(neverType, stringType), "never assignable to string (bottom type)"); - } - finally { - api.close(); - } - }); - - test("returns false when source is not assignable to target", () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": `export {};`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const stringType = project.checker.getStringType(); - const numberType = project.checker.getNumberType(); - assert.ok(!project.checker.isTypeAssignableTo(numberType, stringType), "number not assignable to string"); - assert.ok(!project.checker.isTypeAssignableTo(stringType, numberType), "string not assignable to number"); - } - finally { - api.close(); - } - }); - - test("a string literal type is assignable to string but not number", () => { - const src = `\nexport const x: "hello" = "hello";\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/main.ts": src, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const pos = src.indexOf("x:"); - const sym = project.checker.getSymbolAtPosition("/src/main.ts", pos); - assert.ok(sym); - const litType = project.checker.getTypeOfSymbol(sym); - assert.ok(litType); - assert.ok(litType.flags & TypeFlags.StringLiteral); - const stringType = project.checker.getStringType(); - const numberType = project.checker.getNumberType(); - assert.ok(project.checker.isTypeAssignableTo(litType, stringType)); - assert.ok(!project.checker.isTypeAssignableTo(litType, numberType)); - } - finally { - api.close(); - } - }); -}); - -describe("Emitter - printNode", () => { - const emitterFiles = { - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": ` -export const x = 42; -export function greet(name: string): string { return name; } -export type Pair = [string, number]; -export const obj = { m: 1, s: "hi", b: true }; -`, - }; - - test("printNode with factory-created keyword type", () => { - const api = spawnAPI(emitterFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const node = createKeywordTypeNode(SyntaxKind.StringKeyword); - const text = project.emitter.printNode(node); - assert.strictEqual(text, "string"); - } - finally { - api.close(); - } - }); - - test("printNode with factory-created union type", () => { - const api = spawnAPI(emitterFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const node = createUnionTypeNode([ - createKeywordTypeNode(SyntaxKind.StringKeyword), - createKeywordTypeNode(SyntaxKind.NumberKeyword), - ]); - const text = project.emitter.printNode(node); - assert.strictEqual(text, "string | number"); - } - finally { - api.close(); - } - }); - - test("printNode with factory-created function type", () => { - const api = spawnAPI(emitterFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const param = createParameterDeclaration( - undefined, - undefined, - createIdentifier("x"), - undefined, - createKeywordTypeNode(SyntaxKind.StringKeyword), - undefined, - ); - const node = createFunctionTypeNode( - undefined, - [param], - createKeywordTypeNode(SyntaxKind.NumberKeyword), - ); - const text = project.emitter.printNode(node); - assert.strictEqual(text, "(x: string) => number"); - } - finally { - api.close(); - } - }); - - test("printNode with factory-created type reference", () => { - const api = spawnAPI(emitterFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const node = createTypeReferenceNode(createIdentifier("Array"), [ - createKeywordTypeNode(SyntaxKind.StringKeyword), - ]); - const text = project.emitter.printNode(node); - assert.strictEqual(text, "Array"); - } - finally { - api.close(); - } - }); - - test("printNode with factory-created array type", () => { - const api = spawnAPI(emitterFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const node = createArrayTypeNode(createKeywordTypeNode(SyntaxKind.NumberKeyword)); - const text = project.emitter.printNode(node); - assert.strictEqual(text, "number[]"); - } - finally { - api.close(); - } - }); - - test("typeToTypeNode + printNode round-trip", () => { - const api = spawnAPI(emitterFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const { checker, emitter } = snapshot.getProject("/tsconfig.json")!; - const src = emitterFiles["/src/main.ts"]; - - const greetPos = src.indexOf("greet("); - const symbol = checker.getSymbolAtPosition("/src/main.ts", greetPos); - assert.ok(symbol); - const type = checker.getTypeOfSymbol(symbol); - assert.ok(type); - const typeNode = checker.typeToTypeNode(type); - assert.ok(typeNode); - const text = emitter.printNode(typeNode); - assert.ok(text); - assert.strictEqual(text, "(name: string) => string"); - } - finally { - api.close(); - } - }); - - test("visitEachChild on typeToTypeNode result with keyword types", () => { - const api = spawnAPI(emitterFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const { checker } = snapshot.getProject("/tsconfig.json")!; - const src = emitterFiles["/src/main.ts"]; - const objPos = src.indexOf("obj"); - const symbol = checker.getSymbolAtPosition("/src/main.ts", objPos); - assert.ok(symbol, "should find symbol for obj"); - const type = checker.getTypeOfSymbol(symbol); - assert.ok(type); - const typeNode = checker.typeToTypeNode(type); - assert.ok(typeNode, "typeToTypeNode should return a type node"); - - // Recursively visit to reach PropertySignature.type where isTypeNode is checked. - const visited = (function visit(node: Node): Node { - return visitEachChild(node, visit); - })(typeNode); - assert.ok(visited, "visitEachChild should not throw"); - - const kinds = [ - SyntaxKind.NumberKeyword, - SyntaxKind.StringKeyword, - SyntaxKind.BooleanKeyword, - SyntaxKind.AnyKeyword, - SyntaxKind.VoidKeyword, - SyntaxKind.UndefinedKeyword, - SyntaxKind.NeverKeyword, - SyntaxKind.UnknownKeyword, - SyntaxKind.BigIntKeyword, - SyntaxKind.ObjectKeyword, - SyntaxKind.SymbolKeyword, - SyntaxKind.IntrinsicKeyword, - SyntaxKind.ExpressionWithTypeArguments, - SyntaxKind.JSDocAllType, - SyntaxKind.JSDocNullableType, - SyntaxKind.JSDocNonNullableType, - SyntaxKind.JSDocOptionalType, - SyntaxKind.JSDocVariadicType, - SyntaxKind.JSDocTypeExpression, - SyntaxKind.JSDocTypeLiteral, - SyntaxKind.JSDocSignature, - ]; - for (const kind of kinds) { - assert.ok(isTypeNode({ kind } as any), `isTypeNode should accept ${SyntaxKind[kind]}`); - } - } - finally { - api.close(); - } - }); - - test("typeToString", () => { - const api = spawnAPI(emitterFiles); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const { checker } = snapshot.getProject("/tsconfig.json")!; - const src = emitterFiles["/src/main.ts"]; - - const greetPos = src.indexOf("greet("); - const symbol = checker.getSymbolAtPosition("/src/main.ts", greetPos); - assert.ok(symbol); - const type = checker.getTypeOfSymbol(symbol); - assert.ok(type); - const text = checker.typeToString(type); - assert.strictEqual(text, "(name: string) => string"); - } - finally { - api.close(); - } - }); - - test("typeToString with TypeFormatFlags", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `export function greet(name: string): string[] { return [name]; }`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const { checker } = snapshot.getProject("/tsconfig.json")!; - const greetPos = "export function greet".indexOf("greet"); - const symbol = checker.getSymbolAtPosition("/src/main.ts", greetPos); - assert.ok(symbol); - const type = checker.getTypeOfSymbol(symbol); - assert.ok(type); - const text = checker.typeToString(type, undefined, TypeFormatFlags.WriteArrayAsGenericType); - assert.strictEqual(text, "(name: string) => Array"); - } - finally { - api.close(); - } - }); - - test("printNode with terminateUnterminatedLiterals option", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/main.ts": `const foo = /asdfasf;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/main.ts"); - assert.ok(sourceFile); - - // Find the regex literal node - let regexNode: import("@typescript/typescript/unstable/ast").Node | undefined; - sourceFile.forEachChild(function visit(node) { - if (node.kind === SyntaxKind.RegularExpressionLiteral) { - regexNode = node; - return; - } - node.forEachChild(visit); - }); - assert.ok(regexNode, "Should find a regex literal"); - - // Without the option, regex is printed as-is - const textWithout = project.emitter.printNode(regexNode); - assert.strictEqual(textWithout, "/asdfasf"); - - // With the option, the closing slash is added - const textWith = project.emitter.printNode(regexNode, { terminateUnterminatedLiterals: true }); - assert.strictEqual(textWith, "/asdfasf/"); - } - finally { - api.close(); - } - }); -}); - -describe("Program - selected file emit", () => { - const files = { - "/tsconfig.json": JSON.stringify({ - compilerOptions: { - declarationMap: true, - emitDeclarationOnly: true, - noEmit: true, - noEmitOnError: true, - sourceMap: true, - }, - }), - "/src/a.ts": `export const a: string = 1;`, - "/src/b.ts": `export const b = 2;`, - }; - - test("getJavaScriptEmit forces JavaScript and source maps", () => { - const { api, fs } = spawnAPIWithFS({ ...files }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const result = project.program.getJavaScriptEmit(["/src/a.ts", "/src/b.ts"]); - assert.equal(result.emitSkipped, false); - assert.deepEqual([...result.outputFiles.keys()], [ - "/src/a.js", - "/src/a.js.map", - "/src/b.js", - "/src/b.js.map", - ]); - assert.equal(result.outputFiles.get("/src/a.js")?.sourceFileName, "/src/a.ts"); - assert.match(result.outputFiles.get("/src/a.js")!.text, /export const a = 1/); - assert.equal(fs.readFile?.("/src/a.js"), undefined); - } - finally { - api.close(); - } - }); - - test("getDeclarationEmit forces declarations and declaration maps", () => { - const { api, fs } = spawnAPIWithFS({ ...files }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const result = project.program.getDeclarationEmit(["/src/a.ts", "/src/b.ts"]); - assert.equal(result.emitSkipped, false); - assert.deepEqual([...result.outputFiles.keys()], [ - "/src/a.d.ts", - "/src/a.d.ts.map", - "/src/b.d.ts", - "/src/b.d.ts.map", - ]); - assert.equal(result.outputFiles.get("/src/a.d.ts")?.sourceFileName, "/src/a.ts"); - assert.equal(fs.readFile?.("/src/a.d.ts"), undefined); - } - finally { - api.close(); - } - }); - - test("selected file emit accepts empty arrays", () => { - const api = spawnAPI({ ...files }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - assert.deepEqual((project.program.getJavaScriptEmit([])).outputFiles, new Map()); - assert.deepEqual((project.program.getDeclarationEmit([])).outputFiles, new Map()); - } - finally { - api.close(); - } - }); -}); - -describe("SnapshotInternalAPI - formatNodeForInsertion", () => { - test("formats a synthesized statement with correct indentation for insertion inside a function body", () => { - const files = { - "/tsconfig.json": "{}", - "/src/index.ts": `function greet(name: string) {\n console.log(name);\n}\n`, - }; - const api = spawnAPI(files); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - - const node = createVariableStatement( - undefined, - createVariableDeclarationList( - [createVariableDeclaration(createIdentifier("x"), undefined, undefined, createNumericLiteral("1", 0))], - NodeFlags.Const, - ), - ); - - const sourceText = files["/src/index.ts"]; - const insertionPos = sourceText.indexOf("\n console.log") + 1; - - const formatted = snapshot.internal.formatNodeForInsertion(node, "/src/index.ts", insertionPos); - assert.ok(formatted.includes("const x = 1;"), `Expected 'const x = 1;' in formatted output, got: ${JSON.stringify(formatted)}`); - assert.ok(formatted.startsWith(" "), `Expected 4 spaces of indentation, got: ${JSON.stringify(formatted)}`); - } - finally { - api.close(); - } - }); - - test("formats a node at top-level with no indentation", () => { - const files = { - "/tsconfig.json": "{}", - "/src/index.ts": `const a = 1;\nconst b = 2;\n`, - }; - const api = spawnAPI(files); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - - const node = createVariableStatement( - undefined, - createVariableDeclarationList( - [createVariableDeclaration(createIdentifier("y"), undefined, undefined, createNumericLiteral("42", 0))], - NodeFlags.Const, - ), - ); - - const formatted = snapshot.internal.formatNodeForInsertion(node, "/src/index.ts", 0); - assert.ok(formatted.includes("const y = 42;"), `Expected 'const y = 42;' in formatted output, got: ${JSON.stringify(formatted)}`); - assert.ok(!formatted.startsWith(" "), `Expected no leading spaces at top level, got: ${JSON.stringify(formatted)}`); - } - finally { - api.close(); - } - }); - - test("formats a synthesized statement with correct indentation when non-ASCII characters precede the insertion position", () => { - // 'é' is 1 UTF-16 code unit but 2 UTF-8 bytes, so UTF-16 and UTF-8 offsets diverge after it. - // This test verifies the position is correctly converted from UTF-16 to UTF-8 before use. - const files = { - "/tsconfig.json": "{}", - "/src/index.ts": `// \u00e9\nfunction greet(name: string) {\n console.log(name);\n}\n`, - }; - const api = spawnAPI(files); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - - const node = createVariableStatement( - undefined, - createVariableDeclarationList( - [createVariableDeclaration(createIdentifier("x"), undefined, undefined, createNumericLiteral("1", 0))], - NodeFlags.Const, - ), - ); - - // insertionPos is the UTF-16 code-unit offset of the start of the indented line inside the function body. - const sourceText = files["/src/index.ts"]; - const insertionPos = sourceText.indexOf("\n console.log") + 1; - - const formatted = snapshot.internal.formatNodeForInsertion(node, "/src/index.ts", insertionPos); - // The node should be indented to match the function body (4 spaces) - assert.ok(formatted.includes("const x = 1;"), `Expected 'const x = 1;' in formatted output, got: ${JSON.stringify(formatted)}`); - assert.ok(formatted.startsWith(" "), `Expected 4 spaces of indentation (UTF-16 offset correctly converted), got: ${JSON.stringify(formatted)}`); - } - finally { - api.close(); - } - }); -}); - -describe("modifierFlags", () => { - test("export async function has Export | Async flags", () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `export async function foo() {}`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - - let fnNode: import("@typescript/typescript/unstable/ast").FunctionDeclaration | undefined; - sourceFile.forEachChild(function visit(node) { - if (isFunctionDeclaration(node)) { - fnNode = node; - } - node.forEachChild(visit); - }); - assert.ok(fnNode, "Should find a function declaration"); - assert.ok(fnNode.modifierFlags & ModifierFlags.Export, "Should have Export flag"); - assert.ok(fnNode.modifierFlags & ModifierFlags.Async, "Should have Async flag"); - assert.strictEqual(fnNode.modifierFlags, ModifierFlags.Export | ModifierFlags.Async); - } - finally { - api.close(); - } - }); - - test("node without modifiers has ModifierFlags.None", () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `function bar() {}`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - - let fnNode: import("@typescript/typescript/unstable/ast").FunctionDeclaration | undefined; - sourceFile.forEachChild(function visit(node) { - if (isFunctionDeclaration(node)) { - fnNode = node; - } - node.forEachChild(visit); - }); - assert.ok(fnNode, "Should find a function declaration"); - assert.strictEqual(fnNode.modifierFlags, ModifierFlags.None); - } - finally { - api.close(); - } - }); -}); - -describe("Checker - getResolvedSymbol", () => { - test("resolves variable reference to its declaration symbol", () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `const x = 1;\nconst y = x;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - - // Find the 'x' identifier in `const y = x` - let refNode: import("@typescript/typescript/unstable/ast").Identifier | undefined; - sourceFile.forEachChild(function visit(node) { - if (isIdentifier(node) && node.text === "x") { - // We want the reference, not the declaration - take the last one - refNode = node; - } - node.forEachChild(visit); - }); - assert.ok(refNode, "Should find identifier 'x'"); - - const symbol = project.checker.getResolvedSymbol(refNode); - assert.ok(symbol, "Should resolve symbol for 'x'"); - assert.equal(symbol.name, "x"); - } - finally { - api.close(); - } - }); -}); - -describe("VariableDeclarationList - BlockScoped flags", () => { - test("let declaration has Let flag", () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `let x = 1;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - - let declList: import("@typescript/typescript/unstable/ast").Node | undefined; - sourceFile.forEachChild(function visit(node) { - if (isVariableDeclarationList(node)) { - declList = node; - } - node.forEachChild(visit); - }); - assert.ok(declList, "Should find VariableDeclarationList"); - assert.ok(declList.flags & NodeFlags.Let, "Should have Let flag"); - } - finally { - api.close(); - } - }); - - test("const declaration has Const flag", () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `const x = 1;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - - let declList: import("@typescript/typescript/unstable/ast").Node | undefined; - sourceFile.forEachChild(function visit(node) { - if (isVariableDeclarationList(node)) { - declList = node; - } - node.forEachChild(visit); - }); - assert.ok(declList, "Should find VariableDeclarationList"); - assert.ok(declList.flags & NodeFlags.Const, "Should have Const flag"); - } - finally { - api.close(); - } - }); -}); - -test("TypeOperator operator kind", () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `function test(arg: readonly number[]) { }\n`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - assert(sourceFile); - const param = (sourceFile.statements[0] as import("@typescript/typescript/unstable/ast").FunctionDeclaration).parameters[0]; - assert(param); - const type = param.type as import("@typescript/typescript/unstable/ast").TypeOperatorNode; - assert(type); - assert.equal(type.kind, SyntaxKind.TypeOperator); - assert.equal(type.operator, SyntaxKind.ReadonlyKeyword); - const printed = project.emitter.printNode(sourceFile); - assert.equal(sourceFile.text, printed); - } - finally { - api.close(); - } -}); - -test("SpreadAssignment roundtrip", () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `var thing = { ...other };\n`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - assert(sourceFile); - const stmt = sourceFile.statements[0] as import("@typescript/typescript/unstable/ast").VariableStatement; - const object = stmt.declarationList.declarations[0].initializer as import("@typescript/typescript/unstable/ast").ObjectLiteralExpression; - const assignment = object.properties[0] as import("@typescript/typescript/unstable/ast").SpreadAssignment; - assert(assignment); - assert.equal(assignment.kind, SyntaxKind.SpreadAssignment); - const expr = assignment.expression; - assert(expr); - assert.equal(expr.kind, SyntaxKind.Identifier); - const printed = project.emitter.printNode(sourceFile); - assert.equal(sourceFile.text, printed); - } - finally { - api.close(); - } -}); - -test("VariableDeclarationList const flag clone", () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `const thing = 123;\n`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - assert(sourceFile); - { - const stmt = sourceFile.statements[0] as import("@typescript/typescript/unstable/ast").VariableStatement; - const list = stmt.declarationList; - assert(list.flags & NodeFlags.Const); - } - const cloned = getSynthesizedDeepClone(sourceFile); - { - const stmt = cloned.statements[0] as import("@typescript/typescript/unstable/ast").VariableStatement; - const list = stmt.declarationList; - assert(list.flags & NodeFlags.Const); - } - const printed = project.emitter.printNode(cloned); - assert.equal(sourceFile.text, printed); - } - finally { - api.close(); - } -}); - -test("JSDoc before ExpressionStatement allowed", () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": ` -/** - * A doc. - */ -doThing(); - `, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - assert(sourceFile); - const printed = project.emitter.printNode(sourceFile); - assert.equal(sourceFile.text.trim(), printed.trim()); - } - finally { - api.close(); - } -}); - -test("Factory ModifierList auto-conversion", () => { - const api = spawnAPI(); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const node = createTypeAliasDeclaration( - [createToken(SyntaxKind.ExportKeyword)], - createIdentifier("Test"), - undefined, - createKeywordTypeNode(SyntaxKind.AnyKeyword), - ); - - assert.equal(project.emitter.printNode(node), "export type Test = any;"); - - const cloned = getSynthesizedDeepClone(node); - assert.equal(project.emitter.printNode(cloned), "export type Test = any;"); - } - finally { - api.close(); - } -}); - -test("Parse-clone-emit roundtrip", () => { - const tsSource = fileURLToPath(new URL("../../../../tsc/testdata/fixtures", import.meta.url).toString()); - const api = new API({ - cwd: tsSource, - }); - const target = { - cloneCrashed: 0, - printCrashed: 0, - clonePrintCrashed: 0, - }; - const errors = { ...target }; - try { - for (const tsconfig of globSync("**/tsconfig.json", { cwd: tsSource })) { - const snapshot = api.updateSnapshot({ openProject: resolve(tsSource, tsconfig) }); - const project = snapshot.getProject(tsconfig); - assert(project); - for (const file of project.rootFiles) { - const source = project.program.getSourceFile(file); - assert(source); - let clone: typeof source; - - try { - project.emitter.printNode(source); - } - catch { - errors.printCrashed++; - continue; - } - - try { - clone = getSynthesizedDeepClone(source); - } - catch { - errors.cloneCrashed++; - continue; - } - - try { - project.emitter.printNode(clone); - } - catch { - errors.clonePrintCrashed++; - continue; - } - } - } - } - finally { - api.close(); - } - assert.deepEqual(errors, target); -}); - -describe("Program - diagnostics", () => { - test("getSyntacticDiagnostics", () => { - const source = `const x: = 1;`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": source, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = project.program.getSyntacticDiagnostics("/src/index.ts"); - assert.deepEqual(diags[0].startPosition, { line: 0, character: 9 }); - assert.deepEqual(diags[0].endPosition, { line: 0, character: 10 }); - assert.deepEqual(diags[0].sourceLines, [{ line: 0, text: source }]); - assert.deepEqual(withoutFormattingContext(diags), [{ - fileName: "/src/index.ts", - ...rangeOf(source, "="), - code: 1110, - category: DiagnosticCategory.Error, - text: "Type expected.", - }]); - } - finally { - api.close(); - } - }); - - test("getSemanticDiagnostics with messageChain and relatedInformation", () => { - const source = `interface Props { callback: (x: string) => void }\nconst p: Props = { callback: (x: number) => {} };`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": source, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = project.program.getSemanticDiagnostics("/src/index.ts"); - const declRange = rangeOf(source, "callback", 0); - const assignRange = rangeOf(source, "callback", 1); - assert.deepEqual(withoutFormattingContext(diags), [{ - fileName: "/src/index.ts", - ...assignRange, - code: 2322, - category: DiagnosticCategory.Error, - text: "Type '(x: number) => void' is not assignable to type '(x: string) => void'.", - messageChain: [{ - fileName: "/src/index.ts", - ...assignRange, - code: 2328, - category: DiagnosticCategory.Error, - text: "Types of parameters 'x' and 'x' are incompatible.", - messageChain: [{ - fileName: "/src/index.ts", - ...assignRange, - code: 2322, - category: DiagnosticCategory.Error, - text: "Type 'string' is not assignable to type 'number'.", - }], - }], - relatedInformation: [{ - fileName: "/src/index.ts", - ...declRange, - code: 6500, - category: DiagnosticCategory.Message, - text: "The expected type comes from property 'callback' which is declared here on type 'Props'", - }], - }]); - } - finally { - api.close(); - } - }); - - test("getSuggestionDiagnostics", () => { - const source = `export function f() { const x = 1; return x; }\nconst _unused = 1;\n`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": source, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = project.program.getSuggestionDiagnostics("/src/index.ts"); - assert.deepEqual(withoutFormattingContext(diags), [{ - fileName: "/src/index.ts", - ...rangeOf(source, "_unused"), - code: 6133, - category: DiagnosticCategory.Suggestion, - text: "'_unused' is declared but its value is never read.", - reportsUnnecessary: true, - }]); - } - finally { - api.close(); - } - }); - - test("getConfigFileParsingDiagnostics", () => { - const config = `{ "compilerOptions": { "target": "invalid" } }`; - const api = spawnAPI({ - "/tsconfig.json": config, - "/src/index.ts": `export const x = 1;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = project.program.getConfigFileParsingDiagnostics(); - assert.deepEqual(withoutFormattingContext(diags), [{ - fileName: "/tsconfig.json", - ...rangeOf(config, `"invalid"`), - code: 6046, - category: DiagnosticCategory.Error, - text: "Argument for '--target' option must be: 'es6', 'es2015', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'es2021', 'es2022', 'es2023', 'es2024', 'es2025', 'esnext'.", - }]); - } - finally { - api.close(); - } - }); - - test("getConfigFileNames and getConfigSourceFile", () => { - const baseConfigText = `{ "compilerOptions": { "strict": true } }`; - const { api, fs } = spawnAPIWithFS({ - "/tsconfig.base.json": baseConfigText, - "/tsconfig.json": `{ "extends": "./tsconfig.base.json", "files": ["./src/index.ts"] }`, - "/src/index.ts": `export const x = 1;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const names = project.program.getConfigFileNames(); - assert.deepEqual(names, ["/tsconfig.json", "/tsconfig.base.json"]); - - const rootConfig = project.program.getConfigSourceFile("/tsconfig.json"); - assert.ok(rootConfig); - assert.equal(rootConfig.fileName, "/tsconfig.json"); - assert.equal(project.program.getSourceFile("/tsconfig.json"), undefined); - - fs.writeFile!("/tsconfig.base.json", `{ "compilerOptions": { "strict": false } }`); - const extendedConfig = project.program.getConfigSourceFile("/tsconfig.base.json"); - assert.ok(extendedConfig); - assert.equal(extendedConfig.fileName, "/tsconfig.base.json"); - assert.equal(extendedConfig.getFullText(), baseConfigText); - - const nonConfig = project.program.getConfigSourceFile("/src/index.ts"); - assert.equal(nonConfig, undefined); - } - finally { - api.close(); - } - }); - - test("getDeclarationDiagnostics", () => { - const api = spawnAPI({ - "/tsconfig.json": `{ "compilerOptions": { "declaration": true } }`, - "/src/index.ts": `export const x: number = 1;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = project.program.getDeclarationDiagnostics("/src/index.ts"); - assert.deepEqual(diags, []); - } - finally { - api.close(); - } - }); - - test("getBindDiagnostics", () => { - const source = `let x = 1;\nlet x = 2;`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": source, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = project.program.getBindDiagnostics("/src/index.ts"); - assert.deepEqual(withoutFormattingContext(diags), [ - { - fileName: "/src/index.ts", - ...rangeOf(source, "x", 0), - code: 2451, - category: DiagnosticCategory.Error, - text: "Cannot redeclare block-scoped variable 'x'.", - }, - { - fileName: "/src/index.ts", - ...rangeOf(source, "x", 1), - code: 2451, - category: DiagnosticCategory.Error, - text: "Cannot redeclare block-scoped variable 'x'.", - }, - ]); - } - finally { - api.close(); - } - }); - - test("getProgramDiagnostics", () => { - const config = `{ "compilerOptions": { "moduleResolution": "bundler", "module": "nodenext" } }`; - const api = spawnAPI({ - "/tsconfig.json": config, - "/src/index.ts": `export const x = 1;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = project.program.getProgramDiagnostics(); - assert.deepEqual(withoutFormattingContext(diags), [ - { - fileName: "/tsconfig.json", - ...rangeOf(config, `"bundler"`), - code: 5095, - category: DiagnosticCategory.Error, - text: "Option 'bundler' can only be used when 'module' is set to 'preserve', 'commonjs', or 'es2015' or later.", - }, - { - fileName: "/tsconfig.json", - ...rangeOf(config, `"bundler"`), - code: 5109, - category: DiagnosticCategory.Error, - text: "Option 'moduleResolution' must be set to 'NodeNext' (or left unspecified) when option 'module' is set to 'NodeNext'.", - }, - ]); - } - finally { - api.close(); - } - }); - - test("getGlobalDiagnostics", () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `export const x = 1;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = project.program.getGlobalDiagnostics(); - assert.deepEqual(diags, []); - } - finally { - api.close(); - } - }); - - test("getGlobalDiagnostics returns file-less diagnostics from the checker", () => { - const api = spawnAPI({ - "/tsconfig.json": `{ "compilerOptions": { "noLib": true } }`, - "/src/index.ts": `export const x = [1, 2, 3];`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = project.program.getGlobalDiagnostics(); - // With noLib, the checker reports "Cannot find global type" diagnostics that - // are not associated with any source file. - assert.ok(diags.length > 0, "expected global diagnostics to be reported"); - for (const diag of diags) { - assert.equal(diag.fileName, undefined); - assert.equal(diag.code, 2318); - assert.equal(diag.category, DiagnosticCategory.Error); - } - assert.ok( - diags.some(d => d.text === "Cannot find global type 'Array'."), - "expected a global diagnostic for the 'Array' type", - ); - } - finally { - api.close(); - } - }); - - test("getSyntacticDiagnostics with multiple files", () => { - const sourceA = `const a: = 1;`; - const sourceB = `const b: = 2;`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/a.ts": sourceA, - "/src/b.ts": sourceB, - "/src/clean.ts": `const c = 3;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = project.program.getSyntacticDiagnostics(["/src/a.ts", "/src/b.ts"]); - assert.deepEqual(withoutFormattingContext(diags), [ - { - fileName: "/src/a.ts", - ...rangeOf(sourceA, "="), - code: 1110, - category: DiagnosticCategory.Error, - text: "Type expected.", - }, - { - fileName: "/src/b.ts", - ...rangeOf(sourceB, "="), - code: 1110, - category: DiagnosticCategory.Error, - text: "Type expected.", - }, - ]); - } - finally { - api.close(); - } - }); - - test("getSemanticDiagnostics with multiple files", () => { - const sourceA = `export const a: number = "hello";`; - const sourceB = `export const b: string = 42;`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/a.ts": sourceA, - "/src/b.ts": sourceB, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = project.program.getSemanticDiagnostics(["/src/a.ts", "/src/b.ts"]); - assert.equal(diags.length, 2); - assert.equal(diags[0].fileName, "/src/a.ts"); - assert.equal(diags[0].code, 2322); - assert.equal(diags[1].fileName, "/src/b.ts"); - assert.equal(diags[1].code, 2322); - } - finally { - api.close(); - } - }); - - test("getBindDiagnostics with multiple files", () => { - const sourceA = `let x = 1;\nlet x = 2;`; - const sourceB = `let y = 1;\nlet y = 2;`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/a.ts": sourceA, - "/src/b.ts": sourceB, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = project.program.getBindDiagnostics(["/src/a.ts", "/src/b.ts"]); - assert.equal(diags.length, 4); - assert.equal(diags.filter(d => d.fileName === "/src/a.ts").length, 2); - assert.equal(diags.filter(d => d.fileName === "/src/b.ts").length, 2); - } - finally { - api.close(); - } - }); - - test("diagnostics with no files argument returns all", () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/a.ts": `const a: = 1;`, - "/src/b.ts": `const b: = 2;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = project.program.getSyntacticDiagnostics(); - assert.equal(diags.length, 2); - } - finally { - api.close(); - } - }); - - test("diagnostics with empty files array returns empty", () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/a.ts": `const a: = 1;`, - "/src/b.ts": `const b: = 2;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const diags = project.program.getSyntacticDiagnostics([]); - assert.deepEqual(diags, []); - } - finally { - api.close(); - } - }); -}); - -describe("getDefaultProjectForFile", () => { - test("finds inferred project for d.ts in node_modules after openFiles", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/index.ts": `export const x = 1;`, - "/node_modules/my-lib/package.json": JSON.stringify({ name: "my-lib", types: "./index.d.ts" }), - "/node_modules/my-lib/index.d.ts": `export declare const foo: string;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - - // The d.ts is not imported, so it is not in the project's program - const dtsSf = project.program.getSourceFile("/node_modules/my-lib/index.d.ts"); - assert.equal(dtsSf, undefined, "d.ts not in import graph should not be found via project.program.getSourceFile"); - - // Before opening the file, getDefaultProjectForFile returns undefined (no error) - const noProject = snapshot.getDefaultProjectForFile("/node_modules/my-lib/index.d.ts"); - assert.equal(noProject, undefined, "getDefaultProjectForFile returns undefined for unloaded file"); - - // Load the file into the inferred project via updateSnapshot openFiles - const snapshot2 = api.updateSnapshot({ openFiles: ["/node_modules/my-lib/index.d.ts"] }); - const defaultProject = snapshot2.getDefaultProjectForFile("/node_modules/my-lib/index.d.ts"); - assert.ok(defaultProject, "getDefaultProjectForFile should find inferred project after openFiles"); - - const fooPos = `export declare const foo: string;`.indexOf("foo"); - const fooType = defaultProject.checker.getTypeAtPosition("/node_modules/my-lib/index.d.ts", fooPos); - assert.ok(fooType); - assert.ok(fooType.flags & TypeFlags.String); - } - finally { - api.close(); - } - }); - - test("inferred project reflects file changes without a follow-up open/close request", () => { - const { api, fs } = spawnAPIWithFS({ - "/loose.ts": `export const foo = 1;`, - }); - try { - const snapshot1 = api.updateSnapshot({ openFiles: ["/loose.ts"] }); - const project1 = snapshot1.getDefaultProjectForFile("/loose.ts"); - assert.ok(project1, "file with no config file in its ancestry should load into the inferred project"); - const sf1 = project1.program.getSourceFile("/loose.ts"); - assert.ok(sf1); - assert.equal(sf1.text, `export const foo = 1;`); - - // Mutate the file and notify only via fileChanges — no follow-up openFiles/closeFiles. - fs.writeFile!("/loose.ts", `export const foo = 2;`); - const snapshot2 = api.updateSnapshot({ - fileChanges: { changed: ["/loose.ts"] }, - }); - - const project2 = snapshot2.getDefaultProjectForFile("/loose.ts"); - assert.ok(project2); - const sf2 = project2.program.getSourceFile("/loose.ts"); - assert.ok(sf2); - assert.equal( - sf2.text, - `export const foo = 2;`, - "inferred project's program should reflect the file change even without a follow-up open/close request", - ); - } - finally { - api.close(); - } - }); - - test("keeps previously opened files open across subsequent openFiles calls", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/index.ts": `export const x = 1;`, - "/node_modules/my-lib/package.json": JSON.stringify({ name: "my-lib", types: "./index.d.ts" }), - "/node_modules/my-lib/index.d.ts": `export declare const foo: string;`, - "/node_modules/other-lib/package.json": JSON.stringify({ name: "other-lib", types: "./index.d.ts" }), - "/node_modules/other-lib/index.d.ts": `export declare const bar: number;`, - }); - try { - api.updateSnapshot({ openProject: "/tsconfig.json" }); - api.updateSnapshot({ openFiles: ["/node_modules/my-lib/index.d.ts"] }); - - // Opening a second file in a later snapshot must not close the first one. - const snapshot = api.updateSnapshot({ openFiles: ["/node_modules/other-lib/index.d.ts"] }); - - const firstProject = snapshot.getDefaultProjectForFile("/node_modules/my-lib/index.d.ts"); - assert.ok(firstProject, "previously opened file should remain in the inferred project"); - const secondProject = snapshot.getDefaultProjectForFile("/node_modules/other-lib/index.d.ts"); - assert.ok(secondProject, "newly opened file should be in the inferred project"); - } - finally { - api.close(); - } - }); - - test("opening a file resolves to a configured project via ancestor search", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/index.ts": `export const x = 1;`, - }); - try { - // Open the file without first opening the project. Like LSP's didOpen, this - // should search ancestor directories for a tsconfig that contains the file. - const snapshot = api.updateSnapshot({ openFiles: ["/src/index.ts"] }); - const defaultProject = snapshot.getDefaultProjectForFile("/src/index.ts"); - assert.ok(defaultProject, "should find a project for the opened file"); - assert.equal( - defaultProject.configFileName, - "/tsconfig.json", - "opened file should resolve to the containing configured project, not the inferred project", - ); - } - finally { - api.close(); - } - }); - - test("closeProjects releases a project opened via openProjects", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/index.ts": `export const x = 1;`, - }); - try { - const opened = api.updateSnapshot({ openProjects: ["/tsconfig.json"] }); - assert.ok(opened.getProject("/tsconfig.json"), "project should be open after openProjects"); - - const closed = api.updateSnapshot({ closeProjects: ["/tsconfig.json"] }); - assert.equal( - closed.getProject("/tsconfig.json"), - undefined, - "project should be unloaded after closeProjects", - ); - } - finally { - api.close(); - } - }); - - test("closeFiles releases a file opened via openFiles", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/index.ts": `export const x = 1;`, - "/node_modules/my-lib/package.json": JSON.stringify({ name: "my-lib", types: "./index.d.ts" }), - "/node_modules/my-lib/index.d.ts": `export declare const foo: string;`, - }); - try { - api.updateSnapshot({ openProject: "/tsconfig.json" }); - const opened = api.updateSnapshot({ openFiles: ["/node_modules/my-lib/index.d.ts"] }); - assert.ok( - opened.getDefaultProjectForFile("/node_modules/my-lib/index.d.ts"), - "file should resolve to a project after openFiles", - ); - - const closed = api.updateSnapshot({ closeFiles: ["/node_modules/my-lib/index.d.ts"] }); - assert.equal( - closed.getDefaultProjectForFile("/node_modules/my-lib/index.d.ts"), - undefined, - "file should no longer resolve to a project after closeFiles", - ); - } - finally { - api.close(); - } - }); -}); - -describe("Program - emit", () => { - const files = { - "/tsconfig.json": `{ - "compilerOptions": { - "outDir": "dist", - "declaration": true, - } - }`, - "/src/index.ts": "export const x: number = 1;", - "/src/testing.ts": "export const y: string = 'typescript';", - }; - - test("emit all files", () => { - const fs = createVirtualFileSystem({ ...files }); - - const api = new API({ - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), - fs: fs, - }); - - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const result = project.program.emit(); - assert.deepEqual(result, { - diagnostics: [], - emitSkipped: false, - emittedFiles: [ - "/dist/src/index.js", - "/dist/src/index.d.ts", - "/dist/src/testing.js", - "/dist/src/testing.d.ts", - ], - }); - - const js = fs.readFile?.("/dist/src/index.js"); - const dts = fs.readFile?.("/dist/src/index.d.ts"); - const js2 = fs.readFile?.("/dist/src/testing.js"); - const dts2 = fs.readFile?.("/dist/src/testing.d.ts"); - assert.strictEqual(js, `export const x = 1;\n`); - assert.strictEqual(dts, `export declare const x: number;\n`); - assert.strictEqual(js2, `export const y = 'typescript';\n`); - assert.strictEqual(dts2, `export declare const y: string;\n`); - } - finally { - api.close(); - } - }); - - test("emit only dts", () => { - const fs = createVirtualFileSystem({ ...files }); - - const api = new API({ - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), - fs: fs, - }); - - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const result = project.program.emit(EmitOnly.OnlyDts); - assert.deepEqual(result, { - diagnostics: [], - emitSkipped: false, - emittedFiles: [ - "/dist/src/index.d.ts", - "/dist/src/testing.d.ts", - ], - }); - - const js = fs.readFile?.("/dist/src/index.js"); - const dts = fs.readFile?.("/dist/src/index.d.ts"); - const js2 = fs.readFile?.("/dist/src/testing.js"); - const dts2 = fs.readFile?.("/dist/src/testing.d.ts"); - assert.strictEqual(js, undefined); - assert.strictEqual(dts, `export declare const x: number;\n`); - assert.strictEqual(js2, undefined); - assert.strictEqual(dts2, `export declare const y: string;\n`); - } - finally { - api.close(); - } - }); - - test("emit only js", () => { - const fs = createVirtualFileSystem({ ...files }); - - const api = new API({ - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), - fs: fs, - }); - - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const result = project.program.emit(EmitOnly.OnlyJs); - assert.deepEqual(result, { - diagnostics: [], - emitSkipped: false, - emittedFiles: [ - "/dist/src/index.js", - "/dist/src/testing.js", - ], - }); - - const js = fs.readFile?.("/dist/src/index.js"); - const dts = fs.readFile?.("/dist/src/index.d.ts"); - const js2 = fs.readFile?.("/dist/src/testing.js"); - const dts2 = fs.readFile?.("/dist/src/testing.d.ts"); - assert.strictEqual(js, `export const x = 1;\n`); - assert.strictEqual(dts, undefined); - assert.strictEqual(js2, `export const y = 'typescript';\n`); - assert.strictEqual(dts2, undefined); - } - finally { - api.close(); - } - }); - - test("emitToString emits the whole program and respects emitOnly", () => { - const { api, fs } = spawnAPIWithFS({ ...files }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const result = project.program.emitToString(EmitOnly.OnlyDts); - assert.deepEqual([...result.outputFiles.keys()], [ - "/dist/src/index.d.ts", - "/dist/src/testing.d.ts", - ]); - assert.equal(fs.readFile?.("/dist/src/index.js"), undefined); - } - finally { - api.close(); - } - }); - - test("whole-program emit includes option-controlled maps", () => { - const { api, fs } = spawnAPIWithFS({ - "/tsconfig.json": JSON.stringify({ - compilerOptions: { - declaration: true, - declarationMap: true, - outDir: "dist", - sourceMap: true, - }, - }), - "/src/index.ts": "export const value: number = 1;", - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - - const result = project.program.emit(); - assert.deepEqual( - new Set(result.emittedFiles), - new Set([ - "/dist/src/index.js", - "/dist/src/index.js.map", - "/dist/src/index.d.ts", - "/dist/src/index.d.ts.map", - ]), - ); - assert.ok(fs.fileExists?.("/dist/src/index.js.map")); - assert.ok(fs.fileExists?.("/dist/src/index.d.ts.map")); - - const js = project.program.emitToString(EmitOnly.OnlyJs); - assert.deepEqual([...js.outputFiles.keys()], [ - "/dist/src/index.js", - "/dist/src/index.js.map", - ]); - - const dts = project.program.emitToString(EmitOnly.OnlyDts); - assert.deepEqual([...dts.outputFiles.keys()], [ - "/dist/src/index.d.ts", - "/dist/src/index.d.ts.map", - ]); - } - finally { - api.close(); - } - }); - - test("noEmitOnError reports diagnostics without writing output", () => { - const { api, fs } = spawnAPIWithFS({ - "/tsconfig.json": JSON.stringify({ - compilerOptions: { - noEmitOnError: true, - outDir: "dist", - }, - }), - "/src/bad.ts": "export const bad = ;", - "/src/good.ts": "export const value = 1;", - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const result = project.program.emit(); - assert.equal(result.emitSkipped, true); - assert.ok(result.diagnostics.some(d => d.code === 1109)); - assert.deepEqual(result.emittedFiles, []); - assert.equal(fs.readFile?.("/dist/src/bad.js"), undefined); - assert.equal(fs.readFile?.("/dist/src/good.js"), undefined); - - const stringResult = project.program.emitToString(); - assert.equal(stringResult.emitSkipped, true); - assert.ok(stringResult.diagnostics.some(d => d.code === 1109)); - assert.deepEqual(stringResult.outputFiles, new Map()); - } - finally { - api.close(); - } - }); - - test("emit and emitToString respect noEmit", () => { - const { api, fs } = spawnAPIWithFS({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { noEmit: true } }), - "/src/index.ts": "export const value = 1;", - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - assert.deepEqual(project.program.emit(), { - diagnostics: [], - emitSkipped: false, - emittedFiles: [], - }); - assert.deepEqual(project.program.emitToString(), { - diagnostics: [], - emitSkipped: false, - outputFiles: new Map(), - }); - assert.equal(fs.readFile?.("/src/index.js"), undefined); - } - finally { - api.close(); - } - }); - - test("emit rejects unknown files and invalid emitOnly values", () => { - const api = spawnAPI({ ...files }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - - let error: unknown; - try { - project.program.getJavaScriptEmit(["/src/missing.ts"]); - } - catch (e) { - error = e; - } - assert.match(String(error), /source file not found/); - - error = undefined; - try { - project.program.emitToString(3 as EmitOnly); - } - catch (e) { - error = e; - } - assert.match(String(error), /invalid emitOnly value/); - } - finally { - api.close(); - } - }); -}); - -test("Benchmarks", () => { - runBenchmarks({ singleIteration: true }); -}); - -describe("Timing", () => { - test("collects combined client, server, and transport timing info", () => { - const api = new API({ - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), - fs: createVirtualFileSystem({ ...defaultFiles }), - collectTiming: true, - }); - try { - // Baseline: enabled, but nothing measured yet. - let info = api.getTimingInfo(); - assert.equal(info.enabled, true); - assert.equal(info.totals.requestCount, 0); - assert.equal(info.recentRequests.length, 0); - - // Exercise a JSON request and a binary source-file request. - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - - // Client-side timing: round-trip latency and byte counts. - info = api.getTimingInfo(); - assert.ok(info.totals.requestCount >= 2, "expected at least two measured requests"); - assert.ok(info.totals.bytesSent > 0); - assert.ok(info.totals.bytesReceived > 0); - assert.ok(info.totals.roundTripMs >= 0); - - // Server-side timing is folded into the same snapshot. - assert.ok(info.totals.serverTimeMs >= 0); - assert.ok(info.totals.transportOverheadMs >= 0); - assert.equal( - info.totals.transportOverheadMs, - Math.max(0, info.totals.roundTripMs - info.totals.serverTimeMs), - ); - - // The ring buffer retains at most the 5 most recent requests. - assert.ok(info.recentRequests.length > 0); - assert.ok(info.recentRequests.length <= 5); - assert.ok(info.recentRequests.length <= info.totals.requestCount); - - for (const r of info.recentRequests) { - assert.ok(r.roundTripMs >= 0); - assert.ok(r.bytesSent >= 0); - assert.ok(r.bytesReceived >= 0); - assert.ok(typeof r.method === "string" && r.method.length > 0); - assert.equal(typeof r.timestamp, "number"); - // Transport overhead is present exactly when server time is. - assert.equal(r.transportOverheadMs === undefined, r.serverTimeMs === undefined); - if (r.serverTimeMs !== undefined) { - assert.ok(r.serverTimeMs >= 0); - assert.equal(r.transportOverheadMs, Math.max(0, r.roundTripMs - r.serverTimeMs)); - } - } - - // Server processing time is folded in for the recent requests. - assert.ok(info.recentRequests.every(r => r.serverTimeMs !== undefined), "server time should be reported"); - - // Reset clears totals and history on both client and server. - api.resetTimingInfo(); - info = api.getTimingInfo(); - assert.equal(info.enabled, true); - assert.equal(info.totals.requestCount, 0); - assert.equal(info.totals.serverTimeMs, 0); - assert.equal(info.totals.transportOverheadMs, 0); - assert.equal(info.recentRequests.length, 0); - } - finally { - api.close(); - } - }); - - test("tracks source-file node materialization", () => { - const api = new API({ - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), - fs: createVirtualFileSystem({ ...defaultFiles }), - collectTiming: true, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sourceFile = project.program.getSourceFile("/src/index.ts"); - assert.ok(sourceFile); - - // The source file node itself is pre-cached, so before walking the - // tree no descendant nodes have been materialized. - let info = api.getTimingInfo(); - const before = info.totals.nodesMaterialized; - assert.equal(info.totals.sourceFilesFetched, 1, "one source file was fetched"); - assert.ok(info.totals.nodesFetched > 0, "the fetched file contributes materializable nodes"); - assert.equal(info.totals.nodesMaterialized, 0, "nothing walked yet, so no nodes materialized"); - - // Walk the whole tree to force lazy materialization of every node. - let visited = 0; - sourceFile.forEachChild(function visit(node) { - visited++; - node.forEachChild(visit); - }); - assert.ok(visited > 0, "expected to visit at least one node"); - - info = api.getTimingInfo(); - assert.ok( - info.totals.nodesMaterialized > before, - "walking the tree should materialize nodes", - ); - assert.ok( - info.totals.nodesMaterialized >= visited, - "every visited node should have been materialized", - ); - - // A full walk materializes (nearly) every fetched node, so the share - // of fetched nodes materialized should be substantial. - assert.equal(info.totals.sourceFilesFetched, 1); - assert.ok( - info.totals.nodesMaterialized > 0 - && info.totals.nodesMaterialized <= info.totals.nodesFetched, - "materialized nodes should be in (0, nodesFetched]", - ); - - // Reset clears materialization totals along with the rest. - api.resetTimingInfo(); - info = api.getTimingInfo(); - assert.equal(info.totals.nodesMaterialized, 0); - assert.equal(info.totals.sourceFilesFetched, 0); - assert.equal(info.totals.nodesFetched, 0); - } - finally { - api.close(); - } - }); - - test("is disabled by default", () => { - const api = spawnAPI(); - try { - api.parseConfigFile("/tsconfig.json"); - const info = api.getTimingInfo(); - assert.equal(info.enabled, false); - assert.equal(info.totals.requestCount, 0); - assert.equal(info.totals.nodesMaterialized, 0); - assert.equal(info.totals.sourceFilesFetched, 0); - assert.equal(info.totals.nodesFetched, 0); - assert.equal(info.recentRequests.length, 0); - } - finally { - api.close(); - } - }); -}); - -describe("runWithTemporaryFileUpdate", () => { - test("temporary file update is visible in the callback and reverted afterward", () => { - const api = spawnAPI({ - "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), - "/src/index.ts": `export const x: number = 1;`, - }); - try { - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - - // The original content type-checks cleanly. - const baseDiags = project.program.getSemanticDiagnostics("/src/index.ts"); - assert.equal(baseDiags.length, 0); - - // Keep a newer snapshot active to verify any active snapshot can be the base. - const latestSnapshot = api.updateSnapshot(); - assert.notEqual(latestSnapshot.id, snapshot.id); - - // Inside the callback, the file has the temporary (erroneous) content. - let errorCount = -1; - api.runWithTemporaryFileUpdate(snapshot, "/src/index.ts", `export const x: string = 1;`, tempSnapshot => { - const tempProject = tempSnapshot.getProject("/tsconfig.json")!; - const diags = tempProject.program.getSemanticDiagnostics("/src/index.ts"); - errorCount = diags.length; - }); - assert.ok(errorCount > 0, "temporary content should produce a semantic error inside the callback"); - - // The original snapshot is unaffected by the temporary update. - const afterDiags = project.program.getSemanticDiagnostics("/src/index.ts"); - assert.equal(afterDiags.length, 0); - - // Subsequent regular updates still work and diff against the real latest snapshot. - const snapshot2 = api.updateSnapshot(); - const project2 = snapshot2.getProject("/tsconfig.json")!; - const diags2 = project2.program.getSemanticDiagnostics("/src/index.ts"); - assert.equal(diags2.length, 0); - } - finally { - api.close(); - } - }); -}); - -function spawnAPIWithFS(files: Record = { ...defaultFiles }): { api: API; fs: FileSystem; } { - const fs = createVirtualFileSystem(files); - const api = new API({ - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), - fs, - }); - return { api, fs }; -} - -/** Returns `{ pos, end }` for the nth (0-based, default 0) occurrence of `searchString` in `source`. */ -function rangeOf(source: string, searchString: string, occurrence: number = 0): { pos: number; end: number; } { - let index = -1; - for (let i = 0; i <= occurrence; i++) { - index = source.indexOf(searchString, index + 1); - if (index === -1) { - throw new Error(`Occurrence ${occurrence} of "${searchString}" not found in source`); - } - } - return { pos: index, end: index + searchString.length }; -} - -function withoutFormattingContext(value: T): T { - const formattingKeys = new Set(["startPosition", "endPosition", "sourceLines"]); - return JSON.parse(JSON.stringify(value, (key, item) => formattingKeys.has(key) ? undefined : item)) as T; -} - -function applyTextEdits(source: string, edits: readonly TextEdit[]): string { - const sorted = [...edits].sort((a, b) => b.pos - a.pos); - let result = source; - for (const edit of sorted) { - result = result.slice(0, edit.pos) + edit.newText + result.slice(edit.end); - } - return result; -} +// +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// !!! THIS FILE IS AUTO-GENERATED - DO NOT EDIT !!! +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// +// Source: test/async/api.test.ts +// Regenerate: npm run generate (from packages/typescript) +// +import { + type __String, + cast, + escapeLeadingUnderscores, + type Expression, + getJSDocTags, + getSynthesizedDeepClone, + InternalSymbolName, + isCallExpression, + isExpressionStatement, + isFunctionDeclaration, + isIdentifier, + isImportDeclaration, + isInterfaceDeclaration, + isJSDocParameterTag, + isNamedImports, + isReturnStatement, + isShorthandPropertyAssignment, + isStringLiteral, + isTemplateHead, + isTemplateMiddle, + isTemplateTail, + isTypeAliasDeclaration, + isTypeNode, + isVariableDeclarationList, + isVariableStatement, + type Node, + type NodeArray, + NodeFlags, + SyntaxKind, + unescapeLeadingUnderscores, +} from "@typescript/typescript/unstable/ast"; +import { + createArrayTypeNode, + createFunctionTypeNode, + createIdentifier, + createKeywordTypeNode, + createNumericLiteral, + createParameterDeclaration, + createToken, + createTypeAliasDeclaration, + createTypeReferenceNode, + createUnionTypeNode, + createVariableDeclaration, + createVariableDeclarationList, + createVariableStatement, +} from "@typescript/typescript/unstable/ast/factory"; +import { visitEachChild } from "@typescript/typescript/unstable/ast/visitor"; +import { createVirtualFileSystem } from "@typescript/typescript/unstable/fs"; +import type { FileSystem } from "@typescript/typescript/unstable/fs"; +import { + API, + type BigIntLiteralType, + CheckFlags, + type ConditionalType, + DiagnosticCategory, + EmitOnly, + type FreshableType, + type ImportAdderAction, + type IndexedAccessType, + type IndexType, + type InterfaceType, + type IntrinsicType, + isErrorType, + type LiteralType, + ModifierFlags, + ModuleKind, + ObjectFlags, + type Signature, + SignatureKind, + type StringMappingType, + SymbolFlags, + type TemplateLiteralType, + type TextEdit, + TypeFlags, + TypeFormatFlags, + type TypeParameter, + TypePredicateKind, + type TypeReference, + type UnionOrIntersectionType, +} from "@typescript/typescript/unstable/sync"; +import assert from "node:assert"; +import { globSync } from "node:fs"; +import { resolve } from "node:path"; +import { + describe, + test, +} from "node:test"; +import { fileURLToPath } from "node:url"; +import { isSignatureDeclaration } from "../../src/ast/is.ts"; +import { runBenchmarks } from "./api.bench.ts"; +import { + defaultFiles, + spawnAPI, +} from "./api.testUtils.ts"; + +describe("API", () => { + test("parseCommandLine", () => { + const api = spawnAPI(); + try { + const commandLine = api.parseCommandLine([ + "--strict", + "--outDir", + "dist", + "/src/index.ts", + ]); + assert.deepEqual(commandLine.fileNames, ["/src/index.ts"]); + assert.equal(commandLine.options.strict, true); + assert.equal( + resolve(commandLine.options.outDir!), + resolve(fileURLToPath(new URL("../../../../", import.meta.url)), "dist"), + ); + assert.deepEqual(commandLine.raw, { + strict: true, + outDir: "dist", + }); + assert.deepEqual(commandLine.errors, []); + } + finally { + api.close(); + } + }); + + test("parseCommandLine reports diagnostics", () => { + const api = spawnAPI(); + try { + const commandLine = api.parseCommandLine(["--notAnOption"]); + assert.deepEqual(commandLine.fileNames, []); + assert.equal(commandLine.errors.length, 1); + assert.equal(commandLine.errors[0].code, 5023); + } + finally { + api.close(); + } + }); + + test("readConfigFile", () => { + const api = spawnAPI({ + "/tsconfig.json": `{ + // Comments and trailing commas are supported. + "compilerOptions": { "strict": true, }, + }`, + }); + try { + const result = api.readConfigFile("/tsconfig.json"); + assert.deepEqual(result, { + config: { compilerOptions: { strict: true } }, + }); + } + finally { + api.close(); + } + }); + + test("readConfigFile reports read and parse errors", () => { + const api = spawnAPI({ + "/invalid.json": `{ "compilerOptions": { "strict": true,, } }`, + }); + try { + const invalid = api.readConfigFile("/invalid.json"); + assert.deepEqual(invalid.config, { compilerOptions: { strict: true } }); + assert.ok(invalid.error); + + const missing = api.readConfigFile("/missing.json"); + assert.deepEqual(missing.config, {}); + assert.equal(missing.error?.code, 5083); + } + finally { + api.close(); + } + }); + + test("parseJsonConfigFileContent with configDirectory", () => { + const api = spawnAPI(); + try { + const config = api.parseJsonConfigFileContent( + { + compilerOptions: { strict: true }, + files: ["index.ts"], + }, + { configDirectory: "/src" }, + ); + assert.deepEqual(config.fileNames, ["/src/index.ts"]); + assert.equal(config.options.strict, true); + assert.equal("configFilePath" in config.options, false); + assert.equal(config.compileOnSave, false); + assert.deepEqual(config.errors, []); + } + finally { + api.close(); + } + }); + + test("parseJsonConfigFileContent accepts non-object JSON", () => { + const api = spawnAPI(); + try { + const config = api.parseJsonConfigFileContent(null, { configDirectory: "/src" }); + assert.deepEqual(config.fileNames, ["/src/index.ts", "/src/foo.ts"]); + assert.deepEqual(config.errors, []); + } + finally { + api.close(); + } + }); + + test("parseJsonConfigFileContent with configFileName", () => { + const api = spawnAPI(); + try { + const config = api.parseJsonConfigFileContent( + { + compilerOptions: { strict: true }, + files: ["index.ts"], + }, + { configFileName: "/src/tsconfig.json" }, + ); + assert.deepEqual(config.fileNames, ["/src/index.ts"]); + assert.equal(config.options.strict, true); + assert.equal(config.options.configFilePath, "/src/tsconfig.json"); + assert.deepEqual(config.errors, []); + } + finally { + api.close(); + } + }); + + test("parseJsonConfigFileContent preserves raw config", () => { + const api = spawnAPI(); + try { + const input = { + compileOnSave: true, + customSetting: { enabled: true }, + files: ["index.ts"], + }; + const config = api.parseJsonConfigFileContent(input, { configDirectory: "/src" }); + assert.deepEqual(config.raw, input); + assert.equal(config.compileOnSave, true); + } + finally { + api.close(); + } + }); + + test("parseJsonConfigFileContent preserves an empty files list", () => { + const api = spawnAPI(); + try { + const config = api.parseJsonConfigFileContent( + { files: [] }, + { configDirectory: "/src" }, + ); + assert.deepEqual(config.fileNames, []); + assert.equal(config.errors.length, 1); + assert.equal(config.errors[0].code, 18002); + } + finally { + api.close(); + } + }); + + test("parseJsonConfigFileContent reports null array elements", () => { + const api = spawnAPI(); + try { + const config = api.parseJsonConfigFileContent( + { files: [null], include: [null], exclude: [null] }, + { configDirectory: "/src" }, + ); + assert.equal(config.errors.length, 3); + assert.ok(config.errors.every(diagnostic => diagnostic.code === 5024)); + } + finally { + api.close(); + } + }); + + test("transpile", () => { + const api = spawnAPI({ + "/input.ts": "export const x: number = 1;", + }); + try { + const moduleOutput = api.transpileModule("export const x: number = 1;", { + compilerOptions: { module: ModuleKind.CommonJS }, + }); + assert.match(moduleOutput.outputText, /exports\.x = 1/); + + const isolatedDeclarationModuleOutput = api.transpileModule("export const x: number = 1;", { + compilerOptions: { declaration: true, isolatedDeclarations: true }, + reportDiagnostics: true, + }); + assert.equal(isolatedDeclarationModuleOutput.diagnostics?.length ?? 0, 0); + + const moduleFileOutput = api.transpileModuleFromFile("/input.ts", { + compilerOptions: { module: ModuleKind.CommonJS }, + }); + assert.match(moduleFileOutput.outputText, /exports\.x = 1/); + + const declarationOutput = api.transpileDeclaration("export const x: number = 1;"); + assert.equal(declarationOutput.outputText, "export declare const x: number;\n"); + + const windowsDeclarationOutput = api.transpileDeclaration("export const x: number = 1;", { + fileName: "C:/Users/me/project/input.ts", + }); + assert.equal(windowsDeclarationOutput.outputText, "export declare const x: number;\n"); + + const declarationFileOutput = api.transpileDeclarationFromFile("/input.ts"); + assert.equal(declarationFileOutput.outputText, "export declare const x: number;\n"); + } + finally { + api.close(); + } + }); + + test("parseConfigFile", () => { + const api = spawnAPI(); + try { + const config = api.parseConfigFile("/tsconfig.json"); + assert.deepEqual(config.fileNames, ["/src/index.ts", "/src/foo.ts"]); + assert.deepEqual(config.options, { configFilePath: "/tsconfig.json" }); + assert.equal(config.compileOnSave, false); + assert.equal(config.typeAcquisition, undefined); + assert.equal(config.projectReferences, undefined); + } + finally { + api.close(); + } + }); + + test("parseConfigFile includes projectReferences", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ references: [{ path: "./harness" }, { path: "./server" }] }), + }); + try { + const config = api.parseConfigFile("/tsconfig.json"); + assert.deepEqual(config.fileNames, []); + assert.deepEqual(config.options, { configFilePath: "/tsconfig.json" }); + assert.deepEqual(config.projectReferences, [ + { circular: false, originalPath: "./harness", path: "/harness" }, + { circular: false, originalPath: "./server", path: "/server" }, + ]); + } + finally { + api.close(); + } + }); + + test("parseConfigFile preserves raw config", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ + compileOnSave: true, + customSetting: { enabled: true }, + files: ["/src/index.ts"], + }), + }); + try { + const config = api.parseConfigFile("/tsconfig.json"); + assert.equal(config.compileOnSave, true); + assert.deepEqual(config.raw, { + compileOnSave: true, + customSetting: { enabled: true }, + files: ["/src/index.ts"], + }); + } + finally { + api.close(); + } + }); + + test("parseConfigFile includes compileOnSave", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compileOnSave: true }), + }); + try { + const config = api.parseConfigFile("/tsconfig.json"); + assert.equal(config.compileOnSave, true); + } + finally { + api.close(); + } + }); + + test("parseConfigFile includes typeAcquisition", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ typeAcquisition: { enable: true, include: ["jquery"] } }), + }); + try { + const config = api.parseConfigFile("/tsconfig.json"); + assert.equal(config.typeAcquisition?.enable, true); + assert.deepEqual(config.typeAcquisition?.include, ["jquery"]); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getImmediateAliasedSymbol", () => { + test("resolves one level of alias indirection", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/foo.ts": `export const foo = 42;`, + "/src/main.ts": `import { foo } from "./foo";\nexport const usage = foo;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = `import { foo } from "./foo";`.indexOf("foo }"); + const aliasSymbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(aliasSymbol); + const aliased = project.checker.getImmediateAliasedSymbol(aliasSymbol); + assert.ok(aliased, "Should resolve the immediate aliased symbol"); + assert.equal(aliased.name, "foo"); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getTargetSymbol", () => { + test("gets the target symbol of instantiated symbol", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +class Base { + private value!: T; +} +class Alpha extends Base {} +class Bravo extends Base {} + +declare function test(): void; +test(); +test(); +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const nodes: Array = []; + sourceFile.forEachChild(node => { + if (isExpressionStatement(node) && isCallExpression(node.expression) && node.expression.typeArguments) { + nodes.push(node.expression.typeArguments[0]); + } + }); + const aType = project.checker.getTypeAtLocation(nodes[0]); + const bType = project.checker.getTypeAtLocation(nodes[1]); + const aProperty = (project.checker.getPropertiesOfType(aType))[0]; + const bProperty = (project.checker.getPropertiesOfType(bType))[0]; + assert.ok(aProperty); + assert.ok(bProperty); + assert.equal(aProperty === bProperty, false); + assert.equal(project.checker.getTargetSymbol(aProperty) === project.checker.getTargetSymbol(bProperty), true); + } + finally { + api.close(); + } + }); +}); + +describe("Snapshot", () => { + test("updateSnapshot returns snapshot with projects", () => { + const api = spawnAPI(); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + assert.ok(snapshot); + assert.ok(snapshot.id); + assert.ok(snapshot.getProjects().length > 0); + assert.ok(snapshot.getProject("/tsconfig.json")); + } + finally { + api.close(); + } + }); + + test("project exposes parsedCommandLine", () => { + const api = spawnAPI({ + ...defaultFiles, + "/tsconfig.json": JSON.stringify({ compileOnSave: true }), + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + assert.deepEqual(project.parsedCommandLine.fileNames, ["/src/index.ts", "/src/foo.ts"]); + assert.deepEqual(project.parsedCommandLine.options, { configFilePath: "/tsconfig.json" }); + assert.equal(project.parsedCommandLine.compileOnSave, true); + assert.deepEqual(project.rootFiles, project.parsedCommandLine.fileNames); + assert.deepEqual(project.compilerOptions, project.parsedCommandLine.options); + } + finally { + api.close(); + } + }); + + test("getSymbolAtPosition", () => { + const api = spawnAPI(); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = project.checker.getSymbolAtPosition("/src/index.ts", 9); + assert.ok(symbol); + assert.equal(symbol.name, "foo"); + assert.ok(symbol.flags & SymbolFlags.Alias); + } + finally { + api.close(); + } + }); + + test("getSymbolAtLocation", () => { + const api = spawnAPI(); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + const node = cast( + cast(sourceFile.statements[0], isImportDeclaration).importClause?.namedBindings, + isNamedImports, + ).elements[0].name; + assert.ok(node); + const symbol = project.checker.getSymbolAtLocation(node); + assert.ok(symbol); + assert.equal(symbol.name, "foo"); + assert.ok(symbol.flags & SymbolFlags.Alias); + } + finally { + api.close(); + } + }); + + test("getSymbolOfSourceFile", () => { + const api = spawnAPI(); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const moduleSymbol = project.checker.getSymbolOfSourceFile("/src/foo.ts"); + assert.ok(moduleSymbol); + const exports = moduleSymbol.getExports(); + assert.ok(exports.has(escapeLeadingUnderscores("foo"))); + } + finally { + api.close(); + } + }); + + test("getSymbolOfSourceFile returns undefined for a non-module file", () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/script.ts": `const x = 1;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = project.checker.getSymbolOfSourceFile("/src/script.ts"); + assert.equal(symbol, undefined); + } + finally { + api.close(); + } + }); + + test("getSymbolOfSourceFile batched", () => { + const api = spawnAPI(); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbols = project.checker.getSymbolOfSourceFile(["/src/index.ts", "/src/foo.ts"]); + assert.equal(symbols.length, 2); + assert.ok(symbols[0]); + assert.ok(symbols[1]); + assert.strictEqual(symbols[1], project.checker.getSymbolOfSourceFile("/src/foo.ts")); + } + finally { + api.close(); + } + }); + + test("getTypeOfSymbol", () => { + const api = spawnAPI(); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = project.checker.getSymbolAtPosition("/src/index.ts", 9); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.ok(type.flags & TypeFlags.NumberLiteral); + } + finally { + api.close(); + } + }); + + test("getNonMissingTypeOfSymbol", () => { + const api = spawnAPI({ + "/tsconfig-one.json": JSON.stringify({ + compilerOptions: { + exactOptionalPropertyTypes: true, + strict: true, + }, + }), + "/tsconfig-two.json": JSON.stringify({ + compilerOptions: { + exactOptionalPropertyTypes: false, + strict: true, + }, + }), + "/src/index.ts": "const x: Partial<{ a: string }> = {};", + }); + + try { + // when `"exactOptionalPropertyTypes": true` + const snapshot1 = api.updateSnapshot({ openProject: "/tsconfig-one.json" }); + const project1 = snapshot1.getProject("/tsconfig-one.json")!; + const type1 = project1.checker.getTypeAtPosition("/src/index.ts", 7); + assert.ok(type1); + const symbol1 = project1.checker.getPropertyOfType(type1, "a"); + assert.ok(symbol1); + const propertyType1 = project1.checker.getTypeOfSymbol(symbol1); + assert.ok(propertyType1); + // getTypeOfSymbol returns 'string | undefined' + assert.ok(propertyType1.isUnionType()); + const propertyType2 = project1.checker.getNonMissingTypeOfSymbol(symbol1); + assert.ok(propertyType2); + // getNonMissingTypeOfSymbol returns 'string' + assert.ok(!propertyType2.isUnionType()); + assert.ok(propertyType2.flags & TypeFlags.String); + + // when `"exactOptionalPropertyTypes": false` + const snapshot2 = api.updateSnapshot({ openProject: "/tsconfig-two.json" }); + const project2 = snapshot2.getProject("/tsconfig-two.json")!; + const type2 = project2.checker.getTypeAtPosition("/src/index.ts", 7); + assert.ok(type2); + const symbol2 = project2.checker.getPropertyOfType(type2, "a"); + assert.ok(symbol2); + const propertyType3 = project2.checker.getTypeOfSymbol(symbol2); + assert.ok(propertyType3); + // getTypeOfSymbol returns 'string | undefined' + assert.ok(propertyType3.isUnionType()); + const propertyType4 = project2.checker.getNonMissingTypeOfSymbol(symbol2); + assert.ok(propertyType4); + // getNonMissingTypeOfSymbol returns 'string | undefined' + assert.ok(propertyType4.isUnionType()); + } + finally { + api.close(); + } + }); +}); + +describe("LanguageService - imports", () => { + test("getImportEditsForSymbols adds a named import", () => { + const source = `const value = foo;\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": source, + "/src/foo.ts": `export const foo = 1;\n`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = project.checker.getSymbolAtPosition("/src/foo.ts", "export const ".length); + assert.ok(symbol); + + const edits = project.languageService.getImportEditsForSymbols("/src/index.ts", [symbol.getExportSymbol()]); + + assert.equal(applyTextEdits(source, edits), `import { foo } from "./foo";\n\nconst value = foo;\n`); + } + finally { + api.close(); + } + }); + + test("getImportAdderEdits coalesces multiple importSymbol actions", () => { + const source = `const value = foo + bar;\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": source, + "/src/foo.ts": `export const foo = 1;\nexport const bar = 2;\n`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const foo = project.checker.getSymbolAtPosition("/src/foo.ts", "export const ".length); + const bar = project.checker.getSymbolAtPosition("/src/foo.ts", "export const foo = 1;\nexport const ".length); + assert.ok(foo); + assert.ok(bar); + + const edits = project.languageService.getImportAdderEdits("/src/index.ts", [ + { kind: "importSymbol", symbol: foo.getExportSymbol() }, + { kind: "importSymbol", symbol: bar.getExportSymbol() }, + ]); + + assert.equal(applyTextEdits(source, edits), `import { bar, foo } from "./foo";\n\nconst value = foo + bar;\n`); + } + finally { + api.close(); + } + }); + + test("getImportAdderEdits adds to an existing import", () => { + const source = `import { foo } from "./foo";\nconst value = foo + bar;\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": source, + "/src/foo.ts": `export const foo = 1;\nexport const bar = 2;\n`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const bar = project.checker.getSymbolAtPosition("/src/foo.ts", "export const foo = 1;\nexport const ".length); + assert.ok(bar); + + const edits = project.languageService.getImportAdderEdits("/src/index.ts", [ + { kind: "importSymbol", symbol: bar.getExportSymbol() }, + ]); + + assert.equal(applyTextEdits(source, edits), `import { bar, foo } from "./foo";\nconst value = foo + bar;\n`); + } + finally { + api.close(); + } + }); + + test("getImportAdderEdits returns no edits for non-exported symbols", () => { + const source = `const value = local;\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": source, + "/src/foo.ts": `const local = 1;\n`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = project.checker.getSymbolAtPosition("/src/foo.ts", "const ".length); + assert.ok(symbol); + + const edits = project.languageService.getImportAdderEdits("/src/index.ts", [ + { kind: "importSymbol", symbol }, + ]); + + assert.deepEqual(edits, []); + } + finally { + api.close(); + } + }); + + test("getImportAdderEdits rejects invalid actions", () => { + const api = spawnAPI(); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = project.checker.getSymbolAtPosition("/src/foo.ts", 13); + assert.ok(symbol); + + assert.throws( + () => project.languageService.getImportAdderEdits("/src/index.ts", [{ kind: "unknown", symbol: symbol.id } as unknown as ImportAdderAction]), + /Debug Failure\. Illegal value: "unknown"/, + ); + assert.throws( + () => project.languageService.getImportAdderEdits("/src/index.ts", [{ kind: "importSymbol", symbol: { ...symbol, id: 999_999_999 } } as unknown as ImportAdderAction]), + /symbol handle \d+ not found/, + ); + } + finally { + api.close(); + } + }); +}); + +describe("LanguageService - getCompletionsAtPosition", () => { + test("returns member completions after a dot", () => { + const src = `\nconst obj = { name: "hello", age: 42 };\nobj.\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + // Position right after "obj." — member completion trigger + const pos = src.indexOf("obj.") + "obj.".length; + const completions = project.languageService.getCompletionsAtPosition("/src/main.ts", pos, { triggerCharacter: "." }); + assert.ok(completions, "Expected completions to be returned"); + assert.ok(completions.entries.length > 0, "Expected at least one completion entry"); + assert.ok(completions.entries.some(e => e.name === "name"), "Expected 'name' property in completions"); + assert.ok(completions.entries.some(e => e.name === "age"), "Expected 'age' property in completions"); + assert.ok(completions.entries.every(e => e.symbol === undefined), "Expected no symbol information"); + } + finally { + api.close(); + } + }); + + test("completion entries include sortText", () => { + const src = `\nconst obj = { value: 1 };\nobj.\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("obj.") + "obj.".length; + const completions = project.languageService.getCompletionsAtPosition("/src/main.ts", pos, { triggerCharacter: "." }); + assert.ok(completions); + assert.ok(completions.entries.length > 0); + assert.ok(completions.entries.some(e => e.sortText !== undefined), "Expected sortText on all entries"); + } + finally { + api.close(); + } + }); + + test("returns undefined for a non-existent file", () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": `export {};`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const completions = project.languageService.getCompletionsAtPosition("/src/does-not-exist.ts", 0); + assert.equal(completions, undefined, "Expected undefined for non-existent file"); + } + finally { + api.close(); + } + }); + + test("includeSymbol: true populates symbol on property completions", () => { + const src = `\nconst obj = { name: "hello", age: 42 };\nobj.\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("obj.") + "obj.".length; + const completions = project.languageService.getCompletionsAtPosition("/src/main.ts", pos, { triggerCharacter: ".", includeSymbol: true }); + assert.ok(completions, "Expected completions"); + const nameEntry = completions.entries.find(e => e.name === "name"); + assert.ok(nameEntry, "Expected 'name' entry"); + assert.ok(nameEntry.symbol, "Expected symbol to be set on 'name' entry when includeSymbol: true"); + assert.equal(nameEntry.symbol.name, "name", "Symbol name should match completion name"); + } + finally { + api.close(); + } + }); +}); + +describe("LanguageService - getReferencedSymbolsForNode", () => { + test("getReferencedSymbolsForNode", () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `function greet(name: string) { return name; }\ngreet("world");`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + const funcDecl = cast(sourceFile.statements[0], isFunctionDeclaration); + const funcName = funcDecl.name!; + const refs = project.languageService.getReferencedSymbolsForNode(funcName, funcName.pos); + assert.ok(refs.length > 0); + // Each entry should have a definition and references + const entry = refs[0]; + assert.ok(entry.definition); + assert.ok(entry.references.length > 0); + } + finally { + api.close(); + } + }); +}); + +describe("LanguageService - getSignatureUsage", () => { + test("getSignatureUsage", () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `function greet(name: string) { return name; }\ngreet("world");`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + const funcDecl = cast(sourceFile.statements[0], isFunctionDeclaration); + const usages = project.languageService.getSignatureUsage(funcDecl); + assert.ok(usages.length > 0); + // The call site should have a call expression + const usage = usages.find(u => u.call !== undefined); + assert.ok(usage, "Expected at least one usage with a call expression"); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getApparentType", () => { + test("returns the apparent type of a literal type", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const x = "hello" as const;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = `export const x = "hello" as const;`.indexOf("x ="); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.equal(type.isLiteralType(), true); + assert.equal(type.isStringLiteralType(), true); + assert.equal(type.isIntrinsicType(), false); + const apparent = project.checker.getApparentType(type); + assert.ok(apparent); + assert.ok(apparent.id > 0); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getReducedType", () => { + test("returns the reduced type", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +declare const TaggedError: ( + tag: Tag, +) => new = {}>( + args: { readonly [P in keyof A]: A[P] }, +) => { readonly _tag: Tag } & Readonly; + +class RateLimitError extends TaggedError("RateLimitError")<{ + readonly retryAfter: number; +}> {} + +class QuotaExceededError extends TaggedError("QuotaExceededError")<{ + readonly limit: number; +}> {} + +export type Result = RateLimitError | (RateLimitError & QuotaExceededError);`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const typeAlias = sourceFile.statements.find(isTypeAliasDeclaration); + assert.ok(typeAlias); + const type = project.checker.getTypeAtLocation(typeAlias); + assert.equal(type.isUnionType(), true); + assert.equal(type.isObjectType(), false); + const reducedType = project.checker.getReducedType(type); + assert.equal(reducedType.isUnionType(), false); + assert.equal(reducedType.isObjectType(), true); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getMemberInModuleExports", () => { + test("returns a named export when present", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/index.ts": `export const direct = 1;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + const moduleSymbol = project.checker.getSymbolAtLocation(sourceFile); + assert.ok(moduleSymbol); + const found = project.checker.getMemberInModuleExports(moduleSymbol, "direct"); + assert.ok(found); + assert.equal(found.name, "direct"); + const missing = project.checker.getMemberInModuleExports(moduleSymbol, "missing"); + assert.equal(missing, undefined); + } + finally { + api.close(); + } + }); +}); + +describe("SourceFile", () => { + test("getSourceFileNames returns all program files, not just root files", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ + compilerOptions: { + moduleResolution: "node10", + noLib: true, + }, + }), + "/src/index.ts": `import { foo } from "./foo";\nimport { bar } from "my-lib";\nexport const result = foo + bar;`, + "/src/foo.ts": `export const foo = 42;`, + "/node_modules/my-lib/package.json": JSON.stringify({ name: "my-lib", types: "./index.d.ts" }), + "/node_modules/my-lib/index.d.ts": `export declare const bar: number;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const fileNames = project.program.getSourceFileNames(); + assert.deepEqual(fileNames, [ + "/src/foo.ts", + "/node_modules/my-lib/index.d.ts", + "/src/index.ts", + ]); + } + finally { + api.close(); + } + }); + + test("source file metadata identifies external library and default library files", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ + compilerOptions: { + moduleResolution: "node10", + }, + }), + "/src/index.ts": `import { bar } from "my-lib";\nexport const result = bar;`, + "/node_modules/my-lib/package.json": JSON.stringify({ name: "my-lib", types: "./index.d.ts" }), + "/node_modules/my-lib/index.d.ts": `export declare const bar: number;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const program = project.program; + + const index = program.getSourceFile("/src/index.ts"); + assert.ok(index); + assert.equal(program.isSourceFileFromExternalLibrary(index), false); + assert.equal(program.isSourceFileDefaultLibrary(index), false); + + const lib = program.getSourceFile("/node_modules/my-lib/index.d.ts"); + assert.ok(lib); + assert.equal(program.isSourceFileFromExternalLibrary(lib), true); + assert.equal(program.isSourceFileDefaultLibrary(lib), false); + + const fileNames = program.getSourceFileNames(); + const defaultLibName = fileNames.find(name => name.endsWith("lib.d.ts") || name.includes("/lib.")); + assert.ok(defaultLibName, "expected a default library file in the program"); + const defaultLib = program.getSourceFile(defaultLibName); + assert.ok(defaultLib); + assert.equal(program.isSourceFileDefaultLibrary(defaultLib), true); + } + finally { + api.close(); + } + }); + + test("source file metadata reports implied node format", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ + compilerOptions: { + module: "nodenext", + moduleResolution: "nodenext", + }, + }), + "/src/index.ts": `export const x = 1;`, + "/src/esm.mts": `export const e = 1;`, + "/src/cjs.cts": `export const c = 1;`, + "/esm/package.json": JSON.stringify({ type: "module" }), + "/esm/index.ts": `export const m = 1;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const program = snapshot.getProject("/tsconfig.json")!.program; + + const mts = program.getSourceFile("/src/esm.mts"); + assert.ok(mts); + assert.equal((program.getSourceFileMetadata(mts.fileName))?.impliedNodeFormat, ModuleKind.ESNext); + assert.equal((program.getSourceFileMetadataByPath(mts.path))?.impliedNodeFormat, ModuleKind.ESNext); + + const cts = program.getSourceFile("/src/cjs.cts"); + assert.ok(cts); + assert.equal((program.getSourceFileMetadata(cts.fileName))?.impliedNodeFormat, ModuleKind.CommonJS); + + // A plain .ts file with no nearby `"type": "module"` is CommonJS. + const index = program.getSourceFile("/src/index.ts"); + assert.ok(index); + assert.equal((program.getSourceFileMetadata(index.fileName))?.impliedNodeFormat, ModuleKind.CommonJS); + + // A plain .ts file under a `"type": "module"` package is ESM. + const esmIndex = program.getSourceFile("/esm/index.ts"); + assert.ok(esmIndex); + assert.equal((program.getSourceFileMetadata(esmIndex.fileName))?.impliedNodeFormat, ModuleKind.ESNext); + } + finally { + api.close(); + } + }); + + test("file properties", () => { + const api = spawnAPI(); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + + assert.ok(sourceFile); + assert.equal(sourceFile.text, defaultFiles["/src/index.ts"]); + assert.equal(sourceFile.fileName, "/src/index.ts"); + } + finally { + api.close(); + } + }); + + test("extended data", () => { + const api = spawnAPI(); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + + assert.ok(sourceFile); + let nodeCount = 1; + sourceFile.forEachChild(function visit(node) { + if (isTemplateHead(node)) { + assert.equal(node.text, "head "); + assert.equal(node.rawText, "head "); + assert.equal(node.templateFlags, 0); + } + else if (isTemplateMiddle(node)) { + assert.equal(node.text, "middle"); + assert.equal(node.rawText, "middle"); + assert.equal(node.templateFlags, 0); + } + else if (isTemplateTail(node)) { + assert.equal(node.text, " tail"); + assert.equal(node.rawText, " tail"); + assert.equal(node.templateFlags, 0); + } + nodeCount++; + node.forEachChild(visit); + }); + assert.equal(nodeCount, 8); + } + finally { + api.close(); + } + }); + + test("forEachChild with visitList does not visit array children twice", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ files: ["/input.ts"] }), + "/input.ts": `let arrow = () => {}`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/input.ts"); + + assert.ok(sourceFile); + + const visited: { kind: SyntaxKind; pos: number; end: number; }[] = []; + (function walk(node: Node): void { + visited.push({ kind: node.kind, pos: node.pos, end: node.end }); + node.forEachChild(walk, (nodes: NodeArray) => { + for (let i = 0; i < nodes.length; i++) { + walk(nodes[i]); + } + return undefined; + }); + })(sourceFile); + + // Each node should be visited exactly once, even when a visitList callback + // is supplied. Previously array children were visited twice. + const seen = new Set(); + for (const { kind, pos, end } of visited) { + const key = `${kind}.${pos}.${end}`; + assert.ok(!seen.has(key), `Node ${key} was visited more than once`); + seen.add(key); + } + } + finally { + api.close(); + } + }); +}); + +describe("NodeArray", () => { + test("hasTrailingComma", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `declare function foo(...args: any): void;\nfoo("a", "b",);\nfoo("a", "b");`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const statements = sourceFile.statements.filter(isExpressionStatement); + assert.ok(isCallExpression(statements[0].expression)); + assert.equal(statements[0].expression.arguments.hasTrailingComma, true); + assert.ok(isCallExpression(statements[1].expression)); + assert.equal(statements[1].expression.arguments.hasTrailingComma, false); + } + finally { + api.close(); + } + }); +}); + +test("unicode escapes", () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/1.ts": `"😃"`, + "/src/2.ts": `"\\ud83d\\ude03"`, + "/src/3.ts": `"\\ud800a\\udc00"`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const expectedTexts = new Map([ + ["/src/1.ts", "😃"], + ["/src/2.ts", "😃"], + ["/src/3.ts", "\ud800a\udc00"], + ]); + + for (const file of expectedTexts.keys()) { + const sourceFile = project.program.getSourceFile(file); + assert.ok(sourceFile); + + sourceFile.forEachChild(function visit(node) { + if (isStringLiteral(node)) { + assert.equal(node.text, expectedTexts.get(file)); + } + node.forEachChild(visit); + }); + } + } + finally { + api.close(); + } +}); + +test("template unicode escapes", () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": "`\\ud800${0}\\udc00`", + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + + let sawHead = false; + let sawTail = false; + sourceFile.forEachChild(function visit(node) { + if (isTemplateHead(node)) { + assert.equal(node.text, "\ud800"); + sawHead = true; + } + else if (isTemplateTail(node)) { + assert.equal(node.text, "\udc00"); + sawTail = true; + } + node.forEachChild(visit); + }); + assert.ok(sawHead); + assert.ok(sawTail); + } + finally { + api.close(); + } +}); + +test("Object equality", () => { + const api = spawnAPI(); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + // Same symbol returned from same snapshot's checker + assert.strictEqual( + project.checker.getSymbolAtPosition("/src/index.ts", 9), + project.checker.getSymbolAtPosition("/src/index.ts", 10), + ); + } + finally { + api.close(); + } +}); + +test("Snapshot dispose", () => { + const api = spawnAPI(); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = project.checker.getSymbolAtPosition("/src/index.ts", 9); + assert.ok(symbol); + + // Snapshot dispose should release server-side resources + assert.ok(snapshot.isDisposed() === false); + snapshot.dispose(); + assert.ok(snapshot.isDisposed() === true); + + // After dispose, snapshot methods should throw + assert.throws(() => { + snapshot.getProject("/tsconfig.json"); + }, { + name: "Error", + message: "Snapshot is disposed", + }); + } + finally { + api.close(); + } +}); + +describe("Multiple snapshots", () => { + test("two snapshots work independently", () => { + const api = spawnAPI(); + try { + const snap1 = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const snap2 = api.updateSnapshot({ openProject: "/tsconfig.json" }); + + // Both can fetch source files + const sf1 = snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); + const sf2 = snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); + assert.ok(sf1); + assert.ok(sf2); + + // Disposing one doesn't break the other + snap1.dispose(); + assert.ok(snap1.isDisposed()); + assert.ok(!snap2.isDisposed()); + + // snap2 still works after snap1 is disposed + const symbol = snap2.getProject("/tsconfig.json")!.checker.getSymbolAtPosition("/src/index.ts", 9); + assert.ok(symbol); + assert.equal(symbol.name, "foo"); + } + finally { + api.close(); + } + }); + + test("each snapshot has its own server-side lifecycle", () => { + const { api, fs } = spawnAPIWithFS(); + try { + const snap1 = api.updateSnapshot({ openProject: "/tsconfig.json" }); + + // Verify initial state + const sf1 = snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); + assert.ok(sf1); + assert.equal(sf1.text, `export const foo = 42;`); + + // Mutate the file and create a new snapshot with the change + fs.writeFile!("/src/foo.ts", `export const foo = "changed";`); + const snap2 = api.updateSnapshot({ + fileChanges: { changed: ["/src/foo.ts"] }, + }); + + // snap2 should reflect the updated content + const sf2 = snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); + assert.ok(sf2); + assert.equal(sf2.text, `export const foo = "changed";`); + + // snap1's source file should still have the original content + assert.equal(sf1.text, `export const foo = 42;`); + + snap1.dispose(); + + // snap2 still works independently after snap1 is disposed + const symbol = snap2.getProject("/tsconfig.json")!.checker.getSymbolAtPosition("/src/index.ts", 9); + assert.ok(symbol); + + snap2.dispose(); + + // Both are disposed, new snapshot works fine with latest content + const snap3 = api.updateSnapshot(); + const sf3 = snap3.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); + assert.ok(sf3); + assert.equal(sf3.text, `export const foo = "changed";`); + } + finally { + api.close(); + } + }); + + test("adding a new file is reflected in the next snapshot", () => { + const { api, fs } = spawnAPIWithFS(); + try { + const snap1 = api.updateSnapshot({ openProject: "/tsconfig.json" }); + + // Add a brand new file + fs.writeFile!("/src/bar.ts", `export const bar = true;`); + const snap2 = api.updateSnapshot({ + fileChanges: { created: ["/src/bar.ts"] }, + }); + + const sf = snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/bar.ts"); + assert.ok(sf); + assert.equal(sf.text, `export const bar = true;`); + + // Original snapshot shouldn't have the new file + const sfOld = snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/bar.ts"); + assert.equal(sfOld, undefined); + } + finally { + api.close(); + } + }); + + test("multiple sequential edits produce correct snapshots", () => { + const { api, fs } = spawnAPIWithFS(); + try { + api.updateSnapshot({ openProject: "/tsconfig.json" }); + + const versions = [ + `export const foo = 1;`, + `export const foo = 2;`, + `export const foo = 3;`, + ]; + + for (const version of versions) { + fs.writeFile!("/src/foo.ts", version); + const snap = api.updateSnapshot({ + fileChanges: { changed: ["/src/foo.ts"] }, + }); + const sf = snap.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); + assert.ok(sf); + assert.equal(sf.text, version); + } + } + finally { + api.close(); + } + }); +}); + +describe("Source file caching", () => { + test("same file from same snapshot returns cached object", () => { + const api = spawnAPI(); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sf1 = project.program.getSourceFile("/src/index.ts"); + const sf2 = project.program.getSourceFile("/src/index.ts"); + assert.ok(sf1); + assert.strictEqual(sf1, sf2, "Same source file should be returned from cache"); + } + finally { + api.close(); + } + }); + + test("same file from two snapshots (same content) returns cached object", () => { + const api = spawnAPI(); + try { + const snap1 = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const snap2 = api.updateSnapshot({ openProject: "/tsconfig.json" }); + // Fetch from snap1 first (populates cache), then snap2 (cache hit via hash) + const sf1 = snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); + const sf2 = snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); + assert.ok(sf1); + assert.ok(sf2); + // Same content hash → cache hit → same object + assert.strictEqual(sf1, sf2, "Same file with same content should share cached object"); + } + finally { + api.close(); + } + }); + + test("modified file returns a different source file object", () => { + const { api, fs } = spawnAPIWithFS(); + try { + const snap1 = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const sf1 = snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); + assert.ok(sf1); + assert.equal(sf1.text, `export const foo = 42;`); + + // Mutate the file in the VFS + fs.writeFile!("/src/foo.ts", `export const foo = 100;`); + + // Notify the server about the change + const snap2 = api.updateSnapshot({ + fileChanges: { changed: ["/src/foo.ts"] }, + }); + const sf2 = snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); + assert.ok(sf2); + assert.equal(sf2.text, `export const foo = 100;`); + + // Different content → different object + assert.notStrictEqual(sf1, sf2, "Modified file should return a new source file object"); + } + finally { + api.close(); + } + }); + + test("unmodified file retains cached object across file change notification", () => { + const { api, fs } = spawnAPIWithFS(); + try { + const snap1 = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const sf1 = snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); + assert.ok(sf1); + + // Mutate a different file + fs.writeFile!("/src/foo.ts", `export const foo = 999;`); + + // Notify the server about the change to foo.ts only + const snap2 = api.updateSnapshot({ + fileChanges: { changed: ["/src/foo.ts"] }, + }); + const sf2 = snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); + assert.ok(sf2); + + // index.ts wasn't changed — should still get cached object + assert.strictEqual(sf1, sf2, "Unchanged file should return cached object across snapshots"); + } + finally { + api.close(); + } + }); + + test("cache entries survive when one of two snapshots is disposed", () => { + const api = spawnAPI(); + try { + const snap1 = api.updateSnapshot({ openProject: "/tsconfig.json" }); + // Fetch from snap1 to populate cache + const sf1 = snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); + assert.ok(sf1); + + // snap2 retains snap1's cache refs for unchanged files via snapshot changes + const snap2 = api.updateSnapshot({ openProject: "/tsconfig.json" }); + + // Dispose snap1 — snap2 still holds a ref, so the entry survives + snap1.dispose(); + + // Fetching from snap2 should still return the cached object + const sf2 = snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/index.ts"); + assert.ok(sf2); + assert.strictEqual(sf1, sf2, "Cache entry should survive when retained by the next snapshot"); + } + finally { + api.close(); + } + }); + + test("invalidateAll causes all files to be re-fetched", () => { + const { api, fs } = spawnAPIWithFS(); + try { + const snap1 = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const sf1 = snap1.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); + assert.ok(sf1); + assert.equal(sf1.text, `export const foo = 42;`); + + // Mutate the file + fs.writeFile!("/src/foo.ts", `export const foo = "hello";`); + + // Use invalidateAll to force re-fetch + const snap2 = api.updateSnapshot({ + fileChanges: { invalidateAll: true }, + }); + const sf2 = snap2.getProject("/tsconfig.json")!.program.getSourceFile("/src/foo.ts"); + assert.ok(sf2); + assert.equal(sf2.text, `export const foo = "hello";`); + assert.notStrictEqual(sf1, sf2, "invalidateAll should produce new source file objects"); + } + finally { + api.close(); + } + }); + + test("node handles from a cached source file should be valid in a new snapshot", () => { + const { api, fs } = spawnAPIWithFS({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `function foo(x: number) {}\nfoo(42);`, + "/src/other.ts": `export const x = 1;`, + }); + try { + // Snapshot 1: get a node and verify getContextualType works + const snap1 = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const proj1 = snap1.getProject("/tsconfig.json")!; + + const sf1 = proj1.program.getSourceFile("/src/main.ts"); + assert.ok(sf1); + + let numLiteral: Expression | undefined; + sf1.forEachChild(function visit(node) { + if (isCallExpression(node)) numLiteral = node.arguments[0]; + node.forEachChild(visit); + }); + assert.ok(numLiteral, "should find the 42 argument"); + + const type1 = proj1.checker.getContextualType(numLiteral); + assert.ok(type1); + assert.ok(type1.flags & TypeFlags.Number); + + // Snapshot 2: change a different file + fs.writeFile!("/src/other.ts", `export const x = 2;`); + const snap2 = api.updateSnapshot({ + fileChanges: { changed: ["/src/other.ts"] }, + }); + const proj2 = snap2.getProject("/tsconfig.json")!; + + // main.ts is unchanged — client returns the cached SourceFile (same object) + const sf2 = proj2.program.getSourceFile("/src/main.ts"); + assert.ok(sf2); + assert.strictEqual(sf1, sf2, "unchanged file should be served from client cache"); + + let numLiteral2: Expression | undefined; + sf2.forEachChild(function visit(node) { + if (isCallExpression(node)) numLiteral2 = node.arguments[0]; + node.forEachChild(visit); + }); + assert.ok(numLiteral2, "should find the 42 argument"); + assert.strictEqual(numLiteral, numLiteral2, "unchanged file should be served from client cache"); + + // A type from new snapshot should be resolved + const type2 = proj2.checker.getContextualType(numLiteral); + assert.ok(type2); + assert.ok(type2.flags & TypeFlags.Number); + } + finally { + api.close(); + } + }); +}); + +describe("Snapshot disposal", () => { + test("dispose is idempotent", () => { + const api = spawnAPI(); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + snapshot.dispose(); + assert.ok(snapshot.isDisposed()); + // Second dispose should not throw + snapshot.dispose(); + assert.ok(snapshot.isDisposed()); + } + finally { + api.close(); + } + }); + + test("api.close disposes all active snapshots", () => { + const api = spawnAPI(); + const snap1 = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const snap2 = api.updateSnapshot({ openProject: "/tsconfig.json" }); + assert.ok(!snap1.isDisposed()); + assert.ok(!snap2.isDisposed()); + api.close(); + assert.ok(snap1.isDisposed()); + assert.ok(snap2.isDisposed()); + }); +}); + +describe("Source file cache keying across projects", () => { + // Three projects share the same file (/src/shared.ts). + // The file sits inside a package.json scope with "type": "module". + // + // Project A: moduleResolution: bundler (auto detection, bundler doesn't + // trigger isFileForcedToBeModuleByFormat → file parsed as script) + // Project B: moduleResolution: bundler, moduleDetection: force + // (force → file parsed as module) + // Project C: moduleResolution: nodenext + // (nodenext + type:module → impliedNodeFormat ESNext → + // isFileForcedToBeModuleByFormat → file parsed as module) + // + // Expected: exactly two distinct source file objects are stored: + // - A gets one (script parse) + // - B and C share another (module parse) + const multiProjectFiles: Record = { + "/package.json": JSON.stringify({ type: "module" }), + "/src/shared.ts": `export const x = 1;`, + // Project A – bundler, auto detection (default) + "/projectA/tsconfig.json": JSON.stringify({ + compilerOptions: { + moduleResolution: "bundler", + module: "esnext", + strict: true, + }, + files: ["../src/shared.ts"], + }), + // Project B – bundler, force module detection + "/projectB/tsconfig.json": JSON.stringify({ + compilerOptions: { + moduleResolution: "bundler", + module: "esnext", + moduleDetection: "force", + strict: true, + }, + files: ["../src/shared.ts"], + }), + // Project C – nodenext (type:module → module) + "/projectC/tsconfig.json": JSON.stringify({ + compilerOptions: { + moduleResolution: "nodenext", + module: "nodenext", + strict: true, + }, + files: ["../src/shared.ts"], + }), + }; + + test("different parse modes produce separate cached objects; same parse modes share", () => { + const api = spawnAPI(multiProjectFiles); + try { + // Open all three projects + api.updateSnapshot({ openProject: "/projectA/tsconfig.json" }); + api.updateSnapshot({ openProject: "/projectB/tsconfig.json" }); + const snapshot = api.updateSnapshot({ openProject: "/projectC/tsconfig.json" }); + + const projectA = snapshot.getProject("/projectA/tsconfig.json")!; + const projectB = snapshot.getProject("/projectB/tsconfig.json")!; + const projectC = snapshot.getProject("/projectC/tsconfig.json")!; + assert.ok(projectA, "projectA should exist"); + assert.ok(projectB, "projectB should exist"); + assert.ok(projectC, "projectC should exist"); + + // Fetch the shared file from each project + const sfA = projectA.program.getSourceFile("/src/shared.ts"); + const sfB = projectB.program.getSourceFile("/src/shared.ts"); + const sfC = projectC.program.getSourceFile("/src/shared.ts"); + assert.ok(sfA, "sfA should exist"); + assert.ok(sfB, "sfB should exist"); + assert.ok(sfC, "sfC should exist"); + + // A should differ from B and C (script vs module parse) + assert.notStrictEqual(sfA, sfB, "projectA (script) and projectB (module) should have different cached source files"); + assert.notStrictEqual(sfA, sfC, "projectA (script) and projectC (module) should have different cached source files"); + + // B and C should share the same cached object (both module parse, same content hash) + assert.strictEqual(sfB, sfC, "projectB and projectC (both module parse) should share the same cached source file"); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - symbol identity across projects", () => { + const sharedSymbolFiles = { + "/projectA/tsconfig.json": JSON.stringify({ files: ["../src/shared.ts"] }), + "/projectB/tsconfig.json": JSON.stringify({ files: ["../src/shared.ts"] }), + "/src/shared.ts": `export const sharedVar = 42;`, + }; + + test("getSymbolAtPosition returns same Symbol instance across projects", () => { + const api = spawnAPI(sharedSymbolFiles); + try { + api.updateSnapshot({ openProject: "/projectA/tsconfig.json" }); + const snapshot = api.updateSnapshot({ openProject: "/projectB/tsconfig.json" }); + + const projectA = snapshot.getProject("/projectA/tsconfig.json")!; + const projectB = snapshot.getProject("/projectB/tsconfig.json")!; + assert.ok(projectA, "projectA should exist"); + assert.ok(projectB, "projectB should exist"); + + const src = sharedSymbolFiles["/src/shared.ts"]; + const varPos = src.indexOf("sharedVar"); + + const symbolA = projectA.checker.getSymbolAtPosition("/src/shared.ts", varPos); + const symbolB = projectB.checker.getSymbolAtPosition("/src/shared.ts", varPos); + + assert.ok(symbolA, "symbolA should exist"); + assert.ok(symbolB, "symbolB should exist"); + assert.equal(symbolA.name, "sharedVar"); + assert.equal(symbolB.name, "sharedVar"); + + assert.strictEqual(symbolA, symbolB, "Same source symbol queried from two projects should be the same object"); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - types and signatures", () => { + const checkerFiles = { + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export const x = 42; +export function add(a: number, b: number, ...rest: number[]): number { return a + b; } +export class MyClass { + value: string = ""; + getValue(): string { return this.value; } +} +`, + }; + + test("getTypeAtPosition", () => { + const api = spawnAPI(checkerFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = checkerFiles["/src/main.ts"]; + const xPos = src.indexOf("x = 42"); + const type = project.checker.getTypeAtPosition("/src/main.ts", xPos); + assert.ok(type); + assert.ok(type.flags & TypeFlags.NumberLiteral); + } + finally { + api.close(); + } + }); + + test("getTypeAtPosition batched", () => { + const api = spawnAPI(checkerFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = checkerFiles["/src/main.ts"]; + const xPos = src.indexOf("x = 42"); + const addPos = src.indexOf("add("); + const types = project.checker.getTypeAtPosition("/src/main.ts", [xPos, addPos]); + assert.equal(types.length, 2); + assert.ok(types[0]); + assert.ok(types[1]); + } + finally { + api.close(); + } + }); + + test("getTypeAtLocation", () => { + const api = spawnAPI(checkerFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const firstVarDecl = sourceFile.statements[2]; // "export const x" + assert.ok(firstVarDecl); + const type = project.checker.getTypeAtLocation(firstVarDecl); + assert.ok(type); + } + finally { + api.close(); + } + }); + + test("getTypeAtLocation returns property type for parenthesized and chained access (issue #3938)", () => { + const files = { + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +interface A { + a: number; + b: { c: number }; +} +const obj: A = { a: 1, b: { c: 2 } }; +const a1 = obj.a; +const a2 = (obj).a; +const c = obj.b.c; +`, + }; + + const api = spawnAPI(files); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + + const targetNodes = new Map(); + sourceFile.forEachChild(function visit(node) { + if (node.kind === SyntaxKind.PropertyAccessExpression) { + const text = sourceFile.text.slice(node.pos, node.end).trim(); + if (text === "obj.a" || text === "(obj).a" || text === "obj.b.c") { + targetNodes.set(text, node); + } + } + node.forEachChild(visit); + }); + + assert.equal(targetNodes.size, 3, "Should find all target property access expressions"); + for (const expr of ["obj.a", "(obj).a", "obj.b.c"] as const) { + const node = targetNodes.get(expr); + assert.ok(node, `Should find expression '${expr}'`); + const type = project.checker.getTypeAtLocation(node); + assert.ok(type, `Should get a type for '${expr}'`); + assert.ok(type.flags & TypeFlags.Number, `Expected '${expr}' to have number type flags, got ${type.flags}`); + } + } + finally { + api.close(); + } + }); + + test("getTypeAtLocation returns call result type for private method call (issue #4041)", () => { + const files = { + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true, target: "esnext" } }), + "/src/main.ts": ` +type Result = { readonly value: string }; + +export class Cache { + run(): Result { + return this.#buildCapabilities(); + } + + #buildCapabilities(): Result { + return { value: "ok" }; + } +} +`, + }; + + const api = spawnAPI(files); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + + let callNode: import("@typescript/typescript/unstable/ast").CallExpression | undefined; + sourceFile.forEachChild(function visit(node) { + if (isCallExpression(node)) { + const text = sourceFile.text.slice(node.pos, node.end).trim(); + if (text === "this.#buildCapabilities()") { + callNode = node; + } + } + node.forEachChild(visit); + }); + + assert.ok(callNode, "Should find private method call expression"); + const callType = project.checker.getTypeAtLocation(callNode); + const calleeType = project.checker.getTypeAtLocation(callNode.expression); + assert.ok(callType, "Should get type for call expression"); + assert.ok(calleeType, "Should get type for callee expression"); + + const callSignatures = project.checker.getSignaturesOfType(calleeType, SignatureKind.Call); + assert.ok(callSignatures.length > 0, "Callee should be callable"); + const returnType = project.checker.getReturnTypeOfSignature(callSignatures[0]); + assert.ok(returnType, "Should get return type for private method call"); + const callExprSignatures = project.checker.getSignaturesOfType(callType, SignatureKind.Call); + + assert.ok(callType.flags & TypeFlags.Object, `Expected call expression type to be object-like, got ${callType.flags}`); + assert.ok(returnType.flags & TypeFlags.Object, `Expected return type to be object-like, got ${returnType.flags}`); + assert.equal(callType.flags, returnType.flags, "Call expression type should have same flags as method return type"); + assert.equal(callExprSignatures.length, 0, "Call expression result type should not itself be callable"); + } + finally { + api.close(); + } + }); + + test("getSignaturesOfType - call signatures", () => { + const api = spawnAPI(checkerFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = checkerFiles["/src/main.ts"]; + const addPos = src.indexOf("add("); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", addPos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const callSigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(callSigs.length > 0); + const sig = callSigs[0]; + const typeCallSigs = type.getCallSignatures(); + assert.deepEqual(typeCallSigs, callSigs); + assert.ok((sig.getReturnType()).flags & TypeFlags.Number); + assert.ok((sig.getTypeParameterAtPosition(0)).flags & TypeFlags.Number); + assert.ok(sig.id); + assert.ok(sig.parameters.length >= 2); + assert.ok(sig.hasRestParameter); + assert.ok(!sig.isConstruct); + assert.ok(!sig.isAbstract); + } + finally { + api.close(); + } + }); + + test("getApparentProperties includes CallableFunction members", () => { + const api = spawnAPI(checkerFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = checkerFiles["/src/main.ts"]; + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("add(")); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + const propertyNames = (type.getApparentProperties()).map(property => property.name); + assert.ok(propertyNames.includes("apply")); + assert.ok(propertyNames.includes("call")); + assert.ok(propertyNames.includes("bind")); + } + finally { + api.close(); + } + }); + + test("checker and object methods share cached results", () => { + const api = new API({ + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + fs: createVirtualFileSystem(checkerFiles), + collectTiming: true, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = checkerFiles["/src/main.ts"]; + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("add(")); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + let signature: Signature | undefined; + + function assertOneRequest(callback: () => void) { + api.resetTimingInfo(); + callback(); + assert.equal((api.getTimingInfo()).totals.requestCount, 1); + } + + assertOneRequest(() => { + const properties = type.getProperties(); + assert.strictEqual(project.checker.getPropertiesOfType(type), properties); + }); + assertOneRequest(() => { + const signatures = type.getCallSignatures(); + assert.strictEqual(project.checker.getSignaturesOfType(type, SignatureKind.Call), signatures); + signature = signatures[0]; + }); + assertOneRequest(() => { + const nonNullable = project.checker.getNonNullableType(type); + assert.strictEqual(type.getNonNullableType(), nonNullable); + }); + assertOneRequest(() => { + const apparentType = type.getApparentType(); + assert.strictEqual(project.checker.getApparentType(type), apparentType); + }); + assertOneRequest(() => { + const indexInfos = type.getIndexInfos(); + assert.strictEqual(project.checker.getIndexInfosOfType(type), indexInfos); + }); + assertOneRequest(() => { + assert.ok(signature); + const returnType = project.checker.getReturnTypeOfSignature(signature); + assert.strictEqual(signature.getReturnType(), returnType); + }); + } + finally { + api.close(); + } + }); + + test("getSignaturesOfType - construct signatures", () => { + const api = spawnAPI(checkerFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = checkerFiles["/src/main.ts"]; + const classPos = src.indexOf("MyClass"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", classPos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const constructSigs = project.checker.getSignaturesOfType(type, SignatureKind.Construct); + assert.ok(constructSigs.length > 0); + const sig = constructSigs[0]; + assert.ok(sig.isConstruct); + } + finally { + api.close(); + } + }); + + test("Signature declaration can be resolved", () => { + const api = spawnAPI(checkerFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = checkerFiles["/src/main.ts"]; + const addPos = src.indexOf("add("); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", addPos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const callSigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(callSigs.length > 0); + const sig = callSigs[0]; + assert.ok(sig.declaration); + assert.ok(isSignatureDeclaration.Handle(sig.declaration)); + const node = sig.declaration.resolve(project); + assert.ok(node); + assert.ok(node.parameters); + assert.ok(isSignatureDeclaration(node)); + // The handle remembers its canonical project, so resolve() works without an argument. + const nodeFromCanonical = sig.declaration.resolve(); + assert.ok(nodeFromCanonical); + assert.ok(nodeFromCanonical.parameters); + assert.ok(isSignatureDeclaration(nodeFromCanonical)); + assert.strictEqual(nodeFromCanonical.kind, node.kind); + + const methodPos = src.indexOf("getValue"); + const methodSymbol = project.checker.getSymbolAtPosition("/src/main.ts", methodPos); + assert.ok(methodSymbol); + assert.ok(methodSymbol.valueDeclaration); + const methodNode = methodSymbol.valueDeclaration.resolve(project); + assert.ok(methodNode); + assert.strictEqual(methodNode.parent.kind, SyntaxKind.ClassDeclaration); + assert.strictEqual(methodNode.parent.parent.kind, SyntaxKind.SourceFile); + // A symbol's declaration handles default to the symbol's canonical project. + const methodNodeFromCanonical = methodSymbol.valueDeclaration.resolve(); + assert.ok(methodNodeFromCanonical); + assert.strictEqual(methodNodeFromCanonical.kind, methodNode.kind); + } + finally { + api.close(); + } + }); + + test("getSignaturesOfType - signature type parameters", () => { + const mainFile = ` + interface Operator { + } + export declare class Observable { + lift(operator: Operator): Observable; + } + `; + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": mainFile, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const liftPos = mainFile.indexOf("lift"); + const type = project.checker.getTypeAtPosition("/src/main.ts", liftPos); + assert.ok(type); + const callSigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(callSigs.length === 1, "should have exactly one call signature, found: " + callSigs.length); + const sig = callSigs[0]; + assert.ok(sig.typeParameters?.length === 1, "should have exactly one type parameter, found: " + sig.typeParameters?.length); + const typeParams = sig.getTypeParameters(); + const typeParam = typeParams[0]; + assert.ok(typeParam, "should have type parameter"); + const name = (typeParam.getSymbol())?.name; + assert.ok(name === "R", "should be named R, instead: " + name); + assert.ok(typeParam.flags & TypeFlags.TypeParameter, "should be a type parameter, instead flags: " + typeParam.flags); + } + finally { + api.close(); + } + }); + + test("Signature.getParameters() returns parameter symbols with correct names", () => { + const api = spawnAPI(checkerFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = checkerFiles["/src/main.ts"]; + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("add(")); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const params = sigs[0].getParameters(); + assert.equal(params.length, 3); + assert.equal(params[0].name, "a"); + assert.equal(params[1].name, "b"); + assert.equal(params[2].name, "rest"); + assert.ok(params[0].flags & SymbolFlags.FunctionScopedVariable, `expected FunctionScopedVariable on 'a', got ${params[0].flags}`); + } + finally { + api.close(); + } + }); + + test("Signature.getThisParameter() returns undefined when no explicit this parameter", () => { + const api = spawnAPI(checkerFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = checkerFiles["/src/main.ts"]; + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("add(")); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const thisParam = sigs[0].getThisParameter(); + assert.strictEqual(thisParam, undefined, "add() has no explicit this parameter"); + } + finally { + api.close(); + } + }); + + test("Signature.getThisParameter() returns symbol for explicit this parameter", () => { + const src = `export function foo(this: { n: number }, x: string): void {}`; + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": src, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("foo(")); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const thisParam = sigs[0].getThisParameter(); + assert.ok(thisParam, "foo has an explicit this parameter"); + assert.equal(thisParam.name, "this"); + assert.ok(thisParam.flags & SymbolFlags.FunctionScopedVariable, `expected FunctionScopedVariable, got ${thisParam.flags}`); + } + finally { + api.close(); + } + }); + + test("Signature.getTarget() returns undefined for a non-instantiated signature", () => { + const api = spawnAPI(checkerFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = checkerFiles["/src/main.ts"]; + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("add(")); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const target = sigs[0].getTarget(); + assert.strictEqual(target, undefined, "add() is not an instantiated signature"); + } + finally { + api.close(); + } + }); + + test("Signature.getTarget() returns the generic source signature for an instantiated call", () => { + const src = ` + function identity(x: T): T { return x; } + identity("hello"); + `; + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": src, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + let callNode: Node | undefined; + sourceFile.forEachChild(function visit(node) { + if (isCallExpression(node)) callNode = node; + node.forEachChild(visit); + }); + assert.ok(callNode, "should find a call expression"); + const sig = project.checker.getResolvedSignature(callNode); + assert.ok(sig, "should resolve a signature for the call"); + assert.ok(sig.target !== undefined, "instantiated call should have a target ID"); + const target = sig.getTarget(); + assert.ok(target, "getTarget() should return the generic signature"); + assert.ok(target.typeParameters && target.typeParameters.length > 0, "target should have type parameters"); + } + finally { + api.close(); + } + }); +}); + +describe("Symbol - parent, members, exports", () => { + const symbolFiles = { + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/mod.ts": ` +export class Animal { + name: string = ""; + speak(): void {} +} +export const value = 1; +`, + }; + + test("getMembers returns class members", () => { + const api = spawnAPI(symbolFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = symbolFiles["/src/mod.ts"]; + const animalPos = src.indexOf("Animal"); + const symbol = project.checker.getSymbolAtPosition("/src/mod.ts", animalPos); + assert.ok(symbol); + const members = symbol.getMembers(); + assert.ok(members.size > 0); + const memberNames = [...members.values()].map(m => m.name); + assert.ok(memberNames.includes("name"), "should have 'name' member"); + assert.ok(memberNames.includes("speak"), "should have 'speak' member"); + } + finally { + api.close(); + } + }); + + test("getExports returns module exports via sourceFile symbol", () => { + const api = spawnAPI(symbolFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/mod.ts"); + assert.ok(sourceFile); + const moduleSymbol = project.checker.getSymbolAtLocation(sourceFile); + assert.ok(moduleSymbol); + const exports = moduleSymbol.getExports(); + assert.ok(exports.size > 0); + const exportNames = [...exports.values()].map(e => e.name); + assert.ok(exportNames.includes("Animal"), "should export Animal"); + assert.ok(exportNames.includes("value"), "should export value"); + } + finally { + api.close(); + } + }); + + test("getParent returns containing symbol", () => { + const api = spawnAPI(symbolFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = symbolFiles["/src/mod.ts"]; + const namePos = src.indexOf("name:"); + const nameSymbol = project.checker.getSymbolAtPosition("/src/mod.ts", namePos); + assert.ok(nameSymbol); + assert.equal(nameSymbol.name, "name"); + const parent = nameSymbol.getParent(); + assert.ok(parent); + assert.equal(parent.name, "Animal"); + } + finally { + api.close(); + } + }); + + test("checkFlags is typed as CheckFlags", () => { + const api = spawnAPI(symbolFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = project.checker.getSymbolAtPosition("/src/mod.ts", symbolFiles["/src/mod.ts"].indexOf("Animal")); + assert.ok(symbol); + const checkFlags: CheckFlags = symbol.checkFlags; + assert.equal(checkFlags & CheckFlags.Readonly, 0); + } + finally { + api.close(); + } + }); +}); + +describe("Type - getSymbol", () => { + test("getSymbol returns the symbol of a type", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/types.ts": ` +export class Foo { + x: number = 0; +} +export const instance: Foo = new Foo(); +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\nexport class Foo {\n x: number = 0;\n}\nexport const instance: Foo = new Foo();\n`; + const instancePos = src.indexOf("instance"); + const symbol = project.checker.getSymbolAtPosition("/src/types.ts", instancePos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const typeSymbol = type.getSymbol(); + assert.ok(typeSymbol); + assert.equal(typeSymbol.name, "Foo"); + } + finally { + api.close(); + } + }); +}); + +describe("Type - sub-property fetchers", () => { + const typeFiles = { + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true, target: "esnext" } }), + "/src/types.ts": ` +export const arr: Array = [1, 2, 3]; +export const union: string | number = "hello"; +export const intersection: { a: number } & { b: string } = { a: 1, b: "hi" }; +export type KeyOf = keyof T; +export type Lookup = T[K]; +export type Cond = T extends string ? "yes" : "no"; +export const tpl: \`hello \${string}\` = "hello world"; +export type Upper = Uppercase<"hello">; +export const tuple: readonly [number, string?, ...boolean[]] = [1]; +`, + }; + + function getTypeAtName(api: API, name: string) { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = typeFiles["/src/types.ts"]; + const pos = src.indexOf(name); + assert.ok(pos >= 0, `Could not find "${name}" in source`); + const symbol = project.checker.getSymbolAtPosition("/src/types.ts", pos); + assert.ok(symbol, `No symbol found at "${name}"`); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type, `No type found for symbol "${name}"`); + return { type, project, snapshot, api }; + } + + test("TypeReference.getTarget() returns the generic target", () => { + const { type, api } = getTypeAtName(spawnAPI(typeFiles), "arr:"); + try { + assert.ok(type.flags & TypeFlags.Object); + const ref = type as TypeReference; + assert.ok(ref.objectFlags & ObjectFlags.Reference); + assert.equal(type.isObjectType(), true); + assert.equal(type.isTypeReference(), true); + assert.equal(type.isLiteralType(), false); + const target = ref.getTarget(); + assert.ok(target); + assert.ok(target.flags & TypeFlags.Object); + const properties = type.getProperties(); + assert.ok(properties.some(property => property.name === "length")); + assert.equal((type.getProperty("length"))?.name, "length"); + assert.ok((type.getApparentProperties()).length >= properties.length); + assert.strictEqual(type.getNonNullableType(), type); + } + finally { + api.close(); + } + }); + + test("UnionOrIntersectionType.getTypes() returns union members", () => { + const { type, api } = getTypeAtName(spawnAPI(typeFiles), "union:"); + try { + assert.ok(type.flags & TypeFlags.Union); + const union = type as UnionOrIntersectionType; + const types = union.getTypes(); + assert.ok(types.length >= 2); + assert.equal(type.isUnionType(), true); + assert.equal(type.isIntersectionType(), false); + } + finally { + api.close(); + } + }); + + test("UnionOrIntersectionType.getTypes() returns intersection members", () => { + const { type, api } = getTypeAtName(spawnAPI(typeFiles), "intersection:"); + try { + assert.ok(type.flags & TypeFlags.Intersection); + const inter = type as UnionOrIntersectionType; + const types = inter.getTypes(); + assert.ok(types.length >= 2); + } + finally { + api.close(); + } + }); + + test("UnionOrIntersectionType.getTypes() on a wrongly-cast type returns undefined without hitting the server", () => { + const src = `export const s: string = ""; export const u: string | number = "";`; + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": src, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + + // `string` is neither a union/intersection nor a template literal type, + // so it has no constituent types. The client guards on the type's flags + // and returns undefined without ever sending a request the server cannot satisfy. + const sSymbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("s:")); + assert.ok(sSymbol); + const sType = project.checker.getTypeOfSymbol(sSymbol); + assert.ok(sType); + assert.equal((sType as unknown as UnionOrIntersectionType).getTypes(), undefined); + + // A real union still returns its constituents. + const uSymbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("u:")); + assert.ok(uSymbol); + const uType = project.checker.getTypeOfSymbol(uSymbol); + assert.ok(uType); + assert.equal(((uType as UnionOrIntersectionType).getTypes()).length, 2); + } + finally { + api.close(); + } + }); + + test("IndexType.getTarget() returns the target type", () => { + const api = spawnAPI(typeFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = project.checker.resolveName("KeyOf", SymbolFlags.TypeAlias, { document: "/src/types.ts", position: 0 }); + assert.ok(symbol); + const type = project.checker.getDeclaredTypeOfSymbol(symbol); + assert.ok(type); + // KeyOf = keyof T — this is an IndexType + assert.ok(type.flags & TypeFlags.Index, `Expected IndexType, got flags ${type.flags}`); + const indexType = type as IndexType; + const target = indexType.getTarget(); + assert.ok(target); + } + finally { + api.close(); + } + }); + + test("IndexedAccessType.getObjectType() and getIndexType()", () => { + const api = spawnAPI(typeFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = project.checker.resolveName("Lookup", SymbolFlags.TypeAlias, { document: "/src/types.ts", position: 0 }); + assert.ok(symbol); + const type = project.checker.getDeclaredTypeOfSymbol(symbol); + assert.ok(type); + assert.ok(type.flags & TypeFlags.IndexedAccess, `Expected IndexedAccessType, got flags ${type.flags}`); + const ia = type as IndexedAccessType; + assert.equal(type.isIndexedAccessType(), true); + const objectType = ia.getObjectType(); + assert.ok(objectType); + const indexType = ia.getIndexType(); + assert.ok(indexType); + } + finally { + api.close(); + } + }); + + test("ConditionalType.getCheckType() and getExtendsType()", () => { + const api = spawnAPI(typeFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = project.checker.resolveName("Cond", SymbolFlags.TypeAlias, { document: "/src/types.ts", position: 0 }); + assert.ok(symbol); + const type = project.checker.getDeclaredTypeOfSymbol(symbol); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Conditional, `Expected ConditionalType, got flags ${type.flags}`); + const cond = type as ConditionalType; + assert.equal(type.isConditionalType(), true); + const checkType = cond.getCheckType(); + assert.ok(checkType); + const extendsType = cond.getExtendsType(); + assert.ok(extendsType); + } + finally { + api.close(); + } + }); + + test("ConditionalType.getTrueType() and getFalseType()", () => { + const api = spawnAPI(typeFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = project.checker.resolveName("Cond", SymbolFlags.TypeAlias, { document: "/src/types.ts", position: 0 }); + assert.ok(symbol); + const type = project.checker.getDeclaredTypeOfSymbol(symbol); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Conditional, `Expected ConditionalType, got flags ${type.flags}`); + + const trueType = (type as ConditionalType).getTrueType(); + assert.ok(trueType, "should return the true-branch type"); + assert.ok(trueType.flags & TypeFlags.StringLiteral, `Expected StringLiteral for true branch, got flags ${trueType.flags}`); + assert.equal((trueType as LiteralType).value, "yes"); + + const falseType = (type as ConditionalType).getFalseType(); + assert.ok(falseType, "should return the false-branch type"); + assert.ok(falseType.flags & TypeFlags.StringLiteral, `Expected StringLiteral for false branch, got flags ${falseType.flags}`); + assert.equal((falseType as LiteralType).value, "no"); + } + finally { + api.close(); + } + }); + + test("TemplateLiteralType.texts and getTypes()", () => { + const { type, api } = getTypeAtName(spawnAPI(typeFiles), "tpl:"); + try { + assert.ok(type.flags & TypeFlags.TemplateLiteral, `Expected TemplateLiteralType, got flags ${type.flags}`); + const tpl = type as TemplateLiteralType; + assert.ok(tpl.texts); + assert.ok(tpl.texts.length >= 2); + assert.equal(tpl.texts[0], "hello "); + const types = tpl.getTypes(); + assert.ok(types.length >= 1); + } + finally { + api.close(); + } + }); + + test("StringMappingType.getTarget() returns the mapped type", () => { + const api = spawnAPI(typeFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = typeFiles["/src/types.ts"]; + const pos = src.indexOf("Upper"); + const symbol = project.checker.getSymbolAtPosition("/src/types.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + // Uppercase<"hello"> may resolve to a StringMappingType or a string literal + if (type.flags & TypeFlags.StringMapping) { + const sm = type as StringMappingType; + const target = sm.getTarget(); + assert.ok(target); + } + // If it resolved to "HELLO" literal, that's fine too — it means eager evaluation + } + finally { + api.close(); + } + }); + + test("TupleType properties", () => { + const { type, api } = getTypeAtName(spawnAPI(typeFiles), "tuple:"); + try { + assert.ok(type.flags & TypeFlags.Object); + const ref = type as TypeReference; + assert.ok(ref.objectFlags & ObjectFlags.Reference); + const target = ref.getTarget(); + assert.ok(target); + assert.ok(target.flags & TypeFlags.Object); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - intrinsic type getters", () => { + const intrinsicFiles = { + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const x = 1;`, + }; + + test("getAnyType returns a type with Any flag", () => { + const api = spawnAPI(intrinsicFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const type = project.checker.getAnyType(); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Any); + } + finally { + api.close(); + } + }); + + test("getStringType returns a type with String flag", () => { + const api = spawnAPI(intrinsicFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const type = project.checker.getStringType(); + assert.ok(type); + assert.ok(type.flags & TypeFlags.String); + } + finally { + api.close(); + } + }); + + test("getNumberType returns a type with Number flag", () => { + const api = spawnAPI(intrinsicFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const type = project.checker.getNumberType(); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Number); + } + finally { + api.close(); + } + }); + + test("getBooleanType returns a type with Boolean flag", () => { + const api = spawnAPI(intrinsicFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const type = project.checker.getBooleanType(); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Boolean); + } + finally { + api.close(); + } + }); + + test("getVoidType returns a type with Void flag", () => { + const api = spawnAPI(intrinsicFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const type = project.checker.getVoidType(); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Void); + } + finally { + api.close(); + } + }); + + test("getUndefinedType returns a type with Undefined flag", () => { + const api = spawnAPI(intrinsicFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const type = project.checker.getUndefinedType(); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Undefined); + } + finally { + api.close(); + } + }); + + test("getNullType returns a type with Null flag", () => { + const api = spawnAPI(intrinsicFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const type = project.checker.getNullType(); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Null); + } + finally { + api.close(); + } + }); + + test("getNeverType returns a type with Never flag", () => { + const api = spawnAPI(intrinsicFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const type = project.checker.getNeverType(); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Never); + } + finally { + api.close(); + } + }); + + test("getUnknownType returns a type with Unknown flag", () => { + const api = spawnAPI(intrinsicFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const type = project.checker.getUnknownType(); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Unknown); + } + finally { + api.close(); + } + }); + + test("getBigIntType returns a type with BigInt flag", () => { + const api = spawnAPI(intrinsicFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const type = project.checker.getBigIntType(); + assert.ok(type); + assert.ok(type.flags & TypeFlags.BigInt); + } + finally { + api.close(); + } + }); + + test("getESSymbolType returns a type with ESSymbol flag", () => { + const api = spawnAPI(intrinsicFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const type = project.checker.getESSymbolType(); + assert.ok(type); + assert.ok(type.flags & TypeFlags.ESSymbol); + } + finally { + api.close(); + } + }); + + test("getNonPrimitiveType returns a type with NonPrimitive flag", () => { + const api = spawnAPI(intrinsicFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const type = project.checker.getNonPrimitiveType(); + assert.ok(type); + assert.ok(type.flags & TypeFlags.NonPrimitive); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - multi-project type ID uniqueness", () => { + test("intrinsic types from 3 projects in the same snapshot have non-colliding IDs", () => { + const api = spawnAPI({ + "/proj1/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/proj1/src/index.ts": `export const x = 1;`, + "/proj2/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/proj2/src/index.ts": `export const y = "hello";`, + "/proj3/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/proj3/src/index.ts": `export const z = true;`, + }); + try { + // Open all 3 projects — each updateSnapshot accumulates open projects + api.updateSnapshot({ openProject: "/proj1/tsconfig.json" }); + api.updateSnapshot({ openProject: "/proj2/tsconfig.json" }); + const snapshot = api.updateSnapshot({ openProject: "/proj3/tsconfig.json" }); + + const proj1 = snapshot.getProject("/proj1/tsconfig.json")!; + const proj2 = snapshot.getProject("/proj2/tsconfig.json")!; + const proj3 = snapshot.getProject("/proj3/tsconfig.json")!; + assert.ok(proj1, "proj1 should be in final snapshot"); + assert.ok(proj2, "proj2 should be in final snapshot"); + assert.ok(proj3, "proj3 should be in final snapshot"); + + // Fetch several intrinsic types from each checker. + // If type IDs collide across checkers, registerType panics → API error. + const num1 = proj1.checker.getNumberType(); + const str1 = proj1.checker.getStringType(); + const bool1 = proj1.checker.getBooleanType(); + const any1 = proj1.checker.getAnyType(); + const num2 = proj2.checker.getNumberType(); + const str2 = proj2.checker.getStringType(); + const bool2 = proj2.checker.getBooleanType(); + const any2 = proj2.checker.getAnyType(); + const num3 = proj3.checker.getNumberType(); + const str3 = proj3.checker.getStringType(); + const bool3 = proj3.checker.getBooleanType(); + const any3 = proj3.checker.getAnyType(); + + assert.ok(num1.flags & TypeFlags.Number, "proj1 number type"); + assert.ok(str1.flags & TypeFlags.String, "proj1 string type"); + assert.ok(bool1.flags & TypeFlags.Boolean, "proj1 boolean type"); + assert.ok(any1.flags & TypeFlags.Any, "proj1 any type"); + + assert.ok(num2.flags & TypeFlags.Number, "proj2 number type"); + assert.ok(str2.flags & TypeFlags.String, "proj2 string type"); + assert.ok(bool2.flags & TypeFlags.Boolean, "proj2 boolean type"); + assert.ok(any2.flags & TypeFlags.Any, "proj2 any type"); + + assert.ok(num3.flags & TypeFlags.Number, "proj3 number type"); + assert.ok(str3.flags & TypeFlags.String, "proj3 string type"); + assert.ok(bool3.flags & TypeFlags.Boolean, "proj3 boolean type"); + assert.ok(any3.flags & TypeFlags.Any, "proj3 any type"); + } + finally { + api.close(); + } + }); + + test("symbol and signature handles from 3 projects in the same snapshot have non-colliding IDs", () => { + const api = spawnAPI({ + "/proj1/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/proj1/src/index.ts": `export function add(a: number, b: number): number { return a + b; }`, + "/proj2/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/proj2/src/index.ts": `export function greet(name: string): string { return "hello " + name; }`, + "/proj3/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/proj3/src/index.ts": `export function toggle(b: boolean): boolean { return !b; }`, + }); + try { + api.updateSnapshot({ openProject: "/proj1/tsconfig.json" }); + api.updateSnapshot({ openProject: "/proj2/tsconfig.json" }); + const snapshot = api.updateSnapshot({ openProject: "/proj3/tsconfig.json" }); + + const proj1 = snapshot.getProject("/proj1/tsconfig.json")!; + const proj2 = snapshot.getProject("/proj2/tsconfig.json")!; + const proj3 = snapshot.getProject("/proj3/tsconfig.json")!; + + // Get a symbol from each project (exercises symbol registry) + const src1 = `export function add(a: number, b: number): number { return a + b; }`; + const src2 = `export function greet(name: string): string { return "hello " + name; }`; + const src3 = `export function toggle(b: boolean): boolean { return !b; }`; + + const sym1 = proj1.checker.getSymbolAtPosition("/proj1/src/index.ts", src1.indexOf("add")); + const sym2 = proj2.checker.getSymbolAtPosition("/proj2/src/index.ts", src2.indexOf("greet")); + const sym3 = proj3.checker.getSymbolAtPosition("/proj3/src/index.ts", src3.indexOf("toggle")); + assert.ok(sym1, "proj1 symbol"); + assert.ok(sym2, "proj2 symbol"); + assert.ok(sym3, "proj3 symbol"); + assert.equal(sym1.name, "add", "proj1 symbol name"); + assert.equal(sym2.name, "greet", "proj2 symbol name"); + assert.equal(sym3.name, "toggle", "proj3 symbol name"); + + // Get type of each symbol, then signatures (exercises type + signature registries) + const type1 = proj1.checker.getTypeOfSymbol(sym1); + const type2 = proj2.checker.getTypeOfSymbol(sym2); + const type3 = proj3.checker.getTypeOfSymbol(sym3); + assert.ok(type1, "proj1 function type"); + assert.ok(type2, "proj2 function type"); + assert.ok(type3, "proj3 function type"); + + const sigs1 = proj1.checker.getSignaturesOfType(type1, SignatureKind.Call); + const sigs2 = proj2.checker.getSignaturesOfType(type2, SignatureKind.Call); + const sigs3 = proj3.checker.getSignaturesOfType(type3, SignatureKind.Call); + assert.equal(sigs1.length, 1, "proj1 has 1 call signature"); + assert.equal(sigs2.length, 1, "proj2 has 1 call signature"); + assert.equal(sigs3.length, 1, "proj3 has 1 call signature"); + assert.equal(sigs1[0].parameters.length, 2, "proj1 add() has 2 params"); + assert.equal(sigs2[0].parameters.length, 1, "proj2 greet() has 1 param"); + assert.equal(sigs3[0].parameters.length, 1, "proj3 toggle() has 1 param"); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getBaseTypeOfLiteralType", () => { + test("number literal widens to number", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const x = 42;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export const x = 42;`; + const pos = src.indexOf("x ="); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const literalType = project.checker.getTypeOfSymbol(symbol); + assert.ok(literalType); + assert.ok(literalType.flags & TypeFlags.NumberLiteral); + const baseType = project.checker.getBaseTypeOfLiteralType(literalType); + assert.ok(baseType); + assert.ok(baseType.flags & TypeFlags.Number); + } + finally { + api.close(); + } + }); + + test("string literal widens to string", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const s = "hello";`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export const s = "hello";`; + const pos = src.indexOf("s "); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const literalType = project.checker.getTypeOfSymbol(symbol); + assert.ok(literalType); + assert.ok(literalType.flags & TypeFlags.StringLiteral); + const baseType = project.checker.getBaseTypeOfLiteralType(literalType); + assert.ok(baseType); + assert.ok(baseType.flags & TypeFlags.String); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getContextualType", () => { + test("contextual type from function parameter", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +function foo(x: number) {} +foo(42); +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + + // Find the argument "42" in foo(42) + // statement[1] = foo(42); which is an ExpressionStatement -> CallExpression + const callStmt = sourceFile.statements[1]; + assert.ok(callStmt); + let numLiteral: import("@typescript/typescript/unstable/ast").Expression | undefined; + callStmt.forEachChild(function visit(node) { + if (isCallExpression(node)) { + // First argument + numLiteral = node.arguments[0]; + } + node.forEachChild(visit); + }); + assert.ok(numLiteral); + const contextualType = project.checker.getContextualType(numLiteral); + assert.ok(contextualType); + assert.ok(contextualType.flags & TypeFlags.Number); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getTypeOfSymbolAtLocation", () => { + test("narrowed type via typeof check", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export function check(x: string | number) { + if (typeof x === "string") { + return x; + } + return x; +} +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\nexport function check(x: string | number) {\n if (typeof x === "string") {\n return x;\n }\n return x;\n}\n`; + + // Get the symbol for parameter "x" + const paramPos = src.indexOf("x:"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", paramPos); + assert.ok(symbol); + assert.equal(symbol.name, "x"); + + // Get the type of "x" at the wider function scope — should be string | number + const wideType = project.checker.getTypeOfSymbol(symbol); + assert.ok(wideType); + assert.ok(wideType.flags & TypeFlags.Union, `Expected union type, got flags ${wideType.flags}`); + + // Get the narrowed return x inside the if block + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + + // statement[0] is the function declaration + const funcDecl = sourceFile.statements[0]; + assert.ok(funcDecl); + // Walk to find the first "return x" — inside the if, x should be narrowed to string + let firstReturnX: import("@typescript/typescript/unstable/ast").Node | undefined; + funcDecl.forEachChild(function visit(node) { + if (isReturnStatement(node) && !firstReturnX) { + // The expression of the return statement is the identifier "x" + if (node.expression) { + firstReturnX = node.expression; + } + } + node.forEachChild(visit); + }); + assert.ok(firstReturnX); + const narrowedType = project.checker.getTypeOfSymbolAtLocation(symbol, firstReturnX); + assert.ok(narrowedType); + // Inside the if (typeof x === "string") branch, x should be narrowed to string + assert.ok(narrowedType.flags & TypeFlags.String, `Expected string type, got flags ${narrowedType.flags}`); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getShorthandAssignmentValueSymbol", () => { + test("shorthand property symbol resolves to variable", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +const name = "Alice"; +export const obj = { name }; +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + + // Find the shorthand property assignment { name } + // statement[1] = export const obj = { name }; + let shorthandNode: import("@typescript/typescript/unstable/ast").Node | undefined; + sourceFile.forEachChild(function visit(node) { + if (isShorthandPropertyAssignment(node)) { + shorthandNode = node; + } + node.forEachChild(visit); + }); + assert.ok(shorthandNode, "Should find a shorthand property assignment"); + const valueSymbol = project.checker.getShorthandAssignmentValueSymbol(shorthandNode); + assert.ok(valueSymbol); + assert.equal(valueSymbol.name, "name"); + } + finally { + api.close(); + } + }); +}); + +describe("readFile callback semantics", () => { + test("readFile: string returns content, null blocks fallback, undefined falls through to real FS", () => { + const virtualFiles: Record = { + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/index.ts": `export const x: number = 1;`, + }; + const vfs = createVirtualFileSystem(virtualFiles); + const blockedPath = "/src/blocked.ts"; + + const fs: FileSystem = { + ...vfs, + readFile: (fileName: string) => { + if (fileName === blockedPath) { + // null = file not found, don't fall back to real FS + return null; + } + // Try the VFS first; if it has the file, return its content (string). + // Otherwise return undefined to fall through to the real FS. + return vfs.readFile!(fileName); + }, + }; + + const api = new API({ + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + fs, + }); + + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + + // 1. String content: virtual file is found + const sf = project.program.getSourceFile("/src/index.ts"); + assert.ok(sf, "Virtual file should be found"); + assert.equal(sf.text, virtualFiles["/src/index.ts"]); + + // 2. undefined fallback: lib files from the real FS should be present. + // If readFile returned null for unknowns, lib files would be missing + // and `number` would not resolve — this was the original async bug. + // Verify by checking that `number` resolves to a proper type (not error). + const pos = virtualFiles["/src/index.ts"].indexOf("x:"); + const type = project.checker.getTypeAtPosition("/src/index.ts", pos); + assert.ok(type, "Type should resolve"); + assert.ok(type.flags & TypeFlags.Number, `Expected number type, got flags ${type.flags}`); + + // 3. null blocks fallback: blocked file should not be found + const blockedSf = project.program.getSourceFile(blockedPath); + assert.equal(blockedSf, undefined, "Blocked file should not be found (null prevents fallback)"); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - isArrayType / isTupleType", () => { + test("number[] is array, not tuple", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const xs: number[] = [];`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export const xs: number[] = [];`; + const pos = src.indexOf("xs"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.equal(project.checker.isArrayType(type), true); + assert.equal(project.checker.isTupleType(type), false); + } + finally { + api.close(); + } + }); + + test("readonly number[] is array", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const xs: readonly number[] = [];`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export const xs: readonly number[] = [];`; + const pos = src.indexOf("xs"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.equal(project.checker.isArrayType(type), true); + assert.equal(project.checker.isTupleType(type), false); + } + finally { + api.close(); + } + }); + + test("Array is array, not tuple", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const xs: Array = [];`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export const xs: Array = [];`; + const pos = src.indexOf("xs"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.equal(project.checker.isArrayType(type), true); + assert.equal(project.checker.isTupleType(type), false); + } + finally { + api.close(); + } + }); + + test("[number, string] is tuple, not array", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const tup: [number, string] = [1, "a"];`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export const tup: [number, string] = [1, "a"];`; + const pos = src.indexOf("tup"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.equal(project.checker.isArrayType(type), false); + assert.equal(project.checker.isTupleType(type), true); + } + finally { + api.close(); + } + }); + + test("readonly [number, string] is tuple, not array", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const tup: readonly [number, string] = [1, "a"];`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export const tup: readonly [number, string] = [1, "a"];`; + const pos = src.indexOf("tup"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.equal(project.checker.isArrayType(type), false); + assert.equal(project.checker.isTupleType(type), true); + } + finally { + api.close(); + } + }); + + test("string is neither array nor tuple", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const str: string = "";`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export const str: string = "";`; + const pos = src.indexOf("str"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.equal(project.checker.isArrayType(type), false); + assert.equal(project.checker.isTupleType(type), false); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - isReadonlySymbol", () => { + test("properties with a 'readonly' modifier", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export interface User { + readonly name: string; + age: number; +} + +export type ReadonlyUser = Readonly; +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const user = sourceFile.statements.find(isInterfaceDeclaration); + assert.ok(user); + const userProperties = project.checker.getPropertiesOfType( + project.checker.getTypeAtLocation(user), + ); + assert.equal(project.checker.isReadonlySymbol(userProperties[0]), true); + assert.equal(project.checker.isReadonlySymbol(userProperties[1]), false); + const readonlyUser = sourceFile.statements.find(isTypeAliasDeclaration); + assert.ok(readonlyUser); + const readonlyUserProperties = project.checker.getPropertiesOfType( + project.checker.getTypeAtLocation(readonlyUser), + ); + assert.equal(project.checker.isReadonlySymbol(readonlyUserProperties[0]), true); + assert.equal(project.checker.isReadonlySymbol(readonlyUserProperties[1]), true); + } + finally { + api.close(); + } + }); + + test("variables declared with 'const'", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const a = 1; export let b = 2;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const { checker } = snapshot.getProject("/tsconfig.json")!; + const a = checker.getSymbolAtPosition("/src/main.ts", "export const ".length); + const b = checker.getSymbolAtPosition("/src/main.ts", "export const a = 1; export let ".length); + assert.ok(a); + assert.ok(b); + assert.equal(checker.isReadonlySymbol(a), true); + assert.equal(checker.isReadonlySymbol(b), false); + } + finally { + api.close(); + } + }); + + test("get accessors without matching set accessors", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +class Alpha { + private _value!: number; + get value(): number { + return this._value; + } +} +class Bravo { + private _value!: number; + get value(): number { + return this._value; + } + set value(newValue: number) { + this._value = newValue; + } +} +export type A = InstanceType; +export type B = InstanceType; +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const typeAliases = sourceFile.statements.filter(isTypeAliasDeclaration); + assert.equal(typeAliases.length, 2); + const aProperties = project.checker.getPropertiesOfType( + project.checker.getTypeAtLocation(typeAliases[0]), + ); + assert.equal(project.checker.isReadonlySymbol(aProperties[1]), true); + const bProperties = project.checker.getPropertiesOfType( + project.checker.getTypeAtLocation(typeAliases[1]), + ); + assert.equal(project.checker.isReadonlySymbol(bProperties[1]), false); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getReturnTypeOfSignature", () => { + test("returns the return type of a function signature", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export function add(a: number, b: number): number { return a + b; }`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export function add(a: number, b: number): number { return a + b; }`; + const pos = src.indexOf("add("); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const returnType = project.checker.getReturnTypeOfSignature(sigs[0]); + assert.ok(returnType); + assert.ok(returnType.flags & TypeFlags.Number, `Expected number, got flags ${returnType.flags}`); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getRestTypeOfSignature", () => { + test("returns the rest type of a signature with rest parameter", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export function sum(...nums: number[]): number { return nums.reduce((a, b) => a + b, 0); }`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export function sum(...nums: number[]): number { return nums.reduce((a, b) => a + b, 0); }`; + const pos = src.indexOf("sum("); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const restType = project.checker.getRestTypeOfSignature(sigs[0]); + assert.ok(restType); + assert.ok(restType.flags & TypeFlags.Number, `Expected number type, got flags ${restType.flags}`); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getTypePredicateOfSignature", () => { + test("returns type predicate for 'x is T' guard", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export function isString(x: unknown): x is string { return typeof x === "string"; }`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export function isString(x: unknown): x is string { return typeof x === "string"; }`; + const pos = src.indexOf("isString("); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const predicate = project.checker.getTypePredicateOfSignature(sigs[0]); + assert.ok(predicate); + assert.equal(predicate.kind, TypePredicateKind.Identifier); + assert.equal(predicate.parameterName, "x"); + assert.equal(predicate.parameterIndex, 0); + assert.ok(predicate.type); + assert.ok(predicate.type.flags & TypeFlags.String); + } + finally { + api.close(); + } + }); + + test("returns type predicate for 'this is T' guard", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export class Animal { + isdog(): this is Dog { return this instanceof Dog; } +} +export class Dog extends Animal { + bark() {} +} +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\nexport class Animal {\n isdog(): this is Dog { return this instanceof Dog; }\n}\nexport class Dog extends Animal {\n bark() {}\n}\n`; + const pos = src.indexOf("isdog("); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const predicate = project.checker.getTypePredicateOfSignature(sigs[0]); + assert.ok(predicate); + assert.equal(predicate.kind, TypePredicateKind.This); + } + finally { + api.close(); + } + }); + + test("returns type predicate for 'asserts x is T'", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export function assertIsString(x: unknown): asserts x is string { if (typeof x !== "string") throw new Error(); }`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export function assertIsString(x: unknown): asserts x is string { if (typeof x !== "string") throw new Error(); }`; + const pos = src.indexOf("assertIsString("); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const predicate = project.checker.getTypePredicateOfSignature(sigs[0]); + assert.ok(predicate); + assert.equal(predicate.kind, TypePredicateKind.AssertsIdentifier); + assert.equal(predicate.parameterName, "x"); + assert.equal(predicate.parameterIndex, 0); + assert.ok(predicate.type); + assert.ok(predicate.type.flags & TypeFlags.String); + } + finally { + api.close(); + } + }); + + test("returns undefined for signature without type predicate", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export function add(a: number, b: number): number { return a + b; }`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export function add(a: number, b: number): number { return a + b; }`; + const pos = src.indexOf("add("); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const predicate = project.checker.getTypePredicateOfSignature(sigs[0]); + assert.equal(predicate, undefined); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getBaseTypes", () => { + test("returns base types of a class", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export class Base { + x: number = 0; +} +export class Derived extends Base { + y: string = ""; +} +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\nexport class Base {\n x: number = 0;\n}\nexport class Derived extends Base {\n y: string = "";\n}\n`; + const pos = src.indexOf("Derived"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getDeclaredTypeOfSymbol(symbol); + assert.ok(type); + const baseTypes = project.checker.getBaseTypes(type as InterfaceType); + assert.ok(baseTypes.length > 0, "Should have at least one base type"); + const baseSymbol = baseTypes[0].getSymbol(); + assert.ok(baseSymbol); + assert.equal(baseSymbol.name, "Base"); + } + finally { + api.close(); + } + }); + + test("returns base types of an interface", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export interface Animal { + name: string; +} +export interface Dog extends Animal { + bark(): void; +} +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\nexport interface Animal {\n name: string;\n}\nexport interface Dog extends Animal {\n bark(): void;\n}\n`; + const pos = src.indexOf("Dog"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getDeclaredTypeOfSymbol(symbol); + assert.ok(type); + const baseTypes = project.checker.getBaseTypes(type as InterfaceType); + assert.ok(baseTypes.length > 0, "Should have at least one base type"); + const baseSymbol = baseTypes[0].getSymbol(); + assert.ok(baseSymbol); + assert.equal(baseSymbol.name, "Animal"); + } + finally { + api.close(); + } + }); + + test("does not panic for a type alias to a generic interface instantiation", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export interface Box { + value: T; +} +export type BoxOfString = Box; +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const typeAlias = sourceFile.statements.find(isTypeAliasDeclaration); + assert.ok(typeAlias); + const type = project.checker.getTypeAtLocation(typeAlias); + assert.ok(type); + // A generic interface instantiation produces a type reference, not an + // interface type, so it has no base types and yields []. + const baseTypes = project.checker.getBaseTypes(type as InterfaceType); + assert.deepEqual(baseTypes, []); + } + finally { + api.close(); + } + }); +}); + +describe("Type - getBaseTypes", () => { + test("returns base types for a class type and undefined for a non-class/interface", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export class Base { + x: number = 0; +} +export class Derived extends Base { + y: string = ""; +} +export const n: number = 0; +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\nexport class Base {\n x: number = 0;\n}\nexport class Derived extends Base {\n y: string = "";\n}\nexport const n: number = 0;\n`; + + const derivedSymbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("Derived")); + assert.ok(derivedSymbol); + const derivedType = project.checker.getDeclaredTypeOfSymbol(derivedSymbol); + assert.ok(derivedType.isClassOrInterface(), "Derived should be a class/interface type"); + const baseTypes = derivedType.getBaseTypes(); + assert.ok(baseTypes && baseTypes.length > 0, "class type should have base types"); + const baseSymbol = baseTypes![0].getSymbol(); + assert.equal(baseSymbol?.name, "Base"); + + // A primitive type is not a class/interface, so getBaseTypes() is undefined. + const numberSymbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("n:")); + assert.ok(numberSymbol); + const numberType = project.checker.getTypeOfSymbol(numberSymbol); + assert.ok(numberType); + assert.equal(numberType.isClassOrInterface(), false); + assert.equal(numberType.getBaseTypes(), undefined); + } + finally { + api.close(); + } + }); +}); + +describe("Type - isErrorType", () => { + test("identifies the error type from an unresolvable annotation", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +declare const good: string; +declare const bad: ThisTypeDoesNotExist; +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\ndeclare const good: string;\ndeclare const bad: ThisTypeDoesNotExist;\n`; + + const badSymbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("bad")); + assert.ok(badSymbol); + const badType = project.checker.getTypeOfSymbol(badSymbol); + assert.ok(badType); + assert.ok(badType.isErrorType(), "unresolved annotation should yield the error type"); + assert.ok(isErrorType(badType)); + + const goodSymbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("good")); + assert.ok(goodSymbol); + const goodType = project.checker.getTypeOfSymbol(goodSymbol); + assert.ok(goodType); + assert.equal(goodType.isErrorType(), false, "string type is not the error type"); + assert.equal(isErrorType(goodType), false); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - well-known symbols", () => { + test("isUnknownSymbol identifies the aliased unknown symbol", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export const value = 1; +export type Alias = typeof value; +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\nexport const value = 1;\nexport type Alias = typeof value;\n`; + + // A real symbol is not the unknown/undefined symbol. + const valueSymbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("value")); + assert.ok(valueSymbol); + assert.equal(project.checker.isUnknownSymbol(valueSymbol), false); + assert.equal(project.checker.isUndefinedSymbol(valueSymbol), false); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - well-known signatures", () => { + test("isUnknownSignature identifies an unresolvable call", () => { + const src = ` +const ok = (x: number) => x; +ok(1); +const notCallable = 1; +notCallable(); +`; + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": src, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const calls: Node[] = []; + sourceFile.forEachChild(function visit(node) { + if (isCallExpression(node)) calls.push(node); + node.forEachChild(visit); + }); + assert.equal(calls.length, 2, "should find two call expressions"); + + // The valid call resolves to a real signature, not the unknown signature. + const okSig = project.checker.getResolvedSignature(calls[0]); + assert.equal(project.checker.isUnknownSignature(okSig), false); + + // The call to a non-callable value yields the unknown signature. + const badSig = project.checker.getResolvedSignature(calls[1]); + assert.equal(project.checker.isUnknownSignature(badSig), true); + } + finally { + api.close(); + } + }); +}); + +describe("Symbol - escaped names and tables", () => { + test("getExports/getMembers return a cached Map keyed by escaped name", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export class Animal { + name: string = ""; + speak(): void {} +} +export const value = 1; +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const moduleSymbol = project.checker.getSymbolAtLocation(sourceFile); + assert.ok(moduleSymbol); + + const exports = moduleSymbol.getExports(); + assert.ok(exports.has(escapeLeadingUnderscores("Animal"))); + assert.ok(exports.get(escapeLeadingUnderscores("value"))); + // Calling again returns the same cached Map instance. + assert.strictEqual(moduleSymbol.getExports(), exports); + + const animal = exports.get(escapeLeadingUnderscores("Animal"))!; + const members = animal.getMembers(); + assert.ok(members.get(escapeLeadingUnderscores("name"))); + assert.ok(members.get(escapeLeadingUnderscores("speak"))); + assert.strictEqual(animal.getMembers(), members); + } + finally { + api.close(); + } + }); + + test("anonymous type symbol has a __-escaped name, never the \\xFE sentinel", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export const obj: { a: number } = { a: 1 }; +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\nexport const obj: { a: number } = { a: 1 };\n`; + const objSymbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("obj")); + assert.ok(objSymbol); + const objType = project.checker.getTypeOfSymbol(objSymbol); + assert.ok(objType); + const typeSymbol = objType.getSymbol(); + assert.ok(typeSymbol); + // The anonymous type literal symbol is internally named; over the wire + // it must be the "__type" escaped form, not the raw "\xFE" sentinel. + assert.equal(typeSymbol.escapedName, InternalSymbolName.Type); + assert.ok(!typeSymbol.escapedName.includes("\xFE")); + } + finally { + api.close(); + } + }); +}); + +describe("ast - escapeLeadingUnderscores", () => { + test("round-trips display and escaped names", () => { + assert.equal(escapeLeadingUnderscores("foo"), "foo"); + assert.equal(escapeLeadingUnderscores("_foo"), "_foo"); + assert.equal(escapeLeadingUnderscores("__foo"), "___foo"); + assert.equal(unescapeLeadingUnderscores("foo" as __String), "foo"); + assert.equal(unescapeLeadingUnderscores("__type" as __String), "__type"); + assert.equal(unescapeLeadingUnderscores("___foo" as __String), "__foo"); + }); +}); + +describe("ast - getJSDocTags", () => { + test("returns a node's own tags, and inherited @param / @template tags", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +/** + * Adds two numbers. + * @param a the first number + * @param b the second number + * @returns the sum + */ +export function add(a: number, b: number): number { + return a + b; +} + +/** + * @template T the element type + * @param x a value + */ +export function identity(x: T): T { + return x; +} + +/** @deprecated use add */ +export const total = add(1, 2); +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const functions = [...sourceFile.statements].filter(isFunctionDeclaration); + const add = functions[0]; + const identity = functions[1]; + assert.ok(add); + assert.ok(identity); + + // A function reports its own JSDoc tags. + assert.deepEqual(getJSDocTags(add).map(t => t.tagName.text), ["param", "param", "returns"]); + + // A parameter inherits the matching @param tag from its containing + // signature. + const paramA = add.parameters[0]; + const paramATags = getJSDocTags(paramA); + assert.deepEqual(paramATags.map(t => t.tagName.text), ["param"]); + const paramATag = paramATags[0]; + assert.ok(isJSDocParameterTag(paramATag)); + assert.ok(isIdentifier(paramATag.name)); + assert.equal(paramATag.name.text, "a"); + + const paramB = add.parameters[1]; + const paramBTags = getJSDocTags(paramB); + assert.deepEqual(paramBTags.map(t => t.tagName.text), ["param"]); + const paramBTag = paramBTags[0]; + assert.ok(isJSDocParameterTag(paramBTag)); + assert.ok(isIdentifier(paramBTag.name)); + assert.equal(paramBTag.name.text, "b"); + + // A type parameter inherits the matching @template tag. + const typeParam = identity.typeParameters![0]; + assert.deepEqual(getJSDocTags(typeParam).map(t => t.tagName.text), ["template"]); + + // The value parameter of `identity` inherits its @param tag. + const identityParam = identity.parameters[0]; + assert.deepEqual(getJSDocTags(identityParam).map(t => t.tagName.text), ["param"]); + + // A variable declaration walks up its comment-location chain + // (declaration -> declaration list -> statement) to the JSDoc on the + // containing variable statement. + const variable = sourceFile.statements.find(isVariableStatement); + assert.ok(variable); + const declaration = variable.declarationList.declarations[0]; + assert.deepEqual(getJSDocTags(declaration).map(t => t.tagName.text), ["deprecated"]); + } + finally { + api.close(); + } + }); + + test("a function-expression parameter inherits @param tags from the variable statement", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { allowJs: true, checkJs: true } }), + "/src/main.js": ` +/** + * @param {string} name a name + * @returns {number} the length + */ +var measure = function (name) { + return name.length; +}; +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.js"); + assert.ok(sourceFile); + const variable = sourceFile.statements.find(isVariableStatement); + assert.ok(variable); + const declaration = variable.declarationList.declarations[0]; + const funcExpr = declaration.initializer; + assert.ok(funcExpr); + + // The variable statement carries the @param and @returns tags. + assert.deepEqual(getJSDocTags(declaration).map(t => t.tagName.text), ["param", "returns"]); + + // The function expression's parameter walks declaration -> list -> + // statement and inherits the matching @param tag. + const param = (funcExpr as unknown as { parameters: NodeArray; }).parameters[0]; + const paramTags = getJSDocTags(param); + assert.deepEqual(paramTags.map(t => t.tagName.text), ["param"]); + const paramTag = paramTags[0]; + assert.ok(isJSDocParameterTag(paramTag)); + assert.ok(isIdentifier(paramTag.name)); + assert.equal(paramTag.name.text, "name"); + } + finally { + api.close(); + } + }); + + test("a @type cast tag is owned by its parenthesized expression, not the declaration", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { allowJs: true, checkJs: true } }), + "/src/main.js": ` +/** @type {string} */ +const value = "hello"; + +const cast = /** @type {number} */ (someValue); +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.js"); + assert.ok(sourceFile); + const statements = [...sourceFile.statements].filter(isVariableStatement); + + // A @type tag directly on a declaration is reported for that declaration. + const valueDecl = statements[0].declarationList.declarations[0]; + assert.deepEqual(getJSDocTags(valueDecl).map(t => t.tagName.text), ["type"]); + + // A @type cast tag attached to a parenthesized expression is owned by + // that expression and is not reported for the enclosing declaration. + const castDecl = statements[1].declarationList.declarations[0]; + assert.deepEqual(getJSDocTags(castDecl).map(t => t.tagName.text), []); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getPropertiesOfType", () => { + test("returns properties of an object type", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export interface Person { + name: string; + age: number; + greet(): void; +} +export declare const p: Person; +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\nexport interface Person {\n name: string;\n age: number;\n greet(): void;\n}\nexport declare const p: Person;\n`; + const pos = src.indexOf("p: Person"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const props = project.checker.getPropertiesOfType(type); + assert.ok(props.length >= 3, `Expected at least 3 properties, got ${props.length}`); + const names = props.map(p => p.name); + assert.ok(names.includes("name"), "should have 'name' property"); + assert.ok(names.includes("age"), "should have 'age' property"); + assert.ok(names.includes("greet"), "should have 'greet' property"); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getIndexInfosOfType", () => { + test("returns index signatures of an indexed type", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export interface StringMap { + [key: string]: number; +} +export declare const m: StringMap; +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\nexport interface StringMap {\n [key: string]: number;\n}\nexport declare const m: StringMap;\n`; + const pos = src.indexOf("m: StringMap"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const indexInfos = project.checker.getIndexInfosOfType(type); + assert.ok(indexInfos.length > 0, "Should have at least one index info"); + const info = indexInfos[0]; + assert.ok(info.keyType); + assert.ok(info.keyType.flags & TypeFlags.String, `Expected string key type, got flags ${info.keyType.flags}`); + assert.ok(info.valueType); + assert.ok(info.valueType.flags & TypeFlags.Number, `Expected number value type, got flags ${info.valueType.flags}`); + assert.equal(info.isReadonly, false); + } + finally { + api.close(); + } + }); + + test("readonly index signature reports isReadonly true", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export interface ReadonlyMap { + readonly [key: string]: number; +} +export declare const m: ReadonlyMap; +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\nexport interface ReadonlyMap {\n readonly [key: string]: number;\n}\nexport declare const m: ReadonlyMap;\n`; + const pos = src.indexOf("m: ReadonlyMap"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const indexInfos = project.checker.getIndexInfosOfType(type); + assert.ok(indexInfos.length > 0); + assert.equal(indexInfos[0].isReadonly, true); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getConstraintOfTypeParameter", () => { + test("returns constraint of a type parameter", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export function identity(x: T): T { return x; }`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export function identity(x: T): T { return x; }`; + const pos = src.indexOf("identity<"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + assert.ok(sigs[0].typeParameters && sigs[0].typeParameters.length > 0, "Should have type parameters"); + const typeParams = sigs[0].getTypeParameters(); + const constraint = project.checker.getConstraintOfTypeParameter(typeParams[0]); + assert.ok(constraint); + assert.ok(constraint.flags & TypeFlags.String, `Expected string constraint, got flags ${constraint.flags}`); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - TypeParameter getters", () => { + test("getConstraint() and getDefault() return the constraint and default types", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export function f(x: T): T { return x; }`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export function f(x: T): T { return x; }`; + const pos = src.indexOf("f<"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const typeParams = sigs[0].getTypeParameters(); + assert.ok(typeParams.length > 0, "Should have type parameters"); + const typeParam = typeParams[0] as TypeParameter; + + const constraint = typeParam.getConstraint(); + assert.ok(constraint); + assert.ok(constraint.flags & TypeFlags.String, `Expected string constraint, got flags ${constraint.flags}`); + + const defaultType = typeParam.getDefault(); + assert.ok(defaultType); + assert.ok(defaultType.flags & TypeFlags.StringLiteral, `Expected string literal default, got flags ${defaultType.flags}`); + } + finally { + api.close(); + } + }); + + test("getConstraint() and getDefault() return undefined when there is none", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export function f(x: T): T { return x; }`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export function f(x: T): T { return x; }`; + const pos = src.indexOf("f<"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const typeParams = sigs[0].getTypeParameters(); + assert.ok(typeParams.length > 0, "Should have type parameters"); + const typeParam = typeParams[0] as TypeParameter; + + assert.equal(typeParam.getConstraint(), undefined); + assert.equal(typeParam.getDefault(), undefined); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getTypeArguments", () => { + test("returns type arguments of a generic instantiation", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const arr: Array = [1, 2, 3];`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export const arr: Array = [1, 2, 3];`; + const pos = src.indexOf("arr:"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const typeArgs = project.checker.getTypeArguments(type as TypeReference); + assert.ok(typeArgs.length > 0, "Should have type arguments"); + assert.ok(typeArgs[0].flags & TypeFlags.Number, `Expected number type argument, got flags ${typeArgs[0].flags}`); + } + finally { + api.close(); + } + }); + + test("a wrongly-typed call throws on the client without taking down the server", () => { + const src = `export const s: string = ""; export const arr: Array = [1];`; + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": src, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + + // `string` is not a type reference. When getTypeArguments is reached + // with one, the server panics, but the per-request panic recovery + // converts that into an error response rather than crashing the process. + const sSymbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("s:")); + assert.ok(sSymbol); + const sType = project.checker.getTypeOfSymbol(sSymbol); + assert.ok(sType); + assert.throws(() => project.checker.getTypeArguments(sType as unknown as TypeReference)); + + // The server survived: a subsequent valid request still succeeds. + const arrSymbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("arr:")); + assert.ok(arrSymbol); + const arrType = project.checker.getTypeOfSymbol(arrSymbol); + assert.ok(arrType); + const typeArgs = project.checker.getTypeArguments(arrType as TypeReference); + assert.ok(typeArgs.length > 0, "Server should still serve valid requests"); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getBaseConstraintOfType", () => { + test("returns the base constraint of a type parameter", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export function identity(x: T): T { return x; }`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `export function identity(x: T): T { return x; }`; + const pos = src.indexOf("identity<"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const sigs = project.checker.getSignaturesOfType(type, SignatureKind.Call); + assert.ok(sigs.length > 0); + const typeParams = sigs[0].getTypeParameters(); + const constraint = project.checker.getBaseConstraintOfType(typeParams[0]); + assert.ok(constraint, "Should resolve a base constraint"); + assert.ok(constraint.flags & TypeFlags.String, `Expected string constraint, got flags ${constraint.flags}`); + } + finally { + api.close(); + } + }); + + test("returns undefined for a non-instantiable type", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const x: number = 1;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = `export const x: number = 1;`.indexOf("x:"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const constraint = project.checker.getBaseConstraintOfType(type); + assert.equal(constraint, undefined); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getPropertyOfType", () => { + test("returns a named property symbol of a type", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export interface Person { + name: string; + age: number; +} +export declare const p: Person; +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const src = `\nexport interface Person {\n name: string;\n age: number;\n}\nexport declare const p: Person;\n`; + const pos = src.indexOf("p: Person"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const nameProp = project.checker.getPropertyOfType(type, "name"); + assert.ok(nameProp, "Should find 'name' property"); + assert.equal(nameProp.name, "name"); + const missing = project.checker.getPropertyOfType(type, "doesNotExist"); + assert.equal(missing, undefined); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getConstantValue", () => { + test("returns numeric value of an enum member", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export enum E { A = 1, B = 2 }`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + let memberB: Node | undefined; + sourceFile.forEachChild(function visit(node) { + if (node.kind === SyntaxKind.EnumMember) { + const text = sourceFile.text.slice(node.pos, node.end).trim(); + if (text.startsWith("B")) memberB = node; + } + node.forEachChild(visit); + }); + assert.ok(memberB, "Should find enum member B"); + const value = project.checker.getConstantValue(memberB); + assert.equal(value, 2); + } + finally { + api.close(); + } + }); + + test("returns string value of a string-initialized enum member", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export enum Color { Red = "red" }`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + let member: Node | undefined; + sourceFile.forEachChild(function visit(node) { + if (node.kind === SyntaxKind.EnumMember) member = node; + node.forEachChild(visit); + }); + assert.ok(member); + const value = project.checker.getConstantValue(member); + assert.equal(value, "red"); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getSignatureFromDeclaration", () => { + test("returns the signature of a function declaration", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export function add(a: number, b: number): number { return a + b; }`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + let funcDecl: Node | undefined; + sourceFile.forEachChild(function visit(node) { + if (isFunctionDeclaration(node)) funcDecl = node; + node.forEachChild(visit); + }); + assert.ok(funcDecl, "Should find the function declaration"); + const sig = project.checker.getSignatureFromDeclaration(funcDecl); + assert.ok(sig, "Should resolve a signature"); + assert.equal(sig.parameters.length, 2); + const returnType = project.checker.getReturnTypeOfSignature(sig); + assert.ok(returnType); + assert.ok(returnType.flags & TypeFlags.Number); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getExportSpecifierLocalTargetSymbol", () => { + test("resolves the local target of an export specifier", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +const value = 42; +export { value as renamed }; +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + let exportSpecifier: Node | undefined; + sourceFile.forEachChild(function visit(node) { + if (node.kind === SyntaxKind.ExportSpecifier) exportSpecifier = node; + node.forEachChild(visit); + }); + assert.ok(exportSpecifier, "Should find the export specifier"); + const target = project.checker.getExportSpecifierLocalTargetSymbol(exportSpecifier); + assert.ok(target, "Should resolve a local target symbol"); + assert.equal(target.name, "value"); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getAliasedSymbol", () => { + test("resolves an import alias to its target symbol", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/foo.ts": `export const foo = 42;`, + "/src/main.ts": `import { foo } from "./foo";\nexport const usage = foo;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = `import { foo } from "./foo";`.indexOf("foo }"); + const aliasSymbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(aliasSymbol); + assert.ok(aliasSymbol.flags & SymbolFlags.Alias, "Import binding should be an alias"); + const aliased = project.checker.getAliasedSymbol(aliasSymbol); + assert.ok(aliased, "Should resolve the aliased symbol"); + assert.equal(aliased.name, "foo"); + assert.ok(!(aliased.flags & SymbolFlags.Alias), "Target should not be an alias"); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getFullyQualifiedName", () => { + test("returns module-qualified names for exported symbols and dotted names for members", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/index.ts": ` +export namespace Outer { + export class Inner {} +} +export class Standalone {} +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + const moduleSymbol = project.checker.getSymbolAtLocation(sourceFile); + assert.ok(moduleSymbol); + const exports = project.checker.getExportsOfModule(moduleSymbol); + const standalone = exports.find(e => e.name === "Standalone"); + assert.ok(standalone); + assert.equal(project.checker.getFullyQualifiedName(standalone), `"/src/index".Standalone`); + const outer = exports.find(e => e.name === "Outer"); + assert.ok(outer); + const inner = (outer.getExports()).get("Inner" as __String); + assert.ok(inner); + assert.equal(project.checker.getFullyQualifiedName(inner), `"/src/index".Outer.Inner`); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getExportsOfModule", () => { + test("returns all exports including re-exports via 'export *'", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/inner.ts": `export const innerValue = 1;`, + "/src/index.ts": ` +export const direct = 1; +export * from "./inner"; +`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + const moduleSymbol = project.checker.getSymbolAtLocation(sourceFile); + assert.ok(moduleSymbol, "Source file should have a module symbol"); + const exports = project.checker.getExportsOfModule(moduleSymbol); + const names = exports.map(e => e.name); + assert.ok(names.includes("direct"), "should include directly-declared export"); + assert.ok(names.includes("innerValue"), "should include 'export *' re-export"); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getSymbolsInScope", () => { + const scopeFiles = { + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +const outerValue = 1; +interface OuterType { x: number; } +function f() { + const innerValue = 2; + return innerValue; +} +`, + }; + + test("returns symbols visible at a position", () => { + const api = spawnAPI(scopeFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = scopeFiles["/src/main.ts"].indexOf("return innerValue"); + const symbols = project.checker.getSymbolsInScope( + { document: "/src/main.ts", position: pos }, + SymbolFlags.Value, + ); + const names = symbols.map(s => s.name); + assert.ok(names.includes("innerValue"), "should include local variable"); + assert.ok(names.includes("outerValue"), "should include outer variable"); + assert.ok(names.includes("f"), "should include enclosing function"); + assert.ok(!names.includes("OuterType"), "should not include type-only meanings"); + } + finally { + api.close(); + } + }); + + test("returns type symbols when asked for type meaning at a node", () => { + const api = spawnAPI(scopeFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + const symbols = project.checker.getSymbolsInScope(sourceFile, SymbolFlags.Type); + const names = symbols.map(s => s.name); + assert.ok(names.includes("OuterType"), "should include interface declared in file"); + assert.ok(names.includes("Array"), "should include global type symbols"); + } + finally { + api.close(); + } + }); +}); + +describe("Symbol - getDocumentationComment and getJsDocTags", () => { + const docFiles = { + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +/** + * Adds two numbers together. + * @param a the first number + * @returns the sum + */ +export function add(a: number, b: number): number { return a + b; } +`, + }; + + test("getDocumentationComment returns the leading comment text", () => { + const api = spawnAPI(docFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = docFiles["/src/main.ts"].indexOf("add(a"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const doc = symbol.getDocumentationComment(project.checker); + assert.ok(doc.includes("Adds two numbers together"), `Expected documentation, got: ${doc}`); + assert.ok(!doc.includes("@param"), "Documentation comment should not include tags"); + } + finally { + api.close(); + } + }); + + test("getJsDocTags returns structured tag name/text pairs", () => { + const api = spawnAPI(docFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = docFiles["/src/main.ts"].indexOf("add(a"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const tags = symbol.getJsDocTags(project.checker); + const param = tags.find(t => t.name === "param"); + assert.ok(param, `Expected a @param tag, got: ${JSON.stringify(tags)}`); + assert.equal(param.text, "a the first number"); + const returns = tags.find(t => t.name === "returns"); + assert.ok(returns, `Expected a @returns tag, got: ${JSON.stringify(tags)}`); + assert.equal(returns.text, "the sum"); + } + finally { + api.close(); + } + }); +}); + +describe("TypeParameter - isThisType", () => { + test("isThisType is true for the polymorphic 'this' type in a class method", () => { + const src = `\nexport class Builder {\n setName(name: string): this { return this; }\n}\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + // ": this {" — offset 2 past ': ' lands on 't' in the return-type 'this' + const pos = src.indexOf(": this {") + 2; + const type = project.checker.getTypeAtPosition("/src/main.ts", pos); + assert.ok(type, "Expected a type at the 'this' position"); + assert.ok(type.flags & TypeFlags.TypeParameter, "Expected TypeParameter"); + const typeParam = type as TypeParameter; + assert.equal(typeParam.isThisType, true); + } + finally { + api.close(); + } + }); + + test("isThisType is absent for a regular generic type parameter", () => { + const src = `\nexport function identity(x: T): T { return x; }\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + // Point to 'T' in the type parameter declaration '' — getTypeAtPosition + // on a type annotation reference doesn't resolve to TypeParameter, but + // the declaration position does. + const pos = src.indexOf("") + 1; + const type = project.checker.getTypeAtPosition("/src/main.ts", pos); + assert.ok(type, "Expected a type at the 'T' position"); + assert.ok(type.flags & TypeFlags.TypeParameter, "Expected TypeParameter"); + const typeParam = type as TypeParameter; + assert.ok(!typeParam.isThisType, "Expected isThisType to be absent/false for a regular type parameter"); + } + finally { + api.close(); + } + }); +}); + +describe("Type - getAliasTypeArguments", () => { + test("returns the type arguments of a single-param generic type alias", () => { + const src = `\ntype Box = { value: T };\nexport const x: Box = { value: "hi" };\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("x:"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const aliasArgs = type.getAliasTypeArguments(); + assert.equal(aliasArgs.length, 1, "Expected 1 alias type argument"); + assert.ok(aliasArgs[0].flags & TypeFlags.String, `Expected string, got flags ${aliasArgs[0].flags}`); + } + finally { + api.close(); + } + }); + + test("returns multiple type arguments for a multi-param generic type alias", () => { + const src = `\ntype Pair = [A, B];\nexport const p: Pair = ["hello", 42];\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("p:"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const aliasArgs = type.getAliasTypeArguments(); + assert.equal(aliasArgs.length, 2, "Expected 2 alias type arguments"); + assert.ok(aliasArgs[0].flags & TypeFlags.String, `Expected first arg to be string, got flags ${aliasArgs[0].flags}`); + assert.ok(aliasArgs[1].flags & TypeFlags.Number, `Expected second arg to be number, got flags ${aliasArgs[1].flags}`); + } + finally { + api.close(); + } + }); + + test("returns empty array for a non-alias generic type", () => { + const src = `\nexport const arr: Array = ["hello"];\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("arr:"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const aliasArgs = type.getAliasTypeArguments(); + assert.equal(aliasArgs.length, 0, "Expected no alias type arguments for a direct generic reference"); + } + finally { + api.close(); + } + }); +}); + +describe("Type - getAliasSymbol", () => { + test("returns the symbol for a non-generic type alias", () => { + // Object-type aliases preserve aliasSymbol; primitive aliases (type Foo = string) do not. + const src = `\ntype Point = { x: number; y: number };\nexport const p: Point = { x: 1, y: 2 };\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("p:"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const aliasSymbol = type.getAliasSymbol(); + assert.ok(aliasSymbol, "Expected alias symbol to exist"); + assert.equal(aliasSymbol.name, "Point"); + } + finally { + api.close(); + } + }); + + test("returns the symbol for a generic type alias", () => { + const src = `\ntype Container = { item: T };\nexport const c: Container = { item: 42 };\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("c:"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const aliasSymbol = type.getAliasSymbol(); + assert.ok(aliasSymbol, "Expected alias symbol for generic alias"); + assert.equal(aliasSymbol.name, "Container"); + } + finally { + api.close(); + } + }); + + test("returns undefined for a non-alias type", () => { + const src = `\nexport const str: string = "test";\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("str:"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + const aliasSymbol = type.getAliasSymbol(); + assert.equal(aliasSymbol, undefined, "Expected no alias symbol for primitive type"); + } + finally { + api.close(); + } + }); +}); + +describe("IntrinsicType - intrinsicName", () => { + test("intrinsicName matches the primitive type name", () => { + const src = `\nexport const x: string = "hello";\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const stringType = project.checker.getStringType(); + assert.equal((stringType as IntrinsicType).intrinsicName, "string"); + const anyType = project.checker.getAnyType(); + assert.equal((anyType as IntrinsicType).intrinsicName, "any"); + const neverType = project.checker.getNeverType(); + assert.equal((neverType as IntrinsicType).intrinsicName, "never"); + const pos = src.indexOf("x:"); + const sym = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(sym); + const litType = project.checker.getTypeOfSymbol(sym); + assert.ok(litType); + assert.ok(litType.flags & TypeFlags.Intrinsic); + assert.equal((litType as IntrinsicType).intrinsicName, "string"); + } + finally { + api.close(); + } + }); +}); + +describe("FreshableType - getFreshType and getRegularType", () => { + test("LiteralType.value is empty string for the empty-string literal type", () => { + const src = `\nexport const empty: "" = "";\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("empty:")); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.ok(type.flags & TypeFlags.StringLiteral, "Expected StringLiteral"); + const literal = type as LiteralType; + assert.equal(literal.value, "", "value should be empty string, not undefined"); + } + finally { + api.close(); + } + }); + + test("LiteralType.value is accessible via the FreshableType hierarchy", () => { + const src = `\nexport const greeting: "hello" = "hello";\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("greeting:"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.ok(type.flags & TypeFlags.StringLiteral, "Expected StringLiteral"); + const literal = type as LiteralType; + assert.equal(literal.value, "hello"); + } + finally { + api.close(); + } + }); + + test("BigIntLiteralType.value is a bigint (positive and negative)", () => { + const src = `\nexport const pos = 123n;\nexport const neg = -123n;\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + + const posSymbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("pos =")); + assert.ok(posSymbol); + const posType = project.checker.getTypeOfSymbol(posSymbol); + assert.ok(posType); + assert.ok(posType.flags & TypeFlags.BigIntLiteral, "Expected BigIntLiteral"); + const posLiteral = posType as BigIntLiteralType; + assert.equal(typeof posLiteral.value, "bigint"); + assert.equal(posLiteral.value, 123n); + + const negSymbol = project.checker.getSymbolAtPosition("/src/main.ts", src.indexOf("neg =")); + assert.ok(negSymbol); + const negType = project.checker.getTypeOfSymbol(negSymbol); + assert.ok(negType); + assert.ok(negType.flags & TypeFlags.BigIntLiteral, "Expected BigIntLiteral"); + const negLiteral = negType as BigIntLiteralType; + assert.equal(typeof negLiteral.value, "bigint"); + assert.equal(negLiteral.value, -123n); + } + finally { + api.close(); + } + }); + + test("getFreshType() returns a fresh twin with matching value", () => { + const src = `\nexport const greeting: "hello" = "hello";\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("greeting:"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Freshable, "Type should be a freshable type"); + const fresh = (type as FreshableType).getFreshType(); + assert.ok(fresh, "Expected getFreshType() to return non-undefined for a literal type"); + assert.ok(fresh.flags & TypeFlags.StringLiteral, "Fresh type should be a StringLiteral"); + assert.equal((fresh as LiteralType).value, "hello", "Fresh type should carry the same value"); + assert.notEqual(fresh.id, type.id, "Fresh type should not be the original type"); + const freshFresh = fresh.getFreshType(); + assert.ok(freshFresh, "Expected getFreshType() to return non-undefined for a fresh type"); + assert.equal(freshFresh.id, fresh.id, "Fresh type of a fresh type should be the fresh type"); + } + finally { + api.close(); + } + }); + + test("getRegularType() on a fresh literal returns the regular twin", () => { + // The initial type response from getTypeOfSymbol does not always include the + // regularType handle, so getRegularType() is tested via the fresh twin which + // always includes its regularType in its own response. + const src = `\nexport const greeting: "hello" = "hello";\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("greeting:"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Freshable, "Type should be a freshable type"); + const fresh = (type as FreshableType).getFreshType(); + assert.ok(fresh, "Need fresh type for this test"); + assert.ok(fresh.flags & TypeFlags.Freshable, "Fresh type should be a freshable type"); + const regular = fresh.getRegularType(); + assert.ok(regular, "Expected getRegularType() on the fresh twin to return non-undefined"); + assert.ok(regular.flags & TypeFlags.StringLiteral, "Regular type should be a StringLiteral"); + assert.equal((regular as LiteralType).value, "hello", "Regular type should carry the same value"); + assert.equal(regular.id, type.id, "Regular type should be the original type"); + } + finally { + api.close(); + } + }); + + test("getFreshType() and getRegularType() work for computed enum types (TypeFlags.Enum)", () => { + // getTypeOfSymbol on an ambient enum member returns the FRESH computed enum type. + // For fresh types: getFreshType() returns self, getRegularType() returns the regular twin. + const src = `\ndeclare enum Status { Pending }\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("Pending"); + const symbol = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(symbol); + const type = project.checker.getTypeOfSymbol(symbol); + assert.ok(type); + assert.ok(type.flags & TypeFlags.Enum, `Expected TypeFlags.Enum, got ${type.flags}`); + assert.ok(type.flags & TypeFlags.Freshable, "Enum type should be freshable"); + // The returned type IS the fresh type: getFreshType() returns itself + const fresh = (type as FreshableType).getFreshType(); + assert.ok(fresh, "Expected getFreshType() to return non-undefined"); + assert.equal(fresh.id, type.id, "getFreshType() on a fresh enum type returns itself"); + // getRegularType() returns the regular twin (a different type) + const regular = (type as FreshableType).getRegularType(); + assert.ok(regular, "Expected getRegularType() to return non-undefined"); + assert.ok(regular.flags & TypeFlags.Enum, "Regular enum type should also have TypeFlags.Enum"); + assert.notEqual(regular.id, type.id, "Regular type should be distinct from the fresh type"); + // Round-trip: regular → getFreshType() → back to the original fresh type + const backToFresh = (regular as FreshableType).getFreshType(); + assert.ok(backToFresh); + assert.equal(backToFresh.id, type.id, "Round-trip through regular/fresh returns the original type"); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - isContextSensitive", () => { + test("arrow function with no type annotation is context sensitive", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export const fn = (x) => x;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + // Find the arrow function node + let arrowFn: import("@typescript/typescript/unstable/ast").Node | undefined; + sourceFile.forEachChild(function visit(node) { + if (node.kind === SyntaxKind.ArrowFunction) { + arrowFn = node; + } + node.forEachChild(visit); + }); + assert.ok(arrowFn, "Should find an arrow function"); + const result = project.checker.isContextSensitive(arrowFn); + assert.equal(result, true); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - isTypeAssignableTo", () => { + test("returns true when source is assignable to target", () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": `export {};`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const stringType = project.checker.getStringType(); + const anyType = project.checker.getAnyType(); + const neverType = project.checker.getNeverType(); + assert.ok(project.checker.isTypeAssignableTo(stringType, stringType), "string assignable to string"); + assert.ok(project.checker.isTypeAssignableTo(stringType, anyType), "string assignable to any"); + assert.ok(project.checker.isTypeAssignableTo(neverType, stringType), "never assignable to string (bottom type)"); + } + finally { + api.close(); + } + }); + + test("returns false when source is not assignable to target", () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": `export {};`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const stringType = project.checker.getStringType(); + const numberType = project.checker.getNumberType(); + assert.ok(!project.checker.isTypeAssignableTo(numberType, stringType), "number not assignable to string"); + assert.ok(!project.checker.isTypeAssignableTo(stringType, numberType), "string not assignable to number"); + } + finally { + api.close(); + } + }); + + test("a string literal type is assignable to string but not number", () => { + const src = `\nexport const x: "hello" = "hello";\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/main.ts": src, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const pos = src.indexOf("x:"); + const sym = project.checker.getSymbolAtPosition("/src/main.ts", pos); + assert.ok(sym); + const litType = project.checker.getTypeOfSymbol(sym); + assert.ok(litType); + assert.ok(litType.flags & TypeFlags.StringLiteral); + const stringType = project.checker.getStringType(); + const numberType = project.checker.getNumberType(); + assert.ok(project.checker.isTypeAssignableTo(litType, stringType)); + assert.ok(!project.checker.isTypeAssignableTo(litType, numberType)); + } + finally { + api.close(); + } + }); +}); + +describe("Emitter - printNode", () => { + const emitterFiles = { + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": ` +export const x = 42; +export function greet(name: string): string { return name; } +export type Pair = [string, number]; +export const obj = { m: 1, s: "hi", b: true }; +`, + }; + + test("printNode with factory-created keyword type", () => { + const api = spawnAPI(emitterFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const node = createKeywordTypeNode(SyntaxKind.StringKeyword); + const text = project.emitter.printNode(node); + assert.strictEqual(text, "string"); + } + finally { + api.close(); + } + }); + + test("printNode with factory-created union type", () => { + const api = spawnAPI(emitterFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const node = createUnionTypeNode([ + createKeywordTypeNode(SyntaxKind.StringKeyword), + createKeywordTypeNode(SyntaxKind.NumberKeyword), + ]); + const text = project.emitter.printNode(node); + assert.strictEqual(text, "string | number"); + } + finally { + api.close(); + } + }); + + test("printNode with factory-created function type", () => { + const api = spawnAPI(emitterFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const param = createParameterDeclaration( + undefined, + undefined, + createIdentifier("x"), + undefined, + createKeywordTypeNode(SyntaxKind.StringKeyword), + undefined, + ); + const node = createFunctionTypeNode( + undefined, + [param], + createKeywordTypeNode(SyntaxKind.NumberKeyword), + ); + const text = project.emitter.printNode(node); + assert.strictEqual(text, "(x: string) => number"); + } + finally { + api.close(); + } + }); + + test("printNode with factory-created type reference", () => { + const api = spawnAPI(emitterFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const node = createTypeReferenceNode(createIdentifier("Array"), [ + createKeywordTypeNode(SyntaxKind.StringKeyword), + ]); + const text = project.emitter.printNode(node); + assert.strictEqual(text, "Array"); + } + finally { + api.close(); + } + }); + + test("printNode with factory-created array type", () => { + const api = spawnAPI(emitterFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const node = createArrayTypeNode(createKeywordTypeNode(SyntaxKind.NumberKeyword)); + const text = project.emitter.printNode(node); + assert.strictEqual(text, "number[]"); + } + finally { + api.close(); + } + }); + + test("typeToTypeNode + printNode round-trip", () => { + const api = spawnAPI(emitterFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const { checker, emitter } = snapshot.getProject("/tsconfig.json")!; + const src = emitterFiles["/src/main.ts"]; + + const greetPos = src.indexOf("greet("); + const symbol = checker.getSymbolAtPosition("/src/main.ts", greetPos); + assert.ok(symbol); + const type = checker.getTypeOfSymbol(symbol); + assert.ok(type); + const typeNode = checker.typeToTypeNode(type); + assert.ok(typeNode); + const text = emitter.printNode(typeNode); + assert.ok(text); + assert.strictEqual(text, "(name: string) => string"); + } + finally { + api.close(); + } + }); + + test("visitEachChild on typeToTypeNode result with keyword types", () => { + const api = spawnAPI(emitterFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const { checker } = snapshot.getProject("/tsconfig.json")!; + const src = emitterFiles["/src/main.ts"]; + const objPos = src.indexOf("obj"); + const symbol = checker.getSymbolAtPosition("/src/main.ts", objPos); + assert.ok(symbol, "should find symbol for obj"); + const type = checker.getTypeOfSymbol(symbol); + assert.ok(type); + const typeNode = checker.typeToTypeNode(type); + assert.ok(typeNode, "typeToTypeNode should return a type node"); + + // Recursively visit to reach PropertySignature.type where isTypeNode is checked. + const visited = (function visit(node: Node): Node { + return visitEachChild(node, visit); + })(typeNode); + assert.ok(visited, "visitEachChild should not throw"); + + const kinds = [ + SyntaxKind.NumberKeyword, + SyntaxKind.StringKeyword, + SyntaxKind.BooleanKeyword, + SyntaxKind.AnyKeyword, + SyntaxKind.VoidKeyword, + SyntaxKind.UndefinedKeyword, + SyntaxKind.NeverKeyword, + SyntaxKind.UnknownKeyword, + SyntaxKind.BigIntKeyword, + SyntaxKind.ObjectKeyword, + SyntaxKind.SymbolKeyword, + SyntaxKind.IntrinsicKeyword, + SyntaxKind.ExpressionWithTypeArguments, + SyntaxKind.JSDocAllType, + SyntaxKind.JSDocNullableType, + SyntaxKind.JSDocNonNullableType, + SyntaxKind.JSDocOptionalType, + SyntaxKind.JSDocVariadicType, + SyntaxKind.JSDocTypeExpression, + SyntaxKind.JSDocTypeLiteral, + SyntaxKind.JSDocSignature, + ]; + for (const kind of kinds) { + assert.ok(isTypeNode({ kind } as any), `isTypeNode should accept ${SyntaxKind[kind]}`); + } + } + finally { + api.close(); + } + }); + + test("typeToString", () => { + const api = spawnAPI(emitterFiles); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const { checker } = snapshot.getProject("/tsconfig.json")!; + const src = emitterFiles["/src/main.ts"]; + + const greetPos = src.indexOf("greet("); + const symbol = checker.getSymbolAtPosition("/src/main.ts", greetPos); + assert.ok(symbol); + const type = checker.getTypeOfSymbol(symbol); + assert.ok(type); + const text = checker.typeToString(type); + assert.strictEqual(text, "(name: string) => string"); + } + finally { + api.close(); + } + }); + + test("typeToString with TypeFormatFlags", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `export function greet(name: string): string[] { return [name]; }`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const { checker } = snapshot.getProject("/tsconfig.json")!; + const greetPos = "export function greet".indexOf("greet"); + const symbol = checker.getSymbolAtPosition("/src/main.ts", greetPos); + assert.ok(symbol); + const type = checker.getTypeOfSymbol(symbol); + assert.ok(type); + const text = checker.typeToString(type, undefined, TypeFormatFlags.WriteArrayAsGenericType); + assert.strictEqual(text, "(name: string) => Array"); + } + finally { + api.close(); + } + }); + + test("printNode with terminateUnterminatedLiterals option", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/main.ts": `const foo = /asdfasf;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/main.ts"); + assert.ok(sourceFile); + + // Find the regex literal node + let regexNode: import("@typescript/typescript/unstable/ast").Node | undefined; + sourceFile.forEachChild(function visit(node) { + if (node.kind === SyntaxKind.RegularExpressionLiteral) { + regexNode = node; + return; + } + node.forEachChild(visit); + }); + assert.ok(regexNode, "Should find a regex literal"); + + // Without the option, regex is printed as-is + const textWithout = project.emitter.printNode(regexNode); + assert.strictEqual(textWithout, "/asdfasf"); + + // With the option, the closing slash is added + const textWith = project.emitter.printNode(regexNode, { terminateUnterminatedLiterals: true }); + assert.strictEqual(textWith, "/asdfasf/"); + } + finally { + api.close(); + } + }); +}); + +describe("Program - selected file emit", () => { + const files = { + "/tsconfig.json": JSON.stringify({ + compilerOptions: { + declarationMap: true, + emitDeclarationOnly: true, + noEmit: true, + noEmitOnError: true, + sourceMap: true, + }, + }), + "/src/a.ts": `export const a: string = 1;`, + "/src/b.ts": `export const b = 2;`, + }; + + test("getJavaScriptEmit forces JavaScript and source maps", () => { + const { api, fs } = spawnAPIWithFS({ ...files }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const result = project.program.getJavaScriptEmit(["/src/a.ts", "/src/b.ts"]); + assert.equal(result.emitSkipped, false); + assert.deepEqual([...result.outputFiles.keys()], [ + "/src/a.js", + "/src/a.js.map", + "/src/b.js", + "/src/b.js.map", + ]); + assert.equal(result.outputFiles.get("/src/a.js")?.sourceFileName, "/src/a.ts"); + assert.match(result.outputFiles.get("/src/a.js")!.text, /export const a = 1/); + assert.equal(fs.readFile?.("/src/a.js"), undefined); + } + finally { + api.close(); + } + }); + + test("getDeclarationEmit forces declarations and declaration maps", () => { + const { api, fs } = spawnAPIWithFS({ ...files }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const result = project.program.getDeclarationEmit(["/src/a.ts", "/src/b.ts"]); + assert.equal(result.emitSkipped, false); + assert.deepEqual([...result.outputFiles.keys()], [ + "/src/a.d.ts", + "/src/a.d.ts.map", + "/src/b.d.ts", + "/src/b.d.ts.map", + ]); + assert.equal(result.outputFiles.get("/src/a.d.ts")?.sourceFileName, "/src/a.ts"); + assert.equal(fs.readFile?.("/src/a.d.ts"), undefined); + } + finally { + api.close(); + } + }); + + test("selected file emit accepts empty arrays", () => { + const api = spawnAPI({ ...files }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + assert.deepEqual((project.program.getJavaScriptEmit([])).outputFiles, new Map()); + assert.deepEqual((project.program.getDeclarationEmit([])).outputFiles, new Map()); + } + finally { + api.close(); + } + }); +}); + +describe("SnapshotInternalAPI - formatNodeForInsertion", () => { + test("formats a synthesized statement with correct indentation for insertion inside a function body", () => { + const files = { + "/tsconfig.json": "{}", + "/src/index.ts": `function greet(name: string) {\n console.log(name);\n}\n`, + }; + const api = spawnAPI(files); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + + const node = createVariableStatement( + undefined, + createVariableDeclarationList( + [createVariableDeclaration(createIdentifier("x"), undefined, undefined, createNumericLiteral("1", 0))], + NodeFlags.Const, + ), + ); + + const sourceText = files["/src/index.ts"]; + const insertionPos = sourceText.indexOf("\n console.log") + 1; + + const formatted = snapshot.internal.formatNodeForInsertion(node, "/src/index.ts", insertionPos); + assert.ok(formatted.includes("const x = 1;"), `Expected 'const x = 1;' in formatted output, got: ${JSON.stringify(formatted)}`); + assert.ok(formatted.startsWith(" "), `Expected 4 spaces of indentation, got: ${JSON.stringify(formatted)}`); + } + finally { + api.close(); + } + }); + + test("formats a node at top-level with no indentation", () => { + const files = { + "/tsconfig.json": "{}", + "/src/index.ts": `const a = 1;\nconst b = 2;\n`, + }; + const api = spawnAPI(files); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + + const node = createVariableStatement( + undefined, + createVariableDeclarationList( + [createVariableDeclaration(createIdentifier("y"), undefined, undefined, createNumericLiteral("42", 0))], + NodeFlags.Const, + ), + ); + + const formatted = snapshot.internal.formatNodeForInsertion(node, "/src/index.ts", 0); + assert.ok(formatted.includes("const y = 42;"), `Expected 'const y = 42;' in formatted output, got: ${JSON.stringify(formatted)}`); + assert.ok(!formatted.startsWith(" "), `Expected no leading spaces at top level, got: ${JSON.stringify(formatted)}`); + } + finally { + api.close(); + } + }); + + test("formats a synthesized statement with correct indentation when non-ASCII characters precede the insertion position", () => { + // 'é' is 1 UTF-16 code unit but 2 UTF-8 bytes, so UTF-16 and UTF-8 offsets diverge after it. + // This test verifies the position is correctly converted from UTF-16 to UTF-8 before use. + const files = { + "/tsconfig.json": "{}", + "/src/index.ts": `// \u00e9\nfunction greet(name: string) {\n console.log(name);\n}\n`, + }; + const api = spawnAPI(files); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + + const node = createVariableStatement( + undefined, + createVariableDeclarationList( + [createVariableDeclaration(createIdentifier("x"), undefined, undefined, createNumericLiteral("1", 0))], + NodeFlags.Const, + ), + ); + + // insertionPos is the UTF-16 code-unit offset of the start of the indented line inside the function body. + const sourceText = files["/src/index.ts"]; + const insertionPos = sourceText.indexOf("\n console.log") + 1; + + const formatted = snapshot.internal.formatNodeForInsertion(node, "/src/index.ts", insertionPos); + // The node should be indented to match the function body (4 spaces) + assert.ok(formatted.includes("const x = 1;"), `Expected 'const x = 1;' in formatted output, got: ${JSON.stringify(formatted)}`); + assert.ok(formatted.startsWith(" "), `Expected 4 spaces of indentation (UTF-16 offset correctly converted), got: ${JSON.stringify(formatted)}`); + } + finally { + api.close(); + } + }); +}); + +describe("modifierFlags", () => { + test("export async function has Export | Async flags", () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `export async function foo() {}`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + + let fnNode: import("@typescript/typescript/unstable/ast").FunctionDeclaration | undefined; + sourceFile.forEachChild(function visit(node) { + if (isFunctionDeclaration(node)) { + fnNode = node; + } + node.forEachChild(visit); + }); + assert.ok(fnNode, "Should find a function declaration"); + assert.ok(fnNode.modifierFlags & ModifierFlags.Export, "Should have Export flag"); + assert.ok(fnNode.modifierFlags & ModifierFlags.Async, "Should have Async flag"); + assert.strictEqual(fnNode.modifierFlags, ModifierFlags.Export | ModifierFlags.Async); + } + finally { + api.close(); + } + }); + + test("node without modifiers has ModifierFlags.None", () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `function bar() {}`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + + let fnNode: import("@typescript/typescript/unstable/ast").FunctionDeclaration | undefined; + sourceFile.forEachChild(function visit(node) { + if (isFunctionDeclaration(node)) { + fnNode = node; + } + node.forEachChild(visit); + }); + assert.ok(fnNode, "Should find a function declaration"); + assert.strictEqual(fnNode.modifierFlags, ModifierFlags.None); + } + finally { + api.close(); + } + }); +}); + +describe("Checker - getResolvedSymbol", () => { + test("resolves variable reference to its declaration symbol", () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `const x = 1;\nconst y = x;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + + // Find the 'x' identifier in `const y = x` + let refNode: import("@typescript/typescript/unstable/ast").Identifier | undefined; + sourceFile.forEachChild(function visit(node) { + if (isIdentifier(node) && node.text === "x") { + // We want the reference, not the declaration - take the last one + refNode = node; + } + node.forEachChild(visit); + }); + assert.ok(refNode, "Should find identifier 'x'"); + + const symbol = project.checker.getResolvedSymbol(refNode); + assert.ok(symbol, "Should resolve symbol for 'x'"); + assert.equal(symbol.name, "x"); + } + finally { + api.close(); + } + }); +}); + +describe("VariableDeclarationList - BlockScoped flags", () => { + test("let declaration has Let flag", () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `let x = 1;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + + let declList: import("@typescript/typescript/unstable/ast").Node | undefined; + sourceFile.forEachChild(function visit(node) { + if (isVariableDeclarationList(node)) { + declList = node; + } + node.forEachChild(visit); + }); + assert.ok(declList, "Should find VariableDeclarationList"); + assert.ok(declList.flags & NodeFlags.Let, "Should have Let flag"); + } + finally { + api.close(); + } + }); + + test("const declaration has Const flag", () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `const x = 1;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + + let declList: import("@typescript/typescript/unstable/ast").Node | undefined; + sourceFile.forEachChild(function visit(node) { + if (isVariableDeclarationList(node)) { + declList = node; + } + node.forEachChild(visit); + }); + assert.ok(declList, "Should find VariableDeclarationList"); + assert.ok(declList.flags & NodeFlags.Const, "Should have Const flag"); + } + finally { + api.close(); + } + }); +}); + +test("TypeOperator operator kind", () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `function test(arg: readonly number[]) { }\n`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + assert(sourceFile); + const param = (sourceFile.statements[0] as import("@typescript/typescript/unstable/ast").FunctionDeclaration).parameters[0]; + assert(param); + const type = param.type as import("@typescript/typescript/unstable/ast").TypeOperatorNode; + assert(type); + assert.equal(type.kind, SyntaxKind.TypeOperator); + assert.equal(type.operator, SyntaxKind.ReadonlyKeyword); + const printed = project.emitter.printNode(sourceFile); + assert.equal(sourceFile.text, printed); + } + finally { + api.close(); + } +}); + +test("SpreadAssignment roundtrip", () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `var thing = { ...other };\n`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + assert(sourceFile); + const stmt = sourceFile.statements[0] as import("@typescript/typescript/unstable/ast").VariableStatement; + const object = stmt.declarationList.declarations[0].initializer as import("@typescript/typescript/unstable/ast").ObjectLiteralExpression; + const assignment = object.properties[0] as import("@typescript/typescript/unstable/ast").SpreadAssignment; + assert(assignment); + assert.equal(assignment.kind, SyntaxKind.SpreadAssignment); + const expr = assignment.expression; + assert(expr); + assert.equal(expr.kind, SyntaxKind.Identifier); + const printed = project.emitter.printNode(sourceFile); + assert.equal(sourceFile.text, printed); + } + finally { + api.close(); + } +}); + +test("VariableDeclarationList const flag clone", () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `const thing = 123;\n`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + assert(sourceFile); + { + const stmt = sourceFile.statements[0] as import("@typescript/typescript/unstable/ast").VariableStatement; + const list = stmt.declarationList; + assert(list.flags & NodeFlags.Const); + } + const cloned = getSynthesizedDeepClone(sourceFile); + { + const stmt = cloned.statements[0] as import("@typescript/typescript/unstable/ast").VariableStatement; + const list = stmt.declarationList; + assert(list.flags & NodeFlags.Const); + } + const printed = project.emitter.printNode(cloned); + assert.equal(sourceFile.text, printed); + } + finally { + api.close(); + } +}); + +test("JSDoc before ExpressionStatement allowed", () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": ` +/** + * A doc. + */ +doThing(); + `, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + assert(sourceFile); + const printed = project.emitter.printNode(sourceFile); + assert.equal(sourceFile.text.trim(), printed.trim()); + } + finally { + api.close(); + } +}); + +test("Factory ModifierList auto-conversion", () => { + const api = spawnAPI(); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const node = createTypeAliasDeclaration( + [createToken(SyntaxKind.ExportKeyword)], + createIdentifier("Test"), + undefined, + createKeywordTypeNode(SyntaxKind.AnyKeyword), + ); + + assert.equal(project.emitter.printNode(node), "export type Test = any;"); + + const cloned = getSynthesizedDeepClone(node); + assert.equal(project.emitter.printNode(cloned), "export type Test = any;"); + } + finally { + api.close(); + } +}); + +test("Parse-clone-emit roundtrip", () => { + const tsSource = fileURLToPath(new URL("../../../../tsc/testdata/fixtures", import.meta.url).toString()); + const api = new API({ + cwd: tsSource, + }); + const target = { + cloneCrashed: 0, + printCrashed: 0, + clonePrintCrashed: 0, + }; + const errors = { ...target }; + try { + for (const tsconfig of globSync("**/tsconfig.json", { cwd: tsSource })) { + const snapshot = api.updateSnapshot({ openProject: resolve(tsSource, tsconfig) }); + const project = snapshot.getProject(tsconfig); + assert(project); + for (const file of project.rootFiles) { + const source = project.program.getSourceFile(file); + assert(source); + let clone: typeof source; + + try { + project.emitter.printNode(source); + } + catch { + errors.printCrashed++; + continue; + } + + try { + clone = getSynthesizedDeepClone(source); + } + catch { + errors.cloneCrashed++; + continue; + } + + try { + project.emitter.printNode(clone); + } + catch { + errors.clonePrintCrashed++; + continue; + } + } + } + } + finally { + api.close(); + } + assert.deepEqual(errors, target); +}); + +describe("Program - diagnostics", () => { + test("getSyntacticDiagnostics", () => { + const source = `const x: = 1;`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": source, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = project.program.getSyntacticDiagnostics("/src/index.ts"); + assert.deepEqual(diags[0].startPosition, { line: 0, character: 9 }); + assert.deepEqual(diags[0].endPosition, { line: 0, character: 10 }); + assert.deepEqual(diags[0].sourceLines, [{ line: 0, text: source }]); + assert.deepEqual(withoutFormattingContext(diags), [{ + fileName: "/src/index.ts", + ...rangeOf(source, "="), + code: 1110, + category: DiagnosticCategory.Error, + text: "Type expected.", + }]); + } + finally { + api.close(); + } + }); + + test("getSemanticDiagnostics with messageChain and relatedInformation", () => { + const source = `interface Props { callback: (x: string) => void }\nconst p: Props = { callback: (x: number) => {} };`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": source, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = project.program.getSemanticDiagnostics("/src/index.ts"); + const declRange = rangeOf(source, "callback", 0); + const assignRange = rangeOf(source, "callback", 1); + assert.deepEqual(withoutFormattingContext(diags), [{ + fileName: "/src/index.ts", + ...assignRange, + code: 2322, + category: DiagnosticCategory.Error, + text: "Type '(x: number) => void' is not assignable to type '(x: string) => void'.", + messageChain: [{ + fileName: "/src/index.ts", + ...assignRange, + code: 2328, + category: DiagnosticCategory.Error, + text: "Types of parameters 'x' and 'x' are incompatible.", + messageChain: [{ + fileName: "/src/index.ts", + ...assignRange, + code: 2322, + category: DiagnosticCategory.Error, + text: "Type 'string' is not assignable to type 'number'.", + }], + }], + relatedInformation: [{ + fileName: "/src/index.ts", + ...declRange, + code: 6500, + category: DiagnosticCategory.Message, + text: "The expected type comes from property 'callback' which is declared here on type 'Props'", + }], + }]); + } + finally { + api.close(); + } + }); + + test("getSuggestionDiagnostics", () => { + const source = `export function f() { const x = 1; return x; }\nconst _unused = 1;\n`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": source, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = project.program.getSuggestionDiagnostics("/src/index.ts"); + assert.deepEqual(withoutFormattingContext(diags), [{ + fileName: "/src/index.ts", + ...rangeOf(source, "_unused"), + code: 6133, + category: DiagnosticCategory.Suggestion, + text: "'_unused' is declared but its value is never read.", + reportsUnnecessary: true, + }]); + } + finally { + api.close(); + } + }); + + test("getConfigFileParsingDiagnostics", () => { + const config = `{ "compilerOptions": { "target": "invalid" } }`; + const api = spawnAPI({ + "/tsconfig.json": config, + "/src/index.ts": `export const x = 1;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = project.program.getConfigFileParsingDiagnostics(); + assert.deepEqual(withoutFormattingContext(diags), [{ + fileName: "/tsconfig.json", + ...rangeOf(config, `"invalid"`), + code: 6046, + category: DiagnosticCategory.Error, + text: "Argument for '--target' option must be: 'es6', 'es2015', 'es2016', 'es2017', 'es2018', 'es2019', 'es2020', 'es2021', 'es2022', 'es2023', 'es2024', 'es2025', 'esnext'.", + }]); + } + finally { + api.close(); + } + }); + + test("getConfigFileNames and getConfigSourceFile", () => { + const baseConfigText = `{ "compilerOptions": { "strict": true } }`; + const { api, fs } = spawnAPIWithFS({ + "/tsconfig.base.json": baseConfigText, + "/tsconfig.json": `{ "extends": "./tsconfig.base.json", "files": ["./src/index.ts"] }`, + "/src/index.ts": `export const x = 1;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const names = project.program.getConfigFileNames(); + assert.deepEqual(names, ["/tsconfig.json", "/tsconfig.base.json"]); + + const rootConfig = project.program.getConfigSourceFile("/tsconfig.json"); + assert.ok(rootConfig); + assert.equal(rootConfig.fileName, "/tsconfig.json"); + assert.equal(project.program.getSourceFile("/tsconfig.json"), undefined); + + fs.writeFile!("/tsconfig.base.json", `{ "compilerOptions": { "strict": false } }`); + const extendedConfig = project.program.getConfigSourceFile("/tsconfig.base.json"); + assert.ok(extendedConfig); + assert.equal(extendedConfig.fileName, "/tsconfig.base.json"); + assert.equal(extendedConfig.getFullText(), baseConfigText); + + const nonConfig = project.program.getConfigSourceFile("/src/index.ts"); + assert.equal(nonConfig, undefined); + } + finally { + api.close(); + } + }); + + test("getDeclarationDiagnostics", () => { + const api = spawnAPI({ + "/tsconfig.json": `{ "compilerOptions": { "declaration": true } }`, + "/src/index.ts": `export const x: number = 1;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = project.program.getDeclarationDiagnostics("/src/index.ts"); + assert.deepEqual(diags, []); + } + finally { + api.close(); + } + }); + + test("getBindDiagnostics", () => { + const source = `let x = 1;\nlet x = 2;`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": source, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = project.program.getBindDiagnostics("/src/index.ts"); + assert.deepEqual(withoutFormattingContext(diags), [ + { + fileName: "/src/index.ts", + ...rangeOf(source, "x", 0), + code: 2451, + category: DiagnosticCategory.Error, + text: "Cannot redeclare block-scoped variable 'x'.", + }, + { + fileName: "/src/index.ts", + ...rangeOf(source, "x", 1), + code: 2451, + category: DiagnosticCategory.Error, + text: "Cannot redeclare block-scoped variable 'x'.", + }, + ]); + } + finally { + api.close(); + } + }); + + test("getProgramDiagnostics", () => { + const config = `{ "compilerOptions": { "moduleResolution": "bundler", "module": "nodenext" } }`; + const api = spawnAPI({ + "/tsconfig.json": config, + "/src/index.ts": `export const x = 1;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = project.program.getProgramDiagnostics(); + assert.deepEqual(withoutFormattingContext(diags), [ + { + fileName: "/tsconfig.json", + ...rangeOf(config, `"bundler"`), + code: 5095, + category: DiagnosticCategory.Error, + text: "Option 'bundler' can only be used when 'module' is set to 'preserve', 'commonjs', or 'es2015' or later.", + }, + { + fileName: "/tsconfig.json", + ...rangeOf(config, `"bundler"`), + code: 5109, + category: DiagnosticCategory.Error, + text: "Option 'moduleResolution' must be set to 'NodeNext' (or left unspecified) when option 'module' is set to 'NodeNext'.", + }, + ]); + } + finally { + api.close(); + } + }); + + test("getGlobalDiagnostics", () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `export const x = 1;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = project.program.getGlobalDiagnostics(); + assert.deepEqual(diags, []); + } + finally { + api.close(); + } + }); + + test("getGlobalDiagnostics returns file-less diagnostics from the checker", () => { + const api = spawnAPI({ + "/tsconfig.json": `{ "compilerOptions": { "noLib": true } }`, + "/src/index.ts": `export const x = [1, 2, 3];`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = project.program.getGlobalDiagnostics(); + // With noLib, the checker reports "Cannot find global type" diagnostics that + // are not associated with any source file. + assert.ok(diags.length > 0, "expected global diagnostics to be reported"); + for (const diag of diags) { + assert.equal(diag.fileName, undefined); + assert.equal(diag.code, 2318); + assert.equal(diag.category, DiagnosticCategory.Error); + } + assert.ok( + diags.some(d => d.text === "Cannot find global type 'Array'."), + "expected a global diagnostic for the 'Array' type", + ); + } + finally { + api.close(); + } + }); + + test("getSyntacticDiagnostics with multiple files", () => { + const sourceA = `const a: = 1;`; + const sourceB = `const b: = 2;`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/a.ts": sourceA, + "/src/b.ts": sourceB, + "/src/clean.ts": `const c = 3;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = project.program.getSyntacticDiagnostics(["/src/a.ts", "/src/b.ts"]); + assert.deepEqual(withoutFormattingContext(diags), [ + { + fileName: "/src/a.ts", + ...rangeOf(sourceA, "="), + code: 1110, + category: DiagnosticCategory.Error, + text: "Type expected.", + }, + { + fileName: "/src/b.ts", + ...rangeOf(sourceB, "="), + code: 1110, + category: DiagnosticCategory.Error, + text: "Type expected.", + }, + ]); + } + finally { + api.close(); + } + }); + + test("getSemanticDiagnostics with multiple files", () => { + const sourceA = `export const a: number = "hello";`; + const sourceB = `export const b: string = 42;`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/a.ts": sourceA, + "/src/b.ts": sourceB, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = project.program.getSemanticDiagnostics(["/src/a.ts", "/src/b.ts"]); + assert.equal(diags.length, 2); + assert.equal(diags[0].fileName, "/src/a.ts"); + assert.equal(diags[0].code, 2322); + assert.equal(diags[1].fileName, "/src/b.ts"); + assert.equal(diags[1].code, 2322); + } + finally { + api.close(); + } + }); + + test("getBindDiagnostics with multiple files", () => { + const sourceA = `let x = 1;\nlet x = 2;`; + const sourceB = `let y = 1;\nlet y = 2;`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/a.ts": sourceA, + "/src/b.ts": sourceB, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = project.program.getBindDiagnostics(["/src/a.ts", "/src/b.ts"]); + assert.equal(diags.length, 4); + assert.equal(diags.filter(d => d.fileName === "/src/a.ts").length, 2); + assert.equal(diags.filter(d => d.fileName === "/src/b.ts").length, 2); + } + finally { + api.close(); + } + }); + + test("diagnostics with no files argument returns all", () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/a.ts": `const a: = 1;`, + "/src/b.ts": `const b: = 2;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = project.program.getSyntacticDiagnostics(); + assert.equal(diags.length, 2); + } + finally { + api.close(); + } + }); + + test("diagnostics with empty files array returns empty", () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/a.ts": `const a: = 1;`, + "/src/b.ts": `const b: = 2;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const diags = project.program.getSyntacticDiagnostics([]); + assert.deepEqual(diags, []); + } + finally { + api.close(); + } + }); +}); + +describe("getDefaultProjectForFile", () => { + test("finds inferred project for d.ts in node_modules after openFiles", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/index.ts": `export const x = 1;`, + "/node_modules/my-lib/package.json": JSON.stringify({ name: "my-lib", types: "./index.d.ts" }), + "/node_modules/my-lib/index.d.ts": `export declare const foo: string;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + + // The d.ts is not imported, so it is not in the project's program + const dtsSf = project.program.getSourceFile("/node_modules/my-lib/index.d.ts"); + assert.equal(dtsSf, undefined, "d.ts not in import graph should not be found via project.program.getSourceFile"); + + // Before opening the file, getDefaultProjectForFile returns undefined (no error) + const noProject = snapshot.getDefaultProjectForFile("/node_modules/my-lib/index.d.ts"); + assert.equal(noProject, undefined, "getDefaultProjectForFile returns undefined for unloaded file"); + + // Load the file into the inferred project via updateSnapshot openFiles + const snapshot2 = api.updateSnapshot({ openFiles: ["/node_modules/my-lib/index.d.ts"] }); + const defaultProject = snapshot2.getDefaultProjectForFile("/node_modules/my-lib/index.d.ts"); + assert.ok(defaultProject, "getDefaultProjectForFile should find inferred project after openFiles"); + + const fooPos = `export declare const foo: string;`.indexOf("foo"); + const fooType = defaultProject.checker.getTypeAtPosition("/node_modules/my-lib/index.d.ts", fooPos); + assert.ok(fooType); + assert.ok(fooType.flags & TypeFlags.String); + } + finally { + api.close(); + } + }); + + test("inferred project reflects file changes without a follow-up open/close request", () => { + const { api, fs } = spawnAPIWithFS({ + "/loose.ts": `export const foo = 1;`, + }); + try { + const snapshot1 = api.updateSnapshot({ openFiles: ["/loose.ts"] }); + const project1 = snapshot1.getDefaultProjectForFile("/loose.ts"); + assert.ok(project1, "file with no config file in its ancestry should load into the inferred project"); + const sf1 = project1.program.getSourceFile("/loose.ts"); + assert.ok(sf1); + assert.equal(sf1.text, `export const foo = 1;`); + + // Mutate the file and notify only via fileChanges — no follow-up openFiles/closeFiles. + fs.writeFile!("/loose.ts", `export const foo = 2;`); + const snapshot2 = api.updateSnapshot({ + fileChanges: { changed: ["/loose.ts"] }, + }); + + const project2 = snapshot2.getDefaultProjectForFile("/loose.ts"); + assert.ok(project2); + const sf2 = project2.program.getSourceFile("/loose.ts"); + assert.ok(sf2); + assert.equal( + sf2.text, + `export const foo = 2;`, + "inferred project's program should reflect the file change even without a follow-up open/close request", + ); + } + finally { + api.close(); + } + }); + + test("keeps previously opened files open across subsequent openFiles calls", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/index.ts": `export const x = 1;`, + "/node_modules/my-lib/package.json": JSON.stringify({ name: "my-lib", types: "./index.d.ts" }), + "/node_modules/my-lib/index.d.ts": `export declare const foo: string;`, + "/node_modules/other-lib/package.json": JSON.stringify({ name: "other-lib", types: "./index.d.ts" }), + "/node_modules/other-lib/index.d.ts": `export declare const bar: number;`, + }); + try { + api.updateSnapshot({ openProject: "/tsconfig.json" }); + api.updateSnapshot({ openFiles: ["/node_modules/my-lib/index.d.ts"] }); + + // Opening a second file in a later snapshot must not close the first one. + const snapshot = api.updateSnapshot({ openFiles: ["/node_modules/other-lib/index.d.ts"] }); + + const firstProject = snapshot.getDefaultProjectForFile("/node_modules/my-lib/index.d.ts"); + assert.ok(firstProject, "previously opened file should remain in the inferred project"); + const secondProject = snapshot.getDefaultProjectForFile("/node_modules/other-lib/index.d.ts"); + assert.ok(secondProject, "newly opened file should be in the inferred project"); + } + finally { + api.close(); + } + }); + + test("opening a file resolves to a configured project via ancestor search", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/index.ts": `export const x = 1;`, + }); + try { + // Open the file without first opening the project. Like LSP's didOpen, this + // should search ancestor directories for a tsconfig that contains the file. + const snapshot = api.updateSnapshot({ openFiles: ["/src/index.ts"] }); + const defaultProject = snapshot.getDefaultProjectForFile("/src/index.ts"); + assert.ok(defaultProject, "should find a project for the opened file"); + assert.equal( + defaultProject.configFileName, + "/tsconfig.json", + "opened file should resolve to the containing configured project, not the inferred project", + ); + } + finally { + api.close(); + } + }); + + test("closeProjects releases a project opened via openProjects", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/index.ts": `export const x = 1;`, + }); + try { + const opened = api.updateSnapshot({ openProjects: ["/tsconfig.json"] }); + assert.ok(opened.getProject("/tsconfig.json"), "project should be open after openProjects"); + + const closed = api.updateSnapshot({ closeProjects: ["/tsconfig.json"] }); + assert.equal( + closed.getProject("/tsconfig.json"), + undefined, + "project should be unloaded after closeProjects", + ); + } + finally { + api.close(); + } + }); + + test("closeFiles releases a file opened via openFiles", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/index.ts": `export const x = 1;`, + "/node_modules/my-lib/package.json": JSON.stringify({ name: "my-lib", types: "./index.d.ts" }), + "/node_modules/my-lib/index.d.ts": `export declare const foo: string;`, + }); + try { + api.updateSnapshot({ openProject: "/tsconfig.json" }); + const opened = api.updateSnapshot({ openFiles: ["/node_modules/my-lib/index.d.ts"] }); + assert.ok( + opened.getDefaultProjectForFile("/node_modules/my-lib/index.d.ts"), + "file should resolve to a project after openFiles", + ); + + const closed = api.updateSnapshot({ closeFiles: ["/node_modules/my-lib/index.d.ts"] }); + assert.equal( + closed.getDefaultProjectForFile("/node_modules/my-lib/index.d.ts"), + undefined, + "file should no longer resolve to a project after closeFiles", + ); + } + finally { + api.close(); + } + }); +}); + +describe("Program - emit", () => { + const files = { + "/tsconfig.json": `{ + "compilerOptions": { + "outDir": "dist", + "declaration": true, + } + }`, + "/src/index.ts": "export const x: number = 1;", + "/src/testing.ts": "export const y: string = 'typescript';", + }; + + test("emit all files", () => { + const fs = createVirtualFileSystem({ ...files }); + + const api = new API({ + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + fs: fs, + }); + + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const result = project.program.emit(); + assert.deepEqual(result, { + diagnostics: [], + emitSkipped: false, + emittedFiles: [ + "/dist/src/index.js", + "/dist/src/index.d.ts", + "/dist/src/testing.js", + "/dist/src/testing.d.ts", + ], + }); + + const js = fs.readFile?.("/dist/src/index.js"); + const dts = fs.readFile?.("/dist/src/index.d.ts"); + const js2 = fs.readFile?.("/dist/src/testing.js"); + const dts2 = fs.readFile?.("/dist/src/testing.d.ts"); + assert.strictEqual(js, `export const x = 1;\n`); + assert.strictEqual(dts, `export declare const x: number;\n`); + assert.strictEqual(js2, `export const y = 'typescript';\n`); + assert.strictEqual(dts2, `export declare const y: string;\n`); + } + finally { + api.close(); + } + }); + + test("emit only dts", () => { + const fs = createVirtualFileSystem({ ...files }); + + const api = new API({ + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + fs: fs, + }); + + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const result = project.program.emit(EmitOnly.OnlyDts); + assert.deepEqual(result, { + diagnostics: [], + emitSkipped: false, + emittedFiles: [ + "/dist/src/index.d.ts", + "/dist/src/testing.d.ts", + ], + }); + + const js = fs.readFile?.("/dist/src/index.js"); + const dts = fs.readFile?.("/dist/src/index.d.ts"); + const js2 = fs.readFile?.("/dist/src/testing.js"); + const dts2 = fs.readFile?.("/dist/src/testing.d.ts"); + assert.strictEqual(js, undefined); + assert.strictEqual(dts, `export declare const x: number;\n`); + assert.strictEqual(js2, undefined); + assert.strictEqual(dts2, `export declare const y: string;\n`); + } + finally { + api.close(); + } + }); + + test("emit only js", () => { + const fs = createVirtualFileSystem({ ...files }); + + const api = new API({ + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + fs: fs, + }); + + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const result = project.program.emit(EmitOnly.OnlyJs); + assert.deepEqual(result, { + diagnostics: [], + emitSkipped: false, + emittedFiles: [ + "/dist/src/index.js", + "/dist/src/testing.js", + ], + }); + + const js = fs.readFile?.("/dist/src/index.js"); + const dts = fs.readFile?.("/dist/src/index.d.ts"); + const js2 = fs.readFile?.("/dist/src/testing.js"); + const dts2 = fs.readFile?.("/dist/src/testing.d.ts"); + assert.strictEqual(js, `export const x = 1;\n`); + assert.strictEqual(dts, undefined); + assert.strictEqual(js2, `export const y = 'typescript';\n`); + assert.strictEqual(dts2, undefined); + } + finally { + api.close(); + } + }); + + test("emitToString emits the whole program and respects emitOnly", () => { + const { api, fs } = spawnAPIWithFS({ ...files }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const result = project.program.emitToString(EmitOnly.OnlyDts); + assert.deepEqual([...result.outputFiles.keys()], [ + "/dist/src/index.d.ts", + "/dist/src/testing.d.ts", + ]); + assert.equal(fs.readFile?.("/dist/src/index.js"), undefined); + } + finally { + api.close(); + } + }); + + test("whole-program emit includes option-controlled maps", () => { + const { api, fs } = spawnAPIWithFS({ + "/tsconfig.json": JSON.stringify({ + compilerOptions: { + declaration: true, + declarationMap: true, + outDir: "dist", + sourceMap: true, + }, + }), + "/src/index.ts": "export const value: number = 1;", + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + + const result = project.program.emit(); + assert.deepEqual( + new Set(result.emittedFiles), + new Set([ + "/dist/src/index.js", + "/dist/src/index.js.map", + "/dist/src/index.d.ts", + "/dist/src/index.d.ts.map", + ]), + ); + assert.ok(fs.fileExists?.("/dist/src/index.js.map")); + assert.ok(fs.fileExists?.("/dist/src/index.d.ts.map")); + + const js = project.program.emitToString(EmitOnly.OnlyJs); + assert.deepEqual([...js.outputFiles.keys()], [ + "/dist/src/index.js", + "/dist/src/index.js.map", + ]); + + const dts = project.program.emitToString(EmitOnly.OnlyDts); + assert.deepEqual([...dts.outputFiles.keys()], [ + "/dist/src/index.d.ts", + "/dist/src/index.d.ts.map", + ]); + } + finally { + api.close(); + } + }); + + test("noEmitOnError reports diagnostics without writing output", () => { + const { api, fs } = spawnAPIWithFS({ + "/tsconfig.json": JSON.stringify({ + compilerOptions: { + noEmitOnError: true, + outDir: "dist", + }, + }), + "/src/bad.ts": "export const bad = ;", + "/src/good.ts": "export const value = 1;", + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const result = project.program.emit(); + assert.equal(result.emitSkipped, true); + assert.ok(result.diagnostics.some(d => d.code === 1109)); + assert.deepEqual(result.emittedFiles, []); + assert.equal(fs.readFile?.("/dist/src/bad.js"), undefined); + assert.equal(fs.readFile?.("/dist/src/good.js"), undefined); + + const stringResult = project.program.emitToString(); + assert.equal(stringResult.emitSkipped, true); + assert.ok(stringResult.diagnostics.some(d => d.code === 1109)); + assert.deepEqual(stringResult.outputFiles, new Map()); + } + finally { + api.close(); + } + }); + + test("emit and emitToString respect noEmit", () => { + const { api, fs } = spawnAPIWithFS({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { noEmit: true } }), + "/src/index.ts": "export const value = 1;", + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + assert.deepEqual(project.program.emit(), { + diagnostics: [], + emitSkipped: false, + emittedFiles: [], + }); + assert.deepEqual(project.program.emitToString(), { + diagnostics: [], + emitSkipped: false, + outputFiles: new Map(), + }); + assert.equal(fs.readFile?.("/src/index.js"), undefined); + } + finally { + api.close(); + } + }); + + test("emit rejects unknown files and invalid emitOnly values", () => { + const api = spawnAPI({ ...files }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + + let error: unknown; + try { + project.program.getJavaScriptEmit(["/src/missing.ts"]); + } + catch (e) { + error = e; + } + assert.match(String(error), /source file not found/); + + error = undefined; + try { + project.program.emitToString(3 as EmitOnly); + } + catch (e) { + error = e; + } + assert.match(String(error), /invalid emitOnly value/); + } + finally { + api.close(); + } + }); +}); + +test("Benchmarks", () => { + runBenchmarks({ singleIteration: true }); +}); + +describe("Timing", () => { + test("collects combined client, server, and transport timing info", () => { + const api = new API({ + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + fs: createVirtualFileSystem({ ...defaultFiles }), + collectTiming: true, + }); + try { + // Baseline: enabled, but nothing measured yet. + let info = api.getTimingInfo(); + assert.equal(info.enabled, true); + assert.equal(info.totals.requestCount, 0); + assert.equal(info.recentRequests.length, 0); + + // Exercise a JSON request and a binary source-file request. + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + + // Client-side timing: round-trip latency and byte counts. + info = api.getTimingInfo(); + assert.ok(info.totals.requestCount >= 2, "expected at least two measured requests"); + assert.ok(info.totals.bytesSent > 0); + assert.ok(info.totals.bytesReceived > 0); + assert.ok(info.totals.roundTripMs >= 0); + + // Server-side timing is folded into the same snapshot. + assert.ok(info.totals.serverTimeMs >= 0); + assert.ok(info.totals.transportOverheadMs >= 0); + assert.equal( + info.totals.transportOverheadMs, + Math.max(0, info.totals.roundTripMs - info.totals.serverTimeMs), + ); + + // The ring buffer retains at most the 5 most recent requests. + assert.ok(info.recentRequests.length > 0); + assert.ok(info.recentRequests.length <= 5); + assert.ok(info.recentRequests.length <= info.totals.requestCount); + + for (const r of info.recentRequests) { + assert.ok(r.roundTripMs >= 0); + assert.ok(r.bytesSent >= 0); + assert.ok(r.bytesReceived >= 0); + assert.ok(typeof r.method === "string" && r.method.length > 0); + assert.equal(typeof r.timestamp, "number"); + // Transport overhead is present exactly when server time is. + assert.equal(r.transportOverheadMs === undefined, r.serverTimeMs === undefined); + if (r.serverTimeMs !== undefined) { + assert.ok(r.serverTimeMs >= 0); + assert.equal(r.transportOverheadMs, Math.max(0, r.roundTripMs - r.serverTimeMs)); + } + } + + // Server processing time is folded in for the recent requests. + assert.ok(info.recentRequests.every(r => r.serverTimeMs !== undefined), "server time should be reported"); + + // Reset clears totals and history on both client and server. + api.resetTimingInfo(); + info = api.getTimingInfo(); + assert.equal(info.enabled, true); + assert.equal(info.totals.requestCount, 0); + assert.equal(info.totals.serverTimeMs, 0); + assert.equal(info.totals.transportOverheadMs, 0); + assert.equal(info.recentRequests.length, 0); + } + finally { + api.close(); + } + }); + + test("tracks source-file node materialization", () => { + const api = new API({ + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + fs: createVirtualFileSystem({ ...defaultFiles }), + collectTiming: true, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sourceFile = project.program.getSourceFile("/src/index.ts"); + assert.ok(sourceFile); + + // The source file node itself is pre-cached, so before walking the + // tree no descendant nodes have been materialized. + let info = api.getTimingInfo(); + const before = info.totals.nodesMaterialized; + assert.equal(info.totals.sourceFilesFetched, 1, "one source file was fetched"); + assert.ok(info.totals.nodesFetched > 0, "the fetched file contributes materializable nodes"); + assert.equal(info.totals.nodesMaterialized, 0, "nothing walked yet, so no nodes materialized"); + + // Walk the whole tree to force lazy materialization of every node. + let visited = 0; + sourceFile.forEachChild(function visit(node) { + visited++; + node.forEachChild(visit); + }); + assert.ok(visited > 0, "expected to visit at least one node"); + + info = api.getTimingInfo(); + assert.ok( + info.totals.nodesMaterialized > before, + "walking the tree should materialize nodes", + ); + assert.ok( + info.totals.nodesMaterialized >= visited, + "every visited node should have been materialized", + ); + + // A full walk materializes (nearly) every fetched node, so the share + // of fetched nodes materialized should be substantial. + assert.equal(info.totals.sourceFilesFetched, 1); + assert.ok( + info.totals.nodesMaterialized > 0 + && info.totals.nodesMaterialized <= info.totals.nodesFetched, + "materialized nodes should be in (0, nodesFetched]", + ); + + // Reset clears materialization totals along with the rest. + api.resetTimingInfo(); + info = api.getTimingInfo(); + assert.equal(info.totals.nodesMaterialized, 0); + assert.equal(info.totals.sourceFilesFetched, 0); + assert.equal(info.totals.nodesFetched, 0); + } + finally { + api.close(); + } + }); + + test("is disabled by default", () => { + const api = spawnAPI(); + try { + api.parseConfigFile("/tsconfig.json"); + const info = api.getTimingInfo(); + assert.equal(info.enabled, false); + assert.equal(info.totals.requestCount, 0); + assert.equal(info.totals.nodesMaterialized, 0); + assert.equal(info.totals.sourceFilesFetched, 0); + assert.equal(info.totals.nodesFetched, 0); + assert.equal(info.recentRequests.length, 0); + } + finally { + api.close(); + } + }); +}); + +describe("runWithTemporaryFileUpdate", () => { + test("temporary file update is visible in the callback and reverted afterward", () => { + const api = spawnAPI({ + "/tsconfig.json": JSON.stringify({ compilerOptions: { strict: true } }), + "/src/index.ts": `export const x: number = 1;`, + }); + try { + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + + // The original content type-checks cleanly. + const baseDiags = project.program.getSemanticDiagnostics("/src/index.ts"); + assert.equal(baseDiags.length, 0); + + // Keep a newer snapshot active to verify any active snapshot can be the base. + const latestSnapshot = api.updateSnapshot(); + assert.notEqual(latestSnapshot.id, snapshot.id); + + // Inside the callback, the file has the temporary (erroneous) content. + let errorCount = -1; + api.runWithTemporaryFileUpdate(snapshot, "/src/index.ts", `export const x: string = 1;`, tempSnapshot => { + const tempProject = tempSnapshot.getProject("/tsconfig.json")!; + const diags = tempProject.program.getSemanticDiagnostics("/src/index.ts"); + errorCount = diags.length; + }); + assert.ok(errorCount > 0, "temporary content should produce a semantic error inside the callback"); + + // The original snapshot is unaffected by the temporary update. + const afterDiags = project.program.getSemanticDiagnostics("/src/index.ts"); + assert.equal(afterDiags.length, 0); + + // Subsequent regular updates still work and diff against the real latest snapshot. + const snapshot2 = api.updateSnapshot(); + const project2 = snapshot2.getProject("/tsconfig.json")!; + const diags2 = project2.program.getSemanticDiagnostics("/src/index.ts"); + assert.equal(diags2.length, 0); + } + finally { + api.close(); + } + }); +}); + +function spawnAPIWithFS(files: Record = { ...defaultFiles }): { api: API; fs: FileSystem; } { + const fs = createVirtualFileSystem(files); + const api = new API({ + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + fs, + }); + return { api, fs }; +} + +/** Returns `{ pos, end }` for the nth (0-based, default 0) occurrence of `searchString` in `source`. */ +function rangeOf(source: string, searchString: string, occurrence: number = 0): { pos: number; end: number; } { + let index = -1; + for (let i = 0; i <= occurrence; i++) { + index = source.indexOf(searchString, index + 1); + if (index === -1) { + throw new Error(`Occurrence ${occurrence} of "${searchString}" not found in source`); + } + } + return { pos: index, end: index + searchString.length }; +} + +function withoutFormattingContext(value: T): T { + const formattingKeys = new Set(["startPosition", "endPosition", "sourceLines"]); + return JSON.parse(JSON.stringify(value, (key, item) => formattingKeys.has(key) ? undefined : item)) as T; +} + +function applyTextEdits(source: string, edits: readonly TextEdit[]): string { + const sorted = [...edits].sort((a, b) => b.pos - a.pos); + let result = source; + for (const edit of sorted) { + result = result.slice(0, edit.pos) + edit.newText + result.slice(edit.end); + } + return result; +} diff --git a/packages/typescript/test/sync/api.testUtils.ts b/packages/typescript/test/sync/api.testUtils.ts index 4af16a83f9cc2..98db9f84666f9 100644 --- a/packages/typescript/test/sync/api.testUtils.ts +++ b/packages/typescript/test/sync/api.testUtils.ts @@ -1,20 +1,20 @@ -import { createVirtualFileSystem } from "@typescript/typescript/unstable/fs"; -import { - API, - type APIOptions, -} from "@typescript/typescript/unstable/sync"; -import { fileURLToPath } from "node:url"; - -export const defaultFiles = { - "/tsconfig.json": "{}", - "/src/index.ts": `import { foo } from './foo';`, - "/src/foo.ts": `export const foo = 42;`, -}; - -export function spawnAPI(files: Record = { ...defaultFiles }, options: Omit = {}) { - return new API({ - ...options, - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), - fs: createVirtualFileSystem(files), - }); -} +import { createVirtualFileSystem } from "@typescript/typescript/unstable/fs"; +import { + API, + type APIOptions, +} from "@typescript/typescript/unstable/sync"; +import { fileURLToPath } from "node:url"; + +export const defaultFiles = { + "/tsconfig.json": "{}", + "/src/index.ts": `import { foo } from './foo';`, + "/src/foo.ts": `export const foo = 42;`, +}; + +export function spawnAPI(files: Record = { ...defaultFiles }, options: Omit = {}) { + return new API({ + ...options, + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + fs: createVirtualFileSystem(files), + }); +} diff --git a/packages/typescript/test/sync/ast.test.ts b/packages/typescript/test/sync/ast.test.ts index 254e8d6c63d31..e710b5169ea17 100644 --- a/packages/typescript/test/sync/ast.test.ts +++ b/packages/typescript/test/sync/ast.test.ts @@ -1,1035 +1,1035 @@ -import type { - ExpressionStatement, - Identifier, - JSDoc, - Node, - NodeArray, - SourceFile, - StringLiteralLikeNode, - VariableStatement, -} from "@typescript/typescript/unstable/ast"; -import { - getTokenAtPosition, - isClassDeclaration, - isImportDeclaration, - isInterfaceDeclaration, - isJSDocLink, - isNamedImports, - isValidTypeOnlyAliasUseSite, - SyntaxKind, - TokenFlags, -} from "@typescript/typescript/unstable/ast"; -import { - getSynthesizedDeepClone, - getSynthesizedDeepClones, -} from "@typescript/typescript/unstable/ast/clone"; -import { - cloneNode, - createBinaryExpression, - createBlock, - createExpressionStatement, - createIdentifier, - createIfStatement, - createNodeArray, - createNumericLiteral, - createSourceFile, - createStringLiteral, - createToken, - NodeObject, -} from "@typescript/typescript/unstable/ast/factory"; -import { - visitEachChild, - visitNode, - visitNodes, -} from "@typescript/typescript/unstable/ast/visitor"; -import { createVirtualFileSystem } from "@typescript/typescript/unstable/fs"; -import { API } from "@typescript/typescript/unstable/sync"; -import assert from "node:assert"; -import { - describe, - test, -} from "node:test"; -import { fileURLToPath } from "node:url"; - -// --------------------------------------------------------------------------- -// Helpers -// --------------------------------------------------------------------------- - -function collectKinds(node: Node): SyntaxKind[] { - const kinds: SyntaxKind[] = [node.kind]; - node.forEachChild(child => { - kinds.push(...collectKinds(child)); - return undefined; - }); - return kinds; -} - -// --------------------------------------------------------------------------- -// cloneNode -// --------------------------------------------------------------------------- - -describe("cloneNode", () => { - test("clones an identifier", () => { - const id = createIdentifier("hello"); - const clone = cloneNode(id); - - assert.notStrictEqual(clone, id); - assert.strictEqual(clone.kind, SyntaxKind.Identifier); - assert.strictEqual(clone.text, "hello"); - }); - - test("clones a numeric literal", () => { - const num = createNumericLiteral("42", TokenFlags.None); - const clone = cloneNode(num); - - assert.notStrictEqual(clone, num); - assert.strictEqual(clone.kind, SyntaxKind.NumericLiteral); - assert.strictEqual(clone.text, "42"); - assert.strictEqual(clone.tokenFlags, TokenFlags.None); - }); - - test("clones a string literal", () => { - const str = createStringLiteral("world", TokenFlags.None); - const clone = cloneNode(str); - - assert.notStrictEqual(clone, str); - assert.strictEqual(clone.kind, SyntaxKind.StringLiteral); - assert.strictEqual(clone.text, "world"); - }); - - test("preserves text range", () => { - // Synthesized nodes start at pos=-1,end=-1; verify clone preserves them - const id = createIdentifier("x"); - const clone = cloneNode(id); - - assert.strictEqual(clone.pos, id.pos); - assert.strictEqual(clone.end, id.end); - }); - - test("clone is shallow (children are shared)", () => { - const left = createIdentifier("a"); - const right = createIdentifier("b"); - const op = createToken(SyntaxKind.PlusToken); - const bin = createBinaryExpression(undefined, left, undefined, op, right); - const clone = cloneNode(bin); - - assert.notStrictEqual(clone, bin); - assert.strictEqual(clone.left, bin.left); - assert.strictEqual(clone.right, bin.right); - assert.strictEqual(clone.operatorToken, bin.operatorToken); - }); - - test("clone has no parent", () => { - const id = createIdentifier("x"); - const clone = cloneNode(id); - assert.strictEqual(clone.parent, undefined); - }); - - test("clones a token", () => { - const tok = createToken(SyntaxKind.SemicolonToken); - const clone = cloneNode(tok); - - assert.notStrictEqual(clone, tok); - assert.strictEqual(clone.kind, SyntaxKind.SemicolonToken); - }); -}); - -// --------------------------------------------------------------------------- -// visitNode / visitNodes -// --------------------------------------------------------------------------- - -describe("visitNode", () => { - test("returns undefined for undefined input", () => { - const nothing: Node | undefined = undefined; - const result = visitNode(nothing, () => undefined); - assert.strictEqual(result, undefined); - }); - - test("returns visitor result", () => { - const id = createIdentifier("x"); - const replacement = createIdentifier("y"); - const result = visitNode(id, () => replacement); - assert.strictEqual(result, replacement); - }); - - test("visitor can return undefined to remove node", () => { - const id = createIdentifier("x"); - const result = visitNode(id, () => undefined); - assert.strictEqual(result, undefined); - }); -}); - -describe("visitNodes", () => { - test("returns undefined for undefined input", () => { - const nothing: NodeArray | undefined = undefined; - const result = visitNodes(nothing, () => undefined); - assert.strictEqual(result, undefined); - }); - - test("returns same array if nothing changed", () => { - const a = createIdentifier("a"); - const b = createIdentifier("b"); - const arr = createNodeArray([a, b]); - const result = visitNodes(arr, node => node); - assert.strictEqual(result, arr); - }); - - test("filters out undefined results", () => { - const a = createIdentifier("a"); - const b = createIdentifier("b"); - const arr = createNodeArray([a, b]); - const result = visitNodes(arr, node => { - if (node === a) return undefined; - return node; - }); - assert.strictEqual(result.length, 1); - }); - - test("replaces nodes", () => { - const a = createIdentifier("a"); - const b = createIdentifier("b"); - const c = createIdentifier("c"); - const arr = createNodeArray([a, b]); - const result = visitNodes(arr, node => { - if (node === a) return c; - return node; - }); - assert.strictEqual(result.length, 2); - assert.strictEqual(result.at(0), c); - assert.strictEqual(result.at(1), b); - }); -}); - -// --------------------------------------------------------------------------- -// visitEachChild -// --------------------------------------------------------------------------- - -describe("visitEachChild", () => { - test("returns same node if nothing changed (identity visitor)", () => { - const left = createIdentifier("a"); - const right = createIdentifier("b"); - const op = createToken(SyntaxKind.PlusToken); - const bin = createBinaryExpression(undefined, left, undefined, op, right); - - const result = visitEachChild(bin, node => node); - assert.strictEqual(result, bin); - }); - - test("returns undefined for undefined input", () => { - const nothing: Node | undefined = undefined; - const result = visitEachChild(nothing, node => node); - assert.strictEqual(result, undefined); - }); - - test("creates new node when child changes", () => { - const left = createIdentifier("a"); - const right = createIdentifier("b"); - const op = createToken(SyntaxKind.PlusToken); - const bin = createBinaryExpression(undefined, left, undefined, op, right); - - const newRight = createIdentifier("c"); - const result = visitEachChild(bin, node => { - if (node === right) return newRight; - return node; - }); - - assert.notStrictEqual(result, bin); - assert.strictEqual(result.kind, SyntaxKind.BinaryExpression); - assert.strictEqual(result.left, left); - assert.strictEqual(result.right, newRight); - }); - - test("works on if statement with optional else", () => { - const cond = createIdentifier("ok"); - const thenBranch = createBlock([]); - const ifStmt = createIfStatement(cond, thenBranch, undefined); - - const newCond = createIdentifier("notOk"); - const result = visitEachChild(ifStmt, node => { - if (node === cond) return newCond; - return node; - }); - - assert.notStrictEqual(result, ifStmt); - assert.strictEqual(result.expression, newCond); - assert.strictEqual(result.thenStatement, thenBranch); - assert.strictEqual(result.elseStatement, undefined); - }); - - test("works on nodes with NodeArray children", () => { - const stmtA = createExpressionStatement(createIdentifier("a")); - const stmtB = createExpressionStatement(createIdentifier("b")); - const block = createBlock([stmtA, stmtB]); - - const stmtC = createExpressionStatement(createIdentifier("c")); - const result = visitEachChild(block, node => { - // visitEachChild visits immediate children (the expression statements) - if (node === stmtA) return stmtC; - return node; - }); - - assert.notStrictEqual(result, block); - assert.strictEqual(result.kind, SyntaxKind.Block); - assert.strictEqual(result.statements.length, 2); - assert.strictEqual(result.statements.at(0), stmtC); - assert.strictEqual(result.statements.at(1), stmtB); - }); - - test("returns same node for leaf nodes", () => { - const id = createIdentifier("x"); - const result = visitEachChild(id, () => { - throw new Error("should not be called for leaf node"); - }); - // Identifier has no children, so visitor shouldn't be called - assert.strictEqual(result, id); - }); -}); - -// --------------------------------------------------------------------------- -// getSynthesizedDeepClone -// --------------------------------------------------------------------------- - -describe("getSynthesizedDeepClone", () => { - test("deeply clones identifier", () => { - const id = createIdentifier("hello"); - const clone = getSynthesizedDeepClone(id); - - assert.notStrictEqual(clone, id); - assert.strictEqual(clone.kind, SyntaxKind.Identifier); - assert.strictEqual(clone.text, "hello"); - }); - - test("deeply clones identifier without trivia", () => { - const id = createIdentifier("hello"); - const clone = getSynthesizedDeepClone(id, /*includeTrivia*/ false); - - assert.strictEqual(clone.kind, SyntaxKind.Identifier); - assert.strictEqual(clone.text, "hello"); - assert.strictEqual(clone.pos, -1); - assert.strictEqual(clone.end, -1); - }); - - test("deeply clones binary expression", () => { - const left = createIdentifier("a"); - const right = createNumericLiteral("42", TokenFlags.None); - const op = createToken(SyntaxKind.PlusToken); - const bin = createBinaryExpression(undefined, left, undefined, op, right); - - const clone = getSynthesizedDeepClone(bin); - - // Root is different object - assert.notStrictEqual(clone, bin); - assert.strictEqual(clone.kind, SyntaxKind.BinaryExpression); - - // Children are also different objects (deep clone) - assert.notStrictEqual(clone.left, left); - assert.strictEqual(clone.left.kind, SyntaxKind.Identifier); - assert.strictEqual((clone.left as Identifier).text, "a"); - - assert.notStrictEqual(clone.right, right); - assert.strictEqual(clone.right.kind, SyntaxKind.NumericLiteral); - assert.strictEqual((clone.right as Identifier).text, "42"); - - assert.notStrictEqual(clone.operatorToken, op); - assert.strictEqual(clone.operatorToken.kind, SyntaxKind.PlusToken); - }); - - test("sets parent pointers on deep clone", () => { - const left = createIdentifier("a"); - const right = createIdentifier("b"); - const op = createToken(SyntaxKind.PlusToken); - const bin = createBinaryExpression(undefined, left, undefined, op, right); - - const clone = getSynthesizedDeepClone(bin); - - assert.strictEqual(clone.left.parent, clone); - assert.strictEqual(clone.right.parent, clone); - assert.strictEqual(clone.operatorToken.parent, clone); - }); - - test("returns undefined for undefined input", () => { - const result = getSynthesizedDeepClone(undefined); - assert.strictEqual(result, undefined); - }); - - test("deeply clones if statement with optional else", () => { - const cond = createIdentifier("ok"); - const thenBlock = createBlock([]); - const ifStmt = createIfStatement(cond, thenBlock, undefined); - - const clone = getSynthesizedDeepClone(ifStmt); - - assert.notStrictEqual(clone, ifStmt); - assert.strictEqual(clone.kind, SyntaxKind.IfStatement); - assert.notStrictEqual(clone.expression, cond); - assert.strictEqual(clone.expression.kind, SyntaxKind.Identifier); - assert.notStrictEqual(clone.thenStatement, thenBlock); - assert.strictEqual(clone.thenStatement.kind, SyntaxKind.Block); - assert.strictEqual(clone.elseStatement, undefined); - }); - - test("preserves tree structure with nested children", () => { - const a = createIdentifier("a"); - const b = createIdentifier("b"); - const stmts = [ - createExpressionStatement(a), - createExpressionStatement(b), - ]; - const block = createBlock(stmts); - - const clone = getSynthesizedDeepClone(block); - - assert.notStrictEqual(clone, block); - assert.strictEqual(clone.statements.length, 2); - - const cloneStmt0 = clone.statements.at(0)!; - const cloneStmt1 = clone.statements.at(1)!; - assert.notStrictEqual(cloneStmt0, stmts[0]); - assert.notStrictEqual(cloneStmt1, stmts[1]); - assert.strictEqual(cloneStmt0.kind, SyntaxKind.ExpressionStatement); - assert.strictEqual(cloneStmt1.kind, SyntaxKind.ExpressionStatement); - }); - - test("deeply clones string literal", () => { - const str = createStringLiteral("test", TokenFlags.None); - const clone = getSynthesizedDeepClone(str); - assert.notStrictEqual(clone, str); - assert.strictEqual(clone.kind, SyntaxKind.StringLiteral); - assert.strictEqual(clone.text, "test"); - }); - - test("deeply clones numeric literal", () => { - const num = createNumericLiteral("3.14", TokenFlags.None); - const clone = getSynthesizedDeepClone(num); - assert.notStrictEqual(clone, num); - assert.strictEqual(clone.kind, SyntaxKind.NumericLiteral); - assert.strictEqual(clone.text, "3.14"); - }); - - test("clone has same syntax structure", () => { - const left = createIdentifier("x"); - const right = createNumericLiteral("1", TokenFlags.None); - const op = createToken(SyntaxKind.PlusToken); - const bin = createBinaryExpression(undefined, left, undefined, op, right); - const stmt = createExpressionStatement(bin); - - const clone = getSynthesizedDeepClone(stmt); - assert.deepStrictEqual(collectKinds(clone), collectKinds(stmt)); - }); -}); - -// --------------------------------------------------------------------------- -// getSynthesizedDeepClones (NodeArray) -// --------------------------------------------------------------------------- - -describe("getSynthesizedDeepClones", () => { - test("deeply clones a NodeArray", () => { - const a = createIdentifier("a"); - const b = createIdentifier("b"); - const arr = createNodeArray([a, b]); - - const clone = getSynthesizedDeepClones(arr); - - assert.notStrictEqual(clone, arr); - assert.strictEqual(clone.length, 2); - assert.notStrictEqual(clone.at(0), a); - assert.notStrictEqual(clone.at(1), b); - assert.strictEqual(clone.at(0)!.kind, SyntaxKind.Identifier); - assert.strictEqual(clone.at(1)!.kind, SyntaxKind.Identifier); - }); - - test("returns undefined for undefined input", () => { - const result = getSynthesizedDeepClones(undefined); - assert.strictEqual(result, undefined); - }); - - test("preserves pos and end on NodeArray", () => { - const a = createIdentifier("a"); - const arr = createNodeArray([a], 5, 10); - - const clone = getSynthesizedDeepClones(arr); - assert.strictEqual(clone.pos, 5); - assert.strictEqual(clone.end, 10); - }); -}); - -// --------------------------------------------------------------------------- -// Type-only import use sites -// --------------------------------------------------------------------------- - -describe("isValidTypeOnlyAliasUseSite", () => { - test("classifies syntactic type-only import use sites", () => { - const source = ` -type TypeUse = TypeOnlyName; -const valueUse = ValueName; -const shorthandUse = { ShorthandName }; -interface InterfaceUse extends InterfaceBase {} -class ImplementsUse implements ImplementsBase {} -class ExtendsUse extends ExtendsBase {} -abstract class AbstractComputed { - abstract [AbstractKey](): void; -} -interface InterfaceComputed { - [InterfaceKey]: string; -} -type TypeQueryUse = typeof TypeQueryName; -/** @link JSDocLinkBase#JSDocLinkMember */ -class JSDocLinkUse {} -/** @augments JSDocAugmentsBase */ -class JSDocAugmentsUse {} -`; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": source, - }); - try { - const sourceFile = getRemoteSourceFile(api, "/tsconfig.json", "/src/index.ts"); - const tokenAt = (text: string) => getTokenAtPosition(sourceFile, source.indexOf(text)); - - assert.equal(isValidTypeOnlyAliasUseSite(tokenAt("TypeOnlyName")), true); - assert.equal(isValidTypeOnlyAliasUseSite(tokenAt("ValueName")), false); - assert.equal(isValidTypeOnlyAliasUseSite(tokenAt("ShorthandName")), false); - assert.equal(isValidTypeOnlyAliasUseSite(tokenAt("InterfaceBase")), true); - assert.equal(isValidTypeOnlyAliasUseSite(tokenAt("ImplementsBase")), true); - assert.equal(isValidTypeOnlyAliasUseSite(tokenAt("ExtendsBase")), false); - assert.equal(isValidTypeOnlyAliasUseSite(tokenAt("AbstractKey")), true); - assert.equal(isValidTypeOnlyAliasUseSite(tokenAt("InterfaceKey")), true); - assert.equal(isValidTypeOnlyAliasUseSite(tokenAt("TypeQueryName")), true); - assert.equal(isValidTypeOnlyAliasUseSite(tokenAt("JSDocLinkBase")), true); - assert.equal(isValidTypeOnlyAliasUseSite(tokenAt("JSDocLinkMember")), true); - assert.equal(isValidTypeOnlyAliasUseSite(tokenAt("JSDocAugmentsBase")), true); - } - finally { - api.close(); - } - }); -}); - -// --------------------------------------------------------------------------- -// Integration: visitor transformation -// --------------------------------------------------------------------------- - -describe("visitor transformation", () => { - test("rename all identifiers via recursive visitor", () => { - const a = createIdentifier("oldName"); - const b = createIdentifier("oldName"); - const stmtA = createExpressionStatement(a); - const stmtB = createExpressionStatement(b); - const block = createBlock([stmtA, stmtB]); - - // Use a recursive visitor to rename identifiers at any depth - function renameVisitor(node: Node): Node { - if (node.kind === SyntaxKind.Identifier && (node as Identifier).text === "oldName") { - return createIdentifier("newName"); - } - return visitEachChild(node, renameVisitor); - } - const result = visitEachChild(block, renameVisitor); - - assert.notStrictEqual(result, block); - const stmt0 = result.statements.at(0)! as ExpressionStatement; - assert.strictEqual((stmt0.expression as Identifier).text, "newName"); - const stmt1 = result.statements.at(1)! as ExpressionStatement; - assert.strictEqual((stmt1.expression as Identifier).text, "newName"); - }); - - test("deep clone + modify produces independent tree", () => { - const id = createIdentifier("original"); - const stmt = createExpressionStatement(id); - const block = createBlock([stmt]); - - // Deep clone - const cloned = getSynthesizedDeepClone(block); - - // Modify original using recursive visitor - function modVisitor(node: Node): Node { - if (node.kind === SyntaxKind.Identifier) return createIdentifier("modified"); - return visitEachChild(node, modVisitor); - } - const modified = visitEachChild(block, modVisitor); - - // Cloned tree should be untouched - const clonedStmt = cloned.statements.at(0)! as ExpressionStatement; - assert.strictEqual((clonedStmt.expression as Identifier).text, "original"); - - // Modified tree should have new name - const modifiedStmt = modified.statements.at(0)! as ExpressionStatement; - assert.strictEqual((modifiedStmt.expression as Identifier).text, "modified"); - }); -}); - -// --------------------------------------------------------------------------- -// RemoteNode: cloneNode, visitEachChild, getSynthesizedDeepClone -// --------------------------------------------------------------------------- - -function spawnAPI(files: Record = { - "/tsconfig.json": "{}", - "/src/index.ts": `import { foo } from './foo';\nconst x = foo + 1;\n`, - "/src/foo.ts": `export const foo = 42;`, -}) { - return new API({ - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), - tsserverPath: fileURLToPath(new URL(`../../../../built/local/tsc${process.platform === "win32" ? ".exe" : ""}`, import.meta.url).toString()), - fs: createVirtualFileSystem(files), - }); -} - -function getRemoteSourceFile(api: API, configPath: string, filePath: string) { - const snapshot = api.updateSnapshot({ openProject: configPath }); - const project = snapshot.getProject(configPath)!; - return project.program.getSourceFile(filePath)!; -} - -describe("RemoteNode + cloneNode", () => { - test("does not read a sibling as an invalid JSDoc link name", () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/index.ts": `/** - * {@link #toggled} property - */ -export const x = 1;`, - }); - try { - const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/index.ts"); - const jsDoc = sf.statements[0].jsDoc?.[0] as JSDoc | undefined; - assert.ok(jsDoc?.kind === SyntaxKind.JSDoc); - const comment = jsDoc.comment; - assert.ok(typeof comment !== "string"); - const link = comment?.[1]; - assert.ok(link && isJSDocLink(link)); - assert.strictEqual(link.name, undefined); - } - finally { - api.close(); - } - }); - - test("uses distinct nodes for expression and type heritage", () => { - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/heritage.ts": ` -class C extends Base implements Contract {} -interface I extends Parent {} -`, - }); - try { - const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/heritage.ts"); - const classDecl = sf.statements[0]; - const interfaceDecl = sf.statements[1]; - assert.ok(isClassDeclaration(classDecl)); - assert.ok(isInterfaceDeclaration(interfaceDecl)); - assert.strictEqual(classDecl.heritageClauses?.[0].types[0].kind, SyntaxKind.ExpressionWithTypeArguments); - assert.strictEqual(classDecl.heritageClauses?.[1].types[0].kind, SyntaxKind.TypeReference); - assert.strictEqual(interfaceDecl.heritageClauses?.[0].types[0].kind, SyntaxKind.TypeReference); - } - finally { - api.close(); - } - }); - - test("cloneNode produces a NodeObject from a RemoteNode", () => { - const api = spawnAPI(); - try { - const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/foo.ts"); - const clone = cloneNode(sf); - assert.notStrictEqual(clone, sf); - assert.ok(clone instanceof NodeObject); - assert.strictEqual(clone.statements, sf.statements); - assert.strictEqual(clone.text, sf.text); - assert.strictEqual(clone.fileName, sf.fileName); - assert.strictEqual(clone.path, sf.path); - assert.strictEqual(clone.scriptKind, sf.scriptKind); - assert.strictEqual(clone.languageVariant, sf.languageVariant); - assert.strictEqual(clone.isDeclarationFile, sf.isDeclarationFile); - assert.strictEqual(clone.referencedFiles, sf.referencedFiles); - assert.strictEqual(clone.typeReferenceDirectives, sf.typeReferenceDirectives); - assert.strictEqual(clone.libReferenceDirectives, sf.libReferenceDirectives); - assert.strictEqual(clone.imports, sf.imports); - assert.strictEqual(clone.moduleAugmentations, sf.moduleAugmentations); - assert.strictEqual(clone.ambientModuleNames, sf.ambientModuleNames); - assert.strictEqual(clone.externalModuleIndicator, sf.externalModuleIndicator); - - assert.strictEqual(clone.kind, sf.kind); - assert.strictEqual(clone.pos, sf.pos); - assert.strictEqual(clone.end, sf.end); - } - finally { - api.close(); - } - }); - - test("cloneNode clones a remote import declaration", () => { - const api = spawnAPI(); - try { - const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/index.ts"); - const importDecl = sf.statements[0]; - assert.ok(isImportDeclaration(importDecl)); - - const clone = cloneNode(importDecl); - assert.notStrictEqual(clone, importDecl); - assert.strictEqual(clone.kind, SyntaxKind.ImportDeclaration); - // moduleSpecifier should be the same reference (shallow clone) - assert.ok(clone.moduleSpecifier); - } - finally { - api.close(); - } - }); - - test("cloneNode preserves text on remote identifier", () => { - const api = spawnAPI(); - try { - const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/index.ts"); - const importDecl = sf.statements[0]; - assert.ok(isImportDeclaration(importDecl)); - const named = importDecl.importClause?.namedBindings; - assert.ok(named && isNamedImports(named)); - const fooName = named.elements[0].name; - - const clone = cloneNode(fooName); - assert.strictEqual(clone.kind, SyntaxKind.Identifier); - assert.strictEqual((clone as Identifier).text, "foo"); - } - finally { - api.close(); - } - }); -}); - -describe("RemoteNode + visitEachChild", () => { - test("identity visitor returns same remote node", () => { - const api = spawnAPI(); - try { - const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/foo.ts"); - const firstStmt = sf.statements[0]; - assert.ok(firstStmt); - - // visitEachChild with identity should return the same node - const result = visitEachChild(firstStmt, node => node); - assert.strictEqual(result, firstStmt); - } - finally { - api.close(); - } - }); - - test("visitor can transform remote tree into NodeObject tree", () => { - const api = spawnAPI(); - try { - const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/index.ts"); - const importDecl = sf.statements[0]; - assert.ok(isImportDeclaration(importDecl)); - - // Replace the module specifier with a new string literal - const result = visitEachChild(importDecl, node => { - if (node.kind === SyntaxKind.StringLiteral) { - return createStringLiteral("./bar", TokenFlags.None); - } - return node; - }); - - assert.notStrictEqual(result, importDecl); - assert.strictEqual(result.kind, SyntaxKind.ImportDeclaration); - assert.strictEqual((result.moduleSpecifier as StringLiteralLikeNode).text, "./bar"); - } - finally { - api.close(); - } - }); -}); - -describe("RemoteNodeList inherited array methods", () => { - test("filter/map/slice return plain arrays without throwing", () => { - const api = spawnAPI(); - try { - const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/index.ts"); - const statements = sf.statements; - assert.strictEqual(statements.length, 2); - - // These inherited Array methods previously threw - // "this.view.getUint32 is not a function" via ArraySpeciesCreate. - const filtered = statements.filter(() => true); - assert.ok(Array.isArray(filtered)); - assert.strictEqual(Object.getPrototypeOf(filtered), Array.prototype); - assert.strictEqual(filtered.length, 2); - assert.strictEqual(filtered[0], statements[0]); - - const mapped = statements.map(s => s.kind); - assert.ok(Array.isArray(mapped)); - assert.strictEqual(Object.getPrototypeOf(mapped), Array.prototype); - assert.deepStrictEqual(mapped, [statements[0].kind, statements[1].kind]); - - const sliced = statements.slice(1); - assert.ok(Array.isArray(sliced)); - assert.strictEqual(Object.getPrototypeOf(sliced), Array.prototype); - assert.strictEqual(sliced.length, 1); - assert.strictEqual(sliced[0], statements[1]); - } - finally { - api.close(); - } - }); -}); - -describe("RemoteNode + getSynthesizedDeepClone", () => { - test("deep clones a remote import declaration", () => { - const api = spawnAPI(); - try { - const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/index.ts"); - const importDecl = sf.statements[0]; - assert.ok(isImportDeclaration(importDecl)); - - const clone = getSynthesizedDeepClone(importDecl); - - assert.notStrictEqual(clone, importDecl); - assert.strictEqual(clone.kind, SyntaxKind.ImportDeclaration); - // Deep clone — children should be different objects - assert.notStrictEqual(clone.moduleSpecifier, importDecl.moduleSpecifier); - assert.strictEqual(clone.moduleSpecifier.kind, SyntaxKind.StringLiteral); - } - finally { - api.close(); - } - }); - - test("deep clone of remote tree produces independent NodeObject tree", () => { - const api = spawnAPI(); - try { - const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/foo.ts"); - const firstStmt = sf.statements[0]; - assert.ok(firstStmt); - - const clone = getSynthesizedDeepClone(firstStmt); - - // Clone should have all the same kinds as the original - assert.deepStrictEqual(collectKinds(clone), collectKinds(firstStmt)); - - // But be entirely separate objects - clone.forEachChild(function visit(node) { - assert.ok(node instanceof NodeObject); - node.forEachChild(visit); - }); - } - finally { - api.close(); - } - }); - - test("deep clone of remote SourceFile preserves top-level metadata references", () => { - const api = spawnAPI(); - try { - const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/foo.ts"); - const referencedFiles = sf.referencedFiles; - const typeReferenceDirectives = sf.typeReferenceDirectives; - const libReferenceDirectives = sf.libReferenceDirectives; - const imports = sf.imports; - const moduleAugmentations = sf.moduleAugmentations; - const ambientModuleNames = sf.ambientModuleNames; - - const clone = getSynthesizedDeepClone(sf); - - assert.notStrictEqual(clone, sf); - assert.strictEqual(clone.referencedFiles, referencedFiles); - assert.strictEqual(clone.typeReferenceDirectives, typeReferenceDirectives); - assert.strictEqual(clone.libReferenceDirectives, libReferenceDirectives); - assert.strictEqual(clone.imports, imports); - assert.strictEqual(clone.moduleAugmentations, moduleAugmentations); - assert.strictEqual(clone.ambientModuleNames, ambientModuleNames); - assert.strictEqual(clone.externalModuleIndicator, sf.externalModuleIndicator); - assert.strictEqual(clone.originalText, sf.originalText); - assert.strictEqual(clone.spanMap, sf.spanMap); - assert.strictEqual(clone.contentMapper, sf.contentMapper); - assert.strictEqual(clone.virtualFileName, sf.virtualFileName); - assert.strictEqual(clone.diagnosticDirectives, sf.diagnosticDirectives); - assert.strictEqual(clone.supplementalSourceFileNames, sf.supplementalSourceFileNames); - assert.strictEqual(clone.canonicalSourceFileName, sf.canonicalSourceFileName); - } - finally { - api.close(); - } - }); -}); - -// --------------------------------------------------------------------------- -// RemoteNode: position and text getters -// --------------------------------------------------------------------------- - -// Relationships that must hold between the position/text getters on any node. -function assertGetterInvariants(node: Node, sf: SourceFile) { - const fullStart = node.getFullStart(); - const start = node.getStart(sf); - const end = node.getEnd(); - - assert.strictEqual(fullStart, node.pos); - assert.strictEqual(end, node.end); - assert.ok(start >= fullStart, `getStart (${start}) must be >= getFullStart (${fullStart})`); - assert.ok(end >= start, `getEnd (${end}) must be >= getStart (${start})`); - - assert.strictEqual(node.getFullWidth(), end - fullStart); - assert.strictEqual(node.getWidth(sf), end - start); - assert.strictEqual(node.getLeadingTriviaWidth(sf), start - fullStart); - - const fullText = node.getFullText(sf); - const text = node.getText(sf); - assert.strictEqual(fullText.length, node.getFullWidth()); - assert.strictEqual(text, fullText.slice(node.getLeadingTriviaWidth(sf))); - - // No-argument variants resolve the source file themselves and must agree. - assert.strictEqual(node.getStart(), start); - assert.strictEqual(node.getFullText(), fullText); - assert.strictEqual(node.getText(), text); - - node.forEachChild(child => { - assertGetterInvariants(child, sf); - return undefined; - }); -} - -describe("RemoteNode + position/text getters", () => { - const source = "/* lead */ const value = 123;"; - const files = { - "/tsconfig.json": "{}", - "/src/getters.ts": source, - }; - - test("position and text getters on a parsed statement", () => { - const api = spawnAPI(files); - try { - const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/getters.ts"); - const stmt = sf.statements[0]; - assert.ok(stmt); - - const tokenStart = source.indexOf("const"); - - // Full start includes leading trivia; start skips it. - assert.strictEqual(stmt.getFullStart(), 0); - assert.strictEqual(stmt.getStart(), tokenStart); - assert.strictEqual(stmt.getStart(sf), tokenStart); - assert.strictEqual(stmt.getLeadingTriviaWidth(), tokenStart); - - // End and widths. - assert.strictEqual(stmt.getEnd(), source.length); - assert.strictEqual(stmt.getFullWidth(), source.length); - assert.strictEqual(stmt.getWidth(), source.length - tokenStart); - - // Text slices, with and without leading trivia. - assert.strictEqual(stmt.getFullText(), source); - assert.strictEqual(stmt.getText(), source.slice(tokenStart)); - } - finally { - api.close(); - } - }); - - test("getText/getFullText on a nested node", () => { - const api = spawnAPI(files); - try { - const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/getters.ts"); - const stmt = sf.statements[0] as VariableStatement; - const name = stmt.declarationList.declarations[0].name; - - assert.strictEqual(name.getText(), "value"); - // getFullText keeps the leading whitespace trivia before the identifier. - assert.strictEqual(name.getFullText().trimStart(), "value"); - assert.ok(name.getFullText().endsWith("value")); - } - finally { - api.close(); - } - }); - - test("getStart can include leading JSDoc comments", () => { - const docSource = "/** doc */\nfunction f() {}\n"; - const api = spawnAPI({ - "/tsconfig.json": "{}", - "/src/doc.ts": docSource, - }); - try { - const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/doc.ts"); - const fn = sf.statements[0]; - assert.ok(fn); - - // By default the JSDoc comment is treated as leading trivia and skipped. - assert.strictEqual(fn.getStart(), docSource.indexOf("function")); - - // With includeJsDocComment, the start moves back to the JSDoc comment. - assert.ok(fn.jsDoc && fn.jsDoc.length > 0, "function declaration should have attached JSDoc"); - assert.strictEqual(fn.getStart(sf, /*includeJsDocComment*/ true), 0); - } - finally { - api.close(); - } - }); - - test("a node without leading trivia has zero leading trivia width", () => { - const api = spawnAPI({ "/tsconfig.json": "{}", "/src/plain.ts": "const x = 1;\n" }); - try { - const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/plain.ts"); - const stmt = sf.statements[0]; - assert.ok(stmt); - - assert.strictEqual(stmt.getFullStart(), 0); - assert.strictEqual(stmt.getStart(), 0); - assert.strictEqual(stmt.getLeadingTriviaWidth(), 0); - assert.strictEqual(stmt.getText(), "const x = 1;"); - assert.strictEqual(stmt.getText(), stmt.getFullText()); - } - finally { - api.close(); - } - }); - - test("the SourceFile node spans the whole file text", () => { - const text = "/* head */ const y = 2;\n"; - const api = spawnAPI({ "/tsconfig.json": "{}", "/src/whole.ts": text }); - try { - const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/whole.ts"); - - assert.strictEqual(sf.getFullStart(), 0); - assert.strictEqual(sf.getFullText(), sf.text); - assert.strictEqual(sf.getEnd(), sf.text.length); - // getStart skips the file's leading comment trivia. - assert.strictEqual(sf.getStart(), text.indexOf("const")); - } - finally { - api.close(); - } - }); - - test("getter invariants hold for every node in a representative tree", () => { - const treeSource = [ - `import { foo } from "./foo";`, - ``, - `/** docs */`, - `export function add(a: number, b: number): number {`, - ` // body comment`, - ` return a + b;`, - `}`, - ``, - `const obj = { x: 1, y: "two" };`, - ``, - ].join("\n"); - const api = spawnAPI({ "/tsconfig.json": "{}", "/src/tree.ts": treeSource }); - try { - const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/tree.ts"); - assertGetterInvariants(sf, sf); - } - finally { - api.close(); - } - }); - - test("getter invariants hold even for malformed source with missing nodes", () => { - // Error recovery produces zero-width / missing nodes; the getters must - // still satisfy their invariants and must not throw. - const malformed = "const a = b +;\nfunction ("; - const api = spawnAPI({ "/tsconfig.json": "{}", "/src/broken.ts": malformed }); - try { - const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/broken.ts"); - assertGetterInvariants(sf, sf); - } - finally { - api.close(); - } - }); -}); +import type { + ExpressionStatement, + Identifier, + JSDoc, + Node, + NodeArray, + SourceFile, + StringLiteralLikeNode, + VariableStatement, +} from "@typescript/typescript/unstable/ast"; +import { + getTokenAtPosition, + isClassDeclaration, + isImportDeclaration, + isInterfaceDeclaration, + isJSDocLink, + isNamedImports, + isValidTypeOnlyAliasUseSite, + SyntaxKind, + TokenFlags, +} from "@typescript/typescript/unstable/ast"; +import { + getSynthesizedDeepClone, + getSynthesizedDeepClones, +} from "@typescript/typescript/unstable/ast/clone"; +import { + cloneNode, + createBinaryExpression, + createBlock, + createExpressionStatement, + createIdentifier, + createIfStatement, + createNodeArray, + createNumericLiteral, + createSourceFile, + createStringLiteral, + createToken, + NodeObject, +} from "@typescript/typescript/unstable/ast/factory"; +import { + visitEachChild, + visitNode, + visitNodes, +} from "@typescript/typescript/unstable/ast/visitor"; +import { createVirtualFileSystem } from "@typescript/typescript/unstable/fs"; +import { API } from "@typescript/typescript/unstable/sync"; +import assert from "node:assert"; +import { + describe, + test, +} from "node:test"; +import { fileURLToPath } from "node:url"; + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +function collectKinds(node: Node): SyntaxKind[] { + const kinds: SyntaxKind[] = [node.kind]; + node.forEachChild(child => { + kinds.push(...collectKinds(child)); + return undefined; + }); + return kinds; +} + +// --------------------------------------------------------------------------- +// cloneNode +// --------------------------------------------------------------------------- + +describe("cloneNode", () => { + test("clones an identifier", () => { + const id = createIdentifier("hello"); + const clone = cloneNode(id); + + assert.notStrictEqual(clone, id); + assert.strictEqual(clone.kind, SyntaxKind.Identifier); + assert.strictEqual(clone.text, "hello"); + }); + + test("clones a numeric literal", () => { + const num = createNumericLiteral("42", TokenFlags.None); + const clone = cloneNode(num); + + assert.notStrictEqual(clone, num); + assert.strictEqual(clone.kind, SyntaxKind.NumericLiteral); + assert.strictEqual(clone.text, "42"); + assert.strictEqual(clone.tokenFlags, TokenFlags.None); + }); + + test("clones a string literal", () => { + const str = createStringLiteral("world", TokenFlags.None); + const clone = cloneNode(str); + + assert.notStrictEqual(clone, str); + assert.strictEqual(clone.kind, SyntaxKind.StringLiteral); + assert.strictEqual(clone.text, "world"); + }); + + test("preserves text range", () => { + // Synthesized nodes start at pos=-1,end=-1; verify clone preserves them + const id = createIdentifier("x"); + const clone = cloneNode(id); + + assert.strictEqual(clone.pos, id.pos); + assert.strictEqual(clone.end, id.end); + }); + + test("clone is shallow (children are shared)", () => { + const left = createIdentifier("a"); + const right = createIdentifier("b"); + const op = createToken(SyntaxKind.PlusToken); + const bin = createBinaryExpression(undefined, left, undefined, op, right); + const clone = cloneNode(bin); + + assert.notStrictEqual(clone, bin); + assert.strictEqual(clone.left, bin.left); + assert.strictEqual(clone.right, bin.right); + assert.strictEqual(clone.operatorToken, bin.operatorToken); + }); + + test("clone has no parent", () => { + const id = createIdentifier("x"); + const clone = cloneNode(id); + assert.strictEqual(clone.parent, undefined); + }); + + test("clones a token", () => { + const tok = createToken(SyntaxKind.SemicolonToken); + const clone = cloneNode(tok); + + assert.notStrictEqual(clone, tok); + assert.strictEqual(clone.kind, SyntaxKind.SemicolonToken); + }); +}); + +// --------------------------------------------------------------------------- +// visitNode / visitNodes +// --------------------------------------------------------------------------- + +describe("visitNode", () => { + test("returns undefined for undefined input", () => { + const nothing: Node | undefined = undefined; + const result = visitNode(nothing, () => undefined); + assert.strictEqual(result, undefined); + }); + + test("returns visitor result", () => { + const id = createIdentifier("x"); + const replacement = createIdentifier("y"); + const result = visitNode(id, () => replacement); + assert.strictEqual(result, replacement); + }); + + test("visitor can return undefined to remove node", () => { + const id = createIdentifier("x"); + const result = visitNode(id, () => undefined); + assert.strictEqual(result, undefined); + }); +}); + +describe("visitNodes", () => { + test("returns undefined for undefined input", () => { + const nothing: NodeArray | undefined = undefined; + const result = visitNodes(nothing, () => undefined); + assert.strictEqual(result, undefined); + }); + + test("returns same array if nothing changed", () => { + const a = createIdentifier("a"); + const b = createIdentifier("b"); + const arr = createNodeArray([a, b]); + const result = visitNodes(arr, node => node); + assert.strictEqual(result, arr); + }); + + test("filters out undefined results", () => { + const a = createIdentifier("a"); + const b = createIdentifier("b"); + const arr = createNodeArray([a, b]); + const result = visitNodes(arr, node => { + if (node === a) return undefined; + return node; + }); + assert.strictEqual(result.length, 1); + }); + + test("replaces nodes", () => { + const a = createIdentifier("a"); + const b = createIdentifier("b"); + const c = createIdentifier("c"); + const arr = createNodeArray([a, b]); + const result = visitNodes(arr, node => { + if (node === a) return c; + return node; + }); + assert.strictEqual(result.length, 2); + assert.strictEqual(result.at(0), c); + assert.strictEqual(result.at(1), b); + }); +}); + +// --------------------------------------------------------------------------- +// visitEachChild +// --------------------------------------------------------------------------- + +describe("visitEachChild", () => { + test("returns same node if nothing changed (identity visitor)", () => { + const left = createIdentifier("a"); + const right = createIdentifier("b"); + const op = createToken(SyntaxKind.PlusToken); + const bin = createBinaryExpression(undefined, left, undefined, op, right); + + const result = visitEachChild(bin, node => node); + assert.strictEqual(result, bin); + }); + + test("returns undefined for undefined input", () => { + const nothing: Node | undefined = undefined; + const result = visitEachChild(nothing, node => node); + assert.strictEqual(result, undefined); + }); + + test("creates new node when child changes", () => { + const left = createIdentifier("a"); + const right = createIdentifier("b"); + const op = createToken(SyntaxKind.PlusToken); + const bin = createBinaryExpression(undefined, left, undefined, op, right); + + const newRight = createIdentifier("c"); + const result = visitEachChild(bin, node => { + if (node === right) return newRight; + return node; + }); + + assert.notStrictEqual(result, bin); + assert.strictEqual(result.kind, SyntaxKind.BinaryExpression); + assert.strictEqual(result.left, left); + assert.strictEqual(result.right, newRight); + }); + + test("works on if statement with optional else", () => { + const cond = createIdentifier("ok"); + const thenBranch = createBlock([]); + const ifStmt = createIfStatement(cond, thenBranch, undefined); + + const newCond = createIdentifier("notOk"); + const result = visitEachChild(ifStmt, node => { + if (node === cond) return newCond; + return node; + }); + + assert.notStrictEqual(result, ifStmt); + assert.strictEqual(result.expression, newCond); + assert.strictEqual(result.thenStatement, thenBranch); + assert.strictEqual(result.elseStatement, undefined); + }); + + test("works on nodes with NodeArray children", () => { + const stmtA = createExpressionStatement(createIdentifier("a")); + const stmtB = createExpressionStatement(createIdentifier("b")); + const block = createBlock([stmtA, stmtB]); + + const stmtC = createExpressionStatement(createIdentifier("c")); + const result = visitEachChild(block, node => { + // visitEachChild visits immediate children (the expression statements) + if (node === stmtA) return stmtC; + return node; + }); + + assert.notStrictEqual(result, block); + assert.strictEqual(result.kind, SyntaxKind.Block); + assert.strictEqual(result.statements.length, 2); + assert.strictEqual(result.statements.at(0), stmtC); + assert.strictEqual(result.statements.at(1), stmtB); + }); + + test("returns same node for leaf nodes", () => { + const id = createIdentifier("x"); + const result = visitEachChild(id, () => { + throw new Error("should not be called for leaf node"); + }); + // Identifier has no children, so visitor shouldn't be called + assert.strictEqual(result, id); + }); +}); + +// --------------------------------------------------------------------------- +// getSynthesizedDeepClone +// --------------------------------------------------------------------------- + +describe("getSynthesizedDeepClone", () => { + test("deeply clones identifier", () => { + const id = createIdentifier("hello"); + const clone = getSynthesizedDeepClone(id); + + assert.notStrictEqual(clone, id); + assert.strictEqual(clone.kind, SyntaxKind.Identifier); + assert.strictEqual(clone.text, "hello"); + }); + + test("deeply clones identifier without trivia", () => { + const id = createIdentifier("hello"); + const clone = getSynthesizedDeepClone(id, /*includeTrivia*/ false); + + assert.strictEqual(clone.kind, SyntaxKind.Identifier); + assert.strictEqual(clone.text, "hello"); + assert.strictEqual(clone.pos, -1); + assert.strictEqual(clone.end, -1); + }); + + test("deeply clones binary expression", () => { + const left = createIdentifier("a"); + const right = createNumericLiteral("42", TokenFlags.None); + const op = createToken(SyntaxKind.PlusToken); + const bin = createBinaryExpression(undefined, left, undefined, op, right); + + const clone = getSynthesizedDeepClone(bin); + + // Root is different object + assert.notStrictEqual(clone, bin); + assert.strictEqual(clone.kind, SyntaxKind.BinaryExpression); + + // Children are also different objects (deep clone) + assert.notStrictEqual(clone.left, left); + assert.strictEqual(clone.left.kind, SyntaxKind.Identifier); + assert.strictEqual((clone.left as Identifier).text, "a"); + + assert.notStrictEqual(clone.right, right); + assert.strictEqual(clone.right.kind, SyntaxKind.NumericLiteral); + assert.strictEqual((clone.right as Identifier).text, "42"); + + assert.notStrictEqual(clone.operatorToken, op); + assert.strictEqual(clone.operatorToken.kind, SyntaxKind.PlusToken); + }); + + test("sets parent pointers on deep clone", () => { + const left = createIdentifier("a"); + const right = createIdentifier("b"); + const op = createToken(SyntaxKind.PlusToken); + const bin = createBinaryExpression(undefined, left, undefined, op, right); + + const clone = getSynthesizedDeepClone(bin); + + assert.strictEqual(clone.left.parent, clone); + assert.strictEqual(clone.right.parent, clone); + assert.strictEqual(clone.operatorToken.parent, clone); + }); + + test("returns undefined for undefined input", () => { + const result = getSynthesizedDeepClone(undefined); + assert.strictEqual(result, undefined); + }); + + test("deeply clones if statement with optional else", () => { + const cond = createIdentifier("ok"); + const thenBlock = createBlock([]); + const ifStmt = createIfStatement(cond, thenBlock, undefined); + + const clone = getSynthesizedDeepClone(ifStmt); + + assert.notStrictEqual(clone, ifStmt); + assert.strictEqual(clone.kind, SyntaxKind.IfStatement); + assert.notStrictEqual(clone.expression, cond); + assert.strictEqual(clone.expression.kind, SyntaxKind.Identifier); + assert.notStrictEqual(clone.thenStatement, thenBlock); + assert.strictEqual(clone.thenStatement.kind, SyntaxKind.Block); + assert.strictEqual(clone.elseStatement, undefined); + }); + + test("preserves tree structure with nested children", () => { + const a = createIdentifier("a"); + const b = createIdentifier("b"); + const stmts = [ + createExpressionStatement(a), + createExpressionStatement(b), + ]; + const block = createBlock(stmts); + + const clone = getSynthesizedDeepClone(block); + + assert.notStrictEqual(clone, block); + assert.strictEqual(clone.statements.length, 2); + + const cloneStmt0 = clone.statements.at(0)!; + const cloneStmt1 = clone.statements.at(1)!; + assert.notStrictEqual(cloneStmt0, stmts[0]); + assert.notStrictEqual(cloneStmt1, stmts[1]); + assert.strictEqual(cloneStmt0.kind, SyntaxKind.ExpressionStatement); + assert.strictEqual(cloneStmt1.kind, SyntaxKind.ExpressionStatement); + }); + + test("deeply clones string literal", () => { + const str = createStringLiteral("test", TokenFlags.None); + const clone = getSynthesizedDeepClone(str); + assert.notStrictEqual(clone, str); + assert.strictEqual(clone.kind, SyntaxKind.StringLiteral); + assert.strictEqual(clone.text, "test"); + }); + + test("deeply clones numeric literal", () => { + const num = createNumericLiteral("3.14", TokenFlags.None); + const clone = getSynthesizedDeepClone(num); + assert.notStrictEqual(clone, num); + assert.strictEqual(clone.kind, SyntaxKind.NumericLiteral); + assert.strictEqual(clone.text, "3.14"); + }); + + test("clone has same syntax structure", () => { + const left = createIdentifier("x"); + const right = createNumericLiteral("1", TokenFlags.None); + const op = createToken(SyntaxKind.PlusToken); + const bin = createBinaryExpression(undefined, left, undefined, op, right); + const stmt = createExpressionStatement(bin); + + const clone = getSynthesizedDeepClone(stmt); + assert.deepStrictEqual(collectKinds(clone), collectKinds(stmt)); + }); +}); + +// --------------------------------------------------------------------------- +// getSynthesizedDeepClones (NodeArray) +// --------------------------------------------------------------------------- + +describe("getSynthesizedDeepClones", () => { + test("deeply clones a NodeArray", () => { + const a = createIdentifier("a"); + const b = createIdentifier("b"); + const arr = createNodeArray([a, b]); + + const clone = getSynthesizedDeepClones(arr); + + assert.notStrictEqual(clone, arr); + assert.strictEqual(clone.length, 2); + assert.notStrictEqual(clone.at(0), a); + assert.notStrictEqual(clone.at(1), b); + assert.strictEqual(clone.at(0)!.kind, SyntaxKind.Identifier); + assert.strictEqual(clone.at(1)!.kind, SyntaxKind.Identifier); + }); + + test("returns undefined for undefined input", () => { + const result = getSynthesizedDeepClones(undefined); + assert.strictEqual(result, undefined); + }); + + test("preserves pos and end on NodeArray", () => { + const a = createIdentifier("a"); + const arr = createNodeArray([a], 5, 10); + + const clone = getSynthesizedDeepClones(arr); + assert.strictEqual(clone.pos, 5); + assert.strictEqual(clone.end, 10); + }); +}); + +// --------------------------------------------------------------------------- +// Type-only import use sites +// --------------------------------------------------------------------------- + +describe("isValidTypeOnlyAliasUseSite", () => { + test("classifies syntactic type-only import use sites", () => { + const source = ` +type TypeUse = TypeOnlyName; +const valueUse = ValueName; +const shorthandUse = { ShorthandName }; +interface InterfaceUse extends InterfaceBase {} +class ImplementsUse implements ImplementsBase {} +class ExtendsUse extends ExtendsBase {} +abstract class AbstractComputed { + abstract [AbstractKey](): void; +} +interface InterfaceComputed { + [InterfaceKey]: string; +} +type TypeQueryUse = typeof TypeQueryName; +/** @link JSDocLinkBase#JSDocLinkMember */ +class JSDocLinkUse {} +/** @augments JSDocAugmentsBase */ +class JSDocAugmentsUse {} +`; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": source, + }); + try { + const sourceFile = getRemoteSourceFile(api, "/tsconfig.json", "/src/index.ts"); + const tokenAt = (text: string) => getTokenAtPosition(sourceFile, source.indexOf(text)); + + assert.equal(isValidTypeOnlyAliasUseSite(tokenAt("TypeOnlyName")), true); + assert.equal(isValidTypeOnlyAliasUseSite(tokenAt("ValueName")), false); + assert.equal(isValidTypeOnlyAliasUseSite(tokenAt("ShorthandName")), false); + assert.equal(isValidTypeOnlyAliasUseSite(tokenAt("InterfaceBase")), true); + assert.equal(isValidTypeOnlyAliasUseSite(tokenAt("ImplementsBase")), true); + assert.equal(isValidTypeOnlyAliasUseSite(tokenAt("ExtendsBase")), false); + assert.equal(isValidTypeOnlyAliasUseSite(tokenAt("AbstractKey")), true); + assert.equal(isValidTypeOnlyAliasUseSite(tokenAt("InterfaceKey")), true); + assert.equal(isValidTypeOnlyAliasUseSite(tokenAt("TypeQueryName")), true); + assert.equal(isValidTypeOnlyAliasUseSite(tokenAt("JSDocLinkBase")), true); + assert.equal(isValidTypeOnlyAliasUseSite(tokenAt("JSDocLinkMember")), true); + assert.equal(isValidTypeOnlyAliasUseSite(tokenAt("JSDocAugmentsBase")), true); + } + finally { + api.close(); + } + }); +}); + +// --------------------------------------------------------------------------- +// Integration: visitor transformation +// --------------------------------------------------------------------------- + +describe("visitor transformation", () => { + test("rename all identifiers via recursive visitor", () => { + const a = createIdentifier("oldName"); + const b = createIdentifier("oldName"); + const stmtA = createExpressionStatement(a); + const stmtB = createExpressionStatement(b); + const block = createBlock([stmtA, stmtB]); + + // Use a recursive visitor to rename identifiers at any depth + function renameVisitor(node: Node): Node { + if (node.kind === SyntaxKind.Identifier && (node as Identifier).text === "oldName") { + return createIdentifier("newName"); + } + return visitEachChild(node, renameVisitor); + } + const result = visitEachChild(block, renameVisitor); + + assert.notStrictEqual(result, block); + const stmt0 = result.statements.at(0)! as ExpressionStatement; + assert.strictEqual((stmt0.expression as Identifier).text, "newName"); + const stmt1 = result.statements.at(1)! as ExpressionStatement; + assert.strictEqual((stmt1.expression as Identifier).text, "newName"); + }); + + test("deep clone + modify produces independent tree", () => { + const id = createIdentifier("original"); + const stmt = createExpressionStatement(id); + const block = createBlock([stmt]); + + // Deep clone + const cloned = getSynthesizedDeepClone(block); + + // Modify original using recursive visitor + function modVisitor(node: Node): Node { + if (node.kind === SyntaxKind.Identifier) return createIdentifier("modified"); + return visitEachChild(node, modVisitor); + } + const modified = visitEachChild(block, modVisitor); + + // Cloned tree should be untouched + const clonedStmt = cloned.statements.at(0)! as ExpressionStatement; + assert.strictEqual((clonedStmt.expression as Identifier).text, "original"); + + // Modified tree should have new name + const modifiedStmt = modified.statements.at(0)! as ExpressionStatement; + assert.strictEqual((modifiedStmt.expression as Identifier).text, "modified"); + }); +}); + +// --------------------------------------------------------------------------- +// RemoteNode: cloneNode, visitEachChild, getSynthesizedDeepClone +// --------------------------------------------------------------------------- + +function spawnAPI(files: Record = { + "/tsconfig.json": "{}", + "/src/index.ts": `import { foo } from './foo';\nconst x = foo + 1;\n`, + "/src/foo.ts": `export const foo = 42;`, +}) { + return new API({ + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + tsserverPath: fileURLToPath(new URL(`../../../../built/local/tsc${process.platform === "win32" ? ".exe" : ""}`, import.meta.url).toString()), + fs: createVirtualFileSystem(files), + }); +} + +function getRemoteSourceFile(api: API, configPath: string, filePath: string) { + const snapshot = api.updateSnapshot({ openProject: configPath }); + const project = snapshot.getProject(configPath)!; + return project.program.getSourceFile(filePath)!; +} + +describe("RemoteNode + cloneNode", () => { + test("does not read a sibling as an invalid JSDoc link name", () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/index.ts": `/** + * {@link #toggled} property + */ +export const x = 1;`, + }); + try { + const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/index.ts"); + const jsDoc = sf.statements[0].jsDoc?.[0] as JSDoc | undefined; + assert.ok(jsDoc?.kind === SyntaxKind.JSDoc); + const comment = jsDoc.comment; + assert.ok(typeof comment !== "string"); + const link = comment?.[1]; + assert.ok(link && isJSDocLink(link)); + assert.strictEqual(link.name, undefined); + } + finally { + api.close(); + } + }); + + test("uses distinct nodes for expression and type heritage", () => { + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/heritage.ts": ` +class C extends Base implements Contract {} +interface I extends Parent {} +`, + }); + try { + const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/heritage.ts"); + const classDecl = sf.statements[0]; + const interfaceDecl = sf.statements[1]; + assert.ok(isClassDeclaration(classDecl)); + assert.ok(isInterfaceDeclaration(interfaceDecl)); + assert.strictEqual(classDecl.heritageClauses?.[0].types[0].kind, SyntaxKind.ExpressionWithTypeArguments); + assert.strictEqual(classDecl.heritageClauses?.[1].types[0].kind, SyntaxKind.TypeReference); + assert.strictEqual(interfaceDecl.heritageClauses?.[0].types[0].kind, SyntaxKind.TypeReference); + } + finally { + api.close(); + } + }); + + test("cloneNode produces a NodeObject from a RemoteNode", () => { + const api = spawnAPI(); + try { + const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/foo.ts"); + const clone = cloneNode(sf); + assert.notStrictEqual(clone, sf); + assert.ok(clone instanceof NodeObject); + assert.strictEqual(clone.statements, sf.statements); + assert.strictEqual(clone.text, sf.text); + assert.strictEqual(clone.fileName, sf.fileName); + assert.strictEqual(clone.path, sf.path); + assert.strictEqual(clone.scriptKind, sf.scriptKind); + assert.strictEqual(clone.languageVariant, sf.languageVariant); + assert.strictEqual(clone.isDeclarationFile, sf.isDeclarationFile); + assert.strictEqual(clone.referencedFiles, sf.referencedFiles); + assert.strictEqual(clone.typeReferenceDirectives, sf.typeReferenceDirectives); + assert.strictEqual(clone.libReferenceDirectives, sf.libReferenceDirectives); + assert.strictEqual(clone.imports, sf.imports); + assert.strictEqual(clone.moduleAugmentations, sf.moduleAugmentations); + assert.strictEqual(clone.ambientModuleNames, sf.ambientModuleNames); + assert.strictEqual(clone.externalModuleIndicator, sf.externalModuleIndicator); + + assert.strictEqual(clone.kind, sf.kind); + assert.strictEqual(clone.pos, sf.pos); + assert.strictEqual(clone.end, sf.end); + } + finally { + api.close(); + } + }); + + test("cloneNode clones a remote import declaration", () => { + const api = spawnAPI(); + try { + const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/index.ts"); + const importDecl = sf.statements[0]; + assert.ok(isImportDeclaration(importDecl)); + + const clone = cloneNode(importDecl); + assert.notStrictEqual(clone, importDecl); + assert.strictEqual(clone.kind, SyntaxKind.ImportDeclaration); + // moduleSpecifier should be the same reference (shallow clone) + assert.ok(clone.moduleSpecifier); + } + finally { + api.close(); + } + }); + + test("cloneNode preserves text on remote identifier", () => { + const api = spawnAPI(); + try { + const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/index.ts"); + const importDecl = sf.statements[0]; + assert.ok(isImportDeclaration(importDecl)); + const named = importDecl.importClause?.namedBindings; + assert.ok(named && isNamedImports(named)); + const fooName = named.elements[0].name; + + const clone = cloneNode(fooName); + assert.strictEqual(clone.kind, SyntaxKind.Identifier); + assert.strictEqual((clone as Identifier).text, "foo"); + } + finally { + api.close(); + } + }); +}); + +describe("RemoteNode + visitEachChild", () => { + test("identity visitor returns same remote node", () => { + const api = spawnAPI(); + try { + const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/foo.ts"); + const firstStmt = sf.statements[0]; + assert.ok(firstStmt); + + // visitEachChild with identity should return the same node + const result = visitEachChild(firstStmt, node => node); + assert.strictEqual(result, firstStmt); + } + finally { + api.close(); + } + }); + + test("visitor can transform remote tree into NodeObject tree", () => { + const api = spawnAPI(); + try { + const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/index.ts"); + const importDecl = sf.statements[0]; + assert.ok(isImportDeclaration(importDecl)); + + // Replace the module specifier with a new string literal + const result = visitEachChild(importDecl, node => { + if (node.kind === SyntaxKind.StringLiteral) { + return createStringLiteral("./bar", TokenFlags.None); + } + return node; + }); + + assert.notStrictEqual(result, importDecl); + assert.strictEqual(result.kind, SyntaxKind.ImportDeclaration); + assert.strictEqual((result.moduleSpecifier as StringLiteralLikeNode).text, "./bar"); + } + finally { + api.close(); + } + }); +}); + +describe("RemoteNodeList inherited array methods", () => { + test("filter/map/slice return plain arrays without throwing", () => { + const api = spawnAPI(); + try { + const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/index.ts"); + const statements = sf.statements; + assert.strictEqual(statements.length, 2); + + // These inherited Array methods previously threw + // "this.view.getUint32 is not a function" via ArraySpeciesCreate. + const filtered = statements.filter(() => true); + assert.ok(Array.isArray(filtered)); + assert.strictEqual(Object.getPrototypeOf(filtered), Array.prototype); + assert.strictEqual(filtered.length, 2); + assert.strictEqual(filtered[0], statements[0]); + + const mapped = statements.map(s => s.kind); + assert.ok(Array.isArray(mapped)); + assert.strictEqual(Object.getPrototypeOf(mapped), Array.prototype); + assert.deepStrictEqual(mapped, [statements[0].kind, statements[1].kind]); + + const sliced = statements.slice(1); + assert.ok(Array.isArray(sliced)); + assert.strictEqual(Object.getPrototypeOf(sliced), Array.prototype); + assert.strictEqual(sliced.length, 1); + assert.strictEqual(sliced[0], statements[1]); + } + finally { + api.close(); + } + }); +}); + +describe("RemoteNode + getSynthesizedDeepClone", () => { + test("deep clones a remote import declaration", () => { + const api = spawnAPI(); + try { + const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/index.ts"); + const importDecl = sf.statements[0]; + assert.ok(isImportDeclaration(importDecl)); + + const clone = getSynthesizedDeepClone(importDecl); + + assert.notStrictEqual(clone, importDecl); + assert.strictEqual(clone.kind, SyntaxKind.ImportDeclaration); + // Deep clone — children should be different objects + assert.notStrictEqual(clone.moduleSpecifier, importDecl.moduleSpecifier); + assert.strictEqual(clone.moduleSpecifier.kind, SyntaxKind.StringLiteral); + } + finally { + api.close(); + } + }); + + test("deep clone of remote tree produces independent NodeObject tree", () => { + const api = spawnAPI(); + try { + const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/foo.ts"); + const firstStmt = sf.statements[0]; + assert.ok(firstStmt); + + const clone = getSynthesizedDeepClone(firstStmt); + + // Clone should have all the same kinds as the original + assert.deepStrictEqual(collectKinds(clone), collectKinds(firstStmt)); + + // But be entirely separate objects + clone.forEachChild(function visit(node) { + assert.ok(node instanceof NodeObject); + node.forEachChild(visit); + }); + } + finally { + api.close(); + } + }); + + test("deep clone of remote SourceFile preserves top-level metadata references", () => { + const api = spawnAPI(); + try { + const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/foo.ts"); + const referencedFiles = sf.referencedFiles; + const typeReferenceDirectives = sf.typeReferenceDirectives; + const libReferenceDirectives = sf.libReferenceDirectives; + const imports = sf.imports; + const moduleAugmentations = sf.moduleAugmentations; + const ambientModuleNames = sf.ambientModuleNames; + + const clone = getSynthesizedDeepClone(sf); + + assert.notStrictEqual(clone, sf); + assert.strictEqual(clone.referencedFiles, referencedFiles); + assert.strictEqual(clone.typeReferenceDirectives, typeReferenceDirectives); + assert.strictEqual(clone.libReferenceDirectives, libReferenceDirectives); + assert.strictEqual(clone.imports, imports); + assert.strictEqual(clone.moduleAugmentations, moduleAugmentations); + assert.strictEqual(clone.ambientModuleNames, ambientModuleNames); + assert.strictEqual(clone.externalModuleIndicator, sf.externalModuleIndicator); + assert.strictEqual(clone.originalText, sf.originalText); + assert.strictEqual(clone.spanMap, sf.spanMap); + assert.strictEqual(clone.contentMapper, sf.contentMapper); + assert.strictEqual(clone.virtualFileName, sf.virtualFileName); + assert.strictEqual(clone.diagnosticDirectives, sf.diagnosticDirectives); + assert.strictEqual(clone.supplementalSourceFileNames, sf.supplementalSourceFileNames); + assert.strictEqual(clone.canonicalSourceFileName, sf.canonicalSourceFileName); + } + finally { + api.close(); + } + }); +}); + +// --------------------------------------------------------------------------- +// RemoteNode: position and text getters +// --------------------------------------------------------------------------- + +// Relationships that must hold between the position/text getters on any node. +function assertGetterInvariants(node: Node, sf: SourceFile) { + const fullStart = node.getFullStart(); + const start = node.getStart(sf); + const end = node.getEnd(); + + assert.strictEqual(fullStart, node.pos); + assert.strictEqual(end, node.end); + assert.ok(start >= fullStart, `getStart (${start}) must be >= getFullStart (${fullStart})`); + assert.ok(end >= start, `getEnd (${end}) must be >= getStart (${start})`); + + assert.strictEqual(node.getFullWidth(), end - fullStart); + assert.strictEqual(node.getWidth(sf), end - start); + assert.strictEqual(node.getLeadingTriviaWidth(sf), start - fullStart); + + const fullText = node.getFullText(sf); + const text = node.getText(sf); + assert.strictEqual(fullText.length, node.getFullWidth()); + assert.strictEqual(text, fullText.slice(node.getLeadingTriviaWidth(sf))); + + // No-argument variants resolve the source file themselves and must agree. + assert.strictEqual(node.getStart(), start); + assert.strictEqual(node.getFullText(), fullText); + assert.strictEqual(node.getText(), text); + + node.forEachChild(child => { + assertGetterInvariants(child, sf); + return undefined; + }); +} + +describe("RemoteNode + position/text getters", () => { + const source = "/* lead */ const value = 123;"; + const files = { + "/tsconfig.json": "{}", + "/src/getters.ts": source, + }; + + test("position and text getters on a parsed statement", () => { + const api = spawnAPI(files); + try { + const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/getters.ts"); + const stmt = sf.statements[0]; + assert.ok(stmt); + + const tokenStart = source.indexOf("const"); + + // Full start includes leading trivia; start skips it. + assert.strictEqual(stmt.getFullStart(), 0); + assert.strictEqual(stmt.getStart(), tokenStart); + assert.strictEqual(stmt.getStart(sf), tokenStart); + assert.strictEqual(stmt.getLeadingTriviaWidth(), tokenStart); + + // End and widths. + assert.strictEqual(stmt.getEnd(), source.length); + assert.strictEqual(stmt.getFullWidth(), source.length); + assert.strictEqual(stmt.getWidth(), source.length - tokenStart); + + // Text slices, with and without leading trivia. + assert.strictEqual(stmt.getFullText(), source); + assert.strictEqual(stmt.getText(), source.slice(tokenStart)); + } + finally { + api.close(); + } + }); + + test("getText/getFullText on a nested node", () => { + const api = spawnAPI(files); + try { + const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/getters.ts"); + const stmt = sf.statements[0] as VariableStatement; + const name = stmt.declarationList.declarations[0].name; + + assert.strictEqual(name.getText(), "value"); + // getFullText keeps the leading whitespace trivia before the identifier. + assert.strictEqual(name.getFullText().trimStart(), "value"); + assert.ok(name.getFullText().endsWith("value")); + } + finally { + api.close(); + } + }); + + test("getStart can include leading JSDoc comments", () => { + const docSource = "/** doc */\nfunction f() {}\n"; + const api = spawnAPI({ + "/tsconfig.json": "{}", + "/src/doc.ts": docSource, + }); + try { + const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/doc.ts"); + const fn = sf.statements[0]; + assert.ok(fn); + + // By default the JSDoc comment is treated as leading trivia and skipped. + assert.strictEqual(fn.getStart(), docSource.indexOf("function")); + + // With includeJsDocComment, the start moves back to the JSDoc comment. + assert.ok(fn.jsDoc && fn.jsDoc.length > 0, "function declaration should have attached JSDoc"); + assert.strictEqual(fn.getStart(sf, /*includeJsDocComment*/ true), 0); + } + finally { + api.close(); + } + }); + + test("a node without leading trivia has zero leading trivia width", () => { + const api = spawnAPI({ "/tsconfig.json": "{}", "/src/plain.ts": "const x = 1;\n" }); + try { + const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/plain.ts"); + const stmt = sf.statements[0]; + assert.ok(stmt); + + assert.strictEqual(stmt.getFullStart(), 0); + assert.strictEqual(stmt.getStart(), 0); + assert.strictEqual(stmt.getLeadingTriviaWidth(), 0); + assert.strictEqual(stmt.getText(), "const x = 1;"); + assert.strictEqual(stmt.getText(), stmt.getFullText()); + } + finally { + api.close(); + } + }); + + test("the SourceFile node spans the whole file text", () => { + const text = "/* head */ const y = 2;\n"; + const api = spawnAPI({ "/tsconfig.json": "{}", "/src/whole.ts": text }); + try { + const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/whole.ts"); + + assert.strictEqual(sf.getFullStart(), 0); + assert.strictEqual(sf.getFullText(), sf.text); + assert.strictEqual(sf.getEnd(), sf.text.length); + // getStart skips the file's leading comment trivia. + assert.strictEqual(sf.getStart(), text.indexOf("const")); + } + finally { + api.close(); + } + }); + + test("getter invariants hold for every node in a representative tree", () => { + const treeSource = [ + `import { foo } from "./foo";`, + ``, + `/** docs */`, + `export function add(a: number, b: number): number {`, + ` // body comment`, + ` return a + b;`, + `}`, + ``, + `const obj = { x: 1, y: "two" };`, + ``, + ].join("\n"); + const api = spawnAPI({ "/tsconfig.json": "{}", "/src/tree.ts": treeSource }); + try { + const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/tree.ts"); + assertGetterInvariants(sf, sf); + } + finally { + api.close(); + } + }); + + test("getter invariants hold even for malformed source with missing nodes", () => { + // Error recovery produces zero-width / missing nodes; the getters must + // still satisfy their invariants and must not throw. + const malformed = "const a = b +;\nfunction ("; + const api = spawnAPI({ "/tsconfig.json": "{}", "/src/broken.ts": malformed }); + try { + const sf = getRemoteSourceFile(api, "/tsconfig.json", "/src/broken.ts"); + assertGetterInvariants(sf, sf); + } + finally { + api.close(); + } + }); +}); diff --git a/packages/typescript/test/sync/astnav.test.ts b/packages/typescript/test/sync/astnav.test.ts index 2e82fb944eca8..53ae18400d842 100644 --- a/packages/typescript/test/sync/astnav.test.ts +++ b/packages/typescript/test/sync/astnav.test.ts @@ -1,193 +1,193 @@ -// -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// !!! THIS FILE IS AUTO-GENERATED - DO NOT EDIT !!! -// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -// -// Source: test/async/astnav.test.ts -// Regenerate: npm run generate (from packages/typescript) -// -import { - findNextToken, - findPrecedingToken, - formatSyntaxKind, - getTokenAtPosition, - getTouchingPropertyName, -} from "@typescript/typescript/unstable/ast"; -import type { - Node, - SourceFile, -} from "@typescript/typescript/unstable/ast"; -import { createVirtualFileSystem } from "@typescript/typescript/unstable/fs"; -import { API } from "@typescript/typescript/unstable/sync"; -import assert from "node:assert"; -import { readFileSync } from "node:fs"; -import { resolve } from "node:path"; -import { - after, - before, - describe, - test, -} from "node:test"; -import { fileURLToPath } from "node:url"; - -// --------------------------------------------------------------------------- -// Go JSON baseline format -// --------------------------------------------------------------------------- - -interface TokenRun { - startPos: number; - endPos: number; - kind: string; - nodePos: number; - nodeEnd: number; -} - -/** - * Expand run-length encoded Go baseline into a per-position map. - */ -function expandBaseline(runs: TokenRun[]): Map { - const map = new Map(); - for (const run of runs) { - const entry = { kind: run.kind, pos: run.nodePos, end: run.nodeEnd }; - for (let p = run.startPos; p <= run.endPos; p++) { - map.set(p, entry); - } - } - return map; -} - -// --------------------------------------------------------------------------- -// Helpers -// --------------------------------------------------------------------------- - -/** Convert an astnav result node to a string-based token info for comparison. */ -function toTokenInfo(node: Node): { kind: string; pos: number; end: number; } { - return { - kind: formatSyntaxKind(node.kind), - pos: node.pos, - end: node.end, - }; -} - -// --------------------------------------------------------------------------- -// Test configuration -// --------------------------------------------------------------------------- - -const repoRoot = resolve(import.meta.dirname!, "..", "..", "..", ".."); -const testFile = resolve(repoRoot, "tsc/testdata/fixtures/services/mapCode.ts"); -const baselineDir = resolve(repoRoot, "tsc/testdata/baselines/reference/astnav"); - -// --------------------------------------------------------------------------- -// Tests -// --------------------------------------------------------------------------- - -describe("astnav", () => { - let fileText: string; - - try { - fileText = readFileSync(testFile, "utf-8"); - } - catch { - console.log("Skipping astnav tests: compiler fixtures not available"); - fileText = ""; - } - - if (!fileText) return; - - // Use the Go API to parse the file — the resulting SourceFile is already - // in our SyntaxKind/NodeFlags enum space with correct JSDoc structure. - let api: API; - let sourceFile: SourceFile; - - before(() => { - api = new API({ - cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), - fs: createVirtualFileSystem({ - "/tsconfig.json": JSON.stringify({ files: ["/src/testFile.ts"] }), - "/src/testFile.ts": fileText, - }), - }); - - const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); - const project = snapshot.getProject("/tsconfig.json")!; - const sf = project.program.getSourceFile("/src/testFile.ts"); - assert.ok(sf, "Failed to get source file from API"); - sourceFile = sf; - }); - - after(() => { - api.close(); - }); - - const testCases = [ - { - name: "getTokenAtPosition", - baselineFile: "GetTokenAtPosition.mapCode.ts.baseline.json", - fn: (sf: SourceFile, pos: number) => getTokenAtPosition(sf, pos) as Node | undefined, - }, - { - name: "getTouchingPropertyName", - baselineFile: "GetTouchingPropertyName.mapCode.ts.baseline.json", - fn: (sf: SourceFile, pos: number) => getTouchingPropertyName(sf, pos) as Node | undefined, - }, - { - name: "findPrecedingToken", - baselineFile: "FindPrecedingToken.mapCode.ts.baseline.json", - fn: (sf: SourceFile, pos: number) => findPrecedingToken(sf, pos), - }, - { - name: "findNextToken", - baselineFile: "FindNextToken.mapCode.ts.baseline.json", - fn: (sf: SourceFile, pos: number) => findNextToken(getTokenAtPosition(sf, pos), sf, sf), - }, - ]; - - for (const tc of testCases) { - test(tc.name, () => { - const baselinePath = resolve(baselineDir, tc.baselineFile); - const runs: TokenRun[] = JSON.parse(readFileSync(baselinePath, "utf-8")); - const expected = expandBaseline(runs); - - const failures: string[] = []; - - for (let pos = 0; pos < fileText.length; pos++) { - const node = tc.fn(sourceFile, pos); - const goExpected = expected.get(pos); - - if (!goExpected) continue; - - if (node === undefined) { - failures.push( - ` pos ${pos}: expected ${goExpected.kind} [${goExpected.pos}, ${goExpected.end}), got undefined`, - ); - if (failures.length >= 50) { - failures.push(" ... (truncated, too many failures)"); - break; - } - continue; - } - - const result = toTokenInfo(node); - if (result.kind !== goExpected.kind || result.pos !== goExpected.pos || result.end !== goExpected.end) { - failures.push( - ` pos ${pos}: expected ${goExpected.kind} [${goExpected.pos}, ${goExpected.end}), ` + - `got ${result.kind} [${result.pos}, ${result.end})`, - ); - if (failures.length >= 50) { - failures.push(" ... (truncated, too many failures)"); - break; - } - } - } - - console.log(` ${tc.name}: checked ${fileText.length} positions`); - - if (failures.length > 0) { - assert.fail( - `${tc.name}: ${failures.length} position(s) differ from Go baseline:\n` + - failures.join("\n"), - ); - } - }); - } -}); +// +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// !!! THIS FILE IS AUTO-GENERATED - DO NOT EDIT !!! +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// +// Source: test/async/astnav.test.ts +// Regenerate: npm run generate (from packages/typescript) +// +import { + findNextToken, + findPrecedingToken, + formatSyntaxKind, + getTokenAtPosition, + getTouchingPropertyName, +} from "@typescript/typescript/unstable/ast"; +import type { + Node, + SourceFile, +} from "@typescript/typescript/unstable/ast"; +import { createVirtualFileSystem } from "@typescript/typescript/unstable/fs"; +import { API } from "@typescript/typescript/unstable/sync"; +import assert from "node:assert"; +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { + after, + before, + describe, + test, +} from "node:test"; +import { fileURLToPath } from "node:url"; + +// --------------------------------------------------------------------------- +// Go JSON baseline format +// --------------------------------------------------------------------------- + +interface TokenRun { + startPos: number; + endPos: number; + kind: string; + nodePos: number; + nodeEnd: number; +} + +/** + * Expand run-length encoded Go baseline into a per-position map. + */ +function expandBaseline(runs: TokenRun[]): Map { + const map = new Map(); + for (const run of runs) { + const entry = { kind: run.kind, pos: run.nodePos, end: run.nodeEnd }; + for (let p = run.startPos; p <= run.endPos; p++) { + map.set(p, entry); + } + } + return map; +} + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +/** Convert an astnav result node to a string-based token info for comparison. */ +function toTokenInfo(node: Node): { kind: string; pos: number; end: number; } { + return { + kind: formatSyntaxKind(node.kind), + pos: node.pos, + end: node.end, + }; +} + +// --------------------------------------------------------------------------- +// Test configuration +// --------------------------------------------------------------------------- + +const repoRoot = resolve(import.meta.dirname!, "..", "..", "..", ".."); +const testFile = resolve(repoRoot, "tsc/testdata/fixtures/services/mapCode.ts"); +const baselineDir = resolve(repoRoot, "tsc/testdata/baselines/reference/astnav"); + +// --------------------------------------------------------------------------- +// Tests +// --------------------------------------------------------------------------- + +describe("astnav", () => { + let fileText: string; + + try { + fileText = readFileSync(testFile, "utf-8"); + } + catch { + console.log("Skipping astnav tests: compiler fixtures not available"); + fileText = ""; + } + + if (!fileText) return; + + // Use the Go API to parse the file — the resulting SourceFile is already + // in our SyntaxKind/NodeFlags enum space with correct JSDoc structure. + let api: API; + let sourceFile: SourceFile; + + before(() => { + api = new API({ + cwd: fileURLToPath(new URL("../../../../", import.meta.url).toString()), + fs: createVirtualFileSystem({ + "/tsconfig.json": JSON.stringify({ files: ["/src/testFile.ts"] }), + "/src/testFile.ts": fileText, + }), + }); + + const snapshot = api.updateSnapshot({ openProject: "/tsconfig.json" }); + const project = snapshot.getProject("/tsconfig.json")!; + const sf = project.program.getSourceFile("/src/testFile.ts"); + assert.ok(sf, "Failed to get source file from API"); + sourceFile = sf; + }); + + after(() => { + api.close(); + }); + + const testCases = [ + { + name: "getTokenAtPosition", + baselineFile: "GetTokenAtPosition.mapCode.ts.baseline.json", + fn: (sf: SourceFile, pos: number) => getTokenAtPosition(sf, pos) as Node | undefined, + }, + { + name: "getTouchingPropertyName", + baselineFile: "GetTouchingPropertyName.mapCode.ts.baseline.json", + fn: (sf: SourceFile, pos: number) => getTouchingPropertyName(sf, pos) as Node | undefined, + }, + { + name: "findPrecedingToken", + baselineFile: "FindPrecedingToken.mapCode.ts.baseline.json", + fn: (sf: SourceFile, pos: number) => findPrecedingToken(sf, pos), + }, + { + name: "findNextToken", + baselineFile: "FindNextToken.mapCode.ts.baseline.json", + fn: (sf: SourceFile, pos: number) => findNextToken(getTokenAtPosition(sf, pos), sf, sf), + }, + ]; + + for (const tc of testCases) { + test(tc.name, () => { + const baselinePath = resolve(baselineDir, tc.baselineFile); + const runs: TokenRun[] = JSON.parse(readFileSync(baselinePath, "utf-8")); + const expected = expandBaseline(runs); + + const failures: string[] = []; + + for (let pos = 0; pos < fileText.length; pos++) { + const node = tc.fn(sourceFile, pos); + const goExpected = expected.get(pos); + + if (!goExpected) continue; + + if (node === undefined) { + failures.push( + ` pos ${pos}: expected ${goExpected.kind} [${goExpected.pos}, ${goExpected.end}), got undefined`, + ); + if (failures.length >= 50) { + failures.push(" ... (truncated, too many failures)"); + break; + } + continue; + } + + const result = toTokenInfo(node); + if (result.kind !== goExpected.kind || result.pos !== goExpected.pos || result.end !== goExpected.end) { + failures.push( + ` pos ${pos}: expected ${goExpected.kind} [${goExpected.pos}, ${goExpected.end}), ` + + `got ${result.kind} [${result.pos}, ${result.end})`, + ); + if (failures.length >= 50) { + failures.push(" ... (truncated, too many failures)"); + break; + } + } + } + + console.log(` ${tc.name}: checked ${fileText.length} positions`); + + if (failures.length > 0) { + assert.fail( + `${tc.name}: ${failures.length} position(s) differ from Go baseline:\n` + + failures.join("\n"), + ); + } + }); + } +}); diff --git a/packages/typescript/test/types.d.ts b/packages/typescript/test/types.d.ts index e55d60d63ec2d..9d08c5a569815 100644 --- a/packages/typescript/test/types.d.ts +++ b/packages/typescript/test/types.d.ts @@ -1,2 +1,2 @@ -// tinybench fails to forward declare this so --lib dom isn't required -interface DOMHighResTimeStamp {} +// tinybench fails to forward declare this so --lib dom isn't required +interface DOMHighResTimeStamp {} diff --git a/packages/typescript/test/version.test.ts b/packages/typescript/test/version.test.ts index a2630f570a209..c742554719435 100644 --- a/packages/typescript/test/version.test.ts +++ b/packages/typescript/test/version.test.ts @@ -1,19 +1,19 @@ -import getExePath from "#getExePath"; -import { versionMajorMinor } from "@typescript/typescript"; -import assert from "node:assert"; -import { execFileSync } from "node:child_process"; -import { readFileSync } from "node:fs"; -import { test } from "node:test"; - -test("versionMajorMinor runtime and declaration match the compiler version", () => { - const compilerVersion = execFileSync(getExePath(), ["--version"], { encoding: "utf8" }) - .trim() - .replace(/^Version /, ""); - const majorMinor = compilerVersion.split(".", 2).join("."); - const declaration = readFileSync(new URL("../lib/version.d.cts", import.meta.url), "utf8"); - const declarationMatch = declaration.match(/versionMajorMinor = "([^"]+)"/); - - assert.strictEqual(versionMajorMinor, majorMinor); - assert.ok(declarationMatch, "versionMajorMinor declaration not found"); - assert.strictEqual(declarationMatch[1], majorMinor); -}); +import getExePath from "#getExePath"; +import { versionMajorMinor } from "@typescript/typescript"; +import assert from "node:assert"; +import { execFileSync } from "node:child_process"; +import { readFileSync } from "node:fs"; +import { test } from "node:test"; + +test("versionMajorMinor runtime and declaration match the compiler version", () => { + const compilerVersion = execFileSync(getExePath(), ["--version"], { encoding: "utf8" }) + .trim() + .replace(/^Version /, ""); + const majorMinor = compilerVersion.split(".", 2).join("."); + const declaration = readFileSync(new URL("../lib/version.d.cts", import.meta.url), "utf8"); + const declarationMatch = declaration.match(/versionMajorMinor = "([^"]+)"/); + + assert.strictEqual(versionMajorMinor, majorMinor); + assert.ok(declarationMatch, "versionMajorMinor declaration not found"); + assert.strictEqual(declarationMatch[1], majorMinor); +}); diff --git a/packages/typescript/test/wtf8.test.ts b/packages/typescript/test/wtf8.test.ts index 8fe8fcd36520c..dd4844164d169 100644 --- a/packages/typescript/test/wtf8.test.ts +++ b/packages/typescript/test/wtf8.test.ts @@ -1,44 +1,44 @@ -import assert from "node:assert"; -import { - describe, - test, -} from "node:test"; -import { Wtf8Decoder } from "../src/api/node/wtf8.ts"; - -describe("Wtf8Decoder", () => { - test("decodes standard UTF-8", () => { - const decoder = new Wtf8Decoder(); - assert.strictEqual(decoder.decode(new TextEncoder().encode("hello 🦀")), "hello 🦀"); - }); - - test("preserves WTF-8 encoded lone surrogates", () => { - const decoder = new Wtf8Decoder(); - const text = decoder.decode(Uint8Array.of( - 0xF0, - 0x9F, - 0xA6, - 0x80, - 0xED, - 0x9F, - 0xBF, - 0xED, - 0xA0, - 0x80, - 0xED, - 0xA0, - 0x81, - 0xED, - 0xB0, - 0x80, - 0xF0, - 0x9F, - 0xA6, - 0x80, - )); - - assert.deepStrictEqual( - Array.from({ length: text.length }, (_, i) => text.charCodeAt(i)), - [0xD83E, 0xDD80, 0xD7FF, 0xD800, 0xD801, 0xDC00, 0xD83E, 0xDD80], - ); - }); -}); +import assert from "node:assert"; +import { + describe, + test, +} from "node:test"; +import { Wtf8Decoder } from "../src/api/node/wtf8.ts"; + +describe("Wtf8Decoder", () => { + test("decodes standard UTF-8", () => { + const decoder = new Wtf8Decoder(); + assert.strictEqual(decoder.decode(new TextEncoder().encode("hello 🦀")), "hello 🦀"); + }); + + test("preserves WTF-8 encoded lone surrogates", () => { + const decoder = new Wtf8Decoder(); + const text = decoder.decode(Uint8Array.of( + 0xF0, + 0x9F, + 0xA6, + 0x80, + 0xED, + 0x9F, + 0xBF, + 0xED, + 0xA0, + 0x80, + 0xED, + 0xA0, + 0x81, + 0xED, + 0xB0, + 0x80, + 0xF0, + 0x9F, + 0xA6, + 0x80, + )); + + assert.deepStrictEqual( + Array.from({ length: text.length }, (_, i) => text.charCodeAt(i)), + [0xD83E, 0xDD80, 0xD7FF, 0xD800, 0xD801, 0xDC00, 0xD83E, 0xDD80], + ); + }); +}); diff --git a/packages/vscode-typescript-nightly/LICENSE b/packages/vscode-typescript-nightly/LICENSE index 8746124b27791..edc24fd6e1409 100644 --- a/packages/vscode-typescript-nightly/LICENSE +++ b/packages/vscode-typescript-nightly/LICENSE @@ -1,55 +1,55 @@ -Apache License - -Version 2.0, January 2004 - -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of this License; and - -You must cause any modified files to carry prominent notices stating that You changed the files; and - -You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - -If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and + +You must cause any modified files to carry prominent notices stating that You changed the files; and + +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/packages/vscode-typescript/LICENSE b/packages/vscode-typescript/LICENSE index 8746124b27791..edc24fd6e1409 100644 --- a/packages/vscode-typescript/LICENSE +++ b/packages/vscode-typescript/LICENSE @@ -1,55 +1,55 @@ -Apache License - -Version 2.0, January 2004 - -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of this License; and - -You must cause any modified files to carry prominent notices stating that You changed the files; and - -You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - -If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and + +You must cause any modified files to carry prominent notices stating that You changed the files; and + +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/packages/vscode-typescript/src/activeJsTsEditorTracker.ts b/packages/vscode-typescript/src/activeJsTsEditorTracker.ts index 5fc065e0202ac..c18d6a97256ce 100644 --- a/packages/vscode-typescript/src/activeJsTsEditorTracker.ts +++ b/packages/vscode-typescript/src/activeJsTsEditorTracker.ts @@ -1,84 +1,84 @@ -import * as vscode from "vscode"; -import { - disabledSchemes, - isJsConfigOrTsConfigFileName, - isSupportedLanguageMode, -} from "./util"; - -/** - * Tracks the active JS/TS editor. - * - * This tries to handle the case where the user focuses in the output view / debug console. - * When this happens, we want to treat the last real focused editor as the active editor, - * instead of using `vscode.window.activeTextEditor`. - */ -export class ActiveJsTsEditorTracker implements vscode.Disposable { - private _activeJsTsEditor: vscode.TextEditor | undefined; - private disposables: vscode.Disposable[] = []; - - private readonly _onDidChangeActiveJsTsEditor = new vscode.EventEmitter(); - public readonly onDidChangeActiveJsTsEditor = this._onDidChangeActiveJsTsEditor.event; - - constructor() { - this.disposables.push(this._onDidChangeActiveJsTsEditor); - this.disposables.push(vscode.window.onDidChangeActiveTextEditor(() => this.update())); - this.disposables.push(vscode.window.onDidChangeVisibleTextEditors(() => this.update())); - this.disposables.push(vscode.window.tabGroups.onDidChangeTabGroups(() => this.update())); - this.update(); - } - - public get activeJsTsEditor(): vscode.TextEditor | undefined { - return this._activeJsTsEditor; - } - - private update(): void { - const editorCandidates = this.getEditorCandidatesForActiveTab(); - const newActiveJsTsEditor = editorCandidates.find(editor => this.isManagedFile(editor)); - if (newActiveJsTsEditor !== undefined && this._activeJsTsEditor !== newActiveJsTsEditor) { - this._activeJsTsEditor = newActiveJsTsEditor; - this._onDidChangeActiveJsTsEditor.fire(this._activeJsTsEditor); - } - } - - private getEditorCandidatesForActiveTab(): vscode.TextEditor[] { - const tab = vscode.window.tabGroups.activeTabGroup.activeTab; - if (!tab) { - return []; - } - - // Basic text editor tab - if (tab.input instanceof vscode.TabInputText) { - const inputUriString = tab.input.uri.toString(); - const editor = vscode.window.visibleTextEditors.find(editor => { - return editor.document.uri.toString() === inputUriString - && editor.viewColumn === tab.group.viewColumn; - }); - return editor ? [editor] : []; - } - - // Diff editor tab - if (tab.input instanceof vscode.TabInputTextDiff) { - const original = tab.input.original; - const modified = tab.input.modified; - return [vscode.window.activeTextEditor, ...vscode.window.visibleTextEditors] - .filter((editor): editor is vscode.TextEditor => editor !== undefined) - .filter(editor => { - return (editor.document.uri.toString() === original.toString() || editor.document.uri.toString() === modified.toString()) - && editor.viewColumn === undefined; - }); - } - - return []; - } - - private isManagedFile(editor: vscode.TextEditor): boolean { - if (disabledSchemes.has(editor.document.uri.scheme)) { - return false; - } - return isSupportedLanguageMode(editor.document) || isJsConfigOrTsConfigFileName(editor.document.fileName); - } - - dispose(): void { - this.disposables.forEach(d => d.dispose()); - } -} +import * as vscode from "vscode"; +import { + disabledSchemes, + isJsConfigOrTsConfigFileName, + isSupportedLanguageMode, +} from "./util"; + +/** + * Tracks the active JS/TS editor. + * + * This tries to handle the case where the user focuses in the output view / debug console. + * When this happens, we want to treat the last real focused editor as the active editor, + * instead of using `vscode.window.activeTextEditor`. + */ +export class ActiveJsTsEditorTracker implements vscode.Disposable { + private _activeJsTsEditor: vscode.TextEditor | undefined; + private disposables: vscode.Disposable[] = []; + + private readonly _onDidChangeActiveJsTsEditor = new vscode.EventEmitter(); + public readonly onDidChangeActiveJsTsEditor = this._onDidChangeActiveJsTsEditor.event; + + constructor() { + this.disposables.push(this._onDidChangeActiveJsTsEditor); + this.disposables.push(vscode.window.onDidChangeActiveTextEditor(() => this.update())); + this.disposables.push(vscode.window.onDidChangeVisibleTextEditors(() => this.update())); + this.disposables.push(vscode.window.tabGroups.onDidChangeTabGroups(() => this.update())); + this.update(); + } + + public get activeJsTsEditor(): vscode.TextEditor | undefined { + return this._activeJsTsEditor; + } + + private update(): void { + const editorCandidates = this.getEditorCandidatesForActiveTab(); + const newActiveJsTsEditor = editorCandidates.find(editor => this.isManagedFile(editor)); + if (newActiveJsTsEditor !== undefined && this._activeJsTsEditor !== newActiveJsTsEditor) { + this._activeJsTsEditor = newActiveJsTsEditor; + this._onDidChangeActiveJsTsEditor.fire(this._activeJsTsEditor); + } + } + + private getEditorCandidatesForActiveTab(): vscode.TextEditor[] { + const tab = vscode.window.tabGroups.activeTabGroup.activeTab; + if (!tab) { + return []; + } + + // Basic text editor tab + if (tab.input instanceof vscode.TabInputText) { + const inputUriString = tab.input.uri.toString(); + const editor = vscode.window.visibleTextEditors.find(editor => { + return editor.document.uri.toString() === inputUriString + && editor.viewColumn === tab.group.viewColumn; + }); + return editor ? [editor] : []; + } + + // Diff editor tab + if (tab.input instanceof vscode.TabInputTextDiff) { + const original = tab.input.original; + const modified = tab.input.modified; + return [vscode.window.activeTextEditor, ...vscode.window.visibleTextEditors] + .filter((editor): editor is vscode.TextEditor => editor !== undefined) + .filter(editor => { + return (editor.document.uri.toString() === original.toString() || editor.document.uri.toString() === modified.toString()) + && editor.viewColumn === undefined; + }); + } + + return []; + } + + private isManagedFile(editor: vscode.TextEditor): boolean { + if (disabledSchemes.has(editor.document.uri.scheme)) { + return false; + } + return isSupportedLanguageMode(editor.document) || isJsConfigOrTsConfigFileName(editor.document.fileName); + } + + dispose(): void { + this.disposables.forEach(d => d.dispose()); + } +} diff --git a/packages/vscode-typescript/src/client.ts b/packages/vscode-typescript/src/client.ts index b8bd17df8540a..de4d7d7dd1736 100644 --- a/packages/vscode-typescript/src/client.ts +++ b/packages/vscode-typescript/src/client.ts @@ -1,746 +1,746 @@ -import * as vscode from "vscode"; - -import { CancellationToken } from "vscode-languageclient"; -import { - ClientCapabilities, - CloseAction, - CloseHandlerResult, - ErrorAction, - ErrorHandler, - ErrorHandlerResult, - LanguageClient, - LanguageClientOptions, - Message, - MessageSignature, - NotebookDocumentFilter, - ServerOptions, - StaticFeature, - TextDocumentFilter, - TransportKind, -} from "vscode-languageclient/node"; - -import { codeLensShowLocationsCommandName } from "./commands"; -import { - configurationMiddleware, - sendNotificationMiddleware, -} from "./configurationMiddleware"; -import type { SerializedContentMapperContribution } from "./contentMapperContributions"; -import { registerMultiDocumentHighlightFeature } from "./languageFeatures/documentHighlight"; -import { registerHoverFeature } from "./languageFeatures/hover"; -import { registerOnAutoInsertFeature } from "./languageFeatures/onAutoInsert"; -import { registerSourceDefinitionFeature } from "./languageFeatures/sourceDefinition"; -import * as tr from "./telemetryReporting"; -import { - contentMappersEnabled, - ExeInfo, - getExe, - jsTsLanguageModes, - languageClientName, - readNativePreviewConfig, -} from "./util"; -import { getLanguageForUri } from "./util"; -import { workspaceSymbolSendRequestMiddleware } from "./workspaceSymbolMiddleware"; - -// Registration IDs the server uses for content mapper capabilities all share this prefix (see -// RegisterContentMapperExtensions in internal/lsp/server.go). The extension watches for these dynamic -// registrations to learn which file extensions are content-mapped. -const contentMapperRegistrationPrefix = "content-mapper-"; - -// ContentMapperRegisterOptions is the subset of a dynamic registration's options that our server sends -// for content mapper capabilities: a document selector of simple glob-pattern filters (see -// RegisterContentMapperExtensions in internal/lsp/server.go). -interface ContentMapperRegisterOptions { - documentSelector?: ReadonlyArray<{ pattern: string; }>; -} - -// extractPatternFilters reuses the server's glob-pattern filters so the extension's custom providers match -// the same content-mapped files. -function extractPatternFilters(registerOptions: ContentMapperRegisterOptions | undefined): vscode.DocumentFilter[] { - return (registerOptions?.documentSelector ?? []).map(({ pattern }) => ({ pattern })); -} - -export class Client implements vscode.Disposable { - private outputChannel: vscode.LogOutputChannel; - private initializedEventEmitter: vscode.EventEmitter; - private telemetryReporter: tr.TelemetryReporter; - - private documentSelector: Array<{ scheme: string; language: string; }>; - private clientOptions: LanguageClientOptions; - private client?: LanguageClient; - - private isDisposed = false; - private isStopping = false; - private disposables: vscode.Disposable[] = []; - isInitialized = false; - - // Document filters for content-mapped file extensions, keyed by the server's registration ID. These - // augment the static jsTs document selector so the extension's custom language-feature providers - // (hover, multi-document highlight, on-auto-insert) also cover content-mapped files. - private contentMapperFiltersById = new Map(); - // Disposables for the custom providers registered against the current (selector-scoped) document set. - // Re-registered whenever the set of content-mapped extensions changes. - private selectorScopedFeatures: vscode.Disposable[] = []; - - private exe: ExeInfo | undefined; - private errorHandler: ReportingErrorHandler; - - constructor( - outputChannel: vscode.LogOutputChannel, - initializedEventEmitter: vscode.EventEmitter, - telemetryReporter: tr.TelemetryReporter, - ) { - this.outputChannel = outputChannel; - this.initializedEventEmitter = initializedEventEmitter; - this.telemetryReporter = telemetryReporter; - this.errorHandler = new ReportingErrorHandler(this.telemetryReporter, 5); - - // Monkey-patch the output channel's error method to capture recent stderr lines. - // When the server crashes, vscode-languageclient pipes stderr to outputChannel.error(), - // so the error handler can include the last N lines in crash telemetry. - const originalError = this.outputChannel.error.bind(this.outputChannel); - this.outputChannel.error = (...args: Parameters) => { - originalError(...args); - this.errorHandler.pushStderrLine(String(args[0])); - }; - - this.documentSelector = [ - ...jsTsLanguageModes.map(language => ({ scheme: "file", language })), - ...jsTsLanguageModes.map(language => ({ scheme: "untitled", language })), - ]; - this.clientOptions = { - documentSelector: this.documentSelector, - outputChannel: this.outputChannel, - initializationOptions: { - codeLensShowLocationsCommandName, - enableTelemetry: true, - logVerbosity: this.outputChannel.logLevel, - runExternalCode: contentMappersEnabled(), - }, - errorHandler: this.errorHandler, - middleware: { - workspace: { - ...configurationMiddleware, - didChangeWatchedFile: (event, next) => { - if (this.isStopping || this.isDisposed) { - return Promise.resolve(); - } - return next(event); - }, - }, - sendNotification: sendNotificationMiddleware, - sendRequest: workspaceSymbolSendRequestMiddleware, - provideHover: () => undefined, - handleRegisterCapability: async (params, next) => { - await next(params, CancellationToken.None); - if (this.trackContentMapperRegistrations(params.registrations)) { - this.registerSelectorScopedFeatures(); - } - }, - handleUnregisterCapability: async (params, next) => { - await next(params, CancellationToken.None); - if (this.trackContentMapperUnregistrations(params.unregisterations)) { - this.registerSelectorScopedFeatures(); - } - }, - }, - diagnosticCollectionName: "typescript-push", - diagnosticPullOptions: { - onChange: true, - onSave: true, - onTabs: true, - match(documentSelector, resource) { - // This function is called when diagnostics are requested but - // only the URI itself is known (e.g. open but not yet focused tabs), - // so will not be present in vscode.workspace.textDocuments. - // See if this file matches without consulting vscode.languages.match - // (which requires a TextDocument). - - const language = getLanguageForUri(resource); - - for (const selector of documentSelector) { - if (typeof selector === "string") { - if (selector === language) { - return true; - } - continue; - } - if (NotebookDocumentFilter.is(selector)) { - continue; - } - if (TextDocumentFilter.is(selector)) { - if (selector.language !== undefined && selector.language !== language) { - continue; - } - - if (selector.scheme !== undefined && selector.scheme !== resource.scheme) { - continue; - } - - if (selector.pattern !== undefined) { - // VS Code's full glob matcher is not available via the API - // (microsoft/vscode#237304), but content mapper registrations only ever - // use simple "**/*" patterns, so match those by suffix. Any other - // pattern falls through to the next selector. - const glob = typeof selector.pattern === "string" ? selector.pattern : selector.pattern.pattern; - const globPrefix = "**/*"; - if (glob.startsWith(globPrefix) && resource.path.endsWith(glob.slice(globPrefix.length))) { - return true; - } - continue; - } - - return true; - } - } - - return false; - }, - }, - }; - } - - async start(exe: ExeInfo): Promise { - this.isStopping = false; - this.exe = exe; - this.outputChannel.appendLine(`Resolved to ${this.exe.path}`); - this.telemetryReporter.sendTelemetryEvent("languageServer.start", { - version: this.exe.version, - }); - - const pprofDir = readNativePreviewConfig("server.pprofDir", undefined) - ?? readNativePreviewConfig("pprofDir", undefined); - const pprofArgs = pprofDir ? ["--pprofDir", pprofDir] : []; - - const flakesFlag = vscode.workspace - .getConfiguration("js/ts") - .get<"panic" | "log" | "never" | "auto">("server.trackFlakyDiagnostics", "auto"); - const effectiveflakesFlag = flakesFlag === "auto" ? (isInsiders() ? "log" : "never") : flakesFlag; - - const goMemLimit = readNativePreviewConfig("server.goMemLimit", undefined) - ?? readNativePreviewConfig("goMemLimit", undefined); - const env = { ...process.env }; - if (goMemLimit) { - // Keep this regex aligned with the pattern in package.json. - if (/^[0-9]+(([KMGT]i)?B)?$/.test(goMemLimit)) { - this.outputChannel.appendLine(`Setting GOMEMLIMIT=${goMemLimit}`); - env.GOMEMLIMIT = goMemLimit; - } - else { - this.outputChannel.error(`Invalid goMemLimit: ${goMemLimit}. Must be a valid memory limit (e.g., '2048MiB', '4GiB'). Not overriding GOMEMLIMIT.`); - } - } - - const serverOptions: ServerOptions = { - run: { - command: this.exe.path, - args: ["--lsp", ...pprofArgs], - transport: TransportKind.stdio, - options: { env }, - }, - debug: { - command: this.exe.path, - args: ["--lsp", ...pprofArgs], - transport: TransportKind.stdio, - options: { env }, - }, - }; - - // Refresh options in case they changed between construction and start. - this.clientOptions.initializationOptions.logVerbosity = this.outputChannel.logLevel; - this.clientOptions.initializationOptions.runExternalCode = contentMappersEnabled(); - this.clientOptions.initializationOptions.trackFlakyDiagnostics = effectiveflakesFlag !== "never" ? (effectiveflakesFlag === "panic" ? 2 : 1) : 0; - - this.client = new NativePreviewLanguageClient( - "js/ts", - languageClientName, - serverOptions, - this.clientOptions, - ); - - // Register a static feature to advertise verbosityLevel support in hover capabilities. - this.client.registerFeature( - { - fillClientCapabilities(capabilities: ClientCapabilities): void { - capabilities.experimental = typeof capabilities.experimental === "object" && capabilities.experimental !== null - ? capabilities.experimental - : {}; - (capabilities.experimental as { hoverVerbosityLevel?: boolean; }).hoverVerbosityLevel = true; - }, - initialize(): void {}, - getState() { - return { kind: "static" as const }; - }, - clear(): void {}, - } satisfies StaticFeature, - ); - - this.outputChannel.appendLine(vscode.l10n.t(`Starting language server...`)); - await this.client.start(); - this.isInitialized = true; - this.initializedEventEmitter.fire(); - - // Send the initial log verbosity level to the server, and update it - // whenever the output channel's log level changes (via the gear icon). - this.sendLogVerbosity(); - const logLevelListener = this.outputChannel.onDidChangeLogLevel(() => { - this.sendLogVerbosity(); - }); - - type TelemetryData = { - eventName: string; - telemetryPurpose: "usage" | "error"; - properties?: Record; - measurements?: Record; - }; - - const serverTelemetryListener = this.client.onTelemetry((d: TelemetryData) => { - switch (d.telemetryPurpose) { - case "usage": - this.telemetryReporter.sendTelemetryEventUntyped(d.eventName, d.properties, d.measurements); - break; - case "error": - this.telemetryReporter.sendTelemetryErrorEventUntyped(d.eventName, d.properties, d.measurements); - break; - default: - const _: never = d.telemetryPurpose; - this.telemetryReporter.sendTelemetryErrorEvent("languageServer.unexpectedTelemetryPurpose", { - telemetryPurpose: String(d.telemetryPurpose), - }); - break; - } - }); - - this.disposables.push( - logLevelListener, - serverTelemetryListener, - registerSourceDefinitionFeature(this.client), - registerOnAutoInsertFeature(this.documentSelector, this.client), - ); - // Register the selector-scoped custom providers (hover, multi-document highlight). These start - // scoped to the static jsTs selector and expand as content-mapped extensions register. - this.registerSelectorScopedFeatures(); - } - - // trackContentMapperRegistrations records the content-mapped document filters carried by the server's - // dynamic capability registrations. Returns whether the known set of content-mapper filters changed. - private trackContentMapperRegistrations(registrations: ReadonlyArray<{ id: string; registerOptions?: ContentMapperRegisterOptions; }>): boolean { - let changed = false; - for (const registration of registrations) { - if (!registration.id.startsWith(contentMapperRegistrationPrefix)) { - continue; - } - const filters = extractPatternFilters(registration.registerOptions); - if (filters.length > 0) { - this.contentMapperFiltersById.set(registration.id, filters); - changed = true; - } - else if (this.contentMapperFiltersById.delete(registration.id)) { - changed = true; - } - } - return changed; - } - - // trackContentMapperUnregistrations forgets the filters for any content-mapper registration the server - // has removed. Returns whether the known set of content-mapper filters changed. - private trackContentMapperUnregistrations(unregistrations: ReadonlyArray<{ id: string; }>): boolean { - let changed = false; - for (const unregistration of unregistrations) { - if (this.contentMapperFiltersById.delete(unregistration.id)) { - changed = true; - } - } - return changed; - } - - // selectorScopedDocumentSelector is the static jsTs selector plus every active content-mapper filter, - // deduplicated. The custom providers are registered against it so they also cover content-mapped files. - private selectorScopedDocumentSelector(): vscode.DocumentSelector { - const seen = new Set(); - const filters: vscode.DocumentFilter[] = []; - for (const list of this.contentMapperFiltersById.values()) { - for (const filter of list) { - const key = typeof filter.pattern === "string" ? filter.pattern : JSON.stringify(filter.pattern); - if (!seen.has(key)) { - seen.add(key); - filters.push(filter); - } - } - } - return [...this.documentSelector, ...filters]; - } - - // registerSelectorScopedFeatures (re)registers the custom language-feature providers whose scope must - // track the set of content-mapped extensions. Existing registrations are disposed first. - private registerSelectorScopedFeatures(): void { - for (const disposable of this.selectorScopedFeatures.splice(0)) { - disposable.dispose(); - } - if (!this.client || this.isStopping || this.isDisposed) { - return; - } - const selector = this.selectorScopedDocumentSelector(); - this.selectorScopedFeatures.push( - registerMultiDocumentHighlightFeature(selector, this.client), - registerHoverFeature(selector, this.client), - ); - } - - async stop(): Promise { - if (this.isDisposed) { - return; - } - this.isStopping = true; - this.isInitialized = false; - for (const disposable of this.selectorScopedFeatures.splice(0)) { - disposable.dispose(); - } - this.contentMapperFiltersById.clear(); - const disposables = this.disposables.splice(0); - await Promise.all(disposables.map(d => d.dispose())); - await this.client?.stop(); - } - - async dispose(): Promise { - if (this.isDisposed) { - return; - } - this.isDisposed = true; - this.isStopping = true; - this.isInitialized = false; - for (const disposable of this.selectorScopedFeatures.splice(0)) { - disposable.dispose(); - } - this.contentMapperFiltersById.clear(); - const disposables = this.disposables.splice(0); - await Promise.all(disposables.map(d => d.dispose())); - await this.client?.dispose(); - } - - getCurrentExe(): { path: string; version: string; } | undefined { - return this.exe; - } - - get serverPid(): number | undefined { - return (this.client as any)?._serverProcess?.pid; - } - - /** - * Initialize an API session and return the socket path for connecting. - * This allows other extensions to get a direct connection to the API server. - */ - async initializeAPISession(pipe?: string): Promise<{ sessionId: string; pipe: string; }> { - if (!this.client) { - throw new Error(vscode.l10n.t("Language client is not initialized")); - } - return this.client.sendRequest<{ sessionId: string; pipe: string; }>("custom/initializeAPISession", { pipe }); - } - - /** - * Restart the language server if the executable path has not changed. - * Returns true if a restart was performed. - */ - async tryRestart(context: vscode.ExtensionContext): Promise { - if (!this.client) { - return Promise.reject(new Error(vscode.l10n.t("Language client is not initialized"))); - } - this.isStopping = false; - const exe = await getExe(context); - if (exe.path !== this.exe?.path) { - return false; - } - - this.isInitialized = false; - this.outputChannel.appendLine(vscode.l10n.t("Restarting language server...")); - try { - await this.client.restart(); - } - catch (err) { - this.outputChannel.appendLine(vscode.l10n.t(`Graceful shutdown failed, forcing restart: {0}`, String(err))); - await this.client.start(); - } - this.isInitialized = true; - this.initializedEventEmitter.fire(); - return true; - } - - // Developer/debugging methods - - private sendLogVerbosity(): void { - if (!this.client) { - return; - } - this.client.sendNotification("custom/setLogVerbosity", { - verbosity: this.outputChannel.logLevel, - }); - } - - async runGC(): Promise { - if (!this.client) { - throw new Error(vscode.l10n.t("Language client is not initialized")); - } - await this.client.sendRequest("custom/runGC"); - } - - async saveHeapProfile(dir: string): Promise { - if (!this.client) { - throw new Error(vscode.l10n.t("Language client is not initialized")); - } - const result = await this.client.sendRequest<{ file: string; }>("custom/saveHeapProfile", { dir }); - return result.file; - } - - async saveAllocProfile(dir: string): Promise { - if (!this.client) { - throw new Error(vscode.l10n.t("Language client is not initialized")); - } - const result = await this.client.sendRequest<{ file: string; }>("custom/saveAllocProfile", { dir }); - return result.file; - } - - async startCPUProfile(dir: string): Promise { - if (!this.client) { - throw new Error(vscode.l10n.t("Language client is not initialized")); - } - await this.client.sendRequest("custom/startCPUProfile", { dir }); - } - - async stopCPUProfile(): Promise { - if (!this.client) { - throw new Error(vscode.l10n.t("Language client is not initialized")); - } - const result = await this.client.sendRequest<{ file: string; }>("custom/stopCPUProfile"); - return result.file; - } - - async getProjectInfo(uri: string, token?: vscode.CancellationToken): Promise<{ configFilePath: string; }> { - if (!this.client) { - throw new Error(vscode.l10n.t("Language client is not initialized")); - } - return this.client.sendRequest<{ configFilePath: string; }>("custom/projectInfo", { - textDocument: { uri }, - }, token); - } - - async setContentMapperContributions(contributions: readonly SerializedContentMapperContribution[], openDocuments: readonly vscode.Uri[]): Promise { - if (!this.client || !this.isInitialized) { - return; - } - await this.client.sendRequest("custom/setContentMapperContributions", { - contributions: contentMappersEnabled() ? contributions : [], - openDocuments: openDocuments.map(uri => ({ uri: uri.toString() })), - }); - } -} - -// Returns true when running on a VS Code Insiders build. -function isInsiders(): boolean { - return vscode.env.uriScheme === "vscode-insiders"; -} - -// LanguageClient subclass that lets the user control whether a failed request -// surfaces an error notification, via the `js/ts.server.showFailedResponses` setting. -class NativePreviewLanguageClient extends LanguageClient { - override handleFailedRequest( - type: MessageSignature, - token: vscode.CancellationToken | undefined, - error: unknown, - defaultValue: T, - showNotification: boolean = true, - throwOnCancel: boolean = false, - ): T { - const setting = vscode.workspace - .getConfiguration("js/ts") - .get<"always" | "never" | "auto">("server.showFailedResponses", "auto"); - const effectiveSetting = setting === "auto" ? (isInsiders() ? "always" : "never") : setting; - - let effectiveShowNotification = showNotification; - switch (effectiveSetting) { - case "never": - effectiveShowNotification = false; - break; - default: - // Use the default behavior (showNotification) for "always" and any unrecognized values. - break; - } - - return super.handleFailedRequest( - type, - token, - error, - defaultValue, - effectiveShowNotification, - throwOnCancel, - ); - } -} - -// Adapted from the default error handler in vscode-languageclient. -class ReportingErrorHandler implements ErrorHandler { - telemetryReporter: tr.TelemetryReporter; - maxRestartCount: number; - restarts: number[]; - private stderrBuffer: string[] = []; - private capturingPanic = false; - private static readonly maxStderrLines = 40; - private static readonly maxStderrLength = 8192; - - constructor(telemetryReporter: tr.TelemetryReporter, maxRestartCount: number) { - this.telemetryReporter = telemetryReporter; - this.maxRestartCount = maxRestartCount; - this.restarts = []; - } - - pushStderrLine(line: string): void { - for (const l of line.split("\n")) { - if (!this.capturingPanic) { - if (/^panic:/.test(l.trimStart())) { - // Clear any stale data from a previous session/panic. - this.stderrBuffer = []; - this.capturingPanic = true; - } - else { - continue; - } - } - if (this.stderrBuffer.length < ReportingErrorHandler.maxStderrLines) { - this.stderrBuffer.push(l); - } - else { - this.capturingPanic = false; - } - } - } - - private consumeStderrBuffer(): string { - const raw = this.stderrBuffer.join("\n"); - this.stderrBuffer = []; - this.capturingPanic = false; - return sanitizeStderr(raw).slice(0, ReportingErrorHandler.maxStderrLength); - } - - error(_error: Error, _message: Message | undefined, count: number | undefined): ErrorHandlerResult | Promise { - let errorAction = ErrorAction.Shutdown; - if (count && count <= 3) { - errorAction = ErrorAction.Continue; - } - - let actionString = ""; - switch (errorAction) { - case ErrorAction.Continue: - actionString = "continue"; - break; - case ErrorAction.Shutdown: - actionString = "shutdown"; - break; - default: - const _: never = errorAction; - } - this.telemetryReporter.sendTelemetryErrorEvent("languageServer.connectionError", { - resultingAction: actionString, - }); - - return { action: errorAction }; - } - - closed(): CloseHandlerResult | Promise { - let resultingAction: CloseAction; - - this.restarts.push(Date.now()); - if (this.restarts.length <= this.maxRestartCount) { - resultingAction = CloseAction.Restart; - } - else { - const diff = this.restarts[this.restarts.length - 1] - this.restarts[0]; - if (diff <= 3 * 60 * 1000) { - resultingAction = CloseAction.DoNotRestart; - } - else { - this.restarts.shift(); - resultingAction = CloseAction.Restart; - } - } - - let actionString = ""; - switch (resultingAction) { - case CloseAction.DoNotRestart: - actionString = "doNotRestart"; - break; - case CloseAction.Restart: - actionString = "restart"; - break; - default: - const _: never = resultingAction; - } - const lastStderr = this.consumeStderrBuffer(); - this.telemetryReporter.sendTelemetryErrorEvent("languageServer.connectionClosed", { - resultingAction: actionString, - lastStderr, - }); - - if (resultingAction === CloseAction.DoNotRestart) { - return { - action: resultingAction, - message: vscode.l10n.t(`The TypeScript language server crashed {0} times in the last 3 minutes. The server will not be restarted. See the output for more information.`, String(this.maxRestartCount + 1)), - }; - } - - return { action: resultingAction }; - } -} - -// Matches the server-side sanitizeStackTrace in internal/lsp/stack_sanitizer.go. -// Strips file path prefixes that may contain PII and redacts frames outside of our module. -const genericSecretKeywordRegex = /\b(key|token|signature|sig|pwd)([(\[.|])/gi; - -function sanitizeStderr(stderr: string): string { - if (!stderr) { - return ""; - } - return stderr.split("\n").map(sanitizeStderrLine).join("\n"); -} - -function sanitizeStderrLine(line: string): string { - // Keep "goroutine N [status]:" headers as-is. - if (/^goroutine \d+/.test(line)) { - return line; - } - // Redact the panic message itself — assert messages may contain user data. - // Keep only "panic:" as a marker. - if (/^panic:/.test(line.trimStart())) { - return "panic: (REDACTED)"; - } - // Keep "Server process exited" messages from vscode-languageclient. - if (line.includes("Server process exited")) { - return line; - } - - const leadingWhitespace = line.match(/^(\s*)/)?.[1] ?? ""; - - // Stack frame file path lines look like: \t/full/path/to/file.go:123 +0x40 - // Function lines look like: github.com/microsoft/TypeScript/tsc/internal/foo.Bar(...) - const ourModuleMarker = "TypeScript/tsc/"; - const idx = line.indexOf(ourModuleMarker); - if (idx >= 0) { - let relevantPart = line.slice(idx); - // Strip hex offset suffixes like " +0x40" - relevantPart = relevantPart.replace(/ \+0x[0-9a-fA-F]+$/, ""); - // Strip " in goroutine N" suffixes - relevantPart = relevantPart.replace(/ in goroutine \d+$/, ""); - // Strip function arguments (keep parens empty) - relevantPart = relevantPart.replace(/\([^)]*\)$/, "()"); - // Replace / with |> to defeat path-based secret detection - relevantPart = relevantPart.replace(/\//g, "|>"); - // Defeat generic secret keyword regex - relevantPart = relevantPart.replace(genericSecretKeywordRegex, "$1X_X$2"); - return leadingWhitespace + relevantPart; - } - - // Preserve completely blank lines. - if (line.trim() === "") { - return ""; - } - - // Non-internal frames get fully redacted. - return leadingWhitespace + "(REDACTED)"; -} +import * as vscode from "vscode"; + +import { CancellationToken } from "vscode-languageclient"; +import { + ClientCapabilities, + CloseAction, + CloseHandlerResult, + ErrorAction, + ErrorHandler, + ErrorHandlerResult, + LanguageClient, + LanguageClientOptions, + Message, + MessageSignature, + NotebookDocumentFilter, + ServerOptions, + StaticFeature, + TextDocumentFilter, + TransportKind, +} from "vscode-languageclient/node"; + +import { codeLensShowLocationsCommandName } from "./commands"; +import { + configurationMiddleware, + sendNotificationMiddleware, +} from "./configurationMiddleware"; +import type { SerializedContentMapperContribution } from "./contentMapperContributions"; +import { registerMultiDocumentHighlightFeature } from "./languageFeatures/documentHighlight"; +import { registerHoverFeature } from "./languageFeatures/hover"; +import { registerOnAutoInsertFeature } from "./languageFeatures/onAutoInsert"; +import { registerSourceDefinitionFeature } from "./languageFeatures/sourceDefinition"; +import * as tr from "./telemetryReporting"; +import { + contentMappersEnabled, + ExeInfo, + getExe, + jsTsLanguageModes, + languageClientName, + readNativePreviewConfig, +} from "./util"; +import { getLanguageForUri } from "./util"; +import { workspaceSymbolSendRequestMiddleware } from "./workspaceSymbolMiddleware"; + +// Registration IDs the server uses for content mapper capabilities all share this prefix (see +// RegisterContentMapperExtensions in internal/lsp/server.go). The extension watches for these dynamic +// registrations to learn which file extensions are content-mapped. +const contentMapperRegistrationPrefix = "content-mapper-"; + +// ContentMapperRegisterOptions is the subset of a dynamic registration's options that our server sends +// for content mapper capabilities: a document selector of simple glob-pattern filters (see +// RegisterContentMapperExtensions in internal/lsp/server.go). +interface ContentMapperRegisterOptions { + documentSelector?: ReadonlyArray<{ pattern: string; }>; +} + +// extractPatternFilters reuses the server's glob-pattern filters so the extension's custom providers match +// the same content-mapped files. +function extractPatternFilters(registerOptions: ContentMapperRegisterOptions | undefined): vscode.DocumentFilter[] { + return (registerOptions?.documentSelector ?? []).map(({ pattern }) => ({ pattern })); +} + +export class Client implements vscode.Disposable { + private outputChannel: vscode.LogOutputChannel; + private initializedEventEmitter: vscode.EventEmitter; + private telemetryReporter: tr.TelemetryReporter; + + private documentSelector: Array<{ scheme: string; language: string; }>; + private clientOptions: LanguageClientOptions; + private client?: LanguageClient; + + private isDisposed = false; + private isStopping = false; + private disposables: vscode.Disposable[] = []; + isInitialized = false; + + // Document filters for content-mapped file extensions, keyed by the server's registration ID. These + // augment the static jsTs document selector so the extension's custom language-feature providers + // (hover, multi-document highlight, on-auto-insert) also cover content-mapped files. + private contentMapperFiltersById = new Map(); + // Disposables for the custom providers registered against the current (selector-scoped) document set. + // Re-registered whenever the set of content-mapped extensions changes. + private selectorScopedFeatures: vscode.Disposable[] = []; + + private exe: ExeInfo | undefined; + private errorHandler: ReportingErrorHandler; + + constructor( + outputChannel: vscode.LogOutputChannel, + initializedEventEmitter: vscode.EventEmitter, + telemetryReporter: tr.TelemetryReporter, + ) { + this.outputChannel = outputChannel; + this.initializedEventEmitter = initializedEventEmitter; + this.telemetryReporter = telemetryReporter; + this.errorHandler = new ReportingErrorHandler(this.telemetryReporter, 5); + + // Monkey-patch the output channel's error method to capture recent stderr lines. + // When the server crashes, vscode-languageclient pipes stderr to outputChannel.error(), + // so the error handler can include the last N lines in crash telemetry. + const originalError = this.outputChannel.error.bind(this.outputChannel); + this.outputChannel.error = (...args: Parameters) => { + originalError(...args); + this.errorHandler.pushStderrLine(String(args[0])); + }; + + this.documentSelector = [ + ...jsTsLanguageModes.map(language => ({ scheme: "file", language })), + ...jsTsLanguageModes.map(language => ({ scheme: "untitled", language })), + ]; + this.clientOptions = { + documentSelector: this.documentSelector, + outputChannel: this.outputChannel, + initializationOptions: { + codeLensShowLocationsCommandName, + enableTelemetry: true, + logVerbosity: this.outputChannel.logLevel, + runExternalCode: contentMappersEnabled(), + }, + errorHandler: this.errorHandler, + middleware: { + workspace: { + ...configurationMiddleware, + didChangeWatchedFile: (event, next) => { + if (this.isStopping || this.isDisposed) { + return Promise.resolve(); + } + return next(event); + }, + }, + sendNotification: sendNotificationMiddleware, + sendRequest: workspaceSymbolSendRequestMiddleware, + provideHover: () => undefined, + handleRegisterCapability: async (params, next) => { + await next(params, CancellationToken.None); + if (this.trackContentMapperRegistrations(params.registrations)) { + this.registerSelectorScopedFeatures(); + } + }, + handleUnregisterCapability: async (params, next) => { + await next(params, CancellationToken.None); + if (this.trackContentMapperUnregistrations(params.unregisterations)) { + this.registerSelectorScopedFeatures(); + } + }, + }, + diagnosticCollectionName: "typescript-push", + diagnosticPullOptions: { + onChange: true, + onSave: true, + onTabs: true, + match(documentSelector, resource) { + // This function is called when diagnostics are requested but + // only the URI itself is known (e.g. open but not yet focused tabs), + // so will not be present in vscode.workspace.textDocuments. + // See if this file matches without consulting vscode.languages.match + // (which requires a TextDocument). + + const language = getLanguageForUri(resource); + + for (const selector of documentSelector) { + if (typeof selector === "string") { + if (selector === language) { + return true; + } + continue; + } + if (NotebookDocumentFilter.is(selector)) { + continue; + } + if (TextDocumentFilter.is(selector)) { + if (selector.language !== undefined && selector.language !== language) { + continue; + } + + if (selector.scheme !== undefined && selector.scheme !== resource.scheme) { + continue; + } + + if (selector.pattern !== undefined) { + // VS Code's full glob matcher is not available via the API + // (microsoft/vscode#237304), but content mapper registrations only ever + // use simple "**/*" patterns, so match those by suffix. Any other + // pattern falls through to the next selector. + const glob = typeof selector.pattern === "string" ? selector.pattern : selector.pattern.pattern; + const globPrefix = "**/*"; + if (glob.startsWith(globPrefix) && resource.path.endsWith(glob.slice(globPrefix.length))) { + return true; + } + continue; + } + + return true; + } + } + + return false; + }, + }, + }; + } + + async start(exe: ExeInfo): Promise { + this.isStopping = false; + this.exe = exe; + this.outputChannel.appendLine(`Resolved to ${this.exe.path}`); + this.telemetryReporter.sendTelemetryEvent("languageServer.start", { + version: this.exe.version, + }); + + const pprofDir = readNativePreviewConfig("server.pprofDir", undefined) + ?? readNativePreviewConfig("pprofDir", undefined); + const pprofArgs = pprofDir ? ["--pprofDir", pprofDir] : []; + + const flakesFlag = vscode.workspace + .getConfiguration("js/ts") + .get<"panic" | "log" | "never" | "auto">("server.trackFlakyDiagnostics", "auto"); + const effectiveflakesFlag = flakesFlag === "auto" ? (isInsiders() ? "log" : "never") : flakesFlag; + + const goMemLimit = readNativePreviewConfig("server.goMemLimit", undefined) + ?? readNativePreviewConfig("goMemLimit", undefined); + const env = { ...process.env }; + if (goMemLimit) { + // Keep this regex aligned with the pattern in package.json. + if (/^[0-9]+(([KMGT]i)?B)?$/.test(goMemLimit)) { + this.outputChannel.appendLine(`Setting GOMEMLIMIT=${goMemLimit}`); + env.GOMEMLIMIT = goMemLimit; + } + else { + this.outputChannel.error(`Invalid goMemLimit: ${goMemLimit}. Must be a valid memory limit (e.g., '2048MiB', '4GiB'). Not overriding GOMEMLIMIT.`); + } + } + + const serverOptions: ServerOptions = { + run: { + command: this.exe.path, + args: ["--lsp", ...pprofArgs], + transport: TransportKind.stdio, + options: { env }, + }, + debug: { + command: this.exe.path, + args: ["--lsp", ...pprofArgs], + transport: TransportKind.stdio, + options: { env }, + }, + }; + + // Refresh options in case they changed between construction and start. + this.clientOptions.initializationOptions.logVerbosity = this.outputChannel.logLevel; + this.clientOptions.initializationOptions.runExternalCode = contentMappersEnabled(); + this.clientOptions.initializationOptions.trackFlakyDiagnostics = effectiveflakesFlag !== "never" ? (effectiveflakesFlag === "panic" ? 2 : 1) : 0; + + this.client = new NativePreviewLanguageClient( + "js/ts", + languageClientName, + serverOptions, + this.clientOptions, + ); + + // Register a static feature to advertise verbosityLevel support in hover capabilities. + this.client.registerFeature( + { + fillClientCapabilities(capabilities: ClientCapabilities): void { + capabilities.experimental = typeof capabilities.experimental === "object" && capabilities.experimental !== null + ? capabilities.experimental + : {}; + (capabilities.experimental as { hoverVerbosityLevel?: boolean; }).hoverVerbosityLevel = true; + }, + initialize(): void {}, + getState() { + return { kind: "static" as const }; + }, + clear(): void {}, + } satisfies StaticFeature, + ); + + this.outputChannel.appendLine(vscode.l10n.t(`Starting language server...`)); + await this.client.start(); + this.isInitialized = true; + this.initializedEventEmitter.fire(); + + // Send the initial log verbosity level to the server, and update it + // whenever the output channel's log level changes (via the gear icon). + this.sendLogVerbosity(); + const logLevelListener = this.outputChannel.onDidChangeLogLevel(() => { + this.sendLogVerbosity(); + }); + + type TelemetryData = { + eventName: string; + telemetryPurpose: "usage" | "error"; + properties?: Record; + measurements?: Record; + }; + + const serverTelemetryListener = this.client.onTelemetry((d: TelemetryData) => { + switch (d.telemetryPurpose) { + case "usage": + this.telemetryReporter.sendTelemetryEventUntyped(d.eventName, d.properties, d.measurements); + break; + case "error": + this.telemetryReporter.sendTelemetryErrorEventUntyped(d.eventName, d.properties, d.measurements); + break; + default: + const _: never = d.telemetryPurpose; + this.telemetryReporter.sendTelemetryErrorEvent("languageServer.unexpectedTelemetryPurpose", { + telemetryPurpose: String(d.telemetryPurpose), + }); + break; + } + }); + + this.disposables.push( + logLevelListener, + serverTelemetryListener, + registerSourceDefinitionFeature(this.client), + registerOnAutoInsertFeature(this.documentSelector, this.client), + ); + // Register the selector-scoped custom providers (hover, multi-document highlight). These start + // scoped to the static jsTs selector and expand as content-mapped extensions register. + this.registerSelectorScopedFeatures(); + } + + // trackContentMapperRegistrations records the content-mapped document filters carried by the server's + // dynamic capability registrations. Returns whether the known set of content-mapper filters changed. + private trackContentMapperRegistrations(registrations: ReadonlyArray<{ id: string; registerOptions?: ContentMapperRegisterOptions; }>): boolean { + let changed = false; + for (const registration of registrations) { + if (!registration.id.startsWith(contentMapperRegistrationPrefix)) { + continue; + } + const filters = extractPatternFilters(registration.registerOptions); + if (filters.length > 0) { + this.contentMapperFiltersById.set(registration.id, filters); + changed = true; + } + else if (this.contentMapperFiltersById.delete(registration.id)) { + changed = true; + } + } + return changed; + } + + // trackContentMapperUnregistrations forgets the filters for any content-mapper registration the server + // has removed. Returns whether the known set of content-mapper filters changed. + private trackContentMapperUnregistrations(unregistrations: ReadonlyArray<{ id: string; }>): boolean { + let changed = false; + for (const unregistration of unregistrations) { + if (this.contentMapperFiltersById.delete(unregistration.id)) { + changed = true; + } + } + return changed; + } + + // selectorScopedDocumentSelector is the static jsTs selector plus every active content-mapper filter, + // deduplicated. The custom providers are registered against it so they also cover content-mapped files. + private selectorScopedDocumentSelector(): vscode.DocumentSelector { + const seen = new Set(); + const filters: vscode.DocumentFilter[] = []; + for (const list of this.contentMapperFiltersById.values()) { + for (const filter of list) { + const key = typeof filter.pattern === "string" ? filter.pattern : JSON.stringify(filter.pattern); + if (!seen.has(key)) { + seen.add(key); + filters.push(filter); + } + } + } + return [...this.documentSelector, ...filters]; + } + + // registerSelectorScopedFeatures (re)registers the custom language-feature providers whose scope must + // track the set of content-mapped extensions. Existing registrations are disposed first. + private registerSelectorScopedFeatures(): void { + for (const disposable of this.selectorScopedFeatures.splice(0)) { + disposable.dispose(); + } + if (!this.client || this.isStopping || this.isDisposed) { + return; + } + const selector = this.selectorScopedDocumentSelector(); + this.selectorScopedFeatures.push( + registerMultiDocumentHighlightFeature(selector, this.client), + registerHoverFeature(selector, this.client), + ); + } + + async stop(): Promise { + if (this.isDisposed) { + return; + } + this.isStopping = true; + this.isInitialized = false; + for (const disposable of this.selectorScopedFeatures.splice(0)) { + disposable.dispose(); + } + this.contentMapperFiltersById.clear(); + const disposables = this.disposables.splice(0); + await Promise.all(disposables.map(d => d.dispose())); + await this.client?.stop(); + } + + async dispose(): Promise { + if (this.isDisposed) { + return; + } + this.isDisposed = true; + this.isStopping = true; + this.isInitialized = false; + for (const disposable of this.selectorScopedFeatures.splice(0)) { + disposable.dispose(); + } + this.contentMapperFiltersById.clear(); + const disposables = this.disposables.splice(0); + await Promise.all(disposables.map(d => d.dispose())); + await this.client?.dispose(); + } + + getCurrentExe(): { path: string; version: string; } | undefined { + return this.exe; + } + + get serverPid(): number | undefined { + return (this.client as any)?._serverProcess?.pid; + } + + /** + * Initialize an API session and return the socket path for connecting. + * This allows other extensions to get a direct connection to the API server. + */ + async initializeAPISession(pipe?: string): Promise<{ sessionId: string; pipe: string; }> { + if (!this.client) { + throw new Error(vscode.l10n.t("Language client is not initialized")); + } + return this.client.sendRequest<{ sessionId: string; pipe: string; }>("custom/initializeAPISession", { pipe }); + } + + /** + * Restart the language server if the executable path has not changed. + * Returns true if a restart was performed. + */ + async tryRestart(context: vscode.ExtensionContext): Promise { + if (!this.client) { + return Promise.reject(new Error(vscode.l10n.t("Language client is not initialized"))); + } + this.isStopping = false; + const exe = await getExe(context); + if (exe.path !== this.exe?.path) { + return false; + } + + this.isInitialized = false; + this.outputChannel.appendLine(vscode.l10n.t("Restarting language server...")); + try { + await this.client.restart(); + } + catch (err) { + this.outputChannel.appendLine(vscode.l10n.t(`Graceful shutdown failed, forcing restart: {0}`, String(err))); + await this.client.start(); + } + this.isInitialized = true; + this.initializedEventEmitter.fire(); + return true; + } + + // Developer/debugging methods + + private sendLogVerbosity(): void { + if (!this.client) { + return; + } + this.client.sendNotification("custom/setLogVerbosity", { + verbosity: this.outputChannel.logLevel, + }); + } + + async runGC(): Promise { + if (!this.client) { + throw new Error(vscode.l10n.t("Language client is not initialized")); + } + await this.client.sendRequest("custom/runGC"); + } + + async saveHeapProfile(dir: string): Promise { + if (!this.client) { + throw new Error(vscode.l10n.t("Language client is not initialized")); + } + const result = await this.client.sendRequest<{ file: string; }>("custom/saveHeapProfile", { dir }); + return result.file; + } + + async saveAllocProfile(dir: string): Promise { + if (!this.client) { + throw new Error(vscode.l10n.t("Language client is not initialized")); + } + const result = await this.client.sendRequest<{ file: string; }>("custom/saveAllocProfile", { dir }); + return result.file; + } + + async startCPUProfile(dir: string): Promise { + if (!this.client) { + throw new Error(vscode.l10n.t("Language client is not initialized")); + } + await this.client.sendRequest("custom/startCPUProfile", { dir }); + } + + async stopCPUProfile(): Promise { + if (!this.client) { + throw new Error(vscode.l10n.t("Language client is not initialized")); + } + const result = await this.client.sendRequest<{ file: string; }>("custom/stopCPUProfile"); + return result.file; + } + + async getProjectInfo(uri: string, token?: vscode.CancellationToken): Promise<{ configFilePath: string; }> { + if (!this.client) { + throw new Error(vscode.l10n.t("Language client is not initialized")); + } + return this.client.sendRequest<{ configFilePath: string; }>("custom/projectInfo", { + textDocument: { uri }, + }, token); + } + + async setContentMapperContributions(contributions: readonly SerializedContentMapperContribution[], openDocuments: readonly vscode.Uri[]): Promise { + if (!this.client || !this.isInitialized) { + return; + } + await this.client.sendRequest("custom/setContentMapperContributions", { + contributions: contentMappersEnabled() ? contributions : [], + openDocuments: openDocuments.map(uri => ({ uri: uri.toString() })), + }); + } +} + +// Returns true when running on a VS Code Insiders build. +function isInsiders(): boolean { + return vscode.env.uriScheme === "vscode-insiders"; +} + +// LanguageClient subclass that lets the user control whether a failed request +// surfaces an error notification, via the `js/ts.server.showFailedResponses` setting. +class NativePreviewLanguageClient extends LanguageClient { + override handleFailedRequest( + type: MessageSignature, + token: vscode.CancellationToken | undefined, + error: unknown, + defaultValue: T, + showNotification: boolean = true, + throwOnCancel: boolean = false, + ): T { + const setting = vscode.workspace + .getConfiguration("js/ts") + .get<"always" | "never" | "auto">("server.showFailedResponses", "auto"); + const effectiveSetting = setting === "auto" ? (isInsiders() ? "always" : "never") : setting; + + let effectiveShowNotification = showNotification; + switch (effectiveSetting) { + case "never": + effectiveShowNotification = false; + break; + default: + // Use the default behavior (showNotification) for "always" and any unrecognized values. + break; + } + + return super.handleFailedRequest( + type, + token, + error, + defaultValue, + effectiveShowNotification, + throwOnCancel, + ); + } +} + +// Adapted from the default error handler in vscode-languageclient. +class ReportingErrorHandler implements ErrorHandler { + telemetryReporter: tr.TelemetryReporter; + maxRestartCount: number; + restarts: number[]; + private stderrBuffer: string[] = []; + private capturingPanic = false; + private static readonly maxStderrLines = 40; + private static readonly maxStderrLength = 8192; + + constructor(telemetryReporter: tr.TelemetryReporter, maxRestartCount: number) { + this.telemetryReporter = telemetryReporter; + this.maxRestartCount = maxRestartCount; + this.restarts = []; + } + + pushStderrLine(line: string): void { + for (const l of line.split("\n")) { + if (!this.capturingPanic) { + if (/^panic:/.test(l.trimStart())) { + // Clear any stale data from a previous session/panic. + this.stderrBuffer = []; + this.capturingPanic = true; + } + else { + continue; + } + } + if (this.stderrBuffer.length < ReportingErrorHandler.maxStderrLines) { + this.stderrBuffer.push(l); + } + else { + this.capturingPanic = false; + } + } + } + + private consumeStderrBuffer(): string { + const raw = this.stderrBuffer.join("\n"); + this.stderrBuffer = []; + this.capturingPanic = false; + return sanitizeStderr(raw).slice(0, ReportingErrorHandler.maxStderrLength); + } + + error(_error: Error, _message: Message | undefined, count: number | undefined): ErrorHandlerResult | Promise { + let errorAction = ErrorAction.Shutdown; + if (count && count <= 3) { + errorAction = ErrorAction.Continue; + } + + let actionString = ""; + switch (errorAction) { + case ErrorAction.Continue: + actionString = "continue"; + break; + case ErrorAction.Shutdown: + actionString = "shutdown"; + break; + default: + const _: never = errorAction; + } + this.telemetryReporter.sendTelemetryErrorEvent("languageServer.connectionError", { + resultingAction: actionString, + }); + + return { action: errorAction }; + } + + closed(): CloseHandlerResult | Promise { + let resultingAction: CloseAction; + + this.restarts.push(Date.now()); + if (this.restarts.length <= this.maxRestartCount) { + resultingAction = CloseAction.Restart; + } + else { + const diff = this.restarts[this.restarts.length - 1] - this.restarts[0]; + if (diff <= 3 * 60 * 1000) { + resultingAction = CloseAction.DoNotRestart; + } + else { + this.restarts.shift(); + resultingAction = CloseAction.Restart; + } + } + + let actionString = ""; + switch (resultingAction) { + case CloseAction.DoNotRestart: + actionString = "doNotRestart"; + break; + case CloseAction.Restart: + actionString = "restart"; + break; + default: + const _: never = resultingAction; + } + const lastStderr = this.consumeStderrBuffer(); + this.telemetryReporter.sendTelemetryErrorEvent("languageServer.connectionClosed", { + resultingAction: actionString, + lastStderr, + }); + + if (resultingAction === CloseAction.DoNotRestart) { + return { + action: resultingAction, + message: vscode.l10n.t(`The TypeScript language server crashed {0} times in the last 3 minutes. The server will not be restarted. See the output for more information.`, String(this.maxRestartCount + 1)), + }; + } + + return { action: resultingAction }; + } +} + +// Matches the server-side sanitizeStackTrace in internal/lsp/stack_sanitizer.go. +// Strips file path prefixes that may contain PII and redacts frames outside of our module. +const genericSecretKeywordRegex = /\b(key|token|signature|sig|pwd)([(\[.|])/gi; + +function sanitizeStderr(stderr: string): string { + if (!stderr) { + return ""; + } + return stderr.split("\n").map(sanitizeStderrLine).join("\n"); +} + +function sanitizeStderrLine(line: string): string { + // Keep "goroutine N [status]:" headers as-is. + if (/^goroutine \d+/.test(line)) { + return line; + } + // Redact the panic message itself — assert messages may contain user data. + // Keep only "panic:" as a marker. + if (/^panic:/.test(line.trimStart())) { + return "panic: (REDACTED)"; + } + // Keep "Server process exited" messages from vscode-languageclient. + if (line.includes("Server process exited")) { + return line; + } + + const leadingWhitespace = line.match(/^(\s*)/)?.[1] ?? ""; + + // Stack frame file path lines look like: \t/full/path/to/file.go:123 +0x40 + // Function lines look like: github.com/microsoft/TypeScript/tsc/internal/foo.Bar(...) + const ourModuleMarker = "TypeScript/tsc/"; + const idx = line.indexOf(ourModuleMarker); + if (idx >= 0) { + let relevantPart = line.slice(idx); + // Strip hex offset suffixes like " +0x40" + relevantPart = relevantPart.replace(/ \+0x[0-9a-fA-F]+$/, ""); + // Strip " in goroutine N" suffixes + relevantPart = relevantPart.replace(/ in goroutine \d+$/, ""); + // Strip function arguments (keep parens empty) + relevantPart = relevantPart.replace(/\([^)]*\)$/, "()"); + // Replace / with |> to defeat path-based secret detection + relevantPart = relevantPart.replace(/\//g, "|>"); + // Defeat generic secret keyword regex + relevantPart = relevantPart.replace(genericSecretKeywordRegex, "$1X_X$2"); + return leadingWhitespace + relevantPart; + } + + // Preserve completely blank lines. + if (line.trim() === "") { + return ""; + } + + // Non-internal frames get fully redacted. + return leadingWhitespace + "(REDACTED)"; +} diff --git a/packages/vscode-typescript/src/commands.ts b/packages/vscode-typescript/src/commands.ts index de015f116bd64..c1edb4a0f6f59 100644 --- a/packages/vscode-typescript/src/commands.ts +++ b/packages/vscode-typescript/src/commands.ts @@ -1,99 +1,99 @@ -import * as vscode from "vscode"; - -import type { - DocumentUri, - Location, - Position, -} from "vscode-languageclient"; - -import type * as tr from "./telemetryReporting"; -import { - getExplicitConfigTarget, - restartExtHostOnChangeIfNeeded, -} from "./util"; - -export function registerEnablementCommands(context: vscode.ExtensionContext, telemetryReporter: tr.TelemetryReporter, stopSession: () => Promise): void { - context.subscriptions.push(vscode.commands.registerCommand("typescript.native-preview.enable", () => { - // Fire and forget, because this will restart the extension host and cause an error if we await - telemetryReporter.sendTelemetryEvent("command.enableNativePreview"); - updateUseTsgoSetting(true); - })); - - context.subscriptions.push(vscode.commands.registerCommand("typescript.native-preview.disable", async () => { - telemetryReporter.sendTelemetryEvent("command.disableNativePreview"); - await stopSession(); - await updateUseTsgoSetting(false); - })); -} - -/** - * Updates the TypeScript 7 setting and reloads extension host. - * Handles both `js/ts.experimental.useTsgo` and `typescript.experimental.useTsgo`. - */ -export async function updateUseTsgoSetting(enable: boolean): Promise { - const jsTsConfig = vscode.workspace.getConfiguration("js/ts"); - const tsConfig = vscode.workspace.getConfiguration("typescript"); - - const jsTsTarget = getExplicitConfigTarget(jsTsConfig, "experimental.useTsgo"); - const tsTarget = getExplicitConfigTarget(tsConfig, "experimental.useTsgo"); - - // If any are defined, we'll use the most-specific target, - // but we'll only set it through `js/ts`. - const updates: Thenable[] = []; - if (jsTsTarget !== undefined || tsTarget !== undefined) { - const mostSpecificTarget = Math.max( - jsTsTarget ?? vscode.ConfigurationTarget.Global, - tsTarget ?? vscode.ConfigurationTarget.Global, - ); - - updates.push(jsTsConfig.update("experimental.useTsgo", enable, mostSpecificTarget)); - - // If `typescript` had the most-specific target - // (or shared the most-specific target), then - // we'll erase that since we've just set `js/ts` above. - if (tsTarget === mostSpecificTarget) { - updates.push(tsConfig.update("experimental.useTsgo", undefined, mostSpecificTarget)); - } - - await Promise.all(updates); - } - else { - await jsTsConfig.update("experimental.useTsgo", enable, vscode.ConfigurationTarget.Global); - } - - return restartExtHostOnChangeIfNeeded(); -} - -export async function updateWorkspaceUseTsgoSetting(enable: boolean): Promise { - await vscode.workspace.getConfiguration("js/ts").update("experimental.useTsgo", enable, vscode.ConfigurationTarget.Workspace); - return restartExtHostOnChangeIfNeeded(); -} - -export const codeLensShowLocationsCommandName = "typescript.native-preview.codeLens.showLocations"; -export function registerCodeLensShowLocationsCommand(): vscode.Disposable { - return vscode.commands.registerCommand(codeLensShowLocationsCommandName, showCodeLensLocations); - - function showCodeLensLocations(...args: unknown[]): void { - if (args.length !== 3) { - throw new Error(vscode.l10n.t("Unexpected number of arguments.")); - } - - const lspUri = args[0] as DocumentUri; - const lspPosition = args[1] as Position; - const lspLocations = args[2] as Location[]; - - const editorUri = vscode.Uri.parse(lspUri); - const editorPosition = new vscode.Position(lspPosition.line, lspPosition.character); - const editorLocations = lspLocations.map(loc => - new vscode.Location( - vscode.Uri.parse(loc.uri), - new vscode.Range( - new vscode.Position(loc.range.start.line, loc.range.start.character), - new vscode.Position(loc.range.end.line, loc.range.end.character), - ), - ) - ); - - vscode.commands.executeCommand("editor.action.showReferences", editorUri, editorPosition, editorLocations); - } -} +import * as vscode from "vscode"; + +import type { + DocumentUri, + Location, + Position, +} from "vscode-languageclient"; + +import type * as tr from "./telemetryReporting"; +import { + getExplicitConfigTarget, + restartExtHostOnChangeIfNeeded, +} from "./util"; + +export function registerEnablementCommands(context: vscode.ExtensionContext, telemetryReporter: tr.TelemetryReporter, stopSession: () => Promise): void { + context.subscriptions.push(vscode.commands.registerCommand("typescript.native-preview.enable", () => { + // Fire and forget, because this will restart the extension host and cause an error if we await + telemetryReporter.sendTelemetryEvent("command.enableNativePreview"); + updateUseTsgoSetting(true); + })); + + context.subscriptions.push(vscode.commands.registerCommand("typescript.native-preview.disable", async () => { + telemetryReporter.sendTelemetryEvent("command.disableNativePreview"); + await stopSession(); + await updateUseTsgoSetting(false); + })); +} + +/** + * Updates the TypeScript 7 setting and reloads extension host. + * Handles both `js/ts.experimental.useTsgo` and `typescript.experimental.useTsgo`. + */ +export async function updateUseTsgoSetting(enable: boolean): Promise { + const jsTsConfig = vscode.workspace.getConfiguration("js/ts"); + const tsConfig = vscode.workspace.getConfiguration("typescript"); + + const jsTsTarget = getExplicitConfigTarget(jsTsConfig, "experimental.useTsgo"); + const tsTarget = getExplicitConfigTarget(tsConfig, "experimental.useTsgo"); + + // If any are defined, we'll use the most-specific target, + // but we'll only set it through `js/ts`. + const updates: Thenable[] = []; + if (jsTsTarget !== undefined || tsTarget !== undefined) { + const mostSpecificTarget = Math.max( + jsTsTarget ?? vscode.ConfigurationTarget.Global, + tsTarget ?? vscode.ConfigurationTarget.Global, + ); + + updates.push(jsTsConfig.update("experimental.useTsgo", enable, mostSpecificTarget)); + + // If `typescript` had the most-specific target + // (or shared the most-specific target), then + // we'll erase that since we've just set `js/ts` above. + if (tsTarget === mostSpecificTarget) { + updates.push(tsConfig.update("experimental.useTsgo", undefined, mostSpecificTarget)); + } + + await Promise.all(updates); + } + else { + await jsTsConfig.update("experimental.useTsgo", enable, vscode.ConfigurationTarget.Global); + } + + return restartExtHostOnChangeIfNeeded(); +} + +export async function updateWorkspaceUseTsgoSetting(enable: boolean): Promise { + await vscode.workspace.getConfiguration("js/ts").update("experimental.useTsgo", enable, vscode.ConfigurationTarget.Workspace); + return restartExtHostOnChangeIfNeeded(); +} + +export const codeLensShowLocationsCommandName = "typescript.native-preview.codeLens.showLocations"; +export function registerCodeLensShowLocationsCommand(): vscode.Disposable { + return vscode.commands.registerCommand(codeLensShowLocationsCommandName, showCodeLensLocations); + + function showCodeLensLocations(...args: unknown[]): void { + if (args.length !== 3) { + throw new Error(vscode.l10n.t("Unexpected number of arguments.")); + } + + const lspUri = args[0] as DocumentUri; + const lspPosition = args[1] as Position; + const lspLocations = args[2] as Location[]; + + const editorUri = vscode.Uri.parse(lspUri); + const editorPosition = new vscode.Position(lspPosition.line, lspPosition.character); + const editorLocations = lspLocations.map(loc => + new vscode.Location( + vscode.Uri.parse(loc.uri), + new vscode.Range( + new vscode.Position(loc.range.start.line, loc.range.start.character), + new vscode.Position(loc.range.end.line, loc.range.end.character), + ), + ) + ); + + vscode.commands.executeCommand("editor.action.showReferences", editorUri, editorPosition, editorLocations); + } +} diff --git a/packages/vscode-typescript/src/configurationMiddleware.ts b/packages/vscode-typescript/src/configurationMiddleware.ts index 643d300be04ce..6e19ad92b3eb5 100644 --- a/packages/vscode-typescript/src/configurationMiddleware.ts +++ b/packages/vscode-typescript/src/configurationMiddleware.ts @@ -1,325 +1,325 @@ -import * as vscode from "vscode"; - -import type { ConfigurationMiddleware } from "vscode-languageclient/node"; -import type { MessageSignature } from "vscode-languageserver-protocol"; - -/** - * Configuration middleware for the TypeScript language server. - * - * The default vscode-languageclient handler uses `getConfiguration().get()`, which - * returns "fully resolved" values including VS Code defaults. This is problematic - * because the server has its own defaults, and receiving VS Code's prevents the - * server from distinguishing user-set values from defaults. - * - * This module instead uses `inspect()` to retrieve both explicitly-set values (user/ - * workspace/workspace-folder settings) and VS Code default values from all three - * configuration sections, then merges them with the correct precedence: - * sections: js/ts > typescript > javascript - * values: explicit > default - * - * Both the `workspace/configuration` (pull) and `workspace/didChangeConfiguration` - * (push) middlewares return/send the same merged object for every requested section. - */ - -// Sections merged together. Earlier sections take precedence over later ones. -const configSections = ["js/ts", "typescript", "javascript"]; - -const configAliases = [ - ["validate.enabled", "validate.enable"], - ["format.enabled", "format.enable"], - ["autoClosingTags.enabled", "autoClosingTags"], - ["suggest.jsdoc.enabled", "suggest.completeJSDocs"], -] as const; - -/** - * Build a single merged configuration object from all config sections. - * - * For each key, the value is chosen with this precedence (highest first): - * 1. js/ts explicit 2. typescript explicit 3. javascript explicit - * 4. js/ts default 5. typescript default 6. javascript default - * - * This ensures user-set values always win, and declared-but-unset settings - * still get their default with the right section precedence. - */ -function getMergedConfiguration(resource: vscode.Uri | undefined): Record { - const configs = configSections.map(section => getInspectedConfiguration(section, resource)); - const legacyNativePreviewConfig = getInspectedConfiguration("typescript.native-preview", resource); - - // Defaults: javascript < typescript < js/ts - let defaults: Record = Object.create(null); - for (let i = configs.length - 1; i >= 0; i--) { - if (configs[i].defaults !== null) { - defaults = deepMerge(defaults, configs[i].defaults!); - } - } - - // Explicit values: javascript < typescript < js/ts - let explicit: Record = Object.create(null); - for (let i = configs.length - 1; i >= 0; i--) { - if (configs[i].explicit !== null) { - applyConfigAliases(configs[i].explicit!); - explicit = deepMerge(explicit, configs[i].explicit!); - } - } - - if (explicit.customConfigFileName === undefined) { - const legacyCustomConfigFileName = legacyNativePreviewConfig.explicit?.customConfigFileName; - if (legacyCustomConfigFileName !== undefined) { - explicit.customConfigFileName = legacyCustomConfigFileName; - } - } - if (explicit.trace?.server === undefined) { - const legacyTraceServer = legacyNativePreviewConfig.explicit?.trace?.server; - if (legacyTraceServer !== undefined) { - explicit.trace ??= Object.create(null); - explicit.trace.server = legacyTraceServer; - } - } - - return deepMerge(defaults, explicit); -} - -/** - * Given a configuration section name (e.g., "typescript"), use vscode's - * inspect API to collect both explicitly-set values and default values, - * returning them as separate nested objects. - */ -function getInspectedConfiguration( - section: string, - resource: vscode.Uri | undefined, -): { explicit: Record | null; defaults: Record | null; } { - const config = vscode.workspace.getConfiguration(section, resource); - // Use Object.create(null) so these objects have no prototype to pollute. - const explicit: Record = Object.create(null); - const defaults: Record = Object.create(null); - let hasExplicit = false; - let hasDefaults = false; - - const allKeys = collectConfigurationKeys(config); - - for (const key of allKeys) { - const inspection = config.inspect(key); - if (!inspection) { - continue; - } - - // Pick the most specific explicitly-set value. - // Language-specific overrides (e.g. [typescript]) take precedence - // over non-language values at the same scope. - const explicitValue = inspection.workspaceFolderLanguageValue - ?? inspection.workspaceFolderValue - ?? inspection.workspaceLanguageValue - ?? inspection.workspaceValue - ?? inspection.globalLanguageValue - ?? inspection.globalValue; - - if (explicitValue !== undefined) { - setNestedValue(explicit, key, toJSONObject(explicitValue)); - hasExplicit = true; - } - else if (inspection.defaultValue !== undefined) { - setNestedValue(defaults, key, toJSONObject(inspection.defaultValue)); - hasDefaults = true; - } - } - - return { - explicit: hasExplicit ? explicit : null, - defaults: hasDefaults ? defaults : null, - }; -} - -/** - * Collect all leaf key paths from a workspace configuration section. - */ -function collectConfigurationKeys(config: vscode.WorkspaceConfiguration): string[] { - const keys: string[] = []; - const configMethods = new Set(["get", "has", "inspect", "update"]); - - function walk(obj: any, prefix: string) { - if (obj === null || obj === undefined || typeof obj !== "object" || Array.isArray(obj)) { - return; - } - for (const key of Object.keys(obj)) { - if (configMethods.has(key)) { - continue; - } - const fullKey = prefix ? `${prefix}.${key}` : key; - const value = obj[key]; - if (value !== null && typeof value === "object" && !Array.isArray(value)) { - walk(value, fullKey); - } - else { - keys.push(fullKey); - } - } - } - - walk(config, ""); - return keys; -} - -const prototypeKeys = new Set(["__proto__", "constructor", "prototype"]); - -function setNestedValue(obj: Record, dottedKey: string, value: any): void { - const parts = dottedKey.split("."); - let current = obj; - for (let i = 0; i < parts.length - 1; i++) { - const part = parts[i]; - if (prototypeKeys.has(part)) { - return; - } - if (!(part in current) || typeof current[part] !== "object" || current[part] === null) { - current[part] = Object.create(null); - } - current = current[part]; - } - const lastPart = parts[parts.length - 1]; - if (!prototypeKeys.has(lastPart)) { - current[lastPart] = value; - } -} - -function getNestedValue(obj: Record, dottedKey: string): any { - const parts = dottedKey.split("."); - let current: any = obj; - for (const part of parts) { - if (prototypeKeys.has(part) || current === null || typeof current !== "object" || !(part in current)) { - return undefined; - } - current = current[part]; - } - return current; -} - -function applyConfigAliases(explicit: Record): void { - for (const [preferred, fallback] of configAliases) { - if (getNestedValue(explicit, preferred) === undefined) { - const fallbackValue = getNestedValue(explicit, fallback); - if (fallbackValue !== undefined) { - setNestedValue(explicit, preferred, fallbackValue); - } - } - } -} - -/** - * Deep merge b into a. Values in b take precedence over values in a. - * Returns a new object; does not mutate inputs. - */ -function deepMerge(a: Record, b: Record): Record { - // Use Object.create(null) so the result has no prototype to pollute. - const result: Record = Object.create(null); - Object.assign(result, a); - for (const key of Object.keys(b)) { - if (prototypeKeys.has(key)) { - continue; - } - if ( - key in result - && result[key] !== null && typeof result[key] === "object" && !Array.isArray(result[key]) - && b[key] !== null && typeof b[key] === "object" && !Array.isArray(b[key]) - ) { - result[key] = deepMerge(result[key], b[key]); - } - else { - result[key] = b[key]; - } - } - return result; -} - -function toJSONObject(obj: any): any { - if (obj === null || obj === undefined) { - return obj; - } - if (Array.isArray(obj)) { - return obj.map(toJSONObject); - } - if (typeof obj === "object") { - const res: Record = Object.create(null); - for (const key in obj) { - if (Object.prototype.hasOwnProperty.call(obj, key)) { - res[key] = toJSONObject(obj[key]); - } - } - return res; - } - return obj; -} - -const configSectionsSet: ReadonlySet = new Set(configSections); - -/** - * Intercepts workspace/configuration requests. For items requesting one of - * the JS/TS config sections, returns the merged explicit configuration - * (js/ts > typescript > javascript). For any other section, delegates to - * the default handler via `next`. - */ -export const configurationMiddleware: ConfigurationMiddleware = { - async configuration(params, token, next) { - const hasNonJsTs = params.items.some( - item => item.section === undefined || !configSectionsSet.has(item.section), - ); - - // If all items are JS/TS sections, no need to call next. - let defaultResults: any[] | undefined; - if (hasNonJsTs) { - const res = await next(params, token); - if (Array.isArray(res)) { - defaultResults = res; - } - } - - // Cache merged config per resource URI to avoid redundant recalculation. - const mergedCache = new Map>(); - function getMergedCached(resource: vscode.Uri | undefined): Record { - const key = resource?.toString() ?? ""; - let cached = mergedCache.get(key); - if (cached === undefined) { - cached = getMergedConfiguration(resource); - mergedCache.set(key, cached); - } - return cached; - } - - const result: any[] = params.items.map((item, i) => { - if (item.section !== undefined && configSectionsSet.has(item.section)) { - const resource = item.scopeUri ? vscode.Uri.parse(item.scopeUri) : undefined; - return getMergedCached(resource); - } - return defaultResults?.[i] ?? null; - }); - - return result; - }, -}; - -/** - * Intercepts outgoing workspace/didChangeConfiguration notifications. - * Replaces the default settings (which include VS Code defaults) with - * the merged configuration, keyed by section name. - * - * This is typed as returning `Promise` rather than `void` because the - * `didChangeConfiguration` notification is misannotated in vscode-languageclient - * as returning void, so we must go through `sendNotification` instead. - */ -export function sendNotificationMiddleware( - type: string | MessageSignature, - next: (type: string | MessageSignature, params?: any) => Promise, - params: any, -): Promise { - const method = typeof type === "string" ? type : type.method; - if (method === "workspace/didChangeConfiguration") { - const merged = getMergedConfiguration(undefined); - const settings: Record = {}; - for (const section of configSections) { - settings[section] = merged; - } - if (params?.settings?.editor !== undefined) { - settings.editor = params.settings.editor; - } - return next(type, { settings }); - } - return next(type, params); -} +import * as vscode from "vscode"; + +import type { ConfigurationMiddleware } from "vscode-languageclient/node"; +import type { MessageSignature } from "vscode-languageserver-protocol"; + +/** + * Configuration middleware for the TypeScript language server. + * + * The default vscode-languageclient handler uses `getConfiguration().get()`, which + * returns "fully resolved" values including VS Code defaults. This is problematic + * because the server has its own defaults, and receiving VS Code's prevents the + * server from distinguishing user-set values from defaults. + * + * This module instead uses `inspect()` to retrieve both explicitly-set values (user/ + * workspace/workspace-folder settings) and VS Code default values from all three + * configuration sections, then merges them with the correct precedence: + * sections: js/ts > typescript > javascript + * values: explicit > default + * + * Both the `workspace/configuration` (pull) and `workspace/didChangeConfiguration` + * (push) middlewares return/send the same merged object for every requested section. + */ + +// Sections merged together. Earlier sections take precedence over later ones. +const configSections = ["js/ts", "typescript", "javascript"]; + +const configAliases = [ + ["validate.enabled", "validate.enable"], + ["format.enabled", "format.enable"], + ["autoClosingTags.enabled", "autoClosingTags"], + ["suggest.jsdoc.enabled", "suggest.completeJSDocs"], +] as const; + +/** + * Build a single merged configuration object from all config sections. + * + * For each key, the value is chosen with this precedence (highest first): + * 1. js/ts explicit 2. typescript explicit 3. javascript explicit + * 4. js/ts default 5. typescript default 6. javascript default + * + * This ensures user-set values always win, and declared-but-unset settings + * still get their default with the right section precedence. + */ +function getMergedConfiguration(resource: vscode.Uri | undefined): Record { + const configs = configSections.map(section => getInspectedConfiguration(section, resource)); + const legacyNativePreviewConfig = getInspectedConfiguration("typescript.native-preview", resource); + + // Defaults: javascript < typescript < js/ts + let defaults: Record = Object.create(null); + for (let i = configs.length - 1; i >= 0; i--) { + if (configs[i].defaults !== null) { + defaults = deepMerge(defaults, configs[i].defaults!); + } + } + + // Explicit values: javascript < typescript < js/ts + let explicit: Record = Object.create(null); + for (let i = configs.length - 1; i >= 0; i--) { + if (configs[i].explicit !== null) { + applyConfigAliases(configs[i].explicit!); + explicit = deepMerge(explicit, configs[i].explicit!); + } + } + + if (explicit.customConfigFileName === undefined) { + const legacyCustomConfigFileName = legacyNativePreviewConfig.explicit?.customConfigFileName; + if (legacyCustomConfigFileName !== undefined) { + explicit.customConfigFileName = legacyCustomConfigFileName; + } + } + if (explicit.trace?.server === undefined) { + const legacyTraceServer = legacyNativePreviewConfig.explicit?.trace?.server; + if (legacyTraceServer !== undefined) { + explicit.trace ??= Object.create(null); + explicit.trace.server = legacyTraceServer; + } + } + + return deepMerge(defaults, explicit); +} + +/** + * Given a configuration section name (e.g., "typescript"), use vscode's + * inspect API to collect both explicitly-set values and default values, + * returning them as separate nested objects. + */ +function getInspectedConfiguration( + section: string, + resource: vscode.Uri | undefined, +): { explicit: Record | null; defaults: Record | null; } { + const config = vscode.workspace.getConfiguration(section, resource); + // Use Object.create(null) so these objects have no prototype to pollute. + const explicit: Record = Object.create(null); + const defaults: Record = Object.create(null); + let hasExplicit = false; + let hasDefaults = false; + + const allKeys = collectConfigurationKeys(config); + + for (const key of allKeys) { + const inspection = config.inspect(key); + if (!inspection) { + continue; + } + + // Pick the most specific explicitly-set value. + // Language-specific overrides (e.g. [typescript]) take precedence + // over non-language values at the same scope. + const explicitValue = inspection.workspaceFolderLanguageValue + ?? inspection.workspaceFolderValue + ?? inspection.workspaceLanguageValue + ?? inspection.workspaceValue + ?? inspection.globalLanguageValue + ?? inspection.globalValue; + + if (explicitValue !== undefined) { + setNestedValue(explicit, key, toJSONObject(explicitValue)); + hasExplicit = true; + } + else if (inspection.defaultValue !== undefined) { + setNestedValue(defaults, key, toJSONObject(inspection.defaultValue)); + hasDefaults = true; + } + } + + return { + explicit: hasExplicit ? explicit : null, + defaults: hasDefaults ? defaults : null, + }; +} + +/** + * Collect all leaf key paths from a workspace configuration section. + */ +function collectConfigurationKeys(config: vscode.WorkspaceConfiguration): string[] { + const keys: string[] = []; + const configMethods = new Set(["get", "has", "inspect", "update"]); + + function walk(obj: any, prefix: string) { + if (obj === null || obj === undefined || typeof obj !== "object" || Array.isArray(obj)) { + return; + } + for (const key of Object.keys(obj)) { + if (configMethods.has(key)) { + continue; + } + const fullKey = prefix ? `${prefix}.${key}` : key; + const value = obj[key]; + if (value !== null && typeof value === "object" && !Array.isArray(value)) { + walk(value, fullKey); + } + else { + keys.push(fullKey); + } + } + } + + walk(config, ""); + return keys; +} + +const prototypeKeys = new Set(["__proto__", "constructor", "prototype"]); + +function setNestedValue(obj: Record, dottedKey: string, value: any): void { + const parts = dottedKey.split("."); + let current = obj; + for (let i = 0; i < parts.length - 1; i++) { + const part = parts[i]; + if (prototypeKeys.has(part)) { + return; + } + if (!(part in current) || typeof current[part] !== "object" || current[part] === null) { + current[part] = Object.create(null); + } + current = current[part]; + } + const lastPart = parts[parts.length - 1]; + if (!prototypeKeys.has(lastPart)) { + current[lastPart] = value; + } +} + +function getNestedValue(obj: Record, dottedKey: string): any { + const parts = dottedKey.split("."); + let current: any = obj; + for (const part of parts) { + if (prototypeKeys.has(part) || current === null || typeof current !== "object" || !(part in current)) { + return undefined; + } + current = current[part]; + } + return current; +} + +function applyConfigAliases(explicit: Record): void { + for (const [preferred, fallback] of configAliases) { + if (getNestedValue(explicit, preferred) === undefined) { + const fallbackValue = getNestedValue(explicit, fallback); + if (fallbackValue !== undefined) { + setNestedValue(explicit, preferred, fallbackValue); + } + } + } +} + +/** + * Deep merge b into a. Values in b take precedence over values in a. + * Returns a new object; does not mutate inputs. + */ +function deepMerge(a: Record, b: Record): Record { + // Use Object.create(null) so the result has no prototype to pollute. + const result: Record = Object.create(null); + Object.assign(result, a); + for (const key of Object.keys(b)) { + if (prototypeKeys.has(key)) { + continue; + } + if ( + key in result + && result[key] !== null && typeof result[key] === "object" && !Array.isArray(result[key]) + && b[key] !== null && typeof b[key] === "object" && !Array.isArray(b[key]) + ) { + result[key] = deepMerge(result[key], b[key]); + } + else { + result[key] = b[key]; + } + } + return result; +} + +function toJSONObject(obj: any): any { + if (obj === null || obj === undefined) { + return obj; + } + if (Array.isArray(obj)) { + return obj.map(toJSONObject); + } + if (typeof obj === "object") { + const res: Record = Object.create(null); + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + res[key] = toJSONObject(obj[key]); + } + } + return res; + } + return obj; +} + +const configSectionsSet: ReadonlySet = new Set(configSections); + +/** + * Intercepts workspace/configuration requests. For items requesting one of + * the JS/TS config sections, returns the merged explicit configuration + * (js/ts > typescript > javascript). For any other section, delegates to + * the default handler via `next`. + */ +export const configurationMiddleware: ConfigurationMiddleware = { + async configuration(params, token, next) { + const hasNonJsTs = params.items.some( + item => item.section === undefined || !configSectionsSet.has(item.section), + ); + + // If all items are JS/TS sections, no need to call next. + let defaultResults: any[] | undefined; + if (hasNonJsTs) { + const res = await next(params, token); + if (Array.isArray(res)) { + defaultResults = res; + } + } + + // Cache merged config per resource URI to avoid redundant recalculation. + const mergedCache = new Map>(); + function getMergedCached(resource: vscode.Uri | undefined): Record { + const key = resource?.toString() ?? ""; + let cached = mergedCache.get(key); + if (cached === undefined) { + cached = getMergedConfiguration(resource); + mergedCache.set(key, cached); + } + return cached; + } + + const result: any[] = params.items.map((item, i) => { + if (item.section !== undefined && configSectionsSet.has(item.section)) { + const resource = item.scopeUri ? vscode.Uri.parse(item.scopeUri) : undefined; + return getMergedCached(resource); + } + return defaultResults?.[i] ?? null; + }); + + return result; + }, +}; + +/** + * Intercepts outgoing workspace/didChangeConfiguration notifications. + * Replaces the default settings (which include VS Code defaults) with + * the merged configuration, keyed by section name. + * + * This is typed as returning `Promise` rather than `void` because the + * `didChangeConfiguration` notification is misannotated in vscode-languageclient + * as returning void, so we must go through `sendNotification` instead. + */ +export function sendNotificationMiddleware( + type: string | MessageSignature, + next: (type: string | MessageSignature, params?: any) => Promise, + params: any, +): Promise { + const method = typeof type === "string" ? type : type.method; + if (method === "workspace/didChangeConfiguration") { + const merged = getMergedConfiguration(undefined); + const settings: Record = {}; + for (const section of configSections) { + settings[section] = merged; + } + if (params?.settings?.editor !== undefined) { + settings.editor = params.settings.editor; + } + return next(type, { settings }); + } + return next(type, params); +} diff --git a/packages/vscode-typescript/src/contentMapperContributions.ts b/packages/vscode-typescript/src/contentMapperContributions.ts index a4570d7bd96bb..9f9b7c259d582 100644 --- a/packages/vscode-typescript/src/contentMapperContributions.ts +++ b/packages/vscode-typescript/src/contentMapperContributions.ts @@ -1,94 +1,94 @@ -import * as vscode from "vscode"; - -export interface ContentMapperManifest { - readonly name: string; - readonly version?: string; - readonly exec: readonly string[]; - readonly cwd?: vscode.Uri; - readonly compilerOptions?: readonly string[]; - readonly dynamicConfig?: boolean; -} - -export interface ContentMapperContribution { - readonly extensions: readonly string[]; - readonly inferredProjectContribution?: { - readonly options?: Readonly>; - readonly manifest: ContentMapperManifest; - }; -} - -export interface SerializedContentMapperContribution { - readonly contributorId: string; - readonly extensions: readonly string[]; - readonly inferredProjectContribution?: { - readonly options?: Readonly>; - readonly manifest: { - readonly name: string; - readonly version?: string; - readonly exec: readonly string[]; - readonly cwd?: string; - readonly compilerOptions?: readonly string[]; - readonly dynamicConfig?: boolean; - }; - }; -} - -export function serializeContentMapperContributions( - registrations: ReadonlyMap, -): readonly SerializedContentMapperContribution[] { - const result: SerializedContentMapperContribution[] = []; - for (const [contributorId, contributions] of registrations) { - contributions.forEach(contribution => { - result.push({ - contributorId, - extensions: [...contribution.extensions], - inferredProjectContribution: contribution.inferredProjectContribution && { - options: contribution.inferredProjectContribution.options, - manifest: { - ...contribution.inferredProjectContribution.manifest, - exec: [...contribution.inferredProjectContribution.manifest.exec], - cwd: contribution.inferredProjectContribution.manifest.cwd?.fsPath, - compilerOptions: contribution.inferredProjectContribution.manifest.compilerOptions && [...contribution.inferredProjectContribution.manifest.compilerOptions], - }, - }, - }); - }); - } - return result; -} - -export function validateContentMapperRegistration(contributorId: string, contributions: readonly ContentMapperContribution[]): void { - if (!contributorId) { - throw new TypeError("Content mapper contributor ID must not be empty."); - } - for (const contribution of contributions) { - if (contribution.extensions.length === 0 || contribution.extensions.some(extension => !extension.startsWith(".") || extension.length === 1)) { - throw new TypeError("Content mapper contributions require non-empty extensions beginning with '.'."); - } - const inferredProjectContribution = contribution.inferredProjectContribution; - if (inferredProjectContribution?.options === null || Array.isArray(inferredProjectContribution?.options) || inferredProjectContribution?.options !== undefined && typeof inferredProjectContribution.options !== "object") { - throw new TypeError("Content mapper contribution options must be an object."); - } - if (inferredProjectContribution && (!inferredProjectContribution.manifest.name || inferredProjectContribution.manifest.exec.length === 0)) { - throw new TypeError("Content mapper contribution manifests require a name and non-empty exec."); - } - if (inferredProjectContribution?.manifest.cwd && inferredProjectContribution.manifest.cwd.scheme !== "file") { - throw new TypeError("Content mapper contribution cwd must be a file URI."); - } - } -} - -export function documentMatchesContentMapperContributions( - document: { readonly uri: { readonly path: string; }; }, - registrations: ReadonlyMap, -): boolean { - const documentPath = document.uri.path.toLowerCase(); - for (const contributions of registrations.values()) { - for (const contribution of contributions) { - if (contribution.extensions.some(extension => documentPath.endsWith(extension.toLowerCase()))) { - return true; - } - } - } - return false; -} +import * as vscode from "vscode"; + +export interface ContentMapperManifest { + readonly name: string; + readonly version?: string; + readonly exec: readonly string[]; + readonly cwd?: vscode.Uri; + readonly compilerOptions?: readonly string[]; + readonly dynamicConfig?: boolean; +} + +export interface ContentMapperContribution { + readonly extensions: readonly string[]; + readonly inferredProjectContribution?: { + readonly options?: Readonly>; + readonly manifest: ContentMapperManifest; + }; +} + +export interface SerializedContentMapperContribution { + readonly contributorId: string; + readonly extensions: readonly string[]; + readonly inferredProjectContribution?: { + readonly options?: Readonly>; + readonly manifest: { + readonly name: string; + readonly version?: string; + readonly exec: readonly string[]; + readonly cwd?: string; + readonly compilerOptions?: readonly string[]; + readonly dynamicConfig?: boolean; + }; + }; +} + +export function serializeContentMapperContributions( + registrations: ReadonlyMap, +): readonly SerializedContentMapperContribution[] { + const result: SerializedContentMapperContribution[] = []; + for (const [contributorId, contributions] of registrations) { + contributions.forEach(contribution => { + result.push({ + contributorId, + extensions: [...contribution.extensions], + inferredProjectContribution: contribution.inferredProjectContribution && { + options: contribution.inferredProjectContribution.options, + manifest: { + ...contribution.inferredProjectContribution.manifest, + exec: [...contribution.inferredProjectContribution.manifest.exec], + cwd: contribution.inferredProjectContribution.manifest.cwd?.fsPath, + compilerOptions: contribution.inferredProjectContribution.manifest.compilerOptions && [...contribution.inferredProjectContribution.manifest.compilerOptions], + }, + }, + }); + }); + } + return result; +} + +export function validateContentMapperRegistration(contributorId: string, contributions: readonly ContentMapperContribution[]): void { + if (!contributorId) { + throw new TypeError("Content mapper contributor ID must not be empty."); + } + for (const contribution of contributions) { + if (contribution.extensions.length === 0 || contribution.extensions.some(extension => !extension.startsWith(".") || extension.length === 1)) { + throw new TypeError("Content mapper contributions require non-empty extensions beginning with '.'."); + } + const inferredProjectContribution = contribution.inferredProjectContribution; + if (inferredProjectContribution?.options === null || Array.isArray(inferredProjectContribution?.options) || inferredProjectContribution?.options !== undefined && typeof inferredProjectContribution.options !== "object") { + throw new TypeError("Content mapper contribution options must be an object."); + } + if (inferredProjectContribution && (!inferredProjectContribution.manifest.name || inferredProjectContribution.manifest.exec.length === 0)) { + throw new TypeError("Content mapper contribution manifests require a name and non-empty exec."); + } + if (inferredProjectContribution?.manifest.cwd && inferredProjectContribution.manifest.cwd.scheme !== "file") { + throw new TypeError("Content mapper contribution cwd must be a file URI."); + } + } +} + +export function documentMatchesContentMapperContributions( + document: { readonly uri: { readonly path: string; }; }, + registrations: ReadonlyMap, +): boolean { + const documentPath = document.uri.path.toLowerCase(); + for (const contributions of registrations.values()) { + for (const contribution of contributions) { + if (contribution.extensions.some(extension => documentPath.endsWith(extension.toLowerCase()))) { + return true; + } + } + } + return false; +} diff --git a/packages/vscode-typescript/src/experimentationService.ts b/packages/vscode-typescript/src/experimentationService.ts index ac47ae2bca1f8..efdf2c83b01ee 100644 --- a/packages/vscode-typescript/src/experimentationService.ts +++ b/packages/vscode-typescript/src/experimentationService.ts @@ -1,57 +1,57 @@ -import * as vscode from "vscode"; -import * as tas from "vscode-tas-client"; - -interface ExperimentTypes { - // Experiment variables go here. - suggestNativePreview: boolean; -} - -export class ExperimentationService { - private readonly _experimentationService: tas.IExperimentationService; - private readonly _telemetryReporter: tas.IExperimentationTelemetry; - - constructor(telemetryReporter: tas.IExperimentationTelemetry, id: string, version: string, globalState: vscode.Memento) { - this._telemetryReporter = telemetryReporter; - this._experimentationService = createTasExperimentationService(this._telemetryReporter, id, version, globalState); - } - - public async getTreatmentVariable(name: K, defaultValue: ExperimentTypes[K]): Promise { - const experimentationService = this._experimentationService; - try { - const treatmentVariable = await experimentationService.getTreatmentVariableAsync("vscode", name, /*checkCache*/ true) as ExperimentTypes[K]; - return treatmentVariable ?? defaultValue; - } - catch { - return defaultValue; - } - } -} - -function createTasExperimentationService( - reporter: tas.IExperimentationTelemetry, - id: string, - version: string, - globalState: vscode.Memento, -): tas.IExperimentationService { - let targetPopulation: tas.TargetPopulation; - switch (vscode.env.uriScheme) { - case "vscode": - targetPopulation = tas.TargetPopulation.Public; - break; - case "vscode-insiders": - targetPopulation = tas.TargetPopulation.Insiders; - break; - case "vscode-exploration": - targetPopulation = tas.TargetPopulation.Internal; - break; - case "code-oss": - targetPopulation = tas.TargetPopulation.Team; - break; - default: - targetPopulation = tas.TargetPopulation.Public; - break; - } - - const experimentationService = tas.getExperimentationService(id, version, targetPopulation, reporter, globalState); - return experimentationService; -} +import * as vscode from "vscode"; +import * as tas from "vscode-tas-client"; + +interface ExperimentTypes { + // Experiment variables go here. + suggestNativePreview: boolean; +} + +export class ExperimentationService { + private readonly _experimentationService: tas.IExperimentationService; + private readonly _telemetryReporter: tas.IExperimentationTelemetry; + + constructor(telemetryReporter: tas.IExperimentationTelemetry, id: string, version: string, globalState: vscode.Memento) { + this._telemetryReporter = telemetryReporter; + this._experimentationService = createTasExperimentationService(this._telemetryReporter, id, version, globalState); + } + + public async getTreatmentVariable(name: K, defaultValue: ExperimentTypes[K]): Promise { + const experimentationService = this._experimentationService; + try { + const treatmentVariable = await experimentationService.getTreatmentVariableAsync("vscode", name, /*checkCache*/ true) as ExperimentTypes[K]; + return treatmentVariable ?? defaultValue; + } + catch { + return defaultValue; + } + } +} + +function createTasExperimentationService( + reporter: tas.IExperimentationTelemetry, + id: string, + version: string, + globalState: vscode.Memento, +): tas.IExperimentationService { + let targetPopulation: tas.TargetPopulation; + switch (vscode.env.uriScheme) { + case "vscode": + targetPopulation = tas.TargetPopulation.Public; + break; + case "vscode-insiders": + targetPopulation = tas.TargetPopulation.Insiders; + break; + case "vscode-exploration": + targetPopulation = tas.TargetPopulation.Internal; + break; + case "code-oss": + targetPopulation = tas.TargetPopulation.Team; + break; + default: + targetPopulation = tas.TargetPopulation.Public; + break; + } + + const experimentationService = tas.getExperimentationService(id, version, targetPopulation, reporter, globalState); + return experimentationService; +} diff --git a/packages/vscode-typescript/src/extension.ts b/packages/vscode-typescript/src/extension.ts index 7bff6514e4397..0c8ab51e03bf4 100644 --- a/packages/vscode-typescript/src/extension.ts +++ b/packages/vscode-typescript/src/extension.ts @@ -1,266 +1,266 @@ -import * as vscode from "vscode"; - -import { - registerEnablementCommands, - updateUseTsgoSetting, -} from "./commands"; -import type { ContentMapperContribution } from "./contentMapperContributions"; -import { - aiConnectionString, - getExplicitConfigTarget, - getUseTsgo, - getWinningTsgoConfigKey, - needsExtHostRestartOnChange, -} from "./util"; - -import { TelemetryReporter as VSCodeTelemetryReporter } from "@vscode/extension-telemetry"; -import { - promptUseWorkspaceVersion, - SessionManager, -} from "./session"; - -import { ExperimentationService } from "./experimentationService"; -import { createTelemetryReporter } from "./telemetryReporting"; - -import assert from "node:assert"; - -export interface ExtensionAPI { - onLanguageServerInitialized: vscode.Event; - initializeAPIConnection(pipe?: string): Promise; - registerContentMappers(contributorId: string, contributions: readonly ContentMapperContribution[]): vscode.Disposable; -} - -export async function activate(context: vscode.ExtensionContext): Promise { - await vscode.commands.executeCommand("setContext", "typescript.native-preview.serverRunning", false); - - const telemetryReporter = createTelemetryReporter(new VSCodeTelemetryReporter(aiConnectionString)); - context.subscriptions.push(telemetryReporter); - - const version = context.extension.packageJSON.version; - assert(typeof version === "string"); - // Constructing the experimentation service actually sets shared properties - // so that events include context on treatments/flights. - // If we actually need to read treatment variables we would hold onto this instance, - // but for now we just construct it to ensure shared properties are set for telemetry. - void new ExperimentationService(telemetryReporter, context.extension.id, version, context.globalState); - - const output = vscode.window.createOutputChannel("TypeScript 7", { log: true }); - context.subscriptions.push(output); - - const languageServerInitializedEventEmitter = new vscode.EventEmitter(); - context.subscriptions.push(languageServerInitializedEventEmitter); - - const sessionManager = new SessionManager(context, output, languageServerInitializedEventEmitter, telemetryReporter); - context.subscriptions.push(sessionManager); - registerEnablementCommands(context, telemetryReporter, () => sessionManager.stop()); - - let pluginWarningShown = false; - const onDidChangeExtensions = vscode.extensions.onDidChange(() => { - if (sessionManager.currentSession) { - warnAboutTsServerPlugins(context, output); - } - }); - context.subscriptions.push(onDidChangeExtensions); - - function onLanguageServerInitialized(listener: () => void): vscode.Disposable { - if (sessionManager.currentSession?.client.isInitialized) { - listener(); - } - return languageServerInitializedEventEmitter.event(listener); - } - - async function startNativeServer(): Promise { - await sessionManager.start(context); - warnAboutTsServerPlugins(context, output); - promptUseWorkspaceVersion(context).catch(err => { - output.appendLine(vscode.l10n.t(`Error prompting to use workspace version: {0}`, String(err))); - }); - } - - const api: ExtensionAPI = { - onLanguageServerInitialized: onLanguageServerInitialized, - async initializeAPIConnection(pipe?: string): Promise { - return sessionManager.initializeAPIConnection(pipe); - }, - registerContentMappers(contributorId, contributions): vscode.Disposable { - return sessionManager.registerContentMappers(contributorId, contributions); - }, - }; - - let configChangeTimeout: ReturnType | undefined; - context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(event => { - if ( - event.affectsConfiguration("typescript.experimental.useTsgo") - || event.affectsConfiguration("js/ts.experimental.useTsgo") - ) { - // Debounce to coalesce rapid events when both settings are updated together. - clearTimeout(configChangeTimeout); - configChangeTimeout = setTimeout(async () => { - if (needsExtHostRestartOnChange()) { - const selected = await vscode.window.showInformationMessage(vscode.l10n.t("TypeScript 7 setting has changed. Restart extensions to apply changes."), vscode.l10n.t("Restart Extensions")); - if (selected) { - vscode.commands.executeCommand("workbench.action.restartExtensionHost"); - } - } - else { - if (await shouldStartTsgo(context)) { - await sessionManager.restart(context); - } - else { - await sessionManager.stop(); - } - } - }, 100); - } - })); - context.subscriptions.push({ dispose: () => clearTimeout(configChangeTimeout) }); - - const hasOnboardedTsgoStateKey = "hasOnboardedTsgo"; - const shouldOnboardTsgo = !context.globalState.get(hasOnboardedTsgoStateKey); - if (shouldOnboardTsgo) { - await context.globalState.update(hasOnboardedTsgoStateKey, true); - } - - const useTsgo = getUseTsgo(); - if (useTsgo === undefined && shouldOnboardTsgo) { - updateUseTsgoSetting(true); - return api; - } - - const tsExtension = vscode.extensions.getExtension("vscode.typescript-language-features"); - if (context.extensionMode === vscode.ExtensionMode.Development) { - if (!tsExtension) { - if (useTsgo === false) { - vscode.window.showWarningMessage( - vscode.l10n.t("The built-in TypeScript extension is disabled. Sync launch.json with launch.template.json to reenable."), - vscode.l10n.t("OK"), - ); - return api; - } - } - else if (useTsgo === false) { - const settingName = getWinningTsgoConfigKey() ?? "js/ts.experimental.useTsgo"; - const enableSettingString = vscode.l10n.t("Enable Setting"); - vscode.window.showWarningMessage( - vscode.l10n.t(`TypeScript 7 is running in development mode with "{0}" set to false.`, settingName), - enableSettingString, - vscode.l10n.t("Ignore"), - ).then(selected => { - if (selected === enableSettingString) { - vscode.commands.executeCommand("typescript.native-preview.enable"); - } - }); - return api; - } - } - else if (useTsgo !== true) { - output.appendLine(vscode.l10n.t("TypeScript 7 is disabled. Select 'TypeScript: Enable TypeScript 7' in the command palette to enable it.")); - return api; - } - - async function shouldStartTsgo(context: vscode.ExtensionContext): Promise { - const useTsgo = getUseTsgo(); - if (context.extensionMode === vscode.ExtensionMode.Development && !vscode.extensions.getExtension("vscode.typescript-language-features")) { - return useTsgo !== false; - } - return useTsgo === true; - } - - await startNativeServer(); - - return api; - - async function warnAboutTsServerPlugins(context: vscode.ExtensionContext, output: vscode.LogOutputChannel): Promise { - // Never show more than once. - if (pluginWarningShown) { - return; - } - - if (getUseTsgo() !== true) { - return; - } - - const settingName = getWinningTsgoConfigKey(); - assert(settingName !== undefined, "Expected some useTsgo configuration to be explicitly set."); - - const target = getExplicitConfigTarget(vscode.workspace.getConfiguration(), settingName); - assert(target !== undefined, "Expected an explicit configuration target."); - - // We only want to warn about plugins when Corsa is enabled in a user's global configuration. - // Setting in the workspace is an indication that the user is aware of the potential issues. - if (target !== vscode.ConfigurationTarget.Global) { - return; - } - - const hasWorkspaceFolder = !!vscode.workspace.workspaceFolders?.length; - - if (context.globalState.get(pluginWarningDismissedKey)) { - return; - } - - const pluginExtensions = getExtensionsWithTsServerPlugins(); - if (pluginExtensions.length === 0) { - return; - } - - const uniqueExtensionNames = [...new Set(pluginExtensions.map(p => p.extensionId))]; - const extensionNames = uniqueExtensionNames.join(", "); - output.appendLine(`Extensions contributing tsserver plugins that will not apply with TypeScript 7: ${extensionNames}`); - - const message = uniqueExtensionNames.length === 1 - // Pick the first extension & plugin, even though extensions can have multiple plugins - ? vscode.l10n.t(`TypeScript server plugins from the "{0}" extension will not be loaded because TypeScript 7 is enabled globally.`, pluginExtensions[0].extensionId) - : vscode.l10n.t(`{0} extensions contribute TypeScript server plugins that will not be loaded because TypeScript 7 is enabled globally: {1}`, uniqueExtensionNames.length, extensionNames); - - const ok = vscode.l10n.t("OK"); - const disableInWorkspace = vscode.l10n.t("Disable Native Preview in Workspace"); - const dontShowAgain = vscode.l10n.t("Don't Show Again"); - - const options = [ok]; - if (hasWorkspaceFolder) { - options.push(disableInWorkspace); - } - options.push(dontShowAgain); - - // Make sure we never show this message again in this session. - pluginWarningShown = true; - - const selected = await vscode.window.showWarningMessage(message, ...options); - if (selected === disableInWorkspace) { - await sessionManager.stop(); - await vscode.workspace.getConfiguration("js/ts").update("experimental.useTsgo", false, vscode.ConfigurationTarget.Workspace); - } - else if (selected === dontShowAgain) { - await context.globalState.update(pluginWarningDismissedKey, true); - } - } -} - -const suppressedPluginExtensionIds: Set = new Set([ - "github.copilot", - "github.copilot-chat", -]); - -const pluginWarningDismissedKey = "tsServerPluginWarningDismissed"; - -function getExtensionsWithTsServerPlugins(): { extensionId: string; pluginName: string; }[] { - const results: { extensionId: string; pluginName: string; }[] = []; - // Despite its name, 'all' actually only seems to contain active extensions. - for (const extension of vscode.extensions.all) { - // Ignore built-in extensions that always try to contribute plugins. - const id = extension.id.toLowerCase(); - if (suppressedPluginExtensionIds.has(id)) { - continue; - } - - const contributes = extension.packageJSON?.contributes; - if (contributes && Array.isArray(contributes.typescriptServerPlugins)) { - for (const plugin of contributes.typescriptServerPlugins) { - if (typeof plugin.name === "string" && plugin.name) { - results.push({ extensionId: extension.id, pluginName: plugin.name }); - } - } - } - } - return results; -} +import * as vscode from "vscode"; + +import { + registerEnablementCommands, + updateUseTsgoSetting, +} from "./commands"; +import type { ContentMapperContribution } from "./contentMapperContributions"; +import { + aiConnectionString, + getExplicitConfigTarget, + getUseTsgo, + getWinningTsgoConfigKey, + needsExtHostRestartOnChange, +} from "./util"; + +import { TelemetryReporter as VSCodeTelemetryReporter } from "@vscode/extension-telemetry"; +import { + promptUseWorkspaceVersion, + SessionManager, +} from "./session"; + +import { ExperimentationService } from "./experimentationService"; +import { createTelemetryReporter } from "./telemetryReporting"; + +import assert from "node:assert"; + +export interface ExtensionAPI { + onLanguageServerInitialized: vscode.Event; + initializeAPIConnection(pipe?: string): Promise; + registerContentMappers(contributorId: string, contributions: readonly ContentMapperContribution[]): vscode.Disposable; +} + +export async function activate(context: vscode.ExtensionContext): Promise { + await vscode.commands.executeCommand("setContext", "typescript.native-preview.serverRunning", false); + + const telemetryReporter = createTelemetryReporter(new VSCodeTelemetryReporter(aiConnectionString)); + context.subscriptions.push(telemetryReporter); + + const version = context.extension.packageJSON.version; + assert(typeof version === "string"); + // Constructing the experimentation service actually sets shared properties + // so that events include context on treatments/flights. + // If we actually need to read treatment variables we would hold onto this instance, + // but for now we just construct it to ensure shared properties are set for telemetry. + void new ExperimentationService(telemetryReporter, context.extension.id, version, context.globalState); + + const output = vscode.window.createOutputChannel("TypeScript 7", { log: true }); + context.subscriptions.push(output); + + const languageServerInitializedEventEmitter = new vscode.EventEmitter(); + context.subscriptions.push(languageServerInitializedEventEmitter); + + const sessionManager = new SessionManager(context, output, languageServerInitializedEventEmitter, telemetryReporter); + context.subscriptions.push(sessionManager); + registerEnablementCommands(context, telemetryReporter, () => sessionManager.stop()); + + let pluginWarningShown = false; + const onDidChangeExtensions = vscode.extensions.onDidChange(() => { + if (sessionManager.currentSession) { + warnAboutTsServerPlugins(context, output); + } + }); + context.subscriptions.push(onDidChangeExtensions); + + function onLanguageServerInitialized(listener: () => void): vscode.Disposable { + if (sessionManager.currentSession?.client.isInitialized) { + listener(); + } + return languageServerInitializedEventEmitter.event(listener); + } + + async function startNativeServer(): Promise { + await sessionManager.start(context); + warnAboutTsServerPlugins(context, output); + promptUseWorkspaceVersion(context).catch(err => { + output.appendLine(vscode.l10n.t(`Error prompting to use workspace version: {0}`, String(err))); + }); + } + + const api: ExtensionAPI = { + onLanguageServerInitialized: onLanguageServerInitialized, + async initializeAPIConnection(pipe?: string): Promise { + return sessionManager.initializeAPIConnection(pipe); + }, + registerContentMappers(contributorId, contributions): vscode.Disposable { + return sessionManager.registerContentMappers(contributorId, contributions); + }, + }; + + let configChangeTimeout: ReturnType | undefined; + context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(event => { + if ( + event.affectsConfiguration("typescript.experimental.useTsgo") + || event.affectsConfiguration("js/ts.experimental.useTsgo") + ) { + // Debounce to coalesce rapid events when both settings are updated together. + clearTimeout(configChangeTimeout); + configChangeTimeout = setTimeout(async () => { + if (needsExtHostRestartOnChange()) { + const selected = await vscode.window.showInformationMessage(vscode.l10n.t("TypeScript 7 setting has changed. Restart extensions to apply changes."), vscode.l10n.t("Restart Extensions")); + if (selected) { + vscode.commands.executeCommand("workbench.action.restartExtensionHost"); + } + } + else { + if (await shouldStartTsgo(context)) { + await sessionManager.restart(context); + } + else { + await sessionManager.stop(); + } + } + }, 100); + } + })); + context.subscriptions.push({ dispose: () => clearTimeout(configChangeTimeout) }); + + const hasOnboardedTsgoStateKey = "hasOnboardedTsgo"; + const shouldOnboardTsgo = !context.globalState.get(hasOnboardedTsgoStateKey); + if (shouldOnboardTsgo) { + await context.globalState.update(hasOnboardedTsgoStateKey, true); + } + + const useTsgo = getUseTsgo(); + if (useTsgo === undefined && shouldOnboardTsgo) { + updateUseTsgoSetting(true); + return api; + } + + const tsExtension = vscode.extensions.getExtension("vscode.typescript-language-features"); + if (context.extensionMode === vscode.ExtensionMode.Development) { + if (!tsExtension) { + if (useTsgo === false) { + vscode.window.showWarningMessage( + vscode.l10n.t("The built-in TypeScript extension is disabled. Sync launch.json with launch.template.json to reenable."), + vscode.l10n.t("OK"), + ); + return api; + } + } + else if (useTsgo === false) { + const settingName = getWinningTsgoConfigKey() ?? "js/ts.experimental.useTsgo"; + const enableSettingString = vscode.l10n.t("Enable Setting"); + vscode.window.showWarningMessage( + vscode.l10n.t(`TypeScript 7 is running in development mode with "{0}" set to false.`, settingName), + enableSettingString, + vscode.l10n.t("Ignore"), + ).then(selected => { + if (selected === enableSettingString) { + vscode.commands.executeCommand("typescript.native-preview.enable"); + } + }); + return api; + } + } + else if (useTsgo !== true) { + output.appendLine(vscode.l10n.t("TypeScript 7 is disabled. Select 'TypeScript: Enable TypeScript 7' in the command palette to enable it.")); + return api; + } + + async function shouldStartTsgo(context: vscode.ExtensionContext): Promise { + const useTsgo = getUseTsgo(); + if (context.extensionMode === vscode.ExtensionMode.Development && !vscode.extensions.getExtension("vscode.typescript-language-features")) { + return useTsgo !== false; + } + return useTsgo === true; + } + + await startNativeServer(); + + return api; + + async function warnAboutTsServerPlugins(context: vscode.ExtensionContext, output: vscode.LogOutputChannel): Promise { + // Never show more than once. + if (pluginWarningShown) { + return; + } + + if (getUseTsgo() !== true) { + return; + } + + const settingName = getWinningTsgoConfigKey(); + assert(settingName !== undefined, "Expected some useTsgo configuration to be explicitly set."); + + const target = getExplicitConfigTarget(vscode.workspace.getConfiguration(), settingName); + assert(target !== undefined, "Expected an explicit configuration target."); + + // We only want to warn about plugins when Corsa is enabled in a user's global configuration. + // Setting in the workspace is an indication that the user is aware of the potential issues. + if (target !== vscode.ConfigurationTarget.Global) { + return; + } + + const hasWorkspaceFolder = !!vscode.workspace.workspaceFolders?.length; + + if (context.globalState.get(pluginWarningDismissedKey)) { + return; + } + + const pluginExtensions = getExtensionsWithTsServerPlugins(); + if (pluginExtensions.length === 0) { + return; + } + + const uniqueExtensionNames = [...new Set(pluginExtensions.map(p => p.extensionId))]; + const extensionNames = uniqueExtensionNames.join(", "); + output.appendLine(`Extensions contributing tsserver plugins that will not apply with TypeScript 7: ${extensionNames}`); + + const message = uniqueExtensionNames.length === 1 + // Pick the first extension & plugin, even though extensions can have multiple plugins + ? vscode.l10n.t(`TypeScript server plugins from the "{0}" extension will not be loaded because TypeScript 7 is enabled globally.`, pluginExtensions[0].extensionId) + : vscode.l10n.t(`{0} extensions contribute TypeScript server plugins that will not be loaded because TypeScript 7 is enabled globally: {1}`, uniqueExtensionNames.length, extensionNames); + + const ok = vscode.l10n.t("OK"); + const disableInWorkspace = vscode.l10n.t("Disable Native Preview in Workspace"); + const dontShowAgain = vscode.l10n.t("Don't Show Again"); + + const options = [ok]; + if (hasWorkspaceFolder) { + options.push(disableInWorkspace); + } + options.push(dontShowAgain); + + // Make sure we never show this message again in this session. + pluginWarningShown = true; + + const selected = await vscode.window.showWarningMessage(message, ...options); + if (selected === disableInWorkspace) { + await sessionManager.stop(); + await vscode.workspace.getConfiguration("js/ts").update("experimental.useTsgo", false, vscode.ConfigurationTarget.Workspace); + } + else if (selected === dontShowAgain) { + await context.globalState.update(pluginWarningDismissedKey, true); + } + } +} + +const suppressedPluginExtensionIds: Set = new Set([ + "github.copilot", + "github.copilot-chat", +]); + +const pluginWarningDismissedKey = "tsServerPluginWarningDismissed"; + +function getExtensionsWithTsServerPlugins(): { extensionId: string; pluginName: string; }[] { + const results: { extensionId: string; pluginName: string; }[] = []; + // Despite its name, 'all' actually only seems to contain active extensions. + for (const extension of vscode.extensions.all) { + // Ignore built-in extensions that always try to contribute plugins. + const id = extension.id.toLowerCase(); + if (suppressedPluginExtensionIds.has(id)) { + continue; + } + + const contributes = extension.packageJSON?.contributes; + if (contributes && Array.isArray(contributes.typescriptServerPlugins)) { + for (const plugin of contributes.typescriptServerPlugins) { + if (typeof plugin.name === "string" && plugin.name) { + results.push({ extensionId: extension.id, pluginName: plugin.name }); + } + } + } + } + return results; +} diff --git a/packages/vscode-typescript/src/languageFeatures/documentHighlight.ts b/packages/vscode-typescript/src/languageFeatures/documentHighlight.ts index 65d7f7cc43b47..5e4b41a2ad656 100644 --- a/packages/vscode-typescript/src/languageFeatures/documentHighlight.ts +++ b/packages/vscode-typescript/src/languageFeatures/documentHighlight.ts @@ -1,85 +1,85 @@ -import * as vscode from "vscode"; -import { - DocumentHighlight, - LanguageClient, -} from "vscode-languageclient/node"; - -const multiDocumentHighlightMethod = "custom/textDocument/multiDocumentHighlight"; - -interface MultiDocumentHighlightParams { - textDocument: { uri: string; }; - position: { line: number; character: number; }; - filesToSearch: string[]; -} - -interface MultiDocumentHighlightItem { - uri: string; - highlights: DocumentHighlight[]; -} - -class MultiDocumentHighlightProvider implements vscode.MultiDocumentHighlightProvider { - constructor(private readonly client: LanguageClient) {} - - async provideMultiDocumentHighlights( - document: vscode.TextDocument, - position: vscode.Position, - otherDocuments: vscode.TextDocument[], - token: vscode.CancellationToken, - ): Promise { - const allFiles = [document, ...otherDocuments] - .map(doc => this.client.code2ProtocolConverter.asUri(doc.uri)) - .filter(file => !!file); - - if (allFiles.length === 0) { - return []; - } - - const params: MultiDocumentHighlightParams = { - textDocument: this.client.code2ProtocolConverter.asTextDocumentIdentifier(document), - position: this.client.code2ProtocolConverter.asPosition(position), - filesToSearch: allFiles, - }; - - let response: MultiDocumentHighlightItem[] | null; - try { - response = await this.client.sendRequest(multiDocumentHighlightMethod, params, token); - } - catch (error) { - return []; - } - - if (!response || token.isCancellationRequested) { - return []; - } - - // MultiDocumentHighlight is proposed API; guard against missing or changed constructor. - try { - return response.map(item => - new vscode.MultiDocumentHighlight( - vscode.Uri.parse(item.uri), - item.highlights.map(h => this.client.protocol2CodeConverter.asDocumentHighlight(h)), - ) - ); - } - catch { - return []; - } - } -} - -export function registerMultiDocumentHighlightFeature( - selector: vscode.DocumentSelector, - client: LanguageClient, -): vscode.Disposable { - try { - const experimental = client.initializeResult?.capabilities?.experimental as { customMultiDocumentHighlightProvider?: boolean; } | undefined; - // registerMultiDocumentHighlightProvider is proposed API; guard against it not being available. - if (!experimental?.customMultiDocumentHighlightProvider || typeof vscode.languages.registerMultiDocumentHighlightProvider !== "function") { - return { dispose() {} }; - } - return vscode.languages.registerMultiDocumentHighlightProvider(selector, new MultiDocumentHighlightProvider(client)); - } - catch { - return { dispose() {} }; - } -} +import * as vscode from "vscode"; +import { + DocumentHighlight, + LanguageClient, +} from "vscode-languageclient/node"; + +const multiDocumentHighlightMethod = "custom/textDocument/multiDocumentHighlight"; + +interface MultiDocumentHighlightParams { + textDocument: { uri: string; }; + position: { line: number; character: number; }; + filesToSearch: string[]; +} + +interface MultiDocumentHighlightItem { + uri: string; + highlights: DocumentHighlight[]; +} + +class MultiDocumentHighlightProvider implements vscode.MultiDocumentHighlightProvider { + constructor(private readonly client: LanguageClient) {} + + async provideMultiDocumentHighlights( + document: vscode.TextDocument, + position: vscode.Position, + otherDocuments: vscode.TextDocument[], + token: vscode.CancellationToken, + ): Promise { + const allFiles = [document, ...otherDocuments] + .map(doc => this.client.code2ProtocolConverter.asUri(doc.uri)) + .filter(file => !!file); + + if (allFiles.length === 0) { + return []; + } + + const params: MultiDocumentHighlightParams = { + textDocument: this.client.code2ProtocolConverter.asTextDocumentIdentifier(document), + position: this.client.code2ProtocolConverter.asPosition(position), + filesToSearch: allFiles, + }; + + let response: MultiDocumentHighlightItem[] | null; + try { + response = await this.client.sendRequest(multiDocumentHighlightMethod, params, token); + } + catch (error) { + return []; + } + + if (!response || token.isCancellationRequested) { + return []; + } + + // MultiDocumentHighlight is proposed API; guard against missing or changed constructor. + try { + return response.map(item => + new vscode.MultiDocumentHighlight( + vscode.Uri.parse(item.uri), + item.highlights.map(h => this.client.protocol2CodeConverter.asDocumentHighlight(h)), + ) + ); + } + catch { + return []; + } + } +} + +export function registerMultiDocumentHighlightFeature( + selector: vscode.DocumentSelector, + client: LanguageClient, +): vscode.Disposable { + try { + const experimental = client.initializeResult?.capabilities?.experimental as { customMultiDocumentHighlightProvider?: boolean; } | undefined; + // registerMultiDocumentHighlightProvider is proposed API; guard against it not being available. + if (!experimental?.customMultiDocumentHighlightProvider || typeof vscode.languages.registerMultiDocumentHighlightProvider !== "function") { + return { dispose() {} }; + } + return vscode.languages.registerMultiDocumentHighlightProvider(selector, new MultiDocumentHighlightProvider(client)); + } + catch { + return { dispose() {} }; + } +} diff --git a/packages/vscode-typescript/src/languageFeatures/hover.ts b/packages/vscode-typescript/src/languageFeatures/hover.ts index 13b44d0f8d700..042b8fcb117b6 100644 --- a/packages/vscode-typescript/src/languageFeatures/hover.ts +++ b/packages/vscode-typescript/src/languageFeatures/hover.ts @@ -1,81 +1,81 @@ -import * as vscode from "vscode"; -import { - Hover, - HoverRequest, - LanguageClient, - TextDocumentPositionParams, -} from "vscode-languageclient/node"; - -interface HoverResult extends Hover { - canIncreaseVerbosity?: boolean; -} - -interface HoverParamsWithVerbosity extends TextDocumentPositionParams { - verbosityLevel?: number; -} - -class VerboseHoverProvider implements vscode.HoverProvider { - private lastHoverAndLevel: [vscode.Hover, number] | undefined; - - constructor(private readonly client: LanguageClient) {} - - async provideHover( - document: vscode.TextDocument, - position: vscode.Position, - token: vscode.CancellationToken, - context?: vscode.HoverContext, - ): Promise { - // HoverContext and VerboseHover are proposed API; guard against missing or unexpected properties. - const verbosityDelta = typeof context?.verbosityDelta === "number" ? context.verbosityDelta : undefined; - const previousHover = context?.previousHover instanceof vscode.Hover ? context.previousHover : undefined; - const verbosityLevel = verbosityDelta !== undefined ? Math.max(0, this.getPreviousLevel(previousHover) + verbosityDelta) : undefined; - - const params: HoverParamsWithVerbosity = { - ...this.client.code2ProtocolConverter.asTextDocumentPositionParams(document, position), - verbosityLevel, - }; - - let response: HoverResult | null; - try { - response = await this.client.sendRequest(HoverRequest.type, params, token); - } - catch (error) { - return this.client.handleFailedRequest(HoverRequest.type, token, error, null) ?? undefined; - } - - if (!response || token.isCancellationRequested) { - return undefined; - } - - const hover = this.client.protocol2CodeConverter.asHover(response); - // VerboseHover is proposed API; guard against missing or changed constructor. - try { - const verboseHover = new vscode.VerboseHover( - hover.contents, - hover.range, - response.canIncreaseVerbosity, - (verbosityLevel ?? 0) > 0, - ); - - this.lastHoverAndLevel = [verboseHover, verbosityLevel ?? 0]; - return verboseHover; - } - catch { - return hover; - } - } - - private getPreviousLevel(previousHover: vscode.Hover | undefined): number { - if (previousHover && this.lastHoverAndLevel && this.lastHoverAndLevel[0] === previousHover) { - return this.lastHoverAndLevel[1]; - } - return 0; - } -} - -export function registerHoverFeature( - selector: vscode.DocumentSelector, - client: LanguageClient, -): vscode.Disposable { - return vscode.languages.registerHoverProvider(selector, new VerboseHoverProvider(client)); -} +import * as vscode from "vscode"; +import { + Hover, + HoverRequest, + LanguageClient, + TextDocumentPositionParams, +} from "vscode-languageclient/node"; + +interface HoverResult extends Hover { + canIncreaseVerbosity?: boolean; +} + +interface HoverParamsWithVerbosity extends TextDocumentPositionParams { + verbosityLevel?: number; +} + +class VerboseHoverProvider implements vscode.HoverProvider { + private lastHoverAndLevel: [vscode.Hover, number] | undefined; + + constructor(private readonly client: LanguageClient) {} + + async provideHover( + document: vscode.TextDocument, + position: vscode.Position, + token: vscode.CancellationToken, + context?: vscode.HoverContext, + ): Promise { + // HoverContext and VerboseHover are proposed API; guard against missing or unexpected properties. + const verbosityDelta = typeof context?.verbosityDelta === "number" ? context.verbosityDelta : undefined; + const previousHover = context?.previousHover instanceof vscode.Hover ? context.previousHover : undefined; + const verbosityLevel = verbosityDelta !== undefined ? Math.max(0, this.getPreviousLevel(previousHover) + verbosityDelta) : undefined; + + const params: HoverParamsWithVerbosity = { + ...this.client.code2ProtocolConverter.asTextDocumentPositionParams(document, position), + verbosityLevel, + }; + + let response: HoverResult | null; + try { + response = await this.client.sendRequest(HoverRequest.type, params, token); + } + catch (error) { + return this.client.handleFailedRequest(HoverRequest.type, token, error, null) ?? undefined; + } + + if (!response || token.isCancellationRequested) { + return undefined; + } + + const hover = this.client.protocol2CodeConverter.asHover(response); + // VerboseHover is proposed API; guard against missing or changed constructor. + try { + const verboseHover = new vscode.VerboseHover( + hover.contents, + hover.range, + response.canIncreaseVerbosity, + (verbosityLevel ?? 0) > 0, + ); + + this.lastHoverAndLevel = [verboseHover, verbosityLevel ?? 0]; + return verboseHover; + } + catch { + return hover; + } + } + + private getPreviousLevel(previousHover: vscode.Hover | undefined): number { + if (previousHover && this.lastHoverAndLevel && this.lastHoverAndLevel[0] === previousHover) { + return this.lastHoverAndLevel[1]; + } + return 0; + } +} + +export function registerHoverFeature( + selector: vscode.DocumentSelector, + client: LanguageClient, +): vscode.Disposable { + return vscode.languages.registerHoverProvider(selector, new VerboseHoverProvider(client)); +} diff --git a/packages/vscode-typescript/src/languageFeatures/onAutoInsert.ts b/packages/vscode-typescript/src/languageFeatures/onAutoInsert.ts index c6d7129ca0c9f..8fe521a5d5aaf 100644 --- a/packages/vscode-typescript/src/languageFeatures/onAutoInsert.ts +++ b/packages/vscode-typescript/src/languageFeatures/onAutoInsert.ts @@ -1,181 +1,181 @@ -import * as vscode from "vscode"; -import { - InsertTextFormat, - LanguageClient, - Position, - TextEdit, -} from "vscode-languageclient/node"; -import { readUnifiedConfig } from "../util"; -import { - Condition, - conditionalRegistration, -} from "./util/dependentRegistration"; - -interface VsOnAutoInsertParams { - _vs_textDocument: { uri: string; }; - _vs_position: Position; - _vs_ch: string; -} - -interface VsOnAutoInsertResponse { - _vs_textEditFormat: InsertTextFormat; - _vs_textEdit: TextEdit; -} - -interface VsOnAutoInsertOptions { - _vs_triggerCharacters?: string[]; -} - -interface VsServerCapabilities { - _vs_onAutoInsertProvider?: VsOnAutoInsertOptions; -} - -class AutoInsert { - private cancel: vscode.CancellationTokenSource | undefined; - private readonly subscription: vscode.Disposable; - - constructor( - private readonly client: LanguageClient, - private readonly triggerCharacters: ReadonlySet, - ) { - this.subscription = vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this); - } - - dispose() { - this.subscription.dispose(); - this.cancel?.cancel(); - this.cancel?.dispose(); - this.cancel = undefined; - } - - private async onDidChangeTextDocument({ document, contentChanges, reason }: vscode.TextDocumentChangeEvent) { - if ( - contentChanges.length === 0 - || reason === vscode.TextDocumentChangeReason.Undo - || reason === vscode.TextDocumentChangeReason.Redo - ) { - return; - } - - const activeEditor = vscode.window.activeTextEditor; - if (activeEditor?.document !== document) { - return; - } - - const lastChange = contentChanges[contentChanges.length - 1]; - const lastCharacter = lastChange.text.charAt(lastChange.text.length - 1); - if (lastChange.rangeLength > 0 || !this.triggerCharacters.has(lastCharacter)) { - return; - } - - // Avoid firing again when the user types a trigger character immediately - // after another (e.g. `>>` in JSX). - if (lastChange.range.start.character > 0) { - const priorRange = new vscode.Range(lastChange.range.start.translate(0, -1), lastChange.range.start); - if (this.triggerCharacters.has(document.getText(priorRange))) { - return; - } - } - - // Compute the post-insert cursor position from the change itself; - // `activeEditor.selection.active` is not yet updated when this event fires. - const addedLines = lastChange.text.split(/\r\n|\n/g); - const position = addedLines.length === 1 - ? lastChange.range.start.translate(0, lastChange.text.length) - : new vscode.Position( - lastChange.range.start.line + addedLines.length - 1, - addedLines[addedLines.length - 1].length, - ); - - this.cancel?.cancel(); - this.cancel?.dispose(); - this.cancel = new vscode.CancellationTokenSource(); - const token = this.cancel.token; - const startingVersion = document.version; - - let response: VsOnAutoInsertResponse | null; - try { - response = await this.client.sendRequest( - "textDocument/_vs_onAutoInsert", - { - _vs_textDocument: this.client.code2ProtocolConverter.asTextDocumentIdentifier(document), - _vs_position: this.client.code2ProtocolConverter.asPosition(position), - _vs_ch: lastCharacter, - } satisfies VsOnAutoInsertParams, - token, - ); - } - catch { - return; - } - - if (!response || token.isCancellationRequested || document.version !== startingVersion) { - return; - } - - const edit = this.client.protocol2CodeConverter.asTextEdit(response._vs_textEdit); - // If the same trigger character was typed at every cursor in the same change, - // apply the snippet/edit at every cursor. - const cursors = activeEditor.selections.map(s => s.active); - const insertionRanges = cursors.some(p => p.isEqual(position)) ? cursors : edit.range; - // Swallow rejections from the editor (e.g., document changed under us). These - // return Thenables that can otherwise become unhandled rejections. - const swallow = () => undefined; - if (response._vs_textEditFormat === InsertTextFormat.Snippet) { - void Promise.resolve(activeEditor.insertSnippet(new vscode.SnippetString(edit.newText), insertionRanges)).then(swallow, swallow); - } - else { - void Promise.resolve(activeEditor.edit(b => { - if (Array.isArray(insertionRanges)) { - for (const p of insertionRanges) b.insert(p, edit.newText); - } - else { - b.replace(insertionRanges, edit.newText); - } - })).then(swallow, swallow); - } - } -} - -function isAutoClosingTagsEnabled(scope: vscode.TextDocument): boolean { - // Use the document's own language ("typescript" / "javascript" / "*react") to - // pick the fallback section so per-language overrides apply correctly. - const fallbackSection = scope.languageId === "javascript" || scope.languageId === "javascriptreact" - ? "javascript" - : "typescript"; - return readUnifiedConfig("autoClosingTags.enabled", fallbackSection, "autoClosingTags", scope, true); -} - -function requireActiveDocumentSetting(selector: vscode.DocumentSelector) { - return new Condition( - () => { - const activeDocument = vscode.window.activeTextEditor?.document; - if (!activeDocument || !vscode.languages.match(selector, activeDocument)) { - return false; - } - return isAutoClosingTagsEnabled(activeDocument); - }, - handler => - vscode.Disposable.from( - vscode.window.onDidChangeActiveTextEditor(handler), - vscode.workspace.onDidOpenTextDocument(handler), - vscode.workspace.onDidChangeConfiguration(handler), - ), - ); -} - -export function registerOnAutoInsertFeature( - selector: vscode.DocumentSelector, - client: LanguageClient, -): vscode.Disposable { - const capabilities = client.initializeResult?.capabilities as VsServerCapabilities | undefined; - const triggerCharacters = capabilities?._vs_onAutoInsertProvider?._vs_triggerCharacters; - if (!triggerCharacters?.length) { - return vscode.Disposable.from(); - } - const set = new Set(triggerCharacters); - return conditionalRegistration( - [requireActiveDocumentSetting(selector)], - () => new AutoInsert(client, set), - ); -} +import * as vscode from "vscode"; +import { + InsertTextFormat, + LanguageClient, + Position, + TextEdit, +} from "vscode-languageclient/node"; +import { readUnifiedConfig } from "../util"; +import { + Condition, + conditionalRegistration, +} from "./util/dependentRegistration"; + +interface VsOnAutoInsertParams { + _vs_textDocument: { uri: string; }; + _vs_position: Position; + _vs_ch: string; +} + +interface VsOnAutoInsertResponse { + _vs_textEditFormat: InsertTextFormat; + _vs_textEdit: TextEdit; +} + +interface VsOnAutoInsertOptions { + _vs_triggerCharacters?: string[]; +} + +interface VsServerCapabilities { + _vs_onAutoInsertProvider?: VsOnAutoInsertOptions; +} + +class AutoInsert { + private cancel: vscode.CancellationTokenSource | undefined; + private readonly subscription: vscode.Disposable; + + constructor( + private readonly client: LanguageClient, + private readonly triggerCharacters: ReadonlySet, + ) { + this.subscription = vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this); + } + + dispose() { + this.subscription.dispose(); + this.cancel?.cancel(); + this.cancel?.dispose(); + this.cancel = undefined; + } + + private async onDidChangeTextDocument({ document, contentChanges, reason }: vscode.TextDocumentChangeEvent) { + if ( + contentChanges.length === 0 + || reason === vscode.TextDocumentChangeReason.Undo + || reason === vscode.TextDocumentChangeReason.Redo + ) { + return; + } + + const activeEditor = vscode.window.activeTextEditor; + if (activeEditor?.document !== document) { + return; + } + + const lastChange = contentChanges[contentChanges.length - 1]; + const lastCharacter = lastChange.text.charAt(lastChange.text.length - 1); + if (lastChange.rangeLength > 0 || !this.triggerCharacters.has(lastCharacter)) { + return; + } + + // Avoid firing again when the user types a trigger character immediately + // after another (e.g. `>>` in JSX). + if (lastChange.range.start.character > 0) { + const priorRange = new vscode.Range(lastChange.range.start.translate(0, -1), lastChange.range.start); + if (this.triggerCharacters.has(document.getText(priorRange))) { + return; + } + } + + // Compute the post-insert cursor position from the change itself; + // `activeEditor.selection.active` is not yet updated when this event fires. + const addedLines = lastChange.text.split(/\r\n|\n/g); + const position = addedLines.length === 1 + ? lastChange.range.start.translate(0, lastChange.text.length) + : new vscode.Position( + lastChange.range.start.line + addedLines.length - 1, + addedLines[addedLines.length - 1].length, + ); + + this.cancel?.cancel(); + this.cancel?.dispose(); + this.cancel = new vscode.CancellationTokenSource(); + const token = this.cancel.token; + const startingVersion = document.version; + + let response: VsOnAutoInsertResponse | null; + try { + response = await this.client.sendRequest( + "textDocument/_vs_onAutoInsert", + { + _vs_textDocument: this.client.code2ProtocolConverter.asTextDocumentIdentifier(document), + _vs_position: this.client.code2ProtocolConverter.asPosition(position), + _vs_ch: lastCharacter, + } satisfies VsOnAutoInsertParams, + token, + ); + } + catch { + return; + } + + if (!response || token.isCancellationRequested || document.version !== startingVersion) { + return; + } + + const edit = this.client.protocol2CodeConverter.asTextEdit(response._vs_textEdit); + // If the same trigger character was typed at every cursor in the same change, + // apply the snippet/edit at every cursor. + const cursors = activeEditor.selections.map(s => s.active); + const insertionRanges = cursors.some(p => p.isEqual(position)) ? cursors : edit.range; + // Swallow rejections from the editor (e.g., document changed under us). These + // return Thenables that can otherwise become unhandled rejections. + const swallow = () => undefined; + if (response._vs_textEditFormat === InsertTextFormat.Snippet) { + void Promise.resolve(activeEditor.insertSnippet(new vscode.SnippetString(edit.newText), insertionRanges)).then(swallow, swallow); + } + else { + void Promise.resolve(activeEditor.edit(b => { + if (Array.isArray(insertionRanges)) { + for (const p of insertionRanges) b.insert(p, edit.newText); + } + else { + b.replace(insertionRanges, edit.newText); + } + })).then(swallow, swallow); + } + } +} + +function isAutoClosingTagsEnabled(scope: vscode.TextDocument): boolean { + // Use the document's own language ("typescript" / "javascript" / "*react") to + // pick the fallback section so per-language overrides apply correctly. + const fallbackSection = scope.languageId === "javascript" || scope.languageId === "javascriptreact" + ? "javascript" + : "typescript"; + return readUnifiedConfig("autoClosingTags.enabled", fallbackSection, "autoClosingTags", scope, true); +} + +function requireActiveDocumentSetting(selector: vscode.DocumentSelector) { + return new Condition( + () => { + const activeDocument = vscode.window.activeTextEditor?.document; + if (!activeDocument || !vscode.languages.match(selector, activeDocument)) { + return false; + } + return isAutoClosingTagsEnabled(activeDocument); + }, + handler => + vscode.Disposable.from( + vscode.window.onDidChangeActiveTextEditor(handler), + vscode.workspace.onDidOpenTextDocument(handler), + vscode.workspace.onDidChangeConfiguration(handler), + ), + ); +} + +export function registerOnAutoInsertFeature( + selector: vscode.DocumentSelector, + client: LanguageClient, +): vscode.Disposable { + const capabilities = client.initializeResult?.capabilities as VsServerCapabilities | undefined; + const triggerCharacters = capabilities?._vs_onAutoInsertProvider?._vs_triggerCharacters; + if (!triggerCharacters?.length) { + return vscode.Disposable.from(); + } + const set = new Set(triggerCharacters); + return conditionalRegistration( + [requireActiveDocumentSetting(selector)], + () => new AutoInsert(client, set), + ); +} diff --git a/packages/vscode-typescript/src/languageFeatures/sourceDefinition.ts b/packages/vscode-typescript/src/languageFeatures/sourceDefinition.ts index e719e1b2f4411..3516b9aa8deb6 100644 --- a/packages/vscode-typescript/src/languageFeatures/sourceDefinition.ts +++ b/packages/vscode-typescript/src/languageFeatures/sourceDefinition.ts @@ -1,84 +1,84 @@ -import * as vscode from "vscode"; -import { - LanguageClient, - Location, - LocationLink, -} from "vscode-languageclient/node"; - -const sourceDefinitionMethod = "custom/textDocument/sourceDefinition"; -const sourceDefinitionCommand = "typescript.native-preview.goToSourceDefinition"; -const sourceDefinitionContext = "tsSupportsSourceDefinition"; - -type SourceDefinitionResponse = Location | Location[] | LocationLink[] | null; - -export function registerSourceDefinitionFeature(client: LanguageClient): vscode.Disposable { - const experimental = client.initializeResult?.capabilities?.experimental as { customSourceDefinitionProvider?: boolean; } | undefined; - const enabled = !!experimental?.customSourceDefinitionProvider; - void vscode.commands.executeCommand("setContext", sourceDefinitionContext, enabled); - - if (!enabled) { - return new vscode.Disposable(() => { - void vscode.commands.executeCommand("setContext", sourceDefinitionContext, false); - }); - } - - const disposable = vscode.commands.registerCommand(sourceDefinitionCommand, async () => { - const activeEditor = vscode.window.activeTextEditor; - if (!activeEditor) { - vscode.window.showErrorMessage(vscode.l10n.t("Go to Source Definition failed. No editor is active.")); - return; - } - - const { document } = activeEditor; - if (!["javascript", "javascriptreact", "typescript", "typescriptreact"].includes(document.languageId)) { - vscode.window.showErrorMessage(vscode.l10n.t("Go to Source Definition failed. Unsupported file type.")); - return; - } - - const position = activeEditor.selection.active; - await vscode.window.withProgress({ - location: vscode.ProgressLocation.Window, - title: vscode.l10n.t("Finding source definitions"), - }, async (_, token) => { - let response: SourceDefinitionResponse; - try { - response = await client.sendRequest( - sourceDefinitionMethod, - client.code2ProtocolConverter.asTextDocumentPositionParams(document, position), - token, - ); - } - catch { - return; - } - - if (token.isCancellationRequested) { - return; - } - - const p2c = client.protocol2CodeConverter; - const items = !response ? [] : Array.isArray(response) ? response : [response]; - const locations = items.map(item => - LocationLink.is(item) - ? p2c.asLocation({ uri: item.targetUri, range: item.targetSelectionRange }) - : p2c.asLocation(item) - ); - - await vscode.commands.executeCommand( - "editor.action.goToLocations", - document.uri, - position, - locations, - "goto", - vscode.l10n.t("No source definitions found."), - ); - }); - }); - - return vscode.Disposable.from( - disposable, - new vscode.Disposable(() => { - void vscode.commands.executeCommand("setContext", sourceDefinitionContext, false); - }), - ); -} +import * as vscode from "vscode"; +import { + LanguageClient, + Location, + LocationLink, +} from "vscode-languageclient/node"; + +const sourceDefinitionMethod = "custom/textDocument/sourceDefinition"; +const sourceDefinitionCommand = "typescript.native-preview.goToSourceDefinition"; +const sourceDefinitionContext = "tsSupportsSourceDefinition"; + +type SourceDefinitionResponse = Location | Location[] | LocationLink[] | null; + +export function registerSourceDefinitionFeature(client: LanguageClient): vscode.Disposable { + const experimental = client.initializeResult?.capabilities?.experimental as { customSourceDefinitionProvider?: boolean; } | undefined; + const enabled = !!experimental?.customSourceDefinitionProvider; + void vscode.commands.executeCommand("setContext", sourceDefinitionContext, enabled); + + if (!enabled) { + return new vscode.Disposable(() => { + void vscode.commands.executeCommand("setContext", sourceDefinitionContext, false); + }); + } + + const disposable = vscode.commands.registerCommand(sourceDefinitionCommand, async () => { + const activeEditor = vscode.window.activeTextEditor; + if (!activeEditor) { + vscode.window.showErrorMessage(vscode.l10n.t("Go to Source Definition failed. No editor is active.")); + return; + } + + const { document } = activeEditor; + if (!["javascript", "javascriptreact", "typescript", "typescriptreact"].includes(document.languageId)) { + vscode.window.showErrorMessage(vscode.l10n.t("Go to Source Definition failed. Unsupported file type.")); + return; + } + + const position = activeEditor.selection.active; + await vscode.window.withProgress({ + location: vscode.ProgressLocation.Window, + title: vscode.l10n.t("Finding source definitions"), + }, async (_, token) => { + let response: SourceDefinitionResponse; + try { + response = await client.sendRequest( + sourceDefinitionMethod, + client.code2ProtocolConverter.asTextDocumentPositionParams(document, position), + token, + ); + } + catch { + return; + } + + if (token.isCancellationRequested) { + return; + } + + const p2c = client.protocol2CodeConverter; + const items = !response ? [] : Array.isArray(response) ? response : [response]; + const locations = items.map(item => + LocationLink.is(item) + ? p2c.asLocation({ uri: item.targetUri, range: item.targetSelectionRange }) + : p2c.asLocation(item) + ); + + await vscode.commands.executeCommand( + "editor.action.goToLocations", + document.uri, + position, + locations, + "goto", + vscode.l10n.t("No source definitions found."), + ); + }); + }); + + return vscode.Disposable.from( + disposable, + new vscode.Disposable(() => { + void vscode.commands.executeCommand("setContext", sourceDefinitionContext, false); + }), + ); +} diff --git a/packages/vscode-typescript/src/languageFeatures/util/dependentRegistration.ts b/packages/vscode-typescript/src/languageFeatures/util/dependentRegistration.ts index 2c9608c2ae14f..a7662065a136a 100644 --- a/packages/vscode-typescript/src/languageFeatures/util/dependentRegistration.ts +++ b/packages/vscode-typescript/src/languageFeatures/util/dependentRegistration.ts @@ -1,93 +1,93 @@ -import * as vscode from "vscode"; - -export class Condition { - private _value: boolean; - private isDisposed = false; - - constructor( - private readonly getValue: () => boolean, - onUpdate: (handler: () => void) => void, - ) { - this._value = this.getValue(); - - onUpdate(() => { - const newValue = this.getValue(); - if (newValue !== this._value) { - this._value = newValue; - this._onDidChange.fire(); - } - }); - } - - public get value(): boolean { - return this._value; - } - - private readonly _onDidChange = new vscode.EventEmitter(); - public readonly onDidChange = this._onDidChange.event; - - dispose() { - if (this.isDisposed) { - return; - } - this.isDisposed = true; - this._onDidChange.dispose(); - } -} - -class ConditionalRegistration { - private state?: { - readonly enabled: boolean; - readonly registration: vscode.Disposable | undefined; - }; - - public constructor( - private readonly conditions: readonly Condition[], - private readonly doRegister: () => vscode.Disposable, - private readonly elseDoRegister?: () => vscode.Disposable, - ) { - for (const condition of conditions) { - const listener = condition.onDidChange(() => this.update()); - this.didChangeListeners.push(listener); - } - this.update(); - } - - private didChangeListeners: vscode.Disposable[] = []; - - public dispose() { - this.state?.registration?.dispose(); - while (this.didChangeListeners.length > 0) { - const d = this.didChangeListeners.pop()!; - d.dispose(); - } - for (const c of this.conditions) { - c.dispose(); - } - this.state = undefined; - } - - private update() { - const enabled = this.conditions.every(condition => condition.value); - if (enabled) { - if (!this.state?.enabled) { - this.state?.registration?.dispose(); - this.state = { enabled: true, registration: this.doRegister() }; - } - } - else { - if (this.state?.enabled || !this.state) { - this.state?.registration?.dispose(); - this.state = { enabled: false, registration: this.elseDoRegister?.() }; - } - } - } -} - -export function conditionalRegistration( - conditions: readonly Condition[], - doRegister: () => vscode.Disposable, - elseDoRegister?: () => vscode.Disposable, -): vscode.Disposable { - return new ConditionalRegistration(conditions, doRegister, elseDoRegister); -} +import * as vscode from "vscode"; + +export class Condition { + private _value: boolean; + private isDisposed = false; + + constructor( + private readonly getValue: () => boolean, + onUpdate: (handler: () => void) => void, + ) { + this._value = this.getValue(); + + onUpdate(() => { + const newValue = this.getValue(); + if (newValue !== this._value) { + this._value = newValue; + this._onDidChange.fire(); + } + }); + } + + public get value(): boolean { + return this._value; + } + + private readonly _onDidChange = new vscode.EventEmitter(); + public readonly onDidChange = this._onDidChange.event; + + dispose() { + if (this.isDisposed) { + return; + } + this.isDisposed = true; + this._onDidChange.dispose(); + } +} + +class ConditionalRegistration { + private state?: { + readonly enabled: boolean; + readonly registration: vscode.Disposable | undefined; + }; + + public constructor( + private readonly conditions: readonly Condition[], + private readonly doRegister: () => vscode.Disposable, + private readonly elseDoRegister?: () => vscode.Disposable, + ) { + for (const condition of conditions) { + const listener = condition.onDidChange(() => this.update()); + this.didChangeListeners.push(listener); + } + this.update(); + } + + private didChangeListeners: vscode.Disposable[] = []; + + public dispose() { + this.state?.registration?.dispose(); + while (this.didChangeListeners.length > 0) { + const d = this.didChangeListeners.pop()!; + d.dispose(); + } + for (const c of this.conditions) { + c.dispose(); + } + this.state = undefined; + } + + private update() { + const enabled = this.conditions.every(condition => condition.value); + if (enabled) { + if (!this.state?.enabled) { + this.state?.registration?.dispose(); + this.state = { enabled: true, registration: this.doRegister() }; + } + } + else { + if (this.state?.enabled || !this.state) { + this.state?.registration?.dispose(); + this.state = { enabled: false, registration: this.elseDoRegister?.() }; + } + } + } +} + +export function conditionalRegistration( + conditions: readonly Condition[], + doRegister: () => vscode.Disposable, + elseDoRegister?: () => vscode.Disposable, +): vscode.Disposable { + return new ConditionalRegistration(conditions, doRegister, elseDoRegister); +} diff --git a/packages/vscode-typescript/src/managedFileContext.ts b/packages/vscode-typescript/src/managedFileContext.ts index 8ac254e229d32..d1da83b63332f 100644 --- a/packages/vscode-typescript/src/managedFileContext.ts +++ b/packages/vscode-typescript/src/managedFileContext.ts @@ -1,56 +1,56 @@ -import * as vscode from "vscode"; -import { ActiveJsTsEditorTracker } from "./activeJsTsEditorTracker"; -import { - disabledSchemes, - isJsConfigOrTsConfigFileName, - isSupportedLanguageMode, -} from "./util"; - -/** - * When-clause context set when the current file is managed by tsgo. - */ -export class ManagedFileContextManager implements vscode.Disposable { - private static readonly contextName = "typescript.isManagedFile"; - - private isInManagedFileContext = false; - private disposables: vscode.Disposable[] = []; - - constructor(activeJsTsEditorTracker: ActiveJsTsEditorTracker) { - this.disposables.push( - activeJsTsEditorTracker.onDidChangeActiveJsTsEditor( - editor => this.onDidChangeActiveTextEditor(editor), - ), - ); - this.onDidChangeActiveTextEditor(activeJsTsEditorTracker.activeJsTsEditor); - } - - private onDidChangeActiveTextEditor(editor?: vscode.TextEditor): void { - if (editor) { - this.updateContext(this.isManagedFile(editor)); - } - else { - this.updateContext(false); - } - } - - private isManagedFile(editor: vscode.TextEditor): boolean { - if (disabledSchemes.has(editor.document.uri.scheme)) { - return false; - } - return isSupportedLanguageMode(editor.document) - || isJsConfigOrTsConfigFileName(editor.document.fileName); - } - - private updateContext(newValue: boolean): void { - if (newValue === this.isInManagedFileContext) { - return; - } - vscode.commands.executeCommand("setContext", ManagedFileContextManager.contextName, newValue); - this.isInManagedFileContext = newValue; - } - - dispose(): void { - this.updateContext(false); - this.disposables.forEach(d => d.dispose()); - } -} +import * as vscode from "vscode"; +import { ActiveJsTsEditorTracker } from "./activeJsTsEditorTracker"; +import { + disabledSchemes, + isJsConfigOrTsConfigFileName, + isSupportedLanguageMode, +} from "./util"; + +/** + * When-clause context set when the current file is managed by tsgo. + */ +export class ManagedFileContextManager implements vscode.Disposable { + private static readonly contextName = "typescript.isManagedFile"; + + private isInManagedFileContext = false; + private disposables: vscode.Disposable[] = []; + + constructor(activeJsTsEditorTracker: ActiveJsTsEditorTracker) { + this.disposables.push( + activeJsTsEditorTracker.onDidChangeActiveJsTsEditor( + editor => this.onDidChangeActiveTextEditor(editor), + ), + ); + this.onDidChangeActiveTextEditor(activeJsTsEditorTracker.activeJsTsEditor); + } + + private onDidChangeActiveTextEditor(editor?: vscode.TextEditor): void { + if (editor) { + this.updateContext(this.isManagedFile(editor)); + } + else { + this.updateContext(false); + } + } + + private isManagedFile(editor: vscode.TextEditor): boolean { + if (disabledSchemes.has(editor.document.uri.scheme)) { + return false; + } + return isSupportedLanguageMode(editor.document) + || isJsConfigOrTsConfigFileName(editor.document.fileName); + } + + private updateContext(newValue: boolean): void { + if (newValue === this.isInManagedFileContext) { + return; + } + vscode.commands.executeCommand("setContext", ManagedFileContextManager.contextName, newValue); + this.isInManagedFileContext = newValue; + } + + dispose(): void { + this.updateContext(false); + this.disposables.forEach(d => d.dispose()); + } +} diff --git a/packages/vscode-typescript/src/projectStatus.ts b/packages/vscode-typescript/src/projectStatus.ts index 86ce370342d6a..65ea8b6990e8d 100644 --- a/packages/vscode-typescript/src/projectStatus.ts +++ b/packages/vscode-typescript/src/projectStatus.ts @@ -1,185 +1,185 @@ -import * as vscode from "vscode"; -import { ActiveJsTsEditorTracker } from "./activeJsTsEditorTracker"; -import { Client } from "./client"; -import { - isSupportedLanguageMode, - jsTsLanguageModes, -} from "./util"; - -namespace ProjectInfoState { - export const enum Type { - None, - Pending, - Resolved, - } - - export const None = Object.freeze({ type: Type.None } as const); - - export class Pending { - public readonly type = Type.Pending; - public readonly cancellation = new vscode.CancellationTokenSource(); - - constructor( - public readonly resource: vscode.Uri, - ) {} - } - - export class Resolved { - public readonly type = Type.Resolved; - - constructor( - public readonly resource: vscode.Uri, - public readonly configFile: string, - ) {} - } - - export type State = typeof None | Pending | Resolved; -} - -/** - * Shows which tsconfig/jsconfig the current file belongs to. - */ -export class ProjectStatus implements vscode.Disposable { - private statusItem?: vscode.LanguageStatusItem; - private state: ProjectInfoState.State = ProjectInfoState.None; - private disposables: vscode.Disposable[] = []; - private ready = false; - - constructor( - private readonly client: Client, - private readonly activeEditorTracker: ActiveJsTsEditorTracker, - onReady: vscode.Event, - ) { - this.disposables.push( - activeEditorTracker.onDidChangeActiveJsTsEditor(() => this.updateStatus()), - ); - this.disposables.push( - onReady(() => { - this.ready = true; - this.updateStatus(); - }), - ); - } - - private async updateStatus(): Promise { - const doc = this.activeEditorTracker.activeJsTsEditor?.document; - if (!doc || !isSupportedLanguageMode(doc)) { - this.updateState(ProjectInfoState.None); - return; - } - - if (doc.uri.scheme !== "file" && doc.uri.scheme !== "untitled") { - this.updateState(ProjectInfoState.None); - return; - } - - if (!this.ready) { - return; - } - - const pendingState = new ProjectInfoState.Pending(doc.uri); - this.updateState(pendingState); - - try { - const result = await this.client.getProjectInfo(doc.uri.toString(), pendingState.cancellation.token); - if (this.state === pendingState) { - this.updateState(new ProjectInfoState.Resolved(doc.uri, result.configFilePath)); - } - } - catch { - // If we fail to get project info, just go back to no status - if (this.state === pendingState) { - this.updateState(ProjectInfoState.None); - } - } - } - - private updateState(newState: ProjectInfoState.State): void { - if (this.state === newState) { - return; - } - - if (this.state.type === ProjectInfoState.Type.Pending) { - this.state.cancellation.cancel(); - this.state.cancellation.dispose(); - } - - this.state = newState; - - switch (this.state.type) { - case ProjectInfoState.Type.None: { - this.statusItem?.dispose(); - this.statusItem = undefined; - break; - } - case ProjectInfoState.Type.Pending: { - const statusItem = this.ensureStatusItem(); - statusItem.severity = vscode.LanguageStatusSeverity.Information; - statusItem.text = vscode.l10n.t("Loading project info..."); - statusItem.detail = undefined; - statusItem.command = undefined; - statusItem.busy = true; - break; - } - case ProjectInfoState.Type.Resolved: { - const currentLanguageId = this.activeEditorTracker.activeJsTsEditor?.document.languageId; - const isTypeScript = currentLanguageId === "typescript" - || currentLanguageId === "typescriptreact"; - const noConfigFileText = isTypeScript ? vscode.l10n.t("No tsconfig") : vscode.l10n.t("No jsconfig"); - - const rootPath = this.getWorkspaceRootForResource(this.state.resource); - if (!rootPath) { - if (this.statusItem) { - this.statusItem.text = noConfigFileText; - this.statusItem.detail = !vscode.workspace.workspaceFolders - ? vscode.l10n.t("No opened folders") - : vscode.l10n.t("File is not part of opened folders"); - this.statusItem.busy = false; - } - return; - } - - const statusItem = this.ensureStatusItem(); - statusItem.busy = false; - statusItem.detail = undefined; - statusItem.severity = vscode.LanguageStatusSeverity.Information; - - if (this.state.configFile) { - statusItem.text = vscode.workspace.asRelativePath(this.state.configFile); - statusItem.command = { - command: "vscode.open", - title: vscode.l10n.t("Open Config File"), - arguments: [vscode.Uri.file(this.state.configFile)], - }; - } - else { - statusItem.text = noConfigFileText; - statusItem.command = undefined; - } - break; - } - } - } - - private ensureStatusItem(): vscode.LanguageStatusItem { - if (!this.statusItem) { - this.statusItem = vscode.languages.createLanguageStatusItem("typescript.native-preview.projectStatus", jsTsLanguageModes); - this.statusItem.name = vscode.l10n.t("TypeScript 7 Project Status"); - } - return this.statusItem; - } - - private getWorkspaceRootForResource(resource: vscode.Uri): vscode.Uri | undefined { - const folder = vscode.workspace.getWorkspaceFolder(resource); - return folder?.uri; - } - - dispose(): void { - this.statusItem?.dispose(); - if (this.state.type === ProjectInfoState.Type.Pending) { - this.state.cancellation.cancel(); - this.state.cancellation.dispose(); - } - this.disposables.forEach(d => d.dispose()); - } -} +import * as vscode from "vscode"; +import { ActiveJsTsEditorTracker } from "./activeJsTsEditorTracker"; +import { Client } from "./client"; +import { + isSupportedLanguageMode, + jsTsLanguageModes, +} from "./util"; + +namespace ProjectInfoState { + export const enum Type { + None, + Pending, + Resolved, + } + + export const None = Object.freeze({ type: Type.None } as const); + + export class Pending { + public readonly type = Type.Pending; + public readonly cancellation = new vscode.CancellationTokenSource(); + + constructor( + public readonly resource: vscode.Uri, + ) {} + } + + export class Resolved { + public readonly type = Type.Resolved; + + constructor( + public readonly resource: vscode.Uri, + public readonly configFile: string, + ) {} + } + + export type State = typeof None | Pending | Resolved; +} + +/** + * Shows which tsconfig/jsconfig the current file belongs to. + */ +export class ProjectStatus implements vscode.Disposable { + private statusItem?: vscode.LanguageStatusItem; + private state: ProjectInfoState.State = ProjectInfoState.None; + private disposables: vscode.Disposable[] = []; + private ready = false; + + constructor( + private readonly client: Client, + private readonly activeEditorTracker: ActiveJsTsEditorTracker, + onReady: vscode.Event, + ) { + this.disposables.push( + activeEditorTracker.onDidChangeActiveJsTsEditor(() => this.updateStatus()), + ); + this.disposables.push( + onReady(() => { + this.ready = true; + this.updateStatus(); + }), + ); + } + + private async updateStatus(): Promise { + const doc = this.activeEditorTracker.activeJsTsEditor?.document; + if (!doc || !isSupportedLanguageMode(doc)) { + this.updateState(ProjectInfoState.None); + return; + } + + if (doc.uri.scheme !== "file" && doc.uri.scheme !== "untitled") { + this.updateState(ProjectInfoState.None); + return; + } + + if (!this.ready) { + return; + } + + const pendingState = new ProjectInfoState.Pending(doc.uri); + this.updateState(pendingState); + + try { + const result = await this.client.getProjectInfo(doc.uri.toString(), pendingState.cancellation.token); + if (this.state === pendingState) { + this.updateState(new ProjectInfoState.Resolved(doc.uri, result.configFilePath)); + } + } + catch { + // If we fail to get project info, just go back to no status + if (this.state === pendingState) { + this.updateState(ProjectInfoState.None); + } + } + } + + private updateState(newState: ProjectInfoState.State): void { + if (this.state === newState) { + return; + } + + if (this.state.type === ProjectInfoState.Type.Pending) { + this.state.cancellation.cancel(); + this.state.cancellation.dispose(); + } + + this.state = newState; + + switch (this.state.type) { + case ProjectInfoState.Type.None: { + this.statusItem?.dispose(); + this.statusItem = undefined; + break; + } + case ProjectInfoState.Type.Pending: { + const statusItem = this.ensureStatusItem(); + statusItem.severity = vscode.LanguageStatusSeverity.Information; + statusItem.text = vscode.l10n.t("Loading project info..."); + statusItem.detail = undefined; + statusItem.command = undefined; + statusItem.busy = true; + break; + } + case ProjectInfoState.Type.Resolved: { + const currentLanguageId = this.activeEditorTracker.activeJsTsEditor?.document.languageId; + const isTypeScript = currentLanguageId === "typescript" + || currentLanguageId === "typescriptreact"; + const noConfigFileText = isTypeScript ? vscode.l10n.t("No tsconfig") : vscode.l10n.t("No jsconfig"); + + const rootPath = this.getWorkspaceRootForResource(this.state.resource); + if (!rootPath) { + if (this.statusItem) { + this.statusItem.text = noConfigFileText; + this.statusItem.detail = !vscode.workspace.workspaceFolders + ? vscode.l10n.t("No opened folders") + : vscode.l10n.t("File is not part of opened folders"); + this.statusItem.busy = false; + } + return; + } + + const statusItem = this.ensureStatusItem(); + statusItem.busy = false; + statusItem.detail = undefined; + statusItem.severity = vscode.LanguageStatusSeverity.Information; + + if (this.state.configFile) { + statusItem.text = vscode.workspace.asRelativePath(this.state.configFile); + statusItem.command = { + command: "vscode.open", + title: vscode.l10n.t("Open Config File"), + arguments: [vscode.Uri.file(this.state.configFile)], + }; + } + else { + statusItem.text = noConfigFileText; + statusItem.command = undefined; + } + break; + } + } + } + + private ensureStatusItem(): vscode.LanguageStatusItem { + if (!this.statusItem) { + this.statusItem = vscode.languages.createLanguageStatusItem("typescript.native-preview.projectStatus", jsTsLanguageModes); + this.statusItem.name = vscode.l10n.t("TypeScript 7 Project Status"); + } + return this.statusItem; + } + + private getWorkspaceRootForResource(resource: vscode.Uri): vscode.Uri | undefined { + const folder = vscode.workspace.getWorkspaceFolder(resource); + return folder?.uri; + } + + dispose(): void { + this.statusItem?.dispose(); + if (this.state.type === ProjectInfoState.Type.Pending) { + this.state.cancellation.cancel(); + this.state.cancellation.dispose(); + } + this.disposables.forEach(d => d.dispose()); + } +} diff --git a/packages/vscode-typescript/src/session.ts b/packages/vscode-typescript/src/session.ts index 46303606b836f..299bbcdf9d3d9 100644 --- a/packages/vscode-typescript/src/session.ts +++ b/packages/vscode-typescript/src/session.ts @@ -1,739 +1,739 @@ -import * as path from "path"; -import * as vscode from "vscode"; -import { ActiveJsTsEditorTracker } from "./activeJsTsEditorTracker"; -import { Client } from "./client"; -import { - registerCodeLensShowLocationsCommand, - updateWorkspaceUseTsgoSetting, -} from "./commands"; -import { - type ContentMapperContribution, - documentMatchesContentMapperContributions, - serializeContentMapperContributions, - validateContentMapperRegistration, -} from "./contentMapperContributions"; -import { ProjectStatus } from "./projectStatus"; -import { setupStatusBar } from "./statusBar"; -import { TelemetryReporter } from "./telemetryReporting"; -import { - getDefaultExePath, - getExe, - getWorkspaceTsdkConfigValue, - getWorkspaceTsdkForPrompt, - readNativePreviewConfig, - resolveTsdkPath, - resolveTsdkPathToExe, - updateWorkspaceTsdkConfig, - useWorkspaceTsdkStorageKey, - workspaceConfigBase, -} from "./util"; - -/** - * SessionManager's lifetime is equal to that of the extension. It is responsible - * for starting, restarting, replacing, and disposing the Session. - */ -export class SessionManager implements vscode.Disposable { - currentSession?: Session; - private disposables: vscode.Disposable[] = []; - private outputChannel: vscode.LogOutputChannel; - private initializedEventEmitter: vscode.EventEmitter; - private telemetryReporter: TelemetryReporter; - private readonly contentMapperRegistrations = new Map(); - private lifecycleOperation = Promise.resolve(); - private contentMapperSyncOperation = Promise.resolve(); - - constructor( - context: vscode.ExtensionContext, - outputChannel: vscode.LogOutputChannel, - initializedEventEmitter: vscode.EventEmitter, - telemetryReporter: TelemetryReporter, - ) { - this.outputChannel = outputChannel; - this.telemetryReporter = telemetryReporter; - this.initializedEventEmitter = initializedEventEmitter; - - this.disposables.push(vscode.workspace.onDidChangeConfiguration(event => { - if (this.currentSession && event.affectsConfiguration("js/ts.contentMappers.enabled")) { - void this.restart(context).catch(error => { - this.outputChannel.error(`TypeScript language server restart failed: ${String(error)}`); - }); - } - })); - this.disposables.push(vscode.workspace.onDidOpenTextDocument(document => { - if (documentMatchesContentMapperContributions(document, this.contentMapperRegistrations)) { - void this.syncContentMapperContributions(); - } - })); - this.disposables.push(initializedEventEmitter.event(() => { - void this.syncContentMapperContributions(); - })); - } - - start(context: vscode.ExtensionContext): Promise { - return this.restart(context); - } - - restart(context: vscode.ExtensionContext): Promise { - return this.enqueueLifecycleOperation(() => this.restartNow(context)); - } - - private async restartNow(context: vscode.ExtensionContext): Promise { - if (this.currentSession) { - this.outputChannel.appendLine("Restarting TypeScript language server..."); - await this.currentSession.stop(); - } - const session = new Session(context, this.outputChannel, this.initializedEventEmitter, this.telemetryReporter, () => this.stop(), () => this.restart(context)); - this.currentSession = session; - try { - await session.start(context); - await this.syncContentMapperContributions(); - } - catch (error) { - if (this.currentSession === session) { - this.currentSession = undefined; - } - await session.dispose(); - throw error; - } - } - - stop(): Promise { - return this.enqueueLifecycleOperation(async () => { - if (this.currentSession) { - await this.currentSession.stop(); - this.currentSession = undefined; - } - }); - } - - async initializeAPIConnection(pipe?: string): Promise { - if (!this.currentSession) { - throw new Error(vscode.l10n.t("Language server is not running.")); - } - const result = await this.currentSession.client.initializeAPISession(pipe); - return result.pipe; - } - - registerContentMappers(contributorId: string, contributions: readonly ContentMapperContribution[]): vscode.Disposable { - validateContentMapperRegistration(contributorId, contributions); - if (this.contentMapperRegistrations.has(contributorId)) { - throw new Error(`Content mapper contributor '${contributorId}' is already registered.`); - } - this.contentMapperRegistrations.set(contributorId, contributions); - void this.syncContentMapperContributions(); - let disposed = false; - return new vscode.Disposable(() => { - if (disposed) return; - disposed = true; - this.contentMapperRegistrations.delete(contributorId); - void this.syncContentMapperContributions(); - }); - } - - private syncContentMapperContributions(): Promise { - const operation = this.contentMapperSyncOperation.then(() => this.syncContentMapperContributionsNow()); - this.contentMapperSyncOperation = operation.catch(() => {}); - return operation; - } - - private async syncContentMapperContributionsNow(): Promise { - try { - if (!this.currentSession?.client.isInitialized) return; - const openDocuments = vscode.workspace.textDocuments - .filter(document => documentMatchesContentMapperContributions(document, this.contentMapperRegistrations)) - .map(document => document.uri); - await this.currentSession.client.setContentMapperContributions( - serializeContentMapperContributions(this.contentMapperRegistrations), - openDocuments, - ); - } - catch (error) { - this.outputChannel.warn(`Content mapper contribution synchronization failed: ${String(error)}`); - } - } - - private enqueueLifecycleOperation(operation: () => Promise): Promise { - const result = this.lifecycleOperation.then(operation); - this.lifecycleOperation = result.catch(() => {}); - return result; - } - - dispose(): Promise { - return this.enqueueLifecycleOperation(async () => { - await this.currentSession?.dispose(); - this.currentSession = undefined; - await Promise.all(this.disposables.splice(0).map(d => d.dispose())); - }); - } -} - -/** - * Session's lifetime is equal to that of its LanguageClient. The LanguageClient - * can be restarted within the same Session only if the underlying exe path/version - * has not changed. Otherwise, a new Session must be created. Since Session only - * exists while the LSP server is running (or actively starting/restarting/stopping), - * it also owns the commands and UI elements that should only be active while the - * server is running. - */ -class Session implements vscode.Disposable { - client: Client; - private disposables: vscode.Disposable[] = []; - private context: vscode.ExtensionContext; - private outputChannel: vscode.LogOutputChannel; - private telemetryReporter: TelemetryReporter; - private initializedEventEmitter: vscode.EventEmitter; - private stopSession: () => Promise; - private restartSession: () => Promise; - - constructor( - context: vscode.ExtensionContext, - outputChannel: vscode.LogOutputChannel, - initializedEventEmitter: vscode.EventEmitter, - telemetryReporter: TelemetryReporter, - stopSession: () => Promise, - restartSession: () => Promise, - ) { - this.client = new Client(outputChannel, initializedEventEmitter, telemetryReporter); - this.context = context; - this.outputChannel = outputChannel; - this.telemetryReporter = telemetryReporter; - this.initializedEventEmitter = initializedEventEmitter; - this.stopSession = stopSession; - this.restartSession = restartSession; - this.registerCommands(); - } - - async start(context: vscode.ExtensionContext): Promise { - await vscode.commands.executeCommand("setContext", "typescript.isManagedFile", false); - const exe = await getExe(context); - await this.client.start(exe); - this.disposables.push(setupStatusBar(exe)); - - // Set up active editor tracker and UI features - const activeEditorTracker = new ActiveJsTsEditorTracker(); - this.disposables.push(activeEditorTracker); - - const projectStatus = new ProjectStatus(this.client, activeEditorTracker, this.initializedEventEmitter.event); - this.disposables.push(projectStatus); - - // If already initialized, fire immediately so projectStatus picks it up - if (this.client.isInitialized) { - this.initializedEventEmitter.fire(); - } - - await vscode.commands.executeCommand("setContext", "typescript.native-preview.serverRunning", true); - } - - tryRestartClient(context: vscode.ExtensionContext): Promise { - return this.client.tryRestart(context); - } - - registerCommands(): void { - this.disposables.push(registerCodeLensShowLocationsCommand()); - - this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.restart", async () => { - this.telemetryReporter.sendTelemetryEvent("command.restartLanguageServer"); - if (await this.tryRestartClient(this.context)) { - return; - } - - await this.restartSession(); - })); - - this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.output.focus", () => { - this.outputChannel.show(); - })); - - this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.selectVersion", async () => { - await promptSelectVersion(this.context, this.client, this.outputChannel, this.stopSession); - })); - - // TODO: Support the standard reload/go-to-project-config commands while - // Strada leaves their command palette entries visible in native mode: - // typescript.reloadProjects, javascript.reloadProjects, - // typescript.goToProjectConfig, and javascript.goToProjectConfig. - - this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.showMenu", () => { - showCommands(this.client); - })); - - this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.sortImports", () => { - return vscode.commands.executeCommand("editor.action.sourceAction", { - kind: "source.sortImports", - apply: "first", - }); - })); - - this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.removeUnusedImports", () => { - return vscode.commands.executeCommand("editor.action.sourceAction", { - kind: "source.removeUnusedImports", - apply: "first", - }); - })); - - this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.reportIssue", () => { - this.telemetryReporter.sendTelemetryEvent("command.reportIssue"); - vscode.commands.executeCommand("workbench.action.openIssueReporter", { - extensionId: this.context.extension.id, - }); - })); - - this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.dev.runGC", async () => { - try { - await this.client.runGC(); - vscode.window.showInformationMessage(vscode.l10n.t(`Garbage collection triggered`)); - } - catch (error) { - vscode.window.showErrorMessage(vscode.l10n.t(`Failed to run GC: {0}`, String(error))); - } - })); - - this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.dev.saveHeapProfile", async () => { - const dir = await promptForProfileDirectory(); - if (!dir) return; - try { - const file = await this.client.saveHeapProfile(dir); - vscode.window.showInformationMessage(vscode.l10n.t(`Heap profile saved to: {0}`, file)); - } - catch (error) { - vscode.window.showErrorMessage(vscode.l10n.t(`Failed to save heap profile: {0}`, String(error))); - } - })); - - this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.dev.saveAllocProfile", async () => { - const dir = await promptForProfileDirectory(); - if (!dir) return; - try { - const file = await this.client.saveAllocProfile(dir); - vscode.window.showInformationMessage(vscode.l10n.t(`Allocation profile saved to: {0}`, file)); - } - catch (error) { - vscode.window.showErrorMessage(vscode.l10n.t(`Failed to save allocation profile: {0}`, String(error))); - } - })); - - this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.dev.startCPUProfile", async () => { - const dir = await promptForProfileDirectory(); - if (!dir) return; - try { - await this.client.startCPUProfile(dir); - vscode.commands.executeCommand("setContext", "typescript.native-preview.cpuProfileRunning", true); - vscode.window.showInformationMessage(vscode.l10n.t(`CPU profiling started. Profile will be saved to: {0}`, dir)); - } - catch (error) { - vscode.window.showErrorMessage(vscode.l10n.t(`Failed to start CPU profile: {0}`, String(error))); - vscode.commands.executeCommand("setContext", "typescript.native-preview.cpuProfileRunning", false); - } - })); - - this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.dev.stopCPUProfile", async () => { - try { - const file = await this.client.stopCPUProfile(); - vscode.commands.executeCommand("setContext", "typescript.native-preview.cpuProfileRunning", false); - vscode.window.showInformationMessage(vscode.l10n.t(`CPU profile saved to: {0}`, file)); - } - catch (error) { - vscode.window.showErrorMessage(vscode.l10n.t(`Failed to stop CPU profile: {0}`, String(error))); - } - })); - - this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.initializeAPIConnection", async () => { - const result = await this.client.initializeAPISession(); - return result.pipe; - })); - - this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.initializeAPIConnection.ui", async () => { - try { - const result = await this.client.initializeAPISession(); - const copyString = vscode.l10n.t("Copy"); - const copy = await vscode.window.showInformationMessage(vscode.l10n.t(`API session initialized. Listening on: {0}`, result.pipe), copyString); - if (copy === copyString) { - await vscode.env.clipboard.writeText(result.pipe); - } - } - catch (error) { - const message = error instanceof Error ? error.message : String(error); - vscode.window.showErrorMessage(vscode.l10n.t(`Failed to initialize API session: {0}`, message)); - } - })); - } - - private async disposeSessionState(): Promise { - await vscode.commands.executeCommand("setContext", "typescript.native-preview.serverRunning", false); - await vscode.commands.executeCommand("setContext", "typescript.native-preview.cpuProfileRunning", false); - const disposables = this.disposables.splice(0); - await Promise.all(disposables.map(d => d.dispose())); - } - - async stop(): Promise { - await this.disposeSessionState(); - await this.client.stop(); - } - - async dispose(): Promise { - await this.disposeSessionState(); - await this.client.dispose(); - } -} - -async function showCommands(client: Client): Promise { - interface CommandItem { - label: string; - description?: string; - kind?: vscode.QuickPickItemKind; - command?: string; - action?: () => Promise; - } - const commands: CommandItem[] = [ - { - label: vscode.l10n.t("$(refresh) Restart Server"), - description: vscode.l10n.t("Restart the TypeScript language server"), - command: "typescript.native-preview.restart", - }, - { - label: vscode.l10n.t("$(output) Show Output"), - description: vscode.l10n.t("Show the TypeScript output log"), - command: "typescript.native-preview.output.focus", - }, - { - label: vscode.l10n.t("$(report) Report Issue"), - description: vscode.l10n.t("Report an issue with TypeScript 7"), - command: "typescript.native-preview.reportIssue", - }, - { - label: vscode.l10n.t("$(versions) Select Version"), - description: vscode.l10n.t("Choose between bundled and workspace versions"), - command: "typescript.native-preview.selectVersion", - }, - { - label: vscode.l10n.t("$(stop-circle) Disable TypeScript 7"), - description: vscode.l10n.t("Switch back to the built-in TypeScript extension"), - command: "typescript.native-preview.disable", - }, - ]; - - const showDebugInfo = readNativePreviewConfig("showDebugInfo", false); - if (showDebugInfo) { - const exe = client.getCurrentExe(); - const pid = client.serverPid; - commands.push({ label: "", kind: vscode.QuickPickItemKind.Separator }); - if (exe) { - commands.push({ - label: vscode.l10n.t(`Executable`), - description: exe.path, - action: async () => { - await vscode.env.clipboard.writeText(exe.path); - vscode.window.showInformationMessage(vscode.l10n.t("Executable path copied to clipboard.")); - }, - }); - } - if (pid) { - commands.push({ - label: vscode.l10n.t(`PID`), - description: `${pid}`, - action: async () => { - await vscode.env.clipboard.writeText(`${pid}`); - vscode.window.showInformationMessage(vscode.l10n.t("Server PID copied to clipboard.")); - }, - }); - } - } - - const selected = await vscode.window.showQuickPick(commands, { - placeHolder: vscode.l10n.t("{0} Commands", "TypeScript 7"), - }); - - if (selected) { - if (selected.action) { - await selected.action(); - } - else if (selected.command) { - await vscode.commands.executeCommand(selected.command); - } - } -} - -interface VersionQuickPickItem extends vscode.QuickPickItem { - run(): Promise; - beforeRun?(): Promise; - restart?: boolean; -} - -function readStringArrayConfig(key: string): string[] { - const value = readNativePreviewConfig(key, undefined); - return Array.isArray(value) ? value.filter((v): v is string => typeof v === "string") : []; -} - -interface DetectedVersion { - folder: vscode.WorkspaceFolder; - version: string; - tsdkPath: string; - exePath: string; -} - -interface StradaVersion { - version: string; -} - -async function getStradaVersion(): Promise { - return await getStradaExtensionVersion("ms-vscode.vscode-typescript-next", ["node_modules", "typescript"]) - ?? await getStradaExtensionVersion("vscode.typescript-language-features", ["..", "node_modules", "typescript"]); -} - -async function getStradaExtensionVersion(extensionId: string, pathToTypescript: string[]): Promise { - const extension = vscode.extensions.getExtension(extensionId); - if (!extension) return undefined; - - const packagePath = vscode.Uri.joinPath(extension.extensionUri, ...pathToTypescript); - const tsserverPath = vscode.Uri.joinPath(packagePath, "lib", "tsserver.js"); - try { - await vscode.workspace.fs.stat(tsserverPath); - const packageJson = JSON.parse(await vscode.workspace.fs.readFile(vscode.Uri.joinPath(packagePath, "package.json")).then(buffer => buffer.toString())); - return { - version: typeof packageJson.version === "string" ? packageJson.version : "unknown", - }; - } - catch { - return undefined; - } -} - -async function findWorkspaceNativePreviewPackages(): Promise { - const results: DetectedVersion[] = []; - for (const folder of vscode.workspace.workspaceFolders ?? []) { - const packagePath = vscode.Uri.joinPath(folder.uri, "node_modules", "@typescript", "native-preview"); - const resolved = await resolveTsdkPathToExe(path.normalize(packagePath.fsPath)); - if (!resolved) continue; - results.push({ - folder, - version: resolved?.version ?? "unknown", - tsdkPath: path.normalize(packagePath.fsPath), - exePath: resolved?.path ?? "", - }); - } - return results; -} - -/** - * Compute the tsdk path to persist in workspace config. Uses a path relative - * to the workspace config base directory (the `.code-workspace` file's parent - * in multi-root, or the lone workspace folder in single-root). Falls back to - * the absolute path if there is no workspace. - */ -function tsdkPathForConfig(detected: DetectedVersion): string { - const base = workspaceConfigBase(); - if (!base) { - return detected.tsdkPath; - } - return path.relative(base.fsPath, detected.tsdkPath); -} - -/** - * Update the tsdk config to point at the detected version, but only if the - * existing value doesn't already resolve to the same absolute path (avoiding - * unnecessary config churn from formatting differences like absolute vs - * relative, leading ./, etc.). - */ -async function updateTsdkConfig(detected: DetectedVersion): Promise { - const currentValue = getWorkspaceTsdkConfigValue(); - const newValue = tsdkPathForConfig(detected); - if (currentValue !== undefined && resolveTsdkPath(currentValue) === resolveTsdkPath(newValue)) { - return; - } - await updateWorkspaceTsdkConfig(newValue); -} - -async function promptSelectVersion(context: vscode.ExtensionContext, client: Client, outputChannel: vscode.LogOutputChannel, stopServer: () => Promise): Promise { - const currentExePath = client.getCurrentExe()?.path; - const defaultExe = await getDefaultExePath(context); - const stradaVersion = await getStradaVersion(); - const workspaceVersions = await findWorkspaceNativePreviewPackages(); - const items: VersionQuickPickItem[] = []; - - if (stradaVersion) { - items.push({ - label: vscode.l10n.t("Use Built-in TypeScript"), - description: stradaVersion.version, - detail: "", - beforeRun: stopServer, - run: async () => { - await context.workspaceState.update(useWorkspaceTsdkStorageKey, false); - await updateWorkspaceUseTsgoSetting(false); - outputChannel.appendLine("Switched to bundled TypeScript version."); - }, - }); - } - - items.push({ - label: (currentExePath === defaultExe.path ? "• " : "") + vscode.l10n.t("Use TypeScript 7"), - description: defaultExe.version, - detail: "", - restart: true, - run: async () => { - await context.workspaceState.update(useWorkspaceTsdkStorageKey, false); - await updateWorkspaceUseTsgoSetting(true); - outputChannel.appendLine("Switched to TypeScript 7."); - }, - }); - - // Workspace versions - if (vscode.workspace.isTrusted) { - for (const wsVersion of workspaceVersions) { - const isActive = currentExePath === wsVersion.exePath; - items.push({ - label: (isActive ? "• " : "") + vscode.l10n.t("Use Workspace Version"), - description: wsVersion.version, - detail: wsVersion.tsdkPath, - restart: true, - run: async () => { - await context.workspaceState.update(useWorkspaceTsdkStorageKey, true); - await updateTsdkConfig(wsVersion); - await updateWorkspaceUseTsgoSetting(true); - outputChannel.appendLine(`Switched to workspace tsgo version (${wsVersion.version}).`); - }, - }); - } - } - else if (workspaceVersions.length > 0) { - items.push({ - label: "", - kind: vscode.QuickPickItemKind.Separator, - run: async () => {}, - }); - items.push({ - label: vscode.l10n.t("$(lock) Manage Workspace Trust to select a workspace version"), - run: async () => { - await vscode.commands.executeCommand("workbench.trust.manage"); - }, - }); - } - - // Additional tsdk locations from settings - const additionalLocations = [ - ...readStringArrayConfig("tsdk.additionalLocations"), - ...readStringArrayConfig("additionalTsdkLocations"), - ]; - if (additionalLocations.length > 0) { - items.push({ - label: "", - kind: vscode.QuickPickItemKind.Separator, - run: async () => {}, - }); - for (const loc of additionalLocations) { - const resolved = await resolveTsdkPathToExe(loc); - if (!resolved) continue; - if (resolved.path === defaultExe.path) continue; - if (workspaceVersions.some(ws => ws.exePath === resolved.path)) continue; - const isActive = currentExePath === resolved.path; - items.push({ - label: (isActive ? "• " : "") + vscode.l10n.t("Use Custom Version"), - description: resolved.version, - detail: resolved.path, - restart: true, - run: async () => { - await context.workspaceState.update(useWorkspaceTsdkStorageKey, true); - await updateWorkspaceTsdkConfig(loc); - await updateWorkspaceUseTsgoSetting(true); - outputChannel.appendLine(`Switched to custom TypeScript version at ${loc}.`); - }, - }); - } - } - - const selected = await vscode.window.showQuickPick(items, { - placeHolder: vscode.l10n.t("Select the TypeScript version used for JavaScript and TypeScript language features"), - }); - - if (selected) { - await selected.beforeRun?.(); - await selected.run(); - if (selected.restart) { - await vscode.commands.executeCommand("typescript.native-preview.restart"); - } - } -} - -/** - * If the workspace has a tsdk setting pending consent, or has - * `@typescript/typescript` installed in node_modules, prompt the user - * to allow using it. - */ -export async function promptUseWorkspaceVersion(context: vscode.ExtensionContext): Promise { - if (!vscode.workspace.isTrusted) return; - - const useWorkspaceTsdk = context.workspaceState.get(useWorkspaceTsdkStorageKey, false); - if (useWorkspaceTsdk) return; // already opted in - - const suppressKey = "typescript.native-preview.suppressPromptWorkspaceTsdk"; - if (context.workspaceState.get(suppressKey, false)) return; - - const workspaceTsdk = await getWorkspaceTsdkForPrompt(); - if (workspaceTsdk !== undefined) { - // The workspace config already specifies a tsdk location, but the - // user hasn't consented to using it. Just need their approval. - const resolved = await resolveTsdkPathToExe(workspaceTsdk); - if (!resolved) return; - const allow = vscode.l10n.t("Allow"); - const dismiss = vscode.l10n.t("Dismiss"); - const suppress = vscode.l10n.t("Never in this Workspace"); - - const result = await vscode.window.showInformationMessage( - vscode.l10n.t(`This workspace has a TypeScript 7 tsdk configured ({0}). Would you like to use it?`, resolved.version), - allow, - dismiss, - suppress, - ); - - if (result === allow) { - if (!vscode.workspace.isTrusted) return; - await context.workspaceState.update(useWorkspaceTsdkStorageKey, true); - await vscode.commands.executeCommand("typescript.native-preview.restart"); - } - else if (result === suppress) { - await context.workspaceState.update(suppressKey, true); - } - } - else { - // No workspace tsdk config, but check if native-preview is installed - // in the workspace's node_modules. - const workspaceVersions = await findWorkspaceNativePreviewPackages(); - if (workspaceVersions.length === 0) return; - - const wsVersion = workspaceVersions[0]; - const allow = "Use Workspace Version"; - const dismiss = "Dismiss"; - const suppress = "Never in this Workspace"; - - const result = await vscode.window.showInformationMessage( - `This workspace has TypeScript 7 installed (${wsVersion.version}). Would you like to use it?`, - allow, - dismiss, - suppress, - ); - - if (result === allow) { - if (!vscode.workspace.isTrusted) return; - await context.workspaceState.update(useWorkspaceTsdkStorageKey, true); - await updateTsdkConfig(wsVersion); - await vscode.commands.executeCommand("typescript.native-preview.restart"); - } - else if (result === suppress) { - await context.workspaceState.update(suppressKey, true); - } - } -} - -async function promptForProfileDirectory(): Promise { - const defaultDir = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath ?? ""; - const dir = await vscode.window.showInputBox({ - prompt: vscode.l10n.t("Enter directory path for profile output"), - value: defaultDir, - validateInput: value => { - if (!value.trim()) { - return vscode.l10n.t("Directory path is required"); - } - return undefined; - }, - }); - return dir?.trim(); -} +import * as path from "path"; +import * as vscode from "vscode"; +import { ActiveJsTsEditorTracker } from "./activeJsTsEditorTracker"; +import { Client } from "./client"; +import { + registerCodeLensShowLocationsCommand, + updateWorkspaceUseTsgoSetting, +} from "./commands"; +import { + type ContentMapperContribution, + documentMatchesContentMapperContributions, + serializeContentMapperContributions, + validateContentMapperRegistration, +} from "./contentMapperContributions"; +import { ProjectStatus } from "./projectStatus"; +import { setupStatusBar } from "./statusBar"; +import { TelemetryReporter } from "./telemetryReporting"; +import { + getDefaultExePath, + getExe, + getWorkspaceTsdkConfigValue, + getWorkspaceTsdkForPrompt, + readNativePreviewConfig, + resolveTsdkPath, + resolveTsdkPathToExe, + updateWorkspaceTsdkConfig, + useWorkspaceTsdkStorageKey, + workspaceConfigBase, +} from "./util"; + +/** + * SessionManager's lifetime is equal to that of the extension. It is responsible + * for starting, restarting, replacing, and disposing the Session. + */ +export class SessionManager implements vscode.Disposable { + currentSession?: Session; + private disposables: vscode.Disposable[] = []; + private outputChannel: vscode.LogOutputChannel; + private initializedEventEmitter: vscode.EventEmitter; + private telemetryReporter: TelemetryReporter; + private readonly contentMapperRegistrations = new Map(); + private lifecycleOperation = Promise.resolve(); + private contentMapperSyncOperation = Promise.resolve(); + + constructor( + context: vscode.ExtensionContext, + outputChannel: vscode.LogOutputChannel, + initializedEventEmitter: vscode.EventEmitter, + telemetryReporter: TelemetryReporter, + ) { + this.outputChannel = outputChannel; + this.telemetryReporter = telemetryReporter; + this.initializedEventEmitter = initializedEventEmitter; + + this.disposables.push(vscode.workspace.onDidChangeConfiguration(event => { + if (this.currentSession && event.affectsConfiguration("js/ts.contentMappers.enabled")) { + void this.restart(context).catch(error => { + this.outputChannel.error(`TypeScript language server restart failed: ${String(error)}`); + }); + } + })); + this.disposables.push(vscode.workspace.onDidOpenTextDocument(document => { + if (documentMatchesContentMapperContributions(document, this.contentMapperRegistrations)) { + void this.syncContentMapperContributions(); + } + })); + this.disposables.push(initializedEventEmitter.event(() => { + void this.syncContentMapperContributions(); + })); + } + + start(context: vscode.ExtensionContext): Promise { + return this.restart(context); + } + + restart(context: vscode.ExtensionContext): Promise { + return this.enqueueLifecycleOperation(() => this.restartNow(context)); + } + + private async restartNow(context: vscode.ExtensionContext): Promise { + if (this.currentSession) { + this.outputChannel.appendLine("Restarting TypeScript language server..."); + await this.currentSession.stop(); + } + const session = new Session(context, this.outputChannel, this.initializedEventEmitter, this.telemetryReporter, () => this.stop(), () => this.restart(context)); + this.currentSession = session; + try { + await session.start(context); + await this.syncContentMapperContributions(); + } + catch (error) { + if (this.currentSession === session) { + this.currentSession = undefined; + } + await session.dispose(); + throw error; + } + } + + stop(): Promise { + return this.enqueueLifecycleOperation(async () => { + if (this.currentSession) { + await this.currentSession.stop(); + this.currentSession = undefined; + } + }); + } + + async initializeAPIConnection(pipe?: string): Promise { + if (!this.currentSession) { + throw new Error(vscode.l10n.t("Language server is not running.")); + } + const result = await this.currentSession.client.initializeAPISession(pipe); + return result.pipe; + } + + registerContentMappers(contributorId: string, contributions: readonly ContentMapperContribution[]): vscode.Disposable { + validateContentMapperRegistration(contributorId, contributions); + if (this.contentMapperRegistrations.has(contributorId)) { + throw new Error(`Content mapper contributor '${contributorId}' is already registered.`); + } + this.contentMapperRegistrations.set(contributorId, contributions); + void this.syncContentMapperContributions(); + let disposed = false; + return new vscode.Disposable(() => { + if (disposed) return; + disposed = true; + this.contentMapperRegistrations.delete(contributorId); + void this.syncContentMapperContributions(); + }); + } + + private syncContentMapperContributions(): Promise { + const operation = this.contentMapperSyncOperation.then(() => this.syncContentMapperContributionsNow()); + this.contentMapperSyncOperation = operation.catch(() => {}); + return operation; + } + + private async syncContentMapperContributionsNow(): Promise { + try { + if (!this.currentSession?.client.isInitialized) return; + const openDocuments = vscode.workspace.textDocuments + .filter(document => documentMatchesContentMapperContributions(document, this.contentMapperRegistrations)) + .map(document => document.uri); + await this.currentSession.client.setContentMapperContributions( + serializeContentMapperContributions(this.contentMapperRegistrations), + openDocuments, + ); + } + catch (error) { + this.outputChannel.warn(`Content mapper contribution synchronization failed: ${String(error)}`); + } + } + + private enqueueLifecycleOperation(operation: () => Promise): Promise { + const result = this.lifecycleOperation.then(operation); + this.lifecycleOperation = result.catch(() => {}); + return result; + } + + dispose(): Promise { + return this.enqueueLifecycleOperation(async () => { + await this.currentSession?.dispose(); + this.currentSession = undefined; + await Promise.all(this.disposables.splice(0).map(d => d.dispose())); + }); + } +} + +/** + * Session's lifetime is equal to that of its LanguageClient. The LanguageClient + * can be restarted within the same Session only if the underlying exe path/version + * has not changed. Otherwise, a new Session must be created. Since Session only + * exists while the LSP server is running (or actively starting/restarting/stopping), + * it also owns the commands and UI elements that should only be active while the + * server is running. + */ +class Session implements vscode.Disposable { + client: Client; + private disposables: vscode.Disposable[] = []; + private context: vscode.ExtensionContext; + private outputChannel: vscode.LogOutputChannel; + private telemetryReporter: TelemetryReporter; + private initializedEventEmitter: vscode.EventEmitter; + private stopSession: () => Promise; + private restartSession: () => Promise; + + constructor( + context: vscode.ExtensionContext, + outputChannel: vscode.LogOutputChannel, + initializedEventEmitter: vscode.EventEmitter, + telemetryReporter: TelemetryReporter, + stopSession: () => Promise, + restartSession: () => Promise, + ) { + this.client = new Client(outputChannel, initializedEventEmitter, telemetryReporter); + this.context = context; + this.outputChannel = outputChannel; + this.telemetryReporter = telemetryReporter; + this.initializedEventEmitter = initializedEventEmitter; + this.stopSession = stopSession; + this.restartSession = restartSession; + this.registerCommands(); + } + + async start(context: vscode.ExtensionContext): Promise { + await vscode.commands.executeCommand("setContext", "typescript.isManagedFile", false); + const exe = await getExe(context); + await this.client.start(exe); + this.disposables.push(setupStatusBar(exe)); + + // Set up active editor tracker and UI features + const activeEditorTracker = new ActiveJsTsEditorTracker(); + this.disposables.push(activeEditorTracker); + + const projectStatus = new ProjectStatus(this.client, activeEditorTracker, this.initializedEventEmitter.event); + this.disposables.push(projectStatus); + + // If already initialized, fire immediately so projectStatus picks it up + if (this.client.isInitialized) { + this.initializedEventEmitter.fire(); + } + + await vscode.commands.executeCommand("setContext", "typescript.native-preview.serverRunning", true); + } + + tryRestartClient(context: vscode.ExtensionContext): Promise { + return this.client.tryRestart(context); + } + + registerCommands(): void { + this.disposables.push(registerCodeLensShowLocationsCommand()); + + this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.restart", async () => { + this.telemetryReporter.sendTelemetryEvent("command.restartLanguageServer"); + if (await this.tryRestartClient(this.context)) { + return; + } + + await this.restartSession(); + })); + + this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.output.focus", () => { + this.outputChannel.show(); + })); + + this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.selectVersion", async () => { + await promptSelectVersion(this.context, this.client, this.outputChannel, this.stopSession); + })); + + // TODO: Support the standard reload/go-to-project-config commands while + // Strada leaves their command palette entries visible in native mode: + // typescript.reloadProjects, javascript.reloadProjects, + // typescript.goToProjectConfig, and javascript.goToProjectConfig. + + this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.showMenu", () => { + showCommands(this.client); + })); + + this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.sortImports", () => { + return vscode.commands.executeCommand("editor.action.sourceAction", { + kind: "source.sortImports", + apply: "first", + }); + })); + + this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.removeUnusedImports", () => { + return vscode.commands.executeCommand("editor.action.sourceAction", { + kind: "source.removeUnusedImports", + apply: "first", + }); + })); + + this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.reportIssue", () => { + this.telemetryReporter.sendTelemetryEvent("command.reportIssue"); + vscode.commands.executeCommand("workbench.action.openIssueReporter", { + extensionId: this.context.extension.id, + }); + })); + + this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.dev.runGC", async () => { + try { + await this.client.runGC(); + vscode.window.showInformationMessage(vscode.l10n.t(`Garbage collection triggered`)); + } + catch (error) { + vscode.window.showErrorMessage(vscode.l10n.t(`Failed to run GC: {0}`, String(error))); + } + })); + + this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.dev.saveHeapProfile", async () => { + const dir = await promptForProfileDirectory(); + if (!dir) return; + try { + const file = await this.client.saveHeapProfile(dir); + vscode.window.showInformationMessage(vscode.l10n.t(`Heap profile saved to: {0}`, file)); + } + catch (error) { + vscode.window.showErrorMessage(vscode.l10n.t(`Failed to save heap profile: {0}`, String(error))); + } + })); + + this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.dev.saveAllocProfile", async () => { + const dir = await promptForProfileDirectory(); + if (!dir) return; + try { + const file = await this.client.saveAllocProfile(dir); + vscode.window.showInformationMessage(vscode.l10n.t(`Allocation profile saved to: {0}`, file)); + } + catch (error) { + vscode.window.showErrorMessage(vscode.l10n.t(`Failed to save allocation profile: {0}`, String(error))); + } + })); + + this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.dev.startCPUProfile", async () => { + const dir = await promptForProfileDirectory(); + if (!dir) return; + try { + await this.client.startCPUProfile(dir); + vscode.commands.executeCommand("setContext", "typescript.native-preview.cpuProfileRunning", true); + vscode.window.showInformationMessage(vscode.l10n.t(`CPU profiling started. Profile will be saved to: {0}`, dir)); + } + catch (error) { + vscode.window.showErrorMessage(vscode.l10n.t(`Failed to start CPU profile: {0}`, String(error))); + vscode.commands.executeCommand("setContext", "typescript.native-preview.cpuProfileRunning", false); + } + })); + + this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.dev.stopCPUProfile", async () => { + try { + const file = await this.client.stopCPUProfile(); + vscode.commands.executeCommand("setContext", "typescript.native-preview.cpuProfileRunning", false); + vscode.window.showInformationMessage(vscode.l10n.t(`CPU profile saved to: {0}`, file)); + } + catch (error) { + vscode.window.showErrorMessage(vscode.l10n.t(`Failed to stop CPU profile: {0}`, String(error))); + } + })); + + this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.initializeAPIConnection", async () => { + const result = await this.client.initializeAPISession(); + return result.pipe; + })); + + this.disposables.push(vscode.commands.registerCommand("typescript.native-preview.initializeAPIConnection.ui", async () => { + try { + const result = await this.client.initializeAPISession(); + const copyString = vscode.l10n.t("Copy"); + const copy = await vscode.window.showInformationMessage(vscode.l10n.t(`API session initialized. Listening on: {0}`, result.pipe), copyString); + if (copy === copyString) { + await vscode.env.clipboard.writeText(result.pipe); + } + } + catch (error) { + const message = error instanceof Error ? error.message : String(error); + vscode.window.showErrorMessage(vscode.l10n.t(`Failed to initialize API session: {0}`, message)); + } + })); + } + + private async disposeSessionState(): Promise { + await vscode.commands.executeCommand("setContext", "typescript.native-preview.serverRunning", false); + await vscode.commands.executeCommand("setContext", "typescript.native-preview.cpuProfileRunning", false); + const disposables = this.disposables.splice(0); + await Promise.all(disposables.map(d => d.dispose())); + } + + async stop(): Promise { + await this.disposeSessionState(); + await this.client.stop(); + } + + async dispose(): Promise { + await this.disposeSessionState(); + await this.client.dispose(); + } +} + +async function showCommands(client: Client): Promise { + interface CommandItem { + label: string; + description?: string; + kind?: vscode.QuickPickItemKind; + command?: string; + action?: () => Promise; + } + const commands: CommandItem[] = [ + { + label: vscode.l10n.t("$(refresh) Restart Server"), + description: vscode.l10n.t("Restart the TypeScript language server"), + command: "typescript.native-preview.restart", + }, + { + label: vscode.l10n.t("$(output) Show Output"), + description: vscode.l10n.t("Show the TypeScript output log"), + command: "typescript.native-preview.output.focus", + }, + { + label: vscode.l10n.t("$(report) Report Issue"), + description: vscode.l10n.t("Report an issue with TypeScript 7"), + command: "typescript.native-preview.reportIssue", + }, + { + label: vscode.l10n.t("$(versions) Select Version"), + description: vscode.l10n.t("Choose between bundled and workspace versions"), + command: "typescript.native-preview.selectVersion", + }, + { + label: vscode.l10n.t("$(stop-circle) Disable TypeScript 7"), + description: vscode.l10n.t("Switch back to the built-in TypeScript extension"), + command: "typescript.native-preview.disable", + }, + ]; + + const showDebugInfo = readNativePreviewConfig("showDebugInfo", false); + if (showDebugInfo) { + const exe = client.getCurrentExe(); + const pid = client.serverPid; + commands.push({ label: "", kind: vscode.QuickPickItemKind.Separator }); + if (exe) { + commands.push({ + label: vscode.l10n.t(`Executable`), + description: exe.path, + action: async () => { + await vscode.env.clipboard.writeText(exe.path); + vscode.window.showInformationMessage(vscode.l10n.t("Executable path copied to clipboard.")); + }, + }); + } + if (pid) { + commands.push({ + label: vscode.l10n.t(`PID`), + description: `${pid}`, + action: async () => { + await vscode.env.clipboard.writeText(`${pid}`); + vscode.window.showInformationMessage(vscode.l10n.t("Server PID copied to clipboard.")); + }, + }); + } + } + + const selected = await vscode.window.showQuickPick(commands, { + placeHolder: vscode.l10n.t("{0} Commands", "TypeScript 7"), + }); + + if (selected) { + if (selected.action) { + await selected.action(); + } + else if (selected.command) { + await vscode.commands.executeCommand(selected.command); + } + } +} + +interface VersionQuickPickItem extends vscode.QuickPickItem { + run(): Promise; + beforeRun?(): Promise; + restart?: boolean; +} + +function readStringArrayConfig(key: string): string[] { + const value = readNativePreviewConfig(key, undefined); + return Array.isArray(value) ? value.filter((v): v is string => typeof v === "string") : []; +} + +interface DetectedVersion { + folder: vscode.WorkspaceFolder; + version: string; + tsdkPath: string; + exePath: string; +} + +interface StradaVersion { + version: string; +} + +async function getStradaVersion(): Promise { + return await getStradaExtensionVersion("ms-vscode.vscode-typescript-next", ["node_modules", "typescript"]) + ?? await getStradaExtensionVersion("vscode.typescript-language-features", ["..", "node_modules", "typescript"]); +} + +async function getStradaExtensionVersion(extensionId: string, pathToTypescript: string[]): Promise { + const extension = vscode.extensions.getExtension(extensionId); + if (!extension) return undefined; + + const packagePath = vscode.Uri.joinPath(extension.extensionUri, ...pathToTypescript); + const tsserverPath = vscode.Uri.joinPath(packagePath, "lib", "tsserver.js"); + try { + await vscode.workspace.fs.stat(tsserverPath); + const packageJson = JSON.parse(await vscode.workspace.fs.readFile(vscode.Uri.joinPath(packagePath, "package.json")).then(buffer => buffer.toString())); + return { + version: typeof packageJson.version === "string" ? packageJson.version : "unknown", + }; + } + catch { + return undefined; + } +} + +async function findWorkspaceNativePreviewPackages(): Promise { + const results: DetectedVersion[] = []; + for (const folder of vscode.workspace.workspaceFolders ?? []) { + const packagePath = vscode.Uri.joinPath(folder.uri, "node_modules", "@typescript", "native-preview"); + const resolved = await resolveTsdkPathToExe(path.normalize(packagePath.fsPath)); + if (!resolved) continue; + results.push({ + folder, + version: resolved?.version ?? "unknown", + tsdkPath: path.normalize(packagePath.fsPath), + exePath: resolved?.path ?? "", + }); + } + return results; +} + +/** + * Compute the tsdk path to persist in workspace config. Uses a path relative + * to the workspace config base directory (the `.code-workspace` file's parent + * in multi-root, or the lone workspace folder in single-root). Falls back to + * the absolute path if there is no workspace. + */ +function tsdkPathForConfig(detected: DetectedVersion): string { + const base = workspaceConfigBase(); + if (!base) { + return detected.tsdkPath; + } + return path.relative(base.fsPath, detected.tsdkPath); +} + +/** + * Update the tsdk config to point at the detected version, but only if the + * existing value doesn't already resolve to the same absolute path (avoiding + * unnecessary config churn from formatting differences like absolute vs + * relative, leading ./, etc.). + */ +async function updateTsdkConfig(detected: DetectedVersion): Promise { + const currentValue = getWorkspaceTsdkConfigValue(); + const newValue = tsdkPathForConfig(detected); + if (currentValue !== undefined && resolveTsdkPath(currentValue) === resolveTsdkPath(newValue)) { + return; + } + await updateWorkspaceTsdkConfig(newValue); +} + +async function promptSelectVersion(context: vscode.ExtensionContext, client: Client, outputChannel: vscode.LogOutputChannel, stopServer: () => Promise): Promise { + const currentExePath = client.getCurrentExe()?.path; + const defaultExe = await getDefaultExePath(context); + const stradaVersion = await getStradaVersion(); + const workspaceVersions = await findWorkspaceNativePreviewPackages(); + const items: VersionQuickPickItem[] = []; + + if (stradaVersion) { + items.push({ + label: vscode.l10n.t("Use Built-in TypeScript"), + description: stradaVersion.version, + detail: "", + beforeRun: stopServer, + run: async () => { + await context.workspaceState.update(useWorkspaceTsdkStorageKey, false); + await updateWorkspaceUseTsgoSetting(false); + outputChannel.appendLine("Switched to bundled TypeScript version."); + }, + }); + } + + items.push({ + label: (currentExePath === defaultExe.path ? "• " : "") + vscode.l10n.t("Use TypeScript 7"), + description: defaultExe.version, + detail: "", + restart: true, + run: async () => { + await context.workspaceState.update(useWorkspaceTsdkStorageKey, false); + await updateWorkspaceUseTsgoSetting(true); + outputChannel.appendLine("Switched to TypeScript 7."); + }, + }); + + // Workspace versions + if (vscode.workspace.isTrusted) { + for (const wsVersion of workspaceVersions) { + const isActive = currentExePath === wsVersion.exePath; + items.push({ + label: (isActive ? "• " : "") + vscode.l10n.t("Use Workspace Version"), + description: wsVersion.version, + detail: wsVersion.tsdkPath, + restart: true, + run: async () => { + await context.workspaceState.update(useWorkspaceTsdkStorageKey, true); + await updateTsdkConfig(wsVersion); + await updateWorkspaceUseTsgoSetting(true); + outputChannel.appendLine(`Switched to workspace tsgo version (${wsVersion.version}).`); + }, + }); + } + } + else if (workspaceVersions.length > 0) { + items.push({ + label: "", + kind: vscode.QuickPickItemKind.Separator, + run: async () => {}, + }); + items.push({ + label: vscode.l10n.t("$(lock) Manage Workspace Trust to select a workspace version"), + run: async () => { + await vscode.commands.executeCommand("workbench.trust.manage"); + }, + }); + } + + // Additional tsdk locations from settings + const additionalLocations = [ + ...readStringArrayConfig("tsdk.additionalLocations"), + ...readStringArrayConfig("additionalTsdkLocations"), + ]; + if (additionalLocations.length > 0) { + items.push({ + label: "", + kind: vscode.QuickPickItemKind.Separator, + run: async () => {}, + }); + for (const loc of additionalLocations) { + const resolved = await resolveTsdkPathToExe(loc); + if (!resolved) continue; + if (resolved.path === defaultExe.path) continue; + if (workspaceVersions.some(ws => ws.exePath === resolved.path)) continue; + const isActive = currentExePath === resolved.path; + items.push({ + label: (isActive ? "• " : "") + vscode.l10n.t("Use Custom Version"), + description: resolved.version, + detail: resolved.path, + restart: true, + run: async () => { + await context.workspaceState.update(useWorkspaceTsdkStorageKey, true); + await updateWorkspaceTsdkConfig(loc); + await updateWorkspaceUseTsgoSetting(true); + outputChannel.appendLine(`Switched to custom TypeScript version at ${loc}.`); + }, + }); + } + } + + const selected = await vscode.window.showQuickPick(items, { + placeHolder: vscode.l10n.t("Select the TypeScript version used for JavaScript and TypeScript language features"), + }); + + if (selected) { + await selected.beforeRun?.(); + await selected.run(); + if (selected.restart) { + await vscode.commands.executeCommand("typescript.native-preview.restart"); + } + } +} + +/** + * If the workspace has a tsdk setting pending consent, or has + * `@typescript/typescript` installed in node_modules, prompt the user + * to allow using it. + */ +export async function promptUseWorkspaceVersion(context: vscode.ExtensionContext): Promise { + if (!vscode.workspace.isTrusted) return; + + const useWorkspaceTsdk = context.workspaceState.get(useWorkspaceTsdkStorageKey, false); + if (useWorkspaceTsdk) return; // already opted in + + const suppressKey = "typescript.native-preview.suppressPromptWorkspaceTsdk"; + if (context.workspaceState.get(suppressKey, false)) return; + + const workspaceTsdk = await getWorkspaceTsdkForPrompt(); + if (workspaceTsdk !== undefined) { + // The workspace config already specifies a tsdk location, but the + // user hasn't consented to using it. Just need their approval. + const resolved = await resolveTsdkPathToExe(workspaceTsdk); + if (!resolved) return; + const allow = vscode.l10n.t("Allow"); + const dismiss = vscode.l10n.t("Dismiss"); + const suppress = vscode.l10n.t("Never in this Workspace"); + + const result = await vscode.window.showInformationMessage( + vscode.l10n.t(`This workspace has a TypeScript 7 tsdk configured ({0}). Would you like to use it?`, resolved.version), + allow, + dismiss, + suppress, + ); + + if (result === allow) { + if (!vscode.workspace.isTrusted) return; + await context.workspaceState.update(useWorkspaceTsdkStorageKey, true); + await vscode.commands.executeCommand("typescript.native-preview.restart"); + } + else if (result === suppress) { + await context.workspaceState.update(suppressKey, true); + } + } + else { + // No workspace tsdk config, but check if native-preview is installed + // in the workspace's node_modules. + const workspaceVersions = await findWorkspaceNativePreviewPackages(); + if (workspaceVersions.length === 0) return; + + const wsVersion = workspaceVersions[0]; + const allow = "Use Workspace Version"; + const dismiss = "Dismiss"; + const suppress = "Never in this Workspace"; + + const result = await vscode.window.showInformationMessage( + `This workspace has TypeScript 7 installed (${wsVersion.version}). Would you like to use it?`, + allow, + dismiss, + suppress, + ); + + if (result === allow) { + if (!vscode.workspace.isTrusted) return; + await context.workspaceState.update(useWorkspaceTsdkStorageKey, true); + await updateTsdkConfig(wsVersion); + await vscode.commands.executeCommand("typescript.native-preview.restart"); + } + else if (result === suppress) { + await context.workspaceState.update(suppressKey, true); + } + } +} + +async function promptForProfileDirectory(): Promise { + const defaultDir = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath ?? ""; + const dir = await vscode.window.showInputBox({ + prompt: vscode.l10n.t("Enter directory path for profile output"), + value: defaultDir, + validateInput: value => { + if (!value.trim()) { + return vscode.l10n.t("Directory path is required"); + } + return undefined; + }, + }); + return dir?.trim(); +} diff --git a/packages/vscode-typescript/src/statusBar.ts b/packages/vscode-typescript/src/statusBar.ts index e53cf20db11b8..8489040159294 100644 --- a/packages/vscode-typescript/src/statusBar.ts +++ b/packages/vscode-typescript/src/statusBar.ts @@ -1,17 +1,17 @@ -import * as vscode from "vscode"; -import { - ExeInfo, - jsTsLanguageModes, -} from "./util"; - -export function setupStatusBar(exe: ExeInfo): vscode.Disposable { - const statusItem = vscode.languages.createLanguageStatusItem("typescript.native-preview.status", jsTsLanguageModes); - statusItem.name = vscode.l10n.t("TypeScript 7"); - statusItem.text = exe.isLocal ? vscode.l10n.t("$(beaker) {0}", exe.version) : exe.version; - statusItem.detail = vscode.l10n.t("TypeScript Language Server"); - statusItem.command = { - title: vscode.l10n.t("Show Menu"), - command: "typescript.native-preview.showMenu", - }; - return statusItem; -} +import * as vscode from "vscode"; +import { + ExeInfo, + jsTsLanguageModes, +} from "./util"; + +export function setupStatusBar(exe: ExeInfo): vscode.Disposable { + const statusItem = vscode.languages.createLanguageStatusItem("typescript.native-preview.status", jsTsLanguageModes); + statusItem.name = vscode.l10n.t("TypeScript 7"); + statusItem.text = exe.isLocal ? vscode.l10n.t("$(beaker) {0}", exe.version) : exe.version; + statusItem.detail = vscode.l10n.t("TypeScript Language Server"); + statusItem.command = { + title: vscode.l10n.t("Show Menu"), + command: "typescript.native-preview.showMenu", + }; + return statusItem; +} diff --git a/packages/vscode-typescript/src/telemetryReporting.ts b/packages/vscode-typescript/src/telemetryReporting.ts index 28689e6143262..03f01b19b0e55 100644 --- a/packages/vscode-typescript/src/telemetryReporting.ts +++ b/packages/vscode-typescript/src/telemetryReporting.ts @@ -1,103 +1,103 @@ -import type { TelemetryReporter as VSCodeTelemetryReporter } from "@vscode/extension-telemetry"; -import type { IExperimentationTelemetry } from "vscode-tas-client"; - -// As new events are added, update the TelemetryReporter interface below. -// This helps ensure that the telemetry events used in the codebase are -// properly typed, and that properties/measurements are never forgotten. -// -// The difference between `sendTelemetryEvent` and `sendTelemetryErrorEvent` is that -// these methods respect user preferences around reporting (i.e. `telemetry.telemetryLevel`). -// -// The "untyped" variants are provided for when properties/measurements are not known -// from the editor client - for example, when forwarding telemetry events from the language server. -export interface TelemetryReporter { - sendTelemetryEvent(eventName: "command.enableNativePreview"): void; - sendTelemetryEvent(eventName: "command.disableNativePreview"): void; - sendTelemetryEvent(eventName: "command.restartLanguageServer"): void; - sendTelemetryEvent(eventName: "command.reportIssue"): void; - sendTelemetryEvent(eventName: "languageServer.start", data: LSServerStart): void; - - sendTelemetryErrorEvent(eventName: "languageServer.connectionError", data: LSConnectionError): void; - sendTelemetryErrorEvent(eventName: "languageServer.connectionClosed", data: LSServerConnectionClosed): void; - sendTelemetryErrorEvent(eventName: "languageServer.errorResponse", data: LSErrorResponse): void; - sendTelemetryErrorEvent(eventName: "languageServer.unexpectedTelemetryPurpose", data: UnexpectedTelemetryPurpose): void; - - sendTelemetryEventUntyped(eventName: string, data?: Record, measurements?: Record): void; - sendTelemetryErrorEventUntyped(eventName: string, data?: Record, measurements?: Record): void; - - dispose(): void; -} - -export interface ExperimentationTelemetryReporter extends TelemetryReporter, IExperimentationTelemetry {} - -// Note: -// This reporter *supports* experimentation telemetry, -// but will only do so when passed to an `ExperimentationService` which -// will set shared properties on this reporter. -export function createTelemetryReporter(vscReporter: VSCodeTelemetryReporter): ExperimentationTelemetryReporter { - let sharedProperties: Record = Object.create(null); - - return { - // Primary reporting methods for the extension. - sendTelemetryEvent, - sendTelemetryErrorEvent, - sendTelemetryEventUntyped: sendTelemetryEvent, - sendTelemetryErrorEventUntyped: sendTelemetryErrorEvent, - - // Required for the experimentation telemetry service interface. - setSharedProperty, - postEvent, - - dispose: () => vscReporter.dispose(), - }; - - function setSharedProperty(key: string, value: string): void { - sharedProperties[key] = value; - } - - function postEvent(eventName: string, props: Map): void { - const propsAsObj = { ...sharedProperties, ...Object.fromEntries(props) }; - vscReporter.sendTelemetryEvent(eventName, propsAsObj); - } - - function sendTelemetryEvent(eventName: string, data?: Record, measurements?: Record): void { - data = { ...sharedProperties, ...data }; - vscReporter.sendTelemetryEvent(eventName, data, measurements); - } - - function sendTelemetryErrorEvent(eventName: string, data?: Record, measurements?: Record): void { - data = { ...sharedProperties, ...data }; - vscReporter.sendTelemetryErrorEvent(eventName, data, measurements); - } -} - -export type LSServerStart = { - version: string; -}; - -export type LSConnectionError = { - resultingAction: string; -}; - -export type LSServerConnectionClosed = { - resultingAction: string; - lastStderr: string; -}; - -export type LSErrorResponse = { - errorCode: string; - requestMethod: string; - stack: string; -}; - -export type EnableNativePreview = {}; - -export type DisableNativePreview = {}; - -export type RestartLanguageServer = {}; - -export type ReportIssue = {}; - -export type UnexpectedTelemetryPurpose = { - telemetryPurpose: string; -}; +import type { TelemetryReporter as VSCodeTelemetryReporter } from "@vscode/extension-telemetry"; +import type { IExperimentationTelemetry } from "vscode-tas-client"; + +// As new events are added, update the TelemetryReporter interface below. +// This helps ensure that the telemetry events used in the codebase are +// properly typed, and that properties/measurements are never forgotten. +// +// The difference between `sendTelemetryEvent` and `sendTelemetryErrorEvent` is that +// these methods respect user preferences around reporting (i.e. `telemetry.telemetryLevel`). +// +// The "untyped" variants are provided for when properties/measurements are not known +// from the editor client - for example, when forwarding telemetry events from the language server. +export interface TelemetryReporter { + sendTelemetryEvent(eventName: "command.enableNativePreview"): void; + sendTelemetryEvent(eventName: "command.disableNativePreview"): void; + sendTelemetryEvent(eventName: "command.restartLanguageServer"): void; + sendTelemetryEvent(eventName: "command.reportIssue"): void; + sendTelemetryEvent(eventName: "languageServer.start", data: LSServerStart): void; + + sendTelemetryErrorEvent(eventName: "languageServer.connectionError", data: LSConnectionError): void; + sendTelemetryErrorEvent(eventName: "languageServer.connectionClosed", data: LSServerConnectionClosed): void; + sendTelemetryErrorEvent(eventName: "languageServer.errorResponse", data: LSErrorResponse): void; + sendTelemetryErrorEvent(eventName: "languageServer.unexpectedTelemetryPurpose", data: UnexpectedTelemetryPurpose): void; + + sendTelemetryEventUntyped(eventName: string, data?: Record, measurements?: Record): void; + sendTelemetryErrorEventUntyped(eventName: string, data?: Record, measurements?: Record): void; + + dispose(): void; +} + +export interface ExperimentationTelemetryReporter extends TelemetryReporter, IExperimentationTelemetry {} + +// Note: +// This reporter *supports* experimentation telemetry, +// but will only do so when passed to an `ExperimentationService` which +// will set shared properties on this reporter. +export function createTelemetryReporter(vscReporter: VSCodeTelemetryReporter): ExperimentationTelemetryReporter { + let sharedProperties: Record = Object.create(null); + + return { + // Primary reporting methods for the extension. + sendTelemetryEvent, + sendTelemetryErrorEvent, + sendTelemetryEventUntyped: sendTelemetryEvent, + sendTelemetryErrorEventUntyped: sendTelemetryErrorEvent, + + // Required for the experimentation telemetry service interface. + setSharedProperty, + postEvent, + + dispose: () => vscReporter.dispose(), + }; + + function setSharedProperty(key: string, value: string): void { + sharedProperties[key] = value; + } + + function postEvent(eventName: string, props: Map): void { + const propsAsObj = { ...sharedProperties, ...Object.fromEntries(props) }; + vscReporter.sendTelemetryEvent(eventName, propsAsObj); + } + + function sendTelemetryEvent(eventName: string, data?: Record, measurements?: Record): void { + data = { ...sharedProperties, ...data }; + vscReporter.sendTelemetryEvent(eventName, data, measurements); + } + + function sendTelemetryErrorEvent(eventName: string, data?: Record, measurements?: Record): void { + data = { ...sharedProperties, ...data }; + vscReporter.sendTelemetryErrorEvent(eventName, data, measurements); + } +} + +export type LSServerStart = { + version: string; +}; + +export type LSConnectionError = { + resultingAction: string; +}; + +export type LSServerConnectionClosed = { + resultingAction: string; + lastStderr: string; +}; + +export type LSErrorResponse = { + errorCode: string; + requestMethod: string; + stack: string; +}; + +export type EnableNativePreview = {}; + +export type DisableNativePreview = {}; + +export type RestartLanguageServer = {}; + +export type ReportIssue = {}; + +export type UnexpectedTelemetryPurpose = { + telemetryPurpose: string; +}; diff --git a/packages/vscode-typescript/src/tsdkPackage.ts b/packages/vscode-typescript/src/tsdkPackage.ts index 29ddf8974c37d..9bad2d3deecd8 100644 --- a/packages/vscode-typescript/src/tsdkPackage.ts +++ b/packages/vscode-typescript/src/tsdkPackage.ts @@ -1,8 +1,8 @@ -import module from "node:module"; -import path from "node:path"; - -export function resolvePackageExecutable(packageJsonPath: string, platformPackage: string, exeName: string): string { - const require = module.createRequire(packageJsonPath); - const platformPackageJson = require.resolve(`@typescript/${platformPackage}/package.json`); - return path.join(path.dirname(platformPackageJson), "lib", exeName); -} +import module from "node:module"; +import path from "node:path"; + +export function resolvePackageExecutable(packageJsonPath: string, platformPackage: string, exeName: string): string { + const require = module.createRequire(packageJsonPath); + const platformPackageJson = require.resolve(`@typescript/${platformPackage}/package.json`); + return path.join(path.dirname(platformPackageJson), "lib", exeName); +} diff --git a/packages/vscode-typescript/src/util.ts b/packages/vscode-typescript/src/util.ts index a299423f81948..f2c4f0165d5b4 100644 --- a/packages/vscode-typescript/src/util.ts +++ b/packages/vscode-typescript/src/util.ts @@ -1,527 +1,527 @@ -import * as fs from "fs"; -import * as path from "path"; -import * as vscode from "vscode"; -import { resolvePackageExecutable } from "./tsdkPackage"; - -export const aiConnectionString = "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255"; - -export const languageClientName = "TypeScript Language Server"; -export const nightlyExtensionId = "TypeScriptTeam.vscode-typescript-nightly"; -export const enableContributedNightlyVersion = true; - -export const jsTsLanguageModes = [ - "typescript", - "typescriptreact", - "javascript", - "javascriptreact", -]; - -export const builtinTSExtensionId = "vscode.typescript-language-features"; - -/** - * URI schemes for which JS/TS language features should be disabled. - */ -export const disabledSchemes = new Set([ - "git", - "vsls", - "github", - "azurerepos", - "chat-editing-text-model", -]); - -export function isSupportedLanguageMode(doc: vscode.TextDocument): boolean { - return jsTsLanguageModes.includes(doc.languageId); -} - -const configRegex = /^[jt]sconfig\.(.+\.)?json$/i; -export function isJsConfigOrTsConfigFileName(fileName: string): boolean { - return configRegex.test(path.basename(fileName)); -} - -export interface ExeInfo { - path: string; - version: string; - isLocal?: boolean; -} - -const packagedExeBaseNames = ["tsc", "tsgo"]; - -export async function getBuiltinExePath(context: vscode.ExtensionContext): Promise { - if (context.extensionMode === vscode.ExtensionMode.Development) { - const exeName = `tsc${process.platform === "win32" ? ".exe" : ""}`; - const exe = context.asAbsolutePath(path.join("../../", "built", "local", exeName)); - try { - await vscode.workspace.fs.stat(vscode.Uri.file(exe)); - return { path: exe, version: "(local)", isLocal: true }; - } - catch {} - } - return getPackagedExePath(context.extension.extensionUri, getBundledTypeScriptVersion(context.extension.packageJSON)); -} - -export async function getNightlyExePath(): Promise { - const extension = vscode.extensions.getExtension(nightlyExtensionId); - if (!extension) { - return undefined; - } - - return tryGetPackagedExePath(extension.extensionUri, getBundledTypeScriptVersion(extension.packageJSON)); -} - -export async function getDefaultExePath(context: vscode.ExtensionContext): Promise { - if (enableContributedNightlyVersion) { - const nightlyExe = await getNightlyExePath(); - if (nightlyExe) { - return nightlyExe; - } - } - return getBuiltinExePath(context); -} - -async function getPackagedExePath(extensionUri: vscode.Uri, version: unknown): Promise { - const exe = await tryGetPackagedExePath(extensionUri, version); - if (exe) { - return exe; - } - throw new Error(vscode.l10n.t("Could not find a TypeScript executable in the extension package.")); -} - -function getBundledTypeScriptVersion(packageJSON: unknown): string { - if (packageJSON && typeof packageJSON === "object" && "bundledTypeScriptVersion" in packageJSON) { - const version = packageJSON.bundledTypeScriptVersion; - if (typeof version === "string") { - return version; - } - } - return "unknown"; -} - -async function tryGetPackagedExePath(extensionUri: vscode.Uri, version: unknown): Promise { - for (const baseName of packagedExeBaseNames) { - const exeName = `${baseName}${process.platform === "win32" ? ".exe" : ""}`; - const exePath = vscode.Uri.joinPath(extensionUri, "lib", exeName); - try { - await vscode.workspace.fs.stat(exePath); - return { - path: withLongPathPrefix(exePath.fsPath), - version: typeof version === "string" ? version : "unknown", - }; - } - catch {} - } - return undefined; -} - -/** - * Returns the base directory for resolving relative paths in workspace config. - * - Multi-root workspace: the directory containing the `.code-workspace` file. - * - Single-root workspace: the lone workspace folder. - * - No workspace: undefined. - */ -export function workspaceConfigBase(): vscode.Uri | undefined { - const wsFile = vscode.workspace.workspaceFile; - if (wsFile && wsFile.scheme === "file") { - return vscode.Uri.file(path.dirname(wsFile.fsPath)); - } - if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0) { - return vscode.workspace.workspaceFolders[0].uri; - } - return undefined; -} - -function workspaceResolve(relativePath: string): vscode.Uri { - if (path.isAbsolute(relativePath)) { - return vscode.Uri.file(relativePath); - } - const base = workspaceConfigBase(); - if (base) { - return vscode.Uri.joinPath(base, relativePath); - } - return vscode.Uri.file(relativePath); -} - -/** - * Memento used to control whether the user has opted into using a tsdk location defined - * in workspace settings. This is *not* a trust boundary - workspace trust is required - * before the extension will prompt to set this memento to true. This setting is here to - * provide users a way to opt out of using the workspace-provided tsdk without changing - * committed workspace settings, e.g. when the workspace tsdk is very outdated or the user - * is trialing a nightly TS version. Since the stored value is only a boolean, it does not - * protect against executing a different tsdk than the one the user originally opted into - * if the workspace settings or node_modules content changes - that's why workspace trust - * is always required, and why the prompts that set this value should not be interpreted - * as indicating trust for a specific tsdk installation. - */ -export const useWorkspaceTsdkStorageKey = "typescript.native-preview.useWorkspaceTsdk"; - -export async function getExe(context: vscode.ExtensionContext): Promise { - for (const candidate of getTrustedTsdkCandidates(context, await getTsdkCandidates())) { - const exe = await resolveTsdkPathToExe(candidate.value); - if (exe) { - return exe; - } - } - - return getDefaultExePath(context); -} - -export async function hasTsdkConfigured(): Promise { - return (await getTsdkCandidates({ nativeOnly: false })).length > 0; -} - -export async function hasNativeTsdkConfigured(context: vscode.ExtensionContext): Promise { - return await getTsdkServerKind(context) === "lsp"; -} - -export async function getTsdkServerKind(context: vscode.ExtensionContext): Promise<"lsp" | "tsserver" | undefined> { - for (const candidate of getTrustedTsdkCandidates(context, await getTsdkCandidates({ nativeOnly: false }))) { - const kind = await classifyTsdk(candidate.value); - if (kind) { - return kind; - } - } -} - -async function classifyTsdk(tsdkPath: string): Promise<"lsp" | "tsserver" | undefined> { - if (await pathHasTsserverJs(tsdkPath)) { - return "tsserver"; - } - if (await resolveTsdkPathToExe(tsdkPath)) { - return "lsp"; - } -} - -function getTrustedTsdkCandidates(context: vscode.ExtensionContext, tsdkCandidates: ExplicitConfigValue[]): ExplicitConfigValue[] { - // If tsdk is set at the workspace level, require both workspace trust and - // explicit user opt-in. Workspace trust can be revoked after the memento is - // set, so we must always check both. - if (tsdkCandidates.some(candidate => candidate.target !== vscode.ConfigurationTarget.Global)) { - if (!vscode.workspace.isTrusted || !context.workspaceState.get(useWorkspaceTsdkStorageKey, false)) { - return tsdkCandidates.filter(candidate => candidate.target === vscode.ConfigurationTarget.Global); - } - } - return tsdkCandidates; -} - -interface ExplicitConfigValue { - value: T; - target: vscode.ConfigurationTarget; - order: number; -} - -interface TsdkConfigSource { - section: string; - key: string; - nativeOnly: boolean; -} - -const tsdkConfigSources: readonly TsdkConfigSource[] = [ - { section: "js/ts", key: "tsdk.path", nativeOnly: true }, - { section: "typescript", key: "tsdk", nativeOnly: true }, - { section: "typescript.native-preview", key: "tsdk", nativeOnly: false }, -]; - -export function readNativePreviewConfig(key: string, defaultValue: T): T { - const explicit = getExplicitConfigValues("js/ts", key)[0]; - if (explicit) { - return explicit.value; - } - return vscode.workspace.getConfiguration("typescript.native-preview").get(key, defaultValue); -} - -export function getWorkspaceTsdkConfigValue(): string | undefined { - return vscode.workspace.getConfiguration("js/ts").inspect("tsdk.path")?.workspaceValue; -} - -export async function updateWorkspaceTsdkConfig(value: string): Promise { - await vscode.workspace.getConfiguration("js/ts").update("tsdk.path", value, vscode.ConfigurationTarget.Workspace); -} - -export async function getWorkspaceTsdkForPrompt(): Promise { - const candidates = await getTsdkCandidates({ filter: candidate => candidate.target !== vscode.ConfigurationTarget.Global }); - for (const candidate of candidates) { - if (await resolveTsdkPathToExe(candidate.value)) { - return candidate.value; - } - } - return undefined; -} - -function getExplicitConfigValues(section: string, key: string): ExplicitConfigValue[] { - const inspection = vscode.workspace.getConfiguration(section).inspect(key); - if (!inspection) { - return []; - } - - const candidates: Array<{ value: T | undefined; target: vscode.ConfigurationTarget; order: number; }> = [ - { value: inspection.workspaceFolderLanguageValue, target: vscode.ConfigurationTarget.WorkspaceFolder, order: 0 }, - { value: inspection.workspaceFolderValue, target: vscode.ConfigurationTarget.WorkspaceFolder, order: 1 }, - { value: inspection.workspaceLanguageValue, target: vscode.ConfigurationTarget.Workspace, order: 2 }, - { value: inspection.workspaceValue, target: vscode.ConfigurationTarget.Workspace, order: 3 }, - { value: inspection.globalLanguageValue, target: vscode.ConfigurationTarget.Global, order: 4 }, - { value: inspection.globalValue, target: vscode.ConfigurationTarget.Global, order: 5 }, - ]; - - const result: ExplicitConfigValue[] = []; - for (const candidate of candidates) { - if (candidate.value !== undefined) { - result.push({ - value: candidate.value, - target: candidate.target, - order: candidate.order, - }); - } - } - return result.sort(compareExplicitConfigValues); -} - -function compareExplicitConfigValues(a: ExplicitConfigValue, b: ExplicitConfigValue): number { - return b.target - a.target || a.order - b.order; -} - -async function getTsdkCandidates(options?: { nativeOnly?: boolean; filter?: (candidate: ExplicitConfigValue) => boolean; }): Promise[]> { - const candidates: ExplicitConfigValue[] = []; - for (let sourceIndex = 0; sourceIndex < tsdkConfigSources.length; sourceIndex++) { - const source = tsdkConfigSources[sourceIndex]; - for (const candidate of getExplicitConfigValues(source.section, source.key)) { - if (!candidate.value || typeof candidate.value !== "string") { - continue; - } - if ((options?.nativeOnly ?? true) && source.nativeOnly && await pathHasTsserverJs(candidate.value)) { - continue; - } - candidates.push({ - ...candidate, - order: candidate.order + sourceIndex * 10, - }); - } - } - return candidates.filter(options?.filter ?? (() => true)).sort(compareExplicitConfigValues); -} - -async function pathHasTsserverJs(tsdkPath: string): Promise { - const resolved = workspaceResolve(tsdkPath); - for (const candidate of [vscode.Uri.joinPath(resolved, "tsserver.js"), vscode.Uri.joinPath(resolved, "lib", "tsserver.js")]) { - try { - await vscode.workspace.fs.stat(candidate); - return true; - } - catch {} - } - return false; -} - -/** - * Resolve a tsdk path (which may be relative) to a normalized absolute path. - */ -export function resolveTsdkPath(tsdkPath: string): string { - return path.normalize(workspaceResolve(tsdkPath).fsPath); -} - -export async function resolveTsdkPathToExe(tsdkPath: string): Promise { - const resolved = await realpathUri(workspaceResolve(tsdkPath)); - for (const packagePath of [resolved, vscode.Uri.joinPath(resolved, "..")]) { - try { - const packageJsonPath = vscode.Uri.joinPath(packagePath, "package.json"); - const packageJson = JSON.parse(await vscode.workspace.fs.readFile(packageJsonPath).then(buffer => buffer.toString())); - // NOTE: Keep in sync with packages/typescript/lib/getExePath.js. - const name: unknown = packageJson.name; - const bin: unknown = packageJson.bin; - if (typeof name !== "string" || !bin || typeof bin !== "object") continue; - - const baseName = name.startsWith("@") ? name.split("/")[1] : name; - if (!baseName) continue; - const expectedBinName = baseName === "typescript" ? "tsc" : "tsgo"; - if (!Object.prototype.hasOwnProperty.call(bin, expectedBinName)) continue; - - const exeName = `${expectedBinName}${process.platform === "win32" ? ".exe" : ""}`; - const platformPackage = `${baseName}-${process.platform}-${process.arch}`; - const exePath = vscode.Uri.file(resolvePackageExecutable(packageJsonPath.fsPath, platformPackage, exeName)); - await vscode.workspace.fs.stat(exePath); - return { path: withLongPathPrefix(exePath.fsPath), version: typeof packageJson.version === "string" ? packageJson.version : "unknown" }; - } - catch {} - } - for (const baseName of packagedExeBaseNames) { - try { - const exePath = vscode.Uri.joinPath(resolved, `${baseName}${process.platform === "win32" ? ".exe" : ""}`); - await vscode.workspace.fs.stat(exePath); - return { path: withLongPathPrefix(exePath.fsPath), version: "(local)", isLocal: true }; - } - catch {} - } -} - -async function realpathUri(uri: vscode.Uri): Promise { - if (uri.scheme !== "file") { - return uri; - } - try { - return vscode.Uri.file(await fs.promises.realpath(uri.fsPath)); - } - catch { - return uri; - } -} - -function withLongPathPrefix(exePath: string): string { - if (process.platform === "win32" && exePath.length >= 248 && !exePath.startsWith("\\\\?\\")) { - return "\\\\?\\" + exePath; - } - return exePath; -} - -export function getLanguageForUri(uri: vscode.Uri): string | undefined { - const ext = path.posix.extname(uri.path); - switch (ext) { - case ".ts": - case ".mts": - case ".cts": - return "typescript"; - case ".js": - case ".mjs": - case ".cjs": - return "javascript"; - case ".tsx": - return "typescriptreact"; - case ".jsx": - return "javascriptreact"; - default: - return undefined; - } -} - -export function needsExtHostRestartOnChange() { - const majorVersion = parseInt(vscode.version.split(".")[0]); - const minorVersion = parseInt(vscode.version.split(".")[1]); - return majorVersion <= 1 && minorVersion < 105; -} - -export async function restartExtHostOnChangeIfNeeded(): Promise { - if (needsExtHostRestartOnChange()) { - await vscode.commands.executeCommand("workbench.action.restartExtensionHost"); - } -} - -/** - * Read the useTsgo setting from both `js/ts.experimental.useTsgo` and - * `typescript.experimental.useTsgo`, using `inspect()` to only consider - * explicitly set values (ignoring VS Code defaults). - * - * Each setting key is resolved using standard VS Code precedence (workspace - * folder > workspace > global, with language-specific overrides taking - * priority within each scope). When both keys are explicitly configured, the - * value set at the most specific scope wins, and `js/ts` wins over - * `typescript` at the same scope. Returns `undefined` if neither setting has - * been explicitly configured. - */ -export function getUseTsgo(): boolean | undefined { - const tsValue = getExplicitUseTsgo("typescript"); - const jsTsValue = getExplicitUseTsgo("js/ts"); - - if (tsValue !== undefined || jsTsValue !== undefined) { - const jsTsTarget = getExplicitConfigTarget(vscode.workspace.getConfiguration("js/ts"), "experimental.useTsgo"); - const tsTarget = getExplicitConfigTarget(vscode.workspace.getConfiguration("typescript"), "experimental.useTsgo"); - const mostSpecific = Math.max(jsTsTarget ?? vscode.ConfigurationTarget.Global, tsTarget ?? vscode.ConfigurationTarget.Global); - return jsTsTarget === mostSpecific ? jsTsValue : tsValue; - } - - return undefined; -} - -export function getExplicitConfigTarget( - config: vscode.WorkspaceConfiguration, - key: string, -): vscode.ConfigurationTarget | undefined { - const inspection = config.inspect(key); - if (!inspection) return undefined; - if (inspection.workspaceFolderValue !== undefined) return vscode.ConfigurationTarget.WorkspaceFolder; - if (inspection.workspaceValue !== undefined) return vscode.ConfigurationTarget.Workspace; - if (inspection.globalValue !== undefined) return vscode.ConfigurationTarget.Global; - return undefined; -} - -export enum ConfigName { - JsTsConfigName = "js/ts.experimental.useTsgo", - DeprecatedTypeScriptConfigName = "typescript.experimental.useTsgo", -} - -/** - * Returns the name of the setting with the appropriate precedence that is explicitly set, - * preferring `js/ts` over `typescript`. - * Returns `undefined` if neither is explicitly specified. - */ -export function getWinningTsgoConfigKey(): ConfigName | undefined { - const jsTsValue = getExplicitUseTsgo("js/ts"); - const tsValue = getExplicitUseTsgo("typescript"); - - if (jsTsValue !== undefined || tsValue !== undefined) { - const jsTsTarget = getExplicitConfigTarget(vscode.workspace.getConfiguration("js/ts"), "experimental.useTsgo"); - const tsTarget = getExplicitConfigTarget(vscode.workspace.getConfiguration("typescript"), "experimental.useTsgo"); - const mostSpecific = Math.max(jsTsTarget ?? vscode.ConfigurationTarget.Global, tsTarget ?? vscode.ConfigurationTarget.Global); - return jsTsTarget === mostSpecific ? ConfigName.JsTsConfigName : ConfigName.DeprecatedTypeScriptConfigName; - } - - return undefined; -} - -function getExplicitUseTsgo(section: string): boolean | undefined { - const config = vscode.workspace.getConfiguration(section); - const inspected = config.inspect("experimental.useTsgo"); - if (!inspected) return undefined; - - const explicitValues: (boolean | undefined)[] = [ - inspected.workspaceFolderLanguageValue, - inspected.workspaceLanguageValue, - inspected.globalLanguageValue, - inspected.workspaceFolderValue, - inspected.workspaceValue, - inspected.globalValue, - ]; - - for (const v of explicitValues) { - if (v !== undefined) return v; - } - return undefined; -} - -/** - * Read a setting following the standard `js/ts > [typescript|javascript]` precedence: - * the unified `js/ts.` setting wins if explicitly set at any scope, otherwise - * the per-language `.` setting is used (including its - * declared default). - * - * Note: we intentionally ignore the unified setting's registered default - * (`defaultValue`/`defaultLanguageValue` from `inspect()`) so that the per-language - * fallback's value/default can apply when the user hasn't explicitly set the - * unified key. Both keys typically declare the same default, so this is observably - * equivalent in the unset case. - */ -export function readUnifiedConfig( - key: string, - fallbackSection: "typescript" | "javascript", - fallbackKey: string, - scope: vscode.ConfigurationScope | undefined, - defaultValue: T, -): T { - const unified = vscode.workspace.getConfiguration("js/ts", scope).inspect(key); - const explicit = unified && ( - unified.workspaceFolderLanguageValue - ?? unified.workspaceFolderValue - ?? unified.workspaceLanguageValue - ?? unified.workspaceValue - ?? unified.globalLanguageValue - ?? unified.globalValue - ); - if (explicit !== undefined) return explicit; - return vscode.workspace.getConfiguration(fallbackSection, scope).get(fallbackKey, defaultValue); -} - -export function contentMappersEnabled(): boolean { - return vscode.workspace.getConfiguration("js/ts").get("contentMappers.enabled", true); -} - -export interface PackageInfo { - name: string; - version: string; -} +import * as fs from "fs"; +import * as path from "path"; +import * as vscode from "vscode"; +import { resolvePackageExecutable } from "./tsdkPackage"; + +export const aiConnectionString = "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255"; + +export const languageClientName = "TypeScript Language Server"; +export const nightlyExtensionId = "TypeScriptTeam.vscode-typescript-nightly"; +export const enableContributedNightlyVersion = true; + +export const jsTsLanguageModes = [ + "typescript", + "typescriptreact", + "javascript", + "javascriptreact", +]; + +export const builtinTSExtensionId = "vscode.typescript-language-features"; + +/** + * URI schemes for which JS/TS language features should be disabled. + */ +export const disabledSchemes = new Set([ + "git", + "vsls", + "github", + "azurerepos", + "chat-editing-text-model", +]); + +export function isSupportedLanguageMode(doc: vscode.TextDocument): boolean { + return jsTsLanguageModes.includes(doc.languageId); +} + +const configRegex = /^[jt]sconfig\.(.+\.)?json$/i; +export function isJsConfigOrTsConfigFileName(fileName: string): boolean { + return configRegex.test(path.basename(fileName)); +} + +export interface ExeInfo { + path: string; + version: string; + isLocal?: boolean; +} + +const packagedExeBaseNames = ["tsc", "tsgo"]; + +export async function getBuiltinExePath(context: vscode.ExtensionContext): Promise { + if (context.extensionMode === vscode.ExtensionMode.Development) { + const exeName = `tsc${process.platform === "win32" ? ".exe" : ""}`; + const exe = context.asAbsolutePath(path.join("../../", "built", "local", exeName)); + try { + await vscode.workspace.fs.stat(vscode.Uri.file(exe)); + return { path: exe, version: "(local)", isLocal: true }; + } + catch {} + } + return getPackagedExePath(context.extension.extensionUri, getBundledTypeScriptVersion(context.extension.packageJSON)); +} + +export async function getNightlyExePath(): Promise { + const extension = vscode.extensions.getExtension(nightlyExtensionId); + if (!extension) { + return undefined; + } + + return tryGetPackagedExePath(extension.extensionUri, getBundledTypeScriptVersion(extension.packageJSON)); +} + +export async function getDefaultExePath(context: vscode.ExtensionContext): Promise { + if (enableContributedNightlyVersion) { + const nightlyExe = await getNightlyExePath(); + if (nightlyExe) { + return nightlyExe; + } + } + return getBuiltinExePath(context); +} + +async function getPackagedExePath(extensionUri: vscode.Uri, version: unknown): Promise { + const exe = await tryGetPackagedExePath(extensionUri, version); + if (exe) { + return exe; + } + throw new Error(vscode.l10n.t("Could not find a TypeScript executable in the extension package.")); +} + +function getBundledTypeScriptVersion(packageJSON: unknown): string { + if (packageJSON && typeof packageJSON === "object" && "bundledTypeScriptVersion" in packageJSON) { + const version = packageJSON.bundledTypeScriptVersion; + if (typeof version === "string") { + return version; + } + } + return "unknown"; +} + +async function tryGetPackagedExePath(extensionUri: vscode.Uri, version: unknown): Promise { + for (const baseName of packagedExeBaseNames) { + const exeName = `${baseName}${process.platform === "win32" ? ".exe" : ""}`; + const exePath = vscode.Uri.joinPath(extensionUri, "lib", exeName); + try { + await vscode.workspace.fs.stat(exePath); + return { + path: withLongPathPrefix(exePath.fsPath), + version: typeof version === "string" ? version : "unknown", + }; + } + catch {} + } + return undefined; +} + +/** + * Returns the base directory for resolving relative paths in workspace config. + * - Multi-root workspace: the directory containing the `.code-workspace` file. + * - Single-root workspace: the lone workspace folder. + * - No workspace: undefined. + */ +export function workspaceConfigBase(): vscode.Uri | undefined { + const wsFile = vscode.workspace.workspaceFile; + if (wsFile && wsFile.scheme === "file") { + return vscode.Uri.file(path.dirname(wsFile.fsPath)); + } + if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0) { + return vscode.workspace.workspaceFolders[0].uri; + } + return undefined; +} + +function workspaceResolve(relativePath: string): vscode.Uri { + if (path.isAbsolute(relativePath)) { + return vscode.Uri.file(relativePath); + } + const base = workspaceConfigBase(); + if (base) { + return vscode.Uri.joinPath(base, relativePath); + } + return vscode.Uri.file(relativePath); +} + +/** + * Memento used to control whether the user has opted into using a tsdk location defined + * in workspace settings. This is *not* a trust boundary - workspace trust is required + * before the extension will prompt to set this memento to true. This setting is here to + * provide users a way to opt out of using the workspace-provided tsdk without changing + * committed workspace settings, e.g. when the workspace tsdk is very outdated or the user + * is trialing a nightly TS version. Since the stored value is only a boolean, it does not + * protect against executing a different tsdk than the one the user originally opted into + * if the workspace settings or node_modules content changes - that's why workspace trust + * is always required, and why the prompts that set this value should not be interpreted + * as indicating trust for a specific tsdk installation. + */ +export const useWorkspaceTsdkStorageKey = "typescript.native-preview.useWorkspaceTsdk"; + +export async function getExe(context: vscode.ExtensionContext): Promise { + for (const candidate of getTrustedTsdkCandidates(context, await getTsdkCandidates())) { + const exe = await resolveTsdkPathToExe(candidate.value); + if (exe) { + return exe; + } + } + + return getDefaultExePath(context); +} + +export async function hasTsdkConfigured(): Promise { + return (await getTsdkCandidates({ nativeOnly: false })).length > 0; +} + +export async function hasNativeTsdkConfigured(context: vscode.ExtensionContext): Promise { + return await getTsdkServerKind(context) === "lsp"; +} + +export async function getTsdkServerKind(context: vscode.ExtensionContext): Promise<"lsp" | "tsserver" | undefined> { + for (const candidate of getTrustedTsdkCandidates(context, await getTsdkCandidates({ nativeOnly: false }))) { + const kind = await classifyTsdk(candidate.value); + if (kind) { + return kind; + } + } +} + +async function classifyTsdk(tsdkPath: string): Promise<"lsp" | "tsserver" | undefined> { + if (await pathHasTsserverJs(tsdkPath)) { + return "tsserver"; + } + if (await resolveTsdkPathToExe(tsdkPath)) { + return "lsp"; + } +} + +function getTrustedTsdkCandidates(context: vscode.ExtensionContext, tsdkCandidates: ExplicitConfigValue[]): ExplicitConfigValue[] { + // If tsdk is set at the workspace level, require both workspace trust and + // explicit user opt-in. Workspace trust can be revoked after the memento is + // set, so we must always check both. + if (tsdkCandidates.some(candidate => candidate.target !== vscode.ConfigurationTarget.Global)) { + if (!vscode.workspace.isTrusted || !context.workspaceState.get(useWorkspaceTsdkStorageKey, false)) { + return tsdkCandidates.filter(candidate => candidate.target === vscode.ConfigurationTarget.Global); + } + } + return tsdkCandidates; +} + +interface ExplicitConfigValue { + value: T; + target: vscode.ConfigurationTarget; + order: number; +} + +interface TsdkConfigSource { + section: string; + key: string; + nativeOnly: boolean; +} + +const tsdkConfigSources: readonly TsdkConfigSource[] = [ + { section: "js/ts", key: "tsdk.path", nativeOnly: true }, + { section: "typescript", key: "tsdk", nativeOnly: true }, + { section: "typescript.native-preview", key: "tsdk", nativeOnly: false }, +]; + +export function readNativePreviewConfig(key: string, defaultValue: T): T { + const explicit = getExplicitConfigValues("js/ts", key)[0]; + if (explicit) { + return explicit.value; + } + return vscode.workspace.getConfiguration("typescript.native-preview").get(key, defaultValue); +} + +export function getWorkspaceTsdkConfigValue(): string | undefined { + return vscode.workspace.getConfiguration("js/ts").inspect("tsdk.path")?.workspaceValue; +} + +export async function updateWorkspaceTsdkConfig(value: string): Promise { + await vscode.workspace.getConfiguration("js/ts").update("tsdk.path", value, vscode.ConfigurationTarget.Workspace); +} + +export async function getWorkspaceTsdkForPrompt(): Promise { + const candidates = await getTsdkCandidates({ filter: candidate => candidate.target !== vscode.ConfigurationTarget.Global }); + for (const candidate of candidates) { + if (await resolveTsdkPathToExe(candidate.value)) { + return candidate.value; + } + } + return undefined; +} + +function getExplicitConfigValues(section: string, key: string): ExplicitConfigValue[] { + const inspection = vscode.workspace.getConfiguration(section).inspect(key); + if (!inspection) { + return []; + } + + const candidates: Array<{ value: T | undefined; target: vscode.ConfigurationTarget; order: number; }> = [ + { value: inspection.workspaceFolderLanguageValue, target: vscode.ConfigurationTarget.WorkspaceFolder, order: 0 }, + { value: inspection.workspaceFolderValue, target: vscode.ConfigurationTarget.WorkspaceFolder, order: 1 }, + { value: inspection.workspaceLanguageValue, target: vscode.ConfigurationTarget.Workspace, order: 2 }, + { value: inspection.workspaceValue, target: vscode.ConfigurationTarget.Workspace, order: 3 }, + { value: inspection.globalLanguageValue, target: vscode.ConfigurationTarget.Global, order: 4 }, + { value: inspection.globalValue, target: vscode.ConfigurationTarget.Global, order: 5 }, + ]; + + const result: ExplicitConfigValue[] = []; + for (const candidate of candidates) { + if (candidate.value !== undefined) { + result.push({ + value: candidate.value, + target: candidate.target, + order: candidate.order, + }); + } + } + return result.sort(compareExplicitConfigValues); +} + +function compareExplicitConfigValues(a: ExplicitConfigValue, b: ExplicitConfigValue): number { + return b.target - a.target || a.order - b.order; +} + +async function getTsdkCandidates(options?: { nativeOnly?: boolean; filter?: (candidate: ExplicitConfigValue) => boolean; }): Promise[]> { + const candidates: ExplicitConfigValue[] = []; + for (let sourceIndex = 0; sourceIndex < tsdkConfigSources.length; sourceIndex++) { + const source = tsdkConfigSources[sourceIndex]; + for (const candidate of getExplicitConfigValues(source.section, source.key)) { + if (!candidate.value || typeof candidate.value !== "string") { + continue; + } + if ((options?.nativeOnly ?? true) && source.nativeOnly && await pathHasTsserverJs(candidate.value)) { + continue; + } + candidates.push({ + ...candidate, + order: candidate.order + sourceIndex * 10, + }); + } + } + return candidates.filter(options?.filter ?? (() => true)).sort(compareExplicitConfigValues); +} + +async function pathHasTsserverJs(tsdkPath: string): Promise { + const resolved = workspaceResolve(tsdkPath); + for (const candidate of [vscode.Uri.joinPath(resolved, "tsserver.js"), vscode.Uri.joinPath(resolved, "lib", "tsserver.js")]) { + try { + await vscode.workspace.fs.stat(candidate); + return true; + } + catch {} + } + return false; +} + +/** + * Resolve a tsdk path (which may be relative) to a normalized absolute path. + */ +export function resolveTsdkPath(tsdkPath: string): string { + return path.normalize(workspaceResolve(tsdkPath).fsPath); +} + +export async function resolveTsdkPathToExe(tsdkPath: string): Promise { + const resolved = await realpathUri(workspaceResolve(tsdkPath)); + for (const packagePath of [resolved, vscode.Uri.joinPath(resolved, "..")]) { + try { + const packageJsonPath = vscode.Uri.joinPath(packagePath, "package.json"); + const packageJson = JSON.parse(await vscode.workspace.fs.readFile(packageJsonPath).then(buffer => buffer.toString())); + // NOTE: Keep in sync with packages/typescript/lib/getExePath.js. + const name: unknown = packageJson.name; + const bin: unknown = packageJson.bin; + if (typeof name !== "string" || !bin || typeof bin !== "object") continue; + + const baseName = name.startsWith("@") ? name.split("/")[1] : name; + if (!baseName) continue; + const expectedBinName = baseName === "typescript" ? "tsc" : "tsgo"; + if (!Object.prototype.hasOwnProperty.call(bin, expectedBinName)) continue; + + const exeName = `${expectedBinName}${process.platform === "win32" ? ".exe" : ""}`; + const platformPackage = `${baseName}-${process.platform}-${process.arch}`; + const exePath = vscode.Uri.file(resolvePackageExecutable(packageJsonPath.fsPath, platformPackage, exeName)); + await vscode.workspace.fs.stat(exePath); + return { path: withLongPathPrefix(exePath.fsPath), version: typeof packageJson.version === "string" ? packageJson.version : "unknown" }; + } + catch {} + } + for (const baseName of packagedExeBaseNames) { + try { + const exePath = vscode.Uri.joinPath(resolved, `${baseName}${process.platform === "win32" ? ".exe" : ""}`); + await vscode.workspace.fs.stat(exePath); + return { path: withLongPathPrefix(exePath.fsPath), version: "(local)", isLocal: true }; + } + catch {} + } +} + +async function realpathUri(uri: vscode.Uri): Promise { + if (uri.scheme !== "file") { + return uri; + } + try { + return vscode.Uri.file(await fs.promises.realpath(uri.fsPath)); + } + catch { + return uri; + } +} + +function withLongPathPrefix(exePath: string): string { + if (process.platform === "win32" && exePath.length >= 248 && !exePath.startsWith("\\\\?\\")) { + return "\\\\?\\" + exePath; + } + return exePath; +} + +export function getLanguageForUri(uri: vscode.Uri): string | undefined { + const ext = path.posix.extname(uri.path); + switch (ext) { + case ".ts": + case ".mts": + case ".cts": + return "typescript"; + case ".js": + case ".mjs": + case ".cjs": + return "javascript"; + case ".tsx": + return "typescriptreact"; + case ".jsx": + return "javascriptreact"; + default: + return undefined; + } +} + +export function needsExtHostRestartOnChange() { + const majorVersion = parseInt(vscode.version.split(".")[0]); + const minorVersion = parseInt(vscode.version.split(".")[1]); + return majorVersion <= 1 && minorVersion < 105; +} + +export async function restartExtHostOnChangeIfNeeded(): Promise { + if (needsExtHostRestartOnChange()) { + await vscode.commands.executeCommand("workbench.action.restartExtensionHost"); + } +} + +/** + * Read the useTsgo setting from both `js/ts.experimental.useTsgo` and + * `typescript.experimental.useTsgo`, using `inspect()` to only consider + * explicitly set values (ignoring VS Code defaults). + * + * Each setting key is resolved using standard VS Code precedence (workspace + * folder > workspace > global, with language-specific overrides taking + * priority within each scope). When both keys are explicitly configured, the + * value set at the most specific scope wins, and `js/ts` wins over + * `typescript` at the same scope. Returns `undefined` if neither setting has + * been explicitly configured. + */ +export function getUseTsgo(): boolean | undefined { + const tsValue = getExplicitUseTsgo("typescript"); + const jsTsValue = getExplicitUseTsgo("js/ts"); + + if (tsValue !== undefined || jsTsValue !== undefined) { + const jsTsTarget = getExplicitConfigTarget(vscode.workspace.getConfiguration("js/ts"), "experimental.useTsgo"); + const tsTarget = getExplicitConfigTarget(vscode.workspace.getConfiguration("typescript"), "experimental.useTsgo"); + const mostSpecific = Math.max(jsTsTarget ?? vscode.ConfigurationTarget.Global, tsTarget ?? vscode.ConfigurationTarget.Global); + return jsTsTarget === mostSpecific ? jsTsValue : tsValue; + } + + return undefined; +} + +export function getExplicitConfigTarget( + config: vscode.WorkspaceConfiguration, + key: string, +): vscode.ConfigurationTarget | undefined { + const inspection = config.inspect(key); + if (!inspection) return undefined; + if (inspection.workspaceFolderValue !== undefined) return vscode.ConfigurationTarget.WorkspaceFolder; + if (inspection.workspaceValue !== undefined) return vscode.ConfigurationTarget.Workspace; + if (inspection.globalValue !== undefined) return vscode.ConfigurationTarget.Global; + return undefined; +} + +export enum ConfigName { + JsTsConfigName = "js/ts.experimental.useTsgo", + DeprecatedTypeScriptConfigName = "typescript.experimental.useTsgo", +} + +/** + * Returns the name of the setting with the appropriate precedence that is explicitly set, + * preferring `js/ts` over `typescript`. + * Returns `undefined` if neither is explicitly specified. + */ +export function getWinningTsgoConfigKey(): ConfigName | undefined { + const jsTsValue = getExplicitUseTsgo("js/ts"); + const tsValue = getExplicitUseTsgo("typescript"); + + if (jsTsValue !== undefined || tsValue !== undefined) { + const jsTsTarget = getExplicitConfigTarget(vscode.workspace.getConfiguration("js/ts"), "experimental.useTsgo"); + const tsTarget = getExplicitConfigTarget(vscode.workspace.getConfiguration("typescript"), "experimental.useTsgo"); + const mostSpecific = Math.max(jsTsTarget ?? vscode.ConfigurationTarget.Global, tsTarget ?? vscode.ConfigurationTarget.Global); + return jsTsTarget === mostSpecific ? ConfigName.JsTsConfigName : ConfigName.DeprecatedTypeScriptConfigName; + } + + return undefined; +} + +function getExplicitUseTsgo(section: string): boolean | undefined { + const config = vscode.workspace.getConfiguration(section); + const inspected = config.inspect("experimental.useTsgo"); + if (!inspected) return undefined; + + const explicitValues: (boolean | undefined)[] = [ + inspected.workspaceFolderLanguageValue, + inspected.workspaceLanguageValue, + inspected.globalLanguageValue, + inspected.workspaceFolderValue, + inspected.workspaceValue, + inspected.globalValue, + ]; + + for (const v of explicitValues) { + if (v !== undefined) return v; + } + return undefined; +} + +/** + * Read a setting following the standard `js/ts > [typescript|javascript]` precedence: + * the unified `js/ts.` setting wins if explicitly set at any scope, otherwise + * the per-language `.` setting is used (including its + * declared default). + * + * Note: we intentionally ignore the unified setting's registered default + * (`defaultValue`/`defaultLanguageValue` from `inspect()`) so that the per-language + * fallback's value/default can apply when the user hasn't explicitly set the + * unified key. Both keys typically declare the same default, so this is observably + * equivalent in the unset case. + */ +export function readUnifiedConfig( + key: string, + fallbackSection: "typescript" | "javascript", + fallbackKey: string, + scope: vscode.ConfigurationScope | undefined, + defaultValue: T, +): T { + const unified = vscode.workspace.getConfiguration("js/ts", scope).inspect(key); + const explicit = unified && ( + unified.workspaceFolderLanguageValue + ?? unified.workspaceFolderValue + ?? unified.workspaceLanguageValue + ?? unified.workspaceValue + ?? unified.globalLanguageValue + ?? unified.globalValue + ); + if (explicit !== undefined) return explicit; + return vscode.workspace.getConfiguration(fallbackSection, scope).get(fallbackKey, defaultValue); +} + +export function contentMappersEnabled(): boolean { + return vscode.workspace.getConfiguration("js/ts").get("contentMappers.enabled", true); +} + +export interface PackageInfo { + name: string; + version: string; +} diff --git a/packages/vscode-typescript/src/vscode.proposed.editorHoverVerbosityLevel.d.ts b/packages/vscode-typescript/src/vscode.proposed.editorHoverVerbosityLevel.d.ts index 50b678c034879..ed9eeb000cbe3 100644 --- a/packages/vscode-typescript/src/vscode.proposed.editorHoverVerbosityLevel.d.ts +++ b/packages/vscode-typescript/src/vscode.proposed.editorHoverVerbosityLevel.d.ts @@ -1,72 +1,72 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module "vscode" { - /** - * A hover represents additional information for a symbol or word. Hovers are - * rendered in a tooltip-like widget. - */ - export class VerboseHover extends Hover { - /** - * Can increase the verbosity of the hover - */ - canIncreaseVerbosity?: boolean; - - /** - * Can decrease the verbosity of the hover - */ - canDecreaseVerbosity?: boolean; - - /** - * Creates a new hover object. - * - * @param contents The contents of the hover. - * @param range The range to which the hover applies. - */ - constructor(contents: MarkdownString | MarkedString | Array, range?: Range, canIncreaseVerbosity?: boolean, canDecreaseVerbosity?: boolean); - } - - export interface HoverContext { - /** - * The delta by which to increase/decrease the hover verbosity level - */ - readonly verbosityDelta?: number; - - /** - * The previous hover sent for the same position - */ - readonly previousHover?: Hover; - } - - export enum HoverVerbosityAction { - /** - * Increase the hover verbosity - */ - Increase = 0, - /** - * Decrease the hover verbosity - */ - Decrease = 1, - } - - /** - * The hover provider class - */ - export interface HoverProvider { - /** - * Provide a hover for the given position and document. Multiple hovers at the same - * position will be merged by the editor. A hover can have a range which defaults - * to the word range at the position when omitted. - * - * @param document The document in which the command was invoked. - * @param position The position at which the command was invoked. - * @param token A cancellation token. - * @param context A hover context. - * @returns A hover or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined` or `null`. - */ - provideHover(document: TextDocument, position: Position, token: CancellationToken, context?: HoverContext): ProviderResult; - } -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module "vscode" { + /** + * A hover represents additional information for a symbol or word. Hovers are + * rendered in a tooltip-like widget. + */ + export class VerboseHover extends Hover { + /** + * Can increase the verbosity of the hover + */ + canIncreaseVerbosity?: boolean; + + /** + * Can decrease the verbosity of the hover + */ + canDecreaseVerbosity?: boolean; + + /** + * Creates a new hover object. + * + * @param contents The contents of the hover. + * @param range The range to which the hover applies. + */ + constructor(contents: MarkdownString | MarkedString | Array, range?: Range, canIncreaseVerbosity?: boolean, canDecreaseVerbosity?: boolean); + } + + export interface HoverContext { + /** + * The delta by which to increase/decrease the hover verbosity level + */ + readonly verbosityDelta?: number; + + /** + * The previous hover sent for the same position + */ + readonly previousHover?: Hover; + } + + export enum HoverVerbosityAction { + /** + * Increase the hover verbosity + */ + Increase = 0, + /** + * Decrease the hover verbosity + */ + Decrease = 1, + } + + /** + * The hover provider class + */ + export interface HoverProvider { + /** + * Provide a hover for the given position and document. Multiple hovers at the same + * position will be merged by the editor. A hover can have a range which defaults + * to the word range at the position when omitted. + * + * @param document The document in which the command was invoked. + * @param position The position at which the command was invoked. + * @param token A cancellation token. + * @param context A hover context. + * @returns A hover or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined` or `null`. + */ + provideHover(document: TextDocument, position: Position, token: CancellationToken, context?: HoverContext): ProviderResult; + } +} diff --git a/packages/vscode-typescript/src/vscode.proposed.multiDocumentHighlightProvider.d.ts b/packages/vscode-typescript/src/vscode.proposed.multiDocumentHighlightProvider.d.ts index 1a95e49b4a954..d099435a45fa4 100644 --- a/packages/vscode-typescript/src/vscode.proposed.multiDocumentHighlightProvider.d.ts +++ b/packages/vscode-typescript/src/vscode.proposed.multiDocumentHighlightProvider.d.ts @@ -1,58 +1,58 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module "vscode" { - /** - * Represents a collection of document highlights from multiple documents. - */ - export class MultiDocumentHighlight { - /** - * The URI of the document containing the highlights. - */ - uri: Uri; - - /** - * The highlights for the document. - */ - highlights: DocumentHighlight[]; - - /** - * Creates a new instance of MultiDocumentHighlight. - * @param uri The URI of the document containing the highlights. - * @param highlights The highlights for the document. - */ - constructor(uri: Uri, highlights: DocumentHighlight[]); - } - - export interface MultiDocumentHighlightProvider { - /** - * Provide a set of document highlights, like all occurrences of a variable or - * all exit-points of a function. - * - * @param document The document in which the command was invoked. - * @param position The position at which the command was invoked. - * @param otherDocuments An array of additional valid documents for which highlights should be provided. - * @param token A cancellation token. - * @returns A Map containing a mapping of the Uri of a document to the document highlights or a thenable that resolves to such. The lack of a result can be - * signaled by returning `undefined`, `null`, or an empty map. - */ - provideMultiDocumentHighlights(document: TextDocument, position: Position, otherDocuments: TextDocument[], token: CancellationToken): ProviderResult; - } - - namespace languages { - /** - * Register a multi document highlight provider. - * - * Multiple providers can be registered for a language. In that case providers are sorted - * by their {@link languages.match score} and groups sequentially asked for document highlights. - * The process stops when a provider returns a `non-falsy` or `non-failure` result. - * - * @param selector A selector that defines the documents this provider is applicable to. - * @param provider A multi-document highlight provider. - * @returns A {@link Disposable} that unregisters this provider when being disposed. - */ - export function registerMultiDocumentHighlightProvider(selector: DocumentSelector, provider: MultiDocumentHighlightProvider): Disposable; - } -} +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module "vscode" { + /** + * Represents a collection of document highlights from multiple documents. + */ + export class MultiDocumentHighlight { + /** + * The URI of the document containing the highlights. + */ + uri: Uri; + + /** + * The highlights for the document. + */ + highlights: DocumentHighlight[]; + + /** + * Creates a new instance of MultiDocumentHighlight. + * @param uri The URI of the document containing the highlights. + * @param highlights The highlights for the document. + */ + constructor(uri: Uri, highlights: DocumentHighlight[]); + } + + export interface MultiDocumentHighlightProvider { + /** + * Provide a set of document highlights, like all occurrences of a variable or + * all exit-points of a function. + * + * @param document The document in which the command was invoked. + * @param position The position at which the command was invoked. + * @param otherDocuments An array of additional valid documents for which highlights should be provided. + * @param token A cancellation token. + * @returns A Map containing a mapping of the Uri of a document to the document highlights or a thenable that resolves to such. The lack of a result can be + * signaled by returning `undefined`, `null`, or an empty map. + */ + provideMultiDocumentHighlights(document: TextDocument, position: Position, otherDocuments: TextDocument[], token: CancellationToken): ProviderResult; + } + + namespace languages { + /** + * Register a multi document highlight provider. + * + * Multiple providers can be registered for a language. In that case providers are sorted + * by their {@link languages.match score} and groups sequentially asked for document highlights. + * The process stops when a provider returns a `non-falsy` or `non-failure` result. + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A multi-document highlight provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ + export function registerMultiDocumentHighlightProvider(selector: DocumentSelector, provider: MultiDocumentHighlightProvider): Disposable; + } +} diff --git a/packages/vscode-typescript/src/workspaceSymbolMiddleware.ts b/packages/vscode-typescript/src/workspaceSymbolMiddleware.ts index af7da47a8b8c0..a0dabf2062f80 100644 --- a/packages/vscode-typescript/src/workspaceSymbolMiddleware.ts +++ b/packages/vscode-typescript/src/workspaceSymbolMiddleware.ts @@ -1,41 +1,41 @@ -import * as vscode from "vscode"; -import type { CancellationToken } from "vscode"; -import type { MessageSignature } from "vscode-languageserver-protocol"; -import { isSupportedLanguageMode } from "./util"; - -const supportedSchemes = new Set(["file", "untitled"]); - -function isSupportedDocument(document: vscode.TextDocument): boolean { - return isSupportedLanguageMode(document) && supportedSchemes.has(document.uri.scheme); -} - -function getDocument(): vscode.TextDocument | undefined { - const activeDocument = vscode.window.activeTextEditor?.document; - if (activeDocument && isSupportedDocument(activeDocument)) { - return activeDocument; - } - - return vscode.workspace.textDocuments.find(isSupportedDocument); -} - -export function workspaceSymbolSendRequestMiddleware( - type: string | MessageSignature, - params: P | undefined, - token: CancellationToken | undefined, - next: (type: string | MessageSignature, params?: P, token?: CancellationToken) => Promise, -): Promise { - const method = typeof type === "string" ? type : type.method; - if (method !== "workspace/symbol") { - return next(type, params, token); - } - - const document = getDocument(); - if (!document) { - return next(type, params, token); - } - - return next(type, { - ...params, - textDocument: { uri: document.uri.toString() }, - } as P, token); -} +import * as vscode from "vscode"; +import type { CancellationToken } from "vscode"; +import type { MessageSignature } from "vscode-languageserver-protocol"; +import { isSupportedLanguageMode } from "./util"; + +const supportedSchemes = new Set(["file", "untitled"]); + +function isSupportedDocument(document: vscode.TextDocument): boolean { + return isSupportedLanguageMode(document) && supportedSchemes.has(document.uri.scheme); +} + +function getDocument(): vscode.TextDocument | undefined { + const activeDocument = vscode.window.activeTextEditor?.document; + if (activeDocument && isSupportedDocument(activeDocument)) { + return activeDocument; + } + + return vscode.workspace.textDocuments.find(isSupportedDocument); +} + +export function workspaceSymbolSendRequestMiddleware( + type: string | MessageSignature, + params: P | undefined, + token: CancellationToken | undefined, + next: (type: string | MessageSignature, params?: P, token?: CancellationToken) => Promise, +): Promise { + const method = typeof type === "string" ? type : type.method; + if (method !== "workspace/symbol") { + return next(type, params, token); + } + + const document = getDocument(); + if (!document) { + return next(type, params, token); + } + + return next(type, { + ...params, + textDocument: { uri: document.uri.toString() }, + } as P, token); +} diff --git a/packages/vscode-typescript/test/contentMapperContributions.test.ts b/packages/vscode-typescript/test/contentMapperContributions.test.ts index 74c4dd07116f9..d6e755be1e04f 100644 --- a/packages/vscode-typescript/test/contentMapperContributions.test.ts +++ b/packages/vscode-typescript/test/contentMapperContributions.test.ts @@ -1,54 +1,54 @@ -import assert from "node:assert/strict"; -import test from "node:test"; -import { - type ContentMapperContribution, - documentMatchesContentMapperContributions, - serializeContentMapperContributions, -} from "../src/contentMapperContributions"; - -const documentedContribution = { - extensions: [".vue"], - inferredProjectContribution: { - options: { strictTemplates: true }, - manifest: { - name: "Vue mapper", - version: "1.2.3", - exec: ["node", "mapper.js"], - compilerOptions: ["strict"], - dynamicConfig: true, - }, - }, -} satisfies ContentMapperContribution; - -test("content mapper extensions match document paths case-insensitively", () => { - const registrations = new Map([[ - "publisher.extension", - [{ extensions: [".vue"] }], - ]]); - const document = { uri: { path: "/workspace/Component.VUE" } }; - - assert.equal(documentMatchesContentMapperContributions(document, registrations), true); -}); - -test("serializes the documented inferred project contribution", () => { - const registrations = new Map([[ - "publisher.extension", - [documentedContribution], - ]]); - - assert.deepEqual(serializeContentMapperContributions(registrations), [{ - contributorId: "publisher.extension", - extensions: [".vue"], - inferredProjectContribution: { - options: { strictTemplates: true }, - manifest: { - name: "Vue mapper", - version: "1.2.3", - exec: ["node", "mapper.js"], - cwd: undefined, - compilerOptions: ["strict"], - dynamicConfig: true, - }, - }, - }]); -}); +import assert from "node:assert/strict"; +import test from "node:test"; +import { + type ContentMapperContribution, + documentMatchesContentMapperContributions, + serializeContentMapperContributions, +} from "../src/contentMapperContributions"; + +const documentedContribution = { + extensions: [".vue"], + inferredProjectContribution: { + options: { strictTemplates: true }, + manifest: { + name: "Vue mapper", + version: "1.2.3", + exec: ["node", "mapper.js"], + compilerOptions: ["strict"], + dynamicConfig: true, + }, + }, +} satisfies ContentMapperContribution; + +test("content mapper extensions match document paths case-insensitively", () => { + const registrations = new Map([[ + "publisher.extension", + [{ extensions: [".vue"] }], + ]]); + const document = { uri: { path: "/workspace/Component.VUE" } }; + + assert.equal(documentMatchesContentMapperContributions(document, registrations), true); +}); + +test("serializes the documented inferred project contribution", () => { + const registrations = new Map([[ + "publisher.extension", + [documentedContribution], + ]]); + + assert.deepEqual(serializeContentMapperContributions(registrations), [{ + contributorId: "publisher.extension", + extensions: [".vue"], + inferredProjectContribution: { + options: { strictTemplates: true }, + manifest: { + name: "Vue mapper", + version: "1.2.3", + exec: ["node", "mapper.js"], + cwd: undefined, + compilerOptions: ["strict"], + dynamicConfig: true, + }, + }, + }]); +}); diff --git a/packages/vscode-typescript/test/index.test.ts b/packages/vscode-typescript/test/index.test.ts index 1688ac53bc213..fccc6199bfdfe 100644 --- a/packages/vscode-typescript/test/index.test.ts +++ b/packages/vscode-typescript/test/index.test.ts @@ -1,2 +1,2 @@ -import "./contentMapperContributions.test"; -import "./tsdkPackage.test"; +import "./contentMapperContributions.test"; +import "./tsdkPackage.test"; diff --git a/packages/vscode-typescript/test/tsdkPackage.test.ts b/packages/vscode-typescript/test/tsdkPackage.test.ts index ab633ed55fc98..910421b93e733 100644 --- a/packages/vscode-typescript/test/tsdkPackage.test.ts +++ b/packages/vscode-typescript/test/tsdkPackage.test.ts @@ -1,127 +1,127 @@ -import assert from "node:assert/strict"; -import fs from "node:fs"; -import os from "node:os"; -import path from "node:path"; -import test from "node:test"; -import { resolvePackageExecutable } from "../src/tsdkPackage"; - -const platformPackage = `typescript-${process.platform}-${process.arch}`; -const exeSuffix = process.platform === "win32" ? ".exe" : ""; -const exeName = `tsc${exeSuffix}`; -const nativeExeName = `tsgo${exeSuffix}`; - -function createPackage(root: string, relativePath: string): string { - const packagePath = path.join(root, relativePath); - fs.mkdirSync(packagePath, { recursive: true }); - const packageJsonPath = path.join(packagePath, "package.json"); - fs.writeFileSync(packageJsonPath, "{}"); - return packageJsonPath; -} - -function linkPackage(root: string, relativePath: string, targetPackageJson: string): void { - const packagePath = path.join(root, relativePath); - fs.mkdirSync(path.dirname(packagePath), { recursive: true }); - fs.symlinkSync( - path.dirname(targetPackageJson), - packagePath, - process.platform === "win32" ? "junction" : "dir", - ); -} - -function createFixture(t: test.TestContext): string { - const root = fs.mkdtempSync(path.join(os.tmpdir(), "tsdk-package-")); - t.after(() => fs.rmSync(root, { recursive: true, force: true })); - return fs.realpathSync(root); -} - -interface ResolutionCase { - packagePath: string; - platformPath: string; - dependencyLink?: string; - platformPackage?: string; - exeName?: string; -} - -function testResolution(name: string, resolutionCase: ResolutionCase): void { - test(name, t => { - const root = createFixture(t); - const packageJsonPath = createPackage(root, resolutionCase.packagePath); - const platformPackageJson = createPackage(root, resolutionCase.platformPath); - if (resolutionCase.dependencyLink) { - linkPackage(root, resolutionCase.dependencyLink, platformPackageJson); - } - - const resolvedExeName = resolutionCase.exeName ?? exeName; - assert.equal( - resolvePackageExecutable(packageJsonPath, resolutionCase.platformPackage ?? platformPackage, resolvedExeName), - path.join(path.dirname(platformPackageJson), "lib", resolvedExeName), - ); - }); -} - -testResolution("resolves the TypeScript 7 package", { - packagePath: "node_modules/typescript", - platformPath: `node_modules/@typescript/${platformPackage}`, -}); - -testResolution("resolves the TypeScript 7 scoped alias recommended in the 7.0 release post", { - packagePath: "node_modules/@typescript/native", - platformPath: `node_modules/@typescript/${platformPackage}`, -}); - -testResolution("resolves an unscoped TypeScript 7 alias", { - packagePath: "node_modules/typescript-next", - platformPath: `node_modules/@typescript/${platformPackage}`, -}); - -const nativePlatformPackage = `native-preview-${process.platform}-${process.arch}`; -testResolution("resolves the native-preview package", { - packagePath: "node_modules/@typescript/typescript", - platformPath: `node_modules/@typescript/${nativePlatformPackage}`, - platformPackage: nativePlatformPackage, - exeName: nativeExeName, -}); - -testResolution("resolves a non-hoisted platform package", { - packagePath: "node_modules/@typescript/native", - platformPath: `node_modules/@typescript/native/node_modules/@typescript/${platformPackage}`, -}); - -testResolution("resolves a platform package hoisted above a workspace", { - packagePath: "packages/app/node_modules/@typescript/native", - platformPath: `node_modules/@typescript/${platformPackage}`, -}); - -const pnpmStore = "node_modules/.pnpm/typescript@7.0.2/node_modules"; -testResolution("resolves a pnpm virtual-store package", { - packagePath: `${pnpmStore}/typescript`, - platformPath: `node_modules/.pnpm/@typescript+${platformPackage}@7.0.2/node_modules/@typescript/${platformPackage}`, - dependencyLink: `${pnpmStore}/@typescript/${platformPackage}`, -}); - -const nativePreviewVersion = "7.0.0-dev.20260707.2"; -const pnpmNativePreviewStore = `node_modules/.pnpm/@typescript+native-preview@${nativePreviewVersion}/node_modules`; -testResolution("resolves a pnpm native-preview virtual-store package", { - packagePath: `${pnpmNativePreviewStore}/@typescript/typescript`, - platformPath: `node_modules/.pnpm/@typescript+${nativePlatformPackage}@${nativePreviewVersion}/node_modules/@typescript/${nativePlatformPackage}`, - dependencyLink: `${pnpmNativePreviewStore}/@typescript/${nativePlatformPackage}`, - platformPackage: nativePlatformPackage, - exeName: nativeExeName, -}); - -const npmStore = "node_modules/.store/typescript@7.0.2-hash/node_modules"; -testResolution("resolves an npm linked-store package", { - packagePath: `${npmStore}/typescript`, - platformPath: `node_modules/.store/@typescript+${platformPackage}@7.0.2-hash/node_modules/@typescript/${platformPackage}`, - dependencyLink: `${npmStore}/@typescript/${platformPackage}`, -}); - -test("throws when the platform package is missing", t => { - const root = createFixture(t); - const packageJsonPath = createPackage(root, "node_modules/@typescript/native"); - - assert.throws( - () => resolvePackageExecutable(packageJsonPath, platformPackage, exeName), - { code: "MODULE_NOT_FOUND" }, - ); -}); +import assert from "node:assert/strict"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import test from "node:test"; +import { resolvePackageExecutable } from "../src/tsdkPackage"; + +const platformPackage = `typescript-${process.platform}-${process.arch}`; +const exeSuffix = process.platform === "win32" ? ".exe" : ""; +const exeName = `tsc${exeSuffix}`; +const nativeExeName = `tsgo${exeSuffix}`; + +function createPackage(root: string, relativePath: string): string { + const packagePath = path.join(root, relativePath); + fs.mkdirSync(packagePath, { recursive: true }); + const packageJsonPath = path.join(packagePath, "package.json"); + fs.writeFileSync(packageJsonPath, "{}"); + return packageJsonPath; +} + +function linkPackage(root: string, relativePath: string, targetPackageJson: string): void { + const packagePath = path.join(root, relativePath); + fs.mkdirSync(path.dirname(packagePath), { recursive: true }); + fs.symlinkSync( + path.dirname(targetPackageJson), + packagePath, + process.platform === "win32" ? "junction" : "dir", + ); +} + +function createFixture(t: test.TestContext): string { + const root = fs.mkdtempSync(path.join(os.tmpdir(), "tsdk-package-")); + t.after(() => fs.rmSync(root, { recursive: true, force: true })); + return fs.realpathSync(root); +} + +interface ResolutionCase { + packagePath: string; + platformPath: string; + dependencyLink?: string; + platformPackage?: string; + exeName?: string; +} + +function testResolution(name: string, resolutionCase: ResolutionCase): void { + test(name, t => { + const root = createFixture(t); + const packageJsonPath = createPackage(root, resolutionCase.packagePath); + const platformPackageJson = createPackage(root, resolutionCase.platformPath); + if (resolutionCase.dependencyLink) { + linkPackage(root, resolutionCase.dependencyLink, platformPackageJson); + } + + const resolvedExeName = resolutionCase.exeName ?? exeName; + assert.equal( + resolvePackageExecutable(packageJsonPath, resolutionCase.platformPackage ?? platformPackage, resolvedExeName), + path.join(path.dirname(platformPackageJson), "lib", resolvedExeName), + ); + }); +} + +testResolution("resolves the TypeScript 7 package", { + packagePath: "node_modules/typescript", + platformPath: `node_modules/@typescript/${platformPackage}`, +}); + +testResolution("resolves the TypeScript 7 scoped alias recommended in the 7.0 release post", { + packagePath: "node_modules/@typescript/native", + platformPath: `node_modules/@typescript/${platformPackage}`, +}); + +testResolution("resolves an unscoped TypeScript 7 alias", { + packagePath: "node_modules/typescript-next", + platformPath: `node_modules/@typescript/${platformPackage}`, +}); + +const nativePlatformPackage = `native-preview-${process.platform}-${process.arch}`; +testResolution("resolves the native-preview package", { + packagePath: "node_modules/@typescript/typescript", + platformPath: `node_modules/@typescript/${nativePlatformPackage}`, + platformPackage: nativePlatformPackage, + exeName: nativeExeName, +}); + +testResolution("resolves a non-hoisted platform package", { + packagePath: "node_modules/@typescript/native", + platformPath: `node_modules/@typescript/native/node_modules/@typescript/${platformPackage}`, +}); + +testResolution("resolves a platform package hoisted above a workspace", { + packagePath: "packages/app/node_modules/@typescript/native", + platformPath: `node_modules/@typescript/${platformPackage}`, +}); + +const pnpmStore = "node_modules/.pnpm/typescript@7.0.2/node_modules"; +testResolution("resolves a pnpm virtual-store package", { + packagePath: `${pnpmStore}/typescript`, + platformPath: `node_modules/.pnpm/@typescript+${platformPackage}@7.0.2/node_modules/@typescript/${platformPackage}`, + dependencyLink: `${pnpmStore}/@typescript/${platformPackage}`, +}); + +const nativePreviewVersion = "7.0.0-dev.20260707.2"; +const pnpmNativePreviewStore = `node_modules/.pnpm/@typescript+native-preview@${nativePreviewVersion}/node_modules`; +testResolution("resolves a pnpm native-preview virtual-store package", { + packagePath: `${pnpmNativePreviewStore}/@typescript/typescript`, + platformPath: `node_modules/.pnpm/@typescript+${nativePlatformPackage}@${nativePreviewVersion}/node_modules/@typescript/${nativePlatformPackage}`, + dependencyLink: `${pnpmNativePreviewStore}/@typescript/${nativePlatformPackage}`, + platformPackage: nativePlatformPackage, + exeName: nativeExeName, +}); + +const npmStore = "node_modules/.store/typescript@7.0.2-hash/node_modules"; +testResolution("resolves an npm linked-store package", { + packagePath: `${npmStore}/typescript`, + platformPath: `node_modules/.store/@typescript+${platformPackage}@7.0.2-hash/node_modules/@typescript/${platformPackage}`, + dependencyLink: `${npmStore}/@typescript/${platformPackage}`, +}); + +test("throws when the platform package is missing", t => { + const root = createFixture(t); + const packageJsonPath = createPackage(root, "node_modules/@typescript/native"); + + assert.throws( + () => resolvePackageExecutable(packageJsonPath, platformPackage, exeName), + { code: "MODULE_NOT_FOUND" }, + ); +}); diff --git a/tools/LICENSE b/tools/LICENSE index 8746124b27791..edc24fd6e1409 100644 --- a/tools/LICENSE +++ b/tools/LICENSE @@ -1,55 +1,55 @@ -Apache License - -Version 2.0, January 2004 - -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of this License; and - -You must cause any modified files to carry prominent notices stating that You changed the files; and - -You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - -If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and + +You must cause any modified files to carry prominent notices stating that You changed the files; and + +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/tools/scripts/link-hooks.mjs b/tools/scripts/link-hooks.mjs index 2d81a75b63223..102b8d5837c31 100644 --- a/tools/scripts/link-hooks.mjs +++ b/tools/scripts/link-hooks.mjs @@ -1,25 +1,25 @@ -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 hooks = [ - "post-checkout", - "pre-commit", -]; - -hooks.forEach(hook => { - const hookInSourceControl = path.resolve(__dirname, "..", "hooks", hook); - - if (fs.existsSync(hookInSourceControl)) { - const hookInHiddenDirectory = path.resolve(__dirname, "..", "..", ".git", "hooks", hook); - - if (fs.existsSync(hookInHiddenDirectory)) { - fs.unlinkSync(hookInHiddenDirectory); - } - - fs.linkSync(hookInSourceControl, hookInHiddenDirectory); - } -}); +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 hooks = [ + "post-checkout", + "pre-commit", +]; + +hooks.forEach(hook => { + const hookInSourceControl = path.resolve(__dirname, "..", "hooks", hook); + + if (fs.existsSync(hookInSourceControl)) { + const hookInHiddenDirectory = path.resolve(__dirname, "..", "..", ".git", "hooks", hook); + + if (fs.existsSync(hookInHiddenDirectory)) { + fs.unlinkSync(hookInHiddenDirectory); + } + + fs.linkSync(hookInSourceControl, hookInHiddenDirectory); + } +}); diff --git a/tools/scripts/post-vsts-artifact-comment.mjs b/tools/scripts/post-vsts-artifact-comment.mjs index 1dbfcfe26c7b6..1f8bec395c008 100644 --- a/tools/scripts/post-vsts-artifact-comment.mjs +++ b/tools/scripts/post-vsts-artifact-comment.mjs @@ -1,111 +1,111 @@ -import { Octokit } from "@octokit/rest"; -import assert from "assert"; -import ado from "azure-devops-node-api"; - -/** - * @param {string} name - */ -function mustGetEnv(name) { - const value = process.env[name]; - assert(value, `No ${name} specified`); - return value; -} - -const REQUESTING_USER = mustGetEnv("REQUESTING_USER"); -const SOURCE_ISSUE = +mustGetEnv("SOURCE_ISSUE"); -const BUILD_BUILDID = +mustGetEnv("BUILD_BUILDID"); -const DISTINCT_ID = mustGetEnv("DISTINCT_ID"); -const STATUS_COMMENT = +mustGetEnv("STATUS_COMMENT"); - -const gh = new Octokit({ - auth: process.argv[2], -}); - -async function main() { - // The pipelines API does _not_ make getting the direct URL to a specific file _within_ an artifact trivial - 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", 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 = /** @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; - - // Please keep the strings "an installable tgz" and "packed" in this message, as well as the devDependencies section, - // so that the playgrounds deployment process can find these comments. - - const comment = `Hey @${REQUESTING_USER}, I've packed this into [an installable tgz](${link}). You can install it for testing by referencing it in your \`package.json\` like so: -\`\`\` -{ - "devDependencies": { - "typescript": "${link}" - } -} -\`\`\` -and then running \`npm install\`. -`; - - // Temporarily disable until we get access controls set up right - // Send a ping to https://github.com/microsoft/typescript-make-monaco-builds#pull-request-builds - await gh.request("POST /repos/microsoft/typescript-make-monaco-builds/dispatches", { event_type: `${SOURCE_ISSUE}`, headers: { Accept: "application/vnd.github.everest-preview+json" } }); - - return comment; -} - -let newComment; -let emoji; - -try { - newComment = await main(); - emoji = "✅"; -} -catch (e) { - console.error(e); - newComment = `Hey @${REQUESTING_USER}, something went wrong when looking for the build artifact. ([You can check the log here](https://typescript.visualstudio.com/TypeScript/_build/index?buildId=${BUILD_BUILDID}&_a=summary)).`; - emoji = "❌"; -} - -const resultsComment = await gh.issues.createComment({ - issue_number: SOURCE_ISSUE, - owner: "microsoft", - repo: "TypeScript", - body: newComment, -}); - -const toReplace = ``; -let posted = false; -for (let i = 0; i < 5; i++) { - // Get status comment contents - const statusComment = await gh.rest.issues.getComment({ - comment_id: STATUS_COMMENT, - owner: "microsoft", - repo: "TypeScript", - }); - - const oldComment = statusComment.data.body; - if (!oldComment?.includes(toReplace)) { - posted = true; - break; - } - - const newComment = oldComment.replace(toReplace, `[${emoji} Results](${resultsComment.data.html_url})`); - - // Update status comment - await gh.rest.issues.updateComment({ - comment_id: STATUS_COMMENT, - owner: "microsoft", - repo: "TypeScript", - body: newComment, - }); - - // Repeat; someone may have edited the comment at the same time. - await new Promise(resolve => setTimeout(resolve, 1000)); -} - -if (!posted) { - throw new Error("Failed to update status comment"); -} +import { Octokit } from "@octokit/rest"; +import assert from "assert"; +import ado from "azure-devops-node-api"; + +/** + * @param {string} name + */ +function mustGetEnv(name) { + const value = process.env[name]; + assert(value, `No ${name} specified`); + return value; +} + +const REQUESTING_USER = mustGetEnv("REQUESTING_USER"); +const SOURCE_ISSUE = +mustGetEnv("SOURCE_ISSUE"); +const BUILD_BUILDID = +mustGetEnv("BUILD_BUILDID"); +const DISTINCT_ID = mustGetEnv("DISTINCT_ID"); +const STATUS_COMMENT = +mustGetEnv("STATUS_COMMENT"); + +const gh = new Octokit({ + auth: process.argv[2], +}); + +async function main() { + // The pipelines API does _not_ make getting the direct URL to a specific file _within_ an artifact trivial + 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", 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 = /** @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; + + // Please keep the strings "an installable tgz" and "packed" in this message, as well as the devDependencies section, + // so that the playgrounds deployment process can find these comments. + + const comment = `Hey @${REQUESTING_USER}, I've packed this into [an installable tgz](${link}). You can install it for testing by referencing it in your \`package.json\` like so: +\`\`\` +{ + "devDependencies": { + "typescript": "${link}" + } +} +\`\`\` +and then running \`npm install\`. +`; + + // Temporarily disable until we get access controls set up right + // Send a ping to https://github.com/microsoft/typescript-make-monaco-builds#pull-request-builds + await gh.request("POST /repos/microsoft/typescript-make-monaco-builds/dispatches", { event_type: `${SOURCE_ISSUE}`, headers: { Accept: "application/vnd.github.everest-preview+json" } }); + + return comment; +} + +let newComment; +let emoji; + +try { + newComment = await main(); + emoji = "✅"; +} +catch (e) { + console.error(e); + newComment = `Hey @${REQUESTING_USER}, something went wrong when looking for the build artifact. ([You can check the log here](https://typescript.visualstudio.com/TypeScript/_build/index?buildId=${BUILD_BUILDID}&_a=summary)).`; + emoji = "❌"; +} + +const resultsComment = await gh.issues.createComment({ + issue_number: SOURCE_ISSUE, + owner: "microsoft", + repo: "TypeScript", + body: newComment, +}); + +const toReplace = ``; +let posted = false; +for (let i = 0; i < 5; i++) { + // Get status comment contents + const statusComment = await gh.rest.issues.getComment({ + comment_id: STATUS_COMMENT, + owner: "microsoft", + repo: "TypeScript", + }); + + const oldComment = statusComment.data.body; + if (!oldComment?.includes(toReplace)) { + posted = true; + break; + } + + const newComment = oldComment.replace(toReplace, `[${emoji} Results](${resultsComment.data.html_url})`); + + // Update status comment + await gh.rest.issues.updateComment({ + comment_id: STATUS_COMMENT, + owner: "microsoft", + repo: "TypeScript", + body: newComment, + }); + + // Repeat; someone may have edited the comment at the same time. + await new Promise(resolve => setTimeout(resolve, 1000)); +} + +if (!posted) { + throw new Error("Failed to update status comment"); +} diff --git a/tools/scripts/tsc/generate-encoder.ts b/tools/scripts/tsc/generate-encoder.ts index 239ab7021b193..a7c6b1e96fc31 100644 --- a/tools/scripts/tsc/generate-encoder.ts +++ b/tools/scripts/tsc/generate-encoder.ts @@ -1,2044 +1,2044 @@ -/** - * Encoder/decoder code generator: reads tools/scripts/tsc/ast.json and produces binary - * encoding/decoding code for Go and TypeScript. - * - * Usage: node --experimental-strip-types tools/scripts/tsc/generate-encoder.ts - * - * Generates: - * - internal/api/encoder/encoder_generated.go - * - internal/api/encoder/decoder_generated.go - * - packages/typescript/src/api/node/protocol.generated.ts - */ - -import { execaSync } from "execa"; -import * as fs from "node:fs"; -import * as path from "node:path"; -import { fileURLToPath } from "node:url"; -import type { - KindType, - MemberInfo, - NodeType, -} from "./schema.ts"; -import { api } from "./schema.ts"; - -const ROOT = path.resolve(import.meta.dirname!, "../../.."); - -// ═══════════════════════════════════════════════════════════════════════════ -// Code writer -// ═══════════════════════════════════════════════════════════════════════════ - -class CodeWriter { - private lines: string[] = []; - private indent = 0; - private indentStr: string; - - constructor(indentStr = "\t") { - this.indentStr = indentStr; - } - - write(line = "") { - if (line === "") this.lines.push(""); - else this.lines.push(this.indentStr.repeat(this.indent) + line); - } - - push() { - this.indent++; - } - pop() { - this.indent--; - } - - toString(): string { - return this.lines.join("\n"); - } -} - -// ═══════════════════════════════════════════════════════════════════════════ -// Node classification -// ═══════════════════════════════════════════════════════════════════════════ - -/** Encoder-visible members: excludes noTS and noGo members. */ -function schemaMembers(node: NodeType): MemberInfo[] { - return node.members.filter(m => !m.noTS && !m.noGo); -} - -/** Whether a member is a child that should be encoded in the binary format. */ -function isEncodedChild(m: MemberInfo): boolean { - return m.isChild() && !m.noTS && !m.noGo; -} - -type ChildType = "node" | "nodeList" | "rawNodeList" | "modifierList"; - -/** Determine the child type for an encoded child member. */ -function childType(m: MemberInfo): ChildType { - if (m.listKind === "ModifierList") return "modifierList"; - if (m.listKind === "NodeList") return "nodeList"; - if (m.type.baseKind() === "list" && m.listKind === "raw") return "rawNodeList"; - return "node"; -} - -// ═══════════════════════════════════════════════════════════════════════════ -// Common data analysis -// ═══════════════════════════════════════════════════════════════════════════ - -/** - * Returns true if the member is a NodeFlags member. NodeFlags are already - * stored verbatim in every node's flags field, so they do not need to be - * redundantly encoded in the commonData bits. - */ -function isNodeFlagsMember(m: MemberInfo): boolean { - const dt = m.declaredType; - return (dt.kind === "primitive" && dt.name === "NodeFlags") || - (!!m.bitmask && m.bitmask.startsWith("NodeFlags")); -} - -/** Returns the bool members that can be auto-encoded in the commonData (1 bit each). */ -function getAutoBoolBits(node: NodeType): MemberInfo[] { - // If any non-auto-encodable data exists, all data encoding is hand-written (including bools). - if (needsHandWrittenCommonData(node)) return []; - const members = schemaMembers(node); - return members.filter(m => { - if (m.isChild() || m.isKindParam()) return false; - const dt = m.declaredType; - return dt.kind === "primitive" && dt.name === "bool"; - }); -} - -/** Returns the SyntaxKind union members that can be auto-encoded. */ -function getAutoUnionBits(node: NodeType): MemberInfo[] { - if (needsHandWrittenCommonData(node)) return []; - const members = schemaMembers(node); - return members.filter(m => { - if (m.isChild() || m.isKindParam()) return false; - return isSyntaxKindUnion(m); - }); -} - -/** Check if a member's type is a union of SyntaxKind values (auto-encodable). */ -function isSyntaxKindUnion(m: MemberInfo): boolean { - const dt = resolveUnion(m.declaredType); - if (!dt || dt.kind !== "union") return false; - return dt.types.every(t => t.kind === "kind"); -} - -/** Resolve through aliases to find an underlying union type, if any. */ -function resolveUnion(dt: import("./schema.ts").Type): import("./schema.ts").Type | undefined { - if (dt.kind === "union") return dt; - if (dt.kind === "alias") return resolveUnion(dt.resolved); - return undefined; -} - -/** Number of bits needed to encode a SyntaxKind union member. */ -function unionBitWidth(m: MemberInfo): number { - const dt = resolveUnion(m.declaredType); - if (!dt || dt.kind !== "union") return 0; - // Optional unions need an extra index for "absent" (index 0 = absent) - const count = m.optional ? dt.types.length + 1 : dt.types.length; - return Math.ceil(Math.log2(count)); -} - -/** - * Returns true if the node has non-bool, non-string data members whose encoding - * format cannot be automatically inferred. Such nodes delegate their entire - * commonData encoding/decoding to hand-written functions. - * - * SyntaxKind union members are auto-encodable and do NOT require hand-written functions. - */ -function needsHandWrittenCommonData(node: NodeType): boolean { - if (node.handWritten) return false; // extended data nodes handle everything - const members = schemaMembers(node); - return members.some(m => { - if (m.isChild() || m.isKindParam()) return false; - const dt = m.declaredType; - if (dt.kind === "primitive" && dt.name === "bool") return false; - if (dt.kind === "primitive" && dt.name === "string") return false; - if (isSyntaxKindUnion(m)) return false; - if (isNodeFlagsMember(m)) return false; - return true; - }); -} - -/** - * Returns all data members (non-child, non-kindParam, non-string) for a node - * that needs hand-written commonData. Includes bools, since the hand-written - * function controls the entire 6-bit layout. - */ -function getHandWrittenDataMembers(node: NodeType): MemberInfo[] { - const members = schemaMembers(node); - return members.filter(m => { - if (m.isChild() || m.isKindParam()) return false; - const dt = m.declaredType; - if (dt.kind === "primitive" && dt.name === "string") return false; - if (isNodeFlagsMember(m)) return false; - return true; - }); -} - -// ═══════════════════════════════════════════════════════════════════════════ -// Data type classification -// ═══════════════════════════════════════════════════════════════════════════ - -type DataType = "string" | "children" | "extended"; - -function classifyDataType(node: NodeType): DataType { - if (node.handWritten) return "extended"; - const members = schemaMembers(node); - const stringMembers = members.filter(m => - !m.isChild() && !m.isKindParam() && - m.declaredType.kind === "primitive" && m.declaredType.name === "string" - ); - if (stringMembers.length > 1) return "extended"; - if (stringMembers.length === 1) { - // If the node also has non-bool data members, TokenFlags etc. won't fit in - // 6 common data bits → must use extended data. - if (needsHandWrittenCommonData(node)) return "extended"; - return "string"; - } - return "children"; -} - -// ═══════════════════════════════════════════════════════════════════════════ -// Go factory call helpers -// ═══════════════════════════════════════════════════════════════════════════ - -/** Get all SyntaxKind Go constant names for a node (prefixed with `ast.`). */ -function getGoKinds(node: NodeType): string[] { - return node.allKinds().map(k => `ast.${k.formatGoConstant()}`); -} - -/** Get all SyntaxKind names for a node in TS (without prefix). */ -function getTSKinds(node: NodeType): string[] { - return node.allKinds().map(k => k.name); -} - -/** Get the Go factory method name (e.g., "NewIfStatement"). */ -function goFactoryName(node: NodeType): string { - return `New${node.name}`; -} - -/** Get the Go "As*" cast method name (e.g., "AsIfStatement"). */ -function goCastName(node: NodeType): string { - return `As${node.name}`; -} - -/** Get the Go param name, avoiding Go keywords. */ -function goParamName(m: MemberInfo): string { - return m.goParamName(); -} - -/** Get the Go accessor for a member on a cast struct (e.g., "n.IsTypeOnly", "n.Name()"). */ -function goFieldAccess(m: MemberInfo): string { - if (m.private) return `n.${api.capitalize(m.name)}()`; - return `n.${m.name}`; -} - -/** Get the default Go value for a non-encoded factory parameter. */ -function goDefaultValue(m: MemberInfo): string { - if (m.isChild()) return "nil"; - const dt = m.declaredType; - if (dt.kind === "primitive") { - switch (dt.name) { - case "bool": - return "false"; - case "int": - return "0"; - case "string": - return `""`; - case "NodeFlags": - return "0"; - case "TokenFlags": - return "0"; - case "ModifierFlags": - return "0"; - } - } - if (dt.baseKind() === "kind") return "0"; - return "nil"; -} - -// ═══════════════════════════════════════════════════════════════════════════ -// Encoder node info (per-node analysis) -// ═══════════════════════════════════════════════════════════════════════════ - -interface EncoderNodeInfo { - node: NodeType; - dataType: DataType; - /** Encoded child members in visitor order. */ - childProps: MemberInfo[]; - /** Bool members auto-encoded in commonData (empty if hand-written). */ - autoBoolBits: MemberInfo[]; - /** SyntaxKind union members auto-encoded in commonData. */ - autoUnionBits: MemberInfo[]; - /** Whether commonData encoding is delegated to a hand-written function. */ - handWrittenCommonData: boolean; - /** The text member for string nodes. */ - textMember?: MemberInfo; - /** Whether this node has a Kind parameter in the factory. */ - kindMember?: MemberInfo; - /** All factory parameters, including non-encoded ones. */ - factoryMembers: MemberInfo[]; -} - -function analyzeNode(node: NodeType): EncoderNodeInfo { - const dataType = classifyDataType(node); - const members = schemaMembers(node); - const childProps = members.filter(m => isEncodedChild(m)); - const handWrittenCD = needsHandWrittenCommonData(node); - const autoBoolBits = getAutoBoolBits(node); - const autoUnionBits = getAutoUnionBits(node); - const kindMember = members.find(m => m.isKindParam()); - const textMember = dataType === "string" - ? members.find(m => - !m.isChild() && !m.isKindParam() && - m.declaredType.kind === "primitive" && m.declaredType.name === "string" - ) - : undefined; - // Factory members include all non-noFactory members (even noTS ones), - // since the factory signature is determined by generate-go-ast.ts. - const factoryMembers = node.members.filter(m => !m.noFactory); - - return { - node, - dataType, - childProps, - autoBoolBits, - autoUnionBits, - handWrittenCommonData: handWrittenCD, - textMember, - kindMember, - factoryMembers, - }; -} - -/** - * Compute the bit layout for all auto-encoded data members of a node. - * Bools get 1 bit each, SyntaxKind unions get ceil(log2(N)) bits. - * Returns entries with the bit position (relative to bit 24) and width. - */ -function getAutoEncodedLayout(info: EncoderNodeInfo): { member: MemberInfo; bitPos: number; bitWidth: number; }[] { - const layout: { member: MemberInfo; bitPos: number; bitWidth: number; }[] = []; - let bitPos = 0; - for (const m of info.autoBoolBits) { - layout.push({ member: m, bitPos, bitWidth: 1 }); - bitPos++; - } - for (const m of info.autoUnionBits) { - const width = unionBitWidth(m); - layout.push({ member: m, bitPos, bitWidth: width }); - bitPos += width; - } - return layout; -} - -/** Check if a node has any auto-encoded data members (bools or unions). */ -function hasAutoEncodedData(info: EncoderNodeInfo): boolean { - return info.autoBoolBits.length > 0 || info.autoUnionBits.length > 0; -} - -// ═══════════════════════════════════════════════════════════════════════════ -// Go Encoder Generation -// ═══════════════════════════════════════════════════════════════════════════ - -function generateGoEncoder(): string { - const w = new CodeWriter(); - w.write("// Code generated by tools/scripts/tsc/generate-encoder.ts. DO NOT EDIT."); - w.write(""); - w.write("package encoder"); - w.write(""); - w.write("import ("); - w.push(); - w.write('"fmt"'); - w.write(""); - w.write('"github.com/microsoft/TypeScript/tsc/internal/ast"'); - w.pop(); - w.write(")"); - w.write(""); - - generateGoGetNodeDataType(w); - generateGoGetChildrenPropertyMask(w); - generateGoGetNodeCommonData(w); - generateGoRecordNodeStrings(w); - generateGoRecordExtendedData(w); - - return w.toString(); -} - -function generateGoGetNodeDataType(w: CodeWriter) { - w.write("func getNodeDataType(node *ast.Node) uint32 {"); - w.push(); - w.write("switch node.Kind {"); - - const stringKinds: string[] = []; - const extendedKinds: string[] = []; - - for (const node of api.nodes()) { - const info = analyzeNode(node); - const kinds = getGoKinds(node); - - if (info.dataType === "string") { - stringKinds.push(...kinds); - } - else if (info.dataType === "extended") { - extendedKinds.push(...kinds); - } - } - - if (stringKinds.length > 0) { - w.write(`case ${stringKinds.join(",\n\t\t")}:`); - w.push(); - w.write("return NodeDataTypeString"); - w.pop(); - } - if (extendedKinds.length > 0) { - w.write(`case ${extendedKinds.join(",\n\t\t")}:`); - w.push(); - w.write("return NodeDataTypeExtendedData"); - w.pop(); - } - - w.write("default:"); - w.push(); - w.write("return NodeDataTypeChildren"); - w.pop(); - w.write("}"); - w.pop(); - w.write("}"); - w.write(""); -} - -function generateGoGetChildrenPropertyMask(w: CodeWriter) { - w.write("func getChildrenPropertyMask(node *ast.Node) uint8 {"); - w.push(); - w.write("switch node.Kind {"); - - for (const node of api.nodes()) { - const info = analyzeNode(node); - if (info.dataType === "extended") continue; - if (info.childProps.length === 0) continue; - - const kinds = getGoKinds(node); - const castName = goCastName(node); - - w.write(`case ${kinds.join(", ")}:`); - w.push(); - w.write(`n := node.${castName}()`); - - const parts: string[] = []; - for (let i = 0; i < info.childProps.length; i++) { - const m = info.childProps[i]; - const ct = childType(m); - let check: string; - if (ct === "modifierList") { - check = `hasModifiers(n.Modifiers())`; - } - else if (ct === "nodeList") { - check = `${goFieldAccess(m)} != nil`; - } - else if (ct === "rawNodeList") { - check = `len(${goFieldAccess(m)}) > 0`; - } - else { - check = `${goFieldAccess(m)} != nil`; - } - parts.push(`(boolToByte(${check}) << ${i})`); - } - w.write(`return ${parts.join(" | ")}`); - w.pop(); - } - - w.write("default:"); - w.push(); - w.write("return 0"); - w.pop(); - w.write("}"); - w.pop(); - w.write("}"); - w.write(""); -} - -function generateGoGetNodeCommonData(w: CodeWriter) { - w.write("func getNodeCommonData(node *ast.Node) uint32 {"); - w.push(); - w.write("switch node.Kind {"); - - for (const node of api.nodes()) { - const info = analyzeNode(node); - if (info.dataType === "extended") continue; - - if (info.handWrittenCommonData) { - // Delegate to hand-written function for all non-bool data members. - const kinds = getGoKinds(node); - w.write(`case ${kinds.join(", ")}:`); - w.push(); - w.write(`return getNodeCommonData_${node.name}(node)`); - w.pop(); - } - else if (hasAutoEncodedData(info)) { - const kinds = getGoKinds(node); - const castName = goCastName(node); - const layout = getAutoEncodedLayout(info); - - w.write(`case ${kinds.join(", ")}:`); - w.push(); - w.write(`n := node.${castName}()`); - - const parts: string[] = []; - for (const { member: m, bitPos, bitWidth } of layout) { - if (!isSyntaxKindUnion(m)) { - // Bool or bitmask - parts.push(`uint32(boolToByte(${goFieldAccess(m)})) << ${24 + bitPos}`); - } - else { - // SyntaxKind union → index-based encoding - const kinds = unionKindValues(m.declaredType); - const varName = `${goParamName(m)}Idx`; - w.write(`var ${varName} uint32`); - w.write(`switch ${goFieldAccess(m)} {`); - if (m.optional) { - // Optional: 0 = absent, 1..N = values[0]..values[N-1] - for (let i = 0; i < kinds.length; i++) { - w.write(`case ast.${kinds[i].formatGoConstant()}: ${varName} = ${i + 1}`); - } - } - else { - // Non-optional: 0 = values[0] (default), 1..N-1 = values[1]..values[N-1] - for (let i = 1; i < kinds.length; i++) { - w.write(`case ast.${kinds[i].formatGoConstant()}: ${varName} = ${i}`); - } - } - w.write("}"); - parts.push(`${varName} << ${24 + bitPos}`); - } - } - w.write(`return ${parts.join(" | ")}`); - w.pop(); - } - } - - w.write("}"); - w.write("return 0"); - w.pop(); - w.write("}"); - w.write(""); -} - -function generateGoRecordNodeStrings(w: CodeWriter) { - w.write("func recordNodeStrings(node *ast.Node, strs *stringTable) uint32 {"); - w.push(); - w.write("switch node.Kind {"); - - for (const node of api.nodes()) { - const info = analyzeNode(node); - if (info.dataType !== "string" || !info.textMember) continue; - - const kinds = getGoKinds(node); - const castName = goCastName(node); - const textAccess = info.textMember.private - ? `node.${castName}().${api.capitalize(info.textMember.name)}()` - : `node.${castName}().${info.textMember.name}`; - - w.write(`case ${kinds.join(", ")}:`); - w.push(); - w.write(`return strs.add(${textAccess}, node.Kind, node.Pos(), node.End())`); - w.pop(); - } - - w.write("default:"); - w.push(); - w.write(`panic(fmt.Sprintf("Unexpected node kind %v", node.Kind))`); - w.pop(); - w.write("}"); - w.pop(); - w.write("}"); - w.write(""); -} - -function generateGoRecordExtendedData(w: CodeWriter) { - w.write("func recordExtendedData(node *ast.Node, strs *stringTable, positionMap *ast.PositionMap, extendedData *[]byte, structuredData *[]byte) uint32 {"); - w.push(); - w.write("offset := uint32(len(*extendedData))"); - w.write("switch node.Kind {"); - - for (const node of api.nodes()) { - const info = analyzeNode(node); - if (info.dataType !== "extended") continue; - - const kinds = getGoKinds(node); - - w.write(`case ${kinds.join(", ")}:`); - w.push(); - w.write(`recordExtendedData_${node.name}(node, strs, positionMap, extendedData, structuredData)`); - w.pop(); - } - - w.write("default:"); - w.push(); - w.write(`panic(fmt.Sprintf("unknown extended data node kind %v", node.Kind))`); - w.pop(); - w.write("}"); - w.write("return offset"); - w.pop(); - w.write("}"); - w.write(""); -} - -// ═══════════════════════════════════════════════════════════════════════════ -// Go Decoder Generation -// ═══════════════════════════════════════════════════════════════════════════ - -function generateGoDecoder(): string { - const w = new CodeWriter(); - w.write("// Code generated by tools/scripts/tsc/generate-encoder.ts. DO NOT EDIT."); - w.write(""); - w.write("package encoder"); - w.write(""); - w.write("import ("); - w.push(); - w.write('"fmt"'); - w.write(""); - w.write('"github.com/microsoft/TypeScript/tsc/internal/ast"'); - w.pop(); - w.write(")"); - w.write(""); - - generateGoCreateStringNode(w); - generateGoCreateExtendedNode(w); - generateGoCreateChildrenNode(w); - - return w.toString(); -} - -function generateGoCreateStringNode(w: CodeWriter) { - w.write("func (d *astDecoder) createStringNode(kind ast.Kind, data uint32, commonData uint8) (*ast.Node, error) {"); - w.push(); - w.write("strIdx := data & NodeDataStringIndexMask"); - w.write("text := d.getString(strIdx)"); - w.write(""); - w.write("switch kind {"); - - for (const node of api.nodes()) { - const info = analyzeNode(node); - if (info.dataType !== "string") continue; - - const kinds = getGoKinds(node); - const factoryName = goFactoryName(node); - - w.write(`case ${kinds.join(", ")}:`); - w.push(); - - // Decode commonData - const commonVars = emitCommonDataDecode(w, node, info); - - // Build factory args - const args: string[] = []; - for (const m of info.factoryMembers) { - if (m.isKindParam()) { - args.push("kind"); - } - else if (m === info.textMember) { - if (m.listKind === "raw") { - args.push("[]string{text}"); - } - else { - args.push("text"); - } - } - else if (isEncodedChild(m)) { - args.push("nil"); - } - else { - const v = commonVars.get(m); - if (v) { - args.push(v); - } - else { - args.push(goDefaultValue(m)); - } - } - } - - w.write(`return d.factory.${factoryName}(${args.join(", ")}), nil`); - w.pop(); - } - - w.write("default:"); - w.push(); - w.write(`return nil, fmt.Errorf("unknown string node kind %v", kind)`); - w.pop(); - w.write("}"); - w.pop(); - w.write("}"); - w.write(""); -} - -function generateGoCreateExtendedNode(w: CodeWriter) { - w.write("func (d *astDecoder) createExtendedNode(kind ast.Kind, data uint32, childIndices []int, commonData uint8) (*ast.Node, error) {"); - w.push(); - w.write("switch kind {"); - - for (const node of api.nodes()) { - const info = analyzeNode(node); - if (info.dataType !== "extended") continue; - - const kinds = getGoKinds(node); - - w.write(`case ${kinds.join(", ")}:`); - w.push(); - // Extended data nodes call hand-written decode functions - w.write(`return d.decodeExtendedData_${node.name}(data, childIndices, commonData)`); - w.pop(); - } - - w.write("default:"); - w.push(); - w.write(`return nil, fmt.Errorf("unknown extended data node kind %v", kind)`); - w.pop(); - w.write("}"); - w.pop(); - w.write("}"); - w.write(""); -} - -function generateGoCreateChildrenNode(w: CodeWriter) { - w.write("func (d *astDecoder) createChildrenNode(kind ast.Kind, data uint32, childIndices []int, commonData uint8) (*ast.Node, error) {"); - w.push(); - w.write("mask := uint8(data & NodeDataChildMask)"); - w.write(""); - w.write("switch kind {"); - - // Build a set of all kinds claimed by specific nodes, so that the most - // specific node (fewest kinds) takes priority for shared kinds. - const kindOwner = new Map(); - for (const node of api.nodes()) { - const info = analyzeNode(node); - if (info.dataType !== "children") continue; - const kinds = getGoKinds(node); - for (const k of kinds) { - const existing = kindOwner.get(k); - if (!existing || kinds.length < existing.totalKinds) { - kindOwner.set(k, { node, info, totalKinds: kinds.length }); - } - } - } - - // Group kinds by their owning node (deconflicted). - const nodeKinds = new Map(); - for (const [kind, owner] of kindOwner) { - const key = owner.node.name; - if (!nodeKinds.has(key)) nodeKinds.set(key, []); - nodeKinds.get(key)!.push(kind); - } - - for (const node of api.nodes()) { - const info = analyzeNode(node); - if (info.dataType !== "children") continue; - - const kinds = nodeKinds.get(node.name); - if (!kinds || kinds.length === 0) continue; - - if (info.childProps.length === 0) { - emitZeroChildDecoder(w, node, info, kinds); - } - else if (info.childProps.length === 1) { - emitSingleChildDecoder(w, node, info, kinds); - } - else { - emitMultiChildDecoder(w, node, info, kinds); - } - } - - // Default: error for unhandled kinds - w.write("default:"); - w.push(); - w.write(`return nil, fmt.Errorf("unhandled node kind %v with %d children", kind, len(childIndices))`); - w.pop(); - - w.write("}"); - w.pop(); - w.write("}"); - w.write(""); -} - -// ═══════════════════════════════════════════════════════════════════════════ -// Decoder helpers -// ═══════════════════════════════════════════════════════════════════════════ - -/** - * Emits commonData decoding and returns a map of member → Go variable name. - * For auto-bool nodes: decodes bools from bit positions. - * For hand-written nodes: calls decodeNodeCommonData_(commonData). - */ -function emitCommonDataDecode(w: CodeWriter, node: NodeType, info: EncoderNodeInfo): Map { - const vars = new Map(); - - if (info.handWrittenCommonData) { - const dataMembers = getHandWrittenDataMembers(node); - if (dataMembers.length === 0) return vars; - - const varNames = dataMembers.map(m => goParamName(m)); - if (dataMembers.length === 1) { - w.write(`${varNames[0]} := decodeNodeCommonData_${node.name}(commonData)`); - } - else { - w.write(`${varNames.join(", ")} := decodeNodeCommonData_${node.name}(commonData)`); - } - for (let i = 0; i < dataMembers.length; i++) { - vars.set(dataMembers[i], varNames[i]); - } - } - else { - const layout = getAutoEncodedLayout(info); - for (const { member: m, bitPos, bitWidth } of layout) { - const varName = goParamName(m); - if (!isSyntaxKindUnion(m)) { - // Bool or bitmask - w.write(`${varName} := commonData&${1 << bitPos} != 0`); - vars.set(m, varName); - } - else { - // SyntaxKind union → index-based decoding - const kinds = unionKindValues(m.declaredType); - const mask = (1 << bitWidth) - 1; - const indexExpr = bitPos === 0 - ? `commonData & ${mask}` - : `(commonData >> ${bitPos}) & ${mask}`; - if (m.optional) { - // Optional: 0 = absent (KindUnknown), 1..N = values[0]..values[N-1] - w.write(`var ${varName} ast.Kind`); - w.write(`switch ${indexExpr} {`); - for (let i = 0; i < kinds.length; i++) { - w.write(`case ${i + 1}: ${varName} = ast.${kinds[i].formatGoConstant()}`); - } - w.write("}"); - } - else if (kinds.length === 2) { - // Non-optional 2-value: simple if - w.write(`${varName} := ast.${kinds[0].formatGoConstant()}`); - w.write(`if ${indexExpr} != 0 {`); - w.push(); - w.write(`${varName} = ast.${kinds[1].formatGoConstant()}`); - w.pop(); - w.write("}"); - } - else { - // Non-optional N-value: switch - w.write(`var ${varName} ast.Kind`); - w.write(`switch ${indexExpr} {`); - for (let i = 0; i < kinds.length; i++) { - w.write(`case ${i}: ${varName} = ast.${kinds[i].formatGoConstant()}`); - } - w.write("}"); - } - vars.set(m, varName); - } - } - } - - return vars; -} - -function emitZeroChildDecoder(w: CodeWriter, node: NodeType, info: EncoderNodeInfo, kinds: string[]) { - const factoryName = goFactoryName(node); - - w.write(`case ${kinds.join(",\n\t\t")}:`); - w.push(); - - const commonVars = emitCommonDataDecode(w, node, info); - - const args: string[] = []; - for (const m of info.factoryMembers) { - if (m.isKindParam()) { - args.push("kind"); - continue; - } - const v = commonVars.get(m); - if (v) { - args.push(v); - continue; - } - args.push(goDefaultValue(m)); - } - - w.write(`return d.factory.${factoryName}(${args.join(", ")}), nil`); - w.pop(); -} - -function emitSingleChildDecoder(w: CodeWriter, node: NodeType, info: EncoderNodeInfo, kinds: string[]) { - const cp = info.childProps[0]; - const ct = childType(cp); - const factoryName = goFactoryName(node); - const hasCommonData = info.autoBoolBits.length > 0 || info.handWrittenCommonData; - - w.write(`case ${kinds.join(", ")}:`); - w.push(); - - // Decode commonData if needed - const commonVars = emitCommonDataDecode(w, node, info); - - // Build factory args - const args: string[] = []; - for (const m of info.factoryMembers) { - if (m.isKindParam()) { - args.push("kind"); - } - else if (m === cp) { - if (ct === "rawNodeList") { - w.write(`nodes := d.allocNodeSlice(len(childIndices))`); - w.write("for i, ci := range childIndices {"); - w.push(); - w.write("nodes[i] = d.nodes[ci]"); - w.pop(); - w.write("}"); - args.push("nodes"); - } - else if (ct === "nodeList") { - if (hasCommonData) { - w.write("var list *ast.NodeList"); - w.write("if len(childIndices) > 0 {"); - w.push(); - w.write("list = d.nodeListAt(childIndices[0])"); - w.pop(); - w.write("}"); - args.push("list"); - } - else { - args.push("d.singleNodeListChild(childIndices)"); - } - } - else if (ct === "modifierList") { - w.write("var mods *ast.ModifierList"); - w.write("if len(childIndices) > 0 {"); - w.push(); - w.write("mods = d.modifierListAt(childIndices[0])"); - w.pop(); - w.write("}"); - args.push("mods"); - } - else { - args.push("d.singleChild(childIndices)"); - } - } - else { - const cdVar = commonVars.get(m); - if (cdVar) { - args.push(cdVar); - } - else { - args.push(goDefaultValue(m)); - } - } - } - - w.write(`return d.factory.${factoryName}(${args.join(", ")}), nil`); - w.pop(); -} - -function emitMultiChildDecoder(w: CodeWriter, node: NodeType, info: EncoderNodeInfo, kinds: string[]) { - const factoryName = goFactoryName(node); - - w.write(`case ${kinds.join(", ")}:`); - w.push(); - - // Decode commonData - const commonVars = emitCommonDataDecode(w, node, info); - - // Child iterator - w.write("it := newChildIter(childIndices)"); - - // Decode each child from mask - const childVars = new Map(); - for (let i = 0; i < info.childProps.length; i++) { - const m = info.childProps[i]; - const ct = childType(m); - const varName = goParamName(m); - let decode: string; - if (ct === "modifierList") { - decode = `d.modifierListAt(it.nextIf(mask, ${i}))`; - } - else if (ct === "rawNodeList") { - const nlVar = `${varName}NL`; - w.write(`${nlVar} := d.nodeListAt(it.nextIf(mask, ${i}))`); - w.write(`var ${varName} []*ast.Node`); - w.write(`if ${nlVar} != nil {`); - w.push(); - w.write(`${varName} = ${nlVar}.Nodes`); - w.pop(); - w.write("}"); - childVars.set(m, varName); - continue; - } - else if (ct === "nodeList") { - decode = `d.nodeListAt(it.nextIf(mask, ${i}))`; - } - else { - decode = `d.nodeAt(it.nextIf(mask, ${i}))`; - } - w.write(`${varName} := ${decode}`); - childVars.set(m, varName); - } - - // Build factory args - const args: string[] = []; - for (const m of info.factoryMembers) { - if (m.isKindParam()) { - args.push("kind"); - continue; - } - const childVar = childVars.get(m); - if (childVar) { - args.push(childVar); - continue; - } - const cdVar = commonVars.get(m); - if (cdVar) { - args.push(cdVar); - continue; - } - args.push(goDefaultValue(m)); - } - - // Handle kindAliases with separate factories - if (node.kindAliases.length > 0 && !info.kindMember) { - for (const alias of node.kindAliases) { - w.write(`if kind == ast.Kind${alias} {`); - w.push(); - w.write(`return d.factory.New${alias}(${args.slice(info.kindMember ? 1 : 0).join(", ")}), nil`); - w.pop(); - w.write("}"); - } - } - - w.write(`return d.factory.${factoryName}(${args.join(", ")}), nil`); - w.pop(); -} - -// ═══════════════════════════════════════════════════════════════════════════ -// TypeScript Protocol Generation -// ═══════════════════════════════════════════════════════════════════════════ - -function generateTSProtocol(): string { - const w = new CodeWriter(" "); - w.write("// Code generated by tools/scripts/tsc/generate-encoder.ts. DO NOT EDIT."); - w.write(""); - w.write('import { SyntaxKind } from "../../ast/index.ts";'); - w.write(""); - - // Complete childProperties mapping — includes all nodes with children - // (single-child, multi-child, and extended data nodes). - w.write("export const childProperties: Readonly>> = {"); - w.push(); - - for (const node of api.nodes()) { - const info = analyzeNode(node); - if (info.childProps.length === 0) continue; - - const kinds = getTSKinds(node); - const props = info.childProps.map(m => `"${api.uncapitalize(m.name)}"`).join(", "); - for (const kind of kinds) { - w.write(`[SyntaxKind.${kind}]: [${props}],`); - } - } - - w.pop(); - w.write("};"); - w.write(""); - - // singleChildNodePropertyNames mapping - w.write("export const singleChildNodePropertyNames: Readonly>> = {"); - w.push(); - - for (const node of api.nodes()) { - const info = analyzeNode(node); - if (info.dataType === "extended") continue; - if (info.childProps.length !== 1) continue; - - const kinds = getTSKinds(node); - const prop = api.uncapitalize(info.childProps[0].name); - - for (const kind of kinds) { - w.write(`[SyntaxKind.${kind}]: "${prop}",`); - } - } - - w.pop(); - w.write("};"); - w.write(""); - - return w.toString(); -} - -// ═══════════════════════════════════════════════════════════════════════════ -// TypeScript Encoder Generation (encoder.generated.ts) -// ═══════════════════════════════════════════════════════════════════════════ - -/** Get all KindType values from a SyntaxKind union type. Resolves through aliases. */ -function unionKindValues(dt: import("./schema.ts").Type): import("./schema.ts").KindType[] { - const resolved = resolveUnion(dt); - if (!resolved || resolved.kind !== "union") return []; - return resolved.types.filter((t): t is import("./schema.ts").KindType => t.kind === "kind"); -} - -function generateTSEncoder(): string { - const w = new CodeWriter(" "); - w.write("// Code generated by tools/scripts/tsc/generate-encoder.ts. DO NOT EDIT."); - w.write(""); - - // Collect needed type imports - const typeImports = new Set(); - const valueImports = new Set(["SyntaxKind"]); - - for (const node of api.nodes()) { - const info = analyzeNode(node); - if (info.dataType === "extended") continue; - - if (info.autoBoolBits.length > 0 || info.autoUnionBits.length > 0) { - typeImports.add(node.name); - } - else if (info.handWrittenCommonData) { - typeImports.add(node.name); - } - } - - // Write imports - if (typeImports.size > 0) { - w.write("import type {"); - w.push(); - w.write("Node,"); - for (const t of [...typeImports].sort()) { - w.write(`${t},`); - } - w.pop(); - w.write('} from "../../ast/index.ts";'); - } - w.write("import {"); - w.push(); - for (const v of [...valueImports].sort()) { - w.write(`${v},`); - } - w.pop(); - w.write('} from "../../ast/index.ts";'); - w.write("import {"); - w.push(); - w.write("NODE_DATA_TYPE_CHILDREN,"); - w.write("NODE_DATA_TYPE_EXTENDED,"); - w.write("NODE_DATA_TYPE_STRING,"); - w.pop(); - w.write('} from "./protocol.ts";'); - w.write(""); - - generateTSGetNodeDataType(w); - generateTSGetNodeCommonData(w); - - return w.toString(); -} - -function generateTSGetNodeDataType(w: CodeWriter) { - w.write("export function getNodeDataType(kind: SyntaxKind): number {"); - w.push(); - w.write("switch (kind) {"); - - const stringKinds: string[] = []; - const extendedKinds: string[] = []; - - for (const node of api.nodes()) { - const info = analyzeNode(node); - const kinds = getTSKinds(node); - if (info.dataType === "string") { - stringKinds.push(...kinds); - } - else if (info.dataType === "extended") { - extendedKinds.push(...kinds); - } - } - - if (stringKinds.length > 0) { - for (const k of stringKinds) { - w.write(`case SyntaxKind.${k}:`); - } - w.push(); - w.write("return NODE_DATA_TYPE_STRING;"); - w.pop(); - } - if (extendedKinds.length > 0) { - for (const k of extendedKinds) { - w.write(`case SyntaxKind.${k}:`); - } - w.push(); - w.write("return NODE_DATA_TYPE_EXTENDED;"); - w.pop(); - } - - w.write("default:"); - w.push(); - w.write("return NODE_DATA_TYPE_CHILDREN;"); - w.pop(); - w.write("}"); - w.pop(); - w.write("}"); - w.write(""); -} - -function generateTSGetNodeCommonData(w: CodeWriter) { - w.write("export function getNodeCommonData(node: Node): number {"); - w.push(); - w.write("switch (node.kind) {"); - - for (const node of api.nodes()) { - const info = analyzeNode(node); - if (info.dataType === "extended") continue; - - if (hasAutoEncodedData(info)) { - // Auto-encoded: generate bit packing for bools and index encoding for unions - const kinds = getTSKinds(node); - const layout = getAutoEncodedLayout(info); - for (const k of kinds) { - w.write(`case SyntaxKind.${k}:`); - } - w.push(); - const parts: string[] = []; - for (const { member: m, bitPos, bitWidth } of layout) { - const propName = api.uncapitalize(m.name); - if (!isSyntaxKindUnion(m)) { - // Bool - parts.push(`((node as ${node.name}).${propName} ? 1 : 0) << ${24 + bitPos}`); - } - else { - // SyntaxKind union → index-based encoding - const kinds = unionKindValues(m.declaredType); - if (m.optional) { - // Optional union: 0 = absent, 1..N = values[0]..values[N-1] - const cases = kinds.map((k, i) => `(node as ${node.name}).${propName} === ${k.formatTypeScript()} ? ${i + 1} : `).join(""); - parts.push(`(${cases}0) << ${24 + bitPos}`); - } - else { - // Required union: index into values array - const cases = kinds.slice(1).map((k, i) => `(node as ${node.name}).${propName} === ${k.formatTypeScript()} ? ${i + 1} : `).join(""); - parts.push(`(${cases}0) << ${24 + bitPos}`); - } - } - } - w.write(`return ${parts.join(" | ")};`); - w.pop(); - } - else if (info.handWrittenCommonData) { - // Hand-written: generate encoding based on member types - const kinds = getTSKinds(node); - const dataMembers = getHandWrittenDataMembers(node); - if (dataMembers.length === 0) continue; - - // Skip nodes with 'any' type data members (e.g., SyntheticExpression) — they should never be encoded - if (dataMembers.some(m => m.declaredType.kind === "primitive" && m.declaredType.name === "any")) { - continue; - } - - for (const k of kinds) { - w.write(`case SyntaxKind.${k}:`); - } - w.push(); - - emitTSCommonDataEncoding(w, node, dataMembers); - - w.pop(); - } - } - - w.write("}"); - w.write("return 0;"); - w.pop(); - w.write("}"); - w.write(""); -} - -function emitTSCommonDataEncoding(w: CodeWriter, node: NodeType, dataMembers: MemberInfo[]) { - const parts: string[] = []; - let bitPos = 24; - - for (const m of dataMembers) { - const propName = api.uncapitalize(m.name); - const dt = m.declaredType; - - if (dt.kind === "primitive" && dt.name === "bool") { - parts.push(`((node as ${node.name}).${propName} ? 1 : 0) << ${bitPos}`); - bitPos++; - } - else { - throw new Error(`Unexpected hand-written data member ${node.name}.${m.name} of type ${dt.kind}`); - } - } - - if (parts.length === 0) { - w.write("return 0;"); - } - else if (parts.length === 1) { - w.write(`return ${parts[0]};`); - } - else { - w.write("return " + parts.join(" |\n ") + ";"); - } -} - -// ═══════════════════════════════════════════════════════════════════════════ -// TypeScript Node Generation (node.ts — entire file) -// ═══════════════════════════════════════════════════════════════════════════ - -function generateTSNodeGenerated(): string { - const w = new CodeWriter(" "); - - // Collect all unique child property names and their types (node vs nodeList) - const childGetters = new Map(); - for (const node of api.nodes()) { - const info = analyzeNode(node); - for (const m of info.childProps) { - const propName = api.uncapitalize(m.name); - const ct = childType(m); - const isListType = ct === "nodeList" || ct === "modifierList"; - const existing = childGetters.get(propName); - if (!existing) { - childGetters.set(propName, isListType ? "nodeList" : "node"); - } - else if ((existing === "node" && isListType) || (existing === "nodeList" && !isListType)) { - childGetters.set(propName, "both"); - } - } - } - - // Collect all unique auto-bool property names with their bit positions - const boolGetters = new Map(); - for (const node of api.nodes()) { - const info = analyzeNode(node); - const layout = getAutoEncodedLayout(info); - for (const { member: m, bitPos, bitWidth } of layout) { - if (!isSyntaxKindUnion(m)) { - const propName = api.uncapitalize(m.name); - const absBitPos = 24 + bitPos; - const existing = boolGetters.get(propName); - if (existing !== undefined && existing !== absBitPos) { - throw new Error(`Bool property "${propName}" has inconsistent bit positions: ${existing} vs ${absBitPos}`); - } - boolGetters.set(propName, absBitPos); - } - } - } - - // Collect all unique SyntaxKind union property getters: - // Map from property name → array of { kinds, bitPos, bitWidth, values, optional } - interface UnionGetterEntry { - kinds: string[]; - bitPos: number; - bitWidth: number; - values: KindType[]; - optional: boolean; - } - const unionGetters = new Map(); - for (const node of api.nodes()) { - const info = analyzeNode(node); - const layout = getAutoEncodedLayout(info); - for (const { member: m, bitPos, bitWidth } of layout) { - if (isSyntaxKindUnion(m)) { - const propName = api.uncapitalize(m.name); - const entries = unionGetters.get(propName) || []; - entries.push({ - kinds: getTSKinds(node), - bitPos: 24 + bitPos, - bitWidth, - values: unionKindValues(m.declaredType), - optional: !!m.optional, - }); - unionGetters.set(propName, entries); - } - } - } - - // Collect string data property info - const stringTextKinds: string[] = []; - const extendedTextKinds: string[] = []; - for (const node of api.nodes()) { - const info = analyzeNode(node); - if (info.dataType === "string" && info.textMember) { - stringTextKinds.push(...getTSKinds(node)); - } - else if (info.dataType === "extended") { - extendedTextKinds.push(...getTSKinds(node)); - } - } - - // Emit the file - w.write("// Code generated by tools/scripts/tsc/generate-encoder.ts. DO NOT EDIT."); - w.write(""); - emitNodeGeneratedImports(w); - w.write(""); - emitRemoteNodeList(w); - w.write(""); - emitRemoteNodeClassOpen(w); - w.write(""); - - // ── Generated: Boolean property getters ── - w.write(" // ═══ Generated boolean property getters ═══"); - w.write(""); - for (const [propName, bitPos] of [...boolGetters.entries()].sort((a, b) => a[0].localeCompare(b[0]))) { - w.write(` get ${propName}(): boolean {`); - w.write(` return (this.data & (1 << ${bitPos})) !== 0;`); - w.write(` }`); - w.write(""); - } - - // ── Generated: SyntaxKind union property getters ── - w.write(" // ═══ Generated SyntaxKind union property getters ═══"); - w.write(""); - for (const [propName, entries] of [...unionGetters.entries()].sort((a, b) => a[0].localeCompare(b[0]))) { - // Determine return type: if any entry is optional, return type includes undefined - const anyOptional = entries.some(e => e.optional); - const returnType = anyOptional ? "SyntaxKind | undefined" : "SyntaxKind | undefined"; - w.write(` get ${propName}(): ${returnType} {`); - w.write(` switch (this.kind) {`); - for (const entry of entries) { - for (const kind of entry.kinds) { - w.write(` case SyntaxKind.${kind}:`); - } - const mask = (1 << entry.bitWidth) - 1; - const indexExpr = `(this.data >> ${entry.bitPos}) & 0x${mask.toString(16)}`; - if (entry.optional) { - // Optional: 0 = undefined, 1..N = values[0]..values[N-1] - w.write(` {`); - w.write(` const idx = ${indexExpr};`); - w.write(` if (idx === 0) return undefined;`); - const cases = entry.values.map((k, i) => `idx === ${i + 1} ? ${k.formatTypeScript()} : `).join(""); - w.write(` return ${cases}undefined;`); - w.write(` }`); - } - else if (entry.values.length === 2) { - // 2-value: simple ternary - w.write(` return ${indexExpr} ? ${entry.values[1].formatTypeScript()} : ${entry.values[0].formatTypeScript()};`); - } - else { - // N-value: switch on index - w.write(` {`); - w.write(` const idx = ${indexExpr};`); - for (let i = 0; i < entry.values.length; i++) { - if (i === 0) { - // default case (index 0) - } - else { - w.write(` if (idx === ${i}) return ${entry.values[i].formatTypeScript()};`); - } - } - w.write(` return ${entry.values[0].formatTypeScript()};`); - w.write(` }`); - } - } - w.write(` }`); - w.write(` }`); - w.write(""); - } - - // ── Generated: templateFlags (extended data, not a union) ── - emitTemplateFlagsGetter(w); - w.write(""); - - // ── Generated: tokenFlags (extended data for RegularExpressionLiteral) ── - emitTokenFlagsGetter(w); - w.write(""); - - // ── Generated: Child property getters ── - w.write(" // ═══ Generated child property getters ═══"); - w.write(""); - for (const [propName, type] of [...childGetters.entries()].sort((a, b) => a[0].localeCompare(b[0]))) { - const returnType = type === "nodeList" ? "RemoteNodeList" : type === "both" ? "RemoteNode | RemoteNodeList" : "RemoteNode"; - const castType = type === "nodeList" ? "RemoteNodeList" : type === "both" ? "RemoteNode | RemoteNodeList" : "RemoteNode"; - w.write(` get ${propName}(): ${returnType} | undefined {`); - w.write(` return this.getNamedChild(${JSON.stringify(propName)}) as ${castType};`); - w.write(` }`); - } - w.write(""); - - // ── Generated: String property getters (text, rawText) ── - w.write(" // ═══ Generated string property getters ═══"); - w.write(""); - emitStringGetters(w, stringTextKinds, extendedTextKinds); - w.write(""); - - // ── Generated: Extended data property getters (SourceFile-specific, etc.) ── - w.write(" // ═══ Generated extended data property getters ═══"); - w.write(""); - emitExtendedDataGetters(w); - w.write(""); - - // ── Hand-written remaining getters (flags, modifierFlags, etc.) ── - emitRemoteNodeClassClose(w); - w.write(""); - - return w.toString(); -} - -// ── Infrastructure template sections ── - -function emitNodeGeneratedImports(w: CodeWriter) { - w.write(`import {`); - w.write(` getTokenPosOfNode,`); - w.write(` ModifierFlags,`); - w.write(` type Node,`); - w.write(` type NodeArray,`); - w.write(` type SourceFile,`); - w.write(` SyntaxKind,`); - w.write(`} from "../../ast/index.ts";`); - w.write(`import {`); - w.write(` childProperties,`); - w.write(` KIND_NODE_LIST,`); - w.write(` NODE_DATA_TYPE_CHILDREN,`); - w.write(` NODE_DATA_TYPE_EXTENDED,`); - w.write(` NODE_DATA_TYPE_STRING,`); - w.write(` NODE_LEN,`); - w.write(` NODE_OFFSET_DATA,`); - w.write(` NODE_OFFSET_END,`); - w.write(` NODE_OFFSET_FLAGS,`); - w.write(` NODE_OFFSET_KIND,`); - w.write(` NODE_OFFSET_NEXT,`); - w.write(` NODE_OFFSET_PARENT,`); - w.write(` NODE_OFFSET_POS,`); - w.write(`} from "./protocol.ts";`); - w.write(`import {`); - w.write(` type NodeDataType,`); - w.write(` NODE_CHILD_MASK,`); - w.write(` NODE_DATA_TYPE_MASK,`); - w.write(` NODE_EXTENDED_DATA_MASK,`); - w.write(` NODE_STRING_INDEX_MASK,`); - w.write(` modifierToFlag,`); - w.write(` popcount8,`); - w.write(` RemoteNodeBase,`); - w.write(` type SourceFileInfo,`); - w.write(`} from "./node.infrastructure.ts";`); -} - -function emitRemoteNodeList(w: CodeWriter) { - w.write(`export class RemoteNodeList extends Array implements NodeArray {`); - w.write(` // Inherited Array methods like filter/map/slice use ArraySpeciesCreate, which would`); - w.write(` // otherwise call \`new RemoteNodeList(length)\` and fail. Produce a plain Array instead.`); - w.write(` static get [Symbol.species](): ArrayConstructor {`); - w.write(` return Array;`); - w.write(` }`); - w.write(``); - w.write(` parent: RemoteNode;`); - w.write(` transformFlags: number = 0;`); - w.write(` protected view: DataView;`); - w.write(` protected index: number;`); - w.write(` private _byteIndex: number;`); - w.write(` // Cursor memoizing the last resolved (logical index -> node index) so that`); - w.write(` // sequential forward access (index loops and list[i], plus forEach/map/`); - w.write(` // reduce/filter) resumes instead of re-walking from the head, turning an`); - w.write(` // O(n) pass over the whole list from O(n^2) into O(n).`); - w.write(` private _cursorIndex: number = 0;`); - w.write(` private _cursorNodeIndex: number = 0;`); - w.write(``); - w.write(` get pos(): number {`); - w.write(` return this.view.getUint32(this._byteIndex + NODE_OFFSET_POS, true);`); - w.write(` }`); - w.write(``); - w.write(` get end(): number {`); - w.write(` return this.view.getUint32(this._byteIndex + NODE_OFFSET_END, true);`); - w.write(` }`); - w.write(``); - w.write(` get next(): number {`); - w.write(` return this.view.getUint32(this._byteIndex + NODE_OFFSET_NEXT, true);`); - w.write(` }`); - w.write(``); - w.write(` private get data(): number {`); - w.write(` return this.view.getUint32(this._byteIndex + NODE_OFFSET_DATA, true);`); - w.write(` }`); - w.write(``); - w.write(` get hasTrailingComma(): boolean {`); - w.write(` return (this.view.getUint32(this._byteIndex + NODE_OFFSET_FLAGS, true) & 1) !== 0;`); - w.write(` }`); - w.write(``); - w.write(` private sourceFile: SourceFileInfo;`); - w.write(``); - w.write(` constructor(view: DataView, index: number, parent: RemoteNode, sourceFile: SourceFileInfo, offsetNodes: number) {`); - w.write(` super(view.getUint32(offsetNodes + index * NODE_LEN + NODE_OFFSET_DATA, true));`); - w.write(` this.view = view;`); - w.write(` this.index = index;`); - w.write(` this.parent = parent;`); - w.write(` this.sourceFile = sourceFile;`); - w.write(` this._byteIndex = offsetNodes + index * NODE_LEN;`); - w.write(` this._cursorNodeIndex = index + 1;`); - w.write(``); - w.write(` const length = this.length;`); - w.write(` for (let i = 16; i < length; i++) {`); - w.write(` Object.defineProperty(this, i, {`); - w.write(` get() {`); - w.write(` return this.at(i);`); - w.write(` },`); - w.write(` });`); - w.write(` }`); - w.write(` }`); - - // Emit indexed getters 0..15 - for (let i = 0; i < 16; i++) { - w.write(` get ${i}(): RemoteNode {`); - w.write(` return this.at(${i});`); - w.write(` }`); - } - - w.write(``); - w.write(` *[Symbol.iterator](): ArrayIterator {`); - w.write(` if (!this.length) return;`); - w.write(` let next = this.index + 1;`); - w.write(` while (next) {`); - w.write(` const child = this.getOrCreateChildAtNodeIndex(next);`); - w.write(` next = child.next;`); - w.write(` yield child as RemoteNode;`); - w.write(` }`); - w.write(` }`); - w.write(``); - w.write(` forEachNode(visitNode: (node: RemoteNode) => T | undefined): T | undefined {`); - w.write(` if (!this.length) return;`); - w.write(` let next = this.index + 1;`); - w.write(` while (next) {`); - w.write(` const child = this.getOrCreateChildAtNodeIndex(next);`); - w.write(` next = child.next;`); - w.write(` const result = visitNode(child as RemoteNode);`); - w.write(` if (result) return result;`); - w.write(` }`); - w.write(` }`); - w.write(``); - w.write(` at(index: number): RemoteNode {`); - w.write(` if (!Number.isInteger(index)) {`); - w.write(` return undefined!;`); - w.write(` }`); - w.write(` if (index >= this.data || (index < 0 && -index > this.data)) {`); - w.write(` return undefined!;`); - w.write(` }`); - w.write(` if (index < 0) {`); - w.write(` index = this.length + index;`); - w.write(` }`); - w.write(` // Walk the raw buffer following each node's \`next\` pointer instead of`); - w.write(` // materializing every intermediate RemoteNode just to read it. Resume from`); - w.write(` // the memoized cursor when possible so sequential forward access is O(1)`); - w.write(` // amortized (a full in-order pass is O(n) rather than O(n^2)).`); - w.write(` const offsetNodes = this.sourceFile._offsetNodes;`); - w.write(` let i: number;`); - w.write(` let next: number;`); - w.write(` if (index >= this._cursorIndex) {`); - w.write(` i = this._cursorIndex;`); - w.write(` next = this._cursorNodeIndex;`); - w.write(` }`); - w.write(` else {`); - w.write(` i = 0;`); - w.write(` next = this.index + 1;`); - w.write(` }`); - w.write(` for (; i < index; i++) {`); - w.write(` next = this.view.getUint32(offsetNodes + next * NODE_LEN + NODE_OFFSET_NEXT, true);`); - w.write(` }`); - w.write(` this._cursorIndex = index;`); - w.write(` this._cursorNodeIndex = next;`); - w.write(` return this.getOrCreateChildAtNodeIndex(next) as RemoteNode;`); - w.write(` }`); - w.write(``); - w.write(` private getOrCreateChildAtNodeIndex(index: number): RemoteNode | RemoteNodeList {`); - w.write(` let child = this.sourceFile.nodes[index];`); - w.write(` if (!child) {`); - w.write(` const kind = this.view.getUint32(this.sourceFile._offsetNodes + index * NODE_LEN + NODE_OFFSET_KIND, true);`); - w.write(` if (kind === KIND_NODE_LIST) {`); - w.write(` throw new Error("NodeList cannot directly contain another NodeList");`); - w.write(` }`); - w.write(` const sf = this.sourceFile;`); - w.write(` child = new RemoteNode(this.view, index, this.parent, sf, sf._offsetNodes);`); - w.write(` sf.nodes[index] = child;`); - w.write(` sf._timing?.recordMaterialization();`); - w.write(` }`); - w.write(` return child;`); - w.write(` }`); - w.write(``); - w.write(` __print(): string {`); - w.write(` const result = [];`); - w.write(` result.push(\`kind: NodeList\`);`); - w.write(` result.push(\`index: \${this.index}\`);`); - w.write(` result.push(\`byteIndex: \${this._byteIndex}\`);`); - w.write(` result.push(\`length: \${this.length}\`);`); - w.write(` return result.join("\\n");`); - w.write(` }`); - w.write(`}`); -} - -function emitRemoteNodeClassOpen(w: CodeWriter) { - w.write(`export class RemoteNode extends RemoteNodeBase implements Node {`); - w.write(` protected static NODE_LEN: number = NODE_LEN;`); - w.write(` protected override get sourceFile(): SourceFileInfo {`); - w.write(` return this._sourceFile;`); - w.write(` }`); - w.write(` protected _sourceFile: SourceFileInfo;`); - w.write(` get id(): string {`); - w.write(` return \`\${this.index}.\${this.kind}.\${this.sourceFile.path}\`;`); - w.write(` }`); - w.write(``); - w.write(` constructor(view: DataView, index: number, parent: RemoteNode, sourceFile: SourceFileInfo, offsetNodes: number) {`); - w.write(` super(view, index, parent, offsetNodes + index * NODE_LEN);`); - w.write(` this._sourceFile = sourceFile;`); - w.write(` }`); - w.write(``); - w.write(` forEachChild(visitNode: (node: Node) => T, visitList?: (list: NodeArray) => T): T | undefined {`); - w.write(` if (this.hasChildren()) {`); - w.write(` let next = this.index + 1;`); - w.write(` do {`); - w.write(` const child = this.getOrCreateChildAtNodeIndex(next);`); - w.write(` if (child instanceof RemoteNodeList) {`); - w.write(` if (visitList) {`); - w.write(` const result = visitList(child);`); - w.write(` if (result) {`); - w.write(` return result;`); - w.write(` }`); - w.write(` }`); - w.write(` else {`); - w.write(` const result = child.forEachNode(visitNode);`); - w.write(` if (result) {`); - w.write(` return result;`); - w.write(` }`); - w.write(` }`); - w.write(` }`); - w.write(` else if (child.kind !== SyntaxKind.JSDoc) {`); - w.write(` const result = visitNode(child);`); - w.write(` if (result) {`); - w.write(` return result;`); - w.write(` }`); - w.write(` }`); - w.write(` next = child.next;`); - w.write(` }`); - w.write(` while (next);`); - w.write(` }`); - w.write(` }`); - w.write(``); - w.write(` get jsDoc(): readonly Node[] | undefined {`); - w.write(` if (!this.hasChildren()) {`); - w.write(` return undefined;`); - w.write(` }`); - w.write(` let result: Node[] | undefined;`); - w.write(` let next = this.index + 1;`); - w.write(` do {`); - w.write(` const child = this.getOrCreateChildAtNodeIndex(next);`); - w.write(` if (!(child instanceof RemoteNodeList) && child.kind === SyntaxKind.JSDoc) {`); - w.write(` (result ??= []).push(child);`); - w.write(` }`); - w.write(` next = child.next;`); - w.write(` }`); - w.write(` while (next);`); - w.write(` return result;`); - w.write(` }`); - w.write(``); - w.write(` getSourceFile(): SourceFile {`); - w.write(` return this.sourceFile as unknown as SourceFile;`); - w.write(` }`); - w.write(``); - w.write(` getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number {`); - w.write(` return getTokenPosOfNode(this as unknown as Node, sourceFile ?? this.getSourceFile(), includeJsDocComment);`); - w.write(` }`); - w.write(``); - w.write(` getFullStart(): number {`); - w.write(` return this.pos;`); - w.write(` }`); - w.write(``); - w.write(` getEnd(): number {`); - w.write(` return this.end;`); - w.write(` }`); - w.write(``); - w.write(` getWidth(sourceFile?: SourceFile): number {`); - w.write(` return this.getEnd() - this.getStart(sourceFile);`); - w.write(` }`); - w.write(``); - w.write(` getFullWidth(): number {`); - w.write(` return this.end - this.pos;`); - w.write(` }`); - w.write(``); - w.write(` getLeadingTriviaWidth(sourceFile?: SourceFile): number {`); - w.write(` return this.getStart(sourceFile) - this.pos;`); - w.write(` }`); - w.write(``); - w.write(` getFullText(sourceFile?: SourceFile): string {`); - w.write(` return (sourceFile ?? this.getSourceFile()).text.substring(this.pos, this.end);`); - w.write(` }`); - w.write(``); - w.write(` getText(sourceFile?: SourceFile): string {`); - w.write(` sourceFile ??= this.getSourceFile();`); - w.write(` return sourceFile.text.substring(this.getStart(sourceFile), this.end);`); - w.write(` }`); - w.write(``); - w.write(` protected getString(index: number): string {`); - w.write(` const offsetStringTableOffsets = this.sourceFile._offsetStringTableOffsets;`); - w.write(` const start = this.view.getUint32(offsetStringTableOffsets + index * 4, true);`); - w.write(` const end = this.view.getUint32(offsetStringTableOffsets + (index + 1) * 4, true);`); - w.write(` const offsetStringTable = this.sourceFile._offsetStringTable;`); - w.write(` const text = new Uint8Array(this.view.buffer, this.view.byteOffset + offsetStringTable + start, end - start);`); - w.write(` return this.sourceFile._decoder.decode(text);`); - w.write(` }`); - w.write(``); - w.write(` private getOrCreateChildAtNodeIndex(index: number): RemoteNode | RemoteNodeList {`); - w.write(` let child = this.sourceFile.nodes[index];`); - w.write(` if (!child) {`); - w.write(` const sf = this.sourceFile;`); - w.write(` const offsetNodes = sf._offsetNodes;`); - w.write(` const kind = this.view.getUint32(offsetNodes + index * NODE_LEN + NODE_OFFSET_KIND, true);`); - w.write(` child = kind === KIND_NODE_LIST`); - w.write(` ? new RemoteNodeList(this.view, index, this, sf, offsetNodes)`); - w.write(` : new RemoteNode(this.view, index, this, sf, offsetNodes);`); - w.write(` sf.nodes[index] = child;`); - w.write(` sf._timing?.recordMaterialization();`); - w.write(` }`); - w.write(` return child;`); - w.write(` }`); - w.write(``); - w.write(` private hasChildren(): boolean {`); - w.write(` if (this._byteIndex >= this.view.byteLength - NODE_LEN) {`); - w.write(` return false;`); - w.write(` }`); - w.write(` const nextNodeParent = this.view.getUint32(this.sourceFile._offsetNodes + (this.index + 1) * NODE_LEN + NODE_OFFSET_PARENT, true);`); - w.write(` return nextNodeParent === this.index;`); - w.write(` }`); - w.write(``); - w.write(` private getNamedChild(propertyName: string): RemoteNode | RemoteNodeList | undefined {`); - w.write(` const kind = this.kind;`); - w.write(` const propertyNames = childProperties[kind];`); - w.write(` if (!propertyNames) {`); - w.write(` return undefined;`); - w.write(` }`); - w.write(``); - w.write(` const order = propertyNames.indexOf(propertyName);`); - w.write(` if (order === -1) {`); - w.write(` return undefined;`); - w.write(` }`); - w.write(` return this.getChildAtOrder(order);`); - w.write(` }`); - w.write(``); - w.write(` private getChildAtOrder(order: number): RemoteNode | RemoteNodeList | undefined {`); - w.write(` if (!this.hasChildren()) return undefined;`); - w.write(` const mask = this.childMask;`); - w.write(` if (!(mask & (1 << order))) {`); - w.write(` // Property is not present`); - w.write(` return undefined;`); - w.write(` }`); - w.write(``); - w.write(` // The property index is \`order\`, minus the number of zeros in the mask that are in bit positions less`); - w.write(` // than the \`order\`th bit. Example:`); - w.write(` //`); - w.write(` // This is a MethodDeclaration with mask 0b01110101. The possible properties are`); - w.write(` // ["modifiers", "asteriskToken", "name", "postfixToken", "typeParameters", "parameters", "type", "body"]`); - w.write(` // (it has modifiers, name, typeParameters, parameters, and type).`); - w.write(` //`); - w.write(` // | Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |`); - w.write(` // | ----- | ---- | ---- | ---------- | -------------- | ------------ | ---- | ------------- | --------- |`); - w.write(` // | Value | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 1 |`); - w.write(` // | Name | body | type | parameters | typeParameters | postfixToken | name | asteriskToken | modifiers |`); - w.write(` //`); - w.write(` // We are trying to get the index of "parameters" (bit = 5).`); - w.write(` // First, set all the more significant bits to 1:`); - w.write(` //`); - w.write(` // | Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |`); - w.write(` // | ----- | ---- | ---- | ---------- | -------------- | ------------ | ---- | ------------- | --------- |`); - w.write(` // | Value | 1 | 1 | 1 | 1 | 0 | 1 | 0 | 1 |`); - w.write(` //`); - w.write(` // Then, flip the bits:`); - w.write(` //`); - w.write(` // | Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |`); - w.write(` // | ----- | ---- | ---- | ---------- | -------------- | ------------ | ---- | ------------- | --------- |`); - w.write(` // | Value | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |`); - w.write(` //`); - w.write(` // Counting the 1s gives us the number of *missing properties* before the \`order\`th property. If every property`); - w.write(` // were present, we would have \`parameters = children[5]\`, but since \`postfixToken\` and \`astersiskToken\` are`); - w.write(` // missing, we have \`parameters = children[5 - 2]\`.`); - w.write(` const propertyIndex = order - popcount8[~(mask | ((0xff << order) & 0xff)) & 0xff];`); - w.write(` let childIndex = this.index + 1;`); - w.write(` for (let i = 0; i < propertyIndex; i++) {`); - w.write(` // Walk through children via their \`next\` pointer until we get to the right property index`); - w.write(` childIndex = this.view.getUint32(this.sourceFile._offsetNodes + childIndex * NODE_LEN + NODE_OFFSET_NEXT, true);`); - w.write(` }`); - w.write(` return this.getOrCreateChildAtNodeIndex(childIndex);`); - w.write(` }`); - w.write(``); - w.write(` __print(): string {`); - w.write(` const result = [];`); - w.write(` result.push(\`index: \${this.index}\`);`); - w.write(` result.push(\`byteIndex: \${this._byteIndex}\`);`); - w.write(` result.push(\`kind: \${SyntaxKind[this.kind]}\`);`); - w.write(` result.push(\`pos: \${this.pos}\`);`); - w.write(` result.push(\`end: \${this.end}\`);`); - w.write(` result.push(\`next: \${this.next}\`);`); - w.write(` result.push(\`parent: \${this.parentIndex}\`);`); - w.write(` result.push(\`data: \${this.data.toString(2).padStart(32, "0")}\`);`); - w.write(` const dataType = this.dataType === NODE_DATA_TYPE_CHILDREN ? "children" :`); - w.write(` this.dataType === NODE_DATA_TYPE_STRING ? "string" :`); - w.write(` "extended";`); - w.write(` result.push(\`dataType: \${dataType}\`);`); - w.write(` if (this.dataType === NODE_DATA_TYPE_CHILDREN) {`); - w.write(` result.push(\`childMask: \${this.childMask.toString(2).padStart(8, "0")}\`);`); - w.write(` result.push(\`childProperties: \${childProperties[this.kind]?.join(", ")}\`);`); - w.write(` }`); - w.write(` return result.join("\\n");`); - w.write(` }`); - w.write(``); - w.write(` __printChildren(): string {`); - w.write(` const result = [];`); - w.write(` let next = this.index + 1;`); - w.write(` while (next) {`); - w.write(` const child = this.getOrCreateChildAtNodeIndex(next);`); - w.write(` next = child.next;`); - w.write(` result.push(child.__print());`); - w.write(` }`); - w.write(` return result.join("\\n\\n");`); - w.write(` }`); - w.write(``); - w.write(` __printSubtree(): string {`); - w.write(` const result = [this.__print()];`); - w.write(` this.forEachChild(function visitNode(node) {`); - w.write(` result.push((node as RemoteNode).__print());`); - w.write(` node.forEachChild(visitNode);`); - w.write(` }, visitList => {`); - w.write(` result.push((visitList as RemoteNodeList).__print());`); - w.write(` });`); - w.write(` return result.join("\\n\\n");`); - w.write(` }`); -} - -function emitTemplateFlagsGetter(w: CodeWriter) { - // templateFlags — from extended data (not a commonData union) - w.write(` get templateFlags(): number | undefined {`); - w.write(` switch (this.kind) {`); - w.write(` case SyntaxKind.TemplateHead:`); - w.write(` case SyntaxKind.TemplateMiddle:`); - w.write(` case SyntaxKind.TemplateTail:`); - w.write(` const extendedDataOffset = this.sourceFile._offsetExtendedData + (this.data & NODE_EXTENDED_DATA_MASK);`); - w.write(` return this.view.getUint32(extendedDataOffset + 8, true);`); - w.write(` }`); - w.write(` }`); -} - -function emitTokenFlagsGetter(w: CodeWriter) { - // tokenFlags — from extended data for literal kinds that store TokenFlags - w.write(` get tokenFlags(): number {`); - w.write(` switch (this.kind) {`); - w.write(` case SyntaxKind.StringLiteral:`); - w.write(` case SyntaxKind.NumericLiteral:`); - w.write(` case SyntaxKind.BigIntLiteral:`); - w.write(` case SyntaxKind.RegularExpressionLiteral:`); - w.write(` const extendedDataOffset = this.sourceFile._offsetExtendedData + (this.data & NODE_EXTENDED_DATA_MASK);`); - w.write(` return this.view.getUint32(extendedDataOffset + 4, true);`); - w.write(` default:`); - w.write(` return 0;`); - w.write(` }`); - w.write(` }`); -} - -function emitStringGetters(w: CodeWriter, stringTextKinds: string[], extendedTextKinds: string[]) { - // text getter — string data kinds vs extended data kinds - w.write(` get text(): string | undefined {`); - w.write(` switch (this.kind) {`); - for (const k of stringTextKinds) { - w.write(` case SyntaxKind.${k}:`); - } - w.write(` {`); - w.write(` const stringIndex = this.data & NODE_STRING_INDEX_MASK;`); - w.write(` return this.getString(stringIndex);`); - w.write(` }`); - for (const k of extendedTextKinds) { - w.write(` case SyntaxKind.${k}:`); - } - w.write(` {`); - w.write(` const extendedDataOffset = this.sourceFile._offsetExtendedData + (this.data & NODE_EXTENDED_DATA_MASK);`); - w.write(` const stringIndex = this.view.getUint32(extendedDataOffset, true);`); - w.write(` return this.getString(stringIndex);`); - w.write(` }`); - w.write(` }`); - w.write(` }`); - w.write(``); - // rawText — template kinds only - w.write(` get rawText(): string | undefined {`); - w.write(` switch (this.kind) {`); - w.write(` case SyntaxKind.TemplateHead:`); - w.write(` case SyntaxKind.TemplateMiddle:`); - w.write(` case SyntaxKind.TemplateTail:`); - w.write(` const extendedDataOffset = this.sourceFile._offsetExtendedData + (this.data & NODE_EXTENDED_DATA_MASK);`); - w.write(` const stringIndex = this.view.getUint32(extendedDataOffset + 4, true);`); - w.write(` return this.getString(stringIndex);`); - w.write(` }`); - w.write(` }`); -} - -function emitExtendedDataGetters(w: CodeWriter) { - // SourceFile-specific extended data getters live on RemoteSourceFile in node.ts. - // Only non-SourceFile extended data getters are emitted here. -} - -function emitRemoteNodeClassClose(w: CodeWriter) { - w.write(` // ═══ Other property getters ═══`); - w.write(``); - w.write(` get flags(): number {`); - w.write(` return this.view.getUint32(this._byteIndex + NODE_OFFSET_FLAGS, true);`); - w.write(` }`); - w.write(``); - w.write(` get modifierFlags(): ModifierFlags {`); - w.write(` const mods = this.modifiers;`); - w.write(` if (!mods) return ModifierFlags.None;`); - w.write(` let flags: ModifierFlags = ModifierFlags.None;`); - w.write(` for (const mod of mods) {`); - w.write(` flags |= modifierToFlag(mod.kind);`); - w.write(` }`); - w.write(` return flags;`); - w.write(` }`); - w.write(`}`); -} - -// ═══════════════════════════════════════════════════════════════════════════ -// Main: generate and write all files -// ═══════════════════════════════════════════════════════════════════════════ - -function writeAndFormat(filePath: string, content: string, formatter: string) { - fs.writeFileSync(filePath, content); - try { - const [cmd, ...args] = formatter.split(" "); - execaSync(cmd, [...args, filePath], { stdio: "inherit", cwd: ROOT }); - } - catch { - console.warn(`Warning: formatter failed for ${filePath}`); - } - console.log(`Wrote ${filePath}`); -} - -export default function main() { - console.log("Generating encoder/decoder code..."); - - const goEncoder = generateGoEncoder(); - writeAndFormat( - path.join(ROOT, "tsc/internal/api/encoder/encoder_generated.go"), - goEncoder + "\n", - "dprint fmt", - ); - - const goDecoder = generateGoDecoder(); - writeAndFormat( - path.join(ROOT, "tsc/internal/api/encoder/decoder_generated.go"), - goDecoder + "\n", - "dprint fmt", - ); - - const tsProtocol = generateTSProtocol(); - writeAndFormat( - path.join(ROOT, "packages/typescript/src/api/node/protocol.generated.ts"), - tsProtocol + "\n", - "dprint fmt", - ); - - const tsEncoder = generateTSEncoder(); - writeAndFormat( - path.join(ROOT, "packages/typescript/src/api/node/encoder.generated.ts"), - tsEncoder + "\n", - "dprint fmt", - ); - - const tsNode = generateTSNodeGenerated(); - writeAndFormat( - path.join(ROOT, "packages/typescript/src/api/node/node.generated.ts"), - tsNode + "\n", - "dprint fmt", - ); - - console.log("Done!"); -} - -if (process.argv[1] === fileURLToPath(import.meta.url)) { - main(); -} +/** + * Encoder/decoder code generator: reads tools/scripts/tsc/ast.json and produces binary + * encoding/decoding code for Go and TypeScript. + * + * Usage: node --experimental-strip-types tools/scripts/tsc/generate-encoder.ts + * + * Generates: + * - internal/api/encoder/encoder_generated.go + * - internal/api/encoder/decoder_generated.go + * - packages/typescript/src/api/node/protocol.generated.ts + */ + +import { execaSync } from "execa"; +import * as fs from "node:fs"; +import * as path from "node:path"; +import { fileURLToPath } from "node:url"; +import type { + KindType, + MemberInfo, + NodeType, +} from "./schema.ts"; +import { api } from "./schema.ts"; + +const ROOT = path.resolve(import.meta.dirname!, "../../.."); + +// ═══════════════════════════════════════════════════════════════════════════ +// Code writer +// ═══════════════════════════════════════════════════════════════════════════ + +class CodeWriter { + private lines: string[] = []; + private indent = 0; + private indentStr: string; + + constructor(indentStr = "\t") { + this.indentStr = indentStr; + } + + write(line = "") { + if (line === "") this.lines.push(""); + else this.lines.push(this.indentStr.repeat(this.indent) + line); + } + + push() { + this.indent++; + } + pop() { + this.indent--; + } + + toString(): string { + return this.lines.join("\n"); + } +} + +// ═══════════════════════════════════════════════════════════════════════════ +// Node classification +// ═══════════════════════════════════════════════════════════════════════════ + +/** Encoder-visible members: excludes noTS and noGo members. */ +function schemaMembers(node: NodeType): MemberInfo[] { + return node.members.filter(m => !m.noTS && !m.noGo); +} + +/** Whether a member is a child that should be encoded in the binary format. */ +function isEncodedChild(m: MemberInfo): boolean { + return m.isChild() && !m.noTS && !m.noGo; +} + +type ChildType = "node" | "nodeList" | "rawNodeList" | "modifierList"; + +/** Determine the child type for an encoded child member. */ +function childType(m: MemberInfo): ChildType { + if (m.listKind === "ModifierList") return "modifierList"; + if (m.listKind === "NodeList") return "nodeList"; + if (m.type.baseKind() === "list" && m.listKind === "raw") return "rawNodeList"; + return "node"; +} + +// ═══════════════════════════════════════════════════════════════════════════ +// Common data analysis +// ═══════════════════════════════════════════════════════════════════════════ + +/** + * Returns true if the member is a NodeFlags member. NodeFlags are already + * stored verbatim in every node's flags field, so they do not need to be + * redundantly encoded in the commonData bits. + */ +function isNodeFlagsMember(m: MemberInfo): boolean { + const dt = m.declaredType; + return (dt.kind === "primitive" && dt.name === "NodeFlags") || + (!!m.bitmask && m.bitmask.startsWith("NodeFlags")); +} + +/** Returns the bool members that can be auto-encoded in the commonData (1 bit each). */ +function getAutoBoolBits(node: NodeType): MemberInfo[] { + // If any non-auto-encodable data exists, all data encoding is hand-written (including bools). + if (needsHandWrittenCommonData(node)) return []; + const members = schemaMembers(node); + return members.filter(m => { + if (m.isChild() || m.isKindParam()) return false; + const dt = m.declaredType; + return dt.kind === "primitive" && dt.name === "bool"; + }); +} + +/** Returns the SyntaxKind union members that can be auto-encoded. */ +function getAutoUnionBits(node: NodeType): MemberInfo[] { + if (needsHandWrittenCommonData(node)) return []; + const members = schemaMembers(node); + return members.filter(m => { + if (m.isChild() || m.isKindParam()) return false; + return isSyntaxKindUnion(m); + }); +} + +/** Check if a member's type is a union of SyntaxKind values (auto-encodable). */ +function isSyntaxKindUnion(m: MemberInfo): boolean { + const dt = resolveUnion(m.declaredType); + if (!dt || dt.kind !== "union") return false; + return dt.types.every(t => t.kind === "kind"); +} + +/** Resolve through aliases to find an underlying union type, if any. */ +function resolveUnion(dt: import("./schema.ts").Type): import("./schema.ts").Type | undefined { + if (dt.kind === "union") return dt; + if (dt.kind === "alias") return resolveUnion(dt.resolved); + return undefined; +} + +/** Number of bits needed to encode a SyntaxKind union member. */ +function unionBitWidth(m: MemberInfo): number { + const dt = resolveUnion(m.declaredType); + if (!dt || dt.kind !== "union") return 0; + // Optional unions need an extra index for "absent" (index 0 = absent) + const count = m.optional ? dt.types.length + 1 : dt.types.length; + return Math.ceil(Math.log2(count)); +} + +/** + * Returns true if the node has non-bool, non-string data members whose encoding + * format cannot be automatically inferred. Such nodes delegate their entire + * commonData encoding/decoding to hand-written functions. + * + * SyntaxKind union members are auto-encodable and do NOT require hand-written functions. + */ +function needsHandWrittenCommonData(node: NodeType): boolean { + if (node.handWritten) return false; // extended data nodes handle everything + const members = schemaMembers(node); + return members.some(m => { + if (m.isChild() || m.isKindParam()) return false; + const dt = m.declaredType; + if (dt.kind === "primitive" && dt.name === "bool") return false; + if (dt.kind === "primitive" && dt.name === "string") return false; + if (isSyntaxKindUnion(m)) return false; + if (isNodeFlagsMember(m)) return false; + return true; + }); +} + +/** + * Returns all data members (non-child, non-kindParam, non-string) for a node + * that needs hand-written commonData. Includes bools, since the hand-written + * function controls the entire 6-bit layout. + */ +function getHandWrittenDataMembers(node: NodeType): MemberInfo[] { + const members = schemaMembers(node); + return members.filter(m => { + if (m.isChild() || m.isKindParam()) return false; + const dt = m.declaredType; + if (dt.kind === "primitive" && dt.name === "string") return false; + if (isNodeFlagsMember(m)) return false; + return true; + }); +} + +// ═══════════════════════════════════════════════════════════════════════════ +// Data type classification +// ═══════════════════════════════════════════════════════════════════════════ + +type DataType = "string" | "children" | "extended"; + +function classifyDataType(node: NodeType): DataType { + if (node.handWritten) return "extended"; + const members = schemaMembers(node); + const stringMembers = members.filter(m => + !m.isChild() && !m.isKindParam() && + m.declaredType.kind === "primitive" && m.declaredType.name === "string" + ); + if (stringMembers.length > 1) return "extended"; + if (stringMembers.length === 1) { + // If the node also has non-bool data members, TokenFlags etc. won't fit in + // 6 common data bits → must use extended data. + if (needsHandWrittenCommonData(node)) return "extended"; + return "string"; + } + return "children"; +} + +// ═══════════════════════════════════════════════════════════════════════════ +// Go factory call helpers +// ═══════════════════════════════════════════════════════════════════════════ + +/** Get all SyntaxKind Go constant names for a node (prefixed with `ast.`). */ +function getGoKinds(node: NodeType): string[] { + return node.allKinds().map(k => `ast.${k.formatGoConstant()}`); +} + +/** Get all SyntaxKind names for a node in TS (without prefix). */ +function getTSKinds(node: NodeType): string[] { + return node.allKinds().map(k => k.name); +} + +/** Get the Go factory method name (e.g., "NewIfStatement"). */ +function goFactoryName(node: NodeType): string { + return `New${node.name}`; +} + +/** Get the Go "As*" cast method name (e.g., "AsIfStatement"). */ +function goCastName(node: NodeType): string { + return `As${node.name}`; +} + +/** Get the Go param name, avoiding Go keywords. */ +function goParamName(m: MemberInfo): string { + return m.goParamName(); +} + +/** Get the Go accessor for a member on a cast struct (e.g., "n.IsTypeOnly", "n.Name()"). */ +function goFieldAccess(m: MemberInfo): string { + if (m.private) return `n.${api.capitalize(m.name)}()`; + return `n.${m.name}`; +} + +/** Get the default Go value for a non-encoded factory parameter. */ +function goDefaultValue(m: MemberInfo): string { + if (m.isChild()) return "nil"; + const dt = m.declaredType; + if (dt.kind === "primitive") { + switch (dt.name) { + case "bool": + return "false"; + case "int": + return "0"; + case "string": + return `""`; + case "NodeFlags": + return "0"; + case "TokenFlags": + return "0"; + case "ModifierFlags": + return "0"; + } + } + if (dt.baseKind() === "kind") return "0"; + return "nil"; +} + +// ═══════════════════════════════════════════════════════════════════════════ +// Encoder node info (per-node analysis) +// ═══════════════════════════════════════════════════════════════════════════ + +interface EncoderNodeInfo { + node: NodeType; + dataType: DataType; + /** Encoded child members in visitor order. */ + childProps: MemberInfo[]; + /** Bool members auto-encoded in commonData (empty if hand-written). */ + autoBoolBits: MemberInfo[]; + /** SyntaxKind union members auto-encoded in commonData. */ + autoUnionBits: MemberInfo[]; + /** Whether commonData encoding is delegated to a hand-written function. */ + handWrittenCommonData: boolean; + /** The text member for string nodes. */ + textMember?: MemberInfo; + /** Whether this node has a Kind parameter in the factory. */ + kindMember?: MemberInfo; + /** All factory parameters, including non-encoded ones. */ + factoryMembers: MemberInfo[]; +} + +function analyzeNode(node: NodeType): EncoderNodeInfo { + const dataType = classifyDataType(node); + const members = schemaMembers(node); + const childProps = members.filter(m => isEncodedChild(m)); + const handWrittenCD = needsHandWrittenCommonData(node); + const autoBoolBits = getAutoBoolBits(node); + const autoUnionBits = getAutoUnionBits(node); + const kindMember = members.find(m => m.isKindParam()); + const textMember = dataType === "string" + ? members.find(m => + !m.isChild() && !m.isKindParam() && + m.declaredType.kind === "primitive" && m.declaredType.name === "string" + ) + : undefined; + // Factory members include all non-noFactory members (even noTS ones), + // since the factory signature is determined by generate-go-ast.ts. + const factoryMembers = node.members.filter(m => !m.noFactory); + + return { + node, + dataType, + childProps, + autoBoolBits, + autoUnionBits, + handWrittenCommonData: handWrittenCD, + textMember, + kindMember, + factoryMembers, + }; +} + +/** + * Compute the bit layout for all auto-encoded data members of a node. + * Bools get 1 bit each, SyntaxKind unions get ceil(log2(N)) bits. + * Returns entries with the bit position (relative to bit 24) and width. + */ +function getAutoEncodedLayout(info: EncoderNodeInfo): { member: MemberInfo; bitPos: number; bitWidth: number; }[] { + const layout: { member: MemberInfo; bitPos: number; bitWidth: number; }[] = []; + let bitPos = 0; + for (const m of info.autoBoolBits) { + layout.push({ member: m, bitPos, bitWidth: 1 }); + bitPos++; + } + for (const m of info.autoUnionBits) { + const width = unionBitWidth(m); + layout.push({ member: m, bitPos, bitWidth: width }); + bitPos += width; + } + return layout; +} + +/** Check if a node has any auto-encoded data members (bools or unions). */ +function hasAutoEncodedData(info: EncoderNodeInfo): boolean { + return info.autoBoolBits.length > 0 || info.autoUnionBits.length > 0; +} + +// ═══════════════════════════════════════════════════════════════════════════ +// Go Encoder Generation +// ═══════════════════════════════════════════════════════════════════════════ + +function generateGoEncoder(): string { + const w = new CodeWriter(); + w.write("// Code generated by tools/scripts/tsc/generate-encoder.ts. DO NOT EDIT."); + w.write(""); + w.write("package encoder"); + w.write(""); + w.write("import ("); + w.push(); + w.write('"fmt"'); + w.write(""); + w.write('"github.com/microsoft/TypeScript/tsc/internal/ast"'); + w.pop(); + w.write(")"); + w.write(""); + + generateGoGetNodeDataType(w); + generateGoGetChildrenPropertyMask(w); + generateGoGetNodeCommonData(w); + generateGoRecordNodeStrings(w); + generateGoRecordExtendedData(w); + + return w.toString(); +} + +function generateGoGetNodeDataType(w: CodeWriter) { + w.write("func getNodeDataType(node *ast.Node) uint32 {"); + w.push(); + w.write("switch node.Kind {"); + + const stringKinds: string[] = []; + const extendedKinds: string[] = []; + + for (const node of api.nodes()) { + const info = analyzeNode(node); + const kinds = getGoKinds(node); + + if (info.dataType === "string") { + stringKinds.push(...kinds); + } + else if (info.dataType === "extended") { + extendedKinds.push(...kinds); + } + } + + if (stringKinds.length > 0) { + w.write(`case ${stringKinds.join(",\n\t\t")}:`); + w.push(); + w.write("return NodeDataTypeString"); + w.pop(); + } + if (extendedKinds.length > 0) { + w.write(`case ${extendedKinds.join(",\n\t\t")}:`); + w.push(); + w.write("return NodeDataTypeExtendedData"); + w.pop(); + } + + w.write("default:"); + w.push(); + w.write("return NodeDataTypeChildren"); + w.pop(); + w.write("}"); + w.pop(); + w.write("}"); + w.write(""); +} + +function generateGoGetChildrenPropertyMask(w: CodeWriter) { + w.write("func getChildrenPropertyMask(node *ast.Node) uint8 {"); + w.push(); + w.write("switch node.Kind {"); + + for (const node of api.nodes()) { + const info = analyzeNode(node); + if (info.dataType === "extended") continue; + if (info.childProps.length === 0) continue; + + const kinds = getGoKinds(node); + const castName = goCastName(node); + + w.write(`case ${kinds.join(", ")}:`); + w.push(); + w.write(`n := node.${castName}()`); + + const parts: string[] = []; + for (let i = 0; i < info.childProps.length; i++) { + const m = info.childProps[i]; + const ct = childType(m); + let check: string; + if (ct === "modifierList") { + check = `hasModifiers(n.Modifiers())`; + } + else if (ct === "nodeList") { + check = `${goFieldAccess(m)} != nil`; + } + else if (ct === "rawNodeList") { + check = `len(${goFieldAccess(m)}) > 0`; + } + else { + check = `${goFieldAccess(m)} != nil`; + } + parts.push(`(boolToByte(${check}) << ${i})`); + } + w.write(`return ${parts.join(" | ")}`); + w.pop(); + } + + w.write("default:"); + w.push(); + w.write("return 0"); + w.pop(); + w.write("}"); + w.pop(); + w.write("}"); + w.write(""); +} + +function generateGoGetNodeCommonData(w: CodeWriter) { + w.write("func getNodeCommonData(node *ast.Node) uint32 {"); + w.push(); + w.write("switch node.Kind {"); + + for (const node of api.nodes()) { + const info = analyzeNode(node); + if (info.dataType === "extended") continue; + + if (info.handWrittenCommonData) { + // Delegate to hand-written function for all non-bool data members. + const kinds = getGoKinds(node); + w.write(`case ${kinds.join(", ")}:`); + w.push(); + w.write(`return getNodeCommonData_${node.name}(node)`); + w.pop(); + } + else if (hasAutoEncodedData(info)) { + const kinds = getGoKinds(node); + const castName = goCastName(node); + const layout = getAutoEncodedLayout(info); + + w.write(`case ${kinds.join(", ")}:`); + w.push(); + w.write(`n := node.${castName}()`); + + const parts: string[] = []; + for (const { member: m, bitPos, bitWidth } of layout) { + if (!isSyntaxKindUnion(m)) { + // Bool or bitmask + parts.push(`uint32(boolToByte(${goFieldAccess(m)})) << ${24 + bitPos}`); + } + else { + // SyntaxKind union → index-based encoding + const kinds = unionKindValues(m.declaredType); + const varName = `${goParamName(m)}Idx`; + w.write(`var ${varName} uint32`); + w.write(`switch ${goFieldAccess(m)} {`); + if (m.optional) { + // Optional: 0 = absent, 1..N = values[0]..values[N-1] + for (let i = 0; i < kinds.length; i++) { + w.write(`case ast.${kinds[i].formatGoConstant()}: ${varName} = ${i + 1}`); + } + } + else { + // Non-optional: 0 = values[0] (default), 1..N-1 = values[1]..values[N-1] + for (let i = 1; i < kinds.length; i++) { + w.write(`case ast.${kinds[i].formatGoConstant()}: ${varName} = ${i}`); + } + } + w.write("}"); + parts.push(`${varName} << ${24 + bitPos}`); + } + } + w.write(`return ${parts.join(" | ")}`); + w.pop(); + } + } + + w.write("}"); + w.write("return 0"); + w.pop(); + w.write("}"); + w.write(""); +} + +function generateGoRecordNodeStrings(w: CodeWriter) { + w.write("func recordNodeStrings(node *ast.Node, strs *stringTable) uint32 {"); + w.push(); + w.write("switch node.Kind {"); + + for (const node of api.nodes()) { + const info = analyzeNode(node); + if (info.dataType !== "string" || !info.textMember) continue; + + const kinds = getGoKinds(node); + const castName = goCastName(node); + const textAccess = info.textMember.private + ? `node.${castName}().${api.capitalize(info.textMember.name)}()` + : `node.${castName}().${info.textMember.name}`; + + w.write(`case ${kinds.join(", ")}:`); + w.push(); + w.write(`return strs.add(${textAccess}, node.Kind, node.Pos(), node.End())`); + w.pop(); + } + + w.write("default:"); + w.push(); + w.write(`panic(fmt.Sprintf("Unexpected node kind %v", node.Kind))`); + w.pop(); + w.write("}"); + w.pop(); + w.write("}"); + w.write(""); +} + +function generateGoRecordExtendedData(w: CodeWriter) { + w.write("func recordExtendedData(node *ast.Node, strs *stringTable, positionMap *ast.PositionMap, extendedData *[]byte, structuredData *[]byte) uint32 {"); + w.push(); + w.write("offset := uint32(len(*extendedData))"); + w.write("switch node.Kind {"); + + for (const node of api.nodes()) { + const info = analyzeNode(node); + if (info.dataType !== "extended") continue; + + const kinds = getGoKinds(node); + + w.write(`case ${kinds.join(", ")}:`); + w.push(); + w.write(`recordExtendedData_${node.name}(node, strs, positionMap, extendedData, structuredData)`); + w.pop(); + } + + w.write("default:"); + w.push(); + w.write(`panic(fmt.Sprintf("unknown extended data node kind %v", node.Kind))`); + w.pop(); + w.write("}"); + w.write("return offset"); + w.pop(); + w.write("}"); + w.write(""); +} + +// ═══════════════════════════════════════════════════════════════════════════ +// Go Decoder Generation +// ═══════════════════════════════════════════════════════════════════════════ + +function generateGoDecoder(): string { + const w = new CodeWriter(); + w.write("// Code generated by tools/scripts/tsc/generate-encoder.ts. DO NOT EDIT."); + w.write(""); + w.write("package encoder"); + w.write(""); + w.write("import ("); + w.push(); + w.write('"fmt"'); + w.write(""); + w.write('"github.com/microsoft/TypeScript/tsc/internal/ast"'); + w.pop(); + w.write(")"); + w.write(""); + + generateGoCreateStringNode(w); + generateGoCreateExtendedNode(w); + generateGoCreateChildrenNode(w); + + return w.toString(); +} + +function generateGoCreateStringNode(w: CodeWriter) { + w.write("func (d *astDecoder) createStringNode(kind ast.Kind, data uint32, commonData uint8) (*ast.Node, error) {"); + w.push(); + w.write("strIdx := data & NodeDataStringIndexMask"); + w.write("text := d.getString(strIdx)"); + w.write(""); + w.write("switch kind {"); + + for (const node of api.nodes()) { + const info = analyzeNode(node); + if (info.dataType !== "string") continue; + + const kinds = getGoKinds(node); + const factoryName = goFactoryName(node); + + w.write(`case ${kinds.join(", ")}:`); + w.push(); + + // Decode commonData + const commonVars = emitCommonDataDecode(w, node, info); + + // Build factory args + const args: string[] = []; + for (const m of info.factoryMembers) { + if (m.isKindParam()) { + args.push("kind"); + } + else if (m === info.textMember) { + if (m.listKind === "raw") { + args.push("[]string{text}"); + } + else { + args.push("text"); + } + } + else if (isEncodedChild(m)) { + args.push("nil"); + } + else { + const v = commonVars.get(m); + if (v) { + args.push(v); + } + else { + args.push(goDefaultValue(m)); + } + } + } + + w.write(`return d.factory.${factoryName}(${args.join(", ")}), nil`); + w.pop(); + } + + w.write("default:"); + w.push(); + w.write(`return nil, fmt.Errorf("unknown string node kind %v", kind)`); + w.pop(); + w.write("}"); + w.pop(); + w.write("}"); + w.write(""); +} + +function generateGoCreateExtendedNode(w: CodeWriter) { + w.write("func (d *astDecoder) createExtendedNode(kind ast.Kind, data uint32, childIndices []int, commonData uint8) (*ast.Node, error) {"); + w.push(); + w.write("switch kind {"); + + for (const node of api.nodes()) { + const info = analyzeNode(node); + if (info.dataType !== "extended") continue; + + const kinds = getGoKinds(node); + + w.write(`case ${kinds.join(", ")}:`); + w.push(); + // Extended data nodes call hand-written decode functions + w.write(`return d.decodeExtendedData_${node.name}(data, childIndices, commonData)`); + w.pop(); + } + + w.write("default:"); + w.push(); + w.write(`return nil, fmt.Errorf("unknown extended data node kind %v", kind)`); + w.pop(); + w.write("}"); + w.pop(); + w.write("}"); + w.write(""); +} + +function generateGoCreateChildrenNode(w: CodeWriter) { + w.write("func (d *astDecoder) createChildrenNode(kind ast.Kind, data uint32, childIndices []int, commonData uint8) (*ast.Node, error) {"); + w.push(); + w.write("mask := uint8(data & NodeDataChildMask)"); + w.write(""); + w.write("switch kind {"); + + // Build a set of all kinds claimed by specific nodes, so that the most + // specific node (fewest kinds) takes priority for shared kinds. + const kindOwner = new Map(); + for (const node of api.nodes()) { + const info = analyzeNode(node); + if (info.dataType !== "children") continue; + const kinds = getGoKinds(node); + for (const k of kinds) { + const existing = kindOwner.get(k); + if (!existing || kinds.length < existing.totalKinds) { + kindOwner.set(k, { node, info, totalKinds: kinds.length }); + } + } + } + + // Group kinds by their owning node (deconflicted). + const nodeKinds = new Map(); + for (const [kind, owner] of kindOwner) { + const key = owner.node.name; + if (!nodeKinds.has(key)) nodeKinds.set(key, []); + nodeKinds.get(key)!.push(kind); + } + + for (const node of api.nodes()) { + const info = analyzeNode(node); + if (info.dataType !== "children") continue; + + const kinds = nodeKinds.get(node.name); + if (!kinds || kinds.length === 0) continue; + + if (info.childProps.length === 0) { + emitZeroChildDecoder(w, node, info, kinds); + } + else if (info.childProps.length === 1) { + emitSingleChildDecoder(w, node, info, kinds); + } + else { + emitMultiChildDecoder(w, node, info, kinds); + } + } + + // Default: error for unhandled kinds + w.write("default:"); + w.push(); + w.write(`return nil, fmt.Errorf("unhandled node kind %v with %d children", kind, len(childIndices))`); + w.pop(); + + w.write("}"); + w.pop(); + w.write("}"); + w.write(""); +} + +// ═══════════════════════════════════════════════════════════════════════════ +// Decoder helpers +// ═══════════════════════════════════════════════════════════════════════════ + +/** + * Emits commonData decoding and returns a map of member → Go variable name. + * For auto-bool nodes: decodes bools from bit positions. + * For hand-written nodes: calls decodeNodeCommonData_(commonData). + */ +function emitCommonDataDecode(w: CodeWriter, node: NodeType, info: EncoderNodeInfo): Map { + const vars = new Map(); + + if (info.handWrittenCommonData) { + const dataMembers = getHandWrittenDataMembers(node); + if (dataMembers.length === 0) return vars; + + const varNames = dataMembers.map(m => goParamName(m)); + if (dataMembers.length === 1) { + w.write(`${varNames[0]} := decodeNodeCommonData_${node.name}(commonData)`); + } + else { + w.write(`${varNames.join(", ")} := decodeNodeCommonData_${node.name}(commonData)`); + } + for (let i = 0; i < dataMembers.length; i++) { + vars.set(dataMembers[i], varNames[i]); + } + } + else { + const layout = getAutoEncodedLayout(info); + for (const { member: m, bitPos, bitWidth } of layout) { + const varName = goParamName(m); + if (!isSyntaxKindUnion(m)) { + // Bool or bitmask + w.write(`${varName} := commonData&${1 << bitPos} != 0`); + vars.set(m, varName); + } + else { + // SyntaxKind union → index-based decoding + const kinds = unionKindValues(m.declaredType); + const mask = (1 << bitWidth) - 1; + const indexExpr = bitPos === 0 + ? `commonData & ${mask}` + : `(commonData >> ${bitPos}) & ${mask}`; + if (m.optional) { + // Optional: 0 = absent (KindUnknown), 1..N = values[0]..values[N-1] + w.write(`var ${varName} ast.Kind`); + w.write(`switch ${indexExpr} {`); + for (let i = 0; i < kinds.length; i++) { + w.write(`case ${i + 1}: ${varName} = ast.${kinds[i].formatGoConstant()}`); + } + w.write("}"); + } + else if (kinds.length === 2) { + // Non-optional 2-value: simple if + w.write(`${varName} := ast.${kinds[0].formatGoConstant()}`); + w.write(`if ${indexExpr} != 0 {`); + w.push(); + w.write(`${varName} = ast.${kinds[1].formatGoConstant()}`); + w.pop(); + w.write("}"); + } + else { + // Non-optional N-value: switch + w.write(`var ${varName} ast.Kind`); + w.write(`switch ${indexExpr} {`); + for (let i = 0; i < kinds.length; i++) { + w.write(`case ${i}: ${varName} = ast.${kinds[i].formatGoConstant()}`); + } + w.write("}"); + } + vars.set(m, varName); + } + } + } + + return vars; +} + +function emitZeroChildDecoder(w: CodeWriter, node: NodeType, info: EncoderNodeInfo, kinds: string[]) { + const factoryName = goFactoryName(node); + + w.write(`case ${kinds.join(",\n\t\t")}:`); + w.push(); + + const commonVars = emitCommonDataDecode(w, node, info); + + const args: string[] = []; + for (const m of info.factoryMembers) { + if (m.isKindParam()) { + args.push("kind"); + continue; + } + const v = commonVars.get(m); + if (v) { + args.push(v); + continue; + } + args.push(goDefaultValue(m)); + } + + w.write(`return d.factory.${factoryName}(${args.join(", ")}), nil`); + w.pop(); +} + +function emitSingleChildDecoder(w: CodeWriter, node: NodeType, info: EncoderNodeInfo, kinds: string[]) { + const cp = info.childProps[0]; + const ct = childType(cp); + const factoryName = goFactoryName(node); + const hasCommonData = info.autoBoolBits.length > 0 || info.handWrittenCommonData; + + w.write(`case ${kinds.join(", ")}:`); + w.push(); + + // Decode commonData if needed + const commonVars = emitCommonDataDecode(w, node, info); + + // Build factory args + const args: string[] = []; + for (const m of info.factoryMembers) { + if (m.isKindParam()) { + args.push("kind"); + } + else if (m === cp) { + if (ct === "rawNodeList") { + w.write(`nodes := d.allocNodeSlice(len(childIndices))`); + w.write("for i, ci := range childIndices {"); + w.push(); + w.write("nodes[i] = d.nodes[ci]"); + w.pop(); + w.write("}"); + args.push("nodes"); + } + else if (ct === "nodeList") { + if (hasCommonData) { + w.write("var list *ast.NodeList"); + w.write("if len(childIndices) > 0 {"); + w.push(); + w.write("list = d.nodeListAt(childIndices[0])"); + w.pop(); + w.write("}"); + args.push("list"); + } + else { + args.push("d.singleNodeListChild(childIndices)"); + } + } + else if (ct === "modifierList") { + w.write("var mods *ast.ModifierList"); + w.write("if len(childIndices) > 0 {"); + w.push(); + w.write("mods = d.modifierListAt(childIndices[0])"); + w.pop(); + w.write("}"); + args.push("mods"); + } + else { + args.push("d.singleChild(childIndices)"); + } + } + else { + const cdVar = commonVars.get(m); + if (cdVar) { + args.push(cdVar); + } + else { + args.push(goDefaultValue(m)); + } + } + } + + w.write(`return d.factory.${factoryName}(${args.join(", ")}), nil`); + w.pop(); +} + +function emitMultiChildDecoder(w: CodeWriter, node: NodeType, info: EncoderNodeInfo, kinds: string[]) { + const factoryName = goFactoryName(node); + + w.write(`case ${kinds.join(", ")}:`); + w.push(); + + // Decode commonData + const commonVars = emitCommonDataDecode(w, node, info); + + // Child iterator + w.write("it := newChildIter(childIndices)"); + + // Decode each child from mask + const childVars = new Map(); + for (let i = 0; i < info.childProps.length; i++) { + const m = info.childProps[i]; + const ct = childType(m); + const varName = goParamName(m); + let decode: string; + if (ct === "modifierList") { + decode = `d.modifierListAt(it.nextIf(mask, ${i}))`; + } + else if (ct === "rawNodeList") { + const nlVar = `${varName}NL`; + w.write(`${nlVar} := d.nodeListAt(it.nextIf(mask, ${i}))`); + w.write(`var ${varName} []*ast.Node`); + w.write(`if ${nlVar} != nil {`); + w.push(); + w.write(`${varName} = ${nlVar}.Nodes`); + w.pop(); + w.write("}"); + childVars.set(m, varName); + continue; + } + else if (ct === "nodeList") { + decode = `d.nodeListAt(it.nextIf(mask, ${i}))`; + } + else { + decode = `d.nodeAt(it.nextIf(mask, ${i}))`; + } + w.write(`${varName} := ${decode}`); + childVars.set(m, varName); + } + + // Build factory args + const args: string[] = []; + for (const m of info.factoryMembers) { + if (m.isKindParam()) { + args.push("kind"); + continue; + } + const childVar = childVars.get(m); + if (childVar) { + args.push(childVar); + continue; + } + const cdVar = commonVars.get(m); + if (cdVar) { + args.push(cdVar); + continue; + } + args.push(goDefaultValue(m)); + } + + // Handle kindAliases with separate factories + if (node.kindAliases.length > 0 && !info.kindMember) { + for (const alias of node.kindAliases) { + w.write(`if kind == ast.Kind${alias} {`); + w.push(); + w.write(`return d.factory.New${alias}(${args.slice(info.kindMember ? 1 : 0).join(", ")}), nil`); + w.pop(); + w.write("}"); + } + } + + w.write(`return d.factory.${factoryName}(${args.join(", ")}), nil`); + w.pop(); +} + +// ═══════════════════════════════════════════════════════════════════════════ +// TypeScript Protocol Generation +// ═══════════════════════════════════════════════════════════════════════════ + +function generateTSProtocol(): string { + const w = new CodeWriter(" "); + w.write("// Code generated by tools/scripts/tsc/generate-encoder.ts. DO NOT EDIT."); + w.write(""); + w.write('import { SyntaxKind } from "../../ast/index.ts";'); + w.write(""); + + // Complete childProperties mapping — includes all nodes with children + // (single-child, multi-child, and extended data nodes). + w.write("export const childProperties: Readonly>> = {"); + w.push(); + + for (const node of api.nodes()) { + const info = analyzeNode(node); + if (info.childProps.length === 0) continue; + + const kinds = getTSKinds(node); + const props = info.childProps.map(m => `"${api.uncapitalize(m.name)}"`).join(", "); + for (const kind of kinds) { + w.write(`[SyntaxKind.${kind}]: [${props}],`); + } + } + + w.pop(); + w.write("};"); + w.write(""); + + // singleChildNodePropertyNames mapping + w.write("export const singleChildNodePropertyNames: Readonly>> = {"); + w.push(); + + for (const node of api.nodes()) { + const info = analyzeNode(node); + if (info.dataType === "extended") continue; + if (info.childProps.length !== 1) continue; + + const kinds = getTSKinds(node); + const prop = api.uncapitalize(info.childProps[0].name); + + for (const kind of kinds) { + w.write(`[SyntaxKind.${kind}]: "${prop}",`); + } + } + + w.pop(); + w.write("};"); + w.write(""); + + return w.toString(); +} + +// ═══════════════════════════════════════════════════════════════════════════ +// TypeScript Encoder Generation (encoder.generated.ts) +// ═══════════════════════════════════════════════════════════════════════════ + +/** Get all KindType values from a SyntaxKind union type. Resolves through aliases. */ +function unionKindValues(dt: import("./schema.ts").Type): import("./schema.ts").KindType[] { + const resolved = resolveUnion(dt); + if (!resolved || resolved.kind !== "union") return []; + return resolved.types.filter((t): t is import("./schema.ts").KindType => t.kind === "kind"); +} + +function generateTSEncoder(): string { + const w = new CodeWriter(" "); + w.write("// Code generated by tools/scripts/tsc/generate-encoder.ts. DO NOT EDIT."); + w.write(""); + + // Collect needed type imports + const typeImports = new Set(); + const valueImports = new Set(["SyntaxKind"]); + + for (const node of api.nodes()) { + const info = analyzeNode(node); + if (info.dataType === "extended") continue; + + if (info.autoBoolBits.length > 0 || info.autoUnionBits.length > 0) { + typeImports.add(node.name); + } + else if (info.handWrittenCommonData) { + typeImports.add(node.name); + } + } + + // Write imports + if (typeImports.size > 0) { + w.write("import type {"); + w.push(); + w.write("Node,"); + for (const t of [...typeImports].sort()) { + w.write(`${t},`); + } + w.pop(); + w.write('} from "../../ast/index.ts";'); + } + w.write("import {"); + w.push(); + for (const v of [...valueImports].sort()) { + w.write(`${v},`); + } + w.pop(); + w.write('} from "../../ast/index.ts";'); + w.write("import {"); + w.push(); + w.write("NODE_DATA_TYPE_CHILDREN,"); + w.write("NODE_DATA_TYPE_EXTENDED,"); + w.write("NODE_DATA_TYPE_STRING,"); + w.pop(); + w.write('} from "./protocol.ts";'); + w.write(""); + + generateTSGetNodeDataType(w); + generateTSGetNodeCommonData(w); + + return w.toString(); +} + +function generateTSGetNodeDataType(w: CodeWriter) { + w.write("export function getNodeDataType(kind: SyntaxKind): number {"); + w.push(); + w.write("switch (kind) {"); + + const stringKinds: string[] = []; + const extendedKinds: string[] = []; + + for (const node of api.nodes()) { + const info = analyzeNode(node); + const kinds = getTSKinds(node); + if (info.dataType === "string") { + stringKinds.push(...kinds); + } + else if (info.dataType === "extended") { + extendedKinds.push(...kinds); + } + } + + if (stringKinds.length > 0) { + for (const k of stringKinds) { + w.write(`case SyntaxKind.${k}:`); + } + w.push(); + w.write("return NODE_DATA_TYPE_STRING;"); + w.pop(); + } + if (extendedKinds.length > 0) { + for (const k of extendedKinds) { + w.write(`case SyntaxKind.${k}:`); + } + w.push(); + w.write("return NODE_DATA_TYPE_EXTENDED;"); + w.pop(); + } + + w.write("default:"); + w.push(); + w.write("return NODE_DATA_TYPE_CHILDREN;"); + w.pop(); + w.write("}"); + w.pop(); + w.write("}"); + w.write(""); +} + +function generateTSGetNodeCommonData(w: CodeWriter) { + w.write("export function getNodeCommonData(node: Node): number {"); + w.push(); + w.write("switch (node.kind) {"); + + for (const node of api.nodes()) { + const info = analyzeNode(node); + if (info.dataType === "extended") continue; + + if (hasAutoEncodedData(info)) { + // Auto-encoded: generate bit packing for bools and index encoding for unions + const kinds = getTSKinds(node); + const layout = getAutoEncodedLayout(info); + for (const k of kinds) { + w.write(`case SyntaxKind.${k}:`); + } + w.push(); + const parts: string[] = []; + for (const { member: m, bitPos, bitWidth } of layout) { + const propName = api.uncapitalize(m.name); + if (!isSyntaxKindUnion(m)) { + // Bool + parts.push(`((node as ${node.name}).${propName} ? 1 : 0) << ${24 + bitPos}`); + } + else { + // SyntaxKind union → index-based encoding + const kinds = unionKindValues(m.declaredType); + if (m.optional) { + // Optional union: 0 = absent, 1..N = values[0]..values[N-1] + const cases = kinds.map((k, i) => `(node as ${node.name}).${propName} === ${k.formatTypeScript()} ? ${i + 1} : `).join(""); + parts.push(`(${cases}0) << ${24 + bitPos}`); + } + else { + // Required union: index into values array + const cases = kinds.slice(1).map((k, i) => `(node as ${node.name}).${propName} === ${k.formatTypeScript()} ? ${i + 1} : `).join(""); + parts.push(`(${cases}0) << ${24 + bitPos}`); + } + } + } + w.write(`return ${parts.join(" | ")};`); + w.pop(); + } + else if (info.handWrittenCommonData) { + // Hand-written: generate encoding based on member types + const kinds = getTSKinds(node); + const dataMembers = getHandWrittenDataMembers(node); + if (dataMembers.length === 0) continue; + + // Skip nodes with 'any' type data members (e.g., SyntheticExpression) — they should never be encoded + if (dataMembers.some(m => m.declaredType.kind === "primitive" && m.declaredType.name === "any")) { + continue; + } + + for (const k of kinds) { + w.write(`case SyntaxKind.${k}:`); + } + w.push(); + + emitTSCommonDataEncoding(w, node, dataMembers); + + w.pop(); + } + } + + w.write("}"); + w.write("return 0;"); + w.pop(); + w.write("}"); + w.write(""); +} + +function emitTSCommonDataEncoding(w: CodeWriter, node: NodeType, dataMembers: MemberInfo[]) { + const parts: string[] = []; + let bitPos = 24; + + for (const m of dataMembers) { + const propName = api.uncapitalize(m.name); + const dt = m.declaredType; + + if (dt.kind === "primitive" && dt.name === "bool") { + parts.push(`((node as ${node.name}).${propName} ? 1 : 0) << ${bitPos}`); + bitPos++; + } + else { + throw new Error(`Unexpected hand-written data member ${node.name}.${m.name} of type ${dt.kind}`); + } + } + + if (parts.length === 0) { + w.write("return 0;"); + } + else if (parts.length === 1) { + w.write(`return ${parts[0]};`); + } + else { + w.write("return " + parts.join(" |\n ") + ";"); + } +} + +// ═══════════════════════════════════════════════════════════════════════════ +// TypeScript Node Generation (node.ts — entire file) +// ═══════════════════════════════════════════════════════════════════════════ + +function generateTSNodeGenerated(): string { + const w = new CodeWriter(" "); + + // Collect all unique child property names and their types (node vs nodeList) + const childGetters = new Map(); + for (const node of api.nodes()) { + const info = analyzeNode(node); + for (const m of info.childProps) { + const propName = api.uncapitalize(m.name); + const ct = childType(m); + const isListType = ct === "nodeList" || ct === "modifierList"; + const existing = childGetters.get(propName); + if (!existing) { + childGetters.set(propName, isListType ? "nodeList" : "node"); + } + else if ((existing === "node" && isListType) || (existing === "nodeList" && !isListType)) { + childGetters.set(propName, "both"); + } + } + } + + // Collect all unique auto-bool property names with their bit positions + const boolGetters = new Map(); + for (const node of api.nodes()) { + const info = analyzeNode(node); + const layout = getAutoEncodedLayout(info); + for (const { member: m, bitPos, bitWidth } of layout) { + if (!isSyntaxKindUnion(m)) { + const propName = api.uncapitalize(m.name); + const absBitPos = 24 + bitPos; + const existing = boolGetters.get(propName); + if (existing !== undefined && existing !== absBitPos) { + throw new Error(`Bool property "${propName}" has inconsistent bit positions: ${existing} vs ${absBitPos}`); + } + boolGetters.set(propName, absBitPos); + } + } + } + + // Collect all unique SyntaxKind union property getters: + // Map from property name → array of { kinds, bitPos, bitWidth, values, optional } + interface UnionGetterEntry { + kinds: string[]; + bitPos: number; + bitWidth: number; + values: KindType[]; + optional: boolean; + } + const unionGetters = new Map(); + for (const node of api.nodes()) { + const info = analyzeNode(node); + const layout = getAutoEncodedLayout(info); + for (const { member: m, bitPos, bitWidth } of layout) { + if (isSyntaxKindUnion(m)) { + const propName = api.uncapitalize(m.name); + const entries = unionGetters.get(propName) || []; + entries.push({ + kinds: getTSKinds(node), + bitPos: 24 + bitPos, + bitWidth, + values: unionKindValues(m.declaredType), + optional: !!m.optional, + }); + unionGetters.set(propName, entries); + } + } + } + + // Collect string data property info + const stringTextKinds: string[] = []; + const extendedTextKinds: string[] = []; + for (const node of api.nodes()) { + const info = analyzeNode(node); + if (info.dataType === "string" && info.textMember) { + stringTextKinds.push(...getTSKinds(node)); + } + else if (info.dataType === "extended") { + extendedTextKinds.push(...getTSKinds(node)); + } + } + + // Emit the file + w.write("// Code generated by tools/scripts/tsc/generate-encoder.ts. DO NOT EDIT."); + w.write(""); + emitNodeGeneratedImports(w); + w.write(""); + emitRemoteNodeList(w); + w.write(""); + emitRemoteNodeClassOpen(w); + w.write(""); + + // ── Generated: Boolean property getters ── + w.write(" // ═══ Generated boolean property getters ═══"); + w.write(""); + for (const [propName, bitPos] of [...boolGetters.entries()].sort((a, b) => a[0].localeCompare(b[0]))) { + w.write(` get ${propName}(): boolean {`); + w.write(` return (this.data & (1 << ${bitPos})) !== 0;`); + w.write(` }`); + w.write(""); + } + + // ── Generated: SyntaxKind union property getters ── + w.write(" // ═══ Generated SyntaxKind union property getters ═══"); + w.write(""); + for (const [propName, entries] of [...unionGetters.entries()].sort((a, b) => a[0].localeCompare(b[0]))) { + // Determine return type: if any entry is optional, return type includes undefined + const anyOptional = entries.some(e => e.optional); + const returnType = anyOptional ? "SyntaxKind | undefined" : "SyntaxKind | undefined"; + w.write(` get ${propName}(): ${returnType} {`); + w.write(` switch (this.kind) {`); + for (const entry of entries) { + for (const kind of entry.kinds) { + w.write(` case SyntaxKind.${kind}:`); + } + const mask = (1 << entry.bitWidth) - 1; + const indexExpr = `(this.data >> ${entry.bitPos}) & 0x${mask.toString(16)}`; + if (entry.optional) { + // Optional: 0 = undefined, 1..N = values[0]..values[N-1] + w.write(` {`); + w.write(` const idx = ${indexExpr};`); + w.write(` if (idx === 0) return undefined;`); + const cases = entry.values.map((k, i) => `idx === ${i + 1} ? ${k.formatTypeScript()} : `).join(""); + w.write(` return ${cases}undefined;`); + w.write(` }`); + } + else if (entry.values.length === 2) { + // 2-value: simple ternary + w.write(` return ${indexExpr} ? ${entry.values[1].formatTypeScript()} : ${entry.values[0].formatTypeScript()};`); + } + else { + // N-value: switch on index + w.write(` {`); + w.write(` const idx = ${indexExpr};`); + for (let i = 0; i < entry.values.length; i++) { + if (i === 0) { + // default case (index 0) + } + else { + w.write(` if (idx === ${i}) return ${entry.values[i].formatTypeScript()};`); + } + } + w.write(` return ${entry.values[0].formatTypeScript()};`); + w.write(` }`); + } + } + w.write(` }`); + w.write(` }`); + w.write(""); + } + + // ── Generated: templateFlags (extended data, not a union) ── + emitTemplateFlagsGetter(w); + w.write(""); + + // ── Generated: tokenFlags (extended data for RegularExpressionLiteral) ── + emitTokenFlagsGetter(w); + w.write(""); + + // ── Generated: Child property getters ── + w.write(" // ═══ Generated child property getters ═══"); + w.write(""); + for (const [propName, type] of [...childGetters.entries()].sort((a, b) => a[0].localeCompare(b[0]))) { + const returnType = type === "nodeList" ? "RemoteNodeList" : type === "both" ? "RemoteNode | RemoteNodeList" : "RemoteNode"; + const castType = type === "nodeList" ? "RemoteNodeList" : type === "both" ? "RemoteNode | RemoteNodeList" : "RemoteNode"; + w.write(` get ${propName}(): ${returnType} | undefined {`); + w.write(` return this.getNamedChild(${JSON.stringify(propName)}) as ${castType};`); + w.write(` }`); + } + w.write(""); + + // ── Generated: String property getters (text, rawText) ── + w.write(" // ═══ Generated string property getters ═══"); + w.write(""); + emitStringGetters(w, stringTextKinds, extendedTextKinds); + w.write(""); + + // ── Generated: Extended data property getters (SourceFile-specific, etc.) ── + w.write(" // ═══ Generated extended data property getters ═══"); + w.write(""); + emitExtendedDataGetters(w); + w.write(""); + + // ── Hand-written remaining getters (flags, modifierFlags, etc.) ── + emitRemoteNodeClassClose(w); + w.write(""); + + return w.toString(); +} + +// ── Infrastructure template sections ── + +function emitNodeGeneratedImports(w: CodeWriter) { + w.write(`import {`); + w.write(` getTokenPosOfNode,`); + w.write(` ModifierFlags,`); + w.write(` type Node,`); + w.write(` type NodeArray,`); + w.write(` type SourceFile,`); + w.write(` SyntaxKind,`); + w.write(`} from "../../ast/index.ts";`); + w.write(`import {`); + w.write(` childProperties,`); + w.write(` KIND_NODE_LIST,`); + w.write(` NODE_DATA_TYPE_CHILDREN,`); + w.write(` NODE_DATA_TYPE_EXTENDED,`); + w.write(` NODE_DATA_TYPE_STRING,`); + w.write(` NODE_LEN,`); + w.write(` NODE_OFFSET_DATA,`); + w.write(` NODE_OFFSET_END,`); + w.write(` NODE_OFFSET_FLAGS,`); + w.write(` NODE_OFFSET_KIND,`); + w.write(` NODE_OFFSET_NEXT,`); + w.write(` NODE_OFFSET_PARENT,`); + w.write(` NODE_OFFSET_POS,`); + w.write(`} from "./protocol.ts";`); + w.write(`import {`); + w.write(` type NodeDataType,`); + w.write(` NODE_CHILD_MASK,`); + w.write(` NODE_DATA_TYPE_MASK,`); + w.write(` NODE_EXTENDED_DATA_MASK,`); + w.write(` NODE_STRING_INDEX_MASK,`); + w.write(` modifierToFlag,`); + w.write(` popcount8,`); + w.write(` RemoteNodeBase,`); + w.write(` type SourceFileInfo,`); + w.write(`} from "./node.infrastructure.ts";`); +} + +function emitRemoteNodeList(w: CodeWriter) { + w.write(`export class RemoteNodeList extends Array implements NodeArray {`); + w.write(` // Inherited Array methods like filter/map/slice use ArraySpeciesCreate, which would`); + w.write(` // otherwise call \`new RemoteNodeList(length)\` and fail. Produce a plain Array instead.`); + w.write(` static get [Symbol.species](): ArrayConstructor {`); + w.write(` return Array;`); + w.write(` }`); + w.write(``); + w.write(` parent: RemoteNode;`); + w.write(` transformFlags: number = 0;`); + w.write(` protected view: DataView;`); + w.write(` protected index: number;`); + w.write(` private _byteIndex: number;`); + w.write(` // Cursor memoizing the last resolved (logical index -> node index) so that`); + w.write(` // sequential forward access (index loops and list[i], plus forEach/map/`); + w.write(` // reduce/filter) resumes instead of re-walking from the head, turning an`); + w.write(` // O(n) pass over the whole list from O(n^2) into O(n).`); + w.write(` private _cursorIndex: number = 0;`); + w.write(` private _cursorNodeIndex: number = 0;`); + w.write(``); + w.write(` get pos(): number {`); + w.write(` return this.view.getUint32(this._byteIndex + NODE_OFFSET_POS, true);`); + w.write(` }`); + w.write(``); + w.write(` get end(): number {`); + w.write(` return this.view.getUint32(this._byteIndex + NODE_OFFSET_END, true);`); + w.write(` }`); + w.write(``); + w.write(` get next(): number {`); + w.write(` return this.view.getUint32(this._byteIndex + NODE_OFFSET_NEXT, true);`); + w.write(` }`); + w.write(``); + w.write(` private get data(): number {`); + w.write(` return this.view.getUint32(this._byteIndex + NODE_OFFSET_DATA, true);`); + w.write(` }`); + w.write(``); + w.write(` get hasTrailingComma(): boolean {`); + w.write(` return (this.view.getUint32(this._byteIndex + NODE_OFFSET_FLAGS, true) & 1) !== 0;`); + w.write(` }`); + w.write(``); + w.write(` private sourceFile: SourceFileInfo;`); + w.write(``); + w.write(` constructor(view: DataView, index: number, parent: RemoteNode, sourceFile: SourceFileInfo, offsetNodes: number) {`); + w.write(` super(view.getUint32(offsetNodes + index * NODE_LEN + NODE_OFFSET_DATA, true));`); + w.write(` this.view = view;`); + w.write(` this.index = index;`); + w.write(` this.parent = parent;`); + w.write(` this.sourceFile = sourceFile;`); + w.write(` this._byteIndex = offsetNodes + index * NODE_LEN;`); + w.write(` this._cursorNodeIndex = index + 1;`); + w.write(``); + w.write(` const length = this.length;`); + w.write(` for (let i = 16; i < length; i++) {`); + w.write(` Object.defineProperty(this, i, {`); + w.write(` get() {`); + w.write(` return this.at(i);`); + w.write(` },`); + w.write(` });`); + w.write(` }`); + w.write(` }`); + + // Emit indexed getters 0..15 + for (let i = 0; i < 16; i++) { + w.write(` get ${i}(): RemoteNode {`); + w.write(` return this.at(${i});`); + w.write(` }`); + } + + w.write(``); + w.write(` *[Symbol.iterator](): ArrayIterator {`); + w.write(` if (!this.length) return;`); + w.write(` let next = this.index + 1;`); + w.write(` while (next) {`); + w.write(` const child = this.getOrCreateChildAtNodeIndex(next);`); + w.write(` next = child.next;`); + w.write(` yield child as RemoteNode;`); + w.write(` }`); + w.write(` }`); + w.write(``); + w.write(` forEachNode(visitNode: (node: RemoteNode) => T | undefined): T | undefined {`); + w.write(` if (!this.length) return;`); + w.write(` let next = this.index + 1;`); + w.write(` while (next) {`); + w.write(` const child = this.getOrCreateChildAtNodeIndex(next);`); + w.write(` next = child.next;`); + w.write(` const result = visitNode(child as RemoteNode);`); + w.write(` if (result) return result;`); + w.write(` }`); + w.write(` }`); + w.write(``); + w.write(` at(index: number): RemoteNode {`); + w.write(` if (!Number.isInteger(index)) {`); + w.write(` return undefined!;`); + w.write(` }`); + w.write(` if (index >= this.data || (index < 0 && -index > this.data)) {`); + w.write(` return undefined!;`); + w.write(` }`); + w.write(` if (index < 0) {`); + w.write(` index = this.length + index;`); + w.write(` }`); + w.write(` // Walk the raw buffer following each node's \`next\` pointer instead of`); + w.write(` // materializing every intermediate RemoteNode just to read it. Resume from`); + w.write(` // the memoized cursor when possible so sequential forward access is O(1)`); + w.write(` // amortized (a full in-order pass is O(n) rather than O(n^2)).`); + w.write(` const offsetNodes = this.sourceFile._offsetNodes;`); + w.write(` let i: number;`); + w.write(` let next: number;`); + w.write(` if (index >= this._cursorIndex) {`); + w.write(` i = this._cursorIndex;`); + w.write(` next = this._cursorNodeIndex;`); + w.write(` }`); + w.write(` else {`); + w.write(` i = 0;`); + w.write(` next = this.index + 1;`); + w.write(` }`); + w.write(` for (; i < index; i++) {`); + w.write(` next = this.view.getUint32(offsetNodes + next * NODE_LEN + NODE_OFFSET_NEXT, true);`); + w.write(` }`); + w.write(` this._cursorIndex = index;`); + w.write(` this._cursorNodeIndex = next;`); + w.write(` return this.getOrCreateChildAtNodeIndex(next) as RemoteNode;`); + w.write(` }`); + w.write(``); + w.write(` private getOrCreateChildAtNodeIndex(index: number): RemoteNode | RemoteNodeList {`); + w.write(` let child = this.sourceFile.nodes[index];`); + w.write(` if (!child) {`); + w.write(` const kind = this.view.getUint32(this.sourceFile._offsetNodes + index * NODE_LEN + NODE_OFFSET_KIND, true);`); + w.write(` if (kind === KIND_NODE_LIST) {`); + w.write(` throw new Error("NodeList cannot directly contain another NodeList");`); + w.write(` }`); + w.write(` const sf = this.sourceFile;`); + w.write(` child = new RemoteNode(this.view, index, this.parent, sf, sf._offsetNodes);`); + w.write(` sf.nodes[index] = child;`); + w.write(` sf._timing?.recordMaterialization();`); + w.write(` }`); + w.write(` return child;`); + w.write(` }`); + w.write(``); + w.write(` __print(): string {`); + w.write(` const result = [];`); + w.write(` result.push(\`kind: NodeList\`);`); + w.write(` result.push(\`index: \${this.index}\`);`); + w.write(` result.push(\`byteIndex: \${this._byteIndex}\`);`); + w.write(` result.push(\`length: \${this.length}\`);`); + w.write(` return result.join("\\n");`); + w.write(` }`); + w.write(`}`); +} + +function emitRemoteNodeClassOpen(w: CodeWriter) { + w.write(`export class RemoteNode extends RemoteNodeBase implements Node {`); + w.write(` protected static NODE_LEN: number = NODE_LEN;`); + w.write(` protected override get sourceFile(): SourceFileInfo {`); + w.write(` return this._sourceFile;`); + w.write(` }`); + w.write(` protected _sourceFile: SourceFileInfo;`); + w.write(` get id(): string {`); + w.write(` return \`\${this.index}.\${this.kind}.\${this.sourceFile.path}\`;`); + w.write(` }`); + w.write(``); + w.write(` constructor(view: DataView, index: number, parent: RemoteNode, sourceFile: SourceFileInfo, offsetNodes: number) {`); + w.write(` super(view, index, parent, offsetNodes + index * NODE_LEN);`); + w.write(` this._sourceFile = sourceFile;`); + w.write(` }`); + w.write(``); + w.write(` forEachChild(visitNode: (node: Node) => T, visitList?: (list: NodeArray) => T): T | undefined {`); + w.write(` if (this.hasChildren()) {`); + w.write(` let next = this.index + 1;`); + w.write(` do {`); + w.write(` const child = this.getOrCreateChildAtNodeIndex(next);`); + w.write(` if (child instanceof RemoteNodeList) {`); + w.write(` if (visitList) {`); + w.write(` const result = visitList(child);`); + w.write(` if (result) {`); + w.write(` return result;`); + w.write(` }`); + w.write(` }`); + w.write(` else {`); + w.write(` const result = child.forEachNode(visitNode);`); + w.write(` if (result) {`); + w.write(` return result;`); + w.write(` }`); + w.write(` }`); + w.write(` }`); + w.write(` else if (child.kind !== SyntaxKind.JSDoc) {`); + w.write(` const result = visitNode(child);`); + w.write(` if (result) {`); + w.write(` return result;`); + w.write(` }`); + w.write(` }`); + w.write(` next = child.next;`); + w.write(` }`); + w.write(` while (next);`); + w.write(` }`); + w.write(` }`); + w.write(``); + w.write(` get jsDoc(): readonly Node[] | undefined {`); + w.write(` if (!this.hasChildren()) {`); + w.write(` return undefined;`); + w.write(` }`); + w.write(` let result: Node[] | undefined;`); + w.write(` let next = this.index + 1;`); + w.write(` do {`); + w.write(` const child = this.getOrCreateChildAtNodeIndex(next);`); + w.write(` if (!(child instanceof RemoteNodeList) && child.kind === SyntaxKind.JSDoc) {`); + w.write(` (result ??= []).push(child);`); + w.write(` }`); + w.write(` next = child.next;`); + w.write(` }`); + w.write(` while (next);`); + w.write(` return result;`); + w.write(` }`); + w.write(``); + w.write(` getSourceFile(): SourceFile {`); + w.write(` return this.sourceFile as unknown as SourceFile;`); + w.write(` }`); + w.write(``); + w.write(` getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number {`); + w.write(` return getTokenPosOfNode(this as unknown as Node, sourceFile ?? this.getSourceFile(), includeJsDocComment);`); + w.write(` }`); + w.write(``); + w.write(` getFullStart(): number {`); + w.write(` return this.pos;`); + w.write(` }`); + w.write(``); + w.write(` getEnd(): number {`); + w.write(` return this.end;`); + w.write(` }`); + w.write(``); + w.write(` getWidth(sourceFile?: SourceFile): number {`); + w.write(` return this.getEnd() - this.getStart(sourceFile);`); + w.write(` }`); + w.write(``); + w.write(` getFullWidth(): number {`); + w.write(` return this.end - this.pos;`); + w.write(` }`); + w.write(``); + w.write(` getLeadingTriviaWidth(sourceFile?: SourceFile): number {`); + w.write(` return this.getStart(sourceFile) - this.pos;`); + w.write(` }`); + w.write(``); + w.write(` getFullText(sourceFile?: SourceFile): string {`); + w.write(` return (sourceFile ?? this.getSourceFile()).text.substring(this.pos, this.end);`); + w.write(` }`); + w.write(``); + w.write(` getText(sourceFile?: SourceFile): string {`); + w.write(` sourceFile ??= this.getSourceFile();`); + w.write(` return sourceFile.text.substring(this.getStart(sourceFile), this.end);`); + w.write(` }`); + w.write(``); + w.write(` protected getString(index: number): string {`); + w.write(` const offsetStringTableOffsets = this.sourceFile._offsetStringTableOffsets;`); + w.write(` const start = this.view.getUint32(offsetStringTableOffsets + index * 4, true);`); + w.write(` const end = this.view.getUint32(offsetStringTableOffsets + (index + 1) * 4, true);`); + w.write(` const offsetStringTable = this.sourceFile._offsetStringTable;`); + w.write(` const text = new Uint8Array(this.view.buffer, this.view.byteOffset + offsetStringTable + start, end - start);`); + w.write(` return this.sourceFile._decoder.decode(text);`); + w.write(` }`); + w.write(``); + w.write(` private getOrCreateChildAtNodeIndex(index: number): RemoteNode | RemoteNodeList {`); + w.write(` let child = this.sourceFile.nodes[index];`); + w.write(` if (!child) {`); + w.write(` const sf = this.sourceFile;`); + w.write(` const offsetNodes = sf._offsetNodes;`); + w.write(` const kind = this.view.getUint32(offsetNodes + index * NODE_LEN + NODE_OFFSET_KIND, true);`); + w.write(` child = kind === KIND_NODE_LIST`); + w.write(` ? new RemoteNodeList(this.view, index, this, sf, offsetNodes)`); + w.write(` : new RemoteNode(this.view, index, this, sf, offsetNodes);`); + w.write(` sf.nodes[index] = child;`); + w.write(` sf._timing?.recordMaterialization();`); + w.write(` }`); + w.write(` return child;`); + w.write(` }`); + w.write(``); + w.write(` private hasChildren(): boolean {`); + w.write(` if (this._byteIndex >= this.view.byteLength - NODE_LEN) {`); + w.write(` return false;`); + w.write(` }`); + w.write(` const nextNodeParent = this.view.getUint32(this.sourceFile._offsetNodes + (this.index + 1) * NODE_LEN + NODE_OFFSET_PARENT, true);`); + w.write(` return nextNodeParent === this.index;`); + w.write(` }`); + w.write(``); + w.write(` private getNamedChild(propertyName: string): RemoteNode | RemoteNodeList | undefined {`); + w.write(` const kind = this.kind;`); + w.write(` const propertyNames = childProperties[kind];`); + w.write(` if (!propertyNames) {`); + w.write(` return undefined;`); + w.write(` }`); + w.write(``); + w.write(` const order = propertyNames.indexOf(propertyName);`); + w.write(` if (order === -1) {`); + w.write(` return undefined;`); + w.write(` }`); + w.write(` return this.getChildAtOrder(order);`); + w.write(` }`); + w.write(``); + w.write(` private getChildAtOrder(order: number): RemoteNode | RemoteNodeList | undefined {`); + w.write(` if (!this.hasChildren()) return undefined;`); + w.write(` const mask = this.childMask;`); + w.write(` if (!(mask & (1 << order))) {`); + w.write(` // Property is not present`); + w.write(` return undefined;`); + w.write(` }`); + w.write(``); + w.write(` // The property index is \`order\`, minus the number of zeros in the mask that are in bit positions less`); + w.write(` // than the \`order\`th bit. Example:`); + w.write(` //`); + w.write(` // This is a MethodDeclaration with mask 0b01110101. The possible properties are`); + w.write(` // ["modifiers", "asteriskToken", "name", "postfixToken", "typeParameters", "parameters", "type", "body"]`); + w.write(` // (it has modifiers, name, typeParameters, parameters, and type).`); + w.write(` //`); + w.write(` // | Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |`); + w.write(` // | ----- | ---- | ---- | ---------- | -------------- | ------------ | ---- | ------------- | --------- |`); + w.write(` // | Value | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 1 |`); + w.write(` // | Name | body | type | parameters | typeParameters | postfixToken | name | asteriskToken | modifiers |`); + w.write(` //`); + w.write(` // We are trying to get the index of "parameters" (bit = 5).`); + w.write(` // First, set all the more significant bits to 1:`); + w.write(` //`); + w.write(` // | Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |`); + w.write(` // | ----- | ---- | ---- | ---------- | -------------- | ------------ | ---- | ------------- | --------- |`); + w.write(` // | Value | 1 | 1 | 1 | 1 | 0 | 1 | 0 | 1 |`); + w.write(` //`); + w.write(` // Then, flip the bits:`); + w.write(` //`); + w.write(` // | Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |`); + w.write(` // | ----- | ---- | ---- | ---------- | -------------- | ------------ | ---- | ------------- | --------- |`); + w.write(` // | Value | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 |`); + w.write(` //`); + w.write(` // Counting the 1s gives us the number of *missing properties* before the \`order\`th property. If every property`); + w.write(` // were present, we would have \`parameters = children[5]\`, but since \`postfixToken\` and \`astersiskToken\` are`); + w.write(` // missing, we have \`parameters = children[5 - 2]\`.`); + w.write(` const propertyIndex = order - popcount8[~(mask | ((0xff << order) & 0xff)) & 0xff];`); + w.write(` let childIndex = this.index + 1;`); + w.write(` for (let i = 0; i < propertyIndex; i++) {`); + w.write(` // Walk through children via their \`next\` pointer until we get to the right property index`); + w.write(` childIndex = this.view.getUint32(this.sourceFile._offsetNodes + childIndex * NODE_LEN + NODE_OFFSET_NEXT, true);`); + w.write(` }`); + w.write(` return this.getOrCreateChildAtNodeIndex(childIndex);`); + w.write(` }`); + w.write(``); + w.write(` __print(): string {`); + w.write(` const result = [];`); + w.write(` result.push(\`index: \${this.index}\`);`); + w.write(` result.push(\`byteIndex: \${this._byteIndex}\`);`); + w.write(` result.push(\`kind: \${SyntaxKind[this.kind]}\`);`); + w.write(` result.push(\`pos: \${this.pos}\`);`); + w.write(` result.push(\`end: \${this.end}\`);`); + w.write(` result.push(\`next: \${this.next}\`);`); + w.write(` result.push(\`parent: \${this.parentIndex}\`);`); + w.write(` result.push(\`data: \${this.data.toString(2).padStart(32, "0")}\`);`); + w.write(` const dataType = this.dataType === NODE_DATA_TYPE_CHILDREN ? "children" :`); + w.write(` this.dataType === NODE_DATA_TYPE_STRING ? "string" :`); + w.write(` "extended";`); + w.write(` result.push(\`dataType: \${dataType}\`);`); + w.write(` if (this.dataType === NODE_DATA_TYPE_CHILDREN) {`); + w.write(` result.push(\`childMask: \${this.childMask.toString(2).padStart(8, "0")}\`);`); + w.write(` result.push(\`childProperties: \${childProperties[this.kind]?.join(", ")}\`);`); + w.write(` }`); + w.write(` return result.join("\\n");`); + w.write(` }`); + w.write(``); + w.write(` __printChildren(): string {`); + w.write(` const result = [];`); + w.write(` let next = this.index + 1;`); + w.write(` while (next) {`); + w.write(` const child = this.getOrCreateChildAtNodeIndex(next);`); + w.write(` next = child.next;`); + w.write(` result.push(child.__print());`); + w.write(` }`); + w.write(` return result.join("\\n\\n");`); + w.write(` }`); + w.write(``); + w.write(` __printSubtree(): string {`); + w.write(` const result = [this.__print()];`); + w.write(` this.forEachChild(function visitNode(node) {`); + w.write(` result.push((node as RemoteNode).__print());`); + w.write(` node.forEachChild(visitNode);`); + w.write(` }, visitList => {`); + w.write(` result.push((visitList as RemoteNodeList).__print());`); + w.write(` });`); + w.write(` return result.join("\\n\\n");`); + w.write(` }`); +} + +function emitTemplateFlagsGetter(w: CodeWriter) { + // templateFlags — from extended data (not a commonData union) + w.write(` get templateFlags(): number | undefined {`); + w.write(` switch (this.kind) {`); + w.write(` case SyntaxKind.TemplateHead:`); + w.write(` case SyntaxKind.TemplateMiddle:`); + w.write(` case SyntaxKind.TemplateTail:`); + w.write(` const extendedDataOffset = this.sourceFile._offsetExtendedData + (this.data & NODE_EXTENDED_DATA_MASK);`); + w.write(` return this.view.getUint32(extendedDataOffset + 8, true);`); + w.write(` }`); + w.write(` }`); +} + +function emitTokenFlagsGetter(w: CodeWriter) { + // tokenFlags — from extended data for literal kinds that store TokenFlags + w.write(` get tokenFlags(): number {`); + w.write(` switch (this.kind) {`); + w.write(` case SyntaxKind.StringLiteral:`); + w.write(` case SyntaxKind.NumericLiteral:`); + w.write(` case SyntaxKind.BigIntLiteral:`); + w.write(` case SyntaxKind.RegularExpressionLiteral:`); + w.write(` const extendedDataOffset = this.sourceFile._offsetExtendedData + (this.data & NODE_EXTENDED_DATA_MASK);`); + w.write(` return this.view.getUint32(extendedDataOffset + 4, true);`); + w.write(` default:`); + w.write(` return 0;`); + w.write(` }`); + w.write(` }`); +} + +function emitStringGetters(w: CodeWriter, stringTextKinds: string[], extendedTextKinds: string[]) { + // text getter — string data kinds vs extended data kinds + w.write(` get text(): string | undefined {`); + w.write(` switch (this.kind) {`); + for (const k of stringTextKinds) { + w.write(` case SyntaxKind.${k}:`); + } + w.write(` {`); + w.write(` const stringIndex = this.data & NODE_STRING_INDEX_MASK;`); + w.write(` return this.getString(stringIndex);`); + w.write(` }`); + for (const k of extendedTextKinds) { + w.write(` case SyntaxKind.${k}:`); + } + w.write(` {`); + w.write(` const extendedDataOffset = this.sourceFile._offsetExtendedData + (this.data & NODE_EXTENDED_DATA_MASK);`); + w.write(` const stringIndex = this.view.getUint32(extendedDataOffset, true);`); + w.write(` return this.getString(stringIndex);`); + w.write(` }`); + w.write(` }`); + w.write(` }`); + w.write(``); + // rawText — template kinds only + w.write(` get rawText(): string | undefined {`); + w.write(` switch (this.kind) {`); + w.write(` case SyntaxKind.TemplateHead:`); + w.write(` case SyntaxKind.TemplateMiddle:`); + w.write(` case SyntaxKind.TemplateTail:`); + w.write(` const extendedDataOffset = this.sourceFile._offsetExtendedData + (this.data & NODE_EXTENDED_DATA_MASK);`); + w.write(` const stringIndex = this.view.getUint32(extendedDataOffset + 4, true);`); + w.write(` return this.getString(stringIndex);`); + w.write(` }`); + w.write(` }`); +} + +function emitExtendedDataGetters(w: CodeWriter) { + // SourceFile-specific extended data getters live on RemoteSourceFile in node.ts. + // Only non-SourceFile extended data getters are emitted here. +} + +function emitRemoteNodeClassClose(w: CodeWriter) { + w.write(` // ═══ Other property getters ═══`); + w.write(``); + w.write(` get flags(): number {`); + w.write(` return this.view.getUint32(this._byteIndex + NODE_OFFSET_FLAGS, true);`); + w.write(` }`); + w.write(``); + w.write(` get modifierFlags(): ModifierFlags {`); + w.write(` const mods = this.modifiers;`); + w.write(` if (!mods) return ModifierFlags.None;`); + w.write(` let flags: ModifierFlags = ModifierFlags.None;`); + w.write(` for (const mod of mods) {`); + w.write(` flags |= modifierToFlag(mod.kind);`); + w.write(` }`); + w.write(` return flags;`); + w.write(` }`); + w.write(`}`); +} + +// ═══════════════════════════════════════════════════════════════════════════ +// Main: generate and write all files +// ═══════════════════════════════════════════════════════════════════════════ + +function writeAndFormat(filePath: string, content: string, formatter: string) { + fs.writeFileSync(filePath, content); + try { + const [cmd, ...args] = formatter.split(" "); + execaSync(cmd, [...args, filePath], { stdio: "inherit", cwd: ROOT }); + } + catch { + console.warn(`Warning: formatter failed for ${filePath}`); + } + console.log(`Wrote ${filePath}`); +} + +export default function main() { + console.log("Generating encoder/decoder code..."); + + const goEncoder = generateGoEncoder(); + writeAndFormat( + path.join(ROOT, "tsc/internal/api/encoder/encoder_generated.go"), + goEncoder + "\n", + "dprint fmt", + ); + + const goDecoder = generateGoDecoder(); + writeAndFormat( + path.join(ROOT, "tsc/internal/api/encoder/decoder_generated.go"), + goDecoder + "\n", + "dprint fmt", + ); + + const tsProtocol = generateTSProtocol(); + writeAndFormat( + path.join(ROOT, "packages/typescript/src/api/node/protocol.generated.ts"), + tsProtocol + "\n", + "dprint fmt", + ); + + const tsEncoder = generateTSEncoder(); + writeAndFormat( + path.join(ROOT, "packages/typescript/src/api/node/encoder.generated.ts"), + tsEncoder + "\n", + "dprint fmt", + ); + + const tsNode = generateTSNodeGenerated(); + writeAndFormat( + path.join(ROOT, "packages/typescript/src/api/node/node.generated.ts"), + tsNode + "\n", + "dprint fmt", + ); + + console.log("Done!"); +} + +if (process.argv[1] === fileURLToPath(import.meta.url)) { + main(); +} diff --git a/tools/scripts/tsc/generate-go-ast.ts b/tools/scripts/tsc/generate-go-ast.ts index b64a685d90d67..3492f6f20d942 100644 --- a/tools/scripts/tsc/generate-go-ast.ts +++ b/tools/scripts/tsc/generate-go-ast.ts @@ -1,1105 +1,1105 @@ -/** - * Go AST code generator: reads tools/scripts/tsc/ast.json and produces internal/ast/ast_generated.go - * - * Usage: node --experimental-strip-types tools/scripts/tsc/generate-go-ast.ts - * - * Generates: - * - Struct definitions for each node kind - * - As*() cast methods on *Node - * - New*()/Update*() factory methods on *NodeFactory - * - ForEachChild() implementations - * - VisitEachChild() implementations - * - Clone() implementations - * - Polymorphic accessor switch statements (Expression(), Type(), etc.) - * - Is*() type guard functions - */ - -import { execaSync } from "execa"; -import * as fs from "node:fs"; -import * as path from "node:path"; -import { fileURLToPath } from "node:url"; -import type { - MemberInfo, - NodeType, - Type, -} from "./schema.ts"; -import { - api, - kindGuardName, -} from "./schema.ts"; - -// ──────────────────────────────────────────────────────────────────────────── -// Load schema -// ──────────────────────────────────────────────────────────────────────────── - -const ROOT = path.resolve(import.meta.dirname!, "../../.."); - -// Members that participate in factory/visitor/clone (excludes noFactory) -function schemaMembers(node: NodeType): MemberInfo[] { - return node.members.filter(m => !m.noFactory); -} - -function isTextContentType(type: Type): boolean { - if (type.kind === "primitive") { - return type.name === "string"; - } - if (type.kind === "list" && type.listKind === "raw") { - return type.elementType.kind === "primitive" && type.elementType.name === "string"; - } - return false; -} - -// Does this node have any string-typed member (triggering textCount++)? -function hasTextContent(node: NodeType): boolean { - if (node.members.length === 0) return false; - return schemaMembers(node).some(m => isTextContentType(m.type)); -} - -// ──────────────────────────────────────────────────────────────────────────── -// Code generation -// ──────────────────────────────────────────────────────────────────────────── - -class CodeWriter { - private lines: string[] = []; - private indent = 0; - - write(line: string = "") { - if (line === "") { - this.lines.push(""); - } - else { - this.lines.push("\t".repeat(this.indent) + line); - } - } - - writeRaw(line: string) { - this.lines.push(line); - } - - push() { - this.indent++; - } - pop() { - this.indent--; - } - - toString(): string { - return this.lines.join("\n"); - } -} - -function generateNodeFactoryStruct(w: CodeWriter) { - const arenaFields: { fieldName: string; typeName: string; }[] = []; - for (const node of api.nodes()) { - if (!node.arena) continue; - arenaFields.push({ - fieldName: `${api.uncapitalize(node.name)}Arena`, - typeName: node.name, - }); - } - arenaFields.push({ fieldName: "modifierListArena", typeName: "ModifierList" }); - arenaFields.push({ fieldName: "nodeListArena", typeName: "NodeList" }); - arenaFields.sort((a, b) => a.fieldName.localeCompare(b.fieldName)); - - w.write("// ──────────────────────────────────────────────────────────────────────"); - w.write("// NodeFactory"); - w.write("// ──────────────────────────────────────────────────────────────────────"); - w.write(""); - w.write("type NodeFactory struct {"); - w.push(); - w.write("hooks NodeFactoryHooks"); - for (const { fieldName, typeName } of arenaFields) { - w.write(`${fieldName} core.Arena[${typeName}]`); - } - w.write(""); - w.write("nodeCount int"); - w.write("textCount int"); - w.pop(); - w.write("}"); - w.write(""); -} - -function generateHeader(w: CodeWriter) { - w.write("// Code generated by tools/scripts/tsc/generate-go-ast.ts. DO NOT EDIT."); - w.write(""); - w.write("package ast"); - w.write(""); - w.write("import ("); - w.push(); - w.write('"sync/atomic"'); - w.write(""); - w.write('"github.com/microsoft/TypeScript/tsc/internal/core"'); - w.pop(); - w.write(")"); - w.write(""); - w.write("var _ = core.Same[string] // prevent unused import"); - w.write("var _ atomic.Uint32 // prevent unused import"); - w.write(""); -} - -// ── Generate struct definitions ──────────────────────────────────────────── - -/** - * Verifies that nothing inherits a base along more than one path. - */ -function verifyNoDuplicateBases(): void { - const problems: string[] = []; - const check = (name: string, extendsKeys: string[]) => { - const copies = new Map(); - const walk = (keys: string[]) => { - for (const key of keys) { - copies.set(key, (copies.get(key) ?? 0) + 1); - walk(api.getBase(key)?.extendsKeys ?? []); - } - }; - walk(extendsKeys); - for (const [base, count] of copies) { - if (count > 1) problems.push(`${name} inherits ${base} ${count} times`); - } - }; - for (const base of api.bases()) check(base.key, base.extendsKeys); - for (const node of api.nodes()) check(node.name, node.extendsKeys); - - if (problems.length > 0) { - throw new Error(`ast.json declares duplicate embedded bases:\n ${problems.sort().join("\n ")}`); - } -} - -/** - * Verifies that the inheritance path containing NodeBase is embedded first. - */ -function verifyNodeBaseAtOffsetZero(): void { - const containsNodeBase = (key: string): boolean => { - if (key === "NodeBase") return true; - return api.getBase(key)?.extendsKeys.some(containsNodeBase) ?? false; - }; - const problems: string[] = []; - const check = (name: string, extendsKeys: string[], requireNodeBase: boolean) => { - const nodeBaseIndex = extendsKeys.findIndex(containsNodeBase); - if (nodeBaseIndex < 0 && requireNodeBase) { - problems.push(`${name} does not embed NodeBase`); - } - else if (nodeBaseIndex > 0) { - problems.push(`${name} embeds ${extendsKeys[nodeBaseIndex]} after ${extendsKeys.slice(0, nodeBaseIndex).join(", ")}`); - } - }; - for (const base of api.bases()) check(base.key, base.extendsKeys, false); - for (const node of api.nodes()) check(node.name, node.extendsKeys, true); - - if (problems.length > 0) { - throw new Error(`ast.json does not embed NodeBase at offset zero:\n ${problems.sort().join("\n ")}`); - } -} - -verifyNoDuplicateBases(); -verifyNodeBaseAtOffsetZero(); - -// A base with no fields and no extends is a struct{} marker. Go pads a struct whose last field is -// zero-size (so the field's address stays in bounds), so markers are embedded first, never last; being -// zero-size, they don't move NodeBase off offset zero. -function isZeroSizeBase(key: string): boolean { - const base = api.getBase(key); - return !!base && base.extendsKeys.length === 0 && base.fields.length === 0; -} - -function goEmbeds(extendsKeys: string[]): string[] { - const zeroSized: string[] = []; - const nonZeroSized: string[] = []; - for (const key of extendsKeys) { - if (isZeroSizeBase(key)) { - zeroSized.push(key); - } - else { - nonZeroSized.push(key); - } - } - return [...zeroSized, ...nonZeroSized]; -} - -function generateStructDef(w: CodeWriter, node: NodeType) { - const structName = node.name; - w.write(`type ${structName} struct {`); - w.push(); - - // Embeddings from extends (each maps to a Go struct via convention) - for (const ext of goEmbeds(node.extendsKeys)) { - w.write(ext); - } - - // Fields from members (skip inherited, Kind params, and noGo) - if (node.members.length > 0) { - for (const m of node.members) { - if (m.inherited || m.isKindParam() || m.noGo) continue; - const fieldName = m.name; - const goType = m.goOnly ? m.rawType as string : m.type.formatGoReference(); - const comment = buildFieldComment(m); - if (comment) { - w.write(`${fieldName} ${goType} ${comment}`); - } - else { - w.write(`${fieldName} ${goType}`); - } - } - } - - w.pop(); - w.write("}"); - w.write(""); -} - -function buildFieldComment(m: MemberInfo): string { - const parts: string[] = []; - if (m.optional) parts.push("Optional"); - if (parts.length > 0) return `// ${parts.join(". ")}`; - return ""; -} - -function goSubtreeFactsTerm(m: MemberInfo): string { - const access = `node.${m.name}`; - if (m.listKind === "ModifierList") { - return `propagateModifierListSubtreeFacts(${access})`; - } - if (m.type.kind === "list") { - return `propagateNodeListSubtreeFacts(${access}, propagateSubtreeFacts)`; - } - return `propagateSubtreeFacts(${access})`; -} - -function generateSubtreeFacts(w: CodeWriter, node: NodeType) { - if (!node.generateSubtreeFacts) return; - - const childMembers = schemaMembers(node).filter(m => m.isChild()); - const structName = node.name; - w.write(`func (node *${structName}) computeSubtreeFacts() SubtreeFacts {`); - w.push(); - if (childMembers.length === 0) { - w.write("return SubtreeFactsNone"); - } - else { - for (let index = 0; index < childMembers.length; index++) { - const term = goSubtreeFactsTerm(childMembers[index]); - if (index === 0) { - const suffix = childMembers.length > 1 ? " |" : ""; - w.write(`return ${term}${suffix}`); - } - else { - const suffix = index < childMembers.length - 1 ? " |" : ""; - w.write(`${term}${suffix}`); - } - } - } - w.pop(); - w.write("}"); - w.write(""); -} - -// ── Generate base struct definitions ────────────────────────────────────── - -// Bases that are hand-written and should not be generated -const HAND_WRITTEN_BASES = new Set(["NodeBase"]); - -function generateBaseStructDefs(w: CodeWriter) { - w.write("// ──────────────────────────────────────────────────────────────────────"); - w.write("// Base struct definitions"); - w.write("// ──────────────────────────────────────────────────────────────────────"); - w.write(""); - - for (const base of api.bases()) { - if (HAND_WRITTEN_BASES.has(base.key)) continue; - - const structName = base.key; - - const goExts = goEmbeds(base.extendsKeys); - - w.write(`type ${structName} struct {`); - w.push(); - - // Embeddings from extends - for (const ext of goExts) { - w.write(ext); - } - - // Schema fields - if (base.fields.length > 0) { - for (const field of base.fields) { - if (field.noGo) continue; - const goType = field.goOnly ? field.rawType as string : field.type.formatGoReference(); - const comment = field.optional ? " // Optional" : ""; - w.write(`${field.name} ${goType}${comment}`); - } - } - - w.pop(); - w.write("}"); - w.write(""); - } -} - -// ── Generate As*() cast methods ──────────────────────────────────────────── - -function generateAsCast(w: CodeWriter, name: string) { - const structName = name; - w.write(`func (n *Node) As${name}() *${structName} {`); - w.push(); - w.write(`return n.data.(*${structName})`); - w.pop(); - w.write("}"); - w.write(""); -} - -// ── Generate New*() factory methods ──────────────────────────────────────── - -function isNodeFlagsMember(m: MemberInfo): boolean { - const type = m.type; - return type.kind === "primitive" && type.name === "NodeFlags"; -} - -function emitNewFactory( - w: CodeWriter, - funcName: string, - kindName: string, - structName: string, - node: NodeType, - members: MemberInfo[], - kindMember: MemberInfo | undefined, - nodeFlagsMembers: MemberInfo[], -) { - const params = members.map(m => `${m.goParamName()} ${m.type.formatGoReference()}`).join(", "); - - w.write(`func (f *NodeFactory) ${funcName}(${params}) *Node {`); - w.push(); - - if (node.arena) { - w.write(`data := f.${api.uncapitalize(structName)}Arena.New()`); - } - else { - w.write(`data := &${structName}{}`); - } - - for (const m of members) { - if (m.isKindParam()) continue; - if (isNodeFlagsMember(m)) continue; - const value = m.bitmask ? `${m.goParamName()} & ${m.bitmask}` : m.goParamName(); - w.write(`data.${m.name} = ${value}`); - } - - if (hasTextContent(node)) { - w.write("f.textCount++"); - } - - const kindArg = kindMember ? kindMember.goParamName() : `Kind${kindName}`; - - if (nodeFlagsMembers.length > 0) { - w.write(`node := f.newNode(${kindArg}, data)`); - for (const m of nodeFlagsMembers) { - const param = m.goParamName(); - if (m.bitmask) { - w.write(`node.Flags |= ${param} & ${m.bitmask}`); - } - else { - w.write(`node.Flags = ${param}`); - } - } - w.write("return node"); - } - else { - w.write(`return f.newNode(${kindArg}, data)`); - } - - w.pop(); - w.write("}"); - w.write(""); -} - -function generateNewFactory(w: CodeWriter, node: NodeType) { - const structName = node.name; - const members = schemaMembers(node); - const kindMember = members.find(m => m.isKindParam()); - const nodeFlagsMembers = members.filter(m => isNodeFlagsMember(m)); - emitNewFactory(w, `New${node.name}`, node.syntaxKindName, structName, node, members, kindMember, nodeFlagsMembers); - for (const alias of node.kindAliases) { - emitNewFactory(w, `New${alias}`, alias, structName, node, members, kindMember, nodeFlagsMembers); - } -} - -// ── Generate Update*() factory methods ───────────────────────────────────── - -function generateUpdateFactory(w: CodeWriter, node: NodeType) { - const structName = node.name; - const members = schemaMembers(node); - - // All non-Kind members participate in Update (both child and data) - const updateMembers = members.filter(m => !m.isKindParam()); - if (updateMembers.length === 0) return; - - // Leaf nodes (no child AST node members) don't get Update methods. - if (!updateMembers.some(m => m.isChild())) return; - - // Build parameter list - const params = [ - `node *${structName}`, - ...updateMembers.map(m => `${m.goParamName()} ${m.type.formatGoReference()}`), - ].join(", "); - - w.write(`func (f *NodeFactory) Update${node.name}(${params}) *Node {`); - w.push(); - - // Build comparison (all update members) - const comparisons = updateMembers.map(m => { - const type = m.type; - // Slices can't be compared with != in Go - if (type.kind === "list" && type.listKind === "raw") { - return `!core.Same(${m.goParamName()}, node.${m.name})`; - } - return `${m.goParamName()} != node.${m.name}`; - }); - - w.write(`if ${comparisons.join(" || ")} {`); - w.push(); - - // Call New with all members (Kind from original, everything else from params) - const newArgs = members.map(m => { - if (m.isKindParam()) { - return "node.Kind"; - } - return m.goParamName(); - }).join(", "); - - if (node.kindAliases.length > 0) { - w.write("switch node.Kind {"); - w.push(); - w.write(`case ${api.kindType(`SyntaxKind.${node.syntaxKindName}`).formatGoConstant()}:`); - w.push(); - w.write(`return updateNode(f.New${node.name}(${newArgs}), node.AsNode(), f.hooks)`); - w.pop(); - for (const alias of node.kindAliases) { - w.write(`case Kind${alias}:`); - w.push(); - w.write(`return updateNode(f.New${alias}(${newArgs}), node.AsNode(), f.hooks)`); - w.pop(); - } - w.write("default:"); - w.push(); - w.write(`panic("unexpected kind in Update${node.name}: " + node.Kind.String())`); - w.pop(); - w.pop(); - w.write("}"); - } - else { - w.write(`return updateNode(f.New${node.name}(${newArgs}), node.AsNode(), f.hooks)`); - } - w.pop(); - w.write("}"); - w.write("return node.AsNode()"); - - w.pop(); - w.write("}"); - w.write(""); -} - -// ── Generate ForEachChild() ──────────────────────────────────────────────── - -function generateForEachChild(w: CodeWriter, node: NodeType) { - const members = schemaMembers(node); - const childMembers = members.filter(m => m.isChild()); - - if (childMembers.length === 0) { - // No children - empty implementation (already provided by NodeDefault) - return; - } - - const structName = node.name; - w.write(`func (node *${structName}) ForEachChild(v Visitor) bool {`); - w.push(); - - // Nodes with runtime-dependent child ordering delegate to a hand-written function. - if (node.handWrittenVisitor) { - w.write(`return forEachChild_${structName}(node, v)`); - w.pop(); - w.write("}"); - w.write(""); - return; - } - - // Build visit chain: visit(v, node.Field) || visitNodeList(v, node.Field) || ... - const parts = childMembers.map(m => { - const access = `node.${m.name}`; - const type = m.type; - const listKind = m.listKind; - if (listKind === "raw") { - return `visitNodes(v, ${access})`; - } - if (listKind === "ModifierList") { - return `visitModifiers(v, ${access})`; - } - if (listKind === "NodeList") { - return `visitNodeList(v, ${access})`; - } - return `visit(v, ${access})`; - }); - - // Join with || across multiple lines if needed - if (parts.length <= 3) { - w.write(`return ${parts.join(" || ")}`); - } - else { - w.write(`return ${parts[0]} ||`); - for (let i = 1; i < parts.length - 1; i++) { - w.write(`\t${parts[i]} ||`); - } - w.write(`\t${parts[parts.length - 1]}`); - } - - w.pop(); - w.write("}"); - w.write(""); -} - -// ── Generate ForEachChild dispatch ───────────────────────────────────────── - -// Determines whether a node has a meaningful ForEachChild implementation (i.e. -// one that is not the no-op inherited from NodeDefault). This is true for any -// generated node with child members, or for hand-written nodes (e.g. SourceFile) -// which define ForEachChild manually in ast.go. -function hasForEachChild(node: NodeType): boolean { - if (node.handWritten) return true; - return schemaMembers(node).some(m => m.isChild()); -} - -// Generates a (*Node).ForEachChild method that dispatches on node.Kind to the -// concrete node type's ForEachChild method. -// -// This deliberately avoids calling node.data.ForEachChild(v) through the -// nodeData interface. An interface (or other indirect) call is opaque to escape -// analysis, which must then assume the visitor `v` escapes; that forces caller -// closures — and any locals they capture — onto the heap. Dispatching through a -// Kind switch to a statically-resolved concrete method lets escape analysis -// prove the visitor does not escape, keeping caller closures on the stack. The -// integer switch over Kind also compiles to a jump table, making dispatch -// cheaper than the interface call it replaces. -// -// Kinds whose node has no children fall through to `default` and return false, -// matching NodeDefault.ForEachChild. -function generateForEachChildDispatch(w: CodeWriter) { - w.write("func (n *Node) ForEachChild(v Visitor) bool {"); - w.push(); - w.write("switch n.Kind {"); - for (const node of api.nodes()) { - if (!hasForEachChild(node)) continue; - const kinds = node.allKinds().map(k => k.formatGoConstant()); - if (kinds.length === 0) continue; - w.write(`case ${kinds.join(", ")}:`); - w.push(); - w.write(`return n.data.(*${node.name}).ForEachChild(v)`); - w.pop(); - } - w.write("default:"); - w.push(); - w.write("return false"); - w.pop(); - w.write("}"); - w.pop(); - w.write("}"); - w.write(""); -} - -// ── Generate VisitEachChild() ────────────────────────────────────────────── - -function generateVisitEachChild(w: CodeWriter, node: NodeType) { - const members = schemaMembers(node); - const childMembers = members.filter(m => m.isChild()); - - if (childMembers.length === 0) return; - - const structName = node.name; - w.write(`func (node *${structName}) VisitEachChild(v *NodeVisitor) *Node {`); - w.push(); - - // Nodes with runtime-dependent child ordering delegate to a hand-written function. - if (node.handWrittenVisitor) { - w.write(`return visitEachChild_${structName}(node, v)`); - w.pop(); - w.write("}"); - w.write(""); - return; - } - - // For raw slice lists, emit local variables with core.SameMap - const updateMembers = members.filter(m => !m.isKindParam()); - const rawListLocals: Map = new Map(); - for (const m of updateMembers) { - const type = m.type; - if (m.isChild() && type.kind === "list" && type.listKind === "raw") { - const localName = m.goParamName(); - rawListLocals.set(m.name, localName); - w.write(`${localName} := core.SameMap(node.${m.name}, func(n *Node) *Node { return v.visitNode(n) })`); - } - } - - const args = updateMembers.map(m => { - if (!m.isChild()) { - return `node.${m.name}`; - } - - // Raw list: use the local variable - if (rawListLocals.has(m.name)) { - return rawListLocals.get(m.name)!; - } - - const access = `node.${m.name}`; - const type = m.type; - const listKind = m.listKind; - - if (m.visit) { - return `v.visit${api.capitalize(m.visit)}(${access})`; - } - if (listKind === "ModifierList") { - return `v.visitModifiers(${access})`; - } - if (listKind === "NodeList") { - return `v.visitNodes(${access})`; - } - if (type.kind === "primitive" && type.name === "Token") return `v.visitToken(${access})`; - return `v.visitNode(${access})`; - }).join(", "); - - w.write(`return v.Factory.Update${node.name}(node, ${args})`); - - w.pop(); - w.write("}"); - w.write(""); -} - -// ── Generate Clone() ─────────────────────────────────────────────────────── - -function generateClone(w: CodeWriter, node: NodeType) { - const structName = node.name; - const members = schemaMembers(node); - - w.write(`func (node *${structName}) Clone(f NodeFactoryCoercible) *Node {`); - w.push(); - - // Build args for New* call - // Clone is a struct method, so it can access private fields directly - const args = members.map(m => { - if (m.isKindParam()) { - return "node.Kind"; - } - const type = m.declaredType; - - if (m.inherited && type?.kind === "alias" && type.name === "ModifierLike") { - return "node.Modifiers()"; - } - // Use direct field access (Clone is a struct method, can access private fields) - return `node.${m.name}`; - }).join(", "); - - if (node.kindAliases.length > 0) { - w.write("switch node.Kind {"); - w.push(); - w.write(`case ${api.kindType(`SyntaxKind.${node.syntaxKindName}`).formatGoConstant()}:`); - w.push(); - w.write(`return cloneNode(f.AsNodeFactory().New${node.name}(${args}), node.AsNode(), f.AsNodeFactory().hooks)`); - w.pop(); - for (const alias of node.kindAliases) { - w.write(`case Kind${alias}:`); - w.push(); - w.write(`return cloneNode(f.AsNodeFactory().New${alias}(${args}), node.AsNode(), f.AsNodeFactory().hooks)`); - w.pop(); - } - w.write("default:"); - w.push(); - w.write(`panic("unexpected kind in ${structName}.Clone: " + node.Kind.String())`); - w.pop(); - w.pop(); - w.write("}"); - } - else { - w.write(`return cloneNode(f.AsNodeFactory().New${node.name}(${args}), node.AsNode(), f.AsNodeFactory().hooks)`); - } - - w.pop(); - w.write("}"); - w.write(""); -} - -// ── Generate Is*() functions ─────────────────────────────────────────────── - -function generateIsFunction(w: CodeWriter, node: NodeType) { - const kindTypes = node.kindTypes(); - if (node.kindType.kind === "typeParameter") { - w.write(`func Is${node.name}(node *Node) bool {`); - w.push(); - w.write("switch node.Kind {"); - w.write(`case ${kindTypes.map(kind => kind.formatGoConstant()).join(", ")}:`); - w.push(); - w.write("return true"); - w.pop(); - w.write("}"); - w.write("return false"); - w.pop(); - w.write("}"); - w.write(""); - return; - } - - if (node.isMultiKind()) { - for (const kind of kindTypes) { - const kindName = kind.name; - w.write(`func Is${kindName}(node *Node) bool {`); - w.push(); - w.write(`return node.Kind == ${kind.formatGoConstant()}`); - w.pop(); - w.write("}"); - w.write(""); - } - return; - } - - w.write(`func Is${node.name}(node *Node) bool {`); - w.push(); - w.write(`return node.Kind == ${api.kindType(`SyntaxKind.${node.syntaxKindName}`).formatGoConstant()}`); - w.pop(); - w.write("}"); - w.write(""); - for (const alias of node.kindAliases) { - w.write(`func Is${alias}(node *Node) bool {`); - w.push(); - w.write(`return node.Kind == Kind${alias}`); - w.pop(); - w.write("}"); - w.write(""); - } -} - -// ── Generate kind alias guards ───────────────────────────────────────────── - -/** Go guard name: "IsTokenKind", "IsKeywordKind", etc. Capital I, no "Syntax". */ -function goKindGuardName(aliasName: string): string { - const tsName = kindGuardName(aliasName); // e.g. "isTokenKind" - return tsName.charAt(0).toUpperCase() + tsName.slice(1); // e.g. "IsTokenKind" -} - -function generateKindAliasGuards(w: CodeWriter) { - // JSDocSyntaxKind → "IsJSDocKind" conflicts with the hand-written IsJSDocKind in utilities.go - // which checks JSDoc NODE kinds (FirstJSDocNode..LastJSDocNode), a completely different semantic. - const skipGuards = new Set(["IsJSDocKind"]); - - for (const guard of api.kindGuards()) { - const funcName = goKindGuardName(guard.aliasName); - if (skipGuards.has(funcName)) continue; - - if (guard.type === "range") { - w.write(`func ${funcName}(kind Kind) bool {`); - w.push(); - w.write(`return kind >= Kind${guard.first} && kind <= Kind${guard.last}`); - w.pop(); - w.write("}"); - w.write(""); - } - else { - // Enumerated: generate a switch statement. - // Expand sub-aliases to their concrete members. - const expanded = api.expandKindAliasMembers(guard.aliasName); - w.write(`func ${funcName}(kind Kind) bool {`); - w.push(); - w.write("switch kind {"); - w.push(); - w.write(`case ${expanded.map(m => m.formatGoConstant()).join(", ")}:`); - w.push(); - w.write("return true"); - w.pop(); - w.pop(); - w.write("}"); - w.write("return false"); - w.pop(); - w.write("}"); - w.write(""); - } - } -} - -// ── Generate Name() accessor ─────────────────────────────────────────────── - -function generateNameAccessor(w: CodeWriter, node: NodeType) { - const members = schemaMembers(node); - const nameMember = members.find(m => m.name === "name"); - if (!nameMember) return; - - // Check if private (either directly or via trait) - const isPrivate = nameMember.inherited - ? (nameMember.inheritedField?.private || false) - : (nameMember.private || false); - if (!isPrivate) return; - - const structName = node.name; - w.write(`func (node *${structName}) Name() *DeclarationName {`); - w.push(); - w.write("return node.name"); - w.pop(); - w.write("}"); - w.write(""); -} - -// ──────────────────────────────────────────────────────────────────────────── -// Main generation -// ──────────────────────────────────────────────────────────────────────────── - -function generate(): string { - const w = new CodeWriter(); - - generateHeader(w); - generateNodeFactoryStruct(w); - - // Generate base struct definitions - generateBaseStructDefs(w); - - // Generate node type aliases (FooNode = Node for every concrete node type) - w.write("// ──────────────────────────────────────────────────────────────────────"); - w.write("// Node type aliases"); - w.write("// ──────────────────────────────────────────────────────────────────────"); - w.write(""); - w.write("type ("); - w.push(); - for (const node of api.nodes()) { - w.write(`${node.name}Node = Node`); - } - // Instantiation aliases (e.g. EndOfFile = Node, AbstractKeyword = Node) - for (const node of api.nodes()) { - if (node.instantiationAliases.length === 0) continue; - for (const alias of node.instantiationAliases) { - w.write(`${alias.name} = Node`); - } - } - w.pop(); - w.write(")"); - w.write(""); - - // NodeList type aliases - const listAliases = api.listAliases(); - if (listAliases.length > 0) { - w.write("type ("); - w.push(); - const maxLen = Math.max(...listAliases.map(alias => alias.name.length)); - for (const listAlias of listAliases) { - const padding = " ".repeat(maxLen - listAlias.name.length); - w.write(`${listAlias.name}${padding} = NodeList // NodeList[*${listAlias.elementTypeName}]`); - } - w.pop(); - w.write(")"); - w.write(""); - } - - // Node union aliases (e.g. Statement = Node, BlockOrExpression = Node) - const nodeAliases = api.nodeAliases(); - if (nodeAliases.length > 0) { - w.write("// ──────────────────────────────────────────────────────────────────────"); - w.write("// Node union aliases"); - w.write("// ──────────────────────────────────────────────────────────────────────"); - w.write(""); - w.write("type ("); - w.push(); - const maxAliasLen = Math.max(...nodeAliases.map(alias => alias.name.length)); - for (const alias of nodeAliases) { - const padding = " ".repeat(maxAliasLen - alias.name.length); - let comment: string; - if (alias.isBaseAlias && alias.baseKey) { - comment = `Node with ${alias.baseKey}`; - } - else if (alias.isUnion) { - comment = alias.unionMemberNames.join(" | "); - } - else { - comment = ""; - } - w.write(`${alias.name}${padding} = Node${comment ? ` // ${comment}` : ""}`); - } - w.pop(); - w.write(")"); - w.write(""); - } - - // Generate for each node - for (const node of api.nodes()) { - // Section comment - w.write(`// ──────────────────────────────────────────────────────────────────────`); - w.write(`// ${node.name}`); - w.write(`// ──────────────────────────────────────────────────────────────────────`); - w.write(""); - - if (!node.handWritten) { - generateStructDef(w, node); - generateNewFactory(w, node); - generateUpdateFactory(w, node); - generateForEachChild(w, node); - generateVisitEachChild(w, node); - generateClone(w, node); - generateSubtreeFacts(w, node); - generateNameAccessor(w, node); - } - generateIsFunction(w, node); - if (node.handWritten) { - w.write(""); - w.write("// Struct and factory methods hand-written in ./ast.go"); - } - } - - // ForEachChild dispatch - w.write("// ──────────────────────────────────────────────────────────────────────"); - w.write("// ForEachChild dispatch"); - w.write("// ──────────────────────────────────────────────────────────────────────"); - w.write(""); - generateForEachChildDispatch(w); - - // As*() casts - w.write("// ──────────────────────────────────────────────────────────────────────"); - w.write("// As*() cast methods"); - w.write("// ──────────────────────────────────────────────────────────────────────"); - w.write(""); - for (const node of api.nodes()) { - generateAsCast(w, node.name); - } - - // Kind alias guards - w.write("// ──────────────────────────────────────────────────────────────────────"); - w.write("// Kind alias guards"); - w.write("// ──────────────────────────────────────────────────────────────────────"); - w.write(""); - generateKindAliasGuards(w); - - return w.toString(); -} - -// ──────────────────────────────────────────────────────────────────────────── -// Write output -// ──────────────────────────────────────────────────────────────────────────── - -// ──────────────────────────────────────────────────────────────────────────── -// Kind generation -// ──────────────────────────────────────────────────────────────────────────── - -function generateKind(): string { - const w = new CodeWriter(); - w.write("// Code generated by tools/scripts/tsc/generate-go-ast.ts. DO NOT EDIT."); - w.write(""); - w.write("package ast"); - w.write(""); - w.write("//go:generate go tool golang.org/x/tools/cmd/stringer -type=Kind -output=kind_stringer_generated.go"); - w.write("//go:generate npx dprint fmt kind_stringer_generated.go"); - w.write(""); - w.write("type Kind int16"); - w.write(""); - w.write("const ("); - w.push(); - - const elements = api.kindElements(); - const markers = api.kindMarkers(); - const kindUnions = api.kindAliases(); - - // Build set of all defined kind names for validation - const definedKinds = new Set(); - for (const element of elements) { - if (element.name) { - definedKinds.add(element.name); - } - } - - // Validate marker references (markers can reference kind names or other marker names) - const markerNames = new Set(markers.map(m => m.name)); - for (const marker of markers) { - if (!definedKinds.has(marker.value) && !markerNames.has(marker.value)) { - throw new Error(`Kind marker ${marker.name} references undefined kind or marker "${marker.value}"`); - } - } - - // Validate union references - for (const kindUnion of kindUnions) { - for (const m of kindUnion.members) { - if (!definedKinds.has(m) && !api.hasKindAlias(m)) { - throw new Error(`Kind union ${kindUnion.name} references undefined kind or union "${m}"`); - } - } - } - - // Emit elements - let first = true; - for (const el of elements) { - if (!el.name) { - w.write(`// ${el.comment}`); - continue; - } - const trailing = el.comment ? ` // ${el.comment}` : ""; - if (first) { - w.write(`Kind${el.name} Kind = iota${trailing}`); - first = false; - } - else { - w.write(`Kind${el.name}${trailing}`); - } - } - - // KindCount - w.write("KindCount"); - - // Markers - const maxLen = Math.max(...markers.map(m => m.name.length)); - for (const marker of markers) { - const padding = " ".repeat(maxLen - marker.name.length); - w.write(`Kind${marker.name}${padding} = Kind${marker.value}`); - } - - w.pop(); - w.write(")"); - w.write(""); - - // Kind union type aliases - if (kindUnions.length > 0) { - w.write("type ("); - w.push(); - for (const { name, members } of kindUnions) { - function expandMembers(ms: string[]): string[] { - const result: string[] = []; - for (const m of ms) { - const alias = api.getKindAlias(m); - if (alias) { - result.push(...expandMembers(alias.members)); - } - else { - result.push(m); - } - } - return result; - } - const expanded = expandMembers(members); - const comment = expanded.map(m => `Kind${m}`).join(" | "); - w.write(`${name} = Kind // ${comment}`); - } - w.pop(); - w.write(")"); - w.write(""); - } - - return w.toString(); -} - -function writeAndFormat(filePath: string, content: string) { - fs.writeFileSync(filePath, content); - execaSync("dprint", ["fmt", filePath], { stdio: "inherit", cwd: ROOT }); - console.log(`Wrote ${filePath}`); -} - -export default function main() { - console.log("Generating Go AST code..."); - - const code = generate(); - const outPath = path.join(ROOT, "tsc/internal/ast/ast_generated.go"); - writeAndFormat(outPath, code + "\n"); - - const kindCode = generateKind(); - const kindOutPath = path.join(ROOT, "tsc/internal/ast/kind_generated.go"); - writeAndFormat(kindOutPath, kindCode + "\n"); -} - -if (process.argv[1] === fileURLToPath(import.meta.url)) { - main(); -} +/** + * Go AST code generator: reads tools/scripts/tsc/ast.json and produces internal/ast/ast_generated.go + * + * Usage: node --experimental-strip-types tools/scripts/tsc/generate-go-ast.ts + * + * Generates: + * - Struct definitions for each node kind + * - As*() cast methods on *Node + * - New*()/Update*() factory methods on *NodeFactory + * - ForEachChild() implementations + * - VisitEachChild() implementations + * - Clone() implementations + * - Polymorphic accessor switch statements (Expression(), Type(), etc.) + * - Is*() type guard functions + */ + +import { execaSync } from "execa"; +import * as fs from "node:fs"; +import * as path from "node:path"; +import { fileURLToPath } from "node:url"; +import type { + MemberInfo, + NodeType, + Type, +} from "./schema.ts"; +import { + api, + kindGuardName, +} from "./schema.ts"; + +// ──────────────────────────────────────────────────────────────────────────── +// Load schema +// ──────────────────────────────────────────────────────────────────────────── + +const ROOT = path.resolve(import.meta.dirname!, "../../.."); + +// Members that participate in factory/visitor/clone (excludes noFactory) +function schemaMembers(node: NodeType): MemberInfo[] { + return node.members.filter(m => !m.noFactory); +} + +function isTextContentType(type: Type): boolean { + if (type.kind === "primitive") { + return type.name === "string"; + } + if (type.kind === "list" && type.listKind === "raw") { + return type.elementType.kind === "primitive" && type.elementType.name === "string"; + } + return false; +} + +// Does this node have any string-typed member (triggering textCount++)? +function hasTextContent(node: NodeType): boolean { + if (node.members.length === 0) return false; + return schemaMembers(node).some(m => isTextContentType(m.type)); +} + +// ──────────────────────────────────────────────────────────────────────────── +// Code generation +// ──────────────────────────────────────────────────────────────────────────── + +class CodeWriter { + private lines: string[] = []; + private indent = 0; + + write(line: string = "") { + if (line === "") { + this.lines.push(""); + } + else { + this.lines.push("\t".repeat(this.indent) + line); + } + } + + writeRaw(line: string) { + this.lines.push(line); + } + + push() { + this.indent++; + } + pop() { + this.indent--; + } + + toString(): string { + return this.lines.join("\n"); + } +} + +function generateNodeFactoryStruct(w: CodeWriter) { + const arenaFields: { fieldName: string; typeName: string; }[] = []; + for (const node of api.nodes()) { + if (!node.arena) continue; + arenaFields.push({ + fieldName: `${api.uncapitalize(node.name)}Arena`, + typeName: node.name, + }); + } + arenaFields.push({ fieldName: "modifierListArena", typeName: "ModifierList" }); + arenaFields.push({ fieldName: "nodeListArena", typeName: "NodeList" }); + arenaFields.sort((a, b) => a.fieldName.localeCompare(b.fieldName)); + + w.write("// ──────────────────────────────────────────────────────────────────────"); + w.write("// NodeFactory"); + w.write("// ──────────────────────────────────────────────────────────────────────"); + w.write(""); + w.write("type NodeFactory struct {"); + w.push(); + w.write("hooks NodeFactoryHooks"); + for (const { fieldName, typeName } of arenaFields) { + w.write(`${fieldName} core.Arena[${typeName}]`); + } + w.write(""); + w.write("nodeCount int"); + w.write("textCount int"); + w.pop(); + w.write("}"); + w.write(""); +} + +function generateHeader(w: CodeWriter) { + w.write("// Code generated by tools/scripts/tsc/generate-go-ast.ts. DO NOT EDIT."); + w.write(""); + w.write("package ast"); + w.write(""); + w.write("import ("); + w.push(); + w.write('"sync/atomic"'); + w.write(""); + w.write('"github.com/microsoft/TypeScript/tsc/internal/core"'); + w.pop(); + w.write(")"); + w.write(""); + w.write("var _ = core.Same[string] // prevent unused import"); + w.write("var _ atomic.Uint32 // prevent unused import"); + w.write(""); +} + +// ── Generate struct definitions ──────────────────────────────────────────── + +/** + * Verifies that nothing inherits a base along more than one path. + */ +function verifyNoDuplicateBases(): void { + const problems: string[] = []; + const check = (name: string, extendsKeys: string[]) => { + const copies = new Map(); + const walk = (keys: string[]) => { + for (const key of keys) { + copies.set(key, (copies.get(key) ?? 0) + 1); + walk(api.getBase(key)?.extendsKeys ?? []); + } + }; + walk(extendsKeys); + for (const [base, count] of copies) { + if (count > 1) problems.push(`${name} inherits ${base} ${count} times`); + } + }; + for (const base of api.bases()) check(base.key, base.extendsKeys); + for (const node of api.nodes()) check(node.name, node.extendsKeys); + + if (problems.length > 0) { + throw new Error(`ast.json declares duplicate embedded bases:\n ${problems.sort().join("\n ")}`); + } +} + +/** + * Verifies that the inheritance path containing NodeBase is embedded first. + */ +function verifyNodeBaseAtOffsetZero(): void { + const containsNodeBase = (key: string): boolean => { + if (key === "NodeBase") return true; + return api.getBase(key)?.extendsKeys.some(containsNodeBase) ?? false; + }; + const problems: string[] = []; + const check = (name: string, extendsKeys: string[], requireNodeBase: boolean) => { + const nodeBaseIndex = extendsKeys.findIndex(containsNodeBase); + if (nodeBaseIndex < 0 && requireNodeBase) { + problems.push(`${name} does not embed NodeBase`); + } + else if (nodeBaseIndex > 0) { + problems.push(`${name} embeds ${extendsKeys[nodeBaseIndex]} after ${extendsKeys.slice(0, nodeBaseIndex).join(", ")}`); + } + }; + for (const base of api.bases()) check(base.key, base.extendsKeys, false); + for (const node of api.nodes()) check(node.name, node.extendsKeys, true); + + if (problems.length > 0) { + throw new Error(`ast.json does not embed NodeBase at offset zero:\n ${problems.sort().join("\n ")}`); + } +} + +verifyNoDuplicateBases(); +verifyNodeBaseAtOffsetZero(); + +// A base with no fields and no extends is a struct{} marker. Go pads a struct whose last field is +// zero-size (so the field's address stays in bounds), so markers are embedded first, never last; being +// zero-size, they don't move NodeBase off offset zero. +function isZeroSizeBase(key: string): boolean { + const base = api.getBase(key); + return !!base && base.extendsKeys.length === 0 && base.fields.length === 0; +} + +function goEmbeds(extendsKeys: string[]): string[] { + const zeroSized: string[] = []; + const nonZeroSized: string[] = []; + for (const key of extendsKeys) { + if (isZeroSizeBase(key)) { + zeroSized.push(key); + } + else { + nonZeroSized.push(key); + } + } + return [...zeroSized, ...nonZeroSized]; +} + +function generateStructDef(w: CodeWriter, node: NodeType) { + const structName = node.name; + w.write(`type ${structName} struct {`); + w.push(); + + // Embeddings from extends (each maps to a Go struct via convention) + for (const ext of goEmbeds(node.extendsKeys)) { + w.write(ext); + } + + // Fields from members (skip inherited, Kind params, and noGo) + if (node.members.length > 0) { + for (const m of node.members) { + if (m.inherited || m.isKindParam() || m.noGo) continue; + const fieldName = m.name; + const goType = m.goOnly ? m.rawType as string : m.type.formatGoReference(); + const comment = buildFieldComment(m); + if (comment) { + w.write(`${fieldName} ${goType} ${comment}`); + } + else { + w.write(`${fieldName} ${goType}`); + } + } + } + + w.pop(); + w.write("}"); + w.write(""); +} + +function buildFieldComment(m: MemberInfo): string { + const parts: string[] = []; + if (m.optional) parts.push("Optional"); + if (parts.length > 0) return `// ${parts.join(". ")}`; + return ""; +} + +function goSubtreeFactsTerm(m: MemberInfo): string { + const access = `node.${m.name}`; + if (m.listKind === "ModifierList") { + return `propagateModifierListSubtreeFacts(${access})`; + } + if (m.type.kind === "list") { + return `propagateNodeListSubtreeFacts(${access}, propagateSubtreeFacts)`; + } + return `propagateSubtreeFacts(${access})`; +} + +function generateSubtreeFacts(w: CodeWriter, node: NodeType) { + if (!node.generateSubtreeFacts) return; + + const childMembers = schemaMembers(node).filter(m => m.isChild()); + const structName = node.name; + w.write(`func (node *${structName}) computeSubtreeFacts() SubtreeFacts {`); + w.push(); + if (childMembers.length === 0) { + w.write("return SubtreeFactsNone"); + } + else { + for (let index = 0; index < childMembers.length; index++) { + const term = goSubtreeFactsTerm(childMembers[index]); + if (index === 0) { + const suffix = childMembers.length > 1 ? " |" : ""; + w.write(`return ${term}${suffix}`); + } + else { + const suffix = index < childMembers.length - 1 ? " |" : ""; + w.write(`${term}${suffix}`); + } + } + } + w.pop(); + w.write("}"); + w.write(""); +} + +// ── Generate base struct definitions ────────────────────────────────────── + +// Bases that are hand-written and should not be generated +const HAND_WRITTEN_BASES = new Set(["NodeBase"]); + +function generateBaseStructDefs(w: CodeWriter) { + w.write("// ──────────────────────────────────────────────────────────────────────"); + w.write("// Base struct definitions"); + w.write("// ──────────────────────────────────────────────────────────────────────"); + w.write(""); + + for (const base of api.bases()) { + if (HAND_WRITTEN_BASES.has(base.key)) continue; + + const structName = base.key; + + const goExts = goEmbeds(base.extendsKeys); + + w.write(`type ${structName} struct {`); + w.push(); + + // Embeddings from extends + for (const ext of goExts) { + w.write(ext); + } + + // Schema fields + if (base.fields.length > 0) { + for (const field of base.fields) { + if (field.noGo) continue; + const goType = field.goOnly ? field.rawType as string : field.type.formatGoReference(); + const comment = field.optional ? " // Optional" : ""; + w.write(`${field.name} ${goType}${comment}`); + } + } + + w.pop(); + w.write("}"); + w.write(""); + } +} + +// ── Generate As*() cast methods ──────────────────────────────────────────── + +function generateAsCast(w: CodeWriter, name: string) { + const structName = name; + w.write(`func (n *Node) As${name}() *${structName} {`); + w.push(); + w.write(`return n.data.(*${structName})`); + w.pop(); + w.write("}"); + w.write(""); +} + +// ── Generate New*() factory methods ──────────────────────────────────────── + +function isNodeFlagsMember(m: MemberInfo): boolean { + const type = m.type; + return type.kind === "primitive" && type.name === "NodeFlags"; +} + +function emitNewFactory( + w: CodeWriter, + funcName: string, + kindName: string, + structName: string, + node: NodeType, + members: MemberInfo[], + kindMember: MemberInfo | undefined, + nodeFlagsMembers: MemberInfo[], +) { + const params = members.map(m => `${m.goParamName()} ${m.type.formatGoReference()}`).join(", "); + + w.write(`func (f *NodeFactory) ${funcName}(${params}) *Node {`); + w.push(); + + if (node.arena) { + w.write(`data := f.${api.uncapitalize(structName)}Arena.New()`); + } + else { + w.write(`data := &${structName}{}`); + } + + for (const m of members) { + if (m.isKindParam()) continue; + if (isNodeFlagsMember(m)) continue; + const value = m.bitmask ? `${m.goParamName()} & ${m.bitmask}` : m.goParamName(); + w.write(`data.${m.name} = ${value}`); + } + + if (hasTextContent(node)) { + w.write("f.textCount++"); + } + + const kindArg = kindMember ? kindMember.goParamName() : `Kind${kindName}`; + + if (nodeFlagsMembers.length > 0) { + w.write(`node := f.newNode(${kindArg}, data)`); + for (const m of nodeFlagsMembers) { + const param = m.goParamName(); + if (m.bitmask) { + w.write(`node.Flags |= ${param} & ${m.bitmask}`); + } + else { + w.write(`node.Flags = ${param}`); + } + } + w.write("return node"); + } + else { + w.write(`return f.newNode(${kindArg}, data)`); + } + + w.pop(); + w.write("}"); + w.write(""); +} + +function generateNewFactory(w: CodeWriter, node: NodeType) { + const structName = node.name; + const members = schemaMembers(node); + const kindMember = members.find(m => m.isKindParam()); + const nodeFlagsMembers = members.filter(m => isNodeFlagsMember(m)); + emitNewFactory(w, `New${node.name}`, node.syntaxKindName, structName, node, members, kindMember, nodeFlagsMembers); + for (const alias of node.kindAliases) { + emitNewFactory(w, `New${alias}`, alias, structName, node, members, kindMember, nodeFlagsMembers); + } +} + +// ── Generate Update*() factory methods ───────────────────────────────────── + +function generateUpdateFactory(w: CodeWriter, node: NodeType) { + const structName = node.name; + const members = schemaMembers(node); + + // All non-Kind members participate in Update (both child and data) + const updateMembers = members.filter(m => !m.isKindParam()); + if (updateMembers.length === 0) return; + + // Leaf nodes (no child AST node members) don't get Update methods. + if (!updateMembers.some(m => m.isChild())) return; + + // Build parameter list + const params = [ + `node *${structName}`, + ...updateMembers.map(m => `${m.goParamName()} ${m.type.formatGoReference()}`), + ].join(", "); + + w.write(`func (f *NodeFactory) Update${node.name}(${params}) *Node {`); + w.push(); + + // Build comparison (all update members) + const comparisons = updateMembers.map(m => { + const type = m.type; + // Slices can't be compared with != in Go + if (type.kind === "list" && type.listKind === "raw") { + return `!core.Same(${m.goParamName()}, node.${m.name})`; + } + return `${m.goParamName()} != node.${m.name}`; + }); + + w.write(`if ${comparisons.join(" || ")} {`); + w.push(); + + // Call New with all members (Kind from original, everything else from params) + const newArgs = members.map(m => { + if (m.isKindParam()) { + return "node.Kind"; + } + return m.goParamName(); + }).join(", "); + + if (node.kindAliases.length > 0) { + w.write("switch node.Kind {"); + w.push(); + w.write(`case ${api.kindType(`SyntaxKind.${node.syntaxKindName}`).formatGoConstant()}:`); + w.push(); + w.write(`return updateNode(f.New${node.name}(${newArgs}), node.AsNode(), f.hooks)`); + w.pop(); + for (const alias of node.kindAliases) { + w.write(`case Kind${alias}:`); + w.push(); + w.write(`return updateNode(f.New${alias}(${newArgs}), node.AsNode(), f.hooks)`); + w.pop(); + } + w.write("default:"); + w.push(); + w.write(`panic("unexpected kind in Update${node.name}: " + node.Kind.String())`); + w.pop(); + w.pop(); + w.write("}"); + } + else { + w.write(`return updateNode(f.New${node.name}(${newArgs}), node.AsNode(), f.hooks)`); + } + w.pop(); + w.write("}"); + w.write("return node.AsNode()"); + + w.pop(); + w.write("}"); + w.write(""); +} + +// ── Generate ForEachChild() ──────────────────────────────────────────────── + +function generateForEachChild(w: CodeWriter, node: NodeType) { + const members = schemaMembers(node); + const childMembers = members.filter(m => m.isChild()); + + if (childMembers.length === 0) { + // No children - empty implementation (already provided by NodeDefault) + return; + } + + const structName = node.name; + w.write(`func (node *${structName}) ForEachChild(v Visitor) bool {`); + w.push(); + + // Nodes with runtime-dependent child ordering delegate to a hand-written function. + if (node.handWrittenVisitor) { + w.write(`return forEachChild_${structName}(node, v)`); + w.pop(); + w.write("}"); + w.write(""); + return; + } + + // Build visit chain: visit(v, node.Field) || visitNodeList(v, node.Field) || ... + const parts = childMembers.map(m => { + const access = `node.${m.name}`; + const type = m.type; + const listKind = m.listKind; + if (listKind === "raw") { + return `visitNodes(v, ${access})`; + } + if (listKind === "ModifierList") { + return `visitModifiers(v, ${access})`; + } + if (listKind === "NodeList") { + return `visitNodeList(v, ${access})`; + } + return `visit(v, ${access})`; + }); + + // Join with || across multiple lines if needed + if (parts.length <= 3) { + w.write(`return ${parts.join(" || ")}`); + } + else { + w.write(`return ${parts[0]} ||`); + for (let i = 1; i < parts.length - 1; i++) { + w.write(`\t${parts[i]} ||`); + } + w.write(`\t${parts[parts.length - 1]}`); + } + + w.pop(); + w.write("}"); + w.write(""); +} + +// ── Generate ForEachChild dispatch ───────────────────────────────────────── + +// Determines whether a node has a meaningful ForEachChild implementation (i.e. +// one that is not the no-op inherited from NodeDefault). This is true for any +// generated node with child members, or for hand-written nodes (e.g. SourceFile) +// which define ForEachChild manually in ast.go. +function hasForEachChild(node: NodeType): boolean { + if (node.handWritten) return true; + return schemaMembers(node).some(m => m.isChild()); +} + +// Generates a (*Node).ForEachChild method that dispatches on node.Kind to the +// concrete node type's ForEachChild method. +// +// This deliberately avoids calling node.data.ForEachChild(v) through the +// nodeData interface. An interface (or other indirect) call is opaque to escape +// analysis, which must then assume the visitor `v` escapes; that forces caller +// closures — and any locals they capture — onto the heap. Dispatching through a +// Kind switch to a statically-resolved concrete method lets escape analysis +// prove the visitor does not escape, keeping caller closures on the stack. The +// integer switch over Kind also compiles to a jump table, making dispatch +// cheaper than the interface call it replaces. +// +// Kinds whose node has no children fall through to `default` and return false, +// matching NodeDefault.ForEachChild. +function generateForEachChildDispatch(w: CodeWriter) { + w.write("func (n *Node) ForEachChild(v Visitor) bool {"); + w.push(); + w.write("switch n.Kind {"); + for (const node of api.nodes()) { + if (!hasForEachChild(node)) continue; + const kinds = node.allKinds().map(k => k.formatGoConstant()); + if (kinds.length === 0) continue; + w.write(`case ${kinds.join(", ")}:`); + w.push(); + w.write(`return n.data.(*${node.name}).ForEachChild(v)`); + w.pop(); + } + w.write("default:"); + w.push(); + w.write("return false"); + w.pop(); + w.write("}"); + w.pop(); + w.write("}"); + w.write(""); +} + +// ── Generate VisitEachChild() ────────────────────────────────────────────── + +function generateVisitEachChild(w: CodeWriter, node: NodeType) { + const members = schemaMembers(node); + const childMembers = members.filter(m => m.isChild()); + + if (childMembers.length === 0) return; + + const structName = node.name; + w.write(`func (node *${structName}) VisitEachChild(v *NodeVisitor) *Node {`); + w.push(); + + // Nodes with runtime-dependent child ordering delegate to a hand-written function. + if (node.handWrittenVisitor) { + w.write(`return visitEachChild_${structName}(node, v)`); + w.pop(); + w.write("}"); + w.write(""); + return; + } + + // For raw slice lists, emit local variables with core.SameMap + const updateMembers = members.filter(m => !m.isKindParam()); + const rawListLocals: Map = new Map(); + for (const m of updateMembers) { + const type = m.type; + if (m.isChild() && type.kind === "list" && type.listKind === "raw") { + const localName = m.goParamName(); + rawListLocals.set(m.name, localName); + w.write(`${localName} := core.SameMap(node.${m.name}, func(n *Node) *Node { return v.visitNode(n) })`); + } + } + + const args = updateMembers.map(m => { + if (!m.isChild()) { + return `node.${m.name}`; + } + + // Raw list: use the local variable + if (rawListLocals.has(m.name)) { + return rawListLocals.get(m.name)!; + } + + const access = `node.${m.name}`; + const type = m.type; + const listKind = m.listKind; + + if (m.visit) { + return `v.visit${api.capitalize(m.visit)}(${access})`; + } + if (listKind === "ModifierList") { + return `v.visitModifiers(${access})`; + } + if (listKind === "NodeList") { + return `v.visitNodes(${access})`; + } + if (type.kind === "primitive" && type.name === "Token") return `v.visitToken(${access})`; + return `v.visitNode(${access})`; + }).join(", "); + + w.write(`return v.Factory.Update${node.name}(node, ${args})`); + + w.pop(); + w.write("}"); + w.write(""); +} + +// ── Generate Clone() ─────────────────────────────────────────────────────── + +function generateClone(w: CodeWriter, node: NodeType) { + const structName = node.name; + const members = schemaMembers(node); + + w.write(`func (node *${structName}) Clone(f NodeFactoryCoercible) *Node {`); + w.push(); + + // Build args for New* call + // Clone is a struct method, so it can access private fields directly + const args = members.map(m => { + if (m.isKindParam()) { + return "node.Kind"; + } + const type = m.declaredType; + + if (m.inherited && type?.kind === "alias" && type.name === "ModifierLike") { + return "node.Modifiers()"; + } + // Use direct field access (Clone is a struct method, can access private fields) + return `node.${m.name}`; + }).join(", "); + + if (node.kindAliases.length > 0) { + w.write("switch node.Kind {"); + w.push(); + w.write(`case ${api.kindType(`SyntaxKind.${node.syntaxKindName}`).formatGoConstant()}:`); + w.push(); + w.write(`return cloneNode(f.AsNodeFactory().New${node.name}(${args}), node.AsNode(), f.AsNodeFactory().hooks)`); + w.pop(); + for (const alias of node.kindAliases) { + w.write(`case Kind${alias}:`); + w.push(); + w.write(`return cloneNode(f.AsNodeFactory().New${alias}(${args}), node.AsNode(), f.AsNodeFactory().hooks)`); + w.pop(); + } + w.write("default:"); + w.push(); + w.write(`panic("unexpected kind in ${structName}.Clone: " + node.Kind.String())`); + w.pop(); + w.pop(); + w.write("}"); + } + else { + w.write(`return cloneNode(f.AsNodeFactory().New${node.name}(${args}), node.AsNode(), f.AsNodeFactory().hooks)`); + } + + w.pop(); + w.write("}"); + w.write(""); +} + +// ── Generate Is*() functions ─────────────────────────────────────────────── + +function generateIsFunction(w: CodeWriter, node: NodeType) { + const kindTypes = node.kindTypes(); + if (node.kindType.kind === "typeParameter") { + w.write(`func Is${node.name}(node *Node) bool {`); + w.push(); + w.write("switch node.Kind {"); + w.write(`case ${kindTypes.map(kind => kind.formatGoConstant()).join(", ")}:`); + w.push(); + w.write("return true"); + w.pop(); + w.write("}"); + w.write("return false"); + w.pop(); + w.write("}"); + w.write(""); + return; + } + + if (node.isMultiKind()) { + for (const kind of kindTypes) { + const kindName = kind.name; + w.write(`func Is${kindName}(node *Node) bool {`); + w.push(); + w.write(`return node.Kind == ${kind.formatGoConstant()}`); + w.pop(); + w.write("}"); + w.write(""); + } + return; + } + + w.write(`func Is${node.name}(node *Node) bool {`); + w.push(); + w.write(`return node.Kind == ${api.kindType(`SyntaxKind.${node.syntaxKindName}`).formatGoConstant()}`); + w.pop(); + w.write("}"); + w.write(""); + for (const alias of node.kindAliases) { + w.write(`func Is${alias}(node *Node) bool {`); + w.push(); + w.write(`return node.Kind == Kind${alias}`); + w.pop(); + w.write("}"); + w.write(""); + } +} + +// ── Generate kind alias guards ───────────────────────────────────────────── + +/** Go guard name: "IsTokenKind", "IsKeywordKind", etc. Capital I, no "Syntax". */ +function goKindGuardName(aliasName: string): string { + const tsName = kindGuardName(aliasName); // e.g. "isTokenKind" + return tsName.charAt(0).toUpperCase() + tsName.slice(1); // e.g. "IsTokenKind" +} + +function generateKindAliasGuards(w: CodeWriter) { + // JSDocSyntaxKind → "IsJSDocKind" conflicts with the hand-written IsJSDocKind in utilities.go + // which checks JSDoc NODE kinds (FirstJSDocNode..LastJSDocNode), a completely different semantic. + const skipGuards = new Set(["IsJSDocKind"]); + + for (const guard of api.kindGuards()) { + const funcName = goKindGuardName(guard.aliasName); + if (skipGuards.has(funcName)) continue; + + if (guard.type === "range") { + w.write(`func ${funcName}(kind Kind) bool {`); + w.push(); + w.write(`return kind >= Kind${guard.first} && kind <= Kind${guard.last}`); + w.pop(); + w.write("}"); + w.write(""); + } + else { + // Enumerated: generate a switch statement. + // Expand sub-aliases to their concrete members. + const expanded = api.expandKindAliasMembers(guard.aliasName); + w.write(`func ${funcName}(kind Kind) bool {`); + w.push(); + w.write("switch kind {"); + w.push(); + w.write(`case ${expanded.map(m => m.formatGoConstant()).join(", ")}:`); + w.push(); + w.write("return true"); + w.pop(); + w.pop(); + w.write("}"); + w.write("return false"); + w.pop(); + w.write("}"); + w.write(""); + } + } +} + +// ── Generate Name() accessor ─────────────────────────────────────────────── + +function generateNameAccessor(w: CodeWriter, node: NodeType) { + const members = schemaMembers(node); + const nameMember = members.find(m => m.name === "name"); + if (!nameMember) return; + + // Check if private (either directly or via trait) + const isPrivate = nameMember.inherited + ? (nameMember.inheritedField?.private || false) + : (nameMember.private || false); + if (!isPrivate) return; + + const structName = node.name; + w.write(`func (node *${structName}) Name() *DeclarationName {`); + w.push(); + w.write("return node.name"); + w.pop(); + w.write("}"); + w.write(""); +} + +// ──────────────────────────────────────────────────────────────────────────── +// Main generation +// ──────────────────────────────────────────────────────────────────────────── + +function generate(): string { + const w = new CodeWriter(); + + generateHeader(w); + generateNodeFactoryStruct(w); + + // Generate base struct definitions + generateBaseStructDefs(w); + + // Generate node type aliases (FooNode = Node for every concrete node type) + w.write("// ──────────────────────────────────────────────────────────────────────"); + w.write("// Node type aliases"); + w.write("// ──────────────────────────────────────────────────────────────────────"); + w.write(""); + w.write("type ("); + w.push(); + for (const node of api.nodes()) { + w.write(`${node.name}Node = Node`); + } + // Instantiation aliases (e.g. EndOfFile = Node, AbstractKeyword = Node) + for (const node of api.nodes()) { + if (node.instantiationAliases.length === 0) continue; + for (const alias of node.instantiationAliases) { + w.write(`${alias.name} = Node`); + } + } + w.pop(); + w.write(")"); + w.write(""); + + // NodeList type aliases + const listAliases = api.listAliases(); + if (listAliases.length > 0) { + w.write("type ("); + w.push(); + const maxLen = Math.max(...listAliases.map(alias => alias.name.length)); + for (const listAlias of listAliases) { + const padding = " ".repeat(maxLen - listAlias.name.length); + w.write(`${listAlias.name}${padding} = NodeList // NodeList[*${listAlias.elementTypeName}]`); + } + w.pop(); + w.write(")"); + w.write(""); + } + + // Node union aliases (e.g. Statement = Node, BlockOrExpression = Node) + const nodeAliases = api.nodeAliases(); + if (nodeAliases.length > 0) { + w.write("// ──────────────────────────────────────────────────────────────────────"); + w.write("// Node union aliases"); + w.write("// ──────────────────────────────────────────────────────────────────────"); + w.write(""); + w.write("type ("); + w.push(); + const maxAliasLen = Math.max(...nodeAliases.map(alias => alias.name.length)); + for (const alias of nodeAliases) { + const padding = " ".repeat(maxAliasLen - alias.name.length); + let comment: string; + if (alias.isBaseAlias && alias.baseKey) { + comment = `Node with ${alias.baseKey}`; + } + else if (alias.isUnion) { + comment = alias.unionMemberNames.join(" | "); + } + else { + comment = ""; + } + w.write(`${alias.name}${padding} = Node${comment ? ` // ${comment}` : ""}`); + } + w.pop(); + w.write(")"); + w.write(""); + } + + // Generate for each node + for (const node of api.nodes()) { + // Section comment + w.write(`// ──────────────────────────────────────────────────────────────────────`); + w.write(`// ${node.name}`); + w.write(`// ──────────────────────────────────────────────────────────────────────`); + w.write(""); + + if (!node.handWritten) { + generateStructDef(w, node); + generateNewFactory(w, node); + generateUpdateFactory(w, node); + generateForEachChild(w, node); + generateVisitEachChild(w, node); + generateClone(w, node); + generateSubtreeFacts(w, node); + generateNameAccessor(w, node); + } + generateIsFunction(w, node); + if (node.handWritten) { + w.write(""); + w.write("// Struct and factory methods hand-written in ./ast.go"); + } + } + + // ForEachChild dispatch + w.write("// ──────────────────────────────────────────────────────────────────────"); + w.write("// ForEachChild dispatch"); + w.write("// ──────────────────────────────────────────────────────────────────────"); + w.write(""); + generateForEachChildDispatch(w); + + // As*() casts + w.write("// ──────────────────────────────────────────────────────────────────────"); + w.write("// As*() cast methods"); + w.write("// ──────────────────────────────────────────────────────────────────────"); + w.write(""); + for (const node of api.nodes()) { + generateAsCast(w, node.name); + } + + // Kind alias guards + w.write("// ──────────────────────────────────────────────────────────────────────"); + w.write("// Kind alias guards"); + w.write("// ──────────────────────────────────────────────────────────────────────"); + w.write(""); + generateKindAliasGuards(w); + + return w.toString(); +} + +// ──────────────────────────────────────────────────────────────────────────── +// Write output +// ──────────────────────────────────────────────────────────────────────────── + +// ──────────────────────────────────────────────────────────────────────────── +// Kind generation +// ──────────────────────────────────────────────────────────────────────────── + +function generateKind(): string { + const w = new CodeWriter(); + w.write("// Code generated by tools/scripts/tsc/generate-go-ast.ts. DO NOT EDIT."); + w.write(""); + w.write("package ast"); + w.write(""); + w.write("//go:generate go tool golang.org/x/tools/cmd/stringer -type=Kind -output=kind_stringer_generated.go"); + w.write("//go:generate npx dprint fmt kind_stringer_generated.go"); + w.write(""); + w.write("type Kind int16"); + w.write(""); + w.write("const ("); + w.push(); + + const elements = api.kindElements(); + const markers = api.kindMarkers(); + const kindUnions = api.kindAliases(); + + // Build set of all defined kind names for validation + const definedKinds = new Set(); + for (const element of elements) { + if (element.name) { + definedKinds.add(element.name); + } + } + + // Validate marker references (markers can reference kind names or other marker names) + const markerNames = new Set(markers.map(m => m.name)); + for (const marker of markers) { + if (!definedKinds.has(marker.value) && !markerNames.has(marker.value)) { + throw new Error(`Kind marker ${marker.name} references undefined kind or marker "${marker.value}"`); + } + } + + // Validate union references + for (const kindUnion of kindUnions) { + for (const m of kindUnion.members) { + if (!definedKinds.has(m) && !api.hasKindAlias(m)) { + throw new Error(`Kind union ${kindUnion.name} references undefined kind or union "${m}"`); + } + } + } + + // Emit elements + let first = true; + for (const el of elements) { + if (!el.name) { + w.write(`// ${el.comment}`); + continue; + } + const trailing = el.comment ? ` // ${el.comment}` : ""; + if (first) { + w.write(`Kind${el.name} Kind = iota${trailing}`); + first = false; + } + else { + w.write(`Kind${el.name}${trailing}`); + } + } + + // KindCount + w.write("KindCount"); + + // Markers + const maxLen = Math.max(...markers.map(m => m.name.length)); + for (const marker of markers) { + const padding = " ".repeat(maxLen - marker.name.length); + w.write(`Kind${marker.name}${padding} = Kind${marker.value}`); + } + + w.pop(); + w.write(")"); + w.write(""); + + // Kind union type aliases + if (kindUnions.length > 0) { + w.write("type ("); + w.push(); + for (const { name, members } of kindUnions) { + function expandMembers(ms: string[]): string[] { + const result: string[] = []; + for (const m of ms) { + const alias = api.getKindAlias(m); + if (alias) { + result.push(...expandMembers(alias.members)); + } + else { + result.push(m); + } + } + return result; + } + const expanded = expandMembers(members); + const comment = expanded.map(m => `Kind${m}`).join(" | "); + w.write(`${name} = Kind // ${comment}`); + } + w.pop(); + w.write(")"); + w.write(""); + } + + return w.toString(); +} + +function writeAndFormat(filePath: string, content: string) { + fs.writeFileSync(filePath, content); + execaSync("dprint", ["fmt", filePath], { stdio: "inherit", cwd: ROOT }); + console.log(`Wrote ${filePath}`); +} + +export default function main() { + console.log("Generating Go AST code..."); + + const code = generate(); + const outPath = path.join(ROOT, "tsc/internal/ast/ast_generated.go"); + writeAndFormat(outPath, code + "\n"); + + const kindCode = generateKind(); + const kindOutPath = path.join(ROOT, "tsc/internal/ast/kind_generated.go"); + writeAndFormat(kindOutPath, kindCode + "\n"); +} + +if (process.argv[1] === fileURLToPath(import.meta.url)) { + main(); +} diff --git a/tools/scripts/tsc/generate-ts-ast.ts b/tools/scripts/tsc/generate-ts-ast.ts index 753c7286ae094..be7d1a0b21870 100644 --- a/tools/scripts/tsc/generate-ts-ast.ts +++ b/tools/scripts/tsc/generate-ts-ast.ts @@ -1,1740 +1,1740 @@ -/** - * Schema-driven TypeScript AST code generator. - * Reads tools/scripts/tsc/ast.json and produces: - * - packages/typescript/src/ast/ast.generated.ts - * - packages/typescript/src/ast/factory.generated.ts - * - packages/typescript/src/ast/is.generated.ts - * - * Usage: node --experimental-strip-types tools/scripts/tsc/generate-ts-ast.ts - */ - -import { execaSync } from "execa"; -import * as fs from "node:fs"; -import * as path from "node:path"; -import { fileURLToPath } from "node:url"; -import type { - MemberInfo, - NodeType, - Type, -} from "./schema.ts"; -import { - api, - kindGuardName, -} from "./schema.ts"; - -// ──────────────────────────────────────────────────────────────────────────── -// Load schema -// ──────────────────────────────────────────────────────────────────────────── - -const ROOT = path.resolve(import.meta.dirname!, "../../.."); - -// TS members only (filter noTS/inherited issues) -function tsMembers(node: NodeType): MemberInfo[] { - return node.members.filter(member => !member.isKindParam() && !member.noTS); -} - -function isInheritedFromTsBase(member: MemberInfo): boolean { - return member.inherited && !!member.inheritedField && !member.inheritedField.noTS; -} - -// NodeBase fields (e.g. Flags) are class-level properties on NodeObject, -// not data members stored in _data. -const nodeBaseFieldNames = new Set( - api.bases().find(b => b.name === "NodeBase")?.fields.filter(f => !f.noTS).map(f => f.name) ?? [], -); - -function isNodeBaseProperty(member: MemberInfo): boolean { - return member.inherited && nodeBaseFieldNames.has(member.name); -} - -function tsInterfaceMembers(node: NodeType): MemberInfo[] { - return tsMembers(node).filter(member => !isInheritedFromTsBase(member) || member.hasTypeScriptOverride()); -} - -// ──────────────────────────────────────────────────────────────────────────── -// TS-specific naming and typing -// ──────────────────────────────────────────────────────────────────────────── - -// The JSDoc comment `text` member is stored as a raw []string in Go, but the -// encoder joins it into a single string and both the RemoteNode API and the -// local factory expose it as a scalar string. Collapse just this member to its -// element type in the generated TS. This is intentionally keyed on the property -// name rather than on "raw list of a primitive" so the exception stays narrow -// and explicit; `text` is the only such member. -function collapsedTextType(name: string, type: Type): string | undefined { - if (name === "text" && type.kind === "list" && type.listKind === "raw") { - return type.elementType.formatTypeScript(); - } - return undefined; -} - -function tsParamTypeFrom(name: string, type: Type): string { - return collapsedTextType(name, type) - ?? (type.kind === "list" ? type.raw().formatTypeScript() : type.formatTypeScript()); -} - -function tsParamName(propName: string): string { - if (propName === "arguments") return "arguments_"; - return propName; -} - -// Get factory members in schema order. -function factoryMembers(node: NodeType): MemberInfo[] { - return tsMembers(node).filter(m => !m.noFactory); -} - -function factoryNodes(): NodeType[] { - return api.nodes().filter(node => !node.handWritten && !isVariantNode(node)); -} - -function kindTypeParameter(node: NodeType): { name: string; constraint: string; } | undefined { - const kindType = node.kindType; - if (kindType.kind !== "typeParameter") return undefined; - return { name: kindType.name, constraint: kindType.constraint.formatTypeScript() }; -} - -function isVariantNode(node: NodeType): boolean { - return node.isMultiKind() && node.kindType.kind !== "typeParameter"; -} - -function syntaxKindChecksForNode(node: NodeType): string[] { - return node.allKinds().map(k => k.formatTypeScript()); -} - -// ──────────────────────────────────────────────────────────────────────────── -// Bases for ast.generated.ts -// ──────────────────────────────────────────────────────────────────────────── - -const goOnlyBases = new Set( - api.bases().filter(base => base.isGoOnly()).map(base => base.key), -); - -// Expand goOnly bases transitively: when a goOnly base appears in an extends -// list, replace it with its own non-goOnly extends (recursively). -function expandTsExtends(exts: string[]): string[] { - const result: string[] = []; - const seen = new Set(); - - function expand(bases: string[]) { - for (const b of bases) { - if (seen.has(b)) continue; - seen.add(b); - if (goOnlyBases.has(b)) { - const base = api.getBase(b); - if (base?.extends) { - expand(base.extendsKeys); - } - } - else { - result.push(b); - } - } - } - - expand(exts); - return result; -} - -function baseTsName(base: string | NodeType): string { - return typeof base === "string" ? base : base.formatTypeScript(); -} - -function resolveBaseExtends(base: NodeType): string { - if (base.extendsKeys.length > 0) { - const tsExts = expandTsExtends(base.extendsKeys) - .map(e => baseTsName(e)); - if (tsExts.length > 0) return tsExts.join(", "); - } - return "Node"; -} - -function deriveNodeTsExtends(node: NodeType): string { - const tsExts = expandTsExtends(node.extendsKeys) - .map(e => baseTsName(e)); - return tsExts.length > 0 ? tsExts.join(", ") : "Node"; -} - -function generateBaseFields(base: NodeType): string { - if (base.fields.length === 0) return ""; - let result = ""; - for (const field of base.fields) { - if (field.noTS) continue; - const name = api.uncapitalize(field.name); - const type = collapsedTextType(field.name, field.type) ?? field.type.formatTypeScript(); - const opt = field.optional ? "?" : ""; - result += `\n readonly ${name}${opt}: ${type};`; - } - return result; -} - -// ──────────────────────────────────────────────────────────────────────────── -// Variant types derived from multi-kind Go nodes with enum Kind members. -// ──────────────────────────────────────────────────────────────────────────── - -// Compute TS variant interfaces from multi-kind nodes with enum Kind members. -// For each enum value, create a separate interface; a union type alias uses the Go struct name. -interface TsVariant { - tsName: string; - syntaxKind: string; - tsExtends: string; - members?: MemberInfo[]; - handWrittenVisitor?: boolean; -} - -interface VisitorEntry { - tsName: string; - syntaxKind: string; - updateName: string; - members: MemberInfo[]; - handWrittenVisitor?: boolean; -} - -function computeTsVariants(): TsVariant[] { - const result: TsVariant[] = []; - for (const node of api.nodes()) { - if (!isVariantNode(node)) continue; - - // tsMembers excludes the Kind member; use full set so factory/visitor/clone - // code sees inherited members too. Interface generation filters further. - const otherMembers = tsMembers(node); - const tsExtends = deriveNodeTsExtends(node); - - const kindValues = node.kindTypes().map(kind => kind.name); - - for (const kindValue of kindValues) { - result.push({ - tsName: kindValue, - syntaxKind: kindValue, - tsExtends, - members: otherMembers, - handWrittenVisitor: node.handWrittenVisitor, - }); - } - } - return result; -} - -const tsVariants = computeTsVariants(); - -function visitorNodes(): NodeType[] { - return api.nodes().filter(node => !isVariantNode(node)); -} - -function visitorMembers(node: NodeType): MemberInfo[] { - return factoryMembers(node).filter(member => member.isChild()); -} - -function visitorGuardName(member: MemberInfo): string | undefined { - const typeName = member.type.formatTypeScript(); - if (typeName === "Node" || typeName === "Declaration") { - return undefined; - } - - if (member.typeGuard) { - return member.typeGuard; - } - - switch (member.type.kind) { - case "node": - case "alias": - return `is${member.type.name}`; - case "union": { - throw new Error(`No visitor guard configured for union child type ${member.type.formatTypeScript()} on ${member.node?.name}.${member.name}`); - } - default: - return undefined; - } -} - -function visitorVisitExpression(member: MemberInfo): { expression: string; guardName?: string; } { - const propName = api.uncapitalize(member.name); - if (member.listKind === "raw") { - return { expression: `visitNodesArray(node.${propName}, visitor)` }; - } - if (member.type.kind === "list") { - return { expression: `visitNodes(node.${propName}, visitor)` }; - } - const guardName = visitorGuardName(member); - const guardArg = guardName ? `, ${guardName}` : ""; - return { expression: `visitNode(node.${propName}, visitor${guardArg})`, guardName }; -} - -function visitorEntries(): VisitorEntry[] { - const entries: VisitorEntry[] = []; - - for (const node of visitorNodes()) { - const members = visitorMembers(node); - if (members.length === 0) continue; - entries.push({ - tsName: node.name, - syntaxKind: node.syntaxKindName, - updateName: `update${node.name}`, - members, - handWrittenVisitor: node.handWrittenVisitor, - }); - } - - for (const variant of tsVariants) { - const members = (variant.members || []).filter(member => member.isChild()); - if (members.length === 0) continue; - entries.push({ - tsName: variant.tsName, - syntaxKind: variant.syntaxKind, - updateName: `update${variant.tsName}`, - members, - handWrittenVisitor: variant.handWrittenVisitor, - }); - } - - return entries; -} - -// ──────────────────────────────────────────────────────────────────────────── -// Code generation: ast.generated.ts -// ──────────────────────────────────────────────────────────────────────────── - -function generateAstGenerated(): string { - const eol = "\r\n"; - const parts: string[] = []; - - // ── SyntaxKind union aliases from schema ── - const kindUnions = api.kindAliases(); - for (const { name, members } of kindUnions) { - const parts2 = members.map(m => { - // If it references another union, use the union name directly - if (api.hasKindAlias(m)) return m; - return `SyntaxKind.${m}`; - }); - parts.push(`export type ${name} = ${parts2.join(" | ")};`); - } - - // ── Base interfaces from schema ── - parts.push(""); - for (const base of api.bases()) { - if (goOnlyBases.has(base.key)) continue; - const name = baseTsName(base); - const extendsClause = resolveBaseExtends(base); - const brandProp = base.brand ? `\n readonly ${base.brand}: any;` : ""; - const fields = generateBaseFields(base); - parts.push(`export interface ${name} extends ${extendsClause} {${brandProp}${fields}\n}`); - } - - // ── Concrete node interfaces from schema ── - parts.push(""); - for (const node of api.nodes()) { - if (node.handWritten) continue; - if (isVariantNode(node)) continue; // handled as variant interfaces below - - const interfaceName = node.name; - const extendsClause = deriveNodeTsExtends(node); - - // Generic type parameters - let typeParamStr = ""; - if (node.typeParameters.length > 0) { - const tps = node.typeParameters.map(tp => { - let s = tp.name + " extends " + tp.constraint; - if (tp.default) s += " = " + tp.default; - return s; - }); - typeParamStr = `<${tps.join(", ")}>`; - } - - // Kind line: use type param name if available, otherwise SyntaxKind constant - const kindLine = `\n readonly kind: ${node.kindType.formatTypeScript()};`; - - let memberLines = ""; - for (const m of tsInterfaceMembers(node)) { - if (m.rawType === "SyntaxKind" && m.name === "SyntaxKind") continue; - const propName = api.uncapitalize(m.name); - const propType = m.type.formatTypeScript(); - const opt = m.optional ? "?" : ""; - memberLines += `\n readonly ${propName}${opt}: ${propType};`; - } - - parts.push(`export interface ${interfaceName}${typeParamStr} extends ${extendsClause} {${kindLine}${memberLines}\n}`); - } - - // ── Union types from schema ── - parts.push(""); - for (const alias of api.nodeAliases()) { - if (alias.isUnion) { - const members = alias.unionMemberTypes.map(type => { - if (type.baseKind() === "kind") { - // SyntaxKind -> Node - debugger; - } - return type.formatTypeScript(); - }).join(" | "); - if (!members) continue; - parts.push(`export type ${alias.name} = ${members};`); - } - else if (alias.base) { - parts.push(`export type ${alias.name} = ${baseTsName(alias.base)};`); - } - } - - // ── Variant interfaces (from multi-kind nodes with enum Kind) ── - parts.push(""); - for (const v of tsVariants) { - const kindLine = `\n readonly kind: SyntaxKind.${v.syntaxKind};`; - let memberLines = ""; - if (v.members) { - for (const m of v.members) { - // For interfaces, skip inherited members (they come from the base type) - if (isInheritedFromTsBase(m) && !m.hasTypeScriptOverride()) continue; - const propName = api.uncapitalize(m.name); - const propType = m.type.formatTypeScript(); - const opt = m.optional ? "?" : ""; - memberLines += `\n readonly ${propName}${opt}: ${propType};`; - } - } - parts.push(`export interface ${v.tsName} extends ${v.tsExtends} {${kindLine}${memberLines}\n}`); - } - - // ── Union type aliases for multi-kind nodes ── - for (const node of api.nodes()) { - if (!isVariantNode(node)) continue; - const kindValues = node.kindTypes().map(kind => kind.name); - parts.push(`export type ${node.name} = ${kindValues.join(" | ")};`); - } - - // ── Instantiation aliases for generic nodes ── - for (const node of api.nodes()) { - if (node.instantiationAliases.length === 0) continue; - const interfaceName = node.name; - for (const { name: aliasName, typeArg } of node.instantiationAliases) { - // If the type arg is a kind union name, use it directly; otherwise prefix with SyntaxKind. - const arg = api.hasKindAlias(typeArg) ? typeArg : `SyntaxKind.${typeArg}`; - parts.push(`export type ${aliasName} = ${interfaceName}<${arg}>;`); - } - } - - // ── NodeList type aliases ── - for (const listAlias of api.listAliases()) { - parts.push(`export type ${listAlias.name} = NodeArray<${listAlias.elementTypeName}>;`); - } - - const body = parts.join(eol); - - // ── Header (with imports resolved from the schema model) ── - // Types referenced by the generated declarations but not declared here are - // hand-written in ast.ts; import them so the module type-checks. - const astImports = ["Node", "NodeArray", ...unresolvedAstImports()]; - astImports.sort((a, b) => a.localeCompare(b)); - const header = `// Code generated by tools/scripts/tsc/generate-ts-ast.ts. DO NOT EDIT. - -import type { ModifierFlags } from "#enums/modifierFlags"; -import type { NodeFlags } from "#enums/nodeFlags"; -import { SyntaxKind } from "#enums/syntaxKind"; -import { TokenFlags } from "#enums/tokenFlags"; -import type { ${astImports.join(", ")} } from "./ast.ts";`; - - return [header, body].join(eol); -} - -// TypeScript names that are available without importing from ast.ts: built-in -// primitives, and enums/base types imported from other modules in the header. -const astBuiltinTypeNames = new Set([ - "any", - "boolean", - "never", - "null", - "number", - "string", - "undefined", - "unknown", - "void", - "ModifierFlags", - "NodeFlags", - "TokenFlags", - "SyntaxKind", - "Node", - "NodeArray", -]); - -// Walks a schema Type and records every named type it references in the -// generated TypeScript. Node and alias references contribute their name; -// unions and lists recurse into their members; kinds and type parameters emit -// `SyntaxKind.*` / locally-scoped names and contribute nothing. Names the -// schema cannot resolve fall back to a PrimitiveType whose name is the raw -// reference (e.g. a hand-written `ast.ts` type such as JsxTagNamePropertyAccess). -function collectTypeReferences(type: Type, into: Set): void { - switch (type.kind) { - case "node": - case "alias": - into.add(type.name); - break; - case "union": - for (const member of type.types) collectTypeReferences(member, into); - break; - case "list": - collectTypeReferences(type.elementType, into); - break; - case "primitive": { - const name = type.formatTypeScript(); - if (!astBuiltinTypeNames.has(name)) into.add(name); - break; - } - case "kind": - case "typeParameter": - // Emitted as `SyntaxKind.*` or a locally-declared name; nothing to import. - break; - } -} - -// The set of type names declared within ast.generated.ts itself. Mirrors the -// declarations emitted by generateAstGenerated so references to them are not -// mistaken for hand-written ast.ts types. -function declaredAstNames(): Set { - const declared = new Set(); - for (const { name } of api.kindAliases()) declared.add(name); - for (const base of api.bases()) { - if (!goOnlyBases.has(base.key)) declared.add(baseTsName(base)); - } - // Concrete node interfaces and variant-node union aliases both use node.name. - for (const node of api.nodes()) { - if (!node.handWritten) declared.add(node.name); - } - for (const alias of api.nodeAliases()) declared.add(alias.name); - for (const variant of tsVariants) declared.add(variant.tsName); - for (const node of api.nodes()) { - for (const { name } of node.instantiationAliases) declared.add(name); - } - for (const listAlias of api.listAliases()) declared.add(listAlias.name); - return declared; -} - -// Type names referenced by ast.generated.ts but not declared within it, and so -// hand-written in ast.ts and needing an import. Derived from the schema model: -// the same Type objects generateAstGenerated emits are walked for references. -function unresolvedAstImports(): string[] { - const referenced = new Set(); - - for (const base of api.bases()) { - if (goOnlyBases.has(base.key)) continue; - for (const field of base.fields) { - if (field.noTS) continue; - collectTypeReferences(field.type, referenced); - } - } - for (const node of api.nodes()) { - if (node.handWritten || isVariantNode(node)) continue; - collectTypeReferences(node.kindType, referenced); - for (const member of tsInterfaceMembers(node)) { - collectTypeReferences(member.type, referenced); - } - } - for (const alias of api.nodeAliases()) { - if (alias.isUnion) { - for (const member of alias.unionMemberTypes) collectTypeReferences(member, referenced); - } - else if (alias.base) { - collectTypeReferences(alias.base, referenced); - } - } - for (const variant of tsVariants) { - for (const member of variant.members || []) { - collectTypeReferences(member.type, referenced); - } - } - for (const listAlias of api.listAliases()) { - if (listAlias.elementType) collectTypeReferences(listAlias.elementType, referenced); - } - - const declared = declaredAstNames(); - return [...referenced] - .filter(name => !declared.has(name) && !astBuiltinTypeNames.has(name)) - .sort((a, b) => a.localeCompare(b)); -} - -// ──────────────────────────────────────────────────────────────────────────── -// Code generation: factory.generated.ts -// ──────────────────────────────────────────────────────────────────────────── - -function generateFactory(): string { - const out: string[] = []; - const importTypes = new Set(); - - // Collect all factory-generatable schema nodes - const concreteFactoryNodes = factoryNodes(); - - // ── Collect property names for getters ── - // Exclude names that conflict with NodeObject's own properties - const nodeObjectOwnProps = new Set(["kind", "flags", "pos", "end", "parent", "_data"]); - const getterNames = new Set(); - for (const node of api.nodes()) { - for (const m of tsMembers(node)) { - const name = api.uncapitalize(m.name); - if (!nodeObjectOwnProps.has(name)) getterNames.add(name); - } - } - // Add variant property names - for (const v of tsVariants) { - for (const m of v.members || []) { - const name = api.uncapitalize(m.name); - if (!nodeObjectOwnProps.has(name)) getterNames.add(name); - } - } - // Extra getters for hand-written nodes (SourceFile) whose data properties - // aren't in the schema but are set by hand-written factory functions. - for ( - const name of [ - "fileName", - "originalText", - "contentMapper", - "virtualFileName", - "diagnosticDirectives", - "supplementalSourceFileNames", - "canonicalSourceFileName", - "path", - "languageVariant", - "scriptKind", - "spanMap", - "isDeclarationFile", - "referencedFiles", - "typeReferenceDirectives", - "libReferenceDirectives", - "imports", - "moduleAugmentations", - "ambientModuleNames", - "externalModuleIndicator", - "tokenCache", - ] - ) { - getterNames.add(name); - } - - // ── Collect all types referenced in factory ── - function addType(t: string) { - // Extract base type names from complex types - const stripped = t.replace(/readonly\s+/g, "").replace(/\[\]/g, "") - .replace(/NodeArray<(.+)>/g, "$1").replace(/\s*\|\s*/g, "|"); - for (const part of stripped.split("|")) { - const clean = part.trim(); - if ( - clean && clean !== "undefined" && clean !== "boolean" && clean !== "string" - && clean !== "any" - && clean !== "number" && clean !== "true" && !clean.startsWith("SyntaxKind.") - ) { - importTypes.add(clean); - } - } - } - - // Pre-collect types from factory nodes - for (const node of concreteFactoryNodes) { - addType(node.name); - const kindTypeParam = kindTypeParameter(node); - if (kindTypeParam) addType(kindTypeParam.constraint); - for (const m of tsMembers(node)) { - addType(tsParamTypeFrom(m.name, m.type)); - addType(m.type.formatTypeScript()); - } - } - for (const v of tsVariants) { - addType(v.tsName); - for (const m of v.members || []) { - addType(m.type.formatTypeScript()); - } - } - // Always needed - for (const t of ["Node", "NodeArray", "KeywordTypeSyntaxKind", "Token", "SourceFile", "KeywordTypeNode", "EndOfFile", "ImportPhaseModifierSyntaxKind", "Path", "Statement"]) { - importTypes.add(t); - } - const handWrittenCloneHelpers = api.nodes() - .filter(node => node.handWritten && !isVariantNode(node)) - .map(node => ({ node, helperName: `clone${node.name}Data` })); - // Remove enum types that are imported separately - importTypes.delete("NodeFlags"); - importTypes.delete("SyntaxKind"); - importTypes.delete("TokenFlags"); - - // ── Header ── - out.push(`// Code generated by tools/scripts/tsc/generate-ts-ast.ts. DO NOT EDIT.`); - out.push(""); - out.push(`import { NodeFlags } from "#enums/nodeFlags";`); - out.push(`import { SyntaxKind } from "#enums/syntaxKind";`); - out.push(`import { TokenFlags } from "#enums/tokenFlags";`); - - const sortedImports = [...importTypes].sort((a, b) => a.localeCompare(b)); - out.push(`import type {`); - for (const t of sortedImports) { - out.push(` ${t},`); - } - out.push(`} from "./ast.ts";`); - out.push(`import { getTokenPosOfNode } from "./astnav.ts";`); - if (handWrittenCloneHelpers.length > 0) { - out.push(`import {`); - for (const helperName of [...new Set(handWrittenCloneHelpers.map(h => h.helperName))].sort((a, b) => a.localeCompare(b))) { - out.push(` ${helperName},`); - } - out.push(`} from "./utils.ts";`); - } - - // Import hand-written forEachChild functions - const handWrittenForEachChildImports: string[] = []; - for (const node of api.nodes()) { - if (node.handWrittenVisitor && !node.handWritten) { - const members = tsMembers(node); - if (members.filter(m => m.isChild()).length > 0) { - for (const kind of node.kindTypes()) { - handWrittenForEachChildImports.push(`forEachChildOf${kind.name}`); - } - } - } - } - if (handWrittenForEachChildImports.length > 0) { - out.push(`import {`); - for (const name of handWrittenForEachChildImports.sort((a, b) => a.localeCompare(b))) { - out.push(` ${name},`); - } - out.push(`} from "./visitor.ts";`); - } - - out.push(""); - - // ── NodeObject class ── - const sortedGetters = [...getterNames].sort(); - out.push(`export class NodeObject {`); - out.push(` readonly kind: SyntaxKind;`); - out.push(` flags: NodeFlags = 0 as NodeFlags;`); - out.push(` readonly pos: number = -1;`); - out.push(` readonly end: number = -1;`); - out.push(` parent: Node = undefined!;`); - out.push(` _data: any;`); - out.push(``); - out.push(` constructor(kind: SyntaxKind, data: any) {`); - out.push(` this.kind = kind;`); - out.push(` this._data = data;`); - out.push(` }`); - out.push(``); - - // Getters - for (const name of sortedGetters) { - out.push(` get ${name}(): any { return this._data?.${name}; }`); - } - out.push(``); - - // Methods - out.push(` forEachChild(visitor: (node: Node) => T, visitArray?: (nodes: NodeArray) => T): T | undefined {`); - out.push(` const fn = forEachChildTable[this.kind];`); - out.push(` return fn ? fn(this._data, visitor, visitArray) : undefined;`); - out.push(` }`); - out.push(``); - out.push(` getSourceFile(): SourceFile {`); - out.push(` let node: Node = this as unknown as Node;`); - out.push(` while (node.parent) node = node.parent;`); - out.push(` return node as unknown as SourceFile;`); - out.push(` }`); - out.push(``); - out.push(` getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number {`); - out.push(` return getTokenPosOfNode(this as unknown as Node, sourceFile ?? this.getSourceFile(), includeJsDocComment);`); - out.push(` }`); - out.push(``); - out.push(` getFullStart(): number {`); - out.push(` return this.pos;`); - out.push(` }`); - out.push(``); - out.push(` getEnd(): number {`); - out.push(` return this.end;`); - out.push(` }`); - out.push(``); - out.push(` getWidth(sourceFile?: SourceFile): number {`); - out.push(` return this.getEnd() - this.getStart(sourceFile);`); - out.push(` }`); - out.push(``); - out.push(` getFullWidth(): number {`); - out.push(` return this.end - this.pos;`); - out.push(` }`); - out.push(``); - out.push(` getLeadingTriviaWidth(sourceFile?: SourceFile): number {`); - out.push(` return this.getStart(sourceFile) - this.pos;`); - out.push(` }`); - out.push(``); - out.push(` getFullText(sourceFile?: SourceFile): string {`); - out.push(` return (sourceFile ?? this.getSourceFile()).text.substring(this.pos, this.end);`); - out.push(` }`); - out.push(``); - out.push(` getText(sourceFile?: SourceFile): string {`); - out.push(` sourceFile ??= this.getSourceFile();`); - out.push(` return sourceFile.text.substring(this.getStart(sourceFile), this.end);`); - out.push(` }`); - out.push(`}`); - out.push(``); - - // ── Utility functions ── - out.push(`function isNodeArray(array: readonly T[]): array is NodeArray {`); - out.push(` return "pos" in array && "end" in array;`); - out.push(`}`); - out.push(``); - out.push(`export function createNodeArray(elements: readonly T[], pos: number = -1, end: number = -1): NodeArray {`); - out.push(` if (isNodeArray(elements)) return elements;`); - out.push(` const arr = elements.slice() as unknown as NodeArray & { pos: number; end: number; };`); - out.push(` arr.pos = pos;`); - out.push(` arr.end = end;`); - out.push(` return arr;`); - out.push(`}`); - out.push(``); - out.push(`export function cloneNode(node: T): T {`); - out.push(` const data = cloneNodeData(node);`); - out.push(` const clone = new NodeObject(node.kind, data);`); - out.push(` (clone as any).flags = node.flags;`); - out.push(` (clone as any).pos = node.pos;`); - out.push(` (clone as any).end = node.end;`); - out.push(` return clone as unknown as T;`); - out.push(`}`); - out.push(``); - - // ── cloneNodeData ── - out.push(`function cloneNodeData(node: Node): any {`); - out.push(` const n = node as any;`); - out.push(` switch (node.kind) {`); - // Schema nodes - for (const node of api.nodes()) { - if (node.handWritten) continue; - if (isVariantNode(node)) continue; - const members = tsMembers(node).filter(m => !isNodeBaseProperty(m)); - if (members.length === 0) continue; - const props = members.map(m => api.uncapitalize(m.name)); - if (props.length === 0) continue; - const syntaxKinds = node.allKinds(); - for (const sk of syntaxKinds) { - out.push(` case ${sk.formatTypeScript()}:`); - } - const propStr = props.map(p => `${p}: n.${p}`).join(", "); - out.push(` return { ${propStr} };`); - } - // Variant nodes - for (const v of tsVariants) { - if (!v.members || v.members.length === 0) continue; - const props = v.members.map(m => api.uncapitalize(m.name)); - const propStr = props.map(p => `${p}: n.${p}`).join(", "); - out.push(` case SyntaxKind.${v.syntaxKind}:`); - out.push(` return { ${propStr} };`); - } - for (const { node, helperName } of handWrittenCloneHelpers) { - for (const sk of node.allKinds()) { - out.push(` case ${sk.formatTypeScript()}:`); - } - out.push(` return ${helperName}(n);`); - } - out.push(` default:`); - out.push(` return undefined;`); - out.push(` }`); - out.push(`}`); - out.push(``); - - // ── forEachChildTable ── - out.push(`type ForEachChildFunction = (data: any, cbNode: (node: Node) => T, cbNodes: ((nodes: NodeArray) => T) | undefined) => T | undefined;`); - out.push(``); - out.push(`const forEachChildTable: Record = {`); - // Schema nodes - for (const node of api.nodes()) { - if (node.handWritten) continue; - if (isVariantNode(node)) continue; - const members = tsMembers(node); - const childMembers = members.filter(m => m.isChild()); - if (childMembers.length === 0) continue; - const syntaxKinds = node.allKinds(); - if (node.handWrittenVisitor) { - for (const sk of syntaxKinds) { - out.push(` [${sk.formatTypeScript()}]: forEachChildOf${node.name},`); - } - continue; - } - const visits = childMembers.map(m => { - const propName = api.uncapitalize(m.name); - const listKind = m.listKind; - if (listKind) { - return `visitNodes(cbNode, cbNodes, data.${propName})`; - } - return `visitNode(cbNode, data.${propName})`; - }); - const body = visits.join(" ||\n "); - for (const sk of syntaxKinds) { - out.push(` [${sk.formatTypeScript()}]: (data, cbNode, cbNodes) =>`); - out.push(` ${body},`); - } - } - // Variant nodes - for (const v of tsVariants) { - if (!v.members) continue; - const childMembers = v.members.filter(m => m.type.baseKind() === "node" || m.type.baseKind() === "list"); - if (childMembers.length === 0) continue; - if (v.handWrittenVisitor) { - out.push(` [SyntaxKind.${v.syntaxKind}]: forEachChildOf${v.tsName},`); - continue; - } - const visits = childMembers.map(m => { - const propName = api.uncapitalize(m.name); - if (m.listKind) return `visitNodes(cbNode, cbNodes, data.${propName})`; - return `visitNode(cbNode, data.${propName})`; - }); - const body = visits.join(" ||\n "); - out.push(` [SyntaxKind.${v.syntaxKind}]: (data, cbNode, cbNodes) =>`); - out.push(` ${body},`); - } - // SourceFile is handWritten so we add its forEachChild entry manually - out.push(` [SyntaxKind.SourceFile]: (data, cbNode, cbNodes) =>`); - out.push(` visitNodes(cbNode, cbNodes, data.statements) ||`); - out.push(` visitNode(cbNode, data.endOfFileToken),`); - out.push(`};`); - out.push(``); - - // ── visitNode / visitNodes ── - out.push(`function visitNode(cbNode: (node: Node) => T, node: Node | undefined): T | undefined {`); - out.push(` return node ? cbNode(node) : undefined;`); - out.push(`}`); - out.push(``); - out.push(`function visitNodes(cbNode: (node: Node) => T, cbNodes: ((nodes: NodeArray) => T) | undefined, nodes: NodeArray | undefined): T | undefined {`); - out.push(` if (!nodes) return undefined;`); - out.push(` if (cbNodes) return cbNodes(nodes);`); - out.push(` for (const node of nodes) {`); - out.push(` const result = cbNode(node);`); - out.push(` if (result) return result;`); - out.push(` }`); - out.push(` return undefined;`); - out.push(`}`); - out.push(``); - - // ── Create functions (schema nodes) ── - const createNames: string[] = []; - for (const node of concreteFactoryNodes) { - const iface = node.name; - const members = factoryMembers(node); - const funcName = `create${iface}`; - createNames.push(funcName); - const kindTypeParam = kindTypeParameter(node); - - // Build params - const paramParts: string[] = []; - if (kindTypeParam) { - paramParts.push(`kind: ${kindTypeParam.name}`); - } - - // First pass: determine which members are "required" (cannot be omitted). - // A member is required if it is not optional and not a bool. - const isRequired = members.map(m => { - if (m.optional) return false; - const mt = m.type; - if (!m.isChild() && mt.kind === "primitive" && mt.name === "bool") return false; - return true; - }); - - // Find the index of the last required member. - let lastRequiredIdx = -1; - for (let i = members.length - 1; i >= 0; i--) { - if (isRequired[i]) { - lastRequiredIdx = i; - break; - } - } - - for (let mi = 0; mi < members.length; mi++) { - const m = members[mi]; - const propName = api.uncapitalize(m.name); - const paramName = tsParamName(propName); - const paramType = tsParamTypeFrom(m.name, m.type); - const memberType = m.type; - const isBool = !m.isChild() && memberType.kind === "primitive" && memberType.name === "bool"; - - if (isBool) { - if (mi < lastRequiredIdx) { - // Required params follow — use default value instead of optional - paramParts.push(`${paramName}: ${paramType} = false`); - } - else { - paramParts.push(`${paramName}?: ${paramType}`); - } - } - else if (m.optional) { - if (mi < lastRequiredIdx) { - // Required params follow — cannot use ? syntax - paramParts.push(`${paramName}: ${paramType} | undefined`); - } - else { - paramParts.push(`${paramName}?: ${paramType}`); - } - } - else { - paramParts.push(`${paramName}: ${paramType}`); - } - } - - // Build data object (includes ALL tsMembers, not just factory params) - const allMembers = tsMembers(node); - const dataParts: string[] = []; - const nodeProps: { propName: string; paramName: string; }[] = []; - for (const m of allMembers) { - const propName = api.uncapitalize(m.name); - const paramName = tsParamName(propName); - const listKind = m.listKind; - const isParam = members.includes(m); - // Members inherited from NodeBase (e.g. Flags) are class-level - // properties on NodeObject, not data members. - if (isNodeBaseProperty(m)) { - if (isParam) { - nodeProps.push({ propName, paramName }); - } - continue; - } - if (listKind === "NodeList" || listKind === "ModifierList") { - if (isParam) { - if (m.optional) { - dataParts.push(`${propName}: ${paramName} ? createNodeArray(${paramName}) : undefined`); - } - else { - dataParts.push(`${propName}: createNodeArray(${paramName})`); - } - } - } - else { - if (isParam) { - if (propName === paramName) { - dataParts.push(propName); // shorthand - } - else { - dataParts.push(`${propName}: ${paramName}`); - } - } - } - } - - const paramsStr = paramParts.join(", "); - const dataStr = dataParts.length > 0 - ? `{\n ${dataParts.join(",\n ")},\n }` - : "undefined"; - const genericParamStr = kindTypeParam - ? `<${kindTypeParam.name} extends ${kindTypeParam.constraint}>` - : ""; - const returnType = kindTypeParam - ? `${iface}<${kindTypeParam.name}>` - : iface; - const kindExpr = kindTypeParam ? "kind" : `SyntaxKind.${node.syntaxKindName}`; - - out.push(`export function ${funcName}${genericParamStr}(${paramsStr}): ${returnType} {`); - if (nodeProps.length > 0) { - out.push(` const node = new NodeObject(${kindExpr}, ${dataStr}) as unknown as ${returnType};`); - for (const { propName, paramName } of nodeProps) { - out.push(` (node as any).${propName} = ${paramName};`); - } - out.push(` return node;`); - } - else { - out.push(` return new NodeObject(${kindExpr}, ${dataStr}) as unknown as ${returnType};`); - } - out.push(`}`); - out.push(``); - } - - // ── Create functions (variant nodes) ── - for (const v of tsVariants) { - const funcName = `create${v.tsName}`; - createNames.push(funcName); - if (!v.members || v.members.length === 0) { - out.push(`export function ${funcName}(): ${v.tsName} {`); - out.push(` return new NodeObject(SyntaxKind.${v.syntaxKind}, undefined) as unknown as ${v.tsName};`); - out.push(`}`); - } - else { - const paramParts: string[] = []; - const dataParts: string[] = []; - for (const m of v.members) { - const propName = api.uncapitalize(m.name); - const paramName = tsParamName(propName); - const paramType = tsParamTypeFrom(m.name, m.type); - if (m.optional) { - paramParts.push(`${paramName}: ${paramType} | undefined`); - } - else { - paramParts.push(`${paramName}: ${paramType}`); - } - if (m.listKind === "NodeList") { - if (m.optional) { - dataParts.push(`${propName}: ${paramName} ? createNodeArray(${paramName}) : undefined`); - } - else { - dataParts.push(`${propName}: createNodeArray(${paramName})`); - } - } - else if (propName === paramName) { - dataParts.push(propName); - } - else { - dataParts.push(`${propName}: ${paramName}`); - } - } - const paramsStr = paramParts.join(", "); - const dataStr = `{\n ${dataParts.join(",\n ")},\n }`; - out.push(`export function ${funcName}(${paramsStr}): ${v.tsName} {`); - out.push(` return new NodeObject(SyntaxKind.${v.syntaxKind}, ${dataStr}) as unknown as ${v.tsName};`); - out.push(`}`); - } - out.push(``); - } - - // ── Update functions (schema nodes) ── - for (const node of concreteFactoryNodes) { - const iface = node.name; - const members = factoryMembers(node); - // Update params: all child members - const updateChildMembers = members.filter(m => m.isChild()); - if (updateChildMembers.length === 0) continue; - - const funcName = `update${iface}`; - const paramParts = [`node: ${iface}`]; - - // Determine which update child members are required - const updateIsRequired = updateChildMembers.map(m => !m.optional); - let updateLastRequiredIdx = -1; - for (let i = updateChildMembers.length - 1; i >= 0; i--) { - if (updateIsRequired[i]) { - updateLastRequiredIdx = i; - break; - } - } - - for (let mi = 0; mi < updateChildMembers.length; mi++) { - const m = updateChildMembers[mi]; - const propName = api.uncapitalize(m.name); - const paramName = tsParamName(propName); - const paramType = tsParamTypeFrom(m.name, m.type); - if (m.optional) { - if (mi < updateLastRequiredIdx) { - paramParts.push(`${paramName}: ${paramType} | undefined`); - } - else { - paramParts.push(`${paramName}?: ${paramType}`); - } - } - else { - paramParts.push(`${paramName}: ${paramType}`); - } - } - - // Comparison - const comparisons = updateChildMembers.map(m => { - const propName = api.uncapitalize(m.name); - const paramName = tsParamName(propName); - return `node.${propName} !== ${paramName}`; - }); - - // Create call args (all factory members, using params for update children, node.prop for rest) - const updateChildNames = new Set(updateChildMembers.map(m => m.name)); - const createArgs = members.map(m => { - const propName = api.uncapitalize(m.name); - const paramName = tsParamName(propName); - if (updateChildNames.has(m.name)) return paramName; - return `node.${propName}`; - }); - - out.push(`export function ${funcName}(${paramParts.join(", ")}): ${iface} {`); - out.push(` return ${comparisons.join(" || ")} ? create${iface}(${createArgs.join(", ")}) : node;`); - out.push(`}`); - out.push(``); - } - - // ── Update functions (variant nodes) ── - for (const v of tsVariants) { - if (!v.members || v.members.length === 0) continue; - const childMembers = v.members.filter(m => m.type.baseKind() === "node" || m.type.baseKind() === "list"); - if (childMembers.length === 0) continue; - - const paramParts = [`node: ${v.tsName}`]; - for (const m of childMembers) { - const propName = api.uncapitalize(m.name); - const paramName = tsParamName(propName); - const paramType = tsParamTypeFrom(m.name, m.type); - if (m.optional) { - paramParts.push(`${paramName}: ${paramType} | undefined`); - } - else { - paramParts.push(`${paramName}: ${paramType}`); - } - } - - const comparisons = childMembers.map(m => { - const propName = api.uncapitalize(m.name); - const paramName = tsParamName(propName); - return `node.${propName} !== ${paramName}`; - }); - - const createArgs = (v.members || []).map(m => { - const propName = api.uncapitalize(m.name); - const paramName = tsParamName(propName); - if (m.type.baseKind() !== "node" && m.type.baseKind() !== "list") return `node.${propName}`; - return paramName; - }); - - out.push(`export function update${v.tsName}(${paramParts.join(", ")}): ${v.tsName} {`); - out.push(` return ${comparisons.join(" || ")} ? create${v.tsName}(${createArgs.join(", ")}) : node;`); - out.push(`}`); - out.push(``); - } - - // ── createSourceFile (hand-written — SourceFile is handWritten in schema) ── - out.push(`export function createSourceFile(statements: readonly Statement[], endOfFileToken: EndOfFile, text: string, fileName: string, path: Path): SourceFile {`); - out.push(` return new NodeObject(SyntaxKind.SourceFile, {`); - out.push(` statements: createNodeArray(statements),`); - out.push(` endOfFileToken,`); - out.push(` text,`); - out.push(` originalText: text,`); - out.push(` spanMap: undefined,`); - out.push(` contentMapper: undefined,`); - out.push(` virtualFileName: undefined,`); - out.push(` diagnosticDirectives: undefined,`); - out.push(` fileName,`); - out.push(` path,`); - out.push(` }) as unknown as SourceFile;`); - out.push(`}`); - out.push(``); - - out.push(`function cloneSourceFileWithChanges(source: SourceFile, statements: readonly Statement[], endOfFileToken: EndOfFile): SourceFile {`); - out.push(` return new NodeObject(SyntaxKind.SourceFile, {`); - out.push(` ...cloneSourceFileData(source),`); - out.push(` statements: createNodeArray(statements),`); - out.push(` endOfFileToken,`); - out.push(` }) as unknown as SourceFile;`); - out.push(`}`); - out.push(``); - - // ── updateSourceFile (hand-written in schema) ── - out.push(`export function updateSourceFile(node: SourceFile, statements: readonly Statement[], endOfFileToken: EndOfFile): SourceFile {`); - out.push(` return node.statements !== statements || node.endOfFileToken !== endOfFileToken`); - out.push(` ? cloneSourceFileWithChanges(node, statements, endOfFileToken)`); - out.push(` : node;`); - out.push(`}`); - out.push(``); - - // Clean up - while (out.length > 0 && out[out.length - 1] === "") out.pop(); - out.push(""); - - let result = out.join("\n"); - result = result.replace(/\n/g, "\r\n"); - return result; -} - -// ──────────────────────────────────────────────────────────────────────────── -// Code generation: is.ts -// ──────────────────────────────────────────────────────────────────────────── - -function generateIsGenerated(): string { - const out: string[] = []; - const importTypes = new Set(); - - // ── Simple is* guards from schema nodes ── - const guards: { funcName: string; typeName: string; kindChecks: string[]; kindAliasConstraint?: string; }[] = []; - - for (const node of api.nodes()) { - if (isVariantNode(node)) continue; - - const typeName = node.name; - const funcName = `is${typeName}`; - // If the node has a type parameter constrained to a kind alias, use the kind-level guard. - const kindParam = node.typeParameters.find(tp => tp.constraint && api.hasKindAlias(tp.constraint)); - const kindAliasConstraint = kindParam?.constraint; - guards.push({ funcName, typeName, kindChecks: syntaxKindChecksForNode(node), kindAliasConstraint }); - importTypes.add(typeName); - } - - // Variant node guards - for (const v of tsVariants) { - const funcName = `is${v.tsName}`; - guards.push({ funcName, typeName: v.tsName, kindChecks: [`SyntaxKind.${v.syntaxKind}`] }); - importTypes.add(v.tsName); - } - - // Always needed - importTypes.add("Node"); - - // ── Composite guards from unions ── - interface CompositeGuard { - funcName: string; - typeName: string; - body: string; - } - const compositeGuards: CompositeGuard[] = []; - - // Build a map from instantiation alias name → SyntaxKind name. - // E.g. "AbstractKeyword" → "AbstractKeyword", "TrueLiteral" → "TrueKeyword" - const instantiationAliasToKind = new Map(); - for (const node of api.nodes()) { - for (const { name: aliasName, typeArg } of node.instantiationAliases) { - // typeArg is either a SyntaxKind name or a kind alias name - if (!api.hasKindAlias(typeArg)) { - instantiationAliasToKind.set(aliasName, typeArg); - } - } - } - - // Recursively resolve union members to SyntaxKind checks. - // Returns null if any member cannot be resolved to concrete kinds. - function resolveUnionKindChecks(members: readonly string[]): string[] | null { - const checks: string[] = []; - for (const m of members) { - const node = api.getNode(m); - if (node) { - checks.push(...syntaxKindChecksForNode(node)); - continue; - } - const variant = tsVariants.find(v => v.tsName === m); - if (variant) { - checks.push(`SyntaxKind.${variant.syntaxKind}`); - continue; - } - // Check if it's a Token instantiation alias (e.g. AbstractKeyword, TrueLiteral) - const kindName = instantiationAliasToKind.get(m); - if (kindName) { - checks.push(`SyntaxKind.${kindName}`); - continue; - } - // Recurse into sub-aliases - const subAlias = api.nodeAliases().find(a => a.name === m); - if (subAlias?.isUnion) { - const sub = resolveUnionKindChecks(subAlias.unionMemberNames); - if (sub === null) return null; - checks.push(...sub); - continue; - } - // Check if it's a kind alias (e.g. ModifierSyntaxKind) — expand to SyntaxKind checks - if (api.hasKindAlias(m)) { - const kindMembers = api.expandKindAliasMembers(m); - checks.push(...kindMembers.map(k => k.formatTypeScript())); - continue; - } - // Unresolvable member (e.g. Expression, Statement) — can't enumerate kinds - return null; - } - return checks; - } - - for (const alias of api.nodeAliases()) { - if (!alias.isUnion) continue; - const members = alias.unionMemberNames; - if (members.length === 0) continue; - - const typeName = alias.name; - const funcName = `is${typeName}`; - if (guards.some(g => g.funcName === funcName)) continue; - - const kindChecksRaw = resolveUnionKindChecks(members); - - if (kindChecksRaw === null || kindChecksRaw.length === 0) { - // Union contains unresolvable members (e.g. Expression) — skip, - // must be hand-written in is.ts - continue; - } - - importTypes.add(typeName); - const kindChecks = [...new Set(kindChecksRaw)]; - let body: string; - - // Build conditions: delegate to kind guard for kind alias members, direct checks for others - const kindAliasMembers = members.filter(m => api.hasKindAlias(m)); - if (kindAliasMembers.length > 0) { - // Collect direct SyntaxKind checks for non-kind-alias members - const nonKindAliasMembers = members.filter(m => !api.hasKindAlias(m)); - const directChecks = nonKindAliasMembers.length > 0 - ? resolveUnionKindChecks(nonKindAliasMembers) ?? [] - : []; - const conditions: string[] = [ - ...kindAliasMembers.map(m => `${kindGuardName(m)}(kind)`), - ...[...new Set(directChecks)].map(k => `kind === ${k}`), - ]; - body = `const kind = node.kind;\n return ${conditions.join(" || ")};`; - } - else if (kindChecks.length <= 3) { - body = `return ${kindChecks.map(k => `node.kind === ${k}`).join(" || ")};`; - } - else { - body = `const kind = node.kind;\n return ${kindChecks.map(k => `kind === ${k}`).join(" || ")};`; - } - - compositeGuards.push({ funcName, typeName, body }); - } - - // ── Kind alias guards (SyntaxKind-level) ── - // Generate `is(kind: SyntaxKind): kind is ` for each enumerated kind alias union. - // Range-based aliases generate `is(kind: SyntaxKind): boolean` with range checks. - // Function names drop "Syntax" from the kind alias name: isTriviaSyntaxKind → isTriviaKind. - - interface KindAliasGuard { - funcName: string; - typeName: string; - conditions: string[]; - } - const kindAliasGuards: KindAliasGuard[] = []; - interface RangeKindAliasGuard { - funcName: string; - first: string; - last: string; - } - const rangeKindAliasGuards: RangeKindAliasGuard[] = []; - for (const guard of api.kindGuards()) { - const { guardName: funcName } = guard; - // Skip if a node-level guard with the same name already exists - if (guards.some(g => g.funcName === funcName)) continue; - if (compositeGuards.some(g => g.funcName === funcName)) continue; - - if (guard.type === "range") { - rangeKindAliasGuards.push({ funcName, first: guard.first, last: guard.last }); - } - else { - importTypes.add(guard.aliasName); - const conditions = guard.members.map(m => { - if (api.hasKindAlias(m)) return `${kindGuardName(m)}(kind)`; - return `kind === SyntaxKind.${m}`; - }); - kindAliasGuards.push({ funcName, typeName: guard.aliasName, conditions }); - } - } - - // ── Token instantiation alias guards ── - // For Token instantiation aliases like `BinaryOperatorToken = Token`, - // generate `is(node: Node): node is ` using the kind-level guard. - // Also generate guards for single-kind Token aliases (like EndOfFile, DotToken, etc.) - // Token aliases whose underlying kind alias is range-based return boolean (not a type predicate). - const rangeKindAliasNames = new Set(rangeKindAliasGuards.map(g => g.funcName)); - interface TokenAliasGuard { - funcName: string; - typeName: string; - body: string; - isRangeBased: boolean; - } - const tokenAliasGuards: TokenAliasGuard[] = []; - for (const node of api.nodes()) { - for (const { name: aliasName, typeArg } of node.instantiationAliases) { - const funcName = `is${aliasName}`; - // Skip if already generated - if (guards.some(g => g.funcName === funcName)) continue; - if (compositeGuards.some(g => g.funcName === funcName)) continue; - - if (api.hasKindAlias(typeArg)) { - const isRangeBased = rangeKindAliasNames.has(kindGuardName(typeArg)); - if (!isRangeBased) importTypes.add(aliasName); - // Multi-kind token alias: use the kind-level guard - tokenAliasGuards.push({ funcName, typeName: aliasName, body: `return ${kindGuardName(typeArg)}(node.kind);`, isRangeBased }); - } - else { - importTypes.add(aliasName); - // Single-kind token alias: direct SyntaxKind check - tokenAliasGuards.push({ funcName, typeName: aliasName, body: `return node.kind === SyntaxKind.${typeArg};`, isRangeBased: false }); - } - } - } - - // ── Build output ── - out.push(`// Code generated by tools/scripts/tsc/generate-ts-ast.ts. DO NOT EDIT.`); - out.push(``); - out.push(`import { SyntaxKind } from "#enums/syntaxKind";`); - const sortedImports = [...importTypes].sort((a, b) => a.localeCompare(b)); - out.push(`import type {`); - for (const t of sortedImports) { - out.push(` ${t},`); - } - out.push(`} from "./ast.ts";`); - out.push(`import type { NodeHandle as AsyncNodeHandle } from "../api/async/api.ts";`); - out.push(`import type { NodeHandle as SyncNodeHandle } from "../api/sync/api.ts";`); - out.push(`type NodeHandleLike = { kind: SyntaxKind; resolve(...args: any): any; };`); - // Note that we must `& T` to satisfy the requirements of a type guard, and it must be _second_ so the narrowed `resolve` overload from the specialization is selected (order matters) - out.push(`type SpecializeNodeHandle, U extends Node> = T extends AsyncNodeHandle ? AsyncNodeHandle & T : SyncNodeHandle & T;`); - out.push(``); - - // ── Simple guards ── - for (const g of guards) { - out.push(`export function ${g.funcName}(node: Node): node is ${g.typeName} {`); - if (g.kindAliasConstraint) { - // Use the kind-level guard for nodes with a kind alias constraint - out.push(` return ${kindGuardName(g.kindAliasConstraint)}(node.kind);`); - } - else if (g.kindChecks.length === 1) { - out.push(` return node.kind === ${g.kindChecks[0]};`); - } - else { - out.push(` switch (node.kind) {`); - for (const kindCheck of g.kindChecks) { - out.push(` case ${kindCheck}:`); - } - out.push(` return true;`); - out.push(` default:`); - out.push(` return false;`); - out.push(` }`); - } - out.push(`}`); - out.push(``); - out.push(`export declare namespace ${g.funcName} {`); - out.push(` function Handle>(node: T): node is SpecializeNodeHandle;`); - out.push(`}`); - out.push(`${g.funcName}.Handle = ${g.funcName} as any;`); - out.push(``); - } - - // ── Composite guards ── - for (const g of compositeGuards) { - out.push(`export function ${g.funcName}(node: Node): node is ${g.typeName} {`); - out.push(` ${g.body}`); - out.push(`}`); - out.push(``); - out.push(`export declare namespace ${g.funcName} {`); - out.push(` function Handle>(node: T): node is SpecializeNodeHandle;`); - out.push(`}`); - out.push(``); - out.push(`${g.funcName}.Handle = ${g.funcName} as any;`); - out.push(``); - } - - // ── Kind alias guards ── - for (const g of kindAliasGuards) { - out.push(`export function ${g.funcName}(kind: SyntaxKind): kind is ${g.typeName} {`); - if (g.conditions.length === 1) { - out.push(` return ${g.conditions[0]};`); - } - else { - out.push(` return ${g.conditions.join("\n || ")};`); - } - out.push(`}`); - out.push(``); - } - - // ── Range-based kind alias guards ── - for (const g of rangeKindAliasGuards) { - out.push(`export function ${g.funcName}(kind: SyntaxKind): boolean {`); - out.push(` return kind >= SyntaxKind.${g.first} && kind <= SyntaxKind.${g.last};`); - out.push(`}`); - out.push(``); - } - - // ── Token alias guards ── - for (const g of tokenAliasGuards) { - const returnType = g.isRangeBased ? `boolean` : `node is ${g.typeName}`; - const handleReturnType = g.isRangeBased ? `boolean` : `node is SpecializeNodeHandle`; - out.push(`export function ${g.funcName}(node: Node): ${returnType} {`); - out.push(` ${g.body}`); - out.push(`}`); - out.push(``); - out.push(`export declare namespace ${g.funcName} {`); - out.push(` function Handle>(node: T): ${handleReturnType};`); - out.push(`}`); - out.push(``); - out.push(`${g.funcName}.Handle = ${g.funcName} as any;`); - out.push(``); - } - - while (out.length > 0 && out[out.length - 1] === "") out.pop(); - out.push(""); - - let result = out.join("\n"); - result = result.replace(/\n/g, "\r\n"); - return result; -} - -// ──────────────────────────────────────────────────────────────────────────── -// Code generation: visitor.generated.ts -// ──────────────────────────────────────────────────────────────────────────── - -function generateVisitor(): string { - const out: string[] = []; - const entries = visitorEntries(); - const typeImports = new Set(["Node", "NodeArray"]); - const factoryImports = new Set(["createNodeArray"]); - const isImports = new Set(); - const handWrittenImports = new Set(); - - for (const entry of entries) { - typeImports.add(entry.tsName); - if (entry.handWrittenVisitor) { - handWrittenImports.add(`visitEachChildOf${entry.tsName}`); - } - else { - factoryImports.add(entry.updateName); - } - for (const member of entry.members) { - const { guardName } = visitorVisitExpression(member); - if (guardName) isImports.add(guardName); - } - } - - out.push(`// Code generated by tools/scripts/tsc/generate-ts-ast.ts. DO NOT EDIT.`); - out.push(""); - out.push(`import { SyntaxKind } from "#enums/syntaxKind";`); - out.push(`import type {`); - for (const typeName of [...typeImports].sort((a, b) => a.localeCompare(b))) { - out.push(` ${typeName},`); - } - out.push(`} from "./ast.ts";`); - out.push(`import {`); - for (const name of [...factoryImports].sort((a, b) => a.localeCompare(b))) { - out.push(` ${name},`); - } - out.push(`} from "./factory.generated.ts";`); - out.push(`import {`); - for (const name of [...isImports].sort((a, b) => a.localeCompare(b))) { - out.push(` ${name},`); - } - out.push(`} from "./is.ts";`); - if (handWrittenImports.size > 0) { - out.push(`import {`); - for (const name of [...handWrittenImports].sort((a, b) => a.localeCompare(b))) { - out.push(` ${name},`); - } - out.push(`} from "./visitor.ts";`); - } - out.push(""); - out.push(`/**`); - out.push(` * A callback that receives a node and returns a visited node (or undefined to remove it).`); - out.push(` */`); - out.push(`export type Visitor = (node: Node) => Node | undefined;`); - out.push(""); - out.push(`/**`); - out.push(` * Visits a Node using the supplied visitor, possibly returning a new Node in its place.`); - out.push(` *`); - out.push(` * - If the input node is undefined, then the output is undefined.`); - out.push(` * - If the visitor returns undefined, then the output is undefined.`); - out.push(` * - If the output node is not undefined, then it will satisfy the test function.`); - out.push(` * - In order to obtain a return type that is more specific than \`Node\`, a test`); - out.push(` * function must be provided, and that function must be a type predicate.`); - out.push(` *`); - out.push(` * @param node The Node to visit.`); - out.push(` * @param visitor The callback used to visit the Node.`); - out.push(` * @param test A callback to execute to verify the Node is valid.`); - out.push(` */`); - out.push(`export function visitNode(`); - out.push(` node: TIn,`); - out.push(` visitor: Visitor,`); - out.push(` test: (node: Node) => node is TOut,`); - out.push(`): TOut | (TIn & undefined);`); - out.push(`/**`); - out.push(` * Visits a Node using the supplied visitor, possibly returning a new Node in its place.`); - out.push(` *`); - out.push(` * - If the input node is undefined, then the output is undefined.`); - out.push(` * - If the visitor returns undefined, then the output is undefined.`); - out.push(` *`); - out.push(` * @param node The Node to visit.`); - out.push(` * @param visitor The callback used to visit the Node.`); - out.push(` * @param test An optional callback to execute to verify the Node is valid.`); - out.push(` */`); - out.push(`export function visitNode(`); - out.push(` node: TIn,`); - out.push(` visitor: Visitor,`); - out.push(` test?: (node: Node) => boolean,`); - out.push(`): Node | (TIn & undefined);`); - out.push(`export function visitNode(node: Node | undefined, visitor: Visitor, test?: (node: Node) => boolean): Node | undefined {`); - out.push(` if (node === undefined) return undefined;`); - out.push(` const visited = visitor(node);`); - out.push(` if (visited !== undefined && test !== undefined && !test(visited)) {`); - out.push(` throw new Error("Visited node failed test assertion.");`); - out.push(` }`); - out.push(` return visited;`); - out.push(`}`); - out.push(""); - out.push(`/**`); - out.push(` * Visits a NodeArray using the supplied visitor, possibly returning a new NodeArray in its place.`); - out.push(` *`); - out.push(` * - If the input node array is undefined, the output is undefined.`); - out.push(` * - If the visitor returns undefined for a node, that node is dropped from the result.`); - out.push(` */`); - out.push(`export function visitNodes(nodes: NodeArray, visitor: Visitor): NodeArray;`); - out.push(`export function visitNodes(nodes: NodeArray | undefined, visitor: Visitor): NodeArray | undefined;`); - out.push(`export function visitNodes(nodes: NodeArray | undefined, visitor: Visitor): NodeArray | undefined {`); - out.push(` if (nodes === undefined) return undefined;`); - out.push(` const updated = visitNodesArray(nodes, visitor);`); - out.push(` if (updated === nodes) {`); - out.push(` return nodes;`); - out.push(` }`); - out.push(` return createNodeArray(updated, nodes.pos, nodes.end);`); - out.push(`}`); - out.push(""); - out.push(`export function visitNodesArray(nodes: readonly T[], visitor: Visitor): readonly T[];`); - out.push(`export function visitNodesArray(nodes: readonly T[] | undefined, visitor: Visitor): readonly T[] | undefined;`); - out.push(`export function visitNodesArray(nodes: readonly Node[] | undefined, visitor: Visitor): readonly Node[] | undefined {`); - out.push(` if (nodes === undefined) return undefined;`); - out.push(` let updated: Node[] | undefined;`); - out.push(` for (let i = 0; i < nodes.length; i++) {`); - out.push(` const node = nodes[i];`); - out.push(` const visited = visitor(node);`); - out.push(` if (updated) {`); - out.push(` if (visited) updated.push(visited);`); - out.push(` }`); - out.push(` else if (visited !== node) {`); - out.push(` updated = [];`); - out.push(` for (let j = 0; j < i; j++) updated.push(nodes[j]);`); - out.push(` if (visited) updated.push(visited);`); - out.push(` }`); - out.push(` }`); - out.push(` return updated ?? nodes;`); - out.push(`}`); - out.push(""); - out.push(`/**`); - out.push(` * Visits each child of a Node using the supplied visitor, possibly returning a new Node of the same kind in its place.`); - out.push(` *`); - out.push(` * @param node The Node whose children will be visited.`); - out.push(` * @param visitor The callback used to visit each child.`); - out.push(` * @returns The original node if no children changed, or a new node with visited children.`); - out.push(` */`); - out.push(`export function visitEachChild(node: T, visitor: Visitor): T;`); - out.push(`export function visitEachChild(node: T | undefined, visitor: Visitor): T | undefined;`); - out.push(`export function visitEachChild(node: Node | undefined, visitor: Visitor): Node | undefined {`); - out.push(` if (node === undefined) return undefined;`); - out.push(` const fn = visitEachChildTable[node.kind];`); - out.push(` return fn ? fn(node, visitor) : node;`); - out.push(`}`); - out.push(""); - out.push(`type VisitEachChildFunction = (node: any, visitor: Visitor) => Node;`); - out.push(""); - out.push(`const visitEachChildTable: Record = {`); - for (const entry of entries) { - if (entry.handWrittenVisitor) { - out.push(` [SyntaxKind.${entry.syntaxKind}]: visitEachChildOf${entry.tsName},`); - } - else { - out.push(` [SyntaxKind.${entry.syntaxKind}]: (node: ${entry.tsName}, visitor: Visitor): ${entry.tsName} => {`); - const argNames: string[] = []; - for (const member of entry.members) { - const propName = api.uncapitalize(member.name); - const localName = `_${propName}`; - const { expression } = visitorVisitExpression(member); - out.push(` const ${localName} = ${expression};`); - argNames.push(localName); - } - out.push(` return ${entry.updateName}(node, ${argNames.join(", ")});`); - out.push(` },`); - } - } - out.push(`};`); - out.push(""); - while (out.length > 0 && out[out.length - 1] === "") out.pop(); - out.push(""); - - let result = out.join("\n"); - result = result.replace(/\n/g, "\r\n"); - return result; -} - -// ──────────────────────────────────────────────────────────────────────────── -// Main -// ──────────────────────────────────────────────────────────────────────────── - -function writeAndFormat(filePath: string, content: string) { - fs.writeFileSync(filePath, content); - execaSync("dprint", ["fmt", filePath], { stdio: "inherit", cwd: ROOT }); - console.log(`Generated ${filePath}`); -} - -export default function main() { - console.log("Generating TS AST code..."); - - const factoryPath = path.join(ROOT, "packages/typescript/src/ast/factory.generated.ts"); - const isGenPath = path.join(ROOT, "packages/typescript/src/ast/is.generated.ts"); - const astGenPath = path.join(ROOT, "packages/typescript/src/ast/ast.generated.ts"); - const visitorPath = path.join(ROOT, "packages/typescript/src/ast/visitor.generated.ts"); - - writeAndFormat(astGenPath, generateAstGenerated()); - writeAndFormat(factoryPath, generateFactory()); - writeAndFormat(isGenPath, generateIsGenerated()); - writeAndFormat(visitorPath, generateVisitor()); -} - -if (process.argv[1] === fileURLToPath(import.meta.url)) { - main(); -} +/** + * Schema-driven TypeScript AST code generator. + * Reads tools/scripts/tsc/ast.json and produces: + * - packages/typescript/src/ast/ast.generated.ts + * - packages/typescript/src/ast/factory.generated.ts + * - packages/typescript/src/ast/is.generated.ts + * + * Usage: node --experimental-strip-types tools/scripts/tsc/generate-ts-ast.ts + */ + +import { execaSync } from "execa"; +import * as fs from "node:fs"; +import * as path from "node:path"; +import { fileURLToPath } from "node:url"; +import type { + MemberInfo, + NodeType, + Type, +} from "./schema.ts"; +import { + api, + kindGuardName, +} from "./schema.ts"; + +// ──────────────────────────────────────────────────────────────────────────── +// Load schema +// ──────────────────────────────────────────────────────────────────────────── + +const ROOT = path.resolve(import.meta.dirname!, "../../.."); + +// TS members only (filter noTS/inherited issues) +function tsMembers(node: NodeType): MemberInfo[] { + return node.members.filter(member => !member.isKindParam() && !member.noTS); +} + +function isInheritedFromTsBase(member: MemberInfo): boolean { + return member.inherited && !!member.inheritedField && !member.inheritedField.noTS; +} + +// NodeBase fields (e.g. Flags) are class-level properties on NodeObject, +// not data members stored in _data. +const nodeBaseFieldNames = new Set( + api.bases().find(b => b.name === "NodeBase")?.fields.filter(f => !f.noTS).map(f => f.name) ?? [], +); + +function isNodeBaseProperty(member: MemberInfo): boolean { + return member.inherited && nodeBaseFieldNames.has(member.name); +} + +function tsInterfaceMembers(node: NodeType): MemberInfo[] { + return tsMembers(node).filter(member => !isInheritedFromTsBase(member) || member.hasTypeScriptOverride()); +} + +// ──────────────────────────────────────────────────────────────────────────── +// TS-specific naming and typing +// ──────────────────────────────────────────────────────────────────────────── + +// The JSDoc comment `text` member is stored as a raw []string in Go, but the +// encoder joins it into a single string and both the RemoteNode API and the +// local factory expose it as a scalar string. Collapse just this member to its +// element type in the generated TS. This is intentionally keyed on the property +// name rather than on "raw list of a primitive" so the exception stays narrow +// and explicit; `text` is the only such member. +function collapsedTextType(name: string, type: Type): string | undefined { + if (name === "text" && type.kind === "list" && type.listKind === "raw") { + return type.elementType.formatTypeScript(); + } + return undefined; +} + +function tsParamTypeFrom(name: string, type: Type): string { + return collapsedTextType(name, type) + ?? (type.kind === "list" ? type.raw().formatTypeScript() : type.formatTypeScript()); +} + +function tsParamName(propName: string): string { + if (propName === "arguments") return "arguments_"; + return propName; +} + +// Get factory members in schema order. +function factoryMembers(node: NodeType): MemberInfo[] { + return tsMembers(node).filter(m => !m.noFactory); +} + +function factoryNodes(): NodeType[] { + return api.nodes().filter(node => !node.handWritten && !isVariantNode(node)); +} + +function kindTypeParameter(node: NodeType): { name: string; constraint: string; } | undefined { + const kindType = node.kindType; + if (kindType.kind !== "typeParameter") return undefined; + return { name: kindType.name, constraint: kindType.constraint.formatTypeScript() }; +} + +function isVariantNode(node: NodeType): boolean { + return node.isMultiKind() && node.kindType.kind !== "typeParameter"; +} + +function syntaxKindChecksForNode(node: NodeType): string[] { + return node.allKinds().map(k => k.formatTypeScript()); +} + +// ──────────────────────────────────────────────────────────────────────────── +// Bases for ast.generated.ts +// ──────────────────────────────────────────────────────────────────────────── + +const goOnlyBases = new Set( + api.bases().filter(base => base.isGoOnly()).map(base => base.key), +); + +// Expand goOnly bases transitively: when a goOnly base appears in an extends +// list, replace it with its own non-goOnly extends (recursively). +function expandTsExtends(exts: string[]): string[] { + const result: string[] = []; + const seen = new Set(); + + function expand(bases: string[]) { + for (const b of bases) { + if (seen.has(b)) continue; + seen.add(b); + if (goOnlyBases.has(b)) { + const base = api.getBase(b); + if (base?.extends) { + expand(base.extendsKeys); + } + } + else { + result.push(b); + } + } + } + + expand(exts); + return result; +} + +function baseTsName(base: string | NodeType): string { + return typeof base === "string" ? base : base.formatTypeScript(); +} + +function resolveBaseExtends(base: NodeType): string { + if (base.extendsKeys.length > 0) { + const tsExts = expandTsExtends(base.extendsKeys) + .map(e => baseTsName(e)); + if (tsExts.length > 0) return tsExts.join(", "); + } + return "Node"; +} + +function deriveNodeTsExtends(node: NodeType): string { + const tsExts = expandTsExtends(node.extendsKeys) + .map(e => baseTsName(e)); + return tsExts.length > 0 ? tsExts.join(", ") : "Node"; +} + +function generateBaseFields(base: NodeType): string { + if (base.fields.length === 0) return ""; + let result = ""; + for (const field of base.fields) { + if (field.noTS) continue; + const name = api.uncapitalize(field.name); + const type = collapsedTextType(field.name, field.type) ?? field.type.formatTypeScript(); + const opt = field.optional ? "?" : ""; + result += `\n readonly ${name}${opt}: ${type};`; + } + return result; +} + +// ──────────────────────────────────────────────────────────────────────────── +// Variant types derived from multi-kind Go nodes with enum Kind members. +// ──────────────────────────────────────────────────────────────────────────── + +// Compute TS variant interfaces from multi-kind nodes with enum Kind members. +// For each enum value, create a separate interface; a union type alias uses the Go struct name. +interface TsVariant { + tsName: string; + syntaxKind: string; + tsExtends: string; + members?: MemberInfo[]; + handWrittenVisitor?: boolean; +} + +interface VisitorEntry { + tsName: string; + syntaxKind: string; + updateName: string; + members: MemberInfo[]; + handWrittenVisitor?: boolean; +} + +function computeTsVariants(): TsVariant[] { + const result: TsVariant[] = []; + for (const node of api.nodes()) { + if (!isVariantNode(node)) continue; + + // tsMembers excludes the Kind member; use full set so factory/visitor/clone + // code sees inherited members too. Interface generation filters further. + const otherMembers = tsMembers(node); + const tsExtends = deriveNodeTsExtends(node); + + const kindValues = node.kindTypes().map(kind => kind.name); + + for (const kindValue of kindValues) { + result.push({ + tsName: kindValue, + syntaxKind: kindValue, + tsExtends, + members: otherMembers, + handWrittenVisitor: node.handWrittenVisitor, + }); + } + } + return result; +} + +const tsVariants = computeTsVariants(); + +function visitorNodes(): NodeType[] { + return api.nodes().filter(node => !isVariantNode(node)); +} + +function visitorMembers(node: NodeType): MemberInfo[] { + return factoryMembers(node).filter(member => member.isChild()); +} + +function visitorGuardName(member: MemberInfo): string | undefined { + const typeName = member.type.formatTypeScript(); + if (typeName === "Node" || typeName === "Declaration") { + return undefined; + } + + if (member.typeGuard) { + return member.typeGuard; + } + + switch (member.type.kind) { + case "node": + case "alias": + return `is${member.type.name}`; + case "union": { + throw new Error(`No visitor guard configured for union child type ${member.type.formatTypeScript()} on ${member.node?.name}.${member.name}`); + } + default: + return undefined; + } +} + +function visitorVisitExpression(member: MemberInfo): { expression: string; guardName?: string; } { + const propName = api.uncapitalize(member.name); + if (member.listKind === "raw") { + return { expression: `visitNodesArray(node.${propName}, visitor)` }; + } + if (member.type.kind === "list") { + return { expression: `visitNodes(node.${propName}, visitor)` }; + } + const guardName = visitorGuardName(member); + const guardArg = guardName ? `, ${guardName}` : ""; + return { expression: `visitNode(node.${propName}, visitor${guardArg})`, guardName }; +} + +function visitorEntries(): VisitorEntry[] { + const entries: VisitorEntry[] = []; + + for (const node of visitorNodes()) { + const members = visitorMembers(node); + if (members.length === 0) continue; + entries.push({ + tsName: node.name, + syntaxKind: node.syntaxKindName, + updateName: `update${node.name}`, + members, + handWrittenVisitor: node.handWrittenVisitor, + }); + } + + for (const variant of tsVariants) { + const members = (variant.members || []).filter(member => member.isChild()); + if (members.length === 0) continue; + entries.push({ + tsName: variant.tsName, + syntaxKind: variant.syntaxKind, + updateName: `update${variant.tsName}`, + members, + handWrittenVisitor: variant.handWrittenVisitor, + }); + } + + return entries; +} + +// ──────────────────────────────────────────────────────────────────────────── +// Code generation: ast.generated.ts +// ──────────────────────────────────────────────────────────────────────────── + +function generateAstGenerated(): string { + const eol = "\r\n"; + const parts: string[] = []; + + // ── SyntaxKind union aliases from schema ── + const kindUnions = api.kindAliases(); + for (const { name, members } of kindUnions) { + const parts2 = members.map(m => { + // If it references another union, use the union name directly + if (api.hasKindAlias(m)) return m; + return `SyntaxKind.${m}`; + }); + parts.push(`export type ${name} = ${parts2.join(" | ")};`); + } + + // ── Base interfaces from schema ── + parts.push(""); + for (const base of api.bases()) { + if (goOnlyBases.has(base.key)) continue; + const name = baseTsName(base); + const extendsClause = resolveBaseExtends(base); + const brandProp = base.brand ? `\n readonly ${base.brand}: any;` : ""; + const fields = generateBaseFields(base); + parts.push(`export interface ${name} extends ${extendsClause} {${brandProp}${fields}\n}`); + } + + // ── Concrete node interfaces from schema ── + parts.push(""); + for (const node of api.nodes()) { + if (node.handWritten) continue; + if (isVariantNode(node)) continue; // handled as variant interfaces below + + const interfaceName = node.name; + const extendsClause = deriveNodeTsExtends(node); + + // Generic type parameters + let typeParamStr = ""; + if (node.typeParameters.length > 0) { + const tps = node.typeParameters.map(tp => { + let s = tp.name + " extends " + tp.constraint; + if (tp.default) s += " = " + tp.default; + return s; + }); + typeParamStr = `<${tps.join(", ")}>`; + } + + // Kind line: use type param name if available, otherwise SyntaxKind constant + const kindLine = `\n readonly kind: ${node.kindType.formatTypeScript()};`; + + let memberLines = ""; + for (const m of tsInterfaceMembers(node)) { + if (m.rawType === "SyntaxKind" && m.name === "SyntaxKind") continue; + const propName = api.uncapitalize(m.name); + const propType = m.type.formatTypeScript(); + const opt = m.optional ? "?" : ""; + memberLines += `\n readonly ${propName}${opt}: ${propType};`; + } + + parts.push(`export interface ${interfaceName}${typeParamStr} extends ${extendsClause} {${kindLine}${memberLines}\n}`); + } + + // ── Union types from schema ── + parts.push(""); + for (const alias of api.nodeAliases()) { + if (alias.isUnion) { + const members = alias.unionMemberTypes.map(type => { + if (type.baseKind() === "kind") { + // SyntaxKind -> Node + debugger; + } + return type.formatTypeScript(); + }).join(" | "); + if (!members) continue; + parts.push(`export type ${alias.name} = ${members};`); + } + else if (alias.base) { + parts.push(`export type ${alias.name} = ${baseTsName(alias.base)};`); + } + } + + // ── Variant interfaces (from multi-kind nodes with enum Kind) ── + parts.push(""); + for (const v of tsVariants) { + const kindLine = `\n readonly kind: SyntaxKind.${v.syntaxKind};`; + let memberLines = ""; + if (v.members) { + for (const m of v.members) { + // For interfaces, skip inherited members (they come from the base type) + if (isInheritedFromTsBase(m) && !m.hasTypeScriptOverride()) continue; + const propName = api.uncapitalize(m.name); + const propType = m.type.formatTypeScript(); + const opt = m.optional ? "?" : ""; + memberLines += `\n readonly ${propName}${opt}: ${propType};`; + } + } + parts.push(`export interface ${v.tsName} extends ${v.tsExtends} {${kindLine}${memberLines}\n}`); + } + + // ── Union type aliases for multi-kind nodes ── + for (const node of api.nodes()) { + if (!isVariantNode(node)) continue; + const kindValues = node.kindTypes().map(kind => kind.name); + parts.push(`export type ${node.name} = ${kindValues.join(" | ")};`); + } + + // ── Instantiation aliases for generic nodes ── + for (const node of api.nodes()) { + if (node.instantiationAliases.length === 0) continue; + const interfaceName = node.name; + for (const { name: aliasName, typeArg } of node.instantiationAliases) { + // If the type arg is a kind union name, use it directly; otherwise prefix with SyntaxKind. + const arg = api.hasKindAlias(typeArg) ? typeArg : `SyntaxKind.${typeArg}`; + parts.push(`export type ${aliasName} = ${interfaceName}<${arg}>;`); + } + } + + // ── NodeList type aliases ── + for (const listAlias of api.listAliases()) { + parts.push(`export type ${listAlias.name} = NodeArray<${listAlias.elementTypeName}>;`); + } + + const body = parts.join(eol); + + // ── Header (with imports resolved from the schema model) ── + // Types referenced by the generated declarations but not declared here are + // hand-written in ast.ts; import them so the module type-checks. + const astImports = ["Node", "NodeArray", ...unresolvedAstImports()]; + astImports.sort((a, b) => a.localeCompare(b)); + const header = `// Code generated by tools/scripts/tsc/generate-ts-ast.ts. DO NOT EDIT. + +import type { ModifierFlags } from "#enums/modifierFlags"; +import type { NodeFlags } from "#enums/nodeFlags"; +import { SyntaxKind } from "#enums/syntaxKind"; +import { TokenFlags } from "#enums/tokenFlags"; +import type { ${astImports.join(", ")} } from "./ast.ts";`; + + return [header, body].join(eol); +} + +// TypeScript names that are available without importing from ast.ts: built-in +// primitives, and enums/base types imported from other modules in the header. +const astBuiltinTypeNames = new Set([ + "any", + "boolean", + "never", + "null", + "number", + "string", + "undefined", + "unknown", + "void", + "ModifierFlags", + "NodeFlags", + "TokenFlags", + "SyntaxKind", + "Node", + "NodeArray", +]); + +// Walks a schema Type and records every named type it references in the +// generated TypeScript. Node and alias references contribute their name; +// unions and lists recurse into their members; kinds and type parameters emit +// `SyntaxKind.*` / locally-scoped names and contribute nothing. Names the +// schema cannot resolve fall back to a PrimitiveType whose name is the raw +// reference (e.g. a hand-written `ast.ts` type such as JsxTagNamePropertyAccess). +function collectTypeReferences(type: Type, into: Set): void { + switch (type.kind) { + case "node": + case "alias": + into.add(type.name); + break; + case "union": + for (const member of type.types) collectTypeReferences(member, into); + break; + case "list": + collectTypeReferences(type.elementType, into); + break; + case "primitive": { + const name = type.formatTypeScript(); + if (!astBuiltinTypeNames.has(name)) into.add(name); + break; + } + case "kind": + case "typeParameter": + // Emitted as `SyntaxKind.*` or a locally-declared name; nothing to import. + break; + } +} + +// The set of type names declared within ast.generated.ts itself. Mirrors the +// declarations emitted by generateAstGenerated so references to them are not +// mistaken for hand-written ast.ts types. +function declaredAstNames(): Set { + const declared = new Set(); + for (const { name } of api.kindAliases()) declared.add(name); + for (const base of api.bases()) { + if (!goOnlyBases.has(base.key)) declared.add(baseTsName(base)); + } + // Concrete node interfaces and variant-node union aliases both use node.name. + for (const node of api.nodes()) { + if (!node.handWritten) declared.add(node.name); + } + for (const alias of api.nodeAliases()) declared.add(alias.name); + for (const variant of tsVariants) declared.add(variant.tsName); + for (const node of api.nodes()) { + for (const { name } of node.instantiationAliases) declared.add(name); + } + for (const listAlias of api.listAliases()) declared.add(listAlias.name); + return declared; +} + +// Type names referenced by ast.generated.ts but not declared within it, and so +// hand-written in ast.ts and needing an import. Derived from the schema model: +// the same Type objects generateAstGenerated emits are walked for references. +function unresolvedAstImports(): string[] { + const referenced = new Set(); + + for (const base of api.bases()) { + if (goOnlyBases.has(base.key)) continue; + for (const field of base.fields) { + if (field.noTS) continue; + collectTypeReferences(field.type, referenced); + } + } + for (const node of api.nodes()) { + if (node.handWritten || isVariantNode(node)) continue; + collectTypeReferences(node.kindType, referenced); + for (const member of tsInterfaceMembers(node)) { + collectTypeReferences(member.type, referenced); + } + } + for (const alias of api.nodeAliases()) { + if (alias.isUnion) { + for (const member of alias.unionMemberTypes) collectTypeReferences(member, referenced); + } + else if (alias.base) { + collectTypeReferences(alias.base, referenced); + } + } + for (const variant of tsVariants) { + for (const member of variant.members || []) { + collectTypeReferences(member.type, referenced); + } + } + for (const listAlias of api.listAliases()) { + if (listAlias.elementType) collectTypeReferences(listAlias.elementType, referenced); + } + + const declared = declaredAstNames(); + return [...referenced] + .filter(name => !declared.has(name) && !astBuiltinTypeNames.has(name)) + .sort((a, b) => a.localeCompare(b)); +} + +// ──────────────────────────────────────────────────────────────────────────── +// Code generation: factory.generated.ts +// ──────────────────────────────────────────────────────────────────────────── + +function generateFactory(): string { + const out: string[] = []; + const importTypes = new Set(); + + // Collect all factory-generatable schema nodes + const concreteFactoryNodes = factoryNodes(); + + // ── Collect property names for getters ── + // Exclude names that conflict with NodeObject's own properties + const nodeObjectOwnProps = new Set(["kind", "flags", "pos", "end", "parent", "_data"]); + const getterNames = new Set(); + for (const node of api.nodes()) { + for (const m of tsMembers(node)) { + const name = api.uncapitalize(m.name); + if (!nodeObjectOwnProps.has(name)) getterNames.add(name); + } + } + // Add variant property names + for (const v of tsVariants) { + for (const m of v.members || []) { + const name = api.uncapitalize(m.name); + if (!nodeObjectOwnProps.has(name)) getterNames.add(name); + } + } + // Extra getters for hand-written nodes (SourceFile) whose data properties + // aren't in the schema but are set by hand-written factory functions. + for ( + const name of [ + "fileName", + "originalText", + "contentMapper", + "virtualFileName", + "diagnosticDirectives", + "supplementalSourceFileNames", + "canonicalSourceFileName", + "path", + "languageVariant", + "scriptKind", + "spanMap", + "isDeclarationFile", + "referencedFiles", + "typeReferenceDirectives", + "libReferenceDirectives", + "imports", + "moduleAugmentations", + "ambientModuleNames", + "externalModuleIndicator", + "tokenCache", + ] + ) { + getterNames.add(name); + } + + // ── Collect all types referenced in factory ── + function addType(t: string) { + // Extract base type names from complex types + const stripped = t.replace(/readonly\s+/g, "").replace(/\[\]/g, "") + .replace(/NodeArray<(.+)>/g, "$1").replace(/\s*\|\s*/g, "|"); + for (const part of stripped.split("|")) { + const clean = part.trim(); + if ( + clean && clean !== "undefined" && clean !== "boolean" && clean !== "string" + && clean !== "any" + && clean !== "number" && clean !== "true" && !clean.startsWith("SyntaxKind.") + ) { + importTypes.add(clean); + } + } + } + + // Pre-collect types from factory nodes + for (const node of concreteFactoryNodes) { + addType(node.name); + const kindTypeParam = kindTypeParameter(node); + if (kindTypeParam) addType(kindTypeParam.constraint); + for (const m of tsMembers(node)) { + addType(tsParamTypeFrom(m.name, m.type)); + addType(m.type.formatTypeScript()); + } + } + for (const v of tsVariants) { + addType(v.tsName); + for (const m of v.members || []) { + addType(m.type.formatTypeScript()); + } + } + // Always needed + for (const t of ["Node", "NodeArray", "KeywordTypeSyntaxKind", "Token", "SourceFile", "KeywordTypeNode", "EndOfFile", "ImportPhaseModifierSyntaxKind", "Path", "Statement"]) { + importTypes.add(t); + } + const handWrittenCloneHelpers = api.nodes() + .filter(node => node.handWritten && !isVariantNode(node)) + .map(node => ({ node, helperName: `clone${node.name}Data` })); + // Remove enum types that are imported separately + importTypes.delete("NodeFlags"); + importTypes.delete("SyntaxKind"); + importTypes.delete("TokenFlags"); + + // ── Header ── + out.push(`// Code generated by tools/scripts/tsc/generate-ts-ast.ts. DO NOT EDIT.`); + out.push(""); + out.push(`import { NodeFlags } from "#enums/nodeFlags";`); + out.push(`import { SyntaxKind } from "#enums/syntaxKind";`); + out.push(`import { TokenFlags } from "#enums/tokenFlags";`); + + const sortedImports = [...importTypes].sort((a, b) => a.localeCompare(b)); + out.push(`import type {`); + for (const t of sortedImports) { + out.push(` ${t},`); + } + out.push(`} from "./ast.ts";`); + out.push(`import { getTokenPosOfNode } from "./astnav.ts";`); + if (handWrittenCloneHelpers.length > 0) { + out.push(`import {`); + for (const helperName of [...new Set(handWrittenCloneHelpers.map(h => h.helperName))].sort((a, b) => a.localeCompare(b))) { + out.push(` ${helperName},`); + } + out.push(`} from "./utils.ts";`); + } + + // Import hand-written forEachChild functions + const handWrittenForEachChildImports: string[] = []; + for (const node of api.nodes()) { + if (node.handWrittenVisitor && !node.handWritten) { + const members = tsMembers(node); + if (members.filter(m => m.isChild()).length > 0) { + for (const kind of node.kindTypes()) { + handWrittenForEachChildImports.push(`forEachChildOf${kind.name}`); + } + } + } + } + if (handWrittenForEachChildImports.length > 0) { + out.push(`import {`); + for (const name of handWrittenForEachChildImports.sort((a, b) => a.localeCompare(b))) { + out.push(` ${name},`); + } + out.push(`} from "./visitor.ts";`); + } + + out.push(""); + + // ── NodeObject class ── + const sortedGetters = [...getterNames].sort(); + out.push(`export class NodeObject {`); + out.push(` readonly kind: SyntaxKind;`); + out.push(` flags: NodeFlags = 0 as NodeFlags;`); + out.push(` readonly pos: number = -1;`); + out.push(` readonly end: number = -1;`); + out.push(` parent: Node = undefined!;`); + out.push(` _data: any;`); + out.push(``); + out.push(` constructor(kind: SyntaxKind, data: any) {`); + out.push(` this.kind = kind;`); + out.push(` this._data = data;`); + out.push(` }`); + out.push(``); + + // Getters + for (const name of sortedGetters) { + out.push(` get ${name}(): any { return this._data?.${name}; }`); + } + out.push(``); + + // Methods + out.push(` forEachChild(visitor: (node: Node) => T, visitArray?: (nodes: NodeArray) => T): T | undefined {`); + out.push(` const fn = forEachChildTable[this.kind];`); + out.push(` return fn ? fn(this._data, visitor, visitArray) : undefined;`); + out.push(` }`); + out.push(``); + out.push(` getSourceFile(): SourceFile {`); + out.push(` let node: Node = this as unknown as Node;`); + out.push(` while (node.parent) node = node.parent;`); + out.push(` return node as unknown as SourceFile;`); + out.push(` }`); + out.push(``); + out.push(` getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number {`); + out.push(` return getTokenPosOfNode(this as unknown as Node, sourceFile ?? this.getSourceFile(), includeJsDocComment);`); + out.push(` }`); + out.push(``); + out.push(` getFullStart(): number {`); + out.push(` return this.pos;`); + out.push(` }`); + out.push(``); + out.push(` getEnd(): number {`); + out.push(` return this.end;`); + out.push(` }`); + out.push(``); + out.push(` getWidth(sourceFile?: SourceFile): number {`); + out.push(` return this.getEnd() - this.getStart(sourceFile);`); + out.push(` }`); + out.push(``); + out.push(` getFullWidth(): number {`); + out.push(` return this.end - this.pos;`); + out.push(` }`); + out.push(``); + out.push(` getLeadingTriviaWidth(sourceFile?: SourceFile): number {`); + out.push(` return this.getStart(sourceFile) - this.pos;`); + out.push(` }`); + out.push(``); + out.push(` getFullText(sourceFile?: SourceFile): string {`); + out.push(` return (sourceFile ?? this.getSourceFile()).text.substring(this.pos, this.end);`); + out.push(` }`); + out.push(``); + out.push(` getText(sourceFile?: SourceFile): string {`); + out.push(` sourceFile ??= this.getSourceFile();`); + out.push(` return sourceFile.text.substring(this.getStart(sourceFile), this.end);`); + out.push(` }`); + out.push(`}`); + out.push(``); + + // ── Utility functions ── + out.push(`function isNodeArray(array: readonly T[]): array is NodeArray {`); + out.push(` return "pos" in array && "end" in array;`); + out.push(`}`); + out.push(``); + out.push(`export function createNodeArray(elements: readonly T[], pos: number = -1, end: number = -1): NodeArray {`); + out.push(` if (isNodeArray(elements)) return elements;`); + out.push(` const arr = elements.slice() as unknown as NodeArray & { pos: number; end: number; };`); + out.push(` arr.pos = pos;`); + out.push(` arr.end = end;`); + out.push(` return arr;`); + out.push(`}`); + out.push(``); + out.push(`export function cloneNode(node: T): T {`); + out.push(` const data = cloneNodeData(node);`); + out.push(` const clone = new NodeObject(node.kind, data);`); + out.push(` (clone as any).flags = node.flags;`); + out.push(` (clone as any).pos = node.pos;`); + out.push(` (clone as any).end = node.end;`); + out.push(` return clone as unknown as T;`); + out.push(`}`); + out.push(``); + + // ── cloneNodeData ── + out.push(`function cloneNodeData(node: Node): any {`); + out.push(` const n = node as any;`); + out.push(` switch (node.kind) {`); + // Schema nodes + for (const node of api.nodes()) { + if (node.handWritten) continue; + if (isVariantNode(node)) continue; + const members = tsMembers(node).filter(m => !isNodeBaseProperty(m)); + if (members.length === 0) continue; + const props = members.map(m => api.uncapitalize(m.name)); + if (props.length === 0) continue; + const syntaxKinds = node.allKinds(); + for (const sk of syntaxKinds) { + out.push(` case ${sk.formatTypeScript()}:`); + } + const propStr = props.map(p => `${p}: n.${p}`).join(", "); + out.push(` return { ${propStr} };`); + } + // Variant nodes + for (const v of tsVariants) { + if (!v.members || v.members.length === 0) continue; + const props = v.members.map(m => api.uncapitalize(m.name)); + const propStr = props.map(p => `${p}: n.${p}`).join(", "); + out.push(` case SyntaxKind.${v.syntaxKind}:`); + out.push(` return { ${propStr} };`); + } + for (const { node, helperName } of handWrittenCloneHelpers) { + for (const sk of node.allKinds()) { + out.push(` case ${sk.formatTypeScript()}:`); + } + out.push(` return ${helperName}(n);`); + } + out.push(` default:`); + out.push(` return undefined;`); + out.push(` }`); + out.push(`}`); + out.push(``); + + // ── forEachChildTable ── + out.push(`type ForEachChildFunction = (data: any, cbNode: (node: Node) => T, cbNodes: ((nodes: NodeArray) => T) | undefined) => T | undefined;`); + out.push(``); + out.push(`const forEachChildTable: Record = {`); + // Schema nodes + for (const node of api.nodes()) { + if (node.handWritten) continue; + if (isVariantNode(node)) continue; + const members = tsMembers(node); + const childMembers = members.filter(m => m.isChild()); + if (childMembers.length === 0) continue; + const syntaxKinds = node.allKinds(); + if (node.handWrittenVisitor) { + for (const sk of syntaxKinds) { + out.push(` [${sk.formatTypeScript()}]: forEachChildOf${node.name},`); + } + continue; + } + const visits = childMembers.map(m => { + const propName = api.uncapitalize(m.name); + const listKind = m.listKind; + if (listKind) { + return `visitNodes(cbNode, cbNodes, data.${propName})`; + } + return `visitNode(cbNode, data.${propName})`; + }); + const body = visits.join(" ||\n "); + for (const sk of syntaxKinds) { + out.push(` [${sk.formatTypeScript()}]: (data, cbNode, cbNodes) =>`); + out.push(` ${body},`); + } + } + // Variant nodes + for (const v of tsVariants) { + if (!v.members) continue; + const childMembers = v.members.filter(m => m.type.baseKind() === "node" || m.type.baseKind() === "list"); + if (childMembers.length === 0) continue; + if (v.handWrittenVisitor) { + out.push(` [SyntaxKind.${v.syntaxKind}]: forEachChildOf${v.tsName},`); + continue; + } + const visits = childMembers.map(m => { + const propName = api.uncapitalize(m.name); + if (m.listKind) return `visitNodes(cbNode, cbNodes, data.${propName})`; + return `visitNode(cbNode, data.${propName})`; + }); + const body = visits.join(" ||\n "); + out.push(` [SyntaxKind.${v.syntaxKind}]: (data, cbNode, cbNodes) =>`); + out.push(` ${body},`); + } + // SourceFile is handWritten so we add its forEachChild entry manually + out.push(` [SyntaxKind.SourceFile]: (data, cbNode, cbNodes) =>`); + out.push(` visitNodes(cbNode, cbNodes, data.statements) ||`); + out.push(` visitNode(cbNode, data.endOfFileToken),`); + out.push(`};`); + out.push(``); + + // ── visitNode / visitNodes ── + out.push(`function visitNode(cbNode: (node: Node) => T, node: Node | undefined): T | undefined {`); + out.push(` return node ? cbNode(node) : undefined;`); + out.push(`}`); + out.push(``); + out.push(`function visitNodes(cbNode: (node: Node) => T, cbNodes: ((nodes: NodeArray) => T) | undefined, nodes: NodeArray | undefined): T | undefined {`); + out.push(` if (!nodes) return undefined;`); + out.push(` if (cbNodes) return cbNodes(nodes);`); + out.push(` for (const node of nodes) {`); + out.push(` const result = cbNode(node);`); + out.push(` if (result) return result;`); + out.push(` }`); + out.push(` return undefined;`); + out.push(`}`); + out.push(``); + + // ── Create functions (schema nodes) ── + const createNames: string[] = []; + for (const node of concreteFactoryNodes) { + const iface = node.name; + const members = factoryMembers(node); + const funcName = `create${iface}`; + createNames.push(funcName); + const kindTypeParam = kindTypeParameter(node); + + // Build params + const paramParts: string[] = []; + if (kindTypeParam) { + paramParts.push(`kind: ${kindTypeParam.name}`); + } + + // First pass: determine which members are "required" (cannot be omitted). + // A member is required if it is not optional and not a bool. + const isRequired = members.map(m => { + if (m.optional) return false; + const mt = m.type; + if (!m.isChild() && mt.kind === "primitive" && mt.name === "bool") return false; + return true; + }); + + // Find the index of the last required member. + let lastRequiredIdx = -1; + for (let i = members.length - 1; i >= 0; i--) { + if (isRequired[i]) { + lastRequiredIdx = i; + break; + } + } + + for (let mi = 0; mi < members.length; mi++) { + const m = members[mi]; + const propName = api.uncapitalize(m.name); + const paramName = tsParamName(propName); + const paramType = tsParamTypeFrom(m.name, m.type); + const memberType = m.type; + const isBool = !m.isChild() && memberType.kind === "primitive" && memberType.name === "bool"; + + if (isBool) { + if (mi < lastRequiredIdx) { + // Required params follow — use default value instead of optional + paramParts.push(`${paramName}: ${paramType} = false`); + } + else { + paramParts.push(`${paramName}?: ${paramType}`); + } + } + else if (m.optional) { + if (mi < lastRequiredIdx) { + // Required params follow — cannot use ? syntax + paramParts.push(`${paramName}: ${paramType} | undefined`); + } + else { + paramParts.push(`${paramName}?: ${paramType}`); + } + } + else { + paramParts.push(`${paramName}: ${paramType}`); + } + } + + // Build data object (includes ALL tsMembers, not just factory params) + const allMembers = tsMembers(node); + const dataParts: string[] = []; + const nodeProps: { propName: string; paramName: string; }[] = []; + for (const m of allMembers) { + const propName = api.uncapitalize(m.name); + const paramName = tsParamName(propName); + const listKind = m.listKind; + const isParam = members.includes(m); + // Members inherited from NodeBase (e.g. Flags) are class-level + // properties on NodeObject, not data members. + if (isNodeBaseProperty(m)) { + if (isParam) { + nodeProps.push({ propName, paramName }); + } + continue; + } + if (listKind === "NodeList" || listKind === "ModifierList") { + if (isParam) { + if (m.optional) { + dataParts.push(`${propName}: ${paramName} ? createNodeArray(${paramName}) : undefined`); + } + else { + dataParts.push(`${propName}: createNodeArray(${paramName})`); + } + } + } + else { + if (isParam) { + if (propName === paramName) { + dataParts.push(propName); // shorthand + } + else { + dataParts.push(`${propName}: ${paramName}`); + } + } + } + } + + const paramsStr = paramParts.join(", "); + const dataStr = dataParts.length > 0 + ? `{\n ${dataParts.join(",\n ")},\n }` + : "undefined"; + const genericParamStr = kindTypeParam + ? `<${kindTypeParam.name} extends ${kindTypeParam.constraint}>` + : ""; + const returnType = kindTypeParam + ? `${iface}<${kindTypeParam.name}>` + : iface; + const kindExpr = kindTypeParam ? "kind" : `SyntaxKind.${node.syntaxKindName}`; + + out.push(`export function ${funcName}${genericParamStr}(${paramsStr}): ${returnType} {`); + if (nodeProps.length > 0) { + out.push(` const node = new NodeObject(${kindExpr}, ${dataStr}) as unknown as ${returnType};`); + for (const { propName, paramName } of nodeProps) { + out.push(` (node as any).${propName} = ${paramName};`); + } + out.push(` return node;`); + } + else { + out.push(` return new NodeObject(${kindExpr}, ${dataStr}) as unknown as ${returnType};`); + } + out.push(`}`); + out.push(``); + } + + // ── Create functions (variant nodes) ── + for (const v of tsVariants) { + const funcName = `create${v.tsName}`; + createNames.push(funcName); + if (!v.members || v.members.length === 0) { + out.push(`export function ${funcName}(): ${v.tsName} {`); + out.push(` return new NodeObject(SyntaxKind.${v.syntaxKind}, undefined) as unknown as ${v.tsName};`); + out.push(`}`); + } + else { + const paramParts: string[] = []; + const dataParts: string[] = []; + for (const m of v.members) { + const propName = api.uncapitalize(m.name); + const paramName = tsParamName(propName); + const paramType = tsParamTypeFrom(m.name, m.type); + if (m.optional) { + paramParts.push(`${paramName}: ${paramType} | undefined`); + } + else { + paramParts.push(`${paramName}: ${paramType}`); + } + if (m.listKind === "NodeList") { + if (m.optional) { + dataParts.push(`${propName}: ${paramName} ? createNodeArray(${paramName}) : undefined`); + } + else { + dataParts.push(`${propName}: createNodeArray(${paramName})`); + } + } + else if (propName === paramName) { + dataParts.push(propName); + } + else { + dataParts.push(`${propName}: ${paramName}`); + } + } + const paramsStr = paramParts.join(", "); + const dataStr = `{\n ${dataParts.join(",\n ")},\n }`; + out.push(`export function ${funcName}(${paramsStr}): ${v.tsName} {`); + out.push(` return new NodeObject(SyntaxKind.${v.syntaxKind}, ${dataStr}) as unknown as ${v.tsName};`); + out.push(`}`); + } + out.push(``); + } + + // ── Update functions (schema nodes) ── + for (const node of concreteFactoryNodes) { + const iface = node.name; + const members = factoryMembers(node); + // Update params: all child members + const updateChildMembers = members.filter(m => m.isChild()); + if (updateChildMembers.length === 0) continue; + + const funcName = `update${iface}`; + const paramParts = [`node: ${iface}`]; + + // Determine which update child members are required + const updateIsRequired = updateChildMembers.map(m => !m.optional); + let updateLastRequiredIdx = -1; + for (let i = updateChildMembers.length - 1; i >= 0; i--) { + if (updateIsRequired[i]) { + updateLastRequiredIdx = i; + break; + } + } + + for (let mi = 0; mi < updateChildMembers.length; mi++) { + const m = updateChildMembers[mi]; + const propName = api.uncapitalize(m.name); + const paramName = tsParamName(propName); + const paramType = tsParamTypeFrom(m.name, m.type); + if (m.optional) { + if (mi < updateLastRequiredIdx) { + paramParts.push(`${paramName}: ${paramType} | undefined`); + } + else { + paramParts.push(`${paramName}?: ${paramType}`); + } + } + else { + paramParts.push(`${paramName}: ${paramType}`); + } + } + + // Comparison + const comparisons = updateChildMembers.map(m => { + const propName = api.uncapitalize(m.name); + const paramName = tsParamName(propName); + return `node.${propName} !== ${paramName}`; + }); + + // Create call args (all factory members, using params for update children, node.prop for rest) + const updateChildNames = new Set(updateChildMembers.map(m => m.name)); + const createArgs = members.map(m => { + const propName = api.uncapitalize(m.name); + const paramName = tsParamName(propName); + if (updateChildNames.has(m.name)) return paramName; + return `node.${propName}`; + }); + + out.push(`export function ${funcName}(${paramParts.join(", ")}): ${iface} {`); + out.push(` return ${comparisons.join(" || ")} ? create${iface}(${createArgs.join(", ")}) : node;`); + out.push(`}`); + out.push(``); + } + + // ── Update functions (variant nodes) ── + for (const v of tsVariants) { + if (!v.members || v.members.length === 0) continue; + const childMembers = v.members.filter(m => m.type.baseKind() === "node" || m.type.baseKind() === "list"); + if (childMembers.length === 0) continue; + + const paramParts = [`node: ${v.tsName}`]; + for (const m of childMembers) { + const propName = api.uncapitalize(m.name); + const paramName = tsParamName(propName); + const paramType = tsParamTypeFrom(m.name, m.type); + if (m.optional) { + paramParts.push(`${paramName}: ${paramType} | undefined`); + } + else { + paramParts.push(`${paramName}: ${paramType}`); + } + } + + const comparisons = childMembers.map(m => { + const propName = api.uncapitalize(m.name); + const paramName = tsParamName(propName); + return `node.${propName} !== ${paramName}`; + }); + + const createArgs = (v.members || []).map(m => { + const propName = api.uncapitalize(m.name); + const paramName = tsParamName(propName); + if (m.type.baseKind() !== "node" && m.type.baseKind() !== "list") return `node.${propName}`; + return paramName; + }); + + out.push(`export function update${v.tsName}(${paramParts.join(", ")}): ${v.tsName} {`); + out.push(` return ${comparisons.join(" || ")} ? create${v.tsName}(${createArgs.join(", ")}) : node;`); + out.push(`}`); + out.push(``); + } + + // ── createSourceFile (hand-written — SourceFile is handWritten in schema) ── + out.push(`export function createSourceFile(statements: readonly Statement[], endOfFileToken: EndOfFile, text: string, fileName: string, path: Path): SourceFile {`); + out.push(` return new NodeObject(SyntaxKind.SourceFile, {`); + out.push(` statements: createNodeArray(statements),`); + out.push(` endOfFileToken,`); + out.push(` text,`); + out.push(` originalText: text,`); + out.push(` spanMap: undefined,`); + out.push(` contentMapper: undefined,`); + out.push(` virtualFileName: undefined,`); + out.push(` diagnosticDirectives: undefined,`); + out.push(` fileName,`); + out.push(` path,`); + out.push(` }) as unknown as SourceFile;`); + out.push(`}`); + out.push(``); + + out.push(`function cloneSourceFileWithChanges(source: SourceFile, statements: readonly Statement[], endOfFileToken: EndOfFile): SourceFile {`); + out.push(` return new NodeObject(SyntaxKind.SourceFile, {`); + out.push(` ...cloneSourceFileData(source),`); + out.push(` statements: createNodeArray(statements),`); + out.push(` endOfFileToken,`); + out.push(` }) as unknown as SourceFile;`); + out.push(`}`); + out.push(``); + + // ── updateSourceFile (hand-written in schema) ── + out.push(`export function updateSourceFile(node: SourceFile, statements: readonly Statement[], endOfFileToken: EndOfFile): SourceFile {`); + out.push(` return node.statements !== statements || node.endOfFileToken !== endOfFileToken`); + out.push(` ? cloneSourceFileWithChanges(node, statements, endOfFileToken)`); + out.push(` : node;`); + out.push(`}`); + out.push(``); + + // Clean up + while (out.length > 0 && out[out.length - 1] === "") out.pop(); + out.push(""); + + let result = out.join("\n"); + result = result.replace(/\n/g, "\r\n"); + return result; +} + +// ──────────────────────────────────────────────────────────────────────────── +// Code generation: is.ts +// ──────────────────────────────────────────────────────────────────────────── + +function generateIsGenerated(): string { + const out: string[] = []; + const importTypes = new Set(); + + // ── Simple is* guards from schema nodes ── + const guards: { funcName: string; typeName: string; kindChecks: string[]; kindAliasConstraint?: string; }[] = []; + + for (const node of api.nodes()) { + if (isVariantNode(node)) continue; + + const typeName = node.name; + const funcName = `is${typeName}`; + // If the node has a type parameter constrained to a kind alias, use the kind-level guard. + const kindParam = node.typeParameters.find(tp => tp.constraint && api.hasKindAlias(tp.constraint)); + const kindAliasConstraint = kindParam?.constraint; + guards.push({ funcName, typeName, kindChecks: syntaxKindChecksForNode(node), kindAliasConstraint }); + importTypes.add(typeName); + } + + // Variant node guards + for (const v of tsVariants) { + const funcName = `is${v.tsName}`; + guards.push({ funcName, typeName: v.tsName, kindChecks: [`SyntaxKind.${v.syntaxKind}`] }); + importTypes.add(v.tsName); + } + + // Always needed + importTypes.add("Node"); + + // ── Composite guards from unions ── + interface CompositeGuard { + funcName: string; + typeName: string; + body: string; + } + const compositeGuards: CompositeGuard[] = []; + + // Build a map from instantiation alias name → SyntaxKind name. + // E.g. "AbstractKeyword" → "AbstractKeyword", "TrueLiteral" → "TrueKeyword" + const instantiationAliasToKind = new Map(); + for (const node of api.nodes()) { + for (const { name: aliasName, typeArg } of node.instantiationAliases) { + // typeArg is either a SyntaxKind name or a kind alias name + if (!api.hasKindAlias(typeArg)) { + instantiationAliasToKind.set(aliasName, typeArg); + } + } + } + + // Recursively resolve union members to SyntaxKind checks. + // Returns null if any member cannot be resolved to concrete kinds. + function resolveUnionKindChecks(members: readonly string[]): string[] | null { + const checks: string[] = []; + for (const m of members) { + const node = api.getNode(m); + if (node) { + checks.push(...syntaxKindChecksForNode(node)); + continue; + } + const variant = tsVariants.find(v => v.tsName === m); + if (variant) { + checks.push(`SyntaxKind.${variant.syntaxKind}`); + continue; + } + // Check if it's a Token instantiation alias (e.g. AbstractKeyword, TrueLiteral) + const kindName = instantiationAliasToKind.get(m); + if (kindName) { + checks.push(`SyntaxKind.${kindName}`); + continue; + } + // Recurse into sub-aliases + const subAlias = api.nodeAliases().find(a => a.name === m); + if (subAlias?.isUnion) { + const sub = resolveUnionKindChecks(subAlias.unionMemberNames); + if (sub === null) return null; + checks.push(...sub); + continue; + } + // Check if it's a kind alias (e.g. ModifierSyntaxKind) — expand to SyntaxKind checks + if (api.hasKindAlias(m)) { + const kindMembers = api.expandKindAliasMembers(m); + checks.push(...kindMembers.map(k => k.formatTypeScript())); + continue; + } + // Unresolvable member (e.g. Expression, Statement) — can't enumerate kinds + return null; + } + return checks; + } + + for (const alias of api.nodeAliases()) { + if (!alias.isUnion) continue; + const members = alias.unionMemberNames; + if (members.length === 0) continue; + + const typeName = alias.name; + const funcName = `is${typeName}`; + if (guards.some(g => g.funcName === funcName)) continue; + + const kindChecksRaw = resolveUnionKindChecks(members); + + if (kindChecksRaw === null || kindChecksRaw.length === 0) { + // Union contains unresolvable members (e.g. Expression) — skip, + // must be hand-written in is.ts + continue; + } + + importTypes.add(typeName); + const kindChecks = [...new Set(kindChecksRaw)]; + let body: string; + + // Build conditions: delegate to kind guard for kind alias members, direct checks for others + const kindAliasMembers = members.filter(m => api.hasKindAlias(m)); + if (kindAliasMembers.length > 0) { + // Collect direct SyntaxKind checks for non-kind-alias members + const nonKindAliasMembers = members.filter(m => !api.hasKindAlias(m)); + const directChecks = nonKindAliasMembers.length > 0 + ? resolveUnionKindChecks(nonKindAliasMembers) ?? [] + : []; + const conditions: string[] = [ + ...kindAliasMembers.map(m => `${kindGuardName(m)}(kind)`), + ...[...new Set(directChecks)].map(k => `kind === ${k}`), + ]; + body = `const kind = node.kind;\n return ${conditions.join(" || ")};`; + } + else if (kindChecks.length <= 3) { + body = `return ${kindChecks.map(k => `node.kind === ${k}`).join(" || ")};`; + } + else { + body = `const kind = node.kind;\n return ${kindChecks.map(k => `kind === ${k}`).join(" || ")};`; + } + + compositeGuards.push({ funcName, typeName, body }); + } + + // ── Kind alias guards (SyntaxKind-level) ── + // Generate `is(kind: SyntaxKind): kind is ` for each enumerated kind alias union. + // Range-based aliases generate `is(kind: SyntaxKind): boolean` with range checks. + // Function names drop "Syntax" from the kind alias name: isTriviaSyntaxKind → isTriviaKind. + + interface KindAliasGuard { + funcName: string; + typeName: string; + conditions: string[]; + } + const kindAliasGuards: KindAliasGuard[] = []; + interface RangeKindAliasGuard { + funcName: string; + first: string; + last: string; + } + const rangeKindAliasGuards: RangeKindAliasGuard[] = []; + for (const guard of api.kindGuards()) { + const { guardName: funcName } = guard; + // Skip if a node-level guard with the same name already exists + if (guards.some(g => g.funcName === funcName)) continue; + if (compositeGuards.some(g => g.funcName === funcName)) continue; + + if (guard.type === "range") { + rangeKindAliasGuards.push({ funcName, first: guard.first, last: guard.last }); + } + else { + importTypes.add(guard.aliasName); + const conditions = guard.members.map(m => { + if (api.hasKindAlias(m)) return `${kindGuardName(m)}(kind)`; + return `kind === SyntaxKind.${m}`; + }); + kindAliasGuards.push({ funcName, typeName: guard.aliasName, conditions }); + } + } + + // ── Token instantiation alias guards ── + // For Token instantiation aliases like `BinaryOperatorToken = Token`, + // generate `is(node: Node): node is ` using the kind-level guard. + // Also generate guards for single-kind Token aliases (like EndOfFile, DotToken, etc.) + // Token aliases whose underlying kind alias is range-based return boolean (not a type predicate). + const rangeKindAliasNames = new Set(rangeKindAliasGuards.map(g => g.funcName)); + interface TokenAliasGuard { + funcName: string; + typeName: string; + body: string; + isRangeBased: boolean; + } + const tokenAliasGuards: TokenAliasGuard[] = []; + for (const node of api.nodes()) { + for (const { name: aliasName, typeArg } of node.instantiationAliases) { + const funcName = `is${aliasName}`; + // Skip if already generated + if (guards.some(g => g.funcName === funcName)) continue; + if (compositeGuards.some(g => g.funcName === funcName)) continue; + + if (api.hasKindAlias(typeArg)) { + const isRangeBased = rangeKindAliasNames.has(kindGuardName(typeArg)); + if (!isRangeBased) importTypes.add(aliasName); + // Multi-kind token alias: use the kind-level guard + tokenAliasGuards.push({ funcName, typeName: aliasName, body: `return ${kindGuardName(typeArg)}(node.kind);`, isRangeBased }); + } + else { + importTypes.add(aliasName); + // Single-kind token alias: direct SyntaxKind check + tokenAliasGuards.push({ funcName, typeName: aliasName, body: `return node.kind === SyntaxKind.${typeArg};`, isRangeBased: false }); + } + } + } + + // ── Build output ── + out.push(`// Code generated by tools/scripts/tsc/generate-ts-ast.ts. DO NOT EDIT.`); + out.push(``); + out.push(`import { SyntaxKind } from "#enums/syntaxKind";`); + const sortedImports = [...importTypes].sort((a, b) => a.localeCompare(b)); + out.push(`import type {`); + for (const t of sortedImports) { + out.push(` ${t},`); + } + out.push(`} from "./ast.ts";`); + out.push(`import type { NodeHandle as AsyncNodeHandle } from "../api/async/api.ts";`); + out.push(`import type { NodeHandle as SyncNodeHandle } from "../api/sync/api.ts";`); + out.push(`type NodeHandleLike = { kind: SyntaxKind; resolve(...args: any): any; };`); + // Note that we must `& T` to satisfy the requirements of a type guard, and it must be _second_ so the narrowed `resolve` overload from the specialization is selected (order matters) + out.push(`type SpecializeNodeHandle, U extends Node> = T extends AsyncNodeHandle ? AsyncNodeHandle & T : SyncNodeHandle & T;`); + out.push(``); + + // ── Simple guards ── + for (const g of guards) { + out.push(`export function ${g.funcName}(node: Node): node is ${g.typeName} {`); + if (g.kindAliasConstraint) { + // Use the kind-level guard for nodes with a kind alias constraint + out.push(` return ${kindGuardName(g.kindAliasConstraint)}(node.kind);`); + } + else if (g.kindChecks.length === 1) { + out.push(` return node.kind === ${g.kindChecks[0]};`); + } + else { + out.push(` switch (node.kind) {`); + for (const kindCheck of g.kindChecks) { + out.push(` case ${kindCheck}:`); + } + out.push(` return true;`); + out.push(` default:`); + out.push(` return false;`); + out.push(` }`); + } + out.push(`}`); + out.push(``); + out.push(`export declare namespace ${g.funcName} {`); + out.push(` function Handle>(node: T): node is SpecializeNodeHandle;`); + out.push(`}`); + out.push(`${g.funcName}.Handle = ${g.funcName} as any;`); + out.push(``); + } + + // ── Composite guards ── + for (const g of compositeGuards) { + out.push(`export function ${g.funcName}(node: Node): node is ${g.typeName} {`); + out.push(` ${g.body}`); + out.push(`}`); + out.push(``); + out.push(`export declare namespace ${g.funcName} {`); + out.push(` function Handle>(node: T): node is SpecializeNodeHandle;`); + out.push(`}`); + out.push(``); + out.push(`${g.funcName}.Handle = ${g.funcName} as any;`); + out.push(``); + } + + // ── Kind alias guards ── + for (const g of kindAliasGuards) { + out.push(`export function ${g.funcName}(kind: SyntaxKind): kind is ${g.typeName} {`); + if (g.conditions.length === 1) { + out.push(` return ${g.conditions[0]};`); + } + else { + out.push(` return ${g.conditions.join("\n || ")};`); + } + out.push(`}`); + out.push(``); + } + + // ── Range-based kind alias guards ── + for (const g of rangeKindAliasGuards) { + out.push(`export function ${g.funcName}(kind: SyntaxKind): boolean {`); + out.push(` return kind >= SyntaxKind.${g.first} && kind <= SyntaxKind.${g.last};`); + out.push(`}`); + out.push(``); + } + + // ── Token alias guards ── + for (const g of tokenAliasGuards) { + const returnType = g.isRangeBased ? `boolean` : `node is ${g.typeName}`; + const handleReturnType = g.isRangeBased ? `boolean` : `node is SpecializeNodeHandle`; + out.push(`export function ${g.funcName}(node: Node): ${returnType} {`); + out.push(` ${g.body}`); + out.push(`}`); + out.push(``); + out.push(`export declare namespace ${g.funcName} {`); + out.push(` function Handle>(node: T): ${handleReturnType};`); + out.push(`}`); + out.push(``); + out.push(`${g.funcName}.Handle = ${g.funcName} as any;`); + out.push(``); + } + + while (out.length > 0 && out[out.length - 1] === "") out.pop(); + out.push(""); + + let result = out.join("\n"); + result = result.replace(/\n/g, "\r\n"); + return result; +} + +// ──────────────────────────────────────────────────────────────────────────── +// Code generation: visitor.generated.ts +// ──────────────────────────────────────────────────────────────────────────── + +function generateVisitor(): string { + const out: string[] = []; + const entries = visitorEntries(); + const typeImports = new Set(["Node", "NodeArray"]); + const factoryImports = new Set(["createNodeArray"]); + const isImports = new Set(); + const handWrittenImports = new Set(); + + for (const entry of entries) { + typeImports.add(entry.tsName); + if (entry.handWrittenVisitor) { + handWrittenImports.add(`visitEachChildOf${entry.tsName}`); + } + else { + factoryImports.add(entry.updateName); + } + for (const member of entry.members) { + const { guardName } = visitorVisitExpression(member); + if (guardName) isImports.add(guardName); + } + } + + out.push(`// Code generated by tools/scripts/tsc/generate-ts-ast.ts. DO NOT EDIT.`); + out.push(""); + out.push(`import { SyntaxKind } from "#enums/syntaxKind";`); + out.push(`import type {`); + for (const typeName of [...typeImports].sort((a, b) => a.localeCompare(b))) { + out.push(` ${typeName},`); + } + out.push(`} from "./ast.ts";`); + out.push(`import {`); + for (const name of [...factoryImports].sort((a, b) => a.localeCompare(b))) { + out.push(` ${name},`); + } + out.push(`} from "./factory.generated.ts";`); + out.push(`import {`); + for (const name of [...isImports].sort((a, b) => a.localeCompare(b))) { + out.push(` ${name},`); + } + out.push(`} from "./is.ts";`); + if (handWrittenImports.size > 0) { + out.push(`import {`); + for (const name of [...handWrittenImports].sort((a, b) => a.localeCompare(b))) { + out.push(` ${name},`); + } + out.push(`} from "./visitor.ts";`); + } + out.push(""); + out.push(`/**`); + out.push(` * A callback that receives a node and returns a visited node (or undefined to remove it).`); + out.push(` */`); + out.push(`export type Visitor = (node: Node) => Node | undefined;`); + out.push(""); + out.push(`/**`); + out.push(` * Visits a Node using the supplied visitor, possibly returning a new Node in its place.`); + out.push(` *`); + out.push(` * - If the input node is undefined, then the output is undefined.`); + out.push(` * - If the visitor returns undefined, then the output is undefined.`); + out.push(` * - If the output node is not undefined, then it will satisfy the test function.`); + out.push(` * - In order to obtain a return type that is more specific than \`Node\`, a test`); + out.push(` * function must be provided, and that function must be a type predicate.`); + out.push(` *`); + out.push(` * @param node The Node to visit.`); + out.push(` * @param visitor The callback used to visit the Node.`); + out.push(` * @param test A callback to execute to verify the Node is valid.`); + out.push(` */`); + out.push(`export function visitNode(`); + out.push(` node: TIn,`); + out.push(` visitor: Visitor,`); + out.push(` test: (node: Node) => node is TOut,`); + out.push(`): TOut | (TIn & undefined);`); + out.push(`/**`); + out.push(` * Visits a Node using the supplied visitor, possibly returning a new Node in its place.`); + out.push(` *`); + out.push(` * - If the input node is undefined, then the output is undefined.`); + out.push(` * - If the visitor returns undefined, then the output is undefined.`); + out.push(` *`); + out.push(` * @param node The Node to visit.`); + out.push(` * @param visitor The callback used to visit the Node.`); + out.push(` * @param test An optional callback to execute to verify the Node is valid.`); + out.push(` */`); + out.push(`export function visitNode(`); + out.push(` node: TIn,`); + out.push(` visitor: Visitor,`); + out.push(` test?: (node: Node) => boolean,`); + out.push(`): Node | (TIn & undefined);`); + out.push(`export function visitNode(node: Node | undefined, visitor: Visitor, test?: (node: Node) => boolean): Node | undefined {`); + out.push(` if (node === undefined) return undefined;`); + out.push(` const visited = visitor(node);`); + out.push(` if (visited !== undefined && test !== undefined && !test(visited)) {`); + out.push(` throw new Error("Visited node failed test assertion.");`); + out.push(` }`); + out.push(` return visited;`); + out.push(`}`); + out.push(""); + out.push(`/**`); + out.push(` * Visits a NodeArray using the supplied visitor, possibly returning a new NodeArray in its place.`); + out.push(` *`); + out.push(` * - If the input node array is undefined, the output is undefined.`); + out.push(` * - If the visitor returns undefined for a node, that node is dropped from the result.`); + out.push(` */`); + out.push(`export function visitNodes(nodes: NodeArray, visitor: Visitor): NodeArray;`); + out.push(`export function visitNodes(nodes: NodeArray | undefined, visitor: Visitor): NodeArray | undefined;`); + out.push(`export function visitNodes(nodes: NodeArray | undefined, visitor: Visitor): NodeArray | undefined {`); + out.push(` if (nodes === undefined) return undefined;`); + out.push(` const updated = visitNodesArray(nodes, visitor);`); + out.push(` if (updated === nodes) {`); + out.push(` return nodes;`); + out.push(` }`); + out.push(` return createNodeArray(updated, nodes.pos, nodes.end);`); + out.push(`}`); + out.push(""); + out.push(`export function visitNodesArray(nodes: readonly T[], visitor: Visitor): readonly T[];`); + out.push(`export function visitNodesArray(nodes: readonly T[] | undefined, visitor: Visitor): readonly T[] | undefined;`); + out.push(`export function visitNodesArray(nodes: readonly Node[] | undefined, visitor: Visitor): readonly Node[] | undefined {`); + out.push(` if (nodes === undefined) return undefined;`); + out.push(` let updated: Node[] | undefined;`); + out.push(` for (let i = 0; i < nodes.length; i++) {`); + out.push(` const node = nodes[i];`); + out.push(` const visited = visitor(node);`); + out.push(` if (updated) {`); + out.push(` if (visited) updated.push(visited);`); + out.push(` }`); + out.push(` else if (visited !== node) {`); + out.push(` updated = [];`); + out.push(` for (let j = 0; j < i; j++) updated.push(nodes[j]);`); + out.push(` if (visited) updated.push(visited);`); + out.push(` }`); + out.push(` }`); + out.push(` return updated ?? nodes;`); + out.push(`}`); + out.push(""); + out.push(`/**`); + out.push(` * Visits each child of a Node using the supplied visitor, possibly returning a new Node of the same kind in its place.`); + out.push(` *`); + out.push(` * @param node The Node whose children will be visited.`); + out.push(` * @param visitor The callback used to visit each child.`); + out.push(` * @returns The original node if no children changed, or a new node with visited children.`); + out.push(` */`); + out.push(`export function visitEachChild(node: T, visitor: Visitor): T;`); + out.push(`export function visitEachChild(node: T | undefined, visitor: Visitor): T | undefined;`); + out.push(`export function visitEachChild(node: Node | undefined, visitor: Visitor): Node | undefined {`); + out.push(` if (node === undefined) return undefined;`); + out.push(` const fn = visitEachChildTable[node.kind];`); + out.push(` return fn ? fn(node, visitor) : node;`); + out.push(`}`); + out.push(""); + out.push(`type VisitEachChildFunction = (node: any, visitor: Visitor) => Node;`); + out.push(""); + out.push(`const visitEachChildTable: Record = {`); + for (const entry of entries) { + if (entry.handWrittenVisitor) { + out.push(` [SyntaxKind.${entry.syntaxKind}]: visitEachChildOf${entry.tsName},`); + } + else { + out.push(` [SyntaxKind.${entry.syntaxKind}]: (node: ${entry.tsName}, visitor: Visitor): ${entry.tsName} => {`); + const argNames: string[] = []; + for (const member of entry.members) { + const propName = api.uncapitalize(member.name); + const localName = `_${propName}`; + const { expression } = visitorVisitExpression(member); + out.push(` const ${localName} = ${expression};`); + argNames.push(localName); + } + out.push(` return ${entry.updateName}(node, ${argNames.join(", ")});`); + out.push(` },`); + } + } + out.push(`};`); + out.push(""); + while (out.length > 0 && out[out.length - 1] === "") out.pop(); + out.push(""); + + let result = out.join("\n"); + result = result.replace(/\n/g, "\r\n"); + return result; +} + +// ──────────────────────────────────────────────────────────────────────────── +// Main +// ──────────────────────────────────────────────────────────────────────────── + +function writeAndFormat(filePath: string, content: string) { + fs.writeFileSync(filePath, content); + execaSync("dprint", ["fmt", filePath], { stdio: "inherit", cwd: ROOT }); + console.log(`Generated ${filePath}`); +} + +export default function main() { + console.log("Generating TS AST code..."); + + const factoryPath = path.join(ROOT, "packages/typescript/src/ast/factory.generated.ts"); + const isGenPath = path.join(ROOT, "packages/typescript/src/ast/is.generated.ts"); + const astGenPath = path.join(ROOT, "packages/typescript/src/ast/ast.generated.ts"); + const visitorPath = path.join(ROOT, "packages/typescript/src/ast/visitor.generated.ts"); + + writeAndFormat(astGenPath, generateAstGenerated()); + writeAndFormat(factoryPath, generateFactory()); + writeAndFormat(isGenPath, generateIsGenerated()); + writeAndFormat(visitorPath, generateVisitor()); +} + +if (process.argv[1] === fileURLToPath(import.meta.url)) { + main(); +} diff --git a/tools/scripts/tsc/generate.ts b/tools/scripts/tsc/generate.ts index 6b1904cc4fb74..bca58052b4a0b 100644 --- a/tools/scripts/tsc/generate.ts +++ b/tools/scripts/tsc/generate.ts @@ -1,14 +1,14 @@ -import { fileURLToPath } from "node:url"; -import generateEncoder from "./generate-encoder.ts"; -import generateGoAST from "./generate-go-ast.ts"; -import generateTSAST from "./generate-ts-ast.ts"; - -export default function generate() { - generateEncoder(); - generateGoAST(); - generateTSAST(); -} - -if (process.argv[1] === fileURLToPath(import.meta.url)) { - generate(); -} +import { fileURLToPath } from "node:url"; +import generateEncoder from "./generate-encoder.ts"; +import generateGoAST from "./generate-go-ast.ts"; +import generateTSAST from "./generate-ts-ast.ts"; + +export default function generate() { + generateEncoder(); + generateGoAST(); + generateTSAST(); +} + +if (process.argv[1] === fileURLToPath(import.meta.url)) { + generate(); +} diff --git a/tools/scripts/tsc/schema.ts b/tools/scripts/tsc/schema.ts index 299084c059ebf..f3c9d1e085679 100644 --- a/tools/scripts/tsc/schema.ts +++ b/tools/scripts/tsc/schema.ts @@ -1,1267 +1,1267 @@ -/** Shared type definitions and utilities for ast.json schema, used by both Go and TS generators. */ - -import * as fs from "node:fs"; -import * as path from "node:path"; - -// ──────────────────────────────────────────────────────────────────────────── -// Schema type definitions -// ──────────────────────────────────────────────────────────────────────────── - -export interface Member { - name: string; - type: string | string[]; - optional?: boolean; - list?: "NodeList" | "ModifierList" | "raw"; - visit?: string; - typeGuard?: string; - private?: boolean; - inherited?: boolean; - goOnly?: boolean; - noGo?: boolean; - noTS?: boolean; - noFactory?: boolean; - bitmask?: string; -} - -export interface NodeDef { - kind?: string | string[]; - extends: string[]; - members?: Member[]; - generateSubtreeFacts?: boolean; - arena?: boolean; - handWritten?: boolean; - handWrittenVisitor?: boolean; - typeParameters?: { name: string; constraint: string; default?: string; }[]; - instantiationAliases?: Record; -} - -export interface BaseField { - type: string | string[]; - list?: "NodeList" | "ModifierList" | "raw"; - visit?: string; - typeGuard?: string; - optional?: boolean; - private?: boolean; - goOnly?: boolean; - noGo?: boolean; - noTS?: boolean; - noFactory?: boolean; -} - -export interface BaseEntry { - brand?: string; - extends?: string[]; - fields?: Record; -} - -export interface KindElement { - name?: string; - comment?: string; -} - -export type ListKind = "NodeList" | "ModifierList" | "raw"; -export type BaseTypeKind = "list" | "primitive" | "kind" | "node"; -export type TypeKind = "alias" | "list" | "primitive" | "kind" | "node" | "typeParameter" | "union"; - -abstract class TypeBase { - protected readonly api: SchemaAPI; - - constructor(api: SchemaAPI) { - this.api = api; - } - - abstract readonly kind: TypeKind; - abstract formatGoDeclaration(): string; - abstract formatGoReference(): string; - - abstract formatTypeScript(): string; - abstract baseKind(): BaseTypeKind; -} - -export class PrimitiveType extends TypeBase { - readonly kind = "primitive" as const; - readonly name: string; - - constructor(api: SchemaAPI, name: string) { - super(api); - this.name = name; - } - - formatGoDeclaration(): string { - throw new Error(`Primitive type ${this.name} has no Go declaration form`); - } - - formatGoReference(): string { - return this.name; - } - - formatTypeScript(): string { - const typeMap: Record = { - bool: "boolean", - int: "number", - }; - return typeMap[this.name] || this.name; - } - - baseKind(): BaseTypeKind { - return "primitive"; - } -} - -export class KindType extends TypeBase { - readonly kind = "kind" as const; - readonly value: string; - - constructor(api: SchemaAPI, value: string) { - super(api); - this.value = value; - } - - formatGoDeclaration(): string { - return "Kind"; - } - - formatGoReference(): string { - return "Kind"; - } - - formatTypeScript(): string { - return this.value === "Kind" ? "SyntaxKind" : this.value; - } - - baseKind(): BaseTypeKind { - return "kind"; - } - - get name(): string { - return this.value.startsWith("SyntaxKind.") ? this.value.slice("SyntaxKind.".length) : this.value; - } - - formatGoConstant(): string { - return this.value === "Kind" ? "Kind" : `Kind${this.name}`; - } -} - -export class NodeType extends TypeBase { - readonly kind = "node" as const; - readonly name: string; - private membersCache?: MemberInfo[]; - private fieldsCache?: MemberInfo[]; - private inheritedFieldCache?: Map; - - constructor(api: SchemaAPI, name: string) { - super(api); - this.name = name; - } - - formatGoDeclaration(): string { - return this.name; - } - - formatGoReference(): string { - return this.api.hasNode(this.name) ? `*${this.name}Node` : `*${this.name}`; - } - - formatTypeScript(): string { - return this.name; - } - - baseKind(): BaseTypeKind { - return "node"; - } - - concreteNodeName(): string { - if (!this.isConcrete) { - throw new Error(`${this.name} is not a concrete node`); - } - return this.name; - } - - get key(): string { - return this.name; - } - - get isConcrete(): boolean { - return !!this.def; - } - - get isBase(): boolean { - return !!this.entry; - } - - get def(): NodeDef | undefined { - return this.api.getNodeDef(this.name); - } - - get entry(): BaseEntry | undefined { - return this.api.schema.bases[this.name]; - } - - get syntaxKindName(): string { - const kind = this.def?.kind; - if (Array.isArray(kind)) return kind[0]; - return kind || this.name; - } - - get extendsKeys(): string[] { - return this.def?.extends || this.entry?.extends || []; - } - - get extends(): NodeType[] { - return this.extendsKeys.map(key => this.api.getBase(key)).filter((base): base is NodeType => !!base); - } - - get members(): MemberInfo[] { - if (!this.membersCache) { - this.membersCache = (this.def?.members || []).map(member => new MemberInfo(this.api, member.name, member, this)); - } - return this.membersCache; - } - - get kindAliases(): string[] { - const kind = this.def?.kind; - if (Array.isArray(kind)) return kind.slice(1); - return []; - } - - get brand(): string | undefined { - return this.entry?.brand; - } - - get fields(): MemberInfo[] { - if (!this.fieldsCache) { - this.fieldsCache = Object.entries(this.entry?.fields || {}).map(([name, field]) => new MemberInfo(this.api, name, field)); - } - return this.fieldsCache; - } - - get arena(): boolean { - return this.def?.arena || false; - } - - get handWritten(): boolean { - return this.def?.handWritten || false; - } - - get handWrittenVisitor(): boolean { - return this.def?.handWrittenVisitor || false; - } - - get generateSubtreeFacts(): boolean { - return this.def?.generateSubtreeFacts || false; - } - - get typeParameters(): TypeParameterInfo[] { - return this.def?.typeParameters || []; - } - - get instantiationAliases(): InstantiationAliasInfo[] { - return Object.entries(this.def?.instantiationAliases || {}).map(([name, typeArg]) => ({ name, typeArg })); - } - - get kindType(): Type { - return this.kindMember()?.declaredType ?? this.api.kindType(`SyntaxKind.${this.syntaxKindName}`); - } - - kindMember(): MemberInfo | undefined { - return this.members.find(member => member.name === "Kind" || member.name === "kind"); - } - - field(name: string): MemberInfo | undefined { - return this.fields.find(field => field.name === name); - } - - inheritedField(name: string): MemberInfo | undefined { - if (!this.inheritedFieldCache) { - this.inheritedFieldCache = new Map(); - } - const cached = this.inheritedFieldCache.get(name); - if (cached !== undefined) { - return cached || undefined; - } - - for (const base of this.extends) { - const direct = base.field(name); - if (direct) { - this.inheritedFieldCache.set(name, direct); - return direct; - } - - const inherited = base.inheritedField(name); - if (inherited) { - this.inheritedFieldCache.set(name, inherited); - return inherited; - } - } - - this.inheritedFieldCache.set(name, null); - return undefined; - } - - isMultiKind(): boolean { - return this.kindTypes().length > 1; - } - - kindTypes(): KindType[] { - const collect = (type: Type): KindType[] => { - switch (type.kind) { - case "kind": - return type.value === "Kind" ? [] : [type]; - case "union": - return type.types.flatMap(collect); - case "alias": - return collect(type.resolved); - case "typeParameter": - return collect(type.constraint); - default: - return []; - } - }; - - const kindTypes = collect(this.kindType); - if (kindTypes.length === 0) { - return [this.api.kindType(`SyntaxKind.${this.syntaxKindName}`)]; - } - return [...new Map(kindTypes.map(type => [type.value, type])).values()]; - } - - /** - * All SyntaxKind types for this node, including kindAliases. - * For multi-kind nodes, returns all instantiated kind types. - * For single-kind nodes, returns the primary kind plus any kindAliases. - */ - allKinds(): KindType[] { - return [ - ...this.kindTypes(), - ...this.kindAliases.map(a => this.api.kindType(`SyntaxKind.${a}`)), - ]; - } - - isGoOnly(): boolean { - return this.api.isGoOnlyBase(this.name); - } -} - -export class TypeParameterType extends TypeBase { - readonly kind = "typeParameter" as const; - readonly name: string; - readonly constraint: Type; - - constructor(api: SchemaAPI, name: string, constraint: Type) { - super(api); - this.name = name; - this.constraint = constraint; - } - - formatGoDeclaration(): string { - return this.constraint.formatGoDeclaration(); - } - - formatGoReference(): string { - return this.constraint.formatGoReference(); - } - - formatTypeScript(): string { - return this.name; - } - - baseKind(): BaseTypeKind { - return this.constraint.baseKind(); - } - - concreteNodeName(): string | undefined { - switch (this.constraint.kind) { - case "node": - case "alias": - case "typeParameter": - return this.constraint.concreteNodeName(); - } - return undefined; - } -} - -export class AliasType extends TypeBase { - readonly kind = "alias" as const; - readonly name: string; - readonly resolved: Type; - readonly resolveAs?: "node"; - - constructor(api: SchemaAPI, name: string, resolved: Type, resolveAs?: "node") { - super(api); - this.name = name; - this.resolved = resolved; - this.resolveAs = resolveAs; - } - - formatGoDeclaration(): string { - return this.name; - } - - formatGoReference(): string { - return this.baseKind() === "node" || (this.baseKind() === "list" && (this.resolved as ListType).listKind !== "raw") ? `*${this.name}` : this.name; - } - - formatTypeScript(): string { - return this.name; - } - - baseKind(): BaseTypeKind { - return this.resolved.baseKind(); - } - - concreteNodeName(): string | undefined { - return this.api.getInstantiationNodeName(this.name); - } - - get isUnion(): boolean { - const alias = this.api.schema.nodes.aliases[this.name]; - return Array.isArray(alias); - } - - get isBaseAlias(): boolean { - const alias = this.api.schema.nodes.aliases[this.name]; - return !!alias && !Array.isArray(alias); - } - - get unionMemberNames(): string[] { - const alias = this.api.schema.nodes.aliases[this.name]; - return Array.isArray(alias) ? alias : []; - } - - get unionMemberTypes(): Type[] { - return this.unionMemberNames.map(name => this.api.resolveType(name, undefined, this.resolveAs)); - } - - get baseKey(): string | undefined { - const alias = this.api.schema.nodes.aliases[this.name]; - return alias && !Array.isArray(alias) ? alias.base : undefined; - } - - get base(): NodeType | undefined { - return this.baseKey ? this.api.getBase(this.baseKey) : undefined; - } - - get elementTypeName(): string | undefined { - return this.api.schema.nodes.listAliases?.[this.name]; - } - - get elementType(): Type | undefined { - return this.elementTypeName ? this.api.resolveType(this.elementTypeName) : undefined; - } -} - -export class UnionType extends TypeBase { - readonly kind = "union" as const; - readonly types: Type[]; - - constructor(api: SchemaAPI, types: Type[]) { - super(api); - this.types = types; - } - - formatGoDeclaration(): string { - throw new Error("Cannot declare Go type for union"); - } - - formatGoReference(): string { - const concreteNodes = new Set(); - let allConcreteNodes = true; - for (const type of this.types) { - const nodeName = this.concreteNodeNameOf(type); - if (!nodeName) { - allConcreteNodes = false; - break; - } - concreteNodes.add(nodeName); - } - if (allConcreteNodes && concreteNodes.size === 1) { - return `*${[...concreteNodes][0]}Node`; - } - - if (this.types.some(type => type.baseKind() === "node" || type.baseKind() === "list")) { - return "*Node"; - } - - if (this.types.every(type => type.baseKind() === "kind")) { - return "Kind"; - } - - const references = [...new Set(this.types.map(type => type.formatGoReference()))]; - if (references.length === 1) return references[0]; - throw new Error(`Cannot resolve Go reference form for union ${references.join(" | ")}`); - } - - formatTypeScript(): string { - return this.types.map(type => type.formatTypeScript()).join(" | "); - } - - baseKind(): BaseTypeKind { - const baseKinds = new Set(this.types.map(type => type.baseKind())); - if (baseKinds.size === 1) return this.types[0].baseKind(); - if (this.types.some(type => type.baseKind() === "list" || type.baseKind() === "node")) { - return "node"; - } - if (this.types.some(type => type.baseKind() === "kind")) { - return "kind"; - } - return "primitive"; - } - - private concreteNodeNameOf(type: Type): string | undefined { - switch (type.kind) { - case "node": - case "alias": - case "typeParameter": - return type.concreteNodeName(); - default: - return undefined; - } - } -} - -export class ListType extends TypeBase { - readonly kind = "list" as const; - readonly elementType: Type; - readonly listKind: ListKind; - - constructor(api: SchemaAPI, elementType: Type, listKind: ListKind) { - super(api); - this.elementType = elementType; - this.listKind = listKind; - } - - formatGoDeclaration(): string { - throw new Error(`List type ${this.listKind} has no generated Go declaration form`); - } - - formatGoReference(): string { - if (this.listKind === "raw") { - return this.elementType.baseKind() === "node" ? "[]*Node" : `[]${this.elementType.formatGoReference()}`; - } - if (this.listKind === "ModifierList") { - return "*ModifierList"; - } - return `*${this.api.listAliasName(this.elementType) || "NodeList"}`; - } - - formatTypeScript(): string { - const elementType = this.elementType.formatTypeScript(); - if (this.listKind === "raw") { - return `readonly ${elementType}[]`; - } - return `NodeArray<${elementType}>`; - } - - baseKind(): BaseTypeKind { - return this.elementType.baseKind() === "node" ? "list" : this.elementType.baseKind(); - } - - raw(): ListType { - if (this.listKind === "raw") { - return this; - } - return this.api.listType(this.elementType, "raw"); - } -} - -export type Type = - | AliasType - | KindType - | ListType - | NodeType - | PrimitiveType - | TypeParameterType - | UnionType; - -export interface TypeParameterInfo { - name: string; - constraint: string; - default?: string; -} - -export interface InstantiationAliasInfo { - name: string; - typeArg: string; -} - -export class MemberInfo { - private readonly api: SchemaAPI; - private readonly nameValue: string; - private readonly member?: Member; - private readonly field?: BaseField; - private inheritedFieldCache?: MemberInfo | null; - readonly node?: NodeType; - - constructor(api: SchemaAPI, name: string, source: Member | BaseField, node?: NodeType) { - this.api = api; - this.nameValue = name; - if (node) { - this.member = source as Member; - } - else { - this.field = source as BaseField; - } - this.node = node; - } - - get name(): string { - return this.nameValue; - } - - get declaredType(): Type { - return this.api.resolveType(this.rawType, this.node?.def); - } - - get type(): Type { - return this.listKind ? this.api.listType(this.declaredType, this.listKind) : this.declaredType; - } - - get rawType(): string | string[] { - if (this.field) return this.field.type; - if (this.member && !this.member.inherited) return this.member.type; - // Inherited member with an explicit type override narrows the base type - if (this.member?.inherited && this.member.type) return this.member.type; - if (this.inheritedField) return this.inheritedField.rawType; - if (this.member) return this.member.type; - throw new Error(`Member ${this.name} has no raw type source`); - } - - get listKind(): ListKind | undefined { - if (this.field) return this.field.list; - if (this.member && !this.member.inherited) return this.member.list; - return this.inheritedField?.listKind; - } - - get optional(): boolean { - return this.field?.optional ?? this.member?.optional ?? this.inheritedField?.optional ?? false; - } - - get inherited(): boolean { - return this.member?.inherited ?? false; - } - - get private(): boolean { - if (this.field?.private) return true; - if (this.member?.private) return true; - return this.inheritedField?.private ?? false; - } - - get goOnly(): boolean { - return this.field?.goOnly ?? this.member?.goOnly ?? false; - } - - /** Absent from Go structs, encoding, and factory. noGo implies noFactory. */ - get noGo(): boolean { - return this.field?.noGo ?? this.member?.noGo ?? false; - } - - /** Absent from encoding and all TS files. goOnly implies noTS. */ - get noTS(): boolean { - return this.goOnly || (this.field?.noTS ?? this.member?.noTS ?? false); - } - - /** Absent from factory functions, visitor, and clone. goOnly implies noFactory. */ - get noFactory(): boolean { - return this.goOnly || this.field?.noFactory || this.member?.noFactory || false; - } - - get visit(): string | undefined { - if (this.member?.visit) return this.member.visit; - if (this.field?.visit) return this.field.visit; - return this.inheritedField?.visit; - } - - get typeGuard(): string | undefined { - if (this.member?.typeGuard) return this.member.typeGuard; - if (this.field?.typeGuard) return this.field.typeGuard; - return this.inheritedField?.typeGuard; - } - - get bitmask(): string | undefined { - return this.member?.bitmask; - } - - get inheritedField(): MemberInfo | undefined { - if (!this.member?.inherited || !this.node) return undefined; - if (this.inheritedFieldCache !== undefined) { - return this.inheritedFieldCache || undefined; - } - this.inheritedFieldCache = this.node.inheritedField(this.name) || null; - return this.inheritedFieldCache || undefined; - } - - isKindParam(): boolean { - if (!this.member || !this.node) return false; - if (this.name !== "Kind" && this.name !== "kind") return false; - return this.declaredType.baseKind() === "kind"; - } - - isChild(): boolean { - if (!this.member || !this.node) return false; - return this.type.baseKind() === "list" || this.type.baseKind() === "node"; - } - - /** - * True when this inherited member carries an explicit type or optionality - * override relative to its base field. Such members need their own - * property declaration in a TS interface even though they are inherited. - */ - hasTypeScriptOverride(): boolean { - if (!this.member?.inherited || !this.inheritedField) return false; - // Explicit type override on the member narrows the base type. - if (this.member.type && this.type.formatTypeScript() !== this.inheritedField.type.formatTypeScript()) return true; - // Explicit optionality override (e.g. base is required but this member is optional, or vice-versa). - if (this.member.optional !== undefined && this.optional !== this.inheritedField.optional) return true; - return false; - } - - /** Go parameter name: uncapitalized, with Go keyword avoidance. */ - goParamName(): string { - const name = this.api.uncapitalize(this.name); - if (name === "type") return "typeNode"; - if (name === "default") return "defaultNode"; - if (name === "case") return "caseNode"; - return name; - } -} - -export interface KindElementInfo { - name?: string; - comment?: string; -} - -export interface KindMarkerInfo { - name: string; - value: string; -} - -export interface KindAliasInfo { - name: string; - members: string[]; - range?: [string, string]; -} - -/** A kind guard is either range-based (kind >= First && kind <= Last) or enumerated (switch/conditions). */ -export type KindGuardInfo = - & { - /** The alias name from the schema, e.g. "TokenSyntaxKind". */ - aliasName: string; - /** The guard function name, e.g. "isTokenKind". Drops "Syntax" from the alias name. */ - guardName: string; - } - & ( - | { type: "range"; first: string; last: string; } - | { type: "enumerated"; members: string[]; } - ); - -/** Compute a guard function name from a kind alias name. Drops "Syntax" from the name. */ -export function kindGuardName(aliasName: string): string { - return `is${aliasName.replace("Syntax", "")}`; -} - -export interface Schema { - $schema: string; - bases: Record; - nodes: { - definitions: Record; - aliases: Record; - listAliases?: Record; - }; - kinds?: { - elements: (string | KindElement)[]; - markers: { name: string; value: string; }[]; - aliases?: Record; - }; -} - -// ──────────────────────────────────────────────────────────────────────────── -// SchemaAPI — shared logic for both Go and TS generators -// ──────────────────────────────────────────────────────────────────────────── - -/** - * Wraps the parsed ast.json schema and provides common query/resolution - * methods used by both the Go and TS code generators. - */ -export class SchemaAPI { - readonly schema: Schema; - private readonly listAliasNameMap = new Map(); - private readonly instantiationAliasMap = new Map(); - /** Maps syntax kind names to (aliasName, nodeName) for instantiation aliases, or (undefined, nodeName) for direct nodes. */ - private readonly syntaxKindToNodeInfo = new Map(); - private readonly primitiveTypeMap = new Map(); - private readonly kindTypeMap = new Map(); - private readonly nodeTypeMap = new Map(); - private readonly listTypeMapByKey = new Map(); - private readonly unionTypeMap = new Map(); - private readonly aliasTypeMap = new Map(); - private readonly typeParameterTypeMap = new Map(); - private readonly primitiveTypes = new Set([ - "any", - "bool", - "boolean", - "int", - "ModifierFlags", - "NodeFlags", - "string", - "TokenFlags", - ]); - - constructor(schema: Schema) { - this.schema = schema; - const listAliases = schema.nodes.listAliases || {}; - for (const [aliasName, elementType] of Object.entries(listAliases)) { - this.listAliasNameMap.set(this.typeCacheKey(this.resolveType(elementType)), aliasName); - } - for (const [nodeName, nodeDef] of Object.entries(schema.nodes.definitions)) { - for (const aliasName of Object.keys(nodeDef.instantiationAliases || {})) { - this.instantiationAliasMap.set(aliasName, nodeName); - } - // Map syntax kind names to instantiation aliases - for (const [aliasName, typeArg] of Object.entries(nodeDef.instantiationAliases || {})) { - // typeArg is a syntax kind name or a kind alias name - if (this.hasKindAlias(typeArg)) { - // Multi-kind instantiation alias (e.g. BinaryOperatorToken) — expand the kind alias - for (const kindType of this.expandKindAliasMembers(typeArg)) { - if (!this.syntaxKindToNodeInfo.has(kindType.name)) { - this.syntaxKindToNodeInfo.set(kindType.name, { aliasName, nodeName }); - } - } - } - else { - if (!this.syntaxKindToNodeInfo.has(typeArg)) { - this.syntaxKindToNodeInfo.set(typeArg, { aliasName, nodeName }); - } - } - } - // Map the node's own syntax kind(s) to itself - const primaryKind = Array.isArray(nodeDef.kind) ? nodeDef.kind[0] : (nodeDef.kind || nodeName); - if (!this.syntaxKindToNodeInfo.has(primaryKind)) { - this.syntaxKindToNodeInfo.set(primaryKind, { nodeName }); - } - if (Array.isArray(nodeDef.kind)) { - for (const k of nodeDef.kind.slice(1)) { - if (!this.syntaxKindToNodeInfo.has(k)) { - this.syntaxKindToNodeInfo.set(k, { nodeName }); - } - } - } - } - } - - // ── String helpers ────────────────────────────────────────────────────── - - /** Capitalize the first character: "foo" → "Foo". */ - capitalize(s: string): string { - return s.charAt(0).toUpperCase() + s.slice(1); - } - - /** Uncapitalize, with special handling for "JSDoc" prefix. */ - uncapitalize(s: string): string { - if (s.startsWith("JSDoc")) return "jsdoc" + s.slice(5); - return s.charAt(0).toLowerCase() + s.slice(1); - } - - bases(): NodeType[] { - return Object.keys(this.schema.bases).map(key => this.getBase(key)).filter((base): base is NodeType => !!base); - } - - getBase(key: string): NodeType | undefined { - if (!(key in this.schema.bases)) return undefined; - return this.nodeType(key); - } - - nodes(): NodeType[] { - return Object.keys(this.schema.nodes.definitions).map(key => this.getNode(key)).filter((node): node is NodeType => !!node); - } - - getNode(key: string): NodeType | undefined { - if (!(key in this.schema.nodes.definitions)) return undefined; - return this.nodeType(key); - } - - hasNode(key: string): boolean { - return key in this.schema.nodes.definitions; - } - - getNodeDef(key: string): NodeDef | undefined { - return this.schema.nodes.definitions[key]; - } - - nodeAliases(): AliasType[] { - return Object.keys(this.schema.nodes.aliases).map(name => this.getNodeAlias(name)).filter((alias): alias is AliasType => !!alias); - } - - getNodeAlias(name: string): AliasType | undefined { - if (!(name in this.schema.nodes.aliases)) return undefined; - const type = this.resolveType(name); - return type.kind === "alias" ? type : undefined; - } - - listAliases(): AliasType[] { - return Object.keys(this.schema.nodes.listAliases || {}).map(name => this.resolveType(name)).filter((type): type is AliasType => type.kind === "alias"); - } - - kindElements(): KindElementInfo[] { - return (this.schema.kinds?.elements || []).map(element => { - if (typeof element === "string") { - return { name: element }; - } - return { name: element.name, comment: element.comment }; - }); - } - - kindMarkers(): KindMarkerInfo[] { - return this.schema.kinds?.markers || []; - } - - /** Resolve a marker name to its concrete kind value. If the name is already a kind element, returns itself. */ - resolveKindMarkerValue(name: string): string { - // If it's a marker, resolve to its value (recursively since markers can reference other markers) - const marker = this.kindMarkers().find(m => m.name === name); - if (marker) return this.resolveKindMarkerValue(marker.value); - return name; - } - - kindAliases(): KindAliasInfo[] { - return Object.entries(this.schema.kinds?.aliases || {}).map(([name, value]) => { - if (Array.isArray(value)) { - return { name, members: value }; - } - // Range format: { range: ["FirstX", "LastX"] } - const [first, last] = value.range; - const elements = this.kindElements().filter(e => e.name).map(e => e.name!); - const firstIdx = elements.indexOf(this.resolveKindMarkerValue(first)); - const lastIdx = elements.indexOf(this.resolveKindMarkerValue(last)); - if (firstIdx === -1 || lastIdx === -1) { - throw new Error(`Range alias ${name}: could not resolve range [${first}, ${last}]`); - } - const members = elements.slice(firstIdx, lastIdx + 1); - return { name, members, range: value.range as [string, string] }; - }); - } - - hasKindAlias(name: string): boolean { - return this.kindAliases().some(alias => alias.name === name); - } - - getKindAlias(name: string): KindAliasInfo | undefined { - return this.kindAliases().find(alias => alias.name === name); - } - - /** - * Returns kind guard info for each kind alias. Range-based aliases produce range guards; - * enumerated aliases produce guards whose members reference either sub-alias guard calls - * or concrete kind names. - */ - kindGuards(): KindGuardInfo[] { - return this.kindAliases().map(({ name, members, range }): KindGuardInfo => { - const guardName = kindGuardName(name); - if (range) { - return { aliasName: name, guardName, type: "range", first: range[0], last: range[1] }; - } - return { aliasName: name, guardName, type: "enumerated", members }; - }); - } - - hasKindElement(name: string): boolean { - return this.kindElements().some(element => element.name === name); - } - - expandKindAliasMembers(name: string): KindType[] { - const alias = this.getKindAlias(name); - if (!alias) return [this.kindType(`SyntaxKind.${name}`)]; - const result: KindType[] = []; - for (const member of alias.members) { - if (this.hasKindAlias(member)) { - result.push(...this.expandKindAliasMembers(member)); - } - else { - result.push(this.kindType(`SyntaxKind.${member}`)); - } - } - return result; - } - - /** A base is "Go-only" if it has no brand AND all its fields are both noTS and noFactory. */ - isGoOnlyBase(key: string): boolean { - const base = this.schema.bases[key]; - if (!base) return false; - if (base.brand) return false; - if (!base.fields) return true; - return Object.values(base.fields).every(f => f.goOnly || (f.noTS && f.noFactory)); - } - - /** - * Given a syntax kind name (e.g. "AbstractKeyword"), returns the node type - * that has that syntax kind. For instantiation aliases, returns the alias type - * (e.g. `AliasType("AbstractKeyword", NodeType("Token"))`). For direct nodes, - * returns the node type itself. - */ - resolveNodeTypeForSyntaxKind(syntaxKindName: string): Type | undefined { - const info = this.syntaxKindToNodeInfo.get(syntaxKindName); - if (!info) return undefined; - if (info.aliasName) { - return this.aliasType(info.aliasName, this.nodeType(info.nodeName)); - } - return this.nodeType(info.nodeName); - } - - resolveType(typeName: string | string[], node?: NodeDef, resolveAs?: "node"): Type { - if (Array.isArray(typeName)) { - return this.unionType(typeName.map(type => this.resolveType(type, node, resolveAs))); - } - - if (node?.typeParameters) { - for (const tp of node.typeParameters) { - if (tp.name === typeName) { - const nodeKey = this.nodeKeyFor(node); - if (!nodeKey) { - throw new Error(`Type parameter ${typeName} requires a node context`); - } - const constraint = tp.constraint === typeName - ? this.primitiveType(typeName) - : this.resolveType(tp.constraint, node); - return this.typeParameterType(nodeKey, typeName, constraint); - } - } - } - - const kindAliases = this.schema.kinds?.aliases || {}; - if (typeName in kindAliases) { - if (resolveAs === "node") { - // Resolve each kind in the alias to its corresponding node type - const kindMembers = this.expandKindAliasMembers(typeName); - return this.unionType(kindMembers.map(kindType => { - const resolved = this.resolveNodeTypeForSyntaxKind(kindType.name); - if (!resolved) { - throw new Error(`Kind alias member "${kindType.name}" (from "${typeName}") does not resolve to a node type`); - } - return resolved; - })); - } - return this.aliasType( - typeName, - this.unionType(this.expandKindAliasMembers(typeName)), - ); - } - - if (typeName === "Kind" || typeName.startsWith("SyntaxKind.")) { - return this.kindType(typeName); - } - - if (typeName === "Node") { - return this.nodeType(typeName); - } - - if (this.primitiveTypes.has(typeName)) { - return this.primitiveType(typeName); - } - - const listAliasTarget = this.schema.nodes.listAliases?.[typeName]; - if (listAliasTarget) { - return this.aliasType(typeName, this.listType(this.resolveType(listAliasTarget, node), "NodeList")); - } - - const instantiationNode = this.instantiationAliasMap.get(typeName); - if (instantiationNode) { - return this.aliasType(typeName, this.nodeType(instantiationNode)); - } - - if (typeName in this.schema.nodes.aliases) { - const alias = this.schema.nodes.aliases[typeName]; - if (Array.isArray(alias)) { - return this.aliasType(typeName, this.unionType(alias.map(type => this.resolveType(type, node, "node"))), "node"); - } - return this.aliasType(typeName, this.nodeType(alias.base)); - } - - if (typeName in this.schema.nodes.definitions) { - return this.nodeType(typeName); - } - - if (typeName in this.schema.bases) { - return this.nodeType(typeName); - } - - return this.primitiveType(typeName); - } - - validate(): void { - for (const base of this.bases()) { - if (base !== this.resolveType(base.key)) { - throw new Error(`Base type cache mismatch for ${base.key}`); - } - for (const ext of base.extendsKeys) { - if (!this.getBase(ext)) { - throw new Error(`Unknown base extends target ${ext} from ${base.key}`); - } - } - for (const field of base.fields) { - if (field.type !== field.type) { - throw new Error(`Base field type cache mismatch for ${base.key}.${field.name}`); - } - } - } - - for (const node of this.nodes()) { - if (this.resolveType(node.key) !== this.nodeType(node.key)) { - throw new Error(`Node type cache mismatch for ${node.key}`); - } - for (const ext of node.extendsKeys) { - if (!this.getBase(ext)) { - throw new Error(`Unknown node extends target ${ext} from ${node.key}`); - } - } - for (const member of node.members) { - if (member.type !== member.type) { - throw new Error(`Member type cache mismatch for ${node.key}.${member.name}`); - } - if (member.declaredType !== member.declaredType) { - throw new Error(`Declared member type cache mismatch for ${node.key}.${member.name}`); - } - if (member.inheritedField && member.inheritedField.type !== member.inheritedField.type) { - throw new Error(`Inherited field type cache mismatch for ${node.key}.${member.name}`); - } - } - } - - for (const alias of this.nodeAliases()) { - if (alias !== this.resolveType(alias.name)) { - throw new Error(`Alias type cache mismatch for ${alias.name}`); - } - if (alias.baseKey && !this.getBase(alias.baseKey)) { - throw new Error(`Unknown alias base ${alias.baseKey} from ${alias.name}`); - } - for (const [index, memberName] of alias.unionMemberNames.entries()) { - if (alias.unionMemberTypes[index] !== this.resolveType(memberName, undefined, alias.resolveAs)) { - // Force member types to be reachable and cached via their names. - throw new Error(`Alias union member cache mismatch for ${alias.name}`); - } - } - } - - for (const listAlias of this.listAliases()) { - const resolved = this.resolveType(listAlias.name); - if (resolved.kind !== "alias" || resolved.resolved.kind !== "list") { - throw new Error(`List alias ${listAlias.name} did not resolve to an alias of a list type`); - } - if (!listAlias.elementTypeName) { - throw new Error(`List alias ${listAlias.name} is missing an element type`); - } - if (listAlias.elementType !== this.resolveType(listAlias.elementTypeName)) { - throw new Error(`List alias element type cache mismatch for ${listAlias.name}`); - } - } - - for (const marker of this.kindMarkers()) { - if (!this.hasKindElement(marker.value) && !this.kindMarkers().some(candidate => candidate.name === marker.value)) { - throw new Error(`Kind marker ${marker.name} references undefined kind or marker ${marker.value}`); - } - } - - for (const alias of this.kindAliases()) { - for (const member of alias.members) { - if (!this.hasKindAlias(member) && !this.hasKindElement(member)) { - throw new Error(`Unknown kind alias member ${member} in ${alias.name}`); - } - } - } - } - - primitiveType(name: string): PrimitiveType { - const existing = this.primitiveTypeMap.get(name); - if (existing) return existing; - const type = new PrimitiveType(this, name); - this.primitiveTypeMap.set(name, type); - return type; - } - - kindType(value: string): KindType { - const existing = this.kindTypeMap.get(value); - if (existing) return existing; - const type = new KindType(this, value); - this.kindTypeMap.set(value, type); - return type; - } - - nodeType(name: string): NodeType { - const existing = this.nodeTypeMap.get(name); - if (existing) return existing; - const type = new NodeType(this, name); - this.nodeTypeMap.set(name, type); - return type; - } - - listType(elementType: Type, listKind: ListKind): ListType { - const key = `${listKind}:${this.typeCacheKey(elementType)}`; - const existing = this.listTypeMapByKey.get(key); - if (existing) return existing; - const type = new ListType(this, elementType, listKind); - this.listTypeMapByKey.set(key, type); - return type; - } - - unionType(types: Type[]): UnionType { - const key = types.map(type => this.typeCacheKey(type)).join("|"); - const existing = this.unionTypeMap.get(key); - if (existing) return existing; - const type = new UnionType(this, types); - this.unionTypeMap.set(key, type); - return type; - } - - aliasType(name: string, resolved: Type, resolveAs?: "node"): AliasType { - const key = `${name}:${this.typeCacheKey(resolved)}`; - const existing = this.aliasTypeMap.get(key); - if (existing) return existing; - const type = new AliasType(this, name, resolved, resolveAs); - this.aliasTypeMap.set(key, type); - return type; - } - - typeParameterType(nodeKey: string, name: string, constraint: Type): TypeParameterType { - const key = `${nodeKey}:${name}:${this.typeCacheKey(constraint)}`; - const existing = this.typeParameterTypeMap.get(key); - if (existing) return existing; - const type = new TypeParameterType(this, name, constraint); - this.typeParameterTypeMap.set(key, type); - return type; - } - - getInstantiationNodeName(name: string): string | undefined { - return this.instantiationAliasMap.get(name); - } - - listAliasName(elementType: Type): string | undefined { - return this.listAliasNameMap.get(this.typeCacheKey(elementType)); - } - - private nodeKeyFor(node: NodeDef): string | undefined { - return Object.entries(this.schema.nodes.definitions).find(([, def]) => def === node)?.[0]; - } - - private typeCacheKey(type: Type): string { - switch (type.kind) { - case "primitive": - return `primitive:${type.name}`; - case "kind": - return `kind:${type.value}`; - case "node": - return `node:${type.name}`; - case "alias": - return `alias:${type.name}:${this.typeCacheKey(type.resolved)}`; - case "list": - return `list:${type.listKind}:${this.typeCacheKey(type.elementType)}`; - case "typeParameter": - return `typeParameter:${type.name}:${this.typeCacheKey(type.constraint)}`; - case "union": - return `union:${type.types.map(member => this.typeCacheKey(member)).join("|")}`; - } - } -} - -const ROOT = path.resolve(import.meta.dirname!, "../../.."); -export const api = new SchemaAPI(JSON.parse( - fs.readFileSync(path.join(ROOT, "tools/scripts/tsc/ast.json"), "utf-8"), -) as Schema); -api.validate(); +/** Shared type definitions and utilities for ast.json schema, used by both Go and TS generators. */ + +import * as fs from "node:fs"; +import * as path from "node:path"; + +// ──────────────────────────────────────────────────────────────────────────── +// Schema type definitions +// ──────────────────────────────────────────────────────────────────────────── + +export interface Member { + name: string; + type: string | string[]; + optional?: boolean; + list?: "NodeList" | "ModifierList" | "raw"; + visit?: string; + typeGuard?: string; + private?: boolean; + inherited?: boolean; + goOnly?: boolean; + noGo?: boolean; + noTS?: boolean; + noFactory?: boolean; + bitmask?: string; +} + +export interface NodeDef { + kind?: string | string[]; + extends: string[]; + members?: Member[]; + generateSubtreeFacts?: boolean; + arena?: boolean; + handWritten?: boolean; + handWrittenVisitor?: boolean; + typeParameters?: { name: string; constraint: string; default?: string; }[]; + instantiationAliases?: Record; +} + +export interface BaseField { + type: string | string[]; + list?: "NodeList" | "ModifierList" | "raw"; + visit?: string; + typeGuard?: string; + optional?: boolean; + private?: boolean; + goOnly?: boolean; + noGo?: boolean; + noTS?: boolean; + noFactory?: boolean; +} + +export interface BaseEntry { + brand?: string; + extends?: string[]; + fields?: Record; +} + +export interface KindElement { + name?: string; + comment?: string; +} + +export type ListKind = "NodeList" | "ModifierList" | "raw"; +export type BaseTypeKind = "list" | "primitive" | "kind" | "node"; +export type TypeKind = "alias" | "list" | "primitive" | "kind" | "node" | "typeParameter" | "union"; + +abstract class TypeBase { + protected readonly api: SchemaAPI; + + constructor(api: SchemaAPI) { + this.api = api; + } + + abstract readonly kind: TypeKind; + abstract formatGoDeclaration(): string; + abstract formatGoReference(): string; + + abstract formatTypeScript(): string; + abstract baseKind(): BaseTypeKind; +} + +export class PrimitiveType extends TypeBase { + readonly kind = "primitive" as const; + readonly name: string; + + constructor(api: SchemaAPI, name: string) { + super(api); + this.name = name; + } + + formatGoDeclaration(): string { + throw new Error(`Primitive type ${this.name} has no Go declaration form`); + } + + formatGoReference(): string { + return this.name; + } + + formatTypeScript(): string { + const typeMap: Record = { + bool: "boolean", + int: "number", + }; + return typeMap[this.name] || this.name; + } + + baseKind(): BaseTypeKind { + return "primitive"; + } +} + +export class KindType extends TypeBase { + readonly kind = "kind" as const; + readonly value: string; + + constructor(api: SchemaAPI, value: string) { + super(api); + this.value = value; + } + + formatGoDeclaration(): string { + return "Kind"; + } + + formatGoReference(): string { + return "Kind"; + } + + formatTypeScript(): string { + return this.value === "Kind" ? "SyntaxKind" : this.value; + } + + baseKind(): BaseTypeKind { + return "kind"; + } + + get name(): string { + return this.value.startsWith("SyntaxKind.") ? this.value.slice("SyntaxKind.".length) : this.value; + } + + formatGoConstant(): string { + return this.value === "Kind" ? "Kind" : `Kind${this.name}`; + } +} + +export class NodeType extends TypeBase { + readonly kind = "node" as const; + readonly name: string; + private membersCache?: MemberInfo[]; + private fieldsCache?: MemberInfo[]; + private inheritedFieldCache?: Map; + + constructor(api: SchemaAPI, name: string) { + super(api); + this.name = name; + } + + formatGoDeclaration(): string { + return this.name; + } + + formatGoReference(): string { + return this.api.hasNode(this.name) ? `*${this.name}Node` : `*${this.name}`; + } + + formatTypeScript(): string { + return this.name; + } + + baseKind(): BaseTypeKind { + return "node"; + } + + concreteNodeName(): string { + if (!this.isConcrete) { + throw new Error(`${this.name} is not a concrete node`); + } + return this.name; + } + + get key(): string { + return this.name; + } + + get isConcrete(): boolean { + return !!this.def; + } + + get isBase(): boolean { + return !!this.entry; + } + + get def(): NodeDef | undefined { + return this.api.getNodeDef(this.name); + } + + get entry(): BaseEntry | undefined { + return this.api.schema.bases[this.name]; + } + + get syntaxKindName(): string { + const kind = this.def?.kind; + if (Array.isArray(kind)) return kind[0]; + return kind || this.name; + } + + get extendsKeys(): string[] { + return this.def?.extends || this.entry?.extends || []; + } + + get extends(): NodeType[] { + return this.extendsKeys.map(key => this.api.getBase(key)).filter((base): base is NodeType => !!base); + } + + get members(): MemberInfo[] { + if (!this.membersCache) { + this.membersCache = (this.def?.members || []).map(member => new MemberInfo(this.api, member.name, member, this)); + } + return this.membersCache; + } + + get kindAliases(): string[] { + const kind = this.def?.kind; + if (Array.isArray(kind)) return kind.slice(1); + return []; + } + + get brand(): string | undefined { + return this.entry?.brand; + } + + get fields(): MemberInfo[] { + if (!this.fieldsCache) { + this.fieldsCache = Object.entries(this.entry?.fields || {}).map(([name, field]) => new MemberInfo(this.api, name, field)); + } + return this.fieldsCache; + } + + get arena(): boolean { + return this.def?.arena || false; + } + + get handWritten(): boolean { + return this.def?.handWritten || false; + } + + get handWrittenVisitor(): boolean { + return this.def?.handWrittenVisitor || false; + } + + get generateSubtreeFacts(): boolean { + return this.def?.generateSubtreeFacts || false; + } + + get typeParameters(): TypeParameterInfo[] { + return this.def?.typeParameters || []; + } + + get instantiationAliases(): InstantiationAliasInfo[] { + return Object.entries(this.def?.instantiationAliases || {}).map(([name, typeArg]) => ({ name, typeArg })); + } + + get kindType(): Type { + return this.kindMember()?.declaredType ?? this.api.kindType(`SyntaxKind.${this.syntaxKindName}`); + } + + kindMember(): MemberInfo | undefined { + return this.members.find(member => member.name === "Kind" || member.name === "kind"); + } + + field(name: string): MemberInfo | undefined { + return this.fields.find(field => field.name === name); + } + + inheritedField(name: string): MemberInfo | undefined { + if (!this.inheritedFieldCache) { + this.inheritedFieldCache = new Map(); + } + const cached = this.inheritedFieldCache.get(name); + if (cached !== undefined) { + return cached || undefined; + } + + for (const base of this.extends) { + const direct = base.field(name); + if (direct) { + this.inheritedFieldCache.set(name, direct); + return direct; + } + + const inherited = base.inheritedField(name); + if (inherited) { + this.inheritedFieldCache.set(name, inherited); + return inherited; + } + } + + this.inheritedFieldCache.set(name, null); + return undefined; + } + + isMultiKind(): boolean { + return this.kindTypes().length > 1; + } + + kindTypes(): KindType[] { + const collect = (type: Type): KindType[] => { + switch (type.kind) { + case "kind": + return type.value === "Kind" ? [] : [type]; + case "union": + return type.types.flatMap(collect); + case "alias": + return collect(type.resolved); + case "typeParameter": + return collect(type.constraint); + default: + return []; + } + }; + + const kindTypes = collect(this.kindType); + if (kindTypes.length === 0) { + return [this.api.kindType(`SyntaxKind.${this.syntaxKindName}`)]; + } + return [...new Map(kindTypes.map(type => [type.value, type])).values()]; + } + + /** + * All SyntaxKind types for this node, including kindAliases. + * For multi-kind nodes, returns all instantiated kind types. + * For single-kind nodes, returns the primary kind plus any kindAliases. + */ + allKinds(): KindType[] { + return [ + ...this.kindTypes(), + ...this.kindAliases.map(a => this.api.kindType(`SyntaxKind.${a}`)), + ]; + } + + isGoOnly(): boolean { + return this.api.isGoOnlyBase(this.name); + } +} + +export class TypeParameterType extends TypeBase { + readonly kind = "typeParameter" as const; + readonly name: string; + readonly constraint: Type; + + constructor(api: SchemaAPI, name: string, constraint: Type) { + super(api); + this.name = name; + this.constraint = constraint; + } + + formatGoDeclaration(): string { + return this.constraint.formatGoDeclaration(); + } + + formatGoReference(): string { + return this.constraint.formatGoReference(); + } + + formatTypeScript(): string { + return this.name; + } + + baseKind(): BaseTypeKind { + return this.constraint.baseKind(); + } + + concreteNodeName(): string | undefined { + switch (this.constraint.kind) { + case "node": + case "alias": + case "typeParameter": + return this.constraint.concreteNodeName(); + } + return undefined; + } +} + +export class AliasType extends TypeBase { + readonly kind = "alias" as const; + readonly name: string; + readonly resolved: Type; + readonly resolveAs?: "node"; + + constructor(api: SchemaAPI, name: string, resolved: Type, resolveAs?: "node") { + super(api); + this.name = name; + this.resolved = resolved; + this.resolveAs = resolveAs; + } + + formatGoDeclaration(): string { + return this.name; + } + + formatGoReference(): string { + return this.baseKind() === "node" || (this.baseKind() === "list" && (this.resolved as ListType).listKind !== "raw") ? `*${this.name}` : this.name; + } + + formatTypeScript(): string { + return this.name; + } + + baseKind(): BaseTypeKind { + return this.resolved.baseKind(); + } + + concreteNodeName(): string | undefined { + return this.api.getInstantiationNodeName(this.name); + } + + get isUnion(): boolean { + const alias = this.api.schema.nodes.aliases[this.name]; + return Array.isArray(alias); + } + + get isBaseAlias(): boolean { + const alias = this.api.schema.nodes.aliases[this.name]; + return !!alias && !Array.isArray(alias); + } + + get unionMemberNames(): string[] { + const alias = this.api.schema.nodes.aliases[this.name]; + return Array.isArray(alias) ? alias : []; + } + + get unionMemberTypes(): Type[] { + return this.unionMemberNames.map(name => this.api.resolveType(name, undefined, this.resolveAs)); + } + + get baseKey(): string | undefined { + const alias = this.api.schema.nodes.aliases[this.name]; + return alias && !Array.isArray(alias) ? alias.base : undefined; + } + + get base(): NodeType | undefined { + return this.baseKey ? this.api.getBase(this.baseKey) : undefined; + } + + get elementTypeName(): string | undefined { + return this.api.schema.nodes.listAliases?.[this.name]; + } + + get elementType(): Type | undefined { + return this.elementTypeName ? this.api.resolveType(this.elementTypeName) : undefined; + } +} + +export class UnionType extends TypeBase { + readonly kind = "union" as const; + readonly types: Type[]; + + constructor(api: SchemaAPI, types: Type[]) { + super(api); + this.types = types; + } + + formatGoDeclaration(): string { + throw new Error("Cannot declare Go type for union"); + } + + formatGoReference(): string { + const concreteNodes = new Set(); + let allConcreteNodes = true; + for (const type of this.types) { + const nodeName = this.concreteNodeNameOf(type); + if (!nodeName) { + allConcreteNodes = false; + break; + } + concreteNodes.add(nodeName); + } + if (allConcreteNodes && concreteNodes.size === 1) { + return `*${[...concreteNodes][0]}Node`; + } + + if (this.types.some(type => type.baseKind() === "node" || type.baseKind() === "list")) { + return "*Node"; + } + + if (this.types.every(type => type.baseKind() === "kind")) { + return "Kind"; + } + + const references = [...new Set(this.types.map(type => type.formatGoReference()))]; + if (references.length === 1) return references[0]; + throw new Error(`Cannot resolve Go reference form for union ${references.join(" | ")}`); + } + + formatTypeScript(): string { + return this.types.map(type => type.formatTypeScript()).join(" | "); + } + + baseKind(): BaseTypeKind { + const baseKinds = new Set(this.types.map(type => type.baseKind())); + if (baseKinds.size === 1) return this.types[0].baseKind(); + if (this.types.some(type => type.baseKind() === "list" || type.baseKind() === "node")) { + return "node"; + } + if (this.types.some(type => type.baseKind() === "kind")) { + return "kind"; + } + return "primitive"; + } + + private concreteNodeNameOf(type: Type): string | undefined { + switch (type.kind) { + case "node": + case "alias": + case "typeParameter": + return type.concreteNodeName(); + default: + return undefined; + } + } +} + +export class ListType extends TypeBase { + readonly kind = "list" as const; + readonly elementType: Type; + readonly listKind: ListKind; + + constructor(api: SchemaAPI, elementType: Type, listKind: ListKind) { + super(api); + this.elementType = elementType; + this.listKind = listKind; + } + + formatGoDeclaration(): string { + throw new Error(`List type ${this.listKind} has no generated Go declaration form`); + } + + formatGoReference(): string { + if (this.listKind === "raw") { + return this.elementType.baseKind() === "node" ? "[]*Node" : `[]${this.elementType.formatGoReference()}`; + } + if (this.listKind === "ModifierList") { + return "*ModifierList"; + } + return `*${this.api.listAliasName(this.elementType) || "NodeList"}`; + } + + formatTypeScript(): string { + const elementType = this.elementType.formatTypeScript(); + if (this.listKind === "raw") { + return `readonly ${elementType}[]`; + } + return `NodeArray<${elementType}>`; + } + + baseKind(): BaseTypeKind { + return this.elementType.baseKind() === "node" ? "list" : this.elementType.baseKind(); + } + + raw(): ListType { + if (this.listKind === "raw") { + return this; + } + return this.api.listType(this.elementType, "raw"); + } +} + +export type Type = + | AliasType + | KindType + | ListType + | NodeType + | PrimitiveType + | TypeParameterType + | UnionType; + +export interface TypeParameterInfo { + name: string; + constraint: string; + default?: string; +} + +export interface InstantiationAliasInfo { + name: string; + typeArg: string; +} + +export class MemberInfo { + private readonly api: SchemaAPI; + private readonly nameValue: string; + private readonly member?: Member; + private readonly field?: BaseField; + private inheritedFieldCache?: MemberInfo | null; + readonly node?: NodeType; + + constructor(api: SchemaAPI, name: string, source: Member | BaseField, node?: NodeType) { + this.api = api; + this.nameValue = name; + if (node) { + this.member = source as Member; + } + else { + this.field = source as BaseField; + } + this.node = node; + } + + get name(): string { + return this.nameValue; + } + + get declaredType(): Type { + return this.api.resolveType(this.rawType, this.node?.def); + } + + get type(): Type { + return this.listKind ? this.api.listType(this.declaredType, this.listKind) : this.declaredType; + } + + get rawType(): string | string[] { + if (this.field) return this.field.type; + if (this.member && !this.member.inherited) return this.member.type; + // Inherited member with an explicit type override narrows the base type + if (this.member?.inherited && this.member.type) return this.member.type; + if (this.inheritedField) return this.inheritedField.rawType; + if (this.member) return this.member.type; + throw new Error(`Member ${this.name} has no raw type source`); + } + + get listKind(): ListKind | undefined { + if (this.field) return this.field.list; + if (this.member && !this.member.inherited) return this.member.list; + return this.inheritedField?.listKind; + } + + get optional(): boolean { + return this.field?.optional ?? this.member?.optional ?? this.inheritedField?.optional ?? false; + } + + get inherited(): boolean { + return this.member?.inherited ?? false; + } + + get private(): boolean { + if (this.field?.private) return true; + if (this.member?.private) return true; + return this.inheritedField?.private ?? false; + } + + get goOnly(): boolean { + return this.field?.goOnly ?? this.member?.goOnly ?? false; + } + + /** Absent from Go structs, encoding, and factory. noGo implies noFactory. */ + get noGo(): boolean { + return this.field?.noGo ?? this.member?.noGo ?? false; + } + + /** Absent from encoding and all TS files. goOnly implies noTS. */ + get noTS(): boolean { + return this.goOnly || (this.field?.noTS ?? this.member?.noTS ?? false); + } + + /** Absent from factory functions, visitor, and clone. goOnly implies noFactory. */ + get noFactory(): boolean { + return this.goOnly || this.field?.noFactory || this.member?.noFactory || false; + } + + get visit(): string | undefined { + if (this.member?.visit) return this.member.visit; + if (this.field?.visit) return this.field.visit; + return this.inheritedField?.visit; + } + + get typeGuard(): string | undefined { + if (this.member?.typeGuard) return this.member.typeGuard; + if (this.field?.typeGuard) return this.field.typeGuard; + return this.inheritedField?.typeGuard; + } + + get bitmask(): string | undefined { + return this.member?.bitmask; + } + + get inheritedField(): MemberInfo | undefined { + if (!this.member?.inherited || !this.node) return undefined; + if (this.inheritedFieldCache !== undefined) { + return this.inheritedFieldCache || undefined; + } + this.inheritedFieldCache = this.node.inheritedField(this.name) || null; + return this.inheritedFieldCache || undefined; + } + + isKindParam(): boolean { + if (!this.member || !this.node) return false; + if (this.name !== "Kind" && this.name !== "kind") return false; + return this.declaredType.baseKind() === "kind"; + } + + isChild(): boolean { + if (!this.member || !this.node) return false; + return this.type.baseKind() === "list" || this.type.baseKind() === "node"; + } + + /** + * True when this inherited member carries an explicit type or optionality + * override relative to its base field. Such members need their own + * property declaration in a TS interface even though they are inherited. + */ + hasTypeScriptOverride(): boolean { + if (!this.member?.inherited || !this.inheritedField) return false; + // Explicit type override on the member narrows the base type. + if (this.member.type && this.type.formatTypeScript() !== this.inheritedField.type.formatTypeScript()) return true; + // Explicit optionality override (e.g. base is required but this member is optional, or vice-versa). + if (this.member.optional !== undefined && this.optional !== this.inheritedField.optional) return true; + return false; + } + + /** Go parameter name: uncapitalized, with Go keyword avoidance. */ + goParamName(): string { + const name = this.api.uncapitalize(this.name); + if (name === "type") return "typeNode"; + if (name === "default") return "defaultNode"; + if (name === "case") return "caseNode"; + return name; + } +} + +export interface KindElementInfo { + name?: string; + comment?: string; +} + +export interface KindMarkerInfo { + name: string; + value: string; +} + +export interface KindAliasInfo { + name: string; + members: string[]; + range?: [string, string]; +} + +/** A kind guard is either range-based (kind >= First && kind <= Last) or enumerated (switch/conditions). */ +export type KindGuardInfo = + & { + /** The alias name from the schema, e.g. "TokenSyntaxKind". */ + aliasName: string; + /** The guard function name, e.g. "isTokenKind". Drops "Syntax" from the alias name. */ + guardName: string; + } + & ( + | { type: "range"; first: string; last: string; } + | { type: "enumerated"; members: string[]; } + ); + +/** Compute a guard function name from a kind alias name. Drops "Syntax" from the name. */ +export function kindGuardName(aliasName: string): string { + return `is${aliasName.replace("Syntax", "")}`; +} + +export interface Schema { + $schema: string; + bases: Record; + nodes: { + definitions: Record; + aliases: Record; + listAliases?: Record; + }; + kinds?: { + elements: (string | KindElement)[]; + markers: { name: string; value: string; }[]; + aliases?: Record; + }; +} + +// ──────────────────────────────────────────────────────────────────────────── +// SchemaAPI — shared logic for both Go and TS generators +// ──────────────────────────────────────────────────────────────────────────── + +/** + * Wraps the parsed ast.json schema and provides common query/resolution + * methods used by both the Go and TS code generators. + */ +export class SchemaAPI { + readonly schema: Schema; + private readonly listAliasNameMap = new Map(); + private readonly instantiationAliasMap = new Map(); + /** Maps syntax kind names to (aliasName, nodeName) for instantiation aliases, or (undefined, nodeName) for direct nodes. */ + private readonly syntaxKindToNodeInfo = new Map(); + private readonly primitiveTypeMap = new Map(); + private readonly kindTypeMap = new Map(); + private readonly nodeTypeMap = new Map(); + private readonly listTypeMapByKey = new Map(); + private readonly unionTypeMap = new Map(); + private readonly aliasTypeMap = new Map(); + private readonly typeParameterTypeMap = new Map(); + private readonly primitiveTypes = new Set([ + "any", + "bool", + "boolean", + "int", + "ModifierFlags", + "NodeFlags", + "string", + "TokenFlags", + ]); + + constructor(schema: Schema) { + this.schema = schema; + const listAliases = schema.nodes.listAliases || {}; + for (const [aliasName, elementType] of Object.entries(listAliases)) { + this.listAliasNameMap.set(this.typeCacheKey(this.resolveType(elementType)), aliasName); + } + for (const [nodeName, nodeDef] of Object.entries(schema.nodes.definitions)) { + for (const aliasName of Object.keys(nodeDef.instantiationAliases || {})) { + this.instantiationAliasMap.set(aliasName, nodeName); + } + // Map syntax kind names to instantiation aliases + for (const [aliasName, typeArg] of Object.entries(nodeDef.instantiationAliases || {})) { + // typeArg is a syntax kind name or a kind alias name + if (this.hasKindAlias(typeArg)) { + // Multi-kind instantiation alias (e.g. BinaryOperatorToken) — expand the kind alias + for (const kindType of this.expandKindAliasMembers(typeArg)) { + if (!this.syntaxKindToNodeInfo.has(kindType.name)) { + this.syntaxKindToNodeInfo.set(kindType.name, { aliasName, nodeName }); + } + } + } + else { + if (!this.syntaxKindToNodeInfo.has(typeArg)) { + this.syntaxKindToNodeInfo.set(typeArg, { aliasName, nodeName }); + } + } + } + // Map the node's own syntax kind(s) to itself + const primaryKind = Array.isArray(nodeDef.kind) ? nodeDef.kind[0] : (nodeDef.kind || nodeName); + if (!this.syntaxKindToNodeInfo.has(primaryKind)) { + this.syntaxKindToNodeInfo.set(primaryKind, { nodeName }); + } + if (Array.isArray(nodeDef.kind)) { + for (const k of nodeDef.kind.slice(1)) { + if (!this.syntaxKindToNodeInfo.has(k)) { + this.syntaxKindToNodeInfo.set(k, { nodeName }); + } + } + } + } + } + + // ── String helpers ────────────────────────────────────────────────────── + + /** Capitalize the first character: "foo" → "Foo". */ + capitalize(s: string): string { + return s.charAt(0).toUpperCase() + s.slice(1); + } + + /** Uncapitalize, with special handling for "JSDoc" prefix. */ + uncapitalize(s: string): string { + if (s.startsWith("JSDoc")) return "jsdoc" + s.slice(5); + return s.charAt(0).toLowerCase() + s.slice(1); + } + + bases(): NodeType[] { + return Object.keys(this.schema.bases).map(key => this.getBase(key)).filter((base): base is NodeType => !!base); + } + + getBase(key: string): NodeType | undefined { + if (!(key in this.schema.bases)) return undefined; + return this.nodeType(key); + } + + nodes(): NodeType[] { + return Object.keys(this.schema.nodes.definitions).map(key => this.getNode(key)).filter((node): node is NodeType => !!node); + } + + getNode(key: string): NodeType | undefined { + if (!(key in this.schema.nodes.definitions)) return undefined; + return this.nodeType(key); + } + + hasNode(key: string): boolean { + return key in this.schema.nodes.definitions; + } + + getNodeDef(key: string): NodeDef | undefined { + return this.schema.nodes.definitions[key]; + } + + nodeAliases(): AliasType[] { + return Object.keys(this.schema.nodes.aliases).map(name => this.getNodeAlias(name)).filter((alias): alias is AliasType => !!alias); + } + + getNodeAlias(name: string): AliasType | undefined { + if (!(name in this.schema.nodes.aliases)) return undefined; + const type = this.resolveType(name); + return type.kind === "alias" ? type : undefined; + } + + listAliases(): AliasType[] { + return Object.keys(this.schema.nodes.listAliases || {}).map(name => this.resolveType(name)).filter((type): type is AliasType => type.kind === "alias"); + } + + kindElements(): KindElementInfo[] { + return (this.schema.kinds?.elements || []).map(element => { + if (typeof element === "string") { + return { name: element }; + } + return { name: element.name, comment: element.comment }; + }); + } + + kindMarkers(): KindMarkerInfo[] { + return this.schema.kinds?.markers || []; + } + + /** Resolve a marker name to its concrete kind value. If the name is already a kind element, returns itself. */ + resolveKindMarkerValue(name: string): string { + // If it's a marker, resolve to its value (recursively since markers can reference other markers) + const marker = this.kindMarkers().find(m => m.name === name); + if (marker) return this.resolveKindMarkerValue(marker.value); + return name; + } + + kindAliases(): KindAliasInfo[] { + return Object.entries(this.schema.kinds?.aliases || {}).map(([name, value]) => { + if (Array.isArray(value)) { + return { name, members: value }; + } + // Range format: { range: ["FirstX", "LastX"] } + const [first, last] = value.range; + const elements = this.kindElements().filter(e => e.name).map(e => e.name!); + const firstIdx = elements.indexOf(this.resolveKindMarkerValue(first)); + const lastIdx = elements.indexOf(this.resolveKindMarkerValue(last)); + if (firstIdx === -1 || lastIdx === -1) { + throw new Error(`Range alias ${name}: could not resolve range [${first}, ${last}]`); + } + const members = elements.slice(firstIdx, lastIdx + 1); + return { name, members, range: value.range as [string, string] }; + }); + } + + hasKindAlias(name: string): boolean { + return this.kindAliases().some(alias => alias.name === name); + } + + getKindAlias(name: string): KindAliasInfo | undefined { + return this.kindAliases().find(alias => alias.name === name); + } + + /** + * Returns kind guard info for each kind alias. Range-based aliases produce range guards; + * enumerated aliases produce guards whose members reference either sub-alias guard calls + * or concrete kind names. + */ + kindGuards(): KindGuardInfo[] { + return this.kindAliases().map(({ name, members, range }): KindGuardInfo => { + const guardName = kindGuardName(name); + if (range) { + return { aliasName: name, guardName, type: "range", first: range[0], last: range[1] }; + } + return { aliasName: name, guardName, type: "enumerated", members }; + }); + } + + hasKindElement(name: string): boolean { + return this.kindElements().some(element => element.name === name); + } + + expandKindAliasMembers(name: string): KindType[] { + const alias = this.getKindAlias(name); + if (!alias) return [this.kindType(`SyntaxKind.${name}`)]; + const result: KindType[] = []; + for (const member of alias.members) { + if (this.hasKindAlias(member)) { + result.push(...this.expandKindAliasMembers(member)); + } + else { + result.push(this.kindType(`SyntaxKind.${member}`)); + } + } + return result; + } + + /** A base is "Go-only" if it has no brand AND all its fields are both noTS and noFactory. */ + isGoOnlyBase(key: string): boolean { + const base = this.schema.bases[key]; + if (!base) return false; + if (base.brand) return false; + if (!base.fields) return true; + return Object.values(base.fields).every(f => f.goOnly || (f.noTS && f.noFactory)); + } + + /** + * Given a syntax kind name (e.g. "AbstractKeyword"), returns the node type + * that has that syntax kind. For instantiation aliases, returns the alias type + * (e.g. `AliasType("AbstractKeyword", NodeType("Token"))`). For direct nodes, + * returns the node type itself. + */ + resolveNodeTypeForSyntaxKind(syntaxKindName: string): Type | undefined { + const info = this.syntaxKindToNodeInfo.get(syntaxKindName); + if (!info) return undefined; + if (info.aliasName) { + return this.aliasType(info.aliasName, this.nodeType(info.nodeName)); + } + return this.nodeType(info.nodeName); + } + + resolveType(typeName: string | string[], node?: NodeDef, resolveAs?: "node"): Type { + if (Array.isArray(typeName)) { + return this.unionType(typeName.map(type => this.resolveType(type, node, resolveAs))); + } + + if (node?.typeParameters) { + for (const tp of node.typeParameters) { + if (tp.name === typeName) { + const nodeKey = this.nodeKeyFor(node); + if (!nodeKey) { + throw new Error(`Type parameter ${typeName} requires a node context`); + } + const constraint = tp.constraint === typeName + ? this.primitiveType(typeName) + : this.resolveType(tp.constraint, node); + return this.typeParameterType(nodeKey, typeName, constraint); + } + } + } + + const kindAliases = this.schema.kinds?.aliases || {}; + if (typeName in kindAliases) { + if (resolveAs === "node") { + // Resolve each kind in the alias to its corresponding node type + const kindMembers = this.expandKindAliasMembers(typeName); + return this.unionType(kindMembers.map(kindType => { + const resolved = this.resolveNodeTypeForSyntaxKind(kindType.name); + if (!resolved) { + throw new Error(`Kind alias member "${kindType.name}" (from "${typeName}") does not resolve to a node type`); + } + return resolved; + })); + } + return this.aliasType( + typeName, + this.unionType(this.expandKindAliasMembers(typeName)), + ); + } + + if (typeName === "Kind" || typeName.startsWith("SyntaxKind.")) { + return this.kindType(typeName); + } + + if (typeName === "Node") { + return this.nodeType(typeName); + } + + if (this.primitiveTypes.has(typeName)) { + return this.primitiveType(typeName); + } + + const listAliasTarget = this.schema.nodes.listAliases?.[typeName]; + if (listAliasTarget) { + return this.aliasType(typeName, this.listType(this.resolveType(listAliasTarget, node), "NodeList")); + } + + const instantiationNode = this.instantiationAliasMap.get(typeName); + if (instantiationNode) { + return this.aliasType(typeName, this.nodeType(instantiationNode)); + } + + if (typeName in this.schema.nodes.aliases) { + const alias = this.schema.nodes.aliases[typeName]; + if (Array.isArray(alias)) { + return this.aliasType(typeName, this.unionType(alias.map(type => this.resolveType(type, node, "node"))), "node"); + } + return this.aliasType(typeName, this.nodeType(alias.base)); + } + + if (typeName in this.schema.nodes.definitions) { + return this.nodeType(typeName); + } + + if (typeName in this.schema.bases) { + return this.nodeType(typeName); + } + + return this.primitiveType(typeName); + } + + validate(): void { + for (const base of this.bases()) { + if (base !== this.resolveType(base.key)) { + throw new Error(`Base type cache mismatch for ${base.key}`); + } + for (const ext of base.extendsKeys) { + if (!this.getBase(ext)) { + throw new Error(`Unknown base extends target ${ext} from ${base.key}`); + } + } + for (const field of base.fields) { + if (field.type !== field.type) { + throw new Error(`Base field type cache mismatch for ${base.key}.${field.name}`); + } + } + } + + for (const node of this.nodes()) { + if (this.resolveType(node.key) !== this.nodeType(node.key)) { + throw new Error(`Node type cache mismatch for ${node.key}`); + } + for (const ext of node.extendsKeys) { + if (!this.getBase(ext)) { + throw new Error(`Unknown node extends target ${ext} from ${node.key}`); + } + } + for (const member of node.members) { + if (member.type !== member.type) { + throw new Error(`Member type cache mismatch for ${node.key}.${member.name}`); + } + if (member.declaredType !== member.declaredType) { + throw new Error(`Declared member type cache mismatch for ${node.key}.${member.name}`); + } + if (member.inheritedField && member.inheritedField.type !== member.inheritedField.type) { + throw new Error(`Inherited field type cache mismatch for ${node.key}.${member.name}`); + } + } + } + + for (const alias of this.nodeAliases()) { + if (alias !== this.resolveType(alias.name)) { + throw new Error(`Alias type cache mismatch for ${alias.name}`); + } + if (alias.baseKey && !this.getBase(alias.baseKey)) { + throw new Error(`Unknown alias base ${alias.baseKey} from ${alias.name}`); + } + for (const [index, memberName] of alias.unionMemberNames.entries()) { + if (alias.unionMemberTypes[index] !== this.resolveType(memberName, undefined, alias.resolveAs)) { + // Force member types to be reachable and cached via their names. + throw new Error(`Alias union member cache mismatch for ${alias.name}`); + } + } + } + + for (const listAlias of this.listAliases()) { + const resolved = this.resolveType(listAlias.name); + if (resolved.kind !== "alias" || resolved.resolved.kind !== "list") { + throw new Error(`List alias ${listAlias.name} did not resolve to an alias of a list type`); + } + if (!listAlias.elementTypeName) { + throw new Error(`List alias ${listAlias.name} is missing an element type`); + } + if (listAlias.elementType !== this.resolveType(listAlias.elementTypeName)) { + throw new Error(`List alias element type cache mismatch for ${listAlias.name}`); + } + } + + for (const marker of this.kindMarkers()) { + if (!this.hasKindElement(marker.value) && !this.kindMarkers().some(candidate => candidate.name === marker.value)) { + throw new Error(`Kind marker ${marker.name} references undefined kind or marker ${marker.value}`); + } + } + + for (const alias of this.kindAliases()) { + for (const member of alias.members) { + if (!this.hasKindAlias(member) && !this.hasKindElement(member)) { + throw new Error(`Unknown kind alias member ${member} in ${alias.name}`); + } + } + } + } + + primitiveType(name: string): PrimitiveType { + const existing = this.primitiveTypeMap.get(name); + if (existing) return existing; + const type = new PrimitiveType(this, name); + this.primitiveTypeMap.set(name, type); + return type; + } + + kindType(value: string): KindType { + const existing = this.kindTypeMap.get(value); + if (existing) return existing; + const type = new KindType(this, value); + this.kindTypeMap.set(value, type); + return type; + } + + nodeType(name: string): NodeType { + const existing = this.nodeTypeMap.get(name); + if (existing) return existing; + const type = new NodeType(this, name); + this.nodeTypeMap.set(name, type); + return type; + } + + listType(elementType: Type, listKind: ListKind): ListType { + const key = `${listKind}:${this.typeCacheKey(elementType)}`; + const existing = this.listTypeMapByKey.get(key); + if (existing) return existing; + const type = new ListType(this, elementType, listKind); + this.listTypeMapByKey.set(key, type); + return type; + } + + unionType(types: Type[]): UnionType { + const key = types.map(type => this.typeCacheKey(type)).join("|"); + const existing = this.unionTypeMap.get(key); + if (existing) return existing; + const type = new UnionType(this, types); + this.unionTypeMap.set(key, type); + return type; + } + + aliasType(name: string, resolved: Type, resolveAs?: "node"): AliasType { + const key = `${name}:${this.typeCacheKey(resolved)}`; + const existing = this.aliasTypeMap.get(key); + if (existing) return existing; + const type = new AliasType(this, name, resolved, resolveAs); + this.aliasTypeMap.set(key, type); + return type; + } + + typeParameterType(nodeKey: string, name: string, constraint: Type): TypeParameterType { + const key = `${nodeKey}:${name}:${this.typeCacheKey(constraint)}`; + const existing = this.typeParameterTypeMap.get(key); + if (existing) return existing; + const type = new TypeParameterType(this, name, constraint); + this.typeParameterTypeMap.set(key, type); + return type; + } + + getInstantiationNodeName(name: string): string | undefined { + return this.instantiationAliasMap.get(name); + } + + listAliasName(elementType: Type): string | undefined { + return this.listAliasNameMap.get(this.typeCacheKey(elementType)); + } + + private nodeKeyFor(node: NodeDef): string | undefined { + return Object.entries(this.schema.nodes.definitions).find(([, def]) => def === node)?.[0]; + } + + private typeCacheKey(type: Type): string { + switch (type.kind) { + case "primitive": + return `primitive:${type.name}`; + case "kind": + return `kind:${type.value}`; + case "node": + return `node:${type.name}`; + case "alias": + return `alias:${type.name}:${this.typeCacheKey(type.resolved)}`; + case "list": + return `list:${type.listKind}:${this.typeCacheKey(type.elementType)}`; + case "typeParameter": + return `typeParameter:${type.name}:${this.typeCacheKey(type.constraint)}`; + case "union": + return `union:${type.types.map(member => this.typeCacheKey(member)).join("|")}`; + } + } +} + +const ROOT = path.resolve(import.meta.dirname!, "../../.."); +export const api = new SchemaAPI(JSON.parse( + fs.readFileSync(path.join(ROOT, "tools/scripts/tsc/ast.json"), "utf-8"), +) as Schema); +api.validate(); diff --git a/tsc/LICENSE b/tsc/LICENSE index 8746124b27791..edc24fd6e1409 100644 --- a/tsc/LICENSE +++ b/tsc/LICENSE @@ -1,55 +1,55 @@ -Apache License - -Version 2.0, January 2004 - -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of this License; and - -You must cause any modified files to carry prominent notices stating that You changed the files; and - -You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - -If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of this License; and + +You must cause any modified files to carry prominent notices stating that You changed the files; and + +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/tsc/internal/bundled/README.md b/tsc/internal/bundled/README.md index a82b48b820639..2c421df635839 100644 --- a/tsc/internal/bundled/README.md +++ b/tsc/internal/bundled/README.md @@ -1,26 +1,26 @@ -# Read this! - -The files in `libs` are copied and deployed with TypeScript as the set of APIs available as a part of the JavaScript language. - -There are three main domains of APIs: - - - **ECMAScript language features** - e.g. JavaScript APIs like functions on Array etc which are documented in [ECMA-262](https://tc39.es/ecma262/) - - **DOM APIs** - e.g. APIs which are available in web browsers - - **Intl APIs** - e.g. APIs scoped to `Intl` which are documented in [ECMA-402](https://www.ecma-international.org/publications-and-standards/standards/ecma-402/) - -## How do we figure out when to add something? - -TypeScript has a rule-of-thumb to only add something when it has got far enough through the standards process that it is more or less confirmed. For JavaScript APIs and language features, that means the proposal is at stage 3 or later. - -You can find the source of truth for modern language features and Intl APIs in these completed proposal lists: - - - [JavaScript](https://github.com/tc39/proposals/blob/master/finished-proposals.md) - - [Intl](https://github.com/tc39/proposals/blob/master/ecma402/finished-proposals.md) - -For the DOM APIs, which are a bit more free-form, we have asked that APIs are available un-prefixed/flagged in at least 2 browser _engines_ (i.e. not just 2 chromium browsers.) - -## Generated files - -The DOM and web worker files aren't meant to be edited by hand. - -If you need to make changes to such files, make a change to the input files for [**our library generator**](https://github.com/microsoft/TypeScript-DOM-lib-generator). +# Read this! + +The files in `libs` are copied and deployed with TypeScript as the set of APIs available as a part of the JavaScript language. + +There are three main domains of APIs: + + - **ECMAScript language features** - e.g. JavaScript APIs like functions on Array etc which are documented in [ECMA-262](https://tc39.es/ecma262/) + - **DOM APIs** - e.g. APIs which are available in web browsers + - **Intl APIs** - e.g. APIs scoped to `Intl` which are documented in [ECMA-402](https://www.ecma-international.org/publications-and-standards/standards/ecma-402/) + +## How do we figure out when to add something? + +TypeScript has a rule-of-thumb to only add something when it has got far enough through the standards process that it is more or less confirmed. For JavaScript APIs and language features, that means the proposal is at stage 3 or later. + +You can find the source of truth for modern language features and Intl APIs in these completed proposal lists: + + - [JavaScript](https://github.com/tc39/proposals/blob/master/finished-proposals.md) + - [Intl](https://github.com/tc39/proposals/blob/master/ecma402/finished-proposals.md) + +For the DOM APIs, which are a bit more free-form, we have asked that APIs are available un-prefixed/flagged in at least 2 browser _engines_ (i.e. not just 2 chromium browsers.) + +## Generated files + +The DOM and web worker files aren't meant to be edited by hand. + +If you need to make changes to such files, make a change to the input files for [**our library generator**](https://github.com/microsoft/TypeScript-DOM-lib-generator). diff --git a/tsc/internal/bundled/generate.go b/tsc/internal/bundled/generate.go index 7a0947706005c..da38570b2fca8 100644 --- a/tsc/internal/bundled/generate.go +++ b/tsc/internal/bundled/generate.go @@ -91,10 +91,7 @@ func readLibs() []string { if err != nil { log.Fatalf("failed to read %s: %v", copyrightNotice, err) } - if bytes.ContainsRune(copyright, '\r') || !bytes.HasSuffix(copyright, []byte("\n\n")) { - log.Fatalf("%s must use LF line endings and end with a blank line", copyrightNotice) - } - header := append(copyright, '\n') + header := append(bytes.TrimRight(copyright, "\n"), '\n', '\n') libs := make([]string, 0, len(entries)) for _, entry := range entries { @@ -108,14 +105,8 @@ func readLibs() []string { if err != nil { log.Fatalf("failed to read %s: %v", path, err) } - if bytes.ContainsRune(content, '\r') { - log.Fatalf("%s must use LF line endings", path) - } if !bytes.HasPrefix(content, header) { - log.Fatalf("%s must start with %s followed by a blank line", path, copyrightNotice) - } - if !bytes.HasSuffix(content, []byte("\n")) { - log.Fatalf("%s must end with a newline", path) + log.Fatalf("%s must start with %s", path, copyrightNotice) } libs = append(libs, name) diff --git a/tsc/internal/bundled/libs/lib.d.ts b/tsc/internal/bundled/libs/lib.d.ts index a80f0070486d2..529133f005d28 100644 --- a/tsc/internal/bundled/libs/lib.d.ts +++ b/tsc/internal/bundled/libs/lib.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.decorators.d.ts b/tsc/internal/bundled/libs/lib.decorators.d.ts index 83d82bef2e557..a05ec4d7b671a 100644 --- a/tsc/internal/bundled/libs/lib.decorators.d.ts +++ b/tsc/internal/bundled/libs/lib.decorators.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /** * The decorator context types provided to class element decorators. */ diff --git a/tsc/internal/bundled/libs/lib.decorators.legacy.d.ts b/tsc/internal/bundled/libs/lib.decorators.legacy.d.ts index 89775167cf7e0..0867b0ecc600f 100644 --- a/tsc/internal/bundled/libs/lib.decorators.legacy.d.ts +++ b/tsc/internal/bundled/libs/lib.decorators.legacy.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - declare type ClassDecorator = (target: TFunction) => TFunction | void; declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void; declare type MethodDecorator = (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor) => TypedPropertyDescriptor | void; diff --git a/tsc/internal/bundled/libs/lib.es2015.collection.d.ts b/tsc/internal/bundled/libs/lib.es2015.collection.d.ts index 225e918b76477..1ed676c5abe08 100644 --- a/tsc/internal/bundled/libs/lib.es2015.collection.d.ts +++ b/tsc/internal/bundled/libs/lib.es2015.collection.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface Map { /** * Removes all elements from the Map. diff --git a/tsc/internal/bundled/libs/lib.es2015.core.d.ts b/tsc/internal/bundled/libs/lib.es2015.core.d.ts index e36b9fbdadd0c..4c056d749f643 100644 --- a/tsc/internal/bundled/libs/lib.es2015.core.d.ts +++ b/tsc/internal/bundled/libs/lib.es2015.core.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface Array { /** * Returns the value of the first element in the array where predicate is true, and undefined diff --git a/tsc/internal/bundled/libs/lib.es2015.d.ts b/tsc/internal/bundled/libs/lib.es2015.d.ts index 006d4f486f684..f31b6f2a7b187 100644 --- a/tsc/internal/bundled/libs/lib.es2015.d.ts +++ b/tsc/internal/bundled/libs/lib.es2015.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.es2015.generator.d.ts b/tsc/internal/bundled/libs/lib.es2015.generator.d.ts index 9fedc4d346a13..0e040fde596dc 100644 --- a/tsc/internal/bundled/libs/lib.es2015.generator.d.ts +++ b/tsc/internal/bundled/libs/lib.es2015.generator.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// interface Generator extends IteratorObject { diff --git a/tsc/internal/bundled/libs/lib.es2015.iterable.d.ts b/tsc/internal/bundled/libs/lib.es2015.iterable.d.ts index 7396a53feb8cf..4c57f556a800d 100644 --- a/tsc/internal/bundled/libs/lib.es2015.iterable.d.ts +++ b/tsc/internal/bundled/libs/lib.es2015.iterable.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// interface SymbolConstructor { diff --git a/tsc/internal/bundled/libs/lib.es2015.promise.d.ts b/tsc/internal/bundled/libs/lib.es2015.promise.d.ts index 8030a9bb92783..db2ce423f78d7 100644 --- a/tsc/internal/bundled/libs/lib.es2015.promise.d.ts +++ b/tsc/internal/bundled/libs/lib.es2015.promise.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface PromiseConstructor { /** * A reference to the prototype. diff --git a/tsc/internal/bundled/libs/lib.es2015.proxy.d.ts b/tsc/internal/bundled/libs/lib.es2015.proxy.d.ts index 4f99052fde889..a5f5299f60704 100644 --- a/tsc/internal/bundled/libs/lib.es2015.proxy.d.ts +++ b/tsc/internal/bundled/libs/lib.es2015.proxy.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface ProxyHandler { /** * A trap method for a function call. diff --git a/tsc/internal/bundled/libs/lib.es2015.reflect.d.ts b/tsc/internal/bundled/libs/lib.es2015.reflect.d.ts index f321076579948..296a21529be1d 100644 --- a/tsc/internal/bundled/libs/lib.es2015.reflect.d.ts +++ b/tsc/internal/bundled/libs/lib.es2015.reflect.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - declare namespace Reflect { /** * Calls the function with the specified object as the this value diff --git a/tsc/internal/bundled/libs/lib.es2015.symbol.d.ts b/tsc/internal/bundled/libs/lib.es2015.symbol.d.ts index b2ba22dba66fa..fc5ee0b983659 100644 --- a/tsc/internal/bundled/libs/lib.es2015.symbol.d.ts +++ b/tsc/internal/bundled/libs/lib.es2015.symbol.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface SymbolConstructor { /** * A reference to the prototype. diff --git a/tsc/internal/bundled/libs/lib.es2015.symbol.wellknown.d.ts b/tsc/internal/bundled/libs/lib.es2015.symbol.wellknown.d.ts index e35b14cb98cd0..c6684a5a6339f 100644 --- a/tsc/internal/bundled/libs/lib.es2015.symbol.wellknown.d.ts +++ b/tsc/internal/bundled/libs/lib.es2015.symbol.wellknown.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// interface SymbolConstructor { diff --git a/tsc/internal/bundled/libs/lib.es2016.array.include.d.ts b/tsc/internal/bundled/libs/lib.es2016.array.include.d.ts index d6a31cb38260b..35bf66f77728d 100644 --- a/tsc/internal/bundled/libs/lib.es2016.array.include.d.ts +++ b/tsc/internal/bundled/libs/lib.es2016.array.include.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface Array { /** * Determines whether an array includes a certain element, returning true or false as appropriate. diff --git a/tsc/internal/bundled/libs/lib.es2016.d.ts b/tsc/internal/bundled/libs/lib.es2016.d.ts index 0fd711fd1ce73..2547ac47fdffc 100644 --- a/tsc/internal/bundled/libs/lib.es2016.d.ts +++ b/tsc/internal/bundled/libs/lib.es2016.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.es2016.full.d.ts b/tsc/internal/bundled/libs/lib.es2016.full.d.ts index dfd09e5943910..82b7c35830c8f 100644 --- a/tsc/internal/bundled/libs/lib.es2016.full.d.ts +++ b/tsc/internal/bundled/libs/lib.es2016.full.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.es2016.intl.d.ts b/tsc/internal/bundled/libs/lib.es2016.intl.d.ts index fe677ace3d098..1a9a11b48c13b 100644 --- a/tsc/internal/bundled/libs/lib.es2016.intl.d.ts +++ b/tsc/internal/bundled/libs/lib.es2016.intl.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - declare namespace Intl { /** * The `Intl.getCanonicalLocales()` method returns an array containing diff --git a/tsc/internal/bundled/libs/lib.es2017.arraybuffer.d.ts b/tsc/internal/bundled/libs/lib.es2017.arraybuffer.d.ts index 1c4a1b5da91a2..66dd25016d554 100644 --- a/tsc/internal/bundled/libs/lib.es2017.arraybuffer.d.ts +++ b/tsc/internal/bundled/libs/lib.es2017.arraybuffer.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface ArrayBufferConstructor { new (): ArrayBuffer; } diff --git a/tsc/internal/bundled/libs/lib.es2017.d.ts b/tsc/internal/bundled/libs/lib.es2017.d.ts index c7b5f85c2c2ae..6e1fba4f9f1e9 100644 --- a/tsc/internal/bundled/libs/lib.es2017.d.ts +++ b/tsc/internal/bundled/libs/lib.es2017.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.es2017.date.d.ts b/tsc/internal/bundled/libs/lib.es2017.date.d.ts index 99f161c769726..fb1ea66f5288a 100644 --- a/tsc/internal/bundled/libs/lib.es2017.date.d.ts +++ b/tsc/internal/bundled/libs/lib.es2017.date.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface DateConstructor { /** * Returns the number of milliseconds between midnight, January 1, 1970 Universal Coordinated Time (UTC) (or GMT) and the specified date. diff --git a/tsc/internal/bundled/libs/lib.es2017.full.d.ts b/tsc/internal/bundled/libs/lib.es2017.full.d.ts index aafe7e27d0d2d..cc8c96eb75acf 100644 --- a/tsc/internal/bundled/libs/lib.es2017.full.d.ts +++ b/tsc/internal/bundled/libs/lib.es2017.full.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.es2017.intl.d.ts b/tsc/internal/bundled/libs/lib.es2017.intl.d.ts index cbbf658903825..09ede376c30f0 100644 --- a/tsc/internal/bundled/libs/lib.es2017.intl.d.ts +++ b/tsc/internal/bundled/libs/lib.es2017.intl.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - declare namespace Intl { interface DateTimeFormatPartTypesRegistry { day: any; diff --git a/tsc/internal/bundled/libs/lib.es2017.object.d.ts b/tsc/internal/bundled/libs/lib.es2017.object.d.ts index 85e93ea341221..bb867644b3833 100644 --- a/tsc/internal/bundled/libs/lib.es2017.object.d.ts +++ b/tsc/internal/bundled/libs/lib.es2017.object.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface ObjectConstructor { /** * Returns an array of values of the enumerable own properties of an object diff --git a/tsc/internal/bundled/libs/lib.es2017.sharedmemory.d.ts b/tsc/internal/bundled/libs/lib.es2017.sharedmemory.d.ts index 94aee6434e844..07226ac94859b 100644 --- a/tsc/internal/bundled/libs/lib.es2017.sharedmemory.d.ts +++ b/tsc/internal/bundled/libs/lib.es2017.sharedmemory.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// diff --git a/tsc/internal/bundled/libs/lib.es2017.string.d.ts b/tsc/internal/bundled/libs/lib.es2017.string.d.ts index 6d7f89ee98e9a..330352a839ed8 100644 --- a/tsc/internal/bundled/libs/lib.es2017.string.d.ts +++ b/tsc/internal/bundled/libs/lib.es2017.string.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface String { /** * Pads the current string with a given string (repeated and/or truncated, if needed) so that the resulting string has a given length. diff --git a/tsc/internal/bundled/libs/lib.es2017.typedarrays.d.ts b/tsc/internal/bundled/libs/lib.es2017.typedarrays.d.ts index ca89ff301bcc5..4ac9df4fb980a 100644 --- a/tsc/internal/bundled/libs/lib.es2017.typedarrays.d.ts +++ b/tsc/internal/bundled/libs/lib.es2017.typedarrays.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface Int8ArrayConstructor { new (): Int8Array; } diff --git a/tsc/internal/bundled/libs/lib.es2018.asyncgenerator.d.ts b/tsc/internal/bundled/libs/lib.es2018.asyncgenerator.d.ts index cf9436f74d846..61d30cefe28f7 100644 --- a/tsc/internal/bundled/libs/lib.es2018.asyncgenerator.d.ts +++ b/tsc/internal/bundled/libs/lib.es2018.asyncgenerator.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// interface AsyncGenerator extends AsyncIteratorObject { diff --git a/tsc/internal/bundled/libs/lib.es2018.asynciterable.d.ts b/tsc/internal/bundled/libs/lib.es2018.asynciterable.d.ts index 358fe271faf5b..a3715c5f2ff1e 100644 --- a/tsc/internal/bundled/libs/lib.es2018.asynciterable.d.ts +++ b/tsc/internal/bundled/libs/lib.es2018.asynciterable.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// diff --git a/tsc/internal/bundled/libs/lib.es2018.d.ts b/tsc/internal/bundled/libs/lib.es2018.d.ts index 6bc4051d04f45..b42248b659b62 100644 --- a/tsc/internal/bundled/libs/lib.es2018.d.ts +++ b/tsc/internal/bundled/libs/lib.es2018.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.es2018.full.d.ts b/tsc/internal/bundled/libs/lib.es2018.full.d.ts index 242b8a3d3a66d..6be356c0199d0 100644 --- a/tsc/internal/bundled/libs/lib.es2018.full.d.ts +++ b/tsc/internal/bundled/libs/lib.es2018.full.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.es2018.intl.d.ts b/tsc/internal/bundled/libs/lib.es2018.intl.d.ts index 90d358b355d27..dd957a24a34f3 100644 --- a/tsc/internal/bundled/libs/lib.es2018.intl.d.ts +++ b/tsc/internal/bundled/libs/lib.es2018.intl.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - declare namespace Intl { // http://cldr.unicode.org/index/cldr-spec/plural-rules#TOC-Determining-Plural-Categories type LDMLPluralRule = "zero" | "one" | "two" | "few" | "many" | "other"; diff --git a/tsc/internal/bundled/libs/lib.es2018.promise.d.ts b/tsc/internal/bundled/libs/lib.es2018.promise.d.ts index 156c843bd37a5..72008eddf0402 100644 --- a/tsc/internal/bundled/libs/lib.es2018.promise.d.ts +++ b/tsc/internal/bundled/libs/lib.es2018.promise.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /** * Represents the completion of an asynchronous operation */ diff --git a/tsc/internal/bundled/libs/lib.es2018.regexp.d.ts b/tsc/internal/bundled/libs/lib.es2018.regexp.d.ts index 4bbca17e25935..f1f7fc306e284 100644 --- a/tsc/internal/bundled/libs/lib.es2018.regexp.d.ts +++ b/tsc/internal/bundled/libs/lib.es2018.regexp.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface RegExpMatchArray { groups?: { [key: string]: string; diff --git a/tsc/internal/bundled/libs/lib.es2019.array.d.ts b/tsc/internal/bundled/libs/lib.es2019.array.d.ts index 204f0e69c7919..c620c58379e97 100644 --- a/tsc/internal/bundled/libs/lib.es2019.array.d.ts +++ b/tsc/internal/bundled/libs/lib.es2019.array.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - type FlatArray = { done: Arr; recur: Arr extends ReadonlyArray ? FlatArray diff --git a/tsc/internal/bundled/libs/lib.es2019.d.ts b/tsc/internal/bundled/libs/lib.es2019.d.ts index 09ff746d3a616..bc59e23eadf3b 100644 --- a/tsc/internal/bundled/libs/lib.es2019.d.ts +++ b/tsc/internal/bundled/libs/lib.es2019.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.es2019.full.d.ts b/tsc/internal/bundled/libs/lib.es2019.full.d.ts index 6c89830f49590..c7d6f37914d30 100644 --- a/tsc/internal/bundled/libs/lib.es2019.full.d.ts +++ b/tsc/internal/bundled/libs/lib.es2019.full.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.es2019.intl.d.ts b/tsc/internal/bundled/libs/lib.es2019.intl.d.ts index 7fb16fc44bb60..25c8c0d5109e9 100644 --- a/tsc/internal/bundled/libs/lib.es2019.intl.d.ts +++ b/tsc/internal/bundled/libs/lib.es2019.intl.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - declare namespace Intl { interface DateTimeFormatPartTypesRegistry { unknown: never; diff --git a/tsc/internal/bundled/libs/lib.es2019.object.d.ts b/tsc/internal/bundled/libs/lib.es2019.object.d.ts index 8a9242d937c40..48f99b6a43a0e 100644 --- a/tsc/internal/bundled/libs/lib.es2019.object.d.ts +++ b/tsc/internal/bundled/libs/lib.es2019.object.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// interface ObjectConstructor { diff --git a/tsc/internal/bundled/libs/lib.es2019.string.d.ts b/tsc/internal/bundled/libs/lib.es2019.string.d.ts index 9ae5be7f53978..7cbac58182b9c 100644 --- a/tsc/internal/bundled/libs/lib.es2019.string.d.ts +++ b/tsc/internal/bundled/libs/lib.es2019.string.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface String { /** Removes the trailing white space and line terminator characters from a string. */ trimEnd(): string; diff --git a/tsc/internal/bundled/libs/lib.es2019.symbol.d.ts b/tsc/internal/bundled/libs/lib.es2019.symbol.d.ts index 835350fbdc053..edc93f264b996 100644 --- a/tsc/internal/bundled/libs/lib.es2019.symbol.d.ts +++ b/tsc/internal/bundled/libs/lib.es2019.symbol.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface Symbol { /** * Expose the [[Description]] internal slot of a symbol directly. diff --git a/tsc/internal/bundled/libs/lib.es2020.bigint.d.ts b/tsc/internal/bundled/libs/lib.es2020.bigint.d.ts index fde848f721684..ae31d07ab7ca3 100644 --- a/tsc/internal/bundled/libs/lib.es2020.bigint.d.ts +++ b/tsc/internal/bundled/libs/lib.es2020.bigint.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// interface BigIntToLocaleStringOptions { diff --git a/tsc/internal/bundled/libs/lib.es2020.d.ts b/tsc/internal/bundled/libs/lib.es2020.d.ts index e6a9f3a5d0309..0b60790bf809f 100644 --- a/tsc/internal/bundled/libs/lib.es2020.d.ts +++ b/tsc/internal/bundled/libs/lib.es2020.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.es2020.date.d.ts b/tsc/internal/bundled/libs/lib.es2020.date.d.ts index d56af7f37ebb7..9363e7c4a2327 100644 --- a/tsc/internal/bundled/libs/lib.es2020.date.d.ts +++ b/tsc/internal/bundled/libs/lib.es2020.date.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// interface Date { diff --git a/tsc/internal/bundled/libs/lib.es2020.full.d.ts b/tsc/internal/bundled/libs/lib.es2020.full.d.ts index bec1417c07bd8..2c80d06bb5c8d 100644 --- a/tsc/internal/bundled/libs/lib.es2020.full.d.ts +++ b/tsc/internal/bundled/libs/lib.es2020.full.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.es2020.intl.d.ts b/tsc/internal/bundled/libs/lib.es2020.intl.d.ts index 6f0980e0fe909..82ac6849fbe31 100644 --- a/tsc/internal/bundled/libs/lib.es2020.intl.d.ts +++ b/tsc/internal/bundled/libs/lib.es2020.intl.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// declare namespace Intl { /** diff --git a/tsc/internal/bundled/libs/lib.es2020.number.d.ts b/tsc/internal/bundled/libs/lib.es2020.number.d.ts index 99d414b388d3b..0359f637759fb 100644 --- a/tsc/internal/bundled/libs/lib.es2020.number.d.ts +++ b/tsc/internal/bundled/libs/lib.es2020.number.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// interface Number { diff --git a/tsc/internal/bundled/libs/lib.es2020.promise.d.ts b/tsc/internal/bundled/libs/lib.es2020.promise.d.ts index 1c28cf67d36c4..ee851c083c9a1 100644 --- a/tsc/internal/bundled/libs/lib.es2020.promise.d.ts +++ b/tsc/internal/bundled/libs/lib.es2020.promise.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface PromiseFulfilledResult { status: "fulfilled"; value: T; diff --git a/tsc/internal/bundled/libs/lib.es2020.sharedmemory.d.ts b/tsc/internal/bundled/libs/lib.es2020.sharedmemory.d.ts index ba412f1dc3c64..22a916ce41e62 100644 --- a/tsc/internal/bundled/libs/lib.es2020.sharedmemory.d.ts +++ b/tsc/internal/bundled/libs/lib.es2020.sharedmemory.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// interface Atomics { diff --git a/tsc/internal/bundled/libs/lib.es2020.string.d.ts b/tsc/internal/bundled/libs/lib.es2020.string.d.ts index 0ca623cc895f4..58d595ed532de 100644 --- a/tsc/internal/bundled/libs/lib.es2020.string.d.ts +++ b/tsc/internal/bundled/libs/lib.es2020.string.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.es2020.symbol.wellknown.d.ts b/tsc/internal/bundled/libs/lib.es2020.symbol.wellknown.d.ts index 44bd867eb9c21..de75b048b54f3 100644 --- a/tsc/internal/bundled/libs/lib.es2020.symbol.wellknown.d.ts +++ b/tsc/internal/bundled/libs/lib.es2020.symbol.wellknown.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// diff --git a/tsc/internal/bundled/libs/lib.es2021.d.ts b/tsc/internal/bundled/libs/lib.es2021.d.ts index 03b1692b39a4f..80c4fed001ad9 100644 --- a/tsc/internal/bundled/libs/lib.es2021.d.ts +++ b/tsc/internal/bundled/libs/lib.es2021.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.es2021.full.d.ts b/tsc/internal/bundled/libs/lib.es2021.full.d.ts index 7a173fb19552a..6b2de634ca8dc 100644 --- a/tsc/internal/bundled/libs/lib.es2021.full.d.ts +++ b/tsc/internal/bundled/libs/lib.es2021.full.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.es2021.intl.d.ts b/tsc/internal/bundled/libs/lib.es2021.intl.d.ts index 88f4c7e927f0a..e359e517736c6 100644 --- a/tsc/internal/bundled/libs/lib.es2021.intl.d.ts +++ b/tsc/internal/bundled/libs/lib.es2021.intl.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - declare namespace Intl { interface DateTimeFormatPartTypesRegistry { fractionalSecond: any; diff --git a/tsc/internal/bundled/libs/lib.es2021.promise.d.ts b/tsc/internal/bundled/libs/lib.es2021.promise.d.ts index 63c149f22a1eb..0626880c70496 100644 --- a/tsc/internal/bundled/libs/lib.es2021.promise.d.ts +++ b/tsc/internal/bundled/libs/lib.es2021.promise.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface AggregateError extends Error { errors: any[]; } diff --git a/tsc/internal/bundled/libs/lib.es2021.string.d.ts b/tsc/internal/bundled/libs/lib.es2021.string.d.ts index 507877af72652..45c6c0cede8ae 100644 --- a/tsc/internal/bundled/libs/lib.es2021.string.d.ts +++ b/tsc/internal/bundled/libs/lib.es2021.string.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface String { /** * Replace all instances of a substring in a string, using a regular expression or search string. diff --git a/tsc/internal/bundled/libs/lib.es2021.weakref.d.ts b/tsc/internal/bundled/libs/lib.es2021.weakref.d.ts index 35598e2717691..294b091f1eed5 100644 --- a/tsc/internal/bundled/libs/lib.es2021.weakref.d.ts +++ b/tsc/internal/bundled/libs/lib.es2021.weakref.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// interface WeakRef { diff --git a/tsc/internal/bundled/libs/lib.es2022.array.d.ts b/tsc/internal/bundled/libs/lib.es2022.array.d.ts index 0441a95e64c53..e42fec5ada831 100644 --- a/tsc/internal/bundled/libs/lib.es2022.array.d.ts +++ b/tsc/internal/bundled/libs/lib.es2022.array.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface Array { /** * Returns the item located at the specified index. diff --git a/tsc/internal/bundled/libs/lib.es2022.d.ts b/tsc/internal/bundled/libs/lib.es2022.d.ts index 21f17a5ba65fa..fae46b6cd2353 100644 --- a/tsc/internal/bundled/libs/lib.es2022.d.ts +++ b/tsc/internal/bundled/libs/lib.es2022.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.es2022.error.d.ts b/tsc/internal/bundled/libs/lib.es2022.error.d.ts index 7a1f9898a0e35..37f7bdf5d8ce9 100644 --- a/tsc/internal/bundled/libs/lib.es2022.error.d.ts +++ b/tsc/internal/bundled/libs/lib.es2022.error.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// interface ErrorOptions { diff --git a/tsc/internal/bundled/libs/lib.es2022.full.d.ts b/tsc/internal/bundled/libs/lib.es2022.full.d.ts index 644387dd604f8..abbb322243df7 100644 --- a/tsc/internal/bundled/libs/lib.es2022.full.d.ts +++ b/tsc/internal/bundled/libs/lib.es2022.full.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.es2022.intl.d.ts b/tsc/internal/bundled/libs/lib.es2022.intl.d.ts index d9f758c521173..d486189650b76 100644 --- a/tsc/internal/bundled/libs/lib.es2022.intl.d.ts +++ b/tsc/internal/bundled/libs/lib.es2022.intl.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - declare namespace Intl { /** * An object with some or all properties of the `Intl.Segmenter` constructor `options` parameter. diff --git a/tsc/internal/bundled/libs/lib.es2022.object.d.ts b/tsc/internal/bundled/libs/lib.es2022.object.d.ts index f07c8842f188e..33386b9e5b106 100644 --- a/tsc/internal/bundled/libs/lib.es2022.object.d.ts +++ b/tsc/internal/bundled/libs/lib.es2022.object.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface ObjectConstructor { /** * Determines whether an object has a property with the specified name. diff --git a/tsc/internal/bundled/libs/lib.es2022.regexp.d.ts b/tsc/internal/bundled/libs/lib.es2022.regexp.d.ts index b632b42dad9d8..52e9615930910 100644 --- a/tsc/internal/bundled/libs/lib.es2022.regexp.d.ts +++ b/tsc/internal/bundled/libs/lib.es2022.regexp.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface RegExpMatchArray { indices?: RegExpIndicesArray; } diff --git a/tsc/internal/bundled/libs/lib.es2022.string.d.ts b/tsc/internal/bundled/libs/lib.es2022.string.d.ts index 48e03ea6f486e..bb42b07d620eb 100644 --- a/tsc/internal/bundled/libs/lib.es2022.string.d.ts +++ b/tsc/internal/bundled/libs/lib.es2022.string.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface String { /** * Returns a new String consisting of the single UTF-16 code unit located at the specified index. diff --git a/tsc/internal/bundled/libs/lib.es2023.array.d.ts b/tsc/internal/bundled/libs/lib.es2023.array.d.ts index f5b55982c7c65..fb61d5f3526dc 100644 --- a/tsc/internal/bundled/libs/lib.es2023.array.d.ts +++ b/tsc/internal/bundled/libs/lib.es2023.array.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface Array { /** * Returns the value of the last element in the array where predicate is true, and undefined diff --git a/tsc/internal/bundled/libs/lib.es2023.collection.d.ts b/tsc/internal/bundled/libs/lib.es2023.collection.d.ts index 5e67a83554ba6..1151e4fa1e36d 100644 --- a/tsc/internal/bundled/libs/lib.es2023.collection.d.ts +++ b/tsc/internal/bundled/libs/lib.es2023.collection.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface WeakKeyTypes { symbol: symbol; } diff --git a/tsc/internal/bundled/libs/lib.es2023.d.ts b/tsc/internal/bundled/libs/lib.es2023.d.ts index ca01eec1531cd..52fb8af54049f 100644 --- a/tsc/internal/bundled/libs/lib.es2023.d.ts +++ b/tsc/internal/bundled/libs/lib.es2023.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.es2023.full.d.ts b/tsc/internal/bundled/libs/lib.es2023.full.d.ts index 6e670ae43d909..e86211925795c 100644 --- a/tsc/internal/bundled/libs/lib.es2023.full.d.ts +++ b/tsc/internal/bundled/libs/lib.es2023.full.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.es2023.intl.d.ts b/tsc/internal/bundled/libs/lib.es2023.intl.d.ts index 5ea242308f044..6e269d28c9e05 100644 --- a/tsc/internal/bundled/libs/lib.es2023.intl.d.ts +++ b/tsc/internal/bundled/libs/lib.es2023.intl.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - declare namespace Intl { interface NumberFormatOptionsUseGroupingRegistry { min2: never; diff --git a/tsc/internal/bundled/libs/lib.es2024.arraybuffer.d.ts b/tsc/internal/bundled/libs/lib.es2024.arraybuffer.d.ts index 5f375d3aa57ad..90ff44375d0a9 100644 --- a/tsc/internal/bundled/libs/lib.es2024.arraybuffer.d.ts +++ b/tsc/internal/bundled/libs/lib.es2024.arraybuffer.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface ArrayBuffer { /** * If this ArrayBuffer is resizable, returns the maximum byte length given during construction; returns the byte length if not. diff --git a/tsc/internal/bundled/libs/lib.es2024.collection.d.ts b/tsc/internal/bundled/libs/lib.es2024.collection.d.ts index a254b01572c68..266d74ac90642 100644 --- a/tsc/internal/bundled/libs/lib.es2024.collection.d.ts +++ b/tsc/internal/bundled/libs/lib.es2024.collection.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface MapConstructor { /** * Groups members of an iterable according to the return value of the passed callback. diff --git a/tsc/internal/bundled/libs/lib.es2024.d.ts b/tsc/internal/bundled/libs/lib.es2024.d.ts index 723ed4ac005de..cb34ae9d24af2 100644 --- a/tsc/internal/bundled/libs/lib.es2024.d.ts +++ b/tsc/internal/bundled/libs/lib.es2024.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.es2024.full.d.ts b/tsc/internal/bundled/libs/lib.es2024.full.d.ts index 4264e30645005..838a205f6a68c 100644 --- a/tsc/internal/bundled/libs/lib.es2024.full.d.ts +++ b/tsc/internal/bundled/libs/lib.es2024.full.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.es2024.object.d.ts b/tsc/internal/bundled/libs/lib.es2024.object.d.ts index 5f57880dcd313..89fbbf87f6077 100644 --- a/tsc/internal/bundled/libs/lib.es2024.object.d.ts +++ b/tsc/internal/bundled/libs/lib.es2024.object.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface ObjectConstructor { /** * Groups members of an iterable according to the return value of the passed callback. diff --git a/tsc/internal/bundled/libs/lib.es2024.promise.d.ts b/tsc/internal/bundled/libs/lib.es2024.promise.d.ts index d74e968d878d2..1293e6595d2f0 100644 --- a/tsc/internal/bundled/libs/lib.es2024.promise.d.ts +++ b/tsc/internal/bundled/libs/lib.es2024.promise.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface PromiseWithResolvers { promise: Promise; resolve: (value: T | PromiseLike) => void; diff --git a/tsc/internal/bundled/libs/lib.es2024.regexp.d.ts b/tsc/internal/bundled/libs/lib.es2024.regexp.d.ts index bd023f77e743d..e7a6adc389f11 100644 --- a/tsc/internal/bundled/libs/lib.es2024.regexp.d.ts +++ b/tsc/internal/bundled/libs/lib.es2024.regexp.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface RegExp { /** * Returns a Boolean value indicating the state of the unicodeSets flag (v) used with a regular expression. diff --git a/tsc/internal/bundled/libs/lib.es2024.sharedmemory.d.ts b/tsc/internal/bundled/libs/lib.es2024.sharedmemory.d.ts index f981a325d8236..dcaa149efc503 100644 --- a/tsc/internal/bundled/libs/lib.es2024.sharedmemory.d.ts +++ b/tsc/internal/bundled/libs/lib.es2024.sharedmemory.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// interface Atomics { diff --git a/tsc/internal/bundled/libs/lib.es2024.string.d.ts b/tsc/internal/bundled/libs/lib.es2024.string.d.ts index cb2a4eebf028e..72866901ecb4a 100644 --- a/tsc/internal/bundled/libs/lib.es2024.string.d.ts +++ b/tsc/internal/bundled/libs/lib.es2024.string.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface String { /** * Returns true if all leading surrogates and trailing surrogates appear paired and in order. diff --git a/tsc/internal/bundled/libs/lib.es2025.collection.d.ts b/tsc/internal/bundled/libs/lib.es2025.collection.d.ts index 507e5e72a3525..c8bbbc2dce274 100644 --- a/tsc/internal/bundled/libs/lib.es2025.collection.d.ts +++ b/tsc/internal/bundled/libs/lib.es2025.collection.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// interface ReadonlySetLike { diff --git a/tsc/internal/bundled/libs/lib.es2025.d.ts b/tsc/internal/bundled/libs/lib.es2025.d.ts index 78e41e31e201f..ca93869561a6b 100644 --- a/tsc/internal/bundled/libs/lib.es2025.d.ts +++ b/tsc/internal/bundled/libs/lib.es2025.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.es2025.float16.d.ts b/tsc/internal/bundled/libs/lib.es2025.float16.d.ts index b4ebf8a689d2e..9b670f8ede25a 100644 --- a/tsc/internal/bundled/libs/lib.es2025.float16.d.ts +++ b/tsc/internal/bundled/libs/lib.es2025.float16.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// diff --git a/tsc/internal/bundled/libs/lib.es2025.full.d.ts b/tsc/internal/bundled/libs/lib.es2025.full.d.ts index 0870bb4f53d7b..66090e4494127 100644 --- a/tsc/internal/bundled/libs/lib.es2025.full.d.ts +++ b/tsc/internal/bundled/libs/lib.es2025.full.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.es2025.intl.d.ts b/tsc/internal/bundled/libs/lib.es2025.intl.d.ts index ffc660e027174..9b65bed004982 100644 --- a/tsc/internal/bundled/libs/lib.es2025.intl.d.ts +++ b/tsc/internal/bundled/libs/lib.es2025.intl.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// declare namespace Intl { diff --git a/tsc/internal/bundled/libs/lib.es2025.iterator.d.ts b/tsc/internal/bundled/libs/lib.es2025.iterator.d.ts index 13ca5401a1530..613a90b5980fe 100644 --- a/tsc/internal/bundled/libs/lib.es2025.iterator.d.ts +++ b/tsc/internal/bundled/libs/lib.es2025.iterator.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// // NOTE: This is specified as what is essentially an unreachable module. All actual global declarations can be found diff --git a/tsc/internal/bundled/libs/lib.es2025.promise.d.ts b/tsc/internal/bundled/libs/lib.es2025.promise.d.ts index 791fd54e44ea3..99f3747a0a1ce 100644 --- a/tsc/internal/bundled/libs/lib.es2025.promise.d.ts +++ b/tsc/internal/bundled/libs/lib.es2025.promise.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface PromiseConstructor { /** * Takes a callback of any kind (returns or throws, synchronously or asynchronously) and wraps its result diff --git a/tsc/internal/bundled/libs/lib.es2025.regexp.d.ts b/tsc/internal/bundled/libs/lib.es2025.regexp.d.ts index 1e4d7c14f1c85..11ba372e6cf4c 100644 --- a/tsc/internal/bundled/libs/lib.es2025.regexp.d.ts +++ b/tsc/internal/bundled/libs/lib.es2025.regexp.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface RegExpConstructor { /** * Escapes any RegExp syntax characters in the input string, returning a diff --git a/tsc/internal/bundled/libs/lib.es5.d.ts b/tsc/internal/bundled/libs/lib.es5.d.ts index e394111cb0e6c..4a57b9afac0a6 100644 --- a/tsc/internal/bundled/libs/lib.es5.d.ts +++ b/tsc/internal/bundled/libs/lib.es5.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// diff --git a/tsc/internal/bundled/libs/lib.es6.d.ts b/tsc/internal/bundled/libs/lib.es6.d.ts index cbedf4609e48b..9f04deefda3cd 100644 --- a/tsc/internal/bundled/libs/lib.es6.d.ts +++ b/tsc/internal/bundled/libs/lib.es6.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.esnext.array.d.ts b/tsc/internal/bundled/libs/lib.esnext.array.d.ts index 85fc39eb6c3a8..5a7a4351c9999 100644 --- a/tsc/internal/bundled/libs/lib.esnext.array.d.ts +++ b/tsc/internal/bundled/libs/lib.esnext.array.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface ArrayConstructor { /** * Creates an array from an async iterator or iterable object. diff --git a/tsc/internal/bundled/libs/lib.esnext.collection.d.ts b/tsc/internal/bundled/libs/lib.esnext.collection.d.ts index ab807de98a19d..0e6cac53b0511 100644 --- a/tsc/internal/bundled/libs/lib.esnext.collection.d.ts +++ b/tsc/internal/bundled/libs/lib.esnext.collection.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// interface Map { diff --git a/tsc/internal/bundled/libs/lib.esnext.d.ts b/tsc/internal/bundled/libs/lib.esnext.d.ts index 044947f4d2aa0..90d537b5902e3 100644 --- a/tsc/internal/bundled/libs/lib.esnext.d.ts +++ b/tsc/internal/bundled/libs/lib.esnext.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.esnext.date.d.ts b/tsc/internal/bundled/libs/lib.esnext.date.d.ts index cbdd6a47f19d0..3c19f6a1b4579 100644 --- a/tsc/internal/bundled/libs/lib.esnext.date.d.ts +++ b/tsc/internal/bundled/libs/lib.esnext.date.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// interface Date { diff --git a/tsc/internal/bundled/libs/lib.esnext.decorators.d.ts b/tsc/internal/bundled/libs/lib.esnext.decorators.d.ts index dad8914acafe3..f26d5e977f0c0 100644 --- a/tsc/internal/bundled/libs/lib.esnext.decorators.d.ts +++ b/tsc/internal/bundled/libs/lib.esnext.decorators.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// diff --git a/tsc/internal/bundled/libs/lib.esnext.disposable.d.ts b/tsc/internal/bundled/libs/lib.esnext.disposable.d.ts index 62b07f8832d65..81c1b7066731d 100644 --- a/tsc/internal/bundled/libs/lib.esnext.disposable.d.ts +++ b/tsc/internal/bundled/libs/lib.esnext.disposable.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.esnext.error.d.ts b/tsc/internal/bundled/libs/lib.esnext.error.d.ts index 470aaec5409fe..6908b58f2de62 100644 --- a/tsc/internal/bundled/libs/lib.esnext.error.d.ts +++ b/tsc/internal/bundled/libs/lib.esnext.error.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface ErrorConstructor { /** * Indicates whether the argument provided is a built-in Error instance or not. diff --git a/tsc/internal/bundled/libs/lib.esnext.full.d.ts b/tsc/internal/bundled/libs/lib.esnext.full.d.ts index b963c65b8b652..af69c4071ae52 100644 --- a/tsc/internal/bundled/libs/lib.esnext.full.d.ts +++ b/tsc/internal/bundled/libs/lib.esnext.full.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.esnext.intl.d.ts b/tsc/internal/bundled/libs/lib.esnext.intl.d.ts index d6c2f82c7be32..b4143f7f99074 100644 --- a/tsc/internal/bundled/libs/lib.esnext.intl.d.ts +++ b/tsc/internal/bundled/libs/lib.esnext.intl.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// declare namespace Intl { diff --git a/tsc/internal/bundled/libs/lib.esnext.sharedmemory.d.ts b/tsc/internal/bundled/libs/lib.esnext.sharedmemory.d.ts index 2194de8c67d34..b54a4670ac5ef 100644 --- a/tsc/internal/bundled/libs/lib.esnext.sharedmemory.d.ts +++ b/tsc/internal/bundled/libs/lib.esnext.sharedmemory.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface Atomics { /** * Performs a finite-time microwait by signaling to the operating system or diff --git a/tsc/internal/bundled/libs/lib.esnext.temporal.d.ts b/tsc/internal/bundled/libs/lib.esnext.temporal.d.ts index 2d18011132a25..5f1293f455333 100644 --- a/tsc/internal/bundled/libs/lib.esnext.temporal.d.ts +++ b/tsc/internal/bundled/libs/lib.esnext.temporal.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - /// /// /// diff --git a/tsc/internal/bundled/libs/lib.esnext.typedarrays.d.ts b/tsc/internal/bundled/libs/lib.esnext.typedarrays.d.ts index 993014195c8b9..51f57e601d30f 100644 --- a/tsc/internal/bundled/libs/lib.esnext.typedarrays.d.ts +++ b/tsc/internal/bundled/libs/lib.esnext.typedarrays.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - interface Uint8Array { /** * Converts the `Uint8Array` to a base64-encoded string. diff --git a/tsc/internal/bundled/libs/lib.scripthost.d.ts b/tsc/internal/bundled/libs/lib.scripthost.d.ts index 75c558b3bcd90..70696a13b5c92 100644 --- a/tsc/internal/bundled/libs/lib.scripthost.d.ts +++ b/tsc/internal/bundled/libs/lib.scripthost.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - ///////////////////////////// /// Windows Script Host APIS ///////////////////////////// diff --git a/tsc/internal/bundled/libs/lib.webworker.importscripts.d.ts b/tsc/internal/bundled/libs/lib.webworker.importscripts.d.ts index 529928af9c302..cae93c8dd2ff1 100644 --- a/tsc/internal/bundled/libs/lib.webworker.importscripts.d.ts +++ b/tsc/internal/bundled/libs/lib.webworker.importscripts.d.ts @@ -13,7 +13,6 @@ See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ - ///////////////////////////// /// WorkerGlobalScope APIs ///////////////////////////// diff --git a/tsc/internal/diagnostics/diagnosticMessages.json b/tsc/internal/diagnostics/diagnosticMessages.json index 17586e927d0ac..595eea54bf171 100644 --- a/tsc/internal/diagnostics/diagnosticMessages.json +++ b/tsc/internal/diagnostics/diagnosticMessages.json @@ -1,8559 +1,8559 @@ -{ - "Unterminated string literal.": { - "category": "Error", - "code": 1002 - }, - "Identifier expected.": { - "category": "Error", - "code": 1003 - }, - "'{0}' expected.": { - "category": "Error", - "code": 1005 - }, - "A file cannot have a reference to itself.": { - "category": "Error", - "code": 1006 - }, - "The parser expected to find a '{1}' to match the '{0}' token here.": { - "category": "Error", - "code": 1007 - }, - "Trailing comma not allowed.": { - "category": "Error", - "code": 1009 - }, - "'*/' expected.": { - "category": "Error", - "code": 1010 - }, - "An element access expression should take an argument.": { - "category": "Error", - "code": 1011 - }, - "Unexpected token.": { - "category": "Error", - "code": 1012 - }, - "A rest parameter or binding pattern may not have a trailing comma.": { - "category": "Error", - "code": 1013 - }, - "A rest parameter must be last in a parameter list.": { - "category": "Error", - "code": 1014 - }, - "Parameter cannot have question mark and initializer.": { - "category": "Error", - "code": 1015 - }, - "A required parameter cannot follow an optional parameter.": { - "category": "Error", - "code": 1016 - }, - "An index signature cannot have a rest parameter.": { - "category": "Error", - "code": 1017 - }, - "An index signature parameter cannot have an accessibility modifier.": { - "category": "Error", - "code": 1018 - }, - "An index signature parameter cannot have a question mark.": { - "category": "Error", - "code": 1019 - }, - "An index signature parameter cannot have an initializer.": { - "category": "Error", - "code": 1020 - }, - "An index signature must have a type annotation.": { - "category": "Error", - "code": 1021 - }, - "An index signature parameter must have a type annotation.": { - "category": "Error", - "code": 1022 - }, - "'readonly' modifier can only appear on a property declaration or index signature.": { - "category": "Error", - "code": 1024 - }, - "An index signature cannot have a trailing comma.": { - "category": "Error", - "code": 1025 - }, - "Accessibility modifier already seen.": { - "category": "Error", - "code": 1028 - }, - "'{0}' modifier must precede '{1}' modifier.": { - "category": "Error", - "code": 1029 - }, - "'{0}' modifier already seen.": { - "category": "Error", - "code": 1030 - }, - "'{0}' modifier cannot appear on class elements of this kind.": { - "category": "Error", - "code": 1031 - }, - "'super' must be followed by an argument list or member access.": { - "category": "Error", - "code": 1034 - }, - "Only ambient modules can use quoted names.": { - "category": "Error", - "code": 1035 - }, - "Statements are not allowed in ambient contexts.": { - "category": "Error", - "code": 1036 - }, - "A 'declare' modifier cannot be used in an already ambient context.": { - "category": "Error", - "code": 1038 - }, - "Initializers are not allowed in ambient contexts.": { - "category": "Error", - "code": 1039 - }, - "'{0}' modifier cannot be used in an ambient context.": { - "category": "Error", - "code": 1040 - }, - "'{0}' modifier cannot be used here.": { - "category": "Error", - "code": 1042 - }, - "'{0}' modifier cannot appear on a module or namespace element.": { - "category": "Error", - "code": 1044 - }, - "Top-level declarations in .d.ts files must start with either a 'declare' or 'export' modifier.": { - "category": "Error", - "code": 1046 - }, - "A rest parameter cannot be optional.": { - "category": "Error", - "code": 1047 - }, - "A rest parameter cannot have an initializer.": { - "category": "Error", - "code": 1048 - }, - "A 'set' accessor must have exactly one parameter.": { - "category": "Error", - "code": 1049 - }, - "A 'set' accessor cannot have an optional parameter.": { - "category": "Error", - "code": 1051 - }, - "A 'set' accessor parameter cannot have an initializer.": { - "category": "Error", - "code": 1052 - }, - "A 'set' accessor cannot have rest parameter.": { - "category": "Error", - "code": 1053 - }, - "A 'get' accessor cannot have parameters.": { - "category": "Error", - "code": 1054 - }, - "Type '{0}' is not a valid async function return type in ES5 because it does not refer to a Promise-compatible constructor value.": { - "category": "Error", - "code": 1055 - }, - "Accessors are only available when targeting ECMAScript 5 and higher.": { - "category": "Error", - "code": 1056 - }, - "The return type of an async function must either be a valid promise or must not contain a callable 'then' member.": { - "category": "Error", - "code": 1058 - }, - "A promise must have a 'then' method.": { - "category": "Error", - "code": 1059 - }, - "The first parameter of the 'then' method of a promise must be a callback.": { - "category": "Error", - "code": 1060 - }, - "Enum member must have initializer.": { - "category": "Error", - "code": 1061 - }, - "Type is referenced directly or indirectly in the fulfillment callback of its own 'then' method.": { - "category": "Error", - "code": 1062 - }, - "An export assignment cannot be used in a namespace.": { - "category": "Error", - "code": 1063 - }, - "The return type of an async function or method must be the global Promise type. Did you mean to write 'Promise<{0}>'?": { - "category": "Error", - "code": 1064 - }, - "The return type of an async function or method must be the global Promise type.": { - "category": "Error", - "code": 1065 - }, - "In ambient enum declarations member initializer must be constant expression.": { - "category": "Error", - "code": 1066 - }, - "Unexpected token. A constructor, method, accessor, or property was expected.": { - "category": "Error", - "code": 1068 - }, - "Unexpected token. A type parameter name was expected without curly braces.": { - "category": "Error", - "code": 1069 - }, - "'{0}' modifier cannot appear on a type member.": { - "category": "Error", - "code": 1070 - }, - "'{0}' modifier cannot appear on an index signature.": { - "category": "Error", - "code": 1071 - }, - "A '{0}' modifier cannot be used with an import declaration.": { - "category": "Error", - "code": 1079 - }, - "Invalid 'reference' directive syntax.": { - "category": "Error", - "code": 1084 - }, - "'{0}' modifier cannot appear on a constructor declaration.": { - "category": "Error", - "code": 1089 - }, - "'{0}' modifier cannot appear on a parameter.": { - "category": "Error", - "code": 1090 - }, - "Only a single variable declaration is allowed in a 'for...in' statement.": { - "category": "Error", - "code": 1091 - }, - "Type parameters cannot appear on a constructor declaration.": { - "category": "Error", - "code": 1092 - }, - "Type annotation cannot appear on a constructor declaration.": { - "category": "Error", - "code": 1093 - }, - "An accessor cannot have type parameters.": { - "category": "Error", - "code": 1094 - }, - "A 'set' accessor cannot have a return type annotation.": { - "category": "Error", - "code": 1095 - }, - "An index signature must have exactly one parameter.": { - "category": "Error", - "code": 1096 - }, - "'{0}' list cannot be empty.": { - "category": "Error", - "code": 1097 - }, - "Type parameter list cannot be empty.": { - "category": "Error", - "code": 1098 - }, - "Type argument list cannot be empty.": { - "category": "Error", - "code": 1099 - }, - "Invalid use of '{0}' in strict mode.": { - "category": "Error", - "code": 1100 - }, - "'with' statements are not allowed in strict mode.": { - "category": "Error", - "code": 1101 - }, - "'delete' cannot be called on an identifier in strict mode.": { - "category": "Error", - "code": 1102 - }, - "'for await' loops are only allowed within async functions and at the top levels of modules.": { - "category": "Error", - "code": 1103 - }, - "A 'continue' statement can only be used within an enclosing iteration statement.": { - "category": "Error", - "code": 1104 - }, - "A 'break' statement can only be used within an enclosing iteration or switch statement.": { - "category": "Error", - "code": 1105 - }, - "The left-hand side of a 'for...of' statement may not be 'async'.": { - "category": "Error", - "code": 1106 - }, - "Jump target cannot cross function boundary.": { - "category": "Error", - "code": 1107 - }, - "A 'return' statement can only be used within a function body.": { - "category": "Error", - "code": 1108 - }, - "Expression expected.": { - "category": "Error", - "code": 1109 - }, - "Type expected.": { - "category": "Error", - "code": 1110 - }, - "Private field '{0}' must be declared in an enclosing class.": { - "category": "Error", - "code": 1111 - }, - "A 'default' clause cannot appear more than once in a 'switch' statement.": { - "category": "Error", - "code": 1113 - }, - "Duplicate label '{0}'.": { - "category": "Error", - "code": 1114 - }, - "A 'continue' statement can only jump to a label of an enclosing iteration statement.": { - "category": "Error", - "code": 1115 - }, - "A 'break' statement can only jump to a label of an enclosing statement.": { - "category": "Error", - "code": 1116 - }, - "An object literal cannot have multiple properties with the same name.": { - "category": "Error", - "code": 1117 - }, - "An object literal cannot have multiple get/set accessors with the same name.": { - "category": "Error", - "code": 1118 - }, - "An object literal cannot have property and accessor with the same name.": { - "category": "Error", - "code": 1119 - }, - "An export assignment cannot have modifiers.": { - "category": "Error", - "code": 1120 - }, - "Octal literals are not allowed. Use the syntax '{0}'.": { - "category": "Error", - "code": 1121 - }, - "Variable declaration list cannot be empty.": { - "category": "Error", - "code": 1123 - }, - "Digit expected.": { - "category": "Error", - "code": 1124 - }, - "Hexadecimal digit expected.": { - "category": "Error", - "code": 1125 - }, - "Unexpected end of text.": { - "category": "Error", - "code": 1126 - }, - "Invalid character.": { - "category": "Error", - "code": 1127 - }, - "Declaration or statement expected.": { - "category": "Error", - "code": 1128 - }, - "Statement expected.": { - "category": "Error", - "code": 1129 - }, - "'case' or 'default' expected.": { - "category": "Error", - "code": 1130 - }, - "Property or signature expected.": { - "category": "Error", - "code": 1131 - }, - "Enum member expected.": { - "category": "Error", - "code": 1132 - }, - "Variable declaration expected.": { - "category": "Error", - "code": 1134 - }, - "Argument expression expected.": { - "category": "Error", - "code": 1135 - }, - "Property assignment expected.": { - "category": "Error", - "code": 1136 - }, - "Expression or comma expected.": { - "category": "Error", - "code": 1137 - }, - "Parameter declaration expected.": { - "category": "Error", - "code": 1138 - }, - "Type parameter declaration expected.": { - "category": "Error", - "code": 1139 - }, - "Type argument expected.": { - "category": "Error", - "code": 1140 - }, - "String literal expected.": { - "category": "Error", - "code": 1141 - }, - "Line break not permitted here.": { - "category": "Error", - "code": 1142 - }, - "'{' or ';' expected.": { - "category": "Error", - "code": 1144 - }, - "'{' or JSX element expected.": { - "category": "Error", - "code": 1145 - }, - "Declaration expected.": { - "category": "Error", - "code": 1146 - }, - "Import declarations in a namespace cannot reference a module.": { - "category": "Error", - "code": 1147 - }, - "Cannot use imports, exports, or module augmentations when '--module' is 'none'.": { - "category": "Error", - "code": 1148 - }, - "File name '{0}' differs from already included file name '{1}' only in casing.": { - "category": "Error", - "code": 1149 - }, - "'{0}' declarations must be initialized.": { - "category": "Error", - "code": 1155 - }, - "'{0}' declarations can only be declared inside a block.": { - "category": "Error", - "code": 1156 - }, - "Unterminated template literal.": { - "category": "Error", - "code": 1160 - }, - "Unterminated regular expression literal.": { - "category": "Error", - "code": 1161 - }, - "An object member cannot be declared optional.": { - "category": "Error", - "code": 1162 - }, - "A 'yield' expression is only allowed in a generator body.": { - "category": "Error", - "code": 1163 - }, - "Computed property names are not allowed in enums.": { - "category": "Error", - "code": 1164 - }, - "A computed property name in an ambient context must refer to an expression whose type is a literal type or a 'unique symbol' type.": { - "category": "Error", - "code": 1165 - }, - "A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type.": { - "category": "Error", - "code": 1166 - }, - "A computed property name in a method overload must refer to an expression whose type is a literal type or a 'unique symbol' type.": { - "category": "Error", - "code": 1168 - }, - "A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type.": { - "category": "Error", - "code": 1169 - }, - "A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type.": { - "category": "Error", - "code": 1170 - }, - "A comma expression is not allowed in a computed property name.": { - "category": "Error", - "code": 1171 - }, - "'extends' clause already seen.": { - "category": "Error", - "code": 1172 - }, - "'extends' clause must precede 'implements' clause.": { - "category": "Error", - "code": 1173 - }, - "Classes can only extend a single class.": { - "category": "Error", - "code": 1174 - }, - "'implements' clause already seen.": { - "category": "Error", - "code": 1175 - }, - "Interface declaration cannot have 'implements' clause.": { - "category": "Error", - "code": 1176 - }, - "Binary digit expected.": { - "category": "Error", - "code": 1177 - }, - "Octal digit expected.": { - "category": "Error", - "code": 1178 - }, - "Unexpected token. '{' expected.": { - "category": "Error", - "code": 1179 - }, - "Property destructuring pattern expected.": { - "category": "Error", - "code": 1180 - }, - "Array element destructuring pattern expected.": { - "category": "Error", - "code": 1181 - }, - "A destructuring declaration must have an initializer.": { - "category": "Error", - "code": 1182 - }, - "An implementation cannot be declared in ambient contexts.": { - "category": "Error", - "code": 1183 - }, - "Modifiers cannot appear here.": { - "category": "Error", - "code": 1184 - }, - "Merge conflict marker encountered.": { - "category": "Error", - "code": 1185 - }, - "A rest element cannot have an initializer.": { - "category": "Error", - "code": 1186 - }, - "A parameter property may not be declared using a binding pattern.": { - "category": "Error", - "code": 1187 - }, - "Only a single variable declaration is allowed in a 'for...of' statement.": { - "category": "Error", - "code": 1188 - }, - "The variable declaration of a 'for...in' statement cannot have an initializer.": { - "category": "Error", - "code": 1189 - }, - "The variable declaration of a 'for...of' statement cannot have an initializer.": { - "category": "Error", - "code": 1190 - }, - "An import declaration cannot have modifiers.": { - "category": "Error", - "code": 1191 - }, - "Module '{0}' has no default export.": { - "category": "Error", - "code": 1192 - }, - "An export declaration cannot have modifiers.": { - "category": "Error", - "code": 1193 - }, - "Export declarations are not permitted in a namespace.": { - "category": "Error", - "code": 1194 - }, - "'export *' does not re-export a default.": { - "category": "Error", - "code": 1195 - }, - "Catch clause variable type annotation must be 'any' or 'unknown' if specified.": { - "category": "Error", - "code": 1196 - }, - "Catch clause variable cannot have an initializer.": { - "category": "Error", - "code": 1197 - }, - "An extended Unicode escape value must be between 0x0 and 0x10FFFF inclusive.": { - "category": "Error", - "code": 1198 - }, - "Unterminated Unicode escape sequence.": { - "category": "Error", - "code": 1199 - }, - "Line terminator not permitted before arrow.": { - "category": "Error", - "code": 1200 - }, - "Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from \"mod\"', 'import {a} from \"mod\"', 'import d from \"mod\"', or another module format instead.": { - "category": "Error", - "code": 1202 - }, - "Export assignment cannot be used when targeting ECMAScript modules. Consider using 'export default' or another module format instead.": { - "category": "Error", - "code": 1203 - }, - "Re-exporting a type when '{0}' is enabled requires using 'export type'.": { - "category": "Error", - "code": 1205 - }, - "Decorators are not valid here.": { - "category": "Error", - "code": 1206 - }, - "Decorators cannot be applied to multiple get/set accessors of the same name.": { - "category": "Error", - "code": 1207 - }, - "Invalid optional chain from new expression. Did you mean to call '{0}()'?": { - "category": "Error", - "code": 1209 - }, - "Code contained in a class is evaluated in JavaScript's strict mode which does not allow this use of '{0}'. For more information, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode.": { - "category": "Error", - "code": 1210 - }, - "A class declaration without the 'default' modifier must have a name.": { - "category": "Error", - "code": 1211 - }, - "Identifier expected. '{0}' is a reserved word in strict mode.": { - "category": "Error", - "code": 1212 - }, - "Identifier expected. '{0}' is a reserved word in strict mode. Class definitions are automatically in strict mode.": { - "category": "Error", - "code": 1213 - }, - "Identifier expected. '{0}' is a reserved word in strict mode. Modules are automatically in strict mode.": { - "category": "Error", - "code": 1214 - }, - "Invalid use of '{0}'. Modules are automatically in strict mode.": { - "category": "Error", - "code": 1215 - }, - "Identifier expected. '__esModule' is reserved as an exported marker when transforming ECMAScript modules.": { - "category": "Error", - "code": 1216 - }, - "Export assignment is not supported when '--module' flag is 'system'.": { - "category": "Error", - "code": 1218 - }, - "Generators are not allowed in an ambient context.": { - "category": "Error", - "code": 1221 - }, - "An overload signature cannot be declared as a generator.": { - "category": "Error", - "code": 1222 - }, - "'{0}' tag already specified.": { - "category": "Error", - "code": 1223 - }, - "Signature '{0}' must be a type predicate.": { - "category": "Error", - "code": 1224 - }, - "Cannot find parameter '{0}'.": { - "category": "Error", - "code": 1225 - }, - "Type predicate '{0}' is not assignable to '{1}'.": { - "category": "Error", - "code": 1226 - }, - "Parameter '{0}' is not in the same position as parameter '{1}'.": { - "category": "Error", - "code": 1227 - }, - "A type predicate is only allowed in return type position for functions and methods.": { - "category": "Error", - "code": 1228 - }, - "A type predicate cannot reference a rest parameter.": { - "category": "Error", - "code": 1229 - }, - "A type predicate cannot reference element '{0}' in a binding pattern.": { - "category": "Error", - "code": 1230 - }, - "An export assignment must be at the top level of a file or module declaration.": { - "category": "Error", - "code": 1231 - }, - "An import declaration can only be used at the top level of a namespace or module.": { - "category": "Error", - "code": 1232 - }, - "An export declaration can only be used at the top level of a namespace or module.": { - "category": "Error", - "code": 1233 - }, - "An ambient module declaration is only allowed at the top level in a file.": { - "category": "Error", - "code": 1234 - }, - "A namespace declaration is only allowed at the top level of a namespace or module.": { - "category": "Error", - "code": 1235 - }, - "The return type of a property decorator function must be either 'void' or 'any'.": { - "category": "Error", - "code": 1236 - }, - "The return type of a parameter decorator function must be either 'void' or 'any'.": { - "category": "Error", - "code": 1237 - }, - "Unable to resolve signature of class decorator when called as an expression.": { - "category": "Error", - "code": 1238 - }, - "Unable to resolve signature of parameter decorator when called as an expression.": { - "category": "Error", - "code": 1239 - }, - "Unable to resolve signature of property decorator when called as an expression.": { - "category": "Error", - "code": 1240 - }, - "Unable to resolve signature of method decorator when called as an expression.": { - "category": "Error", - "code": 1241 - }, - "'abstract' modifier can only appear on a class, method, or property declaration.": { - "category": "Error", - "code": 1242 - }, - "'{0}' modifier cannot be used with '{1}' modifier.": { - "category": "Error", - "code": 1243 - }, - "Abstract methods can only appear within an abstract class.": { - "category": "Error", - "code": 1244 - }, - "Method '{0}' cannot have an implementation because it is marked abstract.": { - "category": "Error", - "code": 1245 - }, - "An interface property cannot have an initializer.": { - "category": "Error", - "code": 1246 - }, - "A type literal property cannot have an initializer.": { - "category": "Error", - "code": 1247 - }, - "A class member cannot have the '{0}' keyword.": { - "category": "Error", - "code": 1248 - }, - "A decorator can only decorate a method implementation, not an overload.": { - "category": "Error", - "code": 1249 - }, - "Function declarations are not allowed inside blocks in strict mode when targeting 'ES5'.": { - "category": "Error", - "code": 1250 - }, - "Function declarations are not allowed inside blocks in strict mode when targeting 'ES5'. Class definitions are automatically in strict mode.": { - "category": "Error", - "code": 1251 - }, - "Function declarations are not allowed inside blocks in strict mode when targeting 'ES5'. Modules are automatically in strict mode.": { - "category": "Error", - "code": 1252 - }, - "Abstract properties can only appear within an abstract class.": { - "category": "Error", - "code": 1253 - }, - "A 'const' initializer in an ambient context must be a string or numeric literal or literal enum reference.": { - "category": "Error", - "code": 1254 - }, - "A definite assignment assertion '!' is not permitted in this context.": { - "category": "Error", - "code": 1255 - }, - "A required element cannot follow an optional element.": { - "category": "Error", - "code": 1257 - }, - "A default export must be at the top level of a file or module declaration.": { - "category": "Error", - "code": 1258 - }, - "Module '{0}' can only be default-imported using the '{1}' flag": { - "category": "Error", - "code": 1259 - }, - "Keywords cannot contain escape characters.": { - "category": "Error", - "code": 1260 - }, - "Already included file name '{0}' differs from file name '{1}' only in casing.": { - "category": "Error", - "code": 1261 - }, - "Identifier expected. '{0}' is a reserved word at the top-level of a module.": { - "category": "Error", - "code": 1262 - }, - "Declarations with initializers cannot also have definite assignment assertions.": { - "category": "Error", - "code": 1263 - }, - "Declarations with definite assignment assertions must also have type annotations.": { - "category": "Error", - "code": 1264 - }, - "A rest element cannot follow another rest element.": { - "category": "Error", - "code": 1265 - }, - "An optional element cannot follow a rest element.": { - "category": "Error", - "code": 1266 - }, - "Property '{0}' cannot have an initializer because it is marked abstract.": { - "category": "Error", - "code": 1267 - }, - "An index signature parameter type must be 'string', 'number', 'symbol', or a template literal type.": { - "category": "Error", - "code": 1268 - }, - "Cannot use 'export import' on a type or type-only namespace when '{0}' is enabled.": { - "category": "Error", - "code": 1269 - }, - "Decorator function return type '{0}' is not assignable to type '{1}'.": { - "category": "Error", - "code": 1270 - }, - "Decorator function return type is '{0}' but is expected to be 'void' or 'any'.": { - "category": "Error", - "code": 1271 - }, - "A type referenced in a decorated signature must be imported with 'import type' or a namespace import when 'isolatedModules' and 'emitDecoratorMetadata' are enabled.": { - "category": "Error", - "code": 1272 - }, - "'{0}' modifier cannot appear on a type parameter": { - "category": "Error", - "code": 1273 - }, - "'{0}' modifier can only appear on a type parameter of a class, interface or type alias": { - "category": "Error", - "code": 1274 - }, - "'accessor' modifier can only appear on a property declaration.": { - "category": "Error", - "code": 1275 - }, - "An 'accessor' property cannot be declared optional.": { - "category": "Error", - "code": 1276 - }, - "'{0}' modifier can only appear on a type parameter of a function, method or class": { - "category": "Error", - "code": 1277 - }, - "The runtime will invoke the decorator with {1} arguments, but the decorator expects {0}.": { - "category": "Error", - "code": 1278 - }, - "The runtime will invoke the decorator with {1} arguments, but the decorator expects at least {0}.": { - "category": "Error", - "code": 1279 - }, - "Namespaces are not allowed in global script files when '{0}' is enabled. If this file is not intended to be a global script, set 'moduleDetection' to 'force' or add an empty 'export {}' statement.": { - "category": "Error", - "code": 1280 - }, - "Cannot access '{0}' from another file without qualification when '{1}' is enabled. Use '{2}' instead.": { - "category": "Error", - "code": 1281 - }, - "An 'export =' declaration must reference a value when 'verbatimModuleSyntax' is enabled, but '{0}' only refers to a type.": { - "category": "Error", - "code": 1282 - }, - "An 'export =' declaration must reference a real value when 'verbatimModuleSyntax' is enabled, but '{0}' resolves to a type-only declaration.": { - "category": "Error", - "code": 1283 - }, - "An 'export default' must reference a value when 'verbatimModuleSyntax' is enabled, but '{0}' only refers to a type.": { - "category": "Error", - "code": 1284 - }, - "An 'export default' must reference a real value when 'verbatimModuleSyntax' is enabled, but '{0}' resolves to a type-only declaration.": { - "category": "Error", - "code": 1285 - }, - "ECMAScript imports and exports cannot be written in a CommonJS file under 'verbatimModuleSyntax'.": { - "category": "Error", - "code": 1286 - }, - "A top-level 'export' modifier cannot be used on value declarations in a CommonJS module when 'verbatimModuleSyntax' is enabled.": { - "category": "Error", - "code": 1287 - }, - "An import alias cannot resolve to a type or type-only declaration when 'verbatimModuleSyntax' is enabled.": { - "category": "Error", - "code": 1288 - }, - "'{0}' resolves to a type-only declaration and must be marked type-only in this file before re-exporting when '{1}' is enabled. Consider using 'import type' where '{0}' is imported.": { - "category": "Error", - "code": 1289 - }, - "'{0}' resolves to a type-only declaration and must be marked type-only in this file before re-exporting when '{1}' is enabled. Consider using 'export type { {0} as default }'.": { - "category": "Error", - "code": 1290 - }, - "'{0}' resolves to a type and must be marked type-only in this file before re-exporting when '{1}' is enabled. Consider using 'import type' where '{0}' is imported.": { - "category": "Error", - "code": 1291 - }, - "'{0}' resolves to a type and must be marked type-only in this file before re-exporting when '{1}' is enabled. Consider using 'export type { {0} as default }'.": { - "category": "Error", - "code": 1292 - }, - "ECMAScript module syntax is not allowed in a CommonJS module when 'module' is set to 'preserve'.": { - "category": "Error", - "code": 1293 - }, - "This syntax is not allowed when 'erasableSyntaxOnly' is enabled.": { - "category": "Error", - "code": 1294 - }, - "ECMAScript imports and exports cannot be written in a CommonJS file under 'verbatimModuleSyntax'. Adjust the 'type' field in the nearest 'package.json' to make this file an ECMAScript module, or adjust your 'verbatimModuleSyntax', 'module', and 'moduleResolution' settings in TypeScript.": { - "category": "Error", - "code": 1295 - }, - "'with' statements are not allowed in an async function block.": { - "category": "Error", - "code": 1300 - }, - "'await' expressions are only allowed within async functions and at the top levels of modules.": { - "category": "Error", - "code": 1308 - }, - "The current file is a CommonJS module and cannot use 'await' at the top level.": { - "category": "Error", - "code": 1309 - }, - "Did you mean to use a ':'? An '=' can only follow a property name when the containing object literal is part of a destructuring pattern.": { - "category": "Error", - "code": 1312 - }, - "The body of an 'if' statement cannot be the empty statement.": { - "category": "Error", - "code": 1313 - }, - "Global module exports may only appear in module files.": { - "category": "Error", - "code": 1314 - }, - "Global module exports may only appear in declaration files.": { - "category": "Error", - "code": 1315 - }, - "Global module exports may only appear at top level.": { - "category": "Error", - "code": 1316 - }, - "A parameter property cannot be declared using a rest parameter.": { - "category": "Error", - "code": 1317 - }, - "An abstract accessor cannot have an implementation.": { - "category": "Error", - "code": 1318 - }, - "A default export can only be used in an ECMAScript-style module.": { - "category": "Error", - "code": 1319 - }, - "Type of 'await' operand must either be a valid promise or must not contain a callable 'then' member.": { - "category": "Error", - "code": 1320 - }, - "Type of 'yield' operand in an async generator must either be a valid promise or must not contain a callable 'then' member.": { - "category": "Error", - "code": 1321 - }, - "Type of iterated elements of a 'yield*' operand must either be a valid promise or must not contain a callable 'then' member.": { - "category": "Error", - "code": 1322 - }, - "Dynamic imports are only supported when the '--module' flag is set to 'es2020', 'es2022', 'esnext', 'commonjs', 'amd', 'system', 'umd', 'node16', 'node18', 'node20', or 'nodenext'.": { - "category": "Error", - "code": 1323 - }, - "Dynamic imports only support a second argument when the '--module' option is set to 'esnext', 'node16', 'node18', 'node20', 'nodenext', or 'preserve'.": { - "category": "Error", - "code": 1324 - }, - "Argument of dynamic import cannot be spread element.": { - "category": "Error", - "code": 1325 - }, - "This use of 'import' is invalid. 'import()' calls can be written, but they must have parentheses and cannot have type arguments.": { - "category": "Error", - "code": 1326 - }, - "String literal with double quotes expected.": { - "category": "Error", - "code": 1327 - }, - "Property value can only be string literal, numeric literal, 'true', 'false', 'null', object literal or array literal.": { - "category": "Error", - "code": 1328 - }, - "'{0}' accepts too few arguments to be used as a decorator here. Did you mean to call it first and write '@{0}()'?": { - "category": "Error", - "code": 1329 - }, - "A property of an interface or type literal whose type is a 'unique symbol' type must be 'readonly'.": { - "category": "Error", - "code": 1330 - }, - "A property of a class whose type is a 'unique symbol' type must be both 'static' and 'readonly'.": { - "category": "Error", - "code": 1331 - }, - "A variable whose type is a 'unique symbol' type must be 'const'.": { - "category": "Error", - "code": 1332 - }, - "'unique symbol' types may not be used on a variable declaration with a binding name.": { - "category": "Error", - "code": 1333 - }, - "'unique symbol' types are only allowed on variables in a variable statement.": { - "category": "Error", - "code": 1334 - }, - "'unique symbol' types are not allowed here.": { - "category": "Error", - "code": 1335 - }, - "An index signature parameter type cannot be a literal type or generic type. Consider using a mapped object type instead.": { - "category": "Error", - "code": 1337 - }, - "'infer' declarations are only permitted in the 'extends' clause of a conditional type.": { - "category": "Error", - "code": 1338 - }, - "Module '{0}' does not refer to a value, but is used as a value here.": { - "category": "Error", - "code": 1339 - }, - "Module '{0}' does not refer to a type, but is used as a type here. Did you mean 'typeof import('{0}')'?": { - "category": "Error", - "code": 1340 - }, - "Class constructor may not be an accessor.": { - "category": "Error", - "code": 1341 - }, - "The 'import.meta' meta-property is only allowed when the '--module' option is 'es2020', 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', or 'nodenext'.": { - "category": "Error", - "code": 1343 - }, - "A label is not allowed here.": { - "category": "Error", - "code": 1344 - }, - "An expression of type 'void' cannot be tested for truthiness.": { - "category": "Error", - "code": 1345 - }, - "This parameter is not allowed with 'use strict' directive.": { - "category": "Error", - "code": 1346 - }, - "'use strict' directive cannot be used with non-simple parameter list.": { - "category": "Error", - "code": 1347 - }, - "Non-simple parameter declared here.": { - "category": "Error", - "code": 1348 - }, - "'use strict' directive used here.": { - "category": "Error", - "code": 1349 - }, - "Print the final configuration instead of building.": { - "category": "Message", - "code": 1350 - }, - "An identifier or keyword cannot immediately follow a numeric literal.": { - "category": "Error", - "code": 1351 - }, - "A bigint literal cannot use exponential notation.": { - "category": "Error", - "code": 1352 - }, - "A bigint literal must be an integer.": { - "category": "Error", - "code": 1353 - }, - "'readonly' type modifier is only permitted on array and tuple literal types.": { - "category": "Error", - "code": 1354 - }, - "A 'const' assertion can only be applied to references to enum members, or string, number, boolean, array, or object literals.": { - "category": "Error", - "code": 1355 - }, - "Did you mean to mark this function as 'async'?": { - "category": "Error", - "code": 1356 - }, - "An enum member name must be followed by a ',', '=', or '}'.": { - "category": "Error", - "code": 1357 - }, - "Tagged template expressions are not permitted in an optional chain.": { - "category": "Error", - "code": 1358 - }, - "Identifier expected. '{0}' is a reserved word that cannot be used here.": { - "category": "Error", - "code": 1359 - }, - "Type '{0}' does not satisfy the expected type '{1}'.": { - "category": "Error", - "code": 1360 - }, - "'{0}' cannot be used as a value because it was imported using 'import type'.": { - "category": "Error", - "code": 1361 - }, - "'{0}' cannot be used as a value because it was exported using 'export type'.": { - "category": "Error", - "code": 1362 - }, - "A type-only import can specify a default import or named bindings, but not both.": { - "category": "Error", - "code": 1363 - }, - "Convert to type-only export": { - "category": "Message", - "code": 1364 - }, - "Convert all re-exported types to type-only exports": { - "category": "Message", - "code": 1365 - }, - "Split into two separate import declarations": { - "category": "Message", - "code": 1366 - }, - "Split all invalid type-only imports": { - "category": "Message", - "code": 1367 - }, - "Class constructor may not be a generator.": { - "category": "Error", - "code": 1368 - }, - "Did you mean '{0}'?": { - "category": "Message", - "code": 1369 - }, - "'await' expressions are only allowed at the top level of a file when that file is a module, but this file has no imports or exports. Consider adding an empty 'export {}' to make this file a module.": { - "category": "Error", - "code": 1375 - }, - "'{0}' was imported here.": { - "category": "Message", - "code": 1376 - }, - "'{0}' was exported here.": { - "category": "Message", - "code": 1377 - }, - "Top-level 'await' expressions are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.": { - "category": "Error", - "code": 1378 - }, - "An import alias cannot reference a declaration that was exported using 'export type'.": { - "category": "Error", - "code": 1379 - }, - "An import alias cannot reference a declaration that was imported using 'import type'.": { - "category": "Error", - "code": 1380 - }, - "Unexpected token. Did you mean `{'}'}` or `}`?": { - "category": "Error", - "code": 1381 - }, - "Unexpected token. Did you mean `{'>'}` or `>`?": { - "category": "Error", - "code": 1382 - }, - "Function type notation must be parenthesized when used in a union type.": { - "category": "Error", - "code": 1385 - }, - "Constructor type notation must be parenthesized when used in a union type.": { - "category": "Error", - "code": 1386 - }, - "Function type notation must be parenthesized when used in an intersection type.": { - "category": "Error", - "code": 1387 - }, - "Constructor type notation must be parenthesized when used in an intersection type.": { - "category": "Error", - "code": 1388 - }, - "'{0}' is not allowed as a variable declaration name.": { - "category": "Error", - "code": 1389 - }, - "'{0}' is not allowed as a parameter name.": { - "category": "Error", - "code": 1390 - }, - "An import alias cannot use 'import type'": { - "category": "Error", - "code": 1392 - }, - "Imported via {0} from file '{1}'": { - "category": "Message", - "code": 1393 - }, - "Imported via {0} from file '{1}' with packageId '{2}'": { - "category": "Message", - "code": 1394 - }, - "Imported via {0} from file '{1}' to import 'importHelpers' as specified in compilerOptions": { - "category": "Message", - "code": 1395 - }, - "Imported via {0} from file '{1}' with packageId '{2}' to import 'importHelpers' as specified in compilerOptions": { - "category": "Message", - "code": 1396 - }, - "Imported via {0} from file '{1}' to import 'jsx' and 'jsxs' factory functions": { - "category": "Message", - "code": 1397 - }, - "Imported via {0} from file '{1}' with packageId '{2}' to import 'jsx' and 'jsxs' factory functions": { - "category": "Message", - "code": 1398 - }, - "File is included via import here.": { - "category": "Message", - "code": 1399 - }, - "Referenced via '{0}' from file '{1}'": { - "category": "Message", - "code": 1400 - }, - "File is included via reference here.": { - "category": "Message", - "code": 1401 - }, - "Type library referenced via '{0}' from file '{1}'": { - "category": "Message", - "code": 1402 - }, - "Type library referenced via '{0}' from file '{1}' with packageId '{2}'": { - "category": "Message", - "code": 1403 - }, - "File is included via type library reference here.": { - "category": "Message", - "code": 1404 - }, - "Library referenced via '{0}' from file '{1}'": { - "category": "Message", - "code": 1405 - }, - "File is included via library reference here.": { - "category": "Message", - "code": 1406 - }, - "Matched by include pattern '{0}' in '{1}'": { - "category": "Message", - "code": 1407 - }, - "File is matched by include pattern specified here.": { - "category": "Message", - "code": 1408 - }, - "Part of 'files' list in tsconfig.json": { - "category": "Message", - "code": 1409 - }, - "File is matched by 'files' list specified here.": { - "category": "Message", - "code": 1410 - }, - "Output from referenced project '{0}' included because '{1}' specified": { - "category": "Message", - "code": 1411 - }, - "Output from referenced project '{0}' included because '--module' is specified as 'none'": { - "category": "Message", - "code": 1412 - }, - "File is output from referenced project specified here.": { - "category": "Message", - "code": 1413 - }, - "Source from referenced project '{0}' included because '{1}' specified": { - "category": "Message", - "code": 1414 - }, - "Source from referenced project '{0}' included because '--module' is specified as 'none'": { - "category": "Message", - "code": 1415 - }, - "File is source from referenced project specified here.": { - "category": "Message", - "code": 1416 - }, - "Entry point of type library '{0}' specified in compilerOptions": { - "category": "Message", - "code": 1417 - }, - "Entry point of type library '{0}' specified in compilerOptions with packageId '{1}'": { - "category": "Message", - "code": 1418 - }, - "File is entry point of type library specified here.": { - "category": "Message", - "code": 1419 - }, - "Entry point for implicit type library '{0}'": { - "category": "Message", - "code": 1420 - }, - "Entry point for implicit type library '{0}' with packageId '{1}'": { - "category": "Message", - "code": 1421 - }, - "Library '{0}' specified in compilerOptions": { - "category": "Message", - "code": 1422 - }, - "File is library specified here.": { - "category": "Message", - "code": 1423 - }, - "Default library": { - "category": "Message", - "code": 1424 - }, - "Default library for target '{0}'": { - "category": "Message", - "code": 1425 - }, - "File is default library for target specified here.": { - "category": "Message", - "code": 1426 - }, - "Root file specified for compilation": { - "category": "Message", - "code": 1427 - }, - "File is output of project reference source '{0}'": { - "category": "Message", - "code": 1428 - }, - "File redirects to file '{0}'": { - "category": "Message", - "code": 1429 - }, - "The file is in the program because:": { - "category": "Message", - "code": 1430 - }, - "'for await' loops are only allowed at the top level of a file when that file is a module, but this file has no imports or exports. Consider adding an empty 'export {}' to make this file a module.": { - "category": "Error", - "code": 1431 - }, - "Top-level 'for await' loops are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.": { - "category": "Error", - "code": 1432 - }, - "Neither decorators nor modifiers may be applied to 'this' parameters.": { - "category": "Error", - "code": 1433 - }, - "Unexpected keyword or identifier.": { - "category": "Error", - "code": 1434 - }, - "Unknown keyword or identifier. Did you mean '{0}'?": { - "category": "Error", - "code": 1435 - }, - "Decorators must precede the name and all keywords of property declarations.": { - "category": "Error", - "code": 1436 - }, - "Namespace must be given a name.": { - "category": "Error", - "code": 1437 - }, - "Interface must be given a name.": { - "category": "Error", - "code": 1438 - }, - "Type alias must be given a name.": { - "category": "Error", - "code": 1439 - }, - "Variable declaration not allowed at this location.": { - "category": "Error", - "code": 1440 - }, - "Cannot start a function call in a type annotation.": { - "category": "Error", - "code": 1441 - }, - "Expected '=' for property initializer.": { - "category": "Error", - "code": 1442 - }, - "Module declaration names may only use ' or \" quoted strings.": { - "category": "Error", - "code": 1443 - }, - "'{0}' resolves to a type-only declaration and must be re-exported using a type-only re-export when '{1}' is enabled.": { - "category": "Error", - "code": 1448 - }, - "Preserve unused imported values in the JavaScript output that would otherwise be removed.": { - "category": "Message", - "code": 1449 - }, - "Dynamic imports can only accept a module specifier and an optional set of attributes as arguments": { - "category": "Message", - "code": 1450 - }, - "Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression": { - "category": "Error", - "code": 1451 - }, - "`resolution-mode` should be either `require` or `import`.": { - "category": "Error", - "code": 1453 - }, - "`resolution-mode` can only be set for type-only imports.": { - "category": "Error", - "code": 1454 - }, - "`resolution-mode` is the only valid key for type import assertions.": { - "category": "Error", - "code": 1455 - }, - "Type import assertions should have exactly one key - `resolution-mode` - with value `import` or `require`.": { - "category": "Error", - "code": 1456 - }, - "Matched by default include pattern '**/*'": { - "category": "Message", - "code": 1457 - }, - "File is ECMAScript module because '{0}' has field \"type\" with value \"module\"": { - "category": "Message", - "code": 1458 - }, - "File is CommonJS module because '{0}' has field \"type\" whose value is not \"module\"": { - "category": "Message", - "code": 1459 - }, - "File is CommonJS module because '{0}' does not have field \"type\"": { - "category": "Message", - "code": 1460 - }, - "File is CommonJS module because 'package.json' was not found": { - "category": "Message", - "code": 1461 - }, - "'resolution-mode' is the only valid key for type import attributes.": { - "category": "Error", - "code": 1463 - }, - "Type import attributes should have exactly one key - 'resolution-mode' - with value 'import' or 'require'.": { - "category": "Error", - "code": 1464 - }, - - "The 'import.meta' meta-property is not allowed in files which will build into CommonJS output.": { - "category": "Error", - "code": 1470 - }, - "Module '{0}' cannot be imported using this construct. The specifier only resolves to an ES module, which cannot be imported with 'require'. Use an ECMAScript import instead.": { - "category": "Error", - "code": 1471 - }, - "'catch' or 'finally' expected.": { - "category": "Error", - "code": 1472 - }, - "An import declaration can only be used at the top level of a module.": { - "category": "Error", - "code": 1473 - }, - "An export declaration can only be used at the top level of a module.": { - "category": "Error", - "code": 1474 - }, - "Control what method is used to detect module-format JS files.": { - "category": "Message", - "code": 1475 - }, - "\"auto\": Treat files with imports, exports, import.meta, jsx (with jsx: react-jsx), or esm format (with module: node16+) as modules.": { - "category": "Message", - "code": 1476 - }, - "An instantiation expression cannot be followed by a property access.": { - "category": "Error", - "code": 1477 - }, - "Identifier or string literal expected.": { - "category": "Error", - "code": 1478 - }, - "The current file is a CommonJS module whose imports will produce 'require' calls; however, the referenced file is an ECMAScript module and cannot be imported with 'require'. Consider writing a dynamic 'import(\"{0}\")' call instead.": { - "category": "Error", - "code": 1479 - }, - "To convert this file to an ECMAScript module, change its file extension to '{0}' or create a local package.json file with `{ \"type\": \"module\" }`.": { - "category": "Message", - "code": 1480 - }, - "To convert this file to an ECMAScript module, change its file extension to '{0}', or add the field `\"type\": \"module\"` to '{1}'.": { - "category": "Message", - "code": 1481 - }, - "To convert this file to an ECMAScript module, add the field `\"type\": \"module\"` to '{0}'.": { - "category": "Message", - "code": 1482 - }, - "To convert this file to an ECMAScript module, create a local package.json file with `{ \"type\": \"module\" }`.": { - "category": "Message", - "code": 1483 - }, - "'{0}' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.": { - "category": "Error", - "code": 1484 - }, - "'{0}' resolves to a type-only declaration and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.": { - "category": "Error", - "code": 1485 - }, - "Decorator used before 'export' here.": { - "category": "Error", - "code": 1486 - }, - "Octal escape sequences are not allowed. Use the syntax '{0}'.": { - "category": "Error", - "code": 1487 - }, - "Escape sequence '{0}' is not allowed.": { - "category": "Error", - "code": 1488 - }, - "Decimals with leading zeros are not allowed.": { - "category": "Error", - "code": 1489 - }, - "File appears to be binary.": { - "category": "Error", - "code": 1490 - }, - "'{0}' modifier cannot appear on a 'using' declaration.": { - "category": "Error", - "code": 1491 - }, - "'{0}' declarations may not have binding patterns.": { - "category": "Error", - "code": 1492 - }, - "The left-hand side of a 'for...in' statement cannot be a 'using' declaration.": { - "category": "Error", - "code": 1493 - }, - "The left-hand side of a 'for...in' statement cannot be an 'await using' declaration.": { - "category": "Error", - "code": 1494 - }, - "'{0}' modifier cannot appear on an 'await using' declaration.": { - "category": "Error", - "code": 1495 - }, - "Identifier, string literal, or number literal expected.": { - "category": "Error", - "code": 1496 - }, - "Expression must be enclosed in parentheses to be used as a decorator.": { - "category": "Error", - "code": 1497 - }, - "Invalid syntax in decorator.": { - "category": "Error", - "code": 1498 - }, - "Unknown regular expression flag.": { - "category": "Error", - "code": 1499 - }, - "Duplicate regular expression flag.": { - "category": "Error", - "code": 1500 - }, - "This regular expression flag is only available when targeting '{0}' or later.": { - "category": "Error", - "code": 1501 - }, - "The Unicode (u) flag and the Unicode Sets (v) flag cannot be set simultaneously.": { - "category": "Error", - "code": 1502 - }, - "Named capturing groups are only available when targeting 'ES2018' or later.": { - "category": "Error", - "code": 1503 - }, - "Subpattern flags must be present when there is a minus sign.": { - "category": "Error", - "code": 1504 - }, - "Incomplete quantifier. Digit expected.": { - "category": "Error", - "code": 1505 - }, - "Numbers out of order in quantifier.": { - "category": "Error", - "code": 1506 - }, - "There is nothing available for repetition.": { - "category": "Error", - "code": 1507 - }, - "Unexpected '{0}'. Did you mean to escape it with backslash?": { - "category": "Error", - "code": 1508 - }, - "This regular expression flag cannot be toggled within a subpattern.": { - "category": "Error", - "code": 1509 - }, - "'\\k' must be followed by a capturing group name enclosed in angle brackets.": { - "category": "Error", - "code": 1510 - }, - "'\\q' is only available inside character class.": { - "category": "Error", - "code": 1511 - }, - "'\\c' must be followed by an ASCII letter.": { - "category": "Error", - "code": 1512 - }, - "Undetermined character escape.": { - "category": "Error", - "code": 1513 - }, - "Expected a capturing group name.": { - "category": "Error", - "code": 1514 - }, - "Named capturing groups with the same name must be mutually exclusive to each other.": { - "category": "Error", - "code": 1515 - }, - "A character class range must not be bounded by another character class.": { - "category": "Error", - "code": 1516 - }, - "Range out of order in character class.": { - "category": "Error", - "code": 1517 - }, - "Anything that would possibly match more than a single character is invalid inside a negated character class.": { - "category": "Error", - "code": 1518 - }, - "Operators must not be mixed within a character class. Wrap it in a nested class instead.": { - "category": "Error", - "code": 1519 - }, - "Expected a class set operand.": { - "category": "Error", - "code": 1520 - }, - "'\\q' must be followed by string alternatives enclosed in braces.": { - "category": "Error", - "code": 1521 - }, - "A character class must not contain a reserved double punctuator. Did you mean to escape it with backslash?": { - "category": "Error", - "code": 1522 - }, - "Expected a Unicode property name.": { - "category": "Error", - "code": 1523 - }, - "Unknown Unicode property name.": { - "category": "Error", - "code": 1524 - }, - "Expected a Unicode property value.": { - "category": "Error", - "code": 1525 - }, - "Unknown Unicode property value.": { - "category": "Error", - "code": 1526 - }, - "Expected a Unicode property name or value.": { - "category": "Error", - "code": 1527 - }, - "Any Unicode property that would possibly match more than a single character is only available when the Unicode Sets (v) flag is set.": { - "category": "Error", - "code": 1528 - }, - "Unknown Unicode property name or value.": { - "category": "Error", - "code": 1529 - }, - "Unicode property value expressions are only available when the Unicode (u) flag or the Unicode Sets (v) flag is set.": { - "category": "Error", - "code": 1530 - }, - "'\\{0}' must be followed by a Unicode property value expression enclosed in braces.": { - "category": "Error", - "code": 1531 - }, - "There is no capturing group named '{0}' in this regular expression.": { - "category": "Error", - "code": 1532 - }, - "This backreference refers to a group that does not exist. There are only {0} capturing groups in this regular expression.": { - "category": "Error", - "code": 1533 - }, - "This backreference refers to a group that does not exist. There are no capturing groups in this regular expression.": { - "category": "Error", - "code": 1534 - }, - "This character cannot be escaped in a regular expression.": { - "category": "Error", - "code": 1535 - }, - "Octal escape sequences and backreferences are not allowed in a character class. If this was intended as an escape sequence, use the syntax '{0}' instead.": { - "category": "Error", - "code": 1536 - }, - "Decimal escape sequences and backreferences are not allowed in a character class.": { - "category": "Error", - "code": 1537 - }, - "Unicode escape sequences are only available when the Unicode (u) flag or the Unicode Sets (v) flag is set.": { - "category": "Error", - "code": 1538 - }, - "A 'bigint' literal cannot be used as a property name.": { - "category": "Error", - "code": 1539 - }, - "A 'namespace' declaration should not be declared using the 'module' keyword. Please use the 'namespace' keyword instead.": { - "category": "Error", - "code": 1540 - }, - "Type-only import of an ECMAScript module from a CommonJS module must have a 'resolution-mode' attribute.": { - "category": "Error", - "code": 1541 - }, - "Type import of an ECMAScript module from a CommonJS module must have a 'resolution-mode' attribute.": { - "category": "Error", - "code": 1542 - }, - "Importing a JSON file into an ECMAScript module requires a 'type: \"json\"' import attribute when 'module' is set to '{0}'.": { - "category": "Error", - "code": 1543 - }, - "Named imports from a JSON file into an ECMAScript module are not allowed when 'module' is set to '{0}'.": { - "category": "Error", - "code": 1544 - }, - "'using' declarations are not allowed in ambient contexts.": { - "category": "Error", - "code": 1545 - }, - "'await using' declarations are not allowed in ambient contexts.": { - "category": "Error", - "code": 1546 - }, - "'using' declarations are not allowed in 'case' or 'default' clauses unless contained within a block.": { - "category": "Error", - "code": 1547 - }, - "'await using' declarations are not allowed in 'case' or 'default' clauses unless contained within a block.": { - "category": "Error", - "code": 1548 - }, - "Ignore the tsconfig found and build with commandline options and files.": { - "category": "Message", - "code": 1549 - }, - - "The types of '{0}' are incompatible between these types.": { - "category": "Error", - "code": 2200 - }, - "The types returned by '{0}' are incompatible between these types.": { - "category": "Error", - "code": 2201 - }, - "Call signature return types '{0}' and '{1}' are incompatible.": { - "category": "Error", - "code": 2202, - "elidedInCompatabilityPyramid": true - }, - "Construct signature return types '{0}' and '{1}' are incompatible.": { - "category": "Error", - "code": 2203, - "elidedInCompatabilityPyramid": true - }, - "Call signatures with no arguments have incompatible return types '{0}' and '{1}'.": { - "category": "Error", - "code": 2204, - "elidedInCompatabilityPyramid": true - }, - "Construct signatures with no arguments have incompatible return types '{0}' and '{1}'.": { - "category": "Error", - "code": 2205, - "elidedInCompatabilityPyramid": true - }, - "The 'type' modifier cannot be used on a named import when 'import type' is used on its import statement.": { - "category": "Error", - "code": 2206 - }, - "The 'type' modifier cannot be used on a named export when 'export type' is used on its export statement.": { - "category": "Error", - "code": 2207 - }, - "This type parameter might need an `extends {0}` constraint.": { - "category": "Error", - "code": 2208 - }, - - "The project root is ambiguous, but is required to resolve export map entry '{0}' in file '{1}'. Supply the `rootDir` compiler option to disambiguate.": { - "category": "Error", - "code": 2209 - }, - "The project root is ambiguous, but is required to resolve import map entry '{0}' in file '{1}'. Supply the `rootDir` compiler option to disambiguate.": { - "category": "Error", - "code": 2210 - }, - "Add `extends` constraint.": { - "category": "Message", - "code": 2211 - }, - "Add `extends` constraint to all type parameters": { - "category": "Message", - "code": 2212 - }, - - "Duplicate identifier '{0}'.": { - "category": "Error", - "code": 2300 - }, - "Initializer of instance member variable '{0}' cannot reference identifier '{1}' declared in the constructor.": { - "category": "Error", - "code": 2301 - }, - "Static members cannot reference class type parameters.": { - "category": "Error", - "code": 2302 - }, - "Circular definition of import alias '{0}'.": { - "category": "Error", - "code": 2303 - }, - "Cannot find name '{0}'.": { - "category": "Error", - "code": 2304 - }, - "Module '{0}' has no exported member '{1}'.": { - "category": "Error", - "code": 2305 - }, - "File '{0}' is not a module.": { - "category": "Error", - "code": 2306 - }, - "Cannot find module '{0}' or its corresponding type declarations.": { - "category": "Error", - "code": 2307 - }, - "Module {0} has already exported a member named '{1}'. Consider explicitly re-exporting to resolve the ambiguity.": { - "category": "Error", - "code": 2308 - }, - "An export assignment cannot be used in a module with other exported elements.": { - "category": "Error", - "code": 2309 - }, - "Type '{0}' recursively references itself as a base type.": { - "category": "Error", - "code": 2310 - }, - "Cannot find name '{0}'. Did you mean to write this in an async function?": { - "category": "Error", - "code": 2311 - }, - "An interface can only extend an object type or intersection of object types with statically known members.": { - "category": "Error", - "code": 2312 - }, - "Type parameter '{0}' has a circular constraint.": { - "category": "Error", - "code": 2313 - }, - "Generic type '{0}' requires {1} type argument(s).": { - "category": "Error", - "code": 2314 - }, - "Type '{0}' is not generic.": { - "category": "Error", - "code": 2315 - }, - "Global type '{0}' must be a class or interface type.": { - "category": "Error", - "code": 2316 - }, - "Global type '{0}' must have {1} type parameter(s).": { - "category": "Error", - "code": 2317 - }, - "Cannot find global type '{0}'.": { - "category": "Error", - "code": 2318 - }, - "Named property '{0}' of types '{1}' and '{2}' are not identical.": { - "category": "Error", - "code": 2319 - }, - "Interface '{0}' cannot simultaneously extend types '{1}' and '{2}'.": { - "category": "Error", - "code": 2320 - }, - "Excessive stack depth comparing types '{0}' and '{1}'.": { - "category": "Error", - "code": 2321 - }, - "Type '{0}' is not assignable to type '{1}'.": { - "category": "Error", - "code": 2322 - }, - "Cannot redeclare exported variable '{0}'.": { - "category": "Error", - "code": 2323 - }, - "Property '{0}' is missing in type '{1}'.": { - "category": "Error", - "code": 2324 - }, - "Property '{0}' is private in type '{1}' but not in type '{2}'.": { - "category": "Error", - "code": 2325 - }, - "Types of property '{0}' are incompatible.": { - "category": "Error", - "code": 2326 - }, - "Property '{0}' is optional in type '{1}' but required in type '{2}'.": { - "category": "Error", - "code": 2327 - }, - "Types of parameters '{0}' and '{1}' are incompatible.": { - "category": "Error", - "code": 2328 - }, - "Index signature for type '{0}' is missing in type '{1}'.": { - "category": "Error", - "code": 2329 - }, - "'{0}' and '{1}' index signatures are incompatible.": { - "category": "Error", - "code": 2330 - }, - "'this' cannot be referenced in a module or namespace body.": { - "category": "Error", - "code": 2331 - }, - "'this' cannot be referenced in current location.": { - "category": "Error", - "code": 2332 - }, - "'this' cannot be referenced in a static property initializer.": { - "category": "Error", - "code": 2334 - }, - "'super' can only be referenced in a derived class.": { - "category": "Error", - "code": 2335 - }, - "'super' cannot be referenced in constructor arguments.": { - "category": "Error", - "code": 2336 - }, - "Super calls are not permitted outside constructors or in nested functions inside constructors.": { - "category": "Error", - "code": 2337 - }, - "'super' property access is permitted only in a constructor, member function, or member accessor of a derived class.": { - "category": "Error", - "code": 2338 - }, - "Property '{0}' does not exist on type '{1}'.": { - "category": "Error", - "code": 2339 - }, - "Only public and protected methods of the base class are accessible via the 'super' keyword.": { - "category": "Error", - "code": 2340 - }, - "Property '{0}' is private and only accessible within class '{1}'.": { - "category": "Error", - "code": 2341 - }, - "This syntax requires an imported helper named '{1}' which does not exist in '{0}'. Consider upgrading your version of '{0}'.": { - "category": "Error", - "code": 2343 - }, - "Type '{0}' does not satisfy the constraint '{1}'.": { - "category": "Error", - "code": 2344 - }, - "Argument of type '{0}' is not assignable to parameter of type '{1}'.": { - "category": "Error", - "code": 2345 - }, - "Call target does not contain any signatures.": { - "category": "Error", - "code": 2346 - }, - "Untyped function calls may not accept type arguments.": { - "category": "Error", - "code": 2347 - }, - "Value of type '{0}' is not callable. Did you mean to include 'new'?": { - "category": "Error", - "code": 2348 - }, - "This expression is not callable.": { - "category": "Error", - "code": 2349 - }, - "Only a void function can be called with the 'new' keyword.": { - "category": "Error", - "code": 2350 - }, - "This expression is not constructable.": { - "category": "Error", - "code": 2351 - }, - "Conversion of type '{0}' to type '{1}' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.": { - "category": "Error", - "code": 2352 - }, - "Object literal may only specify known properties, and '{0}' does not exist in type '{1}'.": { - "category": "Error", - "code": 2353 - }, - "This syntax requires an imported helper but module '{0}' cannot be found.": { - "category": "Error", - "code": 2354 - }, - "A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value.": { - "category": "Error", - "code": 2355 - }, - "An arithmetic operand must be of type 'any', 'number', 'bigint' or an enum type.": { - "category": "Error", - "code": 2356 - }, - "The operand of an increment or decrement operator must be a variable or a property access.": { - "category": "Error", - "code": 2357 - }, - "The left-hand side of an 'instanceof' expression must be of type 'any', an object type or a type parameter.": { - "category": "Error", - "code": 2358 - }, - "The right-hand side of an 'instanceof' expression must be either of type 'any', a class, function, or other type assignable to the 'Function' interface type, or an object type with a 'Symbol.hasInstance' method.": { - "category": "Error", - "code": 2359 - }, - "The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.": { - "category": "Error", - "code": 2362 - }, - "The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.": { - "category": "Error", - "code": 2363 - }, - "The left-hand side of an assignment expression must be a variable or a property access.": { - "category": "Error", - "code": 2364 - }, - "Operator '{0}' cannot be applied to types '{1}' and '{2}'.": { - "category": "Error", - "code": 2365 - }, - "Function lacks ending return statement and return type does not include 'undefined'.": { - "category": "Error", - "code": 2366 - }, - "This comparison appears to be unintentional because the types '{0}' and '{1}' have no overlap.": { - "category": "Error", - "code": 2367 - }, - "Type parameter name cannot be '{0}'.": { - "category": "Error", - "code": 2368 - }, - "A parameter property is only allowed in a constructor implementation.": { - "category": "Error", - "code": 2369 - }, - "A rest parameter must be of an array type.": { - "category": "Error", - "code": 2370 - }, - "A parameter initializer is only allowed in a function or constructor implementation.": { - "category": "Error", - "code": 2371 - }, - "Parameter '{0}' cannot reference itself.": { - "category": "Error", - "code": 2372 - }, - "Parameter '{0}' cannot reference identifier '{1}' declared after it.": { - "category": "Error", - "code": 2373 - }, - "Duplicate index signature for type '{0}'.": { - "category": "Error", - "code": 2374 - }, - "Type '{0}' is not assignable to type '{1}' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.": { - "category": "Error", - "code": 2375 - }, - "A 'super' call must be the first statement in the constructor to refer to 'super' or 'this' when a derived class contains initialized properties, parameter properties, or private identifiers.": { - "category": "Error", - "code": 2376 - }, - "Constructors for derived classes must contain a 'super' call.": { - "category": "Error", - "code": 2377 - }, - "A 'get' accessor must return a value.": { - "category": "Error", - "code": 2378 - }, - "Argument of type '{0}' is not assignable to parameter of type '{1}' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.": { - "category": "Error", - "code": 2379 - }, - "Overload signatures must all be exported or non-exported.": { - "category": "Error", - "code": 2383 - }, - "Overload signatures must all be ambient or non-ambient.": { - "category": "Error", - "code": 2384 - }, - "Overload signatures must all be public, private or protected.": { - "category": "Error", - "code": 2385 - }, - "Overload signatures must all be optional or required.": { - "category": "Error", - "code": 2386 - }, - "Function overload must be static.": { - "category": "Error", - "code": 2387 - }, - "Function overload must not be static.": { - "category": "Error", - "code": 2388 - }, - "Function implementation name must be '{0}'.": { - "category": "Error", - "code": 2389 - }, - "Constructor implementation is missing.": { - "category": "Error", - "code": 2390 - }, - "Function implementation is missing or not immediately following the declaration.": { - "category": "Error", - "code": 2391 - }, - "Multiple constructor implementations are not allowed.": { - "category": "Error", - "code": 2392 - }, - "Duplicate function implementation.": { - "category": "Error", - "code": 2393 - }, - "This overload signature is not compatible with its implementation signature.": { - "category": "Error", - "code": 2394 - }, - "Individual declarations in merged declaration '{0}' must be all exported or all local.": { - "category": "Error", - "code": 2395 - }, - "Duplicate identifier 'arguments'. Compiler uses 'arguments' to initialize rest parameters.": { - "category": "Error", - "code": 2396 - }, - "Declaration name conflicts with built-in global identifier '{0}'.": { - "category": "Error", - "code": 2397 - }, - "'constructor' cannot be used as a parameter property name.": { - "category": "Error", - "code": 2398 - }, - "Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.": { - "category": "Error", - "code": 2399 - }, - "Expression resolves to variable declaration '_this' that compiler uses to capture 'this' reference.": { - "category": "Error", - "code": 2400 - }, - "A 'super' call must be a root-level statement within a constructor of a derived class that contains initialized properties, parameter properties, or private identifiers.": { - "category": "Error", - "code": 2401 - }, - "Expression resolves to '_super' that compiler uses to capture base class reference.": { - "category": "Error", - "code": 2402 - }, - "Subsequent variable declarations must have the same type. Variable '{0}' must be of type '{1}', but here has type '{2}'.": { - "category": "Error", - "code": 2403 - }, - "The left-hand side of a 'for...in' statement cannot use a type annotation.": { - "category": "Error", - "code": 2404 - }, - "The left-hand side of a 'for...in' statement must be of type 'string' or 'any'.": { - "category": "Error", - "code": 2405 - }, - "The left-hand side of a 'for...in' statement must be a variable or a property access.": { - "category": "Error", - "code": 2406 - }, - "The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type '{0}'.": { - "category": "Error", - "code": 2407 - }, - "Setters cannot return a value.": { - "category": "Error", - "code": 2408 - }, - "Return type of constructor signature must be assignable to the instance type of the class.": { - "category": "Error", - "code": 2409 - }, - "The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.": { - "category": "Error", - "code": 2410 - }, - "Type '{0}' is not assignable to type '{1}' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the type of the target.": { - "category": "Error", - "code": 2412 - }, - "Property '{0}' of type '{1}' is not assignable to '{2}' index type '{3}'.": { - "category": "Error", - "code": 2411 - }, - "'{0}' index type '{1}' is not assignable to '{2}' index type '{3}'.": { - "category": "Error", - "code": 2413 - }, - "Class name cannot be '{0}'.": { - "category": "Error", - "code": 2414 - }, - "Class '{0}' incorrectly extends base class '{1}'.": { - "category": "Error", - "code": 2415 - }, - "Property '{0}' in type '{1}' is not assignable to the same property in base type '{2}'.": { - "category": "Error", - "code": 2416 - }, - "Class static side '{0}' incorrectly extends base class static side '{1}'.": { - "category": "Error", - "code": 2417 - }, - "Type of computed property's value is '{0}', which is not assignable to type '{1}'.": { - "category": "Error", - "code": 2418 - }, - "Types of construct signatures are incompatible.": { - "category": "Error", - "code": 2419 - }, - "Class '{0}' incorrectly implements interface '{1}'.": { - "category": "Error", - "code": 2420 - }, - "A class can only implement an object type or intersection of object types with statically known members.": { - "category": "Error", - "code": 2422 - }, - "Class '{0}' defines instance member function '{1}', but extended class '{2}' defines it as instance member accessor.": { - "category": "Error", - "code": 2423 - }, - "Class '{0}' defines instance member property '{1}', but extended class '{2}' defines it as instance member function.": { - "category": "Error", - "code": 2425 - }, - "Class '{0}' defines instance member accessor '{1}', but extended class '{2}' defines it as instance member function.": { - "category": "Error", - "code": 2426 - }, - "Interface name cannot be '{0}'.": { - "category": "Error", - "code": 2427 - }, - "All declarations of '{0}' must have identical type parameters.": { - "category": "Error", - "code": 2428 - }, - "Interface '{0}' incorrectly extends interface '{1}'.": { - "category": "Error", - "code": 2430 - }, - "Enum name cannot be '{0}'.": { - "category": "Error", - "code": 2431 - }, - "In an enum with multiple declarations, only one declaration can omit an initializer for its first enum element.": { - "category": "Error", - "code": 2432 - }, - "A namespace declaration cannot be in a different file from a class or function with which it is merged.": { - "category": "Error", - "code": 2433 - }, - "A namespace declaration cannot be located prior to a class or function with which it is merged.": { - "category": "Error", - "code": 2434 - }, - "Ambient modules cannot be nested in other modules or namespaces.": { - "category": "Error", - "code": 2435 - }, - "Ambient module declaration cannot specify relative module name.": { - "category": "Error", - "code": 2436 - }, - "Module '{0}' is hidden by a local declaration with the same name.": { - "category": "Error", - "code": 2437 - }, - "Import name cannot be '{0}'.": { - "category": "Error", - "code": 2438 - }, - "Import or export declaration in an ambient module declaration cannot reference module through relative module name.": { - "category": "Error", - "code": 2439 - }, - "Import declaration conflicts with local declaration of '{0}'.": { - "category": "Error", - "code": 2440 - }, - "Duplicate identifier '{0}'. Compiler reserves name '{1}' in top level scope of a module.": { - "category": "Error", - "code": 2441 - }, - "Types have separate declarations of a private property '{0}'.": { - "category": "Error", - "code": 2442 - }, - "Property '{0}' is protected but type '{1}' is not a class derived from '{2}'.": { - "category": "Error", - "code": 2443 - }, - "Property '{0}' is protected in type '{1}' but public in type '{2}'.": { - "category": "Error", - "code": 2444 - }, - "Property '{0}' is protected and only accessible within class '{1}' and its subclasses.": { - "category": "Error", - "code": 2445 - }, - "Property '{0}' is protected and only accessible through an instance of class '{1}'. This is an instance of class '{2}'.": { - "category": "Error", - "code": 2446 - }, - "The '{0}' operator is not allowed for boolean types. Consider using '{1}' instead.": { - "category": "Error", - "code": 2447 - }, - "Block-scoped variable '{0}' used before its declaration.": { - "category": "Error", - "code": 2448 - }, - "Class '{0}' used before its declaration.": { - "category": "Error", - "code": 2449 - }, - "Enum '{0}' used before its declaration.": { - "category": "Error", - "code": 2450 - }, - "Cannot redeclare block-scoped variable '{0}'.": { - "category": "Error", - "code": 2451 - }, - "An enum member cannot have a numeric name.": { - "category": "Error", - "code": 2452 - }, - "Variable '{0}' is used before being assigned.": { - "category": "Error", - "code": 2454 - }, - "Type alias '{0}' circularly references itself.": { - "category": "Error", - "code": 2456 - }, - "Type alias name cannot be '{0}'.": { - "category": "Error", - "code": 2457 - }, - "An AMD module cannot have multiple name assignments.": { - "category": "Error", - "code": 2458 - }, - "Module '{0}' declares '{1}' locally, but it is not exported.": { - "category": "Error", - "code": 2459 - }, - "Module '{0}' declares '{1}' locally, but it is exported as '{2}'.": { - "category": "Error", - "code": 2460 - }, - "Type '{0}' is not an array type.": { - "category": "Error", - "code": 2461 - }, - "A rest element must be last in a destructuring pattern.": { - "category": "Error", - "code": 2462 - }, - "A binding pattern parameter cannot be optional in an implementation signature.": { - "category": "Error", - "code": 2463 - }, - "A computed property name must be of type 'string', 'number', 'symbol', or 'any'.": { - "category": "Error", - "code": 2464 - }, - "'this' cannot be referenced in a computed property name.": { - "category": "Error", - "code": 2465 - }, - "'super' cannot be referenced in a computed property name.": { - "category": "Error", - "code": 2466 - }, - "A computed property name cannot reference a type parameter from its containing type.": { - "category": "Error", - "code": 2467 - }, - "Cannot find global value '{0}'.": { - "category": "Error", - "code": 2468 - }, - "The '{0}' operator cannot be applied to type 'symbol'.": { - "category": "Error", - "code": 2469 - }, - "Spread operator in 'new' expressions is only available when targeting ECMAScript 5 and higher.": { - "category": "Error", - "code": 2472 - }, - "Enum declarations must all be const or non-const.": { - "category": "Error", - "code": 2473 - }, - "const enum member initializers must be constant expressions.": { - "category": "Error", - "code": 2474 - }, - "'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment or type query.": { - "category": "Error", - "code": 2475 - }, - "A const enum member can only be accessed using a string literal.": { - "category": "Error", - "code": 2476 - }, - "'const' enum member initializer was evaluated to a non-finite value.": { - "category": "Error", - "code": 2477 - }, - "'const' enum member initializer was evaluated to disallowed value 'NaN'.": { - "category": "Error", - "code": 2478 - }, - "'let' is not allowed to be used as a name in 'let' or 'const' declarations.": { - "category": "Error", - "code": 2480 - }, - "Cannot initialize outer scoped variable '{0}' in the same scope as block scoped declaration '{1}'.": { - "category": "Error", - "code": 2481 - }, - "The left-hand side of a 'for...of' statement cannot use a type annotation.": { - "category": "Error", - "code": 2483 - }, - "Export declaration conflicts with exported declaration of '{0}'.": { - "category": "Error", - "code": 2484 - }, - "The left-hand side of a 'for...of' statement must be a variable or a property access.": { - "category": "Error", - "code": 2487 - }, - "Type '{0}' must have a '[Symbol.iterator]()' method that returns an iterator.": { - "category": "Error", - "code": 2488 - }, - "An iterator must have a 'next()' method.": { - "category": "Error", - "code": 2489 - }, - "The type returned by the '{0}()' method of an iterator must have a 'value' property.": { - "category": "Error", - "code": 2490 - }, - "The left-hand side of a 'for...in' statement cannot be a destructuring pattern.": { - "category": "Error", - "code": 2491 - }, - "Cannot redeclare identifier '{0}' in catch clause.": { - "category": "Error", - "code": 2492 - }, - "Tuple type '{0}' of length '{1}' has no element at index '{2}'.": { - "category": "Error", - "code": 2493 - }, - "Using a string in a 'for...of' statement is only supported in ECMAScript 5 and higher.": { - "category": "Error", - "code": 2494 - }, - "Type '{0}' is not an array type or a string type.": { - "category": "Error", - "code": 2495 - }, - "The 'arguments' object cannot be referenced in an arrow function in ES5. Consider using a standard function expression.": { - "category": "Error", - "code": 2496 - }, - "This module can only be referenced with ECMAScript imports/exports by turning on the '{0}' flag and referencing its default export.": { - "category": "Error", - "code": 2497 - }, - "Module '{0}' uses 'export =' and cannot be used with 'export *'.": { - "category": "Error", - "code": 2498 - }, - "An interface can only extend an identifier/qualified-name with optional type arguments.": { - "category": "Error", - "code": 2499 - }, - "A class can only implement an identifier/qualified-name with optional type arguments.": { - "category": "Error", - "code": 2500 - }, - "A rest element cannot contain a binding pattern.": { - "category": "Error", - "code": 2501 - }, - "'{0}' is referenced directly or indirectly in its own type annotation.": { - "category": "Error", - "code": 2502 - }, - "Cannot find namespace '{0}'.": { - "category": "Error", - "code": 2503 - }, - "Type '{0}' must have a '[Symbol.asyncIterator]()' method that returns an async iterator.": { - "category": "Error", - "code": 2504 - }, - "A generator cannot have a 'void' type annotation.": { - "category": "Error", - "code": 2505 - }, - "'{0}' is referenced directly or indirectly in its own base expression.": { - "category": "Error", - "code": 2506 - }, - "Type '{0}' is not a constructor function type.": { - "category": "Error", - "code": 2507 - }, - "No base constructor has the specified number of type arguments.": { - "category": "Error", - "code": 2508 - }, - "Base constructor return type '{0}' is not an object type or intersection of object types with statically known members.": { - "category": "Error", - "code": 2509 - }, - "Base constructors must all have the same return type.": { - "category": "Error", - "code": 2510 - }, - "Cannot create an instance of an abstract class.": { - "category": "Error", - "code": 2511 - }, - "Overload signatures must all be abstract or non-abstract.": { - "category": "Error", - "code": 2512 - }, - "Abstract method '{0}' in class '{1}' cannot be accessed via super expression.": { - "category": "Error", - "code": 2513 - }, - "A tuple type cannot be indexed with a negative value.": { - "category": "Error", - "code": 2514 - }, - "Non-abstract class '{0}' does not implement inherited abstract member {1} from class '{2}'.": { - "category": "Error", - "code": 2515 - }, - "All declarations of an abstract method must be consecutive.": { - "category": "Error", - "code": 2516 - }, - "Cannot assign an abstract constructor type to a non-abstract constructor type.": { - "category": "Error", - "code": 2517 - }, - "A 'this'-based type guard is not compatible with a parameter-based type guard.": { - "category": "Error", - "code": 2518 - }, - "An async iterator must have a 'next()' method.": { - "category": "Error", - "code": 2519 - }, - "Duplicate identifier '{0}'. Compiler uses declaration '{1}' to support async functions.": { - "category": "Error", - "code": 2520 - }, - "The 'arguments' object cannot be referenced in an async function or method in ES5. Consider using a standard function or method.": { - "category": "Error", - "code": 2522 - }, - "'yield' expressions cannot be used in a parameter initializer.": { - "category": "Error", - "code": 2523 - }, - "'await' expressions cannot be used in a parameter initializer.": { - "category": "Error", - "code": 2524 - }, - "A 'this' type is available only in a non-static member of a class or interface.": { - "category": "Error", - "code": 2526 - }, - "The inferred type of '{0}' references an inaccessible '{1}' type. A type annotation is necessary.": { - "category": "Error", - "code": 2527 - }, - "A module cannot have multiple default exports.": { - "category": "Error", - "code": 2528 - }, - "Duplicate identifier '{0}'. Compiler reserves name '{1}' in top level scope of a module containing async functions.": { - "category": "Error", - "code": 2529 - }, - "Property '{0}' is incompatible with index signature.": { - "category": "Error", - "code": 2530 - }, - "Object is possibly 'null'.": { - "category": "Error", - "code": 2531 - }, - "Object is possibly 'undefined'.": { - "category": "Error", - "code": 2532 - }, - "Object is possibly 'null' or 'undefined'.": { - "category": "Error", - "code": 2533 - }, - "A function returning 'never' cannot have a reachable end point.": { - "category": "Error", - "code": 2534 - }, - "Type '{0}' cannot be used to index type '{1}'.": { - "category": "Error", - "code": 2536 - }, - "Type '{0}' has no matching index signature for type '{1}'.": { - "category": "Error", - "code": 2537 - }, - "Type '{0}' cannot be used as an index type.": { - "category": "Error", - "code": 2538 - }, - "Cannot assign to '{0}' because it is not a variable.": { - "category": "Error", - "code": 2539 - }, - "Cannot assign to '{0}' because it is a read-only property.": { - "category": "Error", - "code": 2540 - }, - "Index signature in type '{0}' only permits reading.": { - "category": "Error", - "code": 2542 - }, - "Duplicate identifier '_newTarget'. Compiler uses variable declaration '_newTarget' to capture 'new.target' meta-property reference.": { - "category": "Error", - "code": 2543 - }, - "Expression resolves to variable declaration '_newTarget' that compiler uses to capture 'new.target' meta-property reference.": { - "category": "Error", - "code": 2544 - }, - "A mixin class must have a constructor with a single rest parameter of type 'any[]'.": { - "category": "Error", - "code": 2545 - }, - "The type returned by the '{0}()' method of an async iterator must be a promise for a type with a 'value' property.": { - "category": "Error", - "code": 2547 - }, - "Type '{0}' is not an array type or does not have a '[Symbol.iterator]()' method that returns an iterator.": { - "category": "Error", - "code": 2548 - }, - "Type '{0}' is not an array type or a string type or does not have a '[Symbol.iterator]()' method that returns an iterator.": { - "category": "Error", - "code": 2549 - }, - "Property '{0}' does not exist on type '{1}'. Do you need to change your target library? Try changing the 'lib' compiler option to '{2}' or later.": { - "category": "Error", - "code": 2550 - }, - "Property '{0}' does not exist on type '{1}'. Did you mean '{2}'?": { - "category": "Error", - "code": 2551 - }, - "Cannot find name '{0}'. Did you mean '{1}'?": { - "category": "Error", - "code": 2552 - }, - "Computed values are not permitted in an enum with string valued members.": { - "category": "Error", - "code": 2553 - }, - "Expected {0} arguments, but got {1}.": { - "category": "Error", - "code": 2554 - }, - "Expected at least {0} arguments, but got {1}.": { - "category": "Error", - "code": 2555 - }, - "A spread argument must either have a tuple type or be passed to a rest parameter.": { - "category": "Error", - "code": 2556 - }, - "Expected {0} type arguments, but got {1}.": { - "category": "Error", - "code": 2558 - }, - "Type '{0}' has no properties in common with type '{1}'.": { - "category": "Error", - "code": 2559 - }, - "Value of type '{0}' has no properties in common with type '{1}'. Did you mean to call it?": { - "category": "Error", - "code": 2560 - }, - "Object literal may only specify known properties, but '{0}' does not exist in type '{1}'. Did you mean to write '{2}'?": { - "category": "Error", - "code": 2561 - }, - "Base class expressions cannot reference class type parameters.": { - "category": "Error", - "code": 2562 - }, - "The containing function or module body is too large for control flow analysis.": { - "category": "Error", - "code": 2563 - }, - "Property '{0}' has no initializer and is not definitely assigned in the constructor.": { - "category": "Error", - "code": 2564 - }, - "Property '{0}' is used before being assigned.": { - "category": "Error", - "code": 2565 - }, - "A rest element cannot have a property name.": { - "category": "Error", - "code": 2566 - }, - "Enum declarations can only merge with namespace or other enum declarations.": { - "category": "Error", - "code": 2567 - }, - "Property '{0}' may not exist on type '{1}'. Did you mean '{2}'?": { - "category": "Error", - "code": 2568 - }, - - "Could not find name '{0}'. Did you mean '{1}'?": { - "category": "Error", - "code": 2570 - }, - "Object is of type 'unknown'.": { - "category": "Error", - "code": 2571 - }, - "A rest element type must be an array type.": { - "category": "Error", - "code": 2574 - }, - "No overload expects {0} arguments, but overloads do exist that expect either {1} or {2} arguments.": { - "category": "Error", - "code": 2575 - }, - "Property '{0}' does not exist on type '{1}'. Did you mean to access the static member '{2}' instead?": { - "category": "Error", - "code": 2576 - }, - "Return type annotation circularly references itself.": { - "category": "Error", - "code": 2577 - }, - "Unused '@ts-expect-error' directive.": { - "category": "Error", - "code": 2578 - }, - "Cannot find name '{0}'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node`.": { - "category": "Error", - "code": 2580 - }, - "Cannot find name '{0}'. Do you need to install type definitions for jQuery? Try `npm i --save-dev @types/jquery`.": { - "category": "Error", - "code": 2581 - }, - "Cannot find name '{0}'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.": { - "category": "Error", - "code": 2582 - }, - "Cannot find name '{0}'. Do you need to change your target library? Try changing the 'lib' compiler option to '{1}' or later.": { - "category": "Error", - "code": 2583 - }, - "Cannot find name '{0}'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'dom'.": { - "category": "Error", - "code": 2584 - }, - "'{0}' only refers to a type, but is being used as a value here. Do you need to change your target library? Try changing the 'lib' compiler option to es2015 or later.": { - "category": "Error", - "code": 2585 - }, - "Cannot assign to '{0}' because it is a constant.": { - "category": "Error", - "code": 2588 - }, - "Type instantiation is excessively deep and possibly infinite.": { - "category": "Error", - "code": 2589 - }, - "Expression produces a union type that is too complex to represent.": { - "category": "Error", - "code": 2590 - }, - "Cannot find name '{0}'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.": { - "category": "Error", - "code": 2591 - }, - "Cannot find name '{0}'. Do you need to install type definitions for jQuery? Try `npm i --save-dev @types/jquery` and then add 'jquery' to the types field in your tsconfig.": { - "category": "Error", - "code": 2592 - }, - "Cannot find name '{0}'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha` and then add 'jest' or 'mocha' to the types field in your tsconfig.": { - "category": "Error", - "code": 2593 - }, - "This module is declared with 'export =', and can only be used with a default import when using the '{0}' flag.": { - "category": "Error", - "code": 2594 - }, - "'{0}' can only be imported by using a default import.": { - "category": "Error", - "code": 2595 - }, - "'{0}' can only be imported by turning on the 'esModuleInterop' flag and using a default import.": { - "category": "Error", - "code": 2596 - }, - "'{0}' can only be imported by using a 'require' call or by using a default import.": { - "category": "Error", - "code": 2597 - }, - "'{0}' can only be imported by using a 'require' call or by turning on the 'esModuleInterop' flag and using a default import.": { - "category": "Error", - "code": 2598 - }, - "JSX element implicitly has type 'any' because the global type 'JSX.Element' does not exist.": { - "category": "Error", - "code": 2602 - }, - "Property '{0}' in type '{1}' is not assignable to type '{2}'.": { - "category": "Error", - "code": 2603 - }, - "JSX element type '{0}' does not have any construct or call signatures.": { - "category": "Error", - "code": 2604 - }, - "Property '{0}' of JSX spread attribute is not assignable to target property.": { - "category": "Error", - "code": 2606 - }, - "JSX element class does not support attributes because it does not have a '{0}' property.": { - "category": "Error", - "code": 2607 - }, - "The global type 'JSX.{0}' may not have more than one property.": { - "category": "Error", - "code": 2608 - }, - "JSX spread child must be an array type.": { - "category": "Error", - "code": 2609 - }, - "'{0}' is defined as an accessor in class '{1}', but is overridden here in '{2}' as an instance property.": { - "category": "Error", - "code": 2610 - }, - "'{0}' is defined as a property in class '{1}', but is overridden here in '{2}' as an accessor.": { - "category": "Error", - "code": 2611 - }, - "Property '{0}' will overwrite the base property in '{1}'. If this is intentional, add an initializer. Otherwise, add a 'declare' modifier or remove the redundant declaration.": { - "category": "Error", - "code": 2612 - }, - "Module '{0}' has no default export. Did you mean to use 'import { {1} } from {0}' instead?": { - "category": "Error", - "code": 2613 - }, - "Module '{0}' has no exported member '{1}'. Did you mean to use 'import {1} from {0}' instead?": { - "category": "Error", - "code": 2614 - }, - "Type of property '{0}' circularly references itself in mapped type '{1}'.": { - "category": "Error", - "code": 2615 - }, - "'{0}' can only be imported by using 'import {1} = require({2})' or a default import.": { - "category": "Error", - "code": 2616 - }, - "'{0}' can only be imported by using 'import {1} = require({2})' or by turning on the 'esModuleInterop' flag and using a default import.": { - "category": "Error", - "code": 2617 - }, - "Source has {0} element(s) but target requires {1}.": { - "category": "Error", - "code": 2618 - }, - "Source has {0} element(s) but target allows only {1}.": { - "category": "Error", - "code": 2619 - }, - "Target requires {0} element(s) but source may have fewer.": { - "category": "Error", - "code": 2620 - }, - "Target allows only {0} element(s) but source may have more.": { - "category": "Error", - "code": 2621 - }, - "Source provides no match for required element at position {0} in target.": { - "category": "Error", - "code": 2623 - }, - "Source provides no match for variadic element at position {0} in target.": { - "category": "Error", - "code": 2624 - }, - "Variadic element at position {0} in source does not match element at position {1} in target.": { - "category": "Error", - "code": 2625 - }, - "Type at position {0} in source is not compatible with type at position {1} in target.": { - "category": "Error", - "code": 2626 - }, - "Type at positions {0} through {1} in source is not compatible with type at position {2} in target.": { - "category": "Error", - "code": 2627 - }, - "Cannot assign to '{0}' because it is an enum.": { - "category": "Error", - "code": 2628 - }, - "Cannot assign to '{0}' because it is a class.": { - "category": "Error", - "code": 2629 - }, - "Cannot assign to '{0}' because it is a function.": { - "category": "Error", - "code": 2630 - }, - "Cannot assign to '{0}' because it is a namespace.": { - "category": "Error", - "code": 2631 - }, - "Cannot assign to '{0}' because it is an import.": { - "category": "Error", - "code": 2632 - }, - "JSX property access expressions cannot include JSX namespace names": { - "category": "Error", - "code": 2633 - }, - "'{0}' index signatures are incompatible.": { - "category": "Error", - "code": 2634 - }, - "Type '{0}' has no signatures for which the type argument list is applicable.": { - "category": "Error", - "code": 2635 - }, - "Type '{0}' is not assignable to type '{1}' as implied by variance annotation.": { - "category": "Error", - "code": 2636 - }, - "Variance annotations are only supported in type aliases for object, function, constructor, and mapped types.": { - "category": "Error", - "code": 2637 - }, - "Type '{0}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator.": { - "category": "Error", - "code": 2638 - }, - "React components cannot include JSX namespace names": { - "category": "Error", - "code": 2639 - }, - - "Cannot augment module '{0}' with value exports because it resolves to a non-module entity.": { - "category": "Error", - "code": 2649 - }, - "Non-abstract class expression is missing implementations for the following members of '{0}': {1} and {2} more.": { - "category": "Error", - "code": 2650 - }, - "A member initializer in a enum declaration cannot reference members declared after it, including members defined in other enums.": { - "category": "Error", - "code": 2651 - }, - "Merged declaration '{0}' cannot include a default export declaration. Consider adding a separate 'export default {0}' declaration instead.": { - "category": "Error", - "code": 2652 - }, - "Non-abstract class expression does not implement inherited abstract member '{0}' from class '{1}'.": { - "category": "Error", - "code": 2653 - }, - "Non-abstract class '{0}' is missing implementations for the following members of '{1}': {2}.": { - "category": "Error", - "code": 2654 - }, - "Non-abstract class '{0}' is missing implementations for the following members of '{1}': {2} and {3} more.": { - "category": "Error", - "code": 2655 - }, - "Non-abstract class expression is missing implementations for the following members of '{0}': {1}.": { - "category": "Error", - "code": 2656 - }, - "JSX expressions must have one parent element.": { - "category": "Error", - "code": 2657 - }, - "Type '{0}' provides no match for the signature '{1}'.": { - "category": "Error", - "code": 2658 - }, - "'super' is only allowed in members of object literal expressions when option 'target' is 'ES2015' or higher.": { - "category": "Error", - "code": 2659 - }, - "'super' can only be referenced in members of derived classes or object literal expressions.": { - "category": "Error", - "code": 2660 - }, - "Cannot export '{0}'. Only local declarations can be exported from a module.": { - "category": "Error", - "code": 2661 - }, - "Cannot find name '{0}'. Did you mean the static member '{1}.{0}'?": { - "category": "Error", - "code": 2662 - }, - "Cannot find name '{0}'. Did you mean the instance member 'this.{0}'?": { - "category": "Error", - "code": 2663 - }, - "Invalid module name in augmentation, module '{0}' cannot be found.": { - "category": "Error", - "code": 2664 - }, - "Invalid module name in augmentation. Module '{0}' resolves to an untyped module at '{1}', which cannot be augmented.": { - "category": "Error", - "code": 2665 - }, - "Exports and export assignments are not permitted in module augmentations.": { - "category": "Error", - "code": 2666 - }, - "Imports are not permitted in module augmentations. Consider moving them to the enclosing external module.": { - "category": "Error", - "code": 2667 - }, - "'export' modifier cannot be applied to ambient modules and module augmentations since they are always visible.": { - "category": "Error", - "code": 2668 - }, - "Augmentations for the global scope can only be directly nested in external modules or ambient module declarations.": { - "category": "Error", - "code": 2669 - }, - "Augmentations for the global scope should have 'declare' modifier unless they appear in already ambient context.": { - "category": "Error", - "code": 2670 - }, - "Cannot augment module '{0}' because it resolves to a non-module entity.": { - "category": "Error", - "code": 2671 - }, - "Cannot assign a '{0}' constructor type to a '{1}' constructor type.": { - "category": "Error", - "code": 2672 - }, - "Constructor of class '{0}' is private and only accessible within the class declaration.": { - "category": "Error", - "code": 2673 - }, - "Constructor of class '{0}' is protected and only accessible within the class declaration.": { - "category": "Error", - "code": 2674 - }, - "Cannot extend a class '{0}'. Class constructor is marked as private.": { - "category": "Error", - "code": 2675 - }, - "Accessors must both be abstract or non-abstract.": { - "category": "Error", - "code": 2676 - }, - "A type predicate's type must be assignable to its parameter's type.": { - "category": "Error", - "code": 2677 - }, - "Type '{0}' is not comparable to type '{1}'.": { - "category": "Error", - "code": 2678 - }, - "A function that is called with the 'new' keyword cannot have a 'this' type that is 'void'.": { - "category": "Error", - "code": 2679 - }, - "A '{0}' parameter must be the first parameter.": { - "category": "Error", - "code": 2680 - }, - "A constructor cannot have a 'this' parameter.": { - "category": "Error", - "code": 2681 - }, - "'this' implicitly has type 'any' because it does not have a type annotation.": { - "category": "Error", - "code": 2683 - }, - "The 'this' context of type '{0}' is not assignable to method's 'this' of type '{1}'.": { - "category": "Error", - "code": 2684 - }, - "The 'this' types of each signature are incompatible.": { - "category": "Error", - "code": 2685 - }, - "'{0}' refers to a UMD global, but the current file is a module. Consider adding an import instead.": { - "category": "Error", - "code": 2686 - }, - "All declarations of '{0}' must have identical modifiers.": { - "category": "Error", - "code": 2687 - }, - "Cannot find type definition file for '{0}'.": { - "category": "Error", - "code": 2688 - }, - "Cannot extend an interface '{0}'. Did you mean 'implements'?": { - "category": "Error", - "code": 2689 - }, - "'{0}' only refers to a type, but is being used as a value here. Did you mean to use '{1} in {0}'?": { - "category": "Error", - "code": 2690 - }, - "'{0}' is a primitive, but '{1}' is a wrapper object. Prefer using '{0}' when possible.": { - "category": "Error", - "code": 2692 - }, - "'{0}' only refers to a type, but is being used as a value here.": { - "category": "Error", - "code": 2693 - }, - "Namespace '{0}' has no exported member '{1}'.": { - "category": "Error", - "code": 2694 - }, - "Left side of comma operator is unused and has no side effects.": { - "category": "Error", - "code": 2695, - "reportsUnnecessary": true - }, - "The 'Object' type is assignable to very few other types. Did you mean to use the 'any' type instead?": { - "category": "Error", - "code": 2696 - }, - "An async function or method must return a 'Promise'. Make sure you have a declaration for 'Promise' or include 'ES2015' in your '--lib' option.": { - "category": "Error", - "code": 2697 - }, - "Spread types may only be created from object types.": { - "category": "Error", - "code": 2698 - }, - "Static property '{0}' conflicts with built-in property 'Function.{0}' of constructor function '{1}'.": { - "category": "Error", - "code": 2699 - }, - "Rest types may only be created from object types.": { - "category": "Error", - "code": 2700 - }, - "The target of an object rest assignment must be a variable or a property access.": { - "category": "Error", - "code": 2701 - }, - "'{0}' only refers to a type, but is being used as a namespace here.": { - "category": "Error", - "code": 2702 - }, - "The operand of a 'delete' operator must be a property reference.": { - "category": "Error", - "code": 2703 - }, - "The operand of a 'delete' operator cannot be a read-only property.": { - "category": "Error", - "code": 2704 - }, - "An async function or method in ES5 requires the 'Promise' constructor. Make sure you have a declaration for the 'Promise' constructor or include 'ES2015' in your '--lib' option.": { - "category": "Error", - "code": 2705 - }, - "Required type parameters may not follow optional type parameters.": { - "category": "Error", - "code": 2706 - }, - "Generic type '{0}' requires between {1} and {2} type arguments.": { - "category": "Error", - "code": 2707 - }, - "Cannot use namespace '{0}' as a value.": { - "category": "Error", - "code": 2708 - }, - "Cannot use namespace '{0}' as a type.": { - "category": "Error", - "code": 2709 - }, - "'{0}' are specified twice. The attribute named '{0}' will be overwritten.": { - "category": "Error", - "code": 2710 - }, - "A dynamic import call returns a 'Promise'. Make sure you have a declaration for 'Promise' or include 'ES2015' in your '--lib' option.": { - "category": "Error", - "code": 2711 - }, - "A dynamic import call in ES5 requires the 'Promise' constructor. Make sure you have a declaration for the 'Promise' constructor or include 'ES2015' in your '--lib' option.": { - "category": "Error", - "code": 2712 - }, - "Cannot access '{0}.{1}' because '{0}' is a type, but not a namespace. Did you mean to retrieve the type of the property '{1}' in '{0}' with '{0}[\"{1}\"]'?": { - "category": "Error", - "code": 2713 - }, - "The expression of an export assignment must be an identifier or qualified name in an ambient context.": { - "category": "Error", - "code": 2714 - }, - "Abstract property '{0}' in class '{1}' cannot be accessed in the constructor.": { - "category": "Error", - "code": 2715 - }, - "Type parameter '{0}' has a circular default.": { - "category": "Error", - "code": 2716 - }, - "Subsequent property declarations must have the same type. Property '{0}' must be of type '{1}', but here has type '{2}'.": { - "category": "Error", - "code": 2717 - }, - "Duplicate property '{0}'.": { - "category": "Error", - "code": 2718 - }, - "Type '{0}' is not assignable to type '{1}'. Two different types with this name exist, but they are unrelated.": { - "category": "Error", - "code": 2719 - }, - "Class '{0}' incorrectly implements class '{1}'. Did you mean to extend '{1}' and inherit its members as a subclass?": { - "category": "Error", - "code": 2720 - }, - "Cannot invoke an object which is possibly 'null'.": { - "category": "Error", - "code": 2721 - }, - "Cannot invoke an object which is possibly 'undefined'.": { - "category": "Error", - "code": 2722 - }, - "Cannot invoke an object which is possibly 'null' or 'undefined'.": { - "category": "Error", - "code": 2723 - }, - "'{0}' has no exported member named '{1}'. Did you mean '{2}'?": { - "category": "Error", - "code": 2724 - }, - "Class name cannot be 'Object' when targeting ES5 and above with module {0}.": { - "category": "Error", - "code": 2725 - }, - "Cannot find lib definition for '{0}'.": { - "category": "Error", - "code": 2726 - }, - "Cannot find lib definition for '{0}'. Did you mean '{1}'?": { - "category": "Error", - "code": 2727 - }, - "'{0}' is declared here.": { - "category": "Message", - "code": 2728 - }, - "Property '{0}' is used before its initialization.": { - "category": "Error", - "code": 2729 - }, - "An arrow function cannot have a 'this' parameter.": { - "category": "Error", - "code": 2730 - }, - "Implicit conversion of a 'symbol' to a 'string' will fail at runtime. Consider wrapping this expression in 'String(...)'.": { - "category": "Error", - "code": 2731 - }, - "Cannot find module '{0}'. Consider using '--resolveJsonModule' to import module with '.json' extension.": { - "category": "Error", - "code": 2732 - }, - "Property '{0}' was also declared here.": { - "category": "Error", - "code": 2733 - }, - "Are you missing a semicolon?": { - "category": "Error", - "code": 2734 - }, - "Did you mean for '{0}' to be constrained to type 'new (...args: any[]) => {1}'?": { - "category": "Error", - "code": 2735 - }, - "Operator '{0}' cannot be applied to type '{1}'.": { - "category": "Error", - "code": 2736 - }, - "BigInt literals are not available when targeting lower than ES2020.": { - "category": "Error", - "code": 2737 - }, - "An outer value of 'this' is shadowed by this container.": { - "category": "Message", - "code": 2738 - }, - "Type '{0}' is missing the following properties from type '{1}': {2}": { - "category": "Error", - "code": 2739 - }, - "Type '{0}' is missing the following properties from type '{1}': {2}, and {3} more.": { - "category": "Error", - "code": 2740 - }, - "Property '{0}' is missing in type '{1}' but required in type '{2}'.": { - "category": "Error", - "code": 2741 - }, - "The inferred type of '{0}' cannot be named without a reference to '{1}'. This is likely not portable. A type annotation is necessary.": { - "category": "Error", - "code": 2742 - }, - "No overload expects {0} type arguments, but overloads do exist that expect either {1} or {2} type arguments.": { - "category": "Error", - "code": 2743 - }, - "Type parameter defaults can only reference previously declared type parameters.": { - "category": "Error", - "code": 2744 - }, - "This JSX tag's '{0}' prop expects type '{1}' which requires multiple children, but only a single child was provided.": { - "category": "Error", - "code": 2745 - }, - "This JSX tag's '{0}' prop expects a single child of type '{1}', but multiple children were provided.": { - "category": "Error", - "code": 2746 - }, - "'{0}' components don't accept text as child elements. Text in JSX has the type 'string', but the expected type of '{1}' is '{2}'.": { - "category": "Error", - "code": 2747 - }, - "Cannot access ambient const enums when '{0}' is enabled.": { - "category": "Error", - "code": 2748 - }, - "'{0}' refers to a value, but is being used as a type here. Did you mean 'typeof {0}'?": { - "category": "Error", - "code": 2749 - }, - "The implementation signature is declared here.": { - "category": "Error", - "code": 2750 - }, - "Circularity originates in type at this location.": { - "category": "Error", - "code": 2751 - }, - "The first export default is here.": { - "category": "Error", - "code": 2752 - }, - "Another export default is here.": { - "category": "Error", - "code": 2753 - }, - "'super' may not use type arguments.": { - "category": "Error", - "code": 2754 - }, - "No constituent of type '{0}' is callable.": { - "category": "Error", - "code": 2755 - }, - "Not all constituents of type '{0}' are callable.": { - "category": "Error", - "code": 2756 - }, - "Type '{0}' has no call signatures.": { - "category": "Error", - "code": 2757 - }, - "Each member of the union type '{0}' has signatures, but none of those signatures are compatible with each other.": { - "category": "Error", - "code": 2758 - }, - "No constituent of type '{0}' is constructable.": { - "category": "Error", - "code": 2759 - }, - "Not all constituents of type '{0}' are constructable.": { - "category": "Error", - "code": 2760 - }, - "Type '{0}' has no construct signatures.": { - "category": "Error", - "code": 2761 - }, - "Each member of the union type '{0}' has construct signatures, but none of those signatures are compatible with each other.": { - "category": "Error", - "code": 2762 - }, - "Cannot iterate value because the 'next' method of its iterator expects type '{1}', but for-of will always send '{0}'.": { - "category": "Error", - "code": 2763 - }, - "Cannot iterate value because the 'next' method of its iterator expects type '{1}', but array spread will always send '{0}'.": { - "category": "Error", - "code": 2764 - }, - "Cannot iterate value because the 'next' method of its iterator expects type '{1}', but array destructuring will always send '{0}'.": { - "category": "Error", - "code": 2765 - }, - "Cannot delegate iteration to value because the 'next' method of its iterator expects type '{1}', but the containing generator will always send '{0}'.": { - "category": "Error", - "code": 2766 - }, - "The '{0}' property of an iterator must be a method.": { - "category": "Error", - "code": 2767 - }, - "The '{0}' property of an async iterator must be a method.": { - "category": "Error", - "code": 2768 - }, - "No overload matches this call.": { - "category": "Error", - "code": 2769 - }, - "The last overload gave the following error.": { - "category": "Error", - "code": 2770 - }, - "The last overload is declared here.": { - "category": "Error", - "code": 2771 - }, - "Overload {0} of {1}, '{2}', gave the following error.": { - "category": "Error", - "code": 2772 - }, - "Did you forget to use 'await'?": { - "category": "Error", - "code": 2773 - }, - "This condition will always return true since this function is always defined. Did you mean to call it instead?": { - "category": "Error", - "code": 2774 - }, - "Assertions require every name in the call target to be declared with an explicit type annotation.": { - "category": "Error", - "code": 2775 - }, - "Assertions require the call target to be an identifier or qualified name.": { - "category": "Error", - "code": 2776 - }, - "The operand of an increment or decrement operator may not be an optional property access.": { - "category": "Error", - "code": 2777 - }, - "The target of an object rest assignment may not be an optional property access.": { - "category": "Error", - "code": 2778 - }, - "The left-hand side of an assignment expression may not be an optional property access.": { - "category": "Error", - "code": 2779 - }, - "The left-hand side of a 'for...in' statement may not be an optional property access.": { - "category": "Error", - "code": 2780 - }, - "The left-hand side of a 'for...of' statement may not be an optional property access.": { - "category": "Error", - "code": 2781 - }, - "'{0}' needs an explicit type annotation.": { - "category": "Message", - "code": 2782 - }, - "'{0}' is specified more than once, so this usage will be overwritten.": { - "category": "Error", - "code": 2783 - }, - "'get' and 'set' accessors cannot declare 'this' parameters.": { - "category": "Error", - "code": 2784 - }, - "This spread always overwrites this property.": { - "category": "Error", - "code": 2785 - }, - "'{0}' cannot be used as a JSX component.": { - "category": "Error", - "code": 2786 - }, - "Its return type '{0}' is not a valid JSX element.": { - "category": "Error", - "code": 2787 - }, - "Its instance type '{0}' is not a valid JSX element.": { - "category": "Error", - "code": 2788 - }, - "Its element type '{0}' is not a valid JSX element.": { - "category": "Error", - "code": 2789 - }, - "The operand of a 'delete' operator must be optional.": { - "category": "Error", - "code": 2790 - }, - "Exponentiation cannot be performed on 'bigint' values unless the 'target' option is set to 'es2016' or later.": { - "category": "Error", - "code": 2791 - }, - "Cannot find module '{0}'. Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?": { - "category": "Error", - "code": 2792 - }, - "The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.": { - "category": "Error", - "code": 2793 - }, - "Expected {0} arguments, but got {1}. Did you forget to include 'void' in your type argument to 'Promise'?": { - "category": "Error", - "code": 2794 - }, - "The 'intrinsic' keyword can only be used to declare compiler provided intrinsic types.": { - "category": "Error", - "code": 2795 - }, - "It is likely that you are missing a comma to separate these two template expressions. They form a tagged template expression which cannot be invoked.": { - "category": "Error", - "code": 2796 - }, - "A mixin class that extends from a type variable containing an abstract construct signature must also be declared 'abstract'.": { - "category": "Error", - "code": 2797 - }, - "The declaration was marked as deprecated here.": { - "category": "Error", - "code": 2798 - }, - "Type produces a tuple type that is too large to represent.": { - "category": "Error", - "code": 2799 - }, - "Expression produces a tuple type that is too large to represent.": { - "category": "Error", - "code": 2800 - }, - "This condition will always return true since this '{0}' is always defined.": { - "category": "Error", - "code": 2801 - }, - "Type '{0}' can only be iterated through when using the '--downlevelIteration' flag or with a '--target' of 'es2015' or higher.": { - "category": "Error", - "code": 2802 - }, - "Cannot assign to private method '{0}'. Private methods are not writable.": { - "category": "Error", - "code": 2803 - }, - "Duplicate identifier '{0}'. Static and instance elements cannot share the same private name.": { - "category": "Error", - "code": 2804 - }, - "Private accessor was defined without a getter.": { - "category": "Error", - "code": 2806 - }, - "This syntax requires an imported helper named '{1}' with {2} parameters, which is not compatible with the one in '{0}'. Consider upgrading your version of '{0}'.": { - "category": "Error", - "code": 2807 - }, - "A get accessor must be at least as accessible as the setter": { - "category": "Error", - "code": 2808 - }, - "Declaration or statement expected. This '=' follows a block of statements, so if you intended to write a destructuring assignment, you might need to wrap the whole assignment in parentheses.": { - "category": "Error", - "code": 2809 - }, - "Expected 1 argument, but got 0. 'new Promise()' needs a JSDoc hint to produce a 'resolve' that can be called without arguments.": { - "category": "Error", - "code": 2810 - }, - "Initializer for property '{0}'": { - "category": "Error", - "code": 2811 - }, - "Property '{0}' does not exist on type '{1}'. Try changing the 'lib' compiler option to include 'dom'.": { - "category": "Error", - "code": 2812 - }, - "Class declaration cannot implement overload list for '{0}'.": { - "category": "Error", - "code": 2813 - }, - "Function with bodies can only merge with classes that are ambient.": { - "category": "Error", - "code": 2814 - }, - "'arguments' cannot be referenced in property initializers or class static initialization blocks.": { - "category": "Error", - "code": 2815 - }, - "Cannot use 'this' in a static property initializer of a decorated class.": { - "category": "Error", - "code": 2816 - }, - "Property '{0}' has no initializer and is not definitely assigned in a class static block.": { - "category": "Error", - "code": 2817 - }, - "Duplicate identifier '{0}'. Compiler reserves name '{1}' when emitting 'super' references in static initializers.": { - "category": "Error", - "code": 2818 - }, - "Namespace name cannot be '{0}'.": { - "category": "Error", - "code": 2819 - }, - "Type '{0}' is not assignable to type '{1}'. Did you mean '{2}'?": { - "category": "Error", - "code": 2820 - }, - "Import assertions are only supported when the '--module' option is set to 'esnext', 'node18', 'node20', 'nodenext', or 'preserve'.": { - "category": "Error", - "code": 2821 - }, - "Import assertions cannot be used with type-only imports or exports.": { - "category": "Error", - "code": 2822 - }, - "Import attributes are only supported when the '--module' option is set to 'esnext', 'node18', 'node20', 'nodenext', or 'preserve'.": { - "category": "Error", - "code": 2823 - }, - "Cannot find namespace '{0}'. Did you mean '{1}'?": { - "category": "Error", - "code": 2833 - }, - "Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Consider adding an extension to the import path.": { - "category": "Error", - "code": 2834 - }, - "Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean '{0}'?": { - "category": "Error", - "code": 2835 - }, - "Import assertions are not allowed on statements that compile to CommonJS 'require' calls.": { - "category": "Error", - "code": 2836 - }, - "Import assertion values must be string literal expressions.": { - "category": "Error", - "code": 2837 - }, - "All declarations of '{0}' must have identical constraints.": { - "category": "Error", - "code": 2838 - }, - "This condition will always return '{0}' since JavaScript compares objects by reference, not value.": { - "category": "Error", - "code": 2839 - }, - "An interface cannot extend a primitive type like '{0}'. It can only extend other named object types.": { - "category": "Error", - "code": 2840 - }, - "'{0}' is an unused renaming of '{1}'. Did you intend to use it as a type annotation?": { - "category": "Error", - "code": 2842 - }, - "We can only write a type for '{0}' by adding a type for the entire parameter here.": { - "category": "Error", - "code": 2843 - }, - "Type of instance member variable '{0}' cannot reference identifier '{1}' declared in the constructor.": { - "category": "Error", - "code": 2844 - }, - "This condition will always return '{0}'.": { - "category": "Error", - "code": 2845 - }, - "A declaration file cannot be imported without 'import type'. Did you mean to import an implementation file '{0}' instead?": { - "category": "Error", - "code": 2846 - }, - "The right-hand side of an 'instanceof' expression must not be an instantiation expression.": { - "category": "Error", - "code": 2848 - }, - "Target signature provides too few arguments. Expected {0} or more, but got {1}.": { - "category": "Error", - "code": 2849 - }, - "The initializer of a 'using' declaration must be either an object with a '[Symbol.dispose]()' method, or be 'null' or 'undefined'.": { - "category": "Error", - "code": 2850 - }, - "The initializer of an 'await using' declaration must be either an object with a '[Symbol.asyncDispose]()' or '[Symbol.dispose]()' method, or be 'null' or 'undefined'.": { - "category": "Error", - "code": 2851 - }, - "'await using' statements are only allowed within async functions and at the top levels of modules.": { - "category": "Error", - "code": 2852 - }, - "'await using' statements are only allowed at the top level of a file when that file is a module, but this file has no imports or exports. Consider adding an empty 'export {}' to make this file a module.": { - "category": "Error", - "code": 2853 - }, - "Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.": { - "category": "Error", - "code": 2854 - }, - "Class field '{0}' defined by the parent class is not accessible in the child class via super.": { - "category": "Error", - "code": 2855 - }, - "Import attributes are not allowed on statements that compile to CommonJS 'require' calls.": { - "category": "Error", - "code": 2856 - }, - "Import attributes cannot be used with type-only imports or exports.": { - "category": "Error", - "code": 2857 - }, - "Import attribute values must be string literal expressions.": { - "category": "Error", - "code": 2858 - }, - "Excessive complexity comparing types '{0}' and '{1}'.": { - "category": "Error", - "code": 2859 - }, - "The left-hand side of an 'instanceof' expression must be assignable to the first argument of the right-hand side's '[Symbol.hasInstance]' method.": { - "category": "Error", - "code": 2860 - }, - "An object's '[Symbol.hasInstance]' method must return a boolean value for it to be used on the right-hand side of an 'instanceof' expression.": { - "category": "Error", - "code": 2861 - }, - "Type '{0}' is generic and can only be indexed for reading.": { - "category": "Error", - "code": 2862 - }, - "A class cannot extend a primitive type like '{0}'. Classes can only extend constructable values.": { - "category": "Error", - "code": 2863 - }, - "A class cannot implement a primitive type like '{0}'. It can only implement other named object types.": { - "category": "Error", - "code": 2864 - }, - "Import '{0}' conflicts with local value, so must be declared with a type-only import when 'isolatedModules' is enabled.": { - "category": "Error", - "code": 2865 - }, - "Import '{0}' conflicts with global value used in this file, so must be declared with a type-only import when 'isolatedModules' is enabled.": { - "category": "Error", - "code": 2866 - }, - "Cannot find name '{0}'. Do you need to install type definitions for Bun? Try `npm i --save-dev @types/bun`.": { - "category": "Error", - "code": 2867 - }, - "Cannot find name '{0}'. Do you need to install type definitions for Bun? Try `npm i --save-dev @types/bun` and then add 'bun' to the types field in your tsconfig.": { - "category": "Error", - "code": 2868 - }, - "Right operand of ?? is unreachable because the left operand is never nullish.": { - "category": "Error", - "code": 2869 - }, - "This binary expression is never nullish. Are you missing parentheses?": { - "category": "Error", - "code": 2870 - }, - "This expression is always nullish.": { - "category": "Error", - "code": 2871 - }, - "This kind of expression is always truthy.": { - "category": "Error", - "code": 2872 - }, - "This kind of expression is always falsy.": { - "category": "Error", - "code": 2873 - }, - "This JSX tag requires '{0}' to be in scope, but it could not be found.": { - "category": "Error", - "code": 2874 - }, - "This JSX tag requires the module path '{0}' to exist, but none could be found. Make sure you have types for the appropriate package installed.": { - "category": "Error", - "code": 2875 - }, - "This relative import path is unsafe to rewrite because it looks like a file name, but actually resolves to \"{0}\".": { - "category": "Error", - "code": 2876 - }, - "This import uses a '{0}' extension to resolve to an input TypeScript file, but will not be rewritten during emit because it is not a relative path.": { - "category": "Error", - "code": 2877 - }, - "This import path is unsafe to rewrite because it resolves to another project, and the relative path between the projects' output files is not the same as the relative path between its input files.": { - "category": "Error", - "code": 2878 - }, - "Using JSX fragments requires fragment factory '{0}' to be in scope, but it could not be found.": { - "category": "Error", - "code": 2879 - }, - "Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.": { - "category": "Error", - "code": 2880 - }, - "This expression is never nullish.": { - "category": "Error", - "code": 2881 - }, - "Cannot find module or type declarations for side-effect import of '{0}'.": { - "category": "Error", - "code": 2882 - }, - "The inferred type of '{0}' cannot be named without a reference to '{2}' from '{1}'. This is likely not portable. A type annotation is necessary.": { - "category": "Error", - "code": 2883 - }, - - "Import declaration '{0}' is using private name '{1}'.": { - "category": "Error", - "code": 4000 - }, - "Type parameter '{0}' of exported class has or is using private name '{1}'.": { - "category": "Error", - "code": 4002 - }, - "Type parameter '{0}' of exported interface has or is using private name '{1}'.": { - "category": "Error", - "code": 4004 - }, - "Type parameter '{0}' of constructor signature from exported interface has or is using private name '{1}'.": { - "category": "Error", - "code": 4006 - }, - "Type parameter '{0}' of call signature from exported interface has or is using private name '{1}'.": { - "category": "Error", - "code": 4008 - }, - "Type parameter '{0}' of public static method from exported class has or is using private name '{1}'.": { - "category": "Error", - "code": 4010 - }, - "Type parameter '{0}' of public method from exported class has or is using private name '{1}'.": { - "category": "Error", - "code": 4012 - }, - "Type parameter '{0}' of method from exported interface has or is using private name '{1}'.": { - "category": "Error", - "code": 4014 - }, - "Type parameter '{0}' of exported function has or is using private name '{1}'.": { - "category": "Error", - "code": 4016 - }, - "Implements clause of exported class '{0}' has or is using private name '{1}'.": { - "category": "Error", - "code": 4019 - }, - "'extends' clause of exported class '{0}' has or is using private name '{1}'.": { - "category": "Error", - "code": 4020 - }, - "'extends' clause of exported class has or is using private name '{0}'.": { - "category": "Error", - "code": 4021 - }, - "'extends' clause of exported interface '{0}' has or is using private name '{1}'.": { - "category": "Error", - "code": 4022 - }, - "Exported variable '{0}' has or is using name '{1}' from external module {2} but cannot be named.": { - "category": "Error", - "code": 4023 - }, - "Exported variable '{0}' has or is using name '{1}' from private module '{2}'.": { - "category": "Error", - "code": 4024 - }, - "Exported variable '{0}' has or is using private name '{1}'.": { - "category": "Error", - "code": 4025 - }, - "Public static property '{0}' of exported class has or is using name '{1}' from external module {2} but cannot be named.": { - "category": "Error", - "code": 4026 - }, - "Public static property '{0}' of exported class has or is using name '{1}' from private module '{2}'.": { - "category": "Error", - "code": 4027 - }, - "Public static property '{0}' of exported class has or is using private name '{1}'.": { - "category": "Error", - "code": 4028 - }, - "Public property '{0}' of exported class has or is using name '{1}' from external module {2} but cannot be named.": { - "category": "Error", - "code": 4029 - }, - "Public property '{0}' of exported class has or is using name '{1}' from private module '{2}'.": { - "category": "Error", - "code": 4030 - }, - "Public property '{0}' of exported class has or is using private name '{1}'.": { - "category": "Error", - "code": 4031 - }, - "Property '{0}' of exported interface has or is using name '{1}' from private module '{2}'.": { - "category": "Error", - "code": 4032 - }, - "Property '{0}' of exported interface has or is using private name '{1}'.": { - "category": "Error", - "code": 4033 - }, - "Parameter type of public static setter '{0}' from exported class has or is using name '{1}' from private module '{2}'.": { - "category": "Error", - "code": 4034 - }, - "Parameter type of public static setter '{0}' from exported class has or is using private name '{1}'.": { - "category": "Error", - "code": 4035 - }, - "Parameter type of public setter '{0}' from exported class has or is using name '{1}' from private module '{2}'.": { - "category": "Error", - "code": 4036 - }, - "Parameter type of public setter '{0}' from exported class has or is using private name '{1}'.": { - "category": "Error", - "code": 4037 - }, - "Return type of public static getter '{0}' from exported class has or is using name '{1}' from external module {2} but cannot be named.": { - "category": "Error", - "code": 4038 - }, - "Return type of public static getter '{0}' from exported class has or is using name '{1}' from private module '{2}'.": { - "category": "Error", - "code": 4039 - }, - "Return type of public static getter '{0}' from exported class has or is using private name '{1}'.": { - "category": "Error", - "code": 4040 - }, - "Return type of public getter '{0}' from exported class has or is using name '{1}' from external module {2} but cannot be named.": { - "category": "Error", - "code": 4041 - }, - "Return type of public getter '{0}' from exported class has or is using name '{1}' from private module '{2}'.": { - "category": "Error", - "code": 4042 - }, - "Return type of public getter '{0}' from exported class has or is using private name '{1}'.": { - "category": "Error", - "code": 4043 - }, - "Return type of constructor signature from exported interface has or is using name '{0}' from private module '{1}'.": { - "category": "Error", - "code": 4044 - }, - "Return type of constructor signature from exported interface has or is using private name '{0}'.": { - "category": "Error", - "code": 4045 - }, - "Return type of call signature from exported interface has or is using name '{0}' from private module '{1}'.": { - "category": "Error", - "code": 4046 - }, - "Return type of call signature from exported interface has or is using private name '{0}'.": { - "category": "Error", - "code": 4047 - }, - "Return type of index signature from exported interface has or is using name '{0}' from private module '{1}'.": { - "category": "Error", - "code": 4048 - }, - "Return type of index signature from exported interface has or is using private name '{0}'.": { - "category": "Error", - "code": 4049 - }, - "Return type of public static method from exported class has or is using name '{0}' from external module {1} but cannot be named.": { - "category": "Error", - "code": 4050 - }, - "Return type of public static method from exported class has or is using name '{0}' from private module '{1}'.": { - "category": "Error", - "code": 4051 - }, - "Return type of public static method from exported class has or is using private name '{0}'.": { - "category": "Error", - "code": 4052 - }, - "Return type of public method from exported class has or is using name '{0}' from external module {1} but cannot be named.": { - "category": "Error", - "code": 4053 - }, - "Return type of public method from exported class has or is using name '{0}' from private module '{1}'.": { - "category": "Error", - "code": 4054 - }, - "Return type of public method from exported class has or is using private name '{0}'.": { - "category": "Error", - "code": 4055 - }, - "Return type of method from exported interface has or is using name '{0}' from private module '{1}'.": { - "category": "Error", - "code": 4056 - }, - "Return type of method from exported interface has or is using private name '{0}'.": { - "category": "Error", - "code": 4057 - }, - "Return type of exported function has or is using name '{0}' from external module {1} but cannot be named.": { - "category": "Error", - "code": 4058 - }, - "Return type of exported function has or is using name '{0}' from private module '{1}'.": { - "category": "Error", - "code": 4059 - }, - "Return type of exported function has or is using private name '{0}'.": { - "category": "Error", - "code": 4060 - }, - "Parameter '{0}' of constructor from exported class has or is using name '{1}' from external module {2} but cannot be named.": { - "category": "Error", - "code": 4061 - }, - "Parameter '{0}' of constructor from exported class has or is using name '{1}' from private module '{2}'.": { - "category": "Error", - "code": 4062 - }, - "Parameter '{0}' of constructor from exported class has or is using private name '{1}'.": { - "category": "Error", - "code": 4063 - }, - "Parameter '{0}' of constructor signature from exported interface has or is using name '{1}' from private module '{2}'.": { - "category": "Error", - "code": 4064 - }, - "Parameter '{0}' of constructor signature from exported interface has or is using private name '{1}'.": { - "category": "Error", - "code": 4065 - }, - "Parameter '{0}' of call signature from exported interface has or is using name '{1}' from private module '{2}'.": { - "category": "Error", - "code": 4066 - }, - "Parameter '{0}' of call signature from exported interface has or is using private name '{1}'.": { - "category": "Error", - "code": 4067 - }, - "Parameter '{0}' of public static method from exported class has or is using name '{1}' from external module {2} but cannot be named.": { - "category": "Error", - "code": 4068 - }, - "Parameter '{0}' of public static method from exported class has or is using name '{1}' from private module '{2}'.": { - "category": "Error", - "code": 4069 - }, - "Parameter '{0}' of public static method from exported class has or is using private name '{1}'.": { - "category": "Error", - "code": 4070 - }, - "Parameter '{0}' of public method from exported class has or is using name '{1}' from external module {2} but cannot be named.": { - "category": "Error", - "code": 4071 - }, - "Parameter '{0}' of public method from exported class has or is using name '{1}' from private module '{2}'.": { - "category": "Error", - "code": 4072 - }, - "Parameter '{0}' of public method from exported class has or is using private name '{1}'.": { - "category": "Error", - "code": 4073 - }, - "Parameter '{0}' of method from exported interface has or is using name '{1}' from private module '{2}'.": { - "category": "Error", - "code": 4074 - }, - "Parameter '{0}' of method from exported interface has or is using private name '{1}'.": { - "category": "Error", - "code": 4075 - }, - "Parameter '{0}' of exported function has or is using name '{1}' from external module {2} but cannot be named.": { - "category": "Error", - "code": 4076 - }, - "Parameter '{0}' of exported function has or is using name '{1}' from private module '{2}'.": { - "category": "Error", - "code": 4077 - }, - "Parameter '{0}' of exported function has or is using private name '{1}'.": { - "category": "Error", - "code": 4078 - }, - "Exported type alias '{0}' has or is using private name '{1}'.": { - "category": "Error", - "code": 4081 - }, - "Default export of the module has or is using private name '{0}'.": { - "category": "Error", - "code": 4082 - }, - "Type parameter '{0}' of exported type alias has or is using private name '{1}'.": { - "category": "Error", - "code": 4083 - }, - "Exported type alias '{0}' has or is using private name '{1}' from module {2}.": { - "category": "Error", - "code": 4084 - }, - "Extends clause for inferred type '{0}' has or is using private name '{1}'.": { - "category": "Error", - "code": 4085 - }, - "Parameter '{0}' of index signature from exported interface has or is using name '{1}' from private module '{2}'.": { - "category": "Error", - "code": 4091 - }, - "Parameter '{0}' of index signature from exported interface has or is using private name '{1}'.": { - "category": "Error", - "code": 4092 - }, - "Property '{0}' of exported anonymous class type may not be private or protected.": { - "category": "Error", - "code": 4094 - }, - "Public static method '{0}' of exported class has or is using name '{1}' from external module {2} but cannot be named.": { - "category": "Error", - "code": 4095 - }, - "Public static method '{0}' of exported class has or is using name '{1}' from private module '{2}'.": { - "category": "Error", - "code": 4096 - }, - "Public static method '{0}' of exported class has or is using private name '{1}'.": { - "category": "Error", - "code": 4097 - }, - "Public method '{0}' of exported class has or is using name '{1}' from external module {2} but cannot be named.": { - "category": "Error", - "code": 4098 - }, - "Public method '{0}' of exported class has or is using name '{1}' from private module '{2}'.": { - "category": "Error", - "code": 4099 - }, - "Public method '{0}' of exported class has or is using private name '{1}'.": { - "category": "Error", - "code": 4100 - }, - "Method '{0}' of exported interface has or is using name '{1}' from private module '{2}'.": { - "category": "Error", - "code": 4101 - }, - "Method '{0}' of exported interface has or is using private name '{1}'.": { - "category": "Error", - "code": 4102 - }, - "Type parameter '{0}' of exported mapped object type is using private name '{1}'.": { - "category": "Error", - "code": 4103 - }, - "The type '{0}' is 'readonly' and cannot be assigned to the mutable type '{1}'.": { - "category": "Error", - "code": 4104 - }, - "Private or protected member '{0}' cannot be accessed on a type parameter.": { - "category": "Error", - "code": 4105 - }, - "Parameter '{0}' of accessor has or is using private name '{1}'.": { - "category": "Error", - "code": 4106 - }, - "Parameter '{0}' of accessor has or is using name '{1}' from private module '{2}'.": { - "category": "Error", - "code": 4107 - }, - "Parameter '{0}' of accessor has or is using name '{1}' from external module '{2}' but cannot be named.": { - "category": "Error", - "code": 4108 - }, - "Type arguments for '{0}' circularly reference themselves.": { - "category": "Error", - "code": 4109 - }, - "Tuple type arguments circularly reference themselves.": { - "category": "Error", - "code": 4110 - }, - "Property '{0}' comes from an index signature, so it must be accessed with ['{0}'].": { - "category": "Error", - "code": 4111 - }, - "This member cannot have an 'override' modifier because its containing class '{0}' does not extend another class.": { - "category": "Error", - "code": 4112 - }, - "This member cannot have an 'override' modifier because it is not declared in the base class '{0}'.": { - "category": "Error", - "code": 4113 - }, - "This member must have an 'override' modifier because it overrides a member in the base class '{0}'.": { - "category": "Error", - "code": 4114 - }, - "This parameter property must have an 'override' modifier because it overrides a member in base class '{0}'.": { - "category": "Error", - "code": 4115 - }, - "This member must have an 'override' modifier because it overrides an abstract method that is declared in the base class '{0}'.": { - "category": "Error", - "code": 4116 - }, - "This member cannot have an 'override' modifier because it is not declared in the base class '{0}'. Did you mean '{1}'?": { - "category": "Error", - "code": 4117 - }, - "The type of this node cannot be serialized because its property '{0}' cannot be serialized.": { - "category": "Error", - "code": 4118 - }, - "This member must have a JSDoc comment with an '@override' tag because it overrides a member in the base class '{0}'.": { - "category": "Error", - "code": 4119 - }, - "This parameter property must have a JSDoc comment with an '@override' tag because it overrides a member in the base class '{0}'.": { - "category": "Error", - "code": 4120 - }, - "This member cannot have a JSDoc comment with an '@override' tag because its containing class '{0}' does not extend another class.": { - "category": "Error", - "code": 4121 - }, - "This member cannot have a JSDoc comment with an '@override' tag because it is not declared in the base class '{0}'.": { - "category": "Error", - "code": 4122 - }, - "This member cannot have a JSDoc comment with an 'override' tag because it is not declared in the base class '{0}'. Did you mean '{1}'?": { - "category": "Error", - "code": 4123 - }, - "Compiler option '{0}' of value '{1}' is unstable. Use nightly TypeScript to silence this error. Try updating with 'npm install -D typescript@next'.": { - "category": "Error", - "code": 4124 - }, - "Each declaration of '{0}.{1}' differs in its value, where '{2}' was expected but '{3}' was given.": { - "category": "Error", - "code": 4125 - }, - "One value of '{0}.{1}' is the string '{2}', and the other is assumed to be an unknown numeric value.": { - "category": "Error", - "code": 4126 - }, - "This member cannot have an 'override' modifier because its name is dynamic.": { - "category": "Error", - "code": 4127 - }, - "This member cannot have a JSDoc comment with an '@override' tag because its name is dynamic.": { - "category": "Error", - "code": 4128 - }, - - "The current host does not support the '{0}' option.": { - "category": "Error", - "code": 5001 - }, - "Cannot find the common subdirectory path for the input files.": { - "category": "Error", - "code": 5009 - }, - "File specification cannot end in a recursive directory wildcard ('**'): '{0}'.": { - "category": "Error", - "code": 5010 - }, - "The common source directory of '{0}' is '{1}'. The 'rootDir' setting must be explicitly set to this or another path to adjust your output's file layout.": { - "category": "Error", - "code": 5011 - }, - "Cannot read file '{0}': {1}.": { - "category": "Error", - "code": 5012 - }, - "Unknown compiler option '{0}'.": { - "category": "Error", - "code": 5023 - }, - "Compiler option '{0}' requires a value of type {1}.": { - "category": "Error", - "code": 5024 - }, - "Unknown compiler option '{0}'. Did you mean '{1}'?": { - "category": "Error", - "code": 5025 - }, - "Could not write file '{0}': {1}.": { - "category": "Error", - "code": 5033 - }, - "Option 'project' cannot be mixed with source files on a command line.": { - "category": "Error", - "code": 5042 - }, - "Option 'isolatedModules' can only be used when either option '--module' is provided or option 'target' is 'ES2015' or higher.": { - "category": "Error", - "code": 5047 - }, - "Option '{0} can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided.": { - "category": "Error", - "code": 5051 - }, - "Option '{0}' cannot be specified without specifying option '{1}'.": { - "category": "Error", - "code": 5052 - }, - "Option '{0}' cannot be specified with option '{1}'.": { - "category": "Error", - "code": 5053 - }, - "A 'tsconfig.json' file is already defined at: '{0}'.": { - "category": "Error", - "code": 5054 - }, - "Cannot write file '{0}' because it would overwrite input file.": { - "category": "Error", - "code": 5055 - }, - "Cannot write file '{0}' because it would be overwritten by multiple input files.": { - "category": "Error", - "code": 5056 - }, - "Cannot find a tsconfig.json file at the specified directory: '{0}'.": { - "category": "Error", - "code": 5057 - }, - "The specified path does not exist: '{0}'.": { - "category": "Error", - "code": 5058 - }, - "Invalid value for '--reactNamespace'. '{0}' is not a valid identifier.": { - "category": "Error", - "code": 5059 - }, - "Pattern '{0}' can have at most one '*' character.": { - "category": "Error", - "code": 5061 - }, - "Substitution '{0}' in pattern '{1}' can have at most one '*' character.": { - "category": "Error", - "code": 5062 - }, - "Substitutions for pattern '{0}' should be an array.": { - "category": "Error", - "code": 5063 - }, - "Substitution '{0}' for pattern '{1}' has incorrect type, expected 'string', got '{2}'.": { - "category": "Error", - "code": 5064 - }, - "File specification cannot contain a parent directory ('..') that appears after a recursive directory wildcard ('**'): '{0}'.": { - "category": "Error", - "code": 5065 - }, - "Substitutions for pattern '{0}' shouldn't be an empty array.": { - "category": "Error", - "code": 5066 - }, - "Invalid value for 'jsxFactory'. '{0}' is not a valid identifier or qualified-name.": { - "category": "Error", - "code": 5067 - }, - "Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig.": { - "category": "Error", - "code": 5068 - }, - "Option '{0}' cannot be specified without specifying option '{1}' or option '{2}'.": { - "category": "Error", - "code": 5069 - }, - "Option '--resolveJsonModule' cannot be specified when 'moduleResolution' is set to 'classic'.": { - "category": "Error", - "code": 5070 - }, - "Option '--resolveJsonModule' cannot be specified when 'module' is set to 'none', 'system', or 'umd'.": { - "category": "Error", - "code": 5071 - }, - "Unknown build option '{0}'.": { - "category": "Error", - "code": 5072 - }, - "Build option '{0}' requires a value of type {1}.": { - "category": "Error", - "code": 5073 - }, - "Option '--incremental' can only be specified using tsconfig, emitting to single file or when option '--tsBuildInfoFile' is specified.": { - "category": "Error", - "code": 5074 - }, - "'{0}' is assignable to the constraint of type '{1}', but '{1}' could be instantiated with a different subtype of constraint '{2}'.": { - "category": "Error", - "code": 5075 - }, - "'{0}' and '{1}' operations cannot be mixed without parentheses.": { - "category": "Error", - "code": 5076 - }, - "Unknown build option '{0}'. Did you mean '{1}'?": { - "category": "Error", - "code": 5077 - }, - "Unknown watch option '{0}'.": { - "category": "Error", - "code": 5078 - }, - "Unknown watch option '{0}'. Did you mean '{1}'?": { - "category": "Error", - "code": 5079 - }, - "Watch option '{0}' requires a value of type {1}.": { - "category": "Error", - "code": 5080 - }, - "Cannot find a tsconfig.json file at the current directory: {0}.": { - "category": "Error", - "code": 5081 - }, - "'{0}' could be instantiated with an arbitrary type which could be unrelated to '{1}'.": { - "category": "Error", - "code": 5082 - }, - "Cannot read file '{0}'.": { - "category": "Error", - "code": 5083 - }, - "A tuple member cannot be both optional and rest.": { - "category": "Error", - "code": 5085 - }, - "A labeled tuple element is declared as optional with a question mark after the name and before the colon, rather than after the type.": { - "category": "Error", - "code": 5086 - }, - "A labeled tuple element is declared as rest with a '...' before the name, rather than before the type.": { - "category": "Error", - "code": 5087 - }, - "The inferred type of '{0}' references a type with a cyclic structure which cannot be trivially serialized. A type annotation is necessary.": { - "category": "Error", - "code": 5088 - }, - "Option '{0}' cannot be specified when option 'jsx' is '{1}'.": { - "category": "Error", - "code": 5089 - }, - "Non-relative paths are not allowed when 'baseUrl' is not set. Did you forget a leading './'?": { - "category": "Error", - "code": 5090 - }, - "Option 'preserveConstEnums' cannot be disabled when '{0}' is enabled.": { - "category": "Error", - "code": 5091 - }, - "The root value of a '{0}' file must be an object.": { - "category": "Error", - "code": 5092 - }, - "Compiler option '--{0}' may only be used with '--build'.": { - "category": "Error", - "code": 5093 - }, - "Compiler option '--{0}' may not be used with '--build'.": { - "category": "Error", - "code": 5094 - }, - "Option '{0}' can only be used when 'module' is set to 'preserve', 'commonjs', or 'es2015' or later.": { - "category": "Error", - "code": 5095 - }, - "Option 'allowImportingTsExtensions' can only be used when one of 'noEmit', 'emitDeclarationOnly', or 'rewriteRelativeImportExtensions' is set.": { - "category": "Error", - "code": 5096 - }, - "An import path can only end with a '{0}' extension when 'allowImportingTsExtensions' is enabled.": { - "category": "Error", - "code": 5097 - }, - "Option '{0}' can only be used when 'moduleResolution' is set to 'node16', 'nodenext', or 'bundler'.": { - "category": "Error", - "code": 5098 - }, - "Option '{0}' is deprecated and will stop functioning in TypeScript {1}. Specify compilerOption '\"ignoreDeprecations\": \"{2}\"' to silence this error.": { - "category": "Error", - "code": 5101 - }, - "Option '{0}' has been removed. Please remove it from your configuration.": { - "category": "Error", - "code": 5102 - }, - "Invalid value for '--ignoreDeprecations'.": { - "category": "Error", - "code": 5103 - }, - "Option '{0}' is redundant and cannot be specified with option '{1}'.": { - "category": "Error", - "code": 5104 - }, - "Option 'verbatimModuleSyntax' cannot be used when 'module' is set to 'UMD', 'AMD', or 'System'.": { - "category": "Error", - "code": 5105 - }, - "Use '{0}' instead.": { - "category": "Message", - "code": 5106 - }, - "Option '{0}={1}' is deprecated and will stop functioning in TypeScript {2}. Specify compilerOption '\"ignoreDeprecations\": \"{3}\"' to silence this error.": { - "category": "Error", - "code": 5107 - }, - "Option '{0}={1}' has been removed. Please remove it from your configuration.": { - "category": "Error", - "code": 5108 - }, - "Option 'moduleResolution' must be set to '{0}' (or left unspecified) when option 'module' is set to '{1}'.": { - "category": "Error", - "code": 5109 - }, - "Option 'module' must be set to '{0}' when option 'moduleResolution' is set to '{1}'.": { - "category": "Error", - "code": 5110 - }, - "Visit https://aka.ms/ts6 for migration information.": { - "category": "Message", - "code": 5111 - }, - "tsconfig.json is present but will not be loaded if files are specified on commandline. Use '--ignoreConfig' to skip this error.": { - "category": "Error", - "code": 5112 - }, - - "Generates a sourcemap for each corresponding '.d.ts' file.": { - "category": "Message", - "code": 6000 - }, - "Concatenate and emit output to single file.": { - "category": "Message", - "code": 6001 - }, - "Generates corresponding '.d.ts' file.": { - "category": "Message", - "code": 6002 - }, - "Specify the location where debugger should locate TypeScript files instead of source locations.": { - "category": "Message", - "code": 6004 - }, - "Watch input files.": { - "category": "Message", - "code": 6005 - }, - "Redirect output structure to the directory.": { - "category": "Message", - "code": 6006 - }, - "Do not erase const enum declarations in generated code.": { - "category": "Message", - "code": 6007 - }, - "Do not emit outputs if any errors were reported.": { - "category": "Message", - "code": 6008 - }, - "Do not emit comments to output.": { - "category": "Message", - "code": 6009 - }, - "Do not emit outputs.": { - "category": "Message", - "code": 6010 - }, - "Allow default imports from modules with no default export. This does not affect code emit, just typechecking.": { - "category": "Message", - "code": 6011 - }, - "Skip type checking of declaration files.": { - "category": "Message", - "code": 6012 - }, - "Do not resolve the real path of symlinks.": { - "category": "Message", - "code": 6013 - }, - "Only emit '.d.ts' declaration files.": { - "category": "Message", - "code": 6014 - }, - "Specify ECMAScript target version.": { - "category": "Message", - "code": 6015 - }, - "Specify module code generation.": { - "category": "Message", - "code": 6016 - }, - "Print this message.": { - "category": "Message", - "code": 6017 - }, - "Print the compiler's version.": { - "category": "Message", - "code": 6019 - }, - "Compile the project given the path to its configuration file, or to a folder with a 'tsconfig.json'.": { - "category": "Message", - "code": 6020 - }, - "Syntax: {0}": { - "category": "Message", - "code": 6023 - }, - "options": { - "category": "Message", - "code": 6024 - }, - "file": { - "category": "Message", - "code": 6025 - }, - "Examples: {0}": { - "category": "Message", - "code": 6026 - }, - "Options:": { - "category": "Message", - "code": 6027 - }, - "Version {0}": { - "category": "Message", - "code": 6029 - }, - "Insert command line options and files from a file.": { - "category": "Message", - "code": 6030 - }, - "Starting compilation in watch mode...": { - "category": "Message", - "code": 6031 - }, - "File change detected. Starting incremental compilation...": { - "category": "Message", - "code": 6032 - }, - "KIND": { - "category": "Message", - "code": 6034 - }, - "FILE": { - "category": "Message", - "code": 6035 - }, - "VERSION": { - "category": "Message", - "code": 6036 - }, - "LOCATION": { - "category": "Message", - "code": 6037 - }, - "DIRECTORY": { - "category": "Message", - "code": 6038 - }, - "STRATEGY": { - "category": "Message", - "code": 6039 - }, - "FILE OR DIRECTORY": { - "category": "Message", - "code": 6040 - }, - "Errors Files": { - "_locale_notes": "There is a double space, and the order cannot be changed (they're table headings) ^", - "category": "Message", - "code": 6041 - }, - "Generates corresponding '.map' file.": { - "category": "Message", - "code": 6043 - }, - "Compiler option '{0}' expects an argument.": { - "category": "Error", - "code": 6044 - }, - "Unterminated quoted string in response file '{0}'.": { - "category": "Error", - "code": 6045 - }, - "Argument for '{0}' option must be: {1}.": { - "category": "Error", - "code": 6046 - }, - "Locale must be of the form or -. For example '{0}' or '{1}'.": { - "category": "Error", - "code": 6048 - }, - "Unable to open file '{0}'.": { - "category": "Error", - "code": 6050 - }, - "Corrupted locale file {0}.": { - "category": "Error", - "code": 6051 - }, - "Raise error on expressions and declarations with an implied 'any' type.": { - "category": "Message", - "code": 6052 - }, - "File '{0}' not found.": { - "category": "Error", - "code": 6053 - }, - "File '{0}' has an unsupported extension. The only supported extensions are {1}.": { - "category": "Error", - "code": 6054 - }, - "Suppress noImplicitAny errors for indexing objects lacking index signatures.": { - "category": "Message", - "code": 6055 - }, - "Do not emit declarations for code that has an '@internal' annotation.": { - "category": "Message", - "code": 6056 - }, - "Specify the root directory of input files. Use to control the output directory structure with --outDir.": { - "category": "Message", - "code": 6058 - }, - "File '{0}' is not under 'rootDir' '{1}'. 'rootDir' is expected to contain all source files.": { - "category": "Error", - "code": 6059 - }, - "Specify the end of line sequence to be used when emitting files: 'CRLF' (dos) or 'LF' (unix).": { - "category": "Message", - "code": 6060 - }, - "NEWLINE": { - "category": "Message", - "code": 6061 - }, - "Option '{0}' can only be specified in 'tsconfig.json' file or set to 'null' on command line.": { - "category": "Error", - "code": 6064 - }, - "Enables experimental support for ES7 decorators.": { - "category": "Message", - "code": 6065 - }, - "Enables experimental support for emitting type metadata for decorators.": { - "category": "Message", - "code": 6066 - }, - "Initializes a TypeScript project and creates a tsconfig.json file.": { - "category": "Message", - "code": 6070 - }, - "Successfully created a tsconfig.json file.": { - "category": "Message", - "code": 6071 - }, - "Suppress excess property checks for object literals.": { - "category": "Message", - "code": 6072 - }, - "Stylize errors and messages using color and context (experimental).": { - "category": "Message", - "code": 6073 - }, - "Do not report errors on unused labels.": { - "category": "Message", - "code": 6074 - }, - "Report error when not all code paths in function return a value.": { - "category": "Message", - "code": 6075 - }, - "Report errors for fallthrough cases in switch statement.": { - "category": "Message", - "code": 6076 - }, - "Do not report errors on unreachable code.": { - "category": "Message", - "code": 6077 - }, - "Disallow inconsistently-cased references to the same file.": { - "category": "Message", - "code": 6078 - }, - "Specify library files to be included in the compilation.": { - "category": "Message", - "code": 6079 - }, - "Specify JSX code generation.": { - "category": "Message", - "code": 6080 - }, - "Only 'amd' and 'system' modules are supported alongside --{0}.": { - "category": "Error", - "code": 6082 - }, - "Base directory to resolve non-absolute module names.": { - "category": "Message", - "code": 6083 - }, - "[Deprecated] Use '--jsxFactory' instead. Specify the object invoked for createElement when targeting 'react' JSX emit": { - "category": "Message", - "code": 6084 - }, - "Enable tracing of the name resolution process.": { - "category": "Message", - "code": 6085 - }, - "======== Resolving module '{0}' from '{1}'. ========": { - "category": "Message", - "code": 6086 - }, - "Explicitly specified module resolution kind: '{0}'.": { - "category": "Message", - "code": 6087 - }, - "Module resolution kind is not specified, using '{0}'.": { - "category": "Message", - "code": 6088 - }, - "======== Module name '{0}' was successfully resolved to '{1}'. ========": { - "category": "Message", - "code": 6089 - }, - "======== Module name '{0}' was not resolved. ========": { - "category": "Message", - "code": 6090 - }, - "'paths' option is specified, looking for a pattern to match module name '{0}'.": { - "category": "Message", - "code": 6091 - }, - "Module name '{0}', matched pattern '{1}'.": { - "category": "Message", - "code": 6092 - }, - "Trying substitution '{0}', candidate module location: '{1}'.": { - "category": "Message", - "code": 6093 - }, - "Resolving module name '{0}' relative to base url '{1}' - '{2}'.": { - "category": "Message", - "code": 6094 - }, - "Loading module as file / folder, candidate module location '{0}', target file types: {1}.": { - "category": "Message", - "code": 6095 - }, - "File '{0}' does not exist.": { - "category": "Message", - "code": 6096 - }, - "File '{0}' exists - use it as a name resolution result.": { - "category": "Message", - "code": 6097 - }, - "Loading module '{0}' from 'node_modules' folder, target file types: {1}.": { - "category": "Message", - "code": 6098 - }, - "Found 'package.json' at '{0}'.": { - "category": "Message", - "code": 6099 - }, - "'package.json' does not have a '{0}' field.": { - "category": "Message", - "code": 6100 - }, - "'package.json' has '{0}' field '{1}' that references '{2}'.": { - "category": "Message", - "code": 6101 - }, - "Allow javascript files to be compiled.": { - "category": "Message", - "code": 6102 - }, - "Checking if '{0}' is the longest matching prefix for '{1}' - '{2}'.": { - "category": "Message", - "code": 6104 - }, - "Expected type of '{0}' field in 'package.json' to be '{1}', got '{2}'.": { - "category": "Message", - "code": 6105 - }, - "'baseUrl' option is set to '{0}', using this value to resolve non-relative module name '{1}'.": { - "category": "Message", - "code": 6106 - }, - "'rootDirs' option is set, using it to resolve relative module name '{0}'.": { - "category": "Message", - "code": 6107 - }, - "Longest matching prefix for '{0}' is '{1}'.": { - "category": "Message", - "code": 6108 - }, - "Loading '{0}' from the root dir '{1}', candidate location '{2}'.": { - "category": "Message", - "code": 6109 - }, - "Trying other entries in 'rootDirs'.": { - "category": "Message", - "code": 6110 - }, - "Module resolution using 'rootDirs' has failed.": { - "category": "Message", - "code": 6111 - }, - "Do not emit 'use strict' directives in module output.": { - "category": "Message", - "code": 6112 - }, - "Enable strict null checks.": { - "category": "Message", - "code": 6113 - }, - "Unknown option 'excludes'. Did you mean 'exclude'?": { - "category": "Error", - "code": 6114 - }, - "Raise error on 'this' expressions with an implied 'any' type.": { - "category": "Message", - "code": 6115 - }, - "======== Resolving type reference directive '{0}', containing file '{1}', root directory '{2}'. ========": { - "category": "Message", - "code": 6116 - }, - "======== Type reference directive '{0}' was successfully resolved to '{1}', primary: {2}. ========": { - "category": "Message", - "code": 6119 - }, - "======== Type reference directive '{0}' was not resolved. ========": { - "category": "Message", - "code": 6120 - }, - "Resolving with primary search path '{0}'.": { - "category": "Message", - "code": 6121 - }, - "Root directory cannot be determined, skipping primary search paths.": { - "category": "Message", - "code": 6122 - }, - "======== Resolving type reference directive '{0}', containing file '{1}', root directory not set. ========": { - "category": "Message", - "code": 6123 - }, - "Type declaration files to be included in compilation.": { - "category": "Message", - "code": 6124 - }, - "Looking up in 'node_modules' folder, initial location '{0}'.": { - "category": "Message", - "code": 6125 - }, - "Containing file is not specified and root directory cannot be determined, skipping lookup in 'node_modules' folder.": { - "category": "Message", - "code": 6126 - }, - "======== Resolving type reference directive '{0}', containing file not set, root directory '{1}'. ========": { - "category": "Message", - "code": 6127 - }, - "======== Resolving type reference directive '{0}', containing file not set, root directory not set. ========": { - "category": "Message", - "code": 6128 - }, - "Resolving real path for '{0}', result '{1}'.": { - "category": "Message", - "code": 6130 - }, - "Cannot compile modules using option '{0}' unless the '--module' flag is 'amd' or 'system'.": { - "category": "Error", - "code": 6131 - }, - "File name '{0}' has a '{1}' extension - stripping it.": { - "category": "Message", - "code": 6132 - }, - "'{0}' is declared but its value is never read.": { - "category": "Error", - "code": 6133, - "reportsUnnecessary": true - }, - "Report errors on unused locals.": { - "category": "Message", - "code": 6134 - }, - "Report errors on unused parameters.": { - "category": "Message", - "code": 6135 - }, - "The maximum dependency depth to search under node_modules and load JavaScript files.": { - "category": "Message", - "code": 6136 - }, - "Cannot import type declaration files. Consider importing '{0}' instead of '{1}'.": { - "category": "Error", - "code": 6137 - }, - "Property '{0}' is declared but its value is never read.": { - "category": "Error", - "code": 6138, - "reportsUnnecessary": true - }, - "Import emit helpers from 'tslib'.": { - "category": "Message", - "code": 6139 - }, - "Auto discovery for typings is enabled in project '{0}'. Running extra resolution pass for module '{1}' using cache location '{2}'.": { - "category": "Error", - "code": 6140 - }, - "Parse in strict mode and emit \"use strict\" for each source file.": { - "category": "Message", - "code": 6141 - }, - "Module '{0}' was resolved to '{1}', but '--jsx' is not set.": { - "category": "Error", - "code": 6142 - }, - "Module '{0}' was resolved as locally declared ambient module in file '{1}'.": { - "category": "Message", - "code": 6144 - }, - "Specify the JSX factory function to use when targeting 'react' JSX emit, e.g. 'React.createElement' or 'h'.": { - "category": "Message", - "code": 6146 - }, - "Resolution for module '{0}' was found in cache from location '{1}'.": { - "category": "Message", - "code": 6147 - }, - "Directory '{0}' does not exist, skipping all lookups in it.": { - "category": "Message", - "code": 6148 - }, - "Show diagnostic information.": { - "category": "Message", - "code": 6149 - }, - "Show verbose diagnostic information.": { - "category": "Message", - "code": 6150 - }, - "Emit a single file with source maps instead of having a separate file.": { - "category": "Message", - "code": 6151 - }, - "Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set.": { - "category": "Message", - "code": 6152 - }, - "Transpile each file as a separate module (similar to 'ts.transpileModule').": { - "category": "Message", - "code": 6153 - }, - "Print names of generated files part of the compilation.": { - "category": "Message", - "code": 6154 - }, - "Print names of files part of the compilation.": { - "category": "Message", - "code": 6155 - }, - "The locale used when displaying messages to the user (e.g. 'en-us')": { - "category": "Message", - "code": 6156 - }, - "Do not generate custom helper functions like '__extends' in compiled output.": { - "category": "Message", - "code": 6157 - }, - "Do not include the default library file (lib.d.ts).": { - "category": "Message", - "code": 6158 - }, - "Do not add triple-slash references or imported modules to the list of compiled files.": { - "category": "Message", - "code": 6159 - }, - "[Deprecated] Use '--skipLibCheck' instead. Skip type checking of default library declaration files.": { - "category": "Message", - "code": 6160 - }, - "List of folders to include type definitions from.": { - "category": "Message", - "code": 6161 - }, - "Disable size limitations on JavaScript projects.": { - "category": "Message", - "code": 6162 - }, - "The character set of the input files.": { - "category": "Message", - "code": 6163 - }, - "Skipping module '{0}' that looks like an absolute URI, target file types: {1}.": { - "category": "Message", - "code": 6164 - }, - "Do not truncate error messages.": { - "category": "Message", - "code": 6165 - }, - "Output directory for generated declaration files.": { - "category": "Message", - "code": 6166 - }, - "A series of entries which re-map imports to lookup locations relative to the 'baseUrl'.": { - "category": "Message", - "code": 6167 - }, - "List of root folders whose combined content represents the structure of the project at runtime.": { - "category": "Message", - "code": 6168 - }, - "Show all compiler options.": { - "category": "Message", - "code": 6169 - }, - "[Deprecated] Use '--outFile' instead. Concatenate and emit output to single file": { - "category": "Message", - "code": 6170 - }, - "Command-line Options": { - "category": "Message", - "code": 6171 - }, - "Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5'.": { - "category": "Message", - "code": 6179 - }, - "Enable all strict type-checking options.": { - "category": "Message", - "code": 6180 - }, - "Scoped package detected, looking in '{0}'": { - "category": "Message", - "code": 6182 - }, - "Reusing resolution of module '{0}' from '{1}' of old program, it was successfully resolved to '{2}'.": { - "category": "Message", - "code": 6183 - }, - "Reusing resolution of module '{0}' from '{1}' of old program, it was successfully resolved to '{2}' with Package ID '{3}'.": { - "category": "Message", - "code": 6184 - }, - "Enable strict checking of function types.": { - "category": "Message", - "code": 6186 - }, - "Enable strict checking of property initialization in classes.": { - "category": "Message", - "code": 6187 - }, - "Numeric separators are not allowed here.": { - "category": "Error", - "code": 6188 - }, - "Multiple consecutive numeric separators are not permitted.": { - "category": "Error", - "code": 6189 - }, - "Whether to keep outdated console output in watch mode instead of clearing the screen.": { - "category": "Message", - "code": 6191 - }, - "All imports in import declaration are unused.": { - "category": "Error", - "code": 6192, - "reportsUnnecessary": true - }, - "Found 1 error. Watching for file changes.": { - "category": "Message", - "code": 6193 - }, - "Found {0} errors. Watching for file changes.": { - "category": "Message", - "code": 6194 - }, - "Resolve 'keyof' to string valued property names only (no numbers or symbols).": { - "category": "Message", - "code": 6195 - }, - "'{0}' is declared but never used.": { - "category": "Error", - "code": 6196, - "reportsUnnecessary": true - }, - "Include modules imported with '.json' extension": { - "category": "Message", - "code": 6197 - }, - "All destructured elements are unused.": { - "category": "Error", - "code": 6198, - "reportsUnnecessary": true - }, - "All variables are unused.": { - "category": "Error", - "code": 6199, - "reportsUnnecessary": true - }, - "Definitions of the following identifiers conflict with those in another file: {0}": { - "category": "Error", - "code": 6200 - }, - "Conflicts are in this file.": { - "category": "Message", - "code": 6201 - }, - "Project references may not form a circular graph. Cycle detected: {0}": { - "category": "Error", - "code": 6202 - }, - "'{0}' was also declared here.": { - "category": "Message", - "code": 6203 - }, - "and here.": { - "category": "Message", - "code": 6204 - }, - "All type parameters are unused.": { - "category": "Error", - "code": 6205 - }, - "'package.json' has a 'typesVersions' field with version-specific path mappings.": { - "category": "Message", - "code": 6206 - }, - "'package.json' does not have a 'typesVersions' entry that matches version '{0}'.": { - "category": "Message", - "code": 6207 - }, - "'package.json' has a 'typesVersions' entry '{0}' that matches compiler version '{1}', looking for a pattern to match module name '{2}'.": { - "category": "Message", - "code": 6208 - }, - "'package.json' has a 'typesVersions' entry '{0}' that is not a valid semver range.": { - "category": "Message", - "code": 6209 - }, - "An argument for '{0}' was not provided.": { - "category": "Message", - "code": 6210 - }, - "An argument matching this binding pattern was not provided.": { - "category": "Message", - "code": 6211 - }, - "Did you mean to call this expression?": { - "category": "Message", - "code": 6212 - }, - "Did you mean to use 'new' with this expression?": { - "category": "Message", - "code": 6213 - }, - "Enable strict 'bind', 'call', and 'apply' methods on functions.": { - "category": "Message", - "code": 6214 - }, - "Using compiler options of project reference redirect '{0}'.": { - "category": "Message", - "code": 6215 - }, - "Found 1 error.": { - "category": "Message", - "code": 6216 - }, - "Found {0} errors.": { - "category": "Message", - "code": 6217 - }, - "======== Module name '{0}' was successfully resolved to '{1}' with Package ID '{2}'. ========": { - "category": "Message", - "code": 6218 - }, - "======== Type reference directive '{0}' was successfully resolved to '{1}' with Package ID '{2}', primary: {3}. ========": { - "category": "Message", - "code": 6219 - }, - "'package.json' had a falsy '{0}' field.": { - "category": "Message", - "code": 6220 - }, - "Disable use of source files instead of declaration files from referenced projects.": { - "category": "Message", - "code": 6221 - }, - "Emit class fields with Define instead of Set.": { - "category": "Message", - "code": 6222 - }, - "Generates a CPU profile.": { - "category": "Message", - "code": 6223 - }, - "Disable solution searching for this project.": { - "category": "Message", - "code": 6224 - }, - "Specify strategy for watching file: 'FixedPollingInterval' (default), 'PriorityPollingInterval', 'DynamicPriorityPolling', 'FixedChunkSizePolling', 'UseFsEvents', 'UseFsEventsOnParentDirectory'.": { - "category": "Message", - "code": 6225 - }, - "Specify strategy for watching directory on platforms that don't support recursive watching natively: 'UseFsEvents' (default), 'FixedPollingInterval', 'DynamicPriorityPolling', 'FixedChunkSizePolling'.": { - "category": "Message", - "code": 6226 - }, - "Specify strategy for creating a polling watch when it fails to create using file system events: 'FixedInterval' (default), 'PriorityInterval', 'DynamicPriority', 'FixedChunkSize'.": { - "category": "Message", - "code": 6227 - }, - "Tag '{0}' expects at least '{1}' arguments, but the JSX factory '{2}' provides at most '{3}'.": { - "category": "Error", - "code": 6229 - }, - "Option '{0}' can only be specified in 'tsconfig.json' file or set to 'false' or 'null' on command line.": { - "category": "Error", - "code": 6230 - }, - "Could not resolve the path '{0}' with the extensions: {1}.": { - "category": "Error", - "code": 6231 - }, - "Declaration augments declaration in another file. This cannot be serialized.": { - "category": "Error", - "code": 6232 - }, - "This is the declaration being augmented. Consider moving the augmenting declaration into the same file.": { - "category": "Error", - "code": 6233 - }, - "This expression is not callable because it is a 'get' accessor. Did you mean to use it without '()'?": { - "category": "Error", - "code": 6234 - }, - "Disable loading referenced projects.": { - "category": "Message", - "code": 6235 - }, - "Arguments for the rest parameter '{0}' were not provided.": { - "category": "Error", - "code": 6236 - }, - "Generates an event trace and a list of types.": { - "category": "Message", - "code": 6237 - }, - "Specify the module specifier to be used to import the 'jsx' and 'jsxs' factory functions from. eg, react": { - "category": "Error", - "code": 6238 - }, - "File '{0}' exists according to earlier cached lookups.": { - "category": "Message", - "code": 6239 - }, - "File '{0}' does not exist according to earlier cached lookups.": { - "category": "Message", - "code": 6240 - }, - "Resolution for type reference directive '{0}' was found in cache from location '{1}'.": { - "category": "Message", - "code": 6241 - }, - "======== Resolving type reference directive '{0}', containing file '{1}'. ========": { - "category": "Message", - "code": 6242 - }, - "Interpret optional property types as written, rather than adding 'undefined'.": { - "category": "Message", - "code": 6243 - }, - "Modules": { - "category": "Message", - "code": 6244 - }, - "File Management": { - "category": "Message", - "code": 6245 - }, - "Emit": { - "category": "Message", - "code": 6246 - }, - "JavaScript Support": { - "category": "Message", - "code": 6247 - }, - "Type Checking": { - "category": "Message", - "code": 6248 - }, - "Editor Support": { - "category": "Message", - "code": 6249 - }, - "Watch and Build Modes": { - "category": "Message", - "code": 6250 - }, - "Compiler Diagnostics": { - "category": "Message", - "code": 6251 - }, - "Interop Constraints": { - "category": "Message", - "code": 6252 - }, - "Backwards Compatibility": { - "category": "Message", - "code": 6253 - }, - "Language and Environment": { - "category": "Message", - "code": 6254 - }, - "Projects": { - "category": "Message", - "code": 6255 - }, - "Output Formatting": { - "category": "Message", - "code": 6256 - }, - "Completeness": { - "category": "Message", - "code": 6257 - }, - "'{0}' should be set inside the 'compilerOptions' object of the config json file": { - "category": "Error", - "code": 6258 - }, - "Found 1 error in {0}": { - "category": "Message", - "code": 6259 - }, - "Found {0} errors in the same file, starting at: {1}": { - "category": "Message", - "code": 6260 - }, - "Found {0} errors in {1} files.": { - "category": "Message", - "code": 6261 - }, - "File name '{0}' has a '{1}' extension - looking up '{2}' instead.": { - "category": "Message", - "code": 6262 - }, - "Module '{0}' was resolved to '{1}', but '--allowArbitraryExtensions' is not set.": { - "category": "Error", - "code": 6263 - }, - "Enable importing files with any extension, provided a declaration file is present.": { - "category": "Message", - "code": 6264 - }, - "Resolving type reference directive for program that specifies custom typeRoots, skipping lookup in 'node_modules' folder.": { - "category": "Message", - "code": 6265 - }, - "Option '{0}' can only be specified on command line.": { - "category": "Error", - "code": 6266 - }, - - "Directory '{0}' has no containing package.json scope. Imports will not resolve.": { - "category": "Message", - "code": 6270 - }, - "Import specifier '{0}' does not exist in package.json scope at path '{1}'.": { - "category": "Message", - "code": 6271 - }, - "Invalid import specifier '{0}' has no possible resolutions.": { - "category": "Message", - "code": 6272 - }, - "package.json scope '{0}' has no imports defined.": { - "category": "Message", - "code": 6273 - }, - "package.json scope '{0}' explicitly maps specifier '{1}' to null.": { - "category": "Message", - "code": 6274 - }, - "package.json scope '{0}' has invalid type for target of specifier '{1}'": { - "category": "Message", - "code": 6275 - }, - "Export specifier '{0}' does not exist in package.json scope at path '{1}'.": { - "category": "Message", - "code": 6276 - }, - "Resolution of non-relative name failed; trying with modern Node resolution features disabled to see if npm library needs configuration update.": { - "category": "Message", - "code": 6277 - }, - "There are types at '{0}', but this result could not be resolved when respecting package.json \"exports\". The '{1}' library may need to update its package.json or typings.": { - "category": "Message", - "code": 6278 - }, - "Resolution of non-relative name failed; trying with '--moduleResolution bundler' to see if project may need configuration update.": { - "category": "Message", - "code": 6279 - }, - "There are types at '{0}', but this result could not be resolved under your current 'moduleResolution' setting. Consider updating to 'node16', 'nodenext', or 'bundler'.": { - "category": "Message", - "code": 6280 - }, - "'package.json' has a 'peerDependencies' field.": { - "category": "Message", - "code": 6281 - }, - "Found peerDependency '{0}' with '{1}' version.": { - "category": "Message", - "code": 6282 - }, - "Failed to find peerDependency '{0}'.": { - "category": "Message", - "code": 6283 - }, - "File Layout": { - "category": "Message", - "code": 6284 - }, - "Environment Settings": { - "category": "Message", - "code": 6285 - }, - "See also https://aka.ms/tsconfig/module": { - "category": "Message", - "code": 6286 - }, - "For nodejs:": { - "category": "Message", - "code": 6287 - }, - "and npm install -D @types/node": { - "category": "Message", - "code": 6290 - }, - "Other Outputs": { - "category": "Message", - "code": 6291 - }, - "Stricter Typechecking Options": { - "category": "Message", - "code": 6292 - }, - "Style Options": { - "category": "Message", - "code": 6293 - }, - "Recommended Options": { - "category": "Message", - "code": 6294 - }, - - "Enable project compilation": { - "category": "Message", - "code": 6302 - }, - "Composite projects may not disable declaration emit.": { - "category": "Error", - "code": 6304 - }, - "Output file '{0}' has not been built from source file '{1}'.": { - "category": "Error", - "code": 6305 - }, - "Referenced project '{0}' must have setting \"composite\": true.": { - "category": "Error", - "code": 6306 - }, - "File '{0}' is not listed within the file list of project '{1}'. Projects must list all files or use an 'include' pattern.": { - "category": "Error", - "code": 6307 - }, - "Referenced project '{0}' may not disable emit.": { - "category": "Error", - "code": 6310 - }, - "Project '{0}' is out of date because output '{1}' is older than input '{2}'": { - "category": "Message", - "code": 6350 - }, - "Project '{0}' is up to date because newest input '{1}' is older than output '{2}'": { - "category": "Message", - "code": 6351 - }, - "Project '{0}' is out of date because output file '{1}' does not exist": { - "category": "Message", - "code": 6352 - }, - "Project '{0}' is out of date because its dependency '{1}' is out of date": { - "category": "Message", - "code": 6353 - }, - "Project '{0}' is up to date with .d.ts files from its dependencies": { - "category": "Message", - "code": 6354 - }, - "Projects in this build: {0}": { - "category": "Message", - "code": 6355 - }, - "A non-dry build would delete the following files: {0}": { - "category": "Message", - "code": 6356 - }, - "A non-dry build would build project '{0}'": { - "category": "Message", - "code": 6357 - }, - "Building project '{0}'...": { - "category": "Message", - "code": 6358 - }, - "Updating output timestamps of project '{0}'...": { - "category": "Message", - "code": 6359 - }, - "Project '{0}' is up to date": { - "category": "Message", - "code": 6361 - }, - "Skipping build of project '{0}' because its dependency '{1}' has errors": { - "category": "Message", - "code": 6362 - }, - "Project '{0}' can't be built because its dependency '{1}' has errors": { - "category": "Message", - "code": 6363 - }, - "Build one or more projects and their dependencies, if out of date": { - "category": "Message", - "code": 6364 - }, - "Delete the outputs of all projects.": { - "category": "Message", - "code": 6365 - }, - "Show what would be built (or deleted, if specified with '--clean')": { - "category": "Message", - "code": 6367 - }, - "Option '--build' must be the first command line argument.": { - "category": "Error", - "code": 6369 - }, - "Options '{0}' and '{1}' cannot be combined.": { - "category": "Error", - "code": 6370 - }, - "Updating unchanged output timestamps of project '{0}'...": { - "category": "Message", - "code": 6371 - }, - "A non-dry build would update timestamps for output of project '{0}'": { - "category": "Message", - "code": 6374 - }, - "Cannot write file '{0}' because it will overwrite '.tsbuildinfo' file generated by referenced project '{1}'": { - "category": "Error", - "code": 6377 - }, - "Composite projects may not disable incremental compilation.": { - "category": "Error", - "code": 6379 - }, - "Specify file to store incremental compilation information": { - "category": "Message", - "code": 6380 - }, - "Project '{0}' is out of date because output for it was generated with version '{1}' that differs with current version '{2}'": { - "category": "Message", - "code": 6381 - }, - "Skipping build of project '{0}' because its dependency '{1}' was not built": { - "category": "Message", - "code": 6382 - }, - "Project '{0}' can't be built because its dependency '{1}' was not built": { - "category": "Message", - "code": 6383 - }, - "Have recompiles in '--incremental' and '--watch' assume that changes within a file will only affect files directly depending on it.": { - "category": "Message", - "code": 6384 - }, - "'{0}' is deprecated.": { - "category": "Suggestion", - "code": 6385, - "reportsDeprecated": true - }, - "Performance timings for '--diagnostics' or '--extendedDiagnostics' are not available in this session. A native implementation of the Web Performance API could not be found.": { - "category": "Message", - "code": 6386 - }, - "The signature '{0}' of '{1}' is deprecated.": { - "category": "Suggestion", - "code": 6387, - "reportsDeprecated": true - }, - "Project '{0}' is being forcibly rebuilt": { - "category": "Message", - "code": 6388 - }, - "Reusing resolution of module '{0}' from '{1}' of old program, it was not resolved.": { - "category": "Message", - "code": 6389 - }, - "Reusing resolution of type reference directive '{0}' from '{1}' of old program, it was successfully resolved to '{2}'.": { - "category": "Message", - "code": 6390 - }, - "Reusing resolution of type reference directive '{0}' from '{1}' of old program, it was successfully resolved to '{2}' with Package ID '{3}'.": { - "category": "Message", - "code": 6391 - }, - "Reusing resolution of type reference directive '{0}' from '{1}' of old program, it was not resolved.": { - "category": "Message", - "code": 6392 - }, - "Reusing resolution of module '{0}' from '{1}' found in cache from location '{2}', it was successfully resolved to '{3}'.": { - "category": "Message", - "code": 6393 - }, - "Reusing resolution of module '{0}' from '{1}' found in cache from location '{2}', it was successfully resolved to '{3}' with Package ID '{4}'.": { - "category": "Message", - "code": 6394 - }, - "Reusing resolution of module '{0}' from '{1}' found in cache from location '{2}', it was not resolved.": { - "category": "Message", - "code": 6395 - }, - "Reusing resolution of type reference directive '{0}' from '{1}' found in cache from location '{2}', it was successfully resolved to '{3}'.": { - "category": "Message", - "code": 6396 - }, - "Reusing resolution of type reference directive '{0}' from '{1}' found in cache from location '{2}', it was successfully resolved to '{3}' with Package ID '{4}'.": { - "category": "Message", - "code": 6397 - }, - "Reusing resolution of type reference directive '{0}' from '{1}' found in cache from location '{2}', it was not resolved.": { - "category": "Message", - "code": 6398 - }, - "Project '{0}' is out of date because buildinfo file '{1}' indicates that some of the changes were not emitted": { - "category": "Message", - "code": 6399 - }, - "Project '{0}' is up to date but needs to update timestamps of output files that are older than input files": { - "category": "Message", - "code": 6400 - }, - "Project '{0}' is out of date because there was error reading file '{1}'": { - "category": "Message", - "code": 6401 - }, - "Resolving in {0} mode with conditions {1}.": { - "category": "Message", - "code": 6402 - }, - "Matched '{0}' condition '{1}'.": { - "category": "Message", - "code": 6403 - }, - "Using '{0}' subpath '{1}' with target '{2}'.": { - "category": "Message", - "code": 6404 - }, - "Saw non-matching condition '{0}'.": { - "category": "Message", - "code": 6405 - }, - "Project '{0}' is out of date because buildinfo file '{1}' indicates there is change in compilerOptions": { - "category": "Message", - "code": 6406 - }, - "Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set.": { - "category": "Message", - "code": 6407 - }, - "Use the package.json 'exports' field when resolving package imports.": { - "category": "Message", - "code": 6408 - }, - "Use the package.json 'imports' field when resolving imports.": { - "category": "Message", - "code": 6409 - }, - "Conditions to set in addition to the resolver-specific defaults when resolving imports.": { - "category": "Message", - "code": 6410 - }, - "`true` when 'moduleResolution' is 'node16', 'nodenext', or 'bundler'; otherwise `false`.": { - "category": "Message", - "code": 6411 - }, - "Project '{0}' is out of date because buildinfo file '{1}' indicates that file '{2}' was root file of compilation but not any more.": { - "category": "Message", - "code": 6412 - }, - "Entering conditional exports.": { - "category": "Message", - "code": 6413 - }, - "Resolved under condition '{0}'.": { - "category": "Message", - "code": 6414 - }, - "Failed to resolve under condition '{0}'.": { - "category": "Message", - "code": 6415 - }, - "Exiting conditional exports.": { - "category": "Message", - "code": 6416 - }, - "Searching all ancestor node_modules directories for preferred extensions: {0}.": { - "category": "Message", - "code": 6417 - }, - "Searching all ancestor node_modules directories for fallback extensions: {0}.": { - "category": "Message", - "code": 6418 - }, - "Project '{0}' is out of date because buildinfo file '{1}' indicates that program needs to report errors.": { - "category": "Message", - "code": 6419 - }, - "Project '{0}' is out of date because {1}.": { - "category": "Message", - "code": 6420 - }, - "Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files.": { - "category": "Message", - "code": 6421 - }, - - "The expected type comes from property '{0}' which is declared here on type '{1}'": { - "category": "Message", - "code": 6500 - }, - "The expected type comes from this index signature.": { - "category": "Message", - "code": 6501 - }, - "The expected type comes from the return type of this signature.": { - "category": "Message", - "code": 6502 - }, - "Print names of files that are part of the compilation and then stop processing.": { - "category": "Message", - "code": 6503 - }, - "File '{0}' is a JavaScript file. Did you mean to enable the 'allowJs' option?": { - "category": "Error", - "code": 6504 - }, - "Print names of files and the reason they are part of the compilation.": { - "category": "Message", - "code": 6505 - }, - "Consider adding a 'declare' modifier to this class.": { - "category": "Message", - "code": 6506 - }, - "Allow JavaScript files to be a part of your program. Use the 'checkJs' option to get errors from these files.": { - "category": "Message", - "code": 6600 - }, - "Allow 'import x from y' when a module doesn't have a default export.": { - "category": "Message", - "code": 6601 - }, - "Allow accessing UMD globals from modules.": { - "category": "Message", - "code": 6602 - }, - "Disable error reporting for unreachable code.": { - "category": "Message", - "code": 6603 - }, - "Disable error reporting for unused labels.": { - "category": "Message", - "code": 6604 - }, - "Ensure 'use strict' is always emitted.": { - "category": "Message", - "code": 6605 - }, - "Have recompiles in projects that use 'incremental' and 'watch' mode assume that changes within a file will only affect files directly depending on it.": { - "category": "Message", - "code": 6606 - }, - "Specify the base directory to resolve non-relative module names.": { - "category": "Message", - "code": 6607 - }, - "No longer supported. In early versions, manually set the text encoding for reading files.": { - "category": "Message", - "code": 6608 - }, - "Enable error reporting in type-checked JavaScript files.": { - "category": "Message", - "code": 6609 - }, - "Enable constraints that allow a TypeScript project to be used with project references.": { - "category": "Message", - "code": 6611 - }, - "Generate .d.ts files from TypeScript and JavaScript files in your project.": { - "category": "Message", - "code": 6612 - }, - "Specify the output directory for generated declaration files.": { - "category": "Message", - "code": 6613 - }, - "Create sourcemaps for d.ts files.": { - "category": "Message", - "code": 6614 - }, - "Output compiler performance information after building.": { - "category": "Message", - "code": 6615 - }, - "Disables inference for type acquisition by looking at filenames in a project.": { - "category": "Message", - "code": 6616 - }, - "Reduce the number of projects loaded automatically by TypeScript.": { - "category": "Message", - "code": 6617 - }, - "Remove the 20mb cap on total source code size for JavaScript files in the TypeScript language server.": { - "category": "Message", - "code": 6618 - }, - "Opt a project out of multi-project reference checking when editing.": { - "category": "Message", - "code": 6619 - }, - "Disable preferring source files instead of declaration files when referencing composite projects.": { - "category": "Message", - "code": 6620 - }, - "Emit more compliant, but verbose and less performant JavaScript for iteration.": { - "category": "Message", - "code": 6621 - }, - "Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files.": { - "category": "Message", - "code": 6622 - }, - "Only output d.ts files and not JavaScript files.": { - "category": "Message", - "code": 6623 - }, - "Emit design-type metadata for decorated declarations in source files.": { - "category": "Message", - "code": 6624 - }, - "Disable the type acquisition for JavaScript projects": { - "category": "Message", - "code": 6625 - }, - "Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility.": { - "category": "Message", - "code": 6626 - }, - "Filters results from the `include` option.": { - "category": "Message", - "code": 6627 - }, - "Remove a list of directories from the watch process.": { - "category": "Message", - "code": 6628 - }, - "Remove a list of files from the watch mode's processing.": { - "category": "Message", - "code": 6629 - }, - "Enable experimental support for legacy experimental decorators.": { - "category": "Message", - "code": 6630 - }, - "Print files read during the compilation including why it was included.": { - "category": "Message", - "code": 6631 - }, - "Output more detailed compiler performance information after building.": { - "category": "Message", - "code": 6632 - }, - "Specify one or more path or node module references to base configuration files from which settings are inherited.": { - "category": "Message", - "code": 6633 - }, - "Specify what approach the watcher should use if the system runs out of native file watchers.": { - "category": "Message", - "code": 6634 - }, - "Include a list of files. This does not support glob patterns, as opposed to `include`.": { - "category": "Message", - "code": 6635 - }, - "Build all projects, including those that appear to be up to date.": { - "category": "Message", - "code": 6636 - }, - "Ensure that casing is correct in imports.": { - "category": "Message", - "code": 6637 - }, - "Emit a v8 CPU profile of the compiler run for debugging.": { - "category": "Message", - "code": 6638 - }, - "Allow importing helper functions from tslib once per project, instead of including them per-file.": { - "category": "Message", - "code": 6639 - }, - "Skip building downstream projects on error in upstream project.": { - "category": "Message", - "code": 6640 - }, - "Specify a list of glob patterns that match files to be included in compilation.": { - "category": "Message", - "code": 6641 - }, - "Save .tsbuildinfo files to allow for incremental compilation of projects.": { - "category": "Message", - "code": 6642 - }, - "Include sourcemap files inside the emitted JavaScript.": { - "category": "Message", - "code": 6643 - }, - "Include source code in the sourcemaps inside the emitted JavaScript.": { - "category": "Message", - "code": 6644 - }, - "Ensure that each file can be safely transpiled without relying on other imports.": { - "category": "Message", - "code": 6645 - }, - "Specify what JSX code is generated.": { - "category": "Message", - "code": 6646 - }, - "Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'.": { - "category": "Message", - "code": 6647 - }, - "Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'.": { - "category": "Message", - "code": 6648 - }, - "Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'.": { - "category": "Message", - "code": 6649 - }, - "Make keyof only return strings instead of string, numbers or symbols. Legacy option.": { - "category": "Message", - "code": 6650 - }, - "Specify a set of bundled library declaration files that describe the target runtime environment.": { - "category": "Message", - "code": 6651 - }, - "Print the names of emitted files after a compilation.": { - "category": "Message", - "code": 6652 - }, - "Print all of the files read during the compilation.": { - "category": "Message", - "code": 6653 - }, - "Set the language of the messaging from TypeScript. This does not affect emit.": { - "category": "Message", - "code": 6654 - }, - "Specify the location where debugger should locate map files instead of generated locations.": { - "category": "Message", - "code": 6655 - }, - "Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'.": { - "category": "Message", - "code": 6656 - }, - "Specify what module code is generated.": { - "category": "Message", - "code": 6657 - }, - "Specify how TypeScript looks up a file from a given module specifier.": { - "category": "Message", - "code": 6658 - }, - "Set the newline character for emitting files.": { - "category": "Message", - "code": 6659 - }, - "Disable emitting files from a compilation.": { - "category": "Message", - "code": 6660 - }, - "Disable generating custom helper functions like '__extends' in compiled output.": { - "category": "Message", - "code": 6661 - }, - "Disable emitting files if any type checking errors are reported.": { - "category": "Message", - "code": 6662 - }, - "Disable truncating types in error messages.": { - "category": "Message", - "code": 6663 - }, - "Enable error reporting for fallthrough cases in switch statements.": { - "category": "Message", - "code": 6664 - }, - "Enable error reporting for expressions and declarations with an implied 'any' type.": { - "category": "Message", - "code": 6665 - }, - "Ensure overriding members in derived classes are marked with an override modifier.": { - "category": "Message", - "code": 6666 - }, - "Enable error reporting for codepaths that do not explicitly return in a function.": { - "category": "Message", - "code": 6667 - }, - "Enable error reporting when 'this' is given the type 'any'.": { - "category": "Message", - "code": 6668 - }, - "Disable adding 'use strict' directives in emitted JavaScript files.": { - "category": "Message", - "code": 6669 - }, - "Disable including any library files, including the default lib.d.ts.": { - "category": "Message", - "code": 6670 - }, - "Enforces using indexed accessors for keys declared using an indexed type.": { - "category": "Message", - "code": 6671 - }, - "Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project.": { - "category": "Message", - "code": 6672 - }, - "Disable strict checking of generic signatures in function types.": { - "category": "Message", - "code": 6673 - }, - "Add 'undefined' to a type when accessed using an index.": { - "category": "Message", - "code": 6674 - }, - "Enable error reporting when local variables aren't read.": { - "category": "Message", - "code": 6675 - }, - "Raise an error when a function parameter isn't read.": { - "category": "Message", - "code": 6676 - }, - "Deprecated setting. Use 'outFile' instead.": { - "category": "Message", - "code": 6677 - }, - "Specify an output folder for all emitted files.": { - "category": "Message", - "code": 6678 - }, - "Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output.": { - "category": "Message", - "code": 6679 - }, - "Specify a set of entries that re-map imports to additional lookup locations.": { - "category": "Message", - "code": 6680 - }, - "Specify a list of language service plugins to include.": { - "category": "Message", - "code": 6681 - }, - "Disable erasing 'const enum' declarations in generated code.": { - "category": "Message", - "code": 6682 - }, - "Disable resolving symlinks to their realpath. This correlates to the same flag in node.": { - "category": "Message", - "code": 6683 - }, - "Disable wiping the console in watch mode.": { - "category": "Message", - "code": 6684 - }, - "Enable color and formatting in TypeScript's output to make compiler errors easier to read.": { - "category": "Message", - "code": 6685 - }, - "Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit.": { - "category": "Message", - "code": 6686 - }, - "Specify an array of objects that specify paths for projects. Used in project references.": { - "category": "Message", - "code": 6687 - }, - "Disable emitting comments.": { - "category": "Message", - "code": 6688 - }, - "Enable importing .json files.": { - "category": "Message", - "code": 6689 - }, - "Specify the root folder within your source files.": { - "category": "Message", - "code": 6690 - }, - "Allow multiple folders to be treated as one when resolving modules.": { - "category": "Message", - "code": 6691 - }, - "Skip type checking .d.ts files that are included with TypeScript.": { - "category": "Message", - "code": 6692 - }, - "Skip type checking all .d.ts files.": { - "category": "Message", - "code": 6693 - }, - "Create source map files for emitted JavaScript files.": { - "category": "Message", - "code": 6694 - }, - "Specify the root path for debuggers to find the reference source code.": { - "category": "Message", - "code": 6695 - }, - "Check that the arguments for 'bind', 'call', and 'apply' methods match the original function.": { - "category": "Message", - "code": 6697 - }, - "When assigning functions, check to ensure parameters and the return values are subtype-compatible.": { - "category": "Message", - "code": 6698 - }, - "When type checking, take into account 'null' and 'undefined'.": { - "category": "Message", - "code": 6699 - }, - "Check for class properties that are declared but not set in the constructor.": { - "category": "Message", - "code": 6700 - }, - "Disable emitting declarations that have '@internal' in their JSDoc comments.": { - "category": "Message", - "code": 6701 - }, - "Disable reporting of excess property errors during the creation of object literals.": { - "category": "Message", - "code": 6702 - }, - "Suppress 'noImplicitAny' errors when indexing objects that lack index signatures.": { - "category": "Message", - "code": 6703 - }, - "Synchronously call callbacks and update the state of directory watchers on platforms that don`t support recursive watching natively.": { - "category": "Message", - "code": 6704 - }, - "Set the JavaScript language version for emitted JavaScript and include compatible library declarations.": { - "category": "Message", - "code": 6705 - }, - "Log paths used during the 'moduleResolution' process.": { - "category": "Message", - "code": 6706 - }, - "Specify the path to .tsbuildinfo incremental compilation file.": { - "category": "Message", - "code": 6707 - }, - "Specify options for automatic acquisition of declaration files.": { - "category": "Message", - "code": 6709 - }, - "Specify multiple folders that act like './node_modules/@types'.": { - "category": "Message", - "code": 6710 - }, - "Specify type package names to be included without being referenced in a source file.": { - "category": "Message", - "code": 6711 - }, - "Emit ECMAScript-standard-compliant class fields.": { - "category": "Message", - "code": 6712 - }, - "Enable verbose logging.": { - "category": "Message", - "code": 6713 - }, - "Specify how directories are watched on systems that lack recursive file-watching functionality.": { - "category": "Message", - "code": 6714 - }, - "Specify how the TypeScript watch mode works.": { - "category": "Message", - "code": 6715 - }, - "Require undeclared properties from index signatures to use element accesses.": { - "category": "Message", - "code": 6717 - }, - "Specify emit/checking behavior for imports that are only used for types.": { - "category": "Message", - "code": 6718 - }, - "Require sufficient annotation on exports so other tools can trivially generate declaration files.": { - "category": "Message", - "code": 6719 - }, - "Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'.": { - "category": "Message", - "code": 6720 - }, - "Do not allow runtime constructs that are not part of ECMAScript.": { - "category": "Message", - "code": 6721 - }, - "Default catch clause variables as 'unknown' instead of 'any'.": { - "category": "Message", - "code": 6803 - }, - "Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting.": { - "category": "Message", - "code": 6804 - }, - "Disable full type checking (only critical parse and emit errors will be reported).": { - "category": "Message", - "code": 6805 - }, - "Check side effect imports.": { - "category": "Message", - "code": 6806 - }, - "This operation can be simplified. This shift is identical to `{0} {1} {2}`.": { - "category": "Error", - "code": 6807 - }, - "Enable lib replacement.": { - "category": "Message", - "code": 6808 - }, - "Ensure types are ordered stably and deterministically across compilations.": { - "category": "Message", - "code": 6809 - }, - - "one of:": { - "category": "Message", - "code": 6900 - }, - "one or more:": { - "category": "Message", - "code": 6901 - }, - "type:": { - "category": "Message", - "code": 6902 - }, - "default:": { - "category": "Message", - "code": 6903 - }, - - "`true`, unless `strict` is `false`": { - "category": "Message", - "code": 6905 - }, - "`false`, unless `composite` is set": { - "category": "Message", - "code": 6906 - }, - "`[\"node_modules\", \"bower_components\", \"jspm_packages\"]`, plus the value of `outDir` if one is specified.": { - "category": "Message", - "code": 6907 - }, - "`[]` if `files` is specified, otherwise `[\"**/*\"]`": { - "category": "Message", - "code": 6908 - }, - "`true` if `composite`, `false` otherwise": { - "category": "Message", - "code": 6909 - }, - "`nodenext` if `module` is `nodenext`; `node16` if `module` is `node16` or `node18`; otherwise, `bundler`.": { - "category": "Message", - "code": 69010 - }, - "Computed from the list of input files": { - "category": "Message", - "code": 6911 - }, - "Platform specific": { - "category": "Message", - "code": 6912 - }, - "You can learn about all of the compiler options at {0}": { - "category": "Message", - "code": 6913 - }, - "Including --watch, -w will start watching the current project for the file changes. Once set, you can config watch mode with:": { - "category": "Message", - "code": 6914 - }, - "Using --build, -b will make tsc behave more like a build orchestrator than a compiler. This is used to trigger building composite projects which you can learn more about at {0}": { - "category": "Message", - "code": 6915 - }, - "COMMON COMMANDS": { - "category": "Message", - "code": 6916 - }, - "ALL COMPILER OPTIONS": { - "category": "Message", - "code": 6917 - }, - "WATCH OPTIONS": { - "category": "Message", - "code": 6918 - }, - "BUILD OPTIONS": { - "category": "Message", - "code": 6919 - }, - "COMMON COMPILER OPTIONS": { - "category": "Message", - "code": 6920 - }, - "COMMAND LINE FLAGS": { - "category": "Message", - "code": 6921 - }, - "tsc: The TypeScript Compiler": { - "category": "Message", - "code": 6922 - }, - "Compiles the current project (tsconfig.json in the working directory.)": { - "category": "Message", - "code": 6923 - }, - "Ignoring tsconfig.json, compiles the specified files with default compiler options.": { - "category": "Message", - "code": 6924 - }, - "Build a composite project in the working directory.": { - "category": "Message", - "code": 6925 - }, - "Creates a tsconfig.json with the recommended settings in the working directory.": { - "category": "Message", - "code": 6926 - }, - "Compiles the TypeScript project located at the specified path.": { - "category": "Message", - "code": 6927 - }, - "An expanded version of this information, showing all possible compiler options": { - "category": "Message", - "code": 6928 - }, - "Compiles the current project, with additional settings.": { - "category": "Message", - "code": 6929 - }, - "`true` for ES2022 and above, including ESNext.": { - "category": "Message", - "code": 6930 - }, - "List of file name suffixes to search when resolving a module.": { - "category": "Error", - "code": 6931 - }, - "`false`, unless `checkJs` is set": { - "category": "Message", - "code": 6932 - }, - - "Variable '{0}' implicitly has an '{1}' type.": { - "category": "Error", - "code": 7005 - }, - "Parameter '{0}' implicitly has an '{1}' type.": { - "category": "Error", - "code": 7006 - }, - "Member '{0}' implicitly has an '{1}' type.": { - "category": "Error", - "code": 7008 - }, - "'new' expression, whose target lacks a construct signature, implicitly has an 'any' type.": { - "category": "Error", - "code": 7009 - }, - "'{0}', which lacks return-type annotation, implicitly has an '{1}' return type.": { - "category": "Error", - "code": 7010 - }, - "Function expression, which lacks return-type annotation, implicitly has an '{0}' return type.": { - "category": "Error", - "code": 7011 - }, - "This overload implicitly returns the type '{0}' because it lacks a return type annotation.": { - "category": "Error", - "code": 7012 - }, - "Construct signature, which lacks return-type annotation, implicitly has an 'any' return type.": { - "category": "Error", - "code": 7013 - }, - "Function type, which lacks return-type annotation, implicitly has an '{0}' return type.": { - "category": "Error", - "code": 7014 - }, - "Element implicitly has an 'any' type because index expression is not of type 'number'.": { - "category": "Error", - "code": 7015 - }, - "Could not find a declaration file for module '{0}'. '{1}' implicitly has an 'any' type.": { - "category": "Error", - "code": 7016 - }, - "Element implicitly has an 'any' type because type '{0}' has no index signature.": { - "category": "Error", - "code": 7017 - }, - "Object literal's property '{0}' implicitly has an '{1}' type.": { - "category": "Error", - "code": 7018 - }, - "Rest parameter '{0}' implicitly has an 'any[]' type.": { - "category": "Error", - "code": 7019 - }, - "Call signature, which lacks return-type annotation, implicitly has an 'any' return type.": { - "category": "Error", - "code": 7020 - }, - "'{0}' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.": { - "category": "Error", - "code": 7022 - }, - "'{0}' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.": { - "category": "Error", - "code": 7023 - }, - "Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.": { - "category": "Error", - "code": 7024 - }, - "Generator implicitly has yield type '{0}'. Consider supplying a return type annotation.": { - "category": "Error", - "code": 7025 - }, - "JSX element implicitly has type 'any' because no interface 'JSX.{0}' exists.": { - "category": "Error", - "code": 7026 - }, - "Unreachable code detected.": { - "category": "Error", - "code": 7027, - "reportsUnnecessary": true - }, - "Unused label.": { - "category": "Error", - "code": 7028, - "reportsUnnecessary": true - }, - "Fallthrough case in switch.": { - "category": "Error", - "code": 7029 - }, - "Not all code paths return a value.": { - "category": "Error", - "code": 7030 - }, - "Binding element '{0}' implicitly has an '{1}' type.": { - "category": "Error", - "code": 7031 - }, - "Property '{0}' implicitly has type 'any', because its set accessor lacks a parameter type annotation.": { - "category": "Error", - "code": 7032 - }, - "Property '{0}' implicitly has type 'any', because its get accessor lacks a return type annotation.": { - "category": "Error", - "code": 7033 - }, - "Variable '{0}' implicitly has type '{1}' in some locations where its type cannot be determined.": { - "category": "Error", - "code": 7034 - }, - "Try `npm i --save-dev @types/{1}` if it exists or add a new declaration (.d.ts) file containing `declare module '{0}';`": { - "category": "Error", - "code": 7035 - }, - "Dynamic import's specifier must be of type 'string', but here has type '{0}'.": { - "category": "Error", - "code": 7036 - }, - "Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'.": { - "category": "Message", - "code": 7037 - }, - "Type originates at this import. A namespace-style import cannot be called or constructed, and will cause a failure at runtime. Consider using a default import or import require here instead.": { - "category": "Message", - "code": 7038 - }, - "Mapped object type implicitly has an 'any' template type.": { - "category": "Error", - "code": 7039 - }, - "If the '{0}' package actually exposes this module, consider sending a pull request to amend 'https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/{1}'": { - "category": "Error", - "code": 7040 - }, - "The containing arrow function captures the global value of 'this'.": { - "category": "Error", - "code": 7041 - }, - "Module '{0}' was resolved to '{1}', but '--resolveJsonModule' is not used.": { - "category": "Error", - "code": 7042 - }, - "Variable '{0}' implicitly has an '{1}' type, but a better type may be inferred from usage.": { - "category": "Suggestion", - "code": 7043 - }, - "Parameter '{0}' implicitly has an '{1}' type, but a better type may be inferred from usage.": { - "category": "Suggestion", - "code": 7044 - }, - "Member '{0}' implicitly has an '{1}' type, but a better type may be inferred from usage.": { - "category": "Suggestion", - "code": 7045 - }, - "Variable '{0}' implicitly has type '{1}' in some locations, but a better type may be inferred from usage.": { - "category": "Suggestion", - "code": 7046 - }, - "Rest parameter '{0}' implicitly has an 'any[]' type, but a better type may be inferred from usage.": { - "category": "Suggestion", - "code": 7047 - }, - "Property '{0}' implicitly has type 'any', but a better type for its get accessor may be inferred from usage.": { - "category": "Suggestion", - "code": 7048 - }, - "Property '{0}' implicitly has type 'any', but a better type for its set accessor may be inferred from usage.": { - "category": "Suggestion", - "code": 7049 - }, - "'{0}' implicitly has an '{1}' return type, but a better type may be inferred from usage.": { - "category": "Suggestion", - "code": 7050 - }, - "Parameter has a name but no type. Did you mean '{0}: {1}'?": { - "category": "Error", - "code": 7051 - }, - "Element implicitly has an 'any' type because type '{0}' has no index signature. Did you mean to call '{1}'?": { - "category": "Error", - "code": 7052 - }, - "Element implicitly has an 'any' type because expression of type '{0}' can't be used to index type '{1}'.": { - "category": "Error", - "code": 7053 - }, - "No index signature with a parameter of type '{0}' was found on type '{1}'.": { - "category": "Error", - "code": 7054 - }, - "'{0}', which lacks return-type annotation, implicitly has an '{1}' yield type.": { - "category": "Error", - "code": 7055 - }, - "The inferred type of this node exceeds the maximum length the compiler will serialize. An explicit type annotation is needed.": { - "category": "Error", - "code": 7056 - }, - "'yield' expression implicitly results in an 'any' type because its containing generator lacks a return-type annotation.": { - "category": "Error", - "code": 7057 - }, - "If the '{0}' package actually exposes this module, try adding a new declaration (.d.ts) file containing `declare module '{1}';`": { - "category": "Error", - "code": 7058 - }, - "This syntax is reserved in files with the .mts or .cts extension. Use an `as` expression instead.": { - "category": "Error", - "code": 7059 - }, - "This syntax is reserved in files with the .mts or .cts extension. Add a trailing comma or explicit constraint.": { - "category": "Error", - "code": 7060 - }, - "A mapped type may not declare properties or methods.": { - "category": "Error", - "code": 7061 - }, - - "You cannot rename this element.": { - "category": "Error", - "code": 8000 - }, - "You cannot rename elements that are defined in the standard TypeScript library.": { - "category": "Error", - "code": 8001 - }, - "'import ... =' can only be used in TypeScript files.": { - "category": "Error", - "code": 8002 - }, - "'export =' can only be used in TypeScript files.": { - "category": "Error", - "code": 8003 - }, - "Type parameter declarations can only be used in TypeScript files.": { - "category": "Error", - "code": 8004 - }, - "'implements' clauses can only be used in TypeScript files.": { - "category": "Error", - "code": 8005 - }, - "'{0}' declarations can only be used in TypeScript files.": { - "category": "Error", - "code": 8006 - }, - "Type aliases can only be used in TypeScript files.": { - "category": "Error", - "code": 8008 - }, - "The '{0}' modifier can only be used in TypeScript files.": { - "category": "Error", - "code": 8009 - }, - "Type annotations can only be used in TypeScript files.": { - "category": "Error", - "code": 8010 - }, - "Type arguments can only be used in TypeScript files.": { - "category": "Error", - "code": 8011 - }, - "Parameter modifiers can only be used in TypeScript files.": { - "category": "Error", - "code": 8012 - }, - "Non-null assertions can only be used in TypeScript files.": { - "category": "Error", - "code": 8013 - }, - "Type assertion expressions can only be used in TypeScript files.": { - "category": "Error", - "code": 8016 - }, - "Signature declarations can only be used in TypeScript files.": { - "category": "Error", - "code": 8017 - }, - "Report errors in .js files.": { - "category": "Message", - "code": 8019 - }, - "JSDoc types can only be used inside documentation comments.": { - "category": "Error", - "code": 8020 - }, - "JSDoc '@typedef' tag should either have a type annotation or be followed by '@property' or '@member' tags.": { - "category": "Error", - "code": 8021 - }, - "JSDoc '@{0}' is not attached to a class.": { - "category": "Error", - "code": 8022 - }, - "JSDoc '@{0} {1}' does not match the 'extends {2}' clause.": { - "category": "Error", - "code": 8023 - }, - "JSDoc '@param' tag has name '{0}', but there is no parameter with that name.": { - "category": "Error", - "code": 8024 - }, - "Class declarations cannot have more than one '@augments' or '@extends' tag.": { - "category": "Error", - "code": 8025 - }, - "Expected {0} type arguments; provide these with an '@extends' tag.": { - "category": "Error", - "code": 8026 - }, - "Expected {0}-{1} type arguments; provide these with an '@extends' tag.": { - "category": "Error", - "code": 8027 - }, - "JSDoc '...' may only appear in the last parameter of a signature.": { - "category": "Error", - "code": 8028 - }, - "JSDoc '@param' tag has name '{0}', but there is no parameter with that name. It would match 'arguments' if it had an array type.": { - "category": "Error", - "code": 8029 - }, - "The type of a function declaration must match the function's signature.": { - "category": "Error", - "code": 8030 - }, - "You cannot rename a module via a global import.": { - "category": "Error", - "code": 8031 - }, - "Qualified name '{0}' is not allowed without a leading '@param {object} {1}'.": { - "category": "Error", - "code": 8032 - }, - "A JSDoc '@typedef' comment may not contain multiple '@type' tags.": { - "category": "Error", - "code": 8033 - }, - "The tag was first specified here.": { - "category": "Error", - "code": 8034 - }, - "You cannot rename elements that are defined in a 'node_modules' folder.": { - "category": "Error", - "code": 8035 - }, - "You cannot rename elements that are defined in another 'node_modules' folder.": { - "category": "Error", - "code": 8036 - }, - "Type satisfaction expressions can only be used in TypeScript files.": { - "category": "Error", - "code": 8037 - }, - "Decorators may not appear after 'export' or 'export default' if they also appear before 'export'.": { - "category": "Error", - "code": 8038 - }, - "A JSDoc '@template' tag may not follow a '@typedef', '@callback', or '@overload' tag": { - "category": "Error", - "code": 8039 - }, - - "Declaration emit for this file requires using private name '{0}'. An explicit type annotation may unblock declaration emit.": { - "category": "Error", - "code": 9005 - }, - "Declaration emit for this file requires using private name '{0}' from module '{1}'. An explicit type annotation may unblock declaration emit.": { - "category": "Error", - "code": 9006 - }, - "Function must have an explicit return type annotation with --isolatedDeclarations.": { - "category": "Error", - "code": 9007 - }, - "Method must have an explicit return type annotation with --isolatedDeclarations.": { - "category": "Error", - "code": 9008 - }, - "At least one accessor must have an explicit type annotation with --isolatedDeclarations.": { - "category": "Error", - "code": 9009 - }, - "Variable must have an explicit type annotation with --isolatedDeclarations.": { - "category": "Error", - "code": 9010 - }, - "Parameter must have an explicit type annotation with --isolatedDeclarations.": { - "category": "Error", - "code": 9011 - }, - "Property must have an explicit type annotation with --isolatedDeclarations.": { - "category": "Error", - "code": 9012 - }, - "Expression type can't be inferred with --isolatedDeclarations.": { - "category": "Error", - "code": 9013 - }, - "Computed properties must be number or string literals, variables or dotted expressions with --isolatedDeclarations.": { - "category": "Error", - "code": 9014 - }, - "Objects that contain spread assignments can't be inferred with --isolatedDeclarations.": { - "category": "Error", - "code": 9015 - }, - "Objects that contain shorthand properties can't be inferred with --isolatedDeclarations.": { - "category": "Error", - "code": 9016 - }, - "Only const arrays can be inferred with --isolatedDeclarations.": { - "category": "Error", - "code": 9017 - }, - "Arrays with spread elements can't inferred with --isolatedDeclarations.": { - "category": "Error", - "code": 9018 - }, - "Binding elements can't be exported directly with --isolatedDeclarations.": { - "category": "Error", - "code": 9019 - }, - "Enum member initializers must be computable without references to external symbols with --isolatedDeclarations.": { - "category": "Error", - "code": 9020 - }, - "Extends clause can't contain an expression with --isolatedDeclarations.": { - "category": "Error", - "code": 9021 - }, - "Inference from class expressions is not supported with --isolatedDeclarations.": { - "category": "Error", - "code": 9022 - }, - "Assigning properties to functions without declaring them is not supported with --isolatedDeclarations. Add an explicit declaration for the properties assigned to this function.": { - "category": "Error", - "code": 9023 - }, - "Declaration emit for this parameter requires implicitly adding undefined to its type. This is not supported with --isolatedDeclarations.": { - "category": "Error", - "code": 9025 - }, - "Declaration emit for this file requires preserving this import for augmentations. This is not supported with --isolatedDeclarations.": { - "category": "Error", - "code": 9026 - }, - "Add a type annotation to the variable {0}.": { - "category": "Error", - "code": 9027 - }, - "Add a type annotation to the parameter {0}.": { - "category": "Error", - "code": 9028 - }, - "Add a type annotation to the property {0}.": { - "category": "Error", - "code": 9029 - }, - "Add a return type to the function expression.": { - "category": "Error", - "code": 9030 - }, - "Add a return type to the function declaration.": { - "category": "Error", - "code": 9031 - }, - "Add a return type to the get accessor declaration.": { - "category": "Error", - "code": 9032 - }, - "Add a type to parameter of the set accessor declaration.": { - "category": "Error", - "code": 9033 - }, - "Add a return type to the method": { - "category": "Error", - "code": 9034 - }, - "Add satisfies and a type assertion to this expression (satisfies T as T) to make the type explicit.": { - "category": "Error", - "code": 9035 - }, - "Move the expression in default export to a variable and add a type annotation to it.": { - "category": "Error", - "code": 9036 - }, - "Default exports can't be inferred with --isolatedDeclarations.": { - "category": "Error", - "code": 9037 - }, - "Computed property names on class or object literals cannot be inferred with --isolatedDeclarations.": { - "category": "Error", - "code": 9038 - }, - "Type containing private name '{0}' can't be used with --isolatedDeclarations.": { - "category": "Error", - "code": 9039 - }, - "JSX attributes must only be assigned a non-empty 'expression'.": { - "category": "Error", - "code": 17000 - }, - "JSX elements cannot have multiple attributes with the same name.": { - "category": "Error", - "code": 17001 - }, - "Expected corresponding JSX closing tag for '{0}'.": { - "category": "Error", - "code": 17002 - }, - "Cannot use JSX unless the '--jsx' flag is provided.": { - "category": "Error", - "code": 17004 - }, - "A constructor cannot contain a 'super' call when its class extends 'null'.": { - "category": "Error", - "code": 17005 - }, - "An unary expression with the '{0}' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.": { - "category": "Error", - "code": 17006 - }, - "A type assertion expression is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.": { - "category": "Error", - "code": 17007 - }, - "JSX element '{0}' has no corresponding closing tag.": { - "category": "Error", - "code": 17008 - }, - "'super' must be called before accessing 'this' in the constructor of a derived class.": { - "category": "Error", - "code": 17009 - }, - "Unknown type acquisition option '{0}'.": { - "category": "Error", - "code": 17010 - }, - "'super' must be called before accessing a property of 'super' in the constructor of a derived class.": { - "category": "Error", - "code": 17011 - }, - "'{0}' is not a valid meta-property for keyword '{1}'. Did you mean '{2}'?": { - "category": "Error", - "code": 17012 - }, - "Meta-property '{0}' is only allowed in the body of a function declaration, function expression, or constructor.": { - "category": "Error", - "code": 17013 - }, - "JSX fragment has no corresponding closing tag.": { - "category": "Error", - "code": 17014 - }, - "Expected corresponding closing tag for JSX fragment.": { - "category": "Error", - "code": 17015 - }, - "The 'jsxFragmentFactory' compiler option must be provided to use JSX fragments with the 'jsxFactory' compiler option.": { - "category": "Error", - "code": 17016 - }, - "An @jsxFrag pragma is required when using an @jsx pragma with JSX fragments.": { - "category": "Error", - "code": 17017 - }, - "Unknown type acquisition option '{0}'. Did you mean '{1}'?": { - "category": "Error", - "code": 17018 - }, - "'{0}' at the end of a type is not valid TypeScript syntax. Did you mean to write '{1}'?": { - "category": "Error", - "code": 17019 - }, - "'{0}' at the start of a type is not valid TypeScript syntax. Did you mean to write '{1}'?": { - "category": "Error", - "code": 17020 - }, - "Unicode escape sequence cannot appear here.": { - "category": "Error", - "code": 17021 - }, - "Circularity detected while resolving configuration: {0}": { - "category": "Error", - "code": 18000 - }, - "The 'files' list in config file '{0}' is empty.": { - "category": "Error", - "code": 18002 - }, - "No inputs were found in config file '{0}'. Specified 'include' paths were '{1}' and 'exclude' paths were '{2}'.": { - "category": "Error", - "code": 18003 - }, - - "File is a CommonJS module; it may be converted to an ES module.": { - "category": "Suggestion", - "code": 80001 - }, - "This constructor function may be converted to a class declaration.": { - "category": "Suggestion", - "code": 80002 - }, - "Import may be converted to a default import.": { - "category": "Suggestion", - "code": 80003 - }, - "JSDoc types may be moved to TypeScript types.": { - "category": "Suggestion", - "code": 80004 - }, - "'require' call may be converted to an import.": { - "category": "Suggestion", - "code": 80005 - }, - "This may be converted to an async function.": { - "category": "Suggestion", - "code": 80006 - }, - "'await' has no effect on the type of this expression.": { - "category": "Suggestion", - "code": 80007 - }, - "Numeric literals with absolute values equal to 2^53 or greater are too large to be represented accurately as integers.": { - "category": "Suggestion", - "code": 80008 - }, - "JSDoc typedef may be converted to TypeScript type.": { - "category": "Suggestion", - "code": 80009 - }, - "JSDoc typedefs may be converted to TypeScript types.": { - "category": "Suggestion", - "code": 80010 - }, - - "Add missing 'super()' call": { - "category": "Message", - "code": 90001 - }, - "Make 'super()' call the first statement in the constructor": { - "category": "Message", - "code": 90002 - }, - "Change 'extends' to 'implements'": { - "category": "Message", - "code": 90003 - }, - "Remove unused declaration for: '{0}'": { - "category": "Message", - "code": 90004 - }, - "Remove import from '{0}'": { - "category": "Message", - "code": 90005 - }, - "Implement interface '{0}'": { - "category": "Message", - "code": 90006 - }, - "Implement inherited abstract class": { - "category": "Message", - "code": 90007 - }, - "Add '{0}.' to unresolved variable": { - "category": "Message", - "code": 90008 - }, - "Remove variable statement": { - "category": "Message", - "code": 90010 - }, - "Remove template tag": { - "category": "Message", - "code": 90011 - }, - "Remove type parameters": { - "category": "Message", - "code": 90012 - }, - "Import '{0}' from \"{1}\"": { - "category": "Message", - "code": 90013 - }, - "Change '{0}' to '{1}'": { - "category": "Message", - "code": 90014 - }, - "Declare property '{0}'": { - "category": "Message", - "code": 90016 - }, - "Add index signature for property '{0}'": { - "category": "Message", - "code": 90017 - }, - "Disable checking for this file": { - "category": "Message", - "code": 90018 - }, - "Ignore this error message": { - "category": "Message", - "code": 90019 - }, - "Initialize property '{0}' in the constructor": { - "category": "Message", - "code": 90020 - }, - "Initialize static property '{0}'": { - "category": "Message", - "code": 90021 - }, - "Change spelling to '{0}'": { - "category": "Message", - "code": 90022 - }, - "Declare method '{0}'": { - "category": "Message", - "code": 90023 - }, - "Declare static method '{0}'": { - "category": "Message", - "code": 90024 - }, - "Prefix '{0}' with an underscore": { - "category": "Message", - "code": 90025 - }, - "Rewrite as the indexed access type '{0}'": { - "category": "Message", - "code": 90026 - }, - "Declare static property '{0}'": { - "category": "Message", - "code": 90027 - }, - "Call decorator expression": { - "category": "Message", - "code": 90028 - }, - "Add async modifier to containing function": { - "category": "Message", - "code": 90029 - }, - "Replace 'infer {0}' with 'unknown'": { - "category": "Message", - "code": 90030 - }, - "Replace all unused 'infer' with 'unknown'": { - "category": "Message", - "code": 90031 - }, - "Add parameter name": { - "category": "Message", - "code": 90034 - }, - "Declare private property '{0}'": { - "category": "Message", - "code": 90035 - }, - "Replace '{0}' with 'Promise<{1}>'": { - "category": "Message", - "code": 90036 - }, - "Fix all incorrect return type of an async functions": { - "category": "Message", - "code": 90037 - }, - "Declare private method '{0}'": { - "category": "Message", - "code": 90038 - }, - "Remove unused destructuring declaration": { - "category": "Message", - "code": 90039 - }, - "Remove unused declarations for: '{0}'": { - "category": "Message", - "code": 90041 - }, - "Declare a private field named '{0}'.": { - "category": "Message", - "code": 90053 - }, - "Includes imports of types referenced by '{0}'": { - "category": "Message", - "code": 90054 - }, - "Remove 'type' from import declaration from \"{0}\"": { - "category": "Message", - "code": 90055 - }, - "Remove 'type' from import of '{0}' from \"{1}\"": { - "category": "Message", - "code": 90056 - }, - "Add import from \"{0}\"": { - "category": "Message", - "code": 90057 - }, - "Update import from \"{0}\"": { - "category": "Message", - "code": 90058 - }, - "Export '{0}' from module '{1}'": { - "category": "Message", - "code": 90059 - }, - "Export all referenced locals": { - "category": "Message", - "code": 90060 - }, - "Update modifiers of '{0}'": { - "category": "Message", - "code": 90061 - }, - "Add annotation of type '{0}'": { - "category": "Message", - "code": 90062 - }, - "Add return type '{0}'": { - "category": "Message", - "code": 90063 - }, - "Extract base class to variable": { - "category": "Message", - "code": 90064 - }, - "Extract default export to variable": { - "category": "Message", - "code": 90065 - }, - "Extract binding expressions to variable": { - "category": "Message", - "code": 90066 - }, - "Add all missing type annotations": { - "category": "Message", - "code": 90067 - }, - "Add satisfies and an inline type assertion with '{0}'": { - "category": "Message", - "code": 90068 - }, - "Extract to variable and replace with '{0} as typeof {0}'": { - "category": "Message", - "code": 90069 - }, - "Mark array literal as const": { - "category": "Message", - "code": 90070 - }, - "Annotate types of properties expando function in a namespace": { - "category": "Message", - "code": 90071 - }, - - "Convert function to an ES2015 class": { - "category": "Message", - "code": 95001 - }, - "Convert '{0}' to '{1} in {0}'": { - "category": "Message", - "code": 95003 - }, - "Extract to {0} in {1}": { - "category": "Message", - "code": 95004 - }, - "Extract function": { - "category": "Message", - "code": 95005 - }, - "Extract constant": { - "category": "Message", - "code": 95006 - }, - "Extract to {0} in enclosing scope": { - "category": "Message", - "code": 95007 - }, - "Extract to {0} in {1} scope": { - "category": "Message", - "code": 95008 - }, - "Annotate with type from JSDoc": { - "category": "Message", - "code": 95009 - }, - "Infer type of '{0}' from usage": { - "category": "Message", - "code": 95011 - }, - "Infer parameter types from usage": { - "category": "Message", - "code": 95012 - }, - "Convert to default import": { - "category": "Message", - "code": 95013 - }, - "Install '{0}'": { - "category": "Message", - "code": 95014 - }, - "Replace import with '{0}'.": { - "category": "Message", - "code": 95015 - }, - "Use synthetic 'default' member.": { - "category": "Message", - "code": 95016 - }, - "Convert to ES module": { - "category": "Message", - "code": 95017 - }, - "Add 'undefined' type to property '{0}'": { - "category": "Message", - "code": 95018 - }, - "Add initializer to property '{0}'": { - "category": "Message", - "code": 95019 - }, - "Add definite assignment assertion to property '{0}'": { - "category": "Message", - "code": 95020 - }, - "Convert all type literals to mapped type": { - "category": "Message", - "code": 95021 - }, - "Add all missing members": { - "category": "Message", - "code": 95022 - }, - "Infer all types from usage": { - "category": "Message", - "code": 95023 - }, - "Delete all unused declarations": { - "category": "Message", - "code": 95024 - }, - "Prefix all unused declarations with '_' where possible": { - "category": "Message", - "code": 95025 - }, - "Fix all detected spelling errors": { - "category": "Message", - "code": 95026 - }, - "Add initializers to all uninitialized properties": { - "category": "Message", - "code": 95027 - }, - "Add definite assignment assertions to all uninitialized properties": { - "category": "Message", - "code": 95028 - }, - "Add undefined type to all uninitialized properties": { - "category": "Message", - "code": 95029 - }, - "Change all jsdoc-style types to TypeScript": { - "category": "Message", - "code": 95030 - }, - "Change all jsdoc-style types to TypeScript (and add '| undefined' to nullable types)": { - "category": "Message", - "code": 95031 - }, - "Implement all unimplemented interfaces": { - "category": "Message", - "code": 95032 - }, - "Install all missing types packages": { - "category": "Message", - "code": 95033 - }, - "Rewrite all as indexed access types": { - "category": "Message", - "code": 95034 - }, - "Convert all to default imports": { - "category": "Message", - "code": 95035 - }, - "Make all 'super()' calls the first statement in their constructor": { - "category": "Message", - "code": 95036 - }, - "Add qualifier to all unresolved variables matching a member name": { - "category": "Message", - "code": 95037 - }, - "Change all extended interfaces to 'implements'": { - "category": "Message", - "code": 95038 - }, - "Add all missing super calls": { - "category": "Message", - "code": 95039 - }, - "Implement all inherited abstract classes": { - "category": "Message", - "code": 95040 - }, - "Add all missing 'async' modifiers": { - "category": "Message", - "code": 95041 - }, - "Add '@ts-ignore' to all error messages": { - "category": "Message", - "code": 95042 - }, - "Annotate everything with types from JSDoc": { - "category": "Message", - "code": 95043 - }, - "Add '()' to all uncalled decorators": { - "category": "Message", - "code": 95044 - }, - "Convert all constructor functions to classes": { - "category": "Message", - "code": 95045 - }, - "Generate 'get' and 'set' accessors": { - "category": "Message", - "code": 95046 - }, - "Convert 'require' to 'import'": { - "category": "Message", - "code": 95047 - }, - "Convert all 'require' to 'import'": { - "category": "Message", - "code": 95048 - }, - "Move to a new file": { - "category": "Message", - "code": 95049 - }, - "Remove unreachable code": { - "category": "Message", - "code": 95050 - }, - "Remove all unreachable code": { - "category": "Message", - "code": 95051 - }, - "Add missing 'typeof'": { - "category": "Message", - "code": 95052 - }, - "Remove unused label": { - "category": "Message", - "code": 95053 - }, - "Remove all unused labels": { - "category": "Message", - "code": 95054 - }, - "Convert '{0}' to mapped object type": { - "category": "Message", - "code": 95055 - }, - "Convert namespace import to named imports": { - "category": "Message", - "code": 95056 - }, - "Convert named imports to namespace import": { - "category": "Message", - "code": 95057 - }, - "Add or remove braces in an arrow function": { - "category": "Message", - "code": 95058 - }, - "Add braces to arrow function": { - "category": "Message", - "code": 95059 - }, - "Remove braces from arrow function": { - "category": "Message", - "code": 95060 - }, - "Convert default export to named export": { - "category": "Message", - "code": 95061 - }, - "Convert named export to default export": { - "category": "Message", - "code": 95062 - }, - "Add missing enum member '{0}'": { - "category": "Message", - "code": 95063 - }, - "Add all missing imports": { - "category": "Message", - "code": 95064 - }, - "Convert to async function": { - "category": "Message", - "code": 95065 - }, - "Convert all to async functions": { - "category": "Message", - "code": 95066 - }, - "Add missing call parentheses": { - "category": "Message", - "code": 95067 - }, - "Add all missing call parentheses": { - "category": "Message", - "code": 95068 - }, - "Add 'unknown' conversion for non-overlapping types": { - "category": "Message", - "code": 95069 - }, - "Add 'unknown' to all conversions of non-overlapping types": { - "category": "Message", - "code": 95070 - }, - "Add missing 'new' operator to call": { - "category": "Message", - "code": 95071 - }, - "Add missing 'new' operator to all calls": { - "category": "Message", - "code": 95072 - }, - "Add names to all parameters without names": { - "category": "Message", - "code": 95073 - }, - "Enable the 'experimentalDecorators' option in your configuration file": { - "category": "Message", - "code": 95074 - }, - "Convert parameters to destructured object": { - "category": "Message", - "code": 95075 - }, - "Extract type": { - "category": "Message", - "code": 95077 - }, - "Extract to type alias": { - "category": "Message", - "code": 95078 - }, - "Extract to typedef": { - "category": "Message", - "code": 95079 - }, - "Infer 'this' type of '{0}' from usage": { - "category": "Message", - "code": 95080 - }, - "Add 'const' to unresolved variable": { - "category": "Message", - "code": 95081 - }, - "Add 'const' to all unresolved variables": { - "category": "Message", - "code": 95082 - }, - "Add 'await'": { - "category": "Message", - "code": 95083 - }, - "Add 'await' to initializer for '{0}'": { - "category": "Message", - "code": 95084 - }, - "Fix all expressions possibly missing 'await'": { - "category": "Message", - "code": 95085 - }, - "Remove unnecessary 'await'": { - "category": "Message", - "code": 95086 - }, - "Remove all unnecessary uses of 'await'": { - "category": "Message", - "code": 95087 - }, - "Enable the '--jsx' flag in your configuration file": { - "category": "Message", - "code": 95088 - }, - "Add 'await' to initializers": { - "category": "Message", - "code": 95089 - }, - "Extract to interface": { - "category": "Message", - "code": 95090 - }, - "Convert to a bigint numeric literal": { - "category": "Message", - "code": 95091 - }, - "Convert all to bigint numeric literals": { - "category": "Message", - "code": 95092 - }, - "Convert 'const' to 'let'": { - "category": "Message", - "code": 95093 - }, - "Prefix with 'declare'": { - "category": "Message", - "code": 95094 - }, - "Prefix all incorrect property declarations with 'declare'": { - "category": "Message", - "code": 95095 - }, - "Convert to template string": { - "category": "Message", - "code": 95096 - }, - "Add 'export {}' to make this file into a module": { - "category": "Message", - "code": 95097 - }, - "Set the 'target' option in your configuration file to '{0}'": { - "category": "Message", - "code": 95098 - }, - "Set the 'module' option in your configuration file to '{0}'": { - "category": "Message", - "code": 95099 - }, - "Convert invalid character to its html entity code": { - "category": "Message", - "code": 95100 - }, - "Convert all invalid characters to HTML entity code": { - "category": "Message", - "code": 95101 - }, - "Convert all 'const' to 'let'": { - "category": "Message", - "code": 95102 - }, - "Convert function expression '{0}' to arrow function": { - "category": "Message", - "code": 95105 - }, - "Convert function declaration '{0}' to arrow function": { - "category": "Message", - "code": 95106 - }, - "Fix all implicit-'this' errors": { - "category": "Message", - "code": 95107 - }, - "Wrap invalid character in an expression container": { - "category": "Message", - "code": 95108 - }, - "Wrap all invalid characters in an expression container": { - "category": "Message", - "code": 95109 - }, - "Visit https://aka.ms/tsconfig to read more about this file": { - "category": "Message", - "code": 95110 - }, - "Add a return statement": { - "category": "Message", - "code": 95111 - }, - "Remove braces from arrow function body": { - "category": "Message", - "code": 95112 - }, - "Wrap the following body with parentheses which should be an object literal": { - "category": "Message", - "code": 95113 - }, - "Add all missing return statement": { - "category": "Message", - "code": 95114 - }, - "Remove braces from all arrow function bodies with relevant issues": { - "category": "Message", - "code": 95115 - }, - "Wrap all object literal with parentheses": { - "category": "Message", - "code": 95116 - }, - "Move labeled tuple element modifiers to labels": { - "category": "Message", - "code": 95117 - }, - "Convert overload list to single signature": { - "category": "Message", - "code": 95118 - }, - "Generate 'get' and 'set' accessors for all overriding properties": { - "category": "Message", - "code": 95119 - }, - "Wrap in JSX fragment": { - "category": "Message", - "code": 95120 - }, - "Wrap all unparented JSX in JSX fragment": { - "category": "Message", - "code": 95121 - }, - "Convert arrow function or function expression": { - "category": "Message", - "code": 95122 - }, - "Convert to anonymous function": { - "category": "Message", - "code": 95123 - }, - "Convert to named function": { - "category": "Message", - "code": 95124 - }, - "Convert to arrow function": { - "category": "Message", - "code": 95125 - }, - "Remove parentheses": { - "category": "Message", - "code": 95126 - }, - "Could not find a containing arrow function": { - "category": "Message", - "code": 95127 - }, - "Containing function is not an arrow function": { - "category": "Message", - "code": 95128 - }, - "Could not find export statement": { - "category": "Message", - "code": 95129 - }, - "This file already has a default export": { - "category": "Message", - "code": 95130 - }, - "Could not find import clause": { - "category": "Message", - "code": 95131 - }, - "Could not find namespace import or named imports": { - "category": "Message", - "code": 95132 - }, - "Selection is not a valid type node": { - "category": "Message", - "code": 95133 - }, - "No type could be extracted from this type node": { - "category": "Message", - "code": 95134 - }, - "Could not find property for which to generate accessor": { - "category": "Message", - "code": 95135 - }, - "Name is not valid": { - "category": "Message", - "code": 95136 - }, - "Can only convert property with modifier": { - "category": "Message", - "code": 95137 - }, - "Switch each misused '{0}' to '{1}'": { - "category": "Message", - "code": 95138 - }, - "Convert to optional chain expression": { - "category": "Message", - "code": 95139 - }, - "Could not find convertible access expression": { - "category": "Message", - "code": 95140 - }, - "Could not find matching access expressions": { - "category": "Message", - "code": 95141 - }, - "Can only convert logical AND access chains": { - "category": "Message", - "code": 95142 - }, - "Add 'void' to Promise resolved without a value": { - "category": "Message", - "code": 95143 - }, - "Add 'void' to all Promises resolved without a value": { - "category": "Message", - "code": 95144 - }, - "Use element access for '{0}'": { - "category": "Message", - "code": 95145 - }, - "Use element access for all undeclared properties.": { - "category": "Message", - "code": 95146 - }, - "Delete all unused imports": { - "category": "Message", - "code": 95147 - }, - "Infer function return type": { - "category": "Message", - "code": 95148 - }, - "Return type must be inferred from a function": { - "category": "Message", - "code": 95149 - }, - "Could not determine function return type": { - "category": "Message", - "code": 95150 - }, - "Could not convert to arrow function": { - "category": "Message", - "code": 95151 - }, - "Could not convert to named function": { - "category": "Message", - "code": 95152 - }, - "Could not convert to anonymous function": { - "category": "Message", - "code": 95153 - }, - "Can only convert string concatenations and string literals": { - "category": "Message", - "code": 95154 - }, - "Selection is not a valid statement or statements": { - "category": "Message", - "code": 95155 - }, - "Add missing function declaration '{0}'": { - "category": "Message", - "code": 95156 - }, - "Add all missing function declarations": { - "category": "Message", - "code": 95157 - }, - "Method not implemented.": { - "category": "Message", - "code": 95158 - }, - "Function not implemented.": { - "category": "Message", - "code": 95159 - }, - "Add 'override' modifier": { - "category": "Message", - "code": 95160 - }, - "Remove 'override' modifier": { - "category": "Message", - "code": 95161 - }, - "Add all missing 'override' modifiers": { - "category": "Message", - "code": 95162 - }, - "Remove all unnecessary 'override' modifiers": { - "category": "Message", - "code": 95163 - }, - "Can only convert named export": { - "category": "Message", - "code": 95164 - }, - "Add missing properties": { - "category": "Message", - "code": 95165 - }, - "Add all missing properties": { - "category": "Message", - "code": 95166 - }, - "Add missing attributes": { - "category": "Message", - "code": 95167 - }, - "Add all missing attributes": { - "category": "Message", - "code": 95168 - }, - "Add 'undefined' to optional property type": { - "category": "Message", - "code": 95169 - }, - "Convert named imports to default import": { - "category": "Message", - "code": 95170 - }, - "Delete unused '@param' tag '{0}'": { - "category": "Message", - "code": 95171 - }, - "Delete all unused '@param' tags": { - "category": "Message", - "code": 95172 - }, - "Rename '@param' tag name '{0}' to '{1}'": { - "category": "Message", - "code": 95173 - }, - "Use `{0}`.": { - "category": "Message", - "code": 95174 - }, - "Use `Number.isNaN` in all conditions.": { - "category": "Message", - "code": 95175 - }, - "Convert typedef to TypeScript type.": { - "category": "Message", - "code": 95176 - }, - "Convert all typedef to TypeScript types.": { - "category": "Message", - "code": 95177 - }, - "Move to file": { - "category": "Message", - "code": 95178 - }, - "Cannot move to file, selected file is invalid": { - "category": "Message", - "code": 95179 - }, - "Use 'import type'": { - "category": "Message", - "code": 95180 - }, - "Use 'type {0}'": { - "category": "Message", - "code": 95181 - }, - "Fix all with type-only imports": { - "category": "Message", - "code": 95182 - }, - "Cannot move statements to the selected file": { - "category": "Message", - "code": 95183 - }, - "Inline variable": { - "category": "Message", - "code": 95184 - }, - "Could not find variable to inline.": { - "category": "Message", - "code": 95185 - }, - "Variables with multiple declarations cannot be inlined.": { - "category": "Message", - "code": 95186 - }, - "Add missing comma for object member completion '{0}'.": { - "category": "Message", - "code": 95187 - }, - "Add missing parameter to '{0}'": { - "category": "Message", - "code": 95188 - }, - "Add missing parameters to '{0}'": { - "category": "Message", - "code": 95189 - }, - "Add all missing parameters": { - "category": "Message", - "code": 95190 - }, - "Add optional parameter to '{0}'": { - "category": "Message", - "code": 95191 - }, - "Add optional parameters to '{0}'": { - "category": "Message", - "code": 95192 - }, - "Add all optional parameters": { - "category": "Message", - "code": 95193 - }, - "Wrap in parentheses": { - "category": "Message", - "code": 95194 - }, - "Wrap all invalid decorator expressions in parentheses": { - "category": "Message", - "code": 95195 - }, - "Add 'resolution-mode' import attribute": { - "category": "Message", - "code": 95196 - }, - "Add 'resolution-mode' import attribute to all type-only imports that need it": { - "category": "Message", - "code": 95197 - }, - - "No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": { - "category": "Error", - "code": 18004 - }, - "Classes may not have a field named 'constructor'.": { - "category": "Error", - "code": 18006 - }, - "JSX expressions may not use the comma operator. Did you mean to write an array?": { - "category": "Error", - "code": 18007 - }, - "Private identifiers cannot be used as parameters.": { - "category": "Error", - "code": 18009 - }, - "An accessibility modifier cannot be used with a private identifier.": { - "category": "Error", - "code": 18010 - }, - "The operand of a 'delete' operator cannot be a private identifier.": { - "category": "Error", - "code": 18011 - }, - "'#constructor' is a reserved word.": { - "category": "Error", - "code": 18012 - }, - "Property '{0}' is not accessible outside class '{1}' because it has a private identifier.": { - "category": "Error", - "code": 18013 - }, - "The property '{0}' cannot be accessed on type '{1}' within this class because it is shadowed by another private identifier with the same spelling.": { - "category": "Error", - "code": 18014 - }, - "Property '{0}' in type '{1}' refers to a different member that cannot be accessed from within type '{2}'.": { - "category": "Error", - "code": 18015 - }, - "Private identifiers are not allowed outside class bodies.": { - "category": "Error", - "code": 18016 - }, - "The shadowing declaration of '{0}' is defined here": { - "category": "Error", - "code": 18017 - }, - "The declaration of '{0}' that you probably intended to use is defined here": { - "category": "Error", - "code": 18018 - }, - "'{0}' modifier cannot be used with a private identifier.": { - "category": "Error", - "code": 18019 - }, - "An enum member cannot be named with a private identifier.": { - "category": "Error", - "code": 18024 - }, - "'#!' can only be used at the start of a file.": { - "category": "Error", - "code": 18026 - }, - "Compiler reserves name '{0}' when emitting private identifier downlevel.": { - "category": "Error", - "code": 18027 - }, - "Private identifiers are only available when targeting ECMAScript 2015 and higher.": { - "category": "Error", - "code": 18028 - }, - "Private identifiers are not allowed in variable declarations.": { - "category": "Error", - "code": 18029 - }, - "An optional chain cannot contain private identifiers.": { - "category": "Error", - "code": 18030 - }, - "The intersection '{0}' was reduced to 'never' because property '{1}' has conflicting types in some constituents.": { - "category": "Error", - "code": 18031 - }, - "The intersection '{0}' was reduced to 'never' because property '{1}' exists in multiple constituents and is private in some.": { - "category": "Error", - "code": 18032 - }, - "Type '{0}' is not assignable to type '{1}' as required for computed enum member values.": { - "category": "Error", - "code": 18033 - }, - "Specify the JSX fragment factory function to use when targeting 'react' JSX emit with 'jsxFactory' compiler option is specified, e.g. 'Fragment'.": { - "category": "Message", - "code": 18034 - }, - "Invalid value for 'jsxFragmentFactory'. '{0}' is not a valid identifier or qualified-name.": { - "category": "Error", - "code": 18035 - }, - "Class decorators can't be used with static private identifier. Consider removing the experimental decorator.": { - "category": "Error", - "code": 18036 - }, - "'await' expression cannot be used inside a class static block.": { - "category": "Error", - "code": 18037 - }, - "'for await' loops cannot be used inside a class static block.": { - "category": "Error", - "code": 18038 - }, - "Invalid use of '{0}'. It cannot be used inside a class static block.": { - "category": "Error", - "code": 18039 - }, - "A 'return' statement cannot be used inside a class static block.": { - "category": "Error", - "code": 18041 - }, - "'{0}' is a type and cannot be imported in JavaScript files. Use '{1}' in a JSDoc type annotation.": { - "category": "Error", - "code": 18042 - }, - "Types cannot appear in export declarations in JavaScript files.": { - "category": "Error", - "code": 18043 - }, - "'{0}' is automatically exported here.": { - "category": "Message", - "code": 18044 - }, - "Properties with the 'accessor' modifier are only available when targeting ECMAScript 2015 and higher.": { - "category": "Error", - "code": 18045 - }, - "'{0}' is of type 'unknown'.": { - "category": "Error", - "code": 18046 - }, - "'{0}' is possibly 'null'.": { - "category": "Error", - "code": 18047 - }, - "'{0}' is possibly 'undefined'.": { - "category": "Error", - "code": 18048 - }, - "'{0}' is possibly 'null' or 'undefined'.": { - "category": "Error", - "code": 18049 - }, - "The value '{0}' cannot be used here.": { - "category": "Error", - "code": 18050 - }, - "Compiler option '{0}' cannot be given an empty string.": { - "category": "Error", - "code": 18051 - }, - "Its type '{0}' is not a valid JSX element type.": { - "category": "Error", - "code": 18053 - }, - "'await using' statements cannot be used inside a class static block.": { - "category": "Error", - "code": 18054 - }, - "'{0}' has a string type, but must have syntactically recognizable string syntax when 'isolatedModules' is enabled.": { - "category": "Error", - "code": 18055 - }, - "Enum member following a non-literal numeric member must have an initializer when 'isolatedModules' is enabled.": { - "category": "Error", - "code": 18056 - }, - "String literal import and export names are not supported when the '--module' flag is set to 'es2015' or 'es2020'.": { - "category": "Error", - "code": 18057 - }, - "Default imports are not allowed in a deferred import.": { - "category": "Error", - "code": 18058 - }, - "Named imports are not allowed in a deferred import.": { - "category": "Error", - "code": 18059 - }, - "Deferred imports are only supported when the '--module' flag is set to 'esnext' or 'preserve'.": { - "category": "Error", - "code": 18060 - }, - "'{0}' is not a valid meta-property for keyword 'import'. Did you mean 'meta' or 'defer'?": { - "category": "Error", - "code": 18061 - } -} +{ + "Unterminated string literal.": { + "category": "Error", + "code": 1002 + }, + "Identifier expected.": { + "category": "Error", + "code": 1003 + }, + "'{0}' expected.": { + "category": "Error", + "code": 1005 + }, + "A file cannot have a reference to itself.": { + "category": "Error", + "code": 1006 + }, + "The parser expected to find a '{1}' to match the '{0}' token here.": { + "category": "Error", + "code": 1007 + }, + "Trailing comma not allowed.": { + "category": "Error", + "code": 1009 + }, + "'*/' expected.": { + "category": "Error", + "code": 1010 + }, + "An element access expression should take an argument.": { + "category": "Error", + "code": 1011 + }, + "Unexpected token.": { + "category": "Error", + "code": 1012 + }, + "A rest parameter or binding pattern may not have a trailing comma.": { + "category": "Error", + "code": 1013 + }, + "A rest parameter must be last in a parameter list.": { + "category": "Error", + "code": 1014 + }, + "Parameter cannot have question mark and initializer.": { + "category": "Error", + "code": 1015 + }, + "A required parameter cannot follow an optional parameter.": { + "category": "Error", + "code": 1016 + }, + "An index signature cannot have a rest parameter.": { + "category": "Error", + "code": 1017 + }, + "An index signature parameter cannot have an accessibility modifier.": { + "category": "Error", + "code": 1018 + }, + "An index signature parameter cannot have a question mark.": { + "category": "Error", + "code": 1019 + }, + "An index signature parameter cannot have an initializer.": { + "category": "Error", + "code": 1020 + }, + "An index signature must have a type annotation.": { + "category": "Error", + "code": 1021 + }, + "An index signature parameter must have a type annotation.": { + "category": "Error", + "code": 1022 + }, + "'readonly' modifier can only appear on a property declaration or index signature.": { + "category": "Error", + "code": 1024 + }, + "An index signature cannot have a trailing comma.": { + "category": "Error", + "code": 1025 + }, + "Accessibility modifier already seen.": { + "category": "Error", + "code": 1028 + }, + "'{0}' modifier must precede '{1}' modifier.": { + "category": "Error", + "code": 1029 + }, + "'{0}' modifier already seen.": { + "category": "Error", + "code": 1030 + }, + "'{0}' modifier cannot appear on class elements of this kind.": { + "category": "Error", + "code": 1031 + }, + "'super' must be followed by an argument list or member access.": { + "category": "Error", + "code": 1034 + }, + "Only ambient modules can use quoted names.": { + "category": "Error", + "code": 1035 + }, + "Statements are not allowed in ambient contexts.": { + "category": "Error", + "code": 1036 + }, + "A 'declare' modifier cannot be used in an already ambient context.": { + "category": "Error", + "code": 1038 + }, + "Initializers are not allowed in ambient contexts.": { + "category": "Error", + "code": 1039 + }, + "'{0}' modifier cannot be used in an ambient context.": { + "category": "Error", + "code": 1040 + }, + "'{0}' modifier cannot be used here.": { + "category": "Error", + "code": 1042 + }, + "'{0}' modifier cannot appear on a module or namespace element.": { + "category": "Error", + "code": 1044 + }, + "Top-level declarations in .d.ts files must start with either a 'declare' or 'export' modifier.": { + "category": "Error", + "code": 1046 + }, + "A rest parameter cannot be optional.": { + "category": "Error", + "code": 1047 + }, + "A rest parameter cannot have an initializer.": { + "category": "Error", + "code": 1048 + }, + "A 'set' accessor must have exactly one parameter.": { + "category": "Error", + "code": 1049 + }, + "A 'set' accessor cannot have an optional parameter.": { + "category": "Error", + "code": 1051 + }, + "A 'set' accessor parameter cannot have an initializer.": { + "category": "Error", + "code": 1052 + }, + "A 'set' accessor cannot have rest parameter.": { + "category": "Error", + "code": 1053 + }, + "A 'get' accessor cannot have parameters.": { + "category": "Error", + "code": 1054 + }, + "Type '{0}' is not a valid async function return type in ES5 because it does not refer to a Promise-compatible constructor value.": { + "category": "Error", + "code": 1055 + }, + "Accessors are only available when targeting ECMAScript 5 and higher.": { + "category": "Error", + "code": 1056 + }, + "The return type of an async function must either be a valid promise or must not contain a callable 'then' member.": { + "category": "Error", + "code": 1058 + }, + "A promise must have a 'then' method.": { + "category": "Error", + "code": 1059 + }, + "The first parameter of the 'then' method of a promise must be a callback.": { + "category": "Error", + "code": 1060 + }, + "Enum member must have initializer.": { + "category": "Error", + "code": 1061 + }, + "Type is referenced directly or indirectly in the fulfillment callback of its own 'then' method.": { + "category": "Error", + "code": 1062 + }, + "An export assignment cannot be used in a namespace.": { + "category": "Error", + "code": 1063 + }, + "The return type of an async function or method must be the global Promise type. Did you mean to write 'Promise<{0}>'?": { + "category": "Error", + "code": 1064 + }, + "The return type of an async function or method must be the global Promise type.": { + "category": "Error", + "code": 1065 + }, + "In ambient enum declarations member initializer must be constant expression.": { + "category": "Error", + "code": 1066 + }, + "Unexpected token. A constructor, method, accessor, or property was expected.": { + "category": "Error", + "code": 1068 + }, + "Unexpected token. A type parameter name was expected without curly braces.": { + "category": "Error", + "code": 1069 + }, + "'{0}' modifier cannot appear on a type member.": { + "category": "Error", + "code": 1070 + }, + "'{0}' modifier cannot appear on an index signature.": { + "category": "Error", + "code": 1071 + }, + "A '{0}' modifier cannot be used with an import declaration.": { + "category": "Error", + "code": 1079 + }, + "Invalid 'reference' directive syntax.": { + "category": "Error", + "code": 1084 + }, + "'{0}' modifier cannot appear on a constructor declaration.": { + "category": "Error", + "code": 1089 + }, + "'{0}' modifier cannot appear on a parameter.": { + "category": "Error", + "code": 1090 + }, + "Only a single variable declaration is allowed in a 'for...in' statement.": { + "category": "Error", + "code": 1091 + }, + "Type parameters cannot appear on a constructor declaration.": { + "category": "Error", + "code": 1092 + }, + "Type annotation cannot appear on a constructor declaration.": { + "category": "Error", + "code": 1093 + }, + "An accessor cannot have type parameters.": { + "category": "Error", + "code": 1094 + }, + "A 'set' accessor cannot have a return type annotation.": { + "category": "Error", + "code": 1095 + }, + "An index signature must have exactly one parameter.": { + "category": "Error", + "code": 1096 + }, + "'{0}' list cannot be empty.": { + "category": "Error", + "code": 1097 + }, + "Type parameter list cannot be empty.": { + "category": "Error", + "code": 1098 + }, + "Type argument list cannot be empty.": { + "category": "Error", + "code": 1099 + }, + "Invalid use of '{0}' in strict mode.": { + "category": "Error", + "code": 1100 + }, + "'with' statements are not allowed in strict mode.": { + "category": "Error", + "code": 1101 + }, + "'delete' cannot be called on an identifier in strict mode.": { + "category": "Error", + "code": 1102 + }, + "'for await' loops are only allowed within async functions and at the top levels of modules.": { + "category": "Error", + "code": 1103 + }, + "A 'continue' statement can only be used within an enclosing iteration statement.": { + "category": "Error", + "code": 1104 + }, + "A 'break' statement can only be used within an enclosing iteration or switch statement.": { + "category": "Error", + "code": 1105 + }, + "The left-hand side of a 'for...of' statement may not be 'async'.": { + "category": "Error", + "code": 1106 + }, + "Jump target cannot cross function boundary.": { + "category": "Error", + "code": 1107 + }, + "A 'return' statement can only be used within a function body.": { + "category": "Error", + "code": 1108 + }, + "Expression expected.": { + "category": "Error", + "code": 1109 + }, + "Type expected.": { + "category": "Error", + "code": 1110 + }, + "Private field '{0}' must be declared in an enclosing class.": { + "category": "Error", + "code": 1111 + }, + "A 'default' clause cannot appear more than once in a 'switch' statement.": { + "category": "Error", + "code": 1113 + }, + "Duplicate label '{0}'.": { + "category": "Error", + "code": 1114 + }, + "A 'continue' statement can only jump to a label of an enclosing iteration statement.": { + "category": "Error", + "code": 1115 + }, + "A 'break' statement can only jump to a label of an enclosing statement.": { + "category": "Error", + "code": 1116 + }, + "An object literal cannot have multiple properties with the same name.": { + "category": "Error", + "code": 1117 + }, + "An object literal cannot have multiple get/set accessors with the same name.": { + "category": "Error", + "code": 1118 + }, + "An object literal cannot have property and accessor with the same name.": { + "category": "Error", + "code": 1119 + }, + "An export assignment cannot have modifiers.": { + "category": "Error", + "code": 1120 + }, + "Octal literals are not allowed. Use the syntax '{0}'.": { + "category": "Error", + "code": 1121 + }, + "Variable declaration list cannot be empty.": { + "category": "Error", + "code": 1123 + }, + "Digit expected.": { + "category": "Error", + "code": 1124 + }, + "Hexadecimal digit expected.": { + "category": "Error", + "code": 1125 + }, + "Unexpected end of text.": { + "category": "Error", + "code": 1126 + }, + "Invalid character.": { + "category": "Error", + "code": 1127 + }, + "Declaration or statement expected.": { + "category": "Error", + "code": 1128 + }, + "Statement expected.": { + "category": "Error", + "code": 1129 + }, + "'case' or 'default' expected.": { + "category": "Error", + "code": 1130 + }, + "Property or signature expected.": { + "category": "Error", + "code": 1131 + }, + "Enum member expected.": { + "category": "Error", + "code": 1132 + }, + "Variable declaration expected.": { + "category": "Error", + "code": 1134 + }, + "Argument expression expected.": { + "category": "Error", + "code": 1135 + }, + "Property assignment expected.": { + "category": "Error", + "code": 1136 + }, + "Expression or comma expected.": { + "category": "Error", + "code": 1137 + }, + "Parameter declaration expected.": { + "category": "Error", + "code": 1138 + }, + "Type parameter declaration expected.": { + "category": "Error", + "code": 1139 + }, + "Type argument expected.": { + "category": "Error", + "code": 1140 + }, + "String literal expected.": { + "category": "Error", + "code": 1141 + }, + "Line break not permitted here.": { + "category": "Error", + "code": 1142 + }, + "'{' or ';' expected.": { + "category": "Error", + "code": 1144 + }, + "'{' or JSX element expected.": { + "category": "Error", + "code": 1145 + }, + "Declaration expected.": { + "category": "Error", + "code": 1146 + }, + "Import declarations in a namespace cannot reference a module.": { + "category": "Error", + "code": 1147 + }, + "Cannot use imports, exports, or module augmentations when '--module' is 'none'.": { + "category": "Error", + "code": 1148 + }, + "File name '{0}' differs from already included file name '{1}' only in casing.": { + "category": "Error", + "code": 1149 + }, + "'{0}' declarations must be initialized.": { + "category": "Error", + "code": 1155 + }, + "'{0}' declarations can only be declared inside a block.": { + "category": "Error", + "code": 1156 + }, + "Unterminated template literal.": { + "category": "Error", + "code": 1160 + }, + "Unterminated regular expression literal.": { + "category": "Error", + "code": 1161 + }, + "An object member cannot be declared optional.": { + "category": "Error", + "code": 1162 + }, + "A 'yield' expression is only allowed in a generator body.": { + "category": "Error", + "code": 1163 + }, + "Computed property names are not allowed in enums.": { + "category": "Error", + "code": 1164 + }, + "A computed property name in an ambient context must refer to an expression whose type is a literal type or a 'unique symbol' type.": { + "category": "Error", + "code": 1165 + }, + "A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type.": { + "category": "Error", + "code": 1166 + }, + "A computed property name in a method overload must refer to an expression whose type is a literal type or a 'unique symbol' type.": { + "category": "Error", + "code": 1168 + }, + "A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type.": { + "category": "Error", + "code": 1169 + }, + "A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type.": { + "category": "Error", + "code": 1170 + }, + "A comma expression is not allowed in a computed property name.": { + "category": "Error", + "code": 1171 + }, + "'extends' clause already seen.": { + "category": "Error", + "code": 1172 + }, + "'extends' clause must precede 'implements' clause.": { + "category": "Error", + "code": 1173 + }, + "Classes can only extend a single class.": { + "category": "Error", + "code": 1174 + }, + "'implements' clause already seen.": { + "category": "Error", + "code": 1175 + }, + "Interface declaration cannot have 'implements' clause.": { + "category": "Error", + "code": 1176 + }, + "Binary digit expected.": { + "category": "Error", + "code": 1177 + }, + "Octal digit expected.": { + "category": "Error", + "code": 1178 + }, + "Unexpected token. '{' expected.": { + "category": "Error", + "code": 1179 + }, + "Property destructuring pattern expected.": { + "category": "Error", + "code": 1180 + }, + "Array element destructuring pattern expected.": { + "category": "Error", + "code": 1181 + }, + "A destructuring declaration must have an initializer.": { + "category": "Error", + "code": 1182 + }, + "An implementation cannot be declared in ambient contexts.": { + "category": "Error", + "code": 1183 + }, + "Modifiers cannot appear here.": { + "category": "Error", + "code": 1184 + }, + "Merge conflict marker encountered.": { + "category": "Error", + "code": 1185 + }, + "A rest element cannot have an initializer.": { + "category": "Error", + "code": 1186 + }, + "A parameter property may not be declared using a binding pattern.": { + "category": "Error", + "code": 1187 + }, + "Only a single variable declaration is allowed in a 'for...of' statement.": { + "category": "Error", + "code": 1188 + }, + "The variable declaration of a 'for...in' statement cannot have an initializer.": { + "category": "Error", + "code": 1189 + }, + "The variable declaration of a 'for...of' statement cannot have an initializer.": { + "category": "Error", + "code": 1190 + }, + "An import declaration cannot have modifiers.": { + "category": "Error", + "code": 1191 + }, + "Module '{0}' has no default export.": { + "category": "Error", + "code": 1192 + }, + "An export declaration cannot have modifiers.": { + "category": "Error", + "code": 1193 + }, + "Export declarations are not permitted in a namespace.": { + "category": "Error", + "code": 1194 + }, + "'export *' does not re-export a default.": { + "category": "Error", + "code": 1195 + }, + "Catch clause variable type annotation must be 'any' or 'unknown' if specified.": { + "category": "Error", + "code": 1196 + }, + "Catch clause variable cannot have an initializer.": { + "category": "Error", + "code": 1197 + }, + "An extended Unicode escape value must be between 0x0 and 0x10FFFF inclusive.": { + "category": "Error", + "code": 1198 + }, + "Unterminated Unicode escape sequence.": { + "category": "Error", + "code": 1199 + }, + "Line terminator not permitted before arrow.": { + "category": "Error", + "code": 1200 + }, + "Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from \"mod\"', 'import {a} from \"mod\"', 'import d from \"mod\"', or another module format instead.": { + "category": "Error", + "code": 1202 + }, + "Export assignment cannot be used when targeting ECMAScript modules. Consider using 'export default' or another module format instead.": { + "category": "Error", + "code": 1203 + }, + "Re-exporting a type when '{0}' is enabled requires using 'export type'.": { + "category": "Error", + "code": 1205 + }, + "Decorators are not valid here.": { + "category": "Error", + "code": 1206 + }, + "Decorators cannot be applied to multiple get/set accessors of the same name.": { + "category": "Error", + "code": 1207 + }, + "Invalid optional chain from new expression. Did you mean to call '{0}()'?": { + "category": "Error", + "code": 1209 + }, + "Code contained in a class is evaluated in JavaScript's strict mode which does not allow this use of '{0}'. For more information, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode.": { + "category": "Error", + "code": 1210 + }, + "A class declaration without the 'default' modifier must have a name.": { + "category": "Error", + "code": 1211 + }, + "Identifier expected. '{0}' is a reserved word in strict mode.": { + "category": "Error", + "code": 1212 + }, + "Identifier expected. '{0}' is a reserved word in strict mode. Class definitions are automatically in strict mode.": { + "category": "Error", + "code": 1213 + }, + "Identifier expected. '{0}' is a reserved word in strict mode. Modules are automatically in strict mode.": { + "category": "Error", + "code": 1214 + }, + "Invalid use of '{0}'. Modules are automatically in strict mode.": { + "category": "Error", + "code": 1215 + }, + "Identifier expected. '__esModule' is reserved as an exported marker when transforming ECMAScript modules.": { + "category": "Error", + "code": 1216 + }, + "Export assignment is not supported when '--module' flag is 'system'.": { + "category": "Error", + "code": 1218 + }, + "Generators are not allowed in an ambient context.": { + "category": "Error", + "code": 1221 + }, + "An overload signature cannot be declared as a generator.": { + "category": "Error", + "code": 1222 + }, + "'{0}' tag already specified.": { + "category": "Error", + "code": 1223 + }, + "Signature '{0}' must be a type predicate.": { + "category": "Error", + "code": 1224 + }, + "Cannot find parameter '{0}'.": { + "category": "Error", + "code": 1225 + }, + "Type predicate '{0}' is not assignable to '{1}'.": { + "category": "Error", + "code": 1226 + }, + "Parameter '{0}' is not in the same position as parameter '{1}'.": { + "category": "Error", + "code": 1227 + }, + "A type predicate is only allowed in return type position for functions and methods.": { + "category": "Error", + "code": 1228 + }, + "A type predicate cannot reference a rest parameter.": { + "category": "Error", + "code": 1229 + }, + "A type predicate cannot reference element '{0}' in a binding pattern.": { + "category": "Error", + "code": 1230 + }, + "An export assignment must be at the top level of a file or module declaration.": { + "category": "Error", + "code": 1231 + }, + "An import declaration can only be used at the top level of a namespace or module.": { + "category": "Error", + "code": 1232 + }, + "An export declaration can only be used at the top level of a namespace or module.": { + "category": "Error", + "code": 1233 + }, + "An ambient module declaration is only allowed at the top level in a file.": { + "category": "Error", + "code": 1234 + }, + "A namespace declaration is only allowed at the top level of a namespace or module.": { + "category": "Error", + "code": 1235 + }, + "The return type of a property decorator function must be either 'void' or 'any'.": { + "category": "Error", + "code": 1236 + }, + "The return type of a parameter decorator function must be either 'void' or 'any'.": { + "category": "Error", + "code": 1237 + }, + "Unable to resolve signature of class decorator when called as an expression.": { + "category": "Error", + "code": 1238 + }, + "Unable to resolve signature of parameter decorator when called as an expression.": { + "category": "Error", + "code": 1239 + }, + "Unable to resolve signature of property decorator when called as an expression.": { + "category": "Error", + "code": 1240 + }, + "Unable to resolve signature of method decorator when called as an expression.": { + "category": "Error", + "code": 1241 + }, + "'abstract' modifier can only appear on a class, method, or property declaration.": { + "category": "Error", + "code": 1242 + }, + "'{0}' modifier cannot be used with '{1}' modifier.": { + "category": "Error", + "code": 1243 + }, + "Abstract methods can only appear within an abstract class.": { + "category": "Error", + "code": 1244 + }, + "Method '{0}' cannot have an implementation because it is marked abstract.": { + "category": "Error", + "code": 1245 + }, + "An interface property cannot have an initializer.": { + "category": "Error", + "code": 1246 + }, + "A type literal property cannot have an initializer.": { + "category": "Error", + "code": 1247 + }, + "A class member cannot have the '{0}' keyword.": { + "category": "Error", + "code": 1248 + }, + "A decorator can only decorate a method implementation, not an overload.": { + "category": "Error", + "code": 1249 + }, + "Function declarations are not allowed inside blocks in strict mode when targeting 'ES5'.": { + "category": "Error", + "code": 1250 + }, + "Function declarations are not allowed inside blocks in strict mode when targeting 'ES5'. Class definitions are automatically in strict mode.": { + "category": "Error", + "code": 1251 + }, + "Function declarations are not allowed inside blocks in strict mode when targeting 'ES5'. Modules are automatically in strict mode.": { + "category": "Error", + "code": 1252 + }, + "Abstract properties can only appear within an abstract class.": { + "category": "Error", + "code": 1253 + }, + "A 'const' initializer in an ambient context must be a string or numeric literal or literal enum reference.": { + "category": "Error", + "code": 1254 + }, + "A definite assignment assertion '!' is not permitted in this context.": { + "category": "Error", + "code": 1255 + }, + "A required element cannot follow an optional element.": { + "category": "Error", + "code": 1257 + }, + "A default export must be at the top level of a file or module declaration.": { + "category": "Error", + "code": 1258 + }, + "Module '{0}' can only be default-imported using the '{1}' flag": { + "category": "Error", + "code": 1259 + }, + "Keywords cannot contain escape characters.": { + "category": "Error", + "code": 1260 + }, + "Already included file name '{0}' differs from file name '{1}' only in casing.": { + "category": "Error", + "code": 1261 + }, + "Identifier expected. '{0}' is a reserved word at the top-level of a module.": { + "category": "Error", + "code": 1262 + }, + "Declarations with initializers cannot also have definite assignment assertions.": { + "category": "Error", + "code": 1263 + }, + "Declarations with definite assignment assertions must also have type annotations.": { + "category": "Error", + "code": 1264 + }, + "A rest element cannot follow another rest element.": { + "category": "Error", + "code": 1265 + }, + "An optional element cannot follow a rest element.": { + "category": "Error", + "code": 1266 + }, + "Property '{0}' cannot have an initializer because it is marked abstract.": { + "category": "Error", + "code": 1267 + }, + "An index signature parameter type must be 'string', 'number', 'symbol', or a template literal type.": { + "category": "Error", + "code": 1268 + }, + "Cannot use 'export import' on a type or type-only namespace when '{0}' is enabled.": { + "category": "Error", + "code": 1269 + }, + "Decorator function return type '{0}' is not assignable to type '{1}'.": { + "category": "Error", + "code": 1270 + }, + "Decorator function return type is '{0}' but is expected to be 'void' or 'any'.": { + "category": "Error", + "code": 1271 + }, + "A type referenced in a decorated signature must be imported with 'import type' or a namespace import when 'isolatedModules' and 'emitDecoratorMetadata' are enabled.": { + "category": "Error", + "code": 1272 + }, + "'{0}' modifier cannot appear on a type parameter": { + "category": "Error", + "code": 1273 + }, + "'{0}' modifier can only appear on a type parameter of a class, interface or type alias": { + "category": "Error", + "code": 1274 + }, + "'accessor' modifier can only appear on a property declaration.": { + "category": "Error", + "code": 1275 + }, + "An 'accessor' property cannot be declared optional.": { + "category": "Error", + "code": 1276 + }, + "'{0}' modifier can only appear on a type parameter of a function, method or class": { + "category": "Error", + "code": 1277 + }, + "The runtime will invoke the decorator with {1} arguments, but the decorator expects {0}.": { + "category": "Error", + "code": 1278 + }, + "The runtime will invoke the decorator with {1} arguments, but the decorator expects at least {0}.": { + "category": "Error", + "code": 1279 + }, + "Namespaces are not allowed in global script files when '{0}' is enabled. If this file is not intended to be a global script, set 'moduleDetection' to 'force' or add an empty 'export {}' statement.": { + "category": "Error", + "code": 1280 + }, + "Cannot access '{0}' from another file without qualification when '{1}' is enabled. Use '{2}' instead.": { + "category": "Error", + "code": 1281 + }, + "An 'export =' declaration must reference a value when 'verbatimModuleSyntax' is enabled, but '{0}' only refers to a type.": { + "category": "Error", + "code": 1282 + }, + "An 'export =' declaration must reference a real value when 'verbatimModuleSyntax' is enabled, but '{0}' resolves to a type-only declaration.": { + "category": "Error", + "code": 1283 + }, + "An 'export default' must reference a value when 'verbatimModuleSyntax' is enabled, but '{0}' only refers to a type.": { + "category": "Error", + "code": 1284 + }, + "An 'export default' must reference a real value when 'verbatimModuleSyntax' is enabled, but '{0}' resolves to a type-only declaration.": { + "category": "Error", + "code": 1285 + }, + "ECMAScript imports and exports cannot be written in a CommonJS file under 'verbatimModuleSyntax'.": { + "category": "Error", + "code": 1286 + }, + "A top-level 'export' modifier cannot be used on value declarations in a CommonJS module when 'verbatimModuleSyntax' is enabled.": { + "category": "Error", + "code": 1287 + }, + "An import alias cannot resolve to a type or type-only declaration when 'verbatimModuleSyntax' is enabled.": { + "category": "Error", + "code": 1288 + }, + "'{0}' resolves to a type-only declaration and must be marked type-only in this file before re-exporting when '{1}' is enabled. Consider using 'import type' where '{0}' is imported.": { + "category": "Error", + "code": 1289 + }, + "'{0}' resolves to a type-only declaration and must be marked type-only in this file before re-exporting when '{1}' is enabled. Consider using 'export type { {0} as default }'.": { + "category": "Error", + "code": 1290 + }, + "'{0}' resolves to a type and must be marked type-only in this file before re-exporting when '{1}' is enabled. Consider using 'import type' where '{0}' is imported.": { + "category": "Error", + "code": 1291 + }, + "'{0}' resolves to a type and must be marked type-only in this file before re-exporting when '{1}' is enabled. Consider using 'export type { {0} as default }'.": { + "category": "Error", + "code": 1292 + }, + "ECMAScript module syntax is not allowed in a CommonJS module when 'module' is set to 'preserve'.": { + "category": "Error", + "code": 1293 + }, + "This syntax is not allowed when 'erasableSyntaxOnly' is enabled.": { + "category": "Error", + "code": 1294 + }, + "ECMAScript imports and exports cannot be written in a CommonJS file under 'verbatimModuleSyntax'. Adjust the 'type' field in the nearest 'package.json' to make this file an ECMAScript module, or adjust your 'verbatimModuleSyntax', 'module', and 'moduleResolution' settings in TypeScript.": { + "category": "Error", + "code": 1295 + }, + "'with' statements are not allowed in an async function block.": { + "category": "Error", + "code": 1300 + }, + "'await' expressions are only allowed within async functions and at the top levels of modules.": { + "category": "Error", + "code": 1308 + }, + "The current file is a CommonJS module and cannot use 'await' at the top level.": { + "category": "Error", + "code": 1309 + }, + "Did you mean to use a ':'? An '=' can only follow a property name when the containing object literal is part of a destructuring pattern.": { + "category": "Error", + "code": 1312 + }, + "The body of an 'if' statement cannot be the empty statement.": { + "category": "Error", + "code": 1313 + }, + "Global module exports may only appear in module files.": { + "category": "Error", + "code": 1314 + }, + "Global module exports may only appear in declaration files.": { + "category": "Error", + "code": 1315 + }, + "Global module exports may only appear at top level.": { + "category": "Error", + "code": 1316 + }, + "A parameter property cannot be declared using a rest parameter.": { + "category": "Error", + "code": 1317 + }, + "An abstract accessor cannot have an implementation.": { + "category": "Error", + "code": 1318 + }, + "A default export can only be used in an ECMAScript-style module.": { + "category": "Error", + "code": 1319 + }, + "Type of 'await' operand must either be a valid promise or must not contain a callable 'then' member.": { + "category": "Error", + "code": 1320 + }, + "Type of 'yield' operand in an async generator must either be a valid promise or must not contain a callable 'then' member.": { + "category": "Error", + "code": 1321 + }, + "Type of iterated elements of a 'yield*' operand must either be a valid promise or must not contain a callable 'then' member.": { + "category": "Error", + "code": 1322 + }, + "Dynamic imports are only supported when the '--module' flag is set to 'es2020', 'es2022', 'esnext', 'commonjs', 'amd', 'system', 'umd', 'node16', 'node18', 'node20', or 'nodenext'.": { + "category": "Error", + "code": 1323 + }, + "Dynamic imports only support a second argument when the '--module' option is set to 'esnext', 'node16', 'node18', 'node20', 'nodenext', or 'preserve'.": { + "category": "Error", + "code": 1324 + }, + "Argument of dynamic import cannot be spread element.": { + "category": "Error", + "code": 1325 + }, + "This use of 'import' is invalid. 'import()' calls can be written, but they must have parentheses and cannot have type arguments.": { + "category": "Error", + "code": 1326 + }, + "String literal with double quotes expected.": { + "category": "Error", + "code": 1327 + }, + "Property value can only be string literal, numeric literal, 'true', 'false', 'null', object literal or array literal.": { + "category": "Error", + "code": 1328 + }, + "'{0}' accepts too few arguments to be used as a decorator here. Did you mean to call it first and write '@{0}()'?": { + "category": "Error", + "code": 1329 + }, + "A property of an interface or type literal whose type is a 'unique symbol' type must be 'readonly'.": { + "category": "Error", + "code": 1330 + }, + "A property of a class whose type is a 'unique symbol' type must be both 'static' and 'readonly'.": { + "category": "Error", + "code": 1331 + }, + "A variable whose type is a 'unique symbol' type must be 'const'.": { + "category": "Error", + "code": 1332 + }, + "'unique symbol' types may not be used on a variable declaration with a binding name.": { + "category": "Error", + "code": 1333 + }, + "'unique symbol' types are only allowed on variables in a variable statement.": { + "category": "Error", + "code": 1334 + }, + "'unique symbol' types are not allowed here.": { + "category": "Error", + "code": 1335 + }, + "An index signature parameter type cannot be a literal type or generic type. Consider using a mapped object type instead.": { + "category": "Error", + "code": 1337 + }, + "'infer' declarations are only permitted in the 'extends' clause of a conditional type.": { + "category": "Error", + "code": 1338 + }, + "Module '{0}' does not refer to a value, but is used as a value here.": { + "category": "Error", + "code": 1339 + }, + "Module '{0}' does not refer to a type, but is used as a type here. Did you mean 'typeof import('{0}')'?": { + "category": "Error", + "code": 1340 + }, + "Class constructor may not be an accessor.": { + "category": "Error", + "code": 1341 + }, + "The 'import.meta' meta-property is only allowed when the '--module' option is 'es2020', 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', or 'nodenext'.": { + "category": "Error", + "code": 1343 + }, + "A label is not allowed here.": { + "category": "Error", + "code": 1344 + }, + "An expression of type 'void' cannot be tested for truthiness.": { + "category": "Error", + "code": 1345 + }, + "This parameter is not allowed with 'use strict' directive.": { + "category": "Error", + "code": 1346 + }, + "'use strict' directive cannot be used with non-simple parameter list.": { + "category": "Error", + "code": 1347 + }, + "Non-simple parameter declared here.": { + "category": "Error", + "code": 1348 + }, + "'use strict' directive used here.": { + "category": "Error", + "code": 1349 + }, + "Print the final configuration instead of building.": { + "category": "Message", + "code": 1350 + }, + "An identifier or keyword cannot immediately follow a numeric literal.": { + "category": "Error", + "code": 1351 + }, + "A bigint literal cannot use exponential notation.": { + "category": "Error", + "code": 1352 + }, + "A bigint literal must be an integer.": { + "category": "Error", + "code": 1353 + }, + "'readonly' type modifier is only permitted on array and tuple literal types.": { + "category": "Error", + "code": 1354 + }, + "A 'const' assertion can only be applied to references to enum members, or string, number, boolean, array, or object literals.": { + "category": "Error", + "code": 1355 + }, + "Did you mean to mark this function as 'async'?": { + "category": "Error", + "code": 1356 + }, + "An enum member name must be followed by a ',', '=', or '}'.": { + "category": "Error", + "code": 1357 + }, + "Tagged template expressions are not permitted in an optional chain.": { + "category": "Error", + "code": 1358 + }, + "Identifier expected. '{0}' is a reserved word that cannot be used here.": { + "category": "Error", + "code": 1359 + }, + "Type '{0}' does not satisfy the expected type '{1}'.": { + "category": "Error", + "code": 1360 + }, + "'{0}' cannot be used as a value because it was imported using 'import type'.": { + "category": "Error", + "code": 1361 + }, + "'{0}' cannot be used as a value because it was exported using 'export type'.": { + "category": "Error", + "code": 1362 + }, + "A type-only import can specify a default import or named bindings, but not both.": { + "category": "Error", + "code": 1363 + }, + "Convert to type-only export": { + "category": "Message", + "code": 1364 + }, + "Convert all re-exported types to type-only exports": { + "category": "Message", + "code": 1365 + }, + "Split into two separate import declarations": { + "category": "Message", + "code": 1366 + }, + "Split all invalid type-only imports": { + "category": "Message", + "code": 1367 + }, + "Class constructor may not be a generator.": { + "category": "Error", + "code": 1368 + }, + "Did you mean '{0}'?": { + "category": "Message", + "code": 1369 + }, + "'await' expressions are only allowed at the top level of a file when that file is a module, but this file has no imports or exports. Consider adding an empty 'export {}' to make this file a module.": { + "category": "Error", + "code": 1375 + }, + "'{0}' was imported here.": { + "category": "Message", + "code": 1376 + }, + "'{0}' was exported here.": { + "category": "Message", + "code": 1377 + }, + "Top-level 'await' expressions are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.": { + "category": "Error", + "code": 1378 + }, + "An import alias cannot reference a declaration that was exported using 'export type'.": { + "category": "Error", + "code": 1379 + }, + "An import alias cannot reference a declaration that was imported using 'import type'.": { + "category": "Error", + "code": 1380 + }, + "Unexpected token. Did you mean `{'}'}` or `}`?": { + "category": "Error", + "code": 1381 + }, + "Unexpected token. Did you mean `{'>'}` or `>`?": { + "category": "Error", + "code": 1382 + }, + "Function type notation must be parenthesized when used in a union type.": { + "category": "Error", + "code": 1385 + }, + "Constructor type notation must be parenthesized when used in a union type.": { + "category": "Error", + "code": 1386 + }, + "Function type notation must be parenthesized when used in an intersection type.": { + "category": "Error", + "code": 1387 + }, + "Constructor type notation must be parenthesized when used in an intersection type.": { + "category": "Error", + "code": 1388 + }, + "'{0}' is not allowed as a variable declaration name.": { + "category": "Error", + "code": 1389 + }, + "'{0}' is not allowed as a parameter name.": { + "category": "Error", + "code": 1390 + }, + "An import alias cannot use 'import type'": { + "category": "Error", + "code": 1392 + }, + "Imported via {0} from file '{1}'": { + "category": "Message", + "code": 1393 + }, + "Imported via {0} from file '{1}' with packageId '{2}'": { + "category": "Message", + "code": 1394 + }, + "Imported via {0} from file '{1}' to import 'importHelpers' as specified in compilerOptions": { + "category": "Message", + "code": 1395 + }, + "Imported via {0} from file '{1}' with packageId '{2}' to import 'importHelpers' as specified in compilerOptions": { + "category": "Message", + "code": 1396 + }, + "Imported via {0} from file '{1}' to import 'jsx' and 'jsxs' factory functions": { + "category": "Message", + "code": 1397 + }, + "Imported via {0} from file '{1}' with packageId '{2}' to import 'jsx' and 'jsxs' factory functions": { + "category": "Message", + "code": 1398 + }, + "File is included via import here.": { + "category": "Message", + "code": 1399 + }, + "Referenced via '{0}' from file '{1}'": { + "category": "Message", + "code": 1400 + }, + "File is included via reference here.": { + "category": "Message", + "code": 1401 + }, + "Type library referenced via '{0}' from file '{1}'": { + "category": "Message", + "code": 1402 + }, + "Type library referenced via '{0}' from file '{1}' with packageId '{2}'": { + "category": "Message", + "code": 1403 + }, + "File is included via type library reference here.": { + "category": "Message", + "code": 1404 + }, + "Library referenced via '{0}' from file '{1}'": { + "category": "Message", + "code": 1405 + }, + "File is included via library reference here.": { + "category": "Message", + "code": 1406 + }, + "Matched by include pattern '{0}' in '{1}'": { + "category": "Message", + "code": 1407 + }, + "File is matched by include pattern specified here.": { + "category": "Message", + "code": 1408 + }, + "Part of 'files' list in tsconfig.json": { + "category": "Message", + "code": 1409 + }, + "File is matched by 'files' list specified here.": { + "category": "Message", + "code": 1410 + }, + "Output from referenced project '{0}' included because '{1}' specified": { + "category": "Message", + "code": 1411 + }, + "Output from referenced project '{0}' included because '--module' is specified as 'none'": { + "category": "Message", + "code": 1412 + }, + "File is output from referenced project specified here.": { + "category": "Message", + "code": 1413 + }, + "Source from referenced project '{0}' included because '{1}' specified": { + "category": "Message", + "code": 1414 + }, + "Source from referenced project '{0}' included because '--module' is specified as 'none'": { + "category": "Message", + "code": 1415 + }, + "File is source from referenced project specified here.": { + "category": "Message", + "code": 1416 + }, + "Entry point of type library '{0}' specified in compilerOptions": { + "category": "Message", + "code": 1417 + }, + "Entry point of type library '{0}' specified in compilerOptions with packageId '{1}'": { + "category": "Message", + "code": 1418 + }, + "File is entry point of type library specified here.": { + "category": "Message", + "code": 1419 + }, + "Entry point for implicit type library '{0}'": { + "category": "Message", + "code": 1420 + }, + "Entry point for implicit type library '{0}' with packageId '{1}'": { + "category": "Message", + "code": 1421 + }, + "Library '{0}' specified in compilerOptions": { + "category": "Message", + "code": 1422 + }, + "File is library specified here.": { + "category": "Message", + "code": 1423 + }, + "Default library": { + "category": "Message", + "code": 1424 + }, + "Default library for target '{0}'": { + "category": "Message", + "code": 1425 + }, + "File is default library for target specified here.": { + "category": "Message", + "code": 1426 + }, + "Root file specified for compilation": { + "category": "Message", + "code": 1427 + }, + "File is output of project reference source '{0}'": { + "category": "Message", + "code": 1428 + }, + "File redirects to file '{0}'": { + "category": "Message", + "code": 1429 + }, + "The file is in the program because:": { + "category": "Message", + "code": 1430 + }, + "'for await' loops are only allowed at the top level of a file when that file is a module, but this file has no imports or exports. Consider adding an empty 'export {}' to make this file a module.": { + "category": "Error", + "code": 1431 + }, + "Top-level 'for await' loops are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.": { + "category": "Error", + "code": 1432 + }, + "Neither decorators nor modifiers may be applied to 'this' parameters.": { + "category": "Error", + "code": 1433 + }, + "Unexpected keyword or identifier.": { + "category": "Error", + "code": 1434 + }, + "Unknown keyword or identifier. Did you mean '{0}'?": { + "category": "Error", + "code": 1435 + }, + "Decorators must precede the name and all keywords of property declarations.": { + "category": "Error", + "code": 1436 + }, + "Namespace must be given a name.": { + "category": "Error", + "code": 1437 + }, + "Interface must be given a name.": { + "category": "Error", + "code": 1438 + }, + "Type alias must be given a name.": { + "category": "Error", + "code": 1439 + }, + "Variable declaration not allowed at this location.": { + "category": "Error", + "code": 1440 + }, + "Cannot start a function call in a type annotation.": { + "category": "Error", + "code": 1441 + }, + "Expected '=' for property initializer.": { + "category": "Error", + "code": 1442 + }, + "Module declaration names may only use ' or \" quoted strings.": { + "category": "Error", + "code": 1443 + }, + "'{0}' resolves to a type-only declaration and must be re-exported using a type-only re-export when '{1}' is enabled.": { + "category": "Error", + "code": 1448 + }, + "Preserve unused imported values in the JavaScript output that would otherwise be removed.": { + "category": "Message", + "code": 1449 + }, + "Dynamic imports can only accept a module specifier and an optional set of attributes as arguments": { + "category": "Message", + "code": 1450 + }, + "Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression": { + "category": "Error", + "code": 1451 + }, + "`resolution-mode` should be either `require` or `import`.": { + "category": "Error", + "code": 1453 + }, + "`resolution-mode` can only be set for type-only imports.": { + "category": "Error", + "code": 1454 + }, + "`resolution-mode` is the only valid key for type import assertions.": { + "category": "Error", + "code": 1455 + }, + "Type import assertions should have exactly one key - `resolution-mode` - with value `import` or `require`.": { + "category": "Error", + "code": 1456 + }, + "Matched by default include pattern '**/*'": { + "category": "Message", + "code": 1457 + }, + "File is ECMAScript module because '{0}' has field \"type\" with value \"module\"": { + "category": "Message", + "code": 1458 + }, + "File is CommonJS module because '{0}' has field \"type\" whose value is not \"module\"": { + "category": "Message", + "code": 1459 + }, + "File is CommonJS module because '{0}' does not have field \"type\"": { + "category": "Message", + "code": 1460 + }, + "File is CommonJS module because 'package.json' was not found": { + "category": "Message", + "code": 1461 + }, + "'resolution-mode' is the only valid key for type import attributes.": { + "category": "Error", + "code": 1463 + }, + "Type import attributes should have exactly one key - 'resolution-mode' - with value 'import' or 'require'.": { + "category": "Error", + "code": 1464 + }, + + "The 'import.meta' meta-property is not allowed in files which will build into CommonJS output.": { + "category": "Error", + "code": 1470 + }, + "Module '{0}' cannot be imported using this construct. The specifier only resolves to an ES module, which cannot be imported with 'require'. Use an ECMAScript import instead.": { + "category": "Error", + "code": 1471 + }, + "'catch' or 'finally' expected.": { + "category": "Error", + "code": 1472 + }, + "An import declaration can only be used at the top level of a module.": { + "category": "Error", + "code": 1473 + }, + "An export declaration can only be used at the top level of a module.": { + "category": "Error", + "code": 1474 + }, + "Control what method is used to detect module-format JS files.": { + "category": "Message", + "code": 1475 + }, + "\"auto\": Treat files with imports, exports, import.meta, jsx (with jsx: react-jsx), or esm format (with module: node16+) as modules.": { + "category": "Message", + "code": 1476 + }, + "An instantiation expression cannot be followed by a property access.": { + "category": "Error", + "code": 1477 + }, + "Identifier or string literal expected.": { + "category": "Error", + "code": 1478 + }, + "The current file is a CommonJS module whose imports will produce 'require' calls; however, the referenced file is an ECMAScript module and cannot be imported with 'require'. Consider writing a dynamic 'import(\"{0}\")' call instead.": { + "category": "Error", + "code": 1479 + }, + "To convert this file to an ECMAScript module, change its file extension to '{0}' or create a local package.json file with `{ \"type\": \"module\" }`.": { + "category": "Message", + "code": 1480 + }, + "To convert this file to an ECMAScript module, change its file extension to '{0}', or add the field `\"type\": \"module\"` to '{1}'.": { + "category": "Message", + "code": 1481 + }, + "To convert this file to an ECMAScript module, add the field `\"type\": \"module\"` to '{0}'.": { + "category": "Message", + "code": 1482 + }, + "To convert this file to an ECMAScript module, create a local package.json file with `{ \"type\": \"module\" }`.": { + "category": "Message", + "code": 1483 + }, + "'{0}' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.": { + "category": "Error", + "code": 1484 + }, + "'{0}' resolves to a type-only declaration and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.": { + "category": "Error", + "code": 1485 + }, + "Decorator used before 'export' here.": { + "category": "Error", + "code": 1486 + }, + "Octal escape sequences are not allowed. Use the syntax '{0}'.": { + "category": "Error", + "code": 1487 + }, + "Escape sequence '{0}' is not allowed.": { + "category": "Error", + "code": 1488 + }, + "Decimals with leading zeros are not allowed.": { + "category": "Error", + "code": 1489 + }, + "File appears to be binary.": { + "category": "Error", + "code": 1490 + }, + "'{0}' modifier cannot appear on a 'using' declaration.": { + "category": "Error", + "code": 1491 + }, + "'{0}' declarations may not have binding patterns.": { + "category": "Error", + "code": 1492 + }, + "The left-hand side of a 'for...in' statement cannot be a 'using' declaration.": { + "category": "Error", + "code": 1493 + }, + "The left-hand side of a 'for...in' statement cannot be an 'await using' declaration.": { + "category": "Error", + "code": 1494 + }, + "'{0}' modifier cannot appear on an 'await using' declaration.": { + "category": "Error", + "code": 1495 + }, + "Identifier, string literal, or number literal expected.": { + "category": "Error", + "code": 1496 + }, + "Expression must be enclosed in parentheses to be used as a decorator.": { + "category": "Error", + "code": 1497 + }, + "Invalid syntax in decorator.": { + "category": "Error", + "code": 1498 + }, + "Unknown regular expression flag.": { + "category": "Error", + "code": 1499 + }, + "Duplicate regular expression flag.": { + "category": "Error", + "code": 1500 + }, + "This regular expression flag is only available when targeting '{0}' or later.": { + "category": "Error", + "code": 1501 + }, + "The Unicode (u) flag and the Unicode Sets (v) flag cannot be set simultaneously.": { + "category": "Error", + "code": 1502 + }, + "Named capturing groups are only available when targeting 'ES2018' or later.": { + "category": "Error", + "code": 1503 + }, + "Subpattern flags must be present when there is a minus sign.": { + "category": "Error", + "code": 1504 + }, + "Incomplete quantifier. Digit expected.": { + "category": "Error", + "code": 1505 + }, + "Numbers out of order in quantifier.": { + "category": "Error", + "code": 1506 + }, + "There is nothing available for repetition.": { + "category": "Error", + "code": 1507 + }, + "Unexpected '{0}'. Did you mean to escape it with backslash?": { + "category": "Error", + "code": 1508 + }, + "This regular expression flag cannot be toggled within a subpattern.": { + "category": "Error", + "code": 1509 + }, + "'\\k' must be followed by a capturing group name enclosed in angle brackets.": { + "category": "Error", + "code": 1510 + }, + "'\\q' is only available inside character class.": { + "category": "Error", + "code": 1511 + }, + "'\\c' must be followed by an ASCII letter.": { + "category": "Error", + "code": 1512 + }, + "Undetermined character escape.": { + "category": "Error", + "code": 1513 + }, + "Expected a capturing group name.": { + "category": "Error", + "code": 1514 + }, + "Named capturing groups with the same name must be mutually exclusive to each other.": { + "category": "Error", + "code": 1515 + }, + "A character class range must not be bounded by another character class.": { + "category": "Error", + "code": 1516 + }, + "Range out of order in character class.": { + "category": "Error", + "code": 1517 + }, + "Anything that would possibly match more than a single character is invalid inside a negated character class.": { + "category": "Error", + "code": 1518 + }, + "Operators must not be mixed within a character class. Wrap it in a nested class instead.": { + "category": "Error", + "code": 1519 + }, + "Expected a class set operand.": { + "category": "Error", + "code": 1520 + }, + "'\\q' must be followed by string alternatives enclosed in braces.": { + "category": "Error", + "code": 1521 + }, + "A character class must not contain a reserved double punctuator. Did you mean to escape it with backslash?": { + "category": "Error", + "code": 1522 + }, + "Expected a Unicode property name.": { + "category": "Error", + "code": 1523 + }, + "Unknown Unicode property name.": { + "category": "Error", + "code": 1524 + }, + "Expected a Unicode property value.": { + "category": "Error", + "code": 1525 + }, + "Unknown Unicode property value.": { + "category": "Error", + "code": 1526 + }, + "Expected a Unicode property name or value.": { + "category": "Error", + "code": 1527 + }, + "Any Unicode property that would possibly match more than a single character is only available when the Unicode Sets (v) flag is set.": { + "category": "Error", + "code": 1528 + }, + "Unknown Unicode property name or value.": { + "category": "Error", + "code": 1529 + }, + "Unicode property value expressions are only available when the Unicode (u) flag or the Unicode Sets (v) flag is set.": { + "category": "Error", + "code": 1530 + }, + "'\\{0}' must be followed by a Unicode property value expression enclosed in braces.": { + "category": "Error", + "code": 1531 + }, + "There is no capturing group named '{0}' in this regular expression.": { + "category": "Error", + "code": 1532 + }, + "This backreference refers to a group that does not exist. There are only {0} capturing groups in this regular expression.": { + "category": "Error", + "code": 1533 + }, + "This backreference refers to a group that does not exist. There are no capturing groups in this regular expression.": { + "category": "Error", + "code": 1534 + }, + "This character cannot be escaped in a regular expression.": { + "category": "Error", + "code": 1535 + }, + "Octal escape sequences and backreferences are not allowed in a character class. If this was intended as an escape sequence, use the syntax '{0}' instead.": { + "category": "Error", + "code": 1536 + }, + "Decimal escape sequences and backreferences are not allowed in a character class.": { + "category": "Error", + "code": 1537 + }, + "Unicode escape sequences are only available when the Unicode (u) flag or the Unicode Sets (v) flag is set.": { + "category": "Error", + "code": 1538 + }, + "A 'bigint' literal cannot be used as a property name.": { + "category": "Error", + "code": 1539 + }, + "A 'namespace' declaration should not be declared using the 'module' keyword. Please use the 'namespace' keyword instead.": { + "category": "Error", + "code": 1540 + }, + "Type-only import of an ECMAScript module from a CommonJS module must have a 'resolution-mode' attribute.": { + "category": "Error", + "code": 1541 + }, + "Type import of an ECMAScript module from a CommonJS module must have a 'resolution-mode' attribute.": { + "category": "Error", + "code": 1542 + }, + "Importing a JSON file into an ECMAScript module requires a 'type: \"json\"' import attribute when 'module' is set to '{0}'.": { + "category": "Error", + "code": 1543 + }, + "Named imports from a JSON file into an ECMAScript module are not allowed when 'module' is set to '{0}'.": { + "category": "Error", + "code": 1544 + }, + "'using' declarations are not allowed in ambient contexts.": { + "category": "Error", + "code": 1545 + }, + "'await using' declarations are not allowed in ambient contexts.": { + "category": "Error", + "code": 1546 + }, + "'using' declarations are not allowed in 'case' or 'default' clauses unless contained within a block.": { + "category": "Error", + "code": 1547 + }, + "'await using' declarations are not allowed in 'case' or 'default' clauses unless contained within a block.": { + "category": "Error", + "code": 1548 + }, + "Ignore the tsconfig found and build with commandline options and files.": { + "category": "Message", + "code": 1549 + }, + + "The types of '{0}' are incompatible between these types.": { + "category": "Error", + "code": 2200 + }, + "The types returned by '{0}' are incompatible between these types.": { + "category": "Error", + "code": 2201 + }, + "Call signature return types '{0}' and '{1}' are incompatible.": { + "category": "Error", + "code": 2202, + "elidedInCompatabilityPyramid": true + }, + "Construct signature return types '{0}' and '{1}' are incompatible.": { + "category": "Error", + "code": 2203, + "elidedInCompatabilityPyramid": true + }, + "Call signatures with no arguments have incompatible return types '{0}' and '{1}'.": { + "category": "Error", + "code": 2204, + "elidedInCompatabilityPyramid": true + }, + "Construct signatures with no arguments have incompatible return types '{0}' and '{1}'.": { + "category": "Error", + "code": 2205, + "elidedInCompatabilityPyramid": true + }, + "The 'type' modifier cannot be used on a named import when 'import type' is used on its import statement.": { + "category": "Error", + "code": 2206 + }, + "The 'type' modifier cannot be used on a named export when 'export type' is used on its export statement.": { + "category": "Error", + "code": 2207 + }, + "This type parameter might need an `extends {0}` constraint.": { + "category": "Error", + "code": 2208 + }, + + "The project root is ambiguous, but is required to resolve export map entry '{0}' in file '{1}'. Supply the `rootDir` compiler option to disambiguate.": { + "category": "Error", + "code": 2209 + }, + "The project root is ambiguous, but is required to resolve import map entry '{0}' in file '{1}'. Supply the `rootDir` compiler option to disambiguate.": { + "category": "Error", + "code": 2210 + }, + "Add `extends` constraint.": { + "category": "Message", + "code": 2211 + }, + "Add `extends` constraint to all type parameters": { + "category": "Message", + "code": 2212 + }, + + "Duplicate identifier '{0}'.": { + "category": "Error", + "code": 2300 + }, + "Initializer of instance member variable '{0}' cannot reference identifier '{1}' declared in the constructor.": { + "category": "Error", + "code": 2301 + }, + "Static members cannot reference class type parameters.": { + "category": "Error", + "code": 2302 + }, + "Circular definition of import alias '{0}'.": { + "category": "Error", + "code": 2303 + }, + "Cannot find name '{0}'.": { + "category": "Error", + "code": 2304 + }, + "Module '{0}' has no exported member '{1}'.": { + "category": "Error", + "code": 2305 + }, + "File '{0}' is not a module.": { + "category": "Error", + "code": 2306 + }, + "Cannot find module '{0}' or its corresponding type declarations.": { + "category": "Error", + "code": 2307 + }, + "Module {0} has already exported a member named '{1}'. Consider explicitly re-exporting to resolve the ambiguity.": { + "category": "Error", + "code": 2308 + }, + "An export assignment cannot be used in a module with other exported elements.": { + "category": "Error", + "code": 2309 + }, + "Type '{0}' recursively references itself as a base type.": { + "category": "Error", + "code": 2310 + }, + "Cannot find name '{0}'. Did you mean to write this in an async function?": { + "category": "Error", + "code": 2311 + }, + "An interface can only extend an object type or intersection of object types with statically known members.": { + "category": "Error", + "code": 2312 + }, + "Type parameter '{0}' has a circular constraint.": { + "category": "Error", + "code": 2313 + }, + "Generic type '{0}' requires {1} type argument(s).": { + "category": "Error", + "code": 2314 + }, + "Type '{0}' is not generic.": { + "category": "Error", + "code": 2315 + }, + "Global type '{0}' must be a class or interface type.": { + "category": "Error", + "code": 2316 + }, + "Global type '{0}' must have {1} type parameter(s).": { + "category": "Error", + "code": 2317 + }, + "Cannot find global type '{0}'.": { + "category": "Error", + "code": 2318 + }, + "Named property '{0}' of types '{1}' and '{2}' are not identical.": { + "category": "Error", + "code": 2319 + }, + "Interface '{0}' cannot simultaneously extend types '{1}' and '{2}'.": { + "category": "Error", + "code": 2320 + }, + "Excessive stack depth comparing types '{0}' and '{1}'.": { + "category": "Error", + "code": 2321 + }, + "Type '{0}' is not assignable to type '{1}'.": { + "category": "Error", + "code": 2322 + }, + "Cannot redeclare exported variable '{0}'.": { + "category": "Error", + "code": 2323 + }, + "Property '{0}' is missing in type '{1}'.": { + "category": "Error", + "code": 2324 + }, + "Property '{0}' is private in type '{1}' but not in type '{2}'.": { + "category": "Error", + "code": 2325 + }, + "Types of property '{0}' are incompatible.": { + "category": "Error", + "code": 2326 + }, + "Property '{0}' is optional in type '{1}' but required in type '{2}'.": { + "category": "Error", + "code": 2327 + }, + "Types of parameters '{0}' and '{1}' are incompatible.": { + "category": "Error", + "code": 2328 + }, + "Index signature for type '{0}' is missing in type '{1}'.": { + "category": "Error", + "code": 2329 + }, + "'{0}' and '{1}' index signatures are incompatible.": { + "category": "Error", + "code": 2330 + }, + "'this' cannot be referenced in a module or namespace body.": { + "category": "Error", + "code": 2331 + }, + "'this' cannot be referenced in current location.": { + "category": "Error", + "code": 2332 + }, + "'this' cannot be referenced in a static property initializer.": { + "category": "Error", + "code": 2334 + }, + "'super' can only be referenced in a derived class.": { + "category": "Error", + "code": 2335 + }, + "'super' cannot be referenced in constructor arguments.": { + "category": "Error", + "code": 2336 + }, + "Super calls are not permitted outside constructors or in nested functions inside constructors.": { + "category": "Error", + "code": 2337 + }, + "'super' property access is permitted only in a constructor, member function, or member accessor of a derived class.": { + "category": "Error", + "code": 2338 + }, + "Property '{0}' does not exist on type '{1}'.": { + "category": "Error", + "code": 2339 + }, + "Only public and protected methods of the base class are accessible via the 'super' keyword.": { + "category": "Error", + "code": 2340 + }, + "Property '{0}' is private and only accessible within class '{1}'.": { + "category": "Error", + "code": 2341 + }, + "This syntax requires an imported helper named '{1}' which does not exist in '{0}'. Consider upgrading your version of '{0}'.": { + "category": "Error", + "code": 2343 + }, + "Type '{0}' does not satisfy the constraint '{1}'.": { + "category": "Error", + "code": 2344 + }, + "Argument of type '{0}' is not assignable to parameter of type '{1}'.": { + "category": "Error", + "code": 2345 + }, + "Call target does not contain any signatures.": { + "category": "Error", + "code": 2346 + }, + "Untyped function calls may not accept type arguments.": { + "category": "Error", + "code": 2347 + }, + "Value of type '{0}' is not callable. Did you mean to include 'new'?": { + "category": "Error", + "code": 2348 + }, + "This expression is not callable.": { + "category": "Error", + "code": 2349 + }, + "Only a void function can be called with the 'new' keyword.": { + "category": "Error", + "code": 2350 + }, + "This expression is not constructable.": { + "category": "Error", + "code": 2351 + }, + "Conversion of type '{0}' to type '{1}' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.": { + "category": "Error", + "code": 2352 + }, + "Object literal may only specify known properties, and '{0}' does not exist in type '{1}'.": { + "category": "Error", + "code": 2353 + }, + "This syntax requires an imported helper but module '{0}' cannot be found.": { + "category": "Error", + "code": 2354 + }, + "A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value.": { + "category": "Error", + "code": 2355 + }, + "An arithmetic operand must be of type 'any', 'number', 'bigint' or an enum type.": { + "category": "Error", + "code": 2356 + }, + "The operand of an increment or decrement operator must be a variable or a property access.": { + "category": "Error", + "code": 2357 + }, + "The left-hand side of an 'instanceof' expression must be of type 'any', an object type or a type parameter.": { + "category": "Error", + "code": 2358 + }, + "The right-hand side of an 'instanceof' expression must be either of type 'any', a class, function, or other type assignable to the 'Function' interface type, or an object type with a 'Symbol.hasInstance' method.": { + "category": "Error", + "code": 2359 + }, + "The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.": { + "category": "Error", + "code": 2362 + }, + "The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.": { + "category": "Error", + "code": 2363 + }, + "The left-hand side of an assignment expression must be a variable or a property access.": { + "category": "Error", + "code": 2364 + }, + "Operator '{0}' cannot be applied to types '{1}' and '{2}'.": { + "category": "Error", + "code": 2365 + }, + "Function lacks ending return statement and return type does not include 'undefined'.": { + "category": "Error", + "code": 2366 + }, + "This comparison appears to be unintentional because the types '{0}' and '{1}' have no overlap.": { + "category": "Error", + "code": 2367 + }, + "Type parameter name cannot be '{0}'.": { + "category": "Error", + "code": 2368 + }, + "A parameter property is only allowed in a constructor implementation.": { + "category": "Error", + "code": 2369 + }, + "A rest parameter must be of an array type.": { + "category": "Error", + "code": 2370 + }, + "A parameter initializer is only allowed in a function or constructor implementation.": { + "category": "Error", + "code": 2371 + }, + "Parameter '{0}' cannot reference itself.": { + "category": "Error", + "code": 2372 + }, + "Parameter '{0}' cannot reference identifier '{1}' declared after it.": { + "category": "Error", + "code": 2373 + }, + "Duplicate index signature for type '{0}'.": { + "category": "Error", + "code": 2374 + }, + "Type '{0}' is not assignable to type '{1}' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.": { + "category": "Error", + "code": 2375 + }, + "A 'super' call must be the first statement in the constructor to refer to 'super' or 'this' when a derived class contains initialized properties, parameter properties, or private identifiers.": { + "category": "Error", + "code": 2376 + }, + "Constructors for derived classes must contain a 'super' call.": { + "category": "Error", + "code": 2377 + }, + "A 'get' accessor must return a value.": { + "category": "Error", + "code": 2378 + }, + "Argument of type '{0}' is not assignable to parameter of type '{1}' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.": { + "category": "Error", + "code": 2379 + }, + "Overload signatures must all be exported or non-exported.": { + "category": "Error", + "code": 2383 + }, + "Overload signatures must all be ambient or non-ambient.": { + "category": "Error", + "code": 2384 + }, + "Overload signatures must all be public, private or protected.": { + "category": "Error", + "code": 2385 + }, + "Overload signatures must all be optional or required.": { + "category": "Error", + "code": 2386 + }, + "Function overload must be static.": { + "category": "Error", + "code": 2387 + }, + "Function overload must not be static.": { + "category": "Error", + "code": 2388 + }, + "Function implementation name must be '{0}'.": { + "category": "Error", + "code": 2389 + }, + "Constructor implementation is missing.": { + "category": "Error", + "code": 2390 + }, + "Function implementation is missing or not immediately following the declaration.": { + "category": "Error", + "code": 2391 + }, + "Multiple constructor implementations are not allowed.": { + "category": "Error", + "code": 2392 + }, + "Duplicate function implementation.": { + "category": "Error", + "code": 2393 + }, + "This overload signature is not compatible with its implementation signature.": { + "category": "Error", + "code": 2394 + }, + "Individual declarations in merged declaration '{0}' must be all exported or all local.": { + "category": "Error", + "code": 2395 + }, + "Duplicate identifier 'arguments'. Compiler uses 'arguments' to initialize rest parameters.": { + "category": "Error", + "code": 2396 + }, + "Declaration name conflicts with built-in global identifier '{0}'.": { + "category": "Error", + "code": 2397 + }, + "'constructor' cannot be used as a parameter property name.": { + "category": "Error", + "code": 2398 + }, + "Duplicate identifier '_this'. Compiler uses variable declaration '_this' to capture 'this' reference.": { + "category": "Error", + "code": 2399 + }, + "Expression resolves to variable declaration '_this' that compiler uses to capture 'this' reference.": { + "category": "Error", + "code": 2400 + }, + "A 'super' call must be a root-level statement within a constructor of a derived class that contains initialized properties, parameter properties, or private identifiers.": { + "category": "Error", + "code": 2401 + }, + "Expression resolves to '_super' that compiler uses to capture base class reference.": { + "category": "Error", + "code": 2402 + }, + "Subsequent variable declarations must have the same type. Variable '{0}' must be of type '{1}', but here has type '{2}'.": { + "category": "Error", + "code": 2403 + }, + "The left-hand side of a 'for...in' statement cannot use a type annotation.": { + "category": "Error", + "code": 2404 + }, + "The left-hand side of a 'for...in' statement must be of type 'string' or 'any'.": { + "category": "Error", + "code": 2405 + }, + "The left-hand side of a 'for...in' statement must be a variable or a property access.": { + "category": "Error", + "code": 2406 + }, + "The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type '{0}'.": { + "category": "Error", + "code": 2407 + }, + "Setters cannot return a value.": { + "category": "Error", + "code": 2408 + }, + "Return type of constructor signature must be assignable to the instance type of the class.": { + "category": "Error", + "code": 2409 + }, + "The 'with' statement is not supported. All symbols in a 'with' block will have type 'any'.": { + "category": "Error", + "code": 2410 + }, + "Type '{0}' is not assignable to type '{1}' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the type of the target.": { + "category": "Error", + "code": 2412 + }, + "Property '{0}' of type '{1}' is not assignable to '{2}' index type '{3}'.": { + "category": "Error", + "code": 2411 + }, + "'{0}' index type '{1}' is not assignable to '{2}' index type '{3}'.": { + "category": "Error", + "code": 2413 + }, + "Class name cannot be '{0}'.": { + "category": "Error", + "code": 2414 + }, + "Class '{0}' incorrectly extends base class '{1}'.": { + "category": "Error", + "code": 2415 + }, + "Property '{0}' in type '{1}' is not assignable to the same property in base type '{2}'.": { + "category": "Error", + "code": 2416 + }, + "Class static side '{0}' incorrectly extends base class static side '{1}'.": { + "category": "Error", + "code": 2417 + }, + "Type of computed property's value is '{0}', which is not assignable to type '{1}'.": { + "category": "Error", + "code": 2418 + }, + "Types of construct signatures are incompatible.": { + "category": "Error", + "code": 2419 + }, + "Class '{0}' incorrectly implements interface '{1}'.": { + "category": "Error", + "code": 2420 + }, + "A class can only implement an object type or intersection of object types with statically known members.": { + "category": "Error", + "code": 2422 + }, + "Class '{0}' defines instance member function '{1}', but extended class '{2}' defines it as instance member accessor.": { + "category": "Error", + "code": 2423 + }, + "Class '{0}' defines instance member property '{1}', but extended class '{2}' defines it as instance member function.": { + "category": "Error", + "code": 2425 + }, + "Class '{0}' defines instance member accessor '{1}', but extended class '{2}' defines it as instance member function.": { + "category": "Error", + "code": 2426 + }, + "Interface name cannot be '{0}'.": { + "category": "Error", + "code": 2427 + }, + "All declarations of '{0}' must have identical type parameters.": { + "category": "Error", + "code": 2428 + }, + "Interface '{0}' incorrectly extends interface '{1}'.": { + "category": "Error", + "code": 2430 + }, + "Enum name cannot be '{0}'.": { + "category": "Error", + "code": 2431 + }, + "In an enum with multiple declarations, only one declaration can omit an initializer for its first enum element.": { + "category": "Error", + "code": 2432 + }, + "A namespace declaration cannot be in a different file from a class or function with which it is merged.": { + "category": "Error", + "code": 2433 + }, + "A namespace declaration cannot be located prior to a class or function with which it is merged.": { + "category": "Error", + "code": 2434 + }, + "Ambient modules cannot be nested in other modules or namespaces.": { + "category": "Error", + "code": 2435 + }, + "Ambient module declaration cannot specify relative module name.": { + "category": "Error", + "code": 2436 + }, + "Module '{0}' is hidden by a local declaration with the same name.": { + "category": "Error", + "code": 2437 + }, + "Import name cannot be '{0}'.": { + "category": "Error", + "code": 2438 + }, + "Import or export declaration in an ambient module declaration cannot reference module through relative module name.": { + "category": "Error", + "code": 2439 + }, + "Import declaration conflicts with local declaration of '{0}'.": { + "category": "Error", + "code": 2440 + }, + "Duplicate identifier '{0}'. Compiler reserves name '{1}' in top level scope of a module.": { + "category": "Error", + "code": 2441 + }, + "Types have separate declarations of a private property '{0}'.": { + "category": "Error", + "code": 2442 + }, + "Property '{0}' is protected but type '{1}' is not a class derived from '{2}'.": { + "category": "Error", + "code": 2443 + }, + "Property '{0}' is protected in type '{1}' but public in type '{2}'.": { + "category": "Error", + "code": 2444 + }, + "Property '{0}' is protected and only accessible within class '{1}' and its subclasses.": { + "category": "Error", + "code": 2445 + }, + "Property '{0}' is protected and only accessible through an instance of class '{1}'. This is an instance of class '{2}'.": { + "category": "Error", + "code": 2446 + }, + "The '{0}' operator is not allowed for boolean types. Consider using '{1}' instead.": { + "category": "Error", + "code": 2447 + }, + "Block-scoped variable '{0}' used before its declaration.": { + "category": "Error", + "code": 2448 + }, + "Class '{0}' used before its declaration.": { + "category": "Error", + "code": 2449 + }, + "Enum '{0}' used before its declaration.": { + "category": "Error", + "code": 2450 + }, + "Cannot redeclare block-scoped variable '{0}'.": { + "category": "Error", + "code": 2451 + }, + "An enum member cannot have a numeric name.": { + "category": "Error", + "code": 2452 + }, + "Variable '{0}' is used before being assigned.": { + "category": "Error", + "code": 2454 + }, + "Type alias '{0}' circularly references itself.": { + "category": "Error", + "code": 2456 + }, + "Type alias name cannot be '{0}'.": { + "category": "Error", + "code": 2457 + }, + "An AMD module cannot have multiple name assignments.": { + "category": "Error", + "code": 2458 + }, + "Module '{0}' declares '{1}' locally, but it is not exported.": { + "category": "Error", + "code": 2459 + }, + "Module '{0}' declares '{1}' locally, but it is exported as '{2}'.": { + "category": "Error", + "code": 2460 + }, + "Type '{0}' is not an array type.": { + "category": "Error", + "code": 2461 + }, + "A rest element must be last in a destructuring pattern.": { + "category": "Error", + "code": 2462 + }, + "A binding pattern parameter cannot be optional in an implementation signature.": { + "category": "Error", + "code": 2463 + }, + "A computed property name must be of type 'string', 'number', 'symbol', or 'any'.": { + "category": "Error", + "code": 2464 + }, + "'this' cannot be referenced in a computed property name.": { + "category": "Error", + "code": 2465 + }, + "'super' cannot be referenced in a computed property name.": { + "category": "Error", + "code": 2466 + }, + "A computed property name cannot reference a type parameter from its containing type.": { + "category": "Error", + "code": 2467 + }, + "Cannot find global value '{0}'.": { + "category": "Error", + "code": 2468 + }, + "The '{0}' operator cannot be applied to type 'symbol'.": { + "category": "Error", + "code": 2469 + }, + "Spread operator in 'new' expressions is only available when targeting ECMAScript 5 and higher.": { + "category": "Error", + "code": 2472 + }, + "Enum declarations must all be const or non-const.": { + "category": "Error", + "code": 2473 + }, + "const enum member initializers must be constant expressions.": { + "category": "Error", + "code": 2474 + }, + "'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment or type query.": { + "category": "Error", + "code": 2475 + }, + "A const enum member can only be accessed using a string literal.": { + "category": "Error", + "code": 2476 + }, + "'const' enum member initializer was evaluated to a non-finite value.": { + "category": "Error", + "code": 2477 + }, + "'const' enum member initializer was evaluated to disallowed value 'NaN'.": { + "category": "Error", + "code": 2478 + }, + "'let' is not allowed to be used as a name in 'let' or 'const' declarations.": { + "category": "Error", + "code": 2480 + }, + "Cannot initialize outer scoped variable '{0}' in the same scope as block scoped declaration '{1}'.": { + "category": "Error", + "code": 2481 + }, + "The left-hand side of a 'for...of' statement cannot use a type annotation.": { + "category": "Error", + "code": 2483 + }, + "Export declaration conflicts with exported declaration of '{0}'.": { + "category": "Error", + "code": 2484 + }, + "The left-hand side of a 'for...of' statement must be a variable or a property access.": { + "category": "Error", + "code": 2487 + }, + "Type '{0}' must have a '[Symbol.iterator]()' method that returns an iterator.": { + "category": "Error", + "code": 2488 + }, + "An iterator must have a 'next()' method.": { + "category": "Error", + "code": 2489 + }, + "The type returned by the '{0}()' method of an iterator must have a 'value' property.": { + "category": "Error", + "code": 2490 + }, + "The left-hand side of a 'for...in' statement cannot be a destructuring pattern.": { + "category": "Error", + "code": 2491 + }, + "Cannot redeclare identifier '{0}' in catch clause.": { + "category": "Error", + "code": 2492 + }, + "Tuple type '{0}' of length '{1}' has no element at index '{2}'.": { + "category": "Error", + "code": 2493 + }, + "Using a string in a 'for...of' statement is only supported in ECMAScript 5 and higher.": { + "category": "Error", + "code": 2494 + }, + "Type '{0}' is not an array type or a string type.": { + "category": "Error", + "code": 2495 + }, + "The 'arguments' object cannot be referenced in an arrow function in ES5. Consider using a standard function expression.": { + "category": "Error", + "code": 2496 + }, + "This module can only be referenced with ECMAScript imports/exports by turning on the '{0}' flag and referencing its default export.": { + "category": "Error", + "code": 2497 + }, + "Module '{0}' uses 'export =' and cannot be used with 'export *'.": { + "category": "Error", + "code": 2498 + }, + "An interface can only extend an identifier/qualified-name with optional type arguments.": { + "category": "Error", + "code": 2499 + }, + "A class can only implement an identifier/qualified-name with optional type arguments.": { + "category": "Error", + "code": 2500 + }, + "A rest element cannot contain a binding pattern.": { + "category": "Error", + "code": 2501 + }, + "'{0}' is referenced directly or indirectly in its own type annotation.": { + "category": "Error", + "code": 2502 + }, + "Cannot find namespace '{0}'.": { + "category": "Error", + "code": 2503 + }, + "Type '{0}' must have a '[Symbol.asyncIterator]()' method that returns an async iterator.": { + "category": "Error", + "code": 2504 + }, + "A generator cannot have a 'void' type annotation.": { + "category": "Error", + "code": 2505 + }, + "'{0}' is referenced directly or indirectly in its own base expression.": { + "category": "Error", + "code": 2506 + }, + "Type '{0}' is not a constructor function type.": { + "category": "Error", + "code": 2507 + }, + "No base constructor has the specified number of type arguments.": { + "category": "Error", + "code": 2508 + }, + "Base constructor return type '{0}' is not an object type or intersection of object types with statically known members.": { + "category": "Error", + "code": 2509 + }, + "Base constructors must all have the same return type.": { + "category": "Error", + "code": 2510 + }, + "Cannot create an instance of an abstract class.": { + "category": "Error", + "code": 2511 + }, + "Overload signatures must all be abstract or non-abstract.": { + "category": "Error", + "code": 2512 + }, + "Abstract method '{0}' in class '{1}' cannot be accessed via super expression.": { + "category": "Error", + "code": 2513 + }, + "A tuple type cannot be indexed with a negative value.": { + "category": "Error", + "code": 2514 + }, + "Non-abstract class '{0}' does not implement inherited abstract member {1} from class '{2}'.": { + "category": "Error", + "code": 2515 + }, + "All declarations of an abstract method must be consecutive.": { + "category": "Error", + "code": 2516 + }, + "Cannot assign an abstract constructor type to a non-abstract constructor type.": { + "category": "Error", + "code": 2517 + }, + "A 'this'-based type guard is not compatible with a parameter-based type guard.": { + "category": "Error", + "code": 2518 + }, + "An async iterator must have a 'next()' method.": { + "category": "Error", + "code": 2519 + }, + "Duplicate identifier '{0}'. Compiler uses declaration '{1}' to support async functions.": { + "category": "Error", + "code": 2520 + }, + "The 'arguments' object cannot be referenced in an async function or method in ES5. Consider using a standard function or method.": { + "category": "Error", + "code": 2522 + }, + "'yield' expressions cannot be used in a parameter initializer.": { + "category": "Error", + "code": 2523 + }, + "'await' expressions cannot be used in a parameter initializer.": { + "category": "Error", + "code": 2524 + }, + "A 'this' type is available only in a non-static member of a class or interface.": { + "category": "Error", + "code": 2526 + }, + "The inferred type of '{0}' references an inaccessible '{1}' type. A type annotation is necessary.": { + "category": "Error", + "code": 2527 + }, + "A module cannot have multiple default exports.": { + "category": "Error", + "code": 2528 + }, + "Duplicate identifier '{0}'. Compiler reserves name '{1}' in top level scope of a module containing async functions.": { + "category": "Error", + "code": 2529 + }, + "Property '{0}' is incompatible with index signature.": { + "category": "Error", + "code": 2530 + }, + "Object is possibly 'null'.": { + "category": "Error", + "code": 2531 + }, + "Object is possibly 'undefined'.": { + "category": "Error", + "code": 2532 + }, + "Object is possibly 'null' or 'undefined'.": { + "category": "Error", + "code": 2533 + }, + "A function returning 'never' cannot have a reachable end point.": { + "category": "Error", + "code": 2534 + }, + "Type '{0}' cannot be used to index type '{1}'.": { + "category": "Error", + "code": 2536 + }, + "Type '{0}' has no matching index signature for type '{1}'.": { + "category": "Error", + "code": 2537 + }, + "Type '{0}' cannot be used as an index type.": { + "category": "Error", + "code": 2538 + }, + "Cannot assign to '{0}' because it is not a variable.": { + "category": "Error", + "code": 2539 + }, + "Cannot assign to '{0}' because it is a read-only property.": { + "category": "Error", + "code": 2540 + }, + "Index signature in type '{0}' only permits reading.": { + "category": "Error", + "code": 2542 + }, + "Duplicate identifier '_newTarget'. Compiler uses variable declaration '_newTarget' to capture 'new.target' meta-property reference.": { + "category": "Error", + "code": 2543 + }, + "Expression resolves to variable declaration '_newTarget' that compiler uses to capture 'new.target' meta-property reference.": { + "category": "Error", + "code": 2544 + }, + "A mixin class must have a constructor with a single rest parameter of type 'any[]'.": { + "category": "Error", + "code": 2545 + }, + "The type returned by the '{0}()' method of an async iterator must be a promise for a type with a 'value' property.": { + "category": "Error", + "code": 2547 + }, + "Type '{0}' is not an array type or does not have a '[Symbol.iterator]()' method that returns an iterator.": { + "category": "Error", + "code": 2548 + }, + "Type '{0}' is not an array type or a string type or does not have a '[Symbol.iterator]()' method that returns an iterator.": { + "category": "Error", + "code": 2549 + }, + "Property '{0}' does not exist on type '{1}'. Do you need to change your target library? Try changing the 'lib' compiler option to '{2}' or later.": { + "category": "Error", + "code": 2550 + }, + "Property '{0}' does not exist on type '{1}'. Did you mean '{2}'?": { + "category": "Error", + "code": 2551 + }, + "Cannot find name '{0}'. Did you mean '{1}'?": { + "category": "Error", + "code": 2552 + }, + "Computed values are not permitted in an enum with string valued members.": { + "category": "Error", + "code": 2553 + }, + "Expected {0} arguments, but got {1}.": { + "category": "Error", + "code": 2554 + }, + "Expected at least {0} arguments, but got {1}.": { + "category": "Error", + "code": 2555 + }, + "A spread argument must either have a tuple type or be passed to a rest parameter.": { + "category": "Error", + "code": 2556 + }, + "Expected {0} type arguments, but got {1}.": { + "category": "Error", + "code": 2558 + }, + "Type '{0}' has no properties in common with type '{1}'.": { + "category": "Error", + "code": 2559 + }, + "Value of type '{0}' has no properties in common with type '{1}'. Did you mean to call it?": { + "category": "Error", + "code": 2560 + }, + "Object literal may only specify known properties, but '{0}' does not exist in type '{1}'. Did you mean to write '{2}'?": { + "category": "Error", + "code": 2561 + }, + "Base class expressions cannot reference class type parameters.": { + "category": "Error", + "code": 2562 + }, + "The containing function or module body is too large for control flow analysis.": { + "category": "Error", + "code": 2563 + }, + "Property '{0}' has no initializer and is not definitely assigned in the constructor.": { + "category": "Error", + "code": 2564 + }, + "Property '{0}' is used before being assigned.": { + "category": "Error", + "code": 2565 + }, + "A rest element cannot have a property name.": { + "category": "Error", + "code": 2566 + }, + "Enum declarations can only merge with namespace or other enum declarations.": { + "category": "Error", + "code": 2567 + }, + "Property '{0}' may not exist on type '{1}'. Did you mean '{2}'?": { + "category": "Error", + "code": 2568 + }, + + "Could not find name '{0}'. Did you mean '{1}'?": { + "category": "Error", + "code": 2570 + }, + "Object is of type 'unknown'.": { + "category": "Error", + "code": 2571 + }, + "A rest element type must be an array type.": { + "category": "Error", + "code": 2574 + }, + "No overload expects {0} arguments, but overloads do exist that expect either {1} or {2} arguments.": { + "category": "Error", + "code": 2575 + }, + "Property '{0}' does not exist on type '{1}'. Did you mean to access the static member '{2}' instead?": { + "category": "Error", + "code": 2576 + }, + "Return type annotation circularly references itself.": { + "category": "Error", + "code": 2577 + }, + "Unused '@ts-expect-error' directive.": { + "category": "Error", + "code": 2578 + }, + "Cannot find name '{0}'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node`.": { + "category": "Error", + "code": 2580 + }, + "Cannot find name '{0}'. Do you need to install type definitions for jQuery? Try `npm i --save-dev @types/jquery`.": { + "category": "Error", + "code": 2581 + }, + "Cannot find name '{0}'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.": { + "category": "Error", + "code": 2582 + }, + "Cannot find name '{0}'. Do you need to change your target library? Try changing the 'lib' compiler option to '{1}' or later.": { + "category": "Error", + "code": 2583 + }, + "Cannot find name '{0}'. Do you need to change your target library? Try changing the 'lib' compiler option to include 'dom'.": { + "category": "Error", + "code": 2584 + }, + "'{0}' only refers to a type, but is being used as a value here. Do you need to change your target library? Try changing the 'lib' compiler option to es2015 or later.": { + "category": "Error", + "code": 2585 + }, + "Cannot assign to '{0}' because it is a constant.": { + "category": "Error", + "code": 2588 + }, + "Type instantiation is excessively deep and possibly infinite.": { + "category": "Error", + "code": 2589 + }, + "Expression produces a union type that is too complex to represent.": { + "category": "Error", + "code": 2590 + }, + "Cannot find name '{0}'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.": { + "category": "Error", + "code": 2591 + }, + "Cannot find name '{0}'. Do you need to install type definitions for jQuery? Try `npm i --save-dev @types/jquery` and then add 'jquery' to the types field in your tsconfig.": { + "category": "Error", + "code": 2592 + }, + "Cannot find name '{0}'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha` and then add 'jest' or 'mocha' to the types field in your tsconfig.": { + "category": "Error", + "code": 2593 + }, + "This module is declared with 'export =', and can only be used with a default import when using the '{0}' flag.": { + "category": "Error", + "code": 2594 + }, + "'{0}' can only be imported by using a default import.": { + "category": "Error", + "code": 2595 + }, + "'{0}' can only be imported by turning on the 'esModuleInterop' flag and using a default import.": { + "category": "Error", + "code": 2596 + }, + "'{0}' can only be imported by using a 'require' call or by using a default import.": { + "category": "Error", + "code": 2597 + }, + "'{0}' can only be imported by using a 'require' call or by turning on the 'esModuleInterop' flag and using a default import.": { + "category": "Error", + "code": 2598 + }, + "JSX element implicitly has type 'any' because the global type 'JSX.Element' does not exist.": { + "category": "Error", + "code": 2602 + }, + "Property '{0}' in type '{1}' is not assignable to type '{2}'.": { + "category": "Error", + "code": 2603 + }, + "JSX element type '{0}' does not have any construct or call signatures.": { + "category": "Error", + "code": 2604 + }, + "Property '{0}' of JSX spread attribute is not assignable to target property.": { + "category": "Error", + "code": 2606 + }, + "JSX element class does not support attributes because it does not have a '{0}' property.": { + "category": "Error", + "code": 2607 + }, + "The global type 'JSX.{0}' may not have more than one property.": { + "category": "Error", + "code": 2608 + }, + "JSX spread child must be an array type.": { + "category": "Error", + "code": 2609 + }, + "'{0}' is defined as an accessor in class '{1}', but is overridden here in '{2}' as an instance property.": { + "category": "Error", + "code": 2610 + }, + "'{0}' is defined as a property in class '{1}', but is overridden here in '{2}' as an accessor.": { + "category": "Error", + "code": 2611 + }, + "Property '{0}' will overwrite the base property in '{1}'. If this is intentional, add an initializer. Otherwise, add a 'declare' modifier or remove the redundant declaration.": { + "category": "Error", + "code": 2612 + }, + "Module '{0}' has no default export. Did you mean to use 'import { {1} } from {0}' instead?": { + "category": "Error", + "code": 2613 + }, + "Module '{0}' has no exported member '{1}'. Did you mean to use 'import {1} from {0}' instead?": { + "category": "Error", + "code": 2614 + }, + "Type of property '{0}' circularly references itself in mapped type '{1}'.": { + "category": "Error", + "code": 2615 + }, + "'{0}' can only be imported by using 'import {1} = require({2})' or a default import.": { + "category": "Error", + "code": 2616 + }, + "'{0}' can only be imported by using 'import {1} = require({2})' or by turning on the 'esModuleInterop' flag and using a default import.": { + "category": "Error", + "code": 2617 + }, + "Source has {0} element(s) but target requires {1}.": { + "category": "Error", + "code": 2618 + }, + "Source has {0} element(s) but target allows only {1}.": { + "category": "Error", + "code": 2619 + }, + "Target requires {0} element(s) but source may have fewer.": { + "category": "Error", + "code": 2620 + }, + "Target allows only {0} element(s) but source may have more.": { + "category": "Error", + "code": 2621 + }, + "Source provides no match for required element at position {0} in target.": { + "category": "Error", + "code": 2623 + }, + "Source provides no match for variadic element at position {0} in target.": { + "category": "Error", + "code": 2624 + }, + "Variadic element at position {0} in source does not match element at position {1} in target.": { + "category": "Error", + "code": 2625 + }, + "Type at position {0} in source is not compatible with type at position {1} in target.": { + "category": "Error", + "code": 2626 + }, + "Type at positions {0} through {1} in source is not compatible with type at position {2} in target.": { + "category": "Error", + "code": 2627 + }, + "Cannot assign to '{0}' because it is an enum.": { + "category": "Error", + "code": 2628 + }, + "Cannot assign to '{0}' because it is a class.": { + "category": "Error", + "code": 2629 + }, + "Cannot assign to '{0}' because it is a function.": { + "category": "Error", + "code": 2630 + }, + "Cannot assign to '{0}' because it is a namespace.": { + "category": "Error", + "code": 2631 + }, + "Cannot assign to '{0}' because it is an import.": { + "category": "Error", + "code": 2632 + }, + "JSX property access expressions cannot include JSX namespace names": { + "category": "Error", + "code": 2633 + }, + "'{0}' index signatures are incompatible.": { + "category": "Error", + "code": 2634 + }, + "Type '{0}' has no signatures for which the type argument list is applicable.": { + "category": "Error", + "code": 2635 + }, + "Type '{0}' is not assignable to type '{1}' as implied by variance annotation.": { + "category": "Error", + "code": 2636 + }, + "Variance annotations are only supported in type aliases for object, function, constructor, and mapped types.": { + "category": "Error", + "code": 2637 + }, + "Type '{0}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator.": { + "category": "Error", + "code": 2638 + }, + "React components cannot include JSX namespace names": { + "category": "Error", + "code": 2639 + }, + + "Cannot augment module '{0}' with value exports because it resolves to a non-module entity.": { + "category": "Error", + "code": 2649 + }, + "Non-abstract class expression is missing implementations for the following members of '{0}': {1} and {2} more.": { + "category": "Error", + "code": 2650 + }, + "A member initializer in a enum declaration cannot reference members declared after it, including members defined in other enums.": { + "category": "Error", + "code": 2651 + }, + "Merged declaration '{0}' cannot include a default export declaration. Consider adding a separate 'export default {0}' declaration instead.": { + "category": "Error", + "code": 2652 + }, + "Non-abstract class expression does not implement inherited abstract member '{0}' from class '{1}'.": { + "category": "Error", + "code": 2653 + }, + "Non-abstract class '{0}' is missing implementations for the following members of '{1}': {2}.": { + "category": "Error", + "code": 2654 + }, + "Non-abstract class '{0}' is missing implementations for the following members of '{1}': {2} and {3} more.": { + "category": "Error", + "code": 2655 + }, + "Non-abstract class expression is missing implementations for the following members of '{0}': {1}.": { + "category": "Error", + "code": 2656 + }, + "JSX expressions must have one parent element.": { + "category": "Error", + "code": 2657 + }, + "Type '{0}' provides no match for the signature '{1}'.": { + "category": "Error", + "code": 2658 + }, + "'super' is only allowed in members of object literal expressions when option 'target' is 'ES2015' or higher.": { + "category": "Error", + "code": 2659 + }, + "'super' can only be referenced in members of derived classes or object literal expressions.": { + "category": "Error", + "code": 2660 + }, + "Cannot export '{0}'. Only local declarations can be exported from a module.": { + "category": "Error", + "code": 2661 + }, + "Cannot find name '{0}'. Did you mean the static member '{1}.{0}'?": { + "category": "Error", + "code": 2662 + }, + "Cannot find name '{0}'. Did you mean the instance member 'this.{0}'?": { + "category": "Error", + "code": 2663 + }, + "Invalid module name in augmentation, module '{0}' cannot be found.": { + "category": "Error", + "code": 2664 + }, + "Invalid module name in augmentation. Module '{0}' resolves to an untyped module at '{1}', which cannot be augmented.": { + "category": "Error", + "code": 2665 + }, + "Exports and export assignments are not permitted in module augmentations.": { + "category": "Error", + "code": 2666 + }, + "Imports are not permitted in module augmentations. Consider moving them to the enclosing external module.": { + "category": "Error", + "code": 2667 + }, + "'export' modifier cannot be applied to ambient modules and module augmentations since they are always visible.": { + "category": "Error", + "code": 2668 + }, + "Augmentations for the global scope can only be directly nested in external modules or ambient module declarations.": { + "category": "Error", + "code": 2669 + }, + "Augmentations for the global scope should have 'declare' modifier unless they appear in already ambient context.": { + "category": "Error", + "code": 2670 + }, + "Cannot augment module '{0}' because it resolves to a non-module entity.": { + "category": "Error", + "code": 2671 + }, + "Cannot assign a '{0}' constructor type to a '{1}' constructor type.": { + "category": "Error", + "code": 2672 + }, + "Constructor of class '{0}' is private and only accessible within the class declaration.": { + "category": "Error", + "code": 2673 + }, + "Constructor of class '{0}' is protected and only accessible within the class declaration.": { + "category": "Error", + "code": 2674 + }, + "Cannot extend a class '{0}'. Class constructor is marked as private.": { + "category": "Error", + "code": 2675 + }, + "Accessors must both be abstract or non-abstract.": { + "category": "Error", + "code": 2676 + }, + "A type predicate's type must be assignable to its parameter's type.": { + "category": "Error", + "code": 2677 + }, + "Type '{0}' is not comparable to type '{1}'.": { + "category": "Error", + "code": 2678 + }, + "A function that is called with the 'new' keyword cannot have a 'this' type that is 'void'.": { + "category": "Error", + "code": 2679 + }, + "A '{0}' parameter must be the first parameter.": { + "category": "Error", + "code": 2680 + }, + "A constructor cannot have a 'this' parameter.": { + "category": "Error", + "code": 2681 + }, + "'this' implicitly has type 'any' because it does not have a type annotation.": { + "category": "Error", + "code": 2683 + }, + "The 'this' context of type '{0}' is not assignable to method's 'this' of type '{1}'.": { + "category": "Error", + "code": 2684 + }, + "The 'this' types of each signature are incompatible.": { + "category": "Error", + "code": 2685 + }, + "'{0}' refers to a UMD global, but the current file is a module. Consider adding an import instead.": { + "category": "Error", + "code": 2686 + }, + "All declarations of '{0}' must have identical modifiers.": { + "category": "Error", + "code": 2687 + }, + "Cannot find type definition file for '{0}'.": { + "category": "Error", + "code": 2688 + }, + "Cannot extend an interface '{0}'. Did you mean 'implements'?": { + "category": "Error", + "code": 2689 + }, + "'{0}' only refers to a type, but is being used as a value here. Did you mean to use '{1} in {0}'?": { + "category": "Error", + "code": 2690 + }, + "'{0}' is a primitive, but '{1}' is a wrapper object. Prefer using '{0}' when possible.": { + "category": "Error", + "code": 2692 + }, + "'{0}' only refers to a type, but is being used as a value here.": { + "category": "Error", + "code": 2693 + }, + "Namespace '{0}' has no exported member '{1}'.": { + "category": "Error", + "code": 2694 + }, + "Left side of comma operator is unused and has no side effects.": { + "category": "Error", + "code": 2695, + "reportsUnnecessary": true + }, + "The 'Object' type is assignable to very few other types. Did you mean to use the 'any' type instead?": { + "category": "Error", + "code": 2696 + }, + "An async function or method must return a 'Promise'. Make sure you have a declaration for 'Promise' or include 'ES2015' in your '--lib' option.": { + "category": "Error", + "code": 2697 + }, + "Spread types may only be created from object types.": { + "category": "Error", + "code": 2698 + }, + "Static property '{0}' conflicts with built-in property 'Function.{0}' of constructor function '{1}'.": { + "category": "Error", + "code": 2699 + }, + "Rest types may only be created from object types.": { + "category": "Error", + "code": 2700 + }, + "The target of an object rest assignment must be a variable or a property access.": { + "category": "Error", + "code": 2701 + }, + "'{0}' only refers to a type, but is being used as a namespace here.": { + "category": "Error", + "code": 2702 + }, + "The operand of a 'delete' operator must be a property reference.": { + "category": "Error", + "code": 2703 + }, + "The operand of a 'delete' operator cannot be a read-only property.": { + "category": "Error", + "code": 2704 + }, + "An async function or method in ES5 requires the 'Promise' constructor. Make sure you have a declaration for the 'Promise' constructor or include 'ES2015' in your '--lib' option.": { + "category": "Error", + "code": 2705 + }, + "Required type parameters may not follow optional type parameters.": { + "category": "Error", + "code": 2706 + }, + "Generic type '{0}' requires between {1} and {2} type arguments.": { + "category": "Error", + "code": 2707 + }, + "Cannot use namespace '{0}' as a value.": { + "category": "Error", + "code": 2708 + }, + "Cannot use namespace '{0}' as a type.": { + "category": "Error", + "code": 2709 + }, + "'{0}' are specified twice. The attribute named '{0}' will be overwritten.": { + "category": "Error", + "code": 2710 + }, + "A dynamic import call returns a 'Promise'. Make sure you have a declaration for 'Promise' or include 'ES2015' in your '--lib' option.": { + "category": "Error", + "code": 2711 + }, + "A dynamic import call in ES5 requires the 'Promise' constructor. Make sure you have a declaration for the 'Promise' constructor or include 'ES2015' in your '--lib' option.": { + "category": "Error", + "code": 2712 + }, + "Cannot access '{0}.{1}' because '{0}' is a type, but not a namespace. Did you mean to retrieve the type of the property '{1}' in '{0}' with '{0}[\"{1}\"]'?": { + "category": "Error", + "code": 2713 + }, + "The expression of an export assignment must be an identifier or qualified name in an ambient context.": { + "category": "Error", + "code": 2714 + }, + "Abstract property '{0}' in class '{1}' cannot be accessed in the constructor.": { + "category": "Error", + "code": 2715 + }, + "Type parameter '{0}' has a circular default.": { + "category": "Error", + "code": 2716 + }, + "Subsequent property declarations must have the same type. Property '{0}' must be of type '{1}', but here has type '{2}'.": { + "category": "Error", + "code": 2717 + }, + "Duplicate property '{0}'.": { + "category": "Error", + "code": 2718 + }, + "Type '{0}' is not assignable to type '{1}'. Two different types with this name exist, but they are unrelated.": { + "category": "Error", + "code": 2719 + }, + "Class '{0}' incorrectly implements class '{1}'. Did you mean to extend '{1}' and inherit its members as a subclass?": { + "category": "Error", + "code": 2720 + }, + "Cannot invoke an object which is possibly 'null'.": { + "category": "Error", + "code": 2721 + }, + "Cannot invoke an object which is possibly 'undefined'.": { + "category": "Error", + "code": 2722 + }, + "Cannot invoke an object which is possibly 'null' or 'undefined'.": { + "category": "Error", + "code": 2723 + }, + "'{0}' has no exported member named '{1}'. Did you mean '{2}'?": { + "category": "Error", + "code": 2724 + }, + "Class name cannot be 'Object' when targeting ES5 and above with module {0}.": { + "category": "Error", + "code": 2725 + }, + "Cannot find lib definition for '{0}'.": { + "category": "Error", + "code": 2726 + }, + "Cannot find lib definition for '{0}'. Did you mean '{1}'?": { + "category": "Error", + "code": 2727 + }, + "'{0}' is declared here.": { + "category": "Message", + "code": 2728 + }, + "Property '{0}' is used before its initialization.": { + "category": "Error", + "code": 2729 + }, + "An arrow function cannot have a 'this' parameter.": { + "category": "Error", + "code": 2730 + }, + "Implicit conversion of a 'symbol' to a 'string' will fail at runtime. Consider wrapping this expression in 'String(...)'.": { + "category": "Error", + "code": 2731 + }, + "Cannot find module '{0}'. Consider using '--resolveJsonModule' to import module with '.json' extension.": { + "category": "Error", + "code": 2732 + }, + "Property '{0}' was also declared here.": { + "category": "Error", + "code": 2733 + }, + "Are you missing a semicolon?": { + "category": "Error", + "code": 2734 + }, + "Did you mean for '{0}' to be constrained to type 'new (...args: any[]) => {1}'?": { + "category": "Error", + "code": 2735 + }, + "Operator '{0}' cannot be applied to type '{1}'.": { + "category": "Error", + "code": 2736 + }, + "BigInt literals are not available when targeting lower than ES2020.": { + "category": "Error", + "code": 2737 + }, + "An outer value of 'this' is shadowed by this container.": { + "category": "Message", + "code": 2738 + }, + "Type '{0}' is missing the following properties from type '{1}': {2}": { + "category": "Error", + "code": 2739 + }, + "Type '{0}' is missing the following properties from type '{1}': {2}, and {3} more.": { + "category": "Error", + "code": 2740 + }, + "Property '{0}' is missing in type '{1}' but required in type '{2}'.": { + "category": "Error", + "code": 2741 + }, + "The inferred type of '{0}' cannot be named without a reference to '{1}'. This is likely not portable. A type annotation is necessary.": { + "category": "Error", + "code": 2742 + }, + "No overload expects {0} type arguments, but overloads do exist that expect either {1} or {2} type arguments.": { + "category": "Error", + "code": 2743 + }, + "Type parameter defaults can only reference previously declared type parameters.": { + "category": "Error", + "code": 2744 + }, + "This JSX tag's '{0}' prop expects type '{1}' which requires multiple children, but only a single child was provided.": { + "category": "Error", + "code": 2745 + }, + "This JSX tag's '{0}' prop expects a single child of type '{1}', but multiple children were provided.": { + "category": "Error", + "code": 2746 + }, + "'{0}' components don't accept text as child elements. Text in JSX has the type 'string', but the expected type of '{1}' is '{2}'.": { + "category": "Error", + "code": 2747 + }, + "Cannot access ambient const enums when '{0}' is enabled.": { + "category": "Error", + "code": 2748 + }, + "'{0}' refers to a value, but is being used as a type here. Did you mean 'typeof {0}'?": { + "category": "Error", + "code": 2749 + }, + "The implementation signature is declared here.": { + "category": "Error", + "code": 2750 + }, + "Circularity originates in type at this location.": { + "category": "Error", + "code": 2751 + }, + "The first export default is here.": { + "category": "Error", + "code": 2752 + }, + "Another export default is here.": { + "category": "Error", + "code": 2753 + }, + "'super' may not use type arguments.": { + "category": "Error", + "code": 2754 + }, + "No constituent of type '{0}' is callable.": { + "category": "Error", + "code": 2755 + }, + "Not all constituents of type '{0}' are callable.": { + "category": "Error", + "code": 2756 + }, + "Type '{0}' has no call signatures.": { + "category": "Error", + "code": 2757 + }, + "Each member of the union type '{0}' has signatures, but none of those signatures are compatible with each other.": { + "category": "Error", + "code": 2758 + }, + "No constituent of type '{0}' is constructable.": { + "category": "Error", + "code": 2759 + }, + "Not all constituents of type '{0}' are constructable.": { + "category": "Error", + "code": 2760 + }, + "Type '{0}' has no construct signatures.": { + "category": "Error", + "code": 2761 + }, + "Each member of the union type '{0}' has construct signatures, but none of those signatures are compatible with each other.": { + "category": "Error", + "code": 2762 + }, + "Cannot iterate value because the 'next' method of its iterator expects type '{1}', but for-of will always send '{0}'.": { + "category": "Error", + "code": 2763 + }, + "Cannot iterate value because the 'next' method of its iterator expects type '{1}', but array spread will always send '{0}'.": { + "category": "Error", + "code": 2764 + }, + "Cannot iterate value because the 'next' method of its iterator expects type '{1}', but array destructuring will always send '{0}'.": { + "category": "Error", + "code": 2765 + }, + "Cannot delegate iteration to value because the 'next' method of its iterator expects type '{1}', but the containing generator will always send '{0}'.": { + "category": "Error", + "code": 2766 + }, + "The '{0}' property of an iterator must be a method.": { + "category": "Error", + "code": 2767 + }, + "The '{0}' property of an async iterator must be a method.": { + "category": "Error", + "code": 2768 + }, + "No overload matches this call.": { + "category": "Error", + "code": 2769 + }, + "The last overload gave the following error.": { + "category": "Error", + "code": 2770 + }, + "The last overload is declared here.": { + "category": "Error", + "code": 2771 + }, + "Overload {0} of {1}, '{2}', gave the following error.": { + "category": "Error", + "code": 2772 + }, + "Did you forget to use 'await'?": { + "category": "Error", + "code": 2773 + }, + "This condition will always return true since this function is always defined. Did you mean to call it instead?": { + "category": "Error", + "code": 2774 + }, + "Assertions require every name in the call target to be declared with an explicit type annotation.": { + "category": "Error", + "code": 2775 + }, + "Assertions require the call target to be an identifier or qualified name.": { + "category": "Error", + "code": 2776 + }, + "The operand of an increment or decrement operator may not be an optional property access.": { + "category": "Error", + "code": 2777 + }, + "The target of an object rest assignment may not be an optional property access.": { + "category": "Error", + "code": 2778 + }, + "The left-hand side of an assignment expression may not be an optional property access.": { + "category": "Error", + "code": 2779 + }, + "The left-hand side of a 'for...in' statement may not be an optional property access.": { + "category": "Error", + "code": 2780 + }, + "The left-hand side of a 'for...of' statement may not be an optional property access.": { + "category": "Error", + "code": 2781 + }, + "'{0}' needs an explicit type annotation.": { + "category": "Message", + "code": 2782 + }, + "'{0}' is specified more than once, so this usage will be overwritten.": { + "category": "Error", + "code": 2783 + }, + "'get' and 'set' accessors cannot declare 'this' parameters.": { + "category": "Error", + "code": 2784 + }, + "This spread always overwrites this property.": { + "category": "Error", + "code": 2785 + }, + "'{0}' cannot be used as a JSX component.": { + "category": "Error", + "code": 2786 + }, + "Its return type '{0}' is not a valid JSX element.": { + "category": "Error", + "code": 2787 + }, + "Its instance type '{0}' is not a valid JSX element.": { + "category": "Error", + "code": 2788 + }, + "Its element type '{0}' is not a valid JSX element.": { + "category": "Error", + "code": 2789 + }, + "The operand of a 'delete' operator must be optional.": { + "category": "Error", + "code": 2790 + }, + "Exponentiation cannot be performed on 'bigint' values unless the 'target' option is set to 'es2016' or later.": { + "category": "Error", + "code": 2791 + }, + "Cannot find module '{0}'. Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?": { + "category": "Error", + "code": 2792 + }, + "The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.": { + "category": "Error", + "code": 2793 + }, + "Expected {0} arguments, but got {1}. Did you forget to include 'void' in your type argument to 'Promise'?": { + "category": "Error", + "code": 2794 + }, + "The 'intrinsic' keyword can only be used to declare compiler provided intrinsic types.": { + "category": "Error", + "code": 2795 + }, + "It is likely that you are missing a comma to separate these two template expressions. They form a tagged template expression which cannot be invoked.": { + "category": "Error", + "code": 2796 + }, + "A mixin class that extends from a type variable containing an abstract construct signature must also be declared 'abstract'.": { + "category": "Error", + "code": 2797 + }, + "The declaration was marked as deprecated here.": { + "category": "Error", + "code": 2798 + }, + "Type produces a tuple type that is too large to represent.": { + "category": "Error", + "code": 2799 + }, + "Expression produces a tuple type that is too large to represent.": { + "category": "Error", + "code": 2800 + }, + "This condition will always return true since this '{0}' is always defined.": { + "category": "Error", + "code": 2801 + }, + "Type '{0}' can only be iterated through when using the '--downlevelIteration' flag or with a '--target' of 'es2015' or higher.": { + "category": "Error", + "code": 2802 + }, + "Cannot assign to private method '{0}'. Private methods are not writable.": { + "category": "Error", + "code": 2803 + }, + "Duplicate identifier '{0}'. Static and instance elements cannot share the same private name.": { + "category": "Error", + "code": 2804 + }, + "Private accessor was defined without a getter.": { + "category": "Error", + "code": 2806 + }, + "This syntax requires an imported helper named '{1}' with {2} parameters, which is not compatible with the one in '{0}'. Consider upgrading your version of '{0}'.": { + "category": "Error", + "code": 2807 + }, + "A get accessor must be at least as accessible as the setter": { + "category": "Error", + "code": 2808 + }, + "Declaration or statement expected. This '=' follows a block of statements, so if you intended to write a destructuring assignment, you might need to wrap the whole assignment in parentheses.": { + "category": "Error", + "code": 2809 + }, + "Expected 1 argument, but got 0. 'new Promise()' needs a JSDoc hint to produce a 'resolve' that can be called without arguments.": { + "category": "Error", + "code": 2810 + }, + "Initializer for property '{0}'": { + "category": "Error", + "code": 2811 + }, + "Property '{0}' does not exist on type '{1}'. Try changing the 'lib' compiler option to include 'dom'.": { + "category": "Error", + "code": 2812 + }, + "Class declaration cannot implement overload list for '{0}'.": { + "category": "Error", + "code": 2813 + }, + "Function with bodies can only merge with classes that are ambient.": { + "category": "Error", + "code": 2814 + }, + "'arguments' cannot be referenced in property initializers or class static initialization blocks.": { + "category": "Error", + "code": 2815 + }, + "Cannot use 'this' in a static property initializer of a decorated class.": { + "category": "Error", + "code": 2816 + }, + "Property '{0}' has no initializer and is not definitely assigned in a class static block.": { + "category": "Error", + "code": 2817 + }, + "Duplicate identifier '{0}'. Compiler reserves name '{1}' when emitting 'super' references in static initializers.": { + "category": "Error", + "code": 2818 + }, + "Namespace name cannot be '{0}'.": { + "category": "Error", + "code": 2819 + }, + "Type '{0}' is not assignable to type '{1}'. Did you mean '{2}'?": { + "category": "Error", + "code": 2820 + }, + "Import assertions are only supported when the '--module' option is set to 'esnext', 'node18', 'node20', 'nodenext', or 'preserve'.": { + "category": "Error", + "code": 2821 + }, + "Import assertions cannot be used with type-only imports or exports.": { + "category": "Error", + "code": 2822 + }, + "Import attributes are only supported when the '--module' option is set to 'esnext', 'node18', 'node20', 'nodenext', or 'preserve'.": { + "category": "Error", + "code": 2823 + }, + "Cannot find namespace '{0}'. Did you mean '{1}'?": { + "category": "Error", + "code": 2833 + }, + "Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Consider adding an extension to the import path.": { + "category": "Error", + "code": 2834 + }, + "Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean '{0}'?": { + "category": "Error", + "code": 2835 + }, + "Import assertions are not allowed on statements that compile to CommonJS 'require' calls.": { + "category": "Error", + "code": 2836 + }, + "Import assertion values must be string literal expressions.": { + "category": "Error", + "code": 2837 + }, + "All declarations of '{0}' must have identical constraints.": { + "category": "Error", + "code": 2838 + }, + "This condition will always return '{0}' since JavaScript compares objects by reference, not value.": { + "category": "Error", + "code": 2839 + }, + "An interface cannot extend a primitive type like '{0}'. It can only extend other named object types.": { + "category": "Error", + "code": 2840 + }, + "'{0}' is an unused renaming of '{1}'. Did you intend to use it as a type annotation?": { + "category": "Error", + "code": 2842 + }, + "We can only write a type for '{0}' by adding a type for the entire parameter here.": { + "category": "Error", + "code": 2843 + }, + "Type of instance member variable '{0}' cannot reference identifier '{1}' declared in the constructor.": { + "category": "Error", + "code": 2844 + }, + "This condition will always return '{0}'.": { + "category": "Error", + "code": 2845 + }, + "A declaration file cannot be imported without 'import type'. Did you mean to import an implementation file '{0}' instead?": { + "category": "Error", + "code": 2846 + }, + "The right-hand side of an 'instanceof' expression must not be an instantiation expression.": { + "category": "Error", + "code": 2848 + }, + "Target signature provides too few arguments. Expected {0} or more, but got {1}.": { + "category": "Error", + "code": 2849 + }, + "The initializer of a 'using' declaration must be either an object with a '[Symbol.dispose]()' method, or be 'null' or 'undefined'.": { + "category": "Error", + "code": 2850 + }, + "The initializer of an 'await using' declaration must be either an object with a '[Symbol.asyncDispose]()' or '[Symbol.dispose]()' method, or be 'null' or 'undefined'.": { + "category": "Error", + "code": 2851 + }, + "'await using' statements are only allowed within async functions and at the top levels of modules.": { + "category": "Error", + "code": 2852 + }, + "'await using' statements are only allowed at the top level of a file when that file is a module, but this file has no imports or exports. Consider adding an empty 'export {}' to make this file a module.": { + "category": "Error", + "code": 2853 + }, + "Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'node18', 'node20', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.": { + "category": "Error", + "code": 2854 + }, + "Class field '{0}' defined by the parent class is not accessible in the child class via super.": { + "category": "Error", + "code": 2855 + }, + "Import attributes are not allowed on statements that compile to CommonJS 'require' calls.": { + "category": "Error", + "code": 2856 + }, + "Import attributes cannot be used with type-only imports or exports.": { + "category": "Error", + "code": 2857 + }, + "Import attribute values must be string literal expressions.": { + "category": "Error", + "code": 2858 + }, + "Excessive complexity comparing types '{0}' and '{1}'.": { + "category": "Error", + "code": 2859 + }, + "The left-hand side of an 'instanceof' expression must be assignable to the first argument of the right-hand side's '[Symbol.hasInstance]' method.": { + "category": "Error", + "code": 2860 + }, + "An object's '[Symbol.hasInstance]' method must return a boolean value for it to be used on the right-hand side of an 'instanceof' expression.": { + "category": "Error", + "code": 2861 + }, + "Type '{0}' is generic and can only be indexed for reading.": { + "category": "Error", + "code": 2862 + }, + "A class cannot extend a primitive type like '{0}'. Classes can only extend constructable values.": { + "category": "Error", + "code": 2863 + }, + "A class cannot implement a primitive type like '{0}'. It can only implement other named object types.": { + "category": "Error", + "code": 2864 + }, + "Import '{0}' conflicts with local value, so must be declared with a type-only import when 'isolatedModules' is enabled.": { + "category": "Error", + "code": 2865 + }, + "Import '{0}' conflicts with global value used in this file, so must be declared with a type-only import when 'isolatedModules' is enabled.": { + "category": "Error", + "code": 2866 + }, + "Cannot find name '{0}'. Do you need to install type definitions for Bun? Try `npm i --save-dev @types/bun`.": { + "category": "Error", + "code": 2867 + }, + "Cannot find name '{0}'. Do you need to install type definitions for Bun? Try `npm i --save-dev @types/bun` and then add 'bun' to the types field in your tsconfig.": { + "category": "Error", + "code": 2868 + }, + "Right operand of ?? is unreachable because the left operand is never nullish.": { + "category": "Error", + "code": 2869 + }, + "This binary expression is never nullish. Are you missing parentheses?": { + "category": "Error", + "code": 2870 + }, + "This expression is always nullish.": { + "category": "Error", + "code": 2871 + }, + "This kind of expression is always truthy.": { + "category": "Error", + "code": 2872 + }, + "This kind of expression is always falsy.": { + "category": "Error", + "code": 2873 + }, + "This JSX tag requires '{0}' to be in scope, but it could not be found.": { + "category": "Error", + "code": 2874 + }, + "This JSX tag requires the module path '{0}' to exist, but none could be found. Make sure you have types for the appropriate package installed.": { + "category": "Error", + "code": 2875 + }, + "This relative import path is unsafe to rewrite because it looks like a file name, but actually resolves to \"{0}\".": { + "category": "Error", + "code": 2876 + }, + "This import uses a '{0}' extension to resolve to an input TypeScript file, but will not be rewritten during emit because it is not a relative path.": { + "category": "Error", + "code": 2877 + }, + "This import path is unsafe to rewrite because it resolves to another project, and the relative path between the projects' output files is not the same as the relative path between its input files.": { + "category": "Error", + "code": 2878 + }, + "Using JSX fragments requires fragment factory '{0}' to be in scope, but it could not be found.": { + "category": "Error", + "code": 2879 + }, + "Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.": { + "category": "Error", + "code": 2880 + }, + "This expression is never nullish.": { + "category": "Error", + "code": 2881 + }, + "Cannot find module or type declarations for side-effect import of '{0}'.": { + "category": "Error", + "code": 2882 + }, + "The inferred type of '{0}' cannot be named without a reference to '{2}' from '{1}'. This is likely not portable. A type annotation is necessary.": { + "category": "Error", + "code": 2883 + }, + + "Import declaration '{0}' is using private name '{1}'.": { + "category": "Error", + "code": 4000 + }, + "Type parameter '{0}' of exported class has or is using private name '{1}'.": { + "category": "Error", + "code": 4002 + }, + "Type parameter '{0}' of exported interface has or is using private name '{1}'.": { + "category": "Error", + "code": 4004 + }, + "Type parameter '{0}' of constructor signature from exported interface has or is using private name '{1}'.": { + "category": "Error", + "code": 4006 + }, + "Type parameter '{0}' of call signature from exported interface has or is using private name '{1}'.": { + "category": "Error", + "code": 4008 + }, + "Type parameter '{0}' of public static method from exported class has or is using private name '{1}'.": { + "category": "Error", + "code": 4010 + }, + "Type parameter '{0}' of public method from exported class has or is using private name '{1}'.": { + "category": "Error", + "code": 4012 + }, + "Type parameter '{0}' of method from exported interface has or is using private name '{1}'.": { + "category": "Error", + "code": 4014 + }, + "Type parameter '{0}' of exported function has or is using private name '{1}'.": { + "category": "Error", + "code": 4016 + }, + "Implements clause of exported class '{0}' has or is using private name '{1}'.": { + "category": "Error", + "code": 4019 + }, + "'extends' clause of exported class '{0}' has or is using private name '{1}'.": { + "category": "Error", + "code": 4020 + }, + "'extends' clause of exported class has or is using private name '{0}'.": { + "category": "Error", + "code": 4021 + }, + "'extends' clause of exported interface '{0}' has or is using private name '{1}'.": { + "category": "Error", + "code": 4022 + }, + "Exported variable '{0}' has or is using name '{1}' from external module {2} but cannot be named.": { + "category": "Error", + "code": 4023 + }, + "Exported variable '{0}' has or is using name '{1}' from private module '{2}'.": { + "category": "Error", + "code": 4024 + }, + "Exported variable '{0}' has or is using private name '{1}'.": { + "category": "Error", + "code": 4025 + }, + "Public static property '{0}' of exported class has or is using name '{1}' from external module {2} but cannot be named.": { + "category": "Error", + "code": 4026 + }, + "Public static property '{0}' of exported class has or is using name '{1}' from private module '{2}'.": { + "category": "Error", + "code": 4027 + }, + "Public static property '{0}' of exported class has or is using private name '{1}'.": { + "category": "Error", + "code": 4028 + }, + "Public property '{0}' of exported class has or is using name '{1}' from external module {2} but cannot be named.": { + "category": "Error", + "code": 4029 + }, + "Public property '{0}' of exported class has or is using name '{1}' from private module '{2}'.": { + "category": "Error", + "code": 4030 + }, + "Public property '{0}' of exported class has or is using private name '{1}'.": { + "category": "Error", + "code": 4031 + }, + "Property '{0}' of exported interface has or is using name '{1}' from private module '{2}'.": { + "category": "Error", + "code": 4032 + }, + "Property '{0}' of exported interface has or is using private name '{1}'.": { + "category": "Error", + "code": 4033 + }, + "Parameter type of public static setter '{0}' from exported class has or is using name '{1}' from private module '{2}'.": { + "category": "Error", + "code": 4034 + }, + "Parameter type of public static setter '{0}' from exported class has or is using private name '{1}'.": { + "category": "Error", + "code": 4035 + }, + "Parameter type of public setter '{0}' from exported class has or is using name '{1}' from private module '{2}'.": { + "category": "Error", + "code": 4036 + }, + "Parameter type of public setter '{0}' from exported class has or is using private name '{1}'.": { + "category": "Error", + "code": 4037 + }, + "Return type of public static getter '{0}' from exported class has or is using name '{1}' from external module {2} but cannot be named.": { + "category": "Error", + "code": 4038 + }, + "Return type of public static getter '{0}' from exported class has or is using name '{1}' from private module '{2}'.": { + "category": "Error", + "code": 4039 + }, + "Return type of public static getter '{0}' from exported class has or is using private name '{1}'.": { + "category": "Error", + "code": 4040 + }, + "Return type of public getter '{0}' from exported class has or is using name '{1}' from external module {2} but cannot be named.": { + "category": "Error", + "code": 4041 + }, + "Return type of public getter '{0}' from exported class has or is using name '{1}' from private module '{2}'.": { + "category": "Error", + "code": 4042 + }, + "Return type of public getter '{0}' from exported class has or is using private name '{1}'.": { + "category": "Error", + "code": 4043 + }, + "Return type of constructor signature from exported interface has or is using name '{0}' from private module '{1}'.": { + "category": "Error", + "code": 4044 + }, + "Return type of constructor signature from exported interface has or is using private name '{0}'.": { + "category": "Error", + "code": 4045 + }, + "Return type of call signature from exported interface has or is using name '{0}' from private module '{1}'.": { + "category": "Error", + "code": 4046 + }, + "Return type of call signature from exported interface has or is using private name '{0}'.": { + "category": "Error", + "code": 4047 + }, + "Return type of index signature from exported interface has or is using name '{0}' from private module '{1}'.": { + "category": "Error", + "code": 4048 + }, + "Return type of index signature from exported interface has or is using private name '{0}'.": { + "category": "Error", + "code": 4049 + }, + "Return type of public static method from exported class has or is using name '{0}' from external module {1} but cannot be named.": { + "category": "Error", + "code": 4050 + }, + "Return type of public static method from exported class has or is using name '{0}' from private module '{1}'.": { + "category": "Error", + "code": 4051 + }, + "Return type of public static method from exported class has or is using private name '{0}'.": { + "category": "Error", + "code": 4052 + }, + "Return type of public method from exported class has or is using name '{0}' from external module {1} but cannot be named.": { + "category": "Error", + "code": 4053 + }, + "Return type of public method from exported class has or is using name '{0}' from private module '{1}'.": { + "category": "Error", + "code": 4054 + }, + "Return type of public method from exported class has or is using private name '{0}'.": { + "category": "Error", + "code": 4055 + }, + "Return type of method from exported interface has or is using name '{0}' from private module '{1}'.": { + "category": "Error", + "code": 4056 + }, + "Return type of method from exported interface has or is using private name '{0}'.": { + "category": "Error", + "code": 4057 + }, + "Return type of exported function has or is using name '{0}' from external module {1} but cannot be named.": { + "category": "Error", + "code": 4058 + }, + "Return type of exported function has or is using name '{0}' from private module '{1}'.": { + "category": "Error", + "code": 4059 + }, + "Return type of exported function has or is using private name '{0}'.": { + "category": "Error", + "code": 4060 + }, + "Parameter '{0}' of constructor from exported class has or is using name '{1}' from external module {2} but cannot be named.": { + "category": "Error", + "code": 4061 + }, + "Parameter '{0}' of constructor from exported class has or is using name '{1}' from private module '{2}'.": { + "category": "Error", + "code": 4062 + }, + "Parameter '{0}' of constructor from exported class has or is using private name '{1}'.": { + "category": "Error", + "code": 4063 + }, + "Parameter '{0}' of constructor signature from exported interface has or is using name '{1}' from private module '{2}'.": { + "category": "Error", + "code": 4064 + }, + "Parameter '{0}' of constructor signature from exported interface has or is using private name '{1}'.": { + "category": "Error", + "code": 4065 + }, + "Parameter '{0}' of call signature from exported interface has or is using name '{1}' from private module '{2}'.": { + "category": "Error", + "code": 4066 + }, + "Parameter '{0}' of call signature from exported interface has or is using private name '{1}'.": { + "category": "Error", + "code": 4067 + }, + "Parameter '{0}' of public static method from exported class has or is using name '{1}' from external module {2} but cannot be named.": { + "category": "Error", + "code": 4068 + }, + "Parameter '{0}' of public static method from exported class has or is using name '{1}' from private module '{2}'.": { + "category": "Error", + "code": 4069 + }, + "Parameter '{0}' of public static method from exported class has or is using private name '{1}'.": { + "category": "Error", + "code": 4070 + }, + "Parameter '{0}' of public method from exported class has or is using name '{1}' from external module {2} but cannot be named.": { + "category": "Error", + "code": 4071 + }, + "Parameter '{0}' of public method from exported class has or is using name '{1}' from private module '{2}'.": { + "category": "Error", + "code": 4072 + }, + "Parameter '{0}' of public method from exported class has or is using private name '{1}'.": { + "category": "Error", + "code": 4073 + }, + "Parameter '{0}' of method from exported interface has or is using name '{1}' from private module '{2}'.": { + "category": "Error", + "code": 4074 + }, + "Parameter '{0}' of method from exported interface has or is using private name '{1}'.": { + "category": "Error", + "code": 4075 + }, + "Parameter '{0}' of exported function has or is using name '{1}' from external module {2} but cannot be named.": { + "category": "Error", + "code": 4076 + }, + "Parameter '{0}' of exported function has or is using name '{1}' from private module '{2}'.": { + "category": "Error", + "code": 4077 + }, + "Parameter '{0}' of exported function has or is using private name '{1}'.": { + "category": "Error", + "code": 4078 + }, + "Exported type alias '{0}' has or is using private name '{1}'.": { + "category": "Error", + "code": 4081 + }, + "Default export of the module has or is using private name '{0}'.": { + "category": "Error", + "code": 4082 + }, + "Type parameter '{0}' of exported type alias has or is using private name '{1}'.": { + "category": "Error", + "code": 4083 + }, + "Exported type alias '{0}' has or is using private name '{1}' from module {2}.": { + "category": "Error", + "code": 4084 + }, + "Extends clause for inferred type '{0}' has or is using private name '{1}'.": { + "category": "Error", + "code": 4085 + }, + "Parameter '{0}' of index signature from exported interface has or is using name '{1}' from private module '{2}'.": { + "category": "Error", + "code": 4091 + }, + "Parameter '{0}' of index signature from exported interface has or is using private name '{1}'.": { + "category": "Error", + "code": 4092 + }, + "Property '{0}' of exported anonymous class type may not be private or protected.": { + "category": "Error", + "code": 4094 + }, + "Public static method '{0}' of exported class has or is using name '{1}' from external module {2} but cannot be named.": { + "category": "Error", + "code": 4095 + }, + "Public static method '{0}' of exported class has or is using name '{1}' from private module '{2}'.": { + "category": "Error", + "code": 4096 + }, + "Public static method '{0}' of exported class has or is using private name '{1}'.": { + "category": "Error", + "code": 4097 + }, + "Public method '{0}' of exported class has or is using name '{1}' from external module {2} but cannot be named.": { + "category": "Error", + "code": 4098 + }, + "Public method '{0}' of exported class has or is using name '{1}' from private module '{2}'.": { + "category": "Error", + "code": 4099 + }, + "Public method '{0}' of exported class has or is using private name '{1}'.": { + "category": "Error", + "code": 4100 + }, + "Method '{0}' of exported interface has or is using name '{1}' from private module '{2}'.": { + "category": "Error", + "code": 4101 + }, + "Method '{0}' of exported interface has or is using private name '{1}'.": { + "category": "Error", + "code": 4102 + }, + "Type parameter '{0}' of exported mapped object type is using private name '{1}'.": { + "category": "Error", + "code": 4103 + }, + "The type '{0}' is 'readonly' and cannot be assigned to the mutable type '{1}'.": { + "category": "Error", + "code": 4104 + }, + "Private or protected member '{0}' cannot be accessed on a type parameter.": { + "category": "Error", + "code": 4105 + }, + "Parameter '{0}' of accessor has or is using private name '{1}'.": { + "category": "Error", + "code": 4106 + }, + "Parameter '{0}' of accessor has or is using name '{1}' from private module '{2}'.": { + "category": "Error", + "code": 4107 + }, + "Parameter '{0}' of accessor has or is using name '{1}' from external module '{2}' but cannot be named.": { + "category": "Error", + "code": 4108 + }, + "Type arguments for '{0}' circularly reference themselves.": { + "category": "Error", + "code": 4109 + }, + "Tuple type arguments circularly reference themselves.": { + "category": "Error", + "code": 4110 + }, + "Property '{0}' comes from an index signature, so it must be accessed with ['{0}'].": { + "category": "Error", + "code": 4111 + }, + "This member cannot have an 'override' modifier because its containing class '{0}' does not extend another class.": { + "category": "Error", + "code": 4112 + }, + "This member cannot have an 'override' modifier because it is not declared in the base class '{0}'.": { + "category": "Error", + "code": 4113 + }, + "This member must have an 'override' modifier because it overrides a member in the base class '{0}'.": { + "category": "Error", + "code": 4114 + }, + "This parameter property must have an 'override' modifier because it overrides a member in base class '{0}'.": { + "category": "Error", + "code": 4115 + }, + "This member must have an 'override' modifier because it overrides an abstract method that is declared in the base class '{0}'.": { + "category": "Error", + "code": 4116 + }, + "This member cannot have an 'override' modifier because it is not declared in the base class '{0}'. Did you mean '{1}'?": { + "category": "Error", + "code": 4117 + }, + "The type of this node cannot be serialized because its property '{0}' cannot be serialized.": { + "category": "Error", + "code": 4118 + }, + "This member must have a JSDoc comment with an '@override' tag because it overrides a member in the base class '{0}'.": { + "category": "Error", + "code": 4119 + }, + "This parameter property must have a JSDoc comment with an '@override' tag because it overrides a member in the base class '{0}'.": { + "category": "Error", + "code": 4120 + }, + "This member cannot have a JSDoc comment with an '@override' tag because its containing class '{0}' does not extend another class.": { + "category": "Error", + "code": 4121 + }, + "This member cannot have a JSDoc comment with an '@override' tag because it is not declared in the base class '{0}'.": { + "category": "Error", + "code": 4122 + }, + "This member cannot have a JSDoc comment with an 'override' tag because it is not declared in the base class '{0}'. Did you mean '{1}'?": { + "category": "Error", + "code": 4123 + }, + "Compiler option '{0}' of value '{1}' is unstable. Use nightly TypeScript to silence this error. Try updating with 'npm install -D typescript@next'.": { + "category": "Error", + "code": 4124 + }, + "Each declaration of '{0}.{1}' differs in its value, where '{2}' was expected but '{3}' was given.": { + "category": "Error", + "code": 4125 + }, + "One value of '{0}.{1}' is the string '{2}', and the other is assumed to be an unknown numeric value.": { + "category": "Error", + "code": 4126 + }, + "This member cannot have an 'override' modifier because its name is dynamic.": { + "category": "Error", + "code": 4127 + }, + "This member cannot have a JSDoc comment with an '@override' tag because its name is dynamic.": { + "category": "Error", + "code": 4128 + }, + + "The current host does not support the '{0}' option.": { + "category": "Error", + "code": 5001 + }, + "Cannot find the common subdirectory path for the input files.": { + "category": "Error", + "code": 5009 + }, + "File specification cannot end in a recursive directory wildcard ('**'): '{0}'.": { + "category": "Error", + "code": 5010 + }, + "The common source directory of '{0}' is '{1}'. The 'rootDir' setting must be explicitly set to this or another path to adjust your output's file layout.": { + "category": "Error", + "code": 5011 + }, + "Cannot read file '{0}': {1}.": { + "category": "Error", + "code": 5012 + }, + "Unknown compiler option '{0}'.": { + "category": "Error", + "code": 5023 + }, + "Compiler option '{0}' requires a value of type {1}.": { + "category": "Error", + "code": 5024 + }, + "Unknown compiler option '{0}'. Did you mean '{1}'?": { + "category": "Error", + "code": 5025 + }, + "Could not write file '{0}': {1}.": { + "category": "Error", + "code": 5033 + }, + "Option 'project' cannot be mixed with source files on a command line.": { + "category": "Error", + "code": 5042 + }, + "Option 'isolatedModules' can only be used when either option '--module' is provided or option 'target' is 'ES2015' or higher.": { + "category": "Error", + "code": 5047 + }, + "Option '{0} can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided.": { + "category": "Error", + "code": 5051 + }, + "Option '{0}' cannot be specified without specifying option '{1}'.": { + "category": "Error", + "code": 5052 + }, + "Option '{0}' cannot be specified with option '{1}'.": { + "category": "Error", + "code": 5053 + }, + "A 'tsconfig.json' file is already defined at: '{0}'.": { + "category": "Error", + "code": 5054 + }, + "Cannot write file '{0}' because it would overwrite input file.": { + "category": "Error", + "code": 5055 + }, + "Cannot write file '{0}' because it would be overwritten by multiple input files.": { + "category": "Error", + "code": 5056 + }, + "Cannot find a tsconfig.json file at the specified directory: '{0}'.": { + "category": "Error", + "code": 5057 + }, + "The specified path does not exist: '{0}'.": { + "category": "Error", + "code": 5058 + }, + "Invalid value for '--reactNamespace'. '{0}' is not a valid identifier.": { + "category": "Error", + "code": 5059 + }, + "Pattern '{0}' can have at most one '*' character.": { + "category": "Error", + "code": 5061 + }, + "Substitution '{0}' in pattern '{1}' can have at most one '*' character.": { + "category": "Error", + "code": 5062 + }, + "Substitutions for pattern '{0}' should be an array.": { + "category": "Error", + "code": 5063 + }, + "Substitution '{0}' for pattern '{1}' has incorrect type, expected 'string', got '{2}'.": { + "category": "Error", + "code": 5064 + }, + "File specification cannot contain a parent directory ('..') that appears after a recursive directory wildcard ('**'): '{0}'.": { + "category": "Error", + "code": 5065 + }, + "Substitutions for pattern '{0}' shouldn't be an empty array.": { + "category": "Error", + "code": 5066 + }, + "Invalid value for 'jsxFactory'. '{0}' is not a valid identifier or qualified-name.": { + "category": "Error", + "code": 5067 + }, + "Adding a tsconfig.json file will help organize projects that contain both TypeScript and JavaScript files. Learn more at https://aka.ms/tsconfig.": { + "category": "Error", + "code": 5068 + }, + "Option '{0}' cannot be specified without specifying option '{1}' or option '{2}'.": { + "category": "Error", + "code": 5069 + }, + "Option '--resolveJsonModule' cannot be specified when 'moduleResolution' is set to 'classic'.": { + "category": "Error", + "code": 5070 + }, + "Option '--resolveJsonModule' cannot be specified when 'module' is set to 'none', 'system', or 'umd'.": { + "category": "Error", + "code": 5071 + }, + "Unknown build option '{0}'.": { + "category": "Error", + "code": 5072 + }, + "Build option '{0}' requires a value of type {1}.": { + "category": "Error", + "code": 5073 + }, + "Option '--incremental' can only be specified using tsconfig, emitting to single file or when option '--tsBuildInfoFile' is specified.": { + "category": "Error", + "code": 5074 + }, + "'{0}' is assignable to the constraint of type '{1}', but '{1}' could be instantiated with a different subtype of constraint '{2}'.": { + "category": "Error", + "code": 5075 + }, + "'{0}' and '{1}' operations cannot be mixed without parentheses.": { + "category": "Error", + "code": 5076 + }, + "Unknown build option '{0}'. Did you mean '{1}'?": { + "category": "Error", + "code": 5077 + }, + "Unknown watch option '{0}'.": { + "category": "Error", + "code": 5078 + }, + "Unknown watch option '{0}'. Did you mean '{1}'?": { + "category": "Error", + "code": 5079 + }, + "Watch option '{0}' requires a value of type {1}.": { + "category": "Error", + "code": 5080 + }, + "Cannot find a tsconfig.json file at the current directory: {0}.": { + "category": "Error", + "code": 5081 + }, + "'{0}' could be instantiated with an arbitrary type which could be unrelated to '{1}'.": { + "category": "Error", + "code": 5082 + }, + "Cannot read file '{0}'.": { + "category": "Error", + "code": 5083 + }, + "A tuple member cannot be both optional and rest.": { + "category": "Error", + "code": 5085 + }, + "A labeled tuple element is declared as optional with a question mark after the name and before the colon, rather than after the type.": { + "category": "Error", + "code": 5086 + }, + "A labeled tuple element is declared as rest with a '...' before the name, rather than before the type.": { + "category": "Error", + "code": 5087 + }, + "The inferred type of '{0}' references a type with a cyclic structure which cannot be trivially serialized. A type annotation is necessary.": { + "category": "Error", + "code": 5088 + }, + "Option '{0}' cannot be specified when option 'jsx' is '{1}'.": { + "category": "Error", + "code": 5089 + }, + "Non-relative paths are not allowed when 'baseUrl' is not set. Did you forget a leading './'?": { + "category": "Error", + "code": 5090 + }, + "Option 'preserveConstEnums' cannot be disabled when '{0}' is enabled.": { + "category": "Error", + "code": 5091 + }, + "The root value of a '{0}' file must be an object.": { + "category": "Error", + "code": 5092 + }, + "Compiler option '--{0}' may only be used with '--build'.": { + "category": "Error", + "code": 5093 + }, + "Compiler option '--{0}' may not be used with '--build'.": { + "category": "Error", + "code": 5094 + }, + "Option '{0}' can only be used when 'module' is set to 'preserve', 'commonjs', or 'es2015' or later.": { + "category": "Error", + "code": 5095 + }, + "Option 'allowImportingTsExtensions' can only be used when one of 'noEmit', 'emitDeclarationOnly', or 'rewriteRelativeImportExtensions' is set.": { + "category": "Error", + "code": 5096 + }, + "An import path can only end with a '{0}' extension when 'allowImportingTsExtensions' is enabled.": { + "category": "Error", + "code": 5097 + }, + "Option '{0}' can only be used when 'moduleResolution' is set to 'node16', 'nodenext', or 'bundler'.": { + "category": "Error", + "code": 5098 + }, + "Option '{0}' is deprecated and will stop functioning in TypeScript {1}. Specify compilerOption '\"ignoreDeprecations\": \"{2}\"' to silence this error.": { + "category": "Error", + "code": 5101 + }, + "Option '{0}' has been removed. Please remove it from your configuration.": { + "category": "Error", + "code": 5102 + }, + "Invalid value for '--ignoreDeprecations'.": { + "category": "Error", + "code": 5103 + }, + "Option '{0}' is redundant and cannot be specified with option '{1}'.": { + "category": "Error", + "code": 5104 + }, + "Option 'verbatimModuleSyntax' cannot be used when 'module' is set to 'UMD', 'AMD', or 'System'.": { + "category": "Error", + "code": 5105 + }, + "Use '{0}' instead.": { + "category": "Message", + "code": 5106 + }, + "Option '{0}={1}' is deprecated and will stop functioning in TypeScript {2}. Specify compilerOption '\"ignoreDeprecations\": \"{3}\"' to silence this error.": { + "category": "Error", + "code": 5107 + }, + "Option '{0}={1}' has been removed. Please remove it from your configuration.": { + "category": "Error", + "code": 5108 + }, + "Option 'moduleResolution' must be set to '{0}' (or left unspecified) when option 'module' is set to '{1}'.": { + "category": "Error", + "code": 5109 + }, + "Option 'module' must be set to '{0}' when option 'moduleResolution' is set to '{1}'.": { + "category": "Error", + "code": 5110 + }, + "Visit https://aka.ms/ts6 for migration information.": { + "category": "Message", + "code": 5111 + }, + "tsconfig.json is present but will not be loaded if files are specified on commandline. Use '--ignoreConfig' to skip this error.": { + "category": "Error", + "code": 5112 + }, + + "Generates a sourcemap for each corresponding '.d.ts' file.": { + "category": "Message", + "code": 6000 + }, + "Concatenate and emit output to single file.": { + "category": "Message", + "code": 6001 + }, + "Generates corresponding '.d.ts' file.": { + "category": "Message", + "code": 6002 + }, + "Specify the location where debugger should locate TypeScript files instead of source locations.": { + "category": "Message", + "code": 6004 + }, + "Watch input files.": { + "category": "Message", + "code": 6005 + }, + "Redirect output structure to the directory.": { + "category": "Message", + "code": 6006 + }, + "Do not erase const enum declarations in generated code.": { + "category": "Message", + "code": 6007 + }, + "Do not emit outputs if any errors were reported.": { + "category": "Message", + "code": 6008 + }, + "Do not emit comments to output.": { + "category": "Message", + "code": 6009 + }, + "Do not emit outputs.": { + "category": "Message", + "code": 6010 + }, + "Allow default imports from modules with no default export. This does not affect code emit, just typechecking.": { + "category": "Message", + "code": 6011 + }, + "Skip type checking of declaration files.": { + "category": "Message", + "code": 6012 + }, + "Do not resolve the real path of symlinks.": { + "category": "Message", + "code": 6013 + }, + "Only emit '.d.ts' declaration files.": { + "category": "Message", + "code": 6014 + }, + "Specify ECMAScript target version.": { + "category": "Message", + "code": 6015 + }, + "Specify module code generation.": { + "category": "Message", + "code": 6016 + }, + "Print this message.": { + "category": "Message", + "code": 6017 + }, + "Print the compiler's version.": { + "category": "Message", + "code": 6019 + }, + "Compile the project given the path to its configuration file, or to a folder with a 'tsconfig.json'.": { + "category": "Message", + "code": 6020 + }, + "Syntax: {0}": { + "category": "Message", + "code": 6023 + }, + "options": { + "category": "Message", + "code": 6024 + }, + "file": { + "category": "Message", + "code": 6025 + }, + "Examples: {0}": { + "category": "Message", + "code": 6026 + }, + "Options:": { + "category": "Message", + "code": 6027 + }, + "Version {0}": { + "category": "Message", + "code": 6029 + }, + "Insert command line options and files from a file.": { + "category": "Message", + "code": 6030 + }, + "Starting compilation in watch mode...": { + "category": "Message", + "code": 6031 + }, + "File change detected. Starting incremental compilation...": { + "category": "Message", + "code": 6032 + }, + "KIND": { + "category": "Message", + "code": 6034 + }, + "FILE": { + "category": "Message", + "code": 6035 + }, + "VERSION": { + "category": "Message", + "code": 6036 + }, + "LOCATION": { + "category": "Message", + "code": 6037 + }, + "DIRECTORY": { + "category": "Message", + "code": 6038 + }, + "STRATEGY": { + "category": "Message", + "code": 6039 + }, + "FILE OR DIRECTORY": { + "category": "Message", + "code": 6040 + }, + "Errors Files": { + "_locale_notes": "There is a double space, and the order cannot be changed (they're table headings) ^", + "category": "Message", + "code": 6041 + }, + "Generates corresponding '.map' file.": { + "category": "Message", + "code": 6043 + }, + "Compiler option '{0}' expects an argument.": { + "category": "Error", + "code": 6044 + }, + "Unterminated quoted string in response file '{0}'.": { + "category": "Error", + "code": 6045 + }, + "Argument for '{0}' option must be: {1}.": { + "category": "Error", + "code": 6046 + }, + "Locale must be of the form or -. For example '{0}' or '{1}'.": { + "category": "Error", + "code": 6048 + }, + "Unable to open file '{0}'.": { + "category": "Error", + "code": 6050 + }, + "Corrupted locale file {0}.": { + "category": "Error", + "code": 6051 + }, + "Raise error on expressions and declarations with an implied 'any' type.": { + "category": "Message", + "code": 6052 + }, + "File '{0}' not found.": { + "category": "Error", + "code": 6053 + }, + "File '{0}' has an unsupported extension. The only supported extensions are {1}.": { + "category": "Error", + "code": 6054 + }, + "Suppress noImplicitAny errors for indexing objects lacking index signatures.": { + "category": "Message", + "code": 6055 + }, + "Do not emit declarations for code that has an '@internal' annotation.": { + "category": "Message", + "code": 6056 + }, + "Specify the root directory of input files. Use to control the output directory structure with --outDir.": { + "category": "Message", + "code": 6058 + }, + "File '{0}' is not under 'rootDir' '{1}'. 'rootDir' is expected to contain all source files.": { + "category": "Error", + "code": 6059 + }, + "Specify the end of line sequence to be used when emitting files: 'CRLF' (dos) or 'LF' (unix).": { + "category": "Message", + "code": 6060 + }, + "NEWLINE": { + "category": "Message", + "code": 6061 + }, + "Option '{0}' can only be specified in 'tsconfig.json' file or set to 'null' on command line.": { + "category": "Error", + "code": 6064 + }, + "Enables experimental support for ES7 decorators.": { + "category": "Message", + "code": 6065 + }, + "Enables experimental support for emitting type metadata for decorators.": { + "category": "Message", + "code": 6066 + }, + "Initializes a TypeScript project and creates a tsconfig.json file.": { + "category": "Message", + "code": 6070 + }, + "Successfully created a tsconfig.json file.": { + "category": "Message", + "code": 6071 + }, + "Suppress excess property checks for object literals.": { + "category": "Message", + "code": 6072 + }, + "Stylize errors and messages using color and context (experimental).": { + "category": "Message", + "code": 6073 + }, + "Do not report errors on unused labels.": { + "category": "Message", + "code": 6074 + }, + "Report error when not all code paths in function return a value.": { + "category": "Message", + "code": 6075 + }, + "Report errors for fallthrough cases in switch statement.": { + "category": "Message", + "code": 6076 + }, + "Do not report errors on unreachable code.": { + "category": "Message", + "code": 6077 + }, + "Disallow inconsistently-cased references to the same file.": { + "category": "Message", + "code": 6078 + }, + "Specify library files to be included in the compilation.": { + "category": "Message", + "code": 6079 + }, + "Specify JSX code generation.": { + "category": "Message", + "code": 6080 + }, + "Only 'amd' and 'system' modules are supported alongside --{0}.": { + "category": "Error", + "code": 6082 + }, + "Base directory to resolve non-absolute module names.": { + "category": "Message", + "code": 6083 + }, + "[Deprecated] Use '--jsxFactory' instead. Specify the object invoked for createElement when targeting 'react' JSX emit": { + "category": "Message", + "code": 6084 + }, + "Enable tracing of the name resolution process.": { + "category": "Message", + "code": 6085 + }, + "======== Resolving module '{0}' from '{1}'. ========": { + "category": "Message", + "code": 6086 + }, + "Explicitly specified module resolution kind: '{0}'.": { + "category": "Message", + "code": 6087 + }, + "Module resolution kind is not specified, using '{0}'.": { + "category": "Message", + "code": 6088 + }, + "======== Module name '{0}' was successfully resolved to '{1}'. ========": { + "category": "Message", + "code": 6089 + }, + "======== Module name '{0}' was not resolved. ========": { + "category": "Message", + "code": 6090 + }, + "'paths' option is specified, looking for a pattern to match module name '{0}'.": { + "category": "Message", + "code": 6091 + }, + "Module name '{0}', matched pattern '{1}'.": { + "category": "Message", + "code": 6092 + }, + "Trying substitution '{0}', candidate module location: '{1}'.": { + "category": "Message", + "code": 6093 + }, + "Resolving module name '{0}' relative to base url '{1}' - '{2}'.": { + "category": "Message", + "code": 6094 + }, + "Loading module as file / folder, candidate module location '{0}', target file types: {1}.": { + "category": "Message", + "code": 6095 + }, + "File '{0}' does not exist.": { + "category": "Message", + "code": 6096 + }, + "File '{0}' exists - use it as a name resolution result.": { + "category": "Message", + "code": 6097 + }, + "Loading module '{0}' from 'node_modules' folder, target file types: {1}.": { + "category": "Message", + "code": 6098 + }, + "Found 'package.json' at '{0}'.": { + "category": "Message", + "code": 6099 + }, + "'package.json' does not have a '{0}' field.": { + "category": "Message", + "code": 6100 + }, + "'package.json' has '{0}' field '{1}' that references '{2}'.": { + "category": "Message", + "code": 6101 + }, + "Allow javascript files to be compiled.": { + "category": "Message", + "code": 6102 + }, + "Checking if '{0}' is the longest matching prefix for '{1}' - '{2}'.": { + "category": "Message", + "code": 6104 + }, + "Expected type of '{0}' field in 'package.json' to be '{1}', got '{2}'.": { + "category": "Message", + "code": 6105 + }, + "'baseUrl' option is set to '{0}', using this value to resolve non-relative module name '{1}'.": { + "category": "Message", + "code": 6106 + }, + "'rootDirs' option is set, using it to resolve relative module name '{0}'.": { + "category": "Message", + "code": 6107 + }, + "Longest matching prefix for '{0}' is '{1}'.": { + "category": "Message", + "code": 6108 + }, + "Loading '{0}' from the root dir '{1}', candidate location '{2}'.": { + "category": "Message", + "code": 6109 + }, + "Trying other entries in 'rootDirs'.": { + "category": "Message", + "code": 6110 + }, + "Module resolution using 'rootDirs' has failed.": { + "category": "Message", + "code": 6111 + }, + "Do not emit 'use strict' directives in module output.": { + "category": "Message", + "code": 6112 + }, + "Enable strict null checks.": { + "category": "Message", + "code": 6113 + }, + "Unknown option 'excludes'. Did you mean 'exclude'?": { + "category": "Error", + "code": 6114 + }, + "Raise error on 'this' expressions with an implied 'any' type.": { + "category": "Message", + "code": 6115 + }, + "======== Resolving type reference directive '{0}', containing file '{1}', root directory '{2}'. ========": { + "category": "Message", + "code": 6116 + }, + "======== Type reference directive '{0}' was successfully resolved to '{1}', primary: {2}. ========": { + "category": "Message", + "code": 6119 + }, + "======== Type reference directive '{0}' was not resolved. ========": { + "category": "Message", + "code": 6120 + }, + "Resolving with primary search path '{0}'.": { + "category": "Message", + "code": 6121 + }, + "Root directory cannot be determined, skipping primary search paths.": { + "category": "Message", + "code": 6122 + }, + "======== Resolving type reference directive '{0}', containing file '{1}', root directory not set. ========": { + "category": "Message", + "code": 6123 + }, + "Type declaration files to be included in compilation.": { + "category": "Message", + "code": 6124 + }, + "Looking up in 'node_modules' folder, initial location '{0}'.": { + "category": "Message", + "code": 6125 + }, + "Containing file is not specified and root directory cannot be determined, skipping lookup in 'node_modules' folder.": { + "category": "Message", + "code": 6126 + }, + "======== Resolving type reference directive '{0}', containing file not set, root directory '{1}'. ========": { + "category": "Message", + "code": 6127 + }, + "======== Resolving type reference directive '{0}', containing file not set, root directory not set. ========": { + "category": "Message", + "code": 6128 + }, + "Resolving real path for '{0}', result '{1}'.": { + "category": "Message", + "code": 6130 + }, + "Cannot compile modules using option '{0}' unless the '--module' flag is 'amd' or 'system'.": { + "category": "Error", + "code": 6131 + }, + "File name '{0}' has a '{1}' extension - stripping it.": { + "category": "Message", + "code": 6132 + }, + "'{0}' is declared but its value is never read.": { + "category": "Error", + "code": 6133, + "reportsUnnecessary": true + }, + "Report errors on unused locals.": { + "category": "Message", + "code": 6134 + }, + "Report errors on unused parameters.": { + "category": "Message", + "code": 6135 + }, + "The maximum dependency depth to search under node_modules and load JavaScript files.": { + "category": "Message", + "code": 6136 + }, + "Cannot import type declaration files. Consider importing '{0}' instead of '{1}'.": { + "category": "Error", + "code": 6137 + }, + "Property '{0}' is declared but its value is never read.": { + "category": "Error", + "code": 6138, + "reportsUnnecessary": true + }, + "Import emit helpers from 'tslib'.": { + "category": "Message", + "code": 6139 + }, + "Auto discovery for typings is enabled in project '{0}'. Running extra resolution pass for module '{1}' using cache location '{2}'.": { + "category": "Error", + "code": 6140 + }, + "Parse in strict mode and emit \"use strict\" for each source file.": { + "category": "Message", + "code": 6141 + }, + "Module '{0}' was resolved to '{1}', but '--jsx' is not set.": { + "category": "Error", + "code": 6142 + }, + "Module '{0}' was resolved as locally declared ambient module in file '{1}'.": { + "category": "Message", + "code": 6144 + }, + "Specify the JSX factory function to use when targeting 'react' JSX emit, e.g. 'React.createElement' or 'h'.": { + "category": "Message", + "code": 6146 + }, + "Resolution for module '{0}' was found in cache from location '{1}'.": { + "category": "Message", + "code": 6147 + }, + "Directory '{0}' does not exist, skipping all lookups in it.": { + "category": "Message", + "code": 6148 + }, + "Show diagnostic information.": { + "category": "Message", + "code": 6149 + }, + "Show verbose diagnostic information.": { + "category": "Message", + "code": 6150 + }, + "Emit a single file with source maps instead of having a separate file.": { + "category": "Message", + "code": 6151 + }, + "Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set.": { + "category": "Message", + "code": 6152 + }, + "Transpile each file as a separate module (similar to 'ts.transpileModule').": { + "category": "Message", + "code": 6153 + }, + "Print names of generated files part of the compilation.": { + "category": "Message", + "code": 6154 + }, + "Print names of files part of the compilation.": { + "category": "Message", + "code": 6155 + }, + "The locale used when displaying messages to the user (e.g. 'en-us')": { + "category": "Message", + "code": 6156 + }, + "Do not generate custom helper functions like '__extends' in compiled output.": { + "category": "Message", + "code": 6157 + }, + "Do not include the default library file (lib.d.ts).": { + "category": "Message", + "code": 6158 + }, + "Do not add triple-slash references or imported modules to the list of compiled files.": { + "category": "Message", + "code": 6159 + }, + "[Deprecated] Use '--skipLibCheck' instead. Skip type checking of default library declaration files.": { + "category": "Message", + "code": 6160 + }, + "List of folders to include type definitions from.": { + "category": "Message", + "code": 6161 + }, + "Disable size limitations on JavaScript projects.": { + "category": "Message", + "code": 6162 + }, + "The character set of the input files.": { + "category": "Message", + "code": 6163 + }, + "Skipping module '{0}' that looks like an absolute URI, target file types: {1}.": { + "category": "Message", + "code": 6164 + }, + "Do not truncate error messages.": { + "category": "Message", + "code": 6165 + }, + "Output directory for generated declaration files.": { + "category": "Message", + "code": 6166 + }, + "A series of entries which re-map imports to lookup locations relative to the 'baseUrl'.": { + "category": "Message", + "code": 6167 + }, + "List of root folders whose combined content represents the structure of the project at runtime.": { + "category": "Message", + "code": 6168 + }, + "Show all compiler options.": { + "category": "Message", + "code": 6169 + }, + "[Deprecated] Use '--outFile' instead. Concatenate and emit output to single file": { + "category": "Message", + "code": 6170 + }, + "Command-line Options": { + "category": "Message", + "code": 6171 + }, + "Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5'.": { + "category": "Message", + "code": 6179 + }, + "Enable all strict type-checking options.": { + "category": "Message", + "code": 6180 + }, + "Scoped package detected, looking in '{0}'": { + "category": "Message", + "code": 6182 + }, + "Reusing resolution of module '{0}' from '{1}' of old program, it was successfully resolved to '{2}'.": { + "category": "Message", + "code": 6183 + }, + "Reusing resolution of module '{0}' from '{1}' of old program, it was successfully resolved to '{2}' with Package ID '{3}'.": { + "category": "Message", + "code": 6184 + }, + "Enable strict checking of function types.": { + "category": "Message", + "code": 6186 + }, + "Enable strict checking of property initialization in classes.": { + "category": "Message", + "code": 6187 + }, + "Numeric separators are not allowed here.": { + "category": "Error", + "code": 6188 + }, + "Multiple consecutive numeric separators are not permitted.": { + "category": "Error", + "code": 6189 + }, + "Whether to keep outdated console output in watch mode instead of clearing the screen.": { + "category": "Message", + "code": 6191 + }, + "All imports in import declaration are unused.": { + "category": "Error", + "code": 6192, + "reportsUnnecessary": true + }, + "Found 1 error. Watching for file changes.": { + "category": "Message", + "code": 6193 + }, + "Found {0} errors. Watching for file changes.": { + "category": "Message", + "code": 6194 + }, + "Resolve 'keyof' to string valued property names only (no numbers or symbols).": { + "category": "Message", + "code": 6195 + }, + "'{0}' is declared but never used.": { + "category": "Error", + "code": 6196, + "reportsUnnecessary": true + }, + "Include modules imported with '.json' extension": { + "category": "Message", + "code": 6197 + }, + "All destructured elements are unused.": { + "category": "Error", + "code": 6198, + "reportsUnnecessary": true + }, + "All variables are unused.": { + "category": "Error", + "code": 6199, + "reportsUnnecessary": true + }, + "Definitions of the following identifiers conflict with those in another file: {0}": { + "category": "Error", + "code": 6200 + }, + "Conflicts are in this file.": { + "category": "Message", + "code": 6201 + }, + "Project references may not form a circular graph. Cycle detected: {0}": { + "category": "Error", + "code": 6202 + }, + "'{0}' was also declared here.": { + "category": "Message", + "code": 6203 + }, + "and here.": { + "category": "Message", + "code": 6204 + }, + "All type parameters are unused.": { + "category": "Error", + "code": 6205 + }, + "'package.json' has a 'typesVersions' field with version-specific path mappings.": { + "category": "Message", + "code": 6206 + }, + "'package.json' does not have a 'typesVersions' entry that matches version '{0}'.": { + "category": "Message", + "code": 6207 + }, + "'package.json' has a 'typesVersions' entry '{0}' that matches compiler version '{1}', looking for a pattern to match module name '{2}'.": { + "category": "Message", + "code": 6208 + }, + "'package.json' has a 'typesVersions' entry '{0}' that is not a valid semver range.": { + "category": "Message", + "code": 6209 + }, + "An argument for '{0}' was not provided.": { + "category": "Message", + "code": 6210 + }, + "An argument matching this binding pattern was not provided.": { + "category": "Message", + "code": 6211 + }, + "Did you mean to call this expression?": { + "category": "Message", + "code": 6212 + }, + "Did you mean to use 'new' with this expression?": { + "category": "Message", + "code": 6213 + }, + "Enable strict 'bind', 'call', and 'apply' methods on functions.": { + "category": "Message", + "code": 6214 + }, + "Using compiler options of project reference redirect '{0}'.": { + "category": "Message", + "code": 6215 + }, + "Found 1 error.": { + "category": "Message", + "code": 6216 + }, + "Found {0} errors.": { + "category": "Message", + "code": 6217 + }, + "======== Module name '{0}' was successfully resolved to '{1}' with Package ID '{2}'. ========": { + "category": "Message", + "code": 6218 + }, + "======== Type reference directive '{0}' was successfully resolved to '{1}' with Package ID '{2}', primary: {3}. ========": { + "category": "Message", + "code": 6219 + }, + "'package.json' had a falsy '{0}' field.": { + "category": "Message", + "code": 6220 + }, + "Disable use of source files instead of declaration files from referenced projects.": { + "category": "Message", + "code": 6221 + }, + "Emit class fields with Define instead of Set.": { + "category": "Message", + "code": 6222 + }, + "Generates a CPU profile.": { + "category": "Message", + "code": 6223 + }, + "Disable solution searching for this project.": { + "category": "Message", + "code": 6224 + }, + "Specify strategy for watching file: 'FixedPollingInterval' (default), 'PriorityPollingInterval', 'DynamicPriorityPolling', 'FixedChunkSizePolling', 'UseFsEvents', 'UseFsEventsOnParentDirectory'.": { + "category": "Message", + "code": 6225 + }, + "Specify strategy for watching directory on platforms that don't support recursive watching natively: 'UseFsEvents' (default), 'FixedPollingInterval', 'DynamicPriorityPolling', 'FixedChunkSizePolling'.": { + "category": "Message", + "code": 6226 + }, + "Specify strategy for creating a polling watch when it fails to create using file system events: 'FixedInterval' (default), 'PriorityInterval', 'DynamicPriority', 'FixedChunkSize'.": { + "category": "Message", + "code": 6227 + }, + "Tag '{0}' expects at least '{1}' arguments, but the JSX factory '{2}' provides at most '{3}'.": { + "category": "Error", + "code": 6229 + }, + "Option '{0}' can only be specified in 'tsconfig.json' file or set to 'false' or 'null' on command line.": { + "category": "Error", + "code": 6230 + }, + "Could not resolve the path '{0}' with the extensions: {1}.": { + "category": "Error", + "code": 6231 + }, + "Declaration augments declaration in another file. This cannot be serialized.": { + "category": "Error", + "code": 6232 + }, + "This is the declaration being augmented. Consider moving the augmenting declaration into the same file.": { + "category": "Error", + "code": 6233 + }, + "This expression is not callable because it is a 'get' accessor. Did you mean to use it without '()'?": { + "category": "Error", + "code": 6234 + }, + "Disable loading referenced projects.": { + "category": "Message", + "code": 6235 + }, + "Arguments for the rest parameter '{0}' were not provided.": { + "category": "Error", + "code": 6236 + }, + "Generates an event trace and a list of types.": { + "category": "Message", + "code": 6237 + }, + "Specify the module specifier to be used to import the 'jsx' and 'jsxs' factory functions from. eg, react": { + "category": "Error", + "code": 6238 + }, + "File '{0}' exists according to earlier cached lookups.": { + "category": "Message", + "code": 6239 + }, + "File '{0}' does not exist according to earlier cached lookups.": { + "category": "Message", + "code": 6240 + }, + "Resolution for type reference directive '{0}' was found in cache from location '{1}'.": { + "category": "Message", + "code": 6241 + }, + "======== Resolving type reference directive '{0}', containing file '{1}'. ========": { + "category": "Message", + "code": 6242 + }, + "Interpret optional property types as written, rather than adding 'undefined'.": { + "category": "Message", + "code": 6243 + }, + "Modules": { + "category": "Message", + "code": 6244 + }, + "File Management": { + "category": "Message", + "code": 6245 + }, + "Emit": { + "category": "Message", + "code": 6246 + }, + "JavaScript Support": { + "category": "Message", + "code": 6247 + }, + "Type Checking": { + "category": "Message", + "code": 6248 + }, + "Editor Support": { + "category": "Message", + "code": 6249 + }, + "Watch and Build Modes": { + "category": "Message", + "code": 6250 + }, + "Compiler Diagnostics": { + "category": "Message", + "code": 6251 + }, + "Interop Constraints": { + "category": "Message", + "code": 6252 + }, + "Backwards Compatibility": { + "category": "Message", + "code": 6253 + }, + "Language and Environment": { + "category": "Message", + "code": 6254 + }, + "Projects": { + "category": "Message", + "code": 6255 + }, + "Output Formatting": { + "category": "Message", + "code": 6256 + }, + "Completeness": { + "category": "Message", + "code": 6257 + }, + "'{0}' should be set inside the 'compilerOptions' object of the config json file": { + "category": "Error", + "code": 6258 + }, + "Found 1 error in {0}": { + "category": "Message", + "code": 6259 + }, + "Found {0} errors in the same file, starting at: {1}": { + "category": "Message", + "code": 6260 + }, + "Found {0} errors in {1} files.": { + "category": "Message", + "code": 6261 + }, + "File name '{0}' has a '{1}' extension - looking up '{2}' instead.": { + "category": "Message", + "code": 6262 + }, + "Module '{0}' was resolved to '{1}', but '--allowArbitraryExtensions' is not set.": { + "category": "Error", + "code": 6263 + }, + "Enable importing files with any extension, provided a declaration file is present.": { + "category": "Message", + "code": 6264 + }, + "Resolving type reference directive for program that specifies custom typeRoots, skipping lookup in 'node_modules' folder.": { + "category": "Message", + "code": 6265 + }, + "Option '{0}' can only be specified on command line.": { + "category": "Error", + "code": 6266 + }, + + "Directory '{0}' has no containing package.json scope. Imports will not resolve.": { + "category": "Message", + "code": 6270 + }, + "Import specifier '{0}' does not exist in package.json scope at path '{1}'.": { + "category": "Message", + "code": 6271 + }, + "Invalid import specifier '{0}' has no possible resolutions.": { + "category": "Message", + "code": 6272 + }, + "package.json scope '{0}' has no imports defined.": { + "category": "Message", + "code": 6273 + }, + "package.json scope '{0}' explicitly maps specifier '{1}' to null.": { + "category": "Message", + "code": 6274 + }, + "package.json scope '{0}' has invalid type for target of specifier '{1}'": { + "category": "Message", + "code": 6275 + }, + "Export specifier '{0}' does not exist in package.json scope at path '{1}'.": { + "category": "Message", + "code": 6276 + }, + "Resolution of non-relative name failed; trying with modern Node resolution features disabled to see if npm library needs configuration update.": { + "category": "Message", + "code": 6277 + }, + "There are types at '{0}', but this result could not be resolved when respecting package.json \"exports\". The '{1}' library may need to update its package.json or typings.": { + "category": "Message", + "code": 6278 + }, + "Resolution of non-relative name failed; trying with '--moduleResolution bundler' to see if project may need configuration update.": { + "category": "Message", + "code": 6279 + }, + "There are types at '{0}', but this result could not be resolved under your current 'moduleResolution' setting. Consider updating to 'node16', 'nodenext', or 'bundler'.": { + "category": "Message", + "code": 6280 + }, + "'package.json' has a 'peerDependencies' field.": { + "category": "Message", + "code": 6281 + }, + "Found peerDependency '{0}' with '{1}' version.": { + "category": "Message", + "code": 6282 + }, + "Failed to find peerDependency '{0}'.": { + "category": "Message", + "code": 6283 + }, + "File Layout": { + "category": "Message", + "code": 6284 + }, + "Environment Settings": { + "category": "Message", + "code": 6285 + }, + "See also https://aka.ms/tsconfig/module": { + "category": "Message", + "code": 6286 + }, + "For nodejs:": { + "category": "Message", + "code": 6287 + }, + "and npm install -D @types/node": { + "category": "Message", + "code": 6290 + }, + "Other Outputs": { + "category": "Message", + "code": 6291 + }, + "Stricter Typechecking Options": { + "category": "Message", + "code": 6292 + }, + "Style Options": { + "category": "Message", + "code": 6293 + }, + "Recommended Options": { + "category": "Message", + "code": 6294 + }, + + "Enable project compilation": { + "category": "Message", + "code": 6302 + }, + "Composite projects may not disable declaration emit.": { + "category": "Error", + "code": 6304 + }, + "Output file '{0}' has not been built from source file '{1}'.": { + "category": "Error", + "code": 6305 + }, + "Referenced project '{0}' must have setting \"composite\": true.": { + "category": "Error", + "code": 6306 + }, + "File '{0}' is not listed within the file list of project '{1}'. Projects must list all files or use an 'include' pattern.": { + "category": "Error", + "code": 6307 + }, + "Referenced project '{0}' may not disable emit.": { + "category": "Error", + "code": 6310 + }, + "Project '{0}' is out of date because output '{1}' is older than input '{2}'": { + "category": "Message", + "code": 6350 + }, + "Project '{0}' is up to date because newest input '{1}' is older than output '{2}'": { + "category": "Message", + "code": 6351 + }, + "Project '{0}' is out of date because output file '{1}' does not exist": { + "category": "Message", + "code": 6352 + }, + "Project '{0}' is out of date because its dependency '{1}' is out of date": { + "category": "Message", + "code": 6353 + }, + "Project '{0}' is up to date with .d.ts files from its dependencies": { + "category": "Message", + "code": 6354 + }, + "Projects in this build: {0}": { + "category": "Message", + "code": 6355 + }, + "A non-dry build would delete the following files: {0}": { + "category": "Message", + "code": 6356 + }, + "A non-dry build would build project '{0}'": { + "category": "Message", + "code": 6357 + }, + "Building project '{0}'...": { + "category": "Message", + "code": 6358 + }, + "Updating output timestamps of project '{0}'...": { + "category": "Message", + "code": 6359 + }, + "Project '{0}' is up to date": { + "category": "Message", + "code": 6361 + }, + "Skipping build of project '{0}' because its dependency '{1}' has errors": { + "category": "Message", + "code": 6362 + }, + "Project '{0}' can't be built because its dependency '{1}' has errors": { + "category": "Message", + "code": 6363 + }, + "Build one or more projects and their dependencies, if out of date": { + "category": "Message", + "code": 6364 + }, + "Delete the outputs of all projects.": { + "category": "Message", + "code": 6365 + }, + "Show what would be built (or deleted, if specified with '--clean')": { + "category": "Message", + "code": 6367 + }, + "Option '--build' must be the first command line argument.": { + "category": "Error", + "code": 6369 + }, + "Options '{0}' and '{1}' cannot be combined.": { + "category": "Error", + "code": 6370 + }, + "Updating unchanged output timestamps of project '{0}'...": { + "category": "Message", + "code": 6371 + }, + "A non-dry build would update timestamps for output of project '{0}'": { + "category": "Message", + "code": 6374 + }, + "Cannot write file '{0}' because it will overwrite '.tsbuildinfo' file generated by referenced project '{1}'": { + "category": "Error", + "code": 6377 + }, + "Composite projects may not disable incremental compilation.": { + "category": "Error", + "code": 6379 + }, + "Specify file to store incremental compilation information": { + "category": "Message", + "code": 6380 + }, + "Project '{0}' is out of date because output for it was generated with version '{1}' that differs with current version '{2}'": { + "category": "Message", + "code": 6381 + }, + "Skipping build of project '{0}' because its dependency '{1}' was not built": { + "category": "Message", + "code": 6382 + }, + "Project '{0}' can't be built because its dependency '{1}' was not built": { + "category": "Message", + "code": 6383 + }, + "Have recompiles in '--incremental' and '--watch' assume that changes within a file will only affect files directly depending on it.": { + "category": "Message", + "code": 6384 + }, + "'{0}' is deprecated.": { + "category": "Suggestion", + "code": 6385, + "reportsDeprecated": true + }, + "Performance timings for '--diagnostics' or '--extendedDiagnostics' are not available in this session. A native implementation of the Web Performance API could not be found.": { + "category": "Message", + "code": 6386 + }, + "The signature '{0}' of '{1}' is deprecated.": { + "category": "Suggestion", + "code": 6387, + "reportsDeprecated": true + }, + "Project '{0}' is being forcibly rebuilt": { + "category": "Message", + "code": 6388 + }, + "Reusing resolution of module '{0}' from '{1}' of old program, it was not resolved.": { + "category": "Message", + "code": 6389 + }, + "Reusing resolution of type reference directive '{0}' from '{1}' of old program, it was successfully resolved to '{2}'.": { + "category": "Message", + "code": 6390 + }, + "Reusing resolution of type reference directive '{0}' from '{1}' of old program, it was successfully resolved to '{2}' with Package ID '{3}'.": { + "category": "Message", + "code": 6391 + }, + "Reusing resolution of type reference directive '{0}' from '{1}' of old program, it was not resolved.": { + "category": "Message", + "code": 6392 + }, + "Reusing resolution of module '{0}' from '{1}' found in cache from location '{2}', it was successfully resolved to '{3}'.": { + "category": "Message", + "code": 6393 + }, + "Reusing resolution of module '{0}' from '{1}' found in cache from location '{2}', it was successfully resolved to '{3}' with Package ID '{4}'.": { + "category": "Message", + "code": 6394 + }, + "Reusing resolution of module '{0}' from '{1}' found in cache from location '{2}', it was not resolved.": { + "category": "Message", + "code": 6395 + }, + "Reusing resolution of type reference directive '{0}' from '{1}' found in cache from location '{2}', it was successfully resolved to '{3}'.": { + "category": "Message", + "code": 6396 + }, + "Reusing resolution of type reference directive '{0}' from '{1}' found in cache from location '{2}', it was successfully resolved to '{3}' with Package ID '{4}'.": { + "category": "Message", + "code": 6397 + }, + "Reusing resolution of type reference directive '{0}' from '{1}' found in cache from location '{2}', it was not resolved.": { + "category": "Message", + "code": 6398 + }, + "Project '{0}' is out of date because buildinfo file '{1}' indicates that some of the changes were not emitted": { + "category": "Message", + "code": 6399 + }, + "Project '{0}' is up to date but needs to update timestamps of output files that are older than input files": { + "category": "Message", + "code": 6400 + }, + "Project '{0}' is out of date because there was error reading file '{1}'": { + "category": "Message", + "code": 6401 + }, + "Resolving in {0} mode with conditions {1}.": { + "category": "Message", + "code": 6402 + }, + "Matched '{0}' condition '{1}'.": { + "category": "Message", + "code": 6403 + }, + "Using '{0}' subpath '{1}' with target '{2}'.": { + "category": "Message", + "code": 6404 + }, + "Saw non-matching condition '{0}'.": { + "category": "Message", + "code": 6405 + }, + "Project '{0}' is out of date because buildinfo file '{1}' indicates there is change in compilerOptions": { + "category": "Message", + "code": 6406 + }, + "Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set.": { + "category": "Message", + "code": 6407 + }, + "Use the package.json 'exports' field when resolving package imports.": { + "category": "Message", + "code": 6408 + }, + "Use the package.json 'imports' field when resolving imports.": { + "category": "Message", + "code": 6409 + }, + "Conditions to set in addition to the resolver-specific defaults when resolving imports.": { + "category": "Message", + "code": 6410 + }, + "`true` when 'moduleResolution' is 'node16', 'nodenext', or 'bundler'; otherwise `false`.": { + "category": "Message", + "code": 6411 + }, + "Project '{0}' is out of date because buildinfo file '{1}' indicates that file '{2}' was root file of compilation but not any more.": { + "category": "Message", + "code": 6412 + }, + "Entering conditional exports.": { + "category": "Message", + "code": 6413 + }, + "Resolved under condition '{0}'.": { + "category": "Message", + "code": 6414 + }, + "Failed to resolve under condition '{0}'.": { + "category": "Message", + "code": 6415 + }, + "Exiting conditional exports.": { + "category": "Message", + "code": 6416 + }, + "Searching all ancestor node_modules directories for preferred extensions: {0}.": { + "category": "Message", + "code": 6417 + }, + "Searching all ancestor node_modules directories for fallback extensions: {0}.": { + "category": "Message", + "code": 6418 + }, + "Project '{0}' is out of date because buildinfo file '{1}' indicates that program needs to report errors.": { + "category": "Message", + "code": 6419 + }, + "Project '{0}' is out of date because {1}.": { + "category": "Message", + "code": 6420 + }, + "Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files.": { + "category": "Message", + "code": 6421 + }, + + "The expected type comes from property '{0}' which is declared here on type '{1}'": { + "category": "Message", + "code": 6500 + }, + "The expected type comes from this index signature.": { + "category": "Message", + "code": 6501 + }, + "The expected type comes from the return type of this signature.": { + "category": "Message", + "code": 6502 + }, + "Print names of files that are part of the compilation and then stop processing.": { + "category": "Message", + "code": 6503 + }, + "File '{0}' is a JavaScript file. Did you mean to enable the 'allowJs' option?": { + "category": "Error", + "code": 6504 + }, + "Print names of files and the reason they are part of the compilation.": { + "category": "Message", + "code": 6505 + }, + "Consider adding a 'declare' modifier to this class.": { + "category": "Message", + "code": 6506 + }, + "Allow JavaScript files to be a part of your program. Use the 'checkJs' option to get errors from these files.": { + "category": "Message", + "code": 6600 + }, + "Allow 'import x from y' when a module doesn't have a default export.": { + "category": "Message", + "code": 6601 + }, + "Allow accessing UMD globals from modules.": { + "category": "Message", + "code": 6602 + }, + "Disable error reporting for unreachable code.": { + "category": "Message", + "code": 6603 + }, + "Disable error reporting for unused labels.": { + "category": "Message", + "code": 6604 + }, + "Ensure 'use strict' is always emitted.": { + "category": "Message", + "code": 6605 + }, + "Have recompiles in projects that use 'incremental' and 'watch' mode assume that changes within a file will only affect files directly depending on it.": { + "category": "Message", + "code": 6606 + }, + "Specify the base directory to resolve non-relative module names.": { + "category": "Message", + "code": 6607 + }, + "No longer supported. In early versions, manually set the text encoding for reading files.": { + "category": "Message", + "code": 6608 + }, + "Enable error reporting in type-checked JavaScript files.": { + "category": "Message", + "code": 6609 + }, + "Enable constraints that allow a TypeScript project to be used with project references.": { + "category": "Message", + "code": 6611 + }, + "Generate .d.ts files from TypeScript and JavaScript files in your project.": { + "category": "Message", + "code": 6612 + }, + "Specify the output directory for generated declaration files.": { + "category": "Message", + "code": 6613 + }, + "Create sourcemaps for d.ts files.": { + "category": "Message", + "code": 6614 + }, + "Output compiler performance information after building.": { + "category": "Message", + "code": 6615 + }, + "Disables inference for type acquisition by looking at filenames in a project.": { + "category": "Message", + "code": 6616 + }, + "Reduce the number of projects loaded automatically by TypeScript.": { + "category": "Message", + "code": 6617 + }, + "Remove the 20mb cap on total source code size for JavaScript files in the TypeScript language server.": { + "category": "Message", + "code": 6618 + }, + "Opt a project out of multi-project reference checking when editing.": { + "category": "Message", + "code": 6619 + }, + "Disable preferring source files instead of declaration files when referencing composite projects.": { + "category": "Message", + "code": 6620 + }, + "Emit more compliant, but verbose and less performant JavaScript for iteration.": { + "category": "Message", + "code": 6621 + }, + "Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files.": { + "category": "Message", + "code": 6622 + }, + "Only output d.ts files and not JavaScript files.": { + "category": "Message", + "code": 6623 + }, + "Emit design-type metadata for decorated declarations in source files.": { + "category": "Message", + "code": 6624 + }, + "Disable the type acquisition for JavaScript projects": { + "category": "Message", + "code": 6625 + }, + "Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility.": { + "category": "Message", + "code": 6626 + }, + "Filters results from the `include` option.": { + "category": "Message", + "code": 6627 + }, + "Remove a list of directories from the watch process.": { + "category": "Message", + "code": 6628 + }, + "Remove a list of files from the watch mode's processing.": { + "category": "Message", + "code": 6629 + }, + "Enable experimental support for legacy experimental decorators.": { + "category": "Message", + "code": 6630 + }, + "Print files read during the compilation including why it was included.": { + "category": "Message", + "code": 6631 + }, + "Output more detailed compiler performance information after building.": { + "category": "Message", + "code": 6632 + }, + "Specify one or more path or node module references to base configuration files from which settings are inherited.": { + "category": "Message", + "code": 6633 + }, + "Specify what approach the watcher should use if the system runs out of native file watchers.": { + "category": "Message", + "code": 6634 + }, + "Include a list of files. This does not support glob patterns, as opposed to `include`.": { + "category": "Message", + "code": 6635 + }, + "Build all projects, including those that appear to be up to date.": { + "category": "Message", + "code": 6636 + }, + "Ensure that casing is correct in imports.": { + "category": "Message", + "code": 6637 + }, + "Emit a v8 CPU profile of the compiler run for debugging.": { + "category": "Message", + "code": 6638 + }, + "Allow importing helper functions from tslib once per project, instead of including them per-file.": { + "category": "Message", + "code": 6639 + }, + "Skip building downstream projects on error in upstream project.": { + "category": "Message", + "code": 6640 + }, + "Specify a list of glob patterns that match files to be included in compilation.": { + "category": "Message", + "code": 6641 + }, + "Save .tsbuildinfo files to allow for incremental compilation of projects.": { + "category": "Message", + "code": 6642 + }, + "Include sourcemap files inside the emitted JavaScript.": { + "category": "Message", + "code": 6643 + }, + "Include source code in the sourcemaps inside the emitted JavaScript.": { + "category": "Message", + "code": 6644 + }, + "Ensure that each file can be safely transpiled without relying on other imports.": { + "category": "Message", + "code": 6645 + }, + "Specify what JSX code is generated.": { + "category": "Message", + "code": 6646 + }, + "Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'.": { + "category": "Message", + "code": 6647 + }, + "Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'.": { + "category": "Message", + "code": 6648 + }, + "Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'.": { + "category": "Message", + "code": 6649 + }, + "Make keyof only return strings instead of string, numbers or symbols. Legacy option.": { + "category": "Message", + "code": 6650 + }, + "Specify a set of bundled library declaration files that describe the target runtime environment.": { + "category": "Message", + "code": 6651 + }, + "Print the names of emitted files after a compilation.": { + "category": "Message", + "code": 6652 + }, + "Print all of the files read during the compilation.": { + "category": "Message", + "code": 6653 + }, + "Set the language of the messaging from TypeScript. This does not affect emit.": { + "category": "Message", + "code": 6654 + }, + "Specify the location where debugger should locate map files instead of generated locations.": { + "category": "Message", + "code": 6655 + }, + "Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'.": { + "category": "Message", + "code": 6656 + }, + "Specify what module code is generated.": { + "category": "Message", + "code": 6657 + }, + "Specify how TypeScript looks up a file from a given module specifier.": { + "category": "Message", + "code": 6658 + }, + "Set the newline character for emitting files.": { + "category": "Message", + "code": 6659 + }, + "Disable emitting files from a compilation.": { + "category": "Message", + "code": 6660 + }, + "Disable generating custom helper functions like '__extends' in compiled output.": { + "category": "Message", + "code": 6661 + }, + "Disable emitting files if any type checking errors are reported.": { + "category": "Message", + "code": 6662 + }, + "Disable truncating types in error messages.": { + "category": "Message", + "code": 6663 + }, + "Enable error reporting for fallthrough cases in switch statements.": { + "category": "Message", + "code": 6664 + }, + "Enable error reporting for expressions and declarations with an implied 'any' type.": { + "category": "Message", + "code": 6665 + }, + "Ensure overriding members in derived classes are marked with an override modifier.": { + "category": "Message", + "code": 6666 + }, + "Enable error reporting for codepaths that do not explicitly return in a function.": { + "category": "Message", + "code": 6667 + }, + "Enable error reporting when 'this' is given the type 'any'.": { + "category": "Message", + "code": 6668 + }, + "Disable adding 'use strict' directives in emitted JavaScript files.": { + "category": "Message", + "code": 6669 + }, + "Disable including any library files, including the default lib.d.ts.": { + "category": "Message", + "code": 6670 + }, + "Enforces using indexed accessors for keys declared using an indexed type.": { + "category": "Message", + "code": 6671 + }, + "Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project.": { + "category": "Message", + "code": 6672 + }, + "Disable strict checking of generic signatures in function types.": { + "category": "Message", + "code": 6673 + }, + "Add 'undefined' to a type when accessed using an index.": { + "category": "Message", + "code": 6674 + }, + "Enable error reporting when local variables aren't read.": { + "category": "Message", + "code": 6675 + }, + "Raise an error when a function parameter isn't read.": { + "category": "Message", + "code": 6676 + }, + "Deprecated setting. Use 'outFile' instead.": { + "category": "Message", + "code": 6677 + }, + "Specify an output folder for all emitted files.": { + "category": "Message", + "code": 6678 + }, + "Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output.": { + "category": "Message", + "code": 6679 + }, + "Specify a set of entries that re-map imports to additional lookup locations.": { + "category": "Message", + "code": 6680 + }, + "Specify a list of language service plugins to include.": { + "category": "Message", + "code": 6681 + }, + "Disable erasing 'const enum' declarations in generated code.": { + "category": "Message", + "code": 6682 + }, + "Disable resolving symlinks to their realpath. This correlates to the same flag in node.": { + "category": "Message", + "code": 6683 + }, + "Disable wiping the console in watch mode.": { + "category": "Message", + "code": 6684 + }, + "Enable color and formatting in TypeScript's output to make compiler errors easier to read.": { + "category": "Message", + "code": 6685 + }, + "Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit.": { + "category": "Message", + "code": 6686 + }, + "Specify an array of objects that specify paths for projects. Used in project references.": { + "category": "Message", + "code": 6687 + }, + "Disable emitting comments.": { + "category": "Message", + "code": 6688 + }, + "Enable importing .json files.": { + "category": "Message", + "code": 6689 + }, + "Specify the root folder within your source files.": { + "category": "Message", + "code": 6690 + }, + "Allow multiple folders to be treated as one when resolving modules.": { + "category": "Message", + "code": 6691 + }, + "Skip type checking .d.ts files that are included with TypeScript.": { + "category": "Message", + "code": 6692 + }, + "Skip type checking all .d.ts files.": { + "category": "Message", + "code": 6693 + }, + "Create source map files for emitted JavaScript files.": { + "category": "Message", + "code": 6694 + }, + "Specify the root path for debuggers to find the reference source code.": { + "category": "Message", + "code": 6695 + }, + "Check that the arguments for 'bind', 'call', and 'apply' methods match the original function.": { + "category": "Message", + "code": 6697 + }, + "When assigning functions, check to ensure parameters and the return values are subtype-compatible.": { + "category": "Message", + "code": 6698 + }, + "When type checking, take into account 'null' and 'undefined'.": { + "category": "Message", + "code": 6699 + }, + "Check for class properties that are declared but not set in the constructor.": { + "category": "Message", + "code": 6700 + }, + "Disable emitting declarations that have '@internal' in their JSDoc comments.": { + "category": "Message", + "code": 6701 + }, + "Disable reporting of excess property errors during the creation of object literals.": { + "category": "Message", + "code": 6702 + }, + "Suppress 'noImplicitAny' errors when indexing objects that lack index signatures.": { + "category": "Message", + "code": 6703 + }, + "Synchronously call callbacks and update the state of directory watchers on platforms that don`t support recursive watching natively.": { + "category": "Message", + "code": 6704 + }, + "Set the JavaScript language version for emitted JavaScript and include compatible library declarations.": { + "category": "Message", + "code": 6705 + }, + "Log paths used during the 'moduleResolution' process.": { + "category": "Message", + "code": 6706 + }, + "Specify the path to .tsbuildinfo incremental compilation file.": { + "category": "Message", + "code": 6707 + }, + "Specify options for automatic acquisition of declaration files.": { + "category": "Message", + "code": 6709 + }, + "Specify multiple folders that act like './node_modules/@types'.": { + "category": "Message", + "code": 6710 + }, + "Specify type package names to be included without being referenced in a source file.": { + "category": "Message", + "code": 6711 + }, + "Emit ECMAScript-standard-compliant class fields.": { + "category": "Message", + "code": 6712 + }, + "Enable verbose logging.": { + "category": "Message", + "code": 6713 + }, + "Specify how directories are watched on systems that lack recursive file-watching functionality.": { + "category": "Message", + "code": 6714 + }, + "Specify how the TypeScript watch mode works.": { + "category": "Message", + "code": 6715 + }, + "Require undeclared properties from index signatures to use element accesses.": { + "category": "Message", + "code": 6717 + }, + "Specify emit/checking behavior for imports that are only used for types.": { + "category": "Message", + "code": 6718 + }, + "Require sufficient annotation on exports so other tools can trivially generate declaration files.": { + "category": "Message", + "code": 6719 + }, + "Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'.": { + "category": "Message", + "code": 6720 + }, + "Do not allow runtime constructs that are not part of ECMAScript.": { + "category": "Message", + "code": 6721 + }, + "Default catch clause variables as 'unknown' instead of 'any'.": { + "category": "Message", + "code": 6803 + }, + "Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting.": { + "category": "Message", + "code": 6804 + }, + "Disable full type checking (only critical parse and emit errors will be reported).": { + "category": "Message", + "code": 6805 + }, + "Check side effect imports.": { + "category": "Message", + "code": 6806 + }, + "This operation can be simplified. This shift is identical to `{0} {1} {2}`.": { + "category": "Error", + "code": 6807 + }, + "Enable lib replacement.": { + "category": "Message", + "code": 6808 + }, + "Ensure types are ordered stably and deterministically across compilations.": { + "category": "Message", + "code": 6809 + }, + + "one of:": { + "category": "Message", + "code": 6900 + }, + "one or more:": { + "category": "Message", + "code": 6901 + }, + "type:": { + "category": "Message", + "code": 6902 + }, + "default:": { + "category": "Message", + "code": 6903 + }, + + "`true`, unless `strict` is `false`": { + "category": "Message", + "code": 6905 + }, + "`false`, unless `composite` is set": { + "category": "Message", + "code": 6906 + }, + "`[\"node_modules\", \"bower_components\", \"jspm_packages\"]`, plus the value of `outDir` if one is specified.": { + "category": "Message", + "code": 6907 + }, + "`[]` if `files` is specified, otherwise `[\"**/*\"]`": { + "category": "Message", + "code": 6908 + }, + "`true` if `composite`, `false` otherwise": { + "category": "Message", + "code": 6909 + }, + "`nodenext` if `module` is `nodenext`; `node16` if `module` is `node16` or `node18`; otherwise, `bundler`.": { + "category": "Message", + "code": 69010 + }, + "Computed from the list of input files": { + "category": "Message", + "code": 6911 + }, + "Platform specific": { + "category": "Message", + "code": 6912 + }, + "You can learn about all of the compiler options at {0}": { + "category": "Message", + "code": 6913 + }, + "Including --watch, -w will start watching the current project for the file changes. Once set, you can config watch mode with:": { + "category": "Message", + "code": 6914 + }, + "Using --build, -b will make tsc behave more like a build orchestrator than a compiler. This is used to trigger building composite projects which you can learn more about at {0}": { + "category": "Message", + "code": 6915 + }, + "COMMON COMMANDS": { + "category": "Message", + "code": 6916 + }, + "ALL COMPILER OPTIONS": { + "category": "Message", + "code": 6917 + }, + "WATCH OPTIONS": { + "category": "Message", + "code": 6918 + }, + "BUILD OPTIONS": { + "category": "Message", + "code": 6919 + }, + "COMMON COMPILER OPTIONS": { + "category": "Message", + "code": 6920 + }, + "COMMAND LINE FLAGS": { + "category": "Message", + "code": 6921 + }, + "tsc: The TypeScript Compiler": { + "category": "Message", + "code": 6922 + }, + "Compiles the current project (tsconfig.json in the working directory.)": { + "category": "Message", + "code": 6923 + }, + "Ignoring tsconfig.json, compiles the specified files with default compiler options.": { + "category": "Message", + "code": 6924 + }, + "Build a composite project in the working directory.": { + "category": "Message", + "code": 6925 + }, + "Creates a tsconfig.json with the recommended settings in the working directory.": { + "category": "Message", + "code": 6926 + }, + "Compiles the TypeScript project located at the specified path.": { + "category": "Message", + "code": 6927 + }, + "An expanded version of this information, showing all possible compiler options": { + "category": "Message", + "code": 6928 + }, + "Compiles the current project, with additional settings.": { + "category": "Message", + "code": 6929 + }, + "`true` for ES2022 and above, including ESNext.": { + "category": "Message", + "code": 6930 + }, + "List of file name suffixes to search when resolving a module.": { + "category": "Error", + "code": 6931 + }, + "`false`, unless `checkJs` is set": { + "category": "Message", + "code": 6932 + }, + + "Variable '{0}' implicitly has an '{1}' type.": { + "category": "Error", + "code": 7005 + }, + "Parameter '{0}' implicitly has an '{1}' type.": { + "category": "Error", + "code": 7006 + }, + "Member '{0}' implicitly has an '{1}' type.": { + "category": "Error", + "code": 7008 + }, + "'new' expression, whose target lacks a construct signature, implicitly has an 'any' type.": { + "category": "Error", + "code": 7009 + }, + "'{0}', which lacks return-type annotation, implicitly has an '{1}' return type.": { + "category": "Error", + "code": 7010 + }, + "Function expression, which lacks return-type annotation, implicitly has an '{0}' return type.": { + "category": "Error", + "code": 7011 + }, + "This overload implicitly returns the type '{0}' because it lacks a return type annotation.": { + "category": "Error", + "code": 7012 + }, + "Construct signature, which lacks return-type annotation, implicitly has an 'any' return type.": { + "category": "Error", + "code": 7013 + }, + "Function type, which lacks return-type annotation, implicitly has an '{0}' return type.": { + "category": "Error", + "code": 7014 + }, + "Element implicitly has an 'any' type because index expression is not of type 'number'.": { + "category": "Error", + "code": 7015 + }, + "Could not find a declaration file for module '{0}'. '{1}' implicitly has an 'any' type.": { + "category": "Error", + "code": 7016 + }, + "Element implicitly has an 'any' type because type '{0}' has no index signature.": { + "category": "Error", + "code": 7017 + }, + "Object literal's property '{0}' implicitly has an '{1}' type.": { + "category": "Error", + "code": 7018 + }, + "Rest parameter '{0}' implicitly has an 'any[]' type.": { + "category": "Error", + "code": 7019 + }, + "Call signature, which lacks return-type annotation, implicitly has an 'any' return type.": { + "category": "Error", + "code": 7020 + }, + "'{0}' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.": { + "category": "Error", + "code": 7022 + }, + "'{0}' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.": { + "category": "Error", + "code": 7023 + }, + "Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.": { + "category": "Error", + "code": 7024 + }, + "Generator implicitly has yield type '{0}'. Consider supplying a return type annotation.": { + "category": "Error", + "code": 7025 + }, + "JSX element implicitly has type 'any' because no interface 'JSX.{0}' exists.": { + "category": "Error", + "code": 7026 + }, + "Unreachable code detected.": { + "category": "Error", + "code": 7027, + "reportsUnnecessary": true + }, + "Unused label.": { + "category": "Error", + "code": 7028, + "reportsUnnecessary": true + }, + "Fallthrough case in switch.": { + "category": "Error", + "code": 7029 + }, + "Not all code paths return a value.": { + "category": "Error", + "code": 7030 + }, + "Binding element '{0}' implicitly has an '{1}' type.": { + "category": "Error", + "code": 7031 + }, + "Property '{0}' implicitly has type 'any', because its set accessor lacks a parameter type annotation.": { + "category": "Error", + "code": 7032 + }, + "Property '{0}' implicitly has type 'any', because its get accessor lacks a return type annotation.": { + "category": "Error", + "code": 7033 + }, + "Variable '{0}' implicitly has type '{1}' in some locations where its type cannot be determined.": { + "category": "Error", + "code": 7034 + }, + "Try `npm i --save-dev @types/{1}` if it exists or add a new declaration (.d.ts) file containing `declare module '{0}';`": { + "category": "Error", + "code": 7035 + }, + "Dynamic import's specifier must be of type 'string', but here has type '{0}'.": { + "category": "Error", + "code": 7036 + }, + "Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'.": { + "category": "Message", + "code": 7037 + }, + "Type originates at this import. A namespace-style import cannot be called or constructed, and will cause a failure at runtime. Consider using a default import or import require here instead.": { + "category": "Message", + "code": 7038 + }, + "Mapped object type implicitly has an 'any' template type.": { + "category": "Error", + "code": 7039 + }, + "If the '{0}' package actually exposes this module, consider sending a pull request to amend 'https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/{1}'": { + "category": "Error", + "code": 7040 + }, + "The containing arrow function captures the global value of 'this'.": { + "category": "Error", + "code": 7041 + }, + "Module '{0}' was resolved to '{1}', but '--resolveJsonModule' is not used.": { + "category": "Error", + "code": 7042 + }, + "Variable '{0}' implicitly has an '{1}' type, but a better type may be inferred from usage.": { + "category": "Suggestion", + "code": 7043 + }, + "Parameter '{0}' implicitly has an '{1}' type, but a better type may be inferred from usage.": { + "category": "Suggestion", + "code": 7044 + }, + "Member '{0}' implicitly has an '{1}' type, but a better type may be inferred from usage.": { + "category": "Suggestion", + "code": 7045 + }, + "Variable '{0}' implicitly has type '{1}' in some locations, but a better type may be inferred from usage.": { + "category": "Suggestion", + "code": 7046 + }, + "Rest parameter '{0}' implicitly has an 'any[]' type, but a better type may be inferred from usage.": { + "category": "Suggestion", + "code": 7047 + }, + "Property '{0}' implicitly has type 'any', but a better type for its get accessor may be inferred from usage.": { + "category": "Suggestion", + "code": 7048 + }, + "Property '{0}' implicitly has type 'any', but a better type for its set accessor may be inferred from usage.": { + "category": "Suggestion", + "code": 7049 + }, + "'{0}' implicitly has an '{1}' return type, but a better type may be inferred from usage.": { + "category": "Suggestion", + "code": 7050 + }, + "Parameter has a name but no type. Did you mean '{0}: {1}'?": { + "category": "Error", + "code": 7051 + }, + "Element implicitly has an 'any' type because type '{0}' has no index signature. Did you mean to call '{1}'?": { + "category": "Error", + "code": 7052 + }, + "Element implicitly has an 'any' type because expression of type '{0}' can't be used to index type '{1}'.": { + "category": "Error", + "code": 7053 + }, + "No index signature with a parameter of type '{0}' was found on type '{1}'.": { + "category": "Error", + "code": 7054 + }, + "'{0}', which lacks return-type annotation, implicitly has an '{1}' yield type.": { + "category": "Error", + "code": 7055 + }, + "The inferred type of this node exceeds the maximum length the compiler will serialize. An explicit type annotation is needed.": { + "category": "Error", + "code": 7056 + }, + "'yield' expression implicitly results in an 'any' type because its containing generator lacks a return-type annotation.": { + "category": "Error", + "code": 7057 + }, + "If the '{0}' package actually exposes this module, try adding a new declaration (.d.ts) file containing `declare module '{1}';`": { + "category": "Error", + "code": 7058 + }, + "This syntax is reserved in files with the .mts or .cts extension. Use an `as` expression instead.": { + "category": "Error", + "code": 7059 + }, + "This syntax is reserved in files with the .mts or .cts extension. Add a trailing comma or explicit constraint.": { + "category": "Error", + "code": 7060 + }, + "A mapped type may not declare properties or methods.": { + "category": "Error", + "code": 7061 + }, + + "You cannot rename this element.": { + "category": "Error", + "code": 8000 + }, + "You cannot rename elements that are defined in the standard TypeScript library.": { + "category": "Error", + "code": 8001 + }, + "'import ... =' can only be used in TypeScript files.": { + "category": "Error", + "code": 8002 + }, + "'export =' can only be used in TypeScript files.": { + "category": "Error", + "code": 8003 + }, + "Type parameter declarations can only be used in TypeScript files.": { + "category": "Error", + "code": 8004 + }, + "'implements' clauses can only be used in TypeScript files.": { + "category": "Error", + "code": 8005 + }, + "'{0}' declarations can only be used in TypeScript files.": { + "category": "Error", + "code": 8006 + }, + "Type aliases can only be used in TypeScript files.": { + "category": "Error", + "code": 8008 + }, + "The '{0}' modifier can only be used in TypeScript files.": { + "category": "Error", + "code": 8009 + }, + "Type annotations can only be used in TypeScript files.": { + "category": "Error", + "code": 8010 + }, + "Type arguments can only be used in TypeScript files.": { + "category": "Error", + "code": 8011 + }, + "Parameter modifiers can only be used in TypeScript files.": { + "category": "Error", + "code": 8012 + }, + "Non-null assertions can only be used in TypeScript files.": { + "category": "Error", + "code": 8013 + }, + "Type assertion expressions can only be used in TypeScript files.": { + "category": "Error", + "code": 8016 + }, + "Signature declarations can only be used in TypeScript files.": { + "category": "Error", + "code": 8017 + }, + "Report errors in .js files.": { + "category": "Message", + "code": 8019 + }, + "JSDoc types can only be used inside documentation comments.": { + "category": "Error", + "code": 8020 + }, + "JSDoc '@typedef' tag should either have a type annotation or be followed by '@property' or '@member' tags.": { + "category": "Error", + "code": 8021 + }, + "JSDoc '@{0}' is not attached to a class.": { + "category": "Error", + "code": 8022 + }, + "JSDoc '@{0} {1}' does not match the 'extends {2}' clause.": { + "category": "Error", + "code": 8023 + }, + "JSDoc '@param' tag has name '{0}', but there is no parameter with that name.": { + "category": "Error", + "code": 8024 + }, + "Class declarations cannot have more than one '@augments' or '@extends' tag.": { + "category": "Error", + "code": 8025 + }, + "Expected {0} type arguments; provide these with an '@extends' tag.": { + "category": "Error", + "code": 8026 + }, + "Expected {0}-{1} type arguments; provide these with an '@extends' tag.": { + "category": "Error", + "code": 8027 + }, + "JSDoc '...' may only appear in the last parameter of a signature.": { + "category": "Error", + "code": 8028 + }, + "JSDoc '@param' tag has name '{0}', but there is no parameter with that name. It would match 'arguments' if it had an array type.": { + "category": "Error", + "code": 8029 + }, + "The type of a function declaration must match the function's signature.": { + "category": "Error", + "code": 8030 + }, + "You cannot rename a module via a global import.": { + "category": "Error", + "code": 8031 + }, + "Qualified name '{0}' is not allowed without a leading '@param {object} {1}'.": { + "category": "Error", + "code": 8032 + }, + "A JSDoc '@typedef' comment may not contain multiple '@type' tags.": { + "category": "Error", + "code": 8033 + }, + "The tag was first specified here.": { + "category": "Error", + "code": 8034 + }, + "You cannot rename elements that are defined in a 'node_modules' folder.": { + "category": "Error", + "code": 8035 + }, + "You cannot rename elements that are defined in another 'node_modules' folder.": { + "category": "Error", + "code": 8036 + }, + "Type satisfaction expressions can only be used in TypeScript files.": { + "category": "Error", + "code": 8037 + }, + "Decorators may not appear after 'export' or 'export default' if they also appear before 'export'.": { + "category": "Error", + "code": 8038 + }, + "A JSDoc '@template' tag may not follow a '@typedef', '@callback', or '@overload' tag": { + "category": "Error", + "code": 8039 + }, + + "Declaration emit for this file requires using private name '{0}'. An explicit type annotation may unblock declaration emit.": { + "category": "Error", + "code": 9005 + }, + "Declaration emit for this file requires using private name '{0}' from module '{1}'. An explicit type annotation may unblock declaration emit.": { + "category": "Error", + "code": 9006 + }, + "Function must have an explicit return type annotation with --isolatedDeclarations.": { + "category": "Error", + "code": 9007 + }, + "Method must have an explicit return type annotation with --isolatedDeclarations.": { + "category": "Error", + "code": 9008 + }, + "At least one accessor must have an explicit type annotation with --isolatedDeclarations.": { + "category": "Error", + "code": 9009 + }, + "Variable must have an explicit type annotation with --isolatedDeclarations.": { + "category": "Error", + "code": 9010 + }, + "Parameter must have an explicit type annotation with --isolatedDeclarations.": { + "category": "Error", + "code": 9011 + }, + "Property must have an explicit type annotation with --isolatedDeclarations.": { + "category": "Error", + "code": 9012 + }, + "Expression type can't be inferred with --isolatedDeclarations.": { + "category": "Error", + "code": 9013 + }, + "Computed properties must be number or string literals, variables or dotted expressions with --isolatedDeclarations.": { + "category": "Error", + "code": 9014 + }, + "Objects that contain spread assignments can't be inferred with --isolatedDeclarations.": { + "category": "Error", + "code": 9015 + }, + "Objects that contain shorthand properties can't be inferred with --isolatedDeclarations.": { + "category": "Error", + "code": 9016 + }, + "Only const arrays can be inferred with --isolatedDeclarations.": { + "category": "Error", + "code": 9017 + }, + "Arrays with spread elements can't inferred with --isolatedDeclarations.": { + "category": "Error", + "code": 9018 + }, + "Binding elements can't be exported directly with --isolatedDeclarations.": { + "category": "Error", + "code": 9019 + }, + "Enum member initializers must be computable without references to external symbols with --isolatedDeclarations.": { + "category": "Error", + "code": 9020 + }, + "Extends clause can't contain an expression with --isolatedDeclarations.": { + "category": "Error", + "code": 9021 + }, + "Inference from class expressions is not supported with --isolatedDeclarations.": { + "category": "Error", + "code": 9022 + }, + "Assigning properties to functions without declaring them is not supported with --isolatedDeclarations. Add an explicit declaration for the properties assigned to this function.": { + "category": "Error", + "code": 9023 + }, + "Declaration emit for this parameter requires implicitly adding undefined to its type. This is not supported with --isolatedDeclarations.": { + "category": "Error", + "code": 9025 + }, + "Declaration emit for this file requires preserving this import for augmentations. This is not supported with --isolatedDeclarations.": { + "category": "Error", + "code": 9026 + }, + "Add a type annotation to the variable {0}.": { + "category": "Error", + "code": 9027 + }, + "Add a type annotation to the parameter {0}.": { + "category": "Error", + "code": 9028 + }, + "Add a type annotation to the property {0}.": { + "category": "Error", + "code": 9029 + }, + "Add a return type to the function expression.": { + "category": "Error", + "code": 9030 + }, + "Add a return type to the function declaration.": { + "category": "Error", + "code": 9031 + }, + "Add a return type to the get accessor declaration.": { + "category": "Error", + "code": 9032 + }, + "Add a type to parameter of the set accessor declaration.": { + "category": "Error", + "code": 9033 + }, + "Add a return type to the method": { + "category": "Error", + "code": 9034 + }, + "Add satisfies and a type assertion to this expression (satisfies T as T) to make the type explicit.": { + "category": "Error", + "code": 9035 + }, + "Move the expression in default export to a variable and add a type annotation to it.": { + "category": "Error", + "code": 9036 + }, + "Default exports can't be inferred with --isolatedDeclarations.": { + "category": "Error", + "code": 9037 + }, + "Computed property names on class or object literals cannot be inferred with --isolatedDeclarations.": { + "category": "Error", + "code": 9038 + }, + "Type containing private name '{0}' can't be used with --isolatedDeclarations.": { + "category": "Error", + "code": 9039 + }, + "JSX attributes must only be assigned a non-empty 'expression'.": { + "category": "Error", + "code": 17000 + }, + "JSX elements cannot have multiple attributes with the same name.": { + "category": "Error", + "code": 17001 + }, + "Expected corresponding JSX closing tag for '{0}'.": { + "category": "Error", + "code": 17002 + }, + "Cannot use JSX unless the '--jsx' flag is provided.": { + "category": "Error", + "code": 17004 + }, + "A constructor cannot contain a 'super' call when its class extends 'null'.": { + "category": "Error", + "code": 17005 + }, + "An unary expression with the '{0}' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.": { + "category": "Error", + "code": 17006 + }, + "A type assertion expression is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.": { + "category": "Error", + "code": 17007 + }, + "JSX element '{0}' has no corresponding closing tag.": { + "category": "Error", + "code": 17008 + }, + "'super' must be called before accessing 'this' in the constructor of a derived class.": { + "category": "Error", + "code": 17009 + }, + "Unknown type acquisition option '{0}'.": { + "category": "Error", + "code": 17010 + }, + "'super' must be called before accessing a property of 'super' in the constructor of a derived class.": { + "category": "Error", + "code": 17011 + }, + "'{0}' is not a valid meta-property for keyword '{1}'. Did you mean '{2}'?": { + "category": "Error", + "code": 17012 + }, + "Meta-property '{0}' is only allowed in the body of a function declaration, function expression, or constructor.": { + "category": "Error", + "code": 17013 + }, + "JSX fragment has no corresponding closing tag.": { + "category": "Error", + "code": 17014 + }, + "Expected corresponding closing tag for JSX fragment.": { + "category": "Error", + "code": 17015 + }, + "The 'jsxFragmentFactory' compiler option must be provided to use JSX fragments with the 'jsxFactory' compiler option.": { + "category": "Error", + "code": 17016 + }, + "An @jsxFrag pragma is required when using an @jsx pragma with JSX fragments.": { + "category": "Error", + "code": 17017 + }, + "Unknown type acquisition option '{0}'. Did you mean '{1}'?": { + "category": "Error", + "code": 17018 + }, + "'{0}' at the end of a type is not valid TypeScript syntax. Did you mean to write '{1}'?": { + "category": "Error", + "code": 17019 + }, + "'{0}' at the start of a type is not valid TypeScript syntax. Did you mean to write '{1}'?": { + "category": "Error", + "code": 17020 + }, + "Unicode escape sequence cannot appear here.": { + "category": "Error", + "code": 17021 + }, + "Circularity detected while resolving configuration: {0}": { + "category": "Error", + "code": 18000 + }, + "The 'files' list in config file '{0}' is empty.": { + "category": "Error", + "code": 18002 + }, + "No inputs were found in config file '{0}'. Specified 'include' paths were '{1}' and 'exclude' paths were '{2}'.": { + "category": "Error", + "code": 18003 + }, + + "File is a CommonJS module; it may be converted to an ES module.": { + "category": "Suggestion", + "code": 80001 + }, + "This constructor function may be converted to a class declaration.": { + "category": "Suggestion", + "code": 80002 + }, + "Import may be converted to a default import.": { + "category": "Suggestion", + "code": 80003 + }, + "JSDoc types may be moved to TypeScript types.": { + "category": "Suggestion", + "code": 80004 + }, + "'require' call may be converted to an import.": { + "category": "Suggestion", + "code": 80005 + }, + "This may be converted to an async function.": { + "category": "Suggestion", + "code": 80006 + }, + "'await' has no effect on the type of this expression.": { + "category": "Suggestion", + "code": 80007 + }, + "Numeric literals with absolute values equal to 2^53 or greater are too large to be represented accurately as integers.": { + "category": "Suggestion", + "code": 80008 + }, + "JSDoc typedef may be converted to TypeScript type.": { + "category": "Suggestion", + "code": 80009 + }, + "JSDoc typedefs may be converted to TypeScript types.": { + "category": "Suggestion", + "code": 80010 + }, + + "Add missing 'super()' call": { + "category": "Message", + "code": 90001 + }, + "Make 'super()' call the first statement in the constructor": { + "category": "Message", + "code": 90002 + }, + "Change 'extends' to 'implements'": { + "category": "Message", + "code": 90003 + }, + "Remove unused declaration for: '{0}'": { + "category": "Message", + "code": 90004 + }, + "Remove import from '{0}'": { + "category": "Message", + "code": 90005 + }, + "Implement interface '{0}'": { + "category": "Message", + "code": 90006 + }, + "Implement inherited abstract class": { + "category": "Message", + "code": 90007 + }, + "Add '{0}.' to unresolved variable": { + "category": "Message", + "code": 90008 + }, + "Remove variable statement": { + "category": "Message", + "code": 90010 + }, + "Remove template tag": { + "category": "Message", + "code": 90011 + }, + "Remove type parameters": { + "category": "Message", + "code": 90012 + }, + "Import '{0}' from \"{1}\"": { + "category": "Message", + "code": 90013 + }, + "Change '{0}' to '{1}'": { + "category": "Message", + "code": 90014 + }, + "Declare property '{0}'": { + "category": "Message", + "code": 90016 + }, + "Add index signature for property '{0}'": { + "category": "Message", + "code": 90017 + }, + "Disable checking for this file": { + "category": "Message", + "code": 90018 + }, + "Ignore this error message": { + "category": "Message", + "code": 90019 + }, + "Initialize property '{0}' in the constructor": { + "category": "Message", + "code": 90020 + }, + "Initialize static property '{0}'": { + "category": "Message", + "code": 90021 + }, + "Change spelling to '{0}'": { + "category": "Message", + "code": 90022 + }, + "Declare method '{0}'": { + "category": "Message", + "code": 90023 + }, + "Declare static method '{0}'": { + "category": "Message", + "code": 90024 + }, + "Prefix '{0}' with an underscore": { + "category": "Message", + "code": 90025 + }, + "Rewrite as the indexed access type '{0}'": { + "category": "Message", + "code": 90026 + }, + "Declare static property '{0}'": { + "category": "Message", + "code": 90027 + }, + "Call decorator expression": { + "category": "Message", + "code": 90028 + }, + "Add async modifier to containing function": { + "category": "Message", + "code": 90029 + }, + "Replace 'infer {0}' with 'unknown'": { + "category": "Message", + "code": 90030 + }, + "Replace all unused 'infer' with 'unknown'": { + "category": "Message", + "code": 90031 + }, + "Add parameter name": { + "category": "Message", + "code": 90034 + }, + "Declare private property '{0}'": { + "category": "Message", + "code": 90035 + }, + "Replace '{0}' with 'Promise<{1}>'": { + "category": "Message", + "code": 90036 + }, + "Fix all incorrect return type of an async functions": { + "category": "Message", + "code": 90037 + }, + "Declare private method '{0}'": { + "category": "Message", + "code": 90038 + }, + "Remove unused destructuring declaration": { + "category": "Message", + "code": 90039 + }, + "Remove unused declarations for: '{0}'": { + "category": "Message", + "code": 90041 + }, + "Declare a private field named '{0}'.": { + "category": "Message", + "code": 90053 + }, + "Includes imports of types referenced by '{0}'": { + "category": "Message", + "code": 90054 + }, + "Remove 'type' from import declaration from \"{0}\"": { + "category": "Message", + "code": 90055 + }, + "Remove 'type' from import of '{0}' from \"{1}\"": { + "category": "Message", + "code": 90056 + }, + "Add import from \"{0}\"": { + "category": "Message", + "code": 90057 + }, + "Update import from \"{0}\"": { + "category": "Message", + "code": 90058 + }, + "Export '{0}' from module '{1}'": { + "category": "Message", + "code": 90059 + }, + "Export all referenced locals": { + "category": "Message", + "code": 90060 + }, + "Update modifiers of '{0}'": { + "category": "Message", + "code": 90061 + }, + "Add annotation of type '{0}'": { + "category": "Message", + "code": 90062 + }, + "Add return type '{0}'": { + "category": "Message", + "code": 90063 + }, + "Extract base class to variable": { + "category": "Message", + "code": 90064 + }, + "Extract default export to variable": { + "category": "Message", + "code": 90065 + }, + "Extract binding expressions to variable": { + "category": "Message", + "code": 90066 + }, + "Add all missing type annotations": { + "category": "Message", + "code": 90067 + }, + "Add satisfies and an inline type assertion with '{0}'": { + "category": "Message", + "code": 90068 + }, + "Extract to variable and replace with '{0} as typeof {0}'": { + "category": "Message", + "code": 90069 + }, + "Mark array literal as const": { + "category": "Message", + "code": 90070 + }, + "Annotate types of properties expando function in a namespace": { + "category": "Message", + "code": 90071 + }, + + "Convert function to an ES2015 class": { + "category": "Message", + "code": 95001 + }, + "Convert '{0}' to '{1} in {0}'": { + "category": "Message", + "code": 95003 + }, + "Extract to {0} in {1}": { + "category": "Message", + "code": 95004 + }, + "Extract function": { + "category": "Message", + "code": 95005 + }, + "Extract constant": { + "category": "Message", + "code": 95006 + }, + "Extract to {0} in enclosing scope": { + "category": "Message", + "code": 95007 + }, + "Extract to {0} in {1} scope": { + "category": "Message", + "code": 95008 + }, + "Annotate with type from JSDoc": { + "category": "Message", + "code": 95009 + }, + "Infer type of '{0}' from usage": { + "category": "Message", + "code": 95011 + }, + "Infer parameter types from usage": { + "category": "Message", + "code": 95012 + }, + "Convert to default import": { + "category": "Message", + "code": 95013 + }, + "Install '{0}'": { + "category": "Message", + "code": 95014 + }, + "Replace import with '{0}'.": { + "category": "Message", + "code": 95015 + }, + "Use synthetic 'default' member.": { + "category": "Message", + "code": 95016 + }, + "Convert to ES module": { + "category": "Message", + "code": 95017 + }, + "Add 'undefined' type to property '{0}'": { + "category": "Message", + "code": 95018 + }, + "Add initializer to property '{0}'": { + "category": "Message", + "code": 95019 + }, + "Add definite assignment assertion to property '{0}'": { + "category": "Message", + "code": 95020 + }, + "Convert all type literals to mapped type": { + "category": "Message", + "code": 95021 + }, + "Add all missing members": { + "category": "Message", + "code": 95022 + }, + "Infer all types from usage": { + "category": "Message", + "code": 95023 + }, + "Delete all unused declarations": { + "category": "Message", + "code": 95024 + }, + "Prefix all unused declarations with '_' where possible": { + "category": "Message", + "code": 95025 + }, + "Fix all detected spelling errors": { + "category": "Message", + "code": 95026 + }, + "Add initializers to all uninitialized properties": { + "category": "Message", + "code": 95027 + }, + "Add definite assignment assertions to all uninitialized properties": { + "category": "Message", + "code": 95028 + }, + "Add undefined type to all uninitialized properties": { + "category": "Message", + "code": 95029 + }, + "Change all jsdoc-style types to TypeScript": { + "category": "Message", + "code": 95030 + }, + "Change all jsdoc-style types to TypeScript (and add '| undefined' to nullable types)": { + "category": "Message", + "code": 95031 + }, + "Implement all unimplemented interfaces": { + "category": "Message", + "code": 95032 + }, + "Install all missing types packages": { + "category": "Message", + "code": 95033 + }, + "Rewrite all as indexed access types": { + "category": "Message", + "code": 95034 + }, + "Convert all to default imports": { + "category": "Message", + "code": 95035 + }, + "Make all 'super()' calls the first statement in their constructor": { + "category": "Message", + "code": 95036 + }, + "Add qualifier to all unresolved variables matching a member name": { + "category": "Message", + "code": 95037 + }, + "Change all extended interfaces to 'implements'": { + "category": "Message", + "code": 95038 + }, + "Add all missing super calls": { + "category": "Message", + "code": 95039 + }, + "Implement all inherited abstract classes": { + "category": "Message", + "code": 95040 + }, + "Add all missing 'async' modifiers": { + "category": "Message", + "code": 95041 + }, + "Add '@ts-ignore' to all error messages": { + "category": "Message", + "code": 95042 + }, + "Annotate everything with types from JSDoc": { + "category": "Message", + "code": 95043 + }, + "Add '()' to all uncalled decorators": { + "category": "Message", + "code": 95044 + }, + "Convert all constructor functions to classes": { + "category": "Message", + "code": 95045 + }, + "Generate 'get' and 'set' accessors": { + "category": "Message", + "code": 95046 + }, + "Convert 'require' to 'import'": { + "category": "Message", + "code": 95047 + }, + "Convert all 'require' to 'import'": { + "category": "Message", + "code": 95048 + }, + "Move to a new file": { + "category": "Message", + "code": 95049 + }, + "Remove unreachable code": { + "category": "Message", + "code": 95050 + }, + "Remove all unreachable code": { + "category": "Message", + "code": 95051 + }, + "Add missing 'typeof'": { + "category": "Message", + "code": 95052 + }, + "Remove unused label": { + "category": "Message", + "code": 95053 + }, + "Remove all unused labels": { + "category": "Message", + "code": 95054 + }, + "Convert '{0}' to mapped object type": { + "category": "Message", + "code": 95055 + }, + "Convert namespace import to named imports": { + "category": "Message", + "code": 95056 + }, + "Convert named imports to namespace import": { + "category": "Message", + "code": 95057 + }, + "Add or remove braces in an arrow function": { + "category": "Message", + "code": 95058 + }, + "Add braces to arrow function": { + "category": "Message", + "code": 95059 + }, + "Remove braces from arrow function": { + "category": "Message", + "code": 95060 + }, + "Convert default export to named export": { + "category": "Message", + "code": 95061 + }, + "Convert named export to default export": { + "category": "Message", + "code": 95062 + }, + "Add missing enum member '{0}'": { + "category": "Message", + "code": 95063 + }, + "Add all missing imports": { + "category": "Message", + "code": 95064 + }, + "Convert to async function": { + "category": "Message", + "code": 95065 + }, + "Convert all to async functions": { + "category": "Message", + "code": 95066 + }, + "Add missing call parentheses": { + "category": "Message", + "code": 95067 + }, + "Add all missing call parentheses": { + "category": "Message", + "code": 95068 + }, + "Add 'unknown' conversion for non-overlapping types": { + "category": "Message", + "code": 95069 + }, + "Add 'unknown' to all conversions of non-overlapping types": { + "category": "Message", + "code": 95070 + }, + "Add missing 'new' operator to call": { + "category": "Message", + "code": 95071 + }, + "Add missing 'new' operator to all calls": { + "category": "Message", + "code": 95072 + }, + "Add names to all parameters without names": { + "category": "Message", + "code": 95073 + }, + "Enable the 'experimentalDecorators' option in your configuration file": { + "category": "Message", + "code": 95074 + }, + "Convert parameters to destructured object": { + "category": "Message", + "code": 95075 + }, + "Extract type": { + "category": "Message", + "code": 95077 + }, + "Extract to type alias": { + "category": "Message", + "code": 95078 + }, + "Extract to typedef": { + "category": "Message", + "code": 95079 + }, + "Infer 'this' type of '{0}' from usage": { + "category": "Message", + "code": 95080 + }, + "Add 'const' to unresolved variable": { + "category": "Message", + "code": 95081 + }, + "Add 'const' to all unresolved variables": { + "category": "Message", + "code": 95082 + }, + "Add 'await'": { + "category": "Message", + "code": 95083 + }, + "Add 'await' to initializer for '{0}'": { + "category": "Message", + "code": 95084 + }, + "Fix all expressions possibly missing 'await'": { + "category": "Message", + "code": 95085 + }, + "Remove unnecessary 'await'": { + "category": "Message", + "code": 95086 + }, + "Remove all unnecessary uses of 'await'": { + "category": "Message", + "code": 95087 + }, + "Enable the '--jsx' flag in your configuration file": { + "category": "Message", + "code": 95088 + }, + "Add 'await' to initializers": { + "category": "Message", + "code": 95089 + }, + "Extract to interface": { + "category": "Message", + "code": 95090 + }, + "Convert to a bigint numeric literal": { + "category": "Message", + "code": 95091 + }, + "Convert all to bigint numeric literals": { + "category": "Message", + "code": 95092 + }, + "Convert 'const' to 'let'": { + "category": "Message", + "code": 95093 + }, + "Prefix with 'declare'": { + "category": "Message", + "code": 95094 + }, + "Prefix all incorrect property declarations with 'declare'": { + "category": "Message", + "code": 95095 + }, + "Convert to template string": { + "category": "Message", + "code": 95096 + }, + "Add 'export {}' to make this file into a module": { + "category": "Message", + "code": 95097 + }, + "Set the 'target' option in your configuration file to '{0}'": { + "category": "Message", + "code": 95098 + }, + "Set the 'module' option in your configuration file to '{0}'": { + "category": "Message", + "code": 95099 + }, + "Convert invalid character to its html entity code": { + "category": "Message", + "code": 95100 + }, + "Convert all invalid characters to HTML entity code": { + "category": "Message", + "code": 95101 + }, + "Convert all 'const' to 'let'": { + "category": "Message", + "code": 95102 + }, + "Convert function expression '{0}' to arrow function": { + "category": "Message", + "code": 95105 + }, + "Convert function declaration '{0}' to arrow function": { + "category": "Message", + "code": 95106 + }, + "Fix all implicit-'this' errors": { + "category": "Message", + "code": 95107 + }, + "Wrap invalid character in an expression container": { + "category": "Message", + "code": 95108 + }, + "Wrap all invalid characters in an expression container": { + "category": "Message", + "code": 95109 + }, + "Visit https://aka.ms/tsconfig to read more about this file": { + "category": "Message", + "code": 95110 + }, + "Add a return statement": { + "category": "Message", + "code": 95111 + }, + "Remove braces from arrow function body": { + "category": "Message", + "code": 95112 + }, + "Wrap the following body with parentheses which should be an object literal": { + "category": "Message", + "code": 95113 + }, + "Add all missing return statement": { + "category": "Message", + "code": 95114 + }, + "Remove braces from all arrow function bodies with relevant issues": { + "category": "Message", + "code": 95115 + }, + "Wrap all object literal with parentheses": { + "category": "Message", + "code": 95116 + }, + "Move labeled tuple element modifiers to labels": { + "category": "Message", + "code": 95117 + }, + "Convert overload list to single signature": { + "category": "Message", + "code": 95118 + }, + "Generate 'get' and 'set' accessors for all overriding properties": { + "category": "Message", + "code": 95119 + }, + "Wrap in JSX fragment": { + "category": "Message", + "code": 95120 + }, + "Wrap all unparented JSX in JSX fragment": { + "category": "Message", + "code": 95121 + }, + "Convert arrow function or function expression": { + "category": "Message", + "code": 95122 + }, + "Convert to anonymous function": { + "category": "Message", + "code": 95123 + }, + "Convert to named function": { + "category": "Message", + "code": 95124 + }, + "Convert to arrow function": { + "category": "Message", + "code": 95125 + }, + "Remove parentheses": { + "category": "Message", + "code": 95126 + }, + "Could not find a containing arrow function": { + "category": "Message", + "code": 95127 + }, + "Containing function is not an arrow function": { + "category": "Message", + "code": 95128 + }, + "Could not find export statement": { + "category": "Message", + "code": 95129 + }, + "This file already has a default export": { + "category": "Message", + "code": 95130 + }, + "Could not find import clause": { + "category": "Message", + "code": 95131 + }, + "Could not find namespace import or named imports": { + "category": "Message", + "code": 95132 + }, + "Selection is not a valid type node": { + "category": "Message", + "code": 95133 + }, + "No type could be extracted from this type node": { + "category": "Message", + "code": 95134 + }, + "Could not find property for which to generate accessor": { + "category": "Message", + "code": 95135 + }, + "Name is not valid": { + "category": "Message", + "code": 95136 + }, + "Can only convert property with modifier": { + "category": "Message", + "code": 95137 + }, + "Switch each misused '{0}' to '{1}'": { + "category": "Message", + "code": 95138 + }, + "Convert to optional chain expression": { + "category": "Message", + "code": 95139 + }, + "Could not find convertible access expression": { + "category": "Message", + "code": 95140 + }, + "Could not find matching access expressions": { + "category": "Message", + "code": 95141 + }, + "Can only convert logical AND access chains": { + "category": "Message", + "code": 95142 + }, + "Add 'void' to Promise resolved without a value": { + "category": "Message", + "code": 95143 + }, + "Add 'void' to all Promises resolved without a value": { + "category": "Message", + "code": 95144 + }, + "Use element access for '{0}'": { + "category": "Message", + "code": 95145 + }, + "Use element access for all undeclared properties.": { + "category": "Message", + "code": 95146 + }, + "Delete all unused imports": { + "category": "Message", + "code": 95147 + }, + "Infer function return type": { + "category": "Message", + "code": 95148 + }, + "Return type must be inferred from a function": { + "category": "Message", + "code": 95149 + }, + "Could not determine function return type": { + "category": "Message", + "code": 95150 + }, + "Could not convert to arrow function": { + "category": "Message", + "code": 95151 + }, + "Could not convert to named function": { + "category": "Message", + "code": 95152 + }, + "Could not convert to anonymous function": { + "category": "Message", + "code": 95153 + }, + "Can only convert string concatenations and string literals": { + "category": "Message", + "code": 95154 + }, + "Selection is not a valid statement or statements": { + "category": "Message", + "code": 95155 + }, + "Add missing function declaration '{0}'": { + "category": "Message", + "code": 95156 + }, + "Add all missing function declarations": { + "category": "Message", + "code": 95157 + }, + "Method not implemented.": { + "category": "Message", + "code": 95158 + }, + "Function not implemented.": { + "category": "Message", + "code": 95159 + }, + "Add 'override' modifier": { + "category": "Message", + "code": 95160 + }, + "Remove 'override' modifier": { + "category": "Message", + "code": 95161 + }, + "Add all missing 'override' modifiers": { + "category": "Message", + "code": 95162 + }, + "Remove all unnecessary 'override' modifiers": { + "category": "Message", + "code": 95163 + }, + "Can only convert named export": { + "category": "Message", + "code": 95164 + }, + "Add missing properties": { + "category": "Message", + "code": 95165 + }, + "Add all missing properties": { + "category": "Message", + "code": 95166 + }, + "Add missing attributes": { + "category": "Message", + "code": 95167 + }, + "Add all missing attributes": { + "category": "Message", + "code": 95168 + }, + "Add 'undefined' to optional property type": { + "category": "Message", + "code": 95169 + }, + "Convert named imports to default import": { + "category": "Message", + "code": 95170 + }, + "Delete unused '@param' tag '{0}'": { + "category": "Message", + "code": 95171 + }, + "Delete all unused '@param' tags": { + "category": "Message", + "code": 95172 + }, + "Rename '@param' tag name '{0}' to '{1}'": { + "category": "Message", + "code": 95173 + }, + "Use `{0}`.": { + "category": "Message", + "code": 95174 + }, + "Use `Number.isNaN` in all conditions.": { + "category": "Message", + "code": 95175 + }, + "Convert typedef to TypeScript type.": { + "category": "Message", + "code": 95176 + }, + "Convert all typedef to TypeScript types.": { + "category": "Message", + "code": 95177 + }, + "Move to file": { + "category": "Message", + "code": 95178 + }, + "Cannot move to file, selected file is invalid": { + "category": "Message", + "code": 95179 + }, + "Use 'import type'": { + "category": "Message", + "code": 95180 + }, + "Use 'type {0}'": { + "category": "Message", + "code": 95181 + }, + "Fix all with type-only imports": { + "category": "Message", + "code": 95182 + }, + "Cannot move statements to the selected file": { + "category": "Message", + "code": 95183 + }, + "Inline variable": { + "category": "Message", + "code": 95184 + }, + "Could not find variable to inline.": { + "category": "Message", + "code": 95185 + }, + "Variables with multiple declarations cannot be inlined.": { + "category": "Message", + "code": 95186 + }, + "Add missing comma for object member completion '{0}'.": { + "category": "Message", + "code": 95187 + }, + "Add missing parameter to '{0}'": { + "category": "Message", + "code": 95188 + }, + "Add missing parameters to '{0}'": { + "category": "Message", + "code": 95189 + }, + "Add all missing parameters": { + "category": "Message", + "code": 95190 + }, + "Add optional parameter to '{0}'": { + "category": "Message", + "code": 95191 + }, + "Add optional parameters to '{0}'": { + "category": "Message", + "code": 95192 + }, + "Add all optional parameters": { + "category": "Message", + "code": 95193 + }, + "Wrap in parentheses": { + "category": "Message", + "code": 95194 + }, + "Wrap all invalid decorator expressions in parentheses": { + "category": "Message", + "code": 95195 + }, + "Add 'resolution-mode' import attribute": { + "category": "Message", + "code": 95196 + }, + "Add 'resolution-mode' import attribute to all type-only imports that need it": { + "category": "Message", + "code": 95197 + }, + + "No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": { + "category": "Error", + "code": 18004 + }, + "Classes may not have a field named 'constructor'.": { + "category": "Error", + "code": 18006 + }, + "JSX expressions may not use the comma operator. Did you mean to write an array?": { + "category": "Error", + "code": 18007 + }, + "Private identifiers cannot be used as parameters.": { + "category": "Error", + "code": 18009 + }, + "An accessibility modifier cannot be used with a private identifier.": { + "category": "Error", + "code": 18010 + }, + "The operand of a 'delete' operator cannot be a private identifier.": { + "category": "Error", + "code": 18011 + }, + "'#constructor' is a reserved word.": { + "category": "Error", + "code": 18012 + }, + "Property '{0}' is not accessible outside class '{1}' because it has a private identifier.": { + "category": "Error", + "code": 18013 + }, + "The property '{0}' cannot be accessed on type '{1}' within this class because it is shadowed by another private identifier with the same spelling.": { + "category": "Error", + "code": 18014 + }, + "Property '{0}' in type '{1}' refers to a different member that cannot be accessed from within type '{2}'.": { + "category": "Error", + "code": 18015 + }, + "Private identifiers are not allowed outside class bodies.": { + "category": "Error", + "code": 18016 + }, + "The shadowing declaration of '{0}' is defined here": { + "category": "Error", + "code": 18017 + }, + "The declaration of '{0}' that you probably intended to use is defined here": { + "category": "Error", + "code": 18018 + }, + "'{0}' modifier cannot be used with a private identifier.": { + "category": "Error", + "code": 18019 + }, + "An enum member cannot be named with a private identifier.": { + "category": "Error", + "code": 18024 + }, + "'#!' can only be used at the start of a file.": { + "category": "Error", + "code": 18026 + }, + "Compiler reserves name '{0}' when emitting private identifier downlevel.": { + "category": "Error", + "code": 18027 + }, + "Private identifiers are only available when targeting ECMAScript 2015 and higher.": { + "category": "Error", + "code": 18028 + }, + "Private identifiers are not allowed in variable declarations.": { + "category": "Error", + "code": 18029 + }, + "An optional chain cannot contain private identifiers.": { + "category": "Error", + "code": 18030 + }, + "The intersection '{0}' was reduced to 'never' because property '{1}' has conflicting types in some constituents.": { + "category": "Error", + "code": 18031 + }, + "The intersection '{0}' was reduced to 'never' because property '{1}' exists in multiple constituents and is private in some.": { + "category": "Error", + "code": 18032 + }, + "Type '{0}' is not assignable to type '{1}' as required for computed enum member values.": { + "category": "Error", + "code": 18033 + }, + "Specify the JSX fragment factory function to use when targeting 'react' JSX emit with 'jsxFactory' compiler option is specified, e.g. 'Fragment'.": { + "category": "Message", + "code": 18034 + }, + "Invalid value for 'jsxFragmentFactory'. '{0}' is not a valid identifier or qualified-name.": { + "category": "Error", + "code": 18035 + }, + "Class decorators can't be used with static private identifier. Consider removing the experimental decorator.": { + "category": "Error", + "code": 18036 + }, + "'await' expression cannot be used inside a class static block.": { + "category": "Error", + "code": 18037 + }, + "'for await' loops cannot be used inside a class static block.": { + "category": "Error", + "code": 18038 + }, + "Invalid use of '{0}'. It cannot be used inside a class static block.": { + "category": "Error", + "code": 18039 + }, + "A 'return' statement cannot be used inside a class static block.": { + "category": "Error", + "code": 18041 + }, + "'{0}' is a type and cannot be imported in JavaScript files. Use '{1}' in a JSDoc type annotation.": { + "category": "Error", + "code": 18042 + }, + "Types cannot appear in export declarations in JavaScript files.": { + "category": "Error", + "code": 18043 + }, + "'{0}' is automatically exported here.": { + "category": "Message", + "code": 18044 + }, + "Properties with the 'accessor' modifier are only available when targeting ECMAScript 2015 and higher.": { + "category": "Error", + "code": 18045 + }, + "'{0}' is of type 'unknown'.": { + "category": "Error", + "code": 18046 + }, + "'{0}' is possibly 'null'.": { + "category": "Error", + "code": 18047 + }, + "'{0}' is possibly 'undefined'.": { + "category": "Error", + "code": 18048 + }, + "'{0}' is possibly 'null' or 'undefined'.": { + "category": "Error", + "code": 18049 + }, + "The value '{0}' cannot be used here.": { + "category": "Error", + "code": 18050 + }, + "Compiler option '{0}' cannot be given an empty string.": { + "category": "Error", + "code": 18051 + }, + "Its type '{0}' is not a valid JSX element type.": { + "category": "Error", + "code": 18053 + }, + "'await using' statements cannot be used inside a class static block.": { + "category": "Error", + "code": 18054 + }, + "'{0}' has a string type, but must have syntactically recognizable string syntax when 'isolatedModules' is enabled.": { + "category": "Error", + "code": 18055 + }, + "Enum member following a non-literal numeric member must have an initializer when 'isolatedModules' is enabled.": { + "category": "Error", + "code": 18056 + }, + "String literal import and export names are not supported when the '--module' flag is set to 'es2015' or 'es2020'.": { + "category": "Error", + "code": 18057 + }, + "Default imports are not allowed in a deferred import.": { + "category": "Error", + "code": 18058 + }, + "Named imports are not allowed in a deferred import.": { + "category": "Error", + "code": 18059 + }, + "Deferred imports are only supported when the '--module' flag is set to 'esnext' or 'preserve'.": { + "category": "Error", + "code": 18060 + }, + "'{0}' is not a valid meta-property for keyword 'import'. Did you mean 'meta' or 'defer'?": { + "category": "Error", + "code": 18061 + } +} diff --git a/tsc/internal/format/README.md b/tsc/internal/format/README.md index d1086eaa0b485..fe19d2886b599 100644 --- a/tsc/internal/format/README.md +++ b/tsc/internal/format/README.md @@ -1,26 +1,26 @@ -# How does TypeScript formatting work? - -To format code you need to have a formatting context and a `SourceFile`. The formatting context contains -all user settings like tab size, newline character, etc. - -The end result of formatting is represented by TextChange objects which hold the new string content, and -the text to replace it with. - -## Internals - -Most of the exposed APIs internally are `Format*` and they all set up and configure `FormatSpan` which could be considered the root call for formatting. Span in this case refers to the range of -the sourcefile which should be formatted. - -The formatSpan then uses a scanner (either with or without JSX support) which starts at the highest -node the covers the span of text and recurses down through the node's children. - -As it recurses, `processNode` is called on the children setting the indentation is decided and passed -through into each of that node's children. - -The meat of formatting decisions is made via `processPair`, the pair here being the current node and the previous node. `processPair` which mutates the formatting context to represent the current place in the scanner and requests a set of rules which can be applied to the items via `createRulesMap`. - -There are a lot of rules, which you can find in [rules.ts](./rules.ts) each one has a left and right reference to nodes or token ranges and note of what action should be applied by the formatter. - -### Where is this used? - -The formatter is used mainly from any language service operation that inserts or modifies code. The formatter is not exported publicly, and so all usage can only come through the language server. +# How does TypeScript formatting work? + +To format code you need to have a formatting context and a `SourceFile`. The formatting context contains +all user settings like tab size, newline character, etc. + +The end result of formatting is represented by TextChange objects which hold the new string content, and +the text to replace it with. + +## Internals + +Most of the exposed APIs internally are `Format*` and they all set up and configure `FormatSpan` which could be considered the root call for formatting. Span in this case refers to the range of +the sourcefile which should be formatted. + +The formatSpan then uses a scanner (either with or without JSX support) which starts at the highest +node the covers the span of text and recurses down through the node's children. + +As it recurses, `processNode` is called on the children setting the indentation is decided and passed +through into each of that node's children. + +The meat of formatting decisions is made via `processPair`, the pair here being the current node and the previous node. `processPair` which mutates the formatting context to represent the current place in the scanner and requests a set of rules which can be applied to the items via `createRulesMap`. + +There are a lot of rules, which you can find in [rules.ts](./rules.ts) each one has a left and right reference to nodes or token ranges and note of what action should be applied by the formatter. + +### Where is this used? + +The formatter is used mainly from any language service operation that inserts or modifies code. The formatter is not exported publicly, and so all usage can only come through the language server. diff --git a/tsc/internal/lsp/lsproto/_generate/fetchModel.mts b/tsc/internal/lsp/lsproto/_generate/fetchModel.mts index 21957cfe5a607..f1c764e86130e 100755 --- a/tsc/internal/lsp/lsproto/_generate/fetchModel.mts +++ b/tsc/internal/lsp/lsproto/_generate/fetchModel.mts @@ -1,39 +1,39 @@ -#!/usr/bin/env -S node --experimental-strip-types - -// Usage: node --experimental-strip-types fetchModel.mts - -import fs from "node:fs"; -import path from "node:path"; -import url from "node:url"; - -const __filename = url.fileURLToPath(new URL(import.meta.url)); -const __dirname = path.dirname(__filename); - -const metaModelPath = path.join(__dirname, "metaModel.json"); -const metaModelSchemaPath = path.join(__dirname, "metaModelSchema.mts"); - -// Resolve the vscode-languageclient version from the root package-lock.json. -const lockfilePath = path.resolve(__dirname, "../../../../../package-lock.json"); -const lockfile = JSON.parse(fs.readFileSync(lockfilePath, "utf-8")); -const clientVersion: string = lockfile.packages["node_modules/vscode-languageclient"].version; - -const ref = `release/client/${clientVersion}`; -console.log(`Using vscode-languageclient@${clientVersion}`); - -const metaModelURL = `https://raw.githubusercontent.com/microsoft/vscode-languageserver-node/${ref}/protocol/metaModel.json`; -const metaModelSchemaURL = `https://raw.githubusercontent.com/microsoft/vscode-languageserver-node/${ref}/tools/src/metaModel.ts`; - -const metaModelResponse = await fetch(metaModelURL); -const metaModel = await metaModelResponse.text(); -fs.writeFileSync(metaModelPath, metaModel); - -const metaModelSchemaResponse = await fetch(metaModelSchemaURL); -let metaModelSchema = await metaModelSchemaResponse.text(); - -// Patch the schema to add omitzeroValue property to Property type -metaModelSchema = metaModelSchema.replace( - /(\t \* Whether the property is deprecated or not\. If deprecated\n\t \* the property contains the deprecation message\.\n\t \*\/\n\tdeprecated\?: string;)\n}/m, - `$1\n\n\t/**\n\t * Whether this property uses omitzero without being a pointer.\n\t * Custom extension for special value types.\n\t */\n\tomitzeroValue?: boolean;\n}`, -); - -fs.writeFileSync(metaModelSchemaPath, metaModelSchema); +#!/usr/bin/env -S node --experimental-strip-types + +// Usage: node --experimental-strip-types fetchModel.mts + +import fs from "node:fs"; +import path from "node:path"; +import url from "node:url"; + +const __filename = url.fileURLToPath(new URL(import.meta.url)); +const __dirname = path.dirname(__filename); + +const metaModelPath = path.join(__dirname, "metaModel.json"); +const metaModelSchemaPath = path.join(__dirname, "metaModelSchema.mts"); + +// Resolve the vscode-languageclient version from the root package-lock.json. +const lockfilePath = path.resolve(__dirname, "../../../../../package-lock.json"); +const lockfile = JSON.parse(fs.readFileSync(lockfilePath, "utf-8")); +const clientVersion: string = lockfile.packages["node_modules/vscode-languageclient"].version; + +const ref = `release/client/${clientVersion}`; +console.log(`Using vscode-languageclient@${clientVersion}`); + +const metaModelURL = `https://raw.githubusercontent.com/microsoft/vscode-languageserver-node/${ref}/protocol/metaModel.json`; +const metaModelSchemaURL = `https://raw.githubusercontent.com/microsoft/vscode-languageserver-node/${ref}/tools/src/metaModel.ts`; + +const metaModelResponse = await fetch(metaModelURL); +const metaModel = await metaModelResponse.text(); +fs.writeFileSync(metaModelPath, metaModel); + +const metaModelSchemaResponse = await fetch(metaModelSchemaURL); +let metaModelSchema = await metaModelSchemaResponse.text(); + +// Patch the schema to add omitzeroValue property to Property type +metaModelSchema = metaModelSchema.replace( + /(\t \* Whether the property is deprecated or not\. If deprecated\n\t \* the property contains the deprecation message\.\n\t \*\/\n\tdeprecated\?: string;)\n}/m, + `$1\n\n\t/**\n\t * Whether this property uses omitzero without being a pointer.\n\t * Custom extension for special value types.\n\t */\n\tomitzeroValue?: boolean;\n}`, +); + +fs.writeFileSync(metaModelSchemaPath, metaModelSchema); diff --git a/tsc/internal/lsp/lsproto/_generate/generate.mts b/tsc/internal/lsp/lsproto/_generate/generate.mts index b8466ff18d705..c1cbb43e83007 100755 --- a/tsc/internal/lsp/lsproto/_generate/generate.mts +++ b/tsc/internal/lsp/lsproto/_generate/generate.mts @@ -1,3619 +1,3619 @@ -#!/usr/bin/env -S node --experimental-strip-types - -// Usage: node --experimental-strip-types generate.mts - -import { $ } from "execa"; -import fs from "node:fs"; -import path from "node:path"; -import url from "node:url"; -import type { - Enumeration, - MetaModel, - Notification, - OrType, - Property, - ReferenceType, - Request, - Structure, - Type, - TypeAlias, -} from "./metaModelSchema.mts"; - -const __filename = url.fileURLToPath(new URL(import.meta.url)); -const __dirname = path.dirname(__filename); -const repoRoot = path.resolve(__dirname, "../../../.."); - -const out = path.resolve(__dirname, "../lsp_generated.go"); -const metaModelPath = path.resolve(__dirname, "metaModel.json"); - -if (!fs.existsSync(metaModelPath)) { - console.error("Meta model file not found; did you forget to run fetchModel.mjs?"); - process.exit(1); -} - -const model: MetaModel = JSON.parse(fs.readFileSync(metaModelPath, "utf-8")); - -// Custom structures to add to the model -const customStructures: Structure[] = [ - { - name: "InitializationOptions", - properties: [ - { - name: "disablePushDiagnostics", - type: { kind: "base", name: "boolean" }, - optional: true, - documentation: "DisablePushDiagnostics disables automatic pushing of diagnostics to the client.", - }, - { - name: "codeLensShowLocationsCommandName", - type: { kind: "base", name: "string" }, - optional: true, - documentation: "The client-side command name that resolved references/implementations `CodeLens` should trigger. Arguments passed will be `(DocumentUri, Position, Location[])`.", - }, - { - name: "userPreferences", - type: { kind: "reference", name: "any" }, - optional: true, - documentation: "userPreferences and/or formatting options if provided at initialization.", - }, - { - name: "enableTelemetry", - type: { kind: "base", name: "boolean" }, - optional: true, - documentation: "EnableTelemetry enables sending telemetry events from the server to the client.", - }, - { - name: "logVerbosity", - type: { kind: "reference", name: "LogVerbosity" }, - optional: true, - documentation: "The initial log verbosity level, matching the client's output channel log level at startup. Subsequent changes are sent via custom/setLogVerbosity.", - }, - { - name: "runExternalCode", - type: { kind: "base", name: "boolean" }, - optional: true, - documentation: "RunExternalCode allows configured content mappers to launch external plugin processes. The client should set this only for trusted workspaces. It mirrors the --runExternalCode CLI flag.", - }, - { - name: "trackFlakyDiagnostics", - type: { kind: "reference", name: "DiagnosticFlakeLogLevel" }, - optional: true, - documentation: "The level at which we track flaky diagnostics, if at all.", - }, - ], - documentation: "InitializationOptions contains user-provided initialization options.", - }, - { - name: "AutoImportFix", - properties: [ - { - name: "kind", - type: { kind: "reference", name: "AutoImportFixKind" }, - omitzeroValue: true, - }, - { - name: "name", - type: { kind: "base", name: "string" }, - omitzeroValue: true, - }, - { - name: "importKind", - type: { kind: "reference", name: "ImportKind" }, - }, - { - name: "useRequire", - type: { kind: "base", name: "boolean" }, - omitzeroValue: true, - }, - { - name: "addAsTypeOnly", - type: { kind: "reference", name: "AddAsTypeOnly" }, - }, - { - name: "moduleSpecifier", - type: { kind: "base", name: "string" }, - documentation: "The module specifier for this auto-import.", - omitzeroValue: true, - }, - { - name: "importIndex", - type: { kind: "base", name: "integer" }, - documentation: "Index of the import to modify when adding to an existing import declaration.", - }, - { - name: "usagePosition", - type: { kind: "reference", name: "Position" }, - optional: true, - }, - { - name: "namespacePrefix", - type: { kind: "base", name: "string" }, - omitzeroValue: true, - }, - ], - documentation: "AutoImportFix contains information about an auto-import suggestion.", - }, - { - name: "CompletionItemData", - properties: [ - { - name: "fileName", - type: { kind: "base", name: "string" }, - documentation: "The file name where the completion was requested.", - omitzeroValue: true, - }, - { - name: "position", - type: { kind: "base", name: "integer" }, - documentation: "The position where the completion was requested.", - omitzeroValue: true, - }, - { - name: "supplementalFileIndex", - type: { kind: "base", name: "integer" }, - optional: true, - documentation: "Zero-based index into the canonical file's supplemental source files. Absent when the completion was requested in the canonical file.", - }, - { - name: "source", - type: { kind: "base", name: "string" }, - documentation: "Special source value for disambiguation.", - omitzeroValue: true, - }, - { - name: "name", - type: { kind: "base", name: "string" }, - documentation: "The name of the completion item.", - omitzeroValue: true, - }, - { - name: "autoImport", - type: { kind: "reference", name: "AutoImportFix" }, - optional: true, - documentation: "Auto-import data for this completion item.", - }, - { - name: "isImportStatementCompletion", - type: { kind: "base", name: "boolean" }, - omitzeroValue: true, - }, - ], - documentation: "CompletionItemData is preserved on a CompletionItem between CompletionRequest and CompletionResolveRequest.", - }, - { - name: "CodeLensData", - properties: [ - { - name: "kind", - type: { kind: "reference", name: "CodeLensKind" }, - documentation: `The kind of the code lens ("references" or "implementations").`, - }, - { - name: "uri", - type: { kind: "base", name: "DocumentUri" }, - documentation: `The document in which the code lens and its range are located.`, - }, - { - name: "position", - type: { kind: "base", name: "integer" }, - documentation: `The position of the code lens declaration in its virtual source file.`, - }, - { - name: "supplementalFileIndex", - type: { kind: "base", name: "integer" }, - optional: true, - documentation: `Zero-based index into the canonical file's supplemental source files. Absent for the canonical source file.`, - }, - ], - }, - { - name: "ExperimentalServerCapabilities", - properties: [ - { - name: "customSourceDefinitionProvider", - type: { kind: "base", name: "boolean" }, - optional: true, - documentation: "The server provides source definition support via custom/textDocument/sourceDefinition.", - }, - { - name: "customMultiDocumentHighlightProvider", - type: { kind: "base", name: "boolean" }, - optional: true, - documentation: "The server provides multi-document highlight support via custom/textDocument/multiDocumentHighlight.", - }, - ], - documentation: "ExperimentalServerCapabilities contains experimental capabilities under development.", - }, - { - name: "ExperimentalClientCapabilities", - properties: [ - { - name: "hoverVerbosityLevel", - type: { kind: "base", name: "boolean" }, - optional: true, - documentation: "The client supports hover verbosityLevel requests and canIncreaseVerbosity responses.", - }, - ], - documentation: "ExperimentalClientCapabilities contains experimental capabilities under development.", - }, - { - name: "VSOnAutoInsertOptions", - properties: [ - { - name: "_vs_triggerCharacters", - type: { kind: "array", element: { kind: "base", name: "string" } }, - documentation: "List of trigger characters that trigger auto-insert.", - }, - ], - documentation: "Options for the textDocument/_vs_onAutoInsert provider capability.", - }, - { - name: "VSReferenceItem", - properties: [ - { - name: "_vs_id", - type: { kind: "base", name: "integer" }, - documentation: "Unique identifier for this reference item.", - }, - { - name: "_vs_definitionId", - type: { kind: "base", name: "integer" }, - optional: true, - documentation: "The ID of the definition item this reference belongs to. Absent for definition items themselves.", - }, - { - name: "_vs_kind", - type: { kind: "array", element: { kind: "reference", name: "VSReferenceKind" } }, - optional: true, - documentation: "The kind(s) of this reference (read, write, etc.).", - }, - { - name: "_vs_location", - type: { kind: "reference", name: "Location" }, - documentation: "The location of this reference.", - }, - { - name: "_vs_definitionText", - type: { kind: "reference", name: "VSClassifiedTextElement" }, - optional: true, - documentation: "Classified display text for the definition (used for grouping headers in the UI).", - }, - { - name: "_vs_projectName", - type: { kind: "base", name: "string" }, - optional: true, - documentation: "The project name for this reference.", - }, - { - name: "_vs_containingType", - type: { kind: "base", name: "string" }, - optional: true, - documentation: "The containing type for this reference.", - }, - ], - documentation: "A VS-specific reference item with grouping support for Find All References.", - }, - { - name: "VSOnAutoInsertParams", - properties: [ - { - name: "_vs_textDocument", - type: { kind: "reference", name: "TextDocumentIdentifier" }, - documentation: "The text document.", - }, - { - name: "_vs_position", - type: { kind: "reference", name: "Position" }, - documentation: "The position inside the text document.", - }, - { - name: "_vs_ch", - type: { kind: "base", name: "string" }, - documentation: "The character that triggered the auto-insert.", - }, - ], - documentation: "Parameters for the textDocument/_vs_onAutoInsert request.", - }, - { - name: "VSOnAutoInsertResponseItem", - properties: [ - { - name: "_vs_textEditFormat", - type: { kind: "reference", name: "InsertTextFormat" }, - documentation: "The format of the text edit (plaintext or snippet).", - }, - { - name: "_vs_textEdit", - type: { kind: "reference", name: "TextEdit" }, - documentation: "The text edit to apply for the auto-insertion.", - }, - ], - documentation: "Response item for the textDocument/_vs_onAutoInsert request.", - }, - { - name: "RequestFailureTelemetryEvent", - properties: [ - { - name: "eventName", - type: { kind: "stringLiteral", value: "languageServer.errorResponse" }, - documentation: "The name of the telemetry event.", - }, - { - name: "telemetryPurpose", - type: { kind: "stringLiteral", value: "error" }, - documentation: "Indicates whether the reason for generating the event (e.g. general usage telemetry or errors).", - }, - { - name: "properties", - type: { kind: "reference", name: "RequestFailureTelemetryProperties" }, - documentation: "The properties associated with the event.", - }, - ], - documentation: "A RequestFailureTelemetryEvent is sent when a request fails and the server recovers.", - }, - { - name: "RequestFailureTelemetryProperties", - properties: [ - { - name: "errorCode", - type: { kind: "base", name: "string" }, - documentation: "The error code associated with the event.", - }, - { - name: "requestMethod", - type: { kind: "base", name: "string" }, - documentation: "The method of the request that caused the event.", - }, - { - name: "stack", - type: { kind: "base", name: "string" }, - documentation: "The stack trace associated with the event.", - }, - ], - documentation: "RequestFailureTelemetryProperties contains failure information when an LSP request manages to recover.", - }, - { - name: "ProfileParams", - properties: [ - { - name: "dir", - type: { kind: "base", name: "string" }, - documentation: "The directory path where the profile should be saved.", - }, - ], - documentation: "Parameters for profiling requests.", - }, - { - name: "ProfileResult", - properties: [ - { - name: "file", - type: { kind: "base", name: "string" }, - documentation: "The file path where the profile was saved.", - }, - ], - documentation: "Result of a profiling request.", - }, - { - name: "InitializeAPISessionParams", - properties: [ - { - name: "pipe", - type: { kind: "base", name: "string" }, - optional: true, - documentation: "Optional path to use for the named pipe or Unix domain socket. If not provided, a unique path will be generated.", - }, - ], - documentation: "Parameters for the initializeAPISession request.", - }, - { - name: "InitializeAPISessionResult", - properties: [ - { - name: "sessionId", - type: { kind: "base", name: "string" }, - documentation: "The unique identifier for this API session.", - }, - { - name: "pipe", - type: { kind: "base", name: "string" }, - documentation: "The path to the named pipe or Unix domain socket for API communication.", - }, - ], - documentation: "Result for the initializeAPISession request.", - }, - { - name: "ProjectInfoParams", - properties: [ - { - name: "textDocument", - type: { kind: "reference", name: "TextDocumentIdentifier" }, - documentation: "The text document to get project info for.", - }, - ], - documentation: "Parameters for the custom/projectInfo request.", - }, - { - name: "ProjectInfoResult", - properties: [ - { - name: "configFilePath", - type: { kind: "base", name: "string" }, - documentation: "The absolute path to the config file (e.g. /path/to/tsconfig.json) for the project that contains this file, or an empty string if the file is in an inferred project.", - }, - ], - documentation: "Result for the custom/projectInfo request.", - }, - { - name: "ContentMapperManifest", - properties: [ - { name: "name", type: { kind: "base", name: "string" }, documentation: "Human-readable mapper name." }, - { name: "version", type: { kind: "base", name: "string" }, optional: true, documentation: "Mapper version." }, - { name: "exec", type: { kind: "array", element: { kind: "base", name: "string" } }, documentation: "Executable and arguments used to start the mapper." }, - { name: "cwd", type: { kind: "base", name: "string" }, optional: true, documentation: "Absolute working directory for the mapper process." }, - { name: "compilerOptions", type: { kind: "array", element: { kind: "base", name: "string" } }, optional: true, documentation: "Compiler option names forwarded to the mapper." }, - { name: "dynamicConfig", type: { kind: "base", name: "boolean" }, optional: true, documentation: "Whether the mapper uses project-scoped dynamic configuration." }, - ], - documentation: "Inline content mapper manifest supplied by a contributing extension.", - }, - { - name: "InferredProjectContentMapperContribution", - properties: [ - { name: "options", type: { kind: "reference", name: "LSPObject" }, optional: true, documentation: "Options supplied to transforms in inferred projects." }, - { name: "manifest", type: { kind: "reference", name: "ContentMapperManifest" }, documentation: "Inline manifest for the mapper contributed to inferred projects." }, - ], - documentation: "Content mapper configuration contributed to inferred projects.", - }, - { - name: "ContentMapperContribution", - properties: [ - { name: "contributorId", type: { kind: "base", name: "string" }, documentation: "Unique identifier of the contributor extension." }, - { name: "extensions", type: { kind: "array", element: { kind: "base", name: "string" } }, documentation: "File extensions handled by this content mapper." }, - { name: "inferredProjectContribution", type: { kind: "reference", name: "InferredProjectContentMapperContribution" }, optional: true, documentation: "When present, contributes this mapper to inferred projects." }, - ], - documentation: "One extension-provided content mapper contribution.", - }, - { - name: "SetContentMapperContributionsParams", - properties: [ - { name: "contributions", type: { kind: "array", element: { kind: "reference", name: "ContentMapperContribution" } }, documentation: "Complete replacement set of active extension contributions." }, - { name: "openDocuments", type: { kind: "array", element: { kind: "reference", name: "TextDocumentIdentifier" } }, documentation: "Currently open documents matching contributed extensions." }, - ], - documentation: "Parameters for the custom/setContentMapperContributions request.", - }, - { - name: "SetLogVerbosityParams", - properties: [ - { - name: "verbosity", - type: { kind: "reference", name: "LogVerbosity" }, - documentation: "The log verbosity level.", - }, - ], - documentation: "Parameters for the custom/setLogVerbosity notification.", - }, - { - name: "PerformanceStatsTelemetryEvent", - properties: [ - { - name: "eventName", - type: { kind: "stringLiteral", value: "languageServer.performanceStats" }, - documentation: "The name of the telemetry event.", - }, - { - name: "telemetryPurpose", - type: { kind: "stringLiteral", value: "usage" }, - documentation: "Indicates this is a usage telemetry event.", - }, - { - name: "measurements", - type: { kind: "reference", name: "PerformanceStatsTelemetryMeasurements" }, - documentation: "Numeric measurements for this telemetry event.", - }, - ], - documentation: "A PerformanceStatsTelemetryEvent is sent periodically with performance and resource usage statistics.", - }, - { - name: "PerformanceStatsTelemetryMeasurements", - properties: [ - { name: "openFileCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Number of files currently open in the editor." }, - { name: "uptimeSeconds", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Seconds since the session was initialized." }, - { name: "projectCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Number of loaded projects." }, - { name: "configCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Number of loaded config files." }, - { name: "cachedDiskFileCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Number of files cached from disk." }, - { name: "memoryUsedBytes", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Total memory mapped by the Go runtime in bytes." }, - { name: "goMemLimit", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "GOMEMLIMIT value in bytes, or 0 if not set." }, - { name: "goGCPercent", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "GOGC percentage value configured for the GC." }, - { name: "heapGoalBytes", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Heap size target the GC is working toward in bytes." }, - { name: "heapLiveBytes", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Bytes of live (reachable) heap objects." }, - { name: "heapObjectCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Number of live or unswept objects occupying heap memory." }, - { name: "heapStackBytes", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Heap memory reserved for goroutine stacks." }, - { name: "heapReleasedBytes", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Heap memory returned to the OS." }, - { name: "heapFreeBytes", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Heap memory that is free and eligible to be returned to the OS." }, - { name: "gcScanHeapBytes", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Total scannable heap bytes — how much the GC must traverse." }, - { name: "goMaxProcs", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "The current GOMAXPROCS value." }, - { name: "goroutineCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Current number of goroutines." }, - { name: "gcCyclesTotal", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Total completed GC cycles." }, - { name: "gcCPUSeconds", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Cumulative CPU time spent in GC in seconds." }, - { name: "userCPUSeconds", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Cumulative CPU time spent in user Go code in seconds." }, - { name: "systemMemTotal", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Total physical memory on the system in bytes." }, - { name: "systemMemUsed", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Used physical memory on the system in bytes." }, - { name: "autoImportProjectBucketCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Number of auto-import project buckets." }, - { name: "autoImportNodeModulesBucketCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Number of auto-import node_modules buckets." }, - { name: "autoImportUniquePackageCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Unique packages across all node_modules buckets." }, - { name: "autoImportProjectExportCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Total indexed exports from project files." }, - { name: "autoImportNodeModulesExportCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Total indexed exports from node_modules." }, - { name: "autoImportProjectFileCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Total files tracked across project buckets." }, - { name: "autoImportNodeModulesFileCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Total files tracked across node_modules buckets." }, - { name: "autoImportNodeModulesUnfilteredBucketCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Number of node_modules buckets with no package.json filter." }, - ], - documentation: "Numeric measurements for PerformanceStatsTelemetryEvent.", - }, - { - name: "ProjectInfoTelemetryEvent", - properties: [ - { - name: "eventName", - type: { kind: "stringLiteral", value: "languageServer.projectInfo" }, - documentation: "The name of the telemetry event.", - }, - { - name: "telemetryPurpose", - type: { kind: "stringLiteral", value: "usage" }, - documentation: "Indicates this is a usage telemetry event.", - }, - { - name: "properties", - type: { kind: "map", key: { kind: "base", name: "string" }, value: { kind: "base", name: "string" } }, - documentation: "String properties for this telemetry event. Complex values (compilerOptions, fileStats) are JSON-stringified.", - }, - { - name: "measurements", - type: { kind: "reference", name: "ProjectInfoTelemetryMeasurements" }, - documentation: "Numeric measurements for this telemetry event.", - }, - ], - documentation: "A ProjectInfoTelemetryEvent is sent once per project when it is first loaded.", - }, - { - name: "ProjectInfoTelemetryMeasurements", - properties: [ - { name: "jsFileCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true }, - { name: "jsFileSize", type: { kind: "base", name: "decimal" }, omitzeroValue: true }, - { name: "jsxFileCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true }, - { name: "jsxFileSize", type: { kind: "base", name: "decimal" }, omitzeroValue: true }, - { name: "tsFileCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true }, - { name: "tsFileSize", type: { kind: "base", name: "decimal" }, omitzeroValue: true }, - { name: "tsxFileCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true }, - { name: "tsxFileSize", type: { kind: "base", name: "decimal" }, omitzeroValue: true }, - { name: "dtsFileCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true }, - { name: "dtsFileSize", type: { kind: "base", name: "decimal" }, omitzeroValue: true }, - ], - documentation: "Numeric measurements for ProjectInfoTelemetryEvent.", - }, - { - name: "MultiDocumentHighlight", - properties: [ - { - name: "uri", - type: { kind: "base", name: "DocumentUri" }, - documentation: "The URI of the document containing the highlights.", - }, - { - name: "highlights", - type: { kind: "array", element: { kind: "reference", name: "DocumentHighlight" } }, - documentation: "The highlights for the document.", - }, - ], - documentation: "Represents a collection of document highlights from a single document, used in multi-document highlight responses.", - }, - { - name: "MultiDocumentHighlightParams", - properties: [ - { - name: "textDocument", - type: { kind: "reference", name: "TextDocumentIdentifier" }, - documentation: "The text document.", - }, - { - name: "position", - type: { kind: "reference", name: "Position" }, - documentation: "The position inside the text document.", - }, - { - name: "filesToSearch", - type: { kind: "array", element: { kind: "base", name: "DocumentUri" } }, - documentation: "The list of file URIs to search for highlights across.", - }, - ], - documentation: "Parameters for the custom/textDocument/multiDocumentHighlight request.", - }, - { - name: "VSClassifiedTextRun", - properties: [ - { - name: "ClassificationTypeName", - type: { kind: "base", name: "string" }, - documentation: "The classification type name (e.g. 'keyword', 'class name', 'parameter name').", - }, - { - name: "Text", - type: { kind: "base", name: "string" }, - documentation: "The text content of this run.", - }, - { - name: "MarkerTagType", - type: { kind: "base", name: "string" }, - optional: true, - documentation: "Optional marker tag type.", - }, - { - name: "Style", - type: { kind: "base", name: "integer" }, - optional: true, - omitzeroValue: true, - documentation: "The style of this text run.", - }, - { - name: "_vs_type", - type: { kind: "stringLiteral", value: "ClassifiedTextRun" }, - documentation: "VS type discriminator required by ObjectContentConverter for deserialization.", - }, - ], - documentation: "A classified text run with text and classification type, used for colorized display in VS.", - }, - { - name: "VSClassifiedTextElement", - properties: [ - { - name: "Runs", - type: { kind: "array", element: { kind: "reference", name: "VSClassifiedTextRun" } }, - documentation: "The classified text runs that make up this element.", - }, - { - name: "_vs_type", - type: { kind: "stringLiteral", value: "ClassifiedTextElement" }, - documentation: "VS type discriminator required by ObjectContentConverter for deserialization.", - }, - ], - documentation: "A classified text element containing an array of classified text runs, used for colorized labels in VS.", - }, - { - name: "VSImageId", - properties: [ - { - name: "Guid", - type: { kind: "base", name: "string" }, - documentation: "The GUID of the image catalog containing this image.", - }, - { - name: "Id", - type: { kind: "base", name: "integer" }, - documentation: "The numeric identifier of the image within its catalog.", - }, - { - name: "_vs_type", - type: { kind: "stringLiteral", value: "ImageId" }, - documentation: "VS type discriminator required by ObjectContentConverter for deserialization.", - }, - ], - documentation: "Identifies an image in a VS image catalog. Used to render symbol-kind icons (e.g. in hover tooltips).", - }, - { - name: "VSImageElement", - properties: [ - { - name: "ImageId", - type: { kind: "reference", name: "VSImageId" }, - documentation: "The image to display.", - }, - { - name: "_vs_type", - type: { kind: "stringLiteral", value: "ImageElement" }, - documentation: "VS type discriminator required by ObjectContentConverter for deserialization.", - }, - ], - documentation: "An image element (e.g. a symbol-kind icon) for use in VS rich content such as hover tooltips.", - }, - { - name: "VSContainerElement", - properties: [ - { - name: "Style", - type: { kind: "reference", name: "VSContainerElementStyle" }, - documentation: "Layout style for the child elements.", - }, - { - name: "Elements", - type: { - kind: "array", - element: { - kind: "or", - items: [ - { kind: "reference", name: "VSImageElement" }, - { kind: "reference", name: "VSClassifiedTextElement" }, - { kind: "reference", name: "VSContainerElement" }, - ], - }, - }, - documentation: "The child elements contained within this container.", - }, - { - name: "_vs_type", - type: { kind: "stringLiteral", value: "ContainerElement" }, - documentation: "VS type discriminator required by ObjectContentConverter for deserialization.", - }, - ], - documentation: "A container element that groups other VS rich-content elements (images, classified text, or nested containers). Used to build the VS hover raw content that combines a symbol icon with colorized text.", - }, -]; - -const customEnumerations: Enumeration[] = [ - { - name: "VSContainerElementStyle", - type: { kind: "base", name: "integer" }, - values: [ - { name: "Wrapped", value: 0, documentation: "Child elements are laid out inline, wrapping as needed (e.g. an icon next to a signature line)." }, - { name: "Stacked", value: 1, documentation: "Child elements are stacked vertically, each on its own line (e.g. a signature line followed by documentation)." }, - ], - documentation: "Layout style for a VSContainerElement's children, mirroring VS's Microsoft.VisualStudio.Text.Adornments.ContainerElementStyle.", - }, - { - name: "LogVerbosity", - type: { kind: "base", name: "integer" }, - values: [ - { name: "Off", value: 0, documentation: "All logging disabled." }, - { name: "Trace", value: 1, documentation: "Most verbose; includes LSP request/response traces." }, - { name: "Debug", value: 2, documentation: "Verbose server logs." }, - { name: "Info", value: 3, documentation: "Normal server logs." }, - { name: "Warning", value: 4, documentation: "Warnings only." }, - { name: "Error", value: 5, documentation: "Errors only." }, - ], - documentation: "Log verbosity level, mirroring the VS Code LogLevel enum values.", - }, - { - name: "DiagnosticFlakeLogLevel", - type: { kind: "base", name: "integer" }, - values: [ - { name: "Off", value: 0, documentation: "All flake logging disabled." }, - { name: "Log", value: 1, documentation: "Log flaky diagnostics to the error log." }, - { name: "Panic", value: 2, documentation: "Panic on flaky diagnostics." }, - ], - documentation: "Behavior for tracking and logging flaky diagnostics.", - }, - { - name: "VSReferenceKind", - type: { kind: "base", name: "integer" }, - values: [ - { name: "Inactive", value: 0 }, - { name: "Comment", value: 1 }, - { name: "String", value: 2 }, - { name: "Read", value: 3 }, - { name: "Write", value: 4 }, - { name: "Reference", value: 5 }, - { name: "Name", value: 6 }, - { name: "Qualified", value: 7 }, - { name: "TypeArgument", value: 8 }, - { name: "TypeConstraint", value: 9 }, - { name: "BaseType", value: 10 }, - { name: "Constructor", value: 11 }, - { name: "Destructor", value: 12 }, - { name: "Import", value: 13 }, - { name: "Declaration", value: 14 }, - { name: "AddressOf", value: 15 }, - { name: "NotReference", value: 16 }, - { name: "Unknown", value: 17 }, - ], - }, - { - name: "CodeLensKind", - type: { - kind: "base", - name: "string", - }, - values: [ - { - name: "References", - value: "references", - }, - { - name: "Implementations", - value: "implementations", - }, - ], - }, - { - name: "AutoImportFixKind", - type: { kind: "base", name: "integer" }, - values: [ - { name: "UseNamespace", value: 0, documentation: "Augment an existing namespace import." }, - { name: "JsdocTypeImport", value: 1, documentation: "Add a JSDoc-only type import." }, - { name: "AddToExisting", value: 2, documentation: "Insert into an existing import declaration." }, - { name: "AddNew", value: 3, documentation: "Create a fresh import statement." }, - { name: "PromoteTypeOnly", value: 4, documentation: "Promote a type-only import when necessary." }, - ], - }, - { - name: "ImportKind", - type: { kind: "base", name: "integer" }, - values: [ - { name: "Named", value: 0, documentation: "Adds a named import." }, - { name: "Default", value: 1, documentation: "Adds a default import." }, - { name: "Namespace", value: 2, documentation: "Adds a namespace import." }, - { name: "CommonJS", value: 3, documentation: "Adds a CommonJS import assignment." }, - ], - }, - { - name: "AddAsTypeOnly", - type: { kind: "base", name: "integer" }, - values: [ - { name: "Allowed", value: 1, documentation: "Import may be marked type-only if needed." }, - { name: "Required", value: 2, documentation: "Import must be marked type-only." }, - { name: "NotAllowed", value: 4, documentation: "Import cannot be marked type-only." }, - ], - }, - { - name: "ClassificationTypeName", - type: { kind: "base", name: "string" }, - values: [ - { name: "Keyword", value: "keyword", documentation: "Language keyword (e.g., function, const, class)." }, - { name: "Punctuation", value: "punctuation", documentation: "Punctuation characters (e.g., parentheses, commas, semicolons)." }, - { name: "Operator", value: "operator", documentation: "Operators (e.g., =, +, ?)." }, - { name: "WhiteSpace", value: "whitespace", documentation: "Whitespace including spaces and line breaks." }, - { name: "Text", value: "text", documentation: "Plain text with no special classification." }, - { name: "String", value: "string", documentation: "String and literal values." }, - { name: "Number", value: "number", documentation: "Numeric literal values." }, - { name: "Comment", value: "comment", documentation: "Comment text." }, - { name: "ClassName", value: "class name", documentation: "Class names." }, - { name: "InterfaceName", value: "interface name", documentation: "Interface names." }, - { name: "EnumName", value: "enum name", documentation: "Enum names." }, - { name: "ModuleName", value: "module name", documentation: "Module/namespace names." }, - { name: "MethodName", value: "method name", documentation: "Method and function names." }, - { name: "ParameterName", value: "parameter name", documentation: "Parameter names." }, - { name: "PropertyName", value: "property name", documentation: "Property and accessor names." }, - { name: "FieldName", value: "field name", documentation: "Field names (e.g., enum members)." }, - { name: "LocalName", value: "local name", documentation: "Local variable names." }, - { name: "TypeParameterName", value: "type parameter name", documentation: "Type parameter names." }, - { name: "Identifier", value: "identifier", documentation: "General identifiers (e.g., type aliases, imports)." }, - ], - documentation: "Roslyn classification type names used by VS for syntax coloring in tooltips and other UI elements.", - }, -]; -const customRequests: Request[] = [ - { - method: "custom/runGC", - typeName: "RunGCRequest", - messageDirection: "clientToServer", - result: { kind: "base", name: "null" }, - documentation: "Triggers garbage collection in the language server.", - }, - { - method: "custom/saveHeapProfile", - typeName: "SaveHeapProfileRequest", - params: { kind: "reference", name: "ProfileParams" }, - messageDirection: "clientToServer", - result: { kind: "reference", name: "ProfileResult" }, - documentation: "Saves a heap profile to the specified directory.", - }, - { - method: "custom/saveAllocProfile", - typeName: "SaveAllocProfileRequest", - params: { kind: "reference", name: "ProfileParams" }, - messageDirection: "clientToServer", - result: { kind: "reference", name: "ProfileResult" }, - documentation: "Saves an allocation profile to the specified directory.", - }, - { - method: "custom/startCPUProfile", - typeName: "StartCPUProfileRequest", - params: { kind: "reference", name: "ProfileParams" }, - messageDirection: "clientToServer", - result: { kind: "base", name: "null" }, - documentation: "Starts CPU profiling, writing to the specified directory when stopped.", - }, - { - method: "custom/stopCPUProfile", - typeName: "StopCPUProfileRequest", - messageDirection: "clientToServer", - result: { kind: "reference", name: "ProfileResult" }, - documentation: "Stops CPU profiling and saves the profile.", - }, - { - method: "custom/initializeAPISession", - typeName: "CustomInitializeAPISessionRequest", - params: { kind: "reference", name: "InitializeAPISessionParams" }, - result: { kind: "reference", name: "InitializeAPISessionResult" }, - messageDirection: "clientToServer", - documentation: "Custom request to initialize an API session.", - }, - { - method: "custom/projectInfo", - typeName: "CustomProjectInfoRequest", - params: { kind: "reference", name: "ProjectInfoParams" }, - result: { kind: "reference", name: "ProjectInfoResult" }, - messageDirection: "clientToServer", - documentation: "Returns project information (e.g. the tsconfig.json path) for a given text document.", - }, - { - method: "custom/setContentMapperContributions", - typeName: "CustomSetContentMapperContributionsRequest", - params: { kind: "reference", name: "SetContentMapperContributionsParams" }, - result: { kind: "base", name: "null" }, - messageDirection: "clientToServer", - documentation: "Replaces extension content mapper contributions and discovers configured mappers for matching open documents.", - }, - { - method: "custom/textDocument/sourceDefinition", - typeName: "CustomTextDocumentSourceDefinitionRequest", - params: { kind: "reference", name: "TextDocumentPositionParams" }, - result: { kind: "reference", name: "LocationOrLocationsOrDefinitionLinksOrNull" }, - messageDirection: "clientToServer", - documentation: "Request to get source definitions for a position.", - }, - { - method: "custom/textDocument/multiDocumentHighlight", - typeName: "CustomMultiDocumentHighlightRequest", - params: { kind: "reference", name: "MultiDocumentHighlightParams" }, - result: { - kind: "or", - items: [ - { kind: "array", element: { kind: "reference", name: "MultiDocumentHighlight" } }, - { kind: "base", name: "null" }, - ], - }, - messageDirection: "clientToServer", - documentation: "Request to get document highlights across multiple files.", - }, - { - method: "textDocument/_vs_onAutoInsert", - typeName: "VSOnAutoInsertRequest", - params: { kind: "reference", name: "VSOnAutoInsertParams" }, - result: { - kind: "or", - items: [ - { kind: "reference", name: "VSOnAutoInsertResponseItem" }, - { kind: "base", name: "null" }, - ], - }, - messageDirection: "clientToServer", - documentation: "Request for auto-insert when a trigger character is typed (VS-specific).", - }, - { - method: "textDocument/_vs_references", - typeName: "VSReferencesRequest", - params: { kind: "reference", name: "ReferenceParams" }, - result: { - kind: "or", - items: [ - { kind: "array", element: { kind: "reference", name: "VSReferenceItem" } }, - { kind: "base", name: "null" }, - ], - }, - messageDirection: "clientToServer", - documentation: "VS-specific request for Find All References with grouped reference items.", - }, -]; - -const customNotifications: Notification[] = [ - { - method: "custom/setLogVerbosity", - typeName: "CustomSetLogVerbosityNotification", - params: { kind: "reference", name: "SetLogVerbosityParams" }, - messageDirection: "clientToServer", - documentation: "Notification to set the server's log verbosity level based on the output channel's log level.", - }, -]; - -// compareStructures is the set of generated structures for which a Compare method should be emitted. -// The Compare method defines a total ordering by comparing fields in declaration order. -// All listed structures (and any structure-typed fields they reference) must contain only -// comparable fields: base scalar types, or other structures that are themselves in this set. -const compareStructures = new Set([ - "Position", - "Range", - "TextEdit", -]); - -const customTypeAliases: TypeAlias[] = [ - { - name: "TelemetryEvent", - type: { - kind: "or", - items: [ - { kind: "reference", name: "RequestFailureTelemetryEvent" }, - { kind: "reference", name: "PerformanceStatsTelemetryEvent" }, - { kind: "reference", name: "ProjectInfoTelemetryEvent" }, - { kind: "base", name: "null" }, - ], - }, - }, -]; - -// Track which custom Data structures were declared explicitly -const explicitDataStructures = new Set(customStructures.map(s => s.name)); - -// Map from registration method → { fieldName, optionsTypeName } -// Built during patchAndPreprocessModel, used during code generation. -interface RegistrationMethodInfo { - registrationMethod: string; - fieldName: string; - optionsTypeName: string; - isRegistrationOnly?: boolean; -} -let registrationMethods: RegistrationMethodInfo[] = []; - -// Patch and preprocess the model -function patchAndPreprocessModel() { - // Track which Data types we need to create as placeholders - const neededDataStructures = new Set(); - - // Collect all registration option types from requests and notifications - const registrationOptionTypes: Type[] = []; - for (const request of [...model.requests, ...model.notifications]) { - if (request.registrationOptions) { - registrationOptionTypes.push(request.registrationOptions); - } - } - - // Create synthetic structures for "and" types in registration options - const syntheticStructures: Structure[] = []; - for (let i = 0; i < registrationOptionTypes.length; i++) { - const regOptType = registrationOptionTypes[i]; - if (regOptType.kind === "and") { - // Find which request/notification this registration option belongs to - const owner = [...model.requests, ...model.notifications].find(r => r.registrationOptions === regOptType); - if (!owner) { - throw new Error("Could not find owner for 'and' type registration option"); - } - - // Determine the proper name based on the typeName or method - let structureName: string; - if (owner.typeName) { - // Use typeName as base: "ColorPresentationRequest" -> "ColorPresentationRegistrationOptions" - structureName = owner.typeName.replace(/Request$/, "").replace(/Notification$/, "") + "RegistrationOptions"; - } - else { - // Fall back to method: "textDocument/colorPresentation" -> "ColorPresentationRegistrationOptions" - const methodParts = owner.method.split("/"); - const lastPart = methodParts[methodParts.length - 1]; - structureName = titleCase(lastPart) + "RegistrationOptions"; - } - - // Extract all reference types from the "and" - const refTypes = regOptType.items.filter((item): item is ReferenceType => item.kind === "reference"); - - // Create a synthetic structure that combines all the referenced structures - syntheticStructures.push({ - name: structureName, - properties: [], - extends: refTypes, - documentation: `Registration options for ${owner.method}.`, - }); - - // Replace the "and" type with a reference to the synthetic structure - registrationOptionTypes[i] = { kind: "reference", name: structureName }; - // Also update the model so the request/notification has the resolved type - owner.registrationOptions = registrationOptionTypes[i]; - } - } - - for (const structure of model.structures) { - // Patch ServerCapabilities to add custom tsgo capability flags - if (structure.name === "ServerCapabilities") { - structure.properties.push({ - name: "_vs_onAutoInsertProvider", - type: { kind: "reference", name: "VSOnAutoInsertOptions" }, - optional: true, - documentation: "Provider options for the VS auto-insert feature via textDocument/_vs_onAutoInsert.", - }); - structure.properties.push({ - name: "_vs_referencesProvider", - type: { kind: "base", name: "boolean" }, - optional: true, - documentation: "The server provides VS-specific grouped references via textDocument/_vs_references.", - }); - } - - // Patch HoverParams to add verbosityLevel - if (structure.name === "HoverParams") { - structure.properties.push({ - name: "verbosityLevel", - type: { kind: "base", name: "integer" }, - optional: true, - documentation: "Controls how many levels of type definitions will be expanded. Default is 0.", - }); - } - - // Patch WorkspaceSymbolParams to optionally scope the search to projects - // containing a document, matching Strada's currentProject mode. - if (structure.name === "WorkspaceSymbolParams") { - structure.properties.push({ - name: "textDocument", - type: { kind: "reference", name: "TextDocumentIdentifier" }, - optional: true, - documentation: "Scopes the workspace symbol search to projects containing this document.", - }); - } - - // Patch Hover to add canIncreaseVerbosity - if (structure.name === "Hover") { - structure.properties.push( - { - name: "canIncreaseVerbosity", - type: { kind: "base", name: "boolean" }, - omitzeroValue: true, - documentation: "Whether the verbosity level can be increased for this hover.", - }, - { - name: "_vs_rawContent", - type: { kind: "reference", name: "VSContainerElement" }, - optional: true, - documentation: "VS-specific rich content (symbol icon + colorized/classified text) rendered by clients that support Visual Studio extensions, in place of `contents`.", - }, - ); - } - - // Patch ClientCapabilities to add VS-specific client capabilities - if (structure.name === "ClientCapabilities") { - structure.properties.push( - { - name: "_vs_supportsVisualStudioExtensions", - type: { kind: "base", name: "boolean" }, - optional: true, - documentation: "Whether the client supports Visual Studio extensions.", - }, - { - name: "_vs_supportedSnippetVersion", - type: { kind: "base", name: "integer" }, - optional: true, - documentation: "The snippet version supported by the client.", - }, - { - name: "_vs_supportsNotIncludingTextInTextDocumentDidOpen", - type: { kind: "base", name: "boolean" }, - optional: true, - documentation: "Whether the client supports not including text in textDocument/didOpen notifications.", - }, - { - name: "_vs_supportsIconExtensions", - type: { kind: "base", name: "boolean" }, - optional: true, - documentation: "Whether the client supports icon extensions.", - }, - { - name: "_vs_supportsDiagnosticRequests", - type: { kind: "base", name: "boolean" }, - optional: true, - documentation: "Whether the client supports diagnostic requests.", - }, - ); - } - - // Patch SignatureInformation to add VS-specific colorized label - if (structure.name === "SignatureInformation") { - structure.properties.push({ - name: "_vs_colorizedLabel", - type: { kind: "reference", name: "VSClassifiedTextElement" }, - optional: true, - documentation: "A colorized label for the signature, providing classified text runs for VS syntax coloring.", - }); - } - - for (const prop of structure.properties) { - // Replace initializationOptions type with custom InitializationOptions. - // The spec types this field as LSPAny?, which includes null, so keep - // it nullable so a null value sent by loose clients is accepted. - if (prop.name === "initializationOptions" && prop.type.kind === "reference" && prop.type.name === "LSPAny") { - prop.type = { - kind: "or", - items: [ - { kind: "reference", name: "InitializationOptions" }, - { kind: "base", name: "null" }, - ], - }; - } - - // Replace Data *any fields with custom typed Data fields - if (prop.name === "data" && prop.type.kind === "reference" && prop.type.name === "LSPAny") { - const customDataType = `${structure.name}Data`; - prop.type = { kind: "reference", name: customDataType }; - - // If we haven't explicitly declared this Data structure, we'll need a placeholder - if (!explicitDataStructures.has(customDataType)) { - neededDataStructures.add(customDataType); - } - } - - // Registration.registerOptions and Registration.method are handled specially: - // registerOptions becomes a custom struct, and method is derived from it. - // Remove both from the structure so the normal generator skips them. - if (structure.name === "Registration" && (prop.name === "registerOptions" || prop.name === "method")) { - // Will be filtered out below - } - - // Replace ProgressParams.value with a proper union type - if (structure.name === "ProgressParams" && prop.name === "value" && prop.type.kind === "reference" && prop.type.name === "LSPAny") { - prop.type = { - kind: "or", - items: [ - { kind: "reference", name: "WorkDoneProgressBegin" }, - { kind: "reference", name: "WorkDoneProgressReport" }, - { kind: "reference", name: "WorkDoneProgressEnd" }, - ], - }; - } - } - } - - for (const notification of model.notifications) { - if (notification.typeName === "TelemetryEventNotification") { - notification.params = { - kind: "reference", - name: "TelemetryEvent", - }; - } - } - - // Create placeholder structures for Data types that weren't explicitly declared - for (const dataTypeName of neededDataStructures) { - const baseName = dataTypeName.replace(/Data$/, ""); - customStructures.push({ - name: dataTypeName, - properties: [], - documentation: `${dataTypeName} is a placeholder for custom data preserved on a ${baseName}.`, - }); - } - - // Add custom enumerations, custom structures, custom requests, and synthetic structures to the model - model.enumerations.push(...customEnumerations); - model.structures.push(...customStructures, ...syntheticStructures); - model.requests.push(...customRequests); - model.notifications.push(...customNotifications); - - // Build structure map for preprocessing - const structureMap = new Map(); - for (const structure of model.structures) { - structureMap.set(structure.name, structure); - } - - function collectInheritedProperties(structure: Structure, visited = new Set()): Property[] { - if (visited.has(structure.name)) { - return []; // Avoid circular dependencies - } - visited.add(structure.name); - - const properties: Property[] = []; - const inheritanceTypes = [...(structure.extends || []), ...(structure.mixins || [])]; - - for (const type of inheritanceTypes) { - if (type.kind === "reference") { - const inheritedStructure = structureMap.get(type.name); - if (inheritedStructure) { - properties.push( - ...collectInheritedProperties(inheritedStructure, new Set(visited)), - ...inheritedStructure.properties, - ); - } - } - } - - return properties; - } - - // Inline inheritance for each structure - for (const structure of model.structures) { - const inheritedProperties = collectInheritedProperties(structure); - - // Merge properties with structure's own properties taking precedence - const propertyMap = new Map(); - - inheritedProperties.forEach(prop => propertyMap.set(prop.name, prop)); - structure.properties.forEach(prop => propertyMap.set(prop.name, prop)); - - structure.properties = Array.from(propertyMap.values()); - structure.extends = undefined; - structure.mixins = undefined; - - // Replace experimental LSPAny with typed ExperimentalClientCapabilities in ClientCapabilities - if (structure.name === "ClientCapabilities") { - const expProp = structure.properties.find(p => p.name === "experimental"); - if (expProp) { - expProp.type = { kind: "reference", name: "ExperimentalClientCapabilities" }; - expProp.optional = true; - } - } - - // Replace experimental LSPAny with typed ExperimentalServerCapabilities in ServerCapabilities - if (structure.name === "ServerCapabilities") { - const expProp = structure.properties.find(p => p.name === "experimental"); - if (expProp) { - expProp.type = { kind: "reference", name: "ExperimentalServerCapabilities" }; - expProp.optional = true; - } - } - - // Remove method and registerOptions from Registration (handled by custom codegen) - if (structure.name === "Registration") { - structure.properties = structure.properties.filter(p => p.name !== "method" && p.name !== "registerOptions"); - } - } - - // Remove _InitializeParams structure after flattening (it was only needed for inheritance) - model.structures = model.structures.filter(s => s.name !== "_InitializeParams"); - - // Remove all notebook-related features from the model - function isNotebookRelatedName(name: string): boolean { - const lower = name.toLowerCase(); - return lower.includes("notebook"); - } - - function isNotebookRelatedMethod(method: string): boolean { - return method.toLowerCase().startsWith("notebookdocument/"); - } - - function typeReferencesNotebook(type: Type): boolean { - if (type.kind === "reference") return isNotebookRelatedName(type.name); - if (type.kind === "array") return typeReferencesNotebook(type.element); - if (type.kind === "or" || type.kind === "and") return type.items.some(typeReferencesNotebook); - if (type.kind === "map") return typeReferencesNotebook(type.key) || typeReferencesNotebook(type.value); - return false; - } - - function isEntirelyNotebookType(type: Type): boolean { - if (type.kind === "reference") return isNotebookRelatedName(type.name); - if (type.kind === "array") return isEntirelyNotebookType(type.element); - if (type.kind === "or" || type.kind === "and") return type.items.every(isEntirelyNotebookType); - return false; - } - - function removeNotebookFromType(type: Type): Type { - if (type.kind === "or") { - const filtered = type.items.filter(item => !typeReferencesNotebook(item)).map(removeNotebookFromType); - if (filtered.length === 1) return filtered[0]; - if (filtered.length < type.items.length) { - return { ...type, items: filtered }; - } - } - if (type.kind === "and") { - const filtered = type.items.filter(item => !typeReferencesNotebook(item)).map(removeNotebookFromType); - if (filtered.length === 1) return filtered[0]; - if (filtered.length < type.items.length) { - return { ...type, items: filtered }; - } - } - return type; - } - - // Filter out notebook structures (and notebook-only structures like ExecutionSummary) - const notebookOnlyStructures = new Set(["ExecutionSummary"]); - model.structures = model.structures.filter(s => !isNotebookRelatedName(s.name) && !notebookOnlyStructures.has(s.name)); - - // Remove notebook properties from remaining structures - for (const structure of model.structures) { - structure.properties = structure.properties.filter(p => { - if (isNotebookRelatedName(p.name)) return false; - // Only remove properties whose type is entirely notebook-related - if (isEntirelyNotebookType(p.type)) return false; - return true; - }); - // Clean up union types in remaining properties to remove notebook members - for (const prop of structure.properties) { - prop.type = removeNotebookFromType(prop.type); - } - } - - // Filter out notebook notifications and requests - model.notifications = model.notifications.filter(n => !isNotebookRelatedMethod(n.method)); - model.requests = model.requests.filter(r => !isNotebookRelatedMethod(r.method)); - - // Filter out notebook enumerations - model.enumerations = model.enumerations.filter(e => !isNotebookRelatedName(e.name)); - - // Remove notebook-related values from remaining enumerations - for (const enumeration of model.enumerations) { - enumeration.values = enumeration.values.filter(v => !isNotebookRelatedName(v.name)); - } - - // Filter out notebook type aliases - model.typeAliases = model.typeAliases.filter(ta => !isNotebookRelatedName(ta.name)); - - // Clean up type aliases that reference notebook types (e.g., DocumentFilter) - for (const ta of model.typeAliases) { - if (ta.type.kind === "or") { - ta.type.items = ta.type.items.filter(item => !typeReferencesNotebook(item)); - // If only one item remains, unwrap the union - if (ta.type.items.length === 1) { - ta.type = ta.type.items[0]; - } - } - } - - // Build the registration method map (after notebook filtering). - // Each unique registration method gets a field in the generated RegisterOptions struct. - const regMethodSeen = new Set(); - for (const request of [...model.requests, ...model.notifications]) { - if (!request.registrationOptions) continue; - const regMethod = (request as any).registrationMethod || request.method; - - if (regMethodSeen.has(regMethod)) continue; - regMethodSeen.add(regMethod); - - // Resolve the options type name - const ro = request.registrationOptions; - let optionsTypeName: string; - if (ro.kind === "reference") { - optionsTypeName = ro.name; - } - else { - throw new Error(`Unexpected registrationOptions kind '${ro.kind}' for ${request.method}; expected all to be resolved to references`); - } - - registrationMethods.push({ - registrationMethod: regMethod, - fieldName: methodNameIdentifier(regMethod), - optionsTypeName, - }); - } - - // Identify registration-only methods (not also a request/notification method). - // These need their own Method constant emitted. - const allRequestMethods = new Set([...model.requests, ...model.notifications].map(r => r.method)); - for (const reg of registrationMethods) { - (reg as any).isRegistrationOnly = !allRequestMethods.has(reg.registrationMethod); - } - - // Merge LSPErrorCodes into ErrorCodes and remove LSPErrorCodes - const errorCodesEnum = model.enumerations.find(e => e.name === "ErrorCodes"); - const lspErrorCodesEnum = model.enumerations.find(e => e.name === "LSPErrorCodes"); - if (errorCodesEnum && lspErrorCodesEnum) { - // Merge LSPErrorCodes values into ErrorCodes - errorCodesEnum.values.push(...lspErrorCodesEnum.values); - // Remove LSPErrorCodes from the model - model.enumerations = model.enumerations.filter(e => e.name !== "LSPErrorCodes"); - } - - // Singularize plural enum names (e.g., "ErrorCodes" -> "ErrorCode") - for (const enumeration of model.enumerations) { - if (enumeration.name.endsWith("Codes")) { - enumeration.name = enumeration.name.slice(0, -1); // "Codes" -> "Code" - } - else if (enumeration.name.endsWith("Modifiers")) { - enumeration.name = enumeration.name.slice(0, -1); // "Modifiers" -> "Modifier" - } - else if (enumeration.name.endsWith("Types")) { - enumeration.name = enumeration.name.slice(0, -1); // "Types" -> "Type" - } - } -} - -patchAndPreprocessModel(); - -// Validate that telemetry events in the TelemetryEvent union have properly shaped -// measurements and properties fields. measurements struct fields must only contain -// numeric types (decimal/integer/uinteger). -function validateTelemetryEvents() { - const telemetryAlias = customTypeAliases.find(a => a.name === "TelemetryEvent"); - if (!telemetryAlias || telemetryAlias.type.kind !== "or") return; - - const structureMap = new Map(model.structures.map(s => [s.name, s])); - - for (const item of telemetryAlias.type.items) { - if (item.kind !== "reference") continue; - const eventStruct = structureMap.get(item.name); - if (!eventStruct) continue; - - for (const prop of eventStruct.properties) { - if (prop.name === "measurements" && prop.type.kind === "reference") { - const measurementsStruct = structureMap.get(prop.type.name); - if (!measurementsStruct) continue; - for (const mp of measurementsStruct.properties) { - if (mp.type.kind !== "base" || !["decimal", "integer", "uinteger"].includes(mp.type.name)) { - throw new Error( - `Telemetry measurements struct ${prop.type.name}.${mp.name} must be a numeric type ` + - `(decimal/integer/uinteger), got ${mp.type.kind === "base" ? mp.type.name : mp.type.kind}`, - ); - } - } - } - } - } -} - -validateTelemetryEvents(); - -interface GoType { - name: string; - needsPointer: boolean; -} - -interface TypeInfo { - types: Map; - literalTypes: Map; - unionTypes: Map; - typeAliasMap: Map; -} - -const typeInfo: TypeInfo = { - types: new Map(), - literalTypes: new Map(), - unionTypes: new Map(), - typeAliasMap: new Map(), -}; - -function titleCase(s: string) { - return s.charAt(0).toUpperCase() + s.slice(1); -} - -function goFieldName(prop: Property): string { - if (prop.name.startsWith("_vs_")) { - return "VS" + titleCase(prop.name.slice(4)); - } - return titleCase(prop.name); -} - -function resolveType(type: Type): GoType { - switch (type.kind) { - case "base": - switch (type.name) { - case "integer": - return { name: "int32", needsPointer: false }; - case "uinteger": - return { name: "uint32", needsPointer: false }; - case "string": - return { name: "string", needsPointer: false }; - case "boolean": - return { name: "bool", needsPointer: false }; - case "URI": - return { name: "URI", needsPointer: false }; - case "DocumentUri": - return { name: "DocumentUri", needsPointer: false }; - case "decimal": - return { name: "float64", needsPointer: false }; - case "null": - return { name: "any", needsPointer: false }; - default: - throw new Error(`Unsupported base type: ${type.name}`); - } - - case "reference": - const typeAliasOverride = typeAliasOverrides.get(type.name); - if (typeAliasOverride) { - return typeAliasOverride; - } - - const nonResolved = nonResolvedAliases.has(type.name); - if (nonResolved) { - return { name: type.name, needsPointer: false }; - } - - // Check if this is a type alias that resolves to a union type - const aliasedType = typeInfo.typeAliasMap.get(type.name); - if (aliasedType) { - return resolveType(aliasedType); - } - - let refType = typeInfo.types.get(type.name); - if (!refType) { - refType = { name: type.name, needsPointer: true }; - typeInfo.types.set(type.name, refType); - } - return refType; - - case "array": { - const elementType = resolveType(type.element); - const arrayTypeName = elementType.needsPointer - ? `[]*${elementType.name}` - : `[]${elementType.name}`; - return { - name: arrayTypeName, - needsPointer: false, - }; - } - - case "map": { - const keyType = resolveType(type.key); - const valueType = resolveType(type.value); - const valueTypeName = valueType.needsPointer ? `*${valueType.name}` : valueType.name; - - return { - name: `map[${keyType.name}]${valueTypeName}`, - needsPointer: false, - }; - } - - case "tuple": { - if ( - type.items.length === 2 && - type.items[0].kind === "base" && type.items[0].name === "uinteger" && - type.items[1].kind === "base" && type.items[1].name === "uinteger" - ) { - return { name: "[2]uint32", needsPointer: false }; - } - - throw new Error("Unsupported tuple type: " + JSON.stringify(type)); - } - - case "stringLiteral": { - const typeName = `StringLiteral${type.value.split(".").map(titleCase).join("")}`; - typeInfo.literalTypes.set(String(type.value), typeName); - return { name: typeName, needsPointer: false }; - } - - case "integerLiteral": { - const typeName = `IntegerLiteral${type.value}`; - typeInfo.literalTypes.set(String(type.value), typeName); - return { name: typeName, needsPointer: false }; - } - - case "booleanLiteral": { - const typeName = `BooleanLiteral${type.value ? "True" : "False"}`; - typeInfo.literalTypes.set(String(type.value), typeName); - return { name: typeName, needsPointer: false }; - } - case "literal": - if (type.value.properties.length === 0) { - return { name: "struct{}", needsPointer: false }; - } - - throw new Error("Unexpected non-empty literal object: " + JSON.stringify(type.value)); - - case "or": { - return handleOrType(type); - } - - default: - throw new Error(`Unsupported type kind: ${type.kind}`); - } -} - -function flattenOrTypes(types: Type[]): Type[] { - const flattened = new Set(); - - for (const rawType of types) { - let type = rawType; - - // Dereference reference types that point to OR types - if (rawType.kind === "reference") { - const aliasedType = typeInfo.typeAliasMap.get(rawType.name); - if (aliasedType && aliasedType.kind === "or") { - type = aliasedType; - } - } - - if (type.kind === "or") { - // Recursively flatten OR types - for (const subType of flattenOrTypes(type.items)) { - flattened.add(subType); - } - } - else { - flattened.add(rawType); - } - } - - return Array.from(flattened); -} - -function pluralize(name: string): string { - // Handle common irregular plurals and special cases - if ( - name.endsWith("s") || name.endsWith("x") || name.endsWith("z") || - name.endsWith("ch") || name.endsWith("sh") - ) { - return name + "es"; - } - if (name.endsWith("y") && name.length > 1 && !"aeiou".includes(name[name.length - 2])) { - return name.slice(0, -1) + "ies"; - } - return name + "s"; -} - -function handleOrType(orType: OrType): GoType { - // First, flatten any nested OR types - const types = flattenOrTypes(orType.items); - - // Check for nullable types (OR with null) - const nullIndex = types.findIndex(item => item.kind === "base" && item.name === "null"); - let containedNull = nullIndex !== -1; - - // If it's nullable, remove the null type from the list - let nonNullTypes = types; - if (containedNull) { - nonNullTypes = types.filter((_, i) => i !== nullIndex); - } - - // If no types remain after filtering null, this shouldn't happen - if (nonNullTypes.length === 0) { - throw new Error("Union type with only null is not supported: " + JSON.stringify(types)); - } - - // Even if only one type remains after filtering null, we still need to create a union type - // to preserve the nullable behavior (all fields nil = null) - - let memberNames = nonNullTypes.map(type => { - if (type.kind === "reference") { - return type.name; - } - else if (type.kind === "base") { - return titleCase(type.name); - } - else if ( - type.kind === "array" && - (type.element.kind === "reference" || type.element.kind === "base") - ) { - return pluralize(titleCase(type.element.name)); - } - else if (type.kind === "array") { - // Handle more complex array types - const elementType = resolveType(type.element); - return `${elementType.name}Array`; - } - else if (type.kind === "literal" && type.value.properties.length === 0) { - return "EmptyObject"; - } - else if (type.kind === "tuple") { - return "Tuple"; - } - else { - throw new Error(`Unsupported type kind in union: ${type.kind}`); - } - }); - - // Find longest common prefix of member names chunked by PascalCase - function findLongestCommonPrefix(names: string[]): string { - if (names.length === 0) return ""; - if (names.length === 1) return ""; - - // Split each name into PascalCase chunks - function splitPascalCase(name: string): string[] { - const chunks: string[] = []; - let currentChunk = ""; - - for (let i = 0; i < name.length; i++) { - const char = name[i]; - if (char >= "A" && char <= "Z" && currentChunk.length > 0) { - // Start of a new chunk - chunks.push(currentChunk); - currentChunk = char; - } - else { - currentChunk += char; - } - } - - if (currentChunk.length > 0) { - chunks.push(currentChunk); - } - - return chunks; - } - - const allChunks = names.map(splitPascalCase); - const minChunkLength = Math.min(...allChunks.map(chunks => chunks.length)); - - // Find the longest common prefix of chunks - let commonChunks: string[] = []; - for (let i = 0; i < minChunkLength; i++) { - const chunk = allChunks[0][i]; - if (allChunks.every(chunks => chunks[i] === chunk)) { - commonChunks.push(chunk); - } - else { - break; - } - } - - return commonChunks.join(""); - } - - const commonPrefix = findLongestCommonPrefix(memberNames); - - let unionTypeName = ""; - - if (commonPrefix.length > 0) { - const trimmedMemberNames = memberNames.map(name => name.slice(commonPrefix.length)); - if (trimmedMemberNames.every(name => name)) { - unionTypeName = commonPrefix + trimmedMemberNames.join("Or"); - memberNames = trimmedMemberNames; - } - else { - unionTypeName = memberNames.join("Or"); - } - } - else { - unionTypeName = memberNames.join("Or"); - } - - if (containedNull) { - unionTypeName += "OrNull"; - } - else { - containedNull = false; - } - - const union = memberNames.map((name, i) => ({ name, type: nonNullTypes[i], containedNull })); - - typeInfo.unionTypes.set(unionTypeName, union); - - return { - name: unionTypeName, - needsPointer: false, - }; -} - -const typeAliasOverrides = new Map([ - ["LSPAny", { name: "any", needsPointer: false }], - ["LSPArray", { name: "[]any", needsPointer: false }], - ["LSPObject", { name: "map[string]any", needsPointer: false }], - ["uint64", { name: "uint64", needsPointer: false }], -]); - -// These type aliases are intentionally not resolved to their underlying types. -// It means that we can end up with non-normalized union types in some places. -// Also, unlike other type aliases, these will get a type alias in the generated source code. -// We may want to eventually do this for all type aliases though. -const nonResolvedAliases = new Set(customTypeAliases.map(ta => ta.name)); - -/** - * First pass: Resolve all type information - */ -function collectTypeDefinitions() { - // Process all enumerations first to make them available for struct fields - for (const enumeration of model.enumerations) { - typeInfo.types.set(enumeration.name, { - name: enumeration.name, - needsPointer: false, - }); - } - - const valueTypes = new Set([ - "Position", - "Range", - "Location", - "Color", - "TextDocumentIdentifier", - "PreviousResultId", - "VersionedTextDocumentIdentifier", - "OptionalVersionedTextDocumentIdentifier", - "ExportInfoMapKey", - ]); - - // Process all structures - for (const structure of model.structures) { - typeInfo.types.set(structure.name, { - name: structure.name, - needsPointer: !valueTypes.has(structure.name), - }); - } - - // Process all type aliases - for (const typeAlias of model.typeAliases) { - if (typeAliasOverrides.has(typeAlias.name)) { - continue; - } - - // Store the alias mapping so we can resolve it later - typeInfo.typeAliasMap.set(typeAlias.name, typeAlias.type); - } -} - -function formatDocumentation(s: string | undefined): string { - if (!s) return ""; - - let lines: string[] = []; - - for (let line of s.split("\n")) { - line = line.trimEnd(); - line = line.replace(/(\w ) +/g, "$1"); - // Some upstream docs include dangling block comment delimiters; remove them - // so they don't leak into generated `//` comments. - line = line.replace(/\s*\/\*+\s*/g, " "); - line = line.replace(/\s*\*+\/\s*/g, " "); - line = line.replace(/\s{2,}/g, " ").trimEnd(); - line = line.replace(/\{@link(?:code)?.*?([^} ]+)\}/g, "$1"); - line = line.replace(/^@(since|proposed|deprecated)(.*)/, (_, tag, rest) => { - lines.push(""); - return `${titleCase(tag)}${rest ? ":" + rest : "."}`; - }); - lines.push(line); - } - - // filter out contiguous empty lines - while (true) { - const toRemove = lines.findIndex((line, index) => { - if (line) return false; - if (index === 0) return true; - if (index === lines.length - 1) return true; - return !(lines[index - 1] && lines[index + 1]); - }); - if (toRemove === -1) break; - lines.splice(toRemove, 1); - } - - return lines.length > 0 ? "// " + lines.join("\n// ") + "\n" : ""; -} - -function methodNameIdentifier(name: string) { - return name.split("/").map(v => { - if (v === "$") return ""; - // Mirror goFieldName: "_vs_foo" -> "VSFoo". - if (v.startsWith("_vs_")) return "VS" + titleCase(v.slice(4)); - return titleCase(v); - }).join(""); -} - -/** - * Returns the JSON token kind ("string", "number", "object", "array", "boolean") - * for a given meta model Type, or undefined if the kind cannot be statically determined. - */ -function jsonKindForType(type: Type): string | undefined { - switch (type.kind) { - case "base": - switch (type.name) { - case "integer": - case "uinteger": - case "decimal": - return "number"; - case "string": - case "URI": - case "DocumentUri": - return "string"; - case "boolean": - return "boolean"; - default: - return undefined; - } - case "reference": { - if (typeAliasOverrides.has(type.name)) { - return undefined; - } - if (model.structures.some(s => s.name === type.name)) { - return "object"; - } - const enumeration = model.enumerations.find(e => e.name === type.name); - if (enumeration) { - switch (enumeration.type.name) { - case "string": - return "string"; - case "integer": - case "uinteger": - return "number"; - default: - return undefined; - } - } - const aliasType = typeInfo.typeAliasMap.get(type.name); - if (aliasType) return jsonKindForType(aliasType); - return undefined; - } - case "array": - return "array"; - case "map": - return "object"; - case "tuple": - return "array"; - case "stringLiteral": - return "string"; - case "integerLiteral": - return "number"; - case "booleanLiteral": - return "boolean"; - case "literal": - return "object"; - case "or": { - const kinds = new Set(type.items.map(item => jsonKindForType(item)).filter(Boolean)); - return kinds.size === 1 ? kinds.values().next().value : undefined; - } - default: - return undefined; - } -} - -function goKindCasesForJsonKind(kind: string): string { - switch (kind) { - case "string": - return `case '"':`; - case "number": - return `case '0':`; - case "object": - return `case '{':`; - case "array": - return `case '[':`; - case "boolean": - return `case 't', 'f':`; - default: - return ""; - } -} - -/** - * Checks if a meta model Type can represent a JSON null value. - * Used to determine whether to reject explicit JSON `null` for any field - * that can otherwise decode `null` without a type error. - */ -function typeCanBeNull(type: Type): boolean { - switch (type.kind) { - case "base": - return type.name === "null"; - case "reference": { - const override = typeAliasOverrides.get(type.name); - if (override) { - return override.name === "any"; - } - // A bare "any" reference resolves to Go's `any` (interface), which can hold null. - if (type.name === "any") { - return true; - } - if (nonResolvedAliases.has(type.name)) { - const customAlias = customTypeAliases.find(t => t.name === type.name); - if (customAlias) return typeCanBeNull(customAlias.type); - return false; - } - const aliased = typeInfo.typeAliasMap.get(type.name); - if (aliased) return typeCanBeNull(aliased); - return false; - } - case "or": - return type.items.some(item => typeCanBeNull(item)); - default: - return false; - } -} - -/** - * For a group of union entries that share the same JSON kind (e.g., all objects), - * find a discriminator field — a JSON property whose string literal type differs - * across variants — enabling efficient O(1) dispatch instead of try-each. - */ -function findDiscriminatorField(entries: { fieldName: string; typeName: string; originalType: Type; }[]): { - fieldName: string; - mapping: Map; - unmapped: { fieldName: string; typeName: string; originalType: Type; }[]; -} | null { - // For each entry, find string literal fields and build candidate discriminators. - // A valid discriminator is a field name that appears on multiple variants with - // different string literal values. - const fieldCandidates = new Map>(); - - for (const entry of entries) { - if (entry.originalType.kind !== "reference") continue; - const structure = model.structures.find(s => s.name === (entry.originalType as ReferenceType).name); - if (!structure) continue; - - for (const prop of structure.properties) { - if (prop.type.kind === "stringLiteral") { - if (!fieldCandidates.has(prop.name)) { - fieldCandidates.set(prop.name, new Map()); - } - const mapping = fieldCandidates.get(prop.name)!; - if (!mapping.has(prop.type.value)) { - mapping.set(prop.type.value, entry); - } - else { - // Two entries share the same literal value; invalidate this candidate. - mapping.set(prop.type.value, undefined); - } - } - } - } - - // Pick the discriminator field that covers the most entries. - let bestField: string | null = null; - let bestMapping: Map | null = null; - - for (const [fieldName, mapping] of fieldCandidates) { - const validMapping = new Map(); - for (const [value, entry] of mapping) { - if (entry !== undefined) validMapping.set(value, entry); - } - if (validMapping.size >= 2 && (!bestMapping || validMapping.size > bestMapping.size)) { - bestField = fieldName; - bestMapping = validMapping; - } - } - - if (!bestField || !bestMapping) return null; - - const mappedEntries = new Set(bestMapping.values()); - const unmapped = entries.filter(e => !mappedEntries.has(e)); - - return { fieldName: bestField, mapping: bestMapping, unmapped }; -} - -/** - * For a group of union entries that share the same JSON kind, find fields whose - * presence/absence in the JSON uniquely identifies a variant. A "presence discriminator" - * for variant X is a required field on X that does not appear in any other variant's - * property set at all. - */ -function findPresenceDiscriminator(entries: { fieldName: string; typeName: string; originalType: Type; }[]): { - checks: { jsonFieldName: string; entry: { fieldName: string; typeName: string; originalType: Type; }; }[]; - unmapped: { fieldName: string; typeName: string; originalType: Type; }[]; -} | null { - // Collect all property names for each variant - const variantProps = new Map; }>(); - for (const entry of entries) { - if (entry.originalType.kind !== "reference") continue; - const structure = model.structures.find(s => s.name === (entry.originalType as ReferenceType).name); - if (!structure) continue; - const required = structure.properties.filter(p => !p.optional && !p.omitzeroValue); - const allNames = new Set(structure.properties.map(p => p.name)); - variantProps.set(entry, { required, allNames }); - } - - const checks: { jsonFieldName: string; entry: typeof entries[0]; }[] = []; - const handled = new Set(); - - for (const entry of entries) { - const info = variantProps.get(entry); - if (!info) continue; - - const otherEntries = entries.filter(e => e !== entry); - for (const field of info.required) { - const absentFromAllOthers = otherEntries.every(other => { - const otherInfo = variantProps.get(other); - if (!otherInfo) return false; - return !otherInfo.allNames.has(field.name); - }); - if (absentFromAllOthers) { - checks.push({ jsonFieldName: field.name, entry }); - handled.add(entry); - break; - } - } - } - - if (checks.length === 0) return null; - - const unmapped = entries.filter(e => !handled.has(e)); - return { checks, unmapped }; -} - -/** - * Generate the Go code - */ -function generateCode() { - const parts: string[] = []; - - function write(s: string) { - parts.push(s); - } - - function writeLine(s = "") { - parts.push(s + "\n"); - } - - /** - * Generate Go code for discriminator-based union dispatch. - * Assumes a variable named `data` of type `json.Value` is in scope. - * Returns true if all switch branches return (exhaustive). - */ - function generateDiscriminatorDispatch( - disc: NonNullable>, - indent: string, - ): boolean { - writeLine(`${indent}switch string(jsonObjectRawField(data, ${JSON.stringify(disc.fieldName)})) {`); - for (const [value, entry] of disc.mapping) { - writeLine(`${indent}case \`"${value}"\`:`); - writeLine(`${indent}\to.${entry.fieldName} = new(${entry.typeName})`); - writeLine(`${indent}\treturn json.Unmarshal(data, o.${entry.fieldName})`); - } - let exhaustive = false; - if (disc.unmapped.length > 0) { - writeLine(`${indent}default:`); - exhaustive = generateUnmappedFallback(disc.unmapped, indent + "\t"); - } - writeLine(`${indent}}`); - return exhaustive; - } - - /** - * Generate try-each fallback code for unmapped entries, chaining into - * presence dispatch if possible before falling back to raw try-each. - * Assumes a variable named `data` of type `json.Value` is in scope. - * Returns true if all generated paths return (exhaustive). - */ - function generateUnmappedFallback( - unmapped: { fieldName: string; typeName: string; originalType: Type; }[], - indent: string, - ): boolean { - if (unmapped.length <= 1) { - // Exactly 1 entry: it's the only remaining variant after dispatch, - // so use a hard error return instead of speculative err == nil. - for (const entry of unmapped) { - writeLine(`${indent}o.${entry.fieldName} = new(${entry.typeName})`); - writeLine(`${indent}return json.Unmarshal(data, o.${entry.fieldName})`); - } - return unmapped.length === 1; - } - // Try chaining presence dispatch on the remaining subset - const pres = findPresenceDiscriminator(unmapped); - if (pres) { - return generatePresenceDispatch(pres, indent); - } - else { - for (const entry of unmapped) { - writeLine(`${indent}var v${entry.fieldName} ${entry.typeName}`); - writeLine(`${indent}if err := json.Unmarshal(data, &v${entry.fieldName}); err == nil {`); - writeLine(`${indent}\to.${entry.fieldName} = &v${entry.fieldName}`); - writeLine(`${indent}\treturn nil`); - writeLine(`${indent}}`); - } - return false; - } - } - - /** - * Iteratively collect all presence discriminator checks across multiple - * passes, so they can be emitted as a single flat switch with one scan. - */ - function collectAllPresenceChecks( - pres: NonNullable>, - ): { - allChecks: { jsonFieldName: string; entry: { fieldName: string; typeName: string; originalType: Type; }; }[]; - finalUnmapped: { fieldName: string; typeName: string; originalType: Type; }[]; - } { - const allChecks = [...pres.checks]; - let remaining = pres.unmapped; - while (remaining.length > 1) { - const next = findPresenceDiscriminator(remaining); - if (!next) break; - allChecks.push(...next.checks); - remaining = next.unmapped; - } - return { allChecks, finalUnmapped: remaining }; - } - - /** - * Generate Go code for presence-based union dispatch. - * Assumes a variable named `data` of type `json.Value` is in scope. - * Collects all presence checks iteratively, then emits a single flat - * switch jsonObjectHasKey(data, key1, key2, ...) so data is scanned once. - * Returns true if all switch branches return (exhaustive). - */ - function generatePresenceDispatch( - pres: NonNullable>, - indent: string, - ): boolean { - const { allChecks, finalUnmapped } = collectAllPresenceChecks(pres); - const args = allChecks.map(c => JSON.stringify(c.jsonFieldName)).join(", "); - writeLine(`${indent}switch jsonObjectHasKey(data, ${args}) {`); - for (let i = 0; i < allChecks.length; i++) { - writeLine(`${indent}case ${i}: // ${allChecks[i].jsonFieldName}`); - writeLine(`${indent}\to.${allChecks[i].entry.fieldName} = new(${allChecks[i].entry.typeName})`); - writeLine(`${indent}\treturn json.Unmarshal(data, o.${allChecks[i].entry.fieldName})`); - } - if (finalUnmapped.length > 0) { - writeLine(`${indent}default:`); - if (finalUnmapped.length === 1) { - // Only one variant left after dispatch — use hard error return. - const entry = finalUnmapped[0]; - writeLine(`${indent}\to.${entry.fieldName} = new(${entry.typeName})`); - writeLine(`${indent}\treturn json.Unmarshal(data, o.${entry.fieldName})`); - } - else { - for (const entry of finalUnmapped) { - writeLine(`${indent}\tvar v${entry.fieldName} ${entry.typeName}`); - writeLine(`${indent}\tif err := json.Unmarshal(data, &v${entry.fieldName}); err == nil {`); - writeLine(`${indent}\t\to.${entry.fieldName} = &v${entry.fieldName}`); - writeLine(`${indent}\t\treturn nil`); - writeLine(`${indent}\t}`); - } - } - } - writeLine(`${indent}}`); - // Exhaustive if the default case has a single hard-returning entry - return finalUnmapped.length === 1; - } - - function generateResolvedStruct(structure: Structure, indent: string = "\t"): string[] { - const lines: string[] = []; - - for (const prop of structure.properties) { - // Add property documentation if it exists - if (prop.documentation) { - const propDoc = formatDocumentation(prop.documentation); - if (propDoc) { - // Add the documentation with proper indentation - for (const line of propDoc.split("\n").filter(l => l)) { - lines.push(`${indent}${line}`); - } - } - } - - const type = resolveType(prop.type); - - // For reference types that are structures, use a named resolved type - if (prop.type.kind === "reference") { - const refStructure = model.structures.find(s => s.name === type.name); - if (refStructure) { - // Use a named type for the resolved version - lines.push(`${indent}${goFieldName(prop)} Resolved${type.name} \`json:"${prop.name},omitzero"\``); - continue; - } - } - - // For other types (primitives, enums, arrays, etc.), use the type directly (no pointer) - const goType = type.name; - lines.push(`${indent}${goFieldName(prop)} ${goType} \`json:"${prop.name},omitzero"\``); - } - - return lines; - } - - function generateResolveConversion(structure: Structure, varName: string, indent: string): string[] { - const lines: string[] = []; - - for (const prop of structure.properties) { - const type = resolveType(prop.type); - const fieldName = goFieldName(prop); - const accessPath = `${varName}.${fieldName}`; - - // For reference types that are structures, call the resolve method - if (prop.type.kind === "reference") { - const refStructure = model.structures.find(s => s.name === type.name); - if (refStructure) { - lines.push(`${indent}${fieldName}: ${accessPath}.resolve(),`); - continue; - } - } - - // For other types, dereference if pointer - if (prop.optional || type.needsPointer) { - lines.push(`${indent}${fieldName}: derefOr(${accessPath}),`); - } - else { - lines.push(`${indent}${fieldName}: ${accessPath},`); - } - } - - return lines; - } - - function collectStructureDependencies(structure: Structure, visited = new Set()): Structure[] { - if (visited.has(structure.name)) { - return []; - } - visited.add(structure.name); - - const deps: Structure[] = []; - - for (const prop of structure.properties) { - if (prop.type.kind === "reference") { - const refStructure = model.structures.find(s => s.name === (prop.type as ReferenceType).name); - if (refStructure) { - deps.push(...collectStructureDependencies(refStructure, new Set(visited))); - deps.push(refStructure); - } - } - } - - return deps; - } - - function generateResolvedTypeAndHelper(structure: Structure, isMain: boolean = false): string[] { - const lines: string[] = []; - const typeName = `Resolved${structure.name}`; - // Main method is exported (Resolve), helpers are unexported (resolve) - const methodName = isMain ? `Resolve` : `resolve`; - - // Generate the resolved type with documentation - if (!isMain) { - // For non-main types, add standard documentation header - if (structure.documentation) { - const typeDoc = formatDocumentation(structure.documentation); - if (typeDoc) { - // Prepend comment explaining this is the resolved version - lines.push(`// ${typeName} is a resolved version of ${structure.name} with all optional fields`); - lines.push(`// converted to non-pointer values for easier access.`); - lines.push(`//`); - // Add the original structure documentation - for (const line of typeDoc.split("\n").filter(l => l)) { - lines.push(line); - } - } - } - else { - // If no documentation, just add a basic comment - lines.push(`// ${typeName} is a resolved version of ${structure.name} with all optional fields`); - lines.push(`// converted to non-pointer values for easier access.`); - } - } - // For main type, documentation is added separately before calling this function - - lines.push(`type ${typeName} struct {`); - lines.push(...generateResolvedStruct(structure, "\t")); - lines.push(`}`); - lines.push(``); - - // Generate the conversion method on the pointer receiver - lines.push(`func (v *${structure.name}) ${methodName}() ${typeName} {`); - lines.push(`\tif v == nil {`); - lines.push(`\t\treturn ${typeName}{}`); - lines.push(`\t}`); - lines.push(`\treturn ${typeName}{`); - lines.push(...generateResolveConversion(structure, "v", "\t\t")); - lines.push(`\t}`); - lines.push(`}`); - lines.push(``); - - return lines; - } - - // File header - writeLine("// Code generated by generate.mts; DO NOT EDIT."); - writeLine(""); - writeLine("package lsproto"); - writeLine(""); - writeLine(`import (`); - writeLine(`\t"cmp"`); - writeLine(`\t"fmt"`); - writeLine(`\t"strings"`); - writeLine(""); - writeLine(`\t"github.com/microsoft/TypeScript/tsc/internal/json"`); - writeLine(`)`); - writeLine(""); - writeLine("// Meta model version " + model.metaData.version); - writeLine(""); - - // Generate structures - writeLine("// Structures\n"); - - for (const structure of model.structures) { - function generateStructFields(name: string, includeDocumentation: boolean) { - if (includeDocumentation) { - write(formatDocumentation(structure.documentation)); - } - - writeLine(`type ${name} struct {`); - - // Properties are now inlined, no need to embed extends/mixins - for (const prop of structure.properties) { - if (includeDocumentation) { - write(formatDocumentation(prop.documentation)); - } - - const type = resolveType(prop.type); - - // For properties marked with omitzeroValue, use value type with omitzero instead of pointer - const useOmitzero = prop.optional || prop.omitzeroValue; - const goType = (prop.optional || type.needsPointer) && !prop.omitzeroValue ? `*${type.name}` : type.name; - - // Strictness markers for the shared unmarshalStruct interpreter: - // required = must be present. A nilable field (pointer/slice/map, - // not omitzero) rejects an explicit JSON null by default; mark the - // rare spec-nullable ones with `nullable` so the interpreter allows it. - const required = !prop.optional && !prop.omitzeroValue; - const nilable = prop.optional || type.needsPointer || type.name.startsWith("[]") || type.name.startsWith("map[") || !!prop.omitzeroValue; - const nullable = nilable && (typeCanBeNull(prop.type) || !!prop.omitzeroValue); - const lspMarkers = [required ? "required" : "", nullable ? "nullable" : ""].filter(Boolean).join(","); - const lspTag = lspMarkers ? ` lsp:"${lspMarkers}"` : ""; - - writeLine(`\t${goFieldName(prop)} ${goType} \`json:"${prop.name}${useOmitzero ? ",omitzero" : ""}"${lspTag}\``); - - if (includeDocumentation) { - writeLine(""); - } - } - - // Special: add RegisterOptions field to Registration - if (structure.name === "Registration") { - writeLine(""); - writeLine(`\t// Options necessary for the registration. Determines the method.`); - writeLine(`\tRegisterOptions *RegisterOptions \`json:"-"\``); - } - - writeLine("}"); - writeLine(""); - } - - generateStructFields(structure.name, true); - writeLine(""); - - if (hasTextDocumentURI(structure)) { - // Generate TextDocumentURI method - const textDocProp = structure.properties?.find(p => (p.name === "textDocument" || p.name === "_vs_textDocument") && p.type.kind === "reference" && p.type.name === "TextDocumentIdentifier"); - const textDocFieldName = textDocProp ? goFieldName(textDocProp) : "TextDocument"; - writeLine(`func (s *${structure.name}) TextDocumentURI() DocumentUri {`); - writeLine(`\treturn s.${textDocFieldName}.Uri`); - writeLine(`}`); - writeLine(""); - - if (hasTextDocumentPosition(structure)) { - // Generate TextDocumentPosition method - const posProp = structure.properties?.find(p => (p.name === "position" || p.name === "_vs_position") && p.type.kind === "reference" && p.type.name === "Position"); - const posFieldName = posProp ? goFieldName(posProp) : "Position"; - writeLine(`func (s *${structure.name}) TextDocumentPosition() Position {`); - writeLine(`\treturn s.${posFieldName}`); - writeLine(`}`); - writeLine(""); - } - } - - const locationUriProperty = getLocationUriProperty(structure); - if (locationUriProperty) { - // Generate Location method - writeLine(`func (s ${structure.name}) GetLocation() Location {`); - if (locationUriProperty === "Uri" && structure.name === "Location") { - writeLine(`\treturn s`); - } - else { - writeLine(`\treturn Location{`); - writeLine(`\t\tUri: s.${locationUriProperty},`); - writeLine(`\t\tRange: s.${locationUriProperty.replace(/Uri$/, "Range")},`); - writeLine(`\t}`); - } - writeLine(`}`); - writeLine(""); - } - - // Generate UnmarshalJSONFrom method for structure validation - // Skip Registration (has custom marshal/unmarshal generated separately) - // Skip properties marked with omitzeroValue since they're optional by nature - const requiredProps = structure.properties?.filter(p => { - if (p.optional) return false; - if (p.omitzeroValue) return false; - return true; - }) || []; - // Check if any fields need null rejection - const hasNullRejectableFields = structure.properties?.some(p => { - if (p.omitzeroValue) return false; - if (typeCanBeNull(p.type)) return false; - const resolved = resolveType(p.type); - return p.optional || resolved.needsPointer || resolved.name.startsWith("[]") || resolved.name.startsWith("map["); - }) || false; - if ((requiredProps.length > 0 || hasNullRejectableFields) && structure.name !== "Registration") { - writeLine(`\tvar _ json.UnmarshalerFrom = (*${structure.name})(nil)`); - writeLine(""); - writeLine(`func (s *${structure.name}) UnmarshalJSONFrom(dec *json.Decoder) error {`); - writeLine(`\treturn unmarshalStruct(s, dec)`); - writeLine(`}`); - writeLine(""); - } - - // Generate RegisterOptions struct and custom Registration marshal/unmarshal - // right after the Registration struct definition. - if (structure.name === "Registration") { - // RegisterOptions struct - writeLine(`// RegisterOptions is an externally-tagged union representing the options for a capability registration.`); - writeLine(`// Exactly one field should be set. The set field determines the method for the registration.`); - writeLine(`type RegisterOptions struct {`); - for (const reg of registrationMethods) { - writeLine(`\t${reg.fieldName} *${reg.optionsTypeName}`); - } - writeLine(`}`); - writeLine(""); - - // MarshalJSONTo for Registration - writeLine(`var _ json.MarshalerTo = (*Registration)(nil)`); - writeLine(""); - writeLine(`func (s *Registration) MarshalJSONTo(enc *json.Encoder) error {`); - - // Assert RegisterOptions is set and exactly one field is set - writeLine(`\tif s.RegisterOptions == nil {`); - writeLine(`\t\tpanic("RegisterOptions must be set")`); - writeLine(`\t}`); - writeLine(`\tassertOnlyOne("exactly one element of RegisterOptions should be set", countNonNil(s.RegisterOptions))`); - writeLine(""); - - writeLine(`\tif err := enc.WriteToken(json.BeginObject); err != nil {`); - writeLine(`\t\treturn err`); - writeLine(`\t}`); - writeLine(`\tif err := enc.WriteValue(json.Value(\`"id"\`)); err != nil {`); - writeLine(`\t\treturn err`); - writeLine(`\t}`); - writeLine(`\tif err := json.MarshalEncode(enc, s.Id); err != nil {`); - writeLine(`\t\treturn err`); - writeLine(`\t}`); - writeLine(`\tif err := enc.WriteValue(json.Value(\`"method"\`)); err != nil {`); - writeLine(`\t\treturn err`); - writeLine(`\t}`); - writeLine(`\tvar method json.Value`); - writeLine(`\tvar opts any`); - writeLine(`\tswitch {`); - for (const reg of registrationMethods) { - writeLine(`\tcase s.RegisterOptions.${reg.fieldName} != nil:`); - writeLine(`\t\tmethod = json.Value(\`"${reg.registrationMethod}"\`)`); - writeLine(`\t\topts = s.RegisterOptions.${reg.fieldName}`); - } - writeLine(`\t}`); - writeLine(`\tif err := enc.WriteValue(method); err != nil {`); - writeLine(`\t\treturn err`); - writeLine(`\t}`); - writeLine(`\tif err := enc.WriteValue(json.Value(\`"registerOptions"\`)); err != nil {`); - writeLine(`\t\treturn err`); - writeLine(`\t}`); - writeLine(`\tif err := json.MarshalEncode(enc, opts); err != nil {`); - writeLine(`\t\treturn err`); - writeLine(`\t}`); - writeLine(`\treturn enc.WriteToken(json.EndObject)`); - writeLine(`}`); - writeLine(""); - - // UnmarshalJSONFrom for Registration - writeLine(`var _ json.UnmarshalerFrom = (*Registration)(nil)`); - writeLine(""); - writeLine(`func (s *Registration) UnmarshalJSONFrom(dec *json.Decoder) error {`); - writeLine(`\t*s = Registration{}`); - writeLine(`\tconst (`); - writeLine(`\t\tmissingId uint = 1 << iota`); - writeLine(`\t\tmissingMethod`); - writeLine(`\t\t_missingLast`); - writeLine(`\t)`); - writeLine(`\tmissing := _missingLast - 1`); - writeLine(""); - writeLine(`\tif k := dec.PeekKind(); k != '{' {`); - writeLine(`\t\treturn errNotObject(k)`); - writeLine(`\t}`); - writeLine(`\tif _, err := dec.ReadToken(); err != nil {`); - writeLine(`\t\treturn err`); - writeLine(`\t}`); - writeLine(""); - writeLine(`\tvar method string`); - writeLine(`\tvar rawRegisterOptions json.Value`); - writeLine(""); - writeLine(`\tfor dec.PeekKind() != '}' {`); - writeLine(`\t\tname, err := dec.ReadValue()`); - writeLine(`\t\tif err != nil {`); - writeLine(`\t\t\treturn err`); - writeLine(`\t\t}`); - writeLine(`\t\tswitch string(name) {`); - writeLine(`\t\tcase \`"id"\`:`); - writeLine(`\t\t\tmissing &^= missingId`); - writeLine(`\t\t\tif err := json.UnmarshalDecode(dec, &s.Id); err != nil {`); - writeLine(`\t\t\t\treturn err`); - writeLine(`\t\t\t}`); - writeLine(`\t\tcase \`"method"\`:`); - writeLine(`\t\t\tmissing &^= missingMethod`); - writeLine(`\t\t\tif err := json.UnmarshalDecode(dec, &method); err != nil {`); - writeLine(`\t\t\t\treturn err`); - writeLine(`\t\t\t}`); - writeLine(`\t\tcase \`"registerOptions"\`:`); - writeLine(`\t\t\tv, err := dec.ReadValue()`); - writeLine(`\t\t\tif err != nil {`); - writeLine(`\t\t\t\treturn err`); - writeLine(`\t\t\t}`); - writeLine(`\t\t\trawRegisterOptions = v`); - writeLine(`\t\tdefault:`); - writeLine(`\t\t\tif err := dec.SkipValue(); err != nil {`); - writeLine(`\t\t\t\treturn err`); - writeLine(`\t\t\t}`); - writeLine(`\t\t}`); - writeLine(`\t}`); - writeLine(""); - writeLine(`\tif _, err := dec.ReadToken(); err != nil {`); - writeLine(`\t\treturn err`); - writeLine(`\t}`); - writeLine(""); - writeLine(`\tif missing != 0 {`); - writeLine(`\t\tvar missingProps []string`); - writeLine(`\t\tif missing&missingId != 0 {`); - writeLine(`\t\t\tmissingProps = append(missingProps, "id")`); - writeLine(`\t\t}`); - writeLine(`\t\tif missing&missingMethod != 0 {`); - writeLine(`\t\t\tmissingProps = append(missingProps, "method")`); - writeLine(`\t\t}`); - writeLine(`\t\treturn errMissing(missingProps)`); - writeLine(`\t}`); - writeLine(""); - writeLine(`\tif len(rawRegisterOptions) > 0 {`); - writeLine(`\t\ts.RegisterOptions = &RegisterOptions{}`); - writeLine(`\t\tswitch Method(method) {`); - for (const reg of registrationMethods) { - writeLine(`\t\tcase Method${reg.fieldName}:`); - writeLine(`\t\t\tvar v ${reg.optionsTypeName}`); - writeLine(`\t\t\tif err := json.Unmarshal(rawRegisterOptions, &v); err != nil {`); - writeLine(`\t\t\t\treturn err`); - writeLine(`\t\t\t}`); - writeLine(`\t\t\ts.RegisterOptions.${reg.fieldName} = &v`); - } - writeLine(`\t\tdefault:`); - writeLine(`\t\t\treturn fmt.Errorf("unknown registration method: %s", method)`); - writeLine(`\t\t}`); - writeLine(`\t} else {`); - writeLine(`\t\treturn fmt.Errorf("missing registerOptions for method: %s", method)`); - writeLine(`\t}`); - writeLine(""); - writeLine(`\treturn nil`); - writeLine(`}`); - writeLine(""); - } - - if (compareStructures.has(structure.name)) { - generateCompareMethod(structure); - } - } - - function generateCompareMethod(structure: Structure) { - const props = structure.properties ?? []; - writeLine(`func (s *${structure.name}) Compare(other *${structure.name}) int {`); - for (let i = 0; i < props.length; i++) { - const prop = props[i]; - const isLast = i === props.length - 1; - const fieldName = goFieldName(prop); - const expr = compareExpressionForProperty(structure.name, prop, fieldName); - if (isLast) { - writeLine(`\treturn ${expr}`); - } - else { - writeLine(`\tif c := ${expr}; c != 0 {`); - writeLine(`\t\treturn c`); - writeLine(`\t}`); - } - } - writeLine(`}`); - writeLine(""); - } - - function compareExpressionForProperty(structName: string, prop: Property, fieldName: string): string { - const resolved = resolveType(prop.type); - const isPointerField = (prop.optional || resolved.needsPointer) && !prop.omitzeroValue; - - if (prop.type.kind === "reference") { - const refName = prop.type.name; - if (compareStructures.has(refName)) { - if (isPointerField) { - return `s.${fieldName}.Compare(other.${fieldName})`; - } - return `s.${fieldName}.Compare(&other.${fieldName})`; - } - } - - if (prop.type.kind === "base") { - switch (prop.type.name) { - case "string": - case "URI": - case "DocumentUri": - case "integer": - case "uinteger": - case "decimal": - return `cmp.Compare(s.${fieldName}, other.${fieldName})`; - } - } - - throw new Error(`Cannot generate Compare for ${structName}.${fieldName}: unsupported field type ${JSON.stringify(prop.type)}. Add support in compareExpressionForProperty.`); - } - - // Helper function to detect if an enum is a bitflag enum - // Hardcoded list of bitflag enums - const bitflagEnums = new Set(["WatchKind"]); - - function isBitflagEnum(enumeration: any): boolean { - return bitflagEnums.has(enumeration.name); - } - - // Generate enumerations - writeLine("// Enumerations\n"); - - for (const enumeration of model.enumerations) { - write(formatDocumentation(enumeration.documentation)); - - let baseType; - switch (enumeration.type.name) { - case "string": - baseType = "string"; - break; - case "integer": - baseType = "int32"; - break; - case "uinteger": - baseType = "uint32"; - break; - default: - throw new Error(`Unsupported enum type: ${enumeration.type.name}`); - } - - writeLine(`type ${enumeration.name} ${baseType}`); - writeLine(""); - - // Get the pre-processed enum entries map that avoids duplicates - - const enumValues = enumeration.values.map(value => ({ - value: String(value.value), - numericValue: Number(value.value), - name: value.name, - identifier: `${enumeration.name}${titleCase(value.name)}`, - documentation: value.documentation, - deprecated: value.deprecated, - })); - - writeLine("const ("); - - // Process entries with unique identifiers - for (const entry of enumValues) { - write(formatDocumentation(entry.documentation)); - - let valueLiteral; - // Handle string values - if (enumeration.type.name === "string") { - valueLiteral = `"${entry.value.replace(/^"|"$/g, "")}"`; - } - else { - valueLiteral = entry.value; - } - - writeLine(`\t${entry.identifier} ${enumeration.name} = ${valueLiteral}`); - } - - writeLine(")"); - writeLine(""); - - // Generate String() method for non-string enums - if (enumeration.type.name !== "string") { - const isBitflag = isBitflagEnum(enumeration); - - if (isBitflag) { - // Generate bitflag-aware String() method using stringer-style efficiency - const sortedValues = [...enumValues].sort((a, b) => a.numericValue - b.numericValue); - const names = sortedValues.map(v => v.name); - const values = sortedValues.map(v => v.numericValue); - - const nameConst = `_${enumeration.name}_name`; - const indexVar = `_${enumeration.name}_index`; - const combinedNames = names.join(""); - - writeLine(`const ${nameConst} = "${combinedNames}"`); - write(`var ${indexVar} = [...]uint16{0`); - let offset = 0; - for (const name of names) { - offset += name.length; - write(`, ${offset}`); - } - writeLine(`}`); - writeLine(""); - - writeLine(`func (e ${enumeration.name}) String() string {`); - writeLine(`\tif e == 0 {`); - writeLine(`\t\treturn "0"`); - writeLine(`\t}`); - writeLine(`\tvar parts []string`); - for (let i = 0; i < values.length; i++) { - writeLine(`\tif e&${values[i]} != 0 {`); - writeLine(`\t\tparts = append(parts, ${nameConst}[${indexVar}[${i}]:${indexVar}[${i + 1}]])`); - writeLine(`\t}`); - } - writeLine(`\tif len(parts) == 0 {`); - writeLine(`\t\treturn fmt.Sprintf("${enumeration.name}(%d)", e)`); - writeLine(`\t}`); - writeLine(`\treturn strings.Join(parts, "|")`); - writeLine(`}`); - writeLine(""); - } - else { - // Generate regular String() method using stringer-style approach - // Split values into runs of contiguous values - const sortedValues = [...enumValues].sort((a, b) => a.numericValue - b.numericValue); - - // Split into runs - const runs: Array<{ names: string[]; values: number[]; }> = []; - let currentRun = { names: [sortedValues[0].name], values: [sortedValues[0].numericValue] }; - - for (let i = 1; i < sortedValues.length; i++) { - if (sortedValues[i].numericValue === sortedValues[i - 1].numericValue + 1) { - currentRun.names.push(sortedValues[i].name); - currentRun.values.push(sortedValues[i].numericValue); - } - else { - runs.push(currentRun); - currentRun = { names: [sortedValues[i].name], values: [sortedValues[i].numericValue] }; - } - } - runs.push(currentRun); - - const nameConst = `_${enumeration.name}_name`; - const indexVar = `_${enumeration.name}_index`; - - if (runs.length === 1) { - // Single contiguous run - simple case - const combinedNames = runs[0].names.join(""); - writeLine(`const ${nameConst} = "${combinedNames}"`); - write(`var ${indexVar} = [...]uint16{0`); - let offset = 0; - for (const name of runs[0].names) { - offset += name.length; - write(`, ${offset}`); - } - writeLine(`}`); - writeLine(""); - - const minVal = runs[0].values[0]; - writeLine(`func (e ${enumeration.name}) String() string {`); - writeLine(`\ti := int(e) - ${minVal}`); - // For unsigned types, i can still be negative if e < minVal (due to underflow in conversion) - // So we always need to check both bounds - writeLine(`\tif i < 0 || i >= len(${indexVar})-1 {`); - writeLine(`\t\treturn fmt.Sprintf("${enumeration.name}(%d)", e)`); - writeLine(`\t}`); - writeLine(`\treturn ${nameConst}[${indexVar}[i]:${indexVar}[i+1]]`); - writeLine(`}`); - writeLine(""); - } - else if (runs.length <= 10) { - // Multiple runs - use switch statement - let allNames = ""; - const runInfo: Array<{ startOffset: number; endOffset: number; minVal: number; maxVal: number; }> = []; - - for (const run of runs) { - const startOffset = allNames.length; - allNames += run.names.join(""); - const endOffset = allNames.length; - runInfo.push({ - startOffset, - endOffset, - minVal: run.values[0], - maxVal: run.values[run.values.length - 1], - }); - } - - writeLine(`const ${nameConst} = "${allNames}"`); - writeLine(""); - - // Generate index variables for each run - for (let i = 0; i < runs.length; i++) { - write(`var ${indexVar}_${i} = [...]uint16{0`); - let offset = 0; - for (const name of runs[i].names) { - offset += name.length; - write(`, ${offset}`); - } - writeLine(`}`); - } - writeLine(""); - - writeLine(`func (e ${enumeration.name}) String() string {`); - writeLine(`\tswitch {`); - - for (let i = 0; i < runs.length; i++) { - const run = runs[i]; - const info = runInfo[i]; - - if (run.values.length === 1) { - writeLine(`\tcase e == ${run.values[0]}:`); - writeLine(`\t\treturn ${nameConst}[${info.startOffset}:${info.endOffset}]`); - } - else { - if (info.minVal === 0 && baseType.startsWith("uint")) { - writeLine(`\tcase e <= ${info.maxVal}:`); - } - else if (info.minVal === 0) { - writeLine(`\tcase 0 <= e && e <= ${info.maxVal}:`); - } - else { - writeLine(`\tcase ${info.minVal} <= e && e <= ${info.maxVal}:`); - } - writeLine(`\t\ti := int(e) - ${info.minVal}`); - writeLine(`\t\treturn ${nameConst}[${info.startOffset}+${indexVar}_${i}[i]:${info.startOffset}+${indexVar}_${i}[i+1]]`); - } - } - - writeLine(`\tdefault:`); - writeLine(`\t\treturn fmt.Sprintf("${enumeration.name}(%d)", e)`); - writeLine(`\t}`); - writeLine(`}`); - writeLine(""); - } - else { - // Too many runs - use a map - let allNames = ""; - const valueMap: Array<{ value: number; startOffset: number; endOffset: number; }> = []; - - for (const run of runs) { - for (let i = 0; i < run.names.length; i++) { - const startOffset = allNames.length; - allNames += run.names[i]; - const endOffset = allNames.length; - valueMap.push({ value: run.values[i], startOffset, endOffset }); - } - } - - writeLine(`const ${nameConst} = "${allNames}"`); - writeLine(""); - writeLine(`var ${enumeration.name}_map = map[${enumeration.name}]string{`); - for (const entry of valueMap) { - writeLine(`\t${entry.value}: ${nameConst}[${entry.startOffset}:${entry.endOffset}],`); - } - writeLine(`}`); - writeLine(""); - - writeLine(`func (e ${enumeration.name}) String() string {`); - writeLine(`\tif str, ok := ${enumeration.name}_map[e]; ok {`); - writeLine(`\t\treturn str`); - writeLine(`\t}`); - writeLine(`\treturn fmt.Sprintf("${enumeration.name}(%d)", e)`); - writeLine(`}`); - writeLine(""); - } - } - } - - // Generate Error() method for ErrorCode to implement the error interface - if (enumeration.name === "ErrorCode") { - writeLine(`func (e ${enumeration.name}) Error() string {`); - writeLine(`\treturn e.String()`); - writeLine(`}`); - writeLine(""); - } - } - - const requestsAndNotifications: (Request | Notification)[] = [...model.requests, ...model.notifications]; - - writeLine("// Methods"); - writeLine("const ("); - for (const request of requestsAndNotifications) { - write(formatDocumentation(request.documentation)); - - const methodName = methodNameIdentifier(request.method); - - writeLine(`\tMethod${methodName} Method = "${request.method}"`); - } - // Emit constants for registration-only methods (not also a request/notification) - for (const reg of registrationMethods) { - if (reg.isRegistrationOnly) { - writeLine(`\t// Registration-only method for ${reg.registrationMethod}.`); - writeLine(`\tMethod${reg.fieldName} Method = "${reg.registrationMethod}"`); - } - } - writeLine(")"); - writeLine(""); - - // Generate request response types - writeLine("// Request response types"); - writeLine(""); - - for (const request of requestsAndNotifications) { - const methodName = methodNameIdentifier(request.method); - - let responseTypeName: string | undefined; - - if ("result" in request) { - if (request.typeName && request.typeName.endsWith("Request")) { - responseTypeName = request.typeName.replace(/Request$/, "Response"); - } - else { - responseTypeName = `${methodName}Response`; - } - - writeLine(`// Response type for \`${request.method}\``); - - // Special case for response types that are explicitly base type "null" - if (request.result.kind === "base" && request.result.name === "null") { - writeLine(`type ${responseTypeName} = Null`); - } - else { - const resultType = resolveType(request.result); - const goType = resultType.needsPointer ? `*${resultType.name}` : resultType.name; - writeLine(`type ${responseTypeName} = ${goType}`); - } - writeLine(""); - } - - if (Array.isArray(request.params)) { - throw new Error("Unexpected request params for " + methodName + ": " + JSON.stringify(request.params)); - } - - const paramType = request.params ? resolveType(request.params) : undefined; - const paramGoType = paramType ? (paramType.needsPointer ? `*${paramType.name}` : paramType.name) : "NoParams"; - - writeLine(`// Type mapping info for \`${request.method}\``); - if (responseTypeName) { - writeLine(`var ${methodName}Info = RequestInfo[${paramGoType}, ${responseTypeName}]{Method: Method${methodName}}`); - } - else { - writeLine(`var ${methodName}Info = NotificationInfo[${paramGoType}]{Method: Method${methodName}}`); - } - - writeLine(""); - } - - // Generate type aliases - writeLine("// Type aliases\n"); - for (const aliasName of customTypeAliases) { - const resolvedType = resolveType(aliasName.type); - const goType = resolvedType.needsPointer ? `*${resolvedType.name}` : resolvedType.name; - writeLine(`type ${aliasName.name} = ${goType}`); - writeLine(""); - } - - // Generate union types - writeLine("// Union types\n"); - - for (const [name, members] of typeInfo.unionTypes.entries()) { - writeLine(`type ${name} struct {`); - const uniqueTypeFields = new Map(); // Maps type name -> field name - const uniqueTypeToOriginal = new Map(); // Maps type name -> original meta model Type - - let hasLocations = false; - for (const member of members) { - const type = resolveType(member.type); - const memberType = type.name; - - // If this type name already exists in our map, skip it - if (!uniqueTypeFields.has(memberType)) { - const fieldName = titleCase(member.name); - uniqueTypeFields.set(memberType, fieldName); - uniqueTypeToOriginal.set(memberType, member.type); - writeLine(`\t${fieldName} *${memberType}`); - if (fieldName === "Locations" && memberType === "[]Location") { - hasLocations = true; - } - } - } - - writeLine(`}`); - writeLine(""); - - // Get the field names and types for marshal/unmarshal methods - const fieldEntries = Array.from(uniqueTypeFields.entries()).map(([typeName, fieldName]) => ({ - fieldName, - typeName, - originalType: uniqueTypeToOriginal.get(typeName)!, - })); - - // Marshal method - writeLine(`var _ json.MarshalerTo = (*${name})(nil)`); - writeLine(""); - const unionContainedNull = members.some(member => member.containedNull); - writeLine(`func (o *${name}) MarshalJSONTo(enc *json.Encoder) error {`); - writeLine(`\treturn marshalUnion(o, enc, "${name}", ${unionContainedNull})`); - writeLine(`}`); - writeLine(""); - - // Unmarshal method - writeLine(`var _ json.UnmarshalerFrom = (*${name})(nil)`); - writeLine(""); - - writeLine(`func (o *${name}) UnmarshalJSONFrom(dec *json.Decoder) error {`); - writeLine(`\t*o = ${name}{}`); - writeLine(""); - - // Group field entries by their expected JSON token kind for optimized dispatch. - const kindMap = new Map(); - const unknownKindEntries: typeof fieldEntries = []; - for (const entry of fieldEntries) { - const kind = jsonKindForType(entry.originalType); - if (!kind) { - unknownKindEntries.push(entry); - } - else { - if (!kindMap.has(kind)) kindMap.set(kind, []); - kindMap.get(kind)!.push(entry); - } - } - - // Sort ambiguous variants (same JSON kind) by number of required fields - // descending, so more specific variants are tried first. This prevents - // a less specific variant from greedily matching inputs intended for - // a more specific one. - function countRequiredFields(entry: typeof fieldEntries[0]): number { - if (entry.originalType.kind !== "reference") return 0; - const structure = model.structures.find(s => s.name === (entry.originalType as ReferenceType).name); - if (!structure) return 0; - return structure.properties.filter(p => !p.optional && !p.omitzeroValue).length; - } - - for (const [, entries] of kindMap) { - if (entries.length > 1) { - entries.sort((a, b) => countRequiredFields(b) - countRequiredFields(a)); - } - } - - // Also sort the flat fieldEntries to match (for the fallback path) - // We need to sort only within groups of the same kind. - { - const sorted: typeof fieldEntries = []; - const seen = new Set(); - for (const [, entries] of kindMap) { - for (const entry of entries) { - sorted.push(entry); - seen.add(entry.fieldName); - } - } - for (const entry of unknownKindEntries) { - if (!seen.has(entry.fieldName)) { - sorted.push(entry); - } - } - // Replace fieldEntries contents with sorted order - fieldEntries.length = 0; - fieldEntries.push(...sorted); - } - - // Validate that ambiguous union variants (same JSON kind) don't have - // order-dependent overlap. Two struct variants overlap if one's required - // fields are a subset of the other's, meaning any valid input for the - // superset also successfully parses as the subset (since unknown properties - // are ignored). This would make the unmarshal result depend on try order. - // - // Exception: variants discriminated by literal field values (e.g., a "kind" - // field with different string literal types) are safe because the literal - // unmarshaler rejects mismatched values. - for (const [kind, entries] of kindMap) { - if (entries.length <= 1) continue; - - // Get required fields with their types for each variant - const variantInfo = entries.map(entry => { - if (entry.originalType.kind !== "reference") return null; - const structure = model.structures.find(s => s.name === (entry.originalType as ReferenceType).name); - if (!structure) return null; - const requiredFields = new Map(); - for (const p of structure.properties) { - if (!p.optional && !p.omitzeroValue) { - requiredFields.set(p.name, p.type); - } - } - return { entry, requiredFields }; - }).filter((v): v is NonNullable => v !== null); - - // Check if two variants are discriminated by literal field values. - // Returns true if they share a field where both sides have different - // literal types (stringLiteral, integerLiteral, booleanLiteral). - function isDiscriminatedByLiteral( - a: Map, - b: Map, - ): boolean { - for (const [fieldName, aType] of a) { - const bType = b.get(fieldName); - if (!bType) continue; - const aLiteral = aType.kind === "stringLiteral" || aType.kind === "integerLiteral" || aType.kind === "booleanLiteral"; - const bLiteral = bType.kind === "stringLiteral" || bType.kind === "integerLiteral" || bType.kind === "booleanLiteral"; - if (aLiteral && bLiteral) { - // Both are literals for the same field — check if values differ - if (aType.kind === bType.kind && (aType as any).value !== (bType as any).value) { - return true; - } - // Different literal kinds on same field also discriminates - if (aType.kind !== bType.kind) { - return true; - } - } - } - return false; - } - - // Check each pair for subset relationships - for (let i = 0; i < variantInfo.length; i++) { - for (let j = 0; j < variantInfo.length; j++) { - if (i === j) continue; - const a = variantInfo[i]; - const b = variantInfo[j]; - const aNames = new Set(a.requiredFields.keys()); - const bNames = new Set(b.requiredFields.keys()); - - const aSubsetOfB = [...aNames].every(f => bNames.has(f)); - if (!aSubsetOfB) continue; - - // Skip if discriminated by literal values - if (isDiscriminatedByLiteral(a.requiredFields, b.requiredFields)) continue; - - if (aNames.size < bNames.size) { - // a is a strict subset of b - const aIdx = entries.indexOf(a.entry); - const bIdx = entries.indexOf(b.entry); - if (aIdx < bIdx) { - console.warn( - `Warning: In union ${name} (${kind} variants), ` + - `${a.entry.fieldName} (required: [${[...aNames]}]) is tried before ` + - `${b.entry.fieldName} (required: [${[...bNames]}]), but ` + - `${a.entry.fieldName}'s required fields are a strict subset — ` + - `it will greedily match inputs intended for ${b.entry.fieldName}. ` + - `Reorder so the more specific variant is tried first.`, - ); - } - } - else if (aNames.size === bNames.size && i < j) { - // Identical required fields — truly ambiguous - console.warn( - `Warning: In union ${name} (${kind} variants), ` + - `${a.entry.fieldName} and ${b.entry.fieldName} have identical ` + - `required fields [${[...aNames]}] — they are structurally ` + - `indistinguishable and the unmarshal result is order-dependent.`, - ); - } - } - } - } - - // Determine if we can use PeekKind-based dispatch: - // - Every entry must have a known kind (no `any` etc.) - // - There must be at least 2 distinct cases (kind groups + null) for a switch to be worthwhile - const hasUnknownKinds = unknownKindEntries.length > 0; - const distinctKinds = kindMap.size + (unionContainedNull ? 1 : 0); - const canDispatch = !hasUnknownKinds && distinctKinds >= 2; - - // Check if all kind groups are unambiguous (exactly 1 entry each). - // When unambiguous, we can UnmarshalDecode directly without buffering. - const allUnambiguous = canDispatch && Array.from(kindMap.values()).every(entries => entries.length === 1); - - let fallbackExhaustive = false; - const hasBooleanKind = kindMap.has("boolean"); - if (canDispatch && allUnambiguous) { - // Best case: PeekKind + UnmarshalDecode directly, no ReadValue buffer needed. - if (hasBooleanKind) { - writeLine(`\tswitch kind := dec.PeekKind(); kind {`); - } - else { - writeLine(`\tswitch dec.PeekKind() {`); - } - - if (unionContainedNull) { - writeLine(`\tcase 'n':`); - writeLine(`\t\t_, err := dec.ReadToken()`); - writeLine(`\t\treturn err`); - } - - for (const [kind, entries] of kindMap) { - writeLine(`\t${goKindCasesForJsonKind(kind)}`); - const entry = entries[0]; - if (kind === "boolean") { - writeLine(`\t\to.${entry.fieldName} = new(kind == 't')`); - writeLine(`\t\t_, err := dec.ReadToken()`); - writeLine(`\t\treturn err`); - } - else { - writeLine(`\t\to.${entry.fieldName} = new(${entry.typeName})`); - writeLine(`\t\treturn json.UnmarshalDecode(dec, o.${entry.fieldName})`); - } - } - - writeLine(`\tdefault:`); - writeLine(`\t\treturn errInvalidKind("${name}", dec.PeekKind())`); - writeLine(`\t}`); - } - else if (canDispatch) { - // Mixed case: some kind groups have multiple entries. - // Use PeekKind to dispatch, then ReadValue + try-each within ambiguous groups, - // or UnmarshalDecode directly for unambiguous groups. - if (hasBooleanKind) { - writeLine(`\tswitch kind := dec.PeekKind(); kind {`); - } - else { - writeLine(`\tswitch dec.PeekKind() {`); - } - - if (unionContainedNull) { - writeLine(`\tcase 'n':`); - writeLine(`\t\t_, err := dec.ReadToken()`); - writeLine(`\t\treturn err`); - } - - for (const [kind, entries] of kindMap) { - writeLine(`\t${goKindCasesForJsonKind(kind)}`); - if (entries.length === 1) { - // Unambiguous: decode directly - const entry = entries[0]; - if (kind === "boolean") { - writeLine(`\t\to.${entry.fieldName} = new(kind == 't')`); - writeLine(`\t\t_, err := dec.ReadToken()`); - writeLine(`\t\treturn err`); - } - else { - writeLine(`\t\to.${entry.fieldName} = new(${entry.typeName})`); - writeLine(`\t\treturn json.UnmarshalDecode(dec, o.${entry.fieldName})`); - } - } - else { - // Ambiguous: buffer and dispatch - writeLine(`\t\tdata, err := dec.ReadValue()`); - writeLine(`\t\tif err != nil {`); - writeLine(`\t\t\treturn err`); - writeLine(`\t\t}`); - let exhaustive = false; - const disc = findDiscriminatorField(entries); - if (disc) { - exhaustive = generateDiscriminatorDispatch(disc, "\t\t"); - } - else { - const pres = findPresenceDiscriminator(entries); - if (pres) { - exhaustive = generatePresenceDispatch(pres, "\t\t"); - } - else { - for (const entry of entries) { - writeLine(`\t\tvar v${entry.fieldName} ${entry.typeName}`); - writeLine(`\t\tif err := json.Unmarshal(data, &v${entry.fieldName}); err == nil {`); - writeLine(`\t\t\to.${entry.fieldName} = &v${entry.fieldName}`); - writeLine(`\t\t\treturn nil`); - writeLine(`\t\t}`); - } - } - } - if (!exhaustive) { - writeLine(`\t\treturn errInvalidValue("${name}", data)`); - } - } - } - - writeLine(`\tdefault:`); - writeLine(`\t\treturn errInvalidKind("${name}", dec.PeekKind())`); - writeLine(`\t}`); - } - else { - // Fallback: unknown kinds present (e.g. `any`), use ReadValue + try-each. - writeLine("\tdata, err := dec.ReadValue()"); - writeLine("\tif err != nil {"); - writeLine("\t\treturn err"); - writeLine("\t}"); - - if (unionContainedNull) { - writeLine(`\tif string(data) == "null" {`); - writeLine(`\t\treturn nil`); - writeLine(`\t}`); - writeLine(""); - } - - let exhaustive = false; - const disc = findDiscriminatorField(fieldEntries); - if (disc) { - exhaustive = generateDiscriminatorDispatch(disc, "\t"); - } - else { - const pres = findPresenceDiscriminator(fieldEntries); - if (pres) { - exhaustive = generatePresenceDispatch(pres, "\t"); - } - else { - for (const entry of fieldEntries) { - writeLine(`\tvar v${entry.fieldName} ${entry.typeName}`); - writeLine(`\tif err := json.Unmarshal(data, &v${entry.fieldName}); err == nil {`); - writeLine(`\t\to.${entry.fieldName} = &v${entry.fieldName}`); - writeLine(`\t\treturn nil`); - writeLine(`\t}`); - } - } - } - fallbackExhaustive = exhaustive; - } - - if (canDispatch) { - // Dispatch paths have an exhaustive switch with default, nothing after the switch. - } - else if (!fallbackExhaustive) { - // Fallback paths: the final error references `data` which is in scope. - writeLine(`\treturn errInvalidValue("${name}", data)`); - } - writeLine(`}`); - writeLine(""); - - // Generate GetLocations method - if (hasLocations) { - writeLine(`func (o ${name}) GetLocations() *[]Location {`); - writeLine(`\treturn o.Locations`); - writeLine(`}`); - writeLine(""); - } - } - - // Generate literal types - writeLine("// Literal types\n"); - - for (const [value, name] of typeInfo.literalTypes.entries()) { - const jsonValue = JSON.stringify(value); - - writeLine(`// ${name} is a literal type for ${jsonValue}`); - writeLine(`type ${name} struct{}`); - writeLine(""); - - writeLine(`var _ json.MarshalerTo = ${name}{}`); - writeLine(""); - - writeLine(`func (o ${name}) MarshalJSONTo(enc *json.Encoder) error {`); - writeLine(`\treturn enc.WriteValue(json.Value(\`${jsonValue}\`))`); - writeLine(`}`); - writeLine(""); - - writeLine(`var _ json.UnmarshalerFrom = &${name}{}`); - writeLine(""); - - writeLine(`func (o *${name}) UnmarshalJSONFrom(dec *json.Decoder) error {`); - writeLine(`\tv, err := dec.ReadValue();`); - writeLine(`\tif err != nil {`); - writeLine(`\t\treturn err`); - writeLine(`\t}`); - writeLine(`\tif string(v) != \`${jsonValue}\` {`); - writeLine(`\t\treturn errLiteralMismatch("${name}", \`${jsonValue}\`, v)`); - writeLine(`\t}`); - writeLine(`\treturn nil`); - writeLine(`}`); - writeLine(""); - } - - // Generate resolved capabilities - const clientCapsStructure = model.structures.find(s => s.name === "ClientCapabilities"); - if (clientCapsStructure) { - writeLine("// Helper function for dereferencing pointers with zero value fallback"); - writeLine("func derefOr[T any](v *T) T {"); - writeLine("\tif v != nil {"); - writeLine("\t\treturn *v"); - writeLine("\t}"); - writeLine("\tvar zero T"); - writeLine("\treturn zero"); - writeLine("}"); - writeLine(""); - - // Collect all dependent structures and generate their resolved types - const deps = collectStructureDependencies(clientCapsStructure); - const uniqueDeps = Array.from(new Map(deps.map(d => [d.name, d])).values()); - - for (const dep of uniqueDeps) { - const depLines = generateResolvedTypeAndHelper(dep, false); - for (const line of depLines) { - writeLine(line); - } - } - - // Generate the main ResolvedClientCapabilities type and function - writeLine("// ResolvedClientCapabilities is a version of ClientCapabilities where all nested"); - writeLine("// fields are values (not pointers), making it easier to access deeply nested capabilities."); - writeLine("// Use (*ClientCapabilities).Resolve() to convert from ClientCapabilities."); - if (clientCapsStructure.documentation) { - writeLine("//"); - const typeDoc = formatDocumentation(clientCapsStructure.documentation); - for (const line of typeDoc.split("\n").filter(l => l)) { - writeLine(line); - } - } - const mainLines = generateResolvedTypeAndHelper(clientCapsStructure, true); - for (const line of mainLines) { - writeLine(line); - } - } - - return parts.join(""); -} - -function hasSomeProp(structure: Structure, propName: string, propTypeName: string) { - return structure.properties?.some(p => - !p.optional && - p.name === propName && - p.type.kind === "reference" && - p.type.name === propTypeName - ); -} - -function hasTextDocumentURI(structure: Structure) { - return hasSomeProp(structure, "textDocument", "TextDocumentIdentifier") || - hasSomeProp(structure, "_vs_textDocument", "TextDocumentIdentifier"); -} - -function hasTextDocumentPosition(structure: Structure) { - return hasSomeProp(structure, "position", "Position") || - hasSomeProp(structure, "_vs_position", "Position"); -} - -function getLocationUriProperty(structure: Structure) { - const prop = structure.properties?.find(p => - !p.optional && - titleCase(p.name).endsWith("Uri") && - p.type.kind === "base" && - p.type.name === "DocumentUri" - ); - if ( - prop && - structure.properties.some(p => - !p.optional && - titleCase(p.name) === titleCase(prop.name).replace(/Uri$/, "Range") && - p.type.kind === "reference" && - p.type.name === "Range" - ) - ) { - return titleCase(prop.name); - } -} - -/** - * Main function - */ -async function main() { - collectTypeDefinitions(); - const generatedCode = generateCode(); - fs.writeFileSync(out, generatedCode); - - await $({ cwd: repoRoot })`dprint fmt ${out}`; - - console.log(`Successfully generated ${out}`); -} - -main().catch(e => { - console.error(e); - process.exit(1); -}); +#!/usr/bin/env -S node --experimental-strip-types + +// Usage: node --experimental-strip-types generate.mts + +import { $ } from "execa"; +import fs from "node:fs"; +import path from "node:path"; +import url from "node:url"; +import type { + Enumeration, + MetaModel, + Notification, + OrType, + Property, + ReferenceType, + Request, + Structure, + Type, + TypeAlias, +} from "./metaModelSchema.mts"; + +const __filename = url.fileURLToPath(new URL(import.meta.url)); +const __dirname = path.dirname(__filename); +const repoRoot = path.resolve(__dirname, "../../../.."); + +const out = path.resolve(__dirname, "../lsp_generated.go"); +const metaModelPath = path.resolve(__dirname, "metaModel.json"); + +if (!fs.existsSync(metaModelPath)) { + console.error("Meta model file not found; did you forget to run fetchModel.mjs?"); + process.exit(1); +} + +const model: MetaModel = JSON.parse(fs.readFileSync(metaModelPath, "utf-8")); + +// Custom structures to add to the model +const customStructures: Structure[] = [ + { + name: "InitializationOptions", + properties: [ + { + name: "disablePushDiagnostics", + type: { kind: "base", name: "boolean" }, + optional: true, + documentation: "DisablePushDiagnostics disables automatic pushing of diagnostics to the client.", + }, + { + name: "codeLensShowLocationsCommandName", + type: { kind: "base", name: "string" }, + optional: true, + documentation: "The client-side command name that resolved references/implementations `CodeLens` should trigger. Arguments passed will be `(DocumentUri, Position, Location[])`.", + }, + { + name: "userPreferences", + type: { kind: "reference", name: "any" }, + optional: true, + documentation: "userPreferences and/or formatting options if provided at initialization.", + }, + { + name: "enableTelemetry", + type: { kind: "base", name: "boolean" }, + optional: true, + documentation: "EnableTelemetry enables sending telemetry events from the server to the client.", + }, + { + name: "logVerbosity", + type: { kind: "reference", name: "LogVerbosity" }, + optional: true, + documentation: "The initial log verbosity level, matching the client's output channel log level at startup. Subsequent changes are sent via custom/setLogVerbosity.", + }, + { + name: "runExternalCode", + type: { kind: "base", name: "boolean" }, + optional: true, + documentation: "RunExternalCode allows configured content mappers to launch external plugin processes. The client should set this only for trusted workspaces. It mirrors the --runExternalCode CLI flag.", + }, + { + name: "trackFlakyDiagnostics", + type: { kind: "reference", name: "DiagnosticFlakeLogLevel" }, + optional: true, + documentation: "The level at which we track flaky diagnostics, if at all.", + }, + ], + documentation: "InitializationOptions contains user-provided initialization options.", + }, + { + name: "AutoImportFix", + properties: [ + { + name: "kind", + type: { kind: "reference", name: "AutoImportFixKind" }, + omitzeroValue: true, + }, + { + name: "name", + type: { kind: "base", name: "string" }, + omitzeroValue: true, + }, + { + name: "importKind", + type: { kind: "reference", name: "ImportKind" }, + }, + { + name: "useRequire", + type: { kind: "base", name: "boolean" }, + omitzeroValue: true, + }, + { + name: "addAsTypeOnly", + type: { kind: "reference", name: "AddAsTypeOnly" }, + }, + { + name: "moduleSpecifier", + type: { kind: "base", name: "string" }, + documentation: "The module specifier for this auto-import.", + omitzeroValue: true, + }, + { + name: "importIndex", + type: { kind: "base", name: "integer" }, + documentation: "Index of the import to modify when adding to an existing import declaration.", + }, + { + name: "usagePosition", + type: { kind: "reference", name: "Position" }, + optional: true, + }, + { + name: "namespacePrefix", + type: { kind: "base", name: "string" }, + omitzeroValue: true, + }, + ], + documentation: "AutoImportFix contains information about an auto-import suggestion.", + }, + { + name: "CompletionItemData", + properties: [ + { + name: "fileName", + type: { kind: "base", name: "string" }, + documentation: "The file name where the completion was requested.", + omitzeroValue: true, + }, + { + name: "position", + type: { kind: "base", name: "integer" }, + documentation: "The position where the completion was requested.", + omitzeroValue: true, + }, + { + name: "supplementalFileIndex", + type: { kind: "base", name: "integer" }, + optional: true, + documentation: "Zero-based index into the canonical file's supplemental source files. Absent when the completion was requested in the canonical file.", + }, + { + name: "source", + type: { kind: "base", name: "string" }, + documentation: "Special source value for disambiguation.", + omitzeroValue: true, + }, + { + name: "name", + type: { kind: "base", name: "string" }, + documentation: "The name of the completion item.", + omitzeroValue: true, + }, + { + name: "autoImport", + type: { kind: "reference", name: "AutoImportFix" }, + optional: true, + documentation: "Auto-import data for this completion item.", + }, + { + name: "isImportStatementCompletion", + type: { kind: "base", name: "boolean" }, + omitzeroValue: true, + }, + ], + documentation: "CompletionItemData is preserved on a CompletionItem between CompletionRequest and CompletionResolveRequest.", + }, + { + name: "CodeLensData", + properties: [ + { + name: "kind", + type: { kind: "reference", name: "CodeLensKind" }, + documentation: `The kind of the code lens ("references" or "implementations").`, + }, + { + name: "uri", + type: { kind: "base", name: "DocumentUri" }, + documentation: `The document in which the code lens and its range are located.`, + }, + { + name: "position", + type: { kind: "base", name: "integer" }, + documentation: `The position of the code lens declaration in its virtual source file.`, + }, + { + name: "supplementalFileIndex", + type: { kind: "base", name: "integer" }, + optional: true, + documentation: `Zero-based index into the canonical file's supplemental source files. Absent for the canonical source file.`, + }, + ], + }, + { + name: "ExperimentalServerCapabilities", + properties: [ + { + name: "customSourceDefinitionProvider", + type: { kind: "base", name: "boolean" }, + optional: true, + documentation: "The server provides source definition support via custom/textDocument/sourceDefinition.", + }, + { + name: "customMultiDocumentHighlightProvider", + type: { kind: "base", name: "boolean" }, + optional: true, + documentation: "The server provides multi-document highlight support via custom/textDocument/multiDocumentHighlight.", + }, + ], + documentation: "ExperimentalServerCapabilities contains experimental capabilities under development.", + }, + { + name: "ExperimentalClientCapabilities", + properties: [ + { + name: "hoverVerbosityLevel", + type: { kind: "base", name: "boolean" }, + optional: true, + documentation: "The client supports hover verbosityLevel requests and canIncreaseVerbosity responses.", + }, + ], + documentation: "ExperimentalClientCapabilities contains experimental capabilities under development.", + }, + { + name: "VSOnAutoInsertOptions", + properties: [ + { + name: "_vs_triggerCharacters", + type: { kind: "array", element: { kind: "base", name: "string" } }, + documentation: "List of trigger characters that trigger auto-insert.", + }, + ], + documentation: "Options for the textDocument/_vs_onAutoInsert provider capability.", + }, + { + name: "VSReferenceItem", + properties: [ + { + name: "_vs_id", + type: { kind: "base", name: "integer" }, + documentation: "Unique identifier for this reference item.", + }, + { + name: "_vs_definitionId", + type: { kind: "base", name: "integer" }, + optional: true, + documentation: "The ID of the definition item this reference belongs to. Absent for definition items themselves.", + }, + { + name: "_vs_kind", + type: { kind: "array", element: { kind: "reference", name: "VSReferenceKind" } }, + optional: true, + documentation: "The kind(s) of this reference (read, write, etc.).", + }, + { + name: "_vs_location", + type: { kind: "reference", name: "Location" }, + documentation: "The location of this reference.", + }, + { + name: "_vs_definitionText", + type: { kind: "reference", name: "VSClassifiedTextElement" }, + optional: true, + documentation: "Classified display text for the definition (used for grouping headers in the UI).", + }, + { + name: "_vs_projectName", + type: { kind: "base", name: "string" }, + optional: true, + documentation: "The project name for this reference.", + }, + { + name: "_vs_containingType", + type: { kind: "base", name: "string" }, + optional: true, + documentation: "The containing type for this reference.", + }, + ], + documentation: "A VS-specific reference item with grouping support for Find All References.", + }, + { + name: "VSOnAutoInsertParams", + properties: [ + { + name: "_vs_textDocument", + type: { kind: "reference", name: "TextDocumentIdentifier" }, + documentation: "The text document.", + }, + { + name: "_vs_position", + type: { kind: "reference", name: "Position" }, + documentation: "The position inside the text document.", + }, + { + name: "_vs_ch", + type: { kind: "base", name: "string" }, + documentation: "The character that triggered the auto-insert.", + }, + ], + documentation: "Parameters for the textDocument/_vs_onAutoInsert request.", + }, + { + name: "VSOnAutoInsertResponseItem", + properties: [ + { + name: "_vs_textEditFormat", + type: { kind: "reference", name: "InsertTextFormat" }, + documentation: "The format of the text edit (plaintext or snippet).", + }, + { + name: "_vs_textEdit", + type: { kind: "reference", name: "TextEdit" }, + documentation: "The text edit to apply for the auto-insertion.", + }, + ], + documentation: "Response item for the textDocument/_vs_onAutoInsert request.", + }, + { + name: "RequestFailureTelemetryEvent", + properties: [ + { + name: "eventName", + type: { kind: "stringLiteral", value: "languageServer.errorResponse" }, + documentation: "The name of the telemetry event.", + }, + { + name: "telemetryPurpose", + type: { kind: "stringLiteral", value: "error" }, + documentation: "Indicates whether the reason for generating the event (e.g. general usage telemetry or errors).", + }, + { + name: "properties", + type: { kind: "reference", name: "RequestFailureTelemetryProperties" }, + documentation: "The properties associated with the event.", + }, + ], + documentation: "A RequestFailureTelemetryEvent is sent when a request fails and the server recovers.", + }, + { + name: "RequestFailureTelemetryProperties", + properties: [ + { + name: "errorCode", + type: { kind: "base", name: "string" }, + documentation: "The error code associated with the event.", + }, + { + name: "requestMethod", + type: { kind: "base", name: "string" }, + documentation: "The method of the request that caused the event.", + }, + { + name: "stack", + type: { kind: "base", name: "string" }, + documentation: "The stack trace associated with the event.", + }, + ], + documentation: "RequestFailureTelemetryProperties contains failure information when an LSP request manages to recover.", + }, + { + name: "ProfileParams", + properties: [ + { + name: "dir", + type: { kind: "base", name: "string" }, + documentation: "The directory path where the profile should be saved.", + }, + ], + documentation: "Parameters for profiling requests.", + }, + { + name: "ProfileResult", + properties: [ + { + name: "file", + type: { kind: "base", name: "string" }, + documentation: "The file path where the profile was saved.", + }, + ], + documentation: "Result of a profiling request.", + }, + { + name: "InitializeAPISessionParams", + properties: [ + { + name: "pipe", + type: { kind: "base", name: "string" }, + optional: true, + documentation: "Optional path to use for the named pipe or Unix domain socket. If not provided, a unique path will be generated.", + }, + ], + documentation: "Parameters for the initializeAPISession request.", + }, + { + name: "InitializeAPISessionResult", + properties: [ + { + name: "sessionId", + type: { kind: "base", name: "string" }, + documentation: "The unique identifier for this API session.", + }, + { + name: "pipe", + type: { kind: "base", name: "string" }, + documentation: "The path to the named pipe or Unix domain socket for API communication.", + }, + ], + documentation: "Result for the initializeAPISession request.", + }, + { + name: "ProjectInfoParams", + properties: [ + { + name: "textDocument", + type: { kind: "reference", name: "TextDocumentIdentifier" }, + documentation: "The text document to get project info for.", + }, + ], + documentation: "Parameters for the custom/projectInfo request.", + }, + { + name: "ProjectInfoResult", + properties: [ + { + name: "configFilePath", + type: { kind: "base", name: "string" }, + documentation: "The absolute path to the config file (e.g. /path/to/tsconfig.json) for the project that contains this file, or an empty string if the file is in an inferred project.", + }, + ], + documentation: "Result for the custom/projectInfo request.", + }, + { + name: "ContentMapperManifest", + properties: [ + { name: "name", type: { kind: "base", name: "string" }, documentation: "Human-readable mapper name." }, + { name: "version", type: { kind: "base", name: "string" }, optional: true, documentation: "Mapper version." }, + { name: "exec", type: { kind: "array", element: { kind: "base", name: "string" } }, documentation: "Executable and arguments used to start the mapper." }, + { name: "cwd", type: { kind: "base", name: "string" }, optional: true, documentation: "Absolute working directory for the mapper process." }, + { name: "compilerOptions", type: { kind: "array", element: { kind: "base", name: "string" } }, optional: true, documentation: "Compiler option names forwarded to the mapper." }, + { name: "dynamicConfig", type: { kind: "base", name: "boolean" }, optional: true, documentation: "Whether the mapper uses project-scoped dynamic configuration." }, + ], + documentation: "Inline content mapper manifest supplied by a contributing extension.", + }, + { + name: "InferredProjectContentMapperContribution", + properties: [ + { name: "options", type: { kind: "reference", name: "LSPObject" }, optional: true, documentation: "Options supplied to transforms in inferred projects." }, + { name: "manifest", type: { kind: "reference", name: "ContentMapperManifest" }, documentation: "Inline manifest for the mapper contributed to inferred projects." }, + ], + documentation: "Content mapper configuration contributed to inferred projects.", + }, + { + name: "ContentMapperContribution", + properties: [ + { name: "contributorId", type: { kind: "base", name: "string" }, documentation: "Unique identifier of the contributor extension." }, + { name: "extensions", type: { kind: "array", element: { kind: "base", name: "string" } }, documentation: "File extensions handled by this content mapper." }, + { name: "inferredProjectContribution", type: { kind: "reference", name: "InferredProjectContentMapperContribution" }, optional: true, documentation: "When present, contributes this mapper to inferred projects." }, + ], + documentation: "One extension-provided content mapper contribution.", + }, + { + name: "SetContentMapperContributionsParams", + properties: [ + { name: "contributions", type: { kind: "array", element: { kind: "reference", name: "ContentMapperContribution" } }, documentation: "Complete replacement set of active extension contributions." }, + { name: "openDocuments", type: { kind: "array", element: { kind: "reference", name: "TextDocumentIdentifier" } }, documentation: "Currently open documents matching contributed extensions." }, + ], + documentation: "Parameters for the custom/setContentMapperContributions request.", + }, + { + name: "SetLogVerbosityParams", + properties: [ + { + name: "verbosity", + type: { kind: "reference", name: "LogVerbosity" }, + documentation: "The log verbosity level.", + }, + ], + documentation: "Parameters for the custom/setLogVerbosity notification.", + }, + { + name: "PerformanceStatsTelemetryEvent", + properties: [ + { + name: "eventName", + type: { kind: "stringLiteral", value: "languageServer.performanceStats" }, + documentation: "The name of the telemetry event.", + }, + { + name: "telemetryPurpose", + type: { kind: "stringLiteral", value: "usage" }, + documentation: "Indicates this is a usage telemetry event.", + }, + { + name: "measurements", + type: { kind: "reference", name: "PerformanceStatsTelemetryMeasurements" }, + documentation: "Numeric measurements for this telemetry event.", + }, + ], + documentation: "A PerformanceStatsTelemetryEvent is sent periodically with performance and resource usage statistics.", + }, + { + name: "PerformanceStatsTelemetryMeasurements", + properties: [ + { name: "openFileCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Number of files currently open in the editor." }, + { name: "uptimeSeconds", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Seconds since the session was initialized." }, + { name: "projectCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Number of loaded projects." }, + { name: "configCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Number of loaded config files." }, + { name: "cachedDiskFileCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Number of files cached from disk." }, + { name: "memoryUsedBytes", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Total memory mapped by the Go runtime in bytes." }, + { name: "goMemLimit", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "GOMEMLIMIT value in bytes, or 0 if not set." }, + { name: "goGCPercent", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "GOGC percentage value configured for the GC." }, + { name: "heapGoalBytes", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Heap size target the GC is working toward in bytes." }, + { name: "heapLiveBytes", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Bytes of live (reachable) heap objects." }, + { name: "heapObjectCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Number of live or unswept objects occupying heap memory." }, + { name: "heapStackBytes", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Heap memory reserved for goroutine stacks." }, + { name: "heapReleasedBytes", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Heap memory returned to the OS." }, + { name: "heapFreeBytes", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Heap memory that is free and eligible to be returned to the OS." }, + { name: "gcScanHeapBytes", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Total scannable heap bytes — how much the GC must traverse." }, + { name: "goMaxProcs", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "The current GOMAXPROCS value." }, + { name: "goroutineCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Current number of goroutines." }, + { name: "gcCyclesTotal", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Total completed GC cycles." }, + { name: "gcCPUSeconds", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Cumulative CPU time spent in GC in seconds." }, + { name: "userCPUSeconds", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Cumulative CPU time spent in user Go code in seconds." }, + { name: "systemMemTotal", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Total physical memory on the system in bytes." }, + { name: "systemMemUsed", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Used physical memory on the system in bytes." }, + { name: "autoImportProjectBucketCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Number of auto-import project buckets." }, + { name: "autoImportNodeModulesBucketCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Number of auto-import node_modules buckets." }, + { name: "autoImportUniquePackageCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Unique packages across all node_modules buckets." }, + { name: "autoImportProjectExportCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Total indexed exports from project files." }, + { name: "autoImportNodeModulesExportCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Total indexed exports from node_modules." }, + { name: "autoImportProjectFileCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Total files tracked across project buckets." }, + { name: "autoImportNodeModulesFileCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Total files tracked across node_modules buckets." }, + { name: "autoImportNodeModulesUnfilteredBucketCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true, documentation: "Number of node_modules buckets with no package.json filter." }, + ], + documentation: "Numeric measurements for PerformanceStatsTelemetryEvent.", + }, + { + name: "ProjectInfoTelemetryEvent", + properties: [ + { + name: "eventName", + type: { kind: "stringLiteral", value: "languageServer.projectInfo" }, + documentation: "The name of the telemetry event.", + }, + { + name: "telemetryPurpose", + type: { kind: "stringLiteral", value: "usage" }, + documentation: "Indicates this is a usage telemetry event.", + }, + { + name: "properties", + type: { kind: "map", key: { kind: "base", name: "string" }, value: { kind: "base", name: "string" } }, + documentation: "String properties for this telemetry event. Complex values (compilerOptions, fileStats) are JSON-stringified.", + }, + { + name: "measurements", + type: { kind: "reference", name: "ProjectInfoTelemetryMeasurements" }, + documentation: "Numeric measurements for this telemetry event.", + }, + ], + documentation: "A ProjectInfoTelemetryEvent is sent once per project when it is first loaded.", + }, + { + name: "ProjectInfoTelemetryMeasurements", + properties: [ + { name: "jsFileCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true }, + { name: "jsFileSize", type: { kind: "base", name: "decimal" }, omitzeroValue: true }, + { name: "jsxFileCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true }, + { name: "jsxFileSize", type: { kind: "base", name: "decimal" }, omitzeroValue: true }, + { name: "tsFileCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true }, + { name: "tsFileSize", type: { kind: "base", name: "decimal" }, omitzeroValue: true }, + { name: "tsxFileCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true }, + { name: "tsxFileSize", type: { kind: "base", name: "decimal" }, omitzeroValue: true }, + { name: "dtsFileCount", type: { kind: "base", name: "decimal" }, omitzeroValue: true }, + { name: "dtsFileSize", type: { kind: "base", name: "decimal" }, omitzeroValue: true }, + ], + documentation: "Numeric measurements for ProjectInfoTelemetryEvent.", + }, + { + name: "MultiDocumentHighlight", + properties: [ + { + name: "uri", + type: { kind: "base", name: "DocumentUri" }, + documentation: "The URI of the document containing the highlights.", + }, + { + name: "highlights", + type: { kind: "array", element: { kind: "reference", name: "DocumentHighlight" } }, + documentation: "The highlights for the document.", + }, + ], + documentation: "Represents a collection of document highlights from a single document, used in multi-document highlight responses.", + }, + { + name: "MultiDocumentHighlightParams", + properties: [ + { + name: "textDocument", + type: { kind: "reference", name: "TextDocumentIdentifier" }, + documentation: "The text document.", + }, + { + name: "position", + type: { kind: "reference", name: "Position" }, + documentation: "The position inside the text document.", + }, + { + name: "filesToSearch", + type: { kind: "array", element: { kind: "base", name: "DocumentUri" } }, + documentation: "The list of file URIs to search for highlights across.", + }, + ], + documentation: "Parameters for the custom/textDocument/multiDocumentHighlight request.", + }, + { + name: "VSClassifiedTextRun", + properties: [ + { + name: "ClassificationTypeName", + type: { kind: "base", name: "string" }, + documentation: "The classification type name (e.g. 'keyword', 'class name', 'parameter name').", + }, + { + name: "Text", + type: { kind: "base", name: "string" }, + documentation: "The text content of this run.", + }, + { + name: "MarkerTagType", + type: { kind: "base", name: "string" }, + optional: true, + documentation: "Optional marker tag type.", + }, + { + name: "Style", + type: { kind: "base", name: "integer" }, + optional: true, + omitzeroValue: true, + documentation: "The style of this text run.", + }, + { + name: "_vs_type", + type: { kind: "stringLiteral", value: "ClassifiedTextRun" }, + documentation: "VS type discriminator required by ObjectContentConverter for deserialization.", + }, + ], + documentation: "A classified text run with text and classification type, used for colorized display in VS.", + }, + { + name: "VSClassifiedTextElement", + properties: [ + { + name: "Runs", + type: { kind: "array", element: { kind: "reference", name: "VSClassifiedTextRun" } }, + documentation: "The classified text runs that make up this element.", + }, + { + name: "_vs_type", + type: { kind: "stringLiteral", value: "ClassifiedTextElement" }, + documentation: "VS type discriminator required by ObjectContentConverter for deserialization.", + }, + ], + documentation: "A classified text element containing an array of classified text runs, used for colorized labels in VS.", + }, + { + name: "VSImageId", + properties: [ + { + name: "Guid", + type: { kind: "base", name: "string" }, + documentation: "The GUID of the image catalog containing this image.", + }, + { + name: "Id", + type: { kind: "base", name: "integer" }, + documentation: "The numeric identifier of the image within its catalog.", + }, + { + name: "_vs_type", + type: { kind: "stringLiteral", value: "ImageId" }, + documentation: "VS type discriminator required by ObjectContentConverter for deserialization.", + }, + ], + documentation: "Identifies an image in a VS image catalog. Used to render symbol-kind icons (e.g. in hover tooltips).", + }, + { + name: "VSImageElement", + properties: [ + { + name: "ImageId", + type: { kind: "reference", name: "VSImageId" }, + documentation: "The image to display.", + }, + { + name: "_vs_type", + type: { kind: "stringLiteral", value: "ImageElement" }, + documentation: "VS type discriminator required by ObjectContentConverter for deserialization.", + }, + ], + documentation: "An image element (e.g. a symbol-kind icon) for use in VS rich content such as hover tooltips.", + }, + { + name: "VSContainerElement", + properties: [ + { + name: "Style", + type: { kind: "reference", name: "VSContainerElementStyle" }, + documentation: "Layout style for the child elements.", + }, + { + name: "Elements", + type: { + kind: "array", + element: { + kind: "or", + items: [ + { kind: "reference", name: "VSImageElement" }, + { kind: "reference", name: "VSClassifiedTextElement" }, + { kind: "reference", name: "VSContainerElement" }, + ], + }, + }, + documentation: "The child elements contained within this container.", + }, + { + name: "_vs_type", + type: { kind: "stringLiteral", value: "ContainerElement" }, + documentation: "VS type discriminator required by ObjectContentConverter for deserialization.", + }, + ], + documentation: "A container element that groups other VS rich-content elements (images, classified text, or nested containers). Used to build the VS hover raw content that combines a symbol icon with colorized text.", + }, +]; + +const customEnumerations: Enumeration[] = [ + { + name: "VSContainerElementStyle", + type: { kind: "base", name: "integer" }, + values: [ + { name: "Wrapped", value: 0, documentation: "Child elements are laid out inline, wrapping as needed (e.g. an icon next to a signature line)." }, + { name: "Stacked", value: 1, documentation: "Child elements are stacked vertically, each on its own line (e.g. a signature line followed by documentation)." }, + ], + documentation: "Layout style for a VSContainerElement's children, mirroring VS's Microsoft.VisualStudio.Text.Adornments.ContainerElementStyle.", + }, + { + name: "LogVerbosity", + type: { kind: "base", name: "integer" }, + values: [ + { name: "Off", value: 0, documentation: "All logging disabled." }, + { name: "Trace", value: 1, documentation: "Most verbose; includes LSP request/response traces." }, + { name: "Debug", value: 2, documentation: "Verbose server logs." }, + { name: "Info", value: 3, documentation: "Normal server logs." }, + { name: "Warning", value: 4, documentation: "Warnings only." }, + { name: "Error", value: 5, documentation: "Errors only." }, + ], + documentation: "Log verbosity level, mirroring the VS Code LogLevel enum values.", + }, + { + name: "DiagnosticFlakeLogLevel", + type: { kind: "base", name: "integer" }, + values: [ + { name: "Off", value: 0, documentation: "All flake logging disabled." }, + { name: "Log", value: 1, documentation: "Log flaky diagnostics to the error log." }, + { name: "Panic", value: 2, documentation: "Panic on flaky diagnostics." }, + ], + documentation: "Behavior for tracking and logging flaky diagnostics.", + }, + { + name: "VSReferenceKind", + type: { kind: "base", name: "integer" }, + values: [ + { name: "Inactive", value: 0 }, + { name: "Comment", value: 1 }, + { name: "String", value: 2 }, + { name: "Read", value: 3 }, + { name: "Write", value: 4 }, + { name: "Reference", value: 5 }, + { name: "Name", value: 6 }, + { name: "Qualified", value: 7 }, + { name: "TypeArgument", value: 8 }, + { name: "TypeConstraint", value: 9 }, + { name: "BaseType", value: 10 }, + { name: "Constructor", value: 11 }, + { name: "Destructor", value: 12 }, + { name: "Import", value: 13 }, + { name: "Declaration", value: 14 }, + { name: "AddressOf", value: 15 }, + { name: "NotReference", value: 16 }, + { name: "Unknown", value: 17 }, + ], + }, + { + name: "CodeLensKind", + type: { + kind: "base", + name: "string", + }, + values: [ + { + name: "References", + value: "references", + }, + { + name: "Implementations", + value: "implementations", + }, + ], + }, + { + name: "AutoImportFixKind", + type: { kind: "base", name: "integer" }, + values: [ + { name: "UseNamespace", value: 0, documentation: "Augment an existing namespace import." }, + { name: "JsdocTypeImport", value: 1, documentation: "Add a JSDoc-only type import." }, + { name: "AddToExisting", value: 2, documentation: "Insert into an existing import declaration." }, + { name: "AddNew", value: 3, documentation: "Create a fresh import statement." }, + { name: "PromoteTypeOnly", value: 4, documentation: "Promote a type-only import when necessary." }, + ], + }, + { + name: "ImportKind", + type: { kind: "base", name: "integer" }, + values: [ + { name: "Named", value: 0, documentation: "Adds a named import." }, + { name: "Default", value: 1, documentation: "Adds a default import." }, + { name: "Namespace", value: 2, documentation: "Adds a namespace import." }, + { name: "CommonJS", value: 3, documentation: "Adds a CommonJS import assignment." }, + ], + }, + { + name: "AddAsTypeOnly", + type: { kind: "base", name: "integer" }, + values: [ + { name: "Allowed", value: 1, documentation: "Import may be marked type-only if needed." }, + { name: "Required", value: 2, documentation: "Import must be marked type-only." }, + { name: "NotAllowed", value: 4, documentation: "Import cannot be marked type-only." }, + ], + }, + { + name: "ClassificationTypeName", + type: { kind: "base", name: "string" }, + values: [ + { name: "Keyword", value: "keyword", documentation: "Language keyword (e.g., function, const, class)." }, + { name: "Punctuation", value: "punctuation", documentation: "Punctuation characters (e.g., parentheses, commas, semicolons)." }, + { name: "Operator", value: "operator", documentation: "Operators (e.g., =, +, ?)." }, + { name: "WhiteSpace", value: "whitespace", documentation: "Whitespace including spaces and line breaks." }, + { name: "Text", value: "text", documentation: "Plain text with no special classification." }, + { name: "String", value: "string", documentation: "String and literal values." }, + { name: "Number", value: "number", documentation: "Numeric literal values." }, + { name: "Comment", value: "comment", documentation: "Comment text." }, + { name: "ClassName", value: "class name", documentation: "Class names." }, + { name: "InterfaceName", value: "interface name", documentation: "Interface names." }, + { name: "EnumName", value: "enum name", documentation: "Enum names." }, + { name: "ModuleName", value: "module name", documentation: "Module/namespace names." }, + { name: "MethodName", value: "method name", documentation: "Method and function names." }, + { name: "ParameterName", value: "parameter name", documentation: "Parameter names." }, + { name: "PropertyName", value: "property name", documentation: "Property and accessor names." }, + { name: "FieldName", value: "field name", documentation: "Field names (e.g., enum members)." }, + { name: "LocalName", value: "local name", documentation: "Local variable names." }, + { name: "TypeParameterName", value: "type parameter name", documentation: "Type parameter names." }, + { name: "Identifier", value: "identifier", documentation: "General identifiers (e.g., type aliases, imports)." }, + ], + documentation: "Roslyn classification type names used by VS for syntax coloring in tooltips and other UI elements.", + }, +]; +const customRequests: Request[] = [ + { + method: "custom/runGC", + typeName: "RunGCRequest", + messageDirection: "clientToServer", + result: { kind: "base", name: "null" }, + documentation: "Triggers garbage collection in the language server.", + }, + { + method: "custom/saveHeapProfile", + typeName: "SaveHeapProfileRequest", + params: { kind: "reference", name: "ProfileParams" }, + messageDirection: "clientToServer", + result: { kind: "reference", name: "ProfileResult" }, + documentation: "Saves a heap profile to the specified directory.", + }, + { + method: "custom/saveAllocProfile", + typeName: "SaveAllocProfileRequest", + params: { kind: "reference", name: "ProfileParams" }, + messageDirection: "clientToServer", + result: { kind: "reference", name: "ProfileResult" }, + documentation: "Saves an allocation profile to the specified directory.", + }, + { + method: "custom/startCPUProfile", + typeName: "StartCPUProfileRequest", + params: { kind: "reference", name: "ProfileParams" }, + messageDirection: "clientToServer", + result: { kind: "base", name: "null" }, + documentation: "Starts CPU profiling, writing to the specified directory when stopped.", + }, + { + method: "custom/stopCPUProfile", + typeName: "StopCPUProfileRequest", + messageDirection: "clientToServer", + result: { kind: "reference", name: "ProfileResult" }, + documentation: "Stops CPU profiling and saves the profile.", + }, + { + method: "custom/initializeAPISession", + typeName: "CustomInitializeAPISessionRequest", + params: { kind: "reference", name: "InitializeAPISessionParams" }, + result: { kind: "reference", name: "InitializeAPISessionResult" }, + messageDirection: "clientToServer", + documentation: "Custom request to initialize an API session.", + }, + { + method: "custom/projectInfo", + typeName: "CustomProjectInfoRequest", + params: { kind: "reference", name: "ProjectInfoParams" }, + result: { kind: "reference", name: "ProjectInfoResult" }, + messageDirection: "clientToServer", + documentation: "Returns project information (e.g. the tsconfig.json path) for a given text document.", + }, + { + method: "custom/setContentMapperContributions", + typeName: "CustomSetContentMapperContributionsRequest", + params: { kind: "reference", name: "SetContentMapperContributionsParams" }, + result: { kind: "base", name: "null" }, + messageDirection: "clientToServer", + documentation: "Replaces extension content mapper contributions and discovers configured mappers for matching open documents.", + }, + { + method: "custom/textDocument/sourceDefinition", + typeName: "CustomTextDocumentSourceDefinitionRequest", + params: { kind: "reference", name: "TextDocumentPositionParams" }, + result: { kind: "reference", name: "LocationOrLocationsOrDefinitionLinksOrNull" }, + messageDirection: "clientToServer", + documentation: "Request to get source definitions for a position.", + }, + { + method: "custom/textDocument/multiDocumentHighlight", + typeName: "CustomMultiDocumentHighlightRequest", + params: { kind: "reference", name: "MultiDocumentHighlightParams" }, + result: { + kind: "or", + items: [ + { kind: "array", element: { kind: "reference", name: "MultiDocumentHighlight" } }, + { kind: "base", name: "null" }, + ], + }, + messageDirection: "clientToServer", + documentation: "Request to get document highlights across multiple files.", + }, + { + method: "textDocument/_vs_onAutoInsert", + typeName: "VSOnAutoInsertRequest", + params: { kind: "reference", name: "VSOnAutoInsertParams" }, + result: { + kind: "or", + items: [ + { kind: "reference", name: "VSOnAutoInsertResponseItem" }, + { kind: "base", name: "null" }, + ], + }, + messageDirection: "clientToServer", + documentation: "Request for auto-insert when a trigger character is typed (VS-specific).", + }, + { + method: "textDocument/_vs_references", + typeName: "VSReferencesRequest", + params: { kind: "reference", name: "ReferenceParams" }, + result: { + kind: "or", + items: [ + { kind: "array", element: { kind: "reference", name: "VSReferenceItem" } }, + { kind: "base", name: "null" }, + ], + }, + messageDirection: "clientToServer", + documentation: "VS-specific request for Find All References with grouped reference items.", + }, +]; + +const customNotifications: Notification[] = [ + { + method: "custom/setLogVerbosity", + typeName: "CustomSetLogVerbosityNotification", + params: { kind: "reference", name: "SetLogVerbosityParams" }, + messageDirection: "clientToServer", + documentation: "Notification to set the server's log verbosity level based on the output channel's log level.", + }, +]; + +// compareStructures is the set of generated structures for which a Compare method should be emitted. +// The Compare method defines a total ordering by comparing fields in declaration order. +// All listed structures (and any structure-typed fields they reference) must contain only +// comparable fields: base scalar types, or other structures that are themselves in this set. +const compareStructures = new Set([ + "Position", + "Range", + "TextEdit", +]); + +const customTypeAliases: TypeAlias[] = [ + { + name: "TelemetryEvent", + type: { + kind: "or", + items: [ + { kind: "reference", name: "RequestFailureTelemetryEvent" }, + { kind: "reference", name: "PerformanceStatsTelemetryEvent" }, + { kind: "reference", name: "ProjectInfoTelemetryEvent" }, + { kind: "base", name: "null" }, + ], + }, + }, +]; + +// Track which custom Data structures were declared explicitly +const explicitDataStructures = new Set(customStructures.map(s => s.name)); + +// Map from registration method → { fieldName, optionsTypeName } +// Built during patchAndPreprocessModel, used during code generation. +interface RegistrationMethodInfo { + registrationMethod: string; + fieldName: string; + optionsTypeName: string; + isRegistrationOnly?: boolean; +} +let registrationMethods: RegistrationMethodInfo[] = []; + +// Patch and preprocess the model +function patchAndPreprocessModel() { + // Track which Data types we need to create as placeholders + const neededDataStructures = new Set(); + + // Collect all registration option types from requests and notifications + const registrationOptionTypes: Type[] = []; + for (const request of [...model.requests, ...model.notifications]) { + if (request.registrationOptions) { + registrationOptionTypes.push(request.registrationOptions); + } + } + + // Create synthetic structures for "and" types in registration options + const syntheticStructures: Structure[] = []; + for (let i = 0; i < registrationOptionTypes.length; i++) { + const regOptType = registrationOptionTypes[i]; + if (regOptType.kind === "and") { + // Find which request/notification this registration option belongs to + const owner = [...model.requests, ...model.notifications].find(r => r.registrationOptions === regOptType); + if (!owner) { + throw new Error("Could not find owner for 'and' type registration option"); + } + + // Determine the proper name based on the typeName or method + let structureName: string; + if (owner.typeName) { + // Use typeName as base: "ColorPresentationRequest" -> "ColorPresentationRegistrationOptions" + structureName = owner.typeName.replace(/Request$/, "").replace(/Notification$/, "") + "RegistrationOptions"; + } + else { + // Fall back to method: "textDocument/colorPresentation" -> "ColorPresentationRegistrationOptions" + const methodParts = owner.method.split("/"); + const lastPart = methodParts[methodParts.length - 1]; + structureName = titleCase(lastPart) + "RegistrationOptions"; + } + + // Extract all reference types from the "and" + const refTypes = regOptType.items.filter((item): item is ReferenceType => item.kind === "reference"); + + // Create a synthetic structure that combines all the referenced structures + syntheticStructures.push({ + name: structureName, + properties: [], + extends: refTypes, + documentation: `Registration options for ${owner.method}.`, + }); + + // Replace the "and" type with a reference to the synthetic structure + registrationOptionTypes[i] = { kind: "reference", name: structureName }; + // Also update the model so the request/notification has the resolved type + owner.registrationOptions = registrationOptionTypes[i]; + } + } + + for (const structure of model.structures) { + // Patch ServerCapabilities to add custom tsgo capability flags + if (structure.name === "ServerCapabilities") { + structure.properties.push({ + name: "_vs_onAutoInsertProvider", + type: { kind: "reference", name: "VSOnAutoInsertOptions" }, + optional: true, + documentation: "Provider options for the VS auto-insert feature via textDocument/_vs_onAutoInsert.", + }); + structure.properties.push({ + name: "_vs_referencesProvider", + type: { kind: "base", name: "boolean" }, + optional: true, + documentation: "The server provides VS-specific grouped references via textDocument/_vs_references.", + }); + } + + // Patch HoverParams to add verbosityLevel + if (structure.name === "HoverParams") { + structure.properties.push({ + name: "verbosityLevel", + type: { kind: "base", name: "integer" }, + optional: true, + documentation: "Controls how many levels of type definitions will be expanded. Default is 0.", + }); + } + + // Patch WorkspaceSymbolParams to optionally scope the search to projects + // containing a document, matching Strada's currentProject mode. + if (structure.name === "WorkspaceSymbolParams") { + structure.properties.push({ + name: "textDocument", + type: { kind: "reference", name: "TextDocumentIdentifier" }, + optional: true, + documentation: "Scopes the workspace symbol search to projects containing this document.", + }); + } + + // Patch Hover to add canIncreaseVerbosity + if (structure.name === "Hover") { + structure.properties.push( + { + name: "canIncreaseVerbosity", + type: { kind: "base", name: "boolean" }, + omitzeroValue: true, + documentation: "Whether the verbosity level can be increased for this hover.", + }, + { + name: "_vs_rawContent", + type: { kind: "reference", name: "VSContainerElement" }, + optional: true, + documentation: "VS-specific rich content (symbol icon + colorized/classified text) rendered by clients that support Visual Studio extensions, in place of `contents`.", + }, + ); + } + + // Patch ClientCapabilities to add VS-specific client capabilities + if (structure.name === "ClientCapabilities") { + structure.properties.push( + { + name: "_vs_supportsVisualStudioExtensions", + type: { kind: "base", name: "boolean" }, + optional: true, + documentation: "Whether the client supports Visual Studio extensions.", + }, + { + name: "_vs_supportedSnippetVersion", + type: { kind: "base", name: "integer" }, + optional: true, + documentation: "The snippet version supported by the client.", + }, + { + name: "_vs_supportsNotIncludingTextInTextDocumentDidOpen", + type: { kind: "base", name: "boolean" }, + optional: true, + documentation: "Whether the client supports not including text in textDocument/didOpen notifications.", + }, + { + name: "_vs_supportsIconExtensions", + type: { kind: "base", name: "boolean" }, + optional: true, + documentation: "Whether the client supports icon extensions.", + }, + { + name: "_vs_supportsDiagnosticRequests", + type: { kind: "base", name: "boolean" }, + optional: true, + documentation: "Whether the client supports diagnostic requests.", + }, + ); + } + + // Patch SignatureInformation to add VS-specific colorized label + if (structure.name === "SignatureInformation") { + structure.properties.push({ + name: "_vs_colorizedLabel", + type: { kind: "reference", name: "VSClassifiedTextElement" }, + optional: true, + documentation: "A colorized label for the signature, providing classified text runs for VS syntax coloring.", + }); + } + + for (const prop of structure.properties) { + // Replace initializationOptions type with custom InitializationOptions. + // The spec types this field as LSPAny?, which includes null, so keep + // it nullable so a null value sent by loose clients is accepted. + if (prop.name === "initializationOptions" && prop.type.kind === "reference" && prop.type.name === "LSPAny") { + prop.type = { + kind: "or", + items: [ + { kind: "reference", name: "InitializationOptions" }, + { kind: "base", name: "null" }, + ], + }; + } + + // Replace Data *any fields with custom typed Data fields + if (prop.name === "data" && prop.type.kind === "reference" && prop.type.name === "LSPAny") { + const customDataType = `${structure.name}Data`; + prop.type = { kind: "reference", name: customDataType }; + + // If we haven't explicitly declared this Data structure, we'll need a placeholder + if (!explicitDataStructures.has(customDataType)) { + neededDataStructures.add(customDataType); + } + } + + // Registration.registerOptions and Registration.method are handled specially: + // registerOptions becomes a custom struct, and method is derived from it. + // Remove both from the structure so the normal generator skips them. + if (structure.name === "Registration" && (prop.name === "registerOptions" || prop.name === "method")) { + // Will be filtered out below + } + + // Replace ProgressParams.value with a proper union type + if (structure.name === "ProgressParams" && prop.name === "value" && prop.type.kind === "reference" && prop.type.name === "LSPAny") { + prop.type = { + kind: "or", + items: [ + { kind: "reference", name: "WorkDoneProgressBegin" }, + { kind: "reference", name: "WorkDoneProgressReport" }, + { kind: "reference", name: "WorkDoneProgressEnd" }, + ], + }; + } + } + } + + for (const notification of model.notifications) { + if (notification.typeName === "TelemetryEventNotification") { + notification.params = { + kind: "reference", + name: "TelemetryEvent", + }; + } + } + + // Create placeholder structures for Data types that weren't explicitly declared + for (const dataTypeName of neededDataStructures) { + const baseName = dataTypeName.replace(/Data$/, ""); + customStructures.push({ + name: dataTypeName, + properties: [], + documentation: `${dataTypeName} is a placeholder for custom data preserved on a ${baseName}.`, + }); + } + + // Add custom enumerations, custom structures, custom requests, and synthetic structures to the model + model.enumerations.push(...customEnumerations); + model.structures.push(...customStructures, ...syntheticStructures); + model.requests.push(...customRequests); + model.notifications.push(...customNotifications); + + // Build structure map for preprocessing + const structureMap = new Map(); + for (const structure of model.structures) { + structureMap.set(structure.name, structure); + } + + function collectInheritedProperties(structure: Structure, visited = new Set()): Property[] { + if (visited.has(structure.name)) { + return []; // Avoid circular dependencies + } + visited.add(structure.name); + + const properties: Property[] = []; + const inheritanceTypes = [...(structure.extends || []), ...(structure.mixins || [])]; + + for (const type of inheritanceTypes) { + if (type.kind === "reference") { + const inheritedStructure = structureMap.get(type.name); + if (inheritedStructure) { + properties.push( + ...collectInheritedProperties(inheritedStructure, new Set(visited)), + ...inheritedStructure.properties, + ); + } + } + } + + return properties; + } + + // Inline inheritance for each structure + for (const structure of model.structures) { + const inheritedProperties = collectInheritedProperties(structure); + + // Merge properties with structure's own properties taking precedence + const propertyMap = new Map(); + + inheritedProperties.forEach(prop => propertyMap.set(prop.name, prop)); + structure.properties.forEach(prop => propertyMap.set(prop.name, prop)); + + structure.properties = Array.from(propertyMap.values()); + structure.extends = undefined; + structure.mixins = undefined; + + // Replace experimental LSPAny with typed ExperimentalClientCapabilities in ClientCapabilities + if (structure.name === "ClientCapabilities") { + const expProp = structure.properties.find(p => p.name === "experimental"); + if (expProp) { + expProp.type = { kind: "reference", name: "ExperimentalClientCapabilities" }; + expProp.optional = true; + } + } + + // Replace experimental LSPAny with typed ExperimentalServerCapabilities in ServerCapabilities + if (structure.name === "ServerCapabilities") { + const expProp = structure.properties.find(p => p.name === "experimental"); + if (expProp) { + expProp.type = { kind: "reference", name: "ExperimentalServerCapabilities" }; + expProp.optional = true; + } + } + + // Remove method and registerOptions from Registration (handled by custom codegen) + if (structure.name === "Registration") { + structure.properties = structure.properties.filter(p => p.name !== "method" && p.name !== "registerOptions"); + } + } + + // Remove _InitializeParams structure after flattening (it was only needed for inheritance) + model.structures = model.structures.filter(s => s.name !== "_InitializeParams"); + + // Remove all notebook-related features from the model + function isNotebookRelatedName(name: string): boolean { + const lower = name.toLowerCase(); + return lower.includes("notebook"); + } + + function isNotebookRelatedMethod(method: string): boolean { + return method.toLowerCase().startsWith("notebookdocument/"); + } + + function typeReferencesNotebook(type: Type): boolean { + if (type.kind === "reference") return isNotebookRelatedName(type.name); + if (type.kind === "array") return typeReferencesNotebook(type.element); + if (type.kind === "or" || type.kind === "and") return type.items.some(typeReferencesNotebook); + if (type.kind === "map") return typeReferencesNotebook(type.key) || typeReferencesNotebook(type.value); + return false; + } + + function isEntirelyNotebookType(type: Type): boolean { + if (type.kind === "reference") return isNotebookRelatedName(type.name); + if (type.kind === "array") return isEntirelyNotebookType(type.element); + if (type.kind === "or" || type.kind === "and") return type.items.every(isEntirelyNotebookType); + return false; + } + + function removeNotebookFromType(type: Type): Type { + if (type.kind === "or") { + const filtered = type.items.filter(item => !typeReferencesNotebook(item)).map(removeNotebookFromType); + if (filtered.length === 1) return filtered[0]; + if (filtered.length < type.items.length) { + return { ...type, items: filtered }; + } + } + if (type.kind === "and") { + const filtered = type.items.filter(item => !typeReferencesNotebook(item)).map(removeNotebookFromType); + if (filtered.length === 1) return filtered[0]; + if (filtered.length < type.items.length) { + return { ...type, items: filtered }; + } + } + return type; + } + + // Filter out notebook structures (and notebook-only structures like ExecutionSummary) + const notebookOnlyStructures = new Set(["ExecutionSummary"]); + model.structures = model.structures.filter(s => !isNotebookRelatedName(s.name) && !notebookOnlyStructures.has(s.name)); + + // Remove notebook properties from remaining structures + for (const structure of model.structures) { + structure.properties = structure.properties.filter(p => { + if (isNotebookRelatedName(p.name)) return false; + // Only remove properties whose type is entirely notebook-related + if (isEntirelyNotebookType(p.type)) return false; + return true; + }); + // Clean up union types in remaining properties to remove notebook members + for (const prop of structure.properties) { + prop.type = removeNotebookFromType(prop.type); + } + } + + // Filter out notebook notifications and requests + model.notifications = model.notifications.filter(n => !isNotebookRelatedMethod(n.method)); + model.requests = model.requests.filter(r => !isNotebookRelatedMethod(r.method)); + + // Filter out notebook enumerations + model.enumerations = model.enumerations.filter(e => !isNotebookRelatedName(e.name)); + + // Remove notebook-related values from remaining enumerations + for (const enumeration of model.enumerations) { + enumeration.values = enumeration.values.filter(v => !isNotebookRelatedName(v.name)); + } + + // Filter out notebook type aliases + model.typeAliases = model.typeAliases.filter(ta => !isNotebookRelatedName(ta.name)); + + // Clean up type aliases that reference notebook types (e.g., DocumentFilter) + for (const ta of model.typeAliases) { + if (ta.type.kind === "or") { + ta.type.items = ta.type.items.filter(item => !typeReferencesNotebook(item)); + // If only one item remains, unwrap the union + if (ta.type.items.length === 1) { + ta.type = ta.type.items[0]; + } + } + } + + // Build the registration method map (after notebook filtering). + // Each unique registration method gets a field in the generated RegisterOptions struct. + const regMethodSeen = new Set(); + for (const request of [...model.requests, ...model.notifications]) { + if (!request.registrationOptions) continue; + const regMethod = (request as any).registrationMethod || request.method; + + if (regMethodSeen.has(regMethod)) continue; + regMethodSeen.add(regMethod); + + // Resolve the options type name + const ro = request.registrationOptions; + let optionsTypeName: string; + if (ro.kind === "reference") { + optionsTypeName = ro.name; + } + else { + throw new Error(`Unexpected registrationOptions kind '${ro.kind}' for ${request.method}; expected all to be resolved to references`); + } + + registrationMethods.push({ + registrationMethod: regMethod, + fieldName: methodNameIdentifier(regMethod), + optionsTypeName, + }); + } + + // Identify registration-only methods (not also a request/notification method). + // These need their own Method constant emitted. + const allRequestMethods = new Set([...model.requests, ...model.notifications].map(r => r.method)); + for (const reg of registrationMethods) { + (reg as any).isRegistrationOnly = !allRequestMethods.has(reg.registrationMethod); + } + + // Merge LSPErrorCodes into ErrorCodes and remove LSPErrorCodes + const errorCodesEnum = model.enumerations.find(e => e.name === "ErrorCodes"); + const lspErrorCodesEnum = model.enumerations.find(e => e.name === "LSPErrorCodes"); + if (errorCodesEnum && lspErrorCodesEnum) { + // Merge LSPErrorCodes values into ErrorCodes + errorCodesEnum.values.push(...lspErrorCodesEnum.values); + // Remove LSPErrorCodes from the model + model.enumerations = model.enumerations.filter(e => e.name !== "LSPErrorCodes"); + } + + // Singularize plural enum names (e.g., "ErrorCodes" -> "ErrorCode") + for (const enumeration of model.enumerations) { + if (enumeration.name.endsWith("Codes")) { + enumeration.name = enumeration.name.slice(0, -1); // "Codes" -> "Code" + } + else if (enumeration.name.endsWith("Modifiers")) { + enumeration.name = enumeration.name.slice(0, -1); // "Modifiers" -> "Modifier" + } + else if (enumeration.name.endsWith("Types")) { + enumeration.name = enumeration.name.slice(0, -1); // "Types" -> "Type" + } + } +} + +patchAndPreprocessModel(); + +// Validate that telemetry events in the TelemetryEvent union have properly shaped +// measurements and properties fields. measurements struct fields must only contain +// numeric types (decimal/integer/uinteger). +function validateTelemetryEvents() { + const telemetryAlias = customTypeAliases.find(a => a.name === "TelemetryEvent"); + if (!telemetryAlias || telemetryAlias.type.kind !== "or") return; + + const structureMap = new Map(model.structures.map(s => [s.name, s])); + + for (const item of telemetryAlias.type.items) { + if (item.kind !== "reference") continue; + const eventStruct = structureMap.get(item.name); + if (!eventStruct) continue; + + for (const prop of eventStruct.properties) { + if (prop.name === "measurements" && prop.type.kind === "reference") { + const measurementsStruct = structureMap.get(prop.type.name); + if (!measurementsStruct) continue; + for (const mp of measurementsStruct.properties) { + if (mp.type.kind !== "base" || !["decimal", "integer", "uinteger"].includes(mp.type.name)) { + throw new Error( + `Telemetry measurements struct ${prop.type.name}.${mp.name} must be a numeric type ` + + `(decimal/integer/uinteger), got ${mp.type.kind === "base" ? mp.type.name : mp.type.kind}`, + ); + } + } + } + } + } +} + +validateTelemetryEvents(); + +interface GoType { + name: string; + needsPointer: boolean; +} + +interface TypeInfo { + types: Map; + literalTypes: Map; + unionTypes: Map; + typeAliasMap: Map; +} + +const typeInfo: TypeInfo = { + types: new Map(), + literalTypes: new Map(), + unionTypes: new Map(), + typeAliasMap: new Map(), +}; + +function titleCase(s: string) { + return s.charAt(0).toUpperCase() + s.slice(1); +} + +function goFieldName(prop: Property): string { + if (prop.name.startsWith("_vs_")) { + return "VS" + titleCase(prop.name.slice(4)); + } + return titleCase(prop.name); +} + +function resolveType(type: Type): GoType { + switch (type.kind) { + case "base": + switch (type.name) { + case "integer": + return { name: "int32", needsPointer: false }; + case "uinteger": + return { name: "uint32", needsPointer: false }; + case "string": + return { name: "string", needsPointer: false }; + case "boolean": + return { name: "bool", needsPointer: false }; + case "URI": + return { name: "URI", needsPointer: false }; + case "DocumentUri": + return { name: "DocumentUri", needsPointer: false }; + case "decimal": + return { name: "float64", needsPointer: false }; + case "null": + return { name: "any", needsPointer: false }; + default: + throw new Error(`Unsupported base type: ${type.name}`); + } + + case "reference": + const typeAliasOverride = typeAliasOverrides.get(type.name); + if (typeAliasOverride) { + return typeAliasOverride; + } + + const nonResolved = nonResolvedAliases.has(type.name); + if (nonResolved) { + return { name: type.name, needsPointer: false }; + } + + // Check if this is a type alias that resolves to a union type + const aliasedType = typeInfo.typeAliasMap.get(type.name); + if (aliasedType) { + return resolveType(aliasedType); + } + + let refType = typeInfo.types.get(type.name); + if (!refType) { + refType = { name: type.name, needsPointer: true }; + typeInfo.types.set(type.name, refType); + } + return refType; + + case "array": { + const elementType = resolveType(type.element); + const arrayTypeName = elementType.needsPointer + ? `[]*${elementType.name}` + : `[]${elementType.name}`; + return { + name: arrayTypeName, + needsPointer: false, + }; + } + + case "map": { + const keyType = resolveType(type.key); + const valueType = resolveType(type.value); + const valueTypeName = valueType.needsPointer ? `*${valueType.name}` : valueType.name; + + return { + name: `map[${keyType.name}]${valueTypeName}`, + needsPointer: false, + }; + } + + case "tuple": { + if ( + type.items.length === 2 && + type.items[0].kind === "base" && type.items[0].name === "uinteger" && + type.items[1].kind === "base" && type.items[1].name === "uinteger" + ) { + return { name: "[2]uint32", needsPointer: false }; + } + + throw new Error("Unsupported tuple type: " + JSON.stringify(type)); + } + + case "stringLiteral": { + const typeName = `StringLiteral${type.value.split(".").map(titleCase).join("")}`; + typeInfo.literalTypes.set(String(type.value), typeName); + return { name: typeName, needsPointer: false }; + } + + case "integerLiteral": { + const typeName = `IntegerLiteral${type.value}`; + typeInfo.literalTypes.set(String(type.value), typeName); + return { name: typeName, needsPointer: false }; + } + + case "booleanLiteral": { + const typeName = `BooleanLiteral${type.value ? "True" : "False"}`; + typeInfo.literalTypes.set(String(type.value), typeName); + return { name: typeName, needsPointer: false }; + } + case "literal": + if (type.value.properties.length === 0) { + return { name: "struct{}", needsPointer: false }; + } + + throw new Error("Unexpected non-empty literal object: " + JSON.stringify(type.value)); + + case "or": { + return handleOrType(type); + } + + default: + throw new Error(`Unsupported type kind: ${type.kind}`); + } +} + +function flattenOrTypes(types: Type[]): Type[] { + const flattened = new Set(); + + for (const rawType of types) { + let type = rawType; + + // Dereference reference types that point to OR types + if (rawType.kind === "reference") { + const aliasedType = typeInfo.typeAliasMap.get(rawType.name); + if (aliasedType && aliasedType.kind === "or") { + type = aliasedType; + } + } + + if (type.kind === "or") { + // Recursively flatten OR types + for (const subType of flattenOrTypes(type.items)) { + flattened.add(subType); + } + } + else { + flattened.add(rawType); + } + } + + return Array.from(flattened); +} + +function pluralize(name: string): string { + // Handle common irregular plurals and special cases + if ( + name.endsWith("s") || name.endsWith("x") || name.endsWith("z") || + name.endsWith("ch") || name.endsWith("sh") + ) { + return name + "es"; + } + if (name.endsWith("y") && name.length > 1 && !"aeiou".includes(name[name.length - 2])) { + return name.slice(0, -1) + "ies"; + } + return name + "s"; +} + +function handleOrType(orType: OrType): GoType { + // First, flatten any nested OR types + const types = flattenOrTypes(orType.items); + + // Check for nullable types (OR with null) + const nullIndex = types.findIndex(item => item.kind === "base" && item.name === "null"); + let containedNull = nullIndex !== -1; + + // If it's nullable, remove the null type from the list + let nonNullTypes = types; + if (containedNull) { + nonNullTypes = types.filter((_, i) => i !== nullIndex); + } + + // If no types remain after filtering null, this shouldn't happen + if (nonNullTypes.length === 0) { + throw new Error("Union type with only null is not supported: " + JSON.stringify(types)); + } + + // Even if only one type remains after filtering null, we still need to create a union type + // to preserve the nullable behavior (all fields nil = null) + + let memberNames = nonNullTypes.map(type => { + if (type.kind === "reference") { + return type.name; + } + else if (type.kind === "base") { + return titleCase(type.name); + } + else if ( + type.kind === "array" && + (type.element.kind === "reference" || type.element.kind === "base") + ) { + return pluralize(titleCase(type.element.name)); + } + else if (type.kind === "array") { + // Handle more complex array types + const elementType = resolveType(type.element); + return `${elementType.name}Array`; + } + else if (type.kind === "literal" && type.value.properties.length === 0) { + return "EmptyObject"; + } + else if (type.kind === "tuple") { + return "Tuple"; + } + else { + throw new Error(`Unsupported type kind in union: ${type.kind}`); + } + }); + + // Find longest common prefix of member names chunked by PascalCase + function findLongestCommonPrefix(names: string[]): string { + if (names.length === 0) return ""; + if (names.length === 1) return ""; + + // Split each name into PascalCase chunks + function splitPascalCase(name: string): string[] { + const chunks: string[] = []; + let currentChunk = ""; + + for (let i = 0; i < name.length; i++) { + const char = name[i]; + if (char >= "A" && char <= "Z" && currentChunk.length > 0) { + // Start of a new chunk + chunks.push(currentChunk); + currentChunk = char; + } + else { + currentChunk += char; + } + } + + if (currentChunk.length > 0) { + chunks.push(currentChunk); + } + + return chunks; + } + + const allChunks = names.map(splitPascalCase); + const minChunkLength = Math.min(...allChunks.map(chunks => chunks.length)); + + // Find the longest common prefix of chunks + let commonChunks: string[] = []; + for (let i = 0; i < minChunkLength; i++) { + const chunk = allChunks[0][i]; + if (allChunks.every(chunks => chunks[i] === chunk)) { + commonChunks.push(chunk); + } + else { + break; + } + } + + return commonChunks.join(""); + } + + const commonPrefix = findLongestCommonPrefix(memberNames); + + let unionTypeName = ""; + + if (commonPrefix.length > 0) { + const trimmedMemberNames = memberNames.map(name => name.slice(commonPrefix.length)); + if (trimmedMemberNames.every(name => name)) { + unionTypeName = commonPrefix + trimmedMemberNames.join("Or"); + memberNames = trimmedMemberNames; + } + else { + unionTypeName = memberNames.join("Or"); + } + } + else { + unionTypeName = memberNames.join("Or"); + } + + if (containedNull) { + unionTypeName += "OrNull"; + } + else { + containedNull = false; + } + + const union = memberNames.map((name, i) => ({ name, type: nonNullTypes[i], containedNull })); + + typeInfo.unionTypes.set(unionTypeName, union); + + return { + name: unionTypeName, + needsPointer: false, + }; +} + +const typeAliasOverrides = new Map([ + ["LSPAny", { name: "any", needsPointer: false }], + ["LSPArray", { name: "[]any", needsPointer: false }], + ["LSPObject", { name: "map[string]any", needsPointer: false }], + ["uint64", { name: "uint64", needsPointer: false }], +]); + +// These type aliases are intentionally not resolved to their underlying types. +// It means that we can end up with non-normalized union types in some places. +// Also, unlike other type aliases, these will get a type alias in the generated source code. +// We may want to eventually do this for all type aliases though. +const nonResolvedAliases = new Set(customTypeAliases.map(ta => ta.name)); + +/** + * First pass: Resolve all type information + */ +function collectTypeDefinitions() { + // Process all enumerations first to make them available for struct fields + for (const enumeration of model.enumerations) { + typeInfo.types.set(enumeration.name, { + name: enumeration.name, + needsPointer: false, + }); + } + + const valueTypes = new Set([ + "Position", + "Range", + "Location", + "Color", + "TextDocumentIdentifier", + "PreviousResultId", + "VersionedTextDocumentIdentifier", + "OptionalVersionedTextDocumentIdentifier", + "ExportInfoMapKey", + ]); + + // Process all structures + for (const structure of model.structures) { + typeInfo.types.set(structure.name, { + name: structure.name, + needsPointer: !valueTypes.has(structure.name), + }); + } + + // Process all type aliases + for (const typeAlias of model.typeAliases) { + if (typeAliasOverrides.has(typeAlias.name)) { + continue; + } + + // Store the alias mapping so we can resolve it later + typeInfo.typeAliasMap.set(typeAlias.name, typeAlias.type); + } +} + +function formatDocumentation(s: string | undefined): string { + if (!s) return ""; + + let lines: string[] = []; + + for (let line of s.split("\n")) { + line = line.trimEnd(); + line = line.replace(/(\w ) +/g, "$1"); + // Some upstream docs include dangling block comment delimiters; remove them + // so they don't leak into generated `//` comments. + line = line.replace(/\s*\/\*+\s*/g, " "); + line = line.replace(/\s*\*+\/\s*/g, " "); + line = line.replace(/\s{2,}/g, " ").trimEnd(); + line = line.replace(/\{@link(?:code)?.*?([^} ]+)\}/g, "$1"); + line = line.replace(/^@(since|proposed|deprecated)(.*)/, (_, tag, rest) => { + lines.push(""); + return `${titleCase(tag)}${rest ? ":" + rest : "."}`; + }); + lines.push(line); + } + + // filter out contiguous empty lines + while (true) { + const toRemove = lines.findIndex((line, index) => { + if (line) return false; + if (index === 0) return true; + if (index === lines.length - 1) return true; + return !(lines[index - 1] && lines[index + 1]); + }); + if (toRemove === -1) break; + lines.splice(toRemove, 1); + } + + return lines.length > 0 ? "// " + lines.join("\n// ") + "\n" : ""; +} + +function methodNameIdentifier(name: string) { + return name.split("/").map(v => { + if (v === "$") return ""; + // Mirror goFieldName: "_vs_foo" -> "VSFoo". + if (v.startsWith("_vs_")) return "VS" + titleCase(v.slice(4)); + return titleCase(v); + }).join(""); +} + +/** + * Returns the JSON token kind ("string", "number", "object", "array", "boolean") + * for a given meta model Type, or undefined if the kind cannot be statically determined. + */ +function jsonKindForType(type: Type): string | undefined { + switch (type.kind) { + case "base": + switch (type.name) { + case "integer": + case "uinteger": + case "decimal": + return "number"; + case "string": + case "URI": + case "DocumentUri": + return "string"; + case "boolean": + return "boolean"; + default: + return undefined; + } + case "reference": { + if (typeAliasOverrides.has(type.name)) { + return undefined; + } + if (model.structures.some(s => s.name === type.name)) { + return "object"; + } + const enumeration = model.enumerations.find(e => e.name === type.name); + if (enumeration) { + switch (enumeration.type.name) { + case "string": + return "string"; + case "integer": + case "uinteger": + return "number"; + default: + return undefined; + } + } + const aliasType = typeInfo.typeAliasMap.get(type.name); + if (aliasType) return jsonKindForType(aliasType); + return undefined; + } + case "array": + return "array"; + case "map": + return "object"; + case "tuple": + return "array"; + case "stringLiteral": + return "string"; + case "integerLiteral": + return "number"; + case "booleanLiteral": + return "boolean"; + case "literal": + return "object"; + case "or": { + const kinds = new Set(type.items.map(item => jsonKindForType(item)).filter(Boolean)); + return kinds.size === 1 ? kinds.values().next().value : undefined; + } + default: + return undefined; + } +} + +function goKindCasesForJsonKind(kind: string): string { + switch (kind) { + case "string": + return `case '"':`; + case "number": + return `case '0':`; + case "object": + return `case '{':`; + case "array": + return `case '[':`; + case "boolean": + return `case 't', 'f':`; + default: + return ""; + } +} + +/** + * Checks if a meta model Type can represent a JSON null value. + * Used to determine whether to reject explicit JSON `null` for any field + * that can otherwise decode `null` without a type error. + */ +function typeCanBeNull(type: Type): boolean { + switch (type.kind) { + case "base": + return type.name === "null"; + case "reference": { + const override = typeAliasOverrides.get(type.name); + if (override) { + return override.name === "any"; + } + // A bare "any" reference resolves to Go's `any` (interface), which can hold null. + if (type.name === "any") { + return true; + } + if (nonResolvedAliases.has(type.name)) { + const customAlias = customTypeAliases.find(t => t.name === type.name); + if (customAlias) return typeCanBeNull(customAlias.type); + return false; + } + const aliased = typeInfo.typeAliasMap.get(type.name); + if (aliased) return typeCanBeNull(aliased); + return false; + } + case "or": + return type.items.some(item => typeCanBeNull(item)); + default: + return false; + } +} + +/** + * For a group of union entries that share the same JSON kind (e.g., all objects), + * find a discriminator field — a JSON property whose string literal type differs + * across variants — enabling efficient O(1) dispatch instead of try-each. + */ +function findDiscriminatorField(entries: { fieldName: string; typeName: string; originalType: Type; }[]): { + fieldName: string; + mapping: Map; + unmapped: { fieldName: string; typeName: string; originalType: Type; }[]; +} | null { + // For each entry, find string literal fields and build candidate discriminators. + // A valid discriminator is a field name that appears on multiple variants with + // different string literal values. + const fieldCandidates = new Map>(); + + for (const entry of entries) { + if (entry.originalType.kind !== "reference") continue; + const structure = model.structures.find(s => s.name === (entry.originalType as ReferenceType).name); + if (!structure) continue; + + for (const prop of structure.properties) { + if (prop.type.kind === "stringLiteral") { + if (!fieldCandidates.has(prop.name)) { + fieldCandidates.set(prop.name, new Map()); + } + const mapping = fieldCandidates.get(prop.name)!; + if (!mapping.has(prop.type.value)) { + mapping.set(prop.type.value, entry); + } + else { + // Two entries share the same literal value; invalidate this candidate. + mapping.set(prop.type.value, undefined); + } + } + } + } + + // Pick the discriminator field that covers the most entries. + let bestField: string | null = null; + let bestMapping: Map | null = null; + + for (const [fieldName, mapping] of fieldCandidates) { + const validMapping = new Map(); + for (const [value, entry] of mapping) { + if (entry !== undefined) validMapping.set(value, entry); + } + if (validMapping.size >= 2 && (!bestMapping || validMapping.size > bestMapping.size)) { + bestField = fieldName; + bestMapping = validMapping; + } + } + + if (!bestField || !bestMapping) return null; + + const mappedEntries = new Set(bestMapping.values()); + const unmapped = entries.filter(e => !mappedEntries.has(e)); + + return { fieldName: bestField, mapping: bestMapping, unmapped }; +} + +/** + * For a group of union entries that share the same JSON kind, find fields whose + * presence/absence in the JSON uniquely identifies a variant. A "presence discriminator" + * for variant X is a required field on X that does not appear in any other variant's + * property set at all. + */ +function findPresenceDiscriminator(entries: { fieldName: string; typeName: string; originalType: Type; }[]): { + checks: { jsonFieldName: string; entry: { fieldName: string; typeName: string; originalType: Type; }; }[]; + unmapped: { fieldName: string; typeName: string; originalType: Type; }[]; +} | null { + // Collect all property names for each variant + const variantProps = new Map; }>(); + for (const entry of entries) { + if (entry.originalType.kind !== "reference") continue; + const structure = model.structures.find(s => s.name === (entry.originalType as ReferenceType).name); + if (!structure) continue; + const required = structure.properties.filter(p => !p.optional && !p.omitzeroValue); + const allNames = new Set(structure.properties.map(p => p.name)); + variantProps.set(entry, { required, allNames }); + } + + const checks: { jsonFieldName: string; entry: typeof entries[0]; }[] = []; + const handled = new Set(); + + for (const entry of entries) { + const info = variantProps.get(entry); + if (!info) continue; + + const otherEntries = entries.filter(e => e !== entry); + for (const field of info.required) { + const absentFromAllOthers = otherEntries.every(other => { + const otherInfo = variantProps.get(other); + if (!otherInfo) return false; + return !otherInfo.allNames.has(field.name); + }); + if (absentFromAllOthers) { + checks.push({ jsonFieldName: field.name, entry }); + handled.add(entry); + break; + } + } + } + + if (checks.length === 0) return null; + + const unmapped = entries.filter(e => !handled.has(e)); + return { checks, unmapped }; +} + +/** + * Generate the Go code + */ +function generateCode() { + const parts: string[] = []; + + function write(s: string) { + parts.push(s); + } + + function writeLine(s = "") { + parts.push(s + "\n"); + } + + /** + * Generate Go code for discriminator-based union dispatch. + * Assumes a variable named `data` of type `json.Value` is in scope. + * Returns true if all switch branches return (exhaustive). + */ + function generateDiscriminatorDispatch( + disc: NonNullable>, + indent: string, + ): boolean { + writeLine(`${indent}switch string(jsonObjectRawField(data, ${JSON.stringify(disc.fieldName)})) {`); + for (const [value, entry] of disc.mapping) { + writeLine(`${indent}case \`"${value}"\`:`); + writeLine(`${indent}\to.${entry.fieldName} = new(${entry.typeName})`); + writeLine(`${indent}\treturn json.Unmarshal(data, o.${entry.fieldName})`); + } + let exhaustive = false; + if (disc.unmapped.length > 0) { + writeLine(`${indent}default:`); + exhaustive = generateUnmappedFallback(disc.unmapped, indent + "\t"); + } + writeLine(`${indent}}`); + return exhaustive; + } + + /** + * Generate try-each fallback code for unmapped entries, chaining into + * presence dispatch if possible before falling back to raw try-each. + * Assumes a variable named `data` of type `json.Value` is in scope. + * Returns true if all generated paths return (exhaustive). + */ + function generateUnmappedFallback( + unmapped: { fieldName: string; typeName: string; originalType: Type; }[], + indent: string, + ): boolean { + if (unmapped.length <= 1) { + // Exactly 1 entry: it's the only remaining variant after dispatch, + // so use a hard error return instead of speculative err == nil. + for (const entry of unmapped) { + writeLine(`${indent}o.${entry.fieldName} = new(${entry.typeName})`); + writeLine(`${indent}return json.Unmarshal(data, o.${entry.fieldName})`); + } + return unmapped.length === 1; + } + // Try chaining presence dispatch on the remaining subset + const pres = findPresenceDiscriminator(unmapped); + if (pres) { + return generatePresenceDispatch(pres, indent); + } + else { + for (const entry of unmapped) { + writeLine(`${indent}var v${entry.fieldName} ${entry.typeName}`); + writeLine(`${indent}if err := json.Unmarshal(data, &v${entry.fieldName}); err == nil {`); + writeLine(`${indent}\to.${entry.fieldName} = &v${entry.fieldName}`); + writeLine(`${indent}\treturn nil`); + writeLine(`${indent}}`); + } + return false; + } + } + + /** + * Iteratively collect all presence discriminator checks across multiple + * passes, so they can be emitted as a single flat switch with one scan. + */ + function collectAllPresenceChecks( + pres: NonNullable>, + ): { + allChecks: { jsonFieldName: string; entry: { fieldName: string; typeName: string; originalType: Type; }; }[]; + finalUnmapped: { fieldName: string; typeName: string; originalType: Type; }[]; + } { + const allChecks = [...pres.checks]; + let remaining = pres.unmapped; + while (remaining.length > 1) { + const next = findPresenceDiscriminator(remaining); + if (!next) break; + allChecks.push(...next.checks); + remaining = next.unmapped; + } + return { allChecks, finalUnmapped: remaining }; + } + + /** + * Generate Go code for presence-based union dispatch. + * Assumes a variable named `data` of type `json.Value` is in scope. + * Collects all presence checks iteratively, then emits a single flat + * switch jsonObjectHasKey(data, key1, key2, ...) so data is scanned once. + * Returns true if all switch branches return (exhaustive). + */ + function generatePresenceDispatch( + pres: NonNullable>, + indent: string, + ): boolean { + const { allChecks, finalUnmapped } = collectAllPresenceChecks(pres); + const args = allChecks.map(c => JSON.stringify(c.jsonFieldName)).join(", "); + writeLine(`${indent}switch jsonObjectHasKey(data, ${args}) {`); + for (let i = 0; i < allChecks.length; i++) { + writeLine(`${indent}case ${i}: // ${allChecks[i].jsonFieldName}`); + writeLine(`${indent}\to.${allChecks[i].entry.fieldName} = new(${allChecks[i].entry.typeName})`); + writeLine(`${indent}\treturn json.Unmarshal(data, o.${allChecks[i].entry.fieldName})`); + } + if (finalUnmapped.length > 0) { + writeLine(`${indent}default:`); + if (finalUnmapped.length === 1) { + // Only one variant left after dispatch — use hard error return. + const entry = finalUnmapped[0]; + writeLine(`${indent}\to.${entry.fieldName} = new(${entry.typeName})`); + writeLine(`${indent}\treturn json.Unmarshal(data, o.${entry.fieldName})`); + } + else { + for (const entry of finalUnmapped) { + writeLine(`${indent}\tvar v${entry.fieldName} ${entry.typeName}`); + writeLine(`${indent}\tif err := json.Unmarshal(data, &v${entry.fieldName}); err == nil {`); + writeLine(`${indent}\t\to.${entry.fieldName} = &v${entry.fieldName}`); + writeLine(`${indent}\t\treturn nil`); + writeLine(`${indent}\t}`); + } + } + } + writeLine(`${indent}}`); + // Exhaustive if the default case has a single hard-returning entry + return finalUnmapped.length === 1; + } + + function generateResolvedStruct(structure: Structure, indent: string = "\t"): string[] { + const lines: string[] = []; + + for (const prop of structure.properties) { + // Add property documentation if it exists + if (prop.documentation) { + const propDoc = formatDocumentation(prop.documentation); + if (propDoc) { + // Add the documentation with proper indentation + for (const line of propDoc.split("\n").filter(l => l)) { + lines.push(`${indent}${line}`); + } + } + } + + const type = resolveType(prop.type); + + // For reference types that are structures, use a named resolved type + if (prop.type.kind === "reference") { + const refStructure = model.structures.find(s => s.name === type.name); + if (refStructure) { + // Use a named type for the resolved version + lines.push(`${indent}${goFieldName(prop)} Resolved${type.name} \`json:"${prop.name},omitzero"\``); + continue; + } + } + + // For other types (primitives, enums, arrays, etc.), use the type directly (no pointer) + const goType = type.name; + lines.push(`${indent}${goFieldName(prop)} ${goType} \`json:"${prop.name},omitzero"\``); + } + + return lines; + } + + function generateResolveConversion(structure: Structure, varName: string, indent: string): string[] { + const lines: string[] = []; + + for (const prop of structure.properties) { + const type = resolveType(prop.type); + const fieldName = goFieldName(prop); + const accessPath = `${varName}.${fieldName}`; + + // For reference types that are structures, call the resolve method + if (prop.type.kind === "reference") { + const refStructure = model.structures.find(s => s.name === type.name); + if (refStructure) { + lines.push(`${indent}${fieldName}: ${accessPath}.resolve(),`); + continue; + } + } + + // For other types, dereference if pointer + if (prop.optional || type.needsPointer) { + lines.push(`${indent}${fieldName}: derefOr(${accessPath}),`); + } + else { + lines.push(`${indent}${fieldName}: ${accessPath},`); + } + } + + return lines; + } + + function collectStructureDependencies(structure: Structure, visited = new Set()): Structure[] { + if (visited.has(structure.name)) { + return []; + } + visited.add(structure.name); + + const deps: Structure[] = []; + + for (const prop of structure.properties) { + if (prop.type.kind === "reference") { + const refStructure = model.structures.find(s => s.name === (prop.type as ReferenceType).name); + if (refStructure) { + deps.push(...collectStructureDependencies(refStructure, new Set(visited))); + deps.push(refStructure); + } + } + } + + return deps; + } + + function generateResolvedTypeAndHelper(structure: Structure, isMain: boolean = false): string[] { + const lines: string[] = []; + const typeName = `Resolved${structure.name}`; + // Main method is exported (Resolve), helpers are unexported (resolve) + const methodName = isMain ? `Resolve` : `resolve`; + + // Generate the resolved type with documentation + if (!isMain) { + // For non-main types, add standard documentation header + if (structure.documentation) { + const typeDoc = formatDocumentation(structure.documentation); + if (typeDoc) { + // Prepend comment explaining this is the resolved version + lines.push(`// ${typeName} is a resolved version of ${structure.name} with all optional fields`); + lines.push(`// converted to non-pointer values for easier access.`); + lines.push(`//`); + // Add the original structure documentation + for (const line of typeDoc.split("\n").filter(l => l)) { + lines.push(line); + } + } + } + else { + // If no documentation, just add a basic comment + lines.push(`// ${typeName} is a resolved version of ${structure.name} with all optional fields`); + lines.push(`// converted to non-pointer values for easier access.`); + } + } + // For main type, documentation is added separately before calling this function + + lines.push(`type ${typeName} struct {`); + lines.push(...generateResolvedStruct(structure, "\t")); + lines.push(`}`); + lines.push(``); + + // Generate the conversion method on the pointer receiver + lines.push(`func (v *${structure.name}) ${methodName}() ${typeName} {`); + lines.push(`\tif v == nil {`); + lines.push(`\t\treturn ${typeName}{}`); + lines.push(`\t}`); + lines.push(`\treturn ${typeName}{`); + lines.push(...generateResolveConversion(structure, "v", "\t\t")); + lines.push(`\t}`); + lines.push(`}`); + lines.push(``); + + return lines; + } + + // File header + writeLine("// Code generated by generate.mts; DO NOT EDIT."); + writeLine(""); + writeLine("package lsproto"); + writeLine(""); + writeLine(`import (`); + writeLine(`\t"cmp"`); + writeLine(`\t"fmt"`); + writeLine(`\t"strings"`); + writeLine(""); + writeLine(`\t"github.com/microsoft/TypeScript/tsc/internal/json"`); + writeLine(`)`); + writeLine(""); + writeLine("// Meta model version " + model.metaData.version); + writeLine(""); + + // Generate structures + writeLine("// Structures\n"); + + for (const structure of model.structures) { + function generateStructFields(name: string, includeDocumentation: boolean) { + if (includeDocumentation) { + write(formatDocumentation(structure.documentation)); + } + + writeLine(`type ${name} struct {`); + + // Properties are now inlined, no need to embed extends/mixins + for (const prop of structure.properties) { + if (includeDocumentation) { + write(formatDocumentation(prop.documentation)); + } + + const type = resolveType(prop.type); + + // For properties marked with omitzeroValue, use value type with omitzero instead of pointer + const useOmitzero = prop.optional || prop.omitzeroValue; + const goType = (prop.optional || type.needsPointer) && !prop.omitzeroValue ? `*${type.name}` : type.name; + + // Strictness markers for the shared unmarshalStruct interpreter: + // required = must be present. A nilable field (pointer/slice/map, + // not omitzero) rejects an explicit JSON null by default; mark the + // rare spec-nullable ones with `nullable` so the interpreter allows it. + const required = !prop.optional && !prop.omitzeroValue; + const nilable = prop.optional || type.needsPointer || type.name.startsWith("[]") || type.name.startsWith("map[") || !!prop.omitzeroValue; + const nullable = nilable && (typeCanBeNull(prop.type) || !!prop.omitzeroValue); + const lspMarkers = [required ? "required" : "", nullable ? "nullable" : ""].filter(Boolean).join(","); + const lspTag = lspMarkers ? ` lsp:"${lspMarkers}"` : ""; + + writeLine(`\t${goFieldName(prop)} ${goType} \`json:"${prop.name}${useOmitzero ? ",omitzero" : ""}"${lspTag}\``); + + if (includeDocumentation) { + writeLine(""); + } + } + + // Special: add RegisterOptions field to Registration + if (structure.name === "Registration") { + writeLine(""); + writeLine(`\t// Options necessary for the registration. Determines the method.`); + writeLine(`\tRegisterOptions *RegisterOptions \`json:"-"\``); + } + + writeLine("}"); + writeLine(""); + } + + generateStructFields(structure.name, true); + writeLine(""); + + if (hasTextDocumentURI(structure)) { + // Generate TextDocumentURI method + const textDocProp = structure.properties?.find(p => (p.name === "textDocument" || p.name === "_vs_textDocument") && p.type.kind === "reference" && p.type.name === "TextDocumentIdentifier"); + const textDocFieldName = textDocProp ? goFieldName(textDocProp) : "TextDocument"; + writeLine(`func (s *${structure.name}) TextDocumentURI() DocumentUri {`); + writeLine(`\treturn s.${textDocFieldName}.Uri`); + writeLine(`}`); + writeLine(""); + + if (hasTextDocumentPosition(structure)) { + // Generate TextDocumentPosition method + const posProp = structure.properties?.find(p => (p.name === "position" || p.name === "_vs_position") && p.type.kind === "reference" && p.type.name === "Position"); + const posFieldName = posProp ? goFieldName(posProp) : "Position"; + writeLine(`func (s *${structure.name}) TextDocumentPosition() Position {`); + writeLine(`\treturn s.${posFieldName}`); + writeLine(`}`); + writeLine(""); + } + } + + const locationUriProperty = getLocationUriProperty(structure); + if (locationUriProperty) { + // Generate Location method + writeLine(`func (s ${structure.name}) GetLocation() Location {`); + if (locationUriProperty === "Uri" && structure.name === "Location") { + writeLine(`\treturn s`); + } + else { + writeLine(`\treturn Location{`); + writeLine(`\t\tUri: s.${locationUriProperty},`); + writeLine(`\t\tRange: s.${locationUriProperty.replace(/Uri$/, "Range")},`); + writeLine(`\t}`); + } + writeLine(`}`); + writeLine(""); + } + + // Generate UnmarshalJSONFrom method for structure validation + // Skip Registration (has custom marshal/unmarshal generated separately) + // Skip properties marked with omitzeroValue since they're optional by nature + const requiredProps = structure.properties?.filter(p => { + if (p.optional) return false; + if (p.omitzeroValue) return false; + return true; + }) || []; + // Check if any fields need null rejection + const hasNullRejectableFields = structure.properties?.some(p => { + if (p.omitzeroValue) return false; + if (typeCanBeNull(p.type)) return false; + const resolved = resolveType(p.type); + return p.optional || resolved.needsPointer || resolved.name.startsWith("[]") || resolved.name.startsWith("map["); + }) || false; + if ((requiredProps.length > 0 || hasNullRejectableFields) && structure.name !== "Registration") { + writeLine(`\tvar _ json.UnmarshalerFrom = (*${structure.name})(nil)`); + writeLine(""); + writeLine(`func (s *${structure.name}) UnmarshalJSONFrom(dec *json.Decoder) error {`); + writeLine(`\treturn unmarshalStruct(s, dec)`); + writeLine(`}`); + writeLine(""); + } + + // Generate RegisterOptions struct and custom Registration marshal/unmarshal + // right after the Registration struct definition. + if (structure.name === "Registration") { + // RegisterOptions struct + writeLine(`// RegisterOptions is an externally-tagged union representing the options for a capability registration.`); + writeLine(`// Exactly one field should be set. The set field determines the method for the registration.`); + writeLine(`type RegisterOptions struct {`); + for (const reg of registrationMethods) { + writeLine(`\t${reg.fieldName} *${reg.optionsTypeName}`); + } + writeLine(`}`); + writeLine(""); + + // MarshalJSONTo for Registration + writeLine(`var _ json.MarshalerTo = (*Registration)(nil)`); + writeLine(""); + writeLine(`func (s *Registration) MarshalJSONTo(enc *json.Encoder) error {`); + + // Assert RegisterOptions is set and exactly one field is set + writeLine(`\tif s.RegisterOptions == nil {`); + writeLine(`\t\tpanic("RegisterOptions must be set")`); + writeLine(`\t}`); + writeLine(`\tassertOnlyOne("exactly one element of RegisterOptions should be set", countNonNil(s.RegisterOptions))`); + writeLine(""); + + writeLine(`\tif err := enc.WriteToken(json.BeginObject); err != nil {`); + writeLine(`\t\treturn err`); + writeLine(`\t}`); + writeLine(`\tif err := enc.WriteValue(json.Value(\`"id"\`)); err != nil {`); + writeLine(`\t\treturn err`); + writeLine(`\t}`); + writeLine(`\tif err := json.MarshalEncode(enc, s.Id); err != nil {`); + writeLine(`\t\treturn err`); + writeLine(`\t}`); + writeLine(`\tif err := enc.WriteValue(json.Value(\`"method"\`)); err != nil {`); + writeLine(`\t\treturn err`); + writeLine(`\t}`); + writeLine(`\tvar method json.Value`); + writeLine(`\tvar opts any`); + writeLine(`\tswitch {`); + for (const reg of registrationMethods) { + writeLine(`\tcase s.RegisterOptions.${reg.fieldName} != nil:`); + writeLine(`\t\tmethod = json.Value(\`"${reg.registrationMethod}"\`)`); + writeLine(`\t\topts = s.RegisterOptions.${reg.fieldName}`); + } + writeLine(`\t}`); + writeLine(`\tif err := enc.WriteValue(method); err != nil {`); + writeLine(`\t\treturn err`); + writeLine(`\t}`); + writeLine(`\tif err := enc.WriteValue(json.Value(\`"registerOptions"\`)); err != nil {`); + writeLine(`\t\treturn err`); + writeLine(`\t}`); + writeLine(`\tif err := json.MarshalEncode(enc, opts); err != nil {`); + writeLine(`\t\treturn err`); + writeLine(`\t}`); + writeLine(`\treturn enc.WriteToken(json.EndObject)`); + writeLine(`}`); + writeLine(""); + + // UnmarshalJSONFrom for Registration + writeLine(`var _ json.UnmarshalerFrom = (*Registration)(nil)`); + writeLine(""); + writeLine(`func (s *Registration) UnmarshalJSONFrom(dec *json.Decoder) error {`); + writeLine(`\t*s = Registration{}`); + writeLine(`\tconst (`); + writeLine(`\t\tmissingId uint = 1 << iota`); + writeLine(`\t\tmissingMethod`); + writeLine(`\t\t_missingLast`); + writeLine(`\t)`); + writeLine(`\tmissing := _missingLast - 1`); + writeLine(""); + writeLine(`\tif k := dec.PeekKind(); k != '{' {`); + writeLine(`\t\treturn errNotObject(k)`); + writeLine(`\t}`); + writeLine(`\tif _, err := dec.ReadToken(); err != nil {`); + writeLine(`\t\treturn err`); + writeLine(`\t}`); + writeLine(""); + writeLine(`\tvar method string`); + writeLine(`\tvar rawRegisterOptions json.Value`); + writeLine(""); + writeLine(`\tfor dec.PeekKind() != '}' {`); + writeLine(`\t\tname, err := dec.ReadValue()`); + writeLine(`\t\tif err != nil {`); + writeLine(`\t\t\treturn err`); + writeLine(`\t\t}`); + writeLine(`\t\tswitch string(name) {`); + writeLine(`\t\tcase \`"id"\`:`); + writeLine(`\t\t\tmissing &^= missingId`); + writeLine(`\t\t\tif err := json.UnmarshalDecode(dec, &s.Id); err != nil {`); + writeLine(`\t\t\t\treturn err`); + writeLine(`\t\t\t}`); + writeLine(`\t\tcase \`"method"\`:`); + writeLine(`\t\t\tmissing &^= missingMethod`); + writeLine(`\t\t\tif err := json.UnmarshalDecode(dec, &method); err != nil {`); + writeLine(`\t\t\t\treturn err`); + writeLine(`\t\t\t}`); + writeLine(`\t\tcase \`"registerOptions"\`:`); + writeLine(`\t\t\tv, err := dec.ReadValue()`); + writeLine(`\t\t\tif err != nil {`); + writeLine(`\t\t\t\treturn err`); + writeLine(`\t\t\t}`); + writeLine(`\t\t\trawRegisterOptions = v`); + writeLine(`\t\tdefault:`); + writeLine(`\t\t\tif err := dec.SkipValue(); err != nil {`); + writeLine(`\t\t\t\treturn err`); + writeLine(`\t\t\t}`); + writeLine(`\t\t}`); + writeLine(`\t}`); + writeLine(""); + writeLine(`\tif _, err := dec.ReadToken(); err != nil {`); + writeLine(`\t\treturn err`); + writeLine(`\t}`); + writeLine(""); + writeLine(`\tif missing != 0 {`); + writeLine(`\t\tvar missingProps []string`); + writeLine(`\t\tif missing&missingId != 0 {`); + writeLine(`\t\t\tmissingProps = append(missingProps, "id")`); + writeLine(`\t\t}`); + writeLine(`\t\tif missing&missingMethod != 0 {`); + writeLine(`\t\t\tmissingProps = append(missingProps, "method")`); + writeLine(`\t\t}`); + writeLine(`\t\treturn errMissing(missingProps)`); + writeLine(`\t}`); + writeLine(""); + writeLine(`\tif len(rawRegisterOptions) > 0 {`); + writeLine(`\t\ts.RegisterOptions = &RegisterOptions{}`); + writeLine(`\t\tswitch Method(method) {`); + for (const reg of registrationMethods) { + writeLine(`\t\tcase Method${reg.fieldName}:`); + writeLine(`\t\t\tvar v ${reg.optionsTypeName}`); + writeLine(`\t\t\tif err := json.Unmarshal(rawRegisterOptions, &v); err != nil {`); + writeLine(`\t\t\t\treturn err`); + writeLine(`\t\t\t}`); + writeLine(`\t\t\ts.RegisterOptions.${reg.fieldName} = &v`); + } + writeLine(`\t\tdefault:`); + writeLine(`\t\t\treturn fmt.Errorf("unknown registration method: %s", method)`); + writeLine(`\t\t}`); + writeLine(`\t} else {`); + writeLine(`\t\treturn fmt.Errorf("missing registerOptions for method: %s", method)`); + writeLine(`\t}`); + writeLine(""); + writeLine(`\treturn nil`); + writeLine(`}`); + writeLine(""); + } + + if (compareStructures.has(structure.name)) { + generateCompareMethod(structure); + } + } + + function generateCompareMethod(structure: Structure) { + const props = structure.properties ?? []; + writeLine(`func (s *${structure.name}) Compare(other *${structure.name}) int {`); + for (let i = 0; i < props.length; i++) { + const prop = props[i]; + const isLast = i === props.length - 1; + const fieldName = goFieldName(prop); + const expr = compareExpressionForProperty(structure.name, prop, fieldName); + if (isLast) { + writeLine(`\treturn ${expr}`); + } + else { + writeLine(`\tif c := ${expr}; c != 0 {`); + writeLine(`\t\treturn c`); + writeLine(`\t}`); + } + } + writeLine(`}`); + writeLine(""); + } + + function compareExpressionForProperty(structName: string, prop: Property, fieldName: string): string { + const resolved = resolveType(prop.type); + const isPointerField = (prop.optional || resolved.needsPointer) && !prop.omitzeroValue; + + if (prop.type.kind === "reference") { + const refName = prop.type.name; + if (compareStructures.has(refName)) { + if (isPointerField) { + return `s.${fieldName}.Compare(other.${fieldName})`; + } + return `s.${fieldName}.Compare(&other.${fieldName})`; + } + } + + if (prop.type.kind === "base") { + switch (prop.type.name) { + case "string": + case "URI": + case "DocumentUri": + case "integer": + case "uinteger": + case "decimal": + return `cmp.Compare(s.${fieldName}, other.${fieldName})`; + } + } + + throw new Error(`Cannot generate Compare for ${structName}.${fieldName}: unsupported field type ${JSON.stringify(prop.type)}. Add support in compareExpressionForProperty.`); + } + + // Helper function to detect if an enum is a bitflag enum + // Hardcoded list of bitflag enums + const bitflagEnums = new Set(["WatchKind"]); + + function isBitflagEnum(enumeration: any): boolean { + return bitflagEnums.has(enumeration.name); + } + + // Generate enumerations + writeLine("// Enumerations\n"); + + for (const enumeration of model.enumerations) { + write(formatDocumentation(enumeration.documentation)); + + let baseType; + switch (enumeration.type.name) { + case "string": + baseType = "string"; + break; + case "integer": + baseType = "int32"; + break; + case "uinteger": + baseType = "uint32"; + break; + default: + throw new Error(`Unsupported enum type: ${enumeration.type.name}`); + } + + writeLine(`type ${enumeration.name} ${baseType}`); + writeLine(""); + + // Get the pre-processed enum entries map that avoids duplicates + + const enumValues = enumeration.values.map(value => ({ + value: String(value.value), + numericValue: Number(value.value), + name: value.name, + identifier: `${enumeration.name}${titleCase(value.name)}`, + documentation: value.documentation, + deprecated: value.deprecated, + })); + + writeLine("const ("); + + // Process entries with unique identifiers + for (const entry of enumValues) { + write(formatDocumentation(entry.documentation)); + + let valueLiteral; + // Handle string values + if (enumeration.type.name === "string") { + valueLiteral = `"${entry.value.replace(/^"|"$/g, "")}"`; + } + else { + valueLiteral = entry.value; + } + + writeLine(`\t${entry.identifier} ${enumeration.name} = ${valueLiteral}`); + } + + writeLine(")"); + writeLine(""); + + // Generate String() method for non-string enums + if (enumeration.type.name !== "string") { + const isBitflag = isBitflagEnum(enumeration); + + if (isBitflag) { + // Generate bitflag-aware String() method using stringer-style efficiency + const sortedValues = [...enumValues].sort((a, b) => a.numericValue - b.numericValue); + const names = sortedValues.map(v => v.name); + const values = sortedValues.map(v => v.numericValue); + + const nameConst = `_${enumeration.name}_name`; + const indexVar = `_${enumeration.name}_index`; + const combinedNames = names.join(""); + + writeLine(`const ${nameConst} = "${combinedNames}"`); + write(`var ${indexVar} = [...]uint16{0`); + let offset = 0; + for (const name of names) { + offset += name.length; + write(`, ${offset}`); + } + writeLine(`}`); + writeLine(""); + + writeLine(`func (e ${enumeration.name}) String() string {`); + writeLine(`\tif e == 0 {`); + writeLine(`\t\treturn "0"`); + writeLine(`\t}`); + writeLine(`\tvar parts []string`); + for (let i = 0; i < values.length; i++) { + writeLine(`\tif e&${values[i]} != 0 {`); + writeLine(`\t\tparts = append(parts, ${nameConst}[${indexVar}[${i}]:${indexVar}[${i + 1}]])`); + writeLine(`\t}`); + } + writeLine(`\tif len(parts) == 0 {`); + writeLine(`\t\treturn fmt.Sprintf("${enumeration.name}(%d)", e)`); + writeLine(`\t}`); + writeLine(`\treturn strings.Join(parts, "|")`); + writeLine(`}`); + writeLine(""); + } + else { + // Generate regular String() method using stringer-style approach + // Split values into runs of contiguous values + const sortedValues = [...enumValues].sort((a, b) => a.numericValue - b.numericValue); + + // Split into runs + const runs: Array<{ names: string[]; values: number[]; }> = []; + let currentRun = { names: [sortedValues[0].name], values: [sortedValues[0].numericValue] }; + + for (let i = 1; i < sortedValues.length; i++) { + if (sortedValues[i].numericValue === sortedValues[i - 1].numericValue + 1) { + currentRun.names.push(sortedValues[i].name); + currentRun.values.push(sortedValues[i].numericValue); + } + else { + runs.push(currentRun); + currentRun = { names: [sortedValues[i].name], values: [sortedValues[i].numericValue] }; + } + } + runs.push(currentRun); + + const nameConst = `_${enumeration.name}_name`; + const indexVar = `_${enumeration.name}_index`; + + if (runs.length === 1) { + // Single contiguous run - simple case + const combinedNames = runs[0].names.join(""); + writeLine(`const ${nameConst} = "${combinedNames}"`); + write(`var ${indexVar} = [...]uint16{0`); + let offset = 0; + for (const name of runs[0].names) { + offset += name.length; + write(`, ${offset}`); + } + writeLine(`}`); + writeLine(""); + + const minVal = runs[0].values[0]; + writeLine(`func (e ${enumeration.name}) String() string {`); + writeLine(`\ti := int(e) - ${minVal}`); + // For unsigned types, i can still be negative if e < minVal (due to underflow in conversion) + // So we always need to check both bounds + writeLine(`\tif i < 0 || i >= len(${indexVar})-1 {`); + writeLine(`\t\treturn fmt.Sprintf("${enumeration.name}(%d)", e)`); + writeLine(`\t}`); + writeLine(`\treturn ${nameConst}[${indexVar}[i]:${indexVar}[i+1]]`); + writeLine(`}`); + writeLine(""); + } + else if (runs.length <= 10) { + // Multiple runs - use switch statement + let allNames = ""; + const runInfo: Array<{ startOffset: number; endOffset: number; minVal: number; maxVal: number; }> = []; + + for (const run of runs) { + const startOffset = allNames.length; + allNames += run.names.join(""); + const endOffset = allNames.length; + runInfo.push({ + startOffset, + endOffset, + minVal: run.values[0], + maxVal: run.values[run.values.length - 1], + }); + } + + writeLine(`const ${nameConst} = "${allNames}"`); + writeLine(""); + + // Generate index variables for each run + for (let i = 0; i < runs.length; i++) { + write(`var ${indexVar}_${i} = [...]uint16{0`); + let offset = 0; + for (const name of runs[i].names) { + offset += name.length; + write(`, ${offset}`); + } + writeLine(`}`); + } + writeLine(""); + + writeLine(`func (e ${enumeration.name}) String() string {`); + writeLine(`\tswitch {`); + + for (let i = 0; i < runs.length; i++) { + const run = runs[i]; + const info = runInfo[i]; + + if (run.values.length === 1) { + writeLine(`\tcase e == ${run.values[0]}:`); + writeLine(`\t\treturn ${nameConst}[${info.startOffset}:${info.endOffset}]`); + } + else { + if (info.minVal === 0 && baseType.startsWith("uint")) { + writeLine(`\tcase e <= ${info.maxVal}:`); + } + else if (info.minVal === 0) { + writeLine(`\tcase 0 <= e && e <= ${info.maxVal}:`); + } + else { + writeLine(`\tcase ${info.minVal} <= e && e <= ${info.maxVal}:`); + } + writeLine(`\t\ti := int(e) - ${info.minVal}`); + writeLine(`\t\treturn ${nameConst}[${info.startOffset}+${indexVar}_${i}[i]:${info.startOffset}+${indexVar}_${i}[i+1]]`); + } + } + + writeLine(`\tdefault:`); + writeLine(`\t\treturn fmt.Sprintf("${enumeration.name}(%d)", e)`); + writeLine(`\t}`); + writeLine(`}`); + writeLine(""); + } + else { + // Too many runs - use a map + let allNames = ""; + const valueMap: Array<{ value: number; startOffset: number; endOffset: number; }> = []; + + for (const run of runs) { + for (let i = 0; i < run.names.length; i++) { + const startOffset = allNames.length; + allNames += run.names[i]; + const endOffset = allNames.length; + valueMap.push({ value: run.values[i], startOffset, endOffset }); + } + } + + writeLine(`const ${nameConst} = "${allNames}"`); + writeLine(""); + writeLine(`var ${enumeration.name}_map = map[${enumeration.name}]string{`); + for (const entry of valueMap) { + writeLine(`\t${entry.value}: ${nameConst}[${entry.startOffset}:${entry.endOffset}],`); + } + writeLine(`}`); + writeLine(""); + + writeLine(`func (e ${enumeration.name}) String() string {`); + writeLine(`\tif str, ok := ${enumeration.name}_map[e]; ok {`); + writeLine(`\t\treturn str`); + writeLine(`\t}`); + writeLine(`\treturn fmt.Sprintf("${enumeration.name}(%d)", e)`); + writeLine(`}`); + writeLine(""); + } + } + } + + // Generate Error() method for ErrorCode to implement the error interface + if (enumeration.name === "ErrorCode") { + writeLine(`func (e ${enumeration.name}) Error() string {`); + writeLine(`\treturn e.String()`); + writeLine(`}`); + writeLine(""); + } + } + + const requestsAndNotifications: (Request | Notification)[] = [...model.requests, ...model.notifications]; + + writeLine("// Methods"); + writeLine("const ("); + for (const request of requestsAndNotifications) { + write(formatDocumentation(request.documentation)); + + const methodName = methodNameIdentifier(request.method); + + writeLine(`\tMethod${methodName} Method = "${request.method}"`); + } + // Emit constants for registration-only methods (not also a request/notification) + for (const reg of registrationMethods) { + if (reg.isRegistrationOnly) { + writeLine(`\t// Registration-only method for ${reg.registrationMethod}.`); + writeLine(`\tMethod${reg.fieldName} Method = "${reg.registrationMethod}"`); + } + } + writeLine(")"); + writeLine(""); + + // Generate request response types + writeLine("// Request response types"); + writeLine(""); + + for (const request of requestsAndNotifications) { + const methodName = methodNameIdentifier(request.method); + + let responseTypeName: string | undefined; + + if ("result" in request) { + if (request.typeName && request.typeName.endsWith("Request")) { + responseTypeName = request.typeName.replace(/Request$/, "Response"); + } + else { + responseTypeName = `${methodName}Response`; + } + + writeLine(`// Response type for \`${request.method}\``); + + // Special case for response types that are explicitly base type "null" + if (request.result.kind === "base" && request.result.name === "null") { + writeLine(`type ${responseTypeName} = Null`); + } + else { + const resultType = resolveType(request.result); + const goType = resultType.needsPointer ? `*${resultType.name}` : resultType.name; + writeLine(`type ${responseTypeName} = ${goType}`); + } + writeLine(""); + } + + if (Array.isArray(request.params)) { + throw new Error("Unexpected request params for " + methodName + ": " + JSON.stringify(request.params)); + } + + const paramType = request.params ? resolveType(request.params) : undefined; + const paramGoType = paramType ? (paramType.needsPointer ? `*${paramType.name}` : paramType.name) : "NoParams"; + + writeLine(`// Type mapping info for \`${request.method}\``); + if (responseTypeName) { + writeLine(`var ${methodName}Info = RequestInfo[${paramGoType}, ${responseTypeName}]{Method: Method${methodName}}`); + } + else { + writeLine(`var ${methodName}Info = NotificationInfo[${paramGoType}]{Method: Method${methodName}}`); + } + + writeLine(""); + } + + // Generate type aliases + writeLine("// Type aliases\n"); + for (const aliasName of customTypeAliases) { + const resolvedType = resolveType(aliasName.type); + const goType = resolvedType.needsPointer ? `*${resolvedType.name}` : resolvedType.name; + writeLine(`type ${aliasName.name} = ${goType}`); + writeLine(""); + } + + // Generate union types + writeLine("// Union types\n"); + + for (const [name, members] of typeInfo.unionTypes.entries()) { + writeLine(`type ${name} struct {`); + const uniqueTypeFields = new Map(); // Maps type name -> field name + const uniqueTypeToOriginal = new Map(); // Maps type name -> original meta model Type + + let hasLocations = false; + for (const member of members) { + const type = resolveType(member.type); + const memberType = type.name; + + // If this type name already exists in our map, skip it + if (!uniqueTypeFields.has(memberType)) { + const fieldName = titleCase(member.name); + uniqueTypeFields.set(memberType, fieldName); + uniqueTypeToOriginal.set(memberType, member.type); + writeLine(`\t${fieldName} *${memberType}`); + if (fieldName === "Locations" && memberType === "[]Location") { + hasLocations = true; + } + } + } + + writeLine(`}`); + writeLine(""); + + // Get the field names and types for marshal/unmarshal methods + const fieldEntries = Array.from(uniqueTypeFields.entries()).map(([typeName, fieldName]) => ({ + fieldName, + typeName, + originalType: uniqueTypeToOriginal.get(typeName)!, + })); + + // Marshal method + writeLine(`var _ json.MarshalerTo = (*${name})(nil)`); + writeLine(""); + const unionContainedNull = members.some(member => member.containedNull); + writeLine(`func (o *${name}) MarshalJSONTo(enc *json.Encoder) error {`); + writeLine(`\treturn marshalUnion(o, enc, "${name}", ${unionContainedNull})`); + writeLine(`}`); + writeLine(""); + + // Unmarshal method + writeLine(`var _ json.UnmarshalerFrom = (*${name})(nil)`); + writeLine(""); + + writeLine(`func (o *${name}) UnmarshalJSONFrom(dec *json.Decoder) error {`); + writeLine(`\t*o = ${name}{}`); + writeLine(""); + + // Group field entries by their expected JSON token kind for optimized dispatch. + const kindMap = new Map(); + const unknownKindEntries: typeof fieldEntries = []; + for (const entry of fieldEntries) { + const kind = jsonKindForType(entry.originalType); + if (!kind) { + unknownKindEntries.push(entry); + } + else { + if (!kindMap.has(kind)) kindMap.set(kind, []); + kindMap.get(kind)!.push(entry); + } + } + + // Sort ambiguous variants (same JSON kind) by number of required fields + // descending, so more specific variants are tried first. This prevents + // a less specific variant from greedily matching inputs intended for + // a more specific one. + function countRequiredFields(entry: typeof fieldEntries[0]): number { + if (entry.originalType.kind !== "reference") return 0; + const structure = model.structures.find(s => s.name === (entry.originalType as ReferenceType).name); + if (!structure) return 0; + return structure.properties.filter(p => !p.optional && !p.omitzeroValue).length; + } + + for (const [, entries] of kindMap) { + if (entries.length > 1) { + entries.sort((a, b) => countRequiredFields(b) - countRequiredFields(a)); + } + } + + // Also sort the flat fieldEntries to match (for the fallback path) + // We need to sort only within groups of the same kind. + { + const sorted: typeof fieldEntries = []; + const seen = new Set(); + for (const [, entries] of kindMap) { + for (const entry of entries) { + sorted.push(entry); + seen.add(entry.fieldName); + } + } + for (const entry of unknownKindEntries) { + if (!seen.has(entry.fieldName)) { + sorted.push(entry); + } + } + // Replace fieldEntries contents with sorted order + fieldEntries.length = 0; + fieldEntries.push(...sorted); + } + + // Validate that ambiguous union variants (same JSON kind) don't have + // order-dependent overlap. Two struct variants overlap if one's required + // fields are a subset of the other's, meaning any valid input for the + // superset also successfully parses as the subset (since unknown properties + // are ignored). This would make the unmarshal result depend on try order. + // + // Exception: variants discriminated by literal field values (e.g., a "kind" + // field with different string literal types) are safe because the literal + // unmarshaler rejects mismatched values. + for (const [kind, entries] of kindMap) { + if (entries.length <= 1) continue; + + // Get required fields with their types for each variant + const variantInfo = entries.map(entry => { + if (entry.originalType.kind !== "reference") return null; + const structure = model.structures.find(s => s.name === (entry.originalType as ReferenceType).name); + if (!structure) return null; + const requiredFields = new Map(); + for (const p of structure.properties) { + if (!p.optional && !p.omitzeroValue) { + requiredFields.set(p.name, p.type); + } + } + return { entry, requiredFields }; + }).filter((v): v is NonNullable => v !== null); + + // Check if two variants are discriminated by literal field values. + // Returns true if they share a field where both sides have different + // literal types (stringLiteral, integerLiteral, booleanLiteral). + function isDiscriminatedByLiteral( + a: Map, + b: Map, + ): boolean { + for (const [fieldName, aType] of a) { + const bType = b.get(fieldName); + if (!bType) continue; + const aLiteral = aType.kind === "stringLiteral" || aType.kind === "integerLiteral" || aType.kind === "booleanLiteral"; + const bLiteral = bType.kind === "stringLiteral" || bType.kind === "integerLiteral" || bType.kind === "booleanLiteral"; + if (aLiteral && bLiteral) { + // Both are literals for the same field — check if values differ + if (aType.kind === bType.kind && (aType as any).value !== (bType as any).value) { + return true; + } + // Different literal kinds on same field also discriminates + if (aType.kind !== bType.kind) { + return true; + } + } + } + return false; + } + + // Check each pair for subset relationships + for (let i = 0; i < variantInfo.length; i++) { + for (let j = 0; j < variantInfo.length; j++) { + if (i === j) continue; + const a = variantInfo[i]; + const b = variantInfo[j]; + const aNames = new Set(a.requiredFields.keys()); + const bNames = new Set(b.requiredFields.keys()); + + const aSubsetOfB = [...aNames].every(f => bNames.has(f)); + if (!aSubsetOfB) continue; + + // Skip if discriminated by literal values + if (isDiscriminatedByLiteral(a.requiredFields, b.requiredFields)) continue; + + if (aNames.size < bNames.size) { + // a is a strict subset of b + const aIdx = entries.indexOf(a.entry); + const bIdx = entries.indexOf(b.entry); + if (aIdx < bIdx) { + console.warn( + `Warning: In union ${name} (${kind} variants), ` + + `${a.entry.fieldName} (required: [${[...aNames]}]) is tried before ` + + `${b.entry.fieldName} (required: [${[...bNames]}]), but ` + + `${a.entry.fieldName}'s required fields are a strict subset — ` + + `it will greedily match inputs intended for ${b.entry.fieldName}. ` + + `Reorder so the more specific variant is tried first.`, + ); + } + } + else if (aNames.size === bNames.size && i < j) { + // Identical required fields — truly ambiguous + console.warn( + `Warning: In union ${name} (${kind} variants), ` + + `${a.entry.fieldName} and ${b.entry.fieldName} have identical ` + + `required fields [${[...aNames]}] — they are structurally ` + + `indistinguishable and the unmarshal result is order-dependent.`, + ); + } + } + } + } + + // Determine if we can use PeekKind-based dispatch: + // - Every entry must have a known kind (no `any` etc.) + // - There must be at least 2 distinct cases (kind groups + null) for a switch to be worthwhile + const hasUnknownKinds = unknownKindEntries.length > 0; + const distinctKinds = kindMap.size + (unionContainedNull ? 1 : 0); + const canDispatch = !hasUnknownKinds && distinctKinds >= 2; + + // Check if all kind groups are unambiguous (exactly 1 entry each). + // When unambiguous, we can UnmarshalDecode directly without buffering. + const allUnambiguous = canDispatch && Array.from(kindMap.values()).every(entries => entries.length === 1); + + let fallbackExhaustive = false; + const hasBooleanKind = kindMap.has("boolean"); + if (canDispatch && allUnambiguous) { + // Best case: PeekKind + UnmarshalDecode directly, no ReadValue buffer needed. + if (hasBooleanKind) { + writeLine(`\tswitch kind := dec.PeekKind(); kind {`); + } + else { + writeLine(`\tswitch dec.PeekKind() {`); + } + + if (unionContainedNull) { + writeLine(`\tcase 'n':`); + writeLine(`\t\t_, err := dec.ReadToken()`); + writeLine(`\t\treturn err`); + } + + for (const [kind, entries] of kindMap) { + writeLine(`\t${goKindCasesForJsonKind(kind)}`); + const entry = entries[0]; + if (kind === "boolean") { + writeLine(`\t\to.${entry.fieldName} = new(kind == 't')`); + writeLine(`\t\t_, err := dec.ReadToken()`); + writeLine(`\t\treturn err`); + } + else { + writeLine(`\t\to.${entry.fieldName} = new(${entry.typeName})`); + writeLine(`\t\treturn json.UnmarshalDecode(dec, o.${entry.fieldName})`); + } + } + + writeLine(`\tdefault:`); + writeLine(`\t\treturn errInvalidKind("${name}", dec.PeekKind())`); + writeLine(`\t}`); + } + else if (canDispatch) { + // Mixed case: some kind groups have multiple entries. + // Use PeekKind to dispatch, then ReadValue + try-each within ambiguous groups, + // or UnmarshalDecode directly for unambiguous groups. + if (hasBooleanKind) { + writeLine(`\tswitch kind := dec.PeekKind(); kind {`); + } + else { + writeLine(`\tswitch dec.PeekKind() {`); + } + + if (unionContainedNull) { + writeLine(`\tcase 'n':`); + writeLine(`\t\t_, err := dec.ReadToken()`); + writeLine(`\t\treturn err`); + } + + for (const [kind, entries] of kindMap) { + writeLine(`\t${goKindCasesForJsonKind(kind)}`); + if (entries.length === 1) { + // Unambiguous: decode directly + const entry = entries[0]; + if (kind === "boolean") { + writeLine(`\t\to.${entry.fieldName} = new(kind == 't')`); + writeLine(`\t\t_, err := dec.ReadToken()`); + writeLine(`\t\treturn err`); + } + else { + writeLine(`\t\to.${entry.fieldName} = new(${entry.typeName})`); + writeLine(`\t\treturn json.UnmarshalDecode(dec, o.${entry.fieldName})`); + } + } + else { + // Ambiguous: buffer and dispatch + writeLine(`\t\tdata, err := dec.ReadValue()`); + writeLine(`\t\tif err != nil {`); + writeLine(`\t\t\treturn err`); + writeLine(`\t\t}`); + let exhaustive = false; + const disc = findDiscriminatorField(entries); + if (disc) { + exhaustive = generateDiscriminatorDispatch(disc, "\t\t"); + } + else { + const pres = findPresenceDiscriminator(entries); + if (pres) { + exhaustive = generatePresenceDispatch(pres, "\t\t"); + } + else { + for (const entry of entries) { + writeLine(`\t\tvar v${entry.fieldName} ${entry.typeName}`); + writeLine(`\t\tif err := json.Unmarshal(data, &v${entry.fieldName}); err == nil {`); + writeLine(`\t\t\to.${entry.fieldName} = &v${entry.fieldName}`); + writeLine(`\t\t\treturn nil`); + writeLine(`\t\t}`); + } + } + } + if (!exhaustive) { + writeLine(`\t\treturn errInvalidValue("${name}", data)`); + } + } + } + + writeLine(`\tdefault:`); + writeLine(`\t\treturn errInvalidKind("${name}", dec.PeekKind())`); + writeLine(`\t}`); + } + else { + // Fallback: unknown kinds present (e.g. `any`), use ReadValue + try-each. + writeLine("\tdata, err := dec.ReadValue()"); + writeLine("\tif err != nil {"); + writeLine("\t\treturn err"); + writeLine("\t}"); + + if (unionContainedNull) { + writeLine(`\tif string(data) == "null" {`); + writeLine(`\t\treturn nil`); + writeLine(`\t}`); + writeLine(""); + } + + let exhaustive = false; + const disc = findDiscriminatorField(fieldEntries); + if (disc) { + exhaustive = generateDiscriminatorDispatch(disc, "\t"); + } + else { + const pres = findPresenceDiscriminator(fieldEntries); + if (pres) { + exhaustive = generatePresenceDispatch(pres, "\t"); + } + else { + for (const entry of fieldEntries) { + writeLine(`\tvar v${entry.fieldName} ${entry.typeName}`); + writeLine(`\tif err := json.Unmarshal(data, &v${entry.fieldName}); err == nil {`); + writeLine(`\t\to.${entry.fieldName} = &v${entry.fieldName}`); + writeLine(`\t\treturn nil`); + writeLine(`\t}`); + } + } + } + fallbackExhaustive = exhaustive; + } + + if (canDispatch) { + // Dispatch paths have an exhaustive switch with default, nothing after the switch. + } + else if (!fallbackExhaustive) { + // Fallback paths: the final error references `data` which is in scope. + writeLine(`\treturn errInvalidValue("${name}", data)`); + } + writeLine(`}`); + writeLine(""); + + // Generate GetLocations method + if (hasLocations) { + writeLine(`func (o ${name}) GetLocations() *[]Location {`); + writeLine(`\treturn o.Locations`); + writeLine(`}`); + writeLine(""); + } + } + + // Generate literal types + writeLine("// Literal types\n"); + + for (const [value, name] of typeInfo.literalTypes.entries()) { + const jsonValue = JSON.stringify(value); + + writeLine(`// ${name} is a literal type for ${jsonValue}`); + writeLine(`type ${name} struct{}`); + writeLine(""); + + writeLine(`var _ json.MarshalerTo = ${name}{}`); + writeLine(""); + + writeLine(`func (o ${name}) MarshalJSONTo(enc *json.Encoder) error {`); + writeLine(`\treturn enc.WriteValue(json.Value(\`${jsonValue}\`))`); + writeLine(`}`); + writeLine(""); + + writeLine(`var _ json.UnmarshalerFrom = &${name}{}`); + writeLine(""); + + writeLine(`func (o *${name}) UnmarshalJSONFrom(dec *json.Decoder) error {`); + writeLine(`\tv, err := dec.ReadValue();`); + writeLine(`\tif err != nil {`); + writeLine(`\t\treturn err`); + writeLine(`\t}`); + writeLine(`\tif string(v) != \`${jsonValue}\` {`); + writeLine(`\t\treturn errLiteralMismatch("${name}", \`${jsonValue}\`, v)`); + writeLine(`\t}`); + writeLine(`\treturn nil`); + writeLine(`}`); + writeLine(""); + } + + // Generate resolved capabilities + const clientCapsStructure = model.structures.find(s => s.name === "ClientCapabilities"); + if (clientCapsStructure) { + writeLine("// Helper function for dereferencing pointers with zero value fallback"); + writeLine("func derefOr[T any](v *T) T {"); + writeLine("\tif v != nil {"); + writeLine("\t\treturn *v"); + writeLine("\t}"); + writeLine("\tvar zero T"); + writeLine("\treturn zero"); + writeLine("}"); + writeLine(""); + + // Collect all dependent structures and generate their resolved types + const deps = collectStructureDependencies(clientCapsStructure); + const uniqueDeps = Array.from(new Map(deps.map(d => [d.name, d])).values()); + + for (const dep of uniqueDeps) { + const depLines = generateResolvedTypeAndHelper(dep, false); + for (const line of depLines) { + writeLine(line); + } + } + + // Generate the main ResolvedClientCapabilities type and function + writeLine("// ResolvedClientCapabilities is a version of ClientCapabilities where all nested"); + writeLine("// fields are values (not pointers), making it easier to access deeply nested capabilities."); + writeLine("// Use (*ClientCapabilities).Resolve() to convert from ClientCapabilities."); + if (clientCapsStructure.documentation) { + writeLine("//"); + const typeDoc = formatDocumentation(clientCapsStructure.documentation); + for (const line of typeDoc.split("\n").filter(l => l)) { + writeLine(line); + } + } + const mainLines = generateResolvedTypeAndHelper(clientCapsStructure, true); + for (const line of mainLines) { + writeLine(line); + } + } + + return parts.join(""); +} + +function hasSomeProp(structure: Structure, propName: string, propTypeName: string) { + return structure.properties?.some(p => + !p.optional && + p.name === propName && + p.type.kind === "reference" && + p.type.name === propTypeName + ); +} + +function hasTextDocumentURI(structure: Structure) { + return hasSomeProp(structure, "textDocument", "TextDocumentIdentifier") || + hasSomeProp(structure, "_vs_textDocument", "TextDocumentIdentifier"); +} + +function hasTextDocumentPosition(structure: Structure) { + return hasSomeProp(structure, "position", "Position") || + hasSomeProp(structure, "_vs_position", "Position"); +} + +function getLocationUriProperty(structure: Structure) { + const prop = structure.properties?.find(p => + !p.optional && + titleCase(p.name).endsWith("Uri") && + p.type.kind === "base" && + p.type.name === "DocumentUri" + ); + if ( + prop && + structure.properties.some(p => + !p.optional && + titleCase(p.name) === titleCase(prop.name).replace(/Uri$/, "Range") && + p.type.kind === "reference" && + p.type.name === "Range" + ) + ) { + return titleCase(prop.name); + } +} + +/** + * Main function + */ +async function main() { + collectTypeDefinitions(); + const generatedCode = generateCode(); + fs.writeFileSync(out, generatedCode); + + await $({ cwd: repoRoot })`dprint fmt ${out}`; + + console.log(`Successfully generated ${out}`); +} + +main().catch(e => { + console.error(e); + process.exit(1); +}); diff --git a/tsc/internal/stringutil/_scripts/generate-unicode-data.mts b/tsc/internal/stringutil/_scripts/generate-unicode-data.mts index bd726d68026d5..9cad4364ddb71 100644 --- a/tsc/internal/stringutil/_scripts/generate-unicode-data.mts +++ b/tsc/internal/stringutil/_scripts/generate-unicode-data.mts @@ -1,247 +1,247 @@ -#!/usr/bin/env -S node --experimental-strip-types --no-warnings - -import * as fs from "fs"; -import * as path from "path"; - -// All Unicode data is sourced from a single version-pinned @unicode/unicode-* -// package so the generated tables are reproducible regardless of the Node.js -// runtime used to run `go generate`. Bumping this constant (and the matching -// devDependency in package.json) is the only step needed to move to a newer -// Unicode version. -const UNICODE_VERSION = "15.1.0"; -const PACKAGE = `@unicode/unicode-${UNICODE_VERSION}`; - -const scriptDir = import.meta.dirname; -const CASE_OUTPUT_PATH = path.join(scriptDir, "..", "js_case_generated.go"); -const IDENTIFIER_OUTPUT_PATH = path.join(scriptDir, "..", "identifier_parts_generated.go"); - -// A *unicode.RangeTable is split into 16-bit (BMP) and 32-bit (astral) ranges, -// each carrying a stride so arithmetic sequences (e.g. the alternating -// upper/lower letters that fill the casing tables) collapse to a single entry. -type RangeTable = { - r16: Range[]; - r32: Range[]; - latinOffset: number; -}; - -type Range = { - lo: number; - hi: number; - stride: number; -}; - -type SpecialCasingEntry = { - codePoint: number; - lower: number[]; - upper: number[]; - conditionalLower: number[]; - condition: string; -}; - -async function loadCodePoints(property: string): Promise { - const module = await import(`${PACKAGE}/${property}/code-points.js`); - return module.default as number[]; -} - -async function loadMapping(property: string): Promise> { - const module = await import(`${PACKAGE}/${property}/code-points.js`); - return module.default as Map; -} - -async function loadSimpleMapping(property: string): Promise> { - const module = await import(`${PACKAGE}/${property}/code-points.js`); - return module.default as Map; -} - -// Group a sorted, de-duplicated run of code points into ranges sharing a -// constant stride. The stride of each range is taken from the gap to the next -// code point, so it never includes a code point that is not in the set; this -// matches the invariants unicode.Is relies on. -function toStrideRanges(sorted: number[]): Range[] { - const ranges: Range[] = []; - let i = 0; - while (i < sorted.length) { - const lo = sorted[i]; - const stride = i + 1 < sorted.length ? sorted[i + 1] - sorted[i] : 1; - let hi = lo; - let j = i + 1; - while (j < sorted.length && sorted[j] === hi + stride) { - hi = sorted[j]; - j++; - } - ranges.push({ lo, hi, stride }); - i = j; - } - return ranges; -} - -// Compress a set of code points into a *unicode.RangeTable. BMP and astral code -// points are separated first so no range straddles the U+FFFF boundary between -// the R16 and R32 slices. -function toRangeTable(codePoints: Iterable): RangeTable { - const sorted = [...new Set(codePoints)].sort((a, b) => a - b); - const bmp = sorted.filter(cp => cp <= 0xFFFF); - const astral = sorted.filter(cp => cp > 0xFFFF); - const r16 = toStrideRanges(bmp); - const r32 = toStrideRanges(astral); - // unicode.Is fast-paths Latin-1 by linearly scanning the leading R16 entries - // whose Hi is within Latin-1 (U+00FF); LatinOffset records how many those are. - let latinOffset = 0; - while (latinOffset < r16.length && r16[latinOffset].hi <= 0xFF) { - latinOffset++; - } - return { r16, r32, latinOffset }; -} - -function goRuneLiteral(codePoint: number): string { - return `0x${codePoint.toString(16).toUpperCase()}`; -} - -function goStringLiteral(codePoints: number[]): string { - let text = '"'; - for (const codePoint of codePoints) { - if (codePoint <= 0xFFFF) { - text += `\\u${codePoint.toString(16).toUpperCase().padStart(4, "0")}`; - } - else { - text += `\\U${codePoint.toString(16).toUpperCase().padStart(8, "0")}`; - } - } - text += '"'; - return text; -} - -function renderRangeTable(name: string, table: RangeTable): string { - const r16 = table.r16.map(r => `\t\t{${goRuneLiteral(r.lo)}, ${goRuneLiteral(r.hi)}, ${r.stride}},`).join("\n"); - const r32 = table.r32.map(r => `\t\t{${goRuneLiteral(r.lo)}, ${goRuneLiteral(r.hi)}, ${r.stride}},`).join("\n"); - return `var ${name} = &unicode.RangeTable{ -\tR16: []unicode.Range16{ -${r16} -\t}, -\tR32: []unicode.Range32{ -${r32} -\t}, -\tLatinOffset: ${table.latinOffset}, -} -`; -} - -async function buildSpecialCasing(simpleLowercase: Map, simpleUppercase: Map): Promise { - // The unconditional, locale-insensitive multi-rune mappings. Each map keys a - // code point to its full lower/upper expansion (identity when unchanged). - const lowerMappings = await loadMapping("Special_Casing/Lowercase"); - const upperMappings = await loadMapping("Special_Casing/Uppercase"); - // toLowerCase emits the word-final sigma only in Final_Sigma context, so this - // mapping is tracked separately and applied by the Go caser when in context. - const finalSigmaMappings = await loadMapping("Special_Casing/Lowercase--Final_Sigma"); - - const entries: SpecialCasingEntry[] = []; - - const codePoints = new Set([...simpleLowercase.keys(), ...simpleUppercase.keys(), ...lowerMappings.keys(), ...upperMappings.keys()]); - for (const codePoint of codePoints) { - entries.push({ - codePoint, - lower: lowerMappings.get(codePoint) ?? [simpleLowercase.get(codePoint) ?? codePoint], - upper: upperMappings.get(codePoint) ?? [simpleUppercase.get(codePoint) ?? codePoint], - conditionalLower: [codePoint], - condition: "specialCasingConditionNone", - }); - } - - for (const [codePoint, lower] of finalSigmaMappings) { - const entry = entries.find(entry => entry.codePoint === codePoint); - if (entry === undefined) { - entries.push({ - codePoint, - lower: [simpleLowercase.get(codePoint) ?? codePoint], - upper: upperMappings.get(codePoint) ?? [simpleUppercase.get(codePoint) ?? codePoint], - conditionalLower: lower, - condition: "specialCasingConditionFinalSigma", - }); - } - else { - entry.conditionalLower = lower; - entry.condition = "specialCasingConditionFinalSigma"; - } - } - - entries.sort((a, b) => a.codePoint - b.codePoint); - return entries; -} - -function renderCaseFile(entries: SpecialCasingEntry[], casedTable: RangeTable, caseIgnorableTable: RangeTable): string { - const mappings = entries.map(entry => { - const conditionalLower = entry.condition === "specialCasingConditionFinalSigma" ? `, conditionalLower: ${goStringLiteral(entry.conditionalLower)}` : ""; - return `\t${goRuneLiteral(entry.codePoint)}: {lower: ${goStringLiteral(entry.lower)}, upper: ${goStringLiteral(entry.upper)}${conditionalLower}, condition: ${entry.condition}},`; - }).join("\n"); - - return `// Code generated by generate-unicode-data.mts. DO NOT EDIT. -// Derived from the ${PACKAGE} package (Unicode ${UNICODE_VERSION}). -// Includes only the locale-insensitive multi-rune mappings needed for ECMAScript -// default casing, plus the Final_Sigma context mapping. String.prototype.toLowerCase -// applies Final_Sigma, but Go's unicode package does not, so the caser applies it -// from this data when in context. Simple one-rune mappings are included here too -// so casing stays pinned to this Unicode version, rather than the Go toolchain's -// unicode tables. - -package stringutil - -import "unicode" - -type specialCasingCondition uint8 - -const ( -\tspecialCasingConditionNone specialCasingCondition = iota -\tspecialCasingConditionFinalSigma -) - -type specialCasingMapping struct { -\tlower string -\tupper string -\tconditionalLower string -\tcondition specialCasingCondition -} - -var specialCasingMappings = map[rune]specialCasingMapping{ -${mappings} -} - -${renderRangeTable("unicodeCasedRanges", casedTable)} -${renderRangeTable("unicodeCaseIgnorableRanges", caseIgnorableTable)} -`; -} - -function renderIdentifierFile(startTable: RangeTable, partTable: RangeTable): string { - return `// Code generated by generate-unicode-data.mts. DO NOT EDIT. -// Derived from the ${PACKAGE} package (Unicode ${UNICODE_VERSION}). -// Based on http://www.unicode.org/reports/tr31/ and -// https://www.ecma-international.org/ecma-262/6.0/#sec-names-and-keywords: -// unicodeESNextIdentifierStart corresponds to the ID_Start and Other_ID_Start property, and -// unicodeESNextIdentifierPart corresponds to ID_Continue, Other_ID_Continue, plus ID_Start and Other_ID_Start. - -package stringutil - -import "unicode" - -${renderRangeTable("unicodeESNextIdentifierStart", startTable)} -${renderRangeTable("unicodeESNextIdentifierPart", partTable)} -`; -} - -async function main() { - const simpleLowercase = await loadSimpleMapping("Simple_Case_Mapping/Lowercase"); - const simpleUppercase = await loadSimpleMapping("Simple_Case_Mapping/Uppercase"); - const entries = await buildSpecialCasing(simpleLowercase, simpleUppercase); - const casedTable = toRangeTable(await loadCodePoints("Binary_Property/Cased")); - const caseIgnorableTable = toRangeTable(await loadCodePoints("Binary_Property/Case_Ignorable")); - fs.writeFileSync(CASE_OUTPUT_PATH, renderCaseFile(entries, casedTable, caseIgnorableTable)); - - const idStart = await loadCodePoints("Binary_Property/ID_Start"); - const idContinue = await loadCodePoints("Binary_Property/ID_Continue"); - // Other_ID_Start/Other_ID_Continue are already folded into ID_Start/ID_Continue. - const startTable = toRangeTable(idStart); - const partTable = toRangeTable([...idContinue, ...idStart]); - fs.writeFileSync(IDENTIFIER_OUTPUT_PATH, renderIdentifierFile(startTable, partTable)); -} - -await main(); +#!/usr/bin/env -S node --experimental-strip-types --no-warnings + +import * as fs from "fs"; +import * as path from "path"; + +// All Unicode data is sourced from a single version-pinned @unicode/unicode-* +// package so the generated tables are reproducible regardless of the Node.js +// runtime used to run `go generate`. Bumping this constant (and the matching +// devDependency in package.json) is the only step needed to move to a newer +// Unicode version. +const UNICODE_VERSION = "15.1.0"; +const PACKAGE = `@unicode/unicode-${UNICODE_VERSION}`; + +const scriptDir = import.meta.dirname; +const CASE_OUTPUT_PATH = path.join(scriptDir, "..", "js_case_generated.go"); +const IDENTIFIER_OUTPUT_PATH = path.join(scriptDir, "..", "identifier_parts_generated.go"); + +// A *unicode.RangeTable is split into 16-bit (BMP) and 32-bit (astral) ranges, +// each carrying a stride so arithmetic sequences (e.g. the alternating +// upper/lower letters that fill the casing tables) collapse to a single entry. +type RangeTable = { + r16: Range[]; + r32: Range[]; + latinOffset: number; +}; + +type Range = { + lo: number; + hi: number; + stride: number; +}; + +type SpecialCasingEntry = { + codePoint: number; + lower: number[]; + upper: number[]; + conditionalLower: number[]; + condition: string; +}; + +async function loadCodePoints(property: string): Promise { + const module = await import(`${PACKAGE}/${property}/code-points.js`); + return module.default as number[]; +} + +async function loadMapping(property: string): Promise> { + const module = await import(`${PACKAGE}/${property}/code-points.js`); + return module.default as Map; +} + +async function loadSimpleMapping(property: string): Promise> { + const module = await import(`${PACKAGE}/${property}/code-points.js`); + return module.default as Map; +} + +// Group a sorted, de-duplicated run of code points into ranges sharing a +// constant stride. The stride of each range is taken from the gap to the next +// code point, so it never includes a code point that is not in the set; this +// matches the invariants unicode.Is relies on. +function toStrideRanges(sorted: number[]): Range[] { + const ranges: Range[] = []; + let i = 0; + while (i < sorted.length) { + const lo = sorted[i]; + const stride = i + 1 < sorted.length ? sorted[i + 1] - sorted[i] : 1; + let hi = lo; + let j = i + 1; + while (j < sorted.length && sorted[j] === hi + stride) { + hi = sorted[j]; + j++; + } + ranges.push({ lo, hi, stride }); + i = j; + } + return ranges; +} + +// Compress a set of code points into a *unicode.RangeTable. BMP and astral code +// points are separated first so no range straddles the U+FFFF boundary between +// the R16 and R32 slices. +function toRangeTable(codePoints: Iterable): RangeTable { + const sorted = [...new Set(codePoints)].sort((a, b) => a - b); + const bmp = sorted.filter(cp => cp <= 0xFFFF); + const astral = sorted.filter(cp => cp > 0xFFFF); + const r16 = toStrideRanges(bmp); + const r32 = toStrideRanges(astral); + // unicode.Is fast-paths Latin-1 by linearly scanning the leading R16 entries + // whose Hi is within Latin-1 (U+00FF); LatinOffset records how many those are. + let latinOffset = 0; + while (latinOffset < r16.length && r16[latinOffset].hi <= 0xFF) { + latinOffset++; + } + return { r16, r32, latinOffset }; +} + +function goRuneLiteral(codePoint: number): string { + return `0x${codePoint.toString(16).toUpperCase()}`; +} + +function goStringLiteral(codePoints: number[]): string { + let text = '"'; + for (const codePoint of codePoints) { + if (codePoint <= 0xFFFF) { + text += `\\u${codePoint.toString(16).toUpperCase().padStart(4, "0")}`; + } + else { + text += `\\U${codePoint.toString(16).toUpperCase().padStart(8, "0")}`; + } + } + text += '"'; + return text; +} + +function renderRangeTable(name: string, table: RangeTable): string { + const r16 = table.r16.map(r => `\t\t{${goRuneLiteral(r.lo)}, ${goRuneLiteral(r.hi)}, ${r.stride}},`).join("\n"); + const r32 = table.r32.map(r => `\t\t{${goRuneLiteral(r.lo)}, ${goRuneLiteral(r.hi)}, ${r.stride}},`).join("\n"); + return `var ${name} = &unicode.RangeTable{ +\tR16: []unicode.Range16{ +${r16} +\t}, +\tR32: []unicode.Range32{ +${r32} +\t}, +\tLatinOffset: ${table.latinOffset}, +} +`; +} + +async function buildSpecialCasing(simpleLowercase: Map, simpleUppercase: Map): Promise { + // The unconditional, locale-insensitive multi-rune mappings. Each map keys a + // code point to its full lower/upper expansion (identity when unchanged). + const lowerMappings = await loadMapping("Special_Casing/Lowercase"); + const upperMappings = await loadMapping("Special_Casing/Uppercase"); + // toLowerCase emits the word-final sigma only in Final_Sigma context, so this + // mapping is tracked separately and applied by the Go caser when in context. + const finalSigmaMappings = await loadMapping("Special_Casing/Lowercase--Final_Sigma"); + + const entries: SpecialCasingEntry[] = []; + + const codePoints = new Set([...simpleLowercase.keys(), ...simpleUppercase.keys(), ...lowerMappings.keys(), ...upperMappings.keys()]); + for (const codePoint of codePoints) { + entries.push({ + codePoint, + lower: lowerMappings.get(codePoint) ?? [simpleLowercase.get(codePoint) ?? codePoint], + upper: upperMappings.get(codePoint) ?? [simpleUppercase.get(codePoint) ?? codePoint], + conditionalLower: [codePoint], + condition: "specialCasingConditionNone", + }); + } + + for (const [codePoint, lower] of finalSigmaMappings) { + const entry = entries.find(entry => entry.codePoint === codePoint); + if (entry === undefined) { + entries.push({ + codePoint, + lower: [simpleLowercase.get(codePoint) ?? codePoint], + upper: upperMappings.get(codePoint) ?? [simpleUppercase.get(codePoint) ?? codePoint], + conditionalLower: lower, + condition: "specialCasingConditionFinalSigma", + }); + } + else { + entry.conditionalLower = lower; + entry.condition = "specialCasingConditionFinalSigma"; + } + } + + entries.sort((a, b) => a.codePoint - b.codePoint); + return entries; +} + +function renderCaseFile(entries: SpecialCasingEntry[], casedTable: RangeTable, caseIgnorableTable: RangeTable): string { + const mappings = entries.map(entry => { + const conditionalLower = entry.condition === "specialCasingConditionFinalSigma" ? `, conditionalLower: ${goStringLiteral(entry.conditionalLower)}` : ""; + return `\t${goRuneLiteral(entry.codePoint)}: {lower: ${goStringLiteral(entry.lower)}, upper: ${goStringLiteral(entry.upper)}${conditionalLower}, condition: ${entry.condition}},`; + }).join("\n"); + + return `// Code generated by generate-unicode-data.mts. DO NOT EDIT. +// Derived from the ${PACKAGE} package (Unicode ${UNICODE_VERSION}). +// Includes only the locale-insensitive multi-rune mappings needed for ECMAScript +// default casing, plus the Final_Sigma context mapping. String.prototype.toLowerCase +// applies Final_Sigma, but Go's unicode package does not, so the caser applies it +// from this data when in context. Simple one-rune mappings are included here too +// so casing stays pinned to this Unicode version, rather than the Go toolchain's +// unicode tables. + +package stringutil + +import "unicode" + +type specialCasingCondition uint8 + +const ( +\tspecialCasingConditionNone specialCasingCondition = iota +\tspecialCasingConditionFinalSigma +) + +type specialCasingMapping struct { +\tlower string +\tupper string +\tconditionalLower string +\tcondition specialCasingCondition +} + +var specialCasingMappings = map[rune]specialCasingMapping{ +${mappings} +} + +${renderRangeTable("unicodeCasedRanges", casedTable)} +${renderRangeTable("unicodeCaseIgnorableRanges", caseIgnorableTable)} +`; +} + +function renderIdentifierFile(startTable: RangeTable, partTable: RangeTable): string { + return `// Code generated by generate-unicode-data.mts. DO NOT EDIT. +// Derived from the ${PACKAGE} package (Unicode ${UNICODE_VERSION}). +// Based on http://www.unicode.org/reports/tr31/ and +// https://www.ecma-international.org/ecma-262/6.0/#sec-names-and-keywords: +// unicodeESNextIdentifierStart corresponds to the ID_Start and Other_ID_Start property, and +// unicodeESNextIdentifierPart corresponds to ID_Continue, Other_ID_Continue, plus ID_Start and Other_ID_Start. + +package stringutil + +import "unicode" + +${renderRangeTable("unicodeESNextIdentifierStart", startTable)} +${renderRangeTable("unicodeESNextIdentifierPart", partTable)} +`; +} + +async function main() { + const simpleLowercase = await loadSimpleMapping("Simple_Case_Mapping/Lowercase"); + const simpleUppercase = await loadSimpleMapping("Simple_Case_Mapping/Uppercase"); + const entries = await buildSpecialCasing(simpleLowercase, simpleUppercase); + const casedTable = toRangeTable(await loadCodePoints("Binary_Property/Cased")); + const caseIgnorableTable = toRangeTable(await loadCodePoints("Binary_Property/Case_Ignorable")); + fs.writeFileSync(CASE_OUTPUT_PATH, renderCaseFile(entries, casedTable, caseIgnorableTable)); + + const idStart = await loadCodePoints("Binary_Property/ID_Start"); + const idContinue = await loadCodePoints("Binary_Property/ID_Continue"); + // Other_ID_Start/Other_ID_Continue are already folded into ID_Start/ID_Continue. + const startTable = toRangeTable(idStart); + const partTable = toRangeTable([...idContinue, ...idStart]); + fs.writeFileSync(IDENTIFIER_OUTPUT_PATH, renderIdentifierFile(startTable, partTable)); +} + +await main(); diff --git a/tsc/testdata/baselines/reference/fourslash/callHierarchy/callHierarchyInPropDeclarationOfExportedDefaultClass1.callHierarchy.txt b/tsc/testdata/baselines/reference/fourslash/callHierarchy/callHierarchyInPropDeclarationOfExportedDefaultClass1.callHierarchy.txt index 6c69e4ebcc8fb..4b905a586d10b 100644 --- a/tsc/testdata/baselines/reference/fourslash/callHierarchy/callHierarchyInPropDeclarationOfExportedDefaultClass1.callHierarchy.txt +++ b/tsc/testdata/baselines/reference/fourslash/callHierarchy/callHierarchyInPropDeclarationOfExportedDefaultClass1.callHierarchy.txt @@ -3,21 +3,21 @@ ├ kind: method ├ file: bundled:///libs/lib.es5.d.ts ├ span: -│ ╭ bundled:///libs/lib.es5.d.ts:1341:5-1345:33 -│ │ 1341: /** +│ ╭ bundled:///libs/lib.es5.d.ts:1340:5-1344:33 +│ │ 1340: /** │ │ ^^^ -│ │ 1342: * Appends new elements to the end of an array, and returns the new length of the array. +│ │ 1341: * Appends new elements to the end of an array, and returns the new length of the array. │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -│ │ 1343: * @param items New elements to add to the array. +│ │ 1342: * @param items New elements to add to the array. │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -│ │ 1344: */ +│ │ 1343: */ │ │ ^^^^^^^ -│ │ 1345: push(...items: T[]): number; +│ │ 1344: push(...items: T[]): number; │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ │ ╰ ├ selectionSpan: -│ ╭ bundled:///libs/lib.es5.d.ts:1345:5-1345:9 -│ │ 1345: push(...items: T[]): number; +│ ╭ bundled:///libs/lib.es5.d.ts:1344:5-1344:9 +│ │ 1344: push(...items: T[]): number; │ │ ^^^^ │ ╰ ├ incoming: diff --git a/tsc/testdata/baselines/reference/fourslash/callHierarchy/callHierarchyIncomingCallsNoCrashArrayPush.callHierarchy.txt b/tsc/testdata/baselines/reference/fourslash/callHierarchy/callHierarchyIncomingCallsNoCrashArrayPush.callHierarchy.txt index 3ef3de88af03b..843cc6712512f 100644 --- a/tsc/testdata/baselines/reference/fourslash/callHierarchy/callHierarchyIncomingCallsNoCrashArrayPush.callHierarchy.txt +++ b/tsc/testdata/baselines/reference/fourslash/callHierarchy/callHierarchyIncomingCallsNoCrashArrayPush.callHierarchy.txt @@ -3,21 +3,21 @@ ├ kind: method ├ file: bundled:///libs/lib.es5.d.ts ├ span: -│ ╭ bundled:///libs/lib.es5.d.ts:1341:5-1345:33 -│ │ 1341: /** +│ ╭ bundled:///libs/lib.es5.d.ts:1340:5-1344:33 +│ │ 1340: /** │ │ ^^^ -│ │ 1342: * Appends new elements to the end of an array, and returns the new length of the array. +│ │ 1341: * Appends new elements to the end of an array, and returns the new length of the array. │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -│ │ 1343: * @param items New elements to add to the array. +│ │ 1342: * @param items New elements to add to the array. │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -│ │ 1344: */ +│ │ 1343: */ │ │ ^^^^^^^ -│ │ 1345: push(...items: T[]): number; +│ │ 1344: push(...items: T[]): number; │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ │ ╰ ├ selectionSpan: -│ ╭ bundled:///libs/lib.es5.d.ts:1345:5-1345:9 -│ │ 1345: push(...items: T[]): number; +│ ╭ bundled:///libs/lib.es5.d.ts:1344:5-1344:9 +│ │ 1344: push(...items: T[]): number; │ │ ^^^^ │ ╰ ├ incoming: diff --git a/tsc/testdata/baselines/reference/fourslash/callHierarchy/callHierarchyIncomingCallsObjectLiteralMethodInExpressionComputedProperty.callHierarchy.txt b/tsc/testdata/baselines/reference/fourslash/callHierarchy/callHierarchyIncomingCallsObjectLiteralMethodInExpressionComputedProperty.callHierarchy.txt index 3b95dbe0f909a..f5bdb39565a17 100644 --- a/tsc/testdata/baselines/reference/fourslash/callHierarchy/callHierarchyIncomingCallsObjectLiteralMethodInExpressionComputedProperty.callHierarchy.txt +++ b/tsc/testdata/baselines/reference/fourslash/callHierarchy/callHierarchyIncomingCallsObjectLiteralMethodInExpressionComputedProperty.callHierarchy.txt @@ -3,23 +3,23 @@ ├ kind: method ├ file: bundled:///libs/lib.es2015.symbol.wellknown.d.ts ├ span: -│ ╭ bundled:///libs/lib.es2015.symbol.wellknown.d.ts:261:5-266:110 -│ │ 261: /** +│ ╭ bundled:///libs/lib.es2015.symbol.wellknown.d.ts:260:5-265:110 +│ │ 260: /** │ │ ^^^ -│ │ 262: * Split a string into substrings using the specified separator and return them as an array. +│ │ 261: * Split a string into substrings using the specified separator and return them as an array. │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -│ │ 263: * @param splitter An object that can split a string. +│ │ 262: * @param splitter An object that can split a string. │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -│ │ 264: * @param limit A value used to limit the number of elements returned in the array. +│ │ 263: * @param limit A value used to limit the number of elements returned in the array. │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -│ │ 265: */ +│ │ 264: */ │ │ ^^^^^^^ -│ │ 266: split(splitter: { [Symbol.split](string: string, limit?: number): string[]; }, limit?: number): string[]; +│ │ 265: split(splitter: { [Symbol.split](string: string, limit?: number): string[]; }, limit?: number): string[]; │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ │ ╰ ├ selectionSpan: -│ ╭ bundled:///libs/lib.es2015.symbol.wellknown.d.ts:266:5-266:10 -│ │ 266: split(splitter: { [Symbol.split](string: string, limit?: number): string[]; }, limit?: number): string[]; +│ ╭ bundled:///libs/lib.es2015.symbol.wellknown.d.ts:265:5-265:10 +│ │ 265: split(splitter: { [Symbol.split](string: string, limit?: number): string[]; }, limit?: number): string[]; │ │ ^^^^^ │ ╰ ├ incoming: @@ -53,23 +53,23 @@ ├ kind: method ├ file: bundled:///libs/lib.es5.d.ts ├ span: -│ ╭ bundled:///libs/lib.es5.d.ts:484:5-489:65 -│ │ 484: /** +│ ╭ bundled:///libs/lib.es5.d.ts:483:5-488:65 +│ │ 483: /** │ │ ^^^ -│ │ 485: * Split a string into substrings using the specified separator and return them as an array. +│ │ 484: * Split a string into substrings using the specified separator and return them as an array. │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -│ │ 486: * @param separator A string that identifies character or characters to use in separating the string. If omitted, a single-element array containing the entire string is returned. +│ │ 485: * @param separator A string that identifies character or characters to use in separating the string. If omitted, a single-element array containing the entire string is returned. │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -│ │ 487: * @param limit A value used to limit the number of elements returned in the array. +│ │ 486: * @param limit A value used to limit the number of elements returned in the array. │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -│ │ 488: */ +│ │ 487: */ │ │ ^^^^^^^ -│ │ 489: split(separator: string | RegExp, limit?: number): string[]; +│ │ 488: split(separator: string | RegExp, limit?: number): string[]; │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ │ ╰ ├ selectionSpan: -│ ╭ bundled:///libs/lib.es5.d.ts:489:5-489:10 -│ │ 489: split(separator: string | RegExp, limit?: number): string[]; +│ ╭ bundled:///libs/lib.es5.d.ts:488:5-488:10 +│ │ 488: split(separator: string | RegExp, limit?: number): string[]; │ │ ^^^^^ │ ╰ ├ incoming: diff --git a/tsc/testdata/baselines/reference/fourslash/callHierarchy/callHierarchyIncomingCallsObjectLiteralMethodInIdentifierComputedProperty.callHierarchy.txt b/tsc/testdata/baselines/reference/fourslash/callHierarchy/callHierarchyIncomingCallsObjectLiteralMethodInIdentifierComputedProperty.callHierarchy.txt index b02a66022c82a..004eb1cb4a062 100644 --- a/tsc/testdata/baselines/reference/fourslash/callHierarchy/callHierarchyIncomingCallsObjectLiteralMethodInIdentifierComputedProperty.callHierarchy.txt +++ b/tsc/testdata/baselines/reference/fourslash/callHierarchy/callHierarchyIncomingCallsObjectLiteralMethodInIdentifierComputedProperty.callHierarchy.txt @@ -3,23 +3,23 @@ ├ kind: method ├ file: bundled:///libs/lib.es2015.symbol.wellknown.d.ts ├ span: -│ ╭ bundled:///libs/lib.es2015.symbol.wellknown.d.ts:261:5-266:110 -│ │ 261: /** +│ ╭ bundled:///libs/lib.es2015.symbol.wellknown.d.ts:260:5-265:110 +│ │ 260: /** │ │ ^^^ -│ │ 262: * Split a string into substrings using the specified separator and return them as an array. +│ │ 261: * Split a string into substrings using the specified separator and return them as an array. │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -│ │ 263: * @param splitter An object that can split a string. +│ │ 262: * @param splitter An object that can split a string. │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -│ │ 264: * @param limit A value used to limit the number of elements returned in the array. +│ │ 263: * @param limit A value used to limit the number of elements returned in the array. │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -│ │ 265: */ +│ │ 264: */ │ │ ^^^^^^^ -│ │ 266: split(splitter: { [Symbol.split](string: string, limit?: number): string[]; }, limit?: number): string[]; +│ │ 265: split(splitter: { [Symbol.split](string: string, limit?: number): string[]; }, limit?: number): string[]; │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ │ ╰ ├ selectionSpan: -│ ╭ bundled:///libs/lib.es2015.symbol.wellknown.d.ts:266:5-266:10 -│ │ 266: split(splitter: { [Symbol.split](string: string, limit?: number): string[]; }, limit?: number): string[]; +│ ╭ bundled:///libs/lib.es2015.symbol.wellknown.d.ts:265:5-265:10 +│ │ 265: split(splitter: { [Symbol.split](string: string, limit?: number): string[]; }, limit?: number): string[]; │ │ ^^^^^ │ ╰ ├ incoming: @@ -53,23 +53,23 @@ ├ kind: method ├ file: bundled:///libs/lib.es5.d.ts ├ span: -│ ╭ bundled:///libs/lib.es5.d.ts:484:5-489:65 -│ │ 484: /** +│ ╭ bundled:///libs/lib.es5.d.ts:483:5-488:65 +│ │ 483: /** │ │ ^^^ -│ │ 485: * Split a string into substrings using the specified separator and return them as an array. +│ │ 484: * Split a string into substrings using the specified separator and return them as an array. │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -│ │ 486: * @param separator A string that identifies character or characters to use in separating the string. If omitted, a single-element array containing the entire string is returned. +│ │ 485: * @param separator A string that identifies character or characters to use in separating the string. If omitted, a single-element array containing the entire string is returned. │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -│ │ 487: * @param limit A value used to limit the number of elements returned in the array. +│ │ 486: * @param limit A value used to limit the number of elements returned in the array. │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -│ │ 488: */ +│ │ 487: */ │ │ ^^^^^^^ -│ │ 489: split(separator: string | RegExp, limit?: number): string[]; +│ │ 488: split(separator: string | RegExp, limit?: number): string[]; │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ │ ╰ ├ selectionSpan: -│ ╭ bundled:///libs/lib.es5.d.ts:489:5-489:10 -│ │ 489: split(separator: string | RegExp, limit?: number): string[]; +│ ╭ bundled:///libs/lib.es5.d.ts:488:5-488:10 +│ │ 488: split(separator: string | RegExp, limit?: number): string[]; │ │ ^^^^^ │ ╰ ├ incoming: diff --git a/tsc/testdata/baselines/reference/fourslash/callHierarchy/callHierarchyIncomingCallsObjectLiteralMethodInStringLiteralComputedProperty.callHierarchy.txt b/tsc/testdata/baselines/reference/fourslash/callHierarchy/callHierarchyIncomingCallsObjectLiteralMethodInStringLiteralComputedProperty.callHierarchy.txt index 0476ce888b3e6..10c14b301d3f0 100644 --- a/tsc/testdata/baselines/reference/fourslash/callHierarchy/callHierarchyIncomingCallsObjectLiteralMethodInStringLiteralComputedProperty.callHierarchy.txt +++ b/tsc/testdata/baselines/reference/fourslash/callHierarchy/callHierarchyIncomingCallsObjectLiteralMethodInStringLiteralComputedProperty.callHierarchy.txt @@ -3,23 +3,23 @@ ├ kind: method ├ file: bundled:///libs/lib.es2015.symbol.wellknown.d.ts ├ span: -│ ╭ bundled:///libs/lib.es2015.symbol.wellknown.d.ts:261:5-266:110 -│ │ 261: /** +│ ╭ bundled:///libs/lib.es2015.symbol.wellknown.d.ts:260:5-265:110 +│ │ 260: /** │ │ ^^^ -│ │ 262: * Split a string into substrings using the specified separator and return them as an array. +│ │ 261: * Split a string into substrings using the specified separator and return them as an array. │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -│ │ 263: * @param splitter An object that can split a string. +│ │ 262: * @param splitter An object that can split a string. │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -│ │ 264: * @param limit A value used to limit the number of elements returned in the array. +│ │ 263: * @param limit A value used to limit the number of elements returned in the array. │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -│ │ 265: */ +│ │ 264: */ │ │ ^^^^^^^ -│ │ 266: split(splitter: { [Symbol.split](string: string, limit?: number): string[]; }, limit?: number): string[]; +│ │ 265: split(splitter: { [Symbol.split](string: string, limit?: number): string[]; }, limit?: number): string[]; │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ │ ╰ ├ selectionSpan: -│ ╭ bundled:///libs/lib.es2015.symbol.wellknown.d.ts:266:5-266:10 -│ │ 266: split(splitter: { [Symbol.split](string: string, limit?: number): string[]; }, limit?: number): string[]; +│ ╭ bundled:///libs/lib.es2015.symbol.wellknown.d.ts:265:5-265:10 +│ │ 265: split(splitter: { [Symbol.split](string: string, limit?: number): string[]; }, limit?: number): string[]; │ │ ^^^^^ │ ╰ ├ incoming: @@ -53,23 +53,23 @@ ├ kind: method ├ file: bundled:///libs/lib.es5.d.ts ├ span: -│ ╭ bundled:///libs/lib.es5.d.ts:484:5-489:65 -│ │ 484: /** +│ ╭ bundled:///libs/lib.es5.d.ts:483:5-488:65 +│ │ 483: /** │ │ ^^^ -│ │ 485: * Split a string into substrings using the specified separator and return them as an array. +│ │ 484: * Split a string into substrings using the specified separator and return them as an array. │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -│ │ 486: * @param separator A string that identifies character or characters to use in separating the string. If omitted, a single-element array containing the entire string is returned. +│ │ 485: * @param separator A string that identifies character or characters to use in separating the string. If omitted, a single-element array containing the entire string is returned. │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -│ │ 487: * @param limit A value used to limit the number of elements returned in the array. +│ │ 486: * @param limit A value used to limit the number of elements returned in the array. │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -│ │ 488: */ +│ │ 487: */ │ │ ^^^^^^^ -│ │ 489: split(separator: string | RegExp, limit?: number): string[]; +│ │ 488: split(separator: string | RegExp, limit?: number): string[]; │ │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ │ ╰ ├ selectionSpan: -│ ╭ bundled:///libs/lib.es5.d.ts:489:5-489:10 -│ │ 489: split(separator: string | RegExp, limit?: number): string[]; +│ ╭ bundled:///libs/lib.es5.d.ts:488:5-488:10 +│ │ 488: split(separator: string | RegExp, limit?: number): string[]; │ │ ^^^^^ │ ╰ ├ incoming: diff --git a/tsc/testdata/tests/cases/compiler/collisionCodeGenModuleWithUnicodeNames.ts b/tsc/testdata/tests/cases/compiler/collisionCodeGenModuleWithUnicodeNames.ts index da77af3092aa8..dea896864e5ca 100644 Binary files a/tsc/testdata/tests/cases/compiler/collisionCodeGenModuleWithUnicodeNames.ts and b/tsc/testdata/tests/cases/compiler/collisionCodeGenModuleWithUnicodeNames.ts differ diff --git a/tsc/testdata/tests/cases/compiler/instanceofOperator.ts b/tsc/testdata/tests/cases/compiler/instanceofOperator.ts index a399b9b200f28..f4f8263377bd9 100644 Binary files a/tsc/testdata/tests/cases/compiler/instanceofOperator.ts and b/tsc/testdata/tests/cases/compiler/instanceofOperator.ts differ diff --git a/tsc/testdata/tests/cases/compiler/promiseTest.ts b/tsc/testdata/tests/cases/compiler/promiseTest.ts index c1e14b37851db..897640b72424a 100644 Binary files a/tsc/testdata/tests/cases/compiler/promiseTest.ts and b/tsc/testdata/tests/cases/compiler/promiseTest.ts differ diff --git a/tsc/testdata/tests/cases/compiler/targetTypeBaseCalls.ts b/tsc/testdata/tests/cases/compiler/targetTypeBaseCalls.ts index 8c022dafc7158..6517de090ec09 100644 Binary files a/tsc/testdata/tests/cases/compiler/targetTypeBaseCalls.ts and b/tsc/testdata/tests/cases/compiler/targetTypeBaseCalls.ts differ diff --git a/tsc/testdata/tests/cases/compiler/unicodeIdentifierNames.ts b/tsc/testdata/tests/cases/compiler/unicodeIdentifierNames.ts index 246fa7481d728..7807c933af155 100644 Binary files a/tsc/testdata/tests/cases/compiler/unicodeIdentifierNames.ts and b/tsc/testdata/tests/cases/compiler/unicodeIdentifierNames.ts differ