From c3195a11b9da01ccc23183d287110f5a64d0b386 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Wed, 2 Sep 2026 17:38:10 +0800 Subject: [PATCH 01/52] v4: embed typescript-native-bridge, drop Volar-based framework support - @tsslint/cli now depends on typescript-native-bridge (pinned 6.0.3-bridge.15.tsgo.7.0.2) instead of the typescript peer dep; type checking runs on the TS 7 (tsgo) engine - Remove --vue-project/--vue-vine-project/--mdx-project/--astro-project/ --ts-macro-project flags and the Volar language plugin wiring (@volar/language-core, @volar/language-hub, @volar/typescript) - Meta-framework support will be provided by the TS 7.1 content mapper (tsconfig contentMappers) once TNB rebases onto tsgo 7.1 - Remove fixtures/meta-frameworks and untracked tsgo/relay experiments --- README.md | 28 +- fixtures/meta-frameworks/fixture.astro | 3 - fixtures/meta-frameworks/fixture.mdx | 6 - fixtures/meta-frameworks/fixture.tsx | 3 - fixtures/meta-frameworks/fixture.vine.ts | 6 - fixtures/meta-frameworks/fixture.vue | 3 - fixtures/meta-frameworks/package.json | 12 - fixtures/meta-frameworks/tsconfig.json | 7 - fixtures/meta-frameworks/tsslint.config.ts | 7 - package.json | 2 +- packages/cli/README.md | 4 +- packages/cli/index.ts | 78 +- packages/cli/lib/languagePlugins.ts | 35 - packages/cli/lib/worker.ts | 141 +-- packages/cli/package.json | 9 +- packages/vscode/README.md | 2 +- pnpm-lock.yaml | 1273 ++------------------ 17 files changed, 122 insertions(+), 1497 deletions(-) delete mode 100644 fixtures/meta-frameworks/fixture.astro delete mode 100644 fixtures/meta-frameworks/fixture.mdx delete mode 100644 fixtures/meta-frameworks/fixture.tsx delete mode 100644 fixtures/meta-frameworks/fixture.vine.ts delete mode 100644 fixtures/meta-frameworks/fixture.vue delete mode 100644 fixtures/meta-frameworks/package.json delete mode 100644 fixtures/meta-frameworks/tsconfig.json delete mode 100644 fixtures/meta-frameworks/tsslint.config.ts delete mode 100644 packages/cli/lib/languagePlugins.ts diff --git a/README.md b/README.md index b0792b88..82460edf 100644 --- a/README.md +++ b/README.md @@ -202,11 +202,6 @@ Flags: | Flag | | |---|---| | `--project ` | TypeScript projects to lint | -| `--vue-project ` | Vue projects | -| `--vue-vine-project ` | Vue Vine projects | -| `--mdx-project ` | MDX projects | -| `--astro-project ` | Astro projects | -| `--ts-macro-project ` | TS Macro projects | | `--filter ` | Restrict to matching files | | `--fix` | Apply fixes | | `--force` | Ignore cache | @@ -217,28 +212,7 @@ TSSLint produces diagnostics and edits — it does not format. Run dprint or Pre ## Framework support -The `--*-project` flags wire in [Volar](https://volarjs.dev/) language plugins so framework files (Vue SFCs, MDX, Astro components, etc.) are virtualized as TypeScript before linting. Anything `tsserver` can see, TSSLint can lint. - -``` - .vue ──┐ - .mdx ──┤ ┌──────────────┐ ┌──────────────────┐ - .astro──┼───▶│ Framework │───▶│ tsserver │───▶ diagnostics - .ts ──┘ │ adapters │ │ │ in editor - │ │ │ TypeChecker │ - │ ─▶ virtual │ │ + │ - │ TS file │ │ TSSLint plugin │ - └──────────────┘ └──────────────────┘ -``` - -Each flag resolves the language plugin from your project's `node_modules`, so you must install the corresponding package: - -| Flag | Required package(s) | -|---|---| -| `--vue-project` | `@vue/language-core` or `vue-tsc` | -| `--vue-vine-project` | `@vue-vine/language-service` or `vue-vine-tsc` | -| `--mdx-project` | `@mdx-js/language-service` | -| `--astro-project` | `@astrojs/ts-plugin` | -| `--ts-macro-project` | `@ts-macro/language-plugin` or `@ts-macro/tsc` | +Since v4, meta-framework support is provided by the TS 7.1 content mapper (tsconfig `contentMappers`); see the release notes for the migration guide. ## Importing ESLint, TSLint, or TSL rules diff --git a/fixtures/meta-frameworks/fixture.astro b/fixtures/meta-frameworks/fixture.astro deleted file mode 100644 index d28c6842..00000000 --- a/fixtures/meta-frameworks/fixture.astro +++ /dev/null @@ -1,3 +0,0 @@ ---- -console.log('Hello, world!'); ---- diff --git a/fixtures/meta-frameworks/fixture.mdx b/fixtures/meta-frameworks/fixture.mdx deleted file mode 100644 index 8df956a7..00000000 --- a/fixtures/meta-frameworks/fixture.mdx +++ /dev/null @@ -1,6 +0,0 @@ -{/** Description of `Component` */} -export function Component() { - console.log('Hello, world!'); -} - -Hello diff --git a/fixtures/meta-frameworks/fixture.tsx b/fixtures/meta-frameworks/fixture.tsx deleted file mode 100644 index 59e3239f..00000000 --- a/fixtures/meta-frameworks/fixture.tsx +++ /dev/null @@ -1,3 +0,0 @@ -function MyComponent() { - return
{console.log('Hello, world!')}
; -} diff --git a/fixtures/meta-frameworks/fixture.vine.ts b/fixtures/meta-frameworks/fixture.vine.ts deleted file mode 100644 index f674a6b8..00000000 --- a/fixtures/meta-frameworks/fixture.vine.ts +++ /dev/null @@ -1,6 +0,0 @@ -function MyComponent() { - return vine`
{{ console.log('Hello, world!') }}
`; -} - -// This is also valid -const AnotherComponent = () => vine`
Hello World
`; diff --git a/fixtures/meta-frameworks/fixture.vue b/fixtures/meta-frameworks/fixture.vue deleted file mode 100644 index aa4f600b..00000000 --- a/fixtures/meta-frameworks/fixture.vue +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/fixtures/meta-frameworks/package.json b/fixtures/meta-frameworks/package.json deleted file mode 100644 index 7eddf1ca..00000000 --- a/fixtures/meta-frameworks/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "private": true, - "name": "@tsslint-fixtures/meta-frameworks-support", - "version": "3.1.4", - "devDependencies": { - "@astrojs/ts-plugin": "latest", - "@mdx-js/language-service": "latest", - "@ts-macro/language-plugin": "latest", - "@vue-vine/language-service": "latest", - "@vue/language-core": "latest" - } -} diff --git a/fixtures/meta-frameworks/tsconfig.json b/fixtures/meta-frameworks/tsconfig.json deleted file mode 100644 index 28378984..00000000 --- a/fixtures/meta-frameworks/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "include": ["fixture.*"], - "compilerOptions": { - "noEmit": true, - "allowImportingTsExtensions": true - } -} diff --git a/fixtures/meta-frameworks/tsslint.config.ts b/fixtures/meta-frameworks/tsslint.config.ts deleted file mode 100644 index 5a057d37..00000000 --- a/fixtures/meta-frameworks/tsslint.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { defineConfig } from '../../packages/config/index.js'; - -export default defineConfig({ - rules: { - 'no-console': (await import('../noConsoleRule.ts')), - }, -}); diff --git a/package.json b/package.json index f9096cb0..d6cad0f9 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "format": "dprint fmt", "lint": "packages/cli/bin/tsslint.js --project {tsconfig.json,packages/*/tsconfig.json}", "lint:fix": "npm run lint -- --fix && npm run format", - "lint:fixtures": "packages/cli/bin/tsslint.js --project fixtures/*/tsconfig.json --vue-project fixtures/meta-frameworks/tsconfig.json --mdx-project fixtures/meta-frameworks/tsconfig.json --astro-project fixtures/meta-frameworks/tsconfig.json --ts-macro-project fixtures/meta-frameworks/tsconfig.json" + "lint:fixtures": "packages/cli/bin/tsslint.js --project fixtures/*/tsconfig.json" }, "devDependencies": { "@lerna-lite/cli": "latest", diff --git a/packages/cli/README.md b/packages/cli/README.md index 88088dc5..0b25fd85 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -1,6 +1,6 @@ # @tsslint/cli -Command-line runner for TSSLint. Lints TypeScript projects — and Vue / Vue Vine / MDX / Astro / TS Macro projects via Volar language plugins — in CI or from the terminal. +Command-line runner for TSSLint. Lints TypeScript projects in CI or from the terminal. ## Usage @@ -16,4 +16,4 @@ npx tsslint --project 'packages/*/tsconfig.json' --filter 'src/**/*.ts' Run `tsslint --help` for the full flag list. -See the [root README](../../README.md) for framework project flags (`--vue-project`, `--mdx-project`, …), caching behavior, and how diagnostics are emitted. +See the [root README](../../README.md) for caching behavior and how diagnostics are emitted. diff --git a/packages/cli/index.ts b/packages/cli/index.ts index c92a93f0..0f3e55d7 100644 --- a/packages/cli/index.ts +++ b/packages/cli/index.ts @@ -9,13 +9,12 @@ if (process.argv.includes('--debug-estree')) { process.env.TSSLINT_DEBUG_ESTREE = '1'; } -import ts = require('typescript'); +import ts = require('typescript-native-bridge'); import path = require('path'); import worker = require('./lib/worker.js'); import cache = require('./lib/cache.js'); import fs = require('fs'); import minimatch = require('minimatch'); -import languagePlugins = require('./lib/languagePlugins.js'); import colors = require('./lib/colors.js'); import render = require('./lib/render.js'); @@ -24,11 +23,6 @@ Usage: tsslint [options] Options: --project Lint TypeScript/JavaScript projects - --vue-project Lint Vue projects - --vue-vine-project Lint Vue Vine projects - --mdx-project Lint MDX projects - --astro-project Lint Astro projects - --ts-macro-project Lint TS Macro projects --filter Filter files to lint --fix Apply automatic fixes --force Ignore cache (re-lint every file) @@ -47,9 +41,6 @@ Examples: # Multiple projects with brace expansion tsslint --project {tsconfig.json,packages/*/tsconfig.json} - # Mixed framework projects - tsslint --project packages/*/tsconfig.json --vue-project apps/web/tsconfig.json - # With filter and fix tsslint --project tsconfig.json --filter "src/**/*.ts" --fix `; @@ -60,20 +51,7 @@ if (process.argv.includes('--help') || process.argv.includes('-h')) { } const PROJECT_FLAGS = [ - { flag: '--project', language: undefined }, - { flag: '--vue-project', language: 'vue' }, - { flag: '--vue-vine-project', language: 'vue-vine' }, - { flag: '--mdx-project', language: 'mdx' }, - { flag: '--astro-project', language: 'astro' }, - { flag: '--ts-macro-project', language: 'ts-macro' }, -] as const; - -const LANGUAGE_LABELS = [ - { key: 'ts-macro', label: 'TS Macro', color: colors.tsMacroColor }, - { key: 'vue', label: 'Vue', color: colors.vueColor }, - { key: 'vue-vine', label: 'Vue Vine', color: colors.vueVineColor }, - { key: 'mdx', label: 'MDX', color: colors.mdxColor }, - { key: 'astro', label: 'Astro', color: colors.astroColor }, + { flag: '--project' }, ] as const; class Project { @@ -94,26 +72,12 @@ class Project { constructor( public tsconfig: string, - public languages: string[], ) {} async init(renderer: render.Renderer, filesFilter: string[]) { this.configFile = ts.findConfigFile(path.dirname(this.tsconfig), ts.sys.fileExists, 'tsslint.config.ts'); - const labels: string[] = []; - - if (this.languages.length === 0) { - labels.push(colors.tsColor('TS')); - } - else { - for (const { key, label, color } of LANGUAGE_LABELS) { - if (this.languages.includes(key)) { - labels.push(color(label)); - } - } - } - - const label = labels.join(colors.gray(' | ')); + const label = colors.tsColor('TS'); const relPath = path.relative(process.cwd(), this.tsconfig); if (!this.configFile) { @@ -123,7 +87,7 @@ class Project { return this; } - const commonLine = await parseCommonLine(this.tsconfig, this.languages); + const commonLine = await parseCommonLine(this.tsconfig); this.rawFileNames = commonLine.fileNames; this.options = commonLine.options; @@ -164,7 +128,7 @@ class Project { this.cacheData = cache.loadCache( this.tsconfig, this.configFile, - this.languages, + [], ts.version, ts.sys.createHash, ); @@ -182,7 +146,7 @@ const formatHost: ts.FormatDiagnosticsHost = { (async () => { const renderer = render.createRenderer(); - const tsconfigAndLanguages = new Map(); + const tsconfigs = new Set(); function fail(msg: string): never { renderer.error(colors.red(msg)); @@ -209,7 +173,7 @@ const formatHost: ts.FormatDiagnosticsHost = { process.exit(1); } - for (const { flag, language } of PROJECT_FLAGS) { + for (const { flag } of PROJECT_FLAGS) { if (!process.argv.includes(flag)) { continue; } @@ -221,18 +185,12 @@ const formatHost: ts.FormatDiagnosticsHost = { } foundArg = true; const searchGlob = process.argv[i]; - const tsconfigs = fs.globSync(searchGlob); - if (!tsconfigs.length) { + const matches = fs.globSync(searchGlob); + if (!matches.length) { fail(`No projects found for ${flag} ${searchGlob}.`); } - for (let tsconfig of tsconfigs) { - tsconfig = resolvePath(tsconfig); - if (!tsconfigAndLanguages.has(tsconfig)) { - tsconfigAndLanguages.set(tsconfig, []); - } - if (language) { - tsconfigAndLanguages.get(tsconfig)!.push(language); - } + for (const tsconfig of matches) { + tsconfigs.add(resolvePath(tsconfig)); } } if (!foundArg) { @@ -265,8 +223,8 @@ const formatHost: ts.FormatDiagnosticsHost = { } } - for (const [tsconfig, languages] of tsconfigAndLanguages) { - projects.push(await new Project(tsconfig, languages).init(renderer, filters)); + for (const tsconfig of tsconfigs) { + projects.push(await new Project(tsconfig).init(renderer, filters)); } projects = projects.filter(project => project.configFile && project.fileNames.length); @@ -450,8 +408,6 @@ const formatHost: ts.FormatDiagnosticsHost = { } const setupResult = await linterWorker.setup( - project.tsconfig, - project.languages, project.configFile!, project.rawFileNames, project.options, @@ -572,7 +528,7 @@ const formatHost: ts.FormatDiagnosticsHost = { cache.saveCache( project.tsconfig, project.configFile!, - project.languages, + [], ts.version, project.cacheData, ts.sys.createHash, @@ -618,17 +574,13 @@ function formatConfigError(configFile: string, errorText: string): string { return out.join('\n'); } -async function parseCommonLine(tsconfig: string, languages: string[]) { +async function parseCommonLine(tsconfig: string) { const jsonConfigFile = ts.readJsonConfigFile(tsconfig, ts.sys.readFile); - const plugins = await languagePlugins.load(tsconfig, languages); - const extraFileExtensions = plugins.flatMap(plugin => plugin.typescript?.extraFileExtensions ?? []).flat(); return ts.parseJsonSourceFileConfigFileContent( jsonConfigFile, ts.sys, path.dirname(tsconfig), {}, tsconfig, - undefined, - extraFileExtensions, ); } diff --git a/packages/cli/lib/languagePlugins.ts b/packages/cli/lib/languagePlugins.ts deleted file mode 100644 index 6e2ce88c..00000000 --- a/packages/cli/lib/languagePlugins.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { LanguagePlugin } from '@volar/language-core'; -import { - createAstroPlugin, - createMdxPlugin, - createTsMacroPlugins, - createVuePlugin, - createVueVinePlugins, -} from '@volar/language-hub'; -import ts = require('typescript'); - -const cache = new Map[]>(); - -export async function load(tsconfig: string, languages: string[]) { - if (cache.has(tsconfig)) { - return cache.get(tsconfig)!; - } - const plugins: LanguagePlugin[] = []; - if (languages.includes('vue')) { - plugins.push(createVuePlugin(ts, tsconfig)); - } - if (languages.includes('vue-vine')) { - plugins.push(...createVueVinePlugins(ts, tsconfig)); - } - if (languages.includes('mdx')) { - plugins.push(await createMdxPlugin(ts, tsconfig)); - } - if (languages.includes('astro')) { - plugins.push(createAstroPlugin(ts, tsconfig)); - } - if (languages.includes('ts-macro')) { - plugins.push(...await createTsMacroPlugins(ts, tsconfig)); - } - cache.set(tsconfig, plugins); - return plugins; -} diff --git a/packages/cli/lib/worker.ts b/packages/cli/lib/worker.ts index 825e308b..044273b3 100644 --- a/packages/cli/lib/worker.ts +++ b/packages/cli/lib/worker.ts @@ -1,31 +1,29 @@ -import ts = require('typescript'); +import ts = require('typescript-native-bridge'); import type config = require('@tsslint/config'); import core = require('@tsslint/core'); import url = require('url'); import path = require('path'); import fs = require('fs'); import crypto = require('crypto'); -import languagePlugins = require('./languagePlugins.js'); import cacheFlow = require('./cache-flow.js'); import incrementalState = require('./incremental-state.js'); import type { FileCache } from './cache.js'; import type { IncrementalState } from './incremental-state.js'; +// @tsslint/core typings are declared against stock `typescript`; the two +// modules are nominally distinct types. TNB is a drop-in with the same API +// surface. +const tsStock = ts as unknown as typeof import('typescript'); + // Fallback if `ts.sys.createHash` is undefined on this host (Node ≥ 22.6 // always provides it via crypto, but the type is optional). sha256 hex. const defaultHash = (s: string) => crypto.createHash('sha256').update(s).digest('hex'); -import { createLanguage, FileMap, isCodeActionsEnabled, type Language } from '@volar/language-core'; -import { createProxyLanguageService, decorateLanguageServiceHost, resolveFileLanguageId } from '@volar/typescript'; -import { transformDiagnostic, transformFileTextChanges } from '@volar/typescript/lib/node/transform'; - let projectVersion = 0; let typeRootsVersion = 0; let options: ts.CompilerOptions = {}; let fileNames: string[] = []; -let language: Language | undefined; let linter: core.Linter; -let linterLanguageService!: ts.LanguageService; // Layer 2 state. We wrap the LS program in a SemanticDiagnostics- // BuilderProgram (with the prev session's BP fed back via TS's internal // `tsBuildInfoText` round-trip) and walk affected files once. cache- @@ -39,7 +37,7 @@ let currentBuilder: ts.SemanticDiagnosticsBuilderProgram | undefined; const snapshots = new Map(); const versions = new Map(); -const originalHost: ts.LanguageServiceHost = { +const languageServiceHost: ts.LanguageServiceHost = { ...ts.sys, useCaseSensitiveFileNames() { return ts.sys.useCaseSensitiveFileNames; @@ -75,27 +73,25 @@ const originalHost: ts.LanguageServiceHost = { return snapshots.get(fileName); }, getScriptKind(fileName) { - const languageId = resolveFileLanguageId(fileName); - switch (languageId) { - case 'javascript': + switch (path.extname(fileName).toLowerCase()) { + case '.js': + case '.mjs': + case '.cjs': return ts.ScriptKind.JS; - case 'javascriptreact': + case '.jsx': return ts.ScriptKind.JSX; - case 'typescript': - return ts.ScriptKind.TS; - case 'typescriptreact': + case '.tsx': return ts.ScriptKind.TSX; - case 'json': + case '.json': return ts.ScriptKind.JSON; } - return ts.ScriptKind.Unknown; + return ts.ScriptKind.TS; }, getDefaultLibFileName(options) { return ts.getDefaultLibFilePath(options); }, }; -const linterHost: ts.LanguageServiceHost = { ...originalHost }; -const originalService = ts.createLanguageService(linterHost); +const languageService = ts.createLanguageService(languageServiceHost); // Linter is single-threaded by design. The previous version split into a // worker_threads worker for TTY mode (so the spinner could update during a @@ -129,8 +125,6 @@ export function create() { } async function setup( - tsconfig: string, - languages: string[], configFile: string, _fileNames: string[], _options: ts.CompilerOptions, @@ -148,19 +142,6 @@ async function setup( return String(err); } - for (let key in linterHost) { - if (!(key in originalHost)) { - // @ts-ignore - delete linterHost[key]; - } - else { - // @ts-ignore - linterHost[key] = originalHost[key]; - } - } - linterLanguageService = originalService; - language = undefined; - // Reset per-project state. Multi-project runs reuse the same worker // (in-process) — without this, cross-project file paths accumulate in // `snapshots` / `versions` (memory leak) and `affectedFiles` from a @@ -171,29 +152,6 @@ async function setup( affectedFiles = undefined; currentBuilder = undefined; - const plugins = await languagePlugins.load(tsconfig, languages); - if (plugins.length) { - const { getScriptSnapshot } = originalHost; - language = createLanguage( - [ - ...plugins, - { getLanguageId: fileName => resolveFileLanguageId(fileName) }, - ], - new FileMap(ts.sys.useCaseSensitiveFileNames), - fileName => { - const snapshot = getScriptSnapshot(fileName); - if (snapshot) { - language!.scripts.set(fileName, snapshot); - } - }, - ); - decorateLanguageServiceHost(ts, language, linterHost); - - const proxy = createProxyLanguageService(linterLanguageService); - proxy.initialize(language); - linterLanguageService = proxy.proxy; - } - projectVersion++; typeRootsVersion++; fileNames = _fileNames; @@ -203,17 +161,15 @@ async function setup( // The synthetic path is never written to disk — captured via // writeFile callback at end of session. options = { - ...(plugins.some(plugin => plugin.typescript?.extraFileExtensions.length) - ? { ..._options, allowNonTsExtensions: true } - : _options), + ..._options, incremental: true, tsBuildInfoFile: incrementalState.SYNTHETIC_BUILD_INFO_PATH, }; linter = core.createLinter( { - languageService: linterLanguageService, - languageServiceHost: linterHost, - typescript: ts, + languageService, + languageServiceHost, + typescript: tsStock, }, path.dirname(configFile), config, @@ -222,10 +178,10 @@ async function setup( ); { - const program = linterLanguageService.getProgram()!; + const program = languageService.getProgram()!; // Reconstruct the prev session's BP from cached buildinfo text, // fall through to undefined on any failure (cold-start path). - const oldBuilder = incrementalState.reconstructOldBuilder(ts, prevIncrementalState, { + const oldBuilder = incrementalState.reconstructOldBuilder(tsStock, prevIncrementalState, { useCaseSensitiveFileNames: () => ts.sys.useCaseSensitiveFileNames, getCurrentDirectory: () => ts.sys.getCurrentDirectory(), }); @@ -311,23 +267,16 @@ function lint(fileName: string, fix: boolean, fileCache: FileCache, fileMtime: n let pass = 0; let converged = false; for (; pass < MAX_FIX_PASSES; pass++) { - const program = linterLanguageService.getProgram()!; + const program = languageService.getProgram()!; diagnostics = cacheFlow.lintWithCache(linter, fileName, fileCache, fileMtime, program, { incremental: true, typeAwareUnaffected, }); - let fixes = linter + const fixes = linter .getCodeFixes(fileName, 0, Number.MAX_VALUE, diagnostics) .filter(fix => fix.fixId === 'tsslint'); - if (language) { - fixes = fixes.map(fix => { - fix.changes = transformFileTextChanges(language!, fix.changes, false, isCodeActionsEnabled); - return fix; - }); - } - const textChanges = core.combineCodeFixes(fileName, fixes); if (!textChanges.length) { converged = true; @@ -372,52 +321,20 @@ function lint(fileName: string, fix: boolean, fileCache: FileCache, fileMtime: n } if (shouldCheck) { - const program = linterLanguageService.getProgram()!; + const program = languageService.getProgram()!; diagnostics = cacheFlow.lintWithCache(linter, fileName, fileCache, fileMtime, program, { incremental: true, typeAwareUnaffected, }); } - // Language-transform path (Vue/MDX/etc.): diagnostics map back from - // the transformed file to the original source. The original file - // might not be in the language service's program, so we substitute a - // SourceFile-shaped POJO with the real source text — `formatDiagnostics- - // WithColorAndContext` reads `.file.text` to render code snippets. - if (language) { - diagnostics = diagnostics - .map(d => transformDiagnostic(language!, d, (originalService as any).getCurrentProgram(), false)) - .filter(d => !!d); - const fileShim = new Map(); - const getShim = (fn: string) => { - let s = fileShim.get(fn); - if (!s) { - s = { fileName: fn, text: getFileText(fn) }; - fileShim.set(fn, s); - } - return s; - }; - diagnostics = diagnostics.map(d => ({ - ...d, - file: getShim(d.file.fileName) as any, - relatedInformation: d.relatedInformation?.map(info => ({ - ...info, - file: info.file ? getShim(info.file.fileName) as any : undefined, - })), - })); - } - // Plain-TS path: leave diagnostics as-is. `.file` is the program's real - // `ts.SourceFile` which already shares `lineMap` cache across all - // diagnostics on the same file (so `formatDiagnosticsWithColorAndContext` - // only computes line starts once per file). - + // Diagnostics are already in the original file's coordinates — the + // program's real `ts.SourceFile`, which shares its `lineMap` cache + // across all diagnostics on the same file (so `formatDiagnosticsWith- + // ColorAndContext` only computes line starts once per file). return diagnostics; } -function getFileText(fileName: string) { - return originalHost.getScriptSnapshot(fileName)!.getText(0, Number.MAX_VALUE); -} - function hasCodeFixes(fileName: string) { return linter.hasCodeFixes(fileName); } diff --git a/packages/cli/package.json b/packages/cli/package.json index 86e410e2..d1652eaf 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -24,12 +24,7 @@ "@tsslint/config": "3.1.4", "@tsslint/core": "3.1.4", "@tsslint/types": "3.1.4", - "@volar/language-core": "~2.4.0", - "@volar/language-hub": "0.0.1", - "@volar/typescript": "~2.4.0", - "minimatch": "^10.0.1" - }, - "peerDependencies": { - "typescript": "*" + "minimatch": "^10.0.1", + "typescript-native-bridge": "6.0.3-bridge.15.tsgo.7.0.2" } } diff --git a/packages/vscode/README.md b/packages/vscode/README.md index 8cdc0e13..03b1af6e 100644 --- a/packages/vscode/README.md +++ b/packages/vscode/README.md @@ -8,7 +8,7 @@ TSSLint is a high-performance, project-centric linter that runs directly as a Ty * **Instant Diagnostics**: Violations from your custom TSSLint rules are reported instantly as you type. * **Auto Fix on Save**: Supports quick fixes and automatic application of fixes upon saving the file. -* **Framework Support**: Seamlessly lints TypeScript code within virtual files created by extensions like **Vue Official (Volar)**, **Astro**, and **MDX**. +* **Framework Support**: Seamlessly lints TypeScript code within virtual files created by framework extensions such as **Vue Official**, **Astro**, and **MDX**. ## Installation diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 03fa2261..9256f198 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -60,24 +60,6 @@ importers: specifier: ^6.1.3 version: 6.1.3(typescript@6.0.3) - fixtures/meta-frameworks: - devDependencies: - '@astrojs/ts-plugin': - specifier: latest - version: 1.10.9 - '@mdx-js/language-service': - specifier: latest - version: 0.7.3 - '@ts-macro/language-plugin': - specifier: latest - version: 0.3.7 - '@vue-vine/language-service': - specifier: latest - version: 1.7.27(typescript@6.0.3) - '@vue/language-core': - specifier: latest - version: 3.3.5 - fixtures/typescript-plugin: devDependencies: '@tsslint/typescript-plugin': @@ -98,21 +80,12 @@ importers: '@tsslint/types': specifier: 3.1.4 version: link:../types - '@volar/language-core': - specifier: ~2.4.0 - version: 2.4.28 - '@volar/language-hub': - specifier: 0.0.1 - version: 0.0.1 - '@volar/typescript': - specifier: ~2.4.0 - version: 2.4.28 minimatch: specifier: ^10.0.1 version: 10.2.5 - typescript: - specifier: '*' - version: 6.0.3 + typescript-native-bridge: + specifier: 6.0.3-bridge.15.tsgo.7.0.2 + version: 6.0.3-bridge.15.tsgo.7.0.2 packages/compat-eslint: dependencies: @@ -197,15 +170,6 @@ importers: packages: - '@astrojs/compiler@2.13.1': - resolution: {integrity: sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==} - - '@astrojs/ts-plugin@1.10.9': - resolution: {integrity: sha512-2pNc9EMXJcvYsmjQahYyqOiUNN9hMzgnxgIE/fZBLJAeOxPeOCSMa6BdngwV+bMgZDJcUSfC3qFt3+9abuUxtQ==} - - '@astrojs/yaml2ts@0.2.4': - resolution: {integrity: sha512-8oddpOae35pJsXPQXhTkM0ypfKPskVsh2bCxRtbf7e+/Epw2nReakFYpLKjZMEr75CsoF203PMnCocpfz0s69A==} - '@azu/format-text@1.0.2': resolution: {integrity: sha512-Swi4N7Edy1Eqq82GxgEECXSSLyn6GOb5htRFPzBDdUkECGXtlf12ynO5oJSpWKPwCaUssOu7NfhDcCWpIC6Ywg==} @@ -260,23 +224,10 @@ packages: resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.27.1': - resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} - engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} - '@babel/parser@7.29.2': - resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} - engines: {node: '>=6.0.0'} - hasBin: true - - '@babel/types@7.29.0': - resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} - engines: {node: '>=6.9.0'} - '@conventional-changelog/git-client@2.7.0': resolution: {integrity: sha512-j7A8/LBEQ+3rugMzPXoKYzyUPpw/0CBQCyvtTR7Lmu4olG4yRC/Tfkq79Mr3yuPs0SUitlO2HwGP3gitMJnRFw==} engines: {node: '>=18'} @@ -677,9 +628,6 @@ packages: '@isaacs/string-locale-compare@1.1.0': resolution: {integrity: sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==} - '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@lerna-lite/cli@5.3.0': resolution: {integrity: sha512-gm+XWT2XRR3H8XXDMuWhL1jlqaAt0llwLZCGA/uUwe05HS2QKbEsYeaK0kZZKtt1ff7DAZYadMW4uazz9iHczg==} engines: {node: ^22.17.0 || >=24.0.0} @@ -725,9 +673,6 @@ packages: resolution: {integrity: sha512-ZZUE/ajuCFj1WLNY08wKFmiSD+RYeT0mPosHKYbXejzUGKIBcuOdTApxvRcBr/G2pAWqQowCtE1sOM7AUbvzsw==} engines: {node: ^22.17.0 || >=24.0.0} - '@mdx-js/language-service@0.7.3': - resolution: {integrity: sha512-Kspcrl7Z8iFQ9KlA5/NAbMEXPH1ssSzyHsPxslFhDU6Tw8E2SBBSj1Z9Qyj73pvfdwirT7YKWZdeY/QfIWbCiw==} - '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -953,12 +898,6 @@ packages: '@textlint/types@15.5.4': resolution: {integrity: sha512-mY28j2U7nrWmZbxyKnRvB8vJxJab4AxqOobLfb6iozrLelJbqxcOTvBQednadWPfAk9XWaZVMqUr9Nird3mutg==} - '@ts-macro/language-plugin@0.3.7': - resolution: {integrity: sha512-fZBCGoYmM3fm1EE7v2FQ0Ovab1sOqgDaz48Wypw4KkO3yG8pEtYtpa24PxNY43ydCf0yMNwShufdyV5qt5sRmg==} - - '@ts-morph/common@0.27.0': - resolution: {integrity: sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==} - '@tufjs/canonical-json@2.0.0': resolution: {integrity: sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==} engines: {node: ^16.14.0 || >=18.0.0} @@ -967,33 +906,18 @@ packages: resolution: {integrity: sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww==} engines: {node: ^20.17.0 || >=22.9.0} - '@types/debug@4.1.13': - resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} - '@types/eslint@8.56.12': resolution: {integrity: sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==} '@types/esrecurse@4.3.1': resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} - '@types/estree-jsx@1.0.5': - resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} - '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - '@types/hast@3.0.4': - resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} - '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/mdast@4.0.4': - resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} - - '@types/ms@2.1.0': - resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - '@types/node@25.9.3': resolution: {integrity: sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==} @@ -1007,12 +931,6 @@ packages: '@types/sarif@2.1.7': resolution: {integrity: sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==} - '@types/unist@2.0.11': - resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} - - '@types/unist@3.0.3': - resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - '@types/vscode@1.116.0': resolution: {integrity: sha512-sYHp4MO6BqJ2PD7Hjt0hlIS3tMaYsVPJrd0RUjDJ8HtOYnyJIEej0bLSccM8rE77WrC+Xox/kdBwEFDO8MsxNA==} @@ -1119,42 +1037,44 @@ packages: resolution: {integrity: sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typespec/ts-http-runtime@0.3.5': - resolution: {integrity: sha512-yURCknZhvywvQItHMMmFSo+fq5arCUIyz/CVk7jD89MSai7dkaX8ufjCWp3NttLojoTVbcE72ri+be/TnEbMHw==} - engines: {node: '>=20.0.0'} - - '@umami/node@0.4.0': - resolution: {integrity: sha512-pyphprbiF7KiDSc+SWZ4/rVM8B5vU27zIiFfEPj2lEqczpI4xAKSp+dM3tlzyRAWJL32fcbCfAaLGhJZQV13Rg==} - - '@volar/language-core@2.4.20': - resolution: {integrity: sha512-dRDF1G33xaAIDqR6+mXUIjXYdu9vzSxlMGfMEwBxQsfY/JMUEXSpLTR057oTKlUQ2nIvCmP9k94A8h8z2VrNSA==} - - '@volar/language-core@2.4.28': - resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==} - - '@volar/language-hub@0.0.1': - resolution: {integrity: sha512-2eOUnlMKTyjtlXIVd+6pfAtcuVugxCOgpNgcLWmlPuncQTG5C1E5mTDL/PUMw7aEnLySUOtMTIp8lT3vk/7w6Q==} + '@typescript-native-bridge/darwin-arm64@6.0.3-bridge.15.tsgo.7.0.2': + resolution: {integrity: sha512-nrhPj8nED29uhZH2V7yHXFPy5O8ro5vRMVnChO1CvMf8tDg9tlhlBPFeeYgvp001upBRZu8JmTYw1wpcRUvDVA==} + cpu: [arm64] + os: [darwin] - '@volar/language-server@2.4.20': - resolution: {integrity: sha512-fNNFzEad0sO4pVZnpHggglbIeaKjLs4vH1JPPN+zd/4hSEI2u8+Qck10JhswCSO6xFTFbKxVquvWu2U2tT0EHQ==} + '@typescript-native-bridge/darwin-x64@6.0.3-bridge.15.tsgo.7.0.2': + resolution: {integrity: sha512-JiS2e3W+Vtr8u/oTYeTdCK7S+seB2cBz3l7gQYuon4oTiHS0Mi+YGEYhSG2v2iuRkTMcrVrr9+XazwMcPlIW+Q==} + cpu: [x64] + os: [darwin] - '@volar/language-service@2.4.20': - resolution: {integrity: sha512-LoCD4rEI1Bj5ld6b+2GH1SbDGnoisvJ5skHlrkFEtJWw0T2+bhqGUXwekFudV/bRtp8fPhvD5ZUtjWSW0VRztg==} + '@typescript-native-bridge/linux-arm64@6.0.3-bridge.15.tsgo.7.0.2': + resolution: {integrity: sha512-BFkIGbpdRf+iNK0IHCKUYAvAIQ4tnTRegNU+BLaYUbQ5ER/YGytSzKQqh2c2zls1H+Gc0DyZcDsBIFr0gC6BDg==} + cpu: [arm64] + os: [linux] - '@volar/language-service@2.4.28': - resolution: {integrity: sha512-Rh/wYCZJrI5vCwMk9xyw/Z+MsWxlJY1rmMZPsxUoJKfzIRjS/NF1NmnuEcrMbEVGja00aVpCsInJfixQTMdvLw==} + '@typescript-native-bridge/linux-arm@6.0.3-bridge.15.tsgo.7.0.2': + resolution: {integrity: sha512-VlvPkt0x7ShuPmI58wSsS7VBa9uq4fQtaz82GC8yK1bFP+FdHcLJleM2SyAvHpxyd8nIyKMzcJolY+qffaBRUg==} + cpu: [arm] + os: [linux] - '@volar/source-map@2.4.20': - resolution: {integrity: sha512-mVjmFQH8mC+nUaVwmbxoYUy8cww+abaO8dWzqPUjilsavjxH0jCJ3Mp8HFuHsdewZs2c+SP+EO7hCd8Z92whJg==} + '@typescript-native-bridge/linux-x64@6.0.3-bridge.15.tsgo.7.0.2': + resolution: {integrity: sha512-QB6q8kNk5HO/mc3iyJBx1I7mx2+ObNxK/SgeX18gVydcooVdjLYv9lSDjv/FuA9iYzuhYjfrOuRxuhxKoAOcmQ==} + cpu: [x64] + os: [linux] - '@volar/source-map@2.4.28': - resolution: {integrity: sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==} + '@typescript-native-bridge/win32-arm64@6.0.3-bridge.15.tsgo.7.0.2': + resolution: {integrity: sha512-ZWY1I/6dsInK3S2ue4sKHj3zp3nmqD+9iqq7LfjELNjjYSUvseYfqv3170zeqZV2PQRzjA3Qtd85pkYr0SQDEQ==} + cpu: [arm64] + os: [win32] - '@volar/typescript@2.4.20': - resolution: {integrity: sha512-Oc4DczPwQyXcVbd+5RsNEqX6ia0+w3p+klwdZQ6ZKhFjWoBP9PCPQYlKYRi/tDemWphW93P/Vv13vcE9I9D2GQ==} + '@typescript-native-bridge/win32-x64@6.0.3-bridge.15.tsgo.7.0.2': + resolution: {integrity: sha512-LqaVPjvllVQm/OfID0z9ubDPEuO6asNOH/qrV1xTGFxgHRRn8UUz3/te4zaERalVRgwNWYlSbzSQb+jttLjvMA==} + cpu: [x64] + os: [win32] - '@volar/typescript@2.4.28': - resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==} + '@typespec/ts-http-runtime@0.3.5': + resolution: {integrity: sha512-yURCknZhvywvQItHMMmFSo+fq5arCUIyz/CVk7jD89MSai7dkaX8ufjCWp3NttLojoTVbcE72ri+be/TnEbMHw==} + engines: {node: '>=20.0.0'} '@vscode/vsce-sign-alpine-arm64@2.0.6': resolution: {integrity: sha512-wKkJBsvKF+f0GfsUuGT0tSW0kZL87QggEiqNqK6/8hvqsXvpx8OsTEc3mnE1kejkh5r+qUyQ7PtF8jZYN0mo8Q==} @@ -1209,38 +1129,6 @@ packages: engines: {node: '>= 20'} hasBin: true - '@vue-vine/compiler@1.7.27': - resolution: {integrity: sha512-aWLc1TaCq1m379hMGAUdgZRe6OR7fTqQllSb3BR9oz3SA3hiIZbcvcH89lFr0Vj48CPj2RC16p8bAVCdZDrXAg==} - - '@vue-vine/language-service@1.7.27': - resolution: {integrity: sha512-x/ZFv9WyMeh4fBTOd8xsRziQDd48wkDdI+fRr21kEueRahksoGnuoY5KyygoHZOQP237cls6oqZ48fuHe36scA==} - - '@vue/compiler-core@3.5.33': - resolution: {integrity: sha512-3PZLQwFw4Za3TC8t0FvTy3wI16Kt+pmwcgNZca4Pj9iWL2E72a/gZlpBtAJvEdDMdCxdG/qq0C7PN0bsJuv0Rw==} - - '@vue/compiler-dom@3.5.33': - resolution: {integrity: sha512-PXq0yrfCLzzL07rbXO4awtXY1Z06LG2eu6Adg3RJFa/j3Cii217XxxLXG22N330gw7GmALCY0Z8RgXEviwgpjA==} - - '@vue/compiler-ssr@3.5.33': - resolution: {integrity: sha512-IErjYdnj1qIupG5xxiVIYiiRvDhGWV4zuh/RCrwfYpuL+HWQzeU6lCk/nF9r7olWMnjKxCAkOctT2qFWFkzb1A==} - - '@vue/compiler-vue2@2.7.16': - resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} - - '@vue/language-core@3.0.3': - resolution: {integrity: sha512-I9wY0ULMN9tMSua+2C7g+ez1cIziVMUzIHlDYGSl2rtru3Eh4sXj95vZ+4GBuXwwPnEmYfzSApVbXiVbI8V5Gg==} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@vue/language-core@3.3.5': - resolution: {integrity: sha512-UkKu5nhX89fg4VhlG/FOeI10G3cj/7radKT/cy9BT4Q9qJmJlSTAc/dP63Xqs29aypN4f39xUV6PsLNk/dcD6g==} - - '@vue/shared@3.5.33': - resolution: {integrity: sha512-5vR2QIlmaLG77Ygd4pMP6+SGQ5yox9VhtnbDWTy9DzMzdmeLxZ1QqxrywEZ9sa1AVubfIJyaCG3ytyWU81ufcQ==} - abbrev@4.0.0: resolution: {integrity: sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==} engines: {node: ^20.17.0 || >=22.9.0} @@ -1265,12 +1153,6 @@ packages: ajv@8.18.0: resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} - alien-signals@2.0.8: - resolution: {integrity: sha512-844G1VLkk0Pe2SJjY0J8vp8ADI73IM4KliNu2OGlYzWpO28NexEUvjHTcFjFX3VXoiUtwTbHxLNI9ImkcoBqzA==} - - alien-signals@3.2.1: - resolution: {integrity: sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==} - ansi-escapes@7.3.0: resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} engines: {node: '>=18'} @@ -1314,9 +1196,6 @@ packages: azure-devops-node-api@12.5.0: resolution: {integrity: sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==} - bail@2.0.2: - resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} - balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1391,9 +1270,6 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - ccount@2.0.1: - resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -1406,18 +1282,6 @@ packages: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - character-entities-html4@2.1.0: - resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} - - character-entities-legacy@3.0.0: - resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} - - character-entities@2.0.2: - resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} - - character-reference-invalid@2.0.1: - resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} - cheerio-select@2.1.0: resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} @@ -1452,9 +1316,6 @@ packages: resolution: {integrity: sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q==} engines: {node: '>=16'} - code-block-writer@13.0.3: - resolution: {integrity: sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==} - color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} @@ -1540,9 +1401,6 @@ packages: engines: {node: '>=4'} hasBin: true - de-indent@1.0.2: - resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} - debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -1552,9 +1410,6 @@ packages: supports-color: optional: true - decode-named-character-reference@1.3.0: - resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} - decompress-response@6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} @@ -1582,10 +1437,6 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} - dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} - detect-indent@7.0.2: resolution: {integrity: sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A==} engines: {node: '>=12.20'} @@ -1594,9 +1445,6 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} - devlop@1.1.0: - resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - diff@4.0.4: resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==} engines: {node: '>=0.3.1'} @@ -1785,21 +1633,6 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} - estree-util-is-identifier-name@3.0.0: - resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} - - estree-util-scope@1.0.0: - resolution: {integrity: sha512-2CAASclonf+JFWBNJPndcOpA8EMJwa0Q8LUFJEKqXLW6+qBvbFZuF5gItbQOs/umBUkjviCSDCbBwU2cXbmrhQ==} - - estree-util-visit@2.0.0: - resolution: {integrity: sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==} - - estree-walker@2.0.2: - resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - - estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -1811,9 +1644,6 @@ packages: exponential-backoff@3.1.3: resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} - extend@3.0.2: - resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - fast-content-type-parse@3.0.0: resolution: {integrity: sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==} @@ -1917,9 +1747,6 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} - get-tsconfig@4.14.0: - resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} - git-up@8.1.1: resolution: {integrity: sha512-FDenSF3fVqBYSaJoYy1KSc2wosx0gCvKP+c+PRBht7cAaiCeQlBtfBDX9vgnNOHmdePlSFITVcn4pFfcgNvx3g==} @@ -1987,17 +1814,10 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - hash-sum@2.0.0: - resolution: {integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==} - hasown@2.0.3: resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} engines: {node: '>= 0.4'} - he@1.2.0: - resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} - hasBin: true - hosted-git-info@4.1.0: resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} engines: {node: '>=10'} @@ -2086,19 +1906,10 @@ packages: resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} engines: {node: '>= 12'} - is-alphabetical@2.0.1: - resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} - - is-alphanumerical@2.0.1: - resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} - is-core-module@2.16.1: resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} - is-decimal@2.0.1: - resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} - is-docker@3.0.0: resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -2116,9 +1927,6 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-hexadecimal@2.0.1: - resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} - is-inside-container@1.0.0: resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} engines: {node: '>=14.16'} @@ -2143,9 +1951,6 @@ packages: resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} engines: {node: '>=16'} - isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -2153,10 +1958,6 @@ packages: resolution: {integrity: sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==} engines: {node: '>=20'} - isobject@2.1.0: - resolution: {integrity: sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==} - engines: {node: '>=0.10.0'} - istextorbinary@9.5.0: resolution: {integrity: sha512-5mbUj3SiZXCuRf9fT3ibzbSSEWiy63gFfksmGfdOzujPjW3k+z8WvIBxcJHBoQNlaZaiyB25deviif2+osLmLw==} engines: {node: '>=4'} @@ -2255,9 +2056,6 @@ packages: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} - line-column@1.0.2: - resolution: {integrity: sha512-Ktrjk5noGYlHsVnYWh62FLVs4hTb8A3e+vucNZMgPeAOITdshMSgv4cCZQeRDjm7+goqmo6+liZwTXo+U3sVww==} - linkify-it@5.0.0: resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} @@ -2299,9 +2097,6 @@ packages: lodash@4.18.1: resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} - longest-streak@3.1.0: - resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} - lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -2313,9 +2108,6 @@ packages: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} - magic-string@0.30.21: - resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - make-fetch-happen@15.0.5: resolution: {integrity: sha512-uCbIa8jWWmQZt4dSnEStkVC6gdakiinAm4PiGsywIkguF0eWMdcjDz0ECYhUolFU3pFLOev9VNPCEygydXnddg==} engines: {node: ^20.17.0 || >=22.9.0} @@ -2328,30 +2120,6 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} - mdast-util-from-markdown@2.0.3: - resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} - - mdast-util-mdx-expression@2.0.1: - resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} - - mdast-util-mdx-jsx@3.2.0: - resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} - - mdast-util-mdx@3.0.0: - resolution: {integrity: sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w==} - - mdast-util-mdxjs-esm@2.0.1: - resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} - - mdast-util-phrasing@4.1.0: - resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} - - mdast-util-to-markdown@2.1.2: - resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} - - mdast-util-to-string@4.0.0: - resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} - mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} @@ -2359,97 +2127,10 @@ packages: resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} engines: {node: '>=18'} - merge-source-map@1.1.0: - resolution: {integrity: sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==} - merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - micromark-core-commonmark@2.0.3: - resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} - - micromark-extension-mdx-expression@3.0.1: - resolution: {integrity: sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q==} - - micromark-extension-mdx-jsx@3.0.2: - resolution: {integrity: sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ==} - - micromark-extension-mdx-md@2.0.0: - resolution: {integrity: sha512-EpAiszsB3blw4Rpba7xTOUptcFeBFi+6PY8VnJ2hhimH+vCQDirWgsMpz7w1XcZE7LVrSAUGb9VJpG9ghlYvYQ==} - - micromark-extension-mdxjs-esm@3.0.0: - resolution: {integrity: sha512-DJFl4ZqkErRpq/dAPyeWp15tGrcrrJho1hKK5uBS70BCtfrIFg81sqcTVu3Ta+KD1Tk5vAtBNElWxtAa+m8K9A==} - - micromark-extension-mdxjs@3.0.0: - resolution: {integrity: sha512-A873fJfhnJ2siZyUrJ31l34Uqwy4xIFmvPY1oj+Ean5PHcPBYzEsvqvWGaWcfEIr11O5Dlw3p2y0tZWpKHDejQ==} - - micromark-factory-destination@2.0.1: - resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} - - micromark-factory-label@2.0.1: - resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} - - micromark-factory-mdx-expression@2.0.3: - resolution: {integrity: sha512-kQnEtA3vzucU2BkrIa8/VaSAsP+EJ3CKOvhMuJgOEGg9KDC6OAY6nSnNDVRiVNRqj7Y4SlSzcStaH/5jge8JdQ==} - - micromark-factory-space@2.0.1: - resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} - - micromark-factory-title@2.0.1: - resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} - - micromark-factory-whitespace@2.0.1: - resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} - - micromark-util-character@2.1.1: - resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} - - micromark-util-chunked@2.0.1: - resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} - - micromark-util-classify-character@2.0.1: - resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} - - micromark-util-combine-extensions@2.0.1: - resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} - - micromark-util-decode-numeric-character-reference@2.0.2: - resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} - - micromark-util-decode-string@2.0.1: - resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} - - micromark-util-encode@2.0.1: - resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} - - micromark-util-events-to-acorn@2.0.3: - resolution: {integrity: sha512-jmsiEIiZ1n7X1Rr5k8wVExBQCg5jy4UXVADItHmNk1zkwEVhBuIUKRu3fqv+hs4nxLISi2DQGlqIOGiFxgbfHg==} - - micromark-util-html-tag-name@2.0.1: - resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} - - micromark-util-normalize-identifier@2.0.1: - resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} - - micromark-util-resolve-all@2.0.1: - resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} - - micromark-util-sanitize-uri@2.0.1: - resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} - - micromark-util-subtokenize@2.1.0: - resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} - - micromark-util-symbol@2.0.1: - resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} - - micromark-util-types@2.0.2: - resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} - - micromark@4.0.2: - resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} - micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} @@ -2523,9 +2204,6 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - muggle-string@0.4.1: - resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} - mute-stream@0.0.8: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} @@ -2533,11 +2211,6 @@ packages: resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} engines: {node: ^20.17.0 || >=22.9.0} - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - napi-build-utils@2.0.0: resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} @@ -2663,9 +2336,6 @@ packages: resolution: {integrity: sha512-ZHEmNKMq1wyJXNwLxyHnluPfRAFSIliBvbK/UiOceROt4Xh9Pz0fq49NytIaeaCUf5VR86hwQ/34FCcNU5/LKQ==} engines: {node: ^20.17.0 || >=22.9.0} - parse-entities@4.0.2: - resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} - parse-json@8.3.0: resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==} engines: {node: '>=18'} @@ -2689,9 +2359,6 @@ packages: parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} - path-browserify@1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -2744,10 +2411,6 @@ packages: resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} engines: {node: '>=4'} - postcss@8.5.10: - resolution: {integrity: sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==} - engines: {node: ^10 || ^12 || >=14} - prebuild-install@7.1.3: resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} engines: {node: '>=10'} @@ -2816,15 +2479,6 @@ packages: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} - remark-mdx@3.1.1: - resolution: {integrity: sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg==} - - remark-parse@11.0.0: - resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} - - request-light@0.7.0: - resolution: {integrity: sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q==} - require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} @@ -2841,9 +2495,6 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} - resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - resolve@1.22.12: resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} engines: {node: '>= 0.4'} @@ -2853,15 +2504,6 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rollup-utils@0.0.5: - resolution: {integrity: sha512-Naai1vGKHGTrvrSuWOX1rSJSKodmJ8aZ3mtF0hHFP64NOtsKvnOGbxD4162fWGePdCWxAckEBNHQ/QX7S81H4g==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true - run-applescript@7.1.0: resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} engines: {node: '>=18'} @@ -2960,10 +2602,6 @@ packages: resolution: {integrity: sha512-ueSlHJMwpIw42CJ4B11Uxzh/S0p0AlOyiNktlv2KOu5e1JpUE6DlC4AAUjXqesHdBRv/g0wC9Q4vwq0NP2pA9w==} engines: {node: '>=20'} - source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} - source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} @@ -3007,9 +2645,6 @@ packages: string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - stringify-entities@4.0.4: - resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} - strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -3091,21 +2726,12 @@ packages: resolution: {integrity: sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - trough@2.2.0: - resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} - ts-api-utils@2.5.0: resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' - ts-macro@0.3.7: - resolution: {integrity: sha512-5BinbTKn5WXRslotv5/VWTn3catfK0thWoB4BZI5VNEdxMu6r9AUPFalut4wLEK3nGvxhywA9aAdZnJ+Fnug9A==} - - ts-morph@26.0.0: - resolution: {integrity: sha512-ztMO++owQnz8c/gIENcM9XfCEzgoGphTv+nKpYNM1bgsdOVC/jRZuEBf6N+mLLDNg68Kl+GgUZfOySaRiG1/Ug==} - ts-pattern@5.9.0: resolution: {integrity: sha512-6s5V71mX8qBUmlgbrfL33xDUwO0fq48rxAu2LBE11WBeGdpCPOsXksQbZJHvHwhrd3QjUusd3mAOM5Gg0mFBLg==} @@ -3156,6 +2782,11 @@ packages: typed-rest-client@1.8.11: resolution: {integrity: sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==} + typescript-native-bridge@6.0.3-bridge.15.tsgo.7.0.2: + resolution: {integrity: sha512-utlvj8zRana8mutK9LgoPLtMcMT/j/0ot5eh/YUJgDSVQUGLIC6m6Dwt1SP820HMJZI3Yg8fXnqFrkDaa2Gk4g==} + engines: {node: '>=20.19'} + hasBin: true + typescript@6.0.3: resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} engines: {node: '>=14.17'} @@ -3191,27 +2822,6 @@ packages: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} - unified@11.0.5: - resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} - - unist-util-is@6.0.1: - resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} - - unist-util-lsp@2.1.0: - resolution: {integrity: sha512-ICI1PWbalSfOrsdosGk+WXdZBvdjhZVjbkgHOzKFdkCdzBpVEi8GjsVTAG6NR3VLGcwTOGLj/+k4L0HwGQYvEQ==} - - unist-util-position-from-estree@2.0.0: - resolution: {integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==} - - unist-util-stringify-position@4.0.0: - resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} - - unist-util-visit-parents@6.0.2: - resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} - - unist-util-visit@5.1.0: - resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} - universal-user-agent@7.0.3: resolution: {integrity: sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==} @@ -3243,32 +2853,6 @@ packages: resolution: {integrity: sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg==} engines: {node: '>=4'} - vfile-message@4.0.3: - resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} - - vfile@6.0.3: - resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - - vscode-jsonrpc@8.2.0: - resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==} - engines: {node: '>=14.0.0'} - - vscode-languageserver-protocol@3.17.5: - resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==} - - vscode-languageserver-textdocument@1.0.12: - resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==} - - vscode-languageserver-types@3.17.5: - resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} - - vscode-languageserver@9.0.1: - resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==} - hasBin: true - - vscode-uri@3.1.0: - resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} - walk-up-path@4.0.0: resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} engines: {node: 20 || >=22} @@ -3341,11 +2925,6 @@ packages: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} - yaml@2.8.3: - resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} - engines: {node: '>= 14.6'} - hasBin: true - yaml@2.9.0: resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} engines: {node: '>= 14.6'} @@ -3376,27 +2955,8 @@ packages: zod@4.3.6: resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} - zwitch@2.0.4: - resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} - snapshots: - '@astrojs/compiler@2.13.1': {} - - '@astrojs/ts-plugin@1.10.9': - dependencies: - '@astrojs/compiler': 2.13.1 - '@astrojs/yaml2ts': 0.2.4 - '@jridgewell/sourcemap-codec': 1.5.5 - '@volar/language-core': 2.4.28 - '@volar/typescript': 2.4.28 - semver: 7.7.4 - vscode-languageserver-textdocument: 1.0.12 - - '@astrojs/yaml2ts@0.2.4': - dependencies: - yaml: 2.8.3 - '@azu/format-text@1.0.2': {} '@azu/style-format@1.0.1': @@ -3492,19 +3052,8 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-validator-identifier@7.28.5': {} - '@babel/parser@7.29.2': - dependencies: - '@babel/types': 7.29.0 - - '@babel/types@7.29.0': - dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 - '@conventional-changelog/git-client@2.7.0(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.4.0)': dependencies: '@simple-libs/child-process-utils': 1.0.2 @@ -3828,8 +3377,6 @@ snapshots: '@isaacs/string-locale-compare@1.1.0': {} - '@jridgewell/sourcemap-codec@1.5.5': {} - '@lerna-lite/cli@5.3.0(@lerna-lite/publish@5.3.0(@types/node@25.9.3)(conventional-commits-filter@5.0.0))(@lerna-lite/version@5.3.0(@lerna-lite/publish@5.3.0(@types/node@25.9.3)(conventional-commits-filter@5.0.0))(@types/node@25.9.3)(conventional-commits-filter@5.0.0))(@types/node@25.9.3)': dependencies: '@lerna-lite/core': 5.3.0(@types/node@25.9.3) @@ -3930,26 +3477,6 @@ snapshots: - '@types/node' - conventional-commits-filter - '@mdx-js/language-service@0.7.3': - dependencies: - '@types/estree': 1.0.8 - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - '@volar/language-service': 2.4.28 - estree-util-scope: 1.0.0 - estree-walker: 3.0.3 - mdast-util-mdxjs-esm: 2.0.1 - mdast-util-to-markdown: 2.1.2 - remark-mdx: 3.1.1 - remark-parse: 11.0.0 - unified: 11.0.5 - unist-util-lsp: 2.1.0 - unist-util-visit-parents: 6.0.2 - vfile-message: 4.0.3 - vscode-uri: 3.1.0 - transitivePeerDependencies: - - supports-color - '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -4290,23 +3817,6 @@ snapshots: dependencies: '@textlint/ast-node-types': 15.5.4 - '@ts-macro/language-plugin@0.3.7': - dependencies: - '@volar/language-core': 2.4.28 - '@volar/typescript': 2.4.28 - jiti: 2.6.1 - rollup-utils: 0.0.5 - ts-macro: 0.3.7 - vscode-uri: 3.1.0 - transitivePeerDependencies: - - rollup - - '@ts-morph/common@0.27.0': - dependencies: - fast-glob: 3.3.3 - minimatch: 10.2.5 - path-browserify: 1.0.1 - '@tufjs/canonical-json@2.0.0': {} '@tufjs/models@4.1.0': @@ -4314,10 +3824,6 @@ snapshots: '@tufjs/canonical-json': 2.0.0 minimatch: 10.2.5 - '@types/debug@4.1.13': - dependencies: - '@types/ms': 2.1.0 - '@types/eslint@8.56.12': dependencies: '@types/estree': 1.0.8 @@ -4325,24 +3831,10 @@ snapshots: '@types/esrecurse@4.3.1': {} - '@types/estree-jsx@1.0.5': - dependencies: - '@types/estree': 1.0.8 - '@types/estree@1.0.8': {} - '@types/hast@3.0.4': - dependencies: - '@types/unist': 3.0.3 - '@types/json-schema@7.0.15': {} - '@types/mdast@4.0.4': - dependencies: - '@types/unist': 3.0.3 - - '@types/ms@2.1.0': {} - '@types/node@25.9.3': dependencies: undici-types: 7.24.6 @@ -4355,10 +3847,6 @@ snapshots: '@types/sarif@2.1.7': {} - '@types/unist@2.0.11': {} - - '@types/unist@3.0.3': {} - '@types/vscode@1.116.0': {} '@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.59.0(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3))(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3)': @@ -4515,67 +4003,34 @@ snapshots: '@typescript-eslint/types': 8.61.1 eslint-visitor-keys: 5.0.1 - '@typespec/ts-http-runtime@0.3.5': - dependencies: - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@umami/node@0.4.0': {} - - '@volar/language-core@2.4.20': - dependencies: - '@volar/source-map': 2.4.20 - - '@volar/language-core@2.4.28': - dependencies: - '@volar/source-map': 2.4.28 - - '@volar/language-hub@0.0.1': {} + '@typescript-native-bridge/darwin-arm64@6.0.3-bridge.15.tsgo.7.0.2': + optional: true - '@volar/language-server@2.4.20': - dependencies: - '@volar/language-core': 2.4.20 - '@volar/language-service': 2.4.20 - '@volar/typescript': 2.4.20 - path-browserify: 1.0.1 - request-light: 0.7.0 - vscode-languageserver: 9.0.1 - vscode-languageserver-protocol: 3.17.5 - vscode-languageserver-textdocument: 1.0.12 - vscode-uri: 3.1.0 + '@typescript-native-bridge/darwin-x64@6.0.3-bridge.15.tsgo.7.0.2': + optional: true - '@volar/language-service@2.4.20': - dependencies: - '@volar/language-core': 2.4.20 - vscode-languageserver-protocol: 3.17.5 - vscode-languageserver-textdocument: 1.0.12 - vscode-uri: 3.1.0 + '@typescript-native-bridge/linux-arm64@6.0.3-bridge.15.tsgo.7.0.2': + optional: true - '@volar/language-service@2.4.28': - dependencies: - '@volar/language-core': 2.4.28 - vscode-languageserver-protocol: 3.17.5 - vscode-languageserver-textdocument: 1.0.12 - vscode-uri: 3.1.0 + '@typescript-native-bridge/linux-arm@6.0.3-bridge.15.tsgo.7.0.2': + optional: true - '@volar/source-map@2.4.20': {} + '@typescript-native-bridge/linux-x64@6.0.3-bridge.15.tsgo.7.0.2': + optional: true - '@volar/source-map@2.4.28': {} + '@typescript-native-bridge/win32-arm64@6.0.3-bridge.15.tsgo.7.0.2': + optional: true - '@volar/typescript@2.4.20': - dependencies: - '@volar/language-core': 2.4.20 - path-browserify: 1.0.1 - vscode-uri: 3.1.0 + '@typescript-native-bridge/win32-x64@6.0.3-bridge.15.tsgo.7.0.2': + optional: true - '@volar/typescript@2.4.28': + '@typespec/ts-http-runtime@0.3.5': dependencies: - '@volar/language-core': 2.4.28 - path-browserify: 1.0.1 - vscode-uri: 3.1.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + tslib: 2.8.1 + transitivePeerDependencies: + - supports-color '@vscode/vsce-sign-alpine-arm64@2.0.6': optional: true @@ -4652,84 +4107,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@vue-vine/compiler@1.7.27': - dependencies: - '@babel/parser': 7.29.2 - '@babel/types': 7.29.0 - '@vue/compiler-dom': 3.5.33 - '@vue/compiler-ssr': 3.5.33 - '@vue/shared': 3.5.33 - get-tsconfig: 4.14.0 - hash-sum: 2.0.0 - line-column: 1.0.2 - lru-cache: 11.3.5 - magic-string: 0.30.21 - merge-source-map: 1.1.0 - postcss: 8.5.10 - postcss-selector-parser: 7.1.1 - ts-morph: 26.0.0 - - '@vue-vine/language-service@1.7.27(typescript@6.0.3)': - dependencies: - '@umami/node': 0.4.0 - '@volar/language-core': 2.4.20 - '@volar/language-server': 2.4.20 - '@volar/typescript': 2.4.20 - '@vue-vine/compiler': 1.7.27 - '@vue/language-core': 3.0.3(typescript@6.0.3) - '@vue/shared': 3.5.33 - muggle-string: 0.4.1 - transitivePeerDependencies: - - typescript - - '@vue/compiler-core@3.5.33': - dependencies: - '@babel/parser': 7.29.2 - '@vue/shared': 3.5.33 - entities: 7.0.1 - estree-walker: 2.0.2 - source-map-js: 1.2.1 - - '@vue/compiler-dom@3.5.33': - dependencies: - '@vue/compiler-core': 3.5.33 - '@vue/shared': 3.5.33 - - '@vue/compiler-ssr@3.5.33': - dependencies: - '@vue/compiler-dom': 3.5.33 - '@vue/shared': 3.5.33 - - '@vue/compiler-vue2@2.7.16': - dependencies: - de-indent: 1.0.2 - he: 1.2.0 - - '@vue/language-core@3.0.3(typescript@6.0.3)': - dependencies: - '@volar/language-core': 2.4.20 - '@vue/compiler-dom': 3.5.33 - '@vue/compiler-vue2': 2.7.16 - '@vue/shared': 3.5.33 - alien-signals: 2.0.8 - muggle-string: 0.4.1 - path-browserify: 1.0.1 - picomatch: 4.0.4 - optionalDependencies: - typescript: 6.0.3 - - '@vue/language-core@3.3.5': - dependencies: - '@volar/language-core': 2.4.28 - '@vue/compiler-dom': 3.5.33 - '@vue/shared': 3.5.33 - alien-signals: 3.2.1 - muggle-string: 0.4.1 - path-browserify: 1.0.1 - picomatch: 4.0.4 - - '@vue/shared@3.5.33': {} - abbrev@4.0.0: {} acorn-jsx@5.3.2(acorn@8.16.0): @@ -4754,10 +4131,6 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - alien-signals@2.0.8: {} - - alien-signals@3.2.1: {} - ansi-escapes@7.3.0: dependencies: environment: 1.1.0 @@ -4793,8 +4166,6 @@ snapshots: tunnel: 0.0.6 typed-rest-client: 1.8.11 - bail@2.0.2: {} - balanced-match@1.0.2: {} balanced-match@4.0.4: {} @@ -4881,8 +4252,6 @@ snapshots: callsites@3.1.0: {} - ccount@2.0.1: {} - chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -4896,14 +4265,6 @@ snapshots: chalk@5.6.2: {} - character-entities-html4@2.1.0: {} - - character-entities-legacy@3.0.0: {} - - character-entities@2.0.2: {} - - character-reference-invalid@2.0.1: {} - cheerio-select@2.1.0: dependencies: boolbase: 1.0.0 @@ -4946,8 +4307,6 @@ snapshots: cockatiel@3.2.1: {} - code-block-writer@13.0.3: {} - color-convert@1.9.3: dependencies: color-name: 1.1.3 @@ -5040,16 +4399,10 @@ snapshots: cssesc@3.0.0: {} - de-indent@1.0.2: {} - debug@4.4.3: dependencies: ms: 2.1.3 - decode-named-character-reference@1.3.0: - dependencies: - character-entities: 2.0.2 - decompress-response@6.0.0: dependencies: mimic-response: 3.1.0 @@ -5071,17 +4424,11 @@ snapshots: delayed-stream@1.0.0: {} - dequal@2.0.3: {} - detect-indent@7.0.2: {} detect-libc@2.1.2: optional: true - devlop@1.1.0: - dependencies: - dequal: 2.0.3 - diff@4.0.4: {} dom-serializer@2.0.0: @@ -5364,24 +4711,6 @@ snapshots: estraverse@5.3.0: {} - estree-util-is-identifier-name@3.0.0: {} - - estree-util-scope@1.0.0: - dependencies: - '@types/estree': 1.0.8 - devlop: 1.1.0 - - estree-util-visit@2.0.0: - dependencies: - '@types/estree-jsx': 1.0.5 - '@types/unist': 3.0.3 - - estree-walker@2.0.2: {} - - estree-walker@3.0.3: - dependencies: - '@types/estree': 1.0.8 - esutils@2.0.3: {} expand-template@2.0.3: @@ -5389,8 +4718,6 @@ snapshots: exponential-backoff@3.1.3: {} - extend@3.0.2: {} - fast-content-type-parse@3.0.0: {} fast-deep-equal@3.1.3: {} @@ -5503,10 +4830,6 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 - get-tsconfig@4.14.0: - dependencies: - resolve-pkg-maps: 1.0.0 - git-up@8.1.1: dependencies: is-ssh: 1.4.1 @@ -5580,14 +4903,10 @@ snapshots: dependencies: has-symbols: 1.1.0 - hash-sum@2.0.0: {} - hasown@2.0.3: dependencies: function-bind: 1.1.2 - he@1.2.0: {} - hosted-git-info@4.1.0: dependencies: lru-cache: 6.0.0 @@ -5675,19 +4994,10 @@ snapshots: ip-address@10.1.0: {} - is-alphabetical@2.0.1: {} - - is-alphanumerical@2.0.1: - dependencies: - is-alphabetical: 2.0.1 - is-decimal: 2.0.1 - is-core-module@2.16.1: dependencies: hasown: 2.0.3 - is-decimal@2.0.1: {} - is-docker@3.0.0: {} is-extglob@2.1.1: {} @@ -5698,8 +5008,6 @@ snapshots: dependencies: is-extglob: 2.1.1 - is-hexadecimal@2.0.1: {} - is-inside-container@1.0.0: dependencies: is-docker: 3.0.0 @@ -5718,23 +5026,18 @@ snapshots: dependencies: is-inside-container: 1.0.0 - isarray@1.0.0: {} - isexe@2.0.0: {} isexe@4.0.0: {} - isobject@2.1.0: - dependencies: - isarray: 1.0.0 - istextorbinary@9.5.0: dependencies: binaryextensions: 6.11.0 editions: 6.22.0 textextensions: 6.11.0 - jiti@2.6.1: {} + jiti@2.6.1: + optional: true js-tokens@4.0.0: {} @@ -5840,11 +5143,6 @@ snapshots: lilconfig@3.1.3: {} - line-column@1.0.2: - dependencies: - isarray: 1.0.0 - isobject: 2.1.0 - linkify-it@5.0.0: dependencies: uc.micro: 2.1.0 @@ -5877,8 +5175,6 @@ snapshots: lodash@4.18.1: {} - longest-streak@3.1.0: {} - lru-cache@10.4.3: {} lru-cache@11.3.5: {} @@ -5887,10 +5183,6 @@ snapshots: dependencies: yallist: 4.0.0 - magic-string@0.30.21: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - make-fetch-happen@15.0.5: dependencies: '@gar/promise-retry': 1.0.3 @@ -5919,309 +5211,12 @@ snapshots: math-intrinsics@1.1.0: {} - mdast-util-from-markdown@2.0.3: - dependencies: - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - decode-named-character-reference: 1.3.0 - devlop: 1.1.0 - mdast-util-to-string: 4.0.0 - micromark: 4.0.2 - micromark-util-decode-numeric-character-reference: 2.0.2 - micromark-util-decode-string: 2.0.1 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - unist-util-stringify-position: 4.0.0 - transitivePeerDependencies: - - supports-color - - mdast-util-mdx-expression@2.0.1: - dependencies: - '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-mdx-jsx@3.2.0: - dependencies: - '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - ccount: 2.0.1 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 - mdast-util-to-markdown: 2.1.2 - parse-entities: 4.0.2 - stringify-entities: 4.0.4 - unist-util-stringify-position: 4.0.0 - vfile-message: 4.0.3 - transitivePeerDependencies: - - supports-color - - mdast-util-mdx@3.0.0: - dependencies: - mdast-util-from-markdown: 2.0.3 - mdast-util-mdx-expression: 2.0.1 - mdast-util-mdx-jsx: 3.2.0 - mdast-util-mdxjs-esm: 2.0.1 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-mdxjs-esm@2.0.1: - dependencies: - '@types/estree-jsx': 1.0.5 - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - devlop: 1.1.0 - mdast-util-from-markdown: 2.0.3 - mdast-util-to-markdown: 2.1.2 - transitivePeerDependencies: - - supports-color - - mdast-util-phrasing@4.1.0: - dependencies: - '@types/mdast': 4.0.4 - unist-util-is: 6.0.1 - - mdast-util-to-markdown@2.1.2: - dependencies: - '@types/mdast': 4.0.4 - '@types/unist': 3.0.3 - longest-streak: 3.1.0 - mdast-util-phrasing: 4.1.0 - mdast-util-to-string: 4.0.0 - micromark-util-classify-character: 2.0.1 - micromark-util-decode-string: 2.0.1 - unist-util-visit: 5.1.0 - zwitch: 2.0.4 - - mdast-util-to-string@4.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdurl@2.0.0: {} meow@13.2.0: {} - merge-source-map@1.1.0: - dependencies: - source-map: 0.6.1 - merge2@1.4.1: {} - micromark-core-commonmark@2.0.3: - dependencies: - decode-named-character-reference: 1.3.0 - devlop: 1.1.0 - micromark-factory-destination: 2.0.1 - micromark-factory-label: 2.0.1 - micromark-factory-space: 2.0.1 - micromark-factory-title: 2.0.1 - micromark-factory-whitespace: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-chunked: 2.0.1 - micromark-util-classify-character: 2.0.1 - micromark-util-html-tag-name: 2.0.1 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-resolve-all: 2.0.1 - micromark-util-subtokenize: 2.1.0 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-mdx-expression@3.0.1: - dependencies: - '@types/estree': 1.0.8 - devlop: 1.1.0 - micromark-factory-mdx-expression: 2.0.3 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-events-to-acorn: 2.0.3 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-extension-mdx-jsx@3.0.2: - dependencies: - '@types/estree': 1.0.8 - devlop: 1.1.0 - estree-util-is-identifier-name: 3.0.0 - micromark-factory-mdx-expression: 2.0.3 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-events-to-acorn: 2.0.3 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - vfile-message: 4.0.3 - - micromark-extension-mdx-md@2.0.0: - dependencies: - micromark-util-types: 2.0.2 - - micromark-extension-mdxjs-esm@3.0.0: - dependencies: - '@types/estree': 1.0.8 - devlop: 1.1.0 - micromark-core-commonmark: 2.0.3 - micromark-util-character: 2.1.1 - micromark-util-events-to-acorn: 2.0.3 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - unist-util-position-from-estree: 2.0.0 - vfile-message: 4.0.3 - - micromark-extension-mdxjs@3.0.0: - dependencies: - acorn: 8.16.0 - acorn-jsx: 5.3.2(acorn@8.16.0) - micromark-extension-mdx-expression: 3.0.1 - micromark-extension-mdx-jsx: 3.0.2 - micromark-extension-mdx-md: 2.0.0 - micromark-extension-mdxjs-esm: 3.0.0 - micromark-util-combine-extensions: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-factory-destination@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-factory-label@2.0.1: - dependencies: - devlop: 1.1.0 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-factory-mdx-expression@2.0.3: - dependencies: - '@types/estree': 1.0.8 - devlop: 1.1.0 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-events-to-acorn: 2.0.3 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - unist-util-position-from-estree: 2.0.0 - vfile-message: 4.0.3 - - micromark-factory-space@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-types: 2.0.2 - - micromark-factory-title@2.0.1: - dependencies: - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-factory-whitespace@2.0.1: - dependencies: - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-character@2.1.1: - dependencies: - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-chunked@2.0.1: - dependencies: - micromark-util-symbol: 2.0.1 - - micromark-util-classify-character@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-combine-extensions@2.0.1: - dependencies: - micromark-util-chunked: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-decode-numeric-character-reference@2.0.2: - dependencies: - micromark-util-symbol: 2.0.1 - - micromark-util-decode-string@2.0.1: - dependencies: - decode-named-character-reference: 1.3.0 - micromark-util-character: 2.1.1 - micromark-util-decode-numeric-character-reference: 2.0.2 - micromark-util-symbol: 2.0.1 - - micromark-util-encode@2.0.1: {} - - micromark-util-events-to-acorn@2.0.3: - dependencies: - '@types/estree': 1.0.8 - '@types/unist': 3.0.3 - devlop: 1.1.0 - estree-util-visit: 2.0.0 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - vfile-message: 4.0.3 - - micromark-util-html-tag-name@2.0.1: {} - - micromark-util-normalize-identifier@2.0.1: - dependencies: - micromark-util-symbol: 2.0.1 - - micromark-util-resolve-all@2.0.1: - dependencies: - micromark-util-types: 2.0.2 - - micromark-util-sanitize-uri@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-encode: 2.0.1 - micromark-util-symbol: 2.0.1 - - micromark-util-subtokenize@2.1.0: - dependencies: - devlop: 1.1.0 - micromark-util-chunked: 2.0.1 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-symbol@2.0.1: {} - - micromark-util-types@2.0.2: {} - - micromark@4.0.2: - dependencies: - '@types/debug': 4.1.13 - debug: 4.4.3 - decode-named-character-reference: 1.3.0 - devlop: 1.1.0 - micromark-core-commonmark: 2.0.3 - micromark-factory-space: 2.0.1 - micromark-util-character: 2.1.1 - micromark-util-chunked: 2.0.1 - micromark-util-combine-extensions: 2.0.1 - micromark-util-decode-numeric-character-reference: 2.0.2 - micromark-util-encode: 2.0.1 - micromark-util-normalize-identifier: 2.0.1 - micromark-util-resolve-all: 2.0.1 - micromark-util-sanitize-uri: 2.0.1 - micromark-util-subtokenize: 2.1.0 - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - transitivePeerDependencies: - - supports-color - micromatch@4.0.8: dependencies: braces: 3.0.3 @@ -6291,14 +5286,10 @@ snapshots: ms@2.1.3: {} - muggle-string@0.4.1: {} - mute-stream@0.0.8: {} mute-stream@3.0.0: {} - nanoid@3.3.11: {} - napi-build-utils@2.0.0: optional: true @@ -6470,16 +5461,6 @@ snapshots: just-diff: 6.0.2 just-diff-apply: 5.5.0 - parse-entities@4.0.2: - dependencies: - '@types/unist': 2.0.11 - character-entities-legacy: 3.0.0 - character-reference-invalid: 2.0.1 - decode-named-character-reference: 1.3.0 - is-alphanumerical: 2.0.1 - is-decimal: 2.0.1 - is-hexadecimal: 2.0.1 - parse-json@8.3.0: dependencies: '@babel/code-frame': 7.29.0 @@ -6512,8 +5493,6 @@ snapshots: dependencies: entities: 6.0.1 - path-browserify@1.0.1: {} - path-exists@4.0.0: {} path-is-absolute@1.0.1: {} @@ -6550,12 +5529,6 @@ snapshots: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss@8.5.10: - dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - prebuild-install@7.1.3: dependencies: detect-libc: 2.1.2 @@ -6638,24 +5611,6 @@ snapshots: util-deprecate: 1.0.2 optional: true - remark-mdx@3.1.1: - dependencies: - mdast-util-mdx: 3.0.0 - micromark-extension-mdxjs: 3.0.0 - transitivePeerDependencies: - - supports-color - - remark-parse@11.0.0: - dependencies: - '@types/mdast': 4.0.4 - mdast-util-from-markdown: 2.0.3 - micromark-util-types: 2.0.2 - unified: 11.0.5 - transitivePeerDependencies: - - supports-color - - request-light@0.7.0: {} - require-from-string@2.0.2: {} resolve-cwd@3.0.0: @@ -6666,8 +5621,6 @@ snapshots: resolve-from@5.0.0: {} - resolve-pkg-maps@1.0.0: {} - resolve@1.22.12: dependencies: es-errors: 1.3.0 @@ -6677,13 +5630,6 @@ snapshots: reusify@1.1.0: {} - rollup-utils@0.0.5: - dependencies: - '@types/estree': 1.0.8 - estree-walker: 2.0.2 - path-browserify: 1.0.1 - picomatch: 4.0.4 - run-applescript@7.1.0: {} run-parallel@1.2.0: @@ -6798,8 +5744,6 @@ snapshots: dependencies: is-plain-obj: 4.1.0 - source-map-js@1.2.1: {} - source-map@0.6.1: {} spdx-correct@3.2.0: @@ -6848,11 +5792,6 @@ snapshots: safe-buffer: 5.2.1 optional: true - stringify-entities@4.0.4: - dependencies: - character-entities-html4: 2.1.0 - character-entities-legacy: 3.0.0 - strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -6944,21 +5883,10 @@ snapshots: treeverse@3.0.0: {} - trough@2.2.0: {} - ts-api-utils@2.5.0(typescript@6.0.3): dependencies: typescript: 6.0.3 - ts-macro@0.3.7: - dependencies: - muggle-string: 0.4.1 - - ts-morph@26.0.0: - dependencies: - '@ts-morph/common': 0.27.0 - code-block-writer: 13.0.3 - ts-pattern@5.9.0: {} tsl@1.0.30(typescript@6.0.3): @@ -7020,6 +5948,16 @@ snapshots: tunnel: 0.0.6 underscore: 1.13.8 + typescript-native-bridge@6.0.3-bridge.15.tsgo.7.0.2: + optionalDependencies: + '@typescript-native-bridge/darwin-arm64': 6.0.3-bridge.15.tsgo.7.0.2 + '@typescript-native-bridge/darwin-x64': 6.0.3-bridge.15.tsgo.7.0.2 + '@typescript-native-bridge/linux-arm': 6.0.3-bridge.15.tsgo.7.0.2 + '@typescript-native-bridge/linux-arm64': 6.0.3-bridge.15.tsgo.7.0.2 + '@typescript-native-bridge/linux-x64': 6.0.3-bridge.15.tsgo.7.0.2 + '@typescript-native-bridge/win32-arm64': 6.0.3-bridge.15.tsgo.7.0.2 + '@typescript-native-bridge/win32-x64': 6.0.3-bridge.15.tsgo.7.0.2 + typescript@6.0.3: {} uc.micro@2.1.0: {} @@ -7039,44 +5977,6 @@ snapshots: unicorn-magic@0.3.0: {} - unified@11.0.5: - dependencies: - '@types/unist': 3.0.3 - bail: 2.0.2 - devlop: 1.1.0 - extend: 3.0.2 - is-plain-obj: 4.1.0 - trough: 2.2.0 - vfile: 6.0.3 - - unist-util-is@6.0.1: - dependencies: - '@types/unist': 3.0.3 - - unist-util-lsp@2.1.0: - dependencies: - '@types/unist': 3.0.3 - vscode-languageserver-types: 3.17.5 - - unist-util-position-from-estree@2.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-stringify-position@4.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-visit-parents@6.0.2: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.1 - - unist-util-visit@5.1.0: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.1 - unist-util-visit-parents: 6.0.2 - universal-user-agent@7.0.3: {} universalify@2.0.1: {} @@ -7100,33 +6000,6 @@ snapshots: version-range@4.15.0: {} - vfile-message@4.0.3: - dependencies: - '@types/unist': 3.0.3 - unist-util-stringify-position: 4.0.0 - - vfile@6.0.3: - dependencies: - '@types/unist': 3.0.3 - vfile-message: 4.0.3 - - vscode-jsonrpc@8.2.0: {} - - vscode-languageserver-protocol@3.17.5: - dependencies: - vscode-jsonrpc: 8.2.0 - vscode-languageserver-types: 3.17.5 - - vscode-languageserver-textdocument@1.0.12: {} - - vscode-languageserver-types@3.17.5: {} - - vscode-languageserver@9.0.1: - dependencies: - vscode-languageserver-protocol: 3.17.5 - - vscode-uri@3.1.0: {} - walk-up-path@4.0.0: {} whatwg-encoding@3.1.1: @@ -7188,8 +6061,6 @@ snapshots: yallist@5.0.0: {} - yaml@2.8.3: {} - yaml@2.9.0: {} yargs-parser@22.0.0: {} @@ -7220,5 +6091,3 @@ snapshots: graphmatch: 1.1.1 zod@4.3.6: {} - - zwitch@2.0.4: {} From 99bb697c0bb3ae3625cf11e608ae4e1dbef38a16 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Wed, 2 Sep 2026 17:45:39 +0800 Subject: [PATCH 02/52] Remove languagePlugins.ts from dogfood corpus The file was deleted with the Volar wiring; the hardcoded corpus list in packages/compat-eslint/test/bench/dogfood-corpus.ts still referenced it, failing lazy-estree.test.js with ENOENT in CI. --- packages/compat-eslint/test/bench/dogfood-corpus.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/compat-eslint/test/bench/dogfood-corpus.ts b/packages/compat-eslint/test/bench/dogfood-corpus.ts index 1bdf17fa..5491a73e 100644 --- a/packages/compat-eslint/test/bench/dogfood-corpus.ts +++ b/packages/compat-eslint/test/bench/dogfood-corpus.ts @@ -13,7 +13,6 @@ export const DOGFOOD_FILES = [ 'packages/cli/lib/cache.ts', 'packages/cli/lib/colors.ts', 'packages/cli/lib/fs-cache.ts', - 'packages/cli/lib/languagePlugins.ts', 'packages/cli/lib/render.ts', 'packages/cli/lib/worker.ts', 'packages/compat-eslint/index.ts', From c28e3582f959d87dc214149a9e7b7f048d4059b8 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 10 Sep 2026 07:07:34 +0800 Subject: [PATCH 03/52] Bump typescript-native-bridge to bridge.16, drop leftover languages plumbing - packages/cli now pins typescript-native-bridge 6.0.3-bridge.16.tsgo.7.0.2 - remove the languages parameter from the cache layer (dead since the Volar removal); the cache key no longer carries it, which also invalidates existing caches --- packages/cli/index.ts | 8 ++-- packages/cli/lib/cache.ts | 10 ++--- packages/cli/package.json | 2 +- packages/cli/test/cache.test.ts | 42 ++++++++++----------- pnpm-lock.yaml | 66 ++++++++++++++++----------------- 5 files changed, 61 insertions(+), 67 deletions(-) diff --git a/packages/cli/index.ts b/packages/cli/index.ts index 0f3e55d7..954c96d4 100644 --- a/packages/cli/index.ts +++ b/packages/cli/index.ts @@ -121,14 +121,13 @@ class Project { }`; // Load layer-1 cache unless --force was passed. The cache file path - // key includes tsslint version, TS version, tsconfig, languages, - // and configFile mtime+size — anything that changes the rule set or - // the toolchain mints a fresh file. See packages/cli/lib/cache.ts. + // key includes tsslint version, TS version, tsconfig, and configFile + // mtime+size — anything that changes the rule set or the toolchain + // mints a fresh file. See packages/cli/lib/cache.ts. if (!process.argv.includes('--force')) { this.cacheData = cache.loadCache( this.tsconfig, this.configFile, - [], ts.version, ts.sys.createHash, ); @@ -528,7 +527,6 @@ const formatHost: ts.FormatDiagnosticsHost = { cache.saveCache( project.tsconfig, project.configFile!, - [], ts.version, project.cacheData, ts.sys.createHash, diff --git a/packages/cli/lib/cache.ts b/packages/cli/lib/cache.ts index 804a8df4..aa61c7b6 100644 --- a/packages/cli/lib/cache.ts +++ b/packages/cli/lib/cache.ts @@ -7,7 +7,7 @@ // - tsslint version (segment) → bumping the package invalidates everything // - typescript version (segment) → .tsbuildinfo format / TS internals can // change across TS majors, so segregate per TS version -// - hash of (configFilePath, tsconfig, languages, configFile mtime+size) +// - hash of (configFilePath, tsconfig, configFile mtime+size) // → editing tsslint.config.ts mints a fresh cache file // // Cache file shape — see packages/cli/CACHE.md "Cache file format" section. @@ -72,11 +72,10 @@ export type SerializedRelatedInfo = Omit string = defaultHash, ): CacheData { - const filePath = getCacheFilePath(tsconfig, configFilePath, languages, tsVersion, createHash); + const filePath = getCacheFilePath(tsconfig, configFilePath, tsVersion, createHash); if (!fs.statSync(filePath, { throwIfNoEntry: false })?.isFile()) { return emptyCache(); } @@ -106,12 +105,11 @@ export function loadCache( export function saveCache( tsconfig: string, configFilePath: string, - languages: string[], tsVersion: string, cache: CacheData, createHash: (s: string) => string = defaultHash, ): void { - const filePath = getCacheFilePath(tsconfig, configFilePath, languages, tsVersion, createHash); + const filePath = getCacheFilePath(tsconfig, configFilePath, tsVersion, createHash); fs.mkdirSync(path.dirname(filePath), { recursive: true }); const tmpPath = filePath + '.tmp'; fs.writeFileSync(tmpPath, JSON.stringify(cache)); @@ -154,7 +152,6 @@ function isCacheData(x: unknown): x is CacheData { function getCacheFilePath( tsconfig: string, configFilePath: string, - languages: string[], tsVersion: string, createHash: (s: string) => string, ): string { @@ -162,7 +159,6 @@ function getCacheFilePath( const cacheKey = [ configFilePath, tsconfig, - languages.sort().join(','), configStat?.mtimeMs ?? 0, configStat?.size ?? 0, ].join('\0'); diff --git a/packages/cli/package.json b/packages/cli/package.json index d1652eaf..a9023b6b 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -25,6 +25,6 @@ "@tsslint/core": "3.1.4", "@tsslint/types": "3.1.4", "minimatch": "^10.0.1", - "typescript-native-bridge": "6.0.3-bridge.15.tsgo.7.0.2" + "typescript-native-bridge": "6.0.3-bridge.16.tsgo.7.0.2" } } diff --git a/packages/cli/test/cache.test.ts b/packages/cli/test/cache.test.ts index ca0e8d29..5133a6d9 100644 --- a/packages/cli/test/cache.test.ts +++ b/packages/cli/test/cache.test.ts @@ -49,7 +49,7 @@ function configWithMarker(marker: string): string { { const tsconfig = tmpFile('tsconfig1.json', '{}'); const config = configWithMarker('t1-' + Date.now()); - const data = cache.loadCache(tsconfig, config, [], '6.0.0'); + const data = cache.loadCache(tsconfig, config, '6.0.0'); check('empty cache when file missing', data.files !== undefined && Object.keys(data.files).length === 0); check('ruleModes empty', Object.keys(data.ruleModes).length === 0); check('version present', typeof data.version === 'string'); @@ -71,8 +71,8 @@ function configWithMarker(marker: string): string { }, }, }; - cache.saveCache(tsconfig, config, [], '6.0.0', original); - const loaded = cache.loadCache(tsconfig, config, [], '6.0.0'); + cache.saveCache(tsconfig, config, '6.0.0', original); + const loaded = cache.loadCache(tsconfig, config, '6.0.0'); check('round-trip: ruleModes preserved', loaded.ruleModes['no-undef'] === 'type-aware'); check('round-trip: file mtime preserved', loaded.files['/abs/foo.ts']?.mtime === 1234567890); check('round-trip: rule entry preserved', !!loaded.files['/abs/foo.ts']?.rules['semi']); @@ -82,7 +82,7 @@ function configWithMarker(marker: string): string { { const tsconfig = tmpFile('tsconfig3.json', '{}'); const config = configWithMarker('t3-' + Date.now()); - cache.saveCache(tsconfig, config, [], '6.0.0', cache.emptyCache()); + cache.saveCache(tsconfig, config, '6.0.0', cache.emptyCache()); // Find the cache file and corrupt it. const cacheRoot = path.join(os.tmpdir(), 'tsslint-cache'); @@ -123,7 +123,7 @@ function configWithMarker(marker: string): string { check('found newly-written cache file', !!cacheFile); if (cacheFile) { fs.writeFileSync(cacheFile, '{not valid json'); - const loaded = cache.loadCache(tsconfig, config, [], '6.0.0'); + const loaded = cache.loadCache(tsconfig, config, '6.0.0'); check('corrupted JSON → empty cache', Object.keys(loaded.files).length === 0); } } @@ -138,8 +138,8 @@ function configWithMarker(marker: string): string { files: { '/x': { mtime: 1, rules: {} } }, }; // Manually write a stale-version file - cache.saveCache(tsconfig, config, [], '6.0.0', stale as any); - const loaded = cache.loadCache(tsconfig, config, [], '6.0.0'); + cache.saveCache(tsconfig, config, '6.0.0', stale as any); + const loaded = cache.loadCache(tsconfig, config, '6.0.0'); check('version mismatch → empty', Object.keys(loaded.ruleModes).length === 0); } @@ -147,7 +147,7 @@ function configWithMarker(marker: string): string { { const tsconfig = tmpFile('tsconfig5.json', '{}'); const config = configWithMarker('t5-' + Date.now()); - cache.saveCache(tsconfig, config, [], '6.0.0', cache.emptyCache()); + cache.saveCache(tsconfig, config, '6.0.0', cache.emptyCache()); // Corrupt to a valid-JSON-but-wrong-shape value. Find the file we // just wrote (heuristic: empty cache contents). const cacheRoot = path.join(os.tmpdir(), 'tsslint-cache'); @@ -186,7 +186,7 @@ function configWithMarker(marker: string): string { } if (target) { fs.writeFileSync(target, '"just a string"'); - const loaded = cache.loadCache(tsconfig, config, [], '6.0.0'); + const loaded = cache.loadCache(tsconfig, config, '6.0.0'); check('shape mismatch → empty', Object.keys(loaded.files).length === 0); } } @@ -200,9 +200,9 @@ function configWithMarker(marker: string): string { ruleModes: { 'rule6a': 'type-aware' }, files: {}, }; - cache.saveCache(tsconfig, config, [], '6.0.0', data); - const loadedSameVersion = cache.loadCache(tsconfig, config, [], '6.0.0'); - const loadedDifferentVersion = cache.loadCache(tsconfig, config, [], '7.0.0'); + cache.saveCache(tsconfig, config, '6.0.0', data); + const loadedSameVersion = cache.loadCache(tsconfig, config, '6.0.0'); + const loadedDifferentVersion = cache.loadCache(tsconfig, config, '7.0.0'); check( 'same ts version finds the cache', @@ -227,7 +227,7 @@ function configWithMarker(marker: string): string { ruleModes: { 'rule7': 'type-aware' }, files: {}, }; - cache.saveCache(tsconfig, config, [], '6.0.0', data); + cache.saveCache(tsconfig, config, '6.0.0', data); // Bump config size + mtime. fs.writeFileSync(config, '// changed content'); @@ -236,7 +236,7 @@ function configWithMarker(marker: string): string { const newTime = new Date(Date.now() + 60_000); fs.utimesSync(config, newTime, newTime); - const loaded = cache.loadCache(tsconfig, config, [], '6.0.0'); + const loaded = cache.loadCache(tsconfig, config, '6.0.0'); check( 'config edit invalidates cache (different path key)', !loaded.ruleModes['rule7'], @@ -252,7 +252,7 @@ function configWithMarker(marker: string): string { { const tsconfig = tmpFile('tsconfig8.json', '{}'); const config = configWithMarker('t8-' + Date.now()); - cache.saveCache(tsconfig, config, [], '6.0.0', cache.emptyCache()); + cache.saveCache(tsconfig, config, '6.0.0', cache.emptyCache()); const cacheRoot = path.join(os.tmpdir(), 'tsslint-cache'); let tmpLeaked = false; @@ -283,7 +283,7 @@ function configWithMarker(marker: string): string { { const tsconfig = tmpFile('tsconfig9.json', '{}'); const config = configWithMarker('t9-' + Date.now()); - cache.saveCache(tsconfig, config, [], '6.0.0', cache.emptyCache()); + cache.saveCache(tsconfig, config, '6.0.0', cache.emptyCache()); const cacheRoot = path.join(os.tmpdir(), 'tsslint-cache'); let target: string | null = null; const stack = [cacheRoot]; @@ -320,7 +320,7 @@ function configWithMarker(marker: string): string { files: { '/x': { mtime: 'not-a-number', rules: {} } }, }; fs.writeFileSync(target, JSON.stringify(corrupt)); - const loaded = cache.loadCache(tsconfig, config, [], '6.0.0'); + const loaded = cache.loadCache(tsconfig, config, '6.0.0'); check('inner mtime mismatch → empty cache', Object.keys(loaded.files).length === 0); // rules is null — must also reject. @@ -330,7 +330,7 @@ function configWithMarker(marker: string): string { files: { '/x': { mtime: 1, rules: null } }, }; fs.writeFileSync(target, JSON.stringify(corrupt2)); - const loaded2 = cache.loadCache(tsconfig, config, [], '6.0.0'); + const loaded2 = cache.loadCache(tsconfig, config, '6.0.0'); check('inner rules null → empty cache', Object.keys(loaded2.files).length === 0); } } @@ -344,7 +344,7 @@ function configWithMarker(marker: string): string { { const tsconfig = tmpFile('tsconfig10.json', '{}'); const config = configWithMarker('t10-' + Date.now()); - cache.saveCache(tsconfig, config, [], '6.0.0', cache.emptyCache()); + cache.saveCache(tsconfig, config, '6.0.0', cache.emptyCache()); const cacheRoot = path.join(os.tmpdir(), 'tsslint-cache'); let target: string | null = null; const stack = [cacheRoot]; @@ -376,7 +376,7 @@ function configWithMarker(marker: string): string { if (target) { const corrupt = { version: 'v2', ruleModes: {}, files: {}, incrementalState: 42 }; fs.writeFileSync(target, JSON.stringify(corrupt)); - const loaded = cache.loadCache(tsconfig, config, [], '6.0.0'); + const loaded = cache.loadCache(tsconfig, config, '6.0.0'); check('incrementalState wrong type → empty cache', loaded.incrementalState === undefined); const corrupt2 = { @@ -386,7 +386,7 @@ function configWithMarker(marker: string): string { incrementalState: { version: 'v3' /* tsBuildInfoText missing */ }, }; fs.writeFileSync(target, JSON.stringify(corrupt2)); - const loaded2 = cache.loadCache(tsconfig, config, [], '6.0.0'); + const loaded2 = cache.loadCache(tsconfig, config, '6.0.0'); check('incrementalState missing field → empty cache', loaded2.incrementalState === undefined); } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9256f198..0f15ee0d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -84,8 +84,8 @@ importers: specifier: ^10.0.1 version: 10.2.5 typescript-native-bridge: - specifier: 6.0.3-bridge.15.tsgo.7.0.2 - version: 6.0.3-bridge.15.tsgo.7.0.2 + specifier: 6.0.3-bridge.16.tsgo.7.0.2 + version: 6.0.3-bridge.16.tsgo.7.0.2 packages/compat-eslint: dependencies: @@ -1037,38 +1037,38 @@ packages: resolution: {integrity: sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-native-bridge/darwin-arm64@6.0.3-bridge.15.tsgo.7.0.2': - resolution: {integrity: sha512-nrhPj8nED29uhZH2V7yHXFPy5O8ro5vRMVnChO1CvMf8tDg9tlhlBPFeeYgvp001upBRZu8JmTYw1wpcRUvDVA==} + '@typescript-native-bridge/darwin-arm64@6.0.3-bridge.16.tsgo.7.0.2': + resolution: {integrity: sha512-D0yL7T3gDXwn4XiRtA1x7bW+0zedMUVG3PnIt+X4lxblnId+Wo2FBvf+2ZLZpPOO3DILGz5bdX+Aq8tqboeGEg==} cpu: [arm64] os: [darwin] - '@typescript-native-bridge/darwin-x64@6.0.3-bridge.15.tsgo.7.0.2': - resolution: {integrity: sha512-JiS2e3W+Vtr8u/oTYeTdCK7S+seB2cBz3l7gQYuon4oTiHS0Mi+YGEYhSG2v2iuRkTMcrVrr9+XazwMcPlIW+Q==} + '@typescript-native-bridge/darwin-x64@6.0.3-bridge.16.tsgo.7.0.2': + resolution: {integrity: sha512-HLPoAVsmkgDec3UZpUPI4hquvA/X/qZ71w4dQXP7LbsN+5ROpum7gWx9S0ZwVgvoaC4++iu08Qnw1l5gXknyvg==} cpu: [x64] os: [darwin] - '@typescript-native-bridge/linux-arm64@6.0.3-bridge.15.tsgo.7.0.2': - resolution: {integrity: sha512-BFkIGbpdRf+iNK0IHCKUYAvAIQ4tnTRegNU+BLaYUbQ5ER/YGytSzKQqh2c2zls1H+Gc0DyZcDsBIFr0gC6BDg==} + '@typescript-native-bridge/linux-arm64@6.0.3-bridge.16.tsgo.7.0.2': + resolution: {integrity: sha512-tVOhuwktZREiVdfOTgh4K8sLaJZHyXOz5SwtwkMknM3A6tSdYTqQ3s7s/hDkB18l33+ZWBKYfJ69Sge8pGeKJw==} cpu: [arm64] os: [linux] - '@typescript-native-bridge/linux-arm@6.0.3-bridge.15.tsgo.7.0.2': - resolution: {integrity: sha512-VlvPkt0x7ShuPmI58wSsS7VBa9uq4fQtaz82GC8yK1bFP+FdHcLJleM2SyAvHpxyd8nIyKMzcJolY+qffaBRUg==} + '@typescript-native-bridge/linux-arm@6.0.3-bridge.16.tsgo.7.0.2': + resolution: {integrity: sha512-rCqqctyc4801hX2pCVM1kv7rYfBiSi0SGJU+Nv3RQ5C2gBXkShBA39+Vw0bsqrX+Fj+Tre4kuOdSlUnSeDv2GA==} cpu: [arm] os: [linux] - '@typescript-native-bridge/linux-x64@6.0.3-bridge.15.tsgo.7.0.2': - resolution: {integrity: sha512-QB6q8kNk5HO/mc3iyJBx1I7mx2+ObNxK/SgeX18gVydcooVdjLYv9lSDjv/FuA9iYzuhYjfrOuRxuhxKoAOcmQ==} + '@typescript-native-bridge/linux-x64@6.0.3-bridge.16.tsgo.7.0.2': + resolution: {integrity: sha512-WKIuVkh9YuL1udrL0Dp+XvNLaM9maQZMz38ukNn93kwNhSXIXa+62xK69IKdc8osVZU6fDk0ZbIKkcrGkOi51A==} cpu: [x64] os: [linux] - '@typescript-native-bridge/win32-arm64@6.0.3-bridge.15.tsgo.7.0.2': - resolution: {integrity: sha512-ZWY1I/6dsInK3S2ue4sKHj3zp3nmqD+9iqq7LfjELNjjYSUvseYfqv3170zeqZV2PQRzjA3Qtd85pkYr0SQDEQ==} + '@typescript-native-bridge/win32-arm64@6.0.3-bridge.16.tsgo.7.0.2': + resolution: {integrity: sha512-u0/Aq5bkdFcuw463Y6zsYSKRdX1rcTtLkAK1PTwRH70aMkaU6vkr7BSIjvLj3lDCqsySFuoHHuNg5J2pmqxp3A==} cpu: [arm64] os: [win32] - '@typescript-native-bridge/win32-x64@6.0.3-bridge.15.tsgo.7.0.2': - resolution: {integrity: sha512-LqaVPjvllVQm/OfID0z9ubDPEuO6asNOH/qrV1xTGFxgHRRn8UUz3/te4zaERalVRgwNWYlSbzSQb+jttLjvMA==} + '@typescript-native-bridge/win32-x64@6.0.3-bridge.16.tsgo.7.0.2': + resolution: {integrity: sha512-l0OPZ6xD69vQpyfS5yTSwJ5xJTYM2hUKFRYlvyyUSvbFubUz8jQRLcrn+WU4QehV9WkVIQIx8CLgNCI0Zqhmyg==} cpu: [x64] os: [win32] @@ -2782,8 +2782,8 @@ packages: typed-rest-client@1.8.11: resolution: {integrity: sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==} - typescript-native-bridge@6.0.3-bridge.15.tsgo.7.0.2: - resolution: {integrity: sha512-utlvj8zRana8mutK9LgoPLtMcMT/j/0ot5eh/YUJgDSVQUGLIC6m6Dwt1SP820HMJZI3Yg8fXnqFrkDaa2Gk4g==} + typescript-native-bridge@6.0.3-bridge.16.tsgo.7.0.2: + resolution: {integrity: sha512-bO+fh23fh4j0mK2NmlFOrlbR9Ke5cHpjOsDaM2czXlSXqxNchGdDVyAdaVCFz0DofkFOov96HLvQaP3W57F+CA==} engines: {node: '>=20.19'} hasBin: true @@ -4003,25 +4003,25 @@ snapshots: '@typescript-eslint/types': 8.61.1 eslint-visitor-keys: 5.0.1 - '@typescript-native-bridge/darwin-arm64@6.0.3-bridge.15.tsgo.7.0.2': + '@typescript-native-bridge/darwin-arm64@6.0.3-bridge.16.tsgo.7.0.2': optional: true - '@typescript-native-bridge/darwin-x64@6.0.3-bridge.15.tsgo.7.0.2': + '@typescript-native-bridge/darwin-x64@6.0.3-bridge.16.tsgo.7.0.2': optional: true - '@typescript-native-bridge/linux-arm64@6.0.3-bridge.15.tsgo.7.0.2': + '@typescript-native-bridge/linux-arm64@6.0.3-bridge.16.tsgo.7.0.2': optional: true - '@typescript-native-bridge/linux-arm@6.0.3-bridge.15.tsgo.7.0.2': + '@typescript-native-bridge/linux-arm@6.0.3-bridge.16.tsgo.7.0.2': optional: true - '@typescript-native-bridge/linux-x64@6.0.3-bridge.15.tsgo.7.0.2': + '@typescript-native-bridge/linux-x64@6.0.3-bridge.16.tsgo.7.0.2': optional: true - '@typescript-native-bridge/win32-arm64@6.0.3-bridge.15.tsgo.7.0.2': + '@typescript-native-bridge/win32-arm64@6.0.3-bridge.16.tsgo.7.0.2': optional: true - '@typescript-native-bridge/win32-x64@6.0.3-bridge.15.tsgo.7.0.2': + '@typescript-native-bridge/win32-x64@6.0.3-bridge.16.tsgo.7.0.2': optional: true '@typespec/ts-http-runtime@0.3.5': @@ -5948,15 +5948,15 @@ snapshots: tunnel: 0.0.6 underscore: 1.13.8 - typescript-native-bridge@6.0.3-bridge.15.tsgo.7.0.2: + typescript-native-bridge@6.0.3-bridge.16.tsgo.7.0.2: optionalDependencies: - '@typescript-native-bridge/darwin-arm64': 6.0.3-bridge.15.tsgo.7.0.2 - '@typescript-native-bridge/darwin-x64': 6.0.3-bridge.15.tsgo.7.0.2 - '@typescript-native-bridge/linux-arm': 6.0.3-bridge.15.tsgo.7.0.2 - '@typescript-native-bridge/linux-arm64': 6.0.3-bridge.15.tsgo.7.0.2 - '@typescript-native-bridge/linux-x64': 6.0.3-bridge.15.tsgo.7.0.2 - '@typescript-native-bridge/win32-arm64': 6.0.3-bridge.15.tsgo.7.0.2 - '@typescript-native-bridge/win32-x64': 6.0.3-bridge.15.tsgo.7.0.2 + '@typescript-native-bridge/darwin-arm64': 6.0.3-bridge.16.tsgo.7.0.2 + '@typescript-native-bridge/darwin-x64': 6.0.3-bridge.16.tsgo.7.0.2 + '@typescript-native-bridge/linux-arm': 6.0.3-bridge.16.tsgo.7.0.2 + '@typescript-native-bridge/linux-arm64': 6.0.3-bridge.16.tsgo.7.0.2 + '@typescript-native-bridge/linux-x64': 6.0.3-bridge.16.tsgo.7.0.2 + '@typescript-native-bridge/win32-arm64': 6.0.3-bridge.16.tsgo.7.0.2 + '@typescript-native-bridge/win32-x64': 6.0.3-bridge.16.tsgo.7.0.2 typescript@6.0.3: {} From 92f6375f62814777f6788abba86e81636f0318bc Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 10 Sep 2026 07:07:34 +0800 Subject: [PATCH 04/52] docs: v4 migration guide - README: real framework-support section (tsconfig contentMappers), updated requirements (bundled engine, no typescript pin, musl unsupported) - docs/migration-v4.md: breaking changes, flag -> contentMappers mapping, engine behavior notes, editor status, availability gates --- README.md | 21 ++++++++-- docs/migration-v4.md | 91 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 docs/migration-v4.md diff --git a/README.md b/README.md index 82460edf..26091a1c 100644 --- a/README.md +++ b/README.md @@ -212,7 +212,21 @@ TSSLint produces diagnostics and edits — it does not format. Run dprint or Pre ## Framework support -Since v4, meta-framework support is provided by the TS 7.1 content mapper (tsconfig `contentMappers`); see the release notes for the migration guide. +Since v4, framework files (`.vue`, `.astro`, `.mdx`, `.svelte`, …) are checked through TypeScript 7.1's **content mapper** — the same mechanism `tsc` uses — instead of Volar language plugins. Declare the mappers in `tsconfig.json`; TSSLint picks them up with the project: + +```jsonc +{ + "contentMappers": [ + { "package": "vue-content-mapper", "extensions": [".vue"] } + ] +} +``` + +Rules run against the transformed TypeScript, and diagnostics and fixes are mapped back to the original file through the mapper's span map. Fixes are only offered on exact (verbatim) spans — the same restriction `tsc` applies to edits. + +The v3 flags `--vue-project`, `--vue-vine-project`, `--mdx-project`, `--astro-project`, and `--ts-macro-project` are gone; the mapper configuration in `tsconfig.json` replaces them. See [docs/migration-v4.md](docs/migration-v4.md). + +> **Availability**: content mapper support activates once `typescript-native-bridge` moves to tsgo 7.1. Until then the CLI checks plain TypeScript files only. ## Importing ESLint, TSLint, or TSL rules @@ -309,8 +323,9 @@ Build your own with the `Plugin` type from `@tsslint/types`. ## Requirements - Node.js **22.6.0+** (uses `--experimental-strip-types` to load `tsslint.config.ts` directly — no transpile step) -- Any TypeScript version with Language Service Plugin support -- Not compatible with `typescript-go` (v7), which does not yet support Language Service Plugins +- The CLI bundles its own TypeScript engine ([`typescript-native-bridge`](https://github.com/johnsoncodehk/typescript-native-bridge), TS 7 / tsgo) — you no longer install or pin `typescript` to lint +- Not supported on musl-based systems (Alpine) — the bundled engine links a glibc native binary +- `@tsslint/typescript-plugin` still runs against a classic TypeScript tsserver; the TS 7 language server path is being rebuilt (see [docs/migration-v4.md](docs/migration-v4.md)) ## License diff --git a/docs/migration-v4.md b/docs/migration-v4.md new file mode 100644 index 00000000..f621d463 --- /dev/null +++ b/docs/migration-v4.md @@ -0,0 +1,91 @@ +# Migrating to TSSLint v4 + +v4 moves the lint engine onto TypeScript 7 and replaces the Volar-based +framework support with TypeScript's own content mapper. + +## Breaking changes + +### 1. The CLI bundles its own TypeScript engine + +`@tsslint/cli` no longer declares `typescript` as a peer dependency. It depends +on [`typescript-native-bridge`](https://github.com/johnsoncodehk/typescript-native-bridge) +(exact-pinned), which keeps the classic `typescript` API surface but runs the +TS 7 (tsgo) checker in-process. + +- You no longer need to install or pin `typescript` for linting, and package + manager overrides that redirected `typescript` to the bridge are no longer + required for TSSLint itself. +- Diagnostics come from the tsgo checker. On a real project the output can + differ from TS 6.x in edge cases — that difference is the engine, not a + TSSLint regression. +- The bridge does not support custom module resolvers or JS custom transformers. + If your setup remaps imports to different files on disk, those remappings are + not honoured. + +### 2. Framework flags are gone + +The Volar integration — and with it the five project flags — has been removed: + +| v3 | v4 | +|---|---| +| `tsslint --project tsconfig.json --vue-project tsconfig.json` | declare `contentMappers` in `tsconfig.json`, then `tsslint --project tsconfig.json` | +| `--vue-vine-project`, `--mdx-project`, `--astro-project`, `--ts-macro-project` | same — mapper entries in `tsconfig.json` | + +There is no replacement flag; the tsconfig is the single source of truth. + +### 3. Platform requirements + +- Node.js **22.6.0+** (unchanged) — the bridge itself needs ≥ 20.19. +- **musl-based systems (Alpine) are not supported.** The bridge links a glibc + native binary; run the lint step in a glibc image and copy results into your + Alpine deploy stage. +- Peak memory on checker-heavy runs rises to roughly 1.9× the previous engine. + +## Framework projects + +Framework files are checked through TypeScript 7.1's content mapper. Add the +mapper package for your framework to `tsconfig.json`: + +```jsonc +{ + "compilerOptions": { }, + "contentMappers": [ + { "package": "vue-content-mapper", "extensions": [".vue"], "options": { } } + ], + "include": ["src"] +} +``` + +The mapper package declares how it runs in its own `package.json`: + +```jsonc +{ + "typescript": { + "contentMapper": { + "exec": ["node", "dist/server.js"], + "compilerOptions": ["module", "jsx", "jsxImportSource"] + } + } +} +``` + +Notes that affect what you can lint: + +- Rules run on the transformed TypeScript. Diagnostics and fixes are mapped back + to the original file through the mapper's span map; **fixes are only offered + on exact (verbatim) spans**, matching the edit-safety rule `tsc` applies. +- The protocol has no per-diagnostic-code filtering and does not drop diagnostics + on unmapped (synthetic) regions — both are deliberate design choices upstream. +- `tsc` requires `--runExternalCode` to execute mappers; TSSLint runs them as + part of loading the project. + +**Availability**: content mapper support activates once `typescript-native-bridge` +rebases onto tsgo 7.1. Until then the CLI checks plain TypeScript files only. +Mapper packages per framework follow their own ecosystems' adoption. + +## Editors + +`@tsslint/typescript-plugin` still targets a classic TypeScript tsserver and is +unaffected by the CLI change. The TS 7 language server path (LSP middleware plus +content mapper registration) is being rebuilt on the TS 7.1 APIs; until that +lands, editor diagnostics for framework files keep using the previous setup. From 7f38675d01cb168f6caeffa9098804c3b682ca52 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 22 Oct 2026 17:59:03 +0800 Subject: [PATCH 05/52] chore: remove TS server plugin and VS Code extension TypeScript 7 has no tsserver plugin API, so @tsslint/typescript-plugin and the VS Code extension built on it cannot work. --- .../typescript-plugin/.vscode/extensions.json | 5 - .../typescript-plugin/.vscode/settings.json | 3 - fixtures/typescript-plugin/fixture.ts | 1 - fixtures/typescript-plugin/package.json | 9 - fixtures/typescript-plugin/tsconfig.json | 8 - fixtures/typescript-plugin/tsslint.config.ts | 7 - packages/typescript-plugin/LICENSE | 21 -- packages/typescript-plugin/README.md | 21 -- packages/typescript-plugin/index.ts | 258 ------------------ packages/typescript-plugin/package.json | 21 -- packages/typescript-plugin/tsconfig.json | 7 - packages/vscode/LICENSE | 21 -- packages/vscode/README.md | 60 ---- packages/vscode/extension.js | 143 ---------- packages/vscode/icon.png | Bin 31371 -> 0 bytes packages/vscode/package.json | 45 --- tsconfig.json | 3 +- 17 files changed, 1 insertion(+), 632 deletions(-) delete mode 100644 fixtures/typescript-plugin/.vscode/extensions.json delete mode 100644 fixtures/typescript-plugin/.vscode/settings.json delete mode 100644 fixtures/typescript-plugin/fixture.ts delete mode 100644 fixtures/typescript-plugin/package.json delete mode 100644 fixtures/typescript-plugin/tsconfig.json delete mode 100644 fixtures/typescript-plugin/tsslint.config.ts delete mode 100644 packages/typescript-plugin/LICENSE delete mode 100644 packages/typescript-plugin/README.md delete mode 100644 packages/typescript-plugin/index.ts delete mode 100644 packages/typescript-plugin/package.json delete mode 100644 packages/typescript-plugin/tsconfig.json delete mode 100644 packages/vscode/LICENSE delete mode 100644 packages/vscode/README.md delete mode 100644 packages/vscode/extension.js delete mode 100644 packages/vscode/icon.png delete mode 100644 packages/vscode/package.json diff --git a/fixtures/typescript-plugin/.vscode/extensions.json b/fixtures/typescript-plugin/.vscode/extensions.json deleted file mode 100644 index 7a6b26fb..00000000 --- a/fixtures/typescript-plugin/.vscode/extensions.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "unwantedRecommendations": [ - "johnsoncodehk.vscode-tsslint" - ] -} diff --git a/fixtures/typescript-plugin/.vscode/settings.json b/fixtures/typescript-plugin/.vscode/settings.json deleted file mode 100644 index 4d360cbc..00000000 --- a/fixtures/typescript-plugin/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "typescript.tsdk": "node_modules/typescript/lib" -} diff --git a/fixtures/typescript-plugin/fixture.ts b/fixtures/typescript-plugin/fixture.ts deleted file mode 100644 index e9fe0090..00000000 --- a/fixtures/typescript-plugin/fixture.ts +++ /dev/null @@ -1 +0,0 @@ -console.log('Hello, world!'); diff --git a/fixtures/typescript-plugin/package.json b/fixtures/typescript-plugin/package.json deleted file mode 100644 index 96ed5365..00000000 --- a/fixtures/typescript-plugin/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "private": true, - "name": "@tsslint-fixtures/typescript-plugin", - "version": "3.1.4", - "devDependencies": { - "@tsslint/typescript-plugin": "3.1.4", - "typescript": "latest" - } -} diff --git a/fixtures/typescript-plugin/tsconfig.json b/fixtures/typescript-plugin/tsconfig.json deleted file mode 100644 index 191b1c6e..00000000 --- a/fixtures/typescript-plugin/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "include": ["fixture.ts"], - "compilerOptions": { - "plugins": [{ "name": "@tsslint/typescript-plugin" }], - "noEmit": true, - "allowImportingTsExtensions": true - } -} diff --git a/fixtures/typescript-plugin/tsslint.config.ts b/fixtures/typescript-plugin/tsslint.config.ts deleted file mode 100644 index 5a057d37..00000000 --- a/fixtures/typescript-plugin/tsslint.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { defineConfig } from '../../packages/config/index.js'; - -export default defineConfig({ - rules: { - 'no-console': (await import('../noConsoleRule.ts')), - }, -}); diff --git a/packages/typescript-plugin/LICENSE b/packages/typescript-plugin/LICENSE deleted file mode 100644 index cb8d02b6..00000000 --- a/packages/typescript-plugin/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2023-present Johnson Chu - -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/packages/typescript-plugin/README.md b/packages/typescript-plugin/README.md deleted file mode 100644 index 53b34f81..00000000 --- a/packages/typescript-plugin/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# @tsslint/typescript-plugin - -Registers TSSLint as a `tsserver` language-service plugin. Diagnostics and code fixes flow through the same path as TypeScript's own errors, using the `Program` the editor already built — no second type-check. - -## Usage - -```bash -npm install @tsslint/typescript-plugin --save-dev -``` - -```json -{ - "compilerOptions": { - "plugins": [{ "name": "@tsslint/typescript-plugin" }] - } -} -``` - -For VS Code, the [TSSLint extension](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.vscode-tsslint) wires this up automatically. - -See the [root README](../../README.md) for rules, config, and framework support. diff --git a/packages/typescript-plugin/index.ts b/packages/typescript-plugin/index.ts deleted file mode 100644 index 837886fe..00000000 --- a/packages/typescript-plugin/index.ts +++ /dev/null @@ -1,258 +0,0 @@ -import type { Config, LinterContext } from '@tsslint/config'; -import type * as ts from 'typescript'; - -import core = require('@tsslint/core'); -import path = require('path'); -import url = require('url'); -import ErrorStackParser = require('error-stack-parser'); - -const languageServiceDecorators = new WeakMap>(); -const plugin: ts.server.PluginModuleFactory = modules => { - const { typescript: ts } = modules; - const pluginModule: ts.server.PluginModule = { - create(info) { - let decorator = languageServiceDecorators.get(info.project); - if (!decorator) { - if (info.project.projectKind === ts.server.ProjectKind.Configured) { - const tsconfig = info.project.getProjectName(); - decorator = decorateLanguageService(ts, path.dirname(tsconfig), info); - } - else { - decorator = decorateLanguageService(ts, info.project.getCurrentDirectory(), info); - } - languageServiceDecorators.set(info.project, decorator); - } - decorator.update(); - return info.languageService; - }, - }; - return pluginModule; -}; -const fsFiles = new Map(); - -export = plugin; - -function decorateLanguageService( - ts: typeof import('typescript'), - projectRoot: string, - info: ts.server.PluginCreateInfo, -) { - const { - getSemanticDiagnostics, - getCodeFixesAtPosition, - getCombinedCodeFix, - getApplicableRefactors, - getEditsForRefactor, - } = info.languageService; - const projectFileNameKeys = new Set(); - - let configFile: string | undefined; - let configFileDiagnostics: Omit[] = []; - let config: Config | Config[] | undefined; - let linter: core.Linter | undefined; - - info.languageService.getSemanticDiagnostics = fileName => { - let result = getSemanticDiagnostics(fileName); - if (!isProjectFileName(fileName)) { - return result; - } - if (configFileDiagnostics.length) { - const file = info.languageService.getProgram()?.getSourceFile(fileName); - if (file) { - result = result.concat(configFileDiagnostics.map(diagnostic => ({ - ...diagnostic, - source: 'tsslint', - file, - start: 0, - length: 0, - }))); - } - } - if (linter) { - result = result.concat(linter.lint(fileName)); - } - return result; - }; - info.languageService.getCodeFixesAtPosition = (fileName, start, end, errorCodes, formatOptions, preferences) => { - return [ - ...getCodeFixesAtPosition(fileName, start, end, errorCodes, formatOptions, preferences), - ...linter?.getCodeFixes(fileName, start, end) ?? [], - ]; - }; - info.languageService.getCombinedCodeFix = (scope, fixId, formatOptions, preferences) => { - if (fixId === 'tsslint' && linter) { - const fixes = linter.getCodeFixes(scope.fileName, 0, Number.MAX_VALUE).filter(fix => fix.fixId === 'tsslint'); - const changes = core.combineCodeFixes(scope.fileName, fixes); - return { - changes: [{ - fileName: scope.fileName, - textChanges: changes, - }], - }; - } - return getCombinedCodeFix(scope, fixId, formatOptions, preferences); - }; - info.languageService.getApplicableRefactors = ( - fileName, - positionOrRange, - preferences, - triggerReason, - kind, - includeInteractiveActions, - ) => { - const start = typeof positionOrRange === 'number' ? positionOrRange : positionOrRange.pos; - const end = typeof positionOrRange === 'number' ? positionOrRange : positionOrRange.end; - const refactors = linter?.getRefactors(fileName, start, end) ?? []; - return [ - ...getApplicableRefactors(fileName, positionOrRange, preferences, triggerReason, kind, includeInteractiveActions), - { - actions: refactors, - name: 'TSSLint', - description: 'TSSLint refactor actions', - }, - ]; - }; - info.languageService.getEditsForRefactor = ( - fileName, - formatOptions, - positionOrRange, - refactorName, - actionName, - preferences, - interactiveRefactorArguments, - ) => { - const tsslintEdits = linter?.getRefactorEdits(fileName, actionName); - if (tsslintEdits) { - return { edits: tsslintEdits }; - } - return getEditsForRefactor( - fileName, - formatOptions, - positionOrRange, - refactorName, - actionName, - preferences, - interactiveRefactorArguments, - ); - }; - - return { update }; - - function isProjectFileName(fileName: string) { - fileName = fileName.replace(/\\/g, '/'); - const projectFileNames = info.languageServiceHost.getScriptFileNames(); - if (projectFileNames.length !== projectFileNameKeys.size) { - projectFileNameKeys.clear(); - for (const fileName of projectFileNames) { - projectFileNameKeys.add(getFileKey(fileName)); - } - } - return projectFileNameKeys.has(getFileKey(fileName)); - } - - function getFileKey(fileName: string) { - return info.languageServiceHost.useCaseSensitiveFileNames?.() ? fileName : fileName.toLowerCase(); - } - - async function update() { - const newConfigFile = ts.findConfigFile(projectRoot, ts.sys.fileExists, 'tsslint.config.ts'); - - if (newConfigFile !== configFile) { - configFile = newConfigFile; - config = undefined; - linter = undefined; - configFileDiagnostics = []; - - if (!configFile) { - return; - } - - const projectContext: LinterContext = { - ...info, - typescript: ts, - }; - - try { - config = (await import(url.pathToFileURL(configFile).toString())).default; - linter = core.createLinter(projectContext, path.dirname(configFile), config!, (err, stackIndex) => { - const stacks = ErrorStackParser.parse(err); - if (stacks.length <= stackIndex) { - return []; - } - const relatedInfo = createRelatedInformation(ts, stacks[stackIndex]); - if (relatedInfo) { - return [relatedInfo]; - } - return []; - }); - } - catch (err) { - config = undefined; - linter = undefined; - const prevLength = configFileDiagnostics.length; - if (err instanceof Error) { - const relatedInfo = createRelatedInformation(ts, ErrorStackParser.parse(err)[0]); - if (relatedInfo) { - configFileDiagnostics.push({ - category: ts.DiagnosticCategory.Message, - code: 0, - messageText: err.message, - relatedInformation: [relatedInfo], - }); - } - } - if (prevLength === configFileDiagnostics.length) { - configFileDiagnostics.push({ - category: ts.DiagnosticCategory.Message, - code: 0, - messageText: String(err), - }); - } - } - } - } -} - -function createRelatedInformation( - ts: typeof import('typescript'), - stack: ErrorStackParser.StackFrame, -): ts.DiagnosticRelatedInformation | undefined { - if (stack.fileName && stack.lineNumber !== undefined && stack.columnNumber !== undefined) { - let fileName = stack.fileName.replace(/\\/g, '/'); - if (fileName.startsWith('file://')) { - fileName = fileName.substring('file://'.length); - } - if (fileName.includes('http-url:')) { - fileName = fileName.split('http-url:')[1]; - } - const mtime = ts.sys.getModifiedTime?.(fileName)?.getTime() ?? 0; - const lastMtime = fsFiles.get(fileName)?.[1]; - if (mtime !== lastMtime) { - const text = ts.sys.readFile(fileName); - fsFiles.set( - fileName, - [ - text !== undefined, - mtime, - ts.createSourceFile(fileName, text ?? '', ts.ScriptTarget.Latest, true), - ], - ); - } - const [exist, _mtime, relatedFile] = fsFiles.get(fileName)!; - let pos = 0; - if (exist) { - try { - pos = relatedFile.getPositionOfLineAndCharacter(stack.lineNumber - 1, stack.columnNumber - 1) ?? 0; - } - catch {} - } - return { - category: ts.DiagnosticCategory.Message, - code: 0, - file: relatedFile, - start: pos, - length: 0, - messageText: 'at ' + (stack.functionName ?? ''), - }; - } -} diff --git a/packages/typescript-plugin/package.json b/packages/typescript-plugin/package.json deleted file mode 100644 index 592893f4..00000000 --- a/packages/typescript-plugin/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "@tsslint/typescript-plugin", - "version": "3.1.4", - "license": "MIT", - "files": [ - "**/*.js", - "**/*.d.ts" - ], - "repository": { - "type": "git", - "url": "https://github.com/johnsoncodehk/tsslint.git", - "directory": "packages/typescript-plugin" - }, - "dependencies": { - "@tsslint/core": "3.1.4", - "error-stack-parser": "^2.1.4" - }, - "devDependencies": { - "@tsslint/config": "3.1.4" - } -} diff --git a/packages/typescript-plugin/tsconfig.json b/packages/typescript-plugin/tsconfig.json deleted file mode 100644 index 6cdf7682..00000000 --- a/packages/typescript-plugin/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "include": ["*", "lib/**/*"], - "references": [ - { "path": "../config/tsconfig.json" } - ] -} diff --git a/packages/vscode/LICENSE b/packages/vscode/LICENSE deleted file mode 100644 index cb8d02b6..00000000 --- a/packages/vscode/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2023-present Johnson Chu - -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/packages/vscode/README.md b/packages/vscode/README.md deleted file mode 100644 index 03b1af6e..00000000 --- a/packages/vscode/README.md +++ /dev/null @@ -1,60 +0,0 @@ -# TSSLint VS Code Extension - -This is the official Visual Studio Code extension for **TSSLint**. - -TSSLint is a high-performance, project-centric linter that runs directly as a TypeScript Language Server (`tsserver`) plugin. This architecture allows TSSLint to provide **near-instant diagnostics and fixes** by sharing the existing type-checking context, eliminating the editor lag often experienced in large-scale TypeScript projects. - -## Features - -* **Instant Diagnostics**: Violations from your custom TSSLint rules are reported instantly as you type. -* **Auto Fix on Save**: Supports quick fixes and automatic application of fixes upon saving the file. -* **Framework Support**: Seamlessly lints TypeScript code within virtual files created by framework extensions such as **Vue Official**, **Astro**, and **MDX**. - -## Installation - -1. Search for `TSSLint` in the VS Code Extensions Marketplace. -2. Click **Install**. - -## Usage - -Once installed, the extension automatically loads the TSSLint plugin into your workspace's TypeScript Language Server. - -### 1. Configure Your Project - -Ensure you have installed the necessary TSSLint packages and configured your rules in the project root: - -```bash -npm install @tsslint/config --save-dev -``` - -Create your configuration file, `tsslint.config.ts`, in your project root. - -### 2. Troubleshooting Node.js Version - -TSSLint requires a modern Node.js environment (v22.6.0+ for v3.0+). If your VS Code's default Node.js version is older, the extension may fail to load the plugin. - -To fix this, you can configure VS Code to use a specific Node.js executable for the TypeScript Language Server: - -1. Open VS Code Settings (`Ctrl+,` or `Cmd+,`). -2. Search for `typescript.tsserver.nodePath`. -3. Set the value to the absolute path of a Node.js 23.6.0+ executable: - -```json -{ - "typescript.tsserver.nodePath": "/path/to/node-23.6.0" -} -``` - -### 3. Disabling the Extension - -If you need to temporarily disable TSSLint for a specific workspace, you can: - -1. Go to the Extensions view (`Ctrl+Shift+X` or `Cmd+Shift+X`). -2. Search for `TSSLint`. -3. Click the gear icon next to the extension and select **Disable (Workspace)**. - -## Development - -This extension is primarily a thin wrapper that activates the `@tsslint/typescript-plugin` within VS Code's TypeScript Language Server. - -For core TSSLint development (rules, core logic), please refer to the main repository's [README.md](../../README.md). diff --git a/packages/vscode/extension.js b/packages/vscode/extension.js deleted file mode 100644 index 16b75212..00000000 --- a/packages/vscode/extension.js +++ /dev/null @@ -1,143 +0,0 @@ -module.exports.activate = () => {}; -module.exports.deactivate = () => {}; - -const vscode = require('vscode'); -const tsExtension = vscode.extensions.getExtension('vscode.typescript-language-features'); -if (tsExtension.isActive) { - vscode.window.showInformationMessage( - 'TSSLint may not work properly if the TypeScript Language Features extension is activated first.' - + ' Try restarting the Extension Host in VSCode, or let us know if you keep seeing this issue.', - 'Restart Extension Host', - 'Report Issue', - ).then(selection => { - if (selection === 'Restart Extension Host') { - vscode.commands.executeCommand('workbench.action.restartExtensionHost'); - } - else if (selection === 'Report Issue') { - vscode.commands.executeCommand( - 'vscode.open', - vscode.Uri.parse('https://github.com/johnsoncodehk/tsslint/issues/new'), - ); - } - }); -} -else { - const extensionJsPath = require.resolve('./dist/extension.js', { paths: [tsExtension.extensionPath] }); - const readFileSync = require('fs').readFileSync; - - require('assert')(!require.cache[extensionJsPath]); - require('fs').readFileSync = (...args) => { - if (args[0] === extensionJsPath) { - let text = readFileSync(...args); - - // Fix DiagnosticCategory.Message display - text = text.replace('.category){case', '.category){case "message":return 2;case'); - - // Patch getFixableDiagnosticsForContext - text = text.replace('t.has(e.code+"")', s => `(${s}||e.source==="tsslint")`); - - // Support "Fix all" - for ( - const replaceText of [ - 'const i=new y(t,n,r);', - // VSCode 1.93.1 (#36) - 'const i=new v(t,n,r)', - ] - ) { - if (!text.includes(replaceText)) { - continue; - } - text = text.replace(replaceText, s => - s + ` -const vscode = require('vscode'); -vscode.languages.registerCodeActionsProvider( - e.semantic, - { - async provideCodeActions(document, range, context, token) { - if (!context.only || (context.only.value !== 'source' && context.only.value !== 'source.fixAll' && context.only.value !== 'source.fixAll.tsslint' && !context.only.value.startsWith('source.fixAll.tsslint.'))) { - return; - } - let action; - for (const diagnostic of context.diagnostics) { - if (token.isCancellationRequested) { - return; - } - - if (diagnostic.source !== 'tsslint') { - continue; - } - - if (!action) { - action = new vscode.CodeAction('Fix all TSSLint issues', vscode.CodeActionKind.SourceFixAll.append('tsslint')); - action.edit = new vscode.WorkspaceEdit(); - } - - const args = { - file: document.uri.fsPath, - startLine: diagnostic.range.start.line + 1, - startOffset: diagnostic.range.start.character + 1, - endLine: diagnostic.range.end.line + 1, - endOffset: diagnostic.range.end.character + 1, - errorCodes: [diagnostic.code], - }; - - const response = await n.client.execute('getCodeFixes', args, token); - if (response.type !== 'response') { - continue; - } - - const fix = response.body?.find(fix => fix.fixName === 'tsslint:' + diagnostic.code); - if (fix) { - withFileCodeEdits(action.edit, n.client, fix.changes); - } - } - if (action) { - return [action]; - } - - function withFileCodeEdits( - workspaceEdit, - client, - edits - ) { - for (const edit of edits) { - const resource = client.toResource(edit.fileName); - for (const textChange of edit.textChanges) { - workspaceEdit.replace( - resource, - new vscode.Range( - Math.max(0, textChange.start.line - 1), Math.max(textChange.start.offset - 1, 0), - Math.max(0, textChange.end.line - 1), Math.max(0, textChange.end.offset - 1)), - textChange.newText - ); - } - } - return workspaceEdit; - } - } - }, - { - providedCodeActionKinds: [vscode.CodeActionKind.SourceFixAll.append('tsslint')], - } - );`); - } - - // Ensure tsslint is the first plugin to be loaded, which fixes compatibility with "astro-build.astro-vscode" - const pluginName = require('./package.json').contributes.typescriptServerPlugins[0].name; - text = text.replace( - '"--globalPlugins",i.plugins', - `"--globalPlugins",i.plugins.sort((a,b)=>(b.name==="${pluginName}"?1:0)-(a.name==="${pluginName}"?1:0))`, - ); - - return text; - } - return readFileSync(...args); - }; - - const loadedModule = require.cache[extensionJsPath]; - if (loadedModule) { - delete require.cache[extensionJsPath]; - const patchedModule = require(extensionJsPath); - Object.assign(loadedModule.exports, patchedModule); - } -} diff --git a/packages/vscode/icon.png b/packages/vscode/icon.png deleted file mode 100644 index 7ca6ecf3bd160e01cbb282ff0f9bc988f79bfb10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31371 zcmZ^}19&7|w>I3dI<`BS*tRvXCYac^or!HtY-eIS6K7)Eww-^T_dVZv&wsA(tG;%x zRcqb%Le;Lms&`emqP!#`JRUp%06>(M5>x&w{r?%T(0{-C1q{ai3P>|yIbi^xHWuN{ z0P62P$Vf_A4gm1{@)s8b0KEKzKLP+;m;itieE@(b4FJHl¨T`|AiY(U3NklLOHF z)nNfpU}yk{zZ%%z4*(1g@K3bA8sIA!{(tMrU{wEw0S5p=EdY@Jh0*#e|JkJej(<4+ zmJqpM|B09j{@-Z-T!{bZ|IpeSES&!eID08gCjbBe{ht8__?d|V0DzBLsA@QC$jR~; z+1W4{7~2_|Fu2>;|HA_Cx%2#0ZA_dEK<+lywoW|m{G|Uv@ch;PF*A~a{snQi;wROR zQv`|HIhuet7?>ECNCn_QAP}FUu_=$Tn8bh4|L*aVnmaq&^Dr{Hxw$d8u`<{>nlUnS zb8|B?u`sf*(EmlyJ9*eT8@SWkI+6W5$p48WX5wVzXkqVcVP^~aC$52^or^O+Dd|6n z{(Jj(oz51f|H)+Q^j~fLZIJOF4Kf-#@{4G?h)9 z?5th>DNBHph3{V({(r{*)c0?~fADIy7S00yhw{J7|C^=pAOC;S{7>WmCMY;snEXAE z|Ei1mzsmZbw*P_ur#l`+3wIN14KWKF6I-W$dSPe&TiO5B^S_y*cGh-|D)t6OCIWvG z{fqKHO#d7GuRNOn&piLb^IuFp#(&QC|2X^qo?8F1{+(L_@O+H_Jx>MTwOSc}0{}t* zX)$3{cd)`vizvorj~urTnMbZ8_;AaEEv?%Ua-ILq5oO2Lw(4`HTh< zzM%j^0}=VaqCvv#Aj8G=Owi!e{t?PEr-?h4$_$GXOD}Alh|9!HV9h+ASZ}wp`(&TI z^{RM@p@V9QJE^W#G4ptQ-D-` z@cg9HzdyPfGS-XUZ-S2~VbhGbqDjM$(x3yhU?xyxGMACRYz3~^bLliayy&y>3E}|1 zmHApR5md%E7xw9#R8BccEJsyhvPjx2$%nk5E&)n~(p7Ry_W}x{Va5imIW%OkZt!Oj;7@Gh zz|rCM6@UN!QC{-=9}X?jR$Md?dTk%6vLuTPmrjIF=|bmCiE0Vj_wP!~VGBF8e#5&7 z6>5nR@`d;{YOeqdCBb7UKh=5cdXJ3kar_9oTc~?;f@s)@gG_L=LiN>r!Yk2Ta}7$5 zX-?O!Gjk3ZKrcnog@8yNQ^TW6(4V~OJ~tc`&YL(uK!5^-VipbxC58ci*hz}{r2;Op}BEb%k!#MPl7`Q@YwU*m5^u53IY7O}3cyZ3{W->n{A z4)7zo*YJ+ks={m`wO;c(K~{t75A*_Iv2Z$6ZrPFVzr>OoL9C+y(rP@Yi7F|)cIv8p zvDuVWj-?_k8c&k+yR#cMoM1j)iQ^$wj9+OZLg}b%E}ah&xv0?OElR?HN2=fRgM*2z zx?GrKM^Id;Yf;qk+5!yMEg4Zx;;mGrGNgp4QO>CnwW;I?b^0NSnE_Y!2om6s^CXr` z1c;%hr#7&5x1Fm&VGqb5W5=5tTCFdu-5#G_$QZXng3lw{l!DjcDkZ=kzd_>Rr$fOF zYm4reyJi87Oq;j+`RnvY#NwrcUSTVh2Ejmn!xP7t@`$Gz=*c<>1LjS|N^s@;>lIEN zCHEULn}UD@JaJMyQ8$J5YPo7-l|XY;x^r{CmExg6cRUC(UA*wfSnIg&f}!@Z81bzV zWrW_dkyFBbdRwJTKytQWgcvQ8!UF5lHip6C{cUbytI4Ohw~T9~ScO z&O+MeXus#K&7IK6NN%?#>I2Vg6wMia^u-gqJH)BqlCzBbupXNlwaH_?yl70-i$c-dXhoL z)_;3;!~5(p``xPl_{pxaI+L5XyS`at;$UnaxY^@<{d7=cmAgfeq6gO@8DLO_UhJ%7 z!wqfj;H=O}r6fFHL~5*0Iv0g7tWE+v;#WyDxz z0-X>~a<1sEK?F{x&jd6XV?xUXCSgVHyE|DmaJBfbX~}+eZu&k2bc+jn)n9ac)+3R; z`4JI+dimMSJyH|DcIUiKeoM*tyq@`ZKQa!v8H(=J8C-$g2*_TuY=kT?B%{bvA-5_G zCtk{1QtZM}QIC-&lY;uHjvdT+$9H29=lCkHT2>hhT?0-h&x-!g%ikV&Gk^&hjPB^j zNK_MlP(Jgd_H)6-KY?R!@hQOSL@AH8YwdN&`^FO@>{^P_44NmvrV}akKCd^1n-rdZ zO_6~sIU~RTHb0IJ|45-2Ggk8$EV=5u&GNtk_9xOz-#zs1{CslLQu{5Z!28krC)l5r z440#KK_A%HNxqj5;*Z|Xj;{MRn9qk7`$s)nPCNP(^dLTyr@>(@8lyj+U1;$#l#<%D zW0@?tWsw8;3c;hl;5jmBJKVA1DXPWcB7J+XpcE@--izKHlYhhKPs5t?{?<5ny=~$W zCY@7v&7D+&7E%T4g8eL8W05-N;NtLl}i5bQ7ar39(*RxPThOnY1%c~8$!vA?&dELWD zOc}dpT`gGtQ{i`5qWhH5{V^w*{qw%9a3qH(=)ELHz`6A8&!O+dEdFkmUiNwiGk*5C z#Mnq?D;u6GwEU*tyI@eccT`WhM-CM5snoFPpdSz+t7rb7d)Y~0;Bv`*{@(Z$@Z0aX%lXE+6V1w&wlCWl8& zS4Swe9r=Ettc8y7rk5Xv^^fb1o%;v4l2Nx|-8ZL#k23i5qBCPKGUw2wJ=#=E{g__X zVlar)ITCymh#tiu@dhKbWCg-D2+~mkG8{%b-OsF?x9{p7qbdrG;=fdWeuf7ge9oa( z;Tmhb9tpl}m@P;iehfTETLH_kX(QbQUC&q$$^5>|XBOW3a_=`GD`;7?K^8!$ss7O1 zmz$%g7hxW!mgQezBA%?i(hKBI6YYWL4P-yT13QE6m=wAKoCZRVOl|ze3PoKFc*dep-q5Tb86@bej1vRXi_oXC4hU_kx^ zo(KiROXlKI$RIo-I7p5r4cu%+dW{D`Ql2|4ZT7n`8YiPW{QY5RYO6wN8jTH-^4iRa zQK7pviy$MIz9-neQT$^ud{|f}5brB!NP^6BI7R*Ll=K_r=PQpUS|ywFV2S_!{KL?A zB@)-AV?S(E7#b-BTc}&#cpv8dtQ-V|uaCuVZ%>q;4PBp*g~)!c z+`cblfylO=_l);m~dKgShl^P*J&+( z6#~|SHhrA-u*D>%0u359jVeBV+HDMqA}bIn-J<64zHCsgHjV^f2r7p2%$H|lRca9X(Z&`J7ST=0T?G7?GjKcW=9H{tHGiBXe-%lr=EUxx5wd(`ruF?LVZDjc*aDiXZ?F6 z?VXGQRLqVMAlzkb_xSxtP>2K+(j&+GwXbILvGYK*6=(*%1sL~21YJkLA0L*LfNxp$ z+%T;0MS;Z z#r{dU)UUX1Bp)kP*XW*Lm3X7RhzQSxqOml3?27*6Z9_+UBkr}=@J-*VD!~&Mde!&Z zn9b6$l`NT0%nC2;FNvqDNrKr5VGco0!DK(F+A+F89bWyhfZNvsH@*ZiT{A?l;uRpg>xkM=m>a z@OWMH8QNXzfxrIpeND_M+{1`;5+^$IHe~t?_Pe@|wV|Kz@vL!kV-^%3Vkx)4>)FC_ zyl2_b#_~CuWQB-prdB~k6#c!%>&Z^vxfcKJ`R!*OT3e-Zgqr!HSBFj2?)=NoLp-8w zSXcw~AP_pbUUh<>Eg`iLhO3NfZM4-#?qdk82&_B+UM2X1=xq=rf2}zooo?O_0rzUg zpACar1X_V1VzOHJaTxe1w2`HwD3fs?L^|=35qTF!iP)OYtO#XyQYNf$6+!P(swBVL z4>(=%cZ8ROUF8$)gs!1RnA&kh8TqZm21_;o56R4GZ~DjbyqEE9K@{1i$uBJxHunaq zlSlItc_=qq9%r6w*l130>Xz_#nf1iJvvT%j=bgm0GPWzuUb-@f@Y-O2g@#PFjKfk) z1|`)MmBplo2Dua#H4GIfp7fk3y4Eo76^tv$j8`V+OQuzA^Lt5{+QU6I&%(V#!Zf`E zuNkj~K-L4JeToL@1hy?IT%^J%4(Wm5ZG@J#J`P!~4)zZwa0FT3Sh`+|T$F$7D>ltZ zj9@!ZotsdlsJceV#Z);zlC(%Z#zKbhmprf9OL2!-V%?4`0M?kBA;5SL9N&f`{iB5K z+4_^*t@JT*+V}a=?;U&5tb+LGQ-$~-)~IgcL=v{)CTMF|jg?z}IS z<`b7gN}Pohs*^$)`Z*dVnJ>6c@mPtmdA$NAB1rp^W7;IRdj~q>!JQ>{5|FO@oRiAw z^uLKV*;PZ~orFH*Gqb=p>KK6(Mf2iUsn?Aih{b6IBRjSFSsV`h8O-4C&nohhbtFuo z7y4<^9ioauLnEhG(vd$`B7c3Ftl~n2TzY<)T3IoE-_$j^93Y^kLzR-pQJoe}uTc<- z6E1eZa0ZG3Vcq4kSRt}o97?Q9E3X0Sau5Qi{uW{5xA3c}%VNt5%-)pPF@)CNQfG_$ z1(RQ@MUTs=5TW+PESgGef&hqkzmvi^OoVv1*{W_-ki4Vm2FWXNGOoDPIBF6X`zh_f zKSO7rluMjuGR1$@NM$gUo58nTBvPKMb*i{a2OdzY0?PT08JCxZAm_PZ8?D8Oz_8QC zT%*7gAPANrhR#}{6~51k)ePtl;0S(L8M>z$a$M*6-91ZAthZj`?~c5kjf|}C-?(#V z@H|1}-aekS3!7KF?&T@uPNRU;AOln}wM3<0O|{>S{rxbSTT=-921M->EE7%WazCb={tb3z2A|lg=|GK zHE74WBZSe%G~@O1lJ(iDV~c^V84NcJ7mV73N)(a?Y$NDmLGfi@O!|;*mF`1h#JVKS z@8!XMQdEruksVLw0qGM&_sEe|>tYv&B{+nZt){#rJh&;aS-Oq%jmL!|nZNO;jX!W; zd0oEq#tMFjt~OS39ApB0DFfi@>vvQBZVp2At%yFO#rfpdjHEYG?jg=5o^$Ihsi zAVsz>uL~Jg69)?^;HtEuW@c0V?oO@t4Ex*R8@0-@vk5B3fOfQ@tKC<>h@Fx(tDezC zOHKPa61nMP@BQ(G3FDUMdQ*(^p)tLf88cfYm0A?Kz$WMjtIvlF%#h9<7t(DQ*6>;} zCD`&Lnr*e1y$JVOO&YVzg9R0?^71FPLOL!BtJjIJQJZ#_8+ZgZc$nbQvq}VgSPx5c z&aLqPD^vV3NiXu87e}2;7)kT9DDvl|0j?ROAJ#+m9^~4i7ihRyF>s{Uu{82y`H08g zXh#(j;#G{}`B+aj^liOPQwZn6(fo|I^Z=Ju z7vlKH9fN2xucMw1`FkHZ8rTct`+on+E_zbW=W+GwgK~v=4F00ferhW>ETsAj_k(qy zw-0?L42V{w_!}P@Lw6m6ghs)b=xYhKZO}3Q?{Zu?d@yWrv&Koq3KESFQX!mzsR|!Z z(lfj|r%Z%h52+GRTI;=+#eW0!8xal3J5a(<OJhCZ&P3KvBI|@3s{aj4Vl!*s3DAl>w902-1y2D+%}@y<{}z zL`h~%2X^#AV6nsUuHH@#mn)LPh1d_kbO zh^|5ZQA8$oBo$^@R`Xqjw5xBr1Q|tP4*vZdu=`l%6ScpwTP$C`xPu0`qv zFc!Ek^`X>NN@v_12S8Y_P*>ncUx7y+LWD#5tJo7^?4Q|B&7K*_qmb{M7w|s1Bw2KS9Cr{5rl$fk}$lqpI&%;WSFU4VCdsJ4^1l1Hfn!ygKQrY*S6gN-h%kb_VT%+YcBm}x zXwEn0So^P~3l`c3n_GAOzh72|h+f@b<k3=yvkGj6jC5omxapgi}QkTyO zjN?c&6i~B{(N&yU(d2JoI+3GbDPktKT>RrVFkk9p^4J}gHfUr!HQ}f5Q)An^x9By` z^7V}b|cL=Gu>jRrnY&Y}7RM|>&38~B3LQ-B?!0)B2ADQityfDhXdt zrBeki45I~)Nx@7IVIpT_nJJl`{#3zpnvisFr}LB}#(qKI0;=jjT2=@(cOrP|LI5U8 z`E-$pNJ<$Ntjg54%dAV>GhxXr@b{0<;Dvh#77l}H=A=7Te)e$|4}-dd6?2XcuP-_gjPK=2p>stU>FL_9`< zr-$WA7)0dQsNg3QUBh2(P+|$7<9{9cJzo4>K6o|eiHm6cdYT8NQshdsz8fx_G=3(f z79lueSUq}kl6AKdO*Kz<1W@%gUVZVb|C%Jlu+DCe-#1B8U+L558)z06BGIc-*H1J7;8jN?3uN%z22Xx_?s8hnEtP~51OAbH^3G)fJ1IPra7VLCWc8ehG;f<$P_H%*P@Xw z-510>co*3WLUUf^4jqi{@EYei>y5$<9q69Px`DT_t9_vgyR8 zG0hmv?8c|Y^pN8X{sGc<&kh$&C%IOa_^&e-@vbJYO_Ksb)rOHb^NEf9I82}k<`79@ z?#wxE1{OhztCJt`5Jv>sQvzK{EixwbUy7xFh$`sepjyk5!4Qx}K$R!OO+lOn$z}&u zjBnqoVsPhoryD_X9-j?X(&p$9H{9}`+C)os3iL)Q>uc!F5IeC$id{>|v=0+wBs=)q zA7>H@RsHZSR$4ABkeq&klpP&cQM9epF8IKDnux2H#x>3rTD-xo_N?EwNcVADWPY=| zD0{=aC{lQ1-)eeQ4G_s68%K2GJ?0??5(ZN^T$rtCDIEVRU*9|}JEjJ}_nZQkm)+lMp<^MWZw zJ@zuK2go|FAYH##fnU|wDt~EnzM!bZE9qnt=hA+aOUD!9(C!P|p1QpYPd}&TtUC@? zR~jY0_U%RW`ZG-rPbI2i9`TAa$kNB=Yg^Emv!rVmTUpg$^W;zr3Z8NPs;*)AOkw<9xNv&{;W8lY;ZzHYe!IH$L@e1IG#n3L8T^Bu6D%MnAN! z^j7U!ybiZdH0r$^Q>Lk)HyNnL5@$Sw^61_=LQI|9Q`IJeVIBzRsBc^C-r}TZBOtzR06V&h)PNmAv!Csl0U@!{8tJn05X@$h)1#EgrZb^$>7o&PwPeWO60Uiy61qTm`A8p51 zW5eJDja#2F2@Yu(G1fx52s@UCL9y|O=TJ&i@YT4<>aB*#Ja*OMSS(-KM7^@n6K?{O{ChG<+W6Bbly>in|s&SlW$A5udO-m9lF+# zwXds)@dDYZQf2b!BXg1P!ErKS<~Ygu&_6{t}(NOB<=oJMUCdm!_kM z*z8|=Ms6Kw;?INfJ0~0;XRZCScOGLX&E}Fh+Domhjr?9$8s5d*swfpZjF8|6@u(iQGEe6M5;2_*OPb1q z%3}_sb!(G2y2ksvLI{f{)HyyD47=d#&mqc&Jpo*u{MaKaBw(B7Bpr8Q+a5!NM=FVo z!*u48s3FV*kP)hG!HHr;*27*gSOlm2L@Co}2X|77?nGlW={7uhTML3 z(ZUG;Mk`nl^gj;5O`M11+{`(d$6;ZOpeEJ`1=F#dg}^hr{J8% z;%J9w;1-dBYMSb;m3+nm4`HofLtby}weD>|eNCuZV)N)o$4t5UMi~AGwR4=mMAHy* zOTX_=lwg_%f0N5q0Y1GeE$@(GlHj79b96Pjt!3{=3bkJyQStNj4O=DlKiFjxH|nje zOQ$vRj#z2pF-fWAeKB`FWr}0CNkFG44bd+w zi>Qu!ERT#d)(OB6M^H??3M+mrfqk6GYn*D67snEwd&TqaE!sM}}#(mnFT3QdH z_p$hMA2-J?zY$E*ElU&9--+6D21tRiWs^0@TXX5+W1rUtu~Y@>O2-LFi9#b)c>JOL z7QAzO*nk%x9}~M02d2}c5I!sc_=N!yQud^f^YKGsE?kdRgZ6%c37KoIrPCiNYqo-2 zW#gEGQ>p>`lcVc#Ami+z)#yrhh8GN-w-b5HLSQ@cXb1N_fL4{na`!!H*kYU0{Zo;6 zG@B=AS9Z_e#?DSJy%AZ5tG_4t#s3CT=kT)^W=~DILUAv5hEIXnGbyhf?}#Q~@Ze`z zNynp^=Kzl3QQ`=}KHy8Tv^=0&xF~}wLxJ4Lp@ijrVGs(WgZelP(IEMZCXp>y^^+km z$g$K5h|EwA)dRb=D!@(zI#9GYnC+b|tg@?Y?th2q5OyY2#a%Ohfu&NI5gTLEgdE^xqAzZ8G+kHA*xVj908YkB!Tx`4t0D2&FClGJHM9v_{4xpy=J zc(+4m2Dz~v3veSUq%EeK`lPB8YonfZQng$K>2=i1PsF%iK>%Nkk}n~!`s+xRE8g&{ z0N4Wwgj^ev%Xvyj7rhFsb%iX0{;;g`g&=tV9|yG=)6tK3XfLV77y72%)&+kYtRd$i8P zfT(wuos>jE3C3QYqEG}RRPKFvdjvNdIRBDsOwD?Gu?vsFW15D4T2J-f?S!Vs38Z;iu^+*aF?s=qpUwF@fA&%qEX`>(uD~ak)o}DHj{Kof3~mdWqJ=4-qHCcTV>RZ!tSgQCCktJbH-x8tu8U z1lWFO|0C^y!8oy}X^ZOOykT9V3&E$%X!|LZ7>XotaGus1hm$4Qvy2GIRF)uQ z1#Uh{lMbFnSZf#87%&ZOl@3Cn+CbE1(a5|`=ThI6peyh_e8E4zdSxt*Mh<5CvBH{r zt*Qm#Q1*1wTJ`Wdu>E`_l%dfn1K*u?V?>}Awct-w3z;?UJWfB0b3mfX!MlUSV%dz@ zuT4wNLEs6>4)ggMVFgP}G$gaB-kx>?@0v{4nT2YoSfyJf@@nAe!-YE^-(lgnfZ&6? zP7(Gl`TqQce7~#FfmH{~<9d7I1KRqS)$f~(t>|fob-*GN!Je=2BTA$+a%Jo)(!wPj zwH=GF)WT;Ouegd`?%SqOoF{^rVL+TdROhJhPl^>U_MiB+d7P0#rb=dKD(a)!&7jbe zVT2W%lTL5SipM@hKb{7@2aaj>K9rbe`BV(`U%QCz+5Zi`h>5H#Ycg{m79ab74x3J(~ zLNp5RDd$knK*DHSe-dB!dITx8pyaiu6}3+*GNKPP}RgvW3pE8zog5)^9fH824@X}E{& z$ACP4G@ETv>eHkX<;ik}mg3y45^Zd4$P+%|l3t%-80z%?AqnyBp9?dKH^SN&a%TL+ zwEP|`aBuy3>UUh|v!xT;Xas5Sn9D-Ix1x8-%u)z@LOC7J=G-$snkBu;*ShLT%$ot0 z97uz?rQ8fbkE^WzJXVGJJDQ|@>Iv4n%zpWg^PH7shJ~$1Cnj(+L``q<i?FSDRJ6E&_McKG0a3A?7B%4Tfg(B`IK_%l?^ePw|0Sd)8?)99l zz9hCFXGDMz$T&~2WpN&&o568Ee&`A-Z+1pWZN}YGfDKjwnX*?=X1fekF#$Fg)qFoQ zXqZA9=VZ`~b9JP59UKJ0L34LSsm~`@j;2^UY@B03+Ta7Ph6a0;xv zoV|s5?f3Y~*aX9P)*#7{O$#Q3S0!SK2CZ}Rl<~poQ}#GD>P3HkRn*z!VNf84J3t{K zTrhg27ND9yR9!L1^ewG^`5PuoZ?TQ6caGt~SsY*w?iyTOO$x~0!1rIJw~ zD{`6O@WWj~0A=}reb;9JmSwRSNf10mn0&W`C6K3cvvh0xhtrXO38Fs(iQNSFRBtBH z#U!-YdF|~(+(#Ea@gLlU?s-OpMT?(Q6vaV6I%PZ6 zLuUk9W8=L6co5_H%8;Oy{fGwu1TpusO*TOq^}iG{mqUfyq-YVGpwS3 zP%OkOc?`%tOM`ct(O1Kkf0J5}YYN^vFQ6Y(GE37j075`djA~G*r;5nz85P>w1w(u@ z!DTM<@PKafb~v6u!P4%cB^a;*9IaG$lllw21?Y>G{YKIN)edQFSIiq7j14W?vQ9ym z#3dz9v$Or_!k)<6mz{(h%xz zbR~4Q*P+72POrTjgZ4ySIE*c2^Sv?H?4Oh`-gUq85r3CT2c1tznhrN1P*LCXY1s8@ z1w^HAE`Lg1F;gdnLp$dt*RSIA&{wY8L1PGsX~_wHQSq?SII$M`90(0oB4@Vj{T)rG z$FI_hMs^Ckt-118H4@+3D#c2u%Zwmdem9wr(ZNMGUSMn){b9eeJQ1ifWD@6cb-uVd zp&*#h>7~`Aqe2SRqP;~O$w?m#r7rq-mb0g%4C5lgY7#Mbq=s6#hcy(B&EKiw>$-!Y z)~kAj>8oX>vFEf)eaQ^xAQg5erE6Xow8{ro_Nhl1t^%dDj15QNN*S1xXTy{b#PSc9fa^ht?*?@C zeHc_Zroy1*S%w8m5Y|uLSZKm+3ZFO#QG~N+5*mmv7us~1H{iJlxCvYC44a1fg$2JeQV21T@^g7GKqCI>AsIYuVPA=L%9L6J zQM&6h?MBpAk1jI&~^UbGe)%TFe6hCghGXp2xbaE;z}* z=B-5y2b+1Zo6|(}w5@h!z5{U(!MI9MLwO`_Pi}X+^0au0y2v%Tmayde1Y}GO*`lGZ zQsCdo!$}PAd(6M+Ui$H9@;A@M{Msze$w#v1!vIfv|1`Ue@MdUdB;^B26_1pEdA92Rt7IE;yUK(OF0%b#hHr?>#wng&SIFH*}(D|5_R@ zlmlMay&{tVsTFwoM13d2_6&w6A>|snGiUy*@>hdoJa>i*ns8L=jT*cPn1iGfgo=G0 z!Hc5?wj9sT$C_-vvDHLXsbgC@nzmudUe7ueTe3fIE)w#xev*&%I>v6DQK7YQUW{k~ z773~PzZJ6Af2Q%VbtRAhO}0W$DP+O9h{E&@f!aD(WsE+CCUY6+P$6Pq>_?9fJAwAc z5o$omn1J0QJq=G+SE4fg3tBmh8s96hf&#J-eXof&!20*_heI|&+{X)4Tl)YqUAfPr zj;Clw-)_0;i~5b8zNAbDvejj&OYu0I;Je$j<$`R zNQ_on_KmEk_&p1z;CkmY_7g#y^6mV{`gFJ>&5m0FF{;8iX8uXT2>DD6?(ELw7-FCv zmoE~FOkVL)Z!?752pYR|=2LgSPid+HwUccYQ<>#km=*T@`SRe6aSt4;rUraU#Q0e^ zEOG`dIJXu#&!7+Jz8@GUH$W*q@gVv%1ul)_utAr|x|clmjd3^+;g+d@!`cPoB}m`F z7G@}xBB0(d@UL$0hVcGXrM zJDk8bHXo|KS5C+NHM-{oFGS`UBL8%Uhxq!X_nd>2ZXdNPh3XFhjn3|`;dGL8=NnYv z&qipV*aR4mr6f=(Gae9>Qcwf5Z82Tehz(Ih?6Ln2PF6@kFk8%;IZMlQlF>UikJ90` zuIIkh*m>p1PU`HDK<-edbYJTAlfg$(zo-vRhi=FvQf>;7ak*in;e3$zy@vimA5xaR zo=2*d(V=6ocGBfzrzl1~gOAj^VT4pf|^@a1t^5K_5#hoZ8*d6RRpboQy` zG;^Saq@F5gt8EDi{=|v@{diCs_`Z&U)_Z)+f2dUan1%YgUTSEplXP!`YB1xDSNW?C zF+_z}8Eb`L0}s7+@k?AWs_K#Cah9+~? zfEh&iZ|5xPCmp$CPUS%FgPR1Eb5r1 z&D&&#`#be7v4XyN@4V`1c)KxO8u>kosJ^=1BrH#Rvm3cY5o%(J$GDaIE7bUzcp@(z zGt~uRly&$zL2;wB%&Gbq@O;LAQtsZNof8B6bskfLu~aQ)$Y@aLL?MYNa_>Z}3>FHE z8{B~IfRsQm&3BRn+}}!QBFcHLPzCd9A9jfEC^l7z%Mj|F!O&3sO(SJI9(5*O3S*w` zP3R?GWpGu$MvP(+6uO1?oL13wg#TzlnyT~u3Q-Vc$8{KyivP{5Id;X$?@F83+vt}r zQC`=mcL1Tt1xoR^vo2uoMQ)PbVAz(>Z0M{Sj22DwErj|+li%aW`h!*Kx(=U>diqKB z7L*Z>UiaKK1YWn}4~C~*Oxzt*38>>o&2S{Tv#5>aERzE-H3?WUmt=9u=HVd*ZqIx` z0qzt9jsljoJ3E@OAOzyAd3&f?KMBG_ke9$w9*fYhCj2+*VkjxxXf;)d_#F;-E;liy zx7qxrzK`x_qRBaZRpI4Ix)?88J~p3I{|c%tO#PCm`MmzS zw+H%)qG}TAc!;bKtcN}ayuUENDXw})^#=d&MA<9xce~5>@q+E|q32faTTX5YVWa&W ziChw|ct%7~N;#&u;bV4O$uONnJ;#?tISJ#|#vORW-W`N@7TCKeDRqNGb)Z6dI9>#f zuC^~sYlaOFdNYjcS-XJLlGhmzt{#k|41V!W|S$4ss{t~i) z#$!+V+TEKIc%O$qf}XWHF960ULr6&sUqXZRAzD4NAVg{b6?J{2gTht4=)Mlj z$`xCZH~qP;h4aoBcc_vUca!GM)0xWmFhaG-4K8!Nc4muT*MIg|o%u5as)H*#a?I$8 zqj71NCi#Nk!Ez((zG68(76*!GGNikZQsuINciF4a+`W8av%ST?@gC{R(#!0bA9fk`yYHh26pi9&6;p2kEAZ>6&aYnt#zi_^ir-2H^d89+}-(q z88hKJpcXM!R4^_3a(y(`v~1$ilWYGQKXe_?sK3}A@Y4`lPAMeYE)855^~aUat_pwr zUpO_}JFH%s3;k69BjUV&_YRE)i=CHK zlKojOt$egU1W_IX-!P0>w9OquX|LyOwp7X`g3#6dua30|)*JjyzheIP7vJ_+GnB{M zxm#FyiKJOCY(cot&Mlei^b=|1P*eKOh(aHThgd?s^oKF+I7(fk zeS4voIDs#*Bmj3haXWXSAwevU zp^+4aHWK6DCW#Rjd!3eHtDD27RhSEcCPCkO+gBPd_@<+;>K-3Cl$i_%OIW)$yY0rmiCaK#!u@UWQOd5Xt4eAQ6+Lnb6GugM2qyh(J2ecJF?ds=7wq9BmFA zrV}0s)nrmk2ViF-Y}AHAB{5VIJIr}g+q!Wc+pba1_s;u8 zK04UW(!lv)ojvp*qg^S%@j@D-^H_F29~F^Q2DJ!q^?aluYEQ2ZL*^coD!=Js)BS#N z`+7cKAl&|YR>loL`+~!L54Ha!o{nbRi!jC4697Hh5#1few@#~m!;hK#Vt&s{w*CIFkXg@LB*) zMw~__&cBEAW<-A`%R#A%a_tMzN9^-PS_ihP4g!`cltfU$y%P;$JJ=9=HB=HwA#-1X zDR|C0rrLXA7TeWf!^H%v`f9M7o`s%nb#vn3uSPu&@8I~jEn$OZ+Kg&ObCi$oCW|=oaoboPg1*nz?Z~3;FJ6nQ#9gj449sE9 z6+bh7FyA=J3E|9KDzSVa=~G#Y7fDs;8qnj%uFJC!_W+8l;8ug+z$&OL`;4Qr4Prei zQ+D(Amo&y0@{Pa!;_T={;WcE>P6tbG~yB{qUH>Ywg`heb>5h8P-#p3$@SgUDF~eb1mMN&wTiux5PY#Qb??>lB3e5Yq1(ClP9apN5Kua}9=MaaV%k>EqTfI2QxmuFV zZEhV=5cz|l#2%=XoYn21fqQnXh>Sn7?F5tORf=RGl{zwSC!AhFWE1nw0V zTh4^lpLQ5|<6c5f^BmitV)$k%1RRX7WSzkk*w3idHE14nrV$|X;kZaXRa8R}LbW`L z=%&{(*VE?=o7Zcnj&3Gfg>Lt`uKPjh*MI&OW?8eh|3+(ZXAeRRQ|*BGs^!=RVS>p> zLg`!_$LCQ@?tt4Fu&Hui{JK-SH^~Ip-(+q6JZ;@|MFq>c>JoNUdlrC}nt2}#f>)>J z0;X8AGbw}#LF!kM-N>?k3foJ!|lR(fv%JTd) z{$v^^$TMGNCS^xoct@C~=>>}zETqE#cpO*o8o;UsMu7@D0#WRa?Gj{Gh2|rui)<$| z3Cw!P18(M%gTELg#(OkoDD;Ki1D%RMB}O}X23h(Jf$zcQu7Sb%gc3)TlKl_IhZjwZ z-JWy4Hy_atNFN3R;s;N@5`(q_?=G|``6MX4=!Bj|>S}mF&}Vsz-%|iqAv*9L=`Po2 zJArncOS-FGoj#A{IhFgEK5fO^3eOUlHTbz4(h#wrNFjt9nq07ykA69a<~`{jVdE7-C6rGvSlY5R0bO>+DSS)QgG zQ9lGHTKX4R4(0zBS2(E0)Cj8^u(Jrn1*Yo|M#rHIv=8;gi8iO!S zPvDCG-l=bLADmDHtQfXaV25_x%eVh{VaWc0$!mKV8!Xs$Gh#q-mNk$4yRH2$hVc0p z@3wbbT5a!OGsYw5H``gZWZRvHJ~oA6i@Bn+c#s?So<0ebR3m~qOCfj4Tc_AWw!irH z^X;X*Yi)tyx#82I7nerz>S0UY>=`;{o z00x1mU@4|a1gS86gI9*~%~^qv&I=k?lrK`<-P=RhQmrD>IPIZ_{1R@dj=yM4*zs^c z8C?vWk%ZC+e&9%42L<*hC?gVi+Dn9t)jhcJ1i)EfyBKRlSZOMO$5m^guA9D&?y|iN@ts zA020aM=bP5Vf1~m*AkB8IztG?y2_~9qD~@Gccr$a z6y!Z%ic7BTiA2h%Zv#VS2!{-pEpy}o zJeZEBT3u4o#Jo!Pb+bbzz$gH|<=zH2H^MKJy@6@y<`jLUicn5lhrpPh3bD#QFP*%0 z2SM+U??JdmkW0FS5wEGk;#9sxuipR|5|L(CkJ^;oPU{bC zwG|?Q-yw8u#-q3a3;c)IPA1C5?GnQw!j9)Dpq#QtX}F}h!sq0-vmAwo0br#^cdpYf z)%NWOnr6^fZM}+=g6d)Do?JuxEtQAQrD5uDUqQKx<3rC3Fbaw_;w`)lYom}6~$vm2Uc&wA~e-!8LBw|b4 zj6vG*!IbbkYiDS`iekRShRy%+E4SPK`OWL?b^jv^4mxEgZQ$LPU{fJb?IMm6H42DnI5S4rydGf=%Y=ystvHI9spztVgWz(I?EnWl^nt-#0 z!nvU;dPe{Pwj!o)dyOG7r$I*DkhWw`;NJD?dAgK-wF5|%z!wJi3ogczgi*#!1SHtt z$P570NT43d={i6LD+05USN@o^I#qJm#xo3>6FR9!W{iji8pXZ8q$rcN0!&kANt(J@ z&BOl`u-Fog865f_Q0xNL;Dm=oem2K{QkC@#;_(b4$v?Xw8Z4%SZ$3A!cdk)y%wyL- zetFtn+`rZS>R&k5-om?;8Em5wd6cK{#nttCyJMhdkhn@6?1-7hYCFq^islDUh2pKB zqpTd;)HrC1Z0Ed0aYY5CijUGqezuS&^zTgW#ZrjgW z*@l-6qh88%I6Lw?!DGD&25t+CG{VLbvG_aJuV&LB70M+~h3nvsLqU4V zXb4oX2;NheYS}hkySjC;oxPyf0P+uq#G9v)b&PcFgjYqKD7jbpeSZHUs>{OpE|G9K zrPDdP&~gfgLz>EnNJG1FiIF2@4b!bwzQsCnr3FCp<#!cTyvyQq_9gl+YuzmC6Sxx= zcYK)i8q>#r{PJDb#ZC_~#<07qMV@b_$E0# znIUeYM%nv>ICDLzQrR!wOQ20y6fE7*tc=fa!aM?O(Xg))CqJHI>Tdc$z4(j;R z%fvQz+rrE^;E+$`yl^*z#lVwk4k0_-WM)SOO1AI4XZhv%N~i)8z`#1x;$d|E=IiNi zj1(O-hyxD4DpgDDSJdJ`2vBq?O9Szchj@RkU0aGuVH%tT+;`XNx4lV@iSCeYVU@7F zsk+_O!n7%=6dx+wX&h>$J9D?L;nvf^tz{n4tTB3`)>ANoTM+gqw{U50q**>=v@j`O z4k|Tt-<@0<5$MdFMqfYR-ACr}8C!+!u-V}&-V(jgVDwCuk(&Sj4OMI6 zu7`Tn^C?{L+WFB-tkzo2eJ3fBy0wJ!BZKJz?B{IUF%*U67GXfiA$`At4ErfTg=wz& zjqoMnP09$YZV*MK5Q`xCE#wiB-hx0CmTe8$9u?|MOQA&_rHK&9NPV(XCe78wXkf5c z{K9w%vph-@hE zY$&^dQYOn%UVMjE>7zmZsS|Z=8~Tcka8XS48RF{ObUNWoEM`p?m7`+|>PmLz+l?l8nl{MF}2)2qA=9`#QtEKN^8NSZpXf=1YwGhm&^k5%%T1 z?|i$TrPed7@oS(HgzSeYUg1T{7hgVRnp-z{s)p@ zKqDtK=G{D&h<$%jyneX(E=$PoxPRQXd9&ETp)(NKlO+#ntkXyeK9N$v{{?)LH2|MB zs?1FN6dsivFSywwd)u52fZn270HQu}9ry*96akmV+|%p)X9Pb|gh8Dy1;+^RNG;QT zUxBA@qmLZ%$+D9ZM*hclS@QJbsiC|6DtX&LoQ$Y++3*J~BrgLk@@2|Ae}rL`9+EA4 zWtQ?g8l*hI(#p1?HJ9KL9|^mKQ-6YX;U*vNQ**)<;~tA7=TQ9nKJb=y|Kpe2IksVK z@O?o4k%gzmtwGNirSIL^Yd?DajrQ$l@3dD~T-)Zs%Z6W#2B#|+ND3ryix#7Hhc6ka zagV3@H5EUH=W~*EM)1T4KKeLm?|;iV{v(Oe07IpH3>TP@m)wz7$L5#3{HhrLV~F?H z{7)v9C0aVWNG0TicUM8RPvZdYBuyHy@Wu(B$4n15AMzD8p9P4N_B9qjjTzaK(H#mz zQVtJp0;KZo9%zvo6(y>&AJ15K@u3=9PIpJeKKmJSaY>&L}v9`u%R+J$X>bl6^n7jF z3Z`RKu@?Y9kk&k#!uYWti^hxTJmYa`N7J;kwQ#rsh6*UZhPv(zG}GE+E<#%ZDD9-vSH#?!@lzjtSG(`Y z%Y4q2RXaRAc%6|RtlUkmxsRX%LNCqz*jvVkvniVQpDn~os}5V<9c=sa$X^!*P~M` z46A5uruuTr9qg2WN|b>|B5N!{flP4*LD$y7nqr_@@?(SE@Eb$Q6>2P}rv0q{K>6Lj z{H}I}k4GNzk093B^tZ;-OJD8sYv6t$Y6GQjV5b|d-=oZ07y=WZjCOoZmra{1bRU=6 zlaF3%N6hOFc<1ky=l+ELdD3q4cCp_a9GqnUNfbCockm$Nhet2)Po{V*?01I;=x>#1 zU|+6%c;rnZ%DhfcUOpU6y1bu%Sj9B+n=eiCy4r$;r-7d`K~_8+f;i-xK3>^(qS82Y zbA{lNBlH^qePNWq6gk^%%~>Ft^C~%e0u8xZ4Plx_0R#d5xQHABIKlh*iK5K!;8&t< z`cZGXhrSBH4AqA7L;~S+FAl+^FEfD>eOp!KFhxxXu~i zCA8WGhEEvOJ^7J$WYfe6g52U2ws<(okK$3jMu5KfDybj3_QU|l(+D&GUlZpYR)lDb zgRlmhA#{ri=yrz-Dy;lv5{*R3tNQQ{SXBenZ>o0(309(L@6) za(X!d$3E`}gbrP?6D)Mbo6ORi=Xm~f%3K%XX_tCMzGR!~4ie?Dsg=5RA*o>*KkzMp z4n`S+U5XE|v#^oyr7_Z~Ya=w__FJveMUZLL8n6~mGyX;0A^-eo)j=vuP8sPx_P!_D z=6z?{^k~wy_-|YzwB@JPR&bGZ5ctg9|HPqpuAwv|LH;!i>5vL;l8{4JqWzPb(LeOm z)^7Ib(blyD4aW|fWE5bKcd^A+%lzjV8kNFcQ5hUMOj3ld7#ChDKlO!kKb|=tsvNa1 zK6}JhMR~$Vx8-i6d@j~;0B{pA;ngUh{4&^v0CkMbraTVEy%AC!r;P(p@J)FvkwKuW zY2UF)D@$RK#{$F&1Dpwjqa>`V(f^;pj3s>wblZ&^G28o62&GOn9ra~ z<&%XleM!VjD#k*K0)hLjAO5dZ`eb`5%SA;tb9vH)XVl8-((pw&E zyN_IK)5!^;9ltaD37dxQff#a)LZDZr`}ka8C^&;r22&6nqvc9g+Q$gE(?OM(qo@4C zr>+jw5-pvDU~7p;s#?7>+@OriuqQ;x(U>aIIJRS1f$d;K6dM1NXZG83SBLE)3jltN zG~4%7K5|mF8csO}H!7DRL3`FrE{Jk(nIg{E3gTDnK6ohs859ten=mT?h%Z1`BHkbG zjOA*rK!KrvR-viSA5x|RgZ7eCnOAP&;tkwF!p;zVn> z4P5ZXYhc7@4!Y^Jt9z_cF53CsdD}R@+b+EIe#Vl#7Om@Q{tAwfUAi5UoQaIknNQ0B zlbWd-BL-L4NTzt9gFL5-_1qo+@1EG^>tAGEh_ zBfh~t#4_40ld$4!60k1tsGwJn8R4A6iBbES;> z-catsR}V}g#h=#C=G*n9(_T1J2%Y;$3_AN0+g_VGho@~vc=I=PBi zWpl`AQkEyDze`!FkRO2~*?L^8z*K_MJ|CgdC8!V#puAos;d+ajbE8yV=gFdt$p$j9*a;uTXh0(a(%Nc^+w7@IojIL*B zJzbnVFou_Zi)*Ni67TWo3nhA$Dz3MR-XsUBX+&Ie%Cl+y+SX)OVW#D25ZQ~fgg11;R}r2FSH3XdH`0G4?P-Mjv%by1Cm6@d@-f# z@Iw*VKutGgbq-YtP!*sN$TZh{$%`D>jUcQE6V|H3YoG4>e^J;*0o4_93Wl(;Y{v#Z zPR_1jK9S&{#81gO-L$`}ZU7>wW9Zkrq*xm5H~l*I35z&xz5e~Dzt>)2>-JmUdA_YP zWx)C zKE>t|o-t(qVfugIG5v(`I6@ppqX22mkTxpf^U+2!?vSoP!VApbK+<;q-z z$5L2XmjaY*%7Q5HPV=_w70zFOxwJ47UdH%sXuZgk5Cyu*Zf!h z_?FsohS$5$gn}2m{Zo$R=o_Wc)fi!1w)G*20V6AzIE4{G<>!eUbc8SdSgyomO}y0& zbaEg&GZl+C(KE)3!l_?);SIPN`13!!-9GcB{kDI+%BX+B%!N%ODBG7oTo}vrkE=KY z=E6?^a!Ceq)gbxJtqAf2({k&Fg03uWs%{fuRFHUjy?D2P#X~vqbIp?S;4IE$T}dwCOaAf$)pW2 z)S*$W^no*@Zwafi&w7}e(Pps%=d^` znfb?Gd8Ylh|NS4foh??~u3TnaevO38ybuNcXEKEvKw1zZQ(N zgor;fE4e{4Fp+PRWnYg9YhjM|youhf9UQl3estKr%3Hv9_V`E9Sk?UGn1L-VQFK1P z%k}iDEg7qTM+Im-^($)f%&co7l_Oc|WQ!m9pv!3;>&t_A9_jjoCJ6sVwS|RBU9RC- ztAY_3v|cy1P9Lq7tZQqP$!6%zq^c$bg*i1OjN113QXoZ&s-sAQ(hv;)MuCK2YB>FJ z74)_1*V^Yk|4jSzXTIMqUF2hoTW8zq1|O|vhq9CjkMCdd|EgdOAvmUS7mS}@xpVWV z{owo8+bypCXLOAu>+u=)3E58%ym2i{pz@=Pie(jZ` z_TA@?(95-UmgnaW-nZT!AVOTaxY5qC&cDf1!*$-=S;Z*)S8aJi%CQA>*zHa5+I^;W z*BO_*&MRCm^G_dMzl}06&>e8@@&su{h<7krT@&r-=UwqzdbRuQJYPPr1b?|&Cfyh! z;_P*!gZe=UHBn{Cx-=_~oeF3VELBU9&qb>M1S;Q0j9@K=I`An?QeP#DT`*onBpljH z1u1_d!g|;~=`snuqQVsL9Ew_li^Hw)fd807x}OnDCdci}&ZvFnGhaZkUuHnaKg+l? z%f9+6mp9ra{vp(YZsK^tf9bma2}osBKRYB65oGl`y;BzW@nFHX9q+YyMI7KB+~sZL;a0ov{^uo2t#=tA+)bRRAp^sRjgL<{bvLSuGY*f$y9n83yL!lkw5m}g_{$POeuRI9^_P3qv zGlv#O8DMaE4+{VB^=P@qv}&)N@Di)auV)dGt0{4Gf22Xn2o5r1K+=iQ{il~c$5kE! zc1HPGCFSAX2AHu*3}fi_FN|i%BXbR9dXuf#H}~7x!&~eZTt(sNO~r9l*#lR9hz=^B zj}c%uMFYW?dSwXN@L@T>4l(2}GFJZ59L+<6q`s+)JGo1K_owJLFDC$Lgh?Mc2Cx%O zNkXVX7MMsh3PT~4qcHL+MQK5jDnMFWDlCNoe!R6T3RY0uT4*MZFlbQ(KIUnkrD z-eKE#@;v#jI)cIJhz8)wCSs~S->w(h$v2v1;HpgU{*Yol3@3+K=t!QT11o?(exx%(S(0wsLn{WHS%+WLd-!nt<FU- zOF-9P(HJR&hd_O2F9Rt`N)8AaME>+g9}SFKR>~*Zt<0&XXD+S*o5yv@LX~vo%c&X$ z#$Xgc(rGjDtspX-_9=W;bKCLj-)Q@=!(`6x!9V_@6BQ_fJ%6=o3=_gr;=7_*ww zXOxjVDk9M76dv*txmGC|lu`*PCnxDHFKGbH*_Pzn#IyOyEf&0%FAoPR?}k{W11blP zr-6N{ektRlq5ql6Weg}0tU~ONWYHv{qX>(2Qb(!ibQx4496t}6@q`e_S6*ClqcAep zCL^CqMHvcjI>qaG41M;@lMKcliQ&3ARriWQz|jvd!!U5b1bhUjA#fi-hMrQqw0mTn z!!=%OxkBPs0jnUF$}^xMX%vvgQJPbV2C zbaG|^64@;$&k@gN-=*4LBFwQgtUV||P8l{Vk#=EJfrnU$7p{nuC3p>*W+q}qvkJS4 z6#ycxFFU&#* zMppL>;P1mUW3_4;%JfOA|`;{f;yczLgSWxUc` zbV^Ui)CuyhL`ymrdph2->%thm%PipJ>q`b!JT9bNO@I7nk6EmVyc8rWMtI^R02QAF zWTK54FQ~Zk+Jr5!%Yeo}MY|0JZ`!T`JFGOR2oJak&GwCaMSFeqGAn!!vSs_cHJD2A z4jwc``C)=T{2E~b4?O;Oue+v;QSto5n8j@qF6Pxy#v@)DR5_Jm9(rGBw;VQG~tgAdnc-x#xz>KmiBL*yItxjRLq zO0?{vP4AHzCO<(&4^qG!$f;xr1F9%QD-j}bHW7ljW_&jq)}V?ZH5vmz5xud+kdUX|QR>vVD5p*qe<%rEJ)Ke> zZIPQ_ThU%4up)mP%b2cb3Md_!gePPWXbQ_6!d&gy|n{D!mZ#V4uuZl6~+QnLNVYDhd(WQPGaiei(i%^ANbO)b3Sn4u)gGw}p+E&SEFz%;wYw7x3NH$Rt}25Ww6d~9 z&FMfFAwh_OH&KverPYki?3w-T@B8}w{?A%l9Wb@lx7S{4z3Y9S^YgsVde^(wnmjr? zm_MJ7J9AaCLFDjS*KF1S5Ewz=BI~Ez7@!qPf>zF$dzh6VSQe3I#2n$a#PZEP!xSOj zHeMia}Hk8F_Y8Ec32UtA4rP+rUC^HcjPokjP_SpK`r?j$qen_sDH@tL5 zk4mC9gSeq_^heX_;wKS1;~PA;#+L{cLJ8CwEXf|LL8(1yaI zTIv;QYq(T~DrwH+;r5YAuwB@-T?_?Fk7o)U{OL|;7o+1`xK1DIo>)rx!Am7WZzk1H zCr%;1u2Fm$fR}$hFrva`P)byqQ@-TAj@kE6Izh#5mT)gIA;eN~+RUvjOWLt35;kT+ z52nY~H>OJ)KNmm@;K^Ng|MK!@zd|lBcVUmpx1Wq_Y0*|OP5FY`CcsHWIjlAu zG>z0}y+etuGn0t~Z3IZ8cs|steB~%0>d3roCJH3yLavloe!+fozMM%%p&9kV4fJm% zrijrei}E(C(0#Z#Gya+u*xAx{hU=P@Rqv9HLqHK&nw5E(u-7EK~B zFt{GC(v2KYaxVKw44vr%p;V$S<(jXJY^yP#OApdcW6m)JW%4Q-%2X`%V$2++^%FMLNuUA=aQT0z;F?zrH}oo_vH)#E7Q%+Zu55hurbBkrP= z0uYi4R5(#+)D4Z^{0R0U8sw7K&<)LmFGC*@AwQ{gwIDVplc2Pj!7_IecQJW{4=U3>0)_H2F6&FB|63(-U_rn`2iu_H zpL8d|<79(%AWhF50>*Y!CB4`olT}Gzr{>B)N5eQ!oZsD|x(aa%MVVSeai>re$8@xSq`GUQjZKv(wQ7mo@ za0|HaJ%2|0%m=FPr!5Vtn{;$fSOBui3#GsXb|!no^U<`CfYltsTfttT<-3hO>*hog zcpdQNGv86bGjE%N)^ z6GsH-b9JEdd`+AJQ^_@)NOGl+Y}^RJ-RNIFT4^BkaGC#@k)EgrV?u`FY@;lh)(a@d z@Deo?gvS_IJU&Lrd-@nWTAY7#))|aoV&6YL#+EyPj8{zATIRcrCgca{BJ}zOk6xw% z#GNI?N{dRc9Gc~cV~zVo!?BDae#LTeU`rP-cLziS2hRq4VPtM6?-%x#FQH!VcY1d& z{d5@c>r^EN;9-F29)+z9phy!rgG)W+32u)mm;Q855RvmvfO5oC12+L+cG7g7Srdyx zzAAt0=<{<&Zg-M>~x+?SMdAC?tc2z-IpC(=gL$5IG(w0c*&fr0i zT?m_d?HI)76lI%}pxhWu3{3LHQPj$WMx=68YC&gL+QbxEk-EINidn^DAUX29ErrLv zeQu3m0MTZN8bUV`FrdX$wfQ%ljsfi8t{}AG{l45epE|o)&P$PKHp#Wo^vS7bz%be$%A{4(8{ zjn6yyEoYOzgni~Hc6qN6)6+H=cI+9fZ~XhNdvCn-j@8uz{euVhZz;d1_5aJ3H9)RS zIyZHO7ytST4-dz)@7tLEgQu36nB9b46`#C-<0Ms4g6dZ3%7@RMvEx%c{vs5-=B0}Y zQUSTMW=0)hDm<0LmU9kD^}9UNKUl!ixrW!wSt`t7gz;=dXjfuL6n~T;gvbvup&8Jq zD2>3Y1iLa9JvdZ>E8g$1`_?tf(s1}`Y~}S;Fa5D+^myy?WB4NVZR2u+8$i3>c6MiW zkypqar8{aoU*oKZ3LS0MPkaB080A+mbB9r7>l{sVDV#vDYjCvINd@E@eP%8VxYYD( z?md;itv{MySsAWx9RB_fK5^N{LR6BRmn)*E&Dod!GaooO?p)DXy5#Veem2t`zMFSp zjEDlmO|$d#tz_Pf0fcr#rP9m+3T0;s;R>r%591hvA+HiMt3qgmOn%Yi7!K0($U6qy zCrI8wt$0MHSeP?3o(>hED<29*un@3wOh!g zopQklRK8xX9QjF)bgO*3(Om8k{s+AKz_8!r3uoQA!L+xnH{QN-%Ha6;@I&{Vb=h^D zt2>)(Yt!D^8VyfcBD#~xa=5y>O1QuX-qTsU`0y9+pB*pk8;zeMof?tHtoKZ?%#61Z zD-yag?m!5>}M1UW7ndjT$iqm5TzSV-*)escooN!K_ZW$SEnJUL|PmQV#s;UMvsd z%=Vqzv!}CkD(@pYK^KMRJ5ZE99#VX=M+azpTyrkv#(}^b2LH7Ie$P|f;d5Ew#? z1X@vk56{o1V418+*q;#gr*pe@_s8SsfBd_pd#?Y>{;#c@^c~#)^3V_3ED>dUaPoG` z2=;Xb7ya$?-=3RpyJ0l`7bEz*!eBZYn0&Q!*wPlN{drkv*owL7Fi0mEH&$!6P%)u| z?<)#2U_=^QlNVqNg(Wjr`0TaIR;En3`fxGQWKaa*ltNXQNE;Cw^7++`C>5rGD9lFL z3LFh*h79#$2=V^7bYYhc)^O*!W~K8D9vY>dFNEHau+c2(*S@H znqsyTiKY7M~4yu6=qw!7I|m>C-d zFO?j{i@oZ^P(%?rlqd%c+;|>s6j5@9?3xQu6a(N)GC#Z@{`nSXXMSawN6D5L$3hl* zI(PRBx;BxHZ21=hkQMe-e#we*DklO!PZ=dV;_cGntYb(1!j=)hP{Af~{$gg<*P~L`4a(wY_;%M0{O$_122hVvRIH9{6N8u}%b48Ihv zF)(+U2NW@mZf{{5{^*>|qmLZ#9lrg++dl9_6#r$w7>GB=J>B1!{m+yz67{0bQ-rzNMW7%h0gFQa*0h>_d&t28yacwJiP!g5wM1fj8 zzqY<(1ldP0>34-=d)nxUDykGGoN7x!1lsof!E1dzSQ?ZpbyLqEoqdkkx|`#szRv>| zUf@GWCeWhrnT2_t4&pVj6q@L@gD`*(962th;1~l?=}~;R;cSj+jYqe?*bEYs&V>H* z^eONEpZFrm9?zf+=NGy3Qn>q!6J( z6c9^=cNBsFpoIv+_9{)pDHE2b`R7#j3hye3Vm#KN(FPdR*$-+lt7s1ViAu;@+4kB!RrV1ztSZq0qH;CQ{7z@*e=kG{?QBQP?wENgMupN2CFa2EX4DlJxWg^uh=J22bgJVOCr*7Gv4yq&9>M%i^-AiN!D zd4_kP%`tx8QsUwrS)%OUdDiu@{$TpRX#Uv4_uq2OQ#r}M*X#9dJGavP|14cbmwE>e zt}4xPU!d)|?7Gu-Jb%X936SS^yK`@Wo72H`8YYdZ=)VxGd2u>(eIZ7mn0)4^NYW5; zIq1~^ip^lR0`Bn<&0(MT2N+)%U_yXHHHO3UV7!+b>FHIx;+_#%5=0Eb_A!z^`14{P zkWq(*tQrROnJY=#X>Z1vljsWtU@#0(fFBT&sYB9`^DinDsyKzBi?2=XMh4lIjnp|f zwUcmUfAUXgSSRfJia~~Fylj#j9znIM;Vl$^e~w8qPc!KD=TUe4spHGfJaOpOk3Z8Y m%g$r}fes#Tas`6_arqBH5DcV8LB7}k0000 Date: Thu, 22 Oct 2026 17:59:19 +0800 Subject: [PATCH 06/52] feat!: move types and core to the TypeScript 7 API - Build the workspace with typescript@7.1.0-beta (module nodenext, Node >= 22.12 for require(esm)); drop typescript-native-bridge. - @tsslint/types: SourceFile from typescript/ast, Program/Checker/ DiagnosticCategory from typescript/sync. RuleContext gains `checker` (TS7 programs have no getTypeChecker); LinterContext is { typescript, getProgram }. - @tsslint/core: lint() takes { program, checker, file } instead of a file name resolved through a LanguageService. - Add tools/estree-oracle, a private test-only package pinning typescript@6 + typescript-eslint as the parity reference. - Port core tests to real TS7 projects; drop the BuilderProgram POC. --- .gitignore | 2 +- lerna.json | 3 +- package.json | 5 +- packages/cli/package.json | 11 +- packages/core/index.ts | 118 +- packages/core/package.json | 8 +- .../core/test/builder-program-poc.test.ts | 289 -- packages/core/test/probe.test.ts | 81 +- packages/core/test/project.ts | 53 + packages/core/test/skip-rules.test.ts | 60 +- packages/types/index.ts | 83 +- packages/types/package.json | 6 + pnpm-lock.yaml | 3317 ++--------------- pnpm-workspace.yaml | 1 + tools/estree-oracle/index.d.ts | 7 + tools/estree-oracle/index.js | 24 + tools/estree-oracle/package.json | 13 + tsconfig.base.json | 6 +- 18 files changed, 525 insertions(+), 3562 deletions(-) delete mode 100644 packages/core/test/builder-program-poc.test.ts create mode 100644 packages/core/test/project.ts create mode 100644 tools/estree-oracle/index.d.ts create mode 100644 tools/estree-oracle/index.js create mode 100644 tools/estree-oracle/package.json diff --git a/.gitignore b/.gitignore index 0d467c28..e9192e3c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,6 @@ packages/*/index.js packages/*/lib/**/*.js !packages/compat-eslint/lib/code-path-analysis/*.js !packages/compat-eslint/lib/code-path-analysis/*.d.ts +!tools/estree-oracle/index.d.ts packages/*/test/**/*.js .tsslint/ -packages/compat-eslint/test/upstream-shim.js diff --git a/lerna.json b/lerna.json index eed9e2bf..48c46e3c 100644 --- a/lerna.json +++ b/lerna.json @@ -4,7 +4,8 @@ "packages": [ ".", "packages/*", - "fixtures/*" + "fixtures/*", + "tools/*" ], "version": "3.1.4" } diff --git a/package.json b/package.json index d6cad0f9..f46298bb 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,6 @@ "build": "tsc -b", "watch": "tsc -b -w", "release": "npm run build && lerna publish --exact --force-publish --yes --sync-workspace-lock", - "release:vscode": "npm run build && cd packages/vscode && npm run publish", "start": "packages/cli/bin/tsslint.js", "format": "dprint fmt", "lint": "packages/cli/bin/tsslint.js --project {tsconfig.json,packages/*/tsconfig.json}", @@ -18,9 +17,7 @@ "@lerna-lite/cli": "latest", "@lerna-lite/publish": "latest", "@types/node": "latest", - "@typescript-eslint/eslint-plugin": "latest", "dprint": "latest", - "eslint-plugin-react-x": "^4.2.3", - "typescript": "latest" + "typescript": "7.1.0-beta" } } diff --git a/packages/cli/package.json b/packages/cli/package.json index a9023b6b..85ecc86b 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -3,7 +3,7 @@ "version": "3.1.4", "license": "MIT", "engines": { - "node": ">=22.6.0" + "node": ">=22.12.0" }, "bin": { "tsslint": "./bin/tsslint.js" @@ -24,7 +24,12 @@ "@tsslint/config": "3.1.4", "@tsslint/core": "3.1.4", "@tsslint/types": "3.1.4", - "minimatch": "^10.0.1", - "typescript-native-bridge": "6.0.3-bridge.16.tsgo.7.0.2" + "minimatch": "^10.0.1" + }, + "peerDependencies": { + "typescript": "^7.1.0-beta" + }, + "devDependencies": { + "typescript": "7.1.0-beta" } } diff --git a/packages/core/index.ts b/packages/core/index.ts index f3edf4a7..6cdfb1ff 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -1,5 +1,19 @@ -import type { Config, LinterContext, Reporter, Rule, RuleContext, Rules } from '@tsslint/types'; -import type * as ts from 'typescript'; +import type { + CodeFixAction, + Config, + Diagnostic, + FileTextChanges, + LinterContext, + RefactorAction, + Reporter, + Rule, + RuleContext, + Rules, + TextChange, +} from '@tsslint/types'; +import type { SourceFile } from 'typescript/ast'; +import type { Checker, Program } from 'typescript/sync'; +import { DiagnosticCategory } from 'typescript/sync'; import path = require('path'); import minimatch = require('minimatch'); @@ -13,11 +27,16 @@ export type Linter = ReturnType; // to `{...spread}` so it doesn't leak into the serialised cache. export const NO_CACHE = Symbol.for('@tsslint/no-cache'); +export interface LintTarget { + program: Program; + checker: Checker; + file: SourceFile; +} + export function createLinter( ctx: LinterContext, rootDir: string, config: Config | Config[], - getRelatedInformations: (err: Error, stackIndex: number) => ts.DiagnosticRelatedInformation[], // Rule IDs already known to be type-aware from a prior session (via // the cache file's `ruleModes` map). Pre-seeding lets us treat those // rules as type-aware on their first invocation in this session, @@ -25,21 +44,20 @@ export function createLinter( // the cold-session-with-stale-cache hole 3.0.4 had. initialTypeAwareRules?: Iterable, ) { - const ts = ctx.typescript; const fileRules = new Map>(); const fileConfigs = new Map(); const lintResults = new Map< /* fileName */ string, [ - file: ts.SourceFile, - diagnostic2Fixes: Map ts.FileTextChanges[]; + getEdits: () => FileTextChanges[]; }[]>, refactors: { title: string; - diagnostic: ts.DiagnosticWithLocation; - getEdits: () => ts.FileTextChanges[]; + diagnostic: Diagnostic; + getEdits: () => FileTextChanges[]; }[], ] >(); @@ -53,7 +71,7 @@ export function createLinter( // ignore plugin can no longer match disable comments to rule output — // turning every successful suppression into a warm-only unused-comment // false positive. - const rawDiagnosticsByFile = new Map(); + const rawDiagnosticsByFile = new Map(); const configs = (Array.isArray(config) ? config : [config]) .map(config => ({ include: config.include, @@ -83,21 +101,19 @@ export function createLinter( // which decides "is this disable comment used?" by matching against // fired diagnostics. lint( - fileName: string, + { program, checker, file }: LintTarget, options?: { skipRules?: ReadonlySet; - prevDiagnostics?: readonly ts.DiagnosticWithLocation[]; + prevDiagnostics?: readonly Diagnostic[]; }, - ): ts.DiagnosticWithLocation[] { + ): Diagnostic[] { let currentRuleId: string; const skipRules = options?.skipRules; + const fileName = file.fileName; const rules = getRulesForFile(fileName); - const token = ctx.languageServiceHost.getCancellationToken?.(); const configs = getConfigsForFile(fileName); - const program = ctx.languageService.getProgram()!; - const file = program.getSourceFile(fileName)!; let touchedProgram = false; const rulesContext: RuleContext = { typescript: ctx.typescript, @@ -106,6 +122,10 @@ export function createLinter( touchedProgram = true; return program; }, + get checker() { + touchedProgram = true; + return checker; + }, report, }; @@ -114,10 +134,6 @@ export function createLinter( const lintResult = lintResults.get(fileName)!; for (const [ruleId, rule] of Object.entries(rules)) { - if (token?.isCancellationRequested()) { - break; - } - currentRuleId = ruleId; if (skipRules?.has(currentRuleId)) { @@ -146,7 +162,7 @@ export function createLinter( // re-issued here — they live in the caller's cache. rawDiagnosticsByFile.set(fileName, [...lintResult[1].keys()]); - let diagnostics: ts.DiagnosticWithLocation[] = [ + let diagnostics: Diagnostic[] = [ ...(options?.prevDiagnostics ?? []), ...lintResult[1].keys(), ]; @@ -171,23 +187,15 @@ export function createLinter( return diagnostics; function report(message: string, start: number, end: number): Reporter { - const error: ts.DiagnosticWithLocation = { - category: ts.DiagnosticCategory.Message, - code: currentRuleId as any, + const error: Diagnostic = { + category: DiagnosticCategory.Message, + code: currentRuleId, messageText: message, file: rulesContext.file, start, length: end - start, source: 'tsslint', - get relatedInformation() { - return relatedInformation ??= getRelatedInformations(location[0], location[1]); - }, - set relatedInformation(value) { - relatedInformation = value; - }, }; - let location: [Error, number] = [new Error(), 1]; - let relatedInformation: ts.DiagnosticRelatedInformation[] | undefined; let lintResult = lintResults.get(fileName); if (!lintResult) { @@ -199,20 +207,19 @@ export function createLinter( const fixes = diagnostic2Fixes.get(error)!; return { - at(err, stack) { - location = [err, stack]; + at() { return this; }, asWarning() { - error.category = ts.DiagnosticCategory.Warning; + error.category = DiagnosticCategory.Warning; return this; }, asError() { - error.category = ts.DiagnosticCategory.Error; + error.category = DiagnosticCategory.Error; return this; }, asSuggestion() { - error.category = ts.DiagnosticCategory.Suggestion; + error.category = DiagnosticCategory.Suggestion; return this; }, withDeprecated() { @@ -257,7 +264,7 @@ export function createLinter( // Per-diagnostic fix presence — used by the CLI cache layer to // snapshot `hasFix` for a rule's cache entry without reaching // into core's internal `diagnostic2Fixes` map. - hasFixForDiagnostic(fileName: string, diagnostic: ts.DiagnosticWithLocation): boolean { + hasFixForDiagnostic(fileName: string, diagnostic: Diagnostic): boolean { const lintResult = lintResults.get(fileName); if (!lintResult) return false; const fixes = lintResult[1].get(diagnostic); @@ -267,7 +274,7 @@ export function createLinter( fileName: string, start: number, end: number, - diagnostics?: ts.Diagnostic[], + diagnostics?: Diagnostic[], ) { const lintResult = lintResults.get(fileName); if (!lintResult) { @@ -276,7 +283,7 @@ export function createLinter( const file = lintResult[0]; const configs = getConfigsForFile(fileName); - const result: ts.CodeFixAction[] = []; + const result: CodeFixAction[] = []; for (const [diagnostic, actions] of lintResult[1]) { if (diagnostics?.length && !diagnostics.includes(diagnostic)) { @@ -290,7 +297,7 @@ export function createLinter( || (start >= diagStart && start <= diagEnd) || (end >= diagStart && end <= diagEnd) ) { - let codeFixes: ts.CodeFixAction[] = []; + let codeFixes: CodeFixAction[] = []; for (const action of actions) { codeFixes.push({ fixName: `tsslint:${diagnostic.code}`, @@ -319,7 +326,7 @@ export function createLinter( return []; } - const result: ts.RefactorActionInfo[] = []; + const result: RefactorAction[] = []; for (let i = 0; i < lintResult[2].length; i++) { const refactor = lintResult[2][i]; @@ -366,7 +373,7 @@ export function createLinter( // Pre-suppression rule output from the most recent `lint(fileName)`. // Cache layer keys writes off this so suppressed-but-fired // diagnostics survive into the next session's `prevDiagnostics`. - getRawLintResult(fileName: string): readonly ts.DiagnosticWithLocation[] { + getRawLintResult(fileName: string): readonly Diagnostic[] { return rawDiagnosticsByFile.get(fileName) ?? []; }, }; @@ -410,7 +417,7 @@ export function createLinter( function _minimatch(pattern: string) { let normalized = normalizedPath.get(pattern); if (!normalized) { - normalized = ts.server.toNormalizedPath(path.resolve(rootDir, pattern)); + normalized = path.resolve(rootDir, pattern).replace(/\\/g, '/'); normalizedPath.set(pattern, normalized); } return minimatch.minimatch(fileName, normalized, { dot: true }); @@ -428,7 +435,7 @@ export function createLinter( } } -export function combineCodeFixes(fileName: string, fixes: ts.CodeFixAction[]) { +export function combineCodeFixes(fileName: string, fixes: CodeFixAction[]) { // Match ESLint's `compareMessagesByFixRange` order so overlap resolution // agrees with applyFixes: sort ASC by start, tie-break ASC by end. The // earliest-starting fix wins; later overlapping fixes are dropped. (e.g. @@ -448,7 +455,7 @@ export function combineCodeFixes(fileName: string, fixes: ts.CodeFixAction[]) { }); let lastChangeEndAt = -1; - let finalTextChanges: ts.TextChange[] = []; + let finalTextChanges: TextChange[] = []; for (const change of changes) { const textChanges = [...change.textChanges].sort((a, b) => a.span.start - b.span.start); @@ -463,21 +470,10 @@ export function combineCodeFixes(fileName: string, fixes: ts.CodeFixAction[]) { return finalTextChanges; } -export function applyTextChanges(baseSnapshot: ts.IScriptSnapshot, textChanges: ts.TextChange[]): ts.IScriptSnapshot { - textChanges = [...textChanges].sort((a, b) => b.span.start - a.span.start); - let text = baseSnapshot.getText(0, baseSnapshot.getLength()); - for (const change of textChanges) { +export function applyTextChanges(text: string, textChanges: readonly TextChange[]): string { + const sorted = [...textChanges].sort((a, b) => b.span.start - a.span.start); + for (const change of sorted) { text = text.slice(0, change.span.start) + change.newText + text.slice(change.span.start + change.span.length); } - return { - getText(start, end) { - return text.substring(start, end); - }, - getLength() { - return text.length; - }, - getChangeRange() { - return undefined; - }, - }; + return text; } diff --git a/packages/core/package.json b/packages/core/package.json index 9a5968c4..27572235 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -3,7 +3,7 @@ "version": "3.1.4", "license": "MIT", "engines": { - "node": ">=22.6.0" + "node": ">=22.12.0" }, "files": [ "index.js", @@ -17,5 +17,11 @@ "dependencies": { "@tsslint/types": "3.1.4", "minimatch": "^10.0.1" + }, + "peerDependencies": { + "typescript": "^7.1.0-beta" + }, + "devDependencies": { + "typescript": "7.1.0-beta" } } diff --git a/packages/core/test/builder-program-poc.test.ts b/packages/core/test/builder-program-poc.test.ts deleted file mode 100644 index 3437f722..00000000 --- a/packages/core/test/builder-program-poc.test.ts +++ /dev/null @@ -1,289 +0,0 @@ -// POC for layer 2 of the cache rebuild — verify that -// `ts.createSemanticDiagnosticsBuilderProgram` can wrap the program -// owned by `ts.createLanguageService` without double-building, and -// that affected-file iteration correctly identifies which files' -// type-relevant inputs have changed since the previous pass. -// -// This is the integration risk flagged in CACHE.md item 7. Validate -// in isolation before wiring through the CLI. -// -// Run via: -// node packages/core/test/builder-program-poc.test.js - -import * as ts from 'typescript'; - -const failures: string[] = []; -function check(name: string, cond: boolean, detail?: string) { - if (cond) { - process.stdout.write('.'); - } - else { - failures.push(name + (detail ? ' — ' + detail : '')); - process.stdout.write('F'); - } -} - -// In-memory file system the LanguageServiceHost reads from. Tests -// mutate `files`/`versions` to simulate file edits. -const files: Record = {}; -const versions: Record = {}; -function write(name: string, text: string) { - files[name] = text; - versions[name] = (versions[name] ?? 0) + 1; -} - -const realLibPath = ts.getDefaultLibFilePath({ target: ts.ScriptTarget.Latest }); -const realLibContent = ts.sys.readFile(realLibPath) ?? ''; -let projectVersion = 0; - -const host: ts.LanguageServiceHost = { - getCompilationSettings: () => ({ - target: ts.ScriptTarget.Latest, - noEmit: true, - lib: [realLibPath.split(/[\\/]/).pop()!], - }), - getScriptFileNames: () => Object.keys(files), - getScriptVersion: n => String(versions[n] ?? 0), - getScriptSnapshot: n => { - if (n in files) return ts.ScriptSnapshot.fromString(files[n]); - if (n === realLibPath) return ts.ScriptSnapshot.fromString(realLibContent); - return undefined; - }, - getCurrentDirectory: () => '/', - getDefaultLibFileName: () => realLibPath, - fileExists: n => n in files || n === realLibPath, - readFile: n => (n in files ? files[n] : (n === realLibPath ? realLibContent : undefined)), - getProjectVersion: () => String(projectVersion), -}; - -const languageService = ts.createLanguageService(host); - -// Iterate `getSemanticDiagnosticsOfNextAffectedFile` until exhausted, -// returning the set of file names BuilderProgram considered affected. -function affectedFileNames(builder: ts.SemanticDiagnosticsBuilderProgram): Set { - const seen = new Set(); - while (true) { - const result = builder.getSemanticDiagnosticsOfNextAffectedFile(); - if (!result) break; - const a = result.affected; - if ('fileName' in a) seen.add(a.fileName); - else { - // `affected` was the whole Program (rare — happens when no - // file-granular diff is possible, e.g. compilerOptions changed). - seen.add(''); - } - } - return seen; -} - -// ── Test 1: builder.getProgram() === languageService.getProgram() ──────── -// -// The whole point of layer 2 is to share one Program across LS and -// BP — if BP rebuilt from scratch we'd pay the parse/bind cost twice. -{ - write('/a.ts', 'export const x: number = 1;'); - write('/b.ts', "import { x } from './a'; export const y = x + 1;"); - projectVersion++; - - const program = languageService.getProgram()!; - const builder = ts.createSemanticDiagnosticsBuilderProgram( - program, - { createHash: ts.sys.createHash }, - ); - check( - 'builder shares program with LS', - builder.getProgram() === program, - 'expected identity, got divergent Programs', - ); -} - -// ── Test 2: cold first run flags every file as affected ────────────────── -{ - write('/a.ts', 'export const x: number = 1;'); - write('/b.ts', "import { x } from './a'; export const y = x + 1;"); - projectVersion++; - - const program = languageService.getProgram()!; - const builder = ts.createSemanticDiagnosticsBuilderProgram( - program, - { createHash: ts.sys.createHash }, - ); - const affected = affectedFileNames(builder); - check( - 'cold run: a.ts is affected', - affected.has('/a.ts'), - `got: ${[...affected].join(', ')}`, - ); - check( - 'cold run: b.ts is affected', - affected.has('/b.ts'), - `got: ${[...affected].join(', ')}`, - ); -} - -// ── Test 3: edit a.ts → both a.ts and b.ts are affected ────────────────── -// -// The whole reason layer 2 exists: a change in a.ts should invalidate -// b.ts's type-aware rule cache because b.ts imports a's types. -{ - write('/a.ts', 'export const x: number = 1;'); - write('/b.ts', "import { x } from './a'; export const y = x + 1;"); - projectVersion++; - const oldProgram = languageService.getProgram()!; - const oldBuilder = ts.createSemanticDiagnosticsBuilderProgram( - oldProgram, - { createHash: ts.sys.createHash }, - ); - // Walk to drain — establishes the baseline state. - affectedFileNames(oldBuilder); - - // Edit a.ts: change exported type. - write('/a.ts', 'export const x: string = "1";'); - projectVersion++; - const newProgram = languageService.getProgram()!; - check( - 'edit produced a new Program', - newProgram !== oldProgram, - 'LS did not bump program after projectVersion change', - ); - - const newBuilder = ts.createSemanticDiagnosticsBuilderProgram( - newProgram, - { createHash: ts.sys.createHash }, - oldBuilder, - ); - const affected = affectedFileNames(newBuilder); - check( - 'edited a.ts is in affected set', - affected.has('/a.ts'), - `got: ${[...affected].join(', ')}`, - ); - check( - 'b.ts (importer of a.ts) is in affected set', - affected.has('/b.ts'), - `got: ${[...affected].join(', ')}`, - ); -} - -// ── Test 4: identical re-run after baseline → nothing affected ─────────── -{ - write('/a.ts', 'export const x: number = 1;'); - write('/b.ts', "import { x } from './a'; export const y = x + 1;"); - projectVersion++; - const oldProgram = languageService.getProgram()!; - const oldBuilder = ts.createSemanticDiagnosticsBuilderProgram( - oldProgram, - { createHash: ts.sys.createHash }, - ); - affectedFileNames(oldBuilder); - - // No edits. - const newProgram = languageService.getProgram()!; - const newBuilder = ts.createSemanticDiagnosticsBuilderProgram( - newProgram, - { createHash: ts.sys.createHash }, - oldBuilder, - ); - const affected = affectedFileNames(newBuilder); - check( - 'unchanged second pass: no files affected', - affected.size === 0, - `got ${affected.size} affected: ${[...affected].join(', ')}`, - ); -} - -// ── Test 5: edit unrelated file does NOT affect imports ────────────────── -// -// b.ts imports a.ts, c.ts is independent. Changing c.ts must not put -// b.ts in the affected set. -{ - write('/a.ts', 'export const x: number = 1;'); - write('/b.ts', "import { x } from './a'; export const y = x + 1;"); - write('/c.ts', 'export const z = 42;'); - projectVersion++; - const oldBuilder = ts.createSemanticDiagnosticsBuilderProgram( - languageService.getProgram()!, - { createHash: ts.sys.createHash }, - ); - affectedFileNames(oldBuilder); - - // Edit c.ts only. - write('/c.ts', 'export const z = 100;'); - projectVersion++; - const newBuilder = ts.createSemanticDiagnosticsBuilderProgram( - languageService.getProgram()!, - { createHash: ts.sys.createHash }, - oldBuilder, - ); - const affected = affectedFileNames(newBuilder); - check( - 'c.ts edit affects c.ts', - affected.has('/c.ts'), - `got: ${[...affected].join(', ')}`, - ); - check( - 'c.ts edit does NOT affect b.ts (no import dep)', - !affected.has('/b.ts'), - `got: ${[...affected].join(', ')}`, - ); - check( - 'c.ts edit does NOT affect a.ts', - !affected.has('/a.ts'), - `got: ${[...affected].join(', ')}`, - ); -} - -// ── Test 6: edit globals.d.ts → all files using globals are affected ───── -// -// The motivating soundness case: ambient declarations don't show up -// in any file's `imports`, but BuilderProgram's reference graph -// tracks them transitively. Editing the global declaration must -// propagate to every consumer. -{ - files['/globals.d.ts'] = 'declare const FOO: number;'; - versions['/globals.d.ts'] = (versions['/globals.d.ts'] ?? 0) + 1; - write('/use1.ts', 'export const a = FOO + 1;'); - write('/use2.ts', 'export const b = FOO * 2;'); - write('/no_globals.ts', 'export const c = 42;'); - projectVersion++; - const oldBuilder = ts.createSemanticDiagnosticsBuilderProgram( - languageService.getProgram()!, - { createHash: ts.sys.createHash }, - ); - affectedFileNames(oldBuilder); - - // Change globals.d.ts: type changes from number to string. - files['/globals.d.ts'] = 'declare const FOO: string;'; - versions['/globals.d.ts']++; - projectVersion++; - const newBuilder = ts.createSemanticDiagnosticsBuilderProgram( - languageService.getProgram()!, - { createHash: ts.sys.createHash }, - oldBuilder, - ); - const affected = affectedFileNames(newBuilder); - check( - 'globals.d.ts edit: globals.d.ts is affected', - affected.has('/globals.d.ts'), - `got: ${[...affected].join(', ')}`, - ); - check( - 'globals.d.ts edit: use1.ts is affected (consumer of FOO)', - affected.has('/use1.ts'), - `got: ${[...affected].join(', ')} — this is the soundness case the per-file mtime cache misses`, - ); - check( - 'globals.d.ts edit: use2.ts is affected (consumer of FOO)', - affected.has('/use2.ts'), - `got: ${[...affected].join(', ')}`, - ); -} - -// ── Done ──────────────────────────────────────────────────────────────── -process.stdout.write('\n'); -if (failures.length) { - console.error(`\n${failures.length} failure(s):`); - for (const f of failures) console.error(' - ' + f); - process.exit(1); -} -console.log('OK'); diff --git a/packages/core/test/probe.test.ts b/packages/core/test/probe.test.ts index 9a734d30..8afc2cc7 100644 --- a/packages/core/test/probe.test.ts +++ b/packages/core/test/probe.test.ts @@ -10,7 +10,7 @@ // node packages/core/test/probe.test.js import type { Config, RuleContext } from '@tsslint/types'; -import * as ts from 'typescript'; +import { createProject } from './project'; const core = require('../index.js') as typeof import('../index.js'); @@ -25,34 +25,9 @@ function check(name: string, cond: boolean, detail?: string) { } } -function makeContext(files: Record) { - const realLibPath = ts.getDefaultLibFilePath({ target: ts.ScriptTarget.Latest }); - const realLibContent = ts.sys.readFile(realLibPath) ?? ''; - const host: ts.LanguageServiceHost = { - getCompilationSettings: () => ({ - target: ts.ScriptTarget.Latest, - noEmit: true, - lib: [realLibPath.split(/[\\/]/).pop()!], - }), - getScriptFileNames: () => Object.keys(files), - getScriptVersion: () => '1', - getScriptSnapshot: n => { - if (n in files) return ts.ScriptSnapshot.fromString(files[n]); - if (n === realLibPath) return ts.ScriptSnapshot.fromString(realLibContent); - return undefined; - }, - getCurrentDirectory: () => '/', - getDefaultLibFileName: () => realLibPath, - fileExists: n => n in files || n === realLibPath, - readFile: n => (n in files ? files[n] : (n === realLibPath ? realLibContent : undefined)), - }; - const languageService = ts.createLanguageService(host); - return { typescript: ts, languageServiceHost: host, languageService }; -} - // ── Test 1: rule that doesn't read program → not classified ────────────── { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); const config: Config = { rules: { plain: ((rctx: RuleContext) => { @@ -60,8 +35,8 @@ function makeContext(files: Record) { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); - linter.lint('/a.ts'); + const linter = core.createLinter(project.ctx, project.rootDir, config); + linter.lint(project.target('a.ts')); check( 'plain rule not classified type-aware', !linter.getTypeAwareRules().has('plain'), @@ -70,7 +45,7 @@ function makeContext(files: Record) { // ── Test 2: rule reads program → classified type-aware ──────────────────── { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); const config: Config = { rules: { 'type-aware': ((rctx: RuleContext) => { @@ -79,8 +54,8 @@ function makeContext(files: Record) { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); - linter.lint('/a.ts'); + const linter = core.createLinter(project.ctx, project.rootDir, config); + linter.lint(project.target('a.ts')); check( 'rule that read program classified type-aware', linter.getTypeAwareRules().has('type-aware'), @@ -89,15 +64,15 @@ function makeContext(files: Record) { // ── Test 3: classification sticks across files in same session ─────────── { - const ctx = makeContext({ - '/a.ts': 'const x = 1;', - '/b.ts': 'const y = 2;', + const project = createProject({ + 'a.ts': 'const x = 1;', + 'b.ts': 'const y = 2;', }); let touched = 0; const config: Config = { rules: { 'sometimes-typed': ((rctx: RuleContext) => { - if (rctx.file.fileName === '/a.ts') { + if (rctx.file.fileName.endsWith('/a.ts')) { touched++; void rctx.program; } @@ -105,9 +80,9 @@ function makeContext(files: Record) { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); - linter.lint('/a.ts'); - linter.lint('/b.ts'); + const linter = core.createLinter(project.ctx, project.rootDir, config); + linter.lint(project.target('a.ts')); + linter.lint(project.target('b.ts')); check('rule touched program once', touched === 1); check( 'classification sticks past the touching file', @@ -117,7 +92,7 @@ function makeContext(files: Record) { // ── Test 4: initialTypeAwareRules seeded from prior session is preserved ─ { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); let ran = 0; const config: Config = { rules: { @@ -127,8 +102,8 @@ function makeContext(files: Record) { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => [], ['syntactic-now']); - linter.lint('/a.ts'); + const linter = core.createLinter(project.ctx, project.rootDir, config, ['syntactic-now']); + linter.lint(project.target('a.ts')); check('rule still ran', ran === 1); check( 'seeded classification preserved', @@ -146,25 +121,25 @@ function makeContext(files: Record) { // program flips classification to type-aware mid-session, and it // stays sticky. { - const ctx = makeContext({ - '/skip.ts': 'const x = 1;', - '/check.ts': 'const y = 2;', + const project = createProject({ + 'skip.ts': 'const x = 1;', + 'check.ts': 'const y = 2;', }); const config: Config = { rules: { 'mixed-mode': ((rctx: RuleContext) => { // Cheap pre-filter — only files matching the predicate // take the type-aware branch. /skip.ts early-returns. - if (rctx.file.fileName === '/skip.ts') return; + if (rctx.file.fileName.endsWith('/skip.ts')) return; void rctx.program; rctx.report('typed', 0, 1); }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); + const linter = core.createLinter(project.ctx, project.rootDir, config); // Process the early-return file FIRST. - linter.lint('/skip.ts'); + linter.lint(project.target('skip.ts')); check( 'after early-return file: not yet classified', !linter.getTypeAwareRules().has('mixed-mode'), @@ -172,14 +147,14 @@ function makeContext(files: Record) { ); // Then the type-aware file. Classification flips mid-session. - linter.lint('/check.ts'); + linter.lint(project.target('check.ts')); check( 'after type-aware file: classified type-aware', linter.getTypeAwareRules().has('mixed-mode'), ); // And it stays — re-linting the early-return file doesn't unclassify. - linter.lint('/skip.ts'); + linter.lint(project.target('skip.ts')); check( 'classification sticks even after a subsequent early-return invocation', linter.getTypeAwareRules().has('mixed-mode'), @@ -189,7 +164,7 @@ function makeContext(files: Record) { // ── Test 6: getTypeAwareRules returns live set; mutations not allowed ──── { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); const config: Config = { rules: { r: ((rctx: RuleContext) => { @@ -198,10 +173,10 @@ function makeContext(files: Record) { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); + const linter = core.createLinter(project.ctx, project.rootDir, config); const before = linter.getTypeAwareRules().size; check('initially empty', before === 0); - linter.lint('/a.ts'); + linter.lint(project.target('a.ts')); const after = linter.getTypeAwareRules().size; check('grows after probe', after === 1); } diff --git a/packages/core/test/project.ts b/packages/core/test/project.ts new file mode 100644 index 00000000..f46f7e00 --- /dev/null +++ b/packages/core/test/project.ts @@ -0,0 +1,53 @@ +// A real on-disk TypeScript 7 project for core tests. One API process +// per test run; temp dirs are removed when the process exits. +import type { LintTarget } from '../index.js'; +import type { LinterContext } from '@tsslint/types'; +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; +import * as ts from 'typescript/ast'; +import { API } from 'typescript/sync'; + +let api: API | undefined; +const tempDirs: string[] = []; + +function getApi(): API { + if (!api) { + api = new API({ cwd: process.cwd() }); + process.once('exit', () => { + // The child may already be gone when exiting via process.exit(). + try { + api?.close(); + } + catch {} + for (const dir of tempDirs) fs.rmSync(dir, { recursive: true, force: true }); + }); + } + return api; +} + +export function createProject(files: Record) { + const dir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'tsslint-core-test-'))); + tempDirs.push(dir); + for (const [name, text] of Object.entries(files)) { + fs.writeFileSync(path.join(dir, name), text); + } + const tsconfig = path.join(dir, 'tsconfig.json'); + fs.writeFileSync( + tsconfig, + JSON.stringify({ + compilerOptions: { target: 'esnext', lib: ['esnext'], noEmit: true }, + files: Object.keys(files), + }), + ); + const snapshot = getApi().createSnapshot({ openProjects: [tsconfig] }); + const { program, checker } = snapshot.getConfiguredProject(tsconfig)!; + const ctx: LinterContext = { typescript: ts, getProgram: () => program }; + return { + ctx, + rootDir: dir, + target(name: string): LintTarget { + return { program, checker, file: program.getSourceFile(path.join(dir, name))! }; + }, + }; +} diff --git a/packages/core/test/skip-rules.test.ts b/packages/core/test/skip-rules.test.ts index fceab6c5..6e840cc5 100644 --- a/packages/core/test/skip-rules.test.ts +++ b/packages/core/test/skip-rules.test.ts @@ -6,7 +6,7 @@ // node packages/core/test/skip-rules.test.js import type { Config, RuleContext } from '@tsslint/types'; -import * as ts from 'typescript'; +import { createProject } from './project'; const core = require('../index.js') as typeof import('../index.js'); @@ -21,33 +21,9 @@ function check(name: string, cond: boolean, detail?: string) { } } -function makeContext(files: Record) { - const realLibPath = ts.getDefaultLibFilePath({ target: ts.ScriptTarget.Latest }); - const realLibContent = ts.sys.readFile(realLibPath) ?? ''; - const host: ts.LanguageServiceHost = { - getCompilationSettings: () => ({ - target: ts.ScriptTarget.Latest, - noEmit: true, - lib: [realLibPath.split(/[\\/]/).pop()!], - }), - getScriptFileNames: () => Object.keys(files), - getScriptVersion: () => '1', - getScriptSnapshot: n => { - if (n in files) return ts.ScriptSnapshot.fromString(files[n]); - if (n === realLibPath) return ts.ScriptSnapshot.fromString(realLibContent); - return undefined; - }, - getCurrentDirectory: () => '/', - getDefaultLibFileName: () => realLibPath, - fileExists: n => n in files || n === realLibPath, - readFile: n => (n in files ? files[n] : (n === realLibPath ? realLibContent : undefined)), - }; - return { typescript: ts, languageServiceHost: host, languageService: ts.createLanguageService(host) }; -} - // ── Test 1: rule in skipRules doesn't run, no diagnostic in result ─────── { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); let runs = 0; const config: Config = { rules: { @@ -57,15 +33,15 @@ function makeContext(files: Record) { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); - const out = linter.lint('/a.ts', { skipRules: new Set(['r']) }); + const linter = core.createLinter(project.ctx, project.rootDir, config); + const out = linter.lint(project.target('a.ts'), { skipRules: new Set(['r']) }); check('skipped rule did not run', runs === 0); check('skipped rule produced no diagnostic', out.length === 0); } // ── Test 2: only the specified rule is skipped; others run normally ────── { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); let aRuns = 0; let bRuns = 0; const config: Config = { @@ -80,8 +56,8 @@ function makeContext(files: Record) { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); - const out = linter.lint('/a.ts', { skipRules: new Set(['a']) }); + const linter = core.createLinter(project.ctx, project.rootDir, config); + const out = linter.lint(project.target('a.ts'), { skipRules: new Set(['a']) }); check('a skipped', aRuns === 0); check('b ran', bRuns === 1); check('only b in result', out.length === 1 && String(out[0]?.code) === 'b'); @@ -89,7 +65,7 @@ function makeContext(files: Record) { // ── Test 3: lint without options runs everything (back-compat) ─────────── { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); let runs = 0; const config: Config = { rules: { @@ -99,8 +75,8 @@ function makeContext(files: Record) { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); - linter.lint('/a.ts'); + const linter = core.createLinter(project.ctx, project.rootDir, config); + linter.lint(project.target('a.ts')); check('lint without options runs rule', runs === 1); } @@ -110,7 +86,7 @@ function makeContext(files: Record) { // type-aware status comes from `initialTypeAwareRules` (or stays // uncalculated). Skipping doesn't undo a prior classification. { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); const config: Config = { rules: { r: ((rctx: RuleContext) => { @@ -120,14 +96,14 @@ function makeContext(files: Record) { }, }; // Seed as type-aware. Then skip the rule. Classification persists. - const linter = core.createLinter(ctx, '/', config, () => [], ['r']); - linter.lint('/a.ts', { skipRules: new Set(['r']) }); + const linter = core.createLinter(project.ctx, project.rootDir, config, ['r']); + linter.lint(project.target('a.ts'), { skipRules: new Set(['r']) }); check('seeded classification persists when skipped', linter.getTypeAwareRules().has('r')); } // ── Test 5: hasFixForDiagnostic returns true only for diags with fixes ─── { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); const config: Config = { rules: { fixable: ((rctx: RuleContext) => { @@ -138,12 +114,12 @@ function makeContext(files: Record) { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); - const out = linter.lint('/a.ts'); + const linter = core.createLinter(project.ctx, project.rootDir, config); + const out = linter.lint(project.target('a.ts')); const fixable = out.find(d => String(d.code) === 'fixable')!; const plain = out.find(d => String(d.code) === 'plain')!; - check('hasFixForDiagnostic true for fixable diag', linter.hasFixForDiagnostic('/a.ts', fixable) === true); - check('hasFixForDiagnostic false for plain diag', linter.hasFixForDiagnostic('/a.ts', plain) === false); + check('hasFixForDiagnostic true for fixable diag', linter.hasFixForDiagnostic(project.target('a.ts').file.fileName, fixable) === true); + check('hasFixForDiagnostic false for plain diag', linter.hasFixForDiagnostic(project.target('a.ts').file.fileName, plain) === false); } // ── Done ──────────────────────────────────────────────────────────────── diff --git a/packages/types/index.ts b/packages/types/index.ts index 57ed8c97..68f99e65 100644 --- a/packages/types/index.ts +++ b/packages/types/index.ts @@ -1,18 +1,9 @@ -import type { - CodeFixAction, - Diagnostic, - DiagnosticWithLocation, - FileTextChanges, - LanguageService, - LanguageServiceHost, - Program, - SourceFile, -} from 'typescript'; +import type { SourceFile } from 'typescript/ast'; +import type { Checker, DiagnosticCategory, Program } from 'typescript/sync'; export interface LinterContext { - typescript: typeof import('typescript'); - languageServiceHost: LanguageServiceHost; - languageService: LanguageService; + typescript: typeof import('typescript/ast'); + getProgram(): Program; } export interface Config { @@ -28,7 +19,7 @@ export interface Plugin { export interface PluginInstance { resolveRules?(fileName: string, rules: Record): Record; - resolveDiagnostics?(file: SourceFile, diagnostics: DiagnosticWithLocation[]): DiagnosticWithLocation[]; + resolveDiagnostics?(file: SourceFile, diagnostics: Diagnostic[]): Diagnostic[]; resolveCodeFixes?(file: SourceFile, diagnostic: Diagnostic, codeFixes: CodeFixAction[]): CodeFixAction[]; } @@ -41,8 +32,14 @@ export interface Rule { } export interface RuleContext { - typescript: typeof import('typescript'); + typescript: typeof import('typescript/ast'); program: Program; + checker: Checker; + /** + * For files produced by a tsconfig `contentMappers` entry, this is the + * transformed TypeScript. Report positions in its coordinates; TSSLint + * maps them back to the original file. + */ file: SourceFile; report(message: string, start: number, end: number): Reporter; } @@ -63,7 +60,59 @@ export interface Reporter { // Use when a diagnostic's correctness depends on inputs the layer-1 // mtime check doesn't track (external resources, env, sibling files // the rule reads directly via fs). For type-checker-derived findings - // just read `ctx.program` once — that re-classifies the rule - // type-aware, and layer 2 handles cross-file invalidation properly. + // just read `ctx.program` / `ctx.checker` once — that re-classifies the + // rule type-aware, and layer 2 handles cross-file invalidation properly. withoutCache(): Reporter; } + +export interface TextSpan { + start: number; + length: number; +} + +export interface TextChange { + span: TextSpan; + newText: string; +} + +export interface FileTextChanges { + fileName: string; + textChanges: readonly TextChange[]; +} + +export interface CodeFixAction { + fixName: string; + description: string; + changes: FileTextChanges[]; + fixId?: string; + fixAllDescription?: string; +} + +export interface RefactorAction { + name: string; + description: string; + kind?: string; +} + +export interface RelatedInformation { + category: DiagnosticCategory; + code: string | number; + messageText: string; + file: SourceFile | undefined; + start: number | undefined; + length: number | undefined; +} + +export interface Diagnostic { + category: DiagnosticCategory; + /** The rule id, or the TypeScript diagnostic code for compiler diagnostics. */ + code: string | number; + messageText: string; + file: SourceFile; + start: number; + length: number; + source: string; + relatedInformation?: RelatedInformation[]; + reportsUnnecessary?: boolean; + reportsDeprecated?: boolean; +} diff --git a/packages/types/package.json b/packages/types/package.json index e755f7ab..58708219 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -10,5 +10,11 @@ "type": "git", "url": "https://github.com/johnsoncodehk/tsslint.git", "directory": "packages/types" + }, + "peerDependencies": { + "typescript": "^7.1.0-beta" + }, + "devDependencies": { + "typescript": "7.1.0-beta" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0f15ee0d..92bd7d99 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,18 +17,12 @@ importers: '@types/node': specifier: latest version: 25.9.3 - '@typescript-eslint/eslint-plugin': - specifier: latest - version: 8.61.1(@typescript-eslint/parser@8.59.0(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3))(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) dprint: specifier: latest version: 0.54.0 - eslint-plugin-react-x: - specifier: ^4.2.3 - version: 4.2.3(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) typescript: - specifier: latest - version: 6.0.3 + specifier: 7.1.0-beta + version: 7.1.0-beta fixtures/compat-eslint: devDependencies: @@ -38,36 +32,6 @@ importers: '@tsslint/config': specifier: 3.1.4 version: link:../../packages/config - eslint-plugin-expect-type: - specifier: ^0.4.0 - version: 0.4.3(@typescript-eslint/parser@8.59.0(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3))(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - - fixtures/compat-tsl: - devDependencies: - '@tsslint/config': - specifier: 3.1.4 - version: link:../../packages/config - tsl: - specifier: ^1.0.28 - version: 1.0.30(typescript@6.0.3) - - fixtures/compat-tslint: - devDependencies: - '@tsslint/config': - specifier: 3.1.4 - version: link:../../packages/config - tslint: - specifier: ^6.1.3 - version: 6.1.3(typescript@6.0.3) - - fixtures/typescript-plugin: - devDependencies: - '@tsslint/typescript-plugin': - specifier: 3.1.4 - version: link:../../packages/typescript-plugin - typescript: - specifier: latest - version: 6.0.3 packages/cli: dependencies: @@ -83,34 +47,35 @@ importers: minimatch: specifier: ^10.0.1 version: 10.2.5 - typescript-native-bridge: - specifier: 6.0.3-bridge.16.tsgo.7.0.2 - version: 6.0.3-bridge.16.tsgo.7.0.2 + devDependencies: + typescript: + specifier: 7.1.0-beta + version: 7.1.0-beta packages/compat-eslint: dependencies: '@tsslint/types': specifier: 3.1.4 version: link:../types + '@typescript-eslint/scope-manager': + specifier: ^8.72.0 + version: 8.72.0 esquery: specifier: ^1.5.0 version: 1.7.0 devDependencies: + '@tsslint/estree-oracle': + specifier: workspace:* + version: link:../../tools/estree-oracle '@types/eslint': specifier: ^8.56.10 version: 8.56.12 - '@typescript-eslint/scope-manager': - specifier: ^8.16.0 - version: 8.59.0 - '@typescript-eslint/typescript-estree': - specifier: ^8.16.0 - version: 8.59.0(typescript@6.0.3) eslint: specifier: '>=9.39.0 <10.0.0' version: 9.39.4(jiti@2.6.1) typescript: - specifier: latest - version: 6.0.3 + specifier: 7.1.0-beta + version: 7.1.0-beta packages/config: dependencies: @@ -120,16 +85,13 @@ importers: minimatch: specifier: ^10.0.1 version: 10.2.5 - tsl: - specifier: ^1.0.28 - version: 1.0.30(typescript@6.0.3) devDependencies: '@tsslint/compat-eslint': specifier: 3.1.4 version: link:../compat-eslint - tslint: - specifier: ^6.1.3 - version: 6.1.3(typescript@6.0.3) + typescript: + specifier: 7.1.0-beta + version: 7.1.0-beta packages/core: dependencies: @@ -139,95 +101,37 @@ importers: minimatch: specifier: ^10.0.1 version: 10.2.5 + devDependencies: + typescript: + specifier: 7.1.0-beta + version: 7.1.0-beta - packages/types: {} - - packages/typescript-plugin: - dependencies: - '@tsslint/core': - specifier: 3.1.4 - version: link:../core - error-stack-parser: - specifier: ^2.1.4 - version: 2.1.4 + packages/types: devDependencies: - '@tsslint/config': - specifier: 3.1.4 - version: link:../config + typescript: + specifier: 7.1.0-beta + version: 7.1.0-beta - packages/vscode: + tools/estree-oracle: dependencies: - '@tsslint/typescript-plugin': - specifier: 3.1.4 - version: link:../typescript-plugin - devDependencies: - '@types/vscode': - specifier: ^1.82.0 - version: 1.116.0 - '@vscode/vsce': - specifier: latest - version: 3.9.2 + '@typescript-eslint/parser': + specifier: ^8.72.0 + version: 8.72.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) + '@typescript-eslint/typescript-estree': + specifier: ^8.72.0 + version: 8.72.0(typescript@6.0.3) + '@typescript-eslint/visitor-keys': + specifier: ^8.72.0 + version: 8.72.0 + eslint: + specifier: '>=9.39.0 <10.0.0' + version: 9.39.4(jiti@2.6.1) + typescript: + specifier: 6.0.3 + version: 6.0.3 packages: - '@azu/format-text@1.0.2': - resolution: {integrity: sha512-Swi4N7Edy1Eqq82GxgEECXSSLyn6GOb5htRFPzBDdUkECGXtlf12ynO5oJSpWKPwCaUssOu7NfhDcCWpIC6Ywg==} - - '@azu/style-format@1.0.1': - resolution: {integrity: sha512-AHcTojlNBdD/3/KxIKlg8sxIWHfOtQszLvOpagLTO+bjC3u7SAszu1lf//u7JJC50aUSH+BVWDD/KvaA6Gfn5g==} - - '@azure/abort-controller@2.1.2': - resolution: {integrity: sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==} - engines: {node: '>=18.0.0'} - - '@azure/core-auth@1.10.1': - resolution: {integrity: sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==} - engines: {node: '>=20.0.0'} - - '@azure/core-client@1.10.1': - resolution: {integrity: sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==} - engines: {node: '>=20.0.0'} - - '@azure/core-rest-pipeline@1.23.0': - resolution: {integrity: sha512-Evs1INHo+jUjwHi1T6SG6Ua/LHOQBCLuKEEE6efIpt4ZOoNonaT1kP32GoOcdNDbfqsD2445CPri3MubBy5DEQ==} - engines: {node: '>=20.0.0'} - - '@azure/core-tracing@1.3.1': - resolution: {integrity: sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==} - engines: {node: '>=20.0.0'} - - '@azure/core-util@1.13.1': - resolution: {integrity: sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==} - engines: {node: '>=20.0.0'} - - '@azure/identity@4.13.1': - resolution: {integrity: sha512-5C/2WD5Vb1lHnZS16dNQRPMjN6oV/Upba+C9nBIs15PmOi6A3ZGs4Lr2u60zw4S04gi+u3cEXiqTVP7M4Pz3kw==} - engines: {node: '>=20.0.0'} - - '@azure/logger@1.3.0': - resolution: {integrity: sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==} - engines: {node: '>=20.0.0'} - - '@azure/msal-browser@5.8.0': - resolution: {integrity: sha512-X7IZV77bN56l7sbLjkcbQJX1t3U4tgxqztDr/XFbUcUfKk+z2FavcLgKP+OYUNj0wl/pEEtV9lldW9siY8BuHQ==} - engines: {node: '>=0.8.0'} - - '@azure/msal-common@16.5.1': - resolution: {integrity: sha512-WS9w9SfI8SEYO7mTnxGeZ3UwQfhAVYCWglYF2/7GNx3ioHiAs2gPkl9eSwVs8cPrmiGh+zi9ai/OOKoq4cyzDw==} - engines: {node: '>=0.8.0'} - - '@azure/msal-node@5.1.4': - resolution: {integrity: sha512-G4LXGGggok1QC48uKu64/SV2DPRDlddmV8EieK8pflsNYMj9/Zz+Y9OHoEBhT15h+zpdwXXLYA/7PJCR/yZ8aw==} - engines: {node: '>=20'} - - '@babel/code-frame@7.29.0': - resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} - engines: {node: '>=6.9.0'} - '@conventional-changelog/git-client@2.7.0': resolution: {integrity: sha512-j7A8/LBEQ+3rugMzPXoKYzyUPpw/0CBQCyvtTR7Lmu4olG4yRC/Tfkq79Mr3yuPs0SUitlO2HwGP3gitMJnRFw==} engines: {node: '>=18'} @@ -295,162 +199,6 @@ packages: cpu: [x64] os: [win32] - '@esbuild/aix-ppc64@0.27.7': - resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.27.7': - resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.27.7': - resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.27.7': - resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.27.7': - resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.27.7': - resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.27.7': - resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.27.7': - resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.27.7': - resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.27.7': - resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.27.7': - resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.27.7': - resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.27.7': - resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.27.7': - resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.27.7': - resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.27.7': - resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.27.7': - resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.27.7': - resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.27.7': - resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.27.7': - resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.27.7': - resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openharmony-arm64@0.27.7': - resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.27.7': - resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.27.7': - resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.27.7': - resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.27.7': - resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - '@eslint-community/eslint-utils@4.9.1': resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -461,65 +209,18 @@ packages: resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint-react/ast@4.2.3': - resolution: {integrity: sha512-/XHJPFX8lsp+c/gMzFOnIxqH7YIXVX8SlMHuZ6XTUlYHkGquhydTtgso0VFiLQN1z3dThrybdgBq+JD+LSwK2w==} - engines: {node: '>=22.0.0'} - peerDependencies: - eslint: ^10.0.0 - typescript: '*' - - '@eslint-react/core@4.2.3': - resolution: {integrity: sha512-r0cgJlCemBb61f0qCrXS95hNq2ajIku5V7Tk45fROQu4HIV55ILJeN2ceea1LKmgRWy/pQw8+SvImronwWo16A==} - engines: {node: '>=22.0.0'} - peerDependencies: - eslint: ^10.0.0 - typescript: '*' - - '@eslint-react/jsx@4.2.3': - resolution: {integrity: sha512-lSwRo/PAwf1EvXRxpXA5yBhPIxahFuC4uHh84nc5OxE0mJ7YEmzmASR+ug3QOnVnfDsJDVo6AWVR7PSL99YkOQ==} - engines: {node: '>=22.0.0'} - peerDependencies: - eslint: ^10.0.0 - typescript: '*' - - '@eslint-react/shared@4.2.3': - resolution: {integrity: sha512-6HermdKaTWkID0coAK46ynA9XIwUWGgA2Y+NK6qcmL/qbYzyRYs4hq+SmLMvZZ8DV/SFOaHRXl9iCTvjf6DvXQ==} - engines: {node: '>=22.0.0'} - peerDependencies: - eslint: ^10.0.0 - typescript: '*' - - '@eslint-react/var@4.2.3': - resolution: {integrity: sha512-zkQki2eYbQrMW4O6DCZDQzslFvw0sWAlvW/WWjocEIGHqRGC3IHWcRt3xsq8JPNOW4WjF4/LZ8czkyLoINV9rw==} - engines: {node: '>=22.0.0'} - peerDependencies: - eslint: ^10.0.0 - typescript: '*' - '@eslint/config-array@0.21.2': resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-array@0.23.5': - resolution: {integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/config-helpers@0.4.2': resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-helpers@0.5.5': - resolution: {integrity: sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/core@0.17.0': resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@1.2.1': - resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/eslintrc@3.3.5': resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -532,18 +233,10 @@ packages: resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/object-schema@3.0.5': - resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@eslint/plugin-kit@0.4.1': resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.7.1': - resolution: {integrity: sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@gar/promise-retry@1.0.3': resolution: {integrity: sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA==} engines: {node: ^20.17.0 || >=22.9.0} @@ -673,18 +366,6 @@ packages: resolution: {integrity: sha512-ZZUE/ajuCFj1WLNY08wKFmiSD+RYeT0mPosHKYbXejzUGKIBcuOdTApxvRcBr/G2pAWqQowCtE1sOM7AUbvzsw==} engines: {node: ^22.17.0 || >=24.0.0} - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - '@npmcli/agent@4.0.0': resolution: {integrity: sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA==} engines: {node: ^20.17.0 || >=22.9.0} @@ -798,51 +479,6 @@ packages: '@octokit/types@16.0.0': resolution: {integrity: sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==} - '@secretlint/config-creator@10.2.2': - resolution: {integrity: sha512-BynOBe7Hn3LJjb3CqCHZjeNB09s/vgf0baBaHVw67w7gHF0d25c3ZsZ5+vv8TgwSchRdUCRrbbcq5i2B1fJ2QQ==} - engines: {node: '>=20.0.0'} - - '@secretlint/config-loader@10.2.2': - resolution: {integrity: sha512-ndjjQNgLg4DIcMJp4iaRD6xb9ijWQZVbd9694Ol2IszBIbGPPkwZHzJYKICbTBmh6AH/pLr0CiCaWdGJU7RbpQ==} - engines: {node: '>=20.0.0'} - - '@secretlint/core@10.2.2': - resolution: {integrity: sha512-6rdwBwLP9+TO3rRjMVW1tX+lQeo5gBbxl1I5F8nh8bgGtKwdlCMhMKsBWzWg1ostxx/tIG7OjZI0/BxsP8bUgw==} - engines: {node: '>=20.0.0'} - - '@secretlint/formatter@10.2.2': - resolution: {integrity: sha512-10f/eKV+8YdGKNQmoDUD1QnYL7TzhI2kzyx95vsJKbEa8akzLAR5ZrWIZ3LbcMmBLzxlSQMMccRmi05yDQ5YDA==} - engines: {node: '>=20.0.0'} - - '@secretlint/node@10.2.2': - resolution: {integrity: sha512-eZGJQgcg/3WRBwX1bRnss7RmHHK/YlP/l7zOQsrjexYt6l+JJa5YhUmHbuGXS94yW0++3YkEJp0kQGYhiw1DMQ==} - engines: {node: '>=20.0.0'} - - '@secretlint/profiler@10.2.2': - resolution: {integrity: sha512-qm9rWfkh/o8OvzMIfY8a5bCmgIniSpltbVlUVl983zDG1bUuQNd1/5lUEeWx5o/WJ99bXxS7yNI4/KIXfHexig==} - - '@secretlint/resolver@10.2.2': - resolution: {integrity: sha512-3md0cp12e+Ae5V+crPQYGd6aaO7ahw95s28OlULGyclyyUtf861UoRGS2prnUrKh7MZb23kdDOyGCYb9br5e4w==} - - '@secretlint/secretlint-formatter-sarif@10.2.2': - resolution: {integrity: sha512-ojiF9TGRKJJw308DnYBucHxkpNovDNu1XvPh7IfUp0A12gzTtxuWDqdpuVezL7/IP8Ua7mp5/VkDMN9OLp1doQ==} - - '@secretlint/secretlint-rule-no-dotenv@10.2.2': - resolution: {integrity: sha512-KJRbIShA9DVc5Va3yArtJ6QDzGjg3PRa1uYp9As4RsyKtKSSZjI64jVca57FZ8gbuk4em0/0Jq+uy6485wxIdg==} - engines: {node: '>=20.0.0'} - - '@secretlint/secretlint-rule-preset-recommend@10.2.2': - resolution: {integrity: sha512-K3jPqjva8bQndDKJqctnGfwuAxU2n9XNCPtbXVI5JvC7FnQiNg/yWlQPbMUlBXtBoBGFYp08A94m6fvtc9v+zA==} - engines: {node: '>=20.0.0'} - - '@secretlint/source-creator@10.2.2': - resolution: {integrity: sha512-h6I87xJfwfUTgQ7irWq7UTdq/Bm1RuQ/fYhA3dtTIAop5BwSFmZyrchph4WcoEvbN460BWKmk4RYSvPElIIvxw==} - engines: {node: '>=20.0.0'} - - '@secretlint/types@10.2.2': - resolution: {integrity: sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg==} - engines: {node: '>=20.0.0'} - '@sigstore/bundle@4.0.0': resolution: {integrity: sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A==} engines: {node: ^20.17.0 || >=22.9.0} @@ -879,25 +515,6 @@ packages: resolution: {integrity: sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==} engines: {node: '>=18'} - '@sindresorhus/merge-streams@2.3.0': - resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} - engines: {node: '>=18'} - - '@textlint/ast-node-types@15.5.4': - resolution: {integrity: sha512-bVtB6VEy9U9DpW8cTt25k5T+lz86zV5w6ImePZqY1AXzSuPhqQNT77lkMPxonXzUducEIlSvUu3o7sKw3y9+Sw==} - - '@textlint/linter-formatter@15.5.4': - resolution: {integrity: sha512-D9qJedKBLmAo+kiudop4UKgSxXMi4O8U86KrCidVXZ9RsK0NSVIw6+r2rlMUOExq79iEY81FRENyzmNVRxDBsg==} - - '@textlint/module-interop@15.5.4': - resolution: {integrity: sha512-JyAUd26ll3IFF87LP0uGoa8Tzw5ZKiYvGs6v8jLlzyND1lUYCI4+2oIAslrODLkf0qwoCaJrBQWM3wsw+asVGQ==} - - '@textlint/resolver@15.5.4': - resolution: {integrity: sha512-5GUagtpQuYcmhlOzBGdmVBvDu5lKgVTjwbxtdfoidN4OIqblIxThJHHjazU+ic+/bCIIzI2JcOjHGSaRmE8Gcg==} - - '@textlint/types@15.5.4': - resolution: {integrity: sha512-mY28j2U7nrWmZbxyKnRvB8vJxJab4AxqOobLfb6iozrLelJbqxcOTvBQednadWPfAk9XWaZVMqUr9Nird3mutg==} - '@tufjs/canonical-json@2.0.0': resolution: {integrity: sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==} engines: {node: ^16.14.0 || >=18.0.0} @@ -909,9 +526,6 @@ packages: '@types/eslint@8.56.12': resolution: {integrity: sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==} - '@types/esrecurse@4.3.1': - resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} - '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -928,207 +542,169 @@ packages: resolution: {integrity: sha512-EULJ8LApcVEPbrfND0cRQqutIOdiIgJ1Mgrhpy755r14xMohPTEpkV/k28SJvuOs9bHRFW8x+KeDAEPiGQPB9Q==} deprecated: This is a stub types definition. parse-path provides its own type definitions, so you do not need this installed. - '@types/sarif@2.1.7': - resolution: {integrity: sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==} - - '@types/vscode@1.116.0': - resolution: {integrity: sha512-sYHp4MO6BqJ2PD7Hjt0hlIS3tMaYsVPJrd0RUjDJ8HtOYnyJIEej0bLSccM8rE77WrC+Xox/kdBwEFDO8MsxNA==} - - '@typescript-eslint/eslint-plugin@8.61.1': - resolution: {integrity: sha512-ZPlVl3PB3et/59Ne0fv/sci6ZXz4T4Hp4nTJ56i/Y0gR89ARb+KphojTq6j+56E5PIezmOIOOWyY+aWQFd+IkQ==} + '@typescript-eslint/parser@8.72.0': + resolution: {integrity: sha512-0O+x3ynciD6zuXb1CKSu165haQV6ey53DKhHHUnujP7U+XlltJG+/keT0Xhd1HXCze+IHf7Tvt39ovyepX/LNw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.61.1 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.59.0': - resolution: {integrity: sha512-TI1XGwKbDpo9tRW8UDIXCOeLk55qe9ZFGs8MTKU6/M08HWTw52DD/IYhfQtOEhEdPhLMT26Ka/x7p70nd3dzDg==} + '@typescript-eslint/project-service@8.72.0': + resolution: {integrity: sha512-bzHngBbmIgzj6TsTjVUCntGx6FFLgE7mPjpO0PzHycOaZHXqS43XnrkFIHRS16LLr/xO6FHSXG3GJFo/F4bSBQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.59.0': - resolution: {integrity: sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw==} + '@typescript-eslint/scope-manager@8.72.0': + resolution: {integrity: sha512-JewPyJOYcXBSAS7PIgufscHOwZqhTGyBRiQJhhuDln66tMchxvvt7nJ90JKKIoOelmdHA3kAQbm9cEUHc4JSEA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.61.1': - resolution: {integrity: sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==} + '@typescript-eslint/tsconfig-utils@8.72.0': + resolution: {integrity: sha512-0vbxCf7xLVPSjcOdm6eQbgViH8EDJ8xnl1laLT9yoqSz2YmcSfSg5Vdl0JUvZf6rcuJ0ZivJIuMD4edTg8obaA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.59.0': - resolution: {integrity: sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/scope-manager@8.61.1': - resolution: {integrity: sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==} + '@typescript-eslint/types@8.72.0': + resolution: {integrity: sha512-Adfoy59kxNEjT5E5fnyrp7GpHYuWs8ONbdohu4ez6IU46qDq8LltG90tLmkucZB2uhYL8mqBjtXqGd8yhfjXqQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.59.0': - resolution: {integrity: sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg==} + '@typescript-eslint/typescript-estree@8.72.0': + resolution: {integrity: sha512-gwLfFgWcw15tN2KXDCJvxEOQjPn+P3j/n3eSqkZ5sX4VpW9Cosodr+tWwBZRvJT3TcKx6HnPRGZMCNAiP1Xhug==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/tsconfig-utils@8.61.1': - resolution: {integrity: sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==} + '@typescript-eslint/visitor-keys@8.72.0': + resolution: {integrity: sha512-E2mroviasaWecDzTGiGMtuEguuLVOeRggnrIn93CJuxYJzQBHbyymlT08aVQydbBal5538o+JkBKXh2sRuCyIA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.59.0': - resolution: {integrity: sha512-3TRiZaQSltGqGeNrJzzr1+8YcEobKH9rHnqIp/1psfKFmhRQDNMGP5hBufanYTGznwShzVLs3Mz+gDN7HkWfXg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.1.0' + '@typescript/typescript-aix-ppc64@7.1.0-beta': + resolution: {integrity: sha512-DXDG+jIWxBHGdoO70tcHON33rCnSzsEZj9P13oyf/r/KEGIo6awsdSNVNjAw0uMwEpqRu75IrgCcW58YcVkZHg==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [aix] - '@typescript-eslint/type-utils@8.61.1': - resolution: {integrity: sha512-GYRicKmVK0C4fsKgaACaknOUAq9Oa2kwsjnpFhFcS/5p4Ht5IP9OVLbgIgcK4SRk92nVHFluurg1lumD9dBcLw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.1.0' + '@typescript/typescript-android-arm64@7.1.0-beta': + resolution: {integrity: sha512-UZx6PxoQavuvaVTrSf3aw0Oy/J/uJpRylipA0kzc038jMvZXh0UtE8vnQp20FqtdgD5bhXZ0aJrRJyoTSUO58Q==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [android] - '@typescript-eslint/types@8.59.0': - resolution: {integrity: sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript/typescript-darwin-arm64@7.1.0-beta': + resolution: {integrity: sha512-q7mxblcyX0E7hXsY4k+Q6KWKK5mGmfVq+BDesdTgRVgjodk27aIxER77Zyj9+TgSTlCje8F7+JM6f6hl2BuNdw==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [darwin] - '@typescript-eslint/types@8.61.1': - resolution: {integrity: sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript/typescript-darwin-x64@7.1.0-beta': + resolution: {integrity: sha512-D5EYqG5Wm/guw9+0j8+Mn1Jbr/gfS/VtFTuoH8PJtFbcqeDaQGMulc7SW8q51QG6Y3wGOGX3nbsTOw6fIKWWAA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [darwin] - '@typescript-eslint/typescript-estree@8.59.0': - resolution: {integrity: sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.1.0' + '@typescript/typescript-freebsd-arm64@7.1.0-beta': + resolution: {integrity: sha512-9A0XX0tcnH9qPdolSKKzSjTb93dlB0UEcAJbHYEsrFX2fkFNSum9+dvkQQJ9vLc+7vxHetDPujq9Qe6wx+Xhow==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [freebsd] - '@typescript-eslint/typescript-estree@8.61.1': - resolution: {integrity: sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.1.0' + '@typescript/typescript-freebsd-x64@7.1.0-beta': + resolution: {integrity: sha512-EDf/dBHfY0TxX3NmUTek0hbffeyZlh3+ls6WQW3ICB8vYxwYLFnzg7wkgEUvl9eRlpiJ/nmc2S8arPMzINJ7rg==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [freebsd] - '@typescript-eslint/utils@8.59.0': - resolution: {integrity: sha512-I1R/K7V07XsMJ12Oaxg/O9GfrysGTmCRhvZJBv0RE0NcULMzjqVpR5kRRQjHsz3J/bElU7HwCO7zkqL+MSUz+g==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.1.0' + '@typescript/typescript-linux-arm64@7.1.0-beta': + resolution: {integrity: sha512-hNqQBgQ5b5oGqN76QqbAU6CwYhJZ6iOcWc2kPTUs2OentgpxZktETziH6KIyRFJPaiBncognwn2ZLu3DNhHiWw==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [linux] - '@typescript-eslint/utils@8.61.1': - resolution: {integrity: sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.1.0' - - '@typescript-eslint/visitor-keys@8.59.0': - resolution: {integrity: sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/visitor-keys@8.61.1': - resolution: {integrity: sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-native-bridge/darwin-arm64@6.0.3-bridge.16.tsgo.7.0.2': - resolution: {integrity: sha512-D0yL7T3gDXwn4XiRtA1x7bW+0zedMUVG3PnIt+X4lxblnId+Wo2FBvf+2ZLZpPOO3DILGz5bdX+Aq8tqboeGEg==} - cpu: [arm64] - os: [darwin] + '@typescript/typescript-linux-arm@7.1.0-beta': + resolution: {integrity: sha512-AvN9HmzOQh46G4312ooSgMHhbB7DtnLwg8D9mo324X49B7weMwtJrBG1MxT9IyI8kzDd7YRtW+1hWGun7WG7Qw==} + engines: {node: '>=16.20.0'} + cpu: [arm] + os: [linux] - '@typescript-native-bridge/darwin-x64@6.0.3-bridge.16.tsgo.7.0.2': - resolution: {integrity: sha512-HLPoAVsmkgDec3UZpUPI4hquvA/X/qZ71w4dQXP7LbsN+5ROpum7gWx9S0ZwVgvoaC4++iu08Qnw1l5gXknyvg==} - cpu: [x64] - os: [darwin] + '@typescript/typescript-linux-loong64@7.1.0-beta': + resolution: {integrity: sha512-xbUustlBrFflt2pNEyG5ye+d4WGPc/rfr2xK29MXeobPSw297JqwuKRrvbKM5o3fE/eUM/c+urVm6jQ3IaMVTw==} + engines: {node: '>=16.20.0'} + cpu: [loong64] + os: [linux] - '@typescript-native-bridge/linux-arm64@6.0.3-bridge.16.tsgo.7.0.2': - resolution: {integrity: sha512-tVOhuwktZREiVdfOTgh4K8sLaJZHyXOz5SwtwkMknM3A6tSdYTqQ3s7s/hDkB18l33+ZWBKYfJ69Sge8pGeKJw==} - cpu: [arm64] + '@typescript/typescript-linux-mips64el@7.1.0-beta': + resolution: {integrity: sha512-fS4/G7/0bXZ3NSq3nBMAXyg65I3tO4S7j08VNQ2g1aNC/qq79gE7Qne1BNjVsjMFfHBVBFjQYoTQCjmVU2iFlg==} + engines: {node: '>=16.20.0'} + cpu: [mips64el] os: [linux] - '@typescript-native-bridge/linux-arm@6.0.3-bridge.16.tsgo.7.0.2': - resolution: {integrity: sha512-rCqqctyc4801hX2pCVM1kv7rYfBiSi0SGJU+Nv3RQ5C2gBXkShBA39+Vw0bsqrX+Fj+Tre4kuOdSlUnSeDv2GA==} - cpu: [arm] + '@typescript/typescript-linux-ppc64@7.1.0-beta': + resolution: {integrity: sha512-0upCCxYPX8X6gOwkvfv+Ee7/ufyy7k97mTrGE0e5BxLlnUEa+w+rtgnv2dsKcxFdwdeZYVlZKCqgN/xCBV08+A==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] os: [linux] - '@typescript-native-bridge/linux-x64@6.0.3-bridge.16.tsgo.7.0.2': - resolution: {integrity: sha512-WKIuVkh9YuL1udrL0Dp+XvNLaM9maQZMz38ukNn93kwNhSXIXa+62xK69IKdc8osVZU6fDk0ZbIKkcrGkOi51A==} - cpu: [x64] + '@typescript/typescript-linux-riscv64@7.1.0-beta': + resolution: {integrity: sha512-0jKU9YRgOPRgiy5V/UWv37kB53PaTpIbwGCbbOMUPPAkrjVRNOIAJaz7Wa7s3H7mdDebX1LopUEPEJR+7Xa7zg==} + engines: {node: '>=16.20.0'} + cpu: [riscv64] os: [linux] - '@typescript-native-bridge/win32-arm64@6.0.3-bridge.16.tsgo.7.0.2': - resolution: {integrity: sha512-u0/Aq5bkdFcuw463Y6zsYSKRdX1rcTtLkAK1PTwRH70aMkaU6vkr7BSIjvLj3lDCqsySFuoHHuNg5J2pmqxp3A==} - cpu: [arm64] - os: [win32] + '@typescript/typescript-linux-s390x@7.1.0-beta': + resolution: {integrity: sha512-j+d04s2kxaPQh6DLPb01sknAQfzsrdSZy15jBRHyLEZoaaQ19dVjbFRlkNVvYwUG7ZdP8+y5b/7RmPxdtHUKdQ==} + engines: {node: '>=16.20.0'} + cpu: [s390x] + os: [linux] - '@typescript-native-bridge/win32-x64@6.0.3-bridge.16.tsgo.7.0.2': - resolution: {integrity: sha512-l0OPZ6xD69vQpyfS5yTSwJ5xJTYM2hUKFRYlvyyUSvbFubUz8jQRLcrn+WU4QehV9WkVIQIx8CLgNCI0Zqhmyg==} + '@typescript/typescript-linux-x64@7.1.0-beta': + resolution: {integrity: sha512-kcD51ihf+pnI5KT4uQQYG04OpwznaZYBlIQhDhl8fflFd27NktxXsUeguxZyvUMyWfum7IF2bBS6Jv7ox0AgFQ==} + engines: {node: '>=16.20.0'} cpu: [x64] - os: [win32] - - '@typespec/ts-http-runtime@0.3.5': - resolution: {integrity: sha512-yURCknZhvywvQItHMMmFSo+fq5arCUIyz/CVk7jD89MSai7dkaX8ufjCWp3NttLojoTVbcE72ri+be/TnEbMHw==} - engines: {node: '>=20.0.0'} + os: [linux] - '@vscode/vsce-sign-alpine-arm64@2.0.6': - resolution: {integrity: sha512-wKkJBsvKF+f0GfsUuGT0tSW0kZL87QggEiqNqK6/8hvqsXvpx8OsTEc3mnE1kejkh5r+qUyQ7PtF8jZYN0mo8Q==} + '@typescript/typescript-netbsd-arm64@7.1.0-beta': + resolution: {integrity: sha512-8W85YyoGK+rgGoSU9jJQUpELbrKcZcbdJk2jPNQ6UKDKI7ryPlPFTrV2jCE5RQuv4Tt96hMDTc33s4YfRrLFBA==} + engines: {node: '>=16.20.0'} cpu: [arm64] - os: [alpine] + os: [netbsd] - '@vscode/vsce-sign-alpine-x64@2.0.6': - resolution: {integrity: sha512-YoAGlmdK39vKi9jA18i4ufBbd95OqGJxRvF3n6ZbCyziwy3O+JgOpIUPxv5tjeO6gQfx29qBivQ8ZZTUF2Ba0w==} + '@typescript/typescript-netbsd-x64@7.1.0-beta': + resolution: {integrity: sha512-iGsTE0ve2PG0AcyZinwAR0LnGEZhu/y4dceS0hZsYCaKdYETvQtvtlNWlmYKempsBr6C7Il7dMXHVk+VFm9u0g==} + engines: {node: '>=16.20.0'} cpu: [x64] - os: [alpine] + os: [netbsd] - '@vscode/vsce-sign-darwin-arm64@2.0.6': - resolution: {integrity: sha512-5HMHaJRIQuozm/XQIiJiA0W9uhdblwwl2ZNDSSAeXGO9YhB9MH5C4KIHOmvyjUnKy4UCuiP43VKpIxW1VWP4tQ==} + '@typescript/typescript-openbsd-arm64@7.1.0-beta': + resolution: {integrity: sha512-Z2FERdHB9fAKI+KGqdWn04O7uNlnB+VYiEm6INE1aZuzzLNd3KBqQ6IpKUJF2ny4GigGU6nVu75XD9tAUODPng==} + engines: {node: '>=16.20.0'} cpu: [arm64] - os: [darwin] + os: [openbsd] - '@vscode/vsce-sign-darwin-x64@2.0.6': - resolution: {integrity: sha512-25GsUbTAiNfHSuRItoQafXOIpxlYj+IXb4/qarrXu7kmbH94jlm5sdWSCKrrREs8+GsXF1b+l3OB7VJy5jsykw==} + '@typescript/typescript-openbsd-x64@7.1.0-beta': + resolution: {integrity: sha512-NVG3qeyCq/rJEOhaRjR3LsWgjrjo5vJW25omVLeHAVnpKIG4pE4cenwwKGR06DiJ1s0QTOymcJeKXMEpsiNBVg==} + engines: {node: '>=16.20.0'} cpu: [x64] - os: [darwin] - - '@vscode/vsce-sign-linux-arm64@2.0.6': - resolution: {integrity: sha512-cfb1qK7lygtMa4NUl2582nP7aliLYuDEVpAbXJMkDq1qE+olIw/es+C8j1LJwvcRq1I2yWGtSn3EkDp9Dq5FdA==} - cpu: [arm64] - os: [linux] - - '@vscode/vsce-sign-linux-arm@2.0.6': - resolution: {integrity: sha512-UndEc2Xlq4HsuMPnwu7420uqceXjs4yb5W8E2/UkaHBB9OWCwMd3/bRe/1eLe3D8kPpxzcaeTyXiK3RdzS/1CA==} - cpu: [arm] - os: [linux] + os: [openbsd] - '@vscode/vsce-sign-linux-x64@2.0.6': - resolution: {integrity: sha512-/olerl1A4sOqdP+hjvJ1sbQjKN07Y3DVnxO4gnbn/ahtQvFrdhUi0G1VsZXDNjfqmXw57DmPi5ASnj/8PGZhAA==} + '@typescript/typescript-sunos-x64@7.1.0-beta': + resolution: {integrity: sha512-ovAdoWXEn2B5/fyWG69pLU3FWmQwPWOjQV5TFbcxtpnOv+R3g8ySGHl1NYRq7Lb/GYdFDvaGcZ+SiU7iKuiMsA==} + engines: {node: '>=16.20.0'} cpu: [x64] - os: [linux] + os: [sunos] - '@vscode/vsce-sign-win32-arm64@2.0.6': - resolution: {integrity: sha512-ivM/MiGIY0PJNZBoGtlRBM/xDpwbdlCWomUWuLmIxbi1Cxe/1nooYrEQoaHD8ojVRgzdQEUzMsRbyF5cJJgYOg==} + '@typescript/typescript-win32-arm64@7.1.0-beta': + resolution: {integrity: sha512-lJbo71IEOfL7ZZIZnArRaOLqjn9BAfoSzyUQn0aSjfSpDZL4ru0afUsdBfo6PQEjD6ssdmi0n/RwaPADX6bsvQ==} + engines: {node: '>=16.20.0'} cpu: [arm64] os: [win32] - '@vscode/vsce-sign-win32-x64@2.0.6': - resolution: {integrity: sha512-mgth9Kvze+u8CruYMmhHw6Zgy3GRX2S+Ed5oSokDEK5vPEwGGKnmuXua9tmFhomeAnhgJnL4DCna3TiNuGrBTQ==} + '@typescript/typescript-win32-x64@7.1.0-beta': + resolution: {integrity: sha512-UKcHvbybY2tDTvFW2b1vwiAvDlIRooo69lh8CXWVhOQejcvpM5+IgbiFW9EIwR5Wi5X/v+BdN7gdWbigBuJ3rw==} + engines: {node: '>=16.20.0'} cpu: [x64] os: [win32] - '@vscode/vsce-sign@2.0.9': - resolution: {integrity: sha512-8IvaRvtFyzUnGGl3f5+1Cnor3LqaUWvhaUjAYO8Y39OUYlOf3cRd+dowuQYLpZcP3uwSG+mURwjEBOSq4SOJ0g==} - - '@vscode/vsce@3.9.2': - resolution: {integrity: sha512-XSxMosEEDO6vLxELAHVkwmhC0qe0ijZni2jB9Rcs8kQsW4lhTDQ/wMzmwFs/buotAWSnpmUp/dRWD2ufG3UYKA==} - engines: {node: '>= 20'} - hasBin: true - abbrev@4.0.0: resolution: {integrity: sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==} engines: {node: ^20.17.0 || >=22.9.0} @@ -1150,25 +726,10 @@ packages: ajv@6.15.0: resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} - ajv@8.18.0: - resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} - - ansi-escapes@7.3.0: - resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} - engines: {node: '>=18'} - - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - ansi-regex@6.2.2: resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} - ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} - ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} @@ -1177,25 +738,12 @@ packages: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} - argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} array-ify@1.0.0: resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} - astral-regex@2.0.0: - resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} - engines: {node: '>=8'} - - asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - - azure-devops-node-api@12.5.0: - resolution: {integrity: sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==} - balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1203,9 +751,6 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - before-after-hook@4.0.0: resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} @@ -1213,19 +758,6 @@ packages: resolution: {integrity: sha512-X4CiKlcV2GjnCMwnKAfbVWpHa++65th9TuzAEYtZoATiOE2DQKhSp4CJlyLoTqdhBKlXjpXjCTYPNNFS33Fi6w==} engines: {node: ^20.17.0 || >=22.9.0} - binaryextensions@6.11.0: - resolution: {integrity: sha512-sXnYK/Ij80TO3lcqZVV2YgfKN5QjUWIRk/XSm2J/4bd/lPko3lvk0O4ZppH6m+6hB2/GTu+ptNwVFe1xh+QLQw==} - engines: {node: '>=4'} - - bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - - boolbase@1.0.0: - resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - - boundary@2.0.0: - resolution: {integrity: sha512-rJKn5ooC9u8q13IMCrW0RSp31pxBCHE3y9V/tp3TdWSLf8Em3p6Di4NBpfzbJge9YjjFEsD0RtFEjtvHL5VyEA==} - brace-expansion@1.1.14: resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} @@ -1233,65 +765,18 @@ packages: resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} engines: {node: 18 || 20 || >=22} - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - - buffer-crc32@0.2.13: - resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} - - buffer-equal-constant-time@1.0.1: - resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} - - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - - builtin-modules@1.1.1: - resolution: {integrity: sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==} - engines: {node: '>=0.10.0'} - - bundle-name@4.1.0: - resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} - engines: {node: '>=18'} - cacache@20.0.4: resolution: {integrity: sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA==} engines: {node: ^20.17.0 || >=22.9.0} - call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} - - call-bound@1.0.4: - resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} - engines: {node: '>= 0.4'} - callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} - chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - chalk@5.6.2: - resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - - cheerio-select@2.1.0: - resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} - - cheerio@1.2.0: - resolution: {integrity: sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==} - engines: {node: '>=20.18.1'} - - chownr@1.1.4: - resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} - chownr@3.0.0: resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} engines: {node: '>=18'} @@ -1312,34 +797,13 @@ packages: resolution: {integrity: sha512-Jk/BK6NCapZ58BKUxlSI+ouKRbjH1NLZCgJkYoab+vEHUY3f6OzpNBN9u7HFSv9J6TRDGs4PLOHezoKGaFRSCA==} engines: {node: ^20.17.0 || >=22.9.0} - cockatiel@3.2.1: - resolution: {integrity: sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q==} - engines: {node: '>=16'} - - color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} - color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} - color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - - commander@12.1.0: - resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} - engines: {node: '>=18'} - - commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - common-ancestor-path@2.0.0: resolution: {integrity: sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==} engines: {node: '>= 18'} @@ -1347,9 +811,6 @@ packages: compare-func@2.0.0: resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} - compare-versions@6.1.1: - resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} - concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -1389,13 +850,6 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - css-select@5.2.2: - resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} - - css-what@6.2.2: - resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} - engines: {node: '>= 6'} - cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -1410,58 +864,13 @@ packages: supports-color: optional: true - decompress-response@6.0.0: - resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} - engines: {node: '>=10'} - - deep-extend@0.6.0: - resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} - engines: {node: '>=4.0.0'} - deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - default-browser-id@5.0.1: - resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} - engines: {node: '>=18'} - - default-browser@5.5.0: - resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==} - engines: {node: '>=18'} - - define-lazy-prop@3.0.0: - resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} - engines: {node: '>=12'} - - delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - detect-indent@7.0.2: resolution: {integrity: sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A==} engines: {node: '>=12.20'} - detect-libc@2.1.2: - resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} - engines: {node: '>=8'} - - diff@4.0.4: - resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==} - engines: {node: '>=0.3.1'} - - dom-serializer@2.0.0: - resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} - - domelementtype@2.3.0: - resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} - - domhandler@5.0.3: - resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} - engines: {node: '>= 4'} - - domutils@3.2.2: - resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} - dot-prop@5.3.0: resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} engines: {node: '>=8'} @@ -1474,108 +883,25 @@ packages: resolution: {integrity: sha512-sIy25poR2gRP/tWPTgP0MPeJoJcpv0xzYDcsboapvthbEt1Qw3Al252CA0xFyIh2cYEGGKyBJtKokryv4ERlJw==} hasBin: true - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} - - ecdsa-sig-formatter@1.0.11: - resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} - - editions@6.22.0: - resolution: {integrity: sha512-UgGlf8IW75je7HZjNDpJdCv4cGJWIi6yumFdZ0R7A8/CIhQiWUjyGLCxdHpd8bmyD1gnkfUNK0oeOXqUS2cpfQ==} - engines: {ecmascript: '>= es5', node: '>=4'} - emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - encoding-sniffer@0.2.1: - resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==} - - end-of-stream@1.4.5: - resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - - entities@4.5.0: - resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} - engines: {node: '>=0.12'} - - entities@6.0.1: - resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} - engines: {node: '>=0.12'} - - entities@7.0.1: - resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} - engines: {node: '>=0.12'} - env-paths@2.2.1: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} - environment@1.1.0: - resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} - engines: {node: '>=18'} - - error-stack-parser@2.1.4: - resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} - - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} - engines: {node: '>= 0.4'} - - es-set-tostringtag@2.1.0: - resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} - engines: {node: '>= 0.4'} - - esbuild@0.27.7: - resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} - engines: {node: '>=18'} - hasBin: true - escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} - escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} - escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-plugin-expect-type@0.4.3: - resolution: {integrity: sha512-frRGE4Qg5jFJgY11xRvK3iZu/gDnn83d1a5iPelSotIJw/JhuhqZ9bjE2fRD6nV7Aof9E+SHzNGOMWngB3wbTw==} - engines: {node: '>=18'} - peerDependencies: - '@typescript-eslint/parser': '>=6' - eslint: '>=7' - typescript: '>=4' - - eslint-plugin-react-x@4.2.3: - resolution: {integrity: sha512-kJZXa5QsGA4FzuTyKLKjFt9nm78CZcfHshfgfSXjVOshvlVGeg1RWyNZnXDW3hASdZ/REsPg2mGFYqwUPXnJ5Q==} - engines: {node: '>=22.0.0'} - peerDependencies: - eslint: ^10.0.0 - typescript: '*' - eslint-scope@8.4.0: resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint-scope@9.1.2: - resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1588,16 +914,6 @@ packages: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - eslint@10.2.1: - resolution: {integrity: sha512-wiyGaKsDgqXvF40P8mDwiUp/KQjE1FdrIEJsM8PZ3XCiniTMXS3OHWWUe5FI5agoCnr8x4xPrTDZuxsBlNHl+Q==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - hasBin: true - peerDependencies: - jiti: '*' - peerDependenciesMeta: - jiti: - optional: true - eslint@9.39.4: resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1612,15 +928,6 @@ packages: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - espree@11.2.0: - resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} - engines: {node: ^20.19.0 || ^22.13.0 || >=24} - - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - esquery@1.7.0: resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} @@ -1637,10 +944,6 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - expand-template@2.0.3: - resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} - engines: {node: '>=6'} - exponential-backoff@3.1.3: resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} @@ -1650,10 +953,6 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -1666,15 +965,9 @@ packages: fast-string-width@3.0.2: resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} - fast-uri@3.1.0: - resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - fast-wrap-ansi@0.2.0: resolution: {integrity: sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==} - fastq@1.20.1: - resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} - fd-package-json@2.0.0: resolution: {integrity: sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==} @@ -1691,10 +984,6 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -1710,27 +999,10 @@ packages: flatted@3.4.2: resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} - form-data@4.0.5: - resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} - engines: {node: '>= 6'} - - fs-constants@1.0.0: - resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - - fs-extra@11.3.4: - resolution: {integrity: sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==} - engines: {node: '>=14.14'} - fs-minipass@3.0.3: resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -1739,27 +1011,12 @@ packages: resolution: {integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==} engines: {node: '>=18'} - get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - git-up@8.1.1: resolution: {integrity: sha512-FDenSF3fVqBYSaJoYy1KSc2wosx0gCvKP+c+PRBht7cAaiCeQlBtfBDX9vgnNOHmdePlSFITVcn4pFfcgNvx3g==} git-url-parse@16.1.0: resolution: {integrity: sha512-cPLz4HuK86wClEW7iDdeAKcCVlWXmrLpb2L+G9goW0Z1dtpNS6BXXSOckUTlJT/LDQViE1QZKstNORzHsLnobw==} - github-from-package@0.0.0: - resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} - - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} @@ -1768,22 +1025,10 @@ packages: resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} engines: {node: 18 || 20 || >=22} - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globby@14.1.0: - resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==} - engines: {node: '>=18'} - - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -1798,34 +1043,10 @@ packages: engines: {node: '>=0.4.7'} hasBin: true - has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - - hasown@2.0.3: - resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} - engines: {node: '>= 0.4'} - - hosted-git-info@4.1.0: - resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} - engines: {node: '>=10'} - - hosted-git-info@7.0.2: - resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} - engines: {node: ^16.14.0 || >=18.0.0} - hosted-git-info@8.1.0: resolution: {integrity: sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==} engines: {node: ^18.17.0 || >=20.5.0} @@ -1834,9 +1055,6 @@ packages: resolution: {integrity: sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==} engines: {node: ^20.17.0 || >=22.9.0} - htmlparser2@10.1.0: - resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==} - http-cache-semantics@4.2.0: resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} @@ -1848,17 +1066,10 @@ packages: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} - iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - iconv-lite@0.7.2: resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} engines: {node: '>=0.10.0'} - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - ignore-walk@8.0.0: resolution: {integrity: sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A==} engines: {node: ^20.17.0 || >=22.9.0} @@ -1867,10 +1078,6 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - ignore@7.0.5: - resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} - engines: {node: '>= 4'} - import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -1884,20 +1091,6 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} - index-to-position@1.2.0: - resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==} - engines: {node: '>=18'} - - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - ini@1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - ini@6.0.0: resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==} engines: {node: ^20.17.0 || >=22.9.0} @@ -1906,36 +1099,14 @@ packages: resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} engines: {node: '>= 12'} - is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} - engines: {node: '>= 0.4'} - - is-docker@3.0.0: - resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - hasBin: true - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-inside-container@1.0.0: - resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} - engines: {node: '>=14.16'} - hasBin: true - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - is-obj@2.0.0: resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} engines: {node: '>=8'} @@ -1947,10 +1118,6 @@ packages: is-ssh@1.4.1: resolution: {integrity: sha512-JNeu1wQsHjyHgn9NcWTaXq6zWSR6hqE0++zhfZlkFBbScNkyvxCdeV8sRkSBaeLKxmbpR21brail63ACNxJ0Tg==} - is-wsl@3.1.1: - resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} - engines: {node: '>=16'} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -1958,21 +1125,10 @@ packages: resolution: {integrity: sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==} engines: {node: '>=20'} - istextorbinary@9.5.0: - resolution: {integrity: sha512-5mbUj3SiZXCuRf9fT3ibzbSSEWiy63gFfksmGfdOzujPjW3k+z8WvIBxcJHBoQNlaZaiyB25deviif2+osLmLw==} - engines: {node: '>=4'} - jiti@2.6.1: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - js-yaml@3.14.2: - resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} - hasBin: true - js-yaml@4.1.1: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true @@ -1987,9 +1143,6 @@ packages: json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -2004,42 +1157,19 @@ packages: engines: {node: '>=6'} hasBin: true - jsonc-parser@3.3.1: - resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} - - jsonfile@6.2.1: - resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} - jsonparse@1.3.1: resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} engines: {'0': node >= 0.2.0} - jsonwebtoken@9.0.3: - resolution: {integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==} - engines: {node: '>=12', npm: '>=6'} - just-diff-apply@5.5.0: resolution: {integrity: sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw==} just-diff@6.0.2: resolution: {integrity: sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA==} - jwa@2.0.1: - resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} - - jws@4.0.1: - resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} - - keytar@7.9.0: - resolution: {integrity: sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==} - keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - leven@3.1.0: - resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} - engines: {node: '>=6'} - levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -2056,9 +1186,6 @@ packages: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} - linkify-it@5.0.0: - resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} - locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -2067,36 +1194,9 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - lodash.includes@4.3.0: - resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} - - lodash.isboolean@3.0.3: - resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} - - lodash.isinteger@4.0.4: - resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} - - lodash.isnumber@3.0.3: - resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} - - lodash.isplainobject@4.0.6: - resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} - - lodash.isstring@4.0.1: - resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} - lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - lodash.once@4.1.1: - resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} - - lodash.truncate@4.4.2: - resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} - - lodash@4.18.1: - resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} - lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -2104,54 +1204,14 @@ packages: resolution: {integrity: sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==} engines: {node: 20 || >=22} - lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} - make-fetch-happen@15.0.5: resolution: {integrity: sha512-uCbIa8jWWmQZt4dSnEStkVC6gdakiinAm4PiGsywIkguF0eWMdcjDz0ECYhUolFU3pFLOev9VNPCEygydXnddg==} engines: {node: ^20.17.0 || >=22.9.0} - markdown-it@14.1.1: - resolution: {integrity: sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==} - hasBin: true - - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - - mdurl@2.0.0: - resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} - meow@13.2.0: resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} engines: {node: '>=18'} - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - - mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - - mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - - mime@1.6.0: - resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} - engines: {node: '>=4'} - hasBin: true - - mimic-response@3.1.0: - resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} - engines: {node: '>=10'} - minimatch@10.2.5: resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} engines: {node: 18 || 20 || >=22} @@ -2194,26 +1254,13 @@ packages: resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} engines: {node: '>= 18'} - mkdirp-classic@0.5.3: - resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} - - mkdirp@0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - mute-stream@0.0.8: - resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} - mute-stream@3.0.0: resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} engines: {node: ^20.17.0 || >=22.9.0} - napi-build-utils@2.0.0: - resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} - natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -2224,31 +1271,16 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - node-abi@3.89.0: - resolution: {integrity: sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==} - engines: {node: '>=10'} - - node-addon-api@4.3.0: - resolution: {integrity: sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==} - node-gyp@12.3.0: resolution: {integrity: sha512-QNcUWM+HgJplcPzBvFBZ9VXacyGZ4+VTOb80PwWR+TlVzoHbRKULNEzpRsnaoxG3Wzr7Qh7BYxGDU3CbKib2Yg==} engines: {node: ^20.17.0 || >=22.9.0} hasBin: true - node-sarif-builder@3.4.0: - resolution: {integrity: sha512-tGnJW6OKRii9u/b2WiUViTJS+h7Apxx17qsMUjsUeNDiMMX5ZFf8F8Fcz7PAQ6omvOxHZtvDTmOYKJQwmfpjeg==} - engines: {node: '>=20'} - nopt@9.0.0: resolution: {integrity: sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==} engines: {node: ^20.17.0 || >=22.9.0} hasBin: true - normalize-package-data@6.0.2: - resolution: {integrity: sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==} - engines: {node: ^16.14.0 || >=18.0.0} - normalize-package-data@7.0.1: resolution: {integrity: sha512-linxNAT6M0ebEYZOx2tO6vBEFsVgnPpv+AVjk0wJHfaUIbq31Jm3T6vvZaarnOeWDh8ShnwXuaAyM7WT3RzErA==} engines: {node: ^18.17.0 || >=20.5.0} @@ -2281,20 +1313,6 @@ packages: resolution: {integrity: sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw==} engines: {node: ^20.17.0 || >=22.9.0} - nth-check@2.1.1: - resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - - object-inspect@1.13.4: - resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} - engines: {node: '>= 0.4'} - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - open@10.2.0: - resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} - engines: {node: '>=18'} - optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -2336,62 +1354,25 @@ packages: resolution: {integrity: sha512-ZHEmNKMq1wyJXNwLxyHnluPfRAFSIliBvbK/UiOceROt4Xh9Pz0fq49NytIaeaCUf5VR86hwQ/34FCcNU5/LKQ==} engines: {node: ^20.17.0 || >=22.9.0} - parse-json@8.3.0: - resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==} - engines: {node: '>=18'} - parse-path@7.1.0: resolution: {integrity: sha512-EuCycjZtfPcjWk7KTksnJ5xPMvWGA/6i4zrLYhRG0hGvC3GPU/jGUj3Cy+ZR0v30duV3e23R95T1lE2+lsndSw==} - parse-semver@1.1.1: - resolution: {integrity: sha512-Eg1OuNntBMH0ojvEKSrvDSnwLmvVuUOSdylH/pSCPNMIspLlweJyIWXCE+k/5hm3cj/EBUYwmWkjhBALNP4LXQ==} - parse-url@9.2.0: resolution: {integrity: sha512-bCgsFI+GeGWPAvAiUv63ZorMeif3/U0zaXABGJbOWt5OH2KCaPHF6S+0ok4aqM9RuIPGyZdx9tR9l13PsW4AYQ==} engines: {node: '>=14.13.0'} - parse5-htmlparser2-tree-adapter@7.1.0: - resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} - - parse5-parser-stream@7.1.2: - resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} - - parse5@7.3.0: - resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} - path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@2.0.2: resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} engines: {node: 18 || 20 || >=22} - path-type@6.0.0: - resolution: {integrity: sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==} - engines: {node: '>=18'} - - pend@1.2.0: - resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} - - picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - - picomatch@2.3.2: - resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} - engines: {node: '>=8.6'} - picomatch@4.0.4: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} @@ -2400,23 +1381,10 @@ packages: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} - pluralize@2.0.0: - resolution: {integrity: sha512-TqNZzQCD4S42De9IfnnBvILN7HAW7riLqsCyp8lgjXeysyPlX5HhqKAcJHHHb9XskE4/a+7VGC9zzx8Ls0jOAw==} - - pluralize@8.0.0: - resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} - engines: {node: '>=4'} - postcss-selector-parser@7.1.1: resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} engines: {node: '>=4'} - prebuild-install@7.1.3: - resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} - engines: {node: '>=10'} - deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available. - hasBin: true - prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -2438,51 +1406,14 @@ packages: protocols@2.0.2: resolution: {integrity: sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==} - pump@3.0.4: - resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} - - punycode.js@2.3.1: - resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} - engines: {node: '>=6'} - punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - qs@6.15.1: - resolution: {integrity: sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==} - engines: {node: '>=0.6'} - - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - - rc-config-loader@4.1.4: - resolution: {integrity: sha512-3GiwEzklkbXTDp52UR5nT8iXgYAx1V9ZG/kDZT7p60u2GCv2XTwQq4NzinMoMpNtXhmt3WkhYXcj6HH8HdwCEQ==} - - rc@1.2.8: - resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} - hasBin: true - read-cmd-shim@6.0.0: resolution: {integrity: sha512-1zM5HuOfagXCBWMN83fuFI/x+T/UhZ7k+KIzhrHXcQoeX5+7gmaDYjELQHmmzIodumBHeByBJT4QYS7ufAgs7A==} engines: {node: ^20.17.0 || >=22.9.0} - read-pkg@9.0.1: - resolution: {integrity: sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==} - engines: {node: '>=18'} - - read@1.0.7: - resolution: {integrity: sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==} - engines: {node: '>=0.8'} - - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - - require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - resolve-cwd@3.0.0: resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} engines: {node: '>=8'} @@ -2495,46 +1426,9 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} - resolve@1.22.12: - resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} - engines: {node: '>= 0.4'} - hasBin: true - - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - - run-applescript@7.1.0: - resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} - engines: {node: '>=18'} - - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sax@1.6.0: - resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} - engines: {node: '>=11.0.0'} - - secretlint@10.2.2: - resolution: {integrity: sha512-xVpkeHV/aoWe4vP4TansF622nBEImzCY73y/0042DuJ29iKIaqgoJ8fGxre3rVSHHbxar4FdJobmTnLp9AU0eg==} - engines: {node: '>=20.0.0'} - hasBin: true - - semver@5.7.2: - resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} - hasBin: true - - semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} - engines: {node: '>=10'} - hasBin: true - semver@7.8.4: resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==} engines: {node: '>=10'} @@ -2548,22 +1442,6 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - side-channel-list@1.0.1: - resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} - engines: {node: '>= 0.4'} - - side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} - - side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} - - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} - engines: {node: '>= 0.4'} - signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} @@ -2572,20 +1450,6 @@ packages: resolution: {integrity: sha512-/fUgUhYghuLzVT/gaJoeVehLCgZiUxPCPMcyVNY0lIf/cTCz58K/WTI7PefDarXxp9nUKpEwg1yyz3eSBMTtgA==} engines: {node: ^20.17.0 || >=22.9.0} - simple-concat@1.0.1: - resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} - - simple-get@4.0.1: - resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} - - slash@5.1.0: - resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} - engines: {node: '>=14.16'} - - slice-ansi@4.0.0: - resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} - engines: {node: '>=10'} - smart-buffer@4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} @@ -2621,91 +1485,30 @@ packages: spdx-license-ids@3.0.23: resolution: {integrity: sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==} - sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - ssri@13.0.1: resolution: {integrity: sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ==} engines: {node: ^20.17.0 || >=22.9.0} - stackframe@1.3.4: - resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} - - string-ts@2.3.1: - resolution: {integrity: sha512-xSJq+BS52SaFFAVxuStmx6n5aYZU571uYUnUrPXkPFCfdHyZMMlbP2v2Wx5sNBnAVzq/2+0+mcBLBa3Xa5ubYw==} - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - string-width@7.2.0: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - strip-ansi@7.2.0: resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} - strip-json-comments@2.0.1: - resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} - engines: {node: '>=0.10.0'} - strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - structured-source@4.0.0: - resolution: {integrity: sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==} - - supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - supports-hyperlinks@3.2.0: - resolution: {integrity: sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==} - engines: {node: '>=14.18'} - - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - - table@6.9.0: - resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} - engines: {node: '>=10.0.0'} - - tar-fs@2.1.4: - resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==} - - tar-stream@2.2.0: - resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} - engines: {node: '>=6'} - tar@7.5.16: resolution: {integrity: sha512-56adEpPMouktRlBLXiaYFFzZ/3+JXa8P9n7WbR+ibIjtviN55mEaOkiysCnPnWm+7kkui1Dn8J9l+g6zV8731w==} engines: {node: '>=18'} - terminal-link@4.0.0: - resolution: {integrity: sha512-lk+vH+MccxNqgVqSnkMVKx4VLJfnLjDBGzH16JVZjKE2DoxP57s6/vt6JmXV5I3jBcfGrxNrYtC+mPtU7WJztA==} - engines: {node: '>=18'} - - text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - - textextensions@6.11.0: - resolution: {integrity: sha512-tXJwSr9355kFJI3lbCkPpUH5cP8/M0GGy2xLO34aZCjMXBaK3SoPnZwr/oWmo1FdCnELcs4npdCIOFtq9W3ruQ==} - engines: {node: '>=4'} - tinyexec@1.2.4: resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} engines: {node: '>=18'} @@ -2714,14 +1517,6 @@ packages: resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} engines: {node: '>=12.0.0'} - tmp@0.2.5: - resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} - engines: {node: '>=14.14'} - - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - treeverse@3.0.0: resolution: {integrity: sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -2732,77 +1527,29 @@ packages: peerDependencies: typescript: '>=4.8.4' - ts-pattern@5.9.0: - resolution: {integrity: sha512-6s5V71mX8qBUmlgbrfL33xDUwO0fq48rxAu2LBE11WBeGdpCPOsXksQbZJHvHwhrd3QjUusd3mAOM5Gg0mFBLg==} - - tsl@1.0.30: - resolution: {integrity: sha512-dogG67L7yjUcBdopmjAx/F45CIF+7mcMPKUlwXES3ZnTSqIsMWUZ329r0YZ9FQH8DjHp1xy4RJgKy/pQ8hYxHQ==} - hasBin: true - peerDependencies: - typescript: ~5.8.3 || ~5.9.2 || ~6.0.0 - - tslib@1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - - tslint@6.1.3: - resolution: {integrity: sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==} - engines: {node: '>=4.8.0'} - deprecated: TSLint has been deprecated in favor of ESLint. Please see https://github.com/palantir/tslint/issues/4534 for more information. - hasBin: true - peerDependencies: - typescript: '>=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 4.0.0-dev' - - tsutils@2.29.0: - resolution: {integrity: sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==} - peerDependencies: - typescript: '>=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev' - tuf-js@4.1.0: resolution: {integrity: sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ==} engines: {node: ^20.17.0 || >=22.9.0} - tunnel-agent@0.6.0: - resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} - - tunnel@0.0.6: - resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} - engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} - type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - type-fest@4.41.0: - resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} - engines: {node: '>=16'} - - typed-rest-client@1.8.11: - resolution: {integrity: sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==} - - typescript-native-bridge@6.0.3-bridge.16.tsgo.7.0.2: - resolution: {integrity: sha512-bO+fh23fh4j0mK2NmlFOrlbR9Ke5cHpjOsDaM2czXlSXqxNchGdDVyAdaVCFz0DofkFOov96HLvQaP3W57F+CA==} - engines: {node: '>=20.19'} - hasBin: true - typescript@6.0.3: resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} engines: {node: '>=14.17'} hasBin: true - uc.micro@2.1.0: - resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + typescript@7.1.0-beta: + resolution: {integrity: sha512-cjWjZAJDsmZzsNC5GwHNlW0YPM65jZa5oR0KyQcYX3KSVIfTvtBq//vewISyatKk4OLi5grZjHhIVE9fdrw1UQ==} + engines: {node: '>=16.20.0'} + hasBin: true uglify-js@3.19.3: resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} engines: {node: '>=0.8.0'} hasBin: true - underscore@1.13.8: - resolution: {integrity: sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==} - undici-types@7.24.6: resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} @@ -2810,38 +1557,15 @@ packages: resolution: {integrity: sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==} engines: {node: '>=18.17'} - undici@7.25.0: - resolution: {integrity: sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==} - engines: {node: '>=20.18.1'} - - unicorn-magic@0.1.0: - resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} - engines: {node: '>=18'} - - unicorn-magic@0.3.0: - resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} - engines: {node: '>=18'} - universal-user-agent@7.0.3: resolution: {integrity: sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==} - universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} - uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - url-join@4.0.1: - resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==} - util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - uuid@8.3.2: - resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} - hasBin: true - validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} @@ -2849,23 +1573,10 @@ packages: resolution: {integrity: sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==} engines: {node: ^20.17.0 || >=22.9.0} - version-range@4.15.0: - resolution: {integrity: sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg==} - engines: {node: '>=4'} - walk-up-path@4.0.0: resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} engines: {node: 20 || >=22} - whatwg-encoding@3.1.1: - resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} - engines: {node: '>=18'} - deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation - - whatwg-mimetype@4.0.0: - resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} - engines: {node: '>=18'} - which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -2887,9 +1598,6 @@ packages: resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} engines: {node: '>=18'} - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - write-file-atomic@6.0.0: resolution: {integrity: sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ==} engines: {node: ^18.17.0 || >=20.5.0} @@ -2902,18 +1610,6 @@ packages: resolution: {integrity: sha512-rj8As6LkachKauGxvZkFzCEd6hIRTi9FKtCNKOa4SaH5vPOiACbGcmPUEJXgkhTHwzNsYmcSbD3C9a6whBfyOg==} engines: {node: '>=20'} - wsl-utils@0.1.0: - resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} - engines: {node: '>=18'} - - xml2js@0.5.0: - resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==} - engines: {node: '>=4.0.0'} - - xmlbuilder@11.0.1: - resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} - engines: {node: '>=4.0'} - y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -2938,13 +1634,6 @@ packages: resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} engines: {node: ^20.19.0 || ^22.12.0 || >=23} - yauzl@3.3.0: - resolution: {integrity: sha512-PtGEvEP30p7sbIBJKUBjUnqgTVOyMURc4dLo9iNyAJnNIEz9pm88cCXF21w94Kg3k6RXkeZh5DHOGS0qEONvNQ==} - engines: {node: '>=12'} - - yazl@2.5.1: - resolution: {integrity: sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==} - yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -2952,108 +1641,8 @@ packages: zeptomatch@2.1.0: resolution: {integrity: sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA==} - zod@4.3.6: - resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} - snapshots: - '@azu/format-text@1.0.2': {} - - '@azu/style-format@1.0.1': - dependencies: - '@azu/format-text': 1.0.2 - - '@azure/abort-controller@2.1.2': - dependencies: - tslib: 2.8.1 - - '@azure/core-auth@1.10.1': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-util': 1.13.1 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/core-client@1.10.1': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-auth': 1.10.1 - '@azure/core-rest-pipeline': 1.23.0 - '@azure/core-tracing': 1.3.1 - '@azure/core-util': 1.13.1 - '@azure/logger': 1.3.0 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/core-rest-pipeline@1.23.0': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-auth': 1.10.1 - '@azure/core-tracing': 1.3.1 - '@azure/core-util': 1.13.1 - '@azure/logger': 1.3.0 - '@typespec/ts-http-runtime': 0.3.5 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/core-tracing@1.3.1': - dependencies: - tslib: 2.8.1 - - '@azure/core-util@1.13.1': - dependencies: - '@azure/abort-controller': 2.1.2 - '@typespec/ts-http-runtime': 0.3.5 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/identity@4.13.1': - dependencies: - '@azure/abort-controller': 2.1.2 - '@azure/core-auth': 1.10.1 - '@azure/core-client': 1.10.1 - '@azure/core-rest-pipeline': 1.23.0 - '@azure/core-tracing': 1.3.1 - '@azure/core-util': 1.13.1 - '@azure/logger': 1.3.0 - '@azure/msal-browser': 5.8.0 - '@azure/msal-node': 5.1.4 - open: 10.2.0 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/logger@1.3.0': - dependencies: - '@typespec/ts-http-runtime': 0.3.5 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - - '@azure/msal-browser@5.8.0': - dependencies: - '@azure/msal-common': 16.5.1 - - '@azure/msal-common@16.5.1': {} - - '@azure/msal-node@5.1.4': - dependencies: - '@azure/msal-common': 16.5.1 - jsonwebtoken: 9.0.3 - uuid: 8.3.2 - - '@babel/code-frame@7.29.0': - dependencies: - '@babel/helper-validator-identifier': 7.28.5 - js-tokens: 4.0.0 - picocolors: 1.1.1 - - '@babel/helper-validator-identifier@7.28.5': {} - '@conventional-changelog/git-client@2.7.0(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.4.0)': dependencies: '@simple-libs/child-process-utils': 1.0.2 @@ -3096,89 +1685,6 @@ snapshots: '@dprint/win32-x64@0.54.0': optional: true - '@esbuild/aix-ppc64@0.27.7': - optional: true - - '@esbuild/android-arm64@0.27.7': - optional: true - - '@esbuild/android-arm@0.27.7': - optional: true - - '@esbuild/android-x64@0.27.7': - optional: true - - '@esbuild/darwin-arm64@0.27.7': - optional: true - - '@esbuild/darwin-x64@0.27.7': - optional: true - - '@esbuild/freebsd-arm64@0.27.7': - optional: true - - '@esbuild/freebsd-x64@0.27.7': - optional: true - - '@esbuild/linux-arm64@0.27.7': - optional: true - - '@esbuild/linux-arm@0.27.7': - optional: true - - '@esbuild/linux-ia32@0.27.7': - optional: true - - '@esbuild/linux-loong64@0.27.7': - optional: true - - '@esbuild/linux-mips64el@0.27.7': - optional: true - - '@esbuild/linux-ppc64@0.27.7': - optional: true - - '@esbuild/linux-riscv64@0.27.7': - optional: true - - '@esbuild/linux-s390x@0.27.7': - optional: true - - '@esbuild/linux-x64@0.27.7': - optional: true - - '@esbuild/netbsd-arm64@0.27.7': - optional: true - - '@esbuild/netbsd-x64@0.27.7': - optional: true - - '@esbuild/openbsd-arm64@0.27.7': - optional: true - - '@esbuild/openbsd-x64@0.27.7': - optional: true - - '@esbuild/openharmony-arm64@0.27.7': - optional: true - - '@esbuild/sunos-x64@0.27.7': - optional: true - - '@esbuild/win32-arm64@0.27.7': - optional: true - - '@esbuild/win32-ia32@0.27.7': - optional: true - - '@esbuild/win32-x64@0.27.7': - optional: true - - '@eslint-community/eslint-utils@4.9.1(eslint@10.2.1(jiti@2.6.1))': - dependencies: - eslint: 10.2.1(jiti@2.6.1) - eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.6.1))': dependencies: eslint: 9.39.4(jiti@2.6.1) @@ -3186,68 +1692,6 @@ snapshots: '@eslint-community/regexpp@4.12.2': {} - '@eslint-react/ast@4.2.3(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3)': - dependencies: - '@typescript-eslint/types': 8.59.0 - '@typescript-eslint/typescript-estree': 8.59.0(typescript@6.0.3) - '@typescript-eslint/utils': 8.59.0(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - eslint: 10.2.1(jiti@2.6.1) - string-ts: 2.3.1 - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - - '@eslint-react/core@4.2.3(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3)': - dependencies: - '@eslint-react/ast': 4.2.3(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/jsx': 4.2.3(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/shared': 4.2.3(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/var': 4.2.3(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - '@typescript-eslint/scope-manager': 8.59.0 - '@typescript-eslint/types': 8.59.0 - '@typescript-eslint/utils': 8.59.0(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - eslint: 10.2.1(jiti@2.6.1) - ts-pattern: 5.9.0 - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - - '@eslint-react/jsx@4.2.3(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3)': - dependencies: - '@eslint-react/ast': 4.2.3(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/shared': 4.2.3(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/var': 4.2.3(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - '@typescript-eslint/types': 8.59.0 - '@typescript-eslint/utils': 8.59.0(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - eslint: 10.2.1(jiti@2.6.1) - ts-pattern: 5.9.0 - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - - '@eslint-react/shared@4.2.3(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3)': - dependencies: - '@typescript-eslint/utils': 8.59.0(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - eslint: 10.2.1(jiti@2.6.1) - ts-pattern: 5.9.0 - typescript: 6.0.3 - zod: 4.3.6 - transitivePeerDependencies: - - supports-color - - '@eslint-react/var@4.2.3(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3)': - dependencies: - '@eslint-react/ast': 4.2.3(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/shared': 4.2.3(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - '@typescript-eslint/scope-manager': 8.59.0 - '@typescript-eslint/types': 8.59.0 - '@typescript-eslint/utils': 8.59.0(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - eslint: 10.2.1(jiti@2.6.1) - ts-pattern: 5.9.0 - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - '@eslint/config-array@0.21.2': dependencies: '@eslint/object-schema': 2.1.7 @@ -3256,30 +1700,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/config-array@0.23.5': - dependencies: - '@eslint/object-schema': 3.0.5 - debug: 4.4.3 - minimatch: 10.2.5 - transitivePeerDependencies: - - supports-color - '@eslint/config-helpers@0.4.2': dependencies: '@eslint/core': 0.17.0 - '@eslint/config-helpers@0.5.5': - dependencies: - '@eslint/core': 1.2.1 - '@eslint/core@0.17.0': dependencies: '@types/json-schema': 7.0.15 - '@eslint/core@1.2.1': - dependencies: - '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.5': dependencies: ajv: 6.15.0 @@ -3298,18 +1726,11 @@ snapshots: '@eslint/object-schema@2.1.7': {} - '@eslint/object-schema@3.0.5': {} - '@eslint/plugin-kit@0.4.1': dependencies: '@eslint/core': 0.17.0 levn: 0.4.1 - '@eslint/plugin-kit@0.7.1': - dependencies: - '@eslint/core': 1.2.1 - levn: 0.4.1 - '@gar/promise-retry@1.0.3': {} '@humanfs/core@0.19.2': @@ -3477,18 +1898,6 @@ snapshots: - '@types/node' - conventional-commits-filter - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.20.1 - '@npmcli/agent@4.0.0': dependencies: agent-base: 7.1.4 @@ -3672,80 +2081,6 @@ snapshots: dependencies: '@octokit/openapi-types': 27.0.0 - '@secretlint/config-creator@10.2.2': - dependencies: - '@secretlint/types': 10.2.2 - - '@secretlint/config-loader@10.2.2': - dependencies: - '@secretlint/profiler': 10.2.2 - '@secretlint/resolver': 10.2.2 - '@secretlint/types': 10.2.2 - ajv: 8.18.0 - debug: 4.4.3 - rc-config-loader: 4.1.4 - transitivePeerDependencies: - - supports-color - - '@secretlint/core@10.2.2': - dependencies: - '@secretlint/profiler': 10.2.2 - '@secretlint/types': 10.2.2 - debug: 4.4.3 - structured-source: 4.0.0 - transitivePeerDependencies: - - supports-color - - '@secretlint/formatter@10.2.2': - dependencies: - '@secretlint/resolver': 10.2.2 - '@secretlint/types': 10.2.2 - '@textlint/linter-formatter': 15.5.4 - '@textlint/module-interop': 15.5.4 - '@textlint/types': 15.5.4 - chalk: 5.6.2 - debug: 4.4.3 - pluralize: 8.0.0 - strip-ansi: 7.2.0 - table: 6.9.0 - terminal-link: 4.0.0 - transitivePeerDependencies: - - supports-color - - '@secretlint/node@10.2.2': - dependencies: - '@secretlint/config-loader': 10.2.2 - '@secretlint/core': 10.2.2 - '@secretlint/formatter': 10.2.2 - '@secretlint/profiler': 10.2.2 - '@secretlint/source-creator': 10.2.2 - '@secretlint/types': 10.2.2 - debug: 4.4.3 - p-map: 7.0.4 - transitivePeerDependencies: - - supports-color - - '@secretlint/profiler@10.2.2': {} - - '@secretlint/resolver@10.2.2': {} - - '@secretlint/secretlint-formatter-sarif@10.2.2': - dependencies: - node-sarif-builder: 3.4.0 - - '@secretlint/secretlint-rule-no-dotenv@10.2.2': - dependencies: - '@secretlint/types': 10.2.2 - - '@secretlint/secretlint-rule-preset-recommend@10.2.2': {} - - '@secretlint/source-creator@10.2.2': - dependencies: - '@secretlint/types': 10.2.2 - istextorbinary: 9.5.0 - - '@secretlint/types@10.2.2': {} - '@sigstore/bundle@4.0.0': dependencies: '@sigstore/protobuf-specs': 0.5.1 @@ -3786,37 +2121,6 @@ snapshots: '@simple-libs/stream-utils@1.2.0': {} - '@sindresorhus/merge-streams@2.3.0': {} - - '@textlint/ast-node-types@15.5.4': {} - - '@textlint/linter-formatter@15.5.4': - dependencies: - '@azu/format-text': 1.0.2 - '@azu/style-format': 1.0.1 - '@textlint/module-interop': 15.5.4 - '@textlint/resolver': 15.5.4 - '@textlint/types': 15.5.4 - chalk: 4.1.2 - debug: 4.4.3 - js-yaml: 4.1.1 - lodash: 4.18.1 - pluralize: 2.0.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - table: 6.9.0 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color - - '@textlint/module-interop@15.5.4': {} - - '@textlint/resolver@15.5.4': {} - - '@textlint/types@15.5.4': - dependencies: - '@textlint/ast-node-types': 15.5.4 - '@tufjs/canonical-json@2.0.0': {} '@tufjs/models@4.1.0': @@ -3829,8 +2133,6 @@ snapshots: '@types/estree': 1.0.8 '@types/json-schema': 7.0.15 - '@types/esrecurse@4.3.1': {} - '@types/estree@1.0.8': {} '@types/json-schema@7.0.15': {} @@ -3845,267 +2147,120 @@ snapshots: dependencies: parse-path: 7.1.0 - '@types/sarif@2.1.7': {} - - '@types/vscode@1.116.0': {} - - '@typescript-eslint/eslint-plugin@8.61.1(@typescript-eslint/parser@8.59.0(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3))(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3)': - dependencies: - '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.59.0(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/type-utils': 8.61.1(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - '@typescript-eslint/utils': 8.61.1(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.61.1 - eslint: 10.2.1(jiti@2.6.1) - ignore: 7.0.5 - natural-compare: 1.4.0 - ts-api-utils: 2.5.0(typescript@6.0.3) - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@8.59.0(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3)': - dependencies: - '@typescript-eslint/scope-manager': 8.59.0 - '@typescript-eslint/types': 8.59.0 - '@typescript-eslint/typescript-estree': 8.59.0(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.59.0 - debug: 4.4.3 - eslint: 10.2.1(jiti@2.6.1) - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/project-service@8.59.0(typescript@6.0.3)': - dependencies: - '@typescript-eslint/tsconfig-utils': 8.59.0(typescript@6.0.3) - '@typescript-eslint/types': 8.59.0 - debug: 4.4.3 - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/project-service@8.61.1(typescript@6.0.3)': - dependencies: - '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@6.0.3) - '@typescript-eslint/types': 8.61.1 - debug: 4.4.3 - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/scope-manager@8.59.0': - dependencies: - '@typescript-eslint/types': 8.59.0 - '@typescript-eslint/visitor-keys': 8.59.0 - - '@typescript-eslint/scope-manager@8.61.1': - dependencies: - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/visitor-keys': 8.61.1 - - '@typescript-eslint/tsconfig-utils@8.59.0(typescript@6.0.3)': - dependencies: - typescript: 6.0.3 - - '@typescript-eslint/tsconfig-utils@8.61.1(typescript@6.0.3)': - dependencies: - typescript: 6.0.3 - - '@typescript-eslint/type-utils@8.59.0(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3)': + '@typescript-eslint/parser@8.72.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3)': dependencies: - '@typescript-eslint/types': 8.59.0 - '@typescript-eslint/typescript-estree': 8.59.0(typescript@6.0.3) - '@typescript-eslint/utils': 8.59.0(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.72.0 + '@typescript-eslint/types': 8.72.0 + '@typescript-eslint/typescript-estree': 8.72.0(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.72.0 debug: 4.4.3 - eslint: 10.2.1(jiti@2.6.1) - ts-api-utils: 2.5.0(typescript@6.0.3) + eslint: 9.39.4(jiti@2.6.1) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@8.61.1(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3)': + '@typescript-eslint/project-service@8.72.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) - '@typescript-eslint/utils': 8.61.1(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.72.0(typescript@6.0.3) + '@typescript-eslint/types': 8.72.0 debug: 4.4.3 - eslint: 10.2.1(jiti@2.6.1) - ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.59.0': {} - - '@typescript-eslint/types@8.61.1': {} + '@typescript-eslint/scope-manager@8.72.0': + dependencies: + '@typescript-eslint/types': 8.72.0 + '@typescript-eslint/visitor-keys': 8.72.0 - '@typescript-eslint/typescript-estree@8.59.0(typescript@6.0.3)': + '@typescript-eslint/tsconfig-utils@8.72.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.59.0(typescript@6.0.3) - '@typescript-eslint/tsconfig-utils': 8.59.0(typescript@6.0.3) - '@typescript-eslint/types': 8.59.0 - '@typescript-eslint/visitor-keys': 8.59.0 - debug: 4.4.3 - minimatch: 10.2.5 - semver: 7.7.4 - tinyglobby: 0.2.16 - ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/typescript-estree@8.61.1(typescript@6.0.3)': + '@typescript-eslint/types@8.72.0': {} + + '@typescript-eslint/typescript-estree@8.72.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.61.1(typescript@6.0.3) - '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@6.0.3) - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/visitor-keys': 8.61.1 + '@typescript-eslint/project-service': 8.72.0(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.72.0(typescript@6.0.3) + '@typescript-eslint/types': 8.72.0 + '@typescript-eslint/visitor-keys': 8.72.0 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.7.4 + semver: 7.8.4 tinyglobby: 0.2.16 ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.59.0(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3)': + '@typescript-eslint/visitor-keys@8.72.0': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.2.1(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.59.0 - '@typescript-eslint/types': 8.59.0 - '@typescript-eslint/typescript-estree': 8.59.0(typescript@6.0.3) - eslint: 10.2.1(jiti@2.6.1) - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color + '@typescript-eslint/types': 8.72.0 + eslint-visitor-keys: 5.0.1 - '@typescript-eslint/utils@8.61.1(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3)': - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.2.1(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/typescript-estree': 8.61.1(typescript@6.0.3) - eslint: 10.2.1(jiti@2.6.1) - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color + '@typescript/typescript-aix-ppc64@7.1.0-beta': + optional: true - '@typescript-eslint/visitor-keys@8.59.0': - dependencies: - '@typescript-eslint/types': 8.59.0 - eslint-visitor-keys: 5.0.1 + '@typescript/typescript-android-arm64@7.1.0-beta': + optional: true - '@typescript-eslint/visitor-keys@8.61.1': - dependencies: - '@typescript-eslint/types': 8.61.1 - eslint-visitor-keys: 5.0.1 + '@typescript/typescript-darwin-arm64@7.1.0-beta': + optional: true - '@typescript-native-bridge/darwin-arm64@6.0.3-bridge.16.tsgo.7.0.2': + '@typescript/typescript-darwin-x64@7.1.0-beta': optional: true - '@typescript-native-bridge/darwin-x64@6.0.3-bridge.16.tsgo.7.0.2': + '@typescript/typescript-freebsd-arm64@7.1.0-beta': optional: true - '@typescript-native-bridge/linux-arm64@6.0.3-bridge.16.tsgo.7.0.2': + '@typescript/typescript-freebsd-x64@7.1.0-beta': optional: true - '@typescript-native-bridge/linux-arm@6.0.3-bridge.16.tsgo.7.0.2': + '@typescript/typescript-linux-arm64@7.1.0-beta': optional: true - '@typescript-native-bridge/linux-x64@6.0.3-bridge.16.tsgo.7.0.2': + '@typescript/typescript-linux-arm@7.1.0-beta': optional: true - '@typescript-native-bridge/win32-arm64@6.0.3-bridge.16.tsgo.7.0.2': + '@typescript/typescript-linux-loong64@7.1.0-beta': optional: true - '@typescript-native-bridge/win32-x64@6.0.3-bridge.16.tsgo.7.0.2': + '@typescript/typescript-linux-mips64el@7.1.0-beta': optional: true - '@typespec/ts-http-runtime@0.3.5': - dependencies: - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color + '@typescript/typescript-linux-ppc64@7.1.0-beta': + optional: true - '@vscode/vsce-sign-alpine-arm64@2.0.6': + '@typescript/typescript-linux-riscv64@7.1.0-beta': optional: true - '@vscode/vsce-sign-alpine-x64@2.0.6': + '@typescript/typescript-linux-s390x@7.1.0-beta': optional: true - '@vscode/vsce-sign-darwin-arm64@2.0.6': + '@typescript/typescript-linux-x64@7.1.0-beta': optional: true - '@vscode/vsce-sign-darwin-x64@2.0.6': + '@typescript/typescript-netbsd-arm64@7.1.0-beta': optional: true - '@vscode/vsce-sign-linux-arm64@2.0.6': + '@typescript/typescript-netbsd-x64@7.1.0-beta': optional: true - '@vscode/vsce-sign-linux-arm@2.0.6': + '@typescript/typescript-openbsd-arm64@7.1.0-beta': optional: true - '@vscode/vsce-sign-linux-x64@2.0.6': + '@typescript/typescript-openbsd-x64@7.1.0-beta': optional: true - '@vscode/vsce-sign-win32-arm64@2.0.6': + '@typescript/typescript-sunos-x64@7.1.0-beta': optional: true - '@vscode/vsce-sign-win32-x64@2.0.6': + '@typescript/typescript-win32-arm64@7.1.0-beta': optional: true - '@vscode/vsce-sign@2.0.9': - optionalDependencies: - '@vscode/vsce-sign-alpine-arm64': 2.0.6 - '@vscode/vsce-sign-alpine-x64': 2.0.6 - '@vscode/vsce-sign-darwin-arm64': 2.0.6 - '@vscode/vsce-sign-darwin-x64': 2.0.6 - '@vscode/vsce-sign-linux-arm': 2.0.6 - '@vscode/vsce-sign-linux-arm64': 2.0.6 - '@vscode/vsce-sign-linux-x64': 2.0.6 - '@vscode/vsce-sign-win32-arm64': 2.0.6 - '@vscode/vsce-sign-win32-x64': 2.0.6 - - '@vscode/vsce@3.9.2': - dependencies: - '@azure/identity': 4.13.1 - '@secretlint/node': 10.2.2 - '@secretlint/secretlint-formatter-sarif': 10.2.2 - '@secretlint/secretlint-rule-no-dotenv': 10.2.2 - '@secretlint/secretlint-rule-preset-recommend': 10.2.2 - '@vscode/vsce-sign': 2.0.9 - azure-devops-node-api: 12.5.0 - chalk: 4.1.2 - cheerio: 1.2.0 - cockatiel: 3.2.1 - commander: 12.1.0 - form-data: 4.0.5 - glob: 13.0.6 - hosted-git-info: 4.1.0 - jsonc-parser: 3.3.1 - leven: 3.1.0 - markdown-it: 14.1.1 - mime: 1.6.0 - minimatch: 10.2.5 - parse-semver: 1.1.1 - read: 1.0.7 - secretlint: 10.2.2 - semver: 7.7.4 - tmp: 0.2.5 - typed-rest-client: 1.8.11 - url-join: 4.0.1 - xml2js: 0.5.0 - yauzl: 3.3.0 - yazl: 2.5.1 - optionalDependencies: - keytar: 7.9.0 - transitivePeerDependencies: - - supports-color + '@typescript/typescript-win32-x64@7.1.0-beta': + optional: true abbrev@4.0.0: {} @@ -4124,55 +2279,22 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ajv@8.18.0: - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.0 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - - ansi-escapes@7.3.0: - dependencies: - environment: 1.1.0 - - ansi-regex@5.0.1: {} - ansi-regex@6.2.2: {} - ansi-styles@3.2.1: - dependencies: - color-convert: 1.9.3 - ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 ansi-styles@6.2.3: {} - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 - argparse@2.0.1: {} array-ify@1.0.0: {} - astral-regex@2.0.0: {} - - asynckit@0.4.0: {} - - azure-devops-node-api@12.5.0: - dependencies: - tunnel: 0.0.6 - typed-rest-client: 1.8.11 - balanced-match@1.0.2: {} balanced-match@4.0.4: {} - base64-js@1.5.1: - optional: true - before-after-hook@4.0.0: {} bin-links@6.0.0: @@ -4183,21 +2305,6 @@ snapshots: read-cmd-shim: 6.0.0 write-file-atomic: 7.0.1 - binaryextensions@6.11.0: - dependencies: - editions: 6.22.0 - - bl@4.1.0: - dependencies: - buffer: 5.7.1 - inherits: 2.0.4 - readable-stream: 3.6.2 - optional: true - - boolbase@1.0.0: {} - - boundary@2.0.0: {} - brace-expansion@1.1.14: dependencies: balanced-match: 1.0.2 @@ -4207,26 +2314,6 @@ snapshots: dependencies: balanced-match: 4.0.4 - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - - buffer-crc32@0.2.13: {} - - buffer-equal-constant-time@1.0.1: {} - - buffer@5.7.1: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - optional: true - - builtin-modules@1.1.1: {} - - bundle-name@4.1.0: - dependencies: - run-applescript: 7.1.0 - cacache@20.0.4: dependencies: '@npmcli/fs': 5.0.0 @@ -4240,57 +2327,13 @@ snapshots: p-map: 7.0.4 ssri: 13.0.1 - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - call-bound@1.0.4: - dependencies: - call-bind-apply-helpers: 1.0.2 - get-intrinsic: 1.3.0 - callsites@3.1.0: {} - chalk@2.4.2: - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 - chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - chalk@5.6.2: {} - - cheerio-select@2.1.0: - dependencies: - boolbase: 1.0.0 - css-select: 5.2.2 - css-what: 6.2.2 - domelementtype: 2.3.0 - domhandler: 5.0.3 - domutils: 3.2.2 - - cheerio@1.2.0: - dependencies: - cheerio-select: 2.1.0 - dom-serializer: 2.0.0 - domhandler: 5.0.3 - domutils: 3.2.2 - encoding-sniffer: 0.2.1 - htmlparser2: 10.1.0 - parse5: 7.3.0 - parse5-htmlparser2-tree-adapter: 7.1.0 - parse5-parser-stream: 7.1.2 - undici: 7.25.0 - whatwg-mimetype: 4.0.0 - - chownr@1.1.4: - optional: true - chownr@3.0.0: {} ci-info@4.4.0: {} @@ -4305,28 +2348,12 @@ snapshots: cmd-shim@8.0.0: {} - cockatiel@3.2.1: {} - - color-convert@1.9.3: - dependencies: - color-name: 1.1.3 - color-convert@2.0.1: dependencies: color-name: 1.1.4 - color-name@1.1.3: {} - color-name@1.1.4: {} - combined-stream@1.0.8: - dependencies: - delayed-stream: 1.0.0 - - commander@12.1.0: {} - - commander@2.20.3: {} - common-ancestor-path@2.0.0: {} compare-func@2.0.0: @@ -4334,8 +2361,6 @@ snapshots: array-ify: 1.0.0 dot-prop: 5.3.0 - compare-versions@6.1.1: {} - concat-map@0.0.1: {} conventional-changelog-angular@8.3.1: @@ -4387,68 +2412,16 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - css-select@5.2.2: - dependencies: - boolbase: 1.0.0 - css-what: 6.2.2 - domhandler: 5.0.3 - domutils: 3.2.2 - nth-check: 2.1.1 - - css-what@6.2.2: {} - cssesc@3.0.0: {} debug@4.4.3: dependencies: ms: 2.1.3 - decompress-response@6.0.0: - dependencies: - mimic-response: 3.1.0 - optional: true - - deep-extend@0.6.0: - optional: true - deep-is@0.1.4: {} - default-browser-id@5.0.1: {} - - default-browser@5.5.0: - dependencies: - bundle-name: 4.1.0 - default-browser-id: 5.0.1 - - define-lazy-prop@3.0.0: {} - - delayed-stream@1.0.0: {} - detect-indent@7.0.2: {} - detect-libc@2.1.2: - optional: true - - diff@4.0.4: {} - - dom-serializer@2.0.0: - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - entities: 4.5.0 - - domelementtype@2.3.0: {} - - domhandler@5.0.3: - dependencies: - domelementtype: 2.3.0 - - domutils@3.2.2: - dependencies: - dom-serializer: 2.0.0 - domelementtype: 2.3.0 - domhandler: 5.0.3 - dot-prop@5.3.0: dependencies: is-obj: 2.0.0 @@ -4469,183 +2442,25 @@ snapshots: '@dprint/win32-arm64': 0.54.0 '@dprint/win32-x64': 0.54.0 - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - - ecdsa-sig-formatter@1.0.11: - dependencies: - safe-buffer: 5.2.1 - - editions@6.22.0: - dependencies: - version-range: 4.15.0 - emoji-regex@10.6.0: {} - emoji-regex@8.0.0: {} - - encoding-sniffer@0.2.1: - dependencies: - iconv-lite: 0.6.3 - whatwg-encoding: 3.1.1 - - end-of-stream@1.4.5: - dependencies: - once: 1.4.0 - optional: true - - entities@4.5.0: {} - - entities@6.0.1: {} - - entities@7.0.1: {} - env-paths@2.2.1: {} - environment@1.1.0: {} - - error-stack-parser@2.1.4: - dependencies: - stackframe: 1.3.4 - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-object-atoms@1.1.1: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.1.0: - dependencies: - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - has-tostringtag: 1.0.2 - hasown: 2.0.3 - - esbuild@0.27.7: - optionalDependencies: - '@esbuild/aix-ppc64': 0.27.7 - '@esbuild/android-arm': 0.27.7 - '@esbuild/android-arm64': 0.27.7 - '@esbuild/android-x64': 0.27.7 - '@esbuild/darwin-arm64': 0.27.7 - '@esbuild/darwin-x64': 0.27.7 - '@esbuild/freebsd-arm64': 0.27.7 - '@esbuild/freebsd-x64': 0.27.7 - '@esbuild/linux-arm': 0.27.7 - '@esbuild/linux-arm64': 0.27.7 - '@esbuild/linux-ia32': 0.27.7 - '@esbuild/linux-loong64': 0.27.7 - '@esbuild/linux-mips64el': 0.27.7 - '@esbuild/linux-ppc64': 0.27.7 - '@esbuild/linux-riscv64': 0.27.7 - '@esbuild/linux-s390x': 0.27.7 - '@esbuild/linux-x64': 0.27.7 - '@esbuild/netbsd-arm64': 0.27.7 - '@esbuild/netbsd-x64': 0.27.7 - '@esbuild/openbsd-arm64': 0.27.7 - '@esbuild/openbsd-x64': 0.27.7 - '@esbuild/openharmony-arm64': 0.27.7 - '@esbuild/sunos-x64': 0.27.7 - '@esbuild/win32-arm64': 0.27.7 - '@esbuild/win32-ia32': 0.27.7 - '@esbuild/win32-x64': 0.27.7 - escalade@3.2.0: {} - escape-string-regexp@1.0.5: {} - escape-string-regexp@4.0.0: {} - eslint-plugin-expect-type@0.4.3(@typescript-eslint/parser@8.59.0(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3))(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3): - dependencies: - '@typescript-eslint/parser': 8.59.0(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - '@typescript-eslint/utils': 8.59.0(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - eslint: 10.2.1(jiti@2.6.1) - fs-extra: 11.3.4 - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - - eslint-plugin-react-x@4.2.3(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3): - dependencies: - '@eslint-react/ast': 4.2.3(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/core': 4.2.3(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/jsx': 4.2.3(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/shared': 4.2.3(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - '@eslint-react/var': 4.2.3(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - '@typescript-eslint/scope-manager': 8.59.0 - '@typescript-eslint/type-utils': 8.59.0(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - '@typescript-eslint/types': 8.59.0 - '@typescript-eslint/utils': 8.59.0(eslint@10.2.1(jiti@2.6.1))(typescript@6.0.3) - compare-versions: 6.1.1 - eslint: 10.2.1(jiti@2.6.1) - string-ts: 2.3.1 - ts-api-utils: 2.5.0(typescript@6.0.3) - ts-pattern: 5.9.0 - typescript: 6.0.3 - transitivePeerDependencies: - - supports-color - eslint-scope@8.4.0: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 - eslint-scope@9.1.2: - dependencies: - '@types/esrecurse': 4.3.1 - '@types/estree': 1.0.8 - esrecurse: 4.3.0 - estraverse: 5.3.0 - eslint-visitor-keys@3.4.3: {} eslint-visitor-keys@4.2.1: {} eslint-visitor-keys@5.0.1: {} - eslint@10.2.1(jiti@2.6.1): - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.2.1(jiti@2.6.1)) - '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.23.5 - '@eslint/config-helpers': 0.5.5 - '@eslint/core': 1.2.1 - '@eslint/plugin-kit': 0.7.1 - '@humanfs/node': 0.16.8 - '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.3 - '@types/estree': 1.0.8 - ajv: 6.15.0 - cross-spawn: 7.0.6 - debug: 4.4.3 - escape-string-regexp: 4.0.0 - eslint-scope: 9.1.2 - eslint-visitor-keys: 5.0.1 - espree: 11.2.0 - esquery: 1.7.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 8.0.0 - find-up: 5.0.0 - glob-parent: 6.0.2 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - json-stable-stringify-without-jsonify: 1.0.1 - minimatch: 10.2.5 - natural-compare: 1.4.0 - optionator: 0.9.4 - optionalDependencies: - jiti: 2.6.1 - transitivePeerDependencies: - - supports-color - eslint@9.39.4(jiti@2.6.1): dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) @@ -4693,14 +2508,6 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.16.0) eslint-visitor-keys: 4.2.1 - espree@11.2.0: - dependencies: - acorn: 8.16.0 - acorn-jsx: 5.3.2(acorn@8.16.0) - eslint-visitor-keys: 5.0.1 - - esprima@4.0.1: {} - esquery@1.7.0: dependencies: estraverse: 5.3.0 @@ -4713,23 +2520,12 @@ snapshots: esutils@2.0.3: {} - expand-template@2.0.3: - optional: true - exponential-backoff@3.1.3: {} fast-content-type-parse@3.0.0: {} fast-deep-equal@3.1.3: {} - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} @@ -4740,16 +2536,10 @@ snapshots: dependencies: fast-string-truncated-width: 3.0.3 - fast-uri@3.1.0: {} - fast-wrap-ansi@0.2.0: dependencies: fast-string-width: 3.0.2 - fastq@1.20.1: - dependencies: - reusify: 1.1.0 - fd-package-json@2.0.0: dependencies: walk-up-path: 4.0.0 @@ -4762,10 +2552,6 @@ snapshots: dependencies: flat-cache: 4.0.1 - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - find-up@4.1.0: dependencies: locate-path: 5.0.0 @@ -4783,53 +2569,14 @@ snapshots: flatted@3.4.2: {} - form-data@4.0.5: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - es-set-tostringtag: 2.1.0 - hasown: 2.0.3 - mime-types: 2.1.35 - - fs-constants@1.0.0: - optional: true - - fs-extra@11.3.4: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.2.1 - universalify: 2.0.1 - fs-minipass@3.0.3: dependencies: minipass: 7.1.3 - fs.realpath@1.0.0: {} - - function-bind@1.1.2: {} - get-caller-file@2.0.5: {} get-east-asian-width@1.5.0: {} - get-intrinsic@1.3.0: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.3 - math-intrinsics: 1.1.0 - - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 - git-up@8.1.1: dependencies: is-ssh: 1.4.1 @@ -4839,13 +2586,6 @@ snapshots: dependencies: git-up: 8.1.1 - github-from-package@0.0.0: - optional: true - - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - glob-parent@6.0.2: dependencies: is-glob: 4.0.3 @@ -4856,28 +2596,8 @@ snapshots: minipass: 7.1.3 path-scurry: 2.0.2 - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.5 - once: 1.4.0 - path-is-absolute: 1.0.1 - globals@14.0.0: {} - globby@14.1.0: - dependencies: - '@sindresorhus/merge-streams': 2.3.0 - fast-glob: 3.3.3 - ignore: 7.0.5 - path-type: 6.0.0 - slash: 5.1.0 - unicorn-magic: 0.3.0 - - gopd@1.2.0: {} - graceful-fs@4.2.11: {} grammex@3.1.12: {} @@ -4893,28 +2613,8 @@ snapshots: optionalDependencies: uglify-js: 3.19.3 - has-flag@3.0.0: {} - has-flag@4.0.0: {} - has-symbols@1.1.0: {} - - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.1.0 - - hasown@2.0.3: - dependencies: - function-bind: 1.1.2 - - hosted-git-info@4.1.0: - dependencies: - lru-cache: 6.0.0 - - hosted-git-info@7.0.2: - dependencies: - lru-cache: 10.4.3 - hosted-git-info@8.1.0: dependencies: lru-cache: 10.4.3 @@ -4923,13 +2623,6 @@ snapshots: dependencies: lru-cache: 11.3.5 - htmlparser2@10.1.0: - dependencies: - domelementtype: 2.3.0 - domhandler: 5.0.3 - domutils: 3.2.2 - entities: 7.0.1 - http-cache-semantics@4.2.0: {} http-proxy-agent@7.0.2: @@ -4946,26 +2639,17 @@ snapshots: transitivePeerDependencies: - supports-color - iconv-lite@0.6.3: - dependencies: - safer-buffer: 2.1.2 - iconv-lite@0.7.2: dependencies: safer-buffer: 2.1.2 optional: true - ieee754@1.2.1: - optional: true - ignore-walk@8.0.0: dependencies: minimatch: 10.2.5 ignore@5.3.2: {} - ignore@7.0.5: {} - import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -4973,47 +2657,21 @@ snapshots: import-local@3.2.0: dependencies: - pkg-dir: 4.2.0 - resolve-cwd: 3.0.0 - - imurmurhash@0.1.4: {} - - index-to-position@1.2.0: {} - - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - - inherits@2.0.4: {} + pkg-dir: 4.2.0 + resolve-cwd: 3.0.0 - ini@1.3.8: - optional: true + imurmurhash@0.1.4: {} ini@6.0.0: {} ip-address@10.1.0: {} - is-core-module@2.16.1: - dependencies: - hasown: 2.0.3 - - is-docker@3.0.0: {} - is-extglob@2.1.1: {} - is-fullwidth-code-point@3.0.0: {} - is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - is-inside-container@1.0.0: - dependencies: - is-docker: 3.0.0 - - is-number@7.0.0: {} - is-obj@2.0.0: {} is-plain-obj@4.1.0: {} @@ -5022,30 +2680,13 @@ snapshots: dependencies: protocols: 2.0.2 - is-wsl@3.1.1: - dependencies: - is-inside-container: 1.0.0 - isexe@2.0.0: {} isexe@4.0.0: {} - istextorbinary@9.5.0: - dependencies: - binaryextensions: 6.11.0 - editions: 6.22.0 - textextensions: 6.11.0 - jiti@2.6.1: optional: true - js-tokens@4.0.0: {} - - js-yaml@3.14.2: - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - js-yaml@4.1.1: dependencies: argparse: 2.0.1 @@ -5056,8 +2697,6 @@ snapshots: json-schema-traverse@0.4.1: {} - json-schema-traverse@1.0.0: {} - json-stable-stringify-without-jsonify@1.0.1: {} json-stringify-nice@1.1.4: {} @@ -5066,56 +2705,16 @@ snapshots: json5@2.2.3: {} - jsonc-parser@3.3.1: {} - - jsonfile@6.2.1: - dependencies: - universalify: 2.0.1 - optionalDependencies: - graceful-fs: 4.2.11 - jsonparse@1.3.1: {} - jsonwebtoken@9.0.3: - dependencies: - jws: 4.0.1 - lodash.includes: 4.3.0 - lodash.isboolean: 3.0.3 - lodash.isinteger: 4.0.4 - lodash.isnumber: 3.0.3 - lodash.isplainobject: 4.0.6 - lodash.isstring: 4.0.1 - lodash.once: 4.1.1 - ms: 2.1.3 - semver: 7.7.4 - just-diff-apply@5.5.0: {} just-diff@6.0.2: {} - jwa@2.0.1: - dependencies: - buffer-equal-constant-time: 1.0.1 - ecdsa-sig-formatter: 1.0.11 - safe-buffer: 5.2.1 - - jws@4.0.1: - dependencies: - jwa: 2.0.1 - safe-buffer: 5.2.1 - - keytar@7.9.0: - dependencies: - node-addon-api: 4.3.0 - prebuild-install: 7.1.3 - optional: true - keyv@4.5.4: dependencies: json-buffer: 3.0.1 - leven@3.1.0: {} - levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -5143,10 +2742,6 @@ snapshots: lilconfig@3.1.3: {} - linkify-it@5.0.0: - dependencies: - uc.micro: 2.1.0 - locate-path@5.0.0: dependencies: p-locate: 4.1.0 @@ -5155,34 +2750,12 @@ snapshots: dependencies: p-locate: 5.0.0 - lodash.includes@4.3.0: {} - - lodash.isboolean@3.0.3: {} - - lodash.isinteger@4.0.4: {} - - lodash.isnumber@3.0.3: {} - - lodash.isplainobject@4.0.6: {} - - lodash.isstring@4.0.1: {} - lodash.merge@4.6.2: {} - lodash.once@4.1.1: {} - - lodash.truncate@4.4.2: {} - - lodash@4.18.1: {} - lru-cache@10.4.3: {} lru-cache@11.3.5: {} - lru-cache@6.0.0: - dependencies: - yallist: 4.0.0 - make-fetch-happen@15.0.5: dependencies: '@gar/promise-retry': 1.0.3 @@ -5200,39 +2773,8 @@ snapshots: transitivePeerDependencies: - supports-color - markdown-it@14.1.1: - dependencies: - argparse: 2.0.1 - entities: 4.5.0 - linkify-it: 5.0.0 - mdurl: 2.0.0 - punycode.js: 2.3.1 - uc.micro: 2.1.0 - - math-intrinsics@1.1.0: {} - - mdurl@2.0.0: {} - meow@13.2.0: {} - merge2@1.4.1: {} - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.2 - - mime-db@1.52.0: {} - - mime-types@2.1.35: - dependencies: - mime-db: 1.52.0 - - mime@1.6.0: {} - - mimic-response@3.1.0: - optional: true - minimatch@10.2.5: dependencies: brace-expansion: 5.0.5 @@ -5277,36 +2819,16 @@ snapshots: dependencies: minipass: 7.1.3 - mkdirp-classic@0.5.3: - optional: true - - mkdirp@0.5.6: - dependencies: - minimist: 1.2.8 - ms@2.1.3: {} - mute-stream@0.0.8: {} - mute-stream@3.0.0: {} - napi-build-utils@2.0.0: - optional: true - natural-compare@1.4.0: {} negotiator@1.0.0: {} neo-async@2.6.2: {} - node-abi@3.89.0: - dependencies: - semver: 7.7.4 - optional: true - - node-addon-api@4.3.0: - optional: true - node-gyp@12.3.0: dependencies: env-paths: 2.2.1 @@ -5320,21 +2842,10 @@ snapshots: undici: 6.25.0 which: 6.0.1 - node-sarif-builder@3.4.0: - dependencies: - '@types/sarif': 2.1.7 - fs-extra: 11.3.4 - nopt@9.0.0: dependencies: abbrev: 4.0.0 - normalize-package-data@6.0.2: - dependencies: - hosted-git-info: 7.0.2 - semver: 7.7.4 - validate-npm-package-license: 3.0.4 - normalize-package-data@7.0.1: dependencies: hosted-git-info: 8.1.0 @@ -5383,23 +2894,6 @@ snapshots: transitivePeerDependencies: - supports-color - nth-check@2.1.1: - dependencies: - boolbase: 1.0.0 - - object-inspect@1.13.4: {} - - once@1.4.0: - dependencies: - wrappy: 1.0.2 - - open@10.2.0: - dependencies: - default-browser: 5.5.0 - define-lazy-prop: 3.0.0 - is-inside-container: 1.0.0 - wsl-utils: 0.1.0 - optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -5461,90 +2955,35 @@ snapshots: just-diff: 6.0.2 just-diff-apply: 5.5.0 - parse-json@8.3.0: - dependencies: - '@babel/code-frame': 7.29.0 - index-to-position: 1.2.0 - type-fest: 4.41.0 - parse-path@7.1.0: dependencies: protocols: 2.0.2 - parse-semver@1.1.1: - dependencies: - semver: 5.7.2 - parse-url@9.2.0: dependencies: '@types/parse-path': 7.1.0 parse-path: 7.1.0 - parse5-htmlparser2-tree-adapter@7.1.0: - dependencies: - domhandler: 5.0.3 - parse5: 7.3.0 - - parse5-parser-stream@7.1.2: - dependencies: - parse5: 7.3.0 - - parse5@7.3.0: - dependencies: - entities: 6.0.1 - path-exists@4.0.0: {} - path-is-absolute@1.0.1: {} - path-key@3.1.1: {} - path-parse@1.0.7: {} - path-scurry@2.0.2: dependencies: lru-cache: 11.3.5 minipass: 7.1.3 - path-type@6.0.0: {} - - pend@1.2.0: {} - - picocolors@1.1.1: {} - - picomatch@2.3.2: {} - picomatch@4.0.4: {} pkg-dir@4.2.0: dependencies: find-up: 4.1.0 - pluralize@2.0.0: {} - - pluralize@8.0.0: {} - postcss-selector-parser@7.1.1: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 - prebuild-install@7.1.3: - dependencies: - detect-libc: 2.1.2 - expand-template: 2.0.3 - github-from-package: 0.0.0 - minimist: 1.2.8 - mkdirp-classic: 0.5.3 - napi-build-utils: 2.0.0 - node-abi: 3.89.0 - pump: 3.0.4 - rc: 1.2.8 - simple-get: 4.0.1 - tar-fs: 2.1.4 - tunnel-agent: 0.6.0 - optional: true - prelude-ls@1.2.1: {} proc-log@6.1.0: {} @@ -5557,62 +2996,10 @@ snapshots: protocols@2.0.2: {} - pump@3.0.4: - dependencies: - end-of-stream: 1.4.5 - once: 1.4.0 - optional: true - - punycode.js@2.3.1: {} - punycode@2.3.1: {} - qs@6.15.1: - dependencies: - side-channel: 1.1.0 - - queue-microtask@1.2.3: {} - - rc-config-loader@4.1.4: - dependencies: - debug: 4.4.3 - js-yaml: 4.1.1 - json5: 2.2.3 - require-from-string: 2.0.2 - transitivePeerDependencies: - - supports-color - - rc@1.2.8: - dependencies: - deep-extend: 0.6.0 - ini: 1.3.8 - minimist: 1.2.8 - strip-json-comments: 2.0.1 - optional: true - read-cmd-shim@6.0.0: {} - read-pkg@9.0.1: - dependencies: - '@types/normalize-package-data': 2.4.4 - normalize-package-data: 6.0.2 - parse-json: 8.3.0 - type-fest: 4.41.0 - unicorn-magic: 0.1.0 - - read@1.0.7: - dependencies: - mute-stream: 0.0.8 - - readable-stream@3.6.2: - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - optional: true - - require-from-string@2.0.2: {} - resolve-cwd@3.0.0: dependencies: resolve-from: 5.0.0 @@ -5621,42 +3008,8 @@ snapshots: resolve-from@5.0.0: {} - resolve@1.22.12: - dependencies: - es-errors: 1.3.0 - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - - reusify@1.1.0: {} - - run-applescript@7.1.0: {} - - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - - safe-buffer@5.2.1: {} - - safer-buffer@2.1.2: {} - - sax@1.6.0: {} - - secretlint@10.2.2: - dependencies: - '@secretlint/config-creator': 10.2.2 - '@secretlint/formatter': 10.2.2 - '@secretlint/node': 10.2.2 - '@secretlint/profiler': 10.2.2 - debug: 4.4.3 - globby: 14.1.0 - read-pkg: 9.0.1 - transitivePeerDependencies: - - supports-color - - semver@5.7.2: {} - - semver@7.7.4: {} + safer-buffer@2.1.2: + optional: true semver@7.8.4: {} @@ -5666,34 +3019,6 @@ snapshots: shebang-regex@3.0.0: {} - side-channel-list@1.0.1: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - - side-channel-map@1.0.1: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - - side-channel-weakmap@1.0.2: - dependencies: - call-bound: 1.0.4 - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - object-inspect: 1.13.4 - side-channel-map: 1.0.1 - - side-channel@1.1.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.4 - side-channel-list: 1.0.1 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 - signal-exit@4.1.0: {} sigstore@4.1.0: @@ -5707,24 +3032,6 @@ snapshots: transitivePeerDependencies: - supports-color - simple-concat@1.0.1: - optional: true - - simple-get@4.0.1: - dependencies: - decompress-response: 6.0.0 - once: 1.4.0 - simple-concat: 1.0.1 - optional: true - - slash@5.1.0: {} - - slice-ansi@4.0.0: - dependencies: - ansi-styles: 4.3.0 - astral-regex: 2.0.0 - is-fullwidth-code-point: 3.0.0 - smart-buffer@4.2.0: {} socks-proxy-agent@8.0.5: @@ -5765,90 +3072,26 @@ snapshots: spdx-license-ids@3.0.23: {} - sprintf-js@1.0.3: {} - ssri@13.0.1: dependencies: minipass: 7.1.3 - stackframe@1.3.4: {} - - string-ts@2.3.1: {} - - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - string-width@7.2.0: dependencies: emoji-regex: 10.6.0 get-east-asian-width: 1.5.0 strip-ansi: 7.2.0 - string_decoder@1.3.0: - dependencies: - safe-buffer: 5.2.1 - optional: true - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - strip-ansi@7.2.0: dependencies: ansi-regex: 6.2.2 - strip-json-comments@2.0.1: - optional: true - strip-json-comments@3.1.1: {} - structured-source@4.0.0: - dependencies: - boundary: 2.0.0 - - supports-color@5.5.0: - dependencies: - has-flag: 3.0.0 - supports-color@7.2.0: dependencies: has-flag: 4.0.0 - supports-hyperlinks@3.2.0: - dependencies: - has-flag: 4.0.0 - supports-color: 7.2.0 - - supports-preserve-symlinks-flag@1.0.0: {} - - table@6.9.0: - dependencies: - ajv: 8.18.0 - lodash.truncate: 4.4.2 - slice-ansi: 4.0.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - tar-fs@2.1.4: - dependencies: - chownr: 1.1.4 - mkdirp-classic: 0.5.3 - pump: 3.0.4 - tar-stream: 2.2.0 - optional: true - - tar-stream@2.2.0: - dependencies: - bl: 4.1.0 - end-of-stream: 1.4.5 - fs-constants: 1.0.0 - inherits: 2.0.4 - readable-stream: 3.6.2 - optional: true - tar@7.5.16: dependencies: '@isaacs/fs-minipass': 4.0.1 @@ -5857,17 +3100,6 @@ snapshots: minizlib: 3.1.0 yallist: 5.0.0 - terminal-link@4.0.0: - dependencies: - ansi-escapes: 7.3.0 - supports-hyperlinks: 3.2.0 - - text-table@0.2.0: {} - - textextensions@6.11.0: - dependencies: - editions: 6.22.0 - tinyexec@1.2.4: {} tinyglobby@0.2.16: @@ -5875,52 +3107,12 @@ snapshots: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - tmp@0.2.5: {} - - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - treeverse@3.0.0: {} ts-api-utils@2.5.0(typescript@6.0.3): dependencies: typescript: 6.0.3 - ts-pattern@5.9.0: {} - - tsl@1.0.30(typescript@6.0.3): - dependencies: - esbuild: 0.27.7 - ts-api-utils: 2.5.0(typescript@6.0.3) - typescript: 6.0.3 - - tslib@1.14.1: {} - - tslib@2.8.1: {} - - tslint@6.1.3(typescript@6.0.3): - dependencies: - '@babel/code-frame': 7.29.0 - builtin-modules: 1.1.1 - chalk: 2.4.2 - commander: 2.20.3 - diff: 4.0.4 - glob: 7.2.3 - js-yaml: 3.14.2 - minimatch: 3.1.5 - mkdirp: 0.5.6 - resolve: 1.22.12 - semver: 5.7.2 - tslib: 1.14.1 - tsutils: 2.29.0(typescript@6.0.3) - typescript: 6.0.3 - - tsutils@2.29.0(typescript@6.0.3): - dependencies: - tslib: 1.14.1 - typescript: 6.0.3 - tuf-js@4.1.0: dependencies: '@tufjs/models': 4.1.0 @@ -5929,68 +3121,51 @@ snapshots: transitivePeerDependencies: - supports-color - tunnel-agent@0.6.0: - dependencies: - safe-buffer: 5.2.1 - optional: true - - tunnel@0.0.6: {} - type-check@0.4.0: dependencies: prelude-ls: 1.2.1 - type-fest@4.41.0: {} - - typed-rest-client@1.8.11: - dependencies: - qs: 6.15.1 - tunnel: 0.0.6 - underscore: 1.13.8 - - typescript-native-bridge@6.0.3-bridge.16.tsgo.7.0.2: - optionalDependencies: - '@typescript-native-bridge/darwin-arm64': 6.0.3-bridge.16.tsgo.7.0.2 - '@typescript-native-bridge/darwin-x64': 6.0.3-bridge.16.tsgo.7.0.2 - '@typescript-native-bridge/linux-arm': 6.0.3-bridge.16.tsgo.7.0.2 - '@typescript-native-bridge/linux-arm64': 6.0.3-bridge.16.tsgo.7.0.2 - '@typescript-native-bridge/linux-x64': 6.0.3-bridge.16.tsgo.7.0.2 - '@typescript-native-bridge/win32-arm64': 6.0.3-bridge.16.tsgo.7.0.2 - '@typescript-native-bridge/win32-x64': 6.0.3-bridge.16.tsgo.7.0.2 - typescript@6.0.3: {} - uc.micro@2.1.0: {} + typescript@7.1.0-beta: + optionalDependencies: + '@typescript/typescript-aix-ppc64': 7.1.0-beta + '@typescript/typescript-android-arm64': 7.1.0-beta + '@typescript/typescript-darwin-arm64': 7.1.0-beta + '@typescript/typescript-darwin-x64': 7.1.0-beta + '@typescript/typescript-freebsd-arm64': 7.1.0-beta + '@typescript/typescript-freebsd-x64': 7.1.0-beta + '@typescript/typescript-linux-arm': 7.1.0-beta + '@typescript/typescript-linux-arm64': 7.1.0-beta + '@typescript/typescript-linux-loong64': 7.1.0-beta + '@typescript/typescript-linux-mips64el': 7.1.0-beta + '@typescript/typescript-linux-ppc64': 7.1.0-beta + '@typescript/typescript-linux-riscv64': 7.1.0-beta + '@typescript/typescript-linux-s390x': 7.1.0-beta + '@typescript/typescript-linux-x64': 7.1.0-beta + '@typescript/typescript-netbsd-arm64': 7.1.0-beta + '@typescript/typescript-netbsd-x64': 7.1.0-beta + '@typescript/typescript-openbsd-arm64': 7.1.0-beta + '@typescript/typescript-openbsd-x64': 7.1.0-beta + '@typescript/typescript-sunos-x64': 7.1.0-beta + '@typescript/typescript-win32-arm64': 7.1.0-beta + '@typescript/typescript-win32-x64': 7.1.0-beta uglify-js@3.19.3: optional: true - underscore@1.13.8: {} - undici-types@7.24.6: {} undici@6.25.0: {} - undici@7.25.0: {} - - unicorn-magic@0.1.0: {} - - unicorn-magic@0.3.0: {} - universal-user-agent@7.0.3: {} - universalify@2.0.1: {} - uri-js@4.4.1: dependencies: punycode: 2.3.1 - url-join@4.0.1: {} - util-deprecate@1.0.2: {} - uuid@8.3.2: {} - validate-npm-package-license@3.0.4: dependencies: spdx-correct: 3.2.0 @@ -5998,16 +3173,8 @@ snapshots: validate-npm-package-name@7.0.2: {} - version-range@4.15.0: {} - walk-up-path@4.0.0: {} - whatwg-encoding@3.1.1: - dependencies: - iconv-lite: 0.6.3 - - whatwg-mimetype@4.0.0: {} - which@2.0.2: dependencies: isexe: 2.0.0 @@ -6026,8 +3193,6 @@ snapshots: string-width: 7.2.0 strip-ansi: 7.2.0 - wrappy@1.0.2: {} - write-file-atomic@6.0.0: dependencies: imurmurhash: 0.1.4 @@ -6044,17 +3209,6 @@ snapshots: sort-keys: 6.0.0 write-file-atomic: 6.0.0 - wsl-utils@0.1.0: - dependencies: - is-wsl: 3.1.1 - - xml2js@0.5.0: - dependencies: - sax: 1.6.0 - xmlbuilder: 11.0.1 - - xmlbuilder@11.0.1: {} - y18n@5.0.8: {} yallist@4.0.0: {} @@ -6074,20 +3228,9 @@ snapshots: y18n: 5.0.8 yargs-parser: 22.0.0 - yauzl@3.3.0: - dependencies: - buffer-crc32: 0.2.13 - pend: 1.2.0 - - yazl@2.5.1: - dependencies: - buffer-crc32: 0.2.13 - yocto-queue@0.1.0: {} zeptomatch@2.1.0: dependencies: grammex: 3.1.12 graphmatch: 1.1.1 - - zod@4.3.6: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index f6619822..fbd04a52 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,3 +2,4 @@ packages: - '.' - 'packages/*' - 'fixtures/*' + - 'tools/*' diff --git a/tools/estree-oracle/index.d.ts b/tools/estree-oracle/index.d.ts new file mode 100644 index 00000000..11327592 --- /dev/null +++ b/tools/estree-oracle/index.d.ts @@ -0,0 +1,7 @@ +// Types resolve against this package's own TypeScript 6 dependency. +export declare const ts: typeof import('typescript'); +export declare const typescriptEstree: typeof import('@typescript-eslint/typescript-estree'); +export declare const typescriptEstreeInternals: any; +export declare const parser: typeof import('@typescript-eslint/parser'); +export declare const visitorKeys: typeof import('@typescript-eslint/visitor-keys'); +export declare const eslint: typeof import('eslint'); diff --git a/tools/estree-oracle/index.js b/tools/estree-oracle/index.js new file mode 100644 index 00000000..646f0e56 --- /dev/null +++ b/tools/estree-oracle/index.js @@ -0,0 +1,24 @@ +// Test-only reference implementation for @tsslint/compat-eslint parity +// tests. typescript-eslint has no TypeScript 7 support, so the upstream +// ESTree converter and parser run here on their own TypeScript 6 copy. +// Nothing in the published packages depends on this. +module.exports = { + get ts() { + return require('typescript'); + }, + get typescriptEstree() { + return require('@typescript-eslint/typescript-estree'); + }, + get typescriptEstreeInternals() { + return require('@typescript-eslint/typescript-estree/use-at-your-own-risk'); + }, + get parser() { + return require('@typescript-eslint/parser'); + }, + get visitorKeys() { + return require('@typescript-eslint/visitor-keys'); + }, + get eslint() { + return require('eslint'); + }, +}; diff --git a/tools/estree-oracle/package.json b/tools/estree-oracle/package.json new file mode 100644 index 00000000..003df58c --- /dev/null +++ b/tools/estree-oracle/package.json @@ -0,0 +1,13 @@ +{ + "private": true, + "name": "@tsslint/estree-oracle", + "version": "3.1.4", + "main": "index.js", + "dependencies": { + "@typescript-eslint/parser": "^8.72.0", + "@typescript-eslint/typescript-estree": "^8.72.0", + "@typescript-eslint/visitor-keys": "^8.72.0", + "eslint": ">=9.39.0 <10.0.0", + "typescript": "6.0.3" + } +} diff --git a/tsconfig.base.json b/tsconfig.base.json index 0ecc49b1..f7c9c983 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1,10 +1,10 @@ { "compilerOptions": { - "target": "ES2021", + "target": "ES2022", "lib": [ - "ES2021" + "ES2022" ], - "module": "Node16", + "module": "nodenext", "sourceMap": true, "composite": true, "strict": true, From d49814ebd5a32a44d8152615b218aaf0049a10e1 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 22 Oct 2026 17:59:19 +0800 Subject: [PATCH 07/52] feat(config)!: port config to TypeScript 7, drop tsl/tslint compat tsl and tslint are built on the TypeScript 5/6 API and cannot run on TS7. The eslint, ignore, category and diagnostics plugins now use typescript/ast and typescript/sync. --- fixtures/compat-tsl/fixture.ts | 28 -- fixtures/compat-tsl/package.json | 9 - fixtures/compat-tsl/tsconfig.json | 7 - fixtures/compat-tsl/tsslint.config.ts | 6 - fixtures/compat-tslint/fixture.ts | 28 -- fixtures/compat-tslint/package.json | 9 - fixtures/compat-tslint/tsconfig.json | 7 - fixtures/compat-tslint/tsslint.config.ts | 7 - packages/config/bin/tsslint-docgen.js | 33 --- packages/config/index.ts | 2 - packages/config/lib/eslint.ts | 8 +- packages/config/lib/plugins/category.ts | 6 +- packages/config/lib/plugins/diagnostics.ts | 34 ++- packages/config/lib/plugins/ignore.ts | 131 +-------- packages/config/lib/tsl.ts | 73 ----- packages/config/lib/tslint-gen.ts | 322 --------------------- packages/config/lib/tslint-types.ts | 3 - packages/config/lib/tslint.ts | 187 ------------ packages/config/package.json | 9 +- 19 files changed, 46 insertions(+), 863 deletions(-) delete mode 100644 fixtures/compat-tsl/fixture.ts delete mode 100644 fixtures/compat-tsl/package.json delete mode 100644 fixtures/compat-tsl/tsconfig.json delete mode 100644 fixtures/compat-tsl/tsslint.config.ts delete mode 100644 fixtures/compat-tslint/fixture.ts delete mode 100644 fixtures/compat-tslint/package.json delete mode 100644 fixtures/compat-tslint/tsconfig.json delete mode 100644 fixtures/compat-tslint/tsslint.config.ts delete mode 100644 packages/config/lib/tsl.ts delete mode 100644 packages/config/lib/tslint-gen.ts delete mode 100644 packages/config/lib/tslint-types.ts delete mode 100644 packages/config/lib/tslint.ts diff --git a/fixtures/compat-tsl/fixture.ts b/fixtures/compat-tsl/fixture.ts deleted file mode 100644 index 60b0bd67..00000000 --- a/fixtures/compat-tsl/fixture.ts +++ /dev/null @@ -1,28 +0,0 @@ -// @ts-nocheck -// nullable numbers are considered unsafe by default -let num: number | undefined = 0; -if (num) { - console.log('num is defined'); -} - -// nullable strings are considered unsafe by default -let str: string | null = null; -if (!str) { - console.log('str is empty'); -} - -// nullable booleans are considered unsafe by default -function foo(bool?: boolean) { - if (bool) { - bar(); - } -} - -// `any`, unconstrained generics and unions of more than one primitive type are disallowed -const foo = (arg: T) => (arg ? 1 : 0); - -// always-truthy and always-falsy types are disallowed -let obj = {}; -while (obj) { - obj = getObj(); -} diff --git a/fixtures/compat-tsl/package.json b/fixtures/compat-tsl/package.json deleted file mode 100644 index 8e41a398..00000000 --- a/fixtures/compat-tsl/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "private": true, - "name": "@tsslint-fixtures/compat-tsl", - "version": "3.1.4", - "devDependencies": { - "@tsslint/config": "3.1.4", - "tsl": "^1.0.28" - } -} diff --git a/fixtures/compat-tsl/tsconfig.json b/fixtures/compat-tsl/tsconfig.json deleted file mode 100644 index 78a35e16..00000000 --- a/fixtures/compat-tsl/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "include": ["fixture.ts"], - "compilerOptions": { - "noEmit": true, - "allowImportingTsExtensions": true - } -} diff --git a/fixtures/compat-tsl/tsslint.config.ts b/fixtures/compat-tsl/tsslint.config.ts deleted file mode 100644 index e93a1b17..00000000 --- a/fixtures/compat-tsl/tsslint.config.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { defineConfig, fromTSLRules } from '@tsslint/config'; -import { core } from 'tsl'; - -export default defineConfig({ - rules: fromTSLRules(core.all()), -}); diff --git a/fixtures/compat-tslint/fixture.ts b/fixtures/compat-tslint/fixture.ts deleted file mode 100644 index 60b0bd67..00000000 --- a/fixtures/compat-tslint/fixture.ts +++ /dev/null @@ -1,28 +0,0 @@ -// @ts-nocheck -// nullable numbers are considered unsafe by default -let num: number | undefined = 0; -if (num) { - console.log('num is defined'); -} - -// nullable strings are considered unsafe by default -let str: string | null = null; -if (!str) { - console.log('str is empty'); -} - -// nullable booleans are considered unsafe by default -function foo(bool?: boolean) { - if (bool) { - bar(); - } -} - -// `any`, unconstrained generics and unions of more than one primitive type are disallowed -const foo = (arg: T) => (arg ? 1 : 0); - -// always-truthy and always-falsy types are disallowed -let obj = {}; -while (obj) { - obj = getObj(); -} diff --git a/fixtures/compat-tslint/package.json b/fixtures/compat-tslint/package.json deleted file mode 100644 index 5b630e48..00000000 --- a/fixtures/compat-tslint/package.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "private": true, - "name": "@tsslint-fixtures/compat-tslint", - "version": "3.1.4", - "devDependencies": { - "@tsslint/config": "3.1.4", - "tslint": "^6.1.3" - } -} diff --git a/fixtures/compat-tslint/tsconfig.json b/fixtures/compat-tslint/tsconfig.json deleted file mode 100644 index 78a35e16..00000000 --- a/fixtures/compat-tslint/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "include": ["fixture.ts"], - "compilerOptions": { - "noEmit": true, - "allowImportingTsExtensions": true - } -} diff --git a/fixtures/compat-tslint/tsslint.config.ts b/fixtures/compat-tslint/tsslint.config.ts deleted file mode 100644 index 338642f5..00000000 --- a/fixtures/compat-tslint/tsslint.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { defineConfig, importTSLintRules } from '@tsslint/config'; - -export default defineConfig({ - rules: await importTSLintRules({ - 'strict-boolean-expressions': true, - }), -}); diff --git a/packages/config/bin/tsslint-docgen.js b/packages/config/bin/tsslint-docgen.js index c7b725ae..8c58ec4f 100755 --- a/packages/config/bin/tsslint-docgen.js +++ b/packages/config/bin/tsslint-docgen.js @@ -20,7 +20,6 @@ while (true) { try { const { generateESlintTypes } = require('../lib/eslint-gen'); - const { generateTSLintTypes } = require('../lib/tslint-gen'); generateESlintTypes(nodeModulesDirs).then(({ dts, stats }) => { fs.writeFileSync(path.resolve(__dirname, '..', 'lib', 'eslint-types.d.ts'), dts); @@ -48,38 +47,6 @@ try { * ${statsTable} * * If you have added new ESLint plugins, please run \`npx tsslint-docgen\` to update this list. - */`; - indexContent = indexContent.slice(0, jsDocStart) + newJsDoc + indexContent.slice(jsDocEnd); - fs.writeFileSync(indexPath, indexContent); - } - } - }); - generateTSLintTypes(nodeModulesDirs).then(({ dts, stats }) => { - fs.writeFileSync(path.resolve(__dirname, '..', 'lib', 'tslint-types.d.ts'), dts); - - const indexPath = path.resolve(__dirname, '..', 'lib', 'tslint.d.ts'); - if (fs.existsSync(indexPath)) { - let indexContent = fs.readFileSync(indexPath, 'utf8'); - const fnIndex = indexContent.indexOf('export declare function importTSLintRules'); - const jsDocEnd = indexContent.lastIndexOf('*/', fnIndex) + 2; - const jsDocStart = indexContent.lastIndexOf('/**', jsDocEnd); - - if (jsDocStart !== -1 && jsDocEnd !== -1 && jsDocStart < fnIndex) { - const statsTable = [ - '| Dir | Rules |', - '| :--- | :--- |', - ...Object.entries(stats) - .filter(([_, count]) => count > 0) - .sort((a, b) => b[1] - a[1]) - .map(([name, count]) => `| ${name} | ${count} |`), - ].join('\n * '); - - const newJsDoc = `/** - * Converts a TSLint rules configuration to TSSLint rules. - * - * ${statsTable} - * - * If you have added new TSLint plugins, please run \`npx tsslint-docgen\` to update this list. */`; indexContent = indexContent.slice(0, jsDocStart) + newJsDoc + indexContent.slice(jsDocEnd); fs.writeFileSync(indexPath, indexContent); diff --git a/packages/config/index.ts b/packages/config/index.ts index 11dadb1f..e13d21c8 100644 --- a/packages/config/index.ts +++ b/packages/config/index.ts @@ -3,8 +3,6 @@ export * from './lib/eslint.js'; export { create as createCategoryPlugin } from './lib/plugins/category.js'; export { create as createDiagnosticsPlugin } from './lib/plugins/diagnostics.js'; export { create as createIgnorePlugin } from './lib/plugins/ignore.js'; -export * from './lib/tsl.js'; -export * from './lib/tslint.js'; import type { Config, Plugin, Rule } from '@tsslint/types'; diff --git a/packages/config/lib/eslint.ts b/packages/config/lib/eslint.ts index 722e9f9c..8745b5da 100644 --- a/packages/config/lib/eslint.ts +++ b/packages/config/lib/eslint.ts @@ -2,7 +2,7 @@ import type * as TSSLint from '@tsslint/types'; import type * as ESLint from 'eslint'; import * as fs from 'fs'; import * as path from 'path'; -import type * as ts from 'typescript'; +import { DiagnosticCategory } from 'typescript/sync'; import type { ESLintRulesConfig } from './eslint-types.js'; import { normalizeRuleSeverity, type RuleSeverity } from './utils.js'; @@ -185,10 +185,10 @@ export async function importESLintRules( options, { id: rule, ...context }, severity === 'error' - ? 1 satisfies ts.DiagnosticCategory.Error + ? DiagnosticCategory.Error : severity === 'warn' - ? 0 satisfies ts.DiagnosticCategory.Warning - : 3 satisfies ts.DiagnosticCategory.Message, + ? DiagnosticCategory.Warning + : DiagnosticCategory.Message, ); } return rules; diff --git a/packages/config/lib/plugins/category.ts b/packages/config/lib/plugins/category.ts index 8436b72e..d5cd1a9d 100644 --- a/packages/config/lib/plugins/category.ts +++ b/packages/config/lib/plugins/category.ts @@ -1,10 +1,10 @@ import type { Plugin } from '@tsslint/types'; -import type * as ts from 'typescript'; +import type { DiagnosticCategory } from 'typescript/sync'; import minimatch = require('minimatch'); -export function create(config: Record, source = 'tsslint'): Plugin { - const matchCache = new Map(); +export function create(config: Record, source = 'tsslint'): Plugin { + const matchCache = new Map(); return () => ({ resolveDiagnostics(_file, diagnostics) { for (const diagnostic of diagnostics) { diff --git a/packages/config/lib/plugins/diagnostics.ts b/packages/config/lib/plugins/diagnostics.ts index e8ee9dce..011004f5 100644 --- a/packages/config/lib/plugins/diagnostics.ts +++ b/packages/config/lib/plugins/diagnostics.ts @@ -1,27 +1,43 @@ -import type { Plugin } from '@tsslint/types'; +import type { Diagnostic, Plugin } from '@tsslint/types'; type CheckMode = 'syntactic' | 'semantic' | 'declaration'; export function create(mode: CheckMode | CheckMode[] = 'semantic'): Plugin { const modes = Array.isArray(mode) ? mode : [mode]; - return ({ languageService }) => ({ + return ({ getProgram }) => ({ resolveDiagnostics(file, diagnostics) { - const program = languageService.getProgram()!; + const program = getProgram(); for (const mode of modes) { const diags = mode === 'syntactic' - ? program.getSyntacticDiagnostics(file) + ? program.getSyntacticDiagnostics(file.fileName) : mode === 'semantic' - ? program.getSemanticDiagnostics(file) + ? program.getSemanticDiagnostics(file.fileName) : mode === 'declaration' - ? program.getDeclarationDiagnostics(file) + ? program.getDeclarationDiagnostics(file.fileName) : []; for (const diag of diags) { - diag.start ??= 0; - diag.length ??= 0; - diagnostics.push(diag as any); + diagnostics.push({ + category: diag.category, + code: diag.code, + messageText: flattenMessage(diag), + file, + start: diag.pos, + length: diag.end - diag.pos, + source: diag.source || 'ts', + reportsUnnecessary: diag.reportsUnnecessary, + reportsDeprecated: diag.reportsDeprecated, + } satisfies Diagnostic); } } return diagnostics; }, }); } + +function flattenMessage(diag: { text: string; messageChain?: readonly { text: string; messageChain?: any }[] }, indent = 0): string { + let text = (indent ? '\n' + ' '.repeat(indent) : '') + diag.text; + for (const child of diag.messageChain ?? []) { + text += flattenMessage(child, indent + 1); + } + return text; +} diff --git a/packages/config/lib/plugins/ignore.ts b/packages/config/lib/plugins/ignore.ts index d6301ddf..3feb79bf 100644 --- a/packages/config/lib/plugins/ignore.ts +++ b/packages/config/lib/plugins/ignore.ts @@ -1,5 +1,6 @@ import type { Plugin } from '@tsslint/types'; -import type * as ts from 'typescript'; +import type * as ts from 'typescript/ast'; +import { DiagnosticCategory } from 'typescript/sync'; interface CommentState { used?: boolean; @@ -44,7 +45,7 @@ interface CachedComment { const sharedFileComments = new WeakMap(); function getFileComments( - ts: typeof import('typescript'), + ts: typeof import('typescript/ast'), file: ts.SourceFile, ): CachedComment[] { let comments = sharedFileComments.get(file); @@ -78,7 +79,7 @@ function getFileComments( // `getChildren` array per visit creates the most allocation // pressure; we still need it because `forEachChild` skips token // kinds. Iterative stack avoids the recursive overhead. - let stack: ts.Node[] = [file]; + const stack: ts.Node[] = [file]; while (stack.length > 0) { const node = stack.pop()!; if (ts.isTokenKind(node.kind)) { @@ -112,7 +113,7 @@ function getFileComments( } function canHaveTrailingTrivia( - ts: typeof import('typescript'), + ts: typeof import('typescript/ast'), token: ts.Node, ): boolean { const SK = ts.SyntaxKind; @@ -143,7 +144,7 @@ function canHaveTrailingTrivia( } function isJsxElementOrFragment( - ts: typeof import('typescript'), + ts: typeof import('typescript/ast'), node: ts.Node, ): boolean { const SK = ts.SyntaxKind; @@ -208,109 +209,8 @@ export function create( if (valid.length !== parts.length) return undefined; return valid; } - const completeReg1 = /^\s*\/\/(\s*)([\S]*)?$/; - const completeReg2 = new RegExp(`//\\s*${cmd}(\\S*)?$`); - - return ({ typescript: ts, languageService }) => { - const reportedRulesOfFile = new Map(); - const { getCompletionsAtPosition } = languageService; - - languageService.getCompletionsAtPosition = (fileName, position, ...rest) => { - let result = getCompletionsAtPosition(fileName, position, ...rest); - - const sourceFile = languageService.getProgram()?.getSourceFile(fileName); - if (!sourceFile) { - return result; - } - - const reportedRules = reportedRulesOfFile.get(fileName); - const line = sourceFile.getLineAndCharacterOfPosition(position).line; - const lineStart = sourceFile.getPositionOfLineAndCharacter(line, 0); - const prefix = sourceFile.text.slice(lineStart, position); - const matchCmd = completeReg1 - ? prefix.match(completeReg1) - : undefined; - - if (matchCmd) { - const nextLineRules = reportedRules?.filter(([, reportedLine]) => reportedLine === line + 1) ?? []; - const item: ts.CompletionEntry = { - name: cmdText, - insertText: matchCmd[1].length ? cmdText : ` ${cmdText}`, - kind: ts.ScriptElementKind.keyword, - sortText: 'a', - replacementSpan: matchCmd[2] - ? { - start: position - matchCmd[2].length, - length: matchCmd[2].length, - } - : undefined, - labelDetails: { - description: nextLineRules.length >= 2 - ? `Ignore ${nextLineRules.length} issues in next line` - : nextLineRules.length - ? 'Ignore 1 issue in next line' - : undefined, - }, - }; - if (result) { - result.entries.push(item); - } - else { - result = { - isGlobalCompletion: false, - isMemberCompletion: false, - isNewIdentifierLocation: false, - entries: [item], - }; - } - } - else if (reportedRules?.length) { - const matchRule = completeReg2 - ? prefix.match(completeReg2) - : undefined; - if (matchRule) { - const visited = new Set(); - for (const [ruleId] of reportedRules) { - if (visited.has(ruleId)) { - continue; - } - visited.add(ruleId); - - const reportedLines = reportedRules - .filter(([r]) => r === ruleId) - .map(([, l]) => l + 1); - const item: ts.CompletionEntry = { - name: ruleId, - kind: ts.ScriptElementKind.keyword, - sortText: ruleId, - replacementSpan: matchRule[1] - ? { - start: position - matchRule[1].length, - length: matchRule[1].length, - } - : undefined, - labelDetails: { - description: `Reported in line${reportedLines.length >= 2 ? 's' : ''} ${reportedLines.join(', ')}`, - }, - }; - if (result) { - result.entries.push(item); - } - else { - result = { - isGlobalCompletion: false, - isMemberCompletion: false, - isNewIdentifierLocation: false, - entries: [item], - }; - } - } - } - } - - return result; - }; + return ({ typescript: ts }) => { return { resolveDiagnostics(file, results) { if ( @@ -382,23 +282,14 @@ export function create( } } - let reportedRules = reportedRulesOfFile.get(file.fileName); - if (!reportedRules) { - reportedRules = []; - reportedRulesOfFile.set(file.fileName, reportedRules); - } - reportedRules.length = 0; - results = results.filter(error => { if (error.source !== 'tsslint') { return true; } const line = file.getLineAndCharacterOfPosition(error.start).line; - reportedRules.push([error.code as any, line]); - - for (const code of [undefined, error.code]) { - const states = comments.get(code as any); + for (const code of [undefined, String(error.code)]) { + const states = comments.get(code); if (!states) { continue; } @@ -429,10 +320,10 @@ export function create( file: file, start: state.commentRange[0], length: state.commentRange[1] - state.commentRange[0], - code: 'tsslint:unused-ignore-comment' as any, + code: 'tsslint:unused-ignore-comment', messageText: `Unused comment.`, source: 'tsslint', - category: 1, + category: DiagnosticCategory.Error, }); } } diff --git a/packages/config/lib/tsl.ts b/packages/config/lib/tsl.ts deleted file mode 100644 index 0ab88ba1..00000000 --- a/packages/config/lib/tsl.ts +++ /dev/null @@ -1,73 +0,0 @@ -import type { Rule } from '@tsslint/types'; -import * as fs from 'fs'; -import * as path from 'path'; -import type * as TSL from 'tsl' with { 'resolution-mode': 'import' }; - -export function fromTSLRules(tslRules: TSL.Rule[]) { - let dir = __dirname; - let tslDir: string | undefined; - while (true) { - const potential = path.join(dir, 'node_modules', 'tsl'); - if (fs.existsSync(potential)) { - tslDir = potential; - break; - } - const parentDir = path.dirname(dir); - if (parentDir === dir) { - break; - } - dir = parentDir; - } - if (!tslDir) { - throw new Error('Failed to find tsl package in node_modules.'); - } - const visitorEntriesFile = fs.readdirSync(tslDir).find(f => /^visitorEntries-.*\.js$/.test(f)); - if (!visitorEntriesFile) { - throw new Error('Failed to find visitorEntries file in tsl package.'); - } - const { getContextUtils, visitorEntries } = require(path.join(tslDir, visitorEntriesFile)); - const rules: Record = {}; - for (const tslRule of tslRules) { - rules[tslRule.name] = convertTSLRule(tslRule, visitorEntries); - } - return rules; - - function convertTSLRule(rule: TSL.Rule, visitorEntries: any[]): Rule { - return ({ typescript: ts, file, program, report }) => { - const context1: Omit = { - checker: program.getTypeChecker() as unknown as TSL.Checker, - rawChecker: program.getTypeChecker(), - sourceFile: file as any, - report: descriptor => { - if ('node' in descriptor) { - report(descriptor.message, descriptor.node.getStart(file), descriptor.node.getEnd()) - .at(new Error(), 1); - } - else { - report(descriptor.message, descriptor.start, descriptor.end) - .at(new Error(), 1); - } - }, - compilerOptions: program.getCompilerOptions(), - program, - utils: getContextUtils(() => program), - }; - const context2 = { ...context1, data: rule.createData?.(context1) }; - ts.forEachChild(file, function cb(node) { - const nodeType = visitorEntries.find(e => e[0] === node.kind)?.[1]; - if (nodeType) { - // @ts-expect-error - rule.visitor[nodeType]?.(context2, node as any); - } - ts.forEachChild(node, cb); - if (nodeType) { - // @ts-expect-error - rule.visitor[`${nodeType}_exit` as keyof Visitor]?.( - context2, - node as any, - ); - } - }); - }; - } -} diff --git a/packages/config/lib/tslint-gen.ts b/packages/config/lib/tslint-gen.ts deleted file mode 100644 index fc0042d5..00000000 --- a/packages/config/lib/tslint-gen.ts +++ /dev/null @@ -1,322 +0,0 @@ -import * as fs from 'fs'; -import * as path from 'path'; -import type { IRuleMetadata, RuleConstructor } from 'tslint'; -import { getTSLintRulesDirectories } from './tslint'; - -const variableNameRegex = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/; - -export async function generateTSLintTypes( - nodeModulesDirs: string[], - loader = async (mod: string) => { - try { - return require(mod); - } - catch { - return await import(mod); - } - }, -) { - let indentLevel = 0; - let dts = ''; - let defId = 0; - - line(`export interface TSLintRulesConfig {`); - indentLevel++; - - const visited = new Set(); - const defs = new Map(); - const stats: Record = {}; - const rulesDirectories = getTSLintRulesDirectories(); - - for (const [rawDir, rulesDir] of rulesDirectories) { - if (fs.existsSync(rulesDir)) { - const ruleFiles = fs.readdirSync(rulesDir); - stats[rawDir] = 0; - for (const ruleFile of ruleFiles) { - if (ruleFile.endsWith('Rule.js') || ruleFile.endsWith('Rule.ts')) { - const camelCaseName = ruleFile.slice(0, -'Rule.js'.length); - const ruleName = camelCaseName.replace( - /[A-Z]/g, - (c, i) => (i === 0 ? c.toLowerCase() : '-' + c.toLowerCase()), - ); - if (!visited.has(ruleName)) { - visited.add(ruleName); - try { - const rule: RuleConstructor = (await loader(path.join(rulesDir, ruleFile))).Rule; - addRule(ruleName, rule.metadata, rawDir); - stats[rawDir]++; - } - catch (e) { - addRule(ruleName, undefined, rawDir, e); - stats[rawDir]++; - } - } - } - } - } - } - - // 2. Scan TSLint core rules - for (const nodeModulesDir of nodeModulesDirs) { - const tslintDir = path.join(nodeModulesDir, 'tslint', 'lib', 'rules'); - if (fs.existsSync(tslintDir)) { - const ruleFiles = fs.readdirSync(tslintDir); - stats['tslint'] = stats['tslint'] || 0; - for (const ruleFile of ruleFiles) { - if (ruleFile.endsWith('Rule.js')) { - const camelCaseName = ruleFile.replace('Rule.js', ''); - const ruleName = camelCaseName.replace( - /[A-Z]/g, - (c, i) => (i === 0 ? c.toLowerCase() : '-' + c.toLowerCase()), - ); - if (!visited.has(ruleName)) { - visited.add(ruleName); - const rule: RuleConstructor = (await loader(path.join(tslintDir, ruleFile))).Rule; - addRule(ruleName, rule.metadata); - stats['tslint']++; - } - } - } - } - } - - indentLevel--; - line(`}`); - line(``); - - for (const [typeName, typeString] of defs.values()) { - line(`type ${typeName} = ${typeString};`); - } - - return { dts, stats }; - - function addRule(ruleName: string, metadata: IRuleMetadata | undefined, rulesDir?: string, error?: any) { - if (metadata || rulesDir || error) { - line(`/**`); - if (rulesDir) { - line(` * @rulesDirectory ${rulesDir}`); - } - if (error) { - line(` * @error ${error.message || error.toString()}`); - } - if (metadata?.description) { - for (const lineText of metadata.description.trim().split('\n')) { - line(` * ${lineText.replace(/\*\//g, '* /')}`); - } - } - if (metadata?.descriptionDetails) { - line(` *`); - for (const lineText of metadata.descriptionDetails.trim().split('\n')) { - line(` * ${lineText.replace(/\*\//g, '* /')}`); - } - } - if (metadata?.rationale) { - line(` *`); - line(` * @rationale`); - for (const lineText of metadata.rationale.trim().split('\n')) { - line(` * ${lineText.replace(/\*\//g, '* /')}`); - } - } - if (metadata?.optionsDescription) { - line(` *`); - line(` * @options`); - for (const lineText of metadata.optionsDescription.trim().split('\n')) { - line(` * ${lineText.replace(/\*\//g, '* /')}`); - } - } - if (metadata?.optionExamples) { - line(` *`); - line(` * @example`); - for (const example of metadata.optionExamples) { - if (typeof example === 'string') { - for (const lineText of example.trim().split('\n')) { - line(` * ${lineText.replace(/\*\//g, '* /')}`); - } - } - else { - line(` * ${JSON.stringify(ruleName)}: ${JSON.stringify(example)}`); - } - } - } - if (metadata?.type) { - line(` *`); - line(` * @type ${metadata.type}`); - } - if (metadata?.typescriptOnly) { - line(` *`); - line(` * @typescriptOnly`); - } - line(` */`); - } - - let optionsType: string | undefined; - const schema = metadata?.options; - - if (schema) { - if (Array.isArray(schema)) { - const optionsTypes: string[] = []; - for (const item of schema) { - const itemType = parseSchema(schema, item, indentLevel); - optionsTypes.push(itemType); - } - optionsType = `[`; - optionsType += optionsTypes - .map(type => `(${type})?`) - .join(', '); - optionsType += `]`; - } - else { - optionsType = parseSchema(schema, schema, indentLevel); - } - } - - if (optionsType) { - line(`'${ruleName}'?: ${optionsType},`); - } - else { - line(`'${ruleName}'?: any[],`); - } - } - - function line(line: string) { - dts += indent(indentLevel) + line + '\n'; - } - - function parseSchema(schema: any, item: any, indentLevel: number): string { - if (typeof item === 'object' && item !== null) { - if (item.$ref) { - const paths = item.$ref - .replace('#/items/', '#/') - .split('/').slice(1); - let current = schema; - for (const path of paths) { - try { - current = current[path]; - } - catch { - current = undefined; - break; - } - } - if (current) { - let resolved = defs.get(current); - if (!resolved) { - resolved = [`Def${defId++}_${paths[paths.length - 1]}`, parseSchema(schema, current, 0)]; - defs.set(current, resolved); - } - return resolved[0]; - } - else { - console.error(`Failed to resolve schema path: ${item.$ref}`); - return 'unknown'; - } - } - else if (Array.isArray(item)) { - return item.map(item => parseSchema(schema, item, indentLevel)).join(' | '); - } - else if (Array.isArray(item.type)) { - return item.type.map((type: any) => parseSchema(schema, type, indentLevel)).join(' | '); - } - else if (item.properties) { - let res = `{\n`; - indentLevel++; - const properties = item.properties; - const requiredArr = item.required ?? []; - for (const key in properties) { - const property = properties[key]; - if (property.description) { - res += indent(indentLevel) + `/**\n`; - res += indent(indentLevel) + ` * ${property.description.replace(/\*\//g, '* /')}\n`; - res += indent(indentLevel) + ` */\n`; - } - const propertyType = parseSchema(schema, property, indentLevel); - const isRequired = requiredArr.includes(key); - if (!variableNameRegex.test(key)) { - res += indent(indentLevel) + `'${key}'${isRequired ? '' : '?'}: ${propertyType},\n`; - } - else { - res += indent(indentLevel) + `${key}${isRequired ? '' : '?'}: ${propertyType},\n`; - } - } - indentLevel--; - res += indent(indentLevel) + `}`; - if (item.additionalProperties) { - res += ` & `; - res += parseAdditionalProperties(schema, item.additionalProperties, indentLevel); - } - return res; - } - else if (Array.isArray(item.required)) { - let res = `{ `; - const propertiesType: string[] = []; - for (const key of item.required) { - const propertyType = `any`; - if (!variableNameRegex.test(key)) { - propertiesType.push(`'${key}': ${propertyType}`); - } - else { - propertiesType.push(`${key}: ${propertyType}`); - } - } - res += propertiesType.join(', '); - res += ` }`; - return res; - } - else if (item.const) { - return JSON.stringify(item.const); - } - else if (item.type === 'array') { - if (Array.isArray(item.items)) { - return `[${item.items.map((item: any) => parseSchema(schema, item, indentLevel)).join(', ')}]`; - } - if (item.items) { - return `(${parseSchema(schema, item.items, indentLevel)})[]`; - } - return `any[]`; - } - else if (item.enum) { - return item.enum.map((v: any) => JSON.stringify(v)).join(' | '); - } - else if (item.type) { - return parseSchema(schema, item.type, indentLevel); - } - else if (item.anyOf) { - return item.anyOf.map((item: any) => parseSchema(schema, item, indentLevel)).join(' | '); - } - else if (item.oneOf) { - return item.oneOf.map((item: any) => parseSchema(schema, item, indentLevel)).join(' | '); - } - } - else if (item === 'string' || item === 'boolean' || item === 'null' || item === 'number') { - return item; - } - else if (item === 'object') { - if (item.additionalProperties) { - return parseAdditionalProperties(schema, item.additionalProperties, indentLevel); - } - else { - return `{ [key: string]: unknown }`; - } - } - else if (item === 'integer') { - return 'number'; - } - else if (item === 'array') { - return 'any[]'; - } - return 'unknown'; - } - - function indent(indentLevel: number) { - return '\t'.repeat(indentLevel); - } - - function parseAdditionalProperties(schema: any, item: any, indentLevel: number) { - if (item === true) { - return `{ [key: string]: unknown }`; - } - else { - return `{ [key: string]: ${parseSchema(schema, item, indentLevel)} }`; - } - } -} diff --git a/packages/config/lib/tslint-types.ts b/packages/config/lib/tslint-types.ts deleted file mode 100644 index 6e6bb6b2..00000000 --- a/packages/config/lib/tslint-types.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface TSLintRulesConfig extends Record { - [key: string]: any[]; -} diff --git a/packages/config/lib/tslint.ts b/packages/config/lib/tslint.ts deleted file mode 100644 index cd82b63a..00000000 --- a/packages/config/lib/tslint.ts +++ /dev/null @@ -1,187 +0,0 @@ -import type * as TSSLint from '@tsslint/types'; -import * as fs from 'fs'; -import * as path from 'path'; -import type { IOptions, IRule, IRuleMetadata, ITypedRule } from 'tslint'; -import type * as ts from 'typescript'; -import type { TSLintRulesConfig } from './tslint-types.js'; -import { normalizeRuleSeverity, type RuleSeverity } from './utils.js'; - -const noop = () => {}; - -/** - * Converts a TSLint rules configuration to TSSLint rules. - * - * ⚠️ **Type definitions not generated** - * - * Please run `npx tsslint-docgen` to update them. - */ -export async function importTSLintRules( - config: { [K in keyof TSLintRulesConfig]: RuleSeverity | [RuleSeverity, ...TSLintRulesConfig[K]] }, -) { - const rules: TSSLint.Rules = {}; - const rulesDirectories = getTSLintRulesDirectories(); - - for (const [ruleName, severityOrOptions] of Object.entries(config)) { - let severity: RuleSeverity; - let options: any[]; - if (Array.isArray(severityOrOptions)) { - [severity, ...options] = severityOrOptions; - } - else { - severity = severityOrOptions; - options = []; - } - severity = normalizeRuleSeverity(severity); - if (!severity) { - rules[ruleName] = noop; - continue; - } - const ruleModule = await loadTSLintRule(ruleName, rulesDirectories); - if (!ruleModule) { - throw new Error(`Failed to resolve TSLint rule "${ruleName}".`); - } - rules[ruleName] = convertRule( - ruleModule, - options, - severity === 'error' - ? 1 satisfies ts.DiagnosticCategory.Error - : severity === 'warn' - ? 0 satisfies ts.DiagnosticCategory.Warning - : 3 satisfies ts.DiagnosticCategory.Message, - ); - } - return rules; -} - -export function getTSLintRulesDirectories(): [string, string][] { - const directories: [string, string][] = []; - let dir = __dirname; - while (true) { - const tslintJsonPath = path.join(dir, 'tslint.json'); - if (fs.existsSync(tslintJsonPath)) { - try { - let content = fs.readFileSync(tslintJsonPath, 'utf8'); - // Remove comments - content = content.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '$1'); - const tslintJson = JSON.parse(content); - if (tslintJson.rulesDirectory) { - const rulesDirs = Array.isArray(tslintJson.rulesDirectory) - ? tslintJson.rulesDirectory - : [tslintJson.rulesDirectory]; - for (const rulesDir of rulesDirs) { - directories.push([rulesDir, path.resolve(dir, rulesDir)]); - } - } - } - catch (e) { - // Ignore parse errors - } - break; - } - const parentDir = path.resolve(dir, '..'); - if (parentDir === dir) { - break; - } - dir = parentDir; - } - return directories; -} - -async function loadTSLintRule(ruleName: string, rulesDirectories: [string, string][]): Promise { - const camelCaseName = ruleName.replace(/-([a-z])/g, (_, c) => c.toUpperCase()); - const ruleFileName = `${camelCaseName.charAt(0).toUpperCase() + camelCaseName.slice(1)}Rule.js`; - - for (const [, rulesDir] of rulesDirectories) { - const rulePath = path.resolve(rulesDir, ruleFileName); - if (fs.existsSync(rulePath)) { - const mod = require(rulePath); - return mod.Rule; - } - } - - let dir = __dirname; - while (true) { - const nodeModulesDir = path.join(dir, 'node_modules'); - if (fs.existsSync(nodeModulesDir)) { - const tslintDir = path.join(nodeModulesDir, 'tslint', 'lib', 'rules'); - if (fs.existsSync(tslintDir)) { - const rulePath = path.join(tslintDir, ruleFileName); - if (fs.existsSync(rulePath)) { - const mod = require(rulePath); - return mod.Rule; - } - } - } - const parentDir = path.resolve(dir, '..'); - if (parentDir === dir) { - break; - } - dir = parentDir; - } -} - -function convertRule( - Rule: { - metadata?: IRuleMetadata; - new(options: IOptions): IRule; - }, - ruleArguments: any[] = [], - category: ts.DiagnosticCategory = 3 satisfies ts.DiagnosticCategory.Message, -): TSSLint.Rule { - const rule = new Rule({ - ruleName: Rule.metadata?.ruleName ?? 'unknown', - ruleArguments, - ruleSeverity: 'warning', - disabledIntervals: [], - }); - return ({ typescript: ts, file, report, ...ctx }) => { - if (Rule.metadata?.typescriptOnly) { - const scriptKind = (file as any).scriptKind; - if (scriptKind === ts.ScriptKind.JS || scriptKind === ts.ScriptKind.JSX) { - return; - } - } - - const failures = 'applyWithProgram' in rule - ? (rule as ITypedRule).applyWithProgram(file, ctx.program) - : rule.apply(file); - for (const failure of failures) { - const reporter = report( - failure.getFailure(), - failure.getStartPosition().getPosition(), - failure.getEndPosition().getPosition(), - ).at(new Error(), Number.MAX_VALUE); - - if (category === 0 satisfies ts.DiagnosticCategory.Warning) { - reporter.asWarning(); - } - else if (category === 1 satisfies ts.DiagnosticCategory.Error) { - reporter.asError(); - } - else if (category === 2 satisfies ts.DiagnosticCategory.Suggestion) { - reporter.asSuggestion(); - } - - if (failure.hasFix()) { - const ruleName = Rule.metadata?.ruleName; - reporter.withFix( - ruleName ? `Fix with ${ruleName}` : 'Fix', - () => { - const fix = failure.getFix(); - const replaces = Array.isArray(fix) ? fix : fix ? [fix] : []; - return [{ - fileName: file.fileName, - textChanges: replaces.map(replace => ({ - newText: replace.text, - span: { - start: replace.start, - length: replace.length, - }, - })), - }]; - }, - ); - } - } - }; -} diff --git a/packages/config/package.json b/packages/config/package.json index 875223c8..b3903476 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -3,7 +3,7 @@ "version": "3.1.4", "license": "MIT", "engines": { - "node": ">=22.6.0" + "node": ">=22.12.0" }, "bin": { "tsslint-docgen": "./bin/tsslint-docgen.js" @@ -23,18 +23,15 @@ }, "devDependencies": { "@tsslint/compat-eslint": "3.1.4", - "tslint": "^6.1.3" + "typescript": "7.1.0-beta" }, "peerDependencies": { "@tsslint/compat-eslint": "^3.0.0", - "tsl": "^1.0.28" + "typescript": "^7.1.0-beta" }, "peerDependenciesMeta": { "@tsslint/compat-eslint": { "optional": true - }, - "tsl": { - "optional": true } } } From 9ed70b5b5ec49d34abe9b3131da997811bd857d0 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 22 Oct 2026 17:59:31 +0800 Subject: [PATCH 08/52] feat(compat-eslint)!: port lazy ESTree to the TypeScript 7 AST - lazy-estree / tokens / ts-ast-scan read typescript/ast. TS7 shape differences are handled in lib/ts.ts helpers: postfixToken for ?/!, ModuleDeclaration.keyword, phaseModifier for type-only imports, heritage clauses parsed as TypeReference, synthesized (Reparsed) nodes, nameless array-binding holes, EndOfFile/JSDoc kinds. - Scope analysis uses @typescript-eslint/scope-manager's analyze(), built lazily on first scopeManager read, replacing TsScopeManager. - parserServices type queries go through the project checker. - Parity tests compare against typescript-estree running on its own TypeScript 6 copy (tools/estree-oracle), bridged by node ranges. Tests depending on @typescript-eslint/eslint-plugin or the vendored scope-manager suite are removed: typescript-eslint cannot load TS7. - dogfood: register all rules before the first file is linted, so the shared traversal sees every rule (previously only the first one ran). --- fixtures/compat-eslint/package.json | 3 +- packages/compat-eslint/index.ts | 112 +- packages/compat-eslint/lib/globals.ts | 79 + packages/compat-eslint/lib/lazy-estree.ts | 100 +- .../compat-eslint/lib/lazy-source-code.ts | 23 +- packages/compat-eslint/lib/tokens.ts | 28 +- packages/compat-eslint/lib/ts-ast-scan.ts | 14 +- .../compat-eslint/lib/ts-scope-manager.ts | 2936 ----------------- packages/compat-eslint/lib/ts.ts | 97 + packages/compat-eslint/package.json | 12 +- .../test/bench/dogfood-corpus.ts | 8 +- packages/compat-eslint/test/bench/dogfood.ts | 116 +- .../compat-eslint/test/bench/rules.config.ts | 18 +- packages/compat-eslint/test/bench/run.ts | 138 +- .../test/compat-pipeline.test.ts | 341 +- .../compat-eslint/test/jsx-react-x.test.mjs | 212 -- .../test/lazy-estree-review-bugs.test.ts | 18 +- .../compat-eslint/test/lazy-estree.test.ts | 109 +- .../test/parser-options-gap.test.ts | 77 +- ...predicate-materializer-consistency.test.ts | 13 +- .../compat-eslint/test/scope-compat.test.ts | 388 --- .../compat-eslint/test/ts-ast-scan.test.ts | 5 +- packages/compat-eslint/test/ts7.ts | 81 + .../compat-eslint/test/upstream-runner.ts | 458 --- .../compat-eslint/test/upstream/README.md | 37 - .../test/upstream/analyze.test.ts | 16 - .../test/upstream/eslint-scope/README.md | 12 - .../upstream/eslint-scope/add-globals.test.ts | 433 --- .../upstream/eslint-scope/arguments.test.ts | 28 - .../upstream/eslint-scope/catch-scope.test.ts | 48 - .../eslint-scope/child-visitor-keys.test.ts | 55 - .../eslint-scope/class-fields.test.ts | 105 - .../es6-arrow-function-expression.test.ts | 123 - .../eslint-scope/es6-block-scope.test.ts | 156 - .../upstream/eslint-scope/es6-catch.test.ts | 65 - .../upstream/eslint-scope/es6-class.test.ts | 192 -- .../es6-default-parameters.test.ts | 395 --- .../es6-destructuring-assignments.test.ts | 1345 -------- .../upstream/eslint-scope/es6-export.test.ts | 210 -- .../upstream/eslint-scope/es6-import.test.ts | 151 - .../eslint-scope/es6-iteration-scope.test.ts | 197 -- .../eslint-scope/es6-new-target.test.ts | 28 - .../upstream/eslint-scope/es6-object.test.ts | 70 - .../eslint-scope/es6-rest-args.test.ts | 34 - .../upstream/eslint-scope/es6-super.test.ts | 49 - .../upstream/eslint-scope/es6-switch.test.ts | 47 - .../eslint-scope/es6-template-literal.test.ts | 46 - .../function-expression-name.test.ts | 38 - .../get-declared-variables.test.ts | 197 -- .../eslint-scope/global-increment.test.ts | 17 - .../eslint-scope/global-return.test.ts | 71 - .../implicit-global-reference.test.ts | 238 -- .../eslint-scope/implied-strict.test.ts | 93 - .../test/upstream/eslint-scope/label.test.ts | 63 - .../upstream/eslint-scope/references.test.ts | 636 ---- .../upstream/eslint-scope/typescript.test.ts | 43 - .../eslint-scope/use-strict-directive.test.ts | 191 -- .../upstream/eslint-scope/with-scope.test.ts | 49 - .../compat-eslint/test/upstream/lib.test.ts | 129 - .../populate-globals-from-lib.test.ts | 206 -- .../upstream/types/reference-type.test.ts | 391 --- .../types/variable-definition.test.ts | 185 -- 62 files changed, 701 insertions(+), 11074 deletions(-) create mode 100644 packages/compat-eslint/lib/globals.ts delete mode 100644 packages/compat-eslint/lib/ts-scope-manager.ts create mode 100644 packages/compat-eslint/lib/ts.ts delete mode 100644 packages/compat-eslint/test/jsx-react-x.test.mjs delete mode 100644 packages/compat-eslint/test/scope-compat.test.ts create mode 100644 packages/compat-eslint/test/ts7.ts delete mode 100644 packages/compat-eslint/test/upstream-runner.ts delete mode 100644 packages/compat-eslint/test/upstream/README.md delete mode 100644 packages/compat-eslint/test/upstream/analyze.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/README.md delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/add-globals.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/arguments.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/catch-scope.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/child-visitor-keys.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/class-fields.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/es6-arrow-function-expression.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/es6-block-scope.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/es6-catch.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/es6-class.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/es6-default-parameters.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/es6-destructuring-assignments.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/es6-export.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/es6-import.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/es6-iteration-scope.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/es6-new-target.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/es6-object.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/es6-rest-args.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/es6-super.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/es6-switch.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/es6-template-literal.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/function-expression-name.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/get-declared-variables.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/global-increment.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/global-return.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/implicit-global-reference.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/implied-strict.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/label.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/references.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/typescript.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/use-strict-directive.test.ts delete mode 100644 packages/compat-eslint/test/upstream/eslint-scope/with-scope.test.ts delete mode 100644 packages/compat-eslint/test/upstream/lib.test.ts delete mode 100644 packages/compat-eslint/test/upstream/populate-globals-from-lib.test.ts delete mode 100644 packages/compat-eslint/test/upstream/types/reference-type.test.ts delete mode 100644 packages/compat-eslint/test/upstream/types/variable-definition.test.ts diff --git a/fixtures/compat-eslint/package.json b/fixtures/compat-eslint/package.json index 43d61c2e..b112bb6d 100644 --- a/fixtures/compat-eslint/package.json +++ b/fixtures/compat-eslint/package.json @@ -4,7 +4,6 @@ "version": "3.1.4", "devDependencies": { "@tsslint/compat-eslint": "3.1.4", - "@tsslint/config": "3.1.4", - "eslint-plugin-expect-type": "^0.4.0" + "@tsslint/config": "3.1.4" } } diff --git a/packages/compat-eslint/index.ts b/packages/compat-eslint/index.ts index 5d495dd7..440e03c4 100644 --- a/packages/compat-eslint/index.ts +++ b/packages/compat-eslint/index.ts @@ -1,6 +1,9 @@ import type * as TSSLint from '@tsslint/types'; +import { analyze } from '@typescript-eslint/scope-manager'; import type * as ESLint from 'eslint'; -import * as ts from 'typescript'; +import { type Checker, type CompilerOptions, DiagnosticCategory, type Program, ScriptKind } from 'typescript/sync'; +import { ESLINT_BUILTIN_GLOBALS } from './lib/globals'; +import * as ts from './lib/ts'; import CodePathAnalyzer = require('./lib/code-path-analysis/code-path-analyzer.js'); import { convertLazy, LISTENER_PRE_MATERIALIZE } from './lib/lazy-estree'; @@ -11,7 +14,6 @@ import { LazySourceCode } from './lib/lazy-source-code'; import { decomposeSimple, isCodePathListener } from './lib/selector-analysis'; import { convertComments, convertTokens } from './lib/tokens'; import { predicateAllKinds, predicateForTriggerSet, tsScanTraverse } from './lib/ts-ast-scan'; -import { applyEslintGlobals, TsScopeManager } from './lib/ts-scope-manager'; import { visitorKeys } from './lib/visitor-keys'; // Build a parse-time-named wrapper around a rule listener. V8's CPU @@ -92,7 +94,7 @@ interface RuleEntry { eslintRule: ESLint.Rule.RuleModule; options: any[]; context: Partial; - category: ts.DiagnosticCategory; + category: DiagnosticCategory; } interface DeferredReport { @@ -100,9 +102,9 @@ interface DeferredReport { message: string; start: number; end: number; - category: ts.DiagnosticCategory; - textChanges?: ts.TextChange[]; - suggestions?: { message: string; textChanges: ts.TextChange[] }[]; + category: DiagnosticCategory; + textChanges?: TSSLint.TextChange[]; + suggestions?: { message: string; textChanges: TSSLint.TextChange[] }[]; } // Module-level state — populated by convertRule, queried at lint time. @@ -128,16 +130,11 @@ const fileStates = new WeakMap; }>(); -// `isExternalModule` checks `externalModuleIndicator` only (ESM-only). -// `commonJsModuleIndicator` flags TS files that look like CJS — those -// runtime-wrap in a function scope, so top-level vars are NOT global, -// matching module semantics for our purposes. Fold both flags into one -// classification; pure script (neither flag) is rare in real TS code -// but classifying it correctly avoids `no-var` rewriting a genuinely -// global `var` to `const` (which would change runtime visibility). +// Pure script (no module indicator) is rare in real TS code but +// classifying it correctly avoids `no-var` rewriting a genuinely global +// `var` to `const` (which would change runtime visibility). function classifySourceType(file: ts.SourceFile): 'module' | 'script' { - const f = file as { externalModuleIndicator?: unknown; commonJsModuleIndicator?: unknown }; - return (f.externalModuleIndicator || f.commonJsModuleIndicator) ? 'module' : 'script'; + return file.externalModuleIndicator ? 'module' : 'script'; } // Caller may pass an ESLint-style `Partial` that includes @@ -165,7 +162,7 @@ function stripDerivedFields( console.warn( `[@tsslint/compat-eslint] Dropping caller-supplied ${ dropped.join(', ') - }. tsslint derives these per-file from TS (sourceType from externalModuleIndicator/commonJsModuleIndicator; ecmaVersion fixed at 'latest'). Remove these from the convertRule context to silence this warning.`, + }. tsslint derives these per-file from TS (sourceType from externalModuleIndicator; ecmaVersion fixed at 'latest'). Remove these from the convertRule context to silence this warning.`, ); } const cleaned: Record = { ...(context as Record) }; @@ -196,7 +193,7 @@ export function convertRule( eslintRule: ESLint.Rule.RuleModule, options: any[] = [], context: Partial = {}, - category: ts.DiagnosticCategory = 3 satisfies ts.DiagnosticCategory.Message, + category: DiagnosticCategory = DiagnosticCategory.Message, ): TSSLint.Rule { // ESLint deep-merges `meta.defaultOptions` into user options so each // rule sees the FULL options object (with all defaults filled in). @@ -226,10 +223,11 @@ export function convertRule( const entry: RuleEntry = { id, eslintRule, options, context: cleanedContext, category }; ruleRegistry.set(eslintRule, entry); - const tsslintRule: TSSLint.Rule = ({ file, report, program }) => { + const tsslintRule: TSSLint.Rule = ctx => { + const { file, report, program } = ctx; let perFileState = fileStates.get(file); if (!perFileState) { - const { sourceCode, convertContext } = buildEstree(file, program, classifySourceType(file)); + const { sourceCode, convertContext } = buildEstree(file, program, () => ctx.checker, classifySourceType(file)); perFileState = { sourceCode, convertContext, @@ -252,13 +250,13 @@ export function convertRule( for (const r of myReports) { const reporter = report(r.message, r.start, r.end).at(r.stackErr, 1); - if (r.category === 0 satisfies ts.DiagnosticCategory.Warning) { + if (r.category === DiagnosticCategory.Warning) { reporter.asWarning(); } - else if (r.category === 1 satisfies ts.DiagnosticCategory.Error) { + else if (r.category === DiagnosticCategory.Error) { reporter.asError(); } - else if (r.category === 2 satisfies ts.DiagnosticCategory.Suggestion) { + else if (r.category === DiagnosticCategory.Suggestion) { reporter.asSuggestion(); } if (r.textChanges) { @@ -285,7 +283,7 @@ export function convertRule( function runSharedTraversal( file: ts.SourceFile, - program: ts.Program, + program: Program, state: NonNullable>, ) { const { sourceCode, convertContext, reports, errors } = state; @@ -306,12 +304,8 @@ function runSharedTraversal( // files allow top-level `return`. Built once and reused for both the // legacy `parserOptions` shape and modern `languageOptions.parserOptions` // so rules reading either path see the same view. - // `scriptKind` isn't in TS's public SourceFile typing but is set at - // runtime when the file is parsed; same access pattern as - // `externalModuleIndicator` in classifySourceType. - const scriptKind = (file as { scriptKind?: ts.ScriptKind }).scriptKind; const ecmaFeatures = { - jsx: scriptKind === ts.ScriptKind.TSX || scriptKind === ts.ScriptKind.JSX, + jsx: isJsxFile(file), globalReturn: sourceType === 'script', impliedStrict: sourceType === 'module', } as const; @@ -758,7 +752,7 @@ function runEntries( } } -function getTextChangeMessage(file: ts.SourceFile, textChanges: ts.TextChange[]) { +function getTextChangeMessage(file: ts.SourceFile, textChanges: TSSLint.TextChange[]) { if (textChanges.length === 1) { const change = textChanges[0]; const originalText = file.text.substring(change.span.start, change.span.start + change.span.length); @@ -831,7 +825,7 @@ function getTextChangeMessage(file: ts.SourceFile, textChanges: ts.TextChange[]) function getTextChanges( _file: ts.SourceFile, fix: ESLint.Rule.ReportFixer | null | undefined, -): ts.TextChange[] { +): TSSLint.TextChange[] { if (!fix) { return []; } @@ -866,7 +860,7 @@ function getTextChanges( return []; } const fixes = isIterable(result) ? [...result] : [result]; - const textChanges: ts.TextChange[] = []; + const textChanges: TSSLint.TextChange[] = []; for (const f of fixes) { textChanges.push({ span: { start: f.range[0], length: f.range[1] - f.range[0] }, @@ -880,6 +874,30 @@ function isIterable(obj: unknown): obj is Iterable { return obj != null && typeof (obj as { [Symbol.iterator]?: unknown })[Symbol.iterator] === 'function'; } +function isJsxFile(file: ts.SourceFile): boolean { + return file.scriptKind === ScriptKind.TSX || file.scriptKind === ScriptKind.JSX; +} + +type Lib = NonNullable[1]>['lib']>[number]; + +// Mirrors @typescript-eslint/parser's `getLib`: explicit `lib` entries +// (`lib.es2022.d.ts`) win; otherwise the default lib of `target`. +function getLib(options: CompilerOptions): Lib[] { + if (options.lib) { + const libs: Lib[] = []; + for (const lib of options.lib) { + const match = /^lib\.(.+)\.d\.[cm]?ts$/.exec(lib.toLowerCase()); + if (match) libs.push(match[1] as Lib); + } + return libs; + } + const target = options.target; + if (target === undefined || target === ts.ScriptTarget.ESNext) return ['esnext.full']; + if (target === ts.ScriptTarget.ES2015) return ['es6']; + const name = ts.ScriptTarget[target]?.toLowerCase(); + return name?.startsWith('es20') ? [`${name}.full` as Lib] : ['esnext.full']; +} + // Build a fresh `LazySourceCode` + lazy-estree convert context for `file`. // Pure: no module-level caching here — callers cache via `perFileState`. // @@ -889,7 +907,8 @@ function isIterable(obj: unknown): obj is Iterable { // ts.SourceFile for. Calling our own converter directly avoids the require. function buildEstree( file: ts.SourceFile, - program: ts.Program, + program: Program, + getChecker: () => Checker, sourceType: 'module' | 'script', ): { sourceCode: ESLint.SourceCode; @@ -938,19 +957,24 @@ function buildEstree( // ESM syntax is present); that's intentional, since it reflects how // the file is actually emitted/run. estree.sourceType = sourceType; - const scopeManager = new TsScopeManager(file, program, estree, astMaps, sourceType); - // Inject ECMAScript built-ins + TS lib type globals so `no-undef` - // doesn't fire on `undefined` / `Math` / `Record` / etc. - // `TsScopeManager` itself stays free of this lint-pipeline policy - // (upstream eslint-scope parity tests rely on the un-injected - // shape); the names + de-dupe logic live next to `addGlobals` in - // `ts-scope-manager.ts`. - applyEslintGlobals(scopeManager); const sourceCode = new LazySourceCode({ text: file.text, ast: estree, tsFile: file, - scopeManager, + createScopeManager: () => { + const compilerOptions = program.getCompilerOptions(); + const scopeManager = analyze(estree, { + childVisitorKeys: visitorKeys as Record, + globalReturn: false, + impliedStrict: sourceType === 'module', + jsxPragma: compilerOptions.jsxFactory ?? 'React', + jsxFragmentName: compilerOptions.jsxFragmentFactory ?? null, + lib: getLib(compilerOptions), + sourceType, + }); + scopeManager.addGlobals([...ESLINT_BUILTIN_GLOBALS]); + return scopeManager; + }, visitorKeys: visitorKeys as Record, parserServices: { ...astMaps, @@ -959,10 +983,8 @@ function buildEstree( emitDecoratorMetadata: undefined, experimentalDecorators: undefined, isolatedDeclarations: undefined, - getSymbolAtLocation: (node: any) => - program.getTypeChecker().getSymbolAtLocation(astMaps.esTreeNodeToTSNodeMap.get(node)), - getTypeAtLocation: (node: any) => - program.getTypeChecker().getTypeAtLocation(astMaps.esTreeNodeToTSNodeMap.get(node)), + getSymbolAtLocation: (node: any) => getChecker().getSymbolAtLocation(astMaps.esTreeNodeToTSNodeMap.get(node)), + getTypeAtLocation: (node: any) => getChecker().getTypeAtLocation(astMaps.esTreeNodeToTSNodeMap.get(node)), }, }) as unknown as ESLint.SourceCode; return { sourceCode, convertContext }; diff --git a/packages/compat-eslint/lib/globals.ts b/packages/compat-eslint/lib/globals.ts new file mode 100644 index 00000000..ff7b1770 --- /dev/null +++ b/packages/compat-eslint/lib/globals.ts @@ -0,0 +1,79 @@ +// ECMAScript built-in globals, vendored from `eslint/conf/globals.js`'s +// `es2026` (the latest superset). ESLint registers these before rules +// run (`addDeclaredGlobals`) so `no-undef` doesn't fire on `undefined`, +// `Math`, etc. Update if ESLint adds a new ES year. +export const ESLINT_BUILTIN_GLOBALS: readonly string[] = [ + 'AggregateError', + 'Array', + 'ArrayBuffer', + 'AsyncDisposableStack', + 'Atomics', + 'BigInt', + 'BigInt64Array', + 'BigUint64Array', + 'Boolean', + 'DataView', + 'Date', + 'DisposableStack', + 'Error', + 'EvalError', + 'FinalizationRegistry', + 'Float16Array', + 'Float32Array', + 'Float64Array', + 'Function', + 'Infinity', + 'Int16Array', + 'Int32Array', + 'Int8Array', + 'Intl', + 'Iterator', + 'JSON', + 'Map', + 'Math', + 'NaN', + 'Number', + 'Object', + 'Promise', + 'Proxy', + 'RangeError', + 'ReferenceError', + 'Reflect', + 'RegExp', + 'Set', + 'SharedArrayBuffer', + 'String', + 'SuppressedError', + 'Symbol', + 'SyntaxError', + 'Temporal', + 'TypeError', + 'URIError', + 'Uint16Array', + 'Uint32Array', + 'Uint8Array', + 'Uint8ClampedArray', + 'WeakMap', + 'WeakRef', + 'WeakSet', + 'constructor', + 'decodeURI', + 'decodeURIComponent', + 'encodeURI', + 'encodeURIComponent', + 'escape', + 'eval', + 'globalThis', + 'hasOwnProperty', + 'isFinite', + 'isNaN', + 'isPrototypeOf', + 'parseFloat', + 'parseInt', + 'propertyIsEnumerable', + 'toLocaleString', + 'toString', + 'undefined', + 'unescape', + 'valueOf', +]; diff --git a/packages/compat-eslint/lib/lazy-estree.ts b/packages/compat-eslint/lib/lazy-estree.ts index 5f2cc302..3160f424 100644 --- a/packages/compat-eslint/lib/lazy-estree.ts +++ b/packages/compat-eslint/lib/lazy-estree.ts @@ -50,7 +50,7 @@ // findWrapperRoute as needed. Adding a new "real ESTree class" goes through // `defineShape` — see the table starting around the SHAPE_CLASSES const. -import * as ts from 'typescript'; +import * as ts from './ts'; import { xhtmlEntities } from './xhtml-entities'; const SK = ts.SyntaxKind; @@ -1587,7 +1587,7 @@ function maybeFixExports( // `@dec export class` has `modifiers[0]` = the Decorator. getModifiers // excludes decorators (matching typescript-estree's fixExports), so the // `export` keyword is first whatever the decorator position. - const modifiers = ts.canHaveModifiers(tsNode) ? ts.getModifiers(tsNode) : undefined; + const modifiers = ts.getModifiers(tsNode); if (!modifiers?.length || modifiers[0].kind !== SK.ExportKeyword) return inner; const exportKeyword = modifiers[0]; const next = modifiers[1]; @@ -1897,13 +1897,28 @@ defineShape(SK.IndexedAccessType, { }); // SK.LiteralType NOT migrated: convertLiteralType has a special case // for `null` (wraps NullKeyword as bare TSNullKeyword to match eager). -defineShape(SK.QualifiedName, { +const qualifiedNameShape = makeShapeClass({ type: 'TSQualifiedName', slots: { left: { tsField: 'left' }, right: { tsField: 'right' }, }, }); +const heritageQualifiedNameShape = makeShapeClass({ + type: 'MemberExpression', + defaults: { computed: false, optional: false }, + slots: { + object: { tsField: 'left' }, + property: { tsField: 'right' }, + }, +}); +defineShapeRouter( + SK.QualifiedName, + (tsNode, parent) => + ts.isHeritageQualifiedName(tsNode) + ? new heritageQualifiedNameShape(tsNode, parent) + : new qualifiedNameShape(tsNode, parent), +); defineShape(SK.TypeAssertionExpression, { type: 'TSTypeAssertion', slots: { @@ -2036,7 +2051,7 @@ defineShape(SK.TypeParameter, { slots: { name: { tsField: 'name' }, constraint: { tsField: 'constraint' }, - default: { tsField: 'default' }, + default: { tsField: 'defaultType' }, }, }); defineShape(SK.YieldExpression, { @@ -2090,13 +2105,30 @@ defineShape(SK.TypeOfExpression, { }); // Shapes that use custom converter funcs + whenAbsent='undefined' for // type-position slots: -defineShape(SK.TypeReference, { +const typeReferenceShape = makeShapeClass({ type: 'TSTypeReference', slots: { typeName: { tsField: 'typeName' }, typeArguments: { tsField: 'typeArguments', via: convertTypeArguments, whenAbsent: 'undefined' }, }, }); +// TS 7 parses `implements A.B` and interface `extends A.B` as +// TypeReferences; typescript-estree reads them as expressions. +const heritageTypeReferenceShape = makeShapeClass({ + type: tn => + (tn.parent as ts.HeritageClause).token === SK.ImplementsKeyword ? 'TSClassImplements' : 'TSInterfaceHeritage', + slots: { + expression: { tsField: 'typeName' }, + typeArguments: { tsField: 'typeArguments', via: convertTypeArguments, whenAbsent: 'undefined' }, + }, +}); +defineShapeRouter( + SK.TypeReference, + (tsNode, parent) => + ts.isHeritageTypeReference(tsNode) + ? new heritageTypeReferenceShape(tsNode, parent) + : new typeReferenceShape(tsNode, parent), +); defineShape(SK.ConstructorType, { type: 'TSConstructorType', consts: tn => ({ @@ -2112,7 +2144,7 @@ defineShape(SK.ModuleDeclaration, { type: 'TSModuleDeclaration', consts: tn => ({ declare: hasMod(tn, SK.DeclareKeyword), - global: !!(tn.flags & ts.NodeFlags.GlobalAugmentation), + global: ts.isGlobalAugmentation(tn, tn.getSourceFile().text), // `declare global { ... }` is `kind: 'global'` per typescript-estree. // Distinct from `namespace` and `module` — scope-manager's // Referencer.TSModuleDeclaration skips defineIdentifier for @@ -2124,9 +2156,9 @@ defineShape(SK.ModuleDeclaration, { // declaration selector that would have marked inner var/fn // declarations as used. Caught on prisma matched-type-aware // (4 false fires across runtime/getPrismaClient.ts and friends). - kind: tn.flags & ts.NodeFlags.GlobalAugmentation + kind: ts.isGlobalAugmentation(tn, tn.getSourceFile().text) ? 'global' - : tn.flags & ts.NodeFlags.Namespace + : tn.keyword === SK.NamespaceKeyword ? 'namespace' : 'module', }), @@ -2399,12 +2431,12 @@ defineShape(SK.RegularExpressionLiteral, { void tn; }, }); -defineShape(SK.PropertySignature, { +defineShape(SK.PropertySignature, { type: 'TSPropertySignature', defaults: { accessibility: undefined, static: false }, consts: tn => ({ computed: tn.name.kind === SK.ComputedPropertyName, - optional: !!tn.questionToken, + optional: ts.hasQuestionToken(tn), readonly: hasMod(tn, SK.ReadonlyKeyword), }), slots: { @@ -2412,12 +2444,12 @@ defineShape(SK.PropertySignature, { typeAnnotation: { tsField: 'type', via: convertTypeAnnotation, whenAbsent: 'undefined' }, }, }); -defineShape(SK.MethodSignature, { +defineShape(SK.MethodSignature, { type: 'TSMethodSignature', defaults: { accessibility: undefined, kind: 'method', static: false }, consts: tn => ({ computed: tn.name.kind === SK.ComputedPropertyName, - optional: !!tn.questionToken, + optional: ts.hasQuestionToken(tn), readonly: hasMod(tn, SK.ReadonlyKeyword), }), slots: { @@ -2581,7 +2613,7 @@ defineShape(SK.ArrayBindingPattern, { elements: { tsField: 'elements', via: (els, parent) => - els.map((e: ts.ArrayBindingElement) => e.kind === SK.OmittedExpression ? null : convertChild(e, parent)), + els.map((e: ts.ArrayBindingElement) => isArrayBindingHole(e) ? null : convertChild(e, parent)), }, }, }); @@ -3437,7 +3469,7 @@ const classShape = makeShapeClass({ superClass: { compute: (tn: ts.ClassDeclaration | ts.ClassExpression, parent) => { const ext = tn.heritageClauses?.find(c => c.token === SK.ExtendsKeyword); - return ext ? convertChild(ext.types[0]?.expression, parent) : null; + return ext ? convertChild(ext.types[0] && ts.heritageElementExpression(ext.types[0]), parent) : null; }, }, superTypeArguments: { @@ -3451,7 +3483,11 @@ const classShape = makeShapeClass({ compute: (tn: ts.ClassDeclaration | ts.ClassExpression, parent) => { const impl = tn.heritageClauses?.find(c => c.token === SK.ImplementsKeyword); if (!impl) return []; - return impl.types.map(t => new classImplementsShape(t, parent)); + return impl.types.map(t => + t.kind === SK.TypeReference + ? new heritageTypeReferenceShape(t, parent) + : new classImplementsShape(t, parent) + ); }, }, decorators: { compute: (tn, parent) => convertDecorators(tn, parent) }, @@ -3517,8 +3553,8 @@ defineShape(SK.PropertyDeclaration, { declare: hasMod(tn, SK.DeclareKeyword), accessibility: accessibilityOf(tn), computed: tn.name.kind === SK.ComputedPropertyName, - optional: !!tn.questionToken, - definite: !!tn.exclamationToken, + optional: ts.hasQuestionToken(tn), + definite: ts.hasExclamationToken(tn), }; }, slots: { @@ -3581,7 +3617,7 @@ const objectMethodShape = makeShapeClass({ defaults: { kind: 'init', method: true, shorthand: false }, consts: tn => ({ computed: tn.name.kind === SK.ComputedPropertyName, - optional: !!tn.questionToken, + optional: ts.hasQuestionToken(tn), }), slots: { key: { tsField: 'name' }, @@ -3629,7 +3665,7 @@ const methodDefinitionShape = makeShapeClass< computed: tn.kind !== SK.Constructor && !!(tn as ts.MethodDeclaration).name && (tn as ts.MethodDeclaration).name.kind === SK.ComputedPropertyName, - optional: !!(tn as ts.MethodDeclaration).questionToken, + optional: ts.hasQuestionToken(tn as ts.MethodDeclaration), }; }, slots: { @@ -3705,7 +3741,7 @@ defineShapeRouter(SK.JsxSelfClosingElement, (tsNode, parent) => new jsxElementSh defineShape(SK.ImportDeclaration, { type: 'ImportDeclaration', - consts: tn => ({ importKind: tn.importClause?.isTypeOnly ? 'type' : 'value' }), + consts: tn => ({ importKind: ts.isTypeOnlyImportClause(tn.importClause) ? 'type' : 'value' }), slots: { source: { tsField: 'moduleSpecifier' }, specifiers: { @@ -3905,21 +3941,11 @@ const NO_COUNTERPART_NODE_KINDS = new Set([ SK.JsxAttributes, // → JSXOpeningElement.attributes SK.CaseBlock, // → SwitchStatement.cases SK.HeritageClause, // → ClassDeclaration.superClass / implements - // SK.AssertClause === SK.ImportAttributes (kind 301) — the import- - // attributes CONTAINER. Upstream's `convertImportAttributes` flattens - // `assertClause.elements` directly into `ImportDeclaration.attributes` - // without a wrapper node — so the container has no standalone ESTree - // type. Distinct from SK.ImportAttribute (kind 302, the individual - // `key: value` entry) which DOES have a defineShape and emits - // `'ImportAttribute'`. Confusion potential because TS renamed both at - // the same time (4.5: AssertClause/AssertEntry; 5.x: - // ImportAttributes/ImportAttribute). - SK.AssertClause, + SK.ImportAttributes, // flattened into ImportDeclaration.attributes SK.OmittedExpression, // sparse array hole — represented as null SK.TemplateSpan, // wrapped in TemplateExpression SK.TemplateLiteralTypeSpan, // wrapped in TSTemplateLiteralType SK.MissingDeclaration, // parse error - SK.Bundle, // multi-file container // (UnparsedSource / InputFiles existed in older TS; removed in 5.x — // not listed because the SyntaxKind enum no longer has them) // (SK.SourceFile is intentionally NOT here — `convertLazy` pre- @@ -4016,7 +4042,7 @@ export function hasNoEstreeCounterpart(kind: ts.SyntaxKind): boolean { // Trivia (comments, whitespace, shebang, conflict markers). if (kind >= SK.FirstTriviaToken && kind <= SK.LastTriviaToken) return true; // File boundary token. - if (kind === SK.EndOfFileToken) return true; + if (kind === SK.EndOfFile) return true; // Punctuators / operators (`+`, `?`, `===`, `?.`, `&&`, `=>`, etc.). // ESLint's SourceCode exposes these as Token objects (`{type: // 'Punctuator', value, loc, range}`) — they're NOT ESTree nodes. @@ -4256,7 +4282,8 @@ const ProgramNode = makeShapeClass({ comments: [], tokens: [], }), - range: (tn, ctx) => [tn.getStart(ctx.ast), tn.endOfFileToken.end], + // typescript-estree's Program starts at 0, leading trivia included. + range: tn => [tn.pos, tn.endOfFileToken.end], slots: { body: { tsField: 'statements', @@ -4469,7 +4496,7 @@ const BindingAssignmentPatternNode = makeShapeClass({ type: 'AssignmentPattern', defaults: { decorators: EMPTY_ARRAY, optional: false, typeAnnotation: undefined }, registersInMaps: () => false, - range: (tn, ctx) => [tn.name.getStart(ctx.ast), tn.initializer!.end], + range: (tn, ctx) => [tn.name!.getStart(ctx.ast), tn.initializer!.end], slots: { left: { tsField: 'name', via: 'convertChildAsPattern' }, right: { tsField: 'initializer' }, @@ -4501,6 +4528,11 @@ const TSEnumBodyNode = makeShapeClass({ // Pull `@dec` decorators out of a node's `modifiers` array. TS folds // decorators and modifiers into one list since 4.8; typescript-estree // emits them as a separate `decorators` slot on the owning ESTree node. +// TS 7 parses a hole in an array binding pattern (`[, a]`) as a nameless +// BindingElement rather than an OmittedExpression. +function isArrayBindingHole(e: ts.Node): boolean { + return e.kind === SK.OmittedExpression || (e.kind === SK.BindingElement && !(e as ts.BindingElement).name); +} function hasMod(tsNode: ts.Node, kind: ts.SyntaxKind): boolean { const ms = (tsNode as { modifiers?: ts.NodeArray }).modifiers; return !!ms?.some(m => m.kind === kind); diff --git a/packages/compat-eslint/lib/lazy-source-code.ts b/packages/compat-eslint/lib/lazy-source-code.ts index 01231a76..a8ee6229 100644 --- a/packages/compat-eslint/lib/lazy-source-code.ts +++ b/packages/compat-eslint/lib/lazy-source-code.ts @@ -25,7 +25,7 @@ // method that's missing here, throw a clear error rather than silently // returning undefined. -import * as ts from 'typescript'; +import * as ts from './ts'; import { materialize } from './lazy-estree'; import { buildCommentObject, walkInnerCommentsOf } from './tokens'; @@ -132,7 +132,9 @@ interface LazyConfig { text: string; ast: any; // LazyESTree Program tsFile: ts.SourceFile; - scopeManager: any; + // Scope analysis walks the whole ESTree, so it only runs once a rule + // actually asks for scopes. + createScopeManager: () => any; parserServices: any; visitorKeys: any; } @@ -140,7 +142,6 @@ interface LazyConfig { export class LazySourceCode { readonly text: string; readonly ast: any; - readonly scopeManager: any; readonly parserServices: any; readonly visitorKeys: any; readonly hasBOM: boolean; @@ -154,17 +155,23 @@ export class LazySourceCode { private _lines?: string[]; private _lineStartIndices?: number[]; private _scopeCache?: WeakMap; + private _scopeManager: any; + private _createScopeManager: () => any; constructor(config: LazyConfig) { this.text = config.text; this.ast = config.ast; this._tsFile = config.tsFile; - this.scopeManager = config.scopeManager ?? null; + this._createScopeManager = config.createScopeManager; this.parserServices = config.parserServices ?? {}; this.visitorKeys = config.visitorKeys; this.hasBOM = config.text.charCodeAt(0) === 0xfeff; } + get scopeManager(): any { + return this._scopeManager ??= this._createScopeManager(); + } + // ─── Lazy collections ────────────────────────────────────────────────── get tokens(): Token[] { @@ -460,11 +467,7 @@ export class LazySourceCode { const out: Comment[] = []; const text = this.text; const ast = this._tsFile; - const scanner = this._innerScanner ??= ts.createScanner( - ast.languageVersion, - /*skipTrivia*/ true, - ast.languageVariant, - ); + const scanner = this._innerScanner ??= ts.createScanner(/*skipTrivia*/ true, ast.languageVariant); scanner.setText(text); const collect = (pos: number, end: number, kind: ts.CommentKind): void => { out.push(buildCommentObject(text, pos, end, kind, ast)); @@ -485,7 +488,7 @@ export class LazySourceCode { else { const start = tsNode.getStart(ast); if (start >= tsNode.end) return out; - scanner.setTextPos(start); + scanner.resetTokenState(start); scanner.scan(); innerStart = scanner.getTokenEnd(); } diff --git a/packages/compat-eslint/lib/tokens.ts b/packages/compat-eslint/lib/tokens.ts index c0aaea15..94a9e6e6 100644 --- a/packages/compat-eslint/lib/tokens.ts +++ b/packages/compat-eslint/lib/tokens.ts @@ -8,7 +8,7 @@ // `getTokenBefore` etc. gets the same data ESLint+typescript-estree would // produce. -import * as ts from 'typescript'; +import * as ts from './ts'; const SK = ts.SyntaxKind; @@ -56,9 +56,7 @@ function isComment(node: ts.Node): boolean { return node.kind === SK.SingleLineCommentTrivia || node.kind === SK.MultiLineCommentTrivia; } function isJSDocComment(node: ts.Node): boolean { - // JSDocComment was added in TS 4.7; alias as JSDoc for older versions. - return node.kind === SK.JSDocComment - || (node as { kind: number }).kind === (SK as unknown as { JSDoc?: number }).JSDoc; + return node.kind === SK.JSDoc; } function isJSXToken(node: ts.Node): boolean { return node.kind >= SK.JsxElement && node.kind <= SK.JsxAttribute; @@ -153,19 +151,15 @@ function buildToken(token: ts.Node, start: number, end: number, ast: ts.SourceFi export function convertTokens(ast: ts.SourceFile): Token[] { const result: Token[] = []; const text = ast.text; - const scanner = ts.createScanner( - ast.languageVersion, - /*skipTrivia*/ true, - ast.languageVariant, - ); + const scanner = ts.createScanner(/*skipTrivia*/ true, ast.languageVariant); scanner.setText(text); let pos = 0; function emitScanned(targetEnd: number): void { while (pos < targetEnd) { - scanner.setTextPos(pos); + scanner.resetTokenState(pos); const k = scanner.scan(); - if (k === SK.EndOfFileToken) break; + if (k === SK.EndOfFile) break; const start = scanner.getTokenStart(); if (start >= targetEnd) break; pos = scanner.getTokenEnd(); @@ -185,7 +179,7 @@ export function convertTokens(ast: ts.SourceFile): Token[] { const nodeStart = node.kind === SK.JsxText ? node.getFullStart() : node.getStart(ast); // Scanner sweeps through trivia / punctuators preceding this node. emitScanned(nodeStart); - if (isToken(node) && node.kind !== SK.EndOfFileToken) { + if (isToken(node) && node.kind !== SK.EndOfFile) { result.push(convertToken(node, ast)); pos = node.getEnd(); return; @@ -280,7 +274,7 @@ export function walkInnerCommentsOf( emitCommentsFrom: (scanFrom: number) => void, ): void { if (isComment(node) || isJSDocComment(node)) return; - if (isToken(node) && node.kind !== SK.EndOfFileToken) return; + if (isToken(node) && node.kind !== SK.EndOfFile) return; let firstSeen = false; let lastEnd = -1; ts.forEachChild(node, child => { @@ -298,7 +292,7 @@ export function walkInnerCommentsOf( else { const start = node.getStart(ast); if (start >= node.end) return; - scanner.setTextPos(start); + scanner.resetTokenState(start); scanner.scan(); tailFrom = scanner.getTokenEnd(); } @@ -328,11 +322,7 @@ export function convertComments(ast: ts.SourceFile): Comment[] { const collect = (pos: number, end: number, kind: ts.CommentKind): void => { out.push(buildCommentObject(text, pos, end, kind, ast)); }; - const scanner = ts.createScanner( - ast.languageVersion, - /*skipTrivia*/ true, - ast.languageVariant, - ); + const scanner = ts.createScanner(/*skipTrivia*/ true, ast.languageVariant); scanner.setText(text); const emitCommentsFrom = (scanFrom: number): void => { if (scanFrom > 0) { diff --git a/packages/compat-eslint/lib/ts-ast-scan.ts b/packages/compat-eslint/lib/ts-ast-scan.ts index a69f4912..b14c9057 100644 --- a/packages/compat-eslint/lib/ts-ast-scan.ts +++ b/packages/compat-eslint/lib/ts-ast-scan.ts @@ -17,7 +17,7 @@ // already lazy-builds the parent chain when a rule reads `.parent`, so we // don't pre-build ancestors either. -import * as ts from 'typescript'; +import * as ts from './ts'; import { type ConvertContext, GENERIC_TS_NODE_MARKER, @@ -522,14 +522,15 @@ const PREDICATES: Record = { 'TSInstantiationExpression': n => n.kind === SK.ExpressionWithTypeArguments && n.parent?.kind !== SK.HeritageClause, + // TS 7 parses `implements X` / interface `extends X` as a TypeReference. 'TSClassImplements': n => - n.kind === SK.ExpressionWithTypeArguments + (n.kind === SK.ExpressionWithTypeArguments || n.kind === SK.TypeReference) && n.parent?.kind === SK.HeritageClause && (n.parent as ts.HeritageClause).token === SK.ImplementsKeyword && (n.parent.parent?.kind === SK.ClassDeclaration || n.parent.parent?.kind === SK.ClassExpression), 'TSInterfaceHeritage': n => - n.kind === SK.ExpressionWithTypeArguments + (n.kind === SK.ExpressionWithTypeArguments || n.kind === SK.TypeReference) && n.parent?.kind === SK.HeritageClause && (n.parent as ts.HeritageClause).token === SK.ExtendsKeyword && n.parent.parent?.kind === SK.InterfaceDeclaration, @@ -549,6 +550,10 @@ const PREDICATES: Record = { // Context-dependent — see isInJSXIdentifierPosition / chain helpers. 'JSXIdentifier': n => n.kind === SK.Identifier && isInJSXIdentifierPosition(n), 'JSXMemberExpression': n => n.kind === SK.PropertyAccessExpression && isInJSXMemberExpressionChain(n), + 'MemberExpression': n => + n.kind === SK.PropertyAccessExpression || n.kind === SK.ElementAccessExpression || ts.isHeritageQualifiedName(n), + 'TSTypeReference': n => n.kind === SK.TypeReference && !ts.isHeritageTypeReference(n), + 'TSQualifiedName': n => n.kind === SK.QualifiedName && !ts.isHeritageQualifiedName(n), }; // Sidecar table: ESTree types whose predicate is EXACTLY a TS-kind check @@ -600,7 +605,6 @@ const SIMPLE_KINDS: Record = { 'ClassDeclaration': [SK.ClassDeclaration], 'ClassExpression': [SK.ClassExpression], 'NewExpression': [SK.NewExpression], - 'MemberExpression': [SK.PropertyAccessExpression, SK.ElementAccessExpression], 'ConditionalExpression': [SK.ConditionalExpression], 'AwaitExpression': [SK.AwaitExpression], 'YieldExpression': [SK.YieldExpression], @@ -647,7 +651,6 @@ const SIMPLE_KINDS: Record = { 'TSTypeAssertion': [SK.TypeAssertionExpression], 'TSNonNullExpression': [SK.NonNullExpression], 'TSSatisfiesExpression': [SK.SatisfiesExpression], - 'TSTypeReference': [SK.TypeReference], 'TSUnionType': [SK.UnionType], 'TSIntersectionType': [SK.IntersectionType], 'TSArrayType': [SK.ArrayType], @@ -664,7 +667,6 @@ const SIMPLE_KINDS: Record = { 'TSTemplateLiteralType': [SK.TemplateLiteralType], 'TSTypePredicate': [SK.TypePredicate], 'TSTypeLiteral': [SK.TypeLiteral], - 'TSQualifiedName': [SK.QualifiedName], 'TSOptionalType': [SK.OptionalType], 'TSRestType': [SK.RestType], 'TSTypeParameter': [SK.TypeParameter], diff --git a/packages/compat-eslint/lib/ts-scope-manager.ts b/packages/compat-eslint/lib/ts-scope-manager.ts deleted file mode 100644 index 9879f281..00000000 --- a/packages/compat-eslint/lib/ts-scope-manager.ts +++ /dev/null @@ -1,2936 +0,0 @@ -// TS-backed reimplementation of @typescript-eslint/scope-manager's surface. -// -// Approach: walk the TS AST once at construction time, build a scope tree that -// matches @typescript-eslint/scope-manager's shape (so ESLint rules see the -// same structure they would from the upstream analyzer). Variables and refs -// are populated lazily per scope. The TS type checker is queried lazily for -// reference resolution. -// -// Scope kind dispatch is keyed off TS SyntaxKind directly — not `node.locals`, -// which only contains a subset of ES bindings (var hoisting + let/const) and -// would miss block-scopes-without-decls that ESLint expects. - -import type { TSESTree } from '@typescript-eslint/types'; -import * as ts from 'typescript'; -import { materialize, NoESTreeCounterpartError } from './lazy-estree'; - -type AstMaps = { - esTreeNodeToTSNodeMap: WeakMap; - tsNodeToESTreeNodeMap: WeakMap; -}; - -// ECMAScript built-in globals, vendored from `eslint/conf/globals.js`'s -// `es2026` (the latest superset). ESLint core registers these via -// `addDeclaredGlobals` before rules run; the constructor below mirrors -// that step so `no-undef` doesn't fire on `undefined`, `Math`, `String`, -// `Array`, etc. — names that `@typescript-eslint/scope-manager`'s lib -// data marks TYPE-only after merging (`es2015.core` re-declares es5's -// `TYPE_VALUE Math` as `TYPE`, etc.). Update if ESLint adds a new ES -// year. -const ESLINT_BUILTIN_GLOBALS: readonly string[] = [ - 'AggregateError', - 'Array', - 'ArrayBuffer', - 'AsyncDisposableStack', - 'Atomics', - 'BigInt', - 'BigInt64Array', - 'BigUint64Array', - 'Boolean', - 'DataView', - 'Date', - 'DisposableStack', - 'Error', - 'EvalError', - 'FinalizationRegistry', - 'Float16Array', - 'Float32Array', - 'Float64Array', - 'Function', - 'Infinity', - 'Int16Array', - 'Int32Array', - 'Int8Array', - 'Intl', - 'Iterator', - 'JSON', - 'Map', - 'Math', - 'NaN', - 'Number', - 'Object', - 'Promise', - 'Proxy', - 'RangeError', - 'ReferenceError', - 'Reflect', - 'RegExp', - 'Set', - 'SharedArrayBuffer', - 'String', - 'SuppressedError', - 'Symbol', - 'SyntaxError', - 'Temporal', - 'TypeError', - 'URIError', - 'Uint16Array', - 'Uint32Array', - 'Uint8Array', - 'Uint8ClampedArray', - 'WeakMap', - 'WeakRef', - 'WeakSet', - 'constructor', - 'decodeURI', - 'decodeURIComponent', - 'encodeURI', - 'encodeURIComponent', - 'escape', - 'eval', - 'globalThis', - 'hasOwnProperty', - 'isFinite', - 'isNaN', - 'isPrototypeOf', - 'parseFloat', - 'parseInt', - 'propertyIsEnumerable', - 'toLocaleString', - 'toString', - 'undefined', - 'unescape', - 'valueOf', -]; - -// TypeScript built-in lib type globals. Vendored union of every TYPE-tagged -// name across `@typescript-eslint/scope-manager`'s `dist/lib/es*.js` -// (es5 / es2015–es2025 / esnext, including sub-libs like -// `es2015.collection`, `es2024.regexp`, etc.). Excludes DOM / decorators / -// scripthost (environment-specific — opting in would silence undef on -// browser-only names in pure-Node code, which over-silences). Registering -// these via `addGlobals` makes type-position references to lib utility -// types (`Record`, `Promise`, `Awaited`, …) resolve cleanly -// even when only a partial lib is loaded — without this, our type-position -// guard had to silence ALL type-position freeRefs, hiding ESLint's -// no-undef on real undeclared type names (`NodeJS.ErrnoException`, -// `Visitor`). -const TS_LIB_TYPE_GLOBALS: readonly string[] = [ - 'AggregateError', - 'AggregateErrorConstructor', - 'Array', - 'ArrayBuffer', - 'ArrayBufferConstructor', - 'ArrayBufferLike', - 'ArrayBufferTypes', - 'ArrayBufferView', - 'ArrayConstructor', - 'ArrayIterator', - 'ArrayLike', - 'AsyncDisposable', - 'AsyncDisposableStack', - 'AsyncDisposableStackConstructor', - 'AsyncGenerator', - 'AsyncGeneratorFunction', - 'AsyncGeneratorFunctionConstructor', - 'AsyncIterable', - 'AsyncIterableIterator', - 'AsyncIterator', - 'AsyncIteratorObject', - 'Atomics', - 'Awaited', - 'BigInt', - 'BigInt64Array', - 'BigInt64ArrayConstructor', - 'BigIntConstructor', - 'BigIntToLocaleStringOptions', - 'BigUint64Array', - 'BigUint64ArrayConstructor', - 'Boolean', - 'BooleanConstructor', - 'BuiltinIteratorReturn', - 'CallableFunction', - 'Capitalize', - 'ConcatArray', - 'ConstructorParameters', - 'DataView', - 'DataViewConstructor', - 'Date', - 'DateConstructor', - 'Disposable', - 'DisposableStack', - 'DisposableStackConstructor', - 'Error', - 'ErrorConstructor', - 'ErrorOptions', - 'EvalError', - 'EvalErrorConstructor', - 'Exclude', - 'Extract', - 'FinalizationRegistry', - 'FinalizationRegistryConstructor', - 'FlatArray', - 'Float16Array', - 'Float16ArrayConstructor', - 'Float32Array', - 'Float32ArrayConstructor', - 'Float64Array', - 'Float64ArrayConstructor', - 'Function', - 'FunctionConstructor', - 'Generator', - 'GeneratorFunction', - 'GeneratorFunctionConstructor', - 'IArguments', - 'ImportAssertions', - 'ImportAttributes', - 'ImportCallOptions', - 'ImportMeta', - 'InstanceType', - 'Int16Array', - 'Int16ArrayConstructor', - 'Int32Array', - 'Int32ArrayConstructor', - 'Int8Array', - 'Int8ArrayConstructor', - 'Intl', - 'Iterable', - 'IterableIterator', - 'Iterator', - 'IteratorObject', - 'IteratorObjectConstructor', - 'IteratorResult', - 'IteratorReturnResult', - 'IteratorYieldResult', - 'JSON', - 'Lowercase', - 'Map', - 'MapConstructor', - 'MapIterator', - 'Math', - 'NewableFunction', - 'NoInfer', - 'NonNullable', - 'Number', - 'NumberConstructor', - 'Object', - 'ObjectConstructor', - 'Omit', - 'OmitThisParameter', - 'Parameters', - 'Partial', - 'Pick', - 'Promise', - 'PromiseConstructor', - 'PromiseConstructorLike', - 'PromiseFulfilledResult', - 'PromiseLike', - 'PromiseRejectedResult', - 'PromiseSettledResult', - 'PromiseWithResolvers', - 'PropertyDescriptor', - 'PropertyDescriptorMap', - 'PropertyKey', - 'ProxyConstructor', - 'ProxyHandler', - 'RangeError', - 'RangeErrorConstructor', - 'Readonly', - 'ReadonlyArray', - 'ReadonlyMap', - 'ReadonlySet', - 'ReadonlySetLike', - 'Record', - 'ReferenceError', - 'ReferenceErrorConstructor', - 'Reflect', - 'RegExp', - 'RegExpConstructor', - 'RegExpExecArray', - 'RegExpIndicesArray', - 'RegExpMatchArray', - 'RegExpStringIterator', - 'Required', - 'ReturnType', - 'Set', - 'SetConstructor', - 'SetIterator', - 'SharedArrayBuffer', - 'SharedArrayBufferConstructor', - 'String', - 'StringConstructor', - 'StringIterator', - 'SuppressedError', - 'SuppressedErrorConstructor', - 'Symbol', - 'SymbolConstructor', - 'SyntaxError', - 'SyntaxErrorConstructor', - 'TemplateStringsArray', - 'Temporal', - 'ThisParameterType', - 'ThisType', - 'TypeError', - 'TypeErrorConstructor', - 'TypedPropertyDescriptor', - 'URIError', - 'URIErrorConstructor', - 'Uint16Array', - 'Uint16ArrayConstructor', - 'Uint32Array', - 'Uint32ArrayConstructor', - 'Uint8Array', - 'Uint8ArrayConstructor', - 'Uint8ClampedArray', - 'Uint8ClampedArrayConstructor', - 'Uncapitalize', - 'Uppercase', - 'WeakKey', - 'WeakKeyTypes', - 'WeakMap', - 'WeakMapConstructor', - 'WeakRef', - 'WeakRefConstructor', - 'WeakSet', - 'WeakSetConstructor', - // Decorator types (from `lib.decorators*.d.ts`). Includes both stage-3 - // (ClassMethodDecoratorContext etc.) and legacy (ClassDecorator, - // MethodDecorator, PropertyDecorator, ParameterDecorator). - 'ClassMemberDecoratorContext', - 'DecoratorContext', - 'DecoratorMetadataObject', - 'DecoratorMetadata', - 'ClassDecoratorContext', - 'ClassMethodDecoratorContext', - 'ClassGetterDecoratorContext', - 'ClassSetterDecoratorContext', - 'ClassAccessorDecoratorContext', - 'ClassAccessorDecoratorTarget', - 'ClassAccessorDecoratorResult', - 'ClassFieldDecoratorContext', - 'ClassDecorator', - 'PropertyDecorator', - 'MethodDecorator', - 'ParameterDecorator', -]; - -// Lib globals that upstream's `analyze({ lib: ['esnext'] })` exposes as values -// (i.e. `isValueVariable === true`) after resolving the lib chain. Most lib -// names are type-only because later libs (e.g. `esnext.collection`) override -// es5/es2015's TYPE_VALUE entries with TYPE. Captured empirically from a -// minimal analyze() run; refresh if upstream's lib data changes. -const LIB_VALUE_GLOBALS = new Set([ - 'AggregateError', - 'AsyncDisposableStack', - 'BigInt', - 'Boolean', - 'DisposableStack', - 'EvalError', - 'FinalizationRegistry', - 'Float16Array', - 'Intl', - 'Iterator', - 'Object', - 'RangeError', - 'ReferenceError', - 'Reflect', - 'SuppressedError', - 'SyntaxError', - 'Temporal', - 'TypeError', - 'URIError', - 'WeakRef', -]); - -// Scope kinds we tag. Names match upstream casing (e.g. 'tsEnum', 'tsModule'). -type ScopeType = - | 'global' - | 'module' - | 'function' - | 'block' - | 'switch' - | 'for' - | 'catch' - | 'with' - | 'class' - | 'class-field-initializer' - | 'function-expression-name' - | 'tsEnum' - | 'tsModule' - | 'type' - | 'function-type' - | 'mapped-type' - | 'conditional-type'; - -type DefinitionType = - | 'CatchClause' - | 'ClassName' - | 'FunctionName' - | 'ImplicitGlobalVariable' - | 'ImportBinding' - | 'Parameter' - | 'TDZ' - | 'Variable' - | 'Type' - | 'TSEnumName' - | 'TSEnumMember' - | 'TSModuleName'; - -// _classifyIdentifier dispatch bitmaps. Hot path: ~120k identifiers/file -// run through this method. Most identifiers (~90%) have a parent.kind that -// falls through the switch's `default` returning `0b11` (both flags true) -// — a single Uint8Array lookup short-circuits before entering the switch. -// A second bitmap covers the large fall-through case body that just does -// `name === id ? 0 : 0b11` — also collapses out of the switch. -const _SK_CLASSIFY_BITMAP_SIZE = 400; -const _CLASSIFY_HANDLED_KINDS = new Uint8Array(_SK_CLASSIFY_BITMAP_SIZE); -const _CLASSIFY_NAME_SLOT_KINDS = new Uint8Array(_SK_CLASSIFY_BITMAP_SIZE); -(() => { - const SK = ts.SyntaxKind; - // Fall-through "name slot" cases — body is `(p as { name }).name === id ? 0 : 0b11`. - const nameSlotKinds = [ - SK.PropertyDeclaration, - SK.PropertySignature, - SK.PropertyAssignment, - SK.MethodDeclaration, - SK.MethodSignature, - SK.GetAccessor, - SK.SetAccessor, - SK.EnumMember, - SK.FunctionDeclaration, - SK.FunctionExpression, - SK.ClassDeclaration, - SK.ClassExpression, - SK.EnumDeclaration, - SK.ModuleDeclaration, - SK.TypeAliasDeclaration, - SK.InterfaceDeclaration, - SK.TypeParameter, - SK.ImportClause, - SK.NamespaceImport, - SK.ImportEqualsDeclaration, - SK.NamedTupleMember, - SK.JsxAttribute, - ]; - for (const k of nameSlotKinds) { - _CLASSIFY_NAME_SLOT_KINDS[k] = 1; - _CLASSIFY_HANDLED_KINDS[k] = 1; - } - // All other kinds with a dedicated case body in the switch. - const otherHandled = [ - SK.PropertyAccessExpression, - SK.QualifiedName, - SK.LabeledStatement, - SK.BreakStatement, - SK.ContinueStatement, - SK.MetaProperty, - SK.ImportSpecifier, - SK.VariableDeclaration, - SK.BindingElement, - SK.Parameter, - SK.ExportSpecifier, - SK.TypeReference, - SK.ImportType, - ]; - for (const k of otherHandled) _CLASSIFY_HANDLED_KINDS[k] = 1; -})(); - -// Module-level caches keyed by ts.Program. Lib symbols (`Object`, `Array`, -// `String` …) are stable per-program — IDEs share one program across all -// files, so the same set of lib symbols gets queried over and over. Caching -// per-instance (per-file) means every file rebuilds the same decisions. -// WeakMap lets entries fall away when a program is GC'd. -const _libDecisionByProgram = new WeakMap>(); -const _libSourceFileByProgram = new WeakMap>(); - -function _getLibDecisionMap(program: ts.Program): Map { - let m = _libDecisionByProgram.get(program); - if (!m) _libDecisionByProgram.set(program, m = new Map()); - return m; -} -function _getLibSourceFileMap(program: ts.Program): WeakMap { - let m = _libSourceFileByProgram.get(program); - if (!m) _libSourceFileByProgram.set(program, m = new WeakMap()); - return m; -} - -// Module-level cache of fake `ts.Symbol` placeholders for ESLint declared -// globals. The shape `{ name, declarations: [], flags: 0 }` is completely -// stateless — same fake works for every file's `TsScopeManager`. Without -// this, every `getEstree` would alloc 250+ throwaway plain objects. -// -// `_variableBySymbol` is still per-manager, so the wrapper `TsVariable` -// is per-file (it carries `manager`, `references` are file-local). But -// the symbol that keys it can — and should — be shared. -const _SHARED_FAKE_GLOBAL_SYMBOLS = new Map(); -function _sharedFakeGlobalSymbol(name: string): ts.Symbol { - let s = _SHARED_FAKE_GLOBAL_SYMBOLS.get(name); - if (!s) { - s = { name, declarations: [], flags: 0 } as unknown as ts.Symbol; - _SHARED_FAKE_GLOBAL_SYMBOLS.set(name, s); - } - return s; -} - -// Public surface mirrors `@typescript-eslint/scope-manager`'s -// `ScopeManager` class (`scope-manager/dist/ScopeManager.js`): -// `scopes`, `globalScope`, `getDeclaredVariables`, `acquire`, -// plus the boolean accessors (`isGlobalReturn`, `isModule`, …) -// that ESLint's `Linter` reads at scope-construction time. -// -// Construction is intentionally LAZY: the upstream `Referencer` -// walks the entire AST eagerly to build the scope tree and -// classify every reference. We split that into a single-pass -// scope-tree walk (`_buildScopeTree`) plus a deferred reference -// classification (`_ensureRefIndex`) triggered by the first -// `scope.through` / `var.references` / `getReferencesFor` read. -// Most rules only ever read a small subset of refs. -export class TsScopeManager { - scopes: TsScope[] = []; - globalScope!: TsScope; - moduleScope: TsScope | null = null; - declaredVariables = new WeakMap(); - nodeToScope = new Map(); - currentScope: TsScope | null = null; - - _variableBySymbol = new Map(); - _libVariableBySymbol = new Map(); - _libDecisionBySymbol: Map; - _libSourceFileCache: WeakMap; - _syntheticArguments = new Map(); - readonly checker: ts.TypeChecker; - - constructor( - readonly tsFile: ts.SourceFile, - readonly program: ts.Program, - readonly estree: TSESTree.Program, - readonly astMaps: AstMaps, - readonly sourceType: 'module' | 'script', - ) { - this.checker = program.getTypeChecker(); - // Per-program caches — share lib decisions across every file in the - // same ts.Program (typical IDE setup is one program for the whole - // project). Per-file managers all hit the same Map / WeakMap. - this._libDecisionBySymbol = _getLibDecisionMap(program); - this._libSourceFileCache = _getLibSourceFileMap(program); - // Build scope tree by walking the TS AST. ESLint built-in globals - // are NOT injected here — that's a lint-pipeline policy, not a - // scope-analysis fact. `applyEslintGlobals(manager)` is the - // pipeline-side hook (see compat-eslint/index.ts). - this._buildScopeTree(); - } - - // Identifiers picked up during the scope-tree walk; classified once after - // scope variables are populated (so we know which symbols are "ours"). - _pendingIdentifiers: ts.Identifier[] = []; - - _buildScopeTree() { - this.globalScope = new TsScope(this, this.tsFile, 'global', null); - this._registerScope(this.tsFile, this.globalScope); - - let topScope: TsScope = this.globalScope; - if (this.sourceType === 'module') { - this.moduleScope = new TsScope(this, this.tsFile, 'module', this.globalScope); - this._registerScope(this.tsFile, this.moduleScope); - topScope = this.moduleScope; - } - this.currentScope = topScope; - - // Single AST walk: build scope tree + collect identifiers for later - // classification. We thread `currentParent` through a stack-style - // save/restore around the recursion so `forEachChild` can take a single - // stable callback (no per-call arrow allocation). - const SK_Identifier = ts.SyntaxKind.Identifier; - const SK_ConditionalType = ts.SyntaxKind.ConditionalType; - const idents = this._pendingIdentifiers; - let currentParent: TsScope = topScope; - const walk = (n: ts.Node) => { - const prevParent = currentParent; - const created = this._createScopesFor(n, prevParent); - if (created) currentParent = created; - if (n.kind === SK_Identifier) idents.push(n as ts.Identifier); - // Conditional type: only `trueType` lives inside the conditional-type - // scope (where `infer X` is accessible). `checkType`, `extendsType`, - // `falseType` walk in the OUTER scope. Without this split, a nested - // conditional in `falseType` becomes a child of the outer conditional- - // type scope, and its `infer X` shadows the outer's `infer X`. - // Repro: `type U = T extends Array ? U : T extends Promise ? U : T;` - // — no-shadow incorrectly reported the second `infer U`. - if (n.kind === SK_ConditionalType) { - const cond = n as ts.ConditionalTypeNode; - currentParent = prevParent; - walk(cond.checkType); - walk(cond.extendsType); - if (created) currentParent = created; - walk(cond.trueType); - currentParent = prevParent; - walk(cond.falseType); - } - else { - ts.forEachChild(n, walk); - } - currentParent = prevParent; - }; - ts.forEachChild(this.tsFile, walk); - } - - _registerScope(tsNode: ts.Node, scope: TsScope) { - this.scopes.push(scope); - let arr = this.nodeToScope.get(tsNode); - if (!arr) { - this.nodeToScope.set(tsNode, arr = []); - } - arr.push(scope); - } - - // For nodes that are themselves scope-creating, build the appropriate scope - // (or pair of scopes, for named function expressions). Returns the innermost - // scope created, so its descendants live in it. - _createScopesFor(n: ts.Node, parent: TsScope): TsScope | undefined { - const SK = ts.SyntaxKind; - // Class field initializer: `class C { f = expr }` — `expr` is in its - // own scope so `this` and (in the future) other class-field-only - // constructs resolve correctly. Detected before the kind-switch - // because the scope's boundary is the initializer node itself, which - // can be any expression kind. - const np = n.parent; - if ( - np - && np.kind === SK.PropertyDeclaration - && (np as ts.PropertyDeclaration).initializer === n - ) { - return new TsScope(this, n, 'class-field-initializer', parent, true); - } - switch (n.kind) { - case SK.FunctionDeclaration: - case SK.ArrowFunction: - case SK.MethodDeclaration: - case SK.Constructor: - case SK.GetAccessor: - case SK.SetAccessor: - return new TsScope(this, n, 'function', parent, true); - case SK.FunctionExpression: { - // Named function expressions get a wrapping - // `function-expression-name` scope; inner function scope is its - // child. - const name = (n as ts.FunctionExpression).name; - const outer = name - ? new TsScope(this, n, 'function-expression-name', parent, true) - : parent; - return new TsScope(this, n, 'function', outer, true); - } - case SK.ClassDeclaration: - case SK.ClassExpression: - return new TsScope(this, n, 'class', parent, true); - case SK.Block: { - // Block as a function/method body is NOT its own scope; the - // function scope already covers it. Free-standing blocks are. - const pk = n.parent?.kind; - if ( - pk === SK.FunctionDeclaration - || pk === SK.FunctionExpression - || pk === SK.ArrowFunction - || pk === SK.MethodDeclaration - || pk === SK.Constructor - || pk === SK.GetAccessor - || pk === SK.SetAccessor - ) return undefined; - return new TsScope(this, n, 'block', parent, true); - } - case SK.ForStatement: - case SK.ForInStatement: - case SK.ForOfStatement: { - // Upstream nests a 'for' scope only when the init clause is a - // let/const declaration (block-scoped binding). For - // `for (a in xs)` or `for (var x = …)` the loop binding lives - // in the enclosing scope. - const init = n.kind === SK.ForStatement - ? (n as ts.ForStatement).initializer - : (n as ts.ForInStatement | ts.ForOfStatement).initializer; - if ( - init - && init.kind === SK.VariableDeclarationList - && ((init as ts.VariableDeclarationList).flags - & (ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0 - ) { - return new TsScope(this, n, 'for', parent, true); - } - return undefined; - } - case SK.CatchClause: - return new TsScope(this, n, 'catch', parent, true); - case SK.SwitchStatement: { - // Use CaseBlock (not SwitchStatement) as the scope's boundary - // node, so the discriminant `switch (ok)` resolves to the - // outer scope rather than the switch scope. - return new TsScope(this, (n as ts.SwitchStatement).caseBlock, 'switch', parent, true); - } - case SK.WithStatement: { - // `with (obj) { stmt }` — `obj` resolves to the outer scope - // (visited before entering with scope). Use the body statement - // as the with scope boundary; `block` getter steps back out. - return new TsScope(this, (n as ts.WithStatement).statement, 'with', parent, true); - } - case SK.EnumDeclaration: - return new TsScope(this, n, 'tsEnum', parent, true); - case SK.ModuleDeclaration: - return new TsScope(this, n, 'tsModule', parent, true); - case SK.InterfaceDeclaration: - case SK.TypeAliasDeclaration: - // Upstream only creates a 'type' scope when type parameters are - // present (since they need to be scoped to this declaration). - return (n as { typeParameters?: ts.NodeArray }).typeParameters - ? new TsScope(this, n, 'type', parent, true) - : undefined; - case SK.MappedType: - return new TsScope(this, n, 'mapped-type', parent, true); - case SK.ConditionalType: - return new TsScope(this, n, 'conditional-type', parent, true); - case SK.FunctionType: - case SK.ConstructorType: - case SK.CallSignature: - case SK.ConstructSignature: - case SK.MethodSignature: - return new TsScope(this, n, 'function-type', parent, true); - default: - return undefined; - } - } - - _getOrCreateVariable(symbol: ts.Symbol): TsVariable { - let v = this._variableBySymbol.get(symbol); - if (!v) { - v = new TsVariable(this, symbol); - this._variableBySymbol.set(symbol, v); - } - return v; - } - - _collectBinding(binding: ts.BindingName | undefined, out: TsVariable[]) { - if (!binding) return; - const ts_ = ts; - if (ts_.isIdentifier(binding)) { - const sym = this.checker.getSymbolAtLocation(binding); - if (sym) out.push(this._getOrCreateVariable(sym)); - return; - } - if (ts_.isObjectBindingPattern(binding) || ts_.isArrayBindingPattern(binding)) { - for (const el of binding.elements) { - if (ts_.isOmittedExpression(el)) continue; - this._collectBinding(el.name, out); - } - } - } - - // upstream: `@typescript-eslint/scope-manager/dist/ScopeManager.js` - // `getDeclaredVariables(node)`. Returns every variable declared - // directly in `node` — for `let [a, b] = ...` that's two vars - // (`a`, `b`); for `function f(x, { y }) {}` it's three (`f`, `x`, - // `y`). Critical for rules that walk declarations top-down - // (prefer-const, no-unused-vars, naming-convention). - getDeclaredVariables(node: TSESTree.Node): TsVariable[] { - const tsNode = this.astMaps.esTreeNodeToTSNodeMap.get(node); - if (!tsNode) return []; - const out: TsVariable[] = []; - const ts_ = ts; - const collect = (decl: ts.Node | undefined) => { - if (!decl) return; - // VariableDeclaration / Parameter `.name` may be a binding pattern - // (`let [a, b] = ...`, `function f({ x, y }) {}`) — symbol lives on - // each leaf Identifier, not on the pattern container. Walk via - // `_collectBinding` so destructured bindings are emitted. - // `prefer-const` and friends call `getDeclaredVariables(letNode)` - // to enumerate scoped bindings; without this, the rule sees an - // empty array for any destructuring declaration. - if (ts_.isVariableDeclaration(decl) || ts_.isParameter(decl)) { - this._collectBinding(decl.name, out); - return; - } - // CatchClause holds the param via `variableDeclaration` (which is - // itself a VariableDeclaration with the param identifier or pattern - // as `.name`). Without this, getDeclaredVariables returns [] for - // catch clauses and rules like no-ex-assign / no-shadow can't - // enumerate the catch-bound name. Repro: - // `catch (e) { e = new Error() }` — no-ex-assign must report. - if (ts_.isCatchClause(decl)) { - if (decl.variableDeclaration) { - this._collectBinding(decl.variableDeclaration.name, out); - } - return; - } - let sym: ts.Symbol | undefined = (decl as { symbol?: ts.Symbol }).symbol; - if (!sym) { - const nameNode = (decl as { name?: ts.Node }).name; - if (nameNode) sym = this.checker.getSymbolAtLocation(nameNode); - } - if (sym) out.push(this._getOrCreateVariable(sym)); - }; - if (ts_.isVariableStatement(tsNode)) { - for (const d of tsNode.declarationList.declarations) collect(d); - } - else if (ts_.isVariableDeclarationList(tsNode)) { - for (const d of tsNode.declarations) collect(d); - } - else if ( - ts_.isVariableDeclaration(tsNode) - || ts_.isParameter(tsNode) - || ts_.isFunctionDeclaration(tsNode) - || ts_.isFunctionExpression(tsNode) - || ts_.isArrowFunction(tsNode) - || ts_.isMethodDeclaration(tsNode) - || ts_.isClassDeclaration(tsNode) - || ts_.isClassExpression(tsNode) - || ts_.isImportSpecifier(tsNode) - || ts_.isImportClause(tsNode) - || ts_.isNamespaceImport(tsNode) - || ts_.isImportEqualsDeclaration(tsNode) - || ts_.isEnumDeclaration(tsNode) - || ts_.isModuleDeclaration(tsNode) - || ts_.isTypeAliasDeclaration(tsNode) - || ts_.isInterfaceDeclaration(tsNode) - || ts_.isTypeParameterDeclaration(tsNode) - || ts_.isCatchClause(tsNode) - ) { - collect(tsNode); - } - // For function-like nodes, also return their parameters. - if ( - ts_.isFunctionDeclaration(tsNode) - || ts_.isFunctionExpression(tsNode) - || ts_.isArrowFunction(tsNode) - || ts_.isMethodDeclaration(tsNode) - ) { - const fn = tsNode as ts.FunctionLikeDeclaration; - if (fn.parameters) { - for (const p of fn.parameters) { - this._collectBinding(p.name, out); - } - } - } - return out; - } - - acquire(node: TSESTree.Node, inner = false): TsScope | null { - const tsNode = this.astMaps.esTreeNodeToTSNodeMap.get(node); - if (!tsNode) return null; - const arr = this.nodeToScope.get(tsNode); - if (!arr || arr.length === 0) return null; - if (arr.length === 1) return arr[0]; - // Multiple scopes share this node (e.g. Program → [global, module] or - // FunctionExpression → [fn-expr-name, function]). - const predicate = (s: TsScope) => !(s.type === 'function' && s.functionExpressionScope); - if (inner) { - for (let i = arr.length - 1; i >= 0; --i) { - if (predicate(arr[i])) return arr[i]; - } - return null; - } - return arr.find(predicate) ?? null; - } - - get variables(): TsVariable[] { - const out: TsVariable[] = []; - for (const s of this.scopes) out.push(...s.variables); - return out; - } - - isES6() { - return true; - } - isGlobalReturn() { - return false; - } - isImpliedStrict() { - return this.sourceType === 'module'; - } - isModule() { - return this.sourceType === 'module'; - } - isStrictModeSupported() { - return true; - } - - // Fake globals registered by `addGlobals` whose through-ref reconciliation - // is still pending. Drained when `_ensureRefIndex` actually runs (i.e. - // when a rule first reads references). Reading `globalScope.variables` - // directly already sees these — only the `through` -> `references` - // re-resolve is deferred. - _pendingFakeGlobals?: Map; - - // upstream: `eslint-scope/lib/scope-manager.js` `ScopeManager.addGlobals`, - // invoked by `eslint/lib/languages/js/source-code/source-code.js` - // `addDeclaredGlobals` for every name in `conf/globals.js`'s - // `es${ecmaVersion}` set + user `globals` config. Synthesises a - // Variable in `globalScope` per name and re-resolves matching - // through-refs against it. Compat-eslint's entry point calls this - // with the vendored `ESLINT_BUILTIN_GLOBALS` (es2026) so `no-undef` - // doesn't fire on `undefined` / `Math` / `Array` / etc. - // - // ESLint's contract requires `scopeManager.variables` / - // `globalScope.set` to immediately reflect an `addGlobals` call (see - // `eslint-scope/test/add-globals.test.ts`'s "doesn't affect unrelated - // references" case), so the per-file `TsVariable` is built eagerly. - // What we DO save: - // 1. Fake `ts.Symbol` placeholders are module-shared via - // `_sharedFakeGlobalSymbol(name)` — every file's manager reuses - // the same plain-object symbol per name. Across the TypeScript - // repo's 710 files × ~250 declared names, that's ~177k throwaway - // allocs avoided. - // 2. The through-ref reconciliation is parked in - // `_pendingFakeGlobals` and drained at the tail of - // `_ensureRefIndex`. Rule sets that don't query references - // (most lint configs without `no-undef` / `no-unused-vars`) - // skip it entirely. - addGlobals(names: string[]) { - // Skip names that already exist as declared globals — upstream's - // addGlobals is a no-op for already-known names (test name: - // "doesn't affect already declared global variables"). - const existingNames = new Set(); - for (const v of this.globalScope.variables) { - existingNames.add(v.name); - } - const pending = this._pendingFakeGlobals ??= new Map(); - for (const name of names) { - if (existingNames.has(name)) continue; - const fakeSym = _sharedFakeGlobalSymbol(name); - const v = new TsVariable(this, fakeSym); - this._variableBySymbol.set(fakeSym, v); - this.globalScope._addLibVariable(v); - pending.set(name, v); - } - // If the ref index is already built (rare — only when a rule queried - // references before all `addGlobals` calls finished), reconcile now - // since the deferred path won't fire again. - if (this._refIndex) { - this._reconcileFakeGlobals(); - } - // Clear implicit-globals cache — added globals supersede the - // auto-synthesized ones for any matching names. - this._implicitGlobals = undefined; - } - - // Drain `_pendingFakeGlobals` against the currently-built `_through`, - // moving every ref whose identifier text matches a registered fake into - // the per-symbol `_refIndex`. Called either from `addGlobals` (when the - // ref index is already built) or at the tail of `_ensureRefIndex` (the - // common path for the lint pipeline). - _reconcileFakeGlobals() { - const pending = this._pendingFakeGlobals; - if (!pending || pending.size === 0) return; - const through = this._through!; - const refs = this._refIndex!; - const remaining: TsReference[] = []; - for (const ref of through) { - // Read name from the ts.Identifier directly — `ref.identifier.name` - // would trigger `tsToEstreeOrStub` → `materialize`, which walks - // up the parent chain and may eagerly construct wrapper-class - // ESTree nodes (ChainExpression, ExportNamedDeclaration). ts-ast- - // scan's later traversal then sees those wrappers in the lazy - // cache and dispatches enter/leave on them out of source order - // — desyncing CPA's choice-context stack and crashing - // `popChoiceContext` on null. Reading `tsIdentifier.text` is - // pure and avoids the eager materialize. - const name = ref.tsIdentifier.text; - const added = pending.get(name); - if (!added) { - remaining.push(ref); - continue; - } - // Re-point the ref's symbol so resolved/getReferencesFor work. - (ref as { symbol: ts.Symbol }).symbol = added.symbol; - let arr = refs.get(added.symbol); - if (!arr) refs.set(added.symbol, arr = []); - arr.push(ref); - } - this._through = remaining; - this._pendingFakeGlobals = undefined; - } - - // upstream equivalent: `@typescript-eslint/scope-manager/dist/referencer/Referencer.js` - // running through every node and calling `currentScope().referenceValue(...)` / - // `referenceType(...)`. Upstream is eager — the entire reference graph - // is built during scope-tree construction. We split: - // `_buildScopeTree` (constructor) — scopes + parented walk + collects - // `_pendingIdentifiers`. - // `_ensureRefIndex` (this method, lazy) — classifies each pending - // Identifier into resolved references vs `_through` (escapes file). - // Triggered on first `scope.through` / `scope.references` / - // `var.references` / `getReferencesFor` access. Most rules read - // nothing from this graph, paying nothing. - _through?: TsReference[]; - _refIndex?: Map; - _referencesByScope?: Map; - _ensureRefIndex(): Map { - if (this._refIndex) return this._refIndex; - // Reference classification needs `_variableBySymbol` populated for - // every scope so it can distinguish file-local symbols (resolved) - // from cross-file globals (unresolved → goes to `through`). Walking - // `s.variables` for each scope is what fills the map. Used to be - // done eagerly in `_buildScopeTree`, but rule sets that never query - // references (e.g. eqeqeq + no-var + no-bitwise) don't need this — - // defer until the first ref query forces it. addGlobals reads - // `globalScope.variables` explicitly, so the global scope still - // gets populated at the right time. - for (const s of this.scopes) { - void s.variables; - } - const refs = new Map(); - const through: TsReference[] = []; - const byScope = new Map(); - const recordScope = (ref: TsReference) => { - const s = ref.from; - let arr = byScope.get(s); - if (!arr) byScope.set(s, arr = []); - arr.push(ref); - }; - const checker = this.checker; - const SK = ts.SyntaxKind; - const variableBySymbol = this._variableBySymbol; - // Iterate identifiers gathered during the scope-tree walk — no second - // AST traversal. - const idents = this._pendingIdentifiers; - for (let i = 0; i < idents.length; i++) { - const node = idents[i]; - // Cheap filter first: many identifiers (property names, label - // targets, decl names) are neither reference-eligible nor free — - // skip them before triggering the (expensive) symbol resolver. - // One switch over `parent.kind` produces both flags as a 2-bit - // bitmap (bit 0 = freeRef, bit 1 = refUsage); 0 means skip. - const flags = this._classifyIdentifier(node); - if (flags === 0) continue; - const refUsage = (flags & 0b10) !== 0; - const freeRef = (flags & 0b01) !== 0; - - // `arguments` inside a non-arrow function resolves to the - // function's synthetic arguments TsVariable. TS's checker gives - // each function a separate `arguments` symbol; register the link - // the first time we see it. - if (node.text === 'arguments') { - for (let cur: ts.Node | undefined = node.parent; cur; cur = cur.parent) { - if ( - cur.kind === SK.FunctionDeclaration - || cur.kind === SK.FunctionExpression - || cur.kind === SK.MethodDeclaration - || cur.kind === SK.Constructor - || cur.kind === SK.GetAccessor - || cur.kind === SK.SetAccessor - ) { - const arr = this.nodeToScope.get(cur); - const fnScope = arr?.find(s => s.type === 'function'); - if (fnScope) { - const argsVar = fnScope._getOrCreateArgumentsVar(); - const sym = checker.getSymbolAtLocation(node); - if (sym && !variableBySymbol.has(sym)) { - variableBySymbol.set(sym, argsVar); - } - } - break; - } - } - } - - // Symbol resolution. For binding identifiers (the `x` in `let x = …`, - // destructured `{ x }`, `for (const x of …)`), the symbol is already - // on the parent declaration node (set by the TS binder) — read it - // directly instead of paying for a checker.getSymbolAtLocation walk. - const parent = node.parent; - let sym: ts.Symbol | undefined; - const pk = parent?.kind; - if ( - pk === SK.VariableDeclaration && (parent as ts.VariableDeclaration).name === node - || pk === SK.BindingElement && (parent as ts.BindingElement).name === node - ) { - sym = (parent as { symbol?: ts.Symbol }).symbol; - } - if (!sym) { - sym = checker.getSymbolAtLocation(node); - } - // `{ x }` shorthand: getSymbolAtLocation returns the property - // symbol; we want the binding `x` resolves to. - if (parent && pk === SK.ShorthandPropertyAssignment && (parent as ts.ShorthandPropertyAssignment).name === node) { - const valSym = checker.getShorthandAssignmentValueSymbol(parent); - if (valSym) sym = valSym; - } - // `export { x }` (no `from`): TS produces an alias Symbol whose - // declaration is the ExportSpecifier itself — distinct from the - // LOCAL symbol the import / let / fn declared. `getSymbolAtLocation` - // returns the alias; the rule needs the local target so the ref - // resolves into module scope. `getExportSpecifierLocalTargetSymbol` - // follows the alias to the original local. Without this, every - // `export { foo }` re-export reports `foo` as undefined. - if (parent && pk === SK.ExportSpecifier && (parent as ts.ExportSpecifier).name === node) { - const exportDecl = parent.parent.parent as ts.ExportDeclaration; - if (!exportDecl.moduleSpecifier) { - const localSym = checker.getExportSpecifierLocalTargetSymbol(parent as ts.ExportSpecifier); - if (localSym) sym = localSym; - } - } - if (sym && variableBySymbol.has(sym)) { - // Resolved reference — add to per-symbol index (only when this - // position counts as a reference, e.g. usage or init). - if (refUsage) { - // Key the bucket by the TsVariable's canonical symbol — for - // most cases that's the same as `sym`, but synthetic - // `arguments` is built before any reference is seen and - // uses a symbol from `getSymbolsInScope`, while - // `getSymbolAtLocation` on an actual `arguments` - // identifier returns a function-scoped synthetic symbol - // (the implicit `arguments` per non-arrow function). Both - // alias to the same TsVariable via `variableBySymbol`, - // but keying refs by `sym` would split them across two - // buckets — `getReferencesFor(argsVar.symbol)` then - // returns empty and prefer-rest-params reports nothing. - const v = variableBySymbol.get(sym)!; - const key = v.symbol; - let arr = refs.get(key); - if (!arr) refs.set(key, arr = []); - { - const ref = new TsReference(this, node, sym); - arr.push(ref); - recordScope(ref); - } - } - } - else if (sym) { - // Lib-global decision is cached per symbol: check the cache - // before re-walking the symbol's declarations through - // `_isLibGlobalSymbol`. `null` in the cache means - // "checked, not a lib global". - let cached = this._libDecisionBySymbol.get(sym); - if (cached === undefined) { - cached = this._isLibGlobalSymbol(sym) ? 1 : 0; - this._libDecisionBySymbol.set(sym, cached); - } - if (cached === 1) { - const isValue = LIB_VALUE_GLOBALS.has(sym.name); - const inTypePosition = !this._isValueReferencePosition(node); - if (inTypePosition || isValue) { - let v = this._libVariableBySymbol.get(sym); - if (!v) { - // `addGlobals` (TS_LIB_TYPE_GLOBALS / - // ESLINT_BUILTIN_GLOBALS) may have already - // registered a fake-symbol var for this name. - // Reuse it instead of creating a fresh - // TsVariable so globalScope.variables stays - // stable: the `scope.through` getter snapshots - // localVars BEFORE triggering the ref-index - // build, so a fresh var added during the build - // isn't in the snapshot and `isLocal(realSym)` - // returns false, leaking the ref into - // `through`. Reusing the fake var maps the real - // ts.Symbol → fake TsVariable, which IS in the - // snapshot. Without this, no-undef false-fires - // on Record / Pick / Extract / etc. used in - // type-arg positions across module imports. - // Force `set` getter to populate the name-keyed - // lookup map (lazily built — _set is undefined - // until first access). addGlobals's - // `_addLibVariable` only updates _set when it - // already exists; rebuilding from variables is - // a one-time O(N) cost amortized across the - // entire ref scan. - const existing = this.globalScope.set.get(sym.name); - if (existing) { - v = existing; - } - else { - v = new TsVariable(this, sym); - this.globalScope._addLibVariable(v); - } - this._libVariableBySymbol.set(sym, v); - // Also register in `_variableBySymbol` so - // `scope.through`'s alias-aware lookup finds the - // var by ref.symbol. Without this, lib vars (Map, - // Set, ...) don't show up as local in - // globalScope and refs escape → no-undef false - // positives. - variableBySymbol.set(sym, v); - } - // Key the ref bucket by `v.symbol`, NOT `sym`. When the - // reused var is a fake-symbol global (the existing-var - // branch above), `v.symbol === fakeSym` while `sym === - // realSym`. The follow-up branch - // (`variableBySymbol.has(sym)` → refUsage path) ALSO - // keys by `v.symbol`. Mismatching keys here would - // scatter refs across two buckets and `getReferencesFor - // (v.symbol)` would silently drop the first hit. Same - // reasoning as the synthetic `arguments` aliasing - // case in the upper branch. - const key = v.symbol; - let arr = refs.get(key); - if (!arr) refs.set(key, arr = []); - { - const ref = new TsReference(this, node, sym); - arr.push(ref); - recordScope(ref); - } - } - else if (freeRef) { - // Value ref to a type-only lib global (e.g. Set, Map, - // Iterable in expression position) — upstream treats - // as undefined. Match. - { - const ref = new TsReference(this, node, sym); - through.push(ref); - recordScope(ref); - } - } - } - else if (freeRef) { - // Symbol present but unknown to lib globals. The compat - // entry point pre-registers TS lib type names - // (Record / Promise / Awaited / …) via `addGlobals`, - // so any name that resolves cleanly with @typescript- - // eslint scope-manager already lands in - // `_variableBySymbol` after this point. Whatever falls - // through here is a genuinely unresolved value- or - // type-position reference — fire it (matches plain ESLint - // no-undef on `NodeJS.ErrnoException` / `Visitor` / typo - // names that aren't in the registered lib type set). - { - const ref = new TsReference(this, node, sym); - through.push(ref); - recordScope(ref); - } - } - } - else if (freeRef) { - // Unresolved (no symbol). Through-only. - const ref = new TsReference(this, node, undefined as any); - through.push(ref); - recordScope(ref); - } - } - // Free the identifier list — won't be needed again. - this._pendingIdentifiers = []; - this._through = through; - this._referencesByScope = byScope; - this._refIndex = refs; - // Drain any `addGlobals` calls that landed before the index was - // built — `_through` now exists, so we can move matching refs - // across in one pass instead of forcing the index per `addGlobals`. - this._reconcileFakeGlobals(); - return this._refIndex; - } - - getReferencesFor(symbol: ts.Symbol): TsReference[] { - return this._ensureRefIndex().get(symbol) ?? []; - } - - getThroughReferences(): TsReference[] { - this._ensureRefIndex(); - return this._through!; - } - - // Implicit globals: synthesized variables for unresolved write references - // (e.g. `x = 1;` where `x` isn't declared) that ESLint's `no-undef` - // alternatives might want to model. One Variable per unique name; each - // gets a synthetic ImplicitGlobalVariable def + the first write's - // identifier. - _implicitGlobals?: TsVariable[]; - getImplicitGlobals(): TsVariable[] { - if (this._implicitGlobals) return this._implicitGlobals; - const through = this.globalScope.through; - const out: TsVariable[] = []; - const seen = new Set(); - for (const ref of through) { - if (!ref.isWrite()) continue; - const name = ref.identifier.name; - if (seen.has(name)) continue; - seen.add(name); - const fakeSym = { name, declarations: [], flags: 0 } as unknown as ts.Symbol; - const v = new TsVariable(this, fakeSym); - // Override defs / identifiers via instance-level override so the - // implicit global has a believable single ImplicitGlobalVariable - // def pointing at the write reference's identifier. - (v as TsVariable & { _defsOverride?: TsDefinition[] })._defsOverride = [ - new TsImplicitGlobalDefinition(this, v, ref.tsIdentifier), - ]; - (v as TsVariable & { _identifiersOverride?: TSESTree.Identifier[] })._identifiersOverride = [ref.identifier]; - out.push(v); - } - return this._implicitGlobals = out; - } - - // upstream equivalent: spread across `@typescript-eslint/scope-manager/dist/ - // referencer/{Referencer,TypeVisitor,ExportVisitor,...}.js`. Each - // upstream visitor decides whether to emit `referenceValue`, - // `referenceType`, or skip (e.g. TypeVisitor.TSImportType skips the - // qualifier; PropertyAccessExpression visitor only descends into - // `.object`, never `.property`). - // - // We collapse those visitor decisions into one branchy switch - // keyed by `parent.kind`, returning a 2-bit bitmap: - // bit 0 (FREE_REF=1): identifier is in a reference position (NOT a - // declaration name, property access RHS, label, etc.) - // bit 1 (REF_USAGE=2): identifier should produce an ESLint Reference — - // either a usage (free position) or a declaration with init/iter - // binding that counts as an init Reference. - // - // Adding a new case here requires reading the corresponding - // upstream visitor to understand whether the identifier should be - // classified as a value reference, type reference, or skipped. - // - // Hot-path layout: - // 1. parent.kind not in `_CLASSIFY_HANDLED_KINDS` → return 0b11 (most - // identifiers, e.g. inside expression positions). - // 2. parent.kind in `_CLASSIFY_NAME_SLOT_KINDS` → fall-through "name - // slot" body shared by ~22 declaration-shaped kinds. - // 3. otherwise enter the switch for one of ~10 special-shape cases. - _classifyIdentifier(id: ts.Identifier): number { - const p = id.parent; - if (!p) return 0b11; - const k = p.kind; - if (!_CLASSIFY_HANDLED_KINDS[k]) return 0b11; - if (_CLASSIFY_NAME_SLOT_KINDS[k]) { - return (p as { name?: ts.Node }).name === id ? 0 : 0b11; - } - const SK = ts.SyntaxKind; - switch (k) { - // Single-slot kinds — each consults its own slot field. - case SK.PropertyAccessExpression: - return (p as ts.PropertyAccessExpression).name === id ? 0 : 0b11; - case SK.QualifiedName: { - if ((p as ts.QualifiedName).right === id) return 0; - // `import("mod").A.B.C` — every identifier in the qualifier - // chain references an export of the imported module, NOT a - // local. Walk up through QualifiedName ancestors; if we hit - // an ImportType, skip the whole chain (matches upstream's - // `TypeVisitor.TSImportType` skipping the qualifier). - let cur: ts.Node | undefined = p; - while (cur && cur.kind === SK.QualifiedName) cur = cur.parent; - if (cur && cur.kind === SK.ImportType) return 0; - return 0b11; - } - case SK.LabeledStatement: - return (p as ts.LabeledStatement).label === id ? 0 : 0b11; - case SK.BreakStatement: - case SK.ContinueStatement: - return (p as ts.BreakStatement | ts.ContinueStatement).label === id ? 0 : 0b11; - case SK.MetaProperty: - // `new.target` / `import.meta` — `target` / `meta` are syntactic - // markers, not real references. - return (p as ts.MetaProperty).name === id ? 0 : 0b11; - case SK.ImportSpecifier: { - // Import binding — the names are declarations, not references. - const e = p as ts.ImportSpecifier; - return (e.name === id || e.propertyName === id) ? 0 : 0b11; - } - - // Declaration name slot but with init/iter rules — name slot is - // not free, but may still produce an init Reference. - case SK.VariableDeclaration: { - const v = p as ts.VariableDeclaration; - if (v.name !== id) return 0b11; - // In the name slot: not free; refUsage if initializer or - // for-of/in binding. - if (v.initializer !== undefined) return 0b10; - const list = v.parent; - if (list && list.kind === SK.VariableDeclarationList) { - const stmt = list.parent; - if (stmt && (stmt.kind === SK.ForOfStatement || stmt.kind === SK.ForInStatement)) return 0b10; - } - return 0; - } - case SK.BindingElement: { - const e = p as ts.BindingElement; - if (e.name !== id && e.propertyName !== id) return 0b11; - // In a binding name slot: not free. refUsage walks up to the - // owning VariableDeclaration / Parameter to inherit init/iter - // semantics. - for (let cur: ts.Node | undefined = p; cur; cur = cur.parent) { - if (cur.kind === SK.VariableDeclaration) { - const v = cur as ts.VariableDeclaration; - if (v.initializer !== undefined) return 0b10; - const list = v.parent; - if (list && list.kind === SK.VariableDeclarationList) { - const stmt = list.parent; - if (stmt && (stmt.kind === SK.ForOfStatement || stmt.kind === SK.ForInStatement)) return 0b10; - } - return 0; - } - if (cur.kind === SK.Parameter) { - // `function f([a = 0] = [])` — outer Parameter init counts. - return (cur as ts.ParameterDeclaration).initializer !== undefined ? 0b10 : 0; - } - } - return 0; - } - case SK.Parameter: { - const param = p as ts.ParameterDeclaration; - if (param.name !== id) return 0b11; - // In the name slot: not free; refUsage iff initializer. - return param.initializer !== undefined ? 0b10 : 0; - } - - case SK.ExportSpecifier: { - // `export {x} from "mod";` — re-export, no local reference. - const e = p as ts.ExportSpecifier; - const decl = e.parent.parent; - if (decl.moduleSpecifier) return 0; - // `export {x}` — `x` references the local. `export {x as v}` — - // `x` (propertyName) references the local; `v` (name) is the - // public export name (not a local reference). - if (e.propertyName) return e.name === id ? 0 : 0b11; - return 0b11; - } - - case SK.TypeReference: - // `expr as const` — `const` is a syntactic marker. _isFreeReference - // returned false; _isReferenceableUsage left it on the default - // `return true` path → REF_USAGE only. - if ( - id.text === 'const' - && (p as ts.TypeReferenceNode).typeName === id - && p.parent - && (p.parent.kind === SK.AsExpression || p.parent.kind === SK.TypeAssertionExpression) - && (p.parent as ts.AsExpression | ts.TypeAssertion).type === p - ) return 0b10; - return 0b11; - - case SK.ImportType: - // `import("module").Foo` — `Foo` is the qualifier of an - // EntityName referencing an export of the imported module, - // NOT a local reference. Upstream's TypeVisitor explicitly - // skips visiting the qualifier (see - // `@typescript-eslint/scope-manager/.../TypeVisitor.ts` - // `TSImportType`). Without this skip, the qualifier ends up - // in `globalScope.through` → `no-undef` reports it. - if ((p as ts.ImportTypeNode).qualifier === id) return 0; - return 0b11; - - default: - return 0b11; - } - } - - // Is this Identifier in a value position (not a type)? Used to decide - // whether an unresolved free reference becomes a `no-undef`-eligible - // through-reference. - _isValueReferencePosition(id: ts.Identifier): boolean { - const ts_ = ts; - const SK = ts_.SyntaxKind; - for (let cur: ts.Node | undefined = id.parent; cur; cur = cur.parent) { - // `class Foo extends Base` — Base is a value reference (the - // superclass must be a constructor function). The wrapping - // ExpressionWithTypeArguments returns true from BOTH ts.isTypeNode - // AND ts.isExpression, so the generic isTypeNode-first check - // would short-circuit this position as "type only" and drop the - // reference. `implements I` (class) and `extends I` (interface) - // stay type-only and fall through to the generic logic. - if (cur.kind === SK.ExpressionWithTypeArguments) { - const hc = cur.parent; - if ( - hc?.kind === SK.HeritageClause - && (hc as ts.HeritageClause).token === SK.ExtendsKeyword - && hc.parent - && (hc.parent.kind === SK.ClassDeclaration || hc.parent.kind === SK.ClassExpression) - ) { - return true; - } - } - if (ts_.isTypeNode(cur)) return false; - if (ts_.isExpression(cur) || ts_.isStatement(cur)) return true; - } - return true; - } - - // True if this symbol is declared exclusively in a TS default library - // (`lib.es*.d.ts`). The caller decides whether the reference resolves — - // type-position refs always do; value-position refs only do for the names - // upstream marks as `isValueVariable: true` (LIB_VALUE_GLOBALS). - // - // `decl.getSourceFile()` walks `parent` to the SourceFile (5–20 hops), and - // `program.isSourceFileDefaultLibrary` does its own path check. Lib symbols - // commonly have multiple decls all landing in the same `lib.*.d.ts` file — - // cache by SourceFile so repeat lookups within a symbol (and across symbols - // within the same program) skip the rechecks. - _isLibGlobalSymbol(sym: ts.Symbol): boolean { - const decls = sym.declarations; - if (!decls || decls.length === 0) return false; - const sfCache = this._libSourceFileCache; - for (const d of decls) { - const sf = d.getSourceFile(); - let isLib = sfCache.get(sf); - if (isLib === undefined) { - isLib = this.program.isSourceFileDefaultLibrary(sf); - sfCache.set(sf, isLib); - } - if (!isLib) return false; - } - return true; - } - - tsToEstree(tsNode: ts.Node): T | undefined { - return this.astMaps.tsNodeToESTreeNodeMap.get(tsNode) as T | undefined; - } - - // Compat-eslint specific (no upstream equivalent — typescript-estree - // converts the WHOLE program eagerly, so every ts.Node has its - // ESTree counterpart from parse time). This is the bridge from - // scope-manager's TS-keyed state to the lazy ESTree shim. Every - // `def.node` / `def.parent` / `var.identifiers[]` / `ref.identifier` - // getter reads through here. Returns undefined for kinds with no - // ESTree counterpart (tokens, JSDoc, certain TS-internal containers - // — see `hasNoEstreeCounterpart` in lazy-estree.ts), so callers can - // distinguish "TS node exists but has no ESTree shape" from "TS node - // is missing". Unknown future kinds still go through GenericTSNode - // safety net (with `_parent` non-enumerable defense preventing the - // VisitorBase recursion that previously stack-overflowed). - tsToEstreeOrStub(tsNode: ts.Node | undefined): T | undefined { - if (!tsNode) return undefined; - const real = this.astMaps.tsNodeToESTreeNodeMap.get(tsNode) as T | undefined; - if (real) return real; - try { - return materialize(tsNode, { - ast: this.tsFile, - maps: this.astMaps, - }) as unknown as T; - } - catch (e) { - if (e instanceof NoESTreeCounterpartError) return undefined; - throw e; - } - } -} - -// Public surface mirrors `@typescript-eslint/scope-manager/dist/scope/`'s -// per-type Scope classes (BlockScope, FunctionScope, GlobalScope, …). -// We collapse them into one class with a `type` discriminator. -// -// Rule-facing getters: `block`, `set`, `variables`, `references`, -// `through`, `childScopes`, `upper`, `variableScope`, `isStrict`, -// `functionExpressionScope`, `implicit`. Each has a corresponding -// upstream method/getter on ScopeBase.js or one of its subclasses. -export class TsScope { - _variables?: TsVariable[]; - _childScopes: TsScope[] = []; - _set?: Map; - upper: TsScope | null; - - constructor( - readonly manager: TsScopeManager, - readonly tsNode: ts.Node, - readonly type: ScopeType, - upper: TsScope | null, - register = false, - ) { - this.upper = upper; - if (upper) upper._childScopes.push(this); - if (register) manager._registerScope(tsNode, this); - } - - // upstream: `eslint-scope/lib/scope.js` `Scope.block` — the AST - // node that owns this scope (FunctionDeclaration / Program / - // BlockStatement / etc.). Rules use `scope.block === node` to - // match a listener firing to the scope it should enter - // (no-redeclare's `checkForBlock`, no-shadow's stack walk). - get block(): TSESTree.Node | undefined { - const SK = ts.SyntaxKind; - const k = this.tsNode.kind; - // For class methods (Constructor / MethodDeclaration / accessors), TS - // has a single node but ESTree splits into MethodDefinition + nested - // FunctionExpression. ESLint's scope.block points at the - // FunctionExpression — extract it from the MethodDefinition.value slot. - if ( - k === SK.Constructor - || k === SK.MethodDeclaration - || k === SK.GetAccessor - || k === SK.SetAccessor - ) { - const md = this.manager.tsToEstreeOrStub(this.tsNode) as - | { value?: TSESTree.Node } - | undefined; - if (md?.value) return md.value; - } - // Switch scope's tsNode is the CaseBlock (boundary for `from`). ESLint - // expects scope.block === SwitchStatement, so step out one level. - // Same trick for with scope (tsNode is the body statement). - if (k === SK.CaseBlock && this.tsNode.parent) { - return this.manager.tsToEstreeOrStub(this.tsNode.parent); - } - if (this.type === 'with' && this.tsNode.parent) { - return this.manager.tsToEstreeOrStub(this.tsNode.parent); - } - const result = this.manager.tsToEstreeOrStub(this.tsNode); - // `export function f` / `export default function f` / `export class C`: - // materialize wraps the inner declaration in - // ExportNamedDeclaration / ExportDefaultDeclaration. ESLint listens - // on the inner FunctionDeclaration / ClassDeclaration and tests - // `scope.block === node` to decide whether to enter the scope — - // so scope.block must point at the inner, not the wrapper. - // Unwrap when this scope's tsNode is the inner declaration but - // materialize yielded the wrapper. - if ( - result && ( - (result as { type?: string }).type === 'ExportNamedDeclaration' - || (result as { type?: string }).type === 'ExportDefaultDeclaration' - ) - ) { - const inner = (result as { declaration?: TSESTree.Node }).declaration; - if (inner) return inner; - } - return result; - } - - get variableScope(): TsScope { - // Nearest enclosing scope that owns `var`-style declarations. - // Mirrors upstream `VARIABLE_SCOPE_TYPES` (scope-manager - // `ScopeBase.js`): function / global / module / tsModule / - // class-field-initializer (and class-static-block, which we - // don't tag yet). NOT `class` — classes are NOT variable - // scopes (an upstream test asserts this). - // Without `tsModule`, `markDeclarationChildAsUsed` in - // no-unused-vars walks past the `declare global { var FOO }` / - // `declare module 'X' { var BAR }` body looking for `FOO`/`BAR` - // in the global scope's set, misses, and leaves them flagged - // as unused (4 false fires across prisma's runtime/* declare- - // global blocks). - let s: TsScope | null = this; - while ( - s - && s.type !== 'function' - && s.type !== 'global' - && s.type !== 'module' - && s.type !== 'tsModule' - && s.type !== 'class-field-initializer' - ) { - s = s.upper; - } - return s ?? this.manager.globalScope; - } - - get functionExpressionScope(): boolean { - return this.type === 'function-expression-name'; - } - - get isStrict(): boolean { - // Modules are implicitly strict. - if (this.manager.isModule()) return true; - // Class bodies are always strict. - if (this.type === 'class') return true; - const SK = ts.SyntaxKind; - const hasUseStrictDirective = (statements: ReadonlyArray): boolean => { - for (const stmt of statements) { - if ( - stmt.kind === SK.ExpressionStatement - && (stmt as ts.ExpressionStatement).expression.kind === SK.StringLiteral - && ((stmt as ts.ExpressionStatement).expression as ts.StringLiteral).text === 'use strict' - ) return true; - break; // directive prologue only — first non-directive stops the search. - } - return false; - }; - // SourceFile (script with `'use strict'` at top): propagates. - if (this.type === 'global') { - if (hasUseStrictDirective((this.tsNode as ts.SourceFile).statements)) return true; - } - // Function with 'use strict' directive in body. - if (this.type === 'function') { - const fn = this.tsNode as ts.FunctionLikeDeclaration; - const body = (fn as { body?: ts.Node }).body; - if (body && body.kind === SK.Block && hasUseStrictDirective((body as ts.Block).statements)) { - return true; - } - } - // Inherit from upper scope. - return this.upper?.isStrict ?? false; - } - - get variables(): TsVariable[] { - if (!this._variables) { - this._variables = this._collectVariables(); - } - return this._variables; - } - - // Inject a synthesized lib global (Object, Array, etc.) discovered during - // reference-index walk. Only globalScope receives these. - _addLibVariable(v: TsVariable) { - void this.variables; // ensure populated - if (!this._variables!.includes(v)) { - this._variables!.push(v); - this._set?.set(v.name, v); - } - } - - _collectVariables(): TsVariable[] { - const ts_ = ts; - const SK = ts_.SyntaxKind; - const out: TsVariable[] = []; - const seen = new Set(); - const seenNames = new Set(); - const push = (sym: ts.Symbol | undefined) => { - if (!sym || seen.has(sym)) return; - seen.add(sym); - // Dedupe by name: TS sometimes produces multiple symbols for the - // same lexical binding. Two cases: - // 1. Same declaration seen as two symbols (export alias + local - // symbol, parameter property + class member). These share at - // least one declaration node → just register the symbol alias - // for reference resolution, don't add extra defs. - // 2. Genuine redeclarations with different symbols and different - // declaration nodes: `function foo` + `var foo`, duplicate - // `class X`, `enum C` + `interface C`. Merge so no-redeclare - // sees all identifiers. - if (seenNames.has(sym.name)) { - const existing = out.find(v => v.name === sym.name); - if (existing) { - // Check if the new symbol has any declaration nodes NOT - // already covered by the existing variable's symbol(s). - const existingDecls = new Set(); - if (existing.symbol.declarations) { - for (const d of existing.symbol.declarations) existingDecls.add(d); - } - if (existing._mergedSymbols) { - for (const ms of existing._mergedSymbols) { - if (ms.declarations) { for (const d of ms.declarations) existingDecls.add(d); } - } - } - const newDecls = sym.declarations ?? []; - const hasNewDecl = newDecls.some(d => !existingDecls.has(d)); - if (hasNewDecl) { - // Genuine redeclaration — merge. - (existing._mergedSymbols ??= []).push(sym); - } - // Always register for reference resolution. - this.manager._variableBySymbol.set(sym, existing); - } - return; - } - seenNames.add(sym.name); - out.push(this.manager._getOrCreateVariable(sym)); - }; - const symOf = (decl: ts.Node | undefined): ts.Symbol | undefined => { - if (!decl) return undefined; - // Prefer `checker.getSymbolAtLocation(name)` over `decl.symbol` - // so declaration registration uses the SAME symbol identity - // reference resolution returns. TS merges symbols across: - // - `.d.ts` redeclarations (lib types declared in both - // bundled `lib.*.d.ts` and user-loaded `src/lib/*.d.ts`), - // - `declare module "X" { ... }` augmentations referencing - // imports from the host file's outer module scope, - // - sibling declaration-merging (interface + namespace, - // function + namespace). - // `decl.symbol` returns the local file/declaration symbol; - // `getSymbolAtLocation(name)` returns the merged symbol that - // `variableBySymbol.get(refSym)` will probe at use sites. - // Falling back to `decl.symbol` covers the rare cases where - // the name node is missing (e.g. anonymous default exports). - const nameNode = (decl as { name?: ts.Node }).name; - if (nameNode) { - const sym = this.manager.checker.getSymbolAtLocation(nameNode); - if (sym) return sym; - } - return (decl as { symbol?: ts.Symbol }).symbol; - }; - // Walk the binding pattern of a declarator/parameter, pushing every - // identifier's symbol into `out`. - const pushBinding = (binding: ts.BindingName | undefined) => { - if (!binding) return; - if (ts_.isIdentifier(binding)) { - push(this.manager.checker.getSymbolAtLocation(binding)); - return; - } - if (ts_.isObjectBindingPattern(binding) || ts_.isArrayBindingPattern(binding)) { - for (const el of binding.elements) { - if (ts_.isOmittedExpression(el)) continue; - pushBinding(el.name); - } - } - }; - // Walk children up to (but not into) any nested scope of these kinds. - const walkUntilScope = (n: ts.Node, visit: (c: ts.Node) => void) => { - ts_.forEachChild(n, c => { - visit(c); - // Skip into another scope (we don't recurse into function bodies etc.) - if (this._isOwnScopeBoundary(c)) return; - walkUntilScope(c, visit); - }); - }; - - // In JS files, TS's binder hoists `const x = require('…')` into - // the enclosing `SourceFile.locals` / `FunctionLikeDeclaration.locals` - // as a CommonJS Alias symbol (flags=0x200000) for module-shape - // inference, even when the declaration is nested inside a - // Block/IfStatement/etc. From an ESLint scope-analysis standpoint - // that binding is purely block-scoped — the inner 'block' scope - // already enumerates it, so adding it at the outer scope creates - // a phantom upper-scope binding and `no-shadow` fires on the - // inner declaration as if it shadowed itself. - // - // Filter: only skip if the symbol is a CommonJS Alias whose decl - // sits inside a nested sub-scope. `var` (FunctionScopedVariable / - // BlockScopedVariable) genuinely hoists to the function/global - // scope under JS semantics, so those entries must stay — `for - // (var a in xs)` at the file root would otherwise drop `a`'s - // file-scope reference and break `no-undef` / reference - // resolution. `function f()` in a block likewise hoists in - // sloppy JS, but TS binder treats it as block-scoped and doesn't - // put it in the outer locals so this code never sees it. - const SymbolFlags_Alias = ts.SymbolFlags.Alias; - const owner: ts.Node = this.tsNode; - const directlyOwned = (sym: ts.Symbol): boolean => { - if ((sym.flags & SymbolFlags_Alias) === 0) return true; - const decls = sym.declarations; - if (!decls || decls.length === 0) return true; - outer: for (const decl of decls) { - // Walk up from decl's parent. If we hit any sub-scope - // boundary before reaching `owner`, this decl is nested. - for (let cur: ts.Node | undefined = decl.parent; cur; cur = cur.parent) { - if (cur === owner) return true; - if (this._isOwnScopeBoundary(cur)) continue outer; - } - } - return false; - }; - - switch (this.type) { - case 'global': { - // In module mode the globalScope is empty — moduleScope owns the - // top-level decls. In script mode globalScope owns them. In - // either case TS's binder pre-hoists `var` (including - // `for (var x in …)`) into SourceFile.locals — pull those in too. - if (this.manager.isModule()) break; - const sf = this.tsNode as ts.SourceFile; - for (const stmt of sf.statements) this._collectStatementBindings(stmt, push, pushBinding); - const locals = (sf as { locals?: ts.SymbolTable }).locals; - if (locals) { - locals.forEach(sym => { - // Skip TS's synthetic `default` export symbol. - if (sym.name === 'default') return; - // Skip TS-internal escaped names (double-underscore - // prefix). These are NOT user-facing bindings — TS's - // binder uses them for `__global` (declare global - // augmentations), `__type` (anonymous type literals), - // `__call` / `__index` / `__new` (signatures), etc. - // `declare global { ... }` lifts a `__global` symbol - // into `sf.locals`; without skipping it here, every - // `declare global` block emits a fake - // `'__global' is defined but never used`. - if (sym.name.startsWith('__')) return; - if (sym.flags & (ts_.SymbolFlags.ValueModule | ts_.SymbolFlags.NamespaceModule)) return; - if (!directlyOwned(sym)) return; - push(sym); - }); - } - break; - } - case 'module': { - const sf = this.tsNode as ts.SourceFile; - for (const stmt of sf.statements) this._collectStatementBindings(stmt, push, pushBinding); - const locals = (sf as { locals?: ts.SymbolTable }).locals; - if (locals) { - locals.forEach(sym => { - if (sym.name === 'default') return; - // See `case 'global'`: TS-internal escaped names - // (`__global`, `__type`, `__call`, …) are not - // user-facing bindings. `declare global { ... }` - // in a module-mode file (any file with import/ - // export) lifts `__global` into `sf.locals`; the - // 'global' fix above doesn't apply here because - // module-mode files take this branch instead. - if (sym.name.startsWith('__')) return; - if (sym.flags & (ts_.SymbolFlags.ValueModule | ts_.SymbolFlags.NamespaceModule)) return; - if (!directlyOwned(sym)) return; - push(sym); - }); - } - break; - } - case 'function': { - const fn = this.tsNode as ts.FunctionLikeDeclaration; - // Synthetic `arguments` first (matches upstream order). - if (fn.kind !== SK.ArrowFunction) { - const argsVar = this._getOrCreateArgumentsVar(); - if (!seen.has(argsVar.symbol)) { - out.push(argsVar); - seen.add(argsVar.symbol); - } - } - // Parameters next. - if (fn.parameters) { - for (const p of fn.parameters) { - pushBinding(p.name); - // Parameter property (`constructor(public x: T)`): TS's - // binder produces TWO symbols on the same declaration — - // a Property symbol (flags=Property=4) returned by - // `getSymbolAtLocation(p.name)` and a function-scoped - // FunctionScopedVariable symbol (flags=1) returned by - // `getSymbolAtLocation` on a USE of the binding inside - // the constructor body. pushBinding registered the - // Property symbol; references inside the body resolve - // to the local symbol, miss `_variableBySymbol`, and - // fall through to `through` → no-undef false-positives - // every parameter-property binding. Alias the local - // symbol to the same TsVariable so resolution lands. - // Repro (real code): `class C { constructor(public - // readonly program: ts.Program) { program.x } }`. - const isParamProp = ts_.isParameter(p) - && p.modifiers?.some(m => - m.kind === SK.PublicKeyword || m.kind === SK.PrivateKeyword - || m.kind === SK.ProtectedKeyword || m.kind === SK.ReadonlyKeyword - || m.kind === SK.OverrideKeyword - ); - if (isParamProp && fn.body && ts_.isIdentifier(p.name)) { - const propSym = this.manager.checker.getSymbolAtLocation(p.name); - const localSym = this.manager.checker.getSymbolsInScope( - fn.body, - ts_.SymbolFlags.Variable, - ).find(s => s.name === (p.name as ts.Identifier).text); - if (propSym && localSym && propSym !== localSym) { - const v = this.manager._variableBySymbol.get(propSym); - if (v) this.manager._variableBySymbol.set(localSym, v); - } - } - } - } - // Walk the body in source order to preserve declaration - // ordering (matches upstream's referencer). Body statements - // + var-hoisted decls collapse into the function scope. - if (fn.body && ts_.isBlock(fn.body)) { - for (const stmt of fn.body.statements) { - this._collectStatementBindings(stmt, push, pushBinding); - } - } - // Pick up any var-hoisted decls TS recorded in fn.locals that - // the source-order walk missed (e.g. `var x` inside nested - // blocks of the function body). Same Alias-hoist filter as - // global/module: `const x = require('…')` nested in a sub- - // block is JS-CJS-aliased into fn.locals, but lexically - // belongs to its inner block scope. - const fnLocals = (fn as { locals?: ts.SymbolTable }).locals; - if (fnLocals) { - fnLocals.forEach(sym => { - if (!directlyOwned(sym)) return; - push(sym); - }); - } - break; - } - case 'function-expression-name': { - const fn = this.tsNode as ts.FunctionExpression; - if (fn.name) push(this.manager.checker.getSymbolAtLocation(fn.name)); - break; - } - case 'class': { - const cls = this.tsNode as ts.ClassLikeDeclaration; - if (cls.name) push(this.manager.checker.getSymbolAtLocation(cls.name)); - if (cls.typeParameters) { - for (const tp of cls.typeParameters) push(symOf(tp)); - } - break; - } - case 'block': { - // let/const/function/class declared directly in the block. var is - // excluded — it hoists to the enclosing function/global scope. - const block = this.tsNode as ts.Block; - for (const stmt of block.statements) { - if (ts_.isVariableStatement(stmt)) { - const flags = stmt.declarationList.flags; - if (flags & (ts_.NodeFlags.Let | ts_.NodeFlags.Const)) { - for (const d of stmt.declarationList.declarations) pushBinding(d.name); - } - continue; - } - this._collectStatementBindings(stmt, push, pushBinding); - } - break; - } - case 'for': { - const f = this.tsNode as ts.ForStatement | ts.ForInStatement | ts.ForOfStatement; - const initializer = (f as ts.ForStatement).initializer - ?? (f as ts.ForInStatement | ts.ForOfStatement).initializer; - if (initializer && ts_.isVariableDeclarationList(initializer)) { - for (const d of initializer.declarations) pushBinding(d.name); - } - break; - } - case 'catch': { - const c = this.tsNode as ts.CatchClause; - if (c.variableDeclaration) pushBinding(c.variableDeclaration.name); - break; - } - case 'switch': { - // `switch (…) { case X: let y = …; }` — let/const inside any - // case clause are scoped to the switch (single block scope per - // the spec). this.tsNode is the CaseBlock. - const cb = this.tsNode as ts.CaseBlock; - for (const clause of cb.clauses) { - for (const stmt of clause.statements) { - if (ts_.isVariableStatement(stmt)) { - const flags = stmt.declarationList.flags; - if (flags & (ts_.NodeFlags.Let | ts_.NodeFlags.Const)) { - for (const d of stmt.declarationList.declarations) pushBinding(d.name); - } - continue; - } - this._collectStatementBindings(stmt, push, pushBinding); - } - } - break; - } - case 'with': - break; - case 'tsEnum': { - const e = this.tsNode as ts.EnumDeclaration; - for (const m of e.members) push(symOf(m)); - break; - } - case 'tsModule': { - const mod = this.tsNode as ts.ModuleDeclaration; - const body = mod.body; - if (body && ts_.isModuleBlock(body)) { - for (const stmt of body.statements) this._collectStatementBindings(stmt, push, pushBinding); - } - break; - } - case 'type': { - const tps = (this.tsNode as { typeParameters?: ts.NodeArray }).typeParameters; - if (tps) { - for (const tp of tps) { - // Type parameters in `.d.ts` files can merge across - // redeclarations (TS lib types declared in both - // tsc's bundled `lib.*.d.ts` AND user-loaded - // `src/lib/*.d.ts` — TypeScript's own repo lints - // itself this way). Reference resolution at use- - // sites goes through `checker.getSymbolAtLocation` - // and returns the MERGED symbol. `tp.symbol` is the - // LOCAL one, so binding the var under `tp.symbol` - // makes `variableBySymbol.get(refSym)` miss and - // the ref leaks to `through` — no-undef false - // positive on the type parameter's own name. - // Resolving via the name node makes declaration - // and reference paths agree on the same symbol. - const sym = this.manager.checker.getSymbolAtLocation(tp.name) - ?? (tp as { symbol?: ts.Symbol }).symbol; - push(sym); - } - } - break; - } - case 'conditional-type': { - // `T extends ... infer U ...` — `infer U` introduces `U` - // into the conditional type's scope, accessible only from - // the trueType (and any nested conditionals therein). - // Upstream's TypeVisitor.TSInferType defines the type - // parameter on the enclosing conditional scope. Walk the - // extendsType and collect every InferTypeNode's name. - const cond = this.tsNode as ts.ConditionalTypeNode; - const collectInfer = (n: ts.Node) => { - if (ts_.isInferTypeNode(n)) { - push(symOf(n.typeParameter)); - } - ts_.forEachChild(n, collectInfer); - }; - if (cond.extendsType) collectInfer(cond.extendsType); - break; - } - case 'mapped-type': { - // Mapped types use a single `typeParameter` (not the plural array). - const tp = (this.tsNode as ts.MappedTypeNode).typeParameter; - if (tp) push(symOf(tp)); - break; - } - case 'function-type': { - // Function type signatures (FunctionType, MethodSignature, etc.): - // type params + parameter names are scoped here. Upstream's - // no-unused-vars treats their parameter names as variables. - const tps = (this.tsNode as { typeParameters?: ts.NodeArray }).typeParameters; - if (tps) { - for (const tp of tps) push(symOf(tp)); - } - const params = (this.tsNode as { parameters?: ts.NodeArray }).parameters; - if (params) { - for (const p of params) pushBinding(p.name); - } - break; - } - } - return out; - } - - _isOwnScopeBoundary(n: ts.Node): boolean { - const ts_ = ts; - const SK = ts_.SyntaxKind; - switch (n.kind) { - case SK.FunctionDeclaration: - case SK.FunctionExpression: - case SK.ArrowFunction: - case SK.MethodDeclaration: - case SK.Constructor: - case SK.GetAccessor: - case SK.SetAccessor: - case SK.ClassDeclaration: - case SK.ClassExpression: - case SK.ForStatement: - case SK.ForInStatement: - case SK.ForOfStatement: - case SK.CatchClause: - case SK.SwitchStatement: - case SK.WithStatement: - case SK.EnumDeclaration: - case SK.ModuleDeclaration: - case SK.InterfaceDeclaration: - case SK.TypeAliasDeclaration: - case SK.MappedType: - case SK.ConditionalType: - case SK.FunctionType: - case SK.ConstructorType: - case SK.CallSignature: - case SK.ConstructSignature: - case SK.MethodSignature: - return true; - case SK.Block: { - // Function bodies are not their own block scope (they ARE the function - // scope's body). Free-standing blocks are. - const p = n.parent; - if ( - p && ( - p.kind === SK.FunctionDeclaration - || p.kind === SK.FunctionExpression - || p.kind === SK.ArrowFunction - || p.kind === SK.MethodDeclaration - || p.kind === SK.Constructor - || p.kind === SK.GetAccessor - || p.kind === SK.SetAccessor - ) - ) { - return false; - } - return true; - } - } - return false; - } - - // Collect bindings from a statement that lives in this scope (i.e. block / - // global / module / tsModule scope). Does not recurse into nested scopes. - _collectStatementBindings( - stmt: ts.Statement, - push: (sym: ts.Symbol | undefined) => void, - pushBinding: (b: ts.BindingName | undefined) => void, - ) { - const ts_ = ts; - if (ts_.isVariableStatement(stmt)) { - // Module/global/block scope: only let/const/var (var hoists to function, - // but at top level it lives in the module/global scope). - for (const d of stmt.declarationList.declarations) pushBinding(d.name); - return; - } - if (ts_.isFunctionDeclaration(stmt) || ts_.isClassDeclaration(stmt)) { - if (stmt.name) { - const sym = this.manager.checker.getSymbolAtLocation(stmt.name); - // `export default function f` makes the symbol's name 'default'. - // Skip — the locals walk picks up the local `f` symbol instead. - if (sym && sym.name !== 'default') push(sym); - } - return; - } - if (ts_.isImportDeclaration(stmt)) { - const clause = stmt.importClause; - if (!clause) return; - if (clause.name) push(this.manager.checker.getSymbolAtLocation(clause.name)); - const bindings = clause.namedBindings; - if (bindings) { - if (ts_.isNamespaceImport(bindings)) { - push(this.manager.checker.getSymbolAtLocation(bindings.name)); - } - else { - for (const el of bindings.elements) { - push(this.manager.checker.getSymbolAtLocation(el.name)); - } - } - } - return; - } - if (ts_.isImportEqualsDeclaration(stmt)) { - // `import x = require('mod')` and `import x = OtherNS` — the name - // binds in module/global scope. - push(this.manager.checker.getSymbolAtLocation(stmt.name)); - return; - } - if (ts_.isEnumDeclaration(stmt) || ts_.isModuleDeclaration(stmt)) { - // `declare global { ... }` is an augmentation of the ambient - // global scope — its `name` is the synthetic `global` keyword - // (TS represents the symbol with the escaped name `__global`). - // scope-manager-upstream skips defineIdentifier on this branch - // (`Referencer.TSModuleDeclaration` returns early when - // `node.kind === 'global'`); without the parallel skip here, - // every `declare global` block in the codebase emits a fake - // `'__global' is defined but never used` diagnostic. - if ( - ts_.isModuleDeclaration(stmt) - && (stmt.flags & ts.NodeFlags.GlobalAugmentation) - ) { - return; - } - if (stmt.name && ts.isIdentifier(stmt.name)) { - push(this.manager.checker.getSymbolAtLocation(stmt.name)); - } - return; - } - if (ts_.isInterfaceDeclaration(stmt) || ts_.isTypeAliasDeclaration(stmt)) { - if (stmt.name) push(this.manager.checker.getSymbolAtLocation(stmt.name)); - return; - } - } - - _getOrCreateArgumentsVar(): TsVariable { - let v = this.manager._syntheticArguments.get(this); - if (!v) { - // Try to obtain TS's real `arguments` symbol so references to the - // identifier resolve to this same TsVariable. Fall back to a fake - // symbol for arrow functions and other edge cases (where the var - // won't actually be referenced anyway). - const fn = this.tsNode as ts.FunctionLikeDeclaration; - let sym: ts.Symbol | undefined; - const body = (fn as { body?: ts.Node }).body; - if (body) { - const found = this.manager.checker.getSymbolsInScope( - body, - ts.SymbolFlags.Variable, - ).find(s => s.name === 'arguments'); - if (found) sym = found; - } - sym ??= { name: 'arguments', declarations: [], flags: 0 } as unknown as ts.Symbol; - v = new TsVariable(this.manager, sym); - this.manager._syntheticArguments.set(this, v); - // Register so ref-index resolution finds it. - this.manager._variableBySymbol.set(sym, v); - } - return v; - } - - get set(): Map { - if (!this._set) { - this._set = new Map(); - for (const v of this.variables) this._set.set(v.name, v); - } - return this._set; - } - - get childScopes(): TsScope[] { - return this._childScopes; - } - - get references(): TsReference[] { - this.manager._ensureRefIndex(); - return this.manager._referencesByScope!.get(this) ?? []; - } - - get implicit(): { - variables: TsVariable[]; - left: TsReference[]; - leftToBeResolved: TsReference[]; - set: Map; - } { - // Only globalScope has meaningful implicit globals — references that - // escaped resolution and become implicit globals at runtime - // (e.g. `x = 300;` for undeclared `x`). - if (this.type === 'global') { - const through = this.through; - const implicits = this.manager.getImplicitGlobals(); - const set = new Map(); - for (const v of implicits) set.set(v.name, v); - return { - variables: implicits, - left: through, - leftToBeResolved: through, - set, - }; - } - return { variables: [], left: [], leftToBeResolved: [], set: new Map() }; - } - - // upstream: `eslint-scope/lib/scope.js` `Scope.through` — references - // that escape this scope after delegation up the parent chain. - // `no-undef` reads `globalScope.through` to report unresolved refs. - get through(): TsReference[] { - // Computed lazily; not cached because scope.variables is mutable - // via addGlobals. - // `isLocal` looks up the ref's ts.Symbol via `_variableBySymbol` → - // TsVariable, then checks if that var is in this scope. This handles - // alias symbols (e.g. synthetic `arguments`: ref.symbol from - // `getSymbolAtLocation` differs from `v.symbol` from - // `getSymbolsInScope`, but both map to the same var). Lib vars and - // the addGlobals fakes register in `_variableBySymbol` too, so - // type-position refs to `Map` / `Set` / `Object` resolve to global. - const out: TsReference[] = []; - const localVars = new Set(); - for (const v of this.variables) localVars.add(v); - const isLocal = (sym: ts.Symbol | undefined) => { - if (!sym) return false; - const v = this.manager._variableBySymbol.get(sym); - return v !== undefined && localVars.has(v); - }; - for (const ref of this.references) { - if (!isLocal(ref.symbol)) out.push(ref); - } - for (const child of this.childScopes) { - // Skip function-expression-name (its lookup is the wrapped name's - // own scope and not really visible to ancestors). - for (const ref of child.through) { - if (!isLocal(ref.symbol)) out.push(ref); - } - } - return out; - } -} - -// upstream: `@typescript-eslint/scope-manager/dist/variable/Variable.js` -// (which extends `VariableBase.js`). Public API: -// `name`, `scope`, `defs[]`, `identifiers[]`, `references[]`, -// `writeable`, `isValueVariable`, `isTypeVariable`. -// -// Upstream's `defs` is populated eagerly during the Referencer walk — -// each declaration occurrence pushes one Definition into `defs`. -// We materialise on first access from `symbol.declarations` (TS -// already stores them on the Symbol). The two paths diverge for: -// - lib-source declarations: filtered out (see `defs` getter); they -// materialise to `GenericTSNode(parent=null)` and crash rules -// reading `def.node.parent.type`. -// - declaration merging: TS merges multiple decls into one Symbol; -// upstream creates one Definition per visit. Same shape. -export class TsVariable { - _defs?: TsDefinition[]; - _references?: TsReference[]; - _identifiers?: TSESTree.Identifier[]; - // Extra symbols merged into this variable — occurs when TS uses - // separate symbols for the same lexical name (e.g. `function foo` + - // `var foo` creates two distinct symbols in the checker, but ESLint - // scope-manager sees one Variable with two Definitions). - _mergedSymbols?: ts.Symbol[]; - eslintUsed = false; - eslintExported = false; - eslintExplicitGlobal = false; - eslintExplicitGlobalComments = undefined as undefined | unknown[]; - eslintImplicitGlobalSetting = undefined as undefined | string; - - constructor(readonly manager: TsScopeManager, readonly symbol: ts.Symbol) {} - - get name(): string { - // `export default function f() {}` — TS gives the function's symbol - // the synthetic name 'default'. Local-scope variable name should be - // the declaration's identifier (`f`). - if (this.symbol.name === 'default') { - const decls = this.symbol.declarations; - if (decls && decls.length > 0) { - const nameNode = (decls[0] as { name?: { text?: string } }).name; - if (nameNode?.text) return nameNode.text; - } - } - return this.symbol.name; - } - - private _scope?: TsScope; - get scope(): TsScope { - if (this._scope) return this._scope; - const decl = this.symbol.declarations?.[0]; - if (!decl) return this._scope = this.manager.globalScope; - const ts_ = ts; - const SK = ts_.SyntaxKind; - // Special-case: a named function expression's name binding lives in - // its own `function-expression-name` scope (the wrapper around the - // inner `function` body scope). The decl IS the FunctionExpression - // itself — for body-local bindings (parameter, body let/const) the - // decl is a Parameter / VariableDeclaration whose parent walk - // reaches the FunctionExpression from below, so they land in the - // inner `function` scope as expected. - // - // Without this special-case `no-shadow.isOnInitializer` sees the - // inner `resolve` of `const resolve = function resolve(...)` as - // living in the body scope (one too deep) — `outerScope === - // innerScope.upper` then compares the outer body scope against the - // `function-expression-name` scope and fails, so the rule emits a - // false-positive shadow report on every named function expression - // that shares its name with the surrounding declarator. - if ( - ts_.isFunctionExpression(decl) - && decl.name - && this.symbol.name === decl.name.text - ) { - const arr = this.manager.nodeToScope.get(decl); - if (arr) { - for (const s of arr) { - if (s.type === 'function-expression-name') return this._scope = s; - } - } - } - // Walk parents until we hit a scope-creating node. - for (let cur: ts.Node | undefined = decl; cur; cur = cur.parent) { - const arr = this.manager.nodeToScope.get(cur); - if (arr && arr.length > 0) { - // Pick the innermost non-fn-expr-name scope. - for (let i = arr.length - 1; i >= 0; --i) { - if (arr[i].type !== 'function-expression-name') return this._scope = arr[i]; - } - } - if (cur.kind === SK.SourceFile) break; - } - return this._scope = this.manager.globalScope; - } - - _defsOverride?: TsDefinition[]; - _identifiersOverride?: TSESTree.Identifier[]; - - // upstream: `Variable.js` `defs` (populated eagerly in - // `ScopeBase.defineVariable`). One Definition per declaration - // occurrence. - get defs(): TsDefinition[] { - if (this._defsOverride) return this._defsOverride; - if (!this._defs) { - let decls = this.symbol.declarations ?? []; - // Include declarations from merged symbols (e.g. `function foo` - // + `var foo` create two TS symbols for the same name). - if (this._mergedSymbols) { - for (const ms of this._mergedSymbols) { - if (ms.declarations) decls = decls.concat(ms.declarations); - } - } - // Filter out declarations that aren't in the user's source file - // (e.g. TS lib declarations for `Map`, `Set`, `Promise`). - // `materialize()` can't reach those — convertLazy only - // pre-registers the user's SourceFile — so they fall back to - // `GenericTSNode(parent=null)`. Rules that read - // `def.node.parent.type` (naming-convention's `collectVariables`, - // no-unused-vars, no-redeclare) crash on the null parent. - // Upstream models the same symbols as `ImplicitLibVariable` with - // no defs; mirror that for lib-sourced declarations. - const userFile = this.manager.tsFile; - this._defs = decls - .filter(d => d.getSourceFile() === userFile) - .map(d => new TsDefinition(this.manager, this, d)); - } - return this._defs; - } - - get identifiers(): TSESTree.Identifier[] { - if (this._identifiersOverride) return this._identifiersOverride; - if (!this._identifiers) { - this._identifiers = this.defs.map(d => d.name).filter(Boolean) as TSESTree.Identifier[]; - } - return this._identifiers; - } - - get references(): TsReference[] { - if (!this._references) { - this._references = this.manager.getReferencesFor(this.symbol); - } - return this._references; - } - - get writeable(): boolean { - const decls = this.symbol.declarations ?? []; - for (const d of decls) { - const ts_ = ts; - if (ts_.isVariableDeclaration(d)) { - const list = d.parent; - if (ts_.isVariableDeclarationList(list)) { - if (list.flags & ts_.NodeFlags.Const) return false; - } - } - if (ts_.isImportSpecifier(d) || ts_.isImportClause(d) || ts_.isNamespaceImport(d)) return false; - if (ts_.isFunctionDeclaration(d) || ts_.isClassDeclaration(d) || ts_.isEnumDeclaration(d)) return false; - } - return true; - } - - get isValueVariable(): boolean { - // Match upstream's `Variable.isValueVariable`: `defs.some(d => - // d.isVariableDefinition)`. Definition flags per upstream's - // per-DefinitionType subclasses: - // ImportBinding / Variable / TSEnumName / FunctionName / Parameter / - // CatchClause / TSModuleName / ClassName / TSEnumMember / - // ImplicitGlobalVariable → isVariableDefinition = true - // Type → isVariableDefinition = false - // Reading symbol.flags directly misses ImportBinding (TS marks aliases - // without `Value` until you resolve the alias); walking defs[] gives - // the same answer no-shadow's `isTypeValueShadow` expects. - if (this.defs.length === 0) return true; // implicit lib var (no static info) - return this.defs.some(d => d.type !== 'Type'); - } - - get isTypeVariable(): boolean { - // Match upstream's `Variable.isTypeVariable`: `defs.some(d => - // d.isTypeDefinition)`. Per upstream: - // ImportBinding / TSEnumName / Type / TSModuleName / ClassName / - // TSEnumMember → isTypeDefinition = true - // Variable / FunctionName / Parameter / CatchClause / - // ImplicitGlobalVariable → isTypeDefinition = false - if (this.defs.length === 0) return true; // implicit lib var - const TYPE_DEF_TYPES = new Set([ - 'ImportBinding', - 'TSEnumName', - 'Type', - 'TSModuleName', - 'ClassName', - 'TSEnumMember', - ]); - return this.defs.some(d => TYPE_DEF_TYPES.has(d.type)); - } -} - -// upstream: `@typescript-eslint/scope-manager/dist/referencer/Reference.js`. -// Public API: -// `identifier` (the Identifier ESTree node), `from` (containing -// scope), `resolved` (Variable or null), `init` (declaration init? -// tristate true/false/undefined), `writeExpr`, -// `isWrite()` / `isRead()` / `isReadWrite()` / `isWriteOnly()` / -// `isReadOnly()`, `isValueReference` / `isTypeReference`. -// -// Constructed lazily during `_ensureRefIndex` (one per pending -// Identifier with FREE_REF=1). Upstream constructs eagerly in the -// Referencer's `referenceValue` / `referenceType` calls. -export class TsReference { - _estreeIdent?: TSESTree.Identifier; - - constructor( - readonly manager: TsScopeManager, - readonly tsIdentifier: ts.Identifier, - readonly symbol: ts.Symbol, - ) {} - - get identifier(): TSESTree.Identifier { - if (this._estreeIdent) return this._estreeIdent; - const real = this.manager.tsToEstreeOrStub(this.tsIdentifier); - // Real lazy Identifier — has `name` and a proper parent chain. - if (real && (real as { name?: string }).name) { - return this._estreeIdent = real; - } - // Fallback: tsToEstreeOrStub returned a generic stub (e.g. when the - // identifier sits inside an unsupported kind and materialise fell - // through). Construct the Identifier shape directly with the text, - // and let the stub's parent chain (if any) attach. - const stub: TSESTree.Identifier = { - type: 'Identifier', - name: this.tsIdentifier.text, - range: [this.tsIdentifier.getStart(), this.tsIdentifier.end], - } as TSESTree.Identifier; - if (real && (real as { parent?: object }).parent) { - (stub as { parent?: object }).parent = (real as { parent?: object }).parent; - } - return this._estreeIdent = stub; - } - - private _from?: TsScope; - get from(): TsScope { - if (this._from) return this._from; - const SK = ts.SyntaxKind; - // Computed property names (`class { [expr]() {} }`, `{ [expr]: … }`) - // are evaluated in the OUTER scope, not the method/property scope — - // upstream visits the key before entering the function. Detect this - // by checking for a ComputedPropertyName ancestor and, if so, skip - // past the enclosing method's function scope. - let skipMethodScope = false; - // Inclusive: the identifier itself can be a scope-creating node - // (e.g. class-field-initializer where the scope's block IS the - // initializer expression — possibly the identifier being referenced). - for (let cur: ts.Node | undefined = this.tsIdentifier; cur; cur = cur.parent) { - if (cur.kind === SK.ComputedPropertyName) { - skipMethodScope = true; - } - const arr = this.manager.nodeToScope.get(cur); - if (arr && arr.length > 0) { - for (let i = arr.length - 1; i >= 0; --i) { - const s = arr[i]; - if (s.type === 'function-expression-name') continue; - if (skipMethodScope && s.type === 'function') { - // Skip this method/getter/setter scope — the computed - // key reference belongs in the enclosing class/outer - // scope. - skipMethodScope = false; - continue; - } - return this._from = s; - } - } - if (cur.kind === SK.SourceFile) break; - } - return this._from = this.manager.globalScope; - } - - get resolved(): TsVariable | null { - // Resolved iff the symbol points to a variable declared in our scope - // tree. Through refs (lib globals, undeclared) → null. - if (!this.symbol) return null; - const local = this.manager._variableBySymbol.get(this.symbol); - if (local) return local; - const lib = this.manager._libVariableBySymbol.get(this.symbol); - return lib ?? null; - } - - // Identifier IS the declaration name (binding position), not just a usage - // of the symbol. Used internally by `init` / `isWrite` — cached so - // the decl walk doesn't repeat. - private _isBindingIdent?: boolean; - get _isBindingIdentifier(): boolean { - if (this._isBindingIdent !== undefined) return this._isBindingIdent; - const decls = this.symbol?.declarations ?? []; - const SK = ts.SyntaxKind; - for (const d of decls) { - // TS adds ExportSpecifier / ImportSpecifier as "declarations" of the - // underlying symbol via aliasing. Those identifiers reference the - // declared variable; they aren't fresh bindings, so don't count - // them as init positions. - if (d.kind === SK.ExportSpecifier || d.kind === SK.ImportSpecifier) continue; - if ((d as { name?: ts.Node }).name === this.tsIdentifier) return this._isBindingIdent = true; - } - return this._isBindingIdent = false; - } - - get init(): boolean | undefined { - // ESLint's `init` tristate: - // - true → declaration-with-initializer (`let x = expr`) - // - false → non-init write (`x = expr`) - // - undefined → pure read. - if (this._isBindingIdentifier) return true; - return this.isWrite() ? false : undefined; - } - - isWrite(): boolean { - const SK = ts.SyntaxKind; - const id = this.tsIdentifier; - const parent = id.parent; - if (this._isBindingIdentifier) return true; - if (!parent) return false; - // Direct assignment: `x = …`, `x += …`. - if (parent.kind === SK.BinaryExpression && (parent as ts.BinaryExpression).left === id) { - const op = (parent as ts.BinaryExpression).operatorToken.kind; - return op === SK.EqualsToken - || (op >= SK.FirstCompoundAssignment && op <= SK.LastCompoundAssignment); - } - // Update: `x++` / `++x`. - if (parent.kind === SK.PrefixUnaryExpression || parent.kind === SK.PostfixUnaryExpression) { - const op = (parent as ts.PrefixUnaryExpression | ts.PostfixUnaryExpression).operator; - return op === SK.PlusPlusToken || op === SK.MinusMinusToken; - } - // Destructuring assignment: `[a] = …`, `({a} = …)`, etc. Walk up - // through pattern wrappers (ArrayLiteralExpression / ObjectLiteralExpression - // /SpreadElement / Property assignments / Parens) until we hit either - // a `=` BinaryExpression (write) or anything else (not a write). - for (let cur: ts.Node | undefined = parent; cur; cur = cur.parent) { - switch (cur.kind) { - case SK.ArrayLiteralExpression: - case SK.ObjectLiteralExpression: - case SK.SpreadElement: - case SK.SpreadAssignment: - case SK.PropertyAssignment: - case SK.ShorthandPropertyAssignment: - case SK.ParenthesizedExpression: - continue; - case SK.BinaryExpression: { - const op = (cur as ts.BinaryExpression).operatorToken.kind; - if (op !== SK.EqualsToken) return false; - return (cur as ts.BinaryExpression).left.pos <= id.pos - && (cur as ts.BinaryExpression).left.end >= id.end; - } - case SK.ForInStatement: - case SK.ForOfStatement: - return (cur as ts.ForInStatement | ts.ForOfStatement).initializer.pos <= id.pos - && (cur as ts.ForInStatement | ts.ForOfStatement).initializer.end >= id.end; - default: - return false; - } - } - return false; - } - - isRead(): boolean { - if (this._isBindingIdentifier) return false; - if (!this.isWrite()) return true; - const parent = this.tsIdentifier.parent; - const ts_ = ts; - // Compound assignments (`+=`, `-=`, etc.) read+write. - if (parent && ts_.isBinaryExpression(parent) && parent.left === this.tsIdentifier) { - return parent.operatorToken.kind !== ts_.SyntaxKind.EqualsToken; - } - // `x++` / `++x` / `x--` / `--x` read+write. - if (parent && (ts_.isPrefixUnaryExpression(parent) || ts_.isPostfixUnaryExpression(parent))) { - const op = parent.operator; - return op === ts_.SyntaxKind.PlusPlusToken || op === ts_.SyntaxKind.MinusMinusToken; - } - return false; - } - - isWriteOnly(): boolean { - return this.isWrite() && !this.isRead(); - } - isReadOnly(): boolean { - return this.isRead() && !this.isWrite(); - } - isReadWrite(): boolean { - return this.isRead() && this.isWrite(); - } - - get writeExpr(): TSESTree.Node | null | undefined { - // For an init reference (`let x = expr`, `function f(b = expr)`), - // returns the initializer expression. For an assignment (`x = expr`, - // `x += expr`), returns the right-hand side. For pure reads, returns - // undefined (matches ESLint's distinction: present-but-null vs absent). - const SK = ts.SyntaxKind; - const id = this.tsIdentifier; - const parent = id.parent; - if (!parent) return undefined; - // VariableDeclaration / Parameter init. - if ( - (parent.kind === SK.VariableDeclaration && (parent as ts.VariableDeclaration).name === id) - || (parent.kind === SK.Parameter && (parent as ts.ParameterDeclaration).name === id) - ) { - const init = (parent as { initializer?: ts.Node }).initializer; - return init ? this.manager.tsToEstreeOrStub(init) ?? null : undefined; - } - // BindingElement (destructured init): use the enclosing VariableDeclaration's initializer. - if (parent.kind === SK.BindingElement && (parent as ts.BindingElement).name === id) { - for (let cur: ts.Node | undefined = parent; cur; cur = cur.parent) { - if (cur.kind === SK.VariableDeclaration) { - const init = (cur as ts.VariableDeclaration).initializer; - return init ? this.manager.tsToEstreeOrStub(init) ?? null : undefined; - } - if (cur.kind === SK.Parameter) { - const init = (cur as ts.ParameterDeclaration).initializer; - return init ? this.manager.tsToEstreeOrStub(init) ?? null : undefined; - } - } - return undefined; - } - // Assignment: x = rhs, x += rhs. - if (parent.kind === SK.BinaryExpression && (parent as ts.BinaryExpression).left === id) { - const op = (parent as ts.BinaryExpression).operatorToken.kind; - if ( - op === SK.EqualsToken - || (op >= SK.FirstCompoundAssignment && op <= SK.LastCompoundAssignment) - ) { - return this.manager.tsToEstreeOrStub((parent as ts.BinaryExpression).right) ?? null; - } - } - return undefined; - } - - get isValueReference(): boolean { - const ts_ = ts; - for (let cur: ts.Node | undefined = this.tsIdentifier.parent; cur; cur = cur.parent) { - if (ts_.isTypeNode(cur)) return false; - if (ts_.isExpression(cur)) return true; - } - return true; - } - - get isTypeReference(): boolean { - return !this.isValueReference; - } -} - -// upstream: `@typescript-eslint/scope-manager/dist/definition/`'s -// per-DefinitionType subclasses (VariableDefinition, ParameterDefinition, -// ImportBindingDefinition, TypeDefinition, …). Each upstream subclass -// hard-codes `isTypeDefinition` and `isVariableDefinition` booleans — -// see `Variable.isValueVariable` / `isTypeVariable` for how those -// flags compose. -// -// Public API: `type`, `name`, `node`, `parent`, `rest`, -// `isTypeDefinition`, `isVariableDefinition`. `node` and `parent` -// are the rule-facing accessors that flow through -// `tsToEstreeOrStub` → `materialize`. Both must yield ESTree nodes -// with proper `.parent` chains; rules read `def.node.parent.type` -// (no-redeclare, naming-convention's collectVariables, no-shadow's -// `isTypeValueShadow`) and a null parent here means a crash. -export class TsDefinition { - constructor( - readonly manager: TsScopeManager, - readonly variable: TsVariable, - readonly tsDeclaration: ts.Node, - ) {} - - // True for `function(...rest)` and `function([a, ...rest])` rest binding. - get rest(): boolean { - const d = this.tsDeclaration; - if (ts.isParameter(d)) return d.dotDotDotToken !== undefined; - if (ts.isBindingElement(d)) return d.dotDotDotToken !== undefined; - return false; - } - - get type(): DefinitionType { - const ts_ = ts; - const d = this.tsDeclaration; - // BindingElement (destructured name): walk up to the enclosing - // declaration to decide whether this is a Parameter or Variable def. - if (ts_.isBindingElement(d)) { - for (let cur: ts.Node | undefined = d.parent; cur; cur = cur.parent) { - if (ts_.isParameter(cur)) return 'Parameter'; - if (ts_.isVariableDeclaration(cur)) { - return cur.parent && ts_.isCatchClause(cur.parent) ? 'CatchClause' : 'Variable'; - } - } - return 'Variable'; - } - if (ts_.isVariableDeclaration(d)) { - // In TS, catch params are modeled as VariableDeclaration whose parent is - // CatchClause; ESLint treats those as CatchClauseDefinition. - if (d.parent && ts_.isCatchClause(d.parent)) return 'CatchClause'; - return 'Variable'; - } - if (ts_.isParameter(d)) return 'Parameter'; - if (ts_.isFunctionDeclaration(d) || ts_.isFunctionExpression(d) || ts_.isArrowFunction(d)) return 'FunctionName'; - if (ts_.isClassDeclaration(d) || ts_.isClassExpression(d)) return 'ClassName'; - if ( - ts_.isImportSpecifier(d) - || ts_.isImportClause(d) - || ts_.isNamespaceImport(d) - || ts_.isImportEqualsDeclaration(d) - ) return 'ImportBinding'; - if (ts_.isCatchClause(d)) return 'CatchClause'; - if (ts_.isEnumDeclaration(d)) return 'TSEnumName'; - if (ts_.isEnumMember(d)) return 'TSEnumMember'; - if (ts_.isModuleDeclaration(d)) return 'TSModuleName'; - if (ts_.isTypeAliasDeclaration(d) || ts_.isInterfaceDeclaration(d) || ts_.isTypeParameterDeclaration(d)) { - return 'Type'; - } - return 'Variable'; - } - - get name(): TSESTree.Identifier | undefined { - const nameNode = (this.tsDeclaration as { name?: ts.Node }).name; - if (!nameNode) return undefined; - return this.manager.tsToEstreeOrStub(nameNode); - } - - get node(): TSESTree.Node | undefined { - // For Variable defs, ESLint expects the VariableDeclarator (TS: - // VariableDeclaration). For destructured names whose declaration is a - // BindingElement / pattern, walk up until we find the declarator. - const ts_ = ts; - let target: ts.Node = this.tsDeclaration; - while ( - target - && (ts_.isBindingElement(target) - || ts_.isObjectBindingPattern(target) - || ts_.isArrayBindingPattern(target)) - ) { - target = target.parent; - } - // Parameter: ESLint expects `node` to be the enclosing function-like. - if (target && ts_.isParameter(target) && target.parent) { - target = target.parent; - } - // Catch param: TS models as VariableDeclaration under CatchClause; ESLint - // expects the CatchClause itself as the def's node. - if (target && ts_.isVariableDeclaration(target) && target.parent && ts_.isCatchClause(target.parent)) { - target = target.parent; - } - const result = this.manager.tsToEstreeOrStub(target); - // Unwrap export wrapper: `export function f` materializes to - // ExportNamedDeclaration containing the FunctionDeclaration. ESLint - // expects `def.node` to be the inner declaration so rules like - // `no-shadow` (`isFunctionTypeParameterNameValueShadow` checks - // `def.node.type === 'TSDeclareFunction'`) work on overload sigs. - if ( - result && ( - (result as { type?: string }).type === 'ExportNamedDeclaration' - || (result as { type?: string }).type === 'ExportDefaultDeclaration' - ) - ) { - const inner = (result as { declaration?: TSESTree.Node }).declaration; - if (inner) return inner; - } - return result; - } - - get parent(): TSESTree.Node | undefined { - // For VariableDeclarator, parent is VariableDeclaration (TS: - // VariableDeclarationList → parent is VariableStatement). - const ts_ = ts; - let target: ts.Node = this.tsDeclaration; - while ( - target - && (ts_.isBindingElement(target) - || ts_.isObjectBindingPattern(target) - || ts_.isArrayBindingPattern(target)) - ) { - target = target.parent; - } - // VariableDeclaration → parent is VariableDeclarationList → parent is - // VariableStatement (which corresponds to ESTree VariableDeclaration). - // In for-of / for-in / for-init position the VariableDeclarationList - // itself IS the ESTree VariableDeclaration (mapped via - // VariableDeclarationListAsNode), so the def's parent must point at - // the list — NOT at the enclosing ForOfStatement / ForInStatement / - // ForStatement. ESLint's no-loop-func reads `definition.parent.kind` - // to skip block-scoped `let` / `const` bindings; getting this wrong - // produces false positives on every block-scoped iteration variable. - if (target && ts_.isVariableDeclaration(target)) { - const list = target.parent; - if (list && ts_.isVariableDeclarationList(list)) { - const owner = list.parent; - if (owner && ts_.isVariableStatement(owner)) { - return this.manager.tsToEstreeOrStub(owner); - } - return this.manager.tsToEstreeOrStub(list); - } - } - // Import bindings: TS nests `ImportSpecifier → NamedImports → - // ImportClause → ImportDeclaration`, but ESTree skips NamedImports - // and ImportClause — `ImportSpecifier.parent` IS `ImportDeclaration`. - // no-shadow's `isTypeValueShadow` reads - // `def.parent.type === 'ImportDeclaration'` and - // `def.parent.specifiers.some(s => s.importKind === 'type')` to - // detect any-specifier-type imports; without unwrapping we hand - // back the synthetic `TSNamedImports` and the check silently - // fails → 19 no-shadow false positives in TS repo's - // `src/compiler/utilities.ts` whose import block has type-only - // specifiers. - if (target && (ts_.isImportSpecifier(target) || ts_.isNamespaceImport(target) || ts_.isImportClause(target))) { - let cur: ts.Node = target.parent; - while (cur && !ts_.isImportDeclaration(cur)) cur = cur.parent; - if (cur) return this.manager.tsToEstreeOrStub(cur); - } - return this.manager.tsToEstreeOrStub(target?.parent); - } - - get isVariableDefinition(): boolean { - return this.type === 'Variable' || this.type === 'Parameter'; - } - get isTypeDefinition(): boolean { - return this.type === 'Type'; - } -} - -// Synthetic def for implicit globals — `x = 1;` for an undeclared `x`. -// `tsDeclaration` is the first write reference's identifier (a stand-in -// for a real declaration node). -export class TsImplicitGlobalDefinition extends TsDefinition { - get type(): DefinitionType { - return 'ImplicitGlobalVariable'; - } - get name(): TSESTree.Identifier | undefined { - return this.manager.tsToEstreeOrStub(this.tsDeclaration); - } - get node(): TSESTree.Node | undefined { - return this.name; - } - get parent(): TSESTree.Node | undefined { - return undefined; - } - get isVariableDefinition(): boolean { - return true; - } - get isTypeDefinition(): boolean { - return false; - } -} - -// Declares ECMAScript built-ins (es2026 from ESLint's `conf/globals.js`) -// plus TS `lib.*.d.ts` type globals on the manager's `globalScope`. -// Called by the compat-eslint lint pipeline so `no-undef` doesn't fire on -// `undefined` / `Math` / `Promise` / `Record` / etc. — names that -// `@typescript-eslint/scope-manager`'s lib data marks TYPE-only after -// merging. -// -// Kept as a free function (not a `TsScopeManager` method) because this is -// a lint-pipeline policy, not a scope-analysis fact: upstream -// `eslint-scope` parity tests construct managers without it and would -// fail if it ran inside the constructor. -export function applyEslintGlobals(manager: TsScopeManager): void { - manager.addGlobals(ESLINT_BUILTIN_GLOBALS as string[]); - manager.addGlobals(TS_LIB_TYPE_GLOBALS as string[]); -} diff --git a/packages/compat-eslint/lib/ts.ts b/packages/compat-eslint/lib/ts.ts new file mode 100644 index 00000000..a700f4a6 --- /dev/null +++ b/packages/compat-eslint/lib/ts.ts @@ -0,0 +1,97 @@ +// The TypeScript surface compat-eslint is written against: the public +// TypeScript 7 AST module plus a few helpers the classic `typescript` +// namespace used to provide, rebuilt on top of the public node API. +import * as ast from 'typescript/ast'; + +export * from 'typescript/ast'; + +// TS 7 materializes nodes that have no source text of their own (JSDoc +// type annotations in JS files, the implicit `export` on the inner +// declarations of `namespace A.B {}`) and marks them `Reparsed`. ESTree +// has no counterpart for them, so every traversal here skips them. +export function isReparsed(node: ast.Node): boolean { + return (node.flags & ast.NodeFlags.Reparsed) !== 0; +} + +export function forEachChild( + node: ast.Node, + cbNode: (node: ast.Node) => T, + cbNodes?: (nodes: ast.NodeArray) => T, +): T | undefined { + return node.forEachChild( + child => isReparsed(child) ? undefined : cbNode(child), + cbNodes + ? nodes => cbNodes(nodes.some(isReparsed) ? filterNodeArray(nodes) : nodes) + : undefined, + ); +} + +function filterNodeArray(nodes: ast.NodeArray): ast.NodeArray { + const filtered = nodes.filter(n => !isReparsed(n)) as T[] & { pos?: number; end?: number }; + filtered.pos = nodes.pos; + filtered.end = nodes.end; + return filtered as unknown as ast.NodeArray; +} + +export function getModifierLikes(node: ast.Node): readonly ast.ModifierLike[] | undefined { + const modifiers = (node as { modifiers?: ast.NodeArray }).modifiers; + if (!modifiers?.length) return undefined; + return modifiers.some(isReparsed) ? modifiers.filter(m => !isReparsed(m)) : modifiers; +} + +export function getModifiers(node: ast.Node): ast.Modifier[] | undefined { + const modifiers = getModifierLikes(node); + if (!modifiers) return undefined; + const result = modifiers.filter((m): m is ast.Modifier => m.kind !== ast.SyntaxKind.Decorator); + return result.length ? result : undefined; +} + +export function getDecorators(node: ast.Node): ast.Decorator[] | undefined { + const modifiers = getModifierLikes(node); + if (!modifiers) return undefined; + const result = modifiers.filter(ast.isDecorator); + return result.length ? result : undefined; +} + +export function hasQuestionToken(node: { postfixToken?: ast.Node }): boolean { + return node.postfixToken?.kind === ast.SyntaxKind.QuestionToken; +} + +export function hasExclamationToken(node: { postfixToken?: ast.Node }): boolean { + return node.postfixToken?.kind === ast.SyntaxKind.ExclamationToken; +} + +// `declare global {}` is a ModuleDeclaration whose name is the identifier +// `global` with no `module`/`namespace` keyword written before it. +export function isGlobalAugmentation(node: ast.ModuleDeclaration, sourceText: string): boolean { + if (node.name.kind !== ast.SyntaxKind.Identifier || node.name.text !== 'global') return false; + const modifiers = getModifierLikes(node); + const before = modifiers ? modifiers[modifiers.length - 1].end : node.pos; + return sourceText.slice(before, node.name.getStart()).trim() === ''; +} + +// `implements Foo` parses as a TypeReference in TS 7; `extends Foo` +// stays an ExpressionWithTypeArguments. +export function heritageElementExpression(element: ast.HeritageClauseElement): ast.Node { + return element.kind === ast.SyntaxKind.TypeReference ? element.typeName : element.expression; +} + +export function isTypeOnlyImportClause(clause: ast.ImportClause | undefined): boolean { + return clause?.phaseModifier === ast.SyntaxKind.TypeKeyword; +} + +// A TypeReference that is really a heritage clause element +// (`interface I extends A.B`, `class C implements A.B`). +export function isHeritageTypeReference(node: ast.Node): node is ast.TypeReferenceNode { + return node.kind === ast.SyntaxKind.TypeReference && node.parent?.kind === ast.SyntaxKind.HeritageClause; +} + +// A QualifiedName in the entity name of a heritage TypeReference. +// typescript-estree reads heritage elements as expressions, so these +// surface as MemberExpressions rather than TSQualifiedNames. +export function isHeritageQualifiedName(node: ast.Node): node is ast.QualifiedName { + if (node.kind !== ast.SyntaxKind.QualifiedName) return false; + let current = node; + while (current.parent?.kind === ast.SyntaxKind.QualifiedName) current = current.parent; + return !!current.parent && isHeritageTypeReference(current.parent); +} diff --git a/packages/compat-eslint/package.json b/packages/compat-eslint/package.json index ee74153b..d42f111e 100644 --- a/packages/compat-eslint/package.json +++ b/packages/compat-eslint/package.json @@ -16,22 +16,20 @@ "scripts": { "bench": "tsc --build && node test/bench/run.js", "test:predicate-coverage": "tsc --build && node test/predicate-coverage.test.js", - "test:jsx-react-x": "tsc --build && node test/jsx-react-x.test.mjs", - "test:parser-options-gap": "tsc --build && node test/parser-options-gap.test.js", - "test:upstream": "tsc --build && node test/upstream-runner.js" + "test:parser-options-gap": "tsc --build && node test/parser-options-gap.test.js" }, "devDependencies": { + "@tsslint/estree-oracle": "workspace:*", "@types/eslint": "^8.56.10", - "@typescript-eslint/scope-manager": "^8.16.0", - "@typescript-eslint/typescript-estree": "^8.16.0", "eslint": ">=9.39.0 <10.0.0", - "typescript": "latest" + "typescript": "7.1.0-beta" }, "peerDependencies": { - "typescript": "*" + "typescript": "^7.1.0-beta" }, "dependencies": { "@tsslint/types": "3.1.4", + "@typescript-eslint/scope-manager": "^8.72.0", "esquery": "^1.5.0" } } diff --git a/packages/compat-eslint/test/bench/dogfood-corpus.ts b/packages/compat-eslint/test/bench/dogfood-corpus.ts index 5491a73e..e07f6757 100644 --- a/packages/compat-eslint/test/bench/dogfood-corpus.ts +++ b/packages/compat-eslint/test/bench/dogfood-corpus.ts @@ -16,11 +16,12 @@ export const DOGFOOD_FILES = [ 'packages/cli/lib/render.ts', 'packages/cli/lib/worker.ts', 'packages/compat-eslint/index.ts', + 'packages/compat-eslint/lib/globals.ts', 'packages/compat-eslint/lib/lazy-estree.ts', 'packages/compat-eslint/lib/selector-analysis.ts', 'packages/compat-eslint/lib/tokens.ts', + 'packages/compat-eslint/lib/ts.ts', 'packages/compat-eslint/lib/ts-ast-scan.ts', - 'packages/compat-eslint/lib/ts-scope-manager.ts', 'packages/compat-eslint/lib/visitor-keys.ts', 'packages/config/index.ts', 'packages/config/lib/eslint-gen.ts', @@ -29,13 +30,8 @@ export const DOGFOOD_FILES = [ 'packages/config/lib/plugins/category.ts', 'packages/config/lib/plugins/diagnostics.ts', 'packages/config/lib/plugins/ignore.ts', - 'packages/config/lib/tsl.ts', - 'packages/config/lib/tslint-gen.ts', - 'packages/config/lib/tslint-types.ts', - 'packages/config/lib/tslint.ts', 'packages/config/lib/utils.ts', 'packages/core/index.ts', 'packages/types/index.ts', - 'packages/typescript-plugin/index.ts', 'tsslint.config.ts', ] as const; diff --git a/packages/compat-eslint/test/bench/dogfood.ts b/packages/compat-eslint/test/bench/dogfood.ts index 6ce00377..f2f83dab 100644 --- a/packages/compat-eslint/test/bench/dogfood.ts +++ b/packages/compat-eslint/test/bench/dogfood.ts @@ -14,14 +14,16 @@ // - Output is grouped: each unique (rule, divergence-pattern) is // collapsed to one entry plus a single example location. -import * as fs from 'fs'; +import * as oracle from '@tsslint/estree-oracle'; import * as path from 'path'; -import * as ts from 'typescript'; +import type { SourceFile } from 'typescript/ast'; +import type { Checker, Program } from 'typescript/sync'; +import { createProjectForFiles } from '../ts7'; import { DOGFOOD_FILES } from './dogfood-corpus'; const compat = require('../../index.js') as typeof import('../../index.js'); -const { Linter } = require('eslint') as typeof import('eslint'); -const tsParser = require('@typescript-eslint/parser'); +const { Linter } = oracle.eslint; +const tsParser = oracle.parser; const { RULES } = require('./rules.config.js') as { RULES: Array<[string, unknown[]?]> }; @@ -35,76 +37,42 @@ interface DiagLoc { } function buildProgram(files: readonly string[]) { - const realLibPath = ts.getDefaultLibFilePath({ target: ts.ScriptTarget.ES2020 }); - const realLibName = realLibPath.split(/[\\/]/).pop()!; - const realLib = ts.createSourceFile( - realLibPath, - ts.sys.readFile(realLibPath) ?? '', - ts.ScriptTarget.ES2020, - true, - ts.ScriptKind.TS, - ); - const sources = new Map(); - for (const f of files) { - const abs = path.join(REPO_ROOT, f); - const text = fs.readFileSync(abs, 'utf8'); - // Use the in-repo absolute path so cross-imports between - // packages resolve naturally; the program never writes. - sources.set(abs, ts.createSourceFile(abs, text, ts.ScriptTarget.ES2020, true, ts.ScriptKind.TS)); - } - const host: ts.CompilerHost = { - getSourceFile: name => sources.get(name) ?? (name === realLibPath ? realLib : undefined), - getDefaultLibFileName: () => realLibName, - writeFile: () => {}, - getCurrentDirectory: () => REPO_ROOT, - getDirectories: () => [], - fileExists: name => sources.has(name) || name === realLibPath || ts.sys.fileExists(name), - readFile: name => { - const sf = sources.get(name); - if (sf) return sf.text; - if (name === realLibPath) return realLib.text; - return ts.sys.readFile(name); - }, - getCanonicalFileName: n => n, - useCaseSensitiveFileNames: () => true, - getNewLine: () => '\n', - }; - const program = ts.createProgram({ - rootNames: [...sources.keys()], - options: { - target: ts.ScriptTarget.ES2020, - lib: ['es2020'], - module: ts.ModuleKind.NodeNext, - moduleResolution: ts.ModuleResolutionKind.NodeNext, - types: [], - noEmit: true, - strict: true, - noImplicitAny: false, - allowJs: false, - skipLibCheck: true, - }, - host, + const absFiles = files.map(f => path.join(REPO_ROOT, f)); + const { program, checker } = createProjectForFiles(absFiles, { + target: 'es2020', + lib: ['es2020'], + module: 'nodenext', + moduleResolution: 'nodenext', + types: [], + strict: true, + noImplicitAny: false, + skipLibCheck: true, }); - return { program, sources }; + const sources = new Map(); + for (const abs of absFiles) sources.set(abs, program.getSourceFile(abs)!); + return { program, checker, sources }; } -let _tsPlugin: { rules: Record } | undefined; function loadRule(ruleName: string): unknown { - if (ruleName.startsWith('@typescript-eslint/')) { - _tsPlugin ??= require('@typescript-eslint/eslint-plugin'); - const short = ruleName.slice('@typescript-eslint/'.length); - const rule = _tsPlugin!.rules[short]; - if (!rule) throw new Error(`@typescript-eslint plugin has no rule '${short}'`); - return rule; - } const eslintRoot = path.dirname(require.resolve('eslint/package.json')); return require(path.join(eslintRoot, 'lib/rules', ruleName + '.js')); } -function runTsslint(program: ts.Program, sf: ts.SourceFile, ruleName: string, options: unknown[]): DiagLoc[] { - const rule = loadRule(ruleName); +// convertRule's shared traversal runs once per file with every rule +// registered at that moment, so all rules must be converted up front. +const convertedRules = new Map>(); +for (const [ruleName, options = []] of RULES) { + convertedRules.set(ruleName, compat.convertRule(loadRule(ruleName) as any, options as any[], { id: ruleName })); +} + +function runTsslint( + program: Program, + checker: Checker, + sf: SourceFile, + ruleName: string, +): DiagLoc[] { const out: DiagLoc[] = []; - const tsslintRule = compat.convertRule(rule as any, options as any[], { id: ruleName }); + const tsslintRule = convertedRules.get(ruleName)!; const reportFn: any = (_msg: string, start: number, _end: number) => { const lc = sf.getLineAndCharacterOfPosition(start); out.push({ file: sf.fileName, line: lc.line + 1, column: lc.character + 1, ruleId: ruleName }); @@ -139,7 +107,7 @@ function runTsslint(program: ts.Program, sf: ts.SourceFile, ruleName: string, op }; return r; }; - tsslintRule({ file: sf, report: reportFn, program } as any); + tsslintRule({ file: sf, report: reportFn, program, checker } as any); return out; } @@ -149,23 +117,15 @@ function runEslint( fileName: string, ruleName: string, options: unknown[], - program: ts.Program, ): DiagLoc[] { - const isPluginRule = ruleName.startsWith('@typescript-eslint/'); const config: any = { files: ['**/*.ts'], languageOptions: { parser: tsParser, - parserOptions: isPluginRule - ? { ecmaVersion: 'latest', sourceType: 'module', programs: [program] } - : { ecmaVersion: 'latest', sourceType: 'module' }, + parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, }, rules: { [ruleName]: ['error', ...options] }, }; - if (isPluginRule) { - _tsPlugin ??= require('@typescript-eslint/eslint-plugin'); - config.plugins = { '@typescript-eslint': _tsPlugin }; - } const messages = linter.verify(code, [config], fileName); return messages .filter(m => m.ruleId === ruleName) @@ -191,7 +151,7 @@ interface DivergenceRecord { async function main() { const corpus = DOGFOOD_FILES; - const { program, sources } = buildProgram(corpus); + const { program, checker, sources } = buildProgram(corpus); // Linter's cwd defaults to process.cwd(); set it to REPO_ROOT so that // absolute paths under the repo (e.g. /repo-root/packages/cli/index.ts) // resolve to a relative form (`packages/cli/index.ts`) that matches @@ -214,7 +174,7 @@ async function main() { let t: DiagLoc[] | null = null; let e: DiagLoc[] | null = null; try { - t = runTsslint(program, sf, ruleName, options); + t = runTsslint(program, checker, sf, ruleName); } catch (err: any) { crashes.push({ rule: ruleName, file, runner: 'tsslint', message: err?.message ?? String(err) }); @@ -224,7 +184,7 @@ async function main() { // `parserOptions.programs` lookup succeeds (without it, // type-aware plugin rules silently re-parse without type // info and emit nothing). - e = runEslint(linter, code, abs, ruleName, options, program); + e = runEslint(linter, code, abs, ruleName, options); } catch (err: any) { crashes.push({ rule: ruleName, file, runner: 'eslint', message: err?.message ?? String(err) }); diff --git a/packages/compat-eslint/test/bench/rules.config.ts b/packages/compat-eslint/test/bench/rules.config.ts index 619dd599..a7d4d6e5 100644 --- a/packages/compat-eslint/test/bench/rules.config.ts +++ b/packages/compat-eslint/test/bench/rules.config.ts @@ -6,6 +6,9 @@ // rule's `meta.defaultOptions` the same way ESLint does // (deepMergeArrays). // +// ESLint core rules only: typescript-eslint's rules depend on the +// TypeScript 5/6 compiler API and cannot run on TypeScript 7. +// // New rule? Append below + run `pnpm bench -- --update-baseline`. // If the rule needs a corpus pattern that doesn't exist yet, add // the file to `corpus/` first. @@ -131,19 +134,4 @@ export const RULES: Array<[string, unknown[]?]> = [ ['id-denylist', ['callback']], ['consistent-this'], - // === @typescript-eslint plugin (type-aware + syntactic) === - // type-aware: read parserServices.program — TSSLint shares the - // IDE's program, ESLint side passes the same program via - // `parserOptions.programs` to avoid the duplicate-program problem. - ['@typescript-eslint/no-floating-promises'], - ['@typescript-eslint/no-misused-promises'], - ['@typescript-eslint/await-thenable'], - ['@typescript-eslint/no-unnecessary-condition'], - ['@typescript-eslint/prefer-readonly'], - ['@typescript-eslint/restrict-plus-operands'], - ['@typescript-eslint/restrict-template-expressions'], - // syntactic but plugin-extended: - ['@typescript-eslint/consistent-type-imports'], - ['@typescript-eslint/consistent-type-definitions'], - ['@typescript-eslint/naming-convention'], ]; diff --git a/packages/compat-eslint/test/bench/run.ts b/packages/compat-eslint/test/bench/run.ts index e1c5f184..d9c9d9c1 100644 --- a/packages/compat-eslint/test/bench/run.ts +++ b/packages/compat-eslint/test/bench/run.ts @@ -20,11 +20,14 @@ import * as fs from 'fs'; import * as path from 'path'; -import * as ts from 'typescript'; +import * as oracle from '@tsslint/estree-oracle'; +import type { SourceFile } from 'typescript/ast'; +import type { Checker, Program } from 'typescript/sync'; +import { createProjectForFiles } from '../ts7'; const compat = require('../../index.js') as typeof import('../../index.js'); -const { Linter } = require('eslint') as typeof import('eslint'); -const tsParser = require('@typescript-eslint/parser'); +const { Linter } = oracle.eslint; +const tsParser = oracle.parser; const { RULES } = require('./rules.config.js') as { RULES: Array<[string, unknown[]?]> }; @@ -44,94 +47,25 @@ function listCorpus(): string[] { .sort(); } -// Build one `ts.Program` rooted at every corpus file so cross-file -// imports type-check. Reuse it across all rule runs for the same -// file, since `convertRule` keeps a per-file cache. +// One TypeScript 7 project over every corpus file so cross-file imports +// type-check. Reuse it across all rule runs for the same file, since +// `convertRule` keeps a per-file cache. function buildProgram(files: string[]) { - const realLibPath = ts.getDefaultLibFilePath({ target: ts.ScriptTarget.ES2020 }); - const realLibName = realLibPath.split(/[\\/]/).pop()!; - const realLib = ts.createSourceFile( - realLibPath, - ts.sys.readFile(realLibPath) ?? '', - ts.ScriptTarget.ES2020, - true, - ts.ScriptKind.TS, - ); - // Root program files under `process.cwd()` so the path matches what - // `Linter.verify(code, configs, fullPath)` produces — without this, - // `@typescript-eslint/parser` (with `parserOptions.programs: [program]`) - // can't find the file in the program's source map and falls back to - // re-parsing without type info, breaking every type-aware plugin rule. - // Also makes ESLint's `files: ['**/*.ts']` glob match (an absolute - // path starting with `/_file.ts` doesn't match — produces "No matching - // configuration found"). - const baseDir = process.cwd(); - const sources = new Map(); - for (const f of files) { - const text = fs.readFileSync(path.join(CORPUS_DIR, f), 'utf8'); - const absPath = path.join(baseDir, f); - sources.set(absPath, ts.createSourceFile(absPath, text, ts.ScriptTarget.ES2020, true, ts.ScriptKind.TS)); - } - const host: ts.CompilerHost = { - getSourceFile: name => sources.get(name) ?? (name === realLibPath ? realLib : undefined), - getDefaultLibFileName: () => realLibName, - writeFile: () => {}, - getCurrentDirectory: () => baseDir, - getDirectories: () => [], - fileExists: name => sources.has(name) || name === realLibPath, - readFile: name => { - const sf = sources.get(name); - if (sf) return sf.text; - return name === realLibPath ? realLib.text : undefined; - }, - getCanonicalFileName: n => n, - useCaseSensitiveFileNames: () => true, - getNewLine: () => '\n', - resolveModuleNameLiterals(literals, containing) { - return literals.map(l => { - const spec = l.text.replace(/\.js$/, '.ts'); - const dir = path.dirname(containing); - const resolved = path.normalize(dir + '/' + spec); - return sources.has(resolved) - ? { resolvedModule: { resolvedFileName: resolved, extension: ts.Extension.Ts } } - : { resolvedModule: undefined }; - }); - }, - }; - const program = ts.createProgram({ - rootNames: [...sources.keys()], - options: { - target: ts.ScriptTarget.ES2020, - lib: ['es2020'], - module: ts.ModuleKind.NodeNext, - moduleResolution: ts.ModuleResolutionKind.NodeNext, - types: [], - noEmit: true, - // `strict: true` enables strictNullChecks which several - // type-aware @typescript-eslint plugin rules require - // (no-unnecessary-condition, strict-boolean-expressions). - // Without it the rule emits a warning at file:1:1 instead - // of running normally. - strict: true, - noImplicitAny: false, - }, - host, + const { program, checker } = createProjectForFiles(files.map(f => path.join(CORPUS_DIR, f)), { + target: 'es2020', + lib: ['es2020'], + module: 'nodenext', + moduleResolution: 'nodenext', + types: [], + strict: true, + noImplicitAny: false, }); - return { program, sources }; + const sources = new Map(); + for (const f of files) sources.set(f, program.getSourceFile(path.join(CORPUS_DIR, f))!); + return { program, checker, sources }; } -// Cache plugin lookup; avoid reloading per-rule. -let _tsPlugin: { rules: Record } | undefined; function loadRule(ruleName: string): unknown { - // `@typescript-eslint/` → load from `@typescript-eslint/eslint-plugin`'s - // `rules` registry. Plain `` → load from ESLint core's `lib/rules`. - if (ruleName.startsWith('@typescript-eslint/')) { - _tsPlugin ??= require('@typescript-eslint/eslint-plugin'); - const short = ruleName.slice('@typescript-eslint/'.length); - const rule = _tsPlugin!.rules[short]; - if (!rule) throw new Error(`@typescript-eslint plugin has no rule '${short}'`); - return rule; - } const eslintRoot = path.dirname(require.resolve('eslint/package.json')); return require(path.join(eslintRoot, 'lib/rules', ruleName + '.js')); } @@ -152,7 +86,13 @@ function preRegisterAllRules(): void { } } -function runTsslint(program: ts.Program, sf: ts.SourceFile, ruleName: string, _options: unknown[]): DiagLoc[] { +function runTsslint( + program: Program, + checker: Checker, + sf: SourceFile, + ruleName: string, + _options: unknown[], +): DiagLoc[] { const out: DiagLoc[] = []; const tsslintRule = convertedRules.get(ruleName)!; const reportFn: any = (msg: string, start: number, _end: number) => { @@ -193,7 +133,7 @@ function runTsslint(program: ts.Program, sf: ts.SourceFile, ruleName: string, _o return r; }; try { - tsslintRule({ file: sf, report: reportFn, program } as any); + tsslintRule({ file: sf, report: reportFn, program, checker } as any); } catch (e) { if (process.env.BENCH_DEBUG && ruleName === process.env.BENCH_DEBUG) { @@ -209,27 +149,15 @@ function runEslint( fileName: string, ruleName: string, options: unknown[], - program: ts.Program, ): DiagLoc[] { - // For `@typescript-eslint/...` rules: register the plugin, pass our - // existing ts.Program via `parserOptions.programs` so the parser - // reuses it instead of building a duplicate (mirrors how TSSLint - // shares tsserver's program in production). - const isPluginRule = ruleName.startsWith('@typescript-eslint/'); const config: any = { files: ['**/*.ts'], languageOptions: { parser: tsParser, - parserOptions: isPluginRule - ? { ecmaVersion: 'latest', sourceType: 'module', programs: [program] } - : { ecmaVersion: 'latest', sourceType: 'module' }, + parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, }, rules: { [ruleName]: ['error', ...options] }, }; - if (isPluginRule) { - _tsPlugin ??= require('@typescript-eslint/eslint-plugin'); - config.plugins = { '@typescript-eslint': _tsPlugin }; - } const messages = linter.verify(code, [config], fileName); if (process.env.BENCH_DEBUG_ESLINT && ruleName === process.env.BENCH_DEBUG_ESLINT) { console.log(`[ESLINT ${ruleName}] ${fileName} → ${messages.length} messages`, messages.slice(0, 3)); @@ -293,7 +221,7 @@ async function main() { const verbose = process.argv.includes('--verbose'); const corpus = listCorpus(); - const { program, sources } = buildProgram(corpus); + const { program, checker, sources } = buildProgram(corpus); const linter = new Linter(); const baseline = loadBaseline(); const newBaseline: Record> = {}; @@ -306,10 +234,10 @@ async function main() { for (const [ruleName, options = []] of RULES) { newBaseline[ruleName] = {}; for (const file of corpus) { - const sf = sources.get(path.join(process.cwd(), file))!; + const sf = sources.get(file)!; const code = sf.text; - const t = runTsslint(program, sf, ruleName, options); - const e = runEslint(linter, code, path.join(process.cwd(), file), ruleName, options, program); + const t = runTsslint(program, checker, sf, ruleName, options); + const e = runEslint(linter, code, path.join(process.cwd(), file), ruleName, options); const d = diff(t, e); const hasDiff = d.tsslintOnly.length > 0 || d.eslintOnly.length > 0; diff --git a/packages/compat-eslint/test/compat-pipeline.test.ts b/packages/compat-eslint/test/compat-pipeline.test.ts index a57e306e..b28a1d0d 100644 --- a/packages/compat-eslint/test/compat-pipeline.test.ts +++ b/packages/compat-eslint/test/compat-pipeline.test.ts @@ -12,7 +12,10 @@ // node --experimental-strip-types --no-warnings packages/compat-eslint/test/compat-pipeline.test.ts import type * as ESLint from 'eslint'; -import * as ts from 'typescript'; +import type { SourceFile } from 'typescript/ast'; +import { type Checker, type Program } from 'typescript/sync'; +import { createIdentifier } from 'typescript/ast/factory'; +import { createProject } from './ts7'; const compat = require('../index.js') as typeof import('../index.js'); @@ -64,60 +67,18 @@ function makeRule(listeners: Record): MockRule { return { rule, calls }; } -// Build a minimal ts.Program from in-memory code. Compiler host serves -// `/test.ts` only; type-checker calls in our pipeline are best-effort -// (parserServices.getSymbolAtLocation only fires if a rule explicitly -// asks, which our mock rules don't). +type FileKind = 'ts' | 'tsx' | 'js' | 'jsx'; + function buildProgram( code: string, - kind: ts.ScriptKind = ts.ScriptKind.TS, -): { program: ts.Program; file: ts.SourceFile } { - const isJs = kind === ts.ScriptKind.JS || kind === ts.ScriptKind.JSX; - const fileName = kind === ts.ScriptKind.TSX - ? '/test.tsx' - : kind === ts.ScriptKind.JSX - ? '/test.jsx' - : kind === ts.ScriptKind.JS - ? '/test.js' - : '/test.ts'; - const sf = ts.createSourceFile(fileName, code, ts.ScriptTarget.Latest, true, kind); - const realLibPath = ts.getDefaultLibFilePath({ target: ts.ScriptTarget.Latest }); - const realLibName = realLibPath.split(/[\\/]/).pop()!; - const realLibContent = ts.sys.readFile(realLibPath) ?? ''; - const realLib = ts.createSourceFile( - realLibPath, - realLibContent, - ts.ScriptTarget.Latest, - true, - ts.ScriptKind.TS, - ); - const host: ts.CompilerHost = { - getSourceFile: n => n === fileName ? sf : (n === realLibPath ? realLib : undefined), - getDefaultLibFileName: () => realLibName, - getDefaultLibLocation: () => realLibPath.replace('/' + realLibName, ''), - writeFile: () => {}, - getCurrentDirectory: () => '/', - getDirectories: () => [], - fileExists: n => n === fileName || n === realLibPath, - readFile: n => n === fileName ? code : (n === realLibPath ? realLibContent : undefined), - getCanonicalFileName: n => n, - useCaseSensitiveFileNames: () => true, - getNewLine: () => '\n', - }; - const program = ts.createProgram({ - rootNames: [fileName], - options: { - target: ts.ScriptTarget.Latest, - lib: [realLibName], - noEmit: true, - allowJs: isJs || undefined, - jsx: kind === ts.ScriptKind.TSX || kind === ts.ScriptKind.JSX - ? ts.JsxEmit.Preserve - : undefined, - }, - host, + kind: FileKind = 'ts', +): { program: Program; checker: Checker; file: SourceFile } { + const isJs = kind === 'js' || kind === 'jsx'; + return createProject(`test.${kind}`, code, { + strict: true, + allowJs: isJs || undefined, + jsx: kind === 'tsx' || kind === 'jsx' ? 'preserve' : undefined, }); - return { program, file: sf }; } // Drive the rule through the TSSLint pipeline. Returns the reports the @@ -125,8 +86,9 @@ function buildProgram( // instead, which exercises the same path before the report-replay step). function runRule( rule: ESLint.Rule.RuleModule, - program: ts.Program, - file: ts.SourceFile, + program: Program, + file: SourceFile, + checker?: Checker, ): { reports: any[]; threw?: unknown } { const reports: any[] = []; let threw: unknown; @@ -160,7 +122,7 @@ function runRule( return chain; }; try { - tsslintRule({ file, report: reportFn, program } as any); + tsslintRule({ file, report: reportFn, program, checker } as any); } catch (e) { threw = e; @@ -171,11 +133,11 @@ function runRule( function runMock( code: string, listeners: Record, - kind: ts.ScriptKind = ts.ScriptKind.TS, + kind: FileKind = 'ts', ): { calls: Recorded[]; threw?: unknown } { - const { program, file } = buildProgram(code, kind); + const { program, file, checker } = buildProgram(code, kind); const m = makeRule(listeners); - const r = runRule(m.rule, program, file); + const r = runRule(m.rule, program, file, checker); return { calls: m.calls, threw: r.threw }; } @@ -281,7 +243,7 @@ function runMock( // no-unreachable false-positive seen on TS repo's checker.ts. { const code = `const obj = { method() { return 1; } };\nvar x = 1;`; - const { program, file } = buildProgram(code); + const { program, file, checker } = buildProgram(code); const eslintRoot = require('path').dirname(require.resolve('eslint/package.json')); const noUnreachable = require(eslintRoot + '/lib/rules/no-unreachable.js'); const reports: { start: number; end: number }[] = []; @@ -319,7 +281,7 @@ function runMock( }; return r; }; - tsslintRule({ file, report: reportFn, program } as any); + tsslintRule({ file, report: reportFn, program, checker } as any); check( 'no-unreachable: method shorthand return does not poison outer scope reachability', reports.length === 0, @@ -342,7 +304,7 @@ function runMock( /** @internal */ export function after() {} `; - const { program, file } = buildProgram(code); + const { program, file, checker } = buildProgram(code); const eslintRoot = require('path').dirname(require.resolve('eslint/package.json')); const paddingRule = require(eslintRoot + '/lib/rules/padding-line-between-statements.js'); const reports: { start: number; end: number }[] = []; @@ -384,7 +346,7 @@ function runMock( }; return r; }; - tsslintRule({ file, report: reportFn, program } as any); + tsslintRule({ file, report: reportFn, program, checker } as any); check( 'padding-line: export-wrapped function inherits proper parent (no false positive on JSDoc-then-export)', reports.length === 0, @@ -401,7 +363,7 @@ function runMock( { // `const` binding in a for-of: ESLint says safe, TSSLint must agree. const code = `for (const x of arr) { items.filter(y => y === x); }`; - const { program, file } = buildProgram(code); + const { program, file, checker } = buildProgram(code); const eslintRoot = require('path').dirname(require.resolve('eslint/package.json')); const noLoopFunc = require(eslintRoot + '/lib/rules/no-loop-func.js'); const reports: { msg: string }[] = []; @@ -439,7 +401,7 @@ function runMock( }; return r; }; - tsslintRule({ file, report: reportFn, program } as any); + tsslintRule({ file, report: reportFn, program, checker } as any); check( 'no-loop-func: const for-of binding is block-scoped (safe), no false positive', reports.length === 0, @@ -470,7 +432,7 @@ function runMock( // `tsNode.parent` from it yields undefined immediately, so the // materialize loop exits with `parent === null` and falls through to // the GenericTSNode(parent=null) branch. - const detached = ts.factory.createIdentifier('detached'); + const detached = createIdentifier('detached'); let threw: unknown; let result: unknown; try { @@ -513,7 +475,7 @@ function g() { return modulePaths.length; } `; - const { program, file } = buildProgram(code); + const { program, file, checker } = buildProgram(code); const eslintRoot = require('path').dirname(require.resolve('eslint/package.json')); const preferConst = require(eslintRoot + '/lib/rules/prefer-const.js'); const reports: { msg: string }[] = []; @@ -551,7 +513,7 @@ function g() { }; return r; }; - tsslintRule({ file, report: reportFn, program } as any); + tsslintRule({ file, report: reportFn, program, checker } as any); check( 'prefer-const: array destructuring with mixed reassign reports each non-reassigned binding (destructuring: any default)', reports.length === 4, @@ -583,7 +545,7 @@ const e = JSON.stringify({}); const f = parseInt('1', 10); const g = NOT_A_REAL_GLOBAL; `; - const { program, file } = buildProgram(code); + const { program, file, checker } = buildProgram(code); const eslintRoot = require('path').dirname(require.resolve('eslint/package.json')); const noUndef = require(eslintRoot + '/lib/rules/no-undef.js'); const reports: { msg: string }[] = []; @@ -621,7 +583,7 @@ const g = NOT_A_REAL_GLOBAL; }; return r; }; - tsslintRule({ file, report: reportFn, program } as any); + tsslintRule({ file, report: reportFn, program, checker } as any); check( 'no-undef: built-in ECMAScript globals (undefined/Math/String/Array/JSON/parseInt) are recognised', reports.length === 1, @@ -634,86 +596,6 @@ const g = NOT_A_REAL_GLOBAL; ); } -// 3h. `TsVariable.defs` must filter out declarations that aren't in the -// user's source file (e.g. TS lib `.d.ts` declarations for `Map`, -// `Set`, `Promise`). `materialize()` can't reach lib-source nodes -// because `convertLazy` only pre-registers the user's SourceFile, -// so they fall back to `GenericTSNode(parent=null)`. Rules that -// read `def.node.parent.type` (naming-convention's -// `collectVariables` → `isExported`, no-unused-vars, no-redeclare) -// crash on null parent. Upstream models the same symbols as -// `ImplicitLibVariable` with empty defs. -// -// Originally surfaced when adding `addGlobals` made eager -// `_ensureRefIndex()` populate lib vars in globalScope before -// naming-convention's `collectVariables` walked them. -{ - // `Map` is a type-only lib reference — scope-manager adds the - // Map symbol's TsVariable to globalScope. Without the filter, its - // defs[0].node materializes from lib.es2015.collection.d.ts and - // returns GenericTSNode(parent=null). naming-convention crashes on - // `def.node.parent.type.startsWith('Export')`. - const code = ` -const m: Map = new Map(); -const x = m.size; -`; - const { program, file } = buildProgram(code); - const eslintRoot = require('path').dirname(require.resolve('eslint/package.json')); - const tsPlugin = require(require.resolve('@typescript-eslint/eslint-plugin', { paths: [eslintRoot] })); - const namingConvention = tsPlugin.rules['naming-convention']; - const reports: { msg: string }[] = []; - const tsslintRule = compat.convertRule( - namingConvention, - [{ selector: 'typeLike', format: ['PascalCase'] }], - { id: 'naming-convention' }, - ); - const reportFn: any = (msg: string) => { - reports.push({ msg }); - const r: any = { - at() { - return r; - }, - asWarning() { - return r; - }, - asError() { - return r; - }, - asSuggestion() { - return r; - }, - withFix() { - return r; - }, - withRefactor() { - return r; - }, - withDeprecated() { - return r; - }, - withUnnecessary() { - return r; - }, - withoutCache() { - return r; - }, - }; - return r; - }; - let threw: unknown; - try { - tsslintRule({ file, report: reportFn, program } as any); - } - catch (e) { - threw = e; - } - check( - 'TsVariable.defs: lib-source declarations are filtered (no naming-convention crash on def.node.parent.type)', - threw === undefined, - threw ? `threw: ${(threw as Error).message}` : 'unexpected', - ); -} - // 3i. `addGlobals` must NOT trigger eager `materialize()` on through // ref identifiers — doing so partially populates the lazy ESTree // wrapper cache (ChainExpression / ExportNamedDeclaration / @@ -746,7 +628,7 @@ function f(obj?: { x: { y: string } | undefined }) { return undefined; } `; - const { program, file } = buildProgram(code); + const { program, file, checker } = buildProgram(code); const eslintRoot = require('path').dirname(require.resolve('eslint/package.json')); const consistentReturn = require(eslintRoot + '/lib/rules/consistent-return.js'); const reports: { msg: string }[] = []; @@ -786,7 +668,7 @@ function f(obj?: { x: { y: string } | undefined }) { }; let threw: unknown; try { - tsslintRule({ file, report: reportFn, program } as any); + tsslintRule({ file, report: reportFn, program, checker } as any); } catch (e) { threw = e; @@ -818,7 +700,7 @@ function f() { for (let i = 0, j = 0; i < 10; i++, j++) { x = i, j; } } `; - const { program, file } = buildProgram(code); + const { program, file, checker } = buildProgram(code); const eslintRoot = require('path').dirname(require.resolve('eslint/package.json')); const noSequences = require(eslintRoot + '/lib/rules/no-sequences.js'); const reports: { msg: string }[] = []; @@ -856,7 +738,7 @@ function f() { }; return r; }; - tsslintRule({ file, report: reportFn, program } as any); + tsslintRule({ file, report: reportFn, program, checker } as any); // `x = i, j` — bare comma in expression statement. Not in // allowed-parens skip list, not for-update, no parens → reports. check( @@ -885,7 +767,7 @@ namespace M { f(); } `; - const { program, file } = buildProgram(code); + const { program, file, checker } = buildProgram(code); const eslintRoot = require('path').dirname(require.resolve('eslint/package.json')); const noUseBeforeDef = require(eslintRoot + '/lib/rules/no-use-before-define.js'); const reports: { msg: string }[] = []; @@ -929,7 +811,7 @@ namespace M { }; return r; }; - tsslintRule({ file, report: reportFn, program } as any); + tsslintRule({ file, report: reportFn, program, checker } as any); check( 'convertRule: deep-merges meta.defaultOptions with user options (no-use-before-define enums fires on const enum)', reports.length === 1 && reports[0].msg.includes("'Bar'"), @@ -954,7 +836,7 @@ namespace M { import { sys } from './y.js'; function f(sys: string) { return sys; } `; - const { program, file } = buildProgram(code); + const { program, file, checker } = buildProgram(code); const eslintRoot = require('path').dirname(require.resolve('eslint/package.json')); const noShadow = require(eslintRoot + '/lib/rules/no-shadow.js'); const reports: { msg: string }[] = []; @@ -992,7 +874,7 @@ function f(sys: string) { return sys; } }; return r; }; - tsslintRule({ file, report: reportFn, program } as any); + tsslintRule({ file, report: reportFn, program, checker } as any); check( 'TsVariable.isValueVariable: ImportBinding counts as value (no-shadow fires on import-vs-param value/value shadow)', reports.length === 1 && reports[0].msg.includes("'sys'"), @@ -1022,7 +904,7 @@ function outer() { return resolve; } `; - const { program, file } = buildProgram(code); + const { program, file, checker } = buildProgram(code); const eslintRoot = require('path').dirname(require.resolve('eslint/package.json')); const noShadow = require(eslintRoot + '/lib/rules/no-shadow.js'); const reports: { msg: string }[] = []; @@ -1060,7 +942,7 @@ function outer() { }; return r; }; - tsslintRule({ file, report: reportFn, program } as any); + tsslintRule({ file, report: reportFn, program, checker } as any); check( 'TsVariable.scope: named function expression name binding lives in the function-expression-name scope (not the inner function body)', reports.length === 0, @@ -1087,7 +969,7 @@ if (process.argv.includes('--version')) { require('../dist/x.js'); } `; - const { program, file } = buildProgram(code, ts.ScriptKind.JS); + const { program, file, checker } = buildProgram(code, 'js'); const eslintRoot = require('path').dirname(require.resolve('eslint/package.json')); const noShadow = require(eslintRoot + '/lib/rules/no-shadow.js'); const reports: { msg: string }[] = []; @@ -1125,7 +1007,7 @@ if (process.argv.includes('--version')) { }; return r; }; - tsslintRule({ file, report: reportFn, program } as any); + tsslintRule({ file, report: reportFn, program, checker } as any); check( 'TsScope global/module: skip sf.locals entries whose decls are nested inside a scope-boundary (JS CJS-alias hoist)', reports.length === 0, @@ -1145,7 +1027,7 @@ if (process.argv.includes('--version')) { console.log(x); } }`; - const { program, file } = buildProgram(code, ts.ScriptKind.JS); + const { program, file, checker } = buildProgram(code, 'js'); const eslintRoot = require('path').dirname(require.resolve('eslint/package.json')); const noShadow = require(eslintRoot + '/lib/rules/no-shadow.js'); const reports: { msg: string }[] = []; @@ -1183,7 +1065,7 @@ if (process.argv.includes('--version')) { }; return r; }; - tsslintRule({ file, report: reportFn, program } as any); + tsslintRule({ file, report: reportFn, program, checker } as any); check( 'TsScope function: skip fn.locals entries whose decls are nested inside a sub-scope (JS CJS-alias hoist into function locals)', reports.length === 0, @@ -1207,10 +1089,22 @@ class A extends String {} class B implements String {} type C = String; `; - const { program, file } = buildProgram(code); - const tsRoot = require('path').dirname(require.resolve('@typescript-eslint/eslint-plugin/package.json')); - const ruleMod = require(tsRoot + '/dist/rules/no-wrapper-object-types.js'); - const rule = (ruleMod as { default?: ESLint.Rule.RuleModule }).default ?? ruleMod; + const { program, file, checker } = buildProgram(code); + // Same listeners as typescript-eslint's `no-wrapper-object-types`. + const rule: ESLint.Rule.RuleModule = { + meta: { type: 'problem', schema: [], messages: { x: 'x' } } as any, + create(ctx) { + const check = (node: any, name: any) => { + if (name?.type === 'Identifier' && name.name === 'String') { + ctx.report({ node, message: node.type }); + } + }; + return { + TSClassImplements: (node: any) => check(node, node.expression), + TSTypeReference: (node: any) => check(node, node.typeName), + } as any; + }, + }; const reports: { msg: string }[] = []; const tsslintRule = compat.convertRule(rule, [], { id: 'no-wrapper-object-types' }); const reportFn: any = (msg: string) => { @@ -1246,7 +1140,7 @@ type C = String; }; return r; }; - tsslintRule({ file, report: reportFn, program } as any); + tsslintRule({ file, report: reportFn, program, checker } as any); check( 'EWTA shape: class extends X is NOT TSClassImplements (no FP on `class A extends String`); implements X + type alias still fire', reports.length === 2, @@ -1264,7 +1158,7 @@ type C = String; // none fire — the EWTA collapses out of ESTree entirely for this case. { const code = `class A extends Foo {}`; - const { program, file } = buildProgram(code); + const { program, file, checker } = buildProgram(code); const fired: string[] = []; const probe: any = { meta: { type: 'problem', schema: [], messages: { x: 'x' } }, @@ -1297,7 +1191,7 @@ type C = String; }; return r; }; - tsslintRule({ file, report: reportFn, program } as any); + tsslintRule({ file, report: reportFn, program, checker } as any); check( 'class extends Foo: EWTA fires NO selector (not TSClassImplements / TSInterfaceHeritage / TSInstantiationExpression)', fired.length === 0, @@ -1326,7 +1220,7 @@ export function outer() { return inner; } `; - const { program, file } = buildProgram(code); + const { program, file, checker } = buildProgram(code); const eslintRoot = require('path').dirname(require.resolve('eslint/package.json')); const noRedecl = require(eslintRoot + '/lib/rules/no-redeclare.js'); const reports: { msg: string }[] = []; @@ -1364,7 +1258,7 @@ export function outer() { }; return r; }; - tsslintRule({ file, report: reportFn, program } as any); + tsslintRule({ file, report: reportFn, program, checker } as any); check( 'scope.block / def.node: unwrap export wrappers (no-redeclare fires on overloads inside exported function)', reports.length === 2, @@ -1391,7 +1285,7 @@ function f() { return m.size; } `; - const { program, file } = buildProgram(code); + const { program, file, checker } = buildProgram(code); const eslintRoot = require('path').dirname(require.resolve('eslint/package.json')); const noUndef = require(eslintRoot + '/lib/rules/no-undef.js'); const reports: { msg: string }[] = []; @@ -1429,7 +1323,7 @@ function f() { }; return r; }; - tsslintRule({ file, report: reportFn, program } as any); + tsslintRule({ file, report: reportFn, program, checker } as any); check( 'scope.through: alias-aware lookup resolves synthetic `arguments` and lib `Map` (no-undef false positives)', reports.length === 0, @@ -1449,7 +1343,7 @@ function f() { import * as foo from "./foo.js"; export { foo }; `; - const { program, file } = buildProgram(code); + const { program, file, checker } = buildProgram(code); const eslintRoot = require('path').dirname(require.resolve('eslint/package.json')); const noUndef = require(eslintRoot + '/lib/rules/no-undef.js'); const reports: { msg: string }[] = []; @@ -1487,7 +1381,7 @@ export { foo }; }; return r; }; - tsslintRule({ file, report: reportFn, program } as any); + tsslintRule({ file, report: reportFn, program, checker } as any); check( 'export specifier: re-export resolves to local target via getExportSpecifierLocalTargetSymbol', reports.length === 0, @@ -1514,7 +1408,7 @@ export { foo }; import { length, type SomeType } from "./y.js"; function f(start: number, length: number) { return start + length; } `; - const { program, file } = buildProgram(code); + const { program, file, checker } = buildProgram(code); const eslintRoot = require('path').dirname(require.resolve('eslint/package.json')); const noShadow = require(eslintRoot + '/lib/rules/no-shadow.js'); const reports: { msg: string }[] = []; @@ -1552,7 +1446,7 @@ function f(start: number, length: number) { return start + length; } }; return r; }; - tsslintRule({ file, report: reportFn, program } as any); + tsslintRule({ file, report: reportFn, program, checker } as any); check( 'TsDefinition.parent: ImportSpecifier walks to ImportDeclaration so no-shadow filter sees specifiers', reports.length === 0, @@ -1571,7 +1465,7 @@ function f(start: number, length: number) { return start + length; } function f(): import("inspector").Profiler.Profile { return null as any; } function g(): import("inspector").Session { return null as any; } `; - const { program, file } = buildProgram(code); + const { program, file, checker } = buildProgram(code); const eslintRoot = require('path').dirname(require.resolve('eslint/package.json')); const noUndef = require(eslintRoot + '/lib/rules/no-undef.js'); const reports: { msg: string }[] = []; @@ -1609,7 +1503,7 @@ function g(): import("inspector").Session { return null as any; } }; return r; }; - tsslintRule({ file, report: reportFn, program } as any); + tsslintRule({ file, report: reportFn, program, checker } as any); check( 'TSImportType qualifier: not classified as a free reference (single + nested QualifiedName)', reports.length === 0, @@ -1627,7 +1521,7 @@ function g(): import("inspector").Session { return null as any; } const code = ` type ExtractName = T extends { name: infer TName } ? TName extends string ? TName : never : never; `; - const { program, file } = buildProgram(code); + const { program, file, checker } = buildProgram(code); const eslintRoot = require('path').dirname(require.resolve('eslint/package.json')); const noUndef = require(eslintRoot + '/lib/rules/no-undef.js'); const reports: { msg: string }[] = []; @@ -1665,7 +1559,7 @@ type ExtractName = T extends { name: infer TName } ? TName extends string ? T }; return r; }; - tsslintRule({ file, report: reportFn, program } as any); + tsslintRule({ file, report: reportFn, program, checker } as any); check( 'infer type parameter: defined on the enclosing conditional-type scope', reports.length === 0, @@ -1685,7 +1579,7 @@ const obj = { "foo-bar": 1 }; const { ["foo-bar"]: x } = obj; console.log(x); `; - const { program, file } = buildProgram(code); + const { program, file, checker } = buildProgram(code); const eslintRoot = require('path').dirname(require.resolve('eslint/package.json')); const noUselessComputedKey = require(eslintRoot + '/lib/rules/no-useless-computed-key.js'); const reports: { msg: string }[] = []; @@ -1723,7 +1617,7 @@ console.log(x); }; return r; }; - tsslintRule({ file, report: reportFn, program } as any); + tsslintRule({ file, report: reportFn, program, checker } as any); check( 'BindingElement.computed: ComputedPropertyName propertyName produces Property with computed=true', reports.length === 1, @@ -1753,7 +1647,7 @@ type F = Exclude<'a' | 'b', 'a'>; function f(x: A, y: B, z: C): D | E | F { return null!; } f({ a: 1 } as A, {} as B, 'a' as C); `; - const { program, file } = buildProgram(code); + const { program, file, checker } = buildProgram(code); const eslintRoot = require('path').dirname(require.resolve('eslint/package.json')); const noUndef = require(eslintRoot + '/lib/rules/no-undef.js'); const reports: { msg: string }[] = []; @@ -1791,7 +1685,7 @@ f({ a: 1 } as A, {} as B, 'a' as C); }; return r; }; - tsslintRule({ file, report: reportFn, program } as any); + tsslintRule({ file, report: reportFn, program, checker } as any); check( 'no-undef: TS lib utility types in type-arg positions are recognised globals', reports.length === 0, @@ -1812,34 +1706,10 @@ f({ a: 1 } as A, {} as B, 'a' as C); // `through`. Repro requires a multi-file program — single-file // interfaces don't trigger merging. { - const fA = '/a.d.ts'; const fB = '/b.d.ts'; const codeA = `interface Container { value: T; }\n`; const codeB = `interface Container { extra: T; map(fn: (v: T) => T): T; }\n`; - const sfA = ts.createSourceFile(fA, codeA, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS); - const sfB = ts.createSourceFile(fB, codeB, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS); - const realLibPath = ts.getDefaultLibFilePath({ target: ts.ScriptTarget.Latest }); - const realLibName = realLibPath.split(/[\\/]/).pop()!; - const realLibContent = ts.sys.readFile(realLibPath) ?? ''; - const realLib = ts.createSourceFile(realLibPath, realLibContent, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS); - const host: ts.CompilerHost = { - getSourceFile: n => n === fA ? sfA : (n === fB ? sfB : (n === realLibPath ? realLib : undefined)), - getDefaultLibFileName: () => realLibName, - getDefaultLibLocation: () => realLibPath.replace('/' + realLibName, ''), - writeFile: () => {}, - getCurrentDirectory: () => '/', - getDirectories: () => [], - fileExists: n => n === fA || n === fB || n === realLibPath, - readFile: n => n === fA ? codeA : (n === fB ? codeB : (n === realLibPath ? realLibContent : undefined)), - getCanonicalFileName: n => n, - useCaseSensitiveFileNames: () => true, - getNewLine: () => '\n', - }; - const program = ts.createProgram({ - rootNames: [fA, fB], - options: { target: ts.ScriptTarget.Latest, lib: [realLibName], strict: true, noEmit: true }, - host, - }); + const projectB = createProject(fB.slice(1), codeA + codeB, { strict: true }); const eslintRoot = require('path').dirname(require.resolve('eslint/package.json')); const noUndef = require(eslintRoot + '/lib/rules/no-undef.js'); const reports: { msg: string }[] = []; @@ -1879,7 +1749,7 @@ f({ a: 1 } as A, {} as B, 'a' as C); }; // Lint /b.d.ts — the references to T inside Container should // resolve to the merged Container symbol's type parameter, not leak. - tsslintRule({ file: program.getSourceFile(fB), report: reportFn, program } as any); + tsslintRule({ file: projectB.file, report: reportFn, program: projectB.program, checker: projectB.checker } as any); check( 'no-undef: type parameters in cross-file-merged ambient interfaces resolve', reports.length === 0, @@ -1912,42 +1782,7 @@ type B2 = Omit<{ b: 2 }, 'b'>; type C = Record; type D = Awaited>; `; - const fileName = '/test.ts'; - const sf = ts.createSourceFile(fileName, code, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS); - const realLibPath = ts.getDefaultLibFilePath({ target: ts.ScriptTarget.Latest }); - const libDir = realLibPath.replace(/[/\\][^/\\]+$/, ''); - const fs = require('fs') as typeof import('fs'); - const pathMod = require('path') as typeof import('path'); - const libFiles = new Map(); - for (const f of fs.readdirSync(libDir)) { - if (f.startsWith('lib.') && f.endsWith('.d.ts')) { - const p = pathMod.join(libDir, f); - libFiles.set( - p, - ts.createSourceFile(p, fs.readFileSync(p, 'utf-8'), ts.ScriptTarget.Latest, true, ts.ScriptKind.TS), - ); - } - } - const realLibName = realLibPath.split(/[\\/]/).pop()!; - const host: ts.CompilerHost = { - getSourceFile: n => n === fileName ? sf : libFiles.get(n), - getDefaultLibFileName: () => realLibName, - getDefaultLibLocation: () => libDir, - writeFile: () => {}, - getCurrentDirectory: () => '/', - getDirectories: () => [], - fileExists: n => n === fileName || libFiles.has(n), - readFile: n => n === fileName ? code : libFiles.get(n)?.text, - getCanonicalFileName: n => n, - useCaseSensitiveFileNames: () => true, - getNewLine: () => '\n', - }; - const program = ts.createProgram({ - rootNames: [fileName, realLibPath], - options: { target: ts.ScriptTarget.Latest, strict: true, noEmit: true, noLib: false }, - host, - }); - const file = program.getSourceFile(fileName)!; + const { program, file } = createProject('test.ts', code, { strict: true }); const refCounts = new Map(); const inspectRule: ESLint.Rule.RuleModule = { meta: { type: 'problem', schema: [], messages: { x: 'x' } } as any, @@ -2271,7 +2106,7 @@ type D = Awaited>; 'JSXExpressionContainer': true, 'JSXIdentifier': true, 'JSXText': true, - }, ts.ScriptKind.TSX); + }, 'tsx'); check('JSX: dispatch does not throw', threw === undefined, threw ? String((threw as Error).message ?? threw) : ''); check( @@ -2352,7 +2187,7 @@ type D = Awaited>; const { calls } = runMock(code, { 'JSXEmptyExpression': true, 'JSXExpressionContainer': true, - }, ts.ScriptKind.TSX); + }, 'tsx'); check( 'JSX-empty: JSXExpressionContainer fires for all 3 ({}, {/*…*/}, {x})', calls.filter(c => c.selector === 'JSXExpressionContainer').length === 3, @@ -2374,7 +2209,7 @@ type D = Awaited>; return
{count && }
; } `; - const { program, file } = buildProgram(code, ts.ScriptKind.TSX); + const { program, file } = buildProgram(code, 'tsx'); const events: string[] = []; const rule: ESLint.Rule.RuleModule = { meta: { type: 'problem', schema: [], messages: { x: 'x' } } as any, @@ -2434,7 +2269,7 @@ type D = Awaited>; let _ = ; let __ = ok; `; - const { program, file } = buildProgram(code, ts.ScriptKind.TSX); + const { program, file, checker } = buildProgram(code, 'tsx'); const reports: { line: number; column: number; message: string }[] = []; const rule: ESLint.Rule.RuleModule = { meta: { type: 'problem', schema: [], messages: { missing: 'img missing alt' } } as any, @@ -2489,7 +2324,7 @@ type D = Awaited>; }; let threw: unknown; try { - tsslintRule({ file, report: reportFn, program } as any); + tsslintRule({ file, report: reportFn, program, checker } as any); } catch (e) { threw = e; diff --git a/packages/compat-eslint/test/jsx-react-x.test.mjs b/packages/compat-eslint/test/jsx-react-x.test.mjs deleted file mode 100644 index 25ca310e..00000000 --- a/packages/compat-eslint/test/jsx-react-x.test.mjs +++ /dev/null @@ -1,212 +0,0 @@ -// End-to-end parity test: compat-eslint vs ESLint Linter -// running `eslint-plugin-react-x/no-leaked-conditional-rendering` -// over a TSX fixture that exercises the type-aware leak detection. -// -// Why a separate .mjs file: eslint-plugin-react-x is ESM-only with a -// strict `exports` field, so it can't be `require()`'d from the -// compiled .test.js files. Run via: -// node packages/compat-eslint/test/jsx-react-x.test.mjs - -import { Linter } from 'eslint'; -import { default as plugin } from 'eslint-plugin-react-x'; -import { createRequire } from 'node:module'; -import * as ts from 'typescript'; - -const require = createRequire(import.meta.url); -const compat = require('../index.js'); -const tsParser = require('@typescript-eslint/parser'); - -const ruleId = 'no-leaked-conditional-rendering'; -const rules = plugin.rules || plugin.default?.rules || {}; -const rule = rules[ruleId]; -if (!rule) { - console.error(`rule "${ruleId}" not found in eslint-plugin-react-x`); - process.exit(1); -} - -const fileName = process.cwd() + '/test.tsx'; -const code = `\ -function A({ count }: { count: number }) { - return
{count && x}
; -} -function B({ name }: { name: string }) { - return
{name && x}
; -} -function C({ flag }: { flag: boolean }) { - return
{flag && x}
; -} -function D({ items }: { items: string[] }) { - return
{items.length && x}
; -} -function E({ on }: { on: 0 | 1 }) { - return
{on && x}
; -} -function F({ s }: { s: string | undefined }) { - return
{s && x}
; -} -function G({ obj }: { obj: { x: number } | null }) { - return
{obj && x}
; -} -function H({ arr }: { arr: number[] }) { - return
{arr.length > 0 && x}
; -} -function I({ x }: { x: bigint }) { - return
{x && x}
; -} -function J() { - const v = NaN; - return
{v && x}
; -} -`; - -// --- ts.Program --- -const sf = ts.createSourceFile(fileName, code, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX); -const realLibPath = ts.getDefaultLibFilePath({ target: ts.ScriptTarget.Latest }); -const realLibName = realLibPath.split(/[\\/]/).pop(); -const realLibContent = ts.sys.readFile(realLibPath) ?? ''; -const realLib = ts.createSourceFile(realLibPath, realLibContent, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS); -const host = { - getSourceFile: n => (n === fileName ? sf : n === realLibPath ? realLib : undefined), - getDefaultLibFileName: () => realLibName, - getDefaultLibLocation: () => realLibPath.replace('/' + realLibName, ''), - writeFile: () => {}, - getCurrentDirectory: () => '/', - getDirectories: () => [], - fileExists: n => n === fileName || n === realLibPath, - readFile: n => (n === fileName ? code : n === realLibPath ? realLibContent : undefined), - getCanonicalFileName: n => n, - useCaseSensitiveFileNames: () => true, - getNewLine: () => '\n', -}; -const program = ts.createProgram({ - rootNames: [fileName], - options: { - target: ts.ScriptTarget.Latest, - lib: [realLibName], - jsx: ts.JsxEmit.Preserve, - noEmit: true, - strict: true, - }, - host, -}); - -// --- compat-eslint side --- -const tsslintRule = compat.convertRule(rule, [], { id: ruleId }); -const compatReports = []; -let compatError = null; -const noopReporter = { - at() { - return this; - }, - asWarning() { - return this; - }, - asError() { - return this; - }, - asSuggestion() { - return this; - }, - withDeprecated() { - return this; - }, - withUnnecessary() { - return this; - }, - withFix() { - return this; - }, - withRefactor() { - return this; - }, - withoutCache() { - return this; - }, -}; -try { - tsslintRule({ - typescript: ts, - program, - file: sf, - report(message, start, end) { - const startLC = sf.getLineAndCharacterOfPosition(start); - const endLC = sf.getLineAndCharacterOfPosition(end); - compatReports.push({ - key: `${startLC.line + 1}:${startLC.character + 1}-${endLC.line + 1}:${endLC.character + 1}`, - message, - }); - return noopReporter; - }, - }); -} -catch (e) { - compatError = e; -} - -// --- ESLint Linter side --- -const linter = new Linter({ configType: 'flat' }); -const eslintMessages = linter.verify(code, [{ - files: ['**/*.tsx'], - languageOptions: { - parser: tsParser, - parserOptions: { - ecmaVersion: 2022, - sourceType: 'module', - ecmaFeatures: { jsx: true }, - programs: [program], - project: false, - filePath: fileName, - }, - }, - plugins: { 'react-x': plugin }, - rules: { ['react-x/' + ruleId]: 'error' }, -}], fileName); - -// surface non-rule diagnostics (parse / config errors) -for (const m of eslintMessages) { - if (m.ruleId !== 'react-x/' + ruleId) { - console.log(' [linter-msg]', m.severity, m.ruleId ?? '(parse)', '|', String(m.message).slice(0, 160)); - } -} -const eslintReports = eslintMessages - .filter(m => m.ruleId === 'react-x/' + ruleId) - .map(m => ({ - key: `${m.line}:${m.column}-${m.endLine ?? m.line}:${m.endColumn ?? m.column}`, - message: m.message, - })); - -// --- Diff --- -const compatKeys = new Set(compatReports.map(r => r.key)); -const eslintKeys = new Set(eslintReports.map(r => r.key)); -const onlyCompat = [...compatKeys].filter(k => !eslintKeys.has(k)); -const onlyEslint = [...eslintKeys].filter(k => !compatKeys.has(k)); - -if (compatError) { - console.log('compat-eslint: THREW ' + (compatError.name || 'Error')); - console.log(' message:', String(compatError.message).split('\n')[0]); -} -else { - console.log(`compat-eslint: ${compatReports.length} report(s)`); - for (const r of compatReports.sort((a, b) => a.key.localeCompare(b.key))) { - console.log(' [compat]', r.key, '|', r.message.slice(0, 100)); - } -} -console.log(`ESLint Linter: ${eslintReports.length} report(s)`); -for (const r of eslintReports.sort((a, b) => a.key.localeCompare(b.key))) { - console.log(' [eslint]', r.key, '|', r.message.slice(0, 100)); -} - -if (compatError) { - console.log('\nFAIL: compat-eslint cannot run this rule (JSX coverage gap)'); - process.exit(2); -} -if (onlyCompat.length === 0 && onlyEslint.length === 0) { - console.log('\nPARITY ✓'); - process.exit(0); -} -else { - console.log('\nMISMATCH'); - if (onlyCompat.length) console.log(' only in compat:', onlyCompat); - if (onlyEslint.length) console.log(' only in eslint:', onlyEslint); - process.exit(1); -} diff --git a/packages/compat-eslint/test/lazy-estree-review-bugs.test.ts b/packages/compat-eslint/test/lazy-estree-review-bugs.test.ts index d023db90..10d07242 100644 --- a/packages/compat-eslint/test/lazy-estree-review-bugs.test.ts +++ b/packages/compat-eslint/test/lazy-estree-review-bugs.test.ts @@ -16,12 +16,14 @@ // `.js`, oracle = typescript-estree's eager `astConverter`. // Run: tsc --build && node packages/compat-eslint/test/lazy-estree-review-bugs.test.js -import * as ts from 'typescript'; +import * as oracle from '@tsslint/estree-oracle'; +import * as ts from '../lib/ts'; +import { parse } from './ts7'; const lazy = require('../lib/lazy-estree.js') as typeof import('../lib/lazy-estree.js'); const scan = require('../lib/ts-ast-scan.js') as typeof import('../lib/ts-ast-scan.js'); const { visitorKeys } = require('../lib/visitor-keys.js') as typeof import('../lib/visitor-keys.js'); -const { astConverter } = require('@typescript-eslint/typescript-estree/use-at-your-own-risk'); +const { astConverter } = oracle.typescriptEstreeInternals; // Lazy child slots are PROTOTYPE getters, so Object.keys can't see them — a // generic tree walk must drive off visitorKeys (own-keys fallback for the rare @@ -30,12 +32,16 @@ function childKeys(n: any): readonly string[] { return visitorKeys[n.type] ?? Object.keys(n); } function sfOf(code: string, tsx = false): ts.SourceFile { - return ts.createSourceFile( + return parse(tsx ? '/t.tsx' : '/t.ts', code); +} +function sf6Of(code: string, tsx = false) { + const ts6 = oracle.ts; + return ts6.createSourceFile( tsx ? 't.tsx' : 't.ts', code, - ts.ScriptTarget.Latest, + ts6.ScriptTarget.Latest, true, - tsx ? ts.ScriptKind.TSX : ts.ScriptKind.TS, + tsx ? ts6.ScriptKind.TSX : ts6.ScriptKind.TS, ); } const lazyAst = (code: string, tsx = false) => (lazy.convertLazy(sfOf(code, tsx)) as any).estree; @@ -50,7 +56,7 @@ function eagerAst(code: string, tsx = false, invalid = false): any { suppressDeprecatedPropertyWarnings: true, tokens: false, }; - return astConverter(sfOf(code, tsx), settings as any, false).estree; + return astConverter(sf6Of(code, tsx), settings as any, false).estree; } function find(root: any, pred: (n: any) => boolean): any { const seen = new Set(); diff --git a/packages/compat-eslint/test/lazy-estree.test.ts b/packages/compat-eslint/test/lazy-estree.test.ts index 7f10c372..80717159 100644 --- a/packages/compat-eslint/test/lazy-estree.test.ts +++ b/packages/compat-eslint/test/lazy-estree.test.ts @@ -7,10 +7,12 @@ // MVP only handles a few SyntaxKinds; fixtures stay narrow on purpose. As // classes get added in lazy-estree.ts, broaden the fixtures here. -import * as ts from 'typescript'; +import * as oracle from '@tsslint/estree-oracle'; +import * as ts from '../lib/ts'; +import { parse, parseTs, parseTsx } from './ts7'; const lazy = require('../lib/lazy-estree.js') as typeof import('../lib/lazy-estree.js'); -const { astConverter } = require('@typescript-eslint/typescript-estree/use-at-your-own-risk'); +const { astConverter } = oracle.typescriptEstreeInternals; const PARSE_SETTINGS = { allowInvalidAST: false, @@ -22,16 +24,39 @@ const PARSE_SETTINGS = { tokens: true, }; -function parseTs(code: string): ts.SourceFile { - return ts.createSourceFile('/test.ts', code, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS); +// The reference side: typescript-estree's eager Converter over a +// TypeScript 6 parse of the same text. +function parseTs6(code: string, fileName = '/test.ts') { + const ts6 = oracle.ts; + const kind = fileName.endsWith('x') ? ts6.ScriptKind.TSX : ts6.ScriptKind.TS; + return ts6.createSourceFile(fileName, code, ts6.ScriptTarget.Latest, true, kind); } -function parseTsx(code: string): ts.SourceFile { - return ts.createSourceFile('/test.tsx', code, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX); +function eagerConvert(sf: ReturnType): any { + return astConverter(sf, PARSE_SETTINGS as any, true).estree; } -function eagerConvert(sf: ts.SourceFile): any { - return astConverter(sf, PARSE_SETTINGS as any, true).estree; +// The eager side runs on a TypeScript 6 tree, so node identity can't be +// shared with the TypeScript 7 tree the lazy side converts. Bridge the +// two by kind name + text range. +function eagerConvertWithMaps(code: string, fileName: string): { + estree: any; + astMaps: { tsNodeToESTreeNodeMap: { get(n: ts.Node): any } }; +} { + const ts6 = oracle.ts; + const sf6 = parseTs6(code, fileName); + const r = astConverter(sf6, PARSE_SETTINGS as any, true); + const byRange = new Map(); + const visit = (n: any) => { + const e = r.astMaps.tsNodeToESTreeNodeMap.get(n); + if (e) byRange.set(`${ts6.SyntaxKind[n.kind]}@${n.pos}-${n.end}`, e); + ts6.forEachChild(n, visit); + }; + visit(sf6); + return { + estree: r.estree, + astMaps: { tsNodeToESTreeNodeMap: { get: n => byRange.get(`${ts.SyntaxKind[n.kind]}@${n.pos}-${n.end}`) } }, + }; } const failures: string[] = []; @@ -104,7 +129,7 @@ function runFixture(name: string, code: string) { console.log(`\n[${name}] ${code.replace(/\n/g, ' \\n ').slice(0, 60)}`); const sf = parseTs(code); const lazyResult = lazy.convertLazy(sf); - const eagerResult = eagerConvert(parseTs(code)); + const eagerResult = eagerConvert(parseTs6(code)); compare(lazyResult.estree, eagerResult, 'Program'); } @@ -151,6 +176,19 @@ runFixture('function expression', 'let f = function g() { return 1; };'); runFixture('async arrow', 'let f = async () => 1;'); runFixture('the no-explicit-any fixture', 'let x: any = 1; function foo(y: any): any { return y; }'); +// TypeScript 7 AST shapes that differ from the classic parser +runFixture('implements qualified', 'class C implements A.B, D {}'); +runFixture('interface extends qualified', 'interface I extends A.B.C, D {}'); +runFixture('declare global', 'declare global { var x: number; }'); +runFixture('namespace', 'namespace A { export const x = 1; }'); +runFixture('module keyword', 'declare module M {} declare module "m" {}'); +runFixture('optional / definite members', 'class C { a?: number; b!: string; m?(): void; }'); +runFixture('signature members', 'interface I { a?: number; m?(): void; }'); +runFixture('type-only import', 'import type T from "x"; import { type U } from "y";'); +runFixture('array binding hole', 'const [, a, , b] = c;'); +runFixture('type parameter default', 'type T = A;'); +runFixture('leading comments', '// c\n/** d */\nfunction f() {}'); + // --- Lazy invariants ---------------------------------------------------- // Factory's whenAbsent semantics: a slot configured with whenAbsent: @@ -423,7 +461,7 @@ runFixture('the no-explicit-any fixture', 'let x: any = 1; function foo(y: any): const sf = parseTs('let x;'); const { estree } = lazy.convertLazy(sf); const declarator = (estree.body as any)[0].declarations[0]; - const eager = eagerConvert(parseTs('let x;')); + const eager = eagerConvert(parseTs6('let x;')); const eagerDeclarator = eager.body[0].declarations[0]; check('null/undefined: lazy missing init === null', declarator.init === null); check('null/undefined: matches eager (both null or both undefined)', declarator.init === eagerDeclarator.init); @@ -599,7 +637,7 @@ runFixture('the no-explicit-any fixture', 'let x: any = 1; function foo(y: any): const code = 'let x = 1 as number;'; const sf = parseTs(code); const { estree } = lazy.convertLazy(sf); - const eager = eagerConvert(parseTs(code)); + const eager = eagerConvert(parseTs6(code)); const lazyAs = (estree.body as any)[0].declarations[0].init; const eagerAs = eager.body[0].declarations[0].init; check('AsExpression range: lazy [0]', lazyAs.range[0] === eagerAs.range[0]); @@ -641,7 +679,7 @@ runFixture('the no-explicit-any fixture', 'let x: any = 1; function foo(y: any): const code = 'let a = 1;\r\nlet b = 2;\r\n'; const sf = parseTs(code); const { estree } = lazy.convertLazy(sf); - const eager = eagerConvert(parseTs(code)); + const eager = eagerConvert(parseTs6(code)); const lazyB = (estree.body as any)[1].declarations[0].id; const eagerB = eager.body[1].declarations[0].id; check('CRLF: line numbers match eager', lazyB.loc.start.line === eagerB.loc.start.line); @@ -654,7 +692,7 @@ runFixture('the no-explicit-any fixture', 'let x: any = 1; function foo(y: any): const code = 'let xyz = 1'; const sf = parseTs(code); const { estree } = lazy.convertLazy(sf); - const eager = eagerConvert(parseTs(code)); + const eager = eagerConvert(parseTs6(code)); const lazyId = (estree.body as any)[0].declarations[0].id; const eagerId = eager.body[0].declarations[0].id; check( @@ -668,7 +706,7 @@ runFixture('the no-explicit-any fixture', 'let x: any = 1; function foo(y: any): { const sf = parseTs(''); const { estree } = lazy.convertLazy(sf); - const eager = eagerConvert(parseTs('')); + const eager = eagerConvert(parseTs6('')); check('empty source: lazy body length 0', (estree.body as any).length === 0); check('empty source: range [0,0]', estree.range[0] === eager.range[0] && estree.range[1] === eager.range[1]); check( @@ -685,7 +723,7 @@ runFixture('the no-explicit-any fixture', 'let x: any = 1; function foo(y: any): const code = ' \n '; const sf = parseTs(code); const { estree } = lazy.convertLazy(sf); - const eager = eagerConvert(parseTs(code)); + const eager = eagerConvert(parseTs6(code)); check( 'whitespace-only: range matches eager', estree.range[0] === eager.range[0] && estree.range[1] === eager.range[1], @@ -794,7 +832,7 @@ runFixture('the no-explicit-any fixture', 'let x: any = 1; function foo(y: any): for (const rel of targets) { const realFile = path.resolve(packagesRoot, rel); const source = fs.readFileSync(realFile, 'utf8'); - const sf = ts.createSourceFile(realFile, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS); + const sf = parse(realFile, source); try { const { estree } = lazy.convertLazy(sf); walkAll(estree); @@ -818,11 +856,11 @@ runFixture('the no-explicit-any fixture', 'let x: any = 1; function foo(y: any): for (const rel of targets) { const realFile = path.resolve(packagesRoot, rel); const source = fs.readFileSync(realFile, 'utf8'); - const sf = ts.createSourceFile(realFile, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS); + const sf = parse(realFile, source); const beforeFails = failures.length; try { const { estree: lazyEstree } = lazy.convertLazy(sf); - const eagerSf = ts.createSourceFile(realFile, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS); + const eagerSf = parseTs6(source, realFile); const eagerEstree = eagerConvert(eagerSf); compare(lazyEstree, eagerEstree, `parity:${rel}`); } @@ -970,7 +1008,7 @@ runFixture('the no-explicit-any fixture', 'let x: any = 1; function foo(y: any): const runFixture = (fx: { name: string; code: string }): { matched: boolean; first?: string } => { const sf = parseTs(fx.code); - const eagerSf = parseTs(fx.code); + const eagerSf = parseTs6(fx.code); try { const { estree: lazyEstree } = lazy.convertLazy(sf); const eagerEstree = eagerConvert(eagerSf); @@ -1076,7 +1114,7 @@ runFixture('the no-explicit-any fixture', 'let x: any = 1; function foo(y: any): ]; const runJsxFixture = (fx: { name: string; code: string }): { matched: boolean; first?: string } => { const sf = parseTsx(fx.code); - const eagerSf = parseTsx(fx.code); + const eagerSf = parseTs6(fx.code, '/test.tsx'); try { const { estree: lazyEstree } = lazy.convertLazy(sf); const eagerEstree = eagerConvert(eagerSf); @@ -1560,11 +1598,8 @@ function findTsJsxExpr(sf: ts.SourceFile, attrName?: string): ts.JsxExpression | } } - const eagerWithMaps = (sf: ts.SourceFile) => { - const r = astConverter(sf, PARSE_SETTINGS as any, true) as { - estree: any; - astMaps: { tsNodeToESTreeNodeMap: { get(n: ts.Node): any } }; - }; + const eagerWithMaps = (code: string, fileName: string) => { + const r = eagerConvertWithMaps(code, fileName); setEagerParents(r.estree); return r; }; @@ -1610,7 +1645,7 @@ function findTsJsxExpr(sf: ts.SourceFile, attrName?: string): ts.JsxExpression | // across both maps. let eagerMap: { get(n: ts.Node): any }; try { - eagerMap = eagerWithMaps(sf).astMaps.tsNodeToESTreeNodeMap; + eagerMap = eagerWithMaps(fx.code, fx.tsx ? '/test.tsx' : '/test.ts').astMaps.tsNodeToESTreeNodeMap; } catch (err) { fixtureFailures.push([fx.name, `eager threw: ${(err as Error).message}`]); @@ -1990,7 +2025,7 @@ function findTsJsxExpr(sf: ts.SourceFile, attrName?: string): ts.JsxExpression | // them, so they're not actually in any user-visible tree. const eagerMap = (() => { try { - return astConverter(sf, PARSE_SETTINGS as any, true).astMaps.tsNodeToESTreeNodeMap; + return eagerConvertWithMaps(fx.code, fx.tsx ? '/test.tsx' : '/test.ts').astMaps.tsNodeToESTreeNodeMap; } catch { return null; @@ -2112,7 +2147,7 @@ function findTsJsxExpr(sf: ts.SourceFile, attrName?: string): ts.JsxExpression | ['EqualsEqualsEqualsToken', ts.SyntaxKind.EqualsEqualsEqualsToken, true], ['QuestionDotToken', ts.SyntaxKind.QuestionDotToken, true], // File / trivia boundaries - ['EndOfFileToken', ts.SyntaxKind.EndOfFileToken, true], + ['EndOfFile', ts.SyntaxKind.EndOfFile, true], // Container kinds without standalone ESTree shape ['SyntaxList', ts.SyntaxKind.SyntaxList, true], ['HeritageClause', ts.SyntaxKind.HeritageClause, true], @@ -2121,8 +2156,8 @@ function findTsJsxExpr(sf: ts.SourceFile, attrName?: string): ts.JsxExpression | ['TemplateSpan', ts.SyntaxKind.TemplateSpan, true], ['OmittedExpression', ts.SyntaxKind.OmittedExpression, true], // JSDoc — entire family routed through range check - ['JSDocComment', ts.SyntaxKind.JSDocComment, true], - ['JSDocTag', ts.SyntaxKind.JSDocTag, true], + ['JSDoc', ts.SyntaxKind.JSDoc, true], + ['JSDocUnknownTag', ts.SyntaxKind.JSDocUnknownTag, true], ]; for (const [name, kind, expected] of cases) { const actual = lazy.hasNoEstreeCounterpart(kind); @@ -2186,7 +2221,7 @@ function findTsJsxExpr(sf: ts.SourceFile, attrName?: string): ts.JsxExpression | ['punctuator: PlusToken', ts.SyntaxKind.PlusToken], ['punctuator: SemicolonToken', ts.SyntaxKind.SemicolonToken], ['punctuator: OpenBraceToken', ts.SyntaxKind.OpenBraceToken], - ['EndOfFileToken', ts.SyntaxKind.EndOfFileToken], + ['EndOfFileToken', ts.SyntaxKind.EndOfFile], ['reserved keyword: ReturnKeyword', ts.SyntaxKind.ReturnKeyword], ['reserved keyword: FunctionKeyword', ts.SyntaxKind.FunctionKeyword], ]; @@ -2244,12 +2279,12 @@ function runChild(env: NodeJS.ProcessEnv, code: string): { stdout: string; stder // the eager Program node), then read getNodeTypeCounts on the next // microtask so the deferred queueMicrotask bumps have landed. const childCommon = ` -const ts = require('typescript'); +const { parse } = require(${JSON.stringify(require.resolve('./ts7.js'))}); const lazy = require(${JSON.stringify(repoCompatEslint.replace(/index\.js$/, 'lib/lazy-estree.js'))}); const visitorKeys = require(${ JSON.stringify(repoCompatEslint.replace(/index\.js$/, 'lib/visitor-keys.js')) }).visitorKeys; -const sf = ts.createSourceFile('/t.tsx', 'let _ = ;', ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX); +const sf = parse('/t.tsx', 'let _ = ;'); const { estree } = lazy.convertLazy(sf); const seen = new WeakSet(); function walk(n) { @@ -2296,14 +2331,14 @@ Promise.resolve().then(() => { // ── 10: globalThis-shared counter — second require() instance sees it ── { const childTwoInstances = ` -const ts = require('typescript'); +const { parse } = require(${JSON.stringify(require.resolve('./ts7.js'))}); // Load lazy-estree TWICE via different require paths to mimic the // CLI-vs-project module-resolution split. delete from cache between // to force a fresh module instance, then check that counts populated by // the first instance are visible from the second's getNodeTypeCounts(). const path1 = ${JSON.stringify(repoCompatEslint.replace(/index\.js$/, 'lib/lazy-estree.js'))}; const lazy1 = require(path1); -const sf = ts.createSourceFile('/t.tsx', 'let _ = ;', ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX); +const sf = parse('/t.tsx', 'let _ = ;'); lazy1.convertLazy(sf); delete require.cache[require.resolve(path1)]; const lazy2 = require(path1); @@ -2329,9 +2364,9 @@ Promise.resolve().then(() => { // ── 11: resetNodeTypeCounts() clears the counter ─────────────────────── { const code = ` -const ts = require('typescript'); +const { parse } = require(${JSON.stringify(require.resolve('./ts7.js'))}); const lazy = require(${JSON.stringify(repoCompatEslint.replace(/index\.js$/, 'lib/lazy-estree.js'))}); -lazy.convertLazy(ts.createSourceFile('/t.tsx', 'let _ = ;', ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX)); +lazy.convertLazy(parse('/t.tsx', 'let _ = ;')); Promise.resolve().then(() => { const before = lazy.getNodeTypeCounts().size; lazy.resetNodeTypeCounts(); diff --git a/packages/compat-eslint/test/parser-options-gap.test.ts b/packages/compat-eslint/test/parser-options-gap.test.ts index a2f99082..fa5ecf5d 100644 --- a/packages/compat-eslint/test/parser-options-gap.test.ts +++ b/packages/compat-eslint/test/parser-options-gap.test.ts @@ -11,7 +11,7 @@ // tsc --build && node packages/compat-eslint/test/parser-options-gap.test.js import type * as ESLint from 'eslint'; -import * as ts from 'typescript'; +import { createProject } from './ts7'; const compat = require('../index.js') as typeof import('../index.js'); @@ -26,65 +26,20 @@ function expect(name: string, actual: unknown, predicate: (v: unknown) => boolea } } -// In-memory ts.Program. Same shape as compat-pipeline.test.ts; kept -// inline to avoid cross-test dep on a non-exported helper. -function buildProgram( - code: string, - kind: ts.ScriptKind, -): { program: ts.Program; file: ts.SourceFile } { - const isJs = kind === ts.ScriptKind.JS || kind === ts.ScriptKind.JSX; - const fileName = kind === ts.ScriptKind.TSX - ? '/test.tsx' - : kind === ts.ScriptKind.JSX - ? '/test.jsx' - : kind === ts.ScriptKind.JS - ? '/test.js' - : '/test.ts'; - const sf = ts.createSourceFile(fileName, code, ts.ScriptTarget.Latest, true, kind); - const realLibPath = ts.getDefaultLibFilePath({ target: ts.ScriptTarget.Latest }); - const realLibName = realLibPath.split(/[\\/]/).pop()!; - const realLibContent = ts.sys.readFile(realLibPath) ?? ''; - const realLib = ts.createSourceFile( - realLibPath, - realLibContent, - ts.ScriptTarget.Latest, - true, - ts.ScriptKind.TS, - ); - const host: ts.CompilerHost = { - getSourceFile: n => n === fileName ? sf : (n === realLibPath ? realLib : undefined), - getDefaultLibFileName: () => realLibName, - getDefaultLibLocation: () => realLibPath.replace('/' + realLibName, ''), - writeFile: () => {}, - getCurrentDirectory: () => '/', - getDirectories: () => [], - fileExists: n => n === fileName || n === realLibPath, - readFile: n => n === fileName ? code : (n === realLibPath ? realLibContent : undefined), - getCanonicalFileName: n => n, - useCaseSensitiveFileNames: () => true, - getNewLine: () => '\n', - }; - const program = ts.createProgram({ - rootNames: [fileName], - options: { - target: ts.ScriptTarget.Latest, - lib: [realLibName], - noEmit: true, - allowJs: isJs || undefined, - jsx: kind === ts.ScriptKind.TSX || kind === ts.ScriptKind.JSX - ? ts.JsxEmit.Preserve - : undefined, - }, - host, +type FileKind = 'ts' | 'tsx' | 'js' | 'jsx'; + +function buildProgram(code: string, kind: FileKind) { + return createProject(`test.${kind}`, code, { + allowJs: kind === 'js' || kind === 'jsx' || undefined, + jsx: kind === 'tsx' || kind === 'jsx' ? 'preserve' : undefined, }); - return { program, file: sf }; } // Run a probe rule that just snapshots `context.parserOptions` and // `context.languageOptions` at create-time. Rule never reports. function captureContext( code: string, - kind: ts.ScriptKind, + kind: FileKind, ): { parserOptions: any; languageOptions: any; @@ -127,14 +82,14 @@ function captureContext( }; return chain; }; - const { program, file } = buildProgram(code, kind); - tsslintRule({ file, report: reportFn, program } as any); + const { program, file, checker } = buildProgram(code, kind); + tsslintRule({ file, report: reportFn, program, checker } as any); return captured; } // === Test 1: ecmaFeatures.jsx must be true on .tsx === { - const ctx = captureContext(`const x =
;`, ts.ScriptKind.TSX); + const ctx = captureContext(`const x =
;`, 'tsx'); expect( 'parserOptions.ecmaFeatures.jsx is true on .tsx', ctx.parserOptions?.ecmaFeatures?.jsx, @@ -151,7 +106,7 @@ function captureContext( // === Test 2: ecmaFeatures.jsx must be falsy on .ts (non-JSX) === { - const ctx = captureContext(`const x = 1;`, ts.ScriptKind.TS); + const ctx = captureContext(`const x = 1;`, 'ts'); expect( 'parserOptions.ecmaFeatures.jsx is falsy on .ts', ctx.parserOptions?.ecmaFeatures?.jsx, @@ -164,7 +119,7 @@ function captureContext( // Per ECMAScript spec modules are strict-by-default. ESLint configs // that gate strict-mode-only checks read this field. { - const ctx = captureContext(`export const x = 1;`, ts.ScriptKind.TS); + const ctx = captureContext(`export const x = 1;`, 'ts'); expect( 'parserOptions.ecmaFeatures.impliedStrict is true on module file', ctx.parserOptions?.ecmaFeatures?.impliedStrict, @@ -179,7 +134,7 @@ function captureContext( // `return`. Files with no module syntax under a `module: CommonJS` // tsconfig are 'script' in our classification. { - const ctx = captureContext(`var a = 1; console.log(a);`, ts.ScriptKind.TS); + const ctx = captureContext(`var a = 1; console.log(a);`, 'ts'); // We classify this as 'script' (no externalModuleIndicator, // no commonJsModuleIndicator). globalReturn should follow. expect( @@ -192,7 +147,7 @@ function captureContext( // === Test 5: ecmaFeatures.globalReturn must be falsy on module files === { - const ctx = captureContext(`export {};`, ts.ScriptKind.TS); + const ctx = captureContext(`export {};`, 'ts'); expect( 'parserOptions.ecmaFeatures.globalReturn is falsy on module file', ctx.parserOptions?.ecmaFeatures?.globalReturn, @@ -206,7 +161,7 @@ function captureContext( // `context.languageOptions.parserOptions` (modern flat-config). Both // surfaces must agree to avoid intra-rule disagreement. { - const ctx = captureContext(`const x =
;`, ts.ScriptKind.TSX); + const ctx = captureContext(`const x =
;`, 'tsx'); const top = ctx.parserOptions?.ecmaFeatures; const lang = ctx.languageOptions?.parserOptions?.ecmaFeatures; expect( diff --git a/packages/compat-eslint/test/predicate-materializer-consistency.test.ts b/packages/compat-eslint/test/predicate-materializer-consistency.test.ts index d8f28130..eb9b3e89 100644 --- a/packages/compat-eslint/test/predicate-materializer-consistency.test.ts +++ b/packages/compat-eslint/test/predicate-materializer-consistency.test.ts @@ -30,12 +30,14 @@ import * as fs from 'fs'; import * as path from 'path'; -import * as ts from 'typescript'; +import * as oracle from '@tsslint/estree-oracle'; +import * as ts from '../lib/ts'; +import { parse } from './ts7'; const lazy = require('../lib/lazy-estree.js') as typeof import('../lib/lazy-estree.js'); const scan = require('../lib/ts-ast-scan.js') as typeof import('../lib/ts-ast-scan.js'); const { visitorKeys } = require('../lib/visitor-keys.js') as typeof import('../lib/visitor-keys.js'); -const { astConverter } = require('@typescript-eslint/typescript-estree/use-at-your-own-risk'); +const { astConverter } = oracle.typescriptEstreeInternals; // Curated snippets covering divergence-prone constructs (where the two // descriptions are most likely to drift). The walk hits every ts.Node, so one @@ -118,16 +120,17 @@ function collectTypes(node: any, into: Set, seen: Set): void { function sweep(code: string, tsx: boolean, where: string): void { const fileName = tsx ? 't.tsx' : 't.ts'; - const kind = tsx ? ts.ScriptKind.TSX : ts.ScriptKind.TS; + const ts6 = oracle.ts; + const kind = tsx ? ts6.ScriptKind.TSX : ts6.ScriptKind.TS; // lazyProduced: every ESTree type in the canonical top-down lazy tree. const lazyProduced = new Set(); - const sf = ts.createSourceFile(fileName, code, ts.ScriptTarget.Latest, true, kind); + const sf = parse(fileName, code); collectTypes((lazy.convertLazy(sf) as any).estree, lazyProduced, new Set()); // eagerProduced: the same for typescript-estree's eager converter (oracle). const eagerProduced = new Set(); - const sfE = ts.createSourceFile(fileName, code, ts.ScriptTarget.Latest, true, kind); + const sfE = ts6.createSourceFile(fileName, code, ts6.ScriptTarget.Latest, true, kind); collectTypes(astConverter(sfE, EAGER_SETTINGS as any, false).estree, eagerProduced, new Set()); // predicated: every ESTree type any predicate fires for, over all ts.Nodes. diff --git a/packages/compat-eslint/test/scope-compat.test.ts b/packages/compat-eslint/test/scope-compat.test.ts deleted file mode 100644 index bef6a142..00000000 --- a/packages/compat-eslint/test/scope-compat.test.ts +++ /dev/null @@ -1,388 +0,0 @@ -// Compares TsScopeManager against @typescript-eslint/scope-manager on focused -// fixtures. Run via: node --experimental-strip-types --no-warnings packages/compat-eslint/test/scope-compat.test.ts -// -// Output format: one block per fixture, listing differences in scope shape. -// A clean fixture prints "OK". Exit code is non-zero if any fixture diffs. - -import * as ts from 'typescript'; -import type { TsScope } from '../lib/ts-scope-manager.js'; -const { TsScopeManager } = require('../lib/ts-scope-manager.js') as typeof import('../lib/ts-scope-manager.js'); - -const { astConverter } = require('@typescript-eslint/typescript-estree/use-at-your-own-risk'); -const { analyze } = require('@typescript-eslint/scope-manager'); -const { visitorKeys } = require('@typescript-eslint/visitor-keys'); - -const PARSE_SETTINGS = { - allowInvalidAST: false, - comment: true, - errorOnUnknownASTType: false, - loc: true, - range: true, - suppressDeprecatedPropertyWarnings: true, - tokens: true, -}; - -interface Fixture { - name: string; - code: string; - sourceType?: 'module' | 'script'; - tsx?: boolean; -} - -const fixtures: Fixture[] = [ - { - name: 'simple-var-let-const', - code: ` - var a = 1; - let b = 2; - const c = 3; - console.log(a, b, c); - `, - }, - { - name: 'block-scope-let', - code: ` - if (true) { - let inner = 1; - console.log(inner); - } - `, - }, - { - name: 'block-scope-var-hoist', - code: ` - function f() { - if (true) { - var x = 1; // hoisted to function scope - } - return x; - } - `, - }, - { - name: 'function-decl-and-params', - code: ` - function add(a: number, b: number) { - return a + b; - } - add(1, 2); - `, - }, - { - name: 'arrow-function', - code: ` - const sq = (n: number) => n * n; - sq(4); - `, - }, - { - name: 'function-expression-name', - code: ` - const fn = function named() { - return named; - }; - `, - }, - { - name: 'class-with-methods', - code: ` - class Box { - value: T; - constructor(v: T) { this.value = v; } - get() { return this.value; } - } - new Box(1).get(); - `, - }, - { - name: 'for-loop', - code: ` - for (let i = 0; i < 10; i++) { - const x = i * 2; - console.log(x); - } - `, - }, - { - name: 'for-of', - code: ` - for (const item of [1, 2, 3]) { - console.log(item); - } - `, - }, - { - name: 'try-catch', - code: ` - try { throw new Error('x'); } catch (e) { console.log(e); } - `, - }, - { - name: 'destructuring', - code: ` - const { a, b: renamed } = { a: 1, b: 2 }; - const [first, ...rest] = [1, 2, 3]; - console.log(a, renamed, first, rest); - `, - }, - { - name: 'shadowing', - code: ` - const x = 1; - function f() { - const x = 2; - return x; - } - `, - }, - { - name: 'enum', - code: ` - enum Color { Red, Green, Blue } - const c = Color.Red; - console.log(c); - `, - }, - { - name: 'namespace', - code: ` - namespace N { - export const value = 1; - } - console.log(N.value); - `, - }, - { - name: 'type-alias-and-interface', - code: ` - type ID = string; - interface User { id: ID; name: string; } - const u: User = { id: 'x', name: 'y' }; - console.log(u); - `, - }, - { - name: 'import-binding', - code: ` - import { foo, bar as renamed } from 'somewhere'; - import * as ns from 'other'; - console.log(foo, renamed, ns); - `, - sourceType: 'module', - }, - { - name: 'unused-var', - code: ` - let used = 1; - let unused = 2; - console.log(used); - `, - }, - { - name: 'reassign-let', - code: ` - let counter = 0; - counter += 1; - counter++; - console.log(counter); - `, - }, - // JSX scope fixtures: `` (PascalCase) is a reference to a - // variable in scope. `
` (lowercase) is an HTML tag, not a - // reference. JSX expression containers are normal expression scopes; - // spread attributes and member-expression element names all resolve - // via the surrounding scope. - { - name: 'jsx-component-reference', - code: ` - function Component() { return null; } - let _ = ; - `, - sourceType: 'module', - tsx: true, - }, - { - name: 'jsx-lowercase-element', - code: ` - let used = 1; - let _ =
; - `, - sourceType: 'module', - tsx: true, - }, - { - name: 'jsx-member-element', - code: ` - const Lib = { Btn: function Btn() { return null; } }; - let _ = ; - `, - sourceType: 'module', - tsx: true, - }, - { - name: 'jsx-spread-attribute', - code: ` - const props = { id: 'x' }; - let _ =
; - `, - sourceType: 'module', - tsx: true, - }, - { - name: 'jsx-expression-container-ref', - code: ` - let count = 0; - let _ = {count + 1}; - `, - sourceType: 'module', - tsx: true, - }, - { - name: 'jsx-fragment-children', - code: ` - const a = 1; - const b = 2; - let _ = <>{a}{b}; - `, - sourceType: 'module', - tsx: true, - }, -]; - -function makeProgram(code: string, tsx = false): { program: ts.Program; sourceFile: ts.SourceFile } { - const fileName = tsx ? '/test.tsx' : '/test.ts'; - const sourceFile = ts.createSourceFile( - fileName, - code, - ts.ScriptTarget.Latest, - true, - tsx ? ts.ScriptKind.TSX : ts.ScriptKind.TS, - ); - const realLibPath = ts.getDefaultLibFilePath({ target: ts.ScriptTarget.Latest }); - const realLibName = realLibPath.split(/[\\/]/).pop()!; - const realLibContent = ts.sys.readFile(realLibPath) ?? ''; - const realLib = ts.createSourceFile(realLibPath, realLibContent, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS); - const host: ts.CompilerHost = { - getSourceFile(n) { - if (n === fileName) return sourceFile; - if (n === realLibPath) return realLib; - return undefined; - }, - getDefaultLibFileName: () => realLibName, - getDefaultLibLocation: () => realLibPath.replace('/' + realLibName, ''), - writeFile: () => {}, - getCurrentDirectory: () => '/', - getDirectories: () => [], - fileExists: n => n === fileName || n === realLibPath, - readFile: n => n === fileName ? code : (n === realLibPath ? realLibContent : undefined), - getCanonicalFileName: n => n, - useCaseSensitiveFileNames: () => true, - getNewLine: () => '\n', - }; - const program = ts.createProgram({ - rootNames: [fileName], - options: { - target: ts.ScriptTarget.Latest, - module: ts.ModuleKind.ESNext, - allowJs: false, - noLib: false, - jsx: tsx ? ts.JsxEmit.Preserve : undefined, - }, - host, - }); - return { program, sourceFile: program.getSourceFile(fileName)! }; -} - -function names(arr: { name: string }[]): string { - return arr.map(v => v.name).sort().join(','); -} - -function runFixture(fx: Fixture): string[] { - const { program, sourceFile } = makeProgram(fx.code, fx.tsx); - const { estree, astMaps } = astConverter(sourceFile, PARSE_SETTINGS, true); - const sourceType = fx.sourceType ?? 'module'; - estree.sourceType = sourceType; - // `lib: []` keeps upstream's globalScope from being populated with all the - // TS lib intrinsics — those don't exist in our `node.locals` view, so - // comparing them just spams noise. - const upstream = analyze(estree, { sourceType, childVisitorKeys: visitorKeys, lib: [] }); - const ours = new TsScopeManager(sourceFile, program, estree, astMaps, sourceType); - const diffs: string[] = []; - - // Walk both scope trees in parallel. They should be isomorphic (same shape, - // same type-and-block at each position). We compare scope-by-scope and - // recurse into children pairwise. - let idx = 0; - const visit = (uScope: any, oScope: TsScope | null) => { - const i = idx++; - const blockTag = uScope.block - ? `${uScope.block.type}:${uScope.block.range[0]}-${uScope.block.range[1]}` - : 'no-block'; - const tag = `[scope#${i} ${uScope.type} @${blockTag}]`; - if (!oScope) { - diffs.push(`${tag} ours has no matching scope (tree out of sync)`); - return; - } - if (oScope.type !== uScope.type) { - diffs.push(`${tag} type: upstream=${uScope.type} ours=${oScope.type}`); - } - const skipVars = uScope.type === 'global' || uScope.type === 'module'; - if (!skipVars) { - const uVarNames = names(uScope.variables); - const oVarNames = names(oScope.variables); - if (uVarNames !== oVarNames) { - diffs.push(`${tag} variables: upstream=[${uVarNames}] ours=[${oVarNames}]`); - } - } - for (const uVar of uScope.variables) { - const oVar = oScope.variables.find((v: any) => v.name === uVar.name); - if (!oVar) continue; - const uRefCount = uVar.references.length; - const oRefCount = oVar.references.length; - if (oRefCount < uRefCount) { - diffs.push( - `${tag} var '${uVar.name}' references: upstream=${uRefCount} ours=${oRefCount} (missing)`, - ); - } - if (uVar.writeable !== undefined && uVar.writeable !== oVar.writeable) { - diffs.push( - `${tag} var '${uVar.name}' writeable: upstream=${uVar.writeable} ours=${oVar.writeable}`, - ); - } - } - const uChildren = uScope.childScopes ?? []; - const oChildren = oScope.childScopes ?? []; - if (uChildren.length !== oChildren.length) { - diffs.push(`${tag} childScopes count: upstream=${uChildren.length} ours=${oChildren.length}`); - } - const n = Math.min(uChildren.length, oChildren.length); - for (let j = 0; j < n; j++) visit(uChildren[j], oChildren[j]); - }; - if (upstream.globalScope) visit(upstream.globalScope, ours.globalScope); - - return diffs; -} - -let totalDiffs = 0; -let totalPassed = 0; -for (const fx of fixtures) { - console.log(`\n--- ${fx.name} ---`); - try { - const diffs = runFixture(fx); - if (diffs.length === 0) { - console.log(' OK'); - totalPassed++; - } - else { - totalDiffs += diffs.length; - for (const d of diffs) console.log(' DIFF: ' + d); - } - } - catch (err) { - totalDiffs++; - console.log(' ERROR: ' + (err instanceof Error ? err.message : String(err))); - } -} - -console.log(`\n=== ${totalPassed}/${fixtures.length} fixtures clean, ${totalDiffs} diffs ===`); -process.exit(totalDiffs > 0 ? 1 : 0); diff --git a/packages/compat-eslint/test/ts-ast-scan.test.ts b/packages/compat-eslint/test/ts-ast-scan.test.ts index 27d0e005..ef8f2f24 100644 --- a/packages/compat-eslint/test/ts-ast-scan.test.ts +++ b/packages/compat-eslint/test/ts-ast-scan.test.ts @@ -2,7 +2,8 @@ // Run via: // node --experimental-strip-types --no-warnings packages/compat-eslint/test/ts-ast-scan.test.ts -import * as ts from 'typescript'; +import * as ts from '../lib/ts'; +import { parseTs as parseTs7 } from './ts7'; const lazy = require('../lib/lazy-estree.js') as typeof import('../lib/lazy-estree.js'); const { predicateForTriggerSet, hasPredicate, tsScanTraverse } = require( @@ -21,7 +22,7 @@ function check(name: string, cond: boolean, detail?: string) { } function parseTs(code: string): ts.SourceFile { - return ts.createSourceFile('/test.ts', code, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS); + return parseTs7(code); } // Collect the same `{phase, target}` shape tests previously got back from diff --git a/packages/compat-eslint/test/ts7.ts b/packages/compat-eslint/test/ts7.ts new file mode 100644 index 00000000..8940ba19 --- /dev/null +++ b/packages/compat-eslint/test/ts7.ts @@ -0,0 +1,81 @@ +// Shared TypeScript 7 parsing for tests. One API process per test run; +// everything created here stays alive until the process exits. +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; +import type { SourceFile } from 'typescript/ast'; +import { API, type Checker, type Program } from 'typescript/sync'; + +let api: API | undefined; +const tempDirs: string[] = []; + +function getApi(): API { + if (!api) { + api = new API({ cwd: process.cwd() }); + process.once('exit', () => { + // The child may already be gone when exiting via process.exit(). + try { + api?.close(); + } + catch {} + for (const dir of tempDirs) fs.rmSync(dir, { recursive: true, force: true }); + }); + } + return api; +} + +export function parse(fileName: string, code: string): SourceFile { + return getApi().createSourceFile(fileName, code).sourceFile; +} + +export function parseTs(code: string): SourceFile { + return parse('/test.ts', code); +} + +export function parseTsx(code: string): SourceFile { + return parse('/test.tsx', code); +} + +export interface TestProject { + program: Program; + checker: Checker; + file: SourceFile; +} + +// A real on-disk project holding a single file, so rules get the same +// program/checker pair the CLI hands them. +export function createProject( + fileName: string, + code: string, + compilerOptions: Record = {}, +): TestProject { + const dir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'tsslint-test-'))); + tempDirs.push(dir); + const filePath = path.join(dir, fileName); + fs.writeFileSync(filePath, code); + const tsconfig = path.join(dir, 'tsconfig.json'); + fs.writeFileSync( + tsconfig, + JSON.stringify({ + compilerOptions: { target: 'esnext', lib: ['esnext'], noEmit: true, ...compilerOptions }, + files: [fileName], + }), + ); + const snapshot = getApi().createSnapshot({ openProjects: [tsconfig] }); + const project = snapshot.getConfiguredProject(tsconfig)!; + return { program: project.program, checker: project.checker, file: project.program.getSourceFile(filePath)! }; +} + +// A project over existing files on disk (absolute paths). +export function createProjectForFiles( + files: string[], + compilerOptions: Record = {}, +): { program: Program; checker: Checker } { + const dir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'tsslint-test-'))); + tempDirs.push(dir); + const tsconfig = path.join(dir, 'tsconfig.json'); + fs.writeFileSync(tsconfig, JSON.stringify({ compilerOptions: { noEmit: true, ...compilerOptions }, files })); + const snapshot = getApi().createSnapshot({ openProjects: [tsconfig] }); + const project = snapshot.getConfiguredProject(tsconfig)!; + return { program: project.program, checker: project.checker }; +} diff --git a/packages/compat-eslint/test/upstream-runner.ts b/packages/compat-eslint/test/upstream-runner.ts deleted file mode 100644 index 989bce47..00000000 --- a/packages/compat-eslint/test/upstream-runner.ts +++ /dev/null @@ -1,458 +0,0 @@ -// Minimal test runner for porting upstream @typescript-eslint/scope-manager -// tests to validate TsScopeManager. The upstream tests are vitest-based; this -// shim implements just enough of the API surface (describe/it/expect + -// chai-style isScopeOfType) to load their test files unchanged. -// -// The upstream tests live under `test/upstream/` (vendored from -// typescript-eslint, MIT — see test/upstream/README.md). Override with -// TSE_TESTS_DIR if you want to point at a different copy. -// -// Usage: -// node packages/compat-eslint/test/upstream-runner.js [pattern] -// TSE_TESTS_DIR=/path/to/tests node packages/compat-eslint/test/upstream-runner.js -// -// Each test file calls `parseAndAnalyze(code)` from its own test-utils. We -// intercept the require of '../test-utils/index.js' (the path tests use) and -// substitute an adapter that returns a TsScopeManager-backed result instead -// of upstream's analyze(). - -import * as fs from 'fs'; -import * as path from 'path'; -import * as ts from 'typescript'; -const Module = require('module') as typeof import('module'); - -// Use the same lazy converter as production (`compat-eslint/index.ts`). -// TsScopeManager's tsToEstreeOrStub uses `materialize` (lazy) as fallback, -// so identity comparisons work as long as both sides go through the same -// WeakMap cache. -const { convertLazy } = require('../lib/lazy-estree.js') as { - convertLazy: (file: ts.SourceFile) => { astMaps: any; estree: any; context: any }; -}; -const { TsScopeManager } = require('../lib/ts-scope-manager.js') as typeof import('../lib/ts-scope-manager.js'); - -function buildHost(fileName: string, content: string): ts.CompilerHost { - const realLibPath = ts.getDefaultLibFilePath({ target: ts.ScriptTarget.Latest }); - const realLibName = realLibPath.split(/[\\/]/).pop()!; - const realLibContent = ts.sys.readFile(realLibPath) ?? ''; - const realLib = ts.createSourceFile(realLibPath, realLibContent, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS); - const sf = ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS); - return { - getSourceFile: n => n === fileName ? sf : (n === realLibPath ? realLib : undefined), - getDefaultLibFileName: () => realLibName, - getDefaultLibLocation: () => realLibPath.replace('/' + realLibName, ''), - writeFile: () => {}, - getCurrentDirectory: () => '/', - getDirectories: () => [], - fileExists: n => n === fileName || n === realLibPath, - readFile: n => n === fileName ? content : (n === realLibPath ? realLibContent : undefined), - getCanonicalFileName: n => n, - useCaseSensitiveFileNames: () => true, - getNewLine: () => '\n', - }; -} - -function parseAndAnalyze(code: string, options?: any) { - const fileName = '/test.ts'; - const host = buildHost(fileName, code); - const program = ts.createProgram({ - rootNames: [fileName], - options: { target: ts.ScriptTarget.Latest, module: ts.ModuleKind.ESNext, allowJs: false }, - host, - }); - const sf = program.getSourceFile(fileName)!; - const { astMaps, estree } = convertLazy(sf); - let sourceType: 'module' | 'script' = 'script'; - if (typeof options === 'string') sourceType = options as 'module' | 'script'; - else if (options && typeof options === 'object' && options.sourceType) sourceType = options.sourceType; - (estree as { sourceType: 'module' | 'script' }).sourceType = sourceType; - const scopeManager = new TsScopeManager(sf, program, estree, astMaps, sourceType); - return { ast: estree, scopeManager }; -} - -// --- Minimal vitest/chai shims ------------------------------------------- - -interface TestResult { - name: string; - pass: boolean; - error?: string; -} - -const allResults: TestResult[] = []; -let currentSuite: string[] = []; - -(globalThis as any).describe = (name: string, fn: () => void) => { - currentSuite.push(name); - try { - fn(); - } - finally { - currentSuite.pop(); - } -}; - -(globalThis as any).it = (name: string, fn: () => void | Promise) => { - const fullName = [...currentSuite, name].join(' > '); - try { - const r = fn(); - if (r && typeof (r as any).then === 'function') { - throw new Error('async tests not supported in this runner'); - } - allResults.push({ name: fullName, pass: true }); - } - catch (err) { - const msg = err instanceof Error ? (err.stack ?? err.message) : String(err); - allResults.push({ name: fullName, pass: false, error: msg }); - } -}; - -(globalThis as any).it.skip = (_name: string, _fn: any) => {}; -// vitest's `it.for([cases])(name, fn)` for parametrized tests. fn receives -// `(case, ctx)` where ctx has { expect, ... }. We forward our own expect. -(globalThis as any).it.for = (cases: any[]) => (name: string, fn: (c: any, ctx: any) => void) => { - for (const c of cases) { - (globalThis as any).it(`${name} (${ser(c)})`, () => fn(c, { expect: (globalThis as any).expect })); - } -}; - -class Expect { - constructor(public actual: any, public negated = false) {} - get not() { - return new Expect(this.actual, !this.negated); - } - private check(cond: boolean, msg: string) { - const ok = this.negated ? !cond : cond; - if (!ok) throw new Error(msg + (this.negated ? ' (negated)' : '')); - } - toBe(expected: any) { - this.check(Object.is(this.actual, expected), `Expected ${ser(this.actual)} === ${ser(expected)}`); - } - toStrictEqual(expected: any) { - this.check(deepEqual(this.actual, expected), `Expected ${ser(this.actual)} ≡ ${ser(expected)}`); - } - toHaveLength(n: number) { - this.check(this.actual?.length === n, `Expected length ${this.actual?.length} === ${n}`); - } - toBeNull() { - this.check(this.actual === null, `Expected null, got ${ser(this.actual)}`); - } - toBeUndefined() { - this.check(this.actual === undefined, `Expected undefined, got ${ser(this.actual)}`); - } - toBeTruthy() { - this.check(!!this.actual, `Expected truthy, got ${ser(this.actual)}`); - } - toBeFalsy() { - this.check(!this.actual, `Expected falsy, got ${ser(this.actual)}`); - } - toBeGreaterThanOrEqual(n: number) { - this.check(this.actual >= n, `Expected ${this.actual} >= ${n}`); - } - toContain(item: any) { - this.check(this.actual?.includes?.(item), `Expected ${ser(this.actual)} to contain ${ser(item)}`); - } - toHaveDeclaredVariables(_names: string[]) { - // Custom matcher used in get-declared-variables tests; defer / soft-pass for now. - this.check(true, 'toHaveDeclaredVariables stub'); - } - toMatchInlineSnapshot(_expected?: string) { - // Snapshot tests — soft-pass since we can't update snapshots. - this.check(true, 'snapshot stub'); - } -} - -function ser(v: any): string { - if (v == null) return String(v); - if (typeof v === 'string') return JSON.stringify(v); - if (typeof v !== 'object') return String(v); - if (v.type && v.name) return `<${v.type} ${v.name}>`; - if (Array.isArray(v)) return `[len=${v.length}]`; - return `<${v.constructor?.name ?? 'object'}>`; -} - -function deepEqual(a: any, b: any): boolean { - if (Object.is(a, b)) return true; - if (typeof a !== 'object' || typeof b !== 'object' || !a || !b) return false; - if (Array.isArray(a) && Array.isArray(b)) { - if (a.length !== b.length) return false; - for (let i = 0; i < a.length; i++) if (!deepEqual(a[i], b[i])) return false; - return true; - } - if (a.constructor !== b.constructor) return false; - const ka = Object.keys(a); - const kb = Object.keys(b); - if (ka.length !== kb.length) return false; - for (const k of ka) if (!deepEqual(a[k], b[k])) return false; - return true; -} - -(globalThis as any).expect = (actual: any) => new Expect(actual); - -// chai.assert.isScopeOfType(scope, type) — used in eslint-scope tests. -(globalThis as any).assert = { - isScopeOfType(scope: any, type: string) { - if (scope?.type !== type) { - throw new Error(`Expected scope.type === ${type}, got ${scope?.type}`); - } - }, - isNotScopeOfType(scope: any, type: string) { - if (scope?.type === type) { - throw new Error(`Expected scope.type !== ${type}`); - } - }, - isDefinitionOfType(def: any, type: string) { - if (def?.type !== type) { - throw new Error(`Expected def.type === ${type}, got ${def?.type}`); - } - }, - isNodeOfType(node: any, type: string) { - if (node?.type !== type) { - throw new Error(`Expected node.type === ${type}, got ${node?.type}`); - } - }, - exists(v: any) { - if (v == null) throw new Error(`Expected non-null, got ${ser(v)}`); - }, - notExists(v: any) { - if (v != null) throw new Error(`Expected null/undefined, got ${ser(v)}`); - }, - isNull(v: any) { - if (v !== null) throw new Error(`Expected null, got ${ser(v)}`); - }, - isNotNull(v: any) { - if (v === null) throw new Error(`Expected non-null, got null`); - }, - isTrue(v: any) { - if (v !== true) throw new Error(`Expected true, got ${ser(v)}`); - }, - isFalse(v: any) { - if (v !== false) throw new Error(`Expected false, got ${ser(v)}`); - }, -}; - -// --- Module loader interception ----------------------------------------- - -// Tests do `import { ScopeType, ... } from '../../src/index.js'`. We swap that -// for a tiny shim exporting matching constants/types and our parseAndAnalyze. -const REAL_RESOLVE = (Module as any)._resolveFilename; -(Module as any)._resolveFilename = function(req: string, parent: any, ...rest: any[]) { - if ( - req === '../../src/index.js' || req === '../../../src/index.js' || req === '../test-utils/index.js' - || req === '../../test-utils/index.js' - ) { - return path.resolve(__dirname, 'upstream-shim.js'); - } - return REAL_RESOLVE.call(this, req, parent, ...rest); -}; - -// upstream-shim — generated next to the runner. -const SHIM_PATH = path.resolve(__dirname, 'upstream-shim.js'); -fs.writeFileSync( - SHIM_PATH, - '"use strict";\n' - + 'const { parseAndAnalyze, ScopeType, DefinitionType, getRealVariables } = require(' - + JSON.stringify(__filename) + ');\n' - + 'module.exports = { parseAndAnalyze, ScopeType, DefinitionType, getRealVariables };\n', -); - -export const ScopeType = { - block: 'block', - 'catch': 'catch', - 'class': 'class', - classFieldInitializer: 'class-field-initializer', - conditionalType: 'conditional-type', - 'for': 'for', - 'function': 'function', - functionExpressionName: 'function-expression-name', - functionType: 'function-type', - global: 'global', - mappedType: 'mapped-type', - module: 'module', - 'switch': 'switch', - tsEnum: 'tsEnum', - tsModule: 'tsModule', - type: 'type', - 'with': 'with', -} as const; - -export const DefinitionType = { - CatchClause: 'CatchClause', - ClassName: 'ClassName', - FunctionName: 'FunctionName', - ImplicitGlobalVariable: 'ImplicitGlobalVariable', - ImportBinding: 'ImportBinding', - Parameter: 'Parameter', - TDZ: 'TDZ', - Variable: 'Variable', - Type: 'Type', - TSEnumName: 'TSEnumName', - TSEnumMember: 'TSEnumMember', - TSModuleName: 'TSModuleName', -} as const; - -export function getRealVariables(variables: any[]): any[] { - // Filter synthetic 'arguments' and lib globals (matches upstream's - // ImplicitLibVariable filter — ours has no class but we tag manager-side). - return variables; -} - -export { parseAndAnalyze }; - -// --- Run -------------------------------------------------------------- - -const pattern = process.argv[2]; -const testsRoot = process.env.TSE_TESTS_DIR - ?? path.resolve(__dirname, 'upstream'); -if (!fs.existsSync(testsRoot)) { - console.error( - `Upstream tests not found at ${testsRoot}.\n` - + 'See the comment at the top of upstream-runner.ts for setup steps.', - ); - process.exit(2); -} -const testFiles = collectTests(testsRoot, pattern); - -// The test files are .ts with `import` syntax. Pre-process each into a CJS -// snippet (strip types + rewrite import → require) and run it via vm. -function loadTestFile(filePath: string) { - const source = fs.readFileSync(filePath, 'utf8'); - // Strip TypeScript types — let TS itself transpile (faster than regex). - const transpiled = ts.transpileModule(source, { - compilerOptions: { - module: ts.ModuleKind.CommonJS, - target: ts.ScriptTarget.ES2022, - }, - fileName: filePath, - }).outputText; - const wrapped = `(function(module, exports, require, __filename, __dirname){\n${transpiled}\n})`; - const fn = (0, eval)(wrapped); - const mod = { exports: {} }; - fn( - mod, - mod.exports, - (req: string) => { - // Tests reference '../../src/index.js' or '../test-utils/index.js' — - // route both to our shim. Other requires resolve from this package - // (so '@typescript-eslint/types' etc. find our node_modules). - if ( - req === '../../src/index.js' - || req === '../../../src/index.js' - || req === '../test-utils/index.js' - || req === '../../test-utils/index.js' - ) { - return require(SHIM_PATH); - } - return require(req); - }, - filePath, - path.dirname(filePath), - ); -} - -console.log(`Running ${testFiles.length} test files...\n`); -for (const f of testFiles) { - console.log(`--- ${path.relative(testsRoot, f)} ---`); - const before = allResults.length; - try { - loadTestFile(f); - } - catch (err) { - console.log(' LOAD ERROR: ' + (err instanceof Error ? err.message : String(err))); - } - const after = allResults.length; - const fp = allResults.slice(before, after).filter(r => r.pass).length; - const ff = allResults.slice(before, after).filter(r => !r.pass).length; - console.log(` ${fp} passed, ${ff} failed`); -} - -// Known divergences from upstream's `@typescript-eslint/scope-manager` -// test suite. Each entry is the full test name (the format the -// runner prints with `-v`). Adding to this list is a deliberate -// policy decision: either the divergence is ESLint-specific and -// out of TS scope (`globalReturn`, `impliedStrict`), or it's a -// shape divergence we've decided not to fix because no real-world -// rule depends on it (destructure default-value double-reference, -// off by 1–3 in count). New regressions OUTSIDE this list fail -// the runner; unexpected passes (entry exists but test now passes) -// also fail — the list must stay accurate. -const KNOWN_DIVERGENCES: Record = { - // `[c=d]` default-value destructure: upstream emits TWO references - // per binding identifier (one read for `c`, one write for the - // binding); TS's checker exposes one identifier→symbol mapping per - // occurrence. Off by 1–3 in count. No real rule we tested cared. - 'ES6 destructuring assignments > Pattern with default values in var in ForInStatement': - 'destructure double-ref divergence', - 'ES6 destructuring assignments > Pattern with default values in let in ForInStatement': - 'destructure double-ref divergence', - 'ES6 destructuring assignments > Pattern with nested default values in var in ForInStatement': - 'destructure double-ref divergence', - 'ES6 destructuring assignments > Pattern with nested default values in let in ForInStatement': - 'destructure double-ref divergence', - 'ES6 destructuring assignments > Pattern with default values in var in ForInStatement (separate declarations)': - 'destructure double-ref divergence', - 'ES6 destructuring assignments > default values and patterns in var': 'destructure double-ref divergence', - 'ES6 destructuring assignments > default values containing references and patterns in var': - 'destructure double-ref divergence', - 'ES6 destructuring assignments > nested default values containing references and patterns in var': - 'destructure double-ref divergence', - - // ESLint's `globalReturn` option synthesizes a function scope - // around the program (CommonJS-wrapper convention). Not a TS - // concept; TSSLint doesn't support the option and never will. - 'globalReturn option > creates a function scope following the global scope immediately': - 'ESLint-only globalReturn option, out of TS scope', - 'globalReturn option > creates a function scope following the global scope immediately and creates module scope': - 'ESLint-only globalReturn option, out of TS scope', - - // ESLint's `impliedStrict` option forces strict mode everywhere. - // TS handles strict mode via its own compiler options; we don't - // re-implement the ESLint flag. - 'impliedStrict option > ensures all user scopes are strict': 'ESLint-only impliedStrict option, out of TS scope', - 'impliedStrict option > omits a nodejs global scope when ensuring all user scopes are strict': - 'ESLint-only impliedStrict option, out of TS scope', - 'impliedStrict option > omits a module global scope when ensuring all user scopes are strict': - 'ESLint-only impliedStrict option, out of TS scope', -}; - -const passed = allResults.filter(r => r.pass).length; -const realFailures = allResults.filter(r => !r.pass && !KNOWN_DIVERGENCES[r.name]); -const knownFailures = allResults.filter(r => !r.pass && KNOWN_DIVERGENCES[r.name]); -const unexpectedPasses = allResults.filter(r => r.pass && KNOWN_DIVERGENCES[r.name]); - -console.log( - `\n=== ${passed}/${allResults.length} passed (${knownFailures.length} known divergences, ${realFailures.length} regressions) ===`, -); - -if (process.argv.includes('-v') && (realFailures.length || unexpectedPasses.length)) { - if (realFailures.length) { - console.log('\nRegressions (need fix or KNOWN_DIVERGENCES entry):'); - for (const r of realFailures) { - const firstLine = (r.error ?? '').split('\n')[0]; - console.log(` - ${r.name}: ${firstLine}`); - } - } - if (unexpectedPasses.length) { - console.log('\nUnexpected passes (remove from KNOWN_DIVERGENCES):'); - for (const r of unexpectedPasses) console.log(` - ${r.name}`); - } -} - -process.exit((realFailures.length || unexpectedPasses.length) ? 1 : 0); - -function collectTests(dir: string, pat?: string): string[] { - const out: string[] = []; - const walk = (d: string) => { - for (const e of fs.readdirSync(d, { withFileTypes: true })) { - const full = path.join(d, e.name); - if (e.isDirectory()) { - if (e.name === '__snapshots__' || e.name === 'fixtures' || e.name === 'test-utils') continue; - walk(full); - } - else if (e.name.endsWith('.test.ts')) { - if (pat && !full.includes(pat)) continue; - out.push(full); - } - } - }; - walk(dir); - return out; -} diff --git a/packages/compat-eslint/test/upstream/README.md b/packages/compat-eslint/test/upstream/README.md deleted file mode 100644 index e4527c4b..00000000 --- a/packages/compat-eslint/test/upstream/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# Upstream scope-manager tests - -These are the tests from `@typescript-eslint/scope-manager` (typescript-eslint -v8.x), used to validate that `TsScopeManager` is structurally compatible with -the upstream analyzer. - -Source: https://github.com/typescript-eslint/typescript-eslint/tree/main/packages/scope-manager/tests - -Licensed under MIT (see https://github.com/typescript-eslint/typescript-eslint/blob/main/LICENSE). -The test files are vendored unchanged so their import paths -(`../../src/index.js`, `../test-utils/index.js`) are intercepted by -`upstream-runner.ts`, which routes them to a TsScopeManager-backed shim. - -Run with: `node packages/compat-eslint/test/upstream-runner.js [pattern]` - -## Known failures (212/225 passing) - -The 13 remaining failures fall into three buckets, none of which are bugs -in `TsScopeManager`: - -- **ES6 destructuring with default values (8 tests)**: upstream's analyzer - emits two references for each binding identifier in patterns like - `[c=d]` (one for the read of `c`, one for the write). TS's checker - produces a single identifier-symbol mapping per occurrence, so we emit - one reference. Reflected in counts being off by 1–3. -- **`globalReturn` option (2 tests)**: ESLint's CommonJS-wrapper - convention that synthesizes a function scope around the program. Not a - TS concept; we do not support the option. -- **`impliedStrict` option (3 tests)**: ESLint's flag forcing strict mode - on all scopes regardless of source. Not a TS concept; we do not support - the option. - -(The "ESTree-traversal of type-position nodes" bucket from earlier is -gone: `astConvertSkipTypes` was removed when the lazy ESTree shim -became default — see `lib/lazy-estree.ts`. The runner uses -typescript-estree's eager `astConverter` directly so type subtrees are -fully populated.) diff --git a/packages/compat-eslint/test/upstream/analyze.test.ts b/packages/compat-eslint/test/upstream/analyze.test.ts deleted file mode 100644 index 6ed7478d..00000000 --- a/packages/compat-eslint/test/upstream/analyze.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { analyze } from '../src'; -import { parse } from './test-utils'; - -describe('analyze()', () => { - it('should throw error when passed a partial AST node', () => { - const ast = parse(`const hello: string = 'world';`, { - range: true, - }); - const partialNode = ast.body[0]; - - expect(() => { - // @ts-expect-error -- to reproduce https://github.com/typescript-eslint/typescript-eslint/issues/11727 - analyze(partialNode); - }).toThrowError(); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/README.md b/packages/compat-eslint/test/upstream/eslint-scope/README.md deleted file mode 100644 index cbd9986a..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# `scope-manager` `eslint-scope` Tests - -These tests are taken from the [`eslint-scope`](https://github.com/eslint/eslint-scope) package. -The intention is to help us ensure we do not regress functionality compared to the original package. - -They have been modified to: - -- be written in TypeScript -- work with our folder structure -- adhere to our formatting and linting style - -[The code was forked from `dbddf14d5771b21b5da704213e4508c660ca1c64`](https://github.com/eslint/eslint-scope/blob/dbddf14d5771b21b5da704213e4508c660ca1c64/tests/). diff --git a/packages/compat-eslint/test/upstream/eslint-scope/add-globals.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/add-globals.test.ts deleted file mode 100644 index 1d4b8ed6..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/add-globals.test.ts +++ /dev/null @@ -1,433 +0,0 @@ -/* eslint-disable @typescript-eslint/dot-notation -- ['implicit'] is private */ - -import { ScopeType } from '../../src/index.js'; -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('ScopeManager#addGlobals', () => { - it('adds variables to the global scope and resolves references from the global scope', () => { - const { scopeManager } = parseAndAnalyze(` - foo = bar + bar; - `); - - expect(scopeManager.scopes).toHaveLength(1); - - const globalScope = scopeManager.scopes[0]; - assert.isScopeOfType(globalScope, ScopeType.global); - - const variables = getRealVariables(globalScope.variables); - const typeVariableAmount = globalScope.variables.length - variables.length; - expect(variables).toHaveLength(0); - expect(globalScope.set.size).toBe(typeVariableAmount); - expect(globalScope.references).toHaveLength(3); - expect(globalScope.references[0].identifier.name).toBe('foo'); - expect(globalScope.references[0].from).toStrictEqual(globalScope); - expect(globalScope.references[0].resolved).toBeNull(); - expect(globalScope.references[1].identifier.name).toBe('bar'); - expect(globalScope.references[1].from).toStrictEqual(globalScope); - expect(globalScope.references[1].resolved).toBeNull(); - expect(globalScope.references[2].identifier.name).toBe('bar'); - expect(globalScope.references[2].from).toStrictEqual(globalScope); - expect(globalScope.references[2].resolved).toBeNull(); - expect(globalScope.references[1]).not.toStrictEqual( - globalScope.references[2], - ); - expect(globalScope.through).toHaveLength(3); - expect(globalScope.through[0]).toStrictEqual(globalScope.references[0]); - expect(globalScope.through[1]).toStrictEqual(globalScope.references[1]); - expect(globalScope.through[2]).toStrictEqual(globalScope.references[2]); - expect(globalScope['implicit'].variables).toHaveLength(1); - expect(globalScope['implicit'].variables[0].name).toBe('foo'); - expect(globalScope['implicit'].variables[0].references).toHaveLength(0); - expect(globalScope['implicit'].variables[0].defs).toHaveLength(1); - expect(globalScope['implicit'].variables[0].identifiers).toHaveLength(1); - expect(globalScope['implicit'].set.size).toBe(1); - expect(globalScope['implicit'].set.get('foo')).toStrictEqual( - globalScope['implicit'].variables[0], - ); - expect(globalScope['implicit'].leftToBeResolved).toHaveLength(3); - expect(globalScope['implicit'].leftToBeResolved[0]).toStrictEqual( - globalScope.references[0], - ); - expect(globalScope['implicit'].leftToBeResolved[1]).toStrictEqual( - globalScope.references[1], - ); - expect(globalScope['implicit'].leftToBeResolved[2]).toStrictEqual( - globalScope.references[2], - ); - - scopeManager.addGlobals(['foo', 'bar']); - - const withAddedVariables = getRealVariables(scopeManager.variables); - expect(withAddedVariables).toHaveLength(2); - expect(withAddedVariables[0].name).toBe('foo'); - expect(withAddedVariables[0].scope).toStrictEqual(globalScope); - expect(withAddedVariables[0].defs).toHaveLength(0); - expect(withAddedVariables[0].identifiers).toHaveLength(0); - expect(withAddedVariables[1].name).toBe('bar'); - expect(withAddedVariables[1].scope).toStrictEqual(globalScope); - expect(withAddedVariables[1].defs).toHaveLength(0); - expect(withAddedVariables[1].identifiers).toHaveLength(0); - expect(globalScope.set.size).toBe(typeVariableAmount + 2); - expect(globalScope.set.get('foo')).toStrictEqual(withAddedVariables[0]); - expect(globalScope.set.get('bar')).toStrictEqual(withAddedVariables[1]); - expect(globalScope.references).toHaveLength(3); - expect(globalScope.references[0].identifier.name).toBe('foo'); - expect(globalScope.references[0].from).toStrictEqual(globalScope); - expect(globalScope.references[0].resolved).toStrictEqual( - withAddedVariables[0], - ); - expect(withAddedVariables[0].references).toHaveLength(1); - expect(withAddedVariables[0].references[0]).toStrictEqual( - globalScope.references[0], - ); - expect(globalScope.references[1].identifier.name).toBe('bar'); - expect(globalScope.references[1].from).toStrictEqual(globalScope); - expect(globalScope.references[1].resolved).toStrictEqual( - withAddedVariables[1], - ); - expect(globalScope.references[2].identifier.name).toBe('bar'); - expect(globalScope.references[2].from).toStrictEqual(globalScope); - expect(globalScope.references[2].resolved).toStrictEqual( - withAddedVariables[1], - ); - expect(withAddedVariables[1].references).toHaveLength(2); - expect(withAddedVariables[1].references[0]).toStrictEqual( - globalScope.references[1], - ); - expect(withAddedVariables[1].references[1]).toStrictEqual( - globalScope.references[2], - ); - expect(globalScope.through).toHaveLength(0); - expect(globalScope['implicit'].variables).toHaveLength(0); - expect(globalScope['implicit'].set.size).toBe(0); - expect(globalScope['implicit'].leftToBeResolved).toHaveLength(0); - }); - - it('adds variables to the global scope and resolves references from inner scopes', () => { - const { scopeManager } = parseAndAnalyze(` - () => foo = bar + bar; - `); - - expect(scopeManager.scopes).toHaveLength(2); - - const globalScope = scopeManager.scopes[0]; - assert.isScopeOfType(globalScope, ScopeType.global); - - expect(globalScope.type).toBe('global'); - - const functionScope = scopeManager.scopes[1]; - - expect(functionScope.type).toBe('function'); - - expect(functionScope.variables).toHaveLength(0); - expect(functionScope.set.size).toBe(0); - expect(functionScope.references).toHaveLength(3); - expect(functionScope.references[0].identifier.name).toBe('foo'); - expect(functionScope.references[0].from).toStrictEqual(functionScope); - expect(functionScope.references[0].resolved).toBeNull(); - expect(functionScope.references[1].identifier.name).toBe('bar'); - expect(functionScope.references[1].from).toStrictEqual(functionScope); - expect(functionScope.references[1].resolved).toBeNull(); - expect(functionScope.references[2].identifier.name).toBe('bar'); - expect(functionScope.references[2].from).toStrictEqual(functionScope); - expect(functionScope.references[2].resolved).toBeNull(); - expect(functionScope.references[1]).not.toStrictEqual( - functionScope.references[2], - ); - expect(functionScope.through).toHaveLength(3); - expect(functionScope.through[0]).toStrictEqual(functionScope.references[0]); - expect(functionScope.through[1]).toStrictEqual(functionScope.references[1]); - expect(functionScope.through[2]).toStrictEqual(functionScope.references[2]); - - const variables = getRealVariables(globalScope.variables); - const typeVariableAmount = globalScope.variables.length - variables.length; - expect(variables).toHaveLength(0); - expect(globalScope.set.size).toBe(typeVariableAmount); - expect(globalScope.references).toHaveLength(0); - expect(globalScope.through).toHaveLength(3); - expect(globalScope.through[0]).toStrictEqual(functionScope.references[0]); - expect(globalScope.through[1]).toStrictEqual(functionScope.references[1]); - expect(globalScope.through[2]).toStrictEqual(functionScope.references[2]); - expect(globalScope['implicit'].variables).toHaveLength(1); - expect(globalScope['implicit'].variables[0].name).toBe('foo'); - expect(globalScope['implicit'].variables[0].references).toHaveLength(0); - expect(globalScope['implicit'].variables[0].defs).toHaveLength(1); - expect(globalScope['implicit'].variables[0].identifiers).toHaveLength(1); - expect(globalScope['implicit'].set.size).toBe(1); - expect(globalScope['implicit'].set.get('foo')).toStrictEqual( - globalScope['implicit'].variables[0], - ); - expect(globalScope['implicit'].leftToBeResolved).toHaveLength(3); - expect(globalScope['implicit'].leftToBeResolved[0]).toStrictEqual( - functionScope.references[0], - ); - expect(globalScope['implicit'].leftToBeResolved[1]).toStrictEqual( - functionScope.references[1], - ); - expect(globalScope['implicit'].leftToBeResolved[2]).toStrictEqual( - functionScope.references[2], - ); - - scopeManager.addGlobals(['foo', 'bar']); - - const withAddedVariables = getRealVariables(globalScope.variables); - expect(withAddedVariables).toHaveLength(2); - expect(withAddedVariables[0].name).toBe('foo'); - expect(withAddedVariables[0].scope).toStrictEqual(globalScope); - expect(withAddedVariables[0].defs).toHaveLength(0); - expect(withAddedVariables[0].identifiers).toHaveLength(0); - expect(withAddedVariables[1].name).toBe('bar'); - expect(withAddedVariables[1].scope).toStrictEqual(globalScope); - expect(withAddedVariables[1].defs).toHaveLength(0); - expect(withAddedVariables[1].identifiers).toHaveLength(0); - expect(globalScope.set.size).toBe(typeVariableAmount + 2); - expect(globalScope.set.get('foo')).toStrictEqual(withAddedVariables[0]); - expect(globalScope.set.get('bar')).toStrictEqual(withAddedVariables[1]); - expect(functionScope.variables).toHaveLength(0); - expect(functionScope.set.size).toBe(0); - expect(functionScope.references).toHaveLength(3); - expect(functionScope.references[0].identifier.name).toBe('foo'); - expect(functionScope.references[0].from).toStrictEqual(functionScope); - expect(functionScope.references[0].resolved).toStrictEqual( - withAddedVariables[0], - ); - expect(withAddedVariables[0].references).toHaveLength(1); - expect(withAddedVariables[0].references[0]).toStrictEqual( - functionScope.references[0], - ); - expect(functionScope.references[1].identifier.name).toBe('bar'); - expect(functionScope.references[1].from).toStrictEqual(functionScope); - expect(functionScope.references[1].resolved).toStrictEqual( - withAddedVariables[1], - ); - expect(functionScope.references[2].identifier.name).toBe('bar'); - expect(functionScope.references[2].from).toStrictEqual(functionScope); - expect(functionScope.references[2].resolved).toStrictEqual( - withAddedVariables[1], - ); - expect(functionScope.references[1]).not.toStrictEqual( - functionScope.references[2], - ); - expect(withAddedVariables[1].references).toHaveLength(2); - expect(withAddedVariables[1].references[0]).toStrictEqual( - functionScope.references[1], - ); - expect(withAddedVariables[1].references[1]).toStrictEqual( - functionScope.references[2], - ); - expect(functionScope.through).toHaveLength(3); - expect(functionScope.through[0]).toStrictEqual(functionScope.references[0]); - expect(functionScope.through[1]).toStrictEqual(functionScope.references[1]); - expect(functionScope.through[2]).toStrictEqual(functionScope.references[2]); - expect(globalScope.references).toHaveLength(0); - expect(globalScope.through).toHaveLength(0); - expect(globalScope['implicit'].variables).toHaveLength(0); - expect(globalScope['implicit'].set.size).toBe(0); - expect(globalScope['implicit'].leftToBeResolved).toHaveLength(0); - }); - - it("adds variables to the global scope and doesn't affect unrelated references", () => { - const { scopeManager } = parseAndAnalyze(` - foo = bar + bar; - `); - - expect(scopeManager.scopes).toHaveLength(1); - - const globalScope = scopeManager.scopes[0]; - assert.isScopeOfType(globalScope, ScopeType.global); - - expect(globalScope.type).toBe('global'); - - const variables = getRealVariables(globalScope.variables); - const typeVariableAmount = globalScope.variables.length - variables.length; - expect(variables).toHaveLength(0); - expect(globalScope.set.size).toBe(typeVariableAmount); - expect(globalScope.references).toHaveLength(3); - expect(globalScope.references[0].identifier.name).toBe('foo'); - expect(globalScope.references[0].from).toStrictEqual(globalScope); - expect(globalScope.references[0].resolved).toBeNull(); - expect(globalScope.references[1].identifier.name).toBe('bar'); - expect(globalScope.references[1].from).toStrictEqual(globalScope); - expect(globalScope.references[1].resolved).toBeNull(); - expect(globalScope.references[2].identifier.name).toBe('bar'); - expect(globalScope.references[2].from).toStrictEqual(globalScope); - expect(globalScope.references[2].resolved).toBeNull(); - expect(globalScope.references[1]).not.toStrictEqual( - globalScope.references[2], - ); - expect(globalScope.through).toHaveLength(3); - expect(globalScope.through[0]).toStrictEqual(globalScope.references[0]); - expect(globalScope.through[1]).toStrictEqual(globalScope.references[1]); - expect(globalScope.through[2]).toStrictEqual(globalScope.references[2]); - expect(globalScope['implicit'].variables).toHaveLength(1); - expect(globalScope['implicit'].variables[0].name).toBe('foo'); - expect(globalScope['implicit'].variables[0].references).toHaveLength(0); - expect(globalScope['implicit'].variables[0].defs).toHaveLength(1); - expect(globalScope['implicit'].variables[0].identifiers).toHaveLength(1); - expect(globalScope['implicit'].set.size).toBe(1); - expect(globalScope['implicit'].set.get('foo')).toStrictEqual( - globalScope['implicit'].variables[0], - ); - expect(globalScope['implicit'].leftToBeResolved).toHaveLength(3); - expect(globalScope['implicit'].leftToBeResolved[0]).toStrictEqual( - globalScope.references[0], - ); - expect(globalScope['implicit'].leftToBeResolved[1]).toStrictEqual( - globalScope.references[1], - ); - expect(globalScope['implicit'].leftToBeResolved[2]).toStrictEqual( - globalScope.references[2], - ); - - scopeManager.addGlobals(['baz', 'qux']); - - const withAddedVariables = getRealVariables(scopeManager.variables); - expect(withAddedVariables).toHaveLength(2); - expect(withAddedVariables[0].name).toBe('baz'); - expect(withAddedVariables[0].scope).toStrictEqual(globalScope); - expect(withAddedVariables[0].defs).toHaveLength(0); - expect(withAddedVariables[0].identifiers).toHaveLength(0); - expect(withAddedVariables[0].references).toHaveLength(0); - expect(withAddedVariables[1].name).toBe('qux'); - expect(withAddedVariables[1].scope).toStrictEqual(globalScope); - expect(withAddedVariables[1].defs).toHaveLength(0); - expect(withAddedVariables[1].identifiers).toHaveLength(0); - expect(withAddedVariables[1].references).toHaveLength(0); - expect(globalScope.set.size).toBe(typeVariableAmount + 2); - expect(globalScope.set.get('baz')).toStrictEqual(withAddedVariables[0]); - expect(globalScope.set.get('qux')).toStrictEqual(withAddedVariables[1]); - expect(globalScope.references).toHaveLength(3); - expect(globalScope.references[0].identifier.name).toBe('foo'); - expect(globalScope.references[0].from).toStrictEqual(globalScope); - expect(globalScope.references[0].resolved).toBeNull(); - expect(globalScope.references[1].identifier.name).toBe('bar'); - expect(globalScope.references[1].from).toStrictEqual(globalScope); - expect(globalScope.references[1].resolved).toBeNull(); - expect(globalScope.references[2].identifier.name).toBe('bar'); - expect(globalScope.references[2].from).toStrictEqual(globalScope); - expect(globalScope.references[2].resolved).toBeNull(); - expect(globalScope.references[1]).not.toStrictEqual( - globalScope.references[2], - ); - expect(globalScope.through).toHaveLength(3); - expect(globalScope.through[0]).toStrictEqual(globalScope.references[0]); - expect(globalScope.through[1]).toStrictEqual(globalScope.references[1]); - expect(globalScope.through[2]).toStrictEqual(globalScope.references[2]); - expect(globalScope['implicit'].variables).toHaveLength(1); - expect(globalScope['implicit'].variables[0].name).toBe('foo'); - expect(globalScope['implicit'].variables[0].references).toHaveLength(0); - expect(globalScope['implicit'].variables[0].defs).toHaveLength(1); - expect(globalScope['implicit'].variables[0].identifiers).toHaveLength(1); - expect(globalScope['implicit'].set.size).toBe(1); - expect(globalScope['implicit'].set.get('foo')).toStrictEqual( - globalScope['implicit'].variables[0], - ); - expect(globalScope['implicit'].leftToBeResolved).toHaveLength(3); - expect(globalScope['implicit'].leftToBeResolved[0]).toStrictEqual( - globalScope.references[0], - ); - expect(globalScope['implicit'].leftToBeResolved[1]).toStrictEqual( - globalScope.references[1], - ); - expect(globalScope['implicit'].leftToBeResolved[2]).toStrictEqual( - globalScope.references[2], - ); - }); - - it("doesn't affect already declared global variables", () => { - const { scopeManager } = parseAndAnalyze( - ` - let foo = bar + bar; - var bar; - `, - 'script', - ); - - expect(scopeManager.scopes).toHaveLength(1); - - const globalScope = scopeManager.scopes[0]; - assert.isScopeOfType(globalScope, ScopeType.global); - - expect(globalScope.type).toBe('global'); - - const variables = getRealVariables(globalScope.variables); - const typeVariableAmount = globalScope.variables.length - variables.length; - expect(variables).toHaveLength(2); - expect(variables[0].name).toBe('foo'); - expect(variables[0].scope).toStrictEqual(globalScope); - expect(variables[0].defs).toHaveLength(1); - expect(variables[0].identifiers).toHaveLength(1); - expect(variables[1].name).toBe('bar'); - expect(variables[1].scope).toStrictEqual(globalScope); - expect(variables[1].defs).toHaveLength(1); - expect(variables[1].identifiers).toHaveLength(1); - expect(globalScope.set.size).toBe(typeVariableAmount + 2); - expect(globalScope.set.get('foo')).toStrictEqual(variables[0]); - expect(globalScope.set.get('bar')).toStrictEqual(variables[1]); - expect(globalScope.references).toHaveLength(3); - expect(globalScope.references[0].identifier.name).toBe('foo'); - expect(globalScope.references[0].from).toStrictEqual(globalScope); - expect(globalScope.references[0].resolved).toStrictEqual(variables[0]); - expect(variables[0].references).toHaveLength(1); - expect(variables[0].references[0]).toStrictEqual(globalScope.references[0]); - expect(globalScope.references[1].identifier.name).toBe('bar'); - expect(globalScope.references[1].from).toStrictEqual(globalScope); - expect(globalScope.references[1].resolved).toStrictEqual(variables[1]); - expect(globalScope.references[2].identifier.name).toBe('bar'); - expect(globalScope.references[2].from).toStrictEqual(globalScope); - expect(globalScope.references[2].resolved).toStrictEqual(variables[1]); - expect(variables[1].references).toHaveLength(2); - expect(variables[1].references[0]).toStrictEqual(globalScope.references[1]); - expect(variables[1].references[1]).toStrictEqual(globalScope.references[2]); - expect(globalScope.through).toHaveLength(0); - expect(globalScope['implicit'].variables).toHaveLength(0); - expect(globalScope['implicit'].set.size).toBe(0); - expect(globalScope['implicit'].leftToBeResolved).toHaveLength(0); - - scopeManager.addGlobals(['foo', 'bar']); - const withAddedVariables = getRealVariables(scopeManager.variables); - expect(withAddedVariables).toHaveLength(2); - expect(withAddedVariables[0].name).toBe('foo'); - expect(withAddedVariables[0].scope).toStrictEqual(globalScope); - expect(withAddedVariables[0].defs).toHaveLength(1); - expect(withAddedVariables[0].identifiers).toHaveLength(1); - expect(withAddedVariables[1].name).toBe('bar'); - expect(withAddedVariables[1].scope).toStrictEqual(globalScope); - expect(withAddedVariables[1].defs).toHaveLength(1); - expect(withAddedVariables[1].identifiers).toHaveLength(1); - expect(globalScope.set.size).toBe(typeVariableAmount + 2); - expect(globalScope.set.get('foo')).toStrictEqual(withAddedVariables[0]); - expect(globalScope.set.get('bar')).toStrictEqual(withAddedVariables[1]); - expect(globalScope.references).toHaveLength(3); - expect(globalScope.references[0].identifier.name).toBe('foo'); - expect(globalScope.references[0].from).toStrictEqual(globalScope); - expect(globalScope.references[0].resolved).toStrictEqual( - withAddedVariables[0], - ); - expect(withAddedVariables[0].references).toHaveLength(1); - expect(withAddedVariables[0].references[0]).toStrictEqual( - globalScope.references[0], - ); - expect(globalScope.references[1].identifier.name).toBe('bar'); - expect(globalScope.references[1].from).toStrictEqual(globalScope); - expect(globalScope.references[1].resolved).toStrictEqual( - withAddedVariables[1], - ); - expect(globalScope.references[2].identifier.name).toBe('bar'); - expect(globalScope.references[2].from).toStrictEqual(globalScope); - expect(globalScope.references[2].resolved).toStrictEqual( - withAddedVariables[1], - ); - expect(withAddedVariables[1].references).toHaveLength(2); - expect(withAddedVariables[1].references[0]).toStrictEqual( - globalScope.references[1], - ); - expect(withAddedVariables[1].references[1]).toStrictEqual( - globalScope.references[2], - ); - expect(globalScope.through).toHaveLength(0); - expect(globalScope['implicit'].variables).toHaveLength(0); - expect(globalScope['implicit'].set.size).toBe(0); - expect(globalScope['implicit'].leftToBeResolved).toHaveLength(0); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/arguments.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/arguments.test.ts deleted file mode 100644 index 6e53ab4e..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/arguments.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { ScopeType } from '../../src/index.js'; -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('arguments', () => { - it('arguments are correctly materialized', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - arguments; - }()); - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('arguments'); - expect(scope.references).toHaveLength(1); - expect(scope.references[0].resolved).toBe(variables[0]); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/catch-scope.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/catch-scope.test.ts deleted file mode 100644 index dd769d9a..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/catch-scope.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { ScopeType } from '../../src/index.js'; -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('catch', () => { - it('creates scope', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - try { - } catch (e) { - } - }()); - `); - - expect(scopeManager.scopes).toHaveLength(5); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('arguments'); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[2]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.block); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[3]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.catch); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('e'); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[4]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.block); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/child-visitor-keys.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/child-visitor-keys.test.ts deleted file mode 100644 index db79c3c7..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/child-visitor-keys.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import type { TSESTree } from '@typescript-eslint/types'; - -import { analyze } from '../../src/analyze'; -import { parse } from '../test-utils'; - -describe('childVisitorKeys option', () => { - it('should not visit to properties which are not given.', () => { - const ast = parse(` - let foo = bar; - `); - - const decl = ast.body[0] as TSESTree.VariableDeclaration; - decl.declarations[0].init = { - argument: decl.declarations[0].init, - type: 'TestNode', - } as never; - - const result = analyze(ast, { - childVisitorKeys: { - TestNode: [], - }, - }); - - expect(result.scopes).toHaveLength(1); - const globalScope = result.scopes[0]; - - // `bar` in TestNode has not been visited. - expect(globalScope.through).toHaveLength(0); - }); - - it('should visit to given properties.', () => { - const ast = parse(` - let foo = bar; - `); - - const decl = ast.body[0] as TSESTree.VariableDeclaration; - decl.declarations[0].init = { - argument: decl.declarations[0].init, - type: 'TestNode', - } as never; - - const result = analyze(ast, { - childVisitorKeys: { - TestNode: ['argument'], - }, - }); - - expect(result.scopes).toHaveLength(1); - const globalScope = result.scopes[0]; - - // `bar` in TestNode has been visited. - expect(globalScope.through).toHaveLength(1); - expect(globalScope.through[0].identifier.name).toBe('bar'); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/class-fields.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/class-fields.test.ts deleted file mode 100644 index c1ea0dc9..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/class-fields.test.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { AST_NODE_TYPES } from '@typescript-eslint/types'; - -import { ScopeType } from '../../src/index.js'; -import { parseAndAnalyze } from '../test-utils/index.js'; - -describe('Class fields', () => { - it('class C { f = g }', () => { - const { scopeManager } = parseAndAnalyze('class C { f = g }'); - - const globalScope = scopeManager.scopes[0]; - assert.isScopeOfType(globalScope, ScopeType.global); - - const classScope = globalScope.childScopes[0]; - // should create a class scope - assert.isScopeOfType(classScope, ScopeType.class); - // The class scope has no references - expect(classScope.references).toHaveLength(0); - // The class scope has only the variable 'C'; it doesn't have the field name 'f'. - expect(classScope.variables).toHaveLength(1); - expect(classScope.variables[0].name).toBe('C'); - - const classFieldInitializerScope = classScope.childScopes[0]; - // The class scope has a class-field-initializer scope. - assert.isScopeOfType( - classFieldInitializerScope, - ScopeType.classFieldInitializer, - ); - // The class-field-initializer scope's block is the node of the field initializer. - assert.isNodeOfType( - classFieldInitializerScope.block, - AST_NODE_TYPES.Identifier, - ); - expect(classFieldInitializerScope.block.name).toBe('g'); - // The class-field-initializer scope's variableScope is itself. - expect(classFieldInitializerScope.variableScope).toBe( - classFieldInitializerScope, - ); - // The class-field-initializer scope has only the reference 'g'. - expect(classFieldInitializerScope.references).toHaveLength(1); - expect(classFieldInitializerScope.references[0].identifier.name).toBe('g'); - // The class-field-initializer scope has no variables. - expect(classFieldInitializerScope.variables).toHaveLength(0); - }); - - it('class C { f }', () => { - const { scopeManager } = parseAndAnalyze('class C { f }'); - - const globalScope = scopeManager.scopes[0]; - assert.isScopeOfType(globalScope, ScopeType.global); - - const classScope = globalScope.childScopes[0]; - // should create a class scope - assert.isScopeOfType(classScope, ScopeType.class); - // The class scope has no references - expect(classScope.references).toHaveLength(0); - // The class scope has no child scopes; fields that don't have initializers don't create any class-field-initializer scopes. - expect(classScope.childScopes).toHaveLength(0); - // The class scope has only the variable 'C'; it doesn't have the field name 'f'. - expect(classScope.variables).toHaveLength(1); - expect(classScope.variables[0].name).toBe('C'); - }); - - it('class C { [fname] }', () => { - const { scopeManager } = parseAndAnalyze('class C { [fname] }'); - - const globalScope = scopeManager.scopes[0]; - assert.isScopeOfType(globalScope, ScopeType.global); - - const classScope = globalScope.childScopes[0]; - // should create a class scope - assert.isScopeOfType(classScope, ScopeType.class); - // The class scope has only the reference `fname`. - expect(classScope.references).toHaveLength(1); - // The class scope has no child scopes; fields that don't have initializers don't create any class-field-initializer scopes. - expect(classScope.childScopes).toHaveLength(0); - }); - - it('class C { [fname] = value }', () => { - const { scopeManager } = parseAndAnalyze('class C { [fname] = value }'); - - const globalScope = scopeManager.scopes[0]; - assert.isScopeOfType(globalScope, ScopeType.global); - - const classScope = globalScope.childScopes[0]; - // should create a class scope - assert.isScopeOfType(classScope, ScopeType.class); - // The class scope has only the reference `fname`; it doesn't have the reference 'value'. - expect(classScope.references).toHaveLength(1); - expect(classScope.references[0].identifier.name).toBe('fname'); - - const classFieldInitializerScope = classScope.childScopes[0]; - // The class scope has a class-field-initializer scope. - assert.isScopeOfType( - classFieldInitializerScope, - ScopeType.classFieldInitializer, - ); - // The class-field-initializer scope has the reference 'value'. - expect(classFieldInitializerScope.references).toHaveLength(1); - expect(classFieldInitializerScope.references[0].identifier.name).toBe( - 'value', - ); - // The class-field-initializer scope has no variables. - expect(classFieldInitializerScope.variables).toHaveLength(0); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/es6-arrow-function-expression.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/es6-arrow-function-expression.test.ts deleted file mode 100644 index 92bfeec1..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/es6-arrow-function-expression.test.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { AST_NODE_TYPES } from '@typescript-eslint/types'; - -import { ScopeType } from '../../src/index.js'; -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('ES6 arrow function expression', () => { - it('materialize scope for arrow function expression', () => { - const { scopeManager } = parseAndAnalyze(` - var arrow = () => { - let i = 0; - var j = 20; - console.log(i); - } - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(scope.block.type).toBe(AST_NODE_TYPES.Program); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(1); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(scope.block.type).toBe(AST_NODE_TYPES.ArrowFunctionExpression); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(2); - - // There's no "arguments" - expect(variables[0].name).toBe('i'); - expect(variables[1].name).toBe('j'); - }); - - it('generate bindings for parameters', () => { - const { scopeManager } = parseAndAnalyze('var arrow = (a, b, c, d) => {}'); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(scope.block.type).toBe(AST_NODE_TYPES.Program); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(1); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(scope.block.type).toBe(AST_NODE_TYPES.ArrowFunctionExpression); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(4); - - // There's no "arguments" - expect(variables[0].name).toBe('a'); - expect(variables[1].name).toBe('b'); - expect(variables[2].name).toBe('c'); - expect(variables[3].name).toBe('d'); - }); - - it('inherits upper scope strictness', () => { - const { scopeManager } = parseAndAnalyze(` - "use strict"; - var arrow = () => {}; - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(scope.block.type).toBe(AST_NODE_TYPES.Program); - expect(scope.isStrict).toBe(true); - expect(variables).toHaveLength(1); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(scope.block.type).toBe(AST_NODE_TYPES.ArrowFunctionExpression); - expect(scope.isStrict).toBe(true); - expect(variables).toHaveLength(0); - }); - - it('is strict when a strictness directive is used', () => { - const { scopeManager } = parseAndAnalyze(` - var arrow = () => { - "use strict"; - }; - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(scope.block.type).toBe(AST_NODE_TYPES.Program); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(1); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(scope.block.type).toBe(AST_NODE_TYPES.ArrowFunctionExpression); - expect(scope.isStrict).toBe(true); - expect(variables).toHaveLength(0); - }); - - it('works with no body', () => { - const { scopeManager } = parseAndAnalyze('var arrow = a => a;'); - - expect(scopeManager.scopes).toHaveLength(2); - - const scope = scopeManager.scopes[1]; - const variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.function); - expect(scope.block.type).toBe(AST_NODE_TYPES.ArrowFunctionExpression); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(1); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/es6-block-scope.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/es6-block-scope.test.ts deleted file mode 100644 index e3aabdfd..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/es6-block-scope.test.ts +++ /dev/null @@ -1,156 +0,0 @@ -import { ScopeType } from '../../src/index.js'; -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('ES6 block scope', () => { - it('let is materialized in ES6 block scope#1', () => { - const { scopeManager } = parseAndAnalyze(` - { - let i = 20; - i; - } - `); - - expect(scopeManager.scopes).toHaveLength(2); // Program and BlockStatement scope. - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); // No variable in Program scope. - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.block); - expect(variables).toHaveLength(1); // `i` in block scope. - expect(variables[0].name).toBe('i'); - expect(scope.references).toHaveLength(2); - expect(scope.references[0].identifier.name).toBe('i'); - expect(scope.references[1].identifier.name).toBe('i'); - }); - - it('function declaration is materialized in ES6 block scope', () => { - const { scopeManager } = parseAndAnalyze(` - { - function test() { - } - test(); - } - `); - - expect(scopeManager.scopes).toHaveLength(3); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.block); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('test'); - expect(scope.references).toHaveLength(1); - expect(scope.references[0].identifier.name).toBe('test'); - - scope = scopeManager.scopes[2]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('arguments'); - expect(scope.references).toHaveLength(0); - }); - - it('let is not hoistable#1', () => { - const { scopeManager } = parseAndAnalyze(` - var i = 42; (1) - { - i; // (2) ReferenceError at runtime. - let i = 20; // (2) - i; // (2) - } - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('i'); - expect(scope.references).toHaveLength(1); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.block); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('i'); - expect(scope.references).toHaveLength(3); - expect(scope.references[0].resolved).toBe(variables[0]); - expect(scope.references[1].resolved).toBe(variables[0]); - expect(scope.references[2].resolved).toBe(variables[0]); - }); - - it('let is not hoistable#2', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - var i = 42; // (1) - i; // (1) - { - i; // (3) - { - i; // (2) - let i = 20; // (2) - i; // (2) - } - let i = 30; // (3) - i; // (3) - } - i; // (1) - }()); - `); - - expect(scopeManager.scopes).toHaveLength(4); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(2); - expect(variables[0].name).toBe('arguments'); - expect(variables[1].name).toBe('i'); - const v1 = variables[1]; - - expect(scope.references).toHaveLength(3); - expect(scope.references[0].resolved).toBe(v1); - expect(scope.references[1].resolved).toBe(v1); - expect(scope.references[2].resolved).toBe(v1); - - scope = scopeManager.scopes[2]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.block); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('i'); - const v3 = variables[0]; - - expect(scope.references).toHaveLength(3); - expect(scope.references[0].resolved).toBe(v3); - expect(scope.references[1].resolved).toBe(v3); - expect(scope.references[2].resolved).toBe(v3); - - scope = scopeManager.scopes[3]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.block); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('i'); - const v2 = variables[0]; - - expect(scope.references).toHaveLength(3); - expect(scope.references[0].resolved).toBe(v2); - expect(scope.references[1].resolved).toBe(v2); - expect(scope.references[2].resolved).toBe(v2); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/es6-catch.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/es6-catch.test.ts deleted file mode 100644 index cc7c1ccc..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/es6-catch.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { AST_NODE_TYPES } from '@typescript-eslint/types'; - -import { ScopeType } from '../../src/index.js'; -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('ES6 catch', () => { - it('takes binding pattern', () => { - const { scopeManager } = parseAndAnalyze(` - try { - } catch ({ a, b, c, d }) { - let e = 20; - a; - b; - c; - d; - } - `); - - expect(scopeManager.scopes).toHaveLength(4); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(scope.block.type).toBe(AST_NODE_TYPES.Program); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.block); - expect(scope.block.type).toBe(AST_NODE_TYPES.BlockStatement); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[2]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.catch); - expect(scope.block.type).toBe(AST_NODE_TYPES.CatchClause); - expect(scope.isStrict).toBe(false); - - expect(variables).toHaveLength(4); - expect(variables[0].name).toBe('a'); - expect(variables[1].name).toBe('b'); - expect(variables[2].name).toBe('c'); - expect(variables[3].name).toBe('d'); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[3]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.block); - expect(scope.block.type).toBe(AST_NODE_TYPES.BlockStatement); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(1); - expect(variables.map(variable => variable.name)).toStrictEqual(['e']); - expect(scope.references.map(ref => ref.identifier.name)).toStrictEqual([ - 'e', - 'a', - 'b', - 'c', - 'd', - ]); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/es6-class.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/es6-class.test.ts deleted file mode 100644 index eaae8dd0..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/es6-class.test.ts +++ /dev/null @@ -1,192 +0,0 @@ -import { AST_NODE_TYPES } from '@typescript-eslint/types'; - -import { ScopeType } from '../../src/index.js'; -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('ES6 class', () => { - it('declaration name creates class scope', () => { - const { scopeManager } = parseAndAnalyze(` - class Derived extends Base { - constructor() { - } - } - new Derived(); - `); - - expect(scopeManager.scopes).toHaveLength(3); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(scope.block.type).toBe(AST_NODE_TYPES.Program); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('Derived'); - expect(scope.references).toHaveLength(1); - expect(scope.references[0].identifier.name).toBe('Derived'); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.class); - expect(scope.block.type).toBe(AST_NODE_TYPES.ClassDeclaration); - expect(scope.isStrict).toBe(true); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('Derived'); - expect(scope.references).toHaveLength(1); - expect(scope.references[0].identifier.name).toBe('Base'); - - scope = scopeManager.scopes[2]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(scope.block.type).toBe(AST_NODE_TYPES.FunctionExpression); - expect(scope.isStrict).toBe(true); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('arguments'); - expect(scope.references).toHaveLength(0); - }); - - it('expression name creates class scope#1', () => { - const { scopeManager } = parseAndAnalyze(` - (class Derived extends Base { - constructor() { - } - }); - `); - - expect(scopeManager.scopes).toHaveLength(3); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(scope.block.type).toBe(AST_NODE_TYPES.Program); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.class); - expect(scope.block.type).toBe(AST_NODE_TYPES.ClassExpression); - expect(scope.isStrict).toBe(true); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('Derived'); - expect(scope.references).toHaveLength(1); - expect(scope.references[0].identifier.name).toBe('Base'); - - scope = scopeManager.scopes[2]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(scope.block.type).toBe(AST_NODE_TYPES.FunctionExpression); - expect(scope.isStrict).toBe(true); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('arguments'); - expect(scope.references).toHaveLength(0); - }); - - it('expression name creates class scope#2', () => { - const { scopeManager } = parseAndAnalyze(` - (class extends Base { - constructor() { - } - }); - `); - - expect(scopeManager.scopes).toHaveLength(3); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(scope.block.type).toBe(AST_NODE_TYPES.Program); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.class); - expect(scope.block.type).toBe(AST_NODE_TYPES.ClassExpression); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(1); - expect(scope.references[0].identifier.name).toBe('Base'); - - scope = scopeManager.scopes[2]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(scope.block.type).toBe(AST_NODE_TYPES.FunctionExpression); - expect(scope.isStrict).toBe(true); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('arguments'); - expect(scope.references).toHaveLength(0); - }); - - it('computed property key may refer variables', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - var yuyushiki = 42; - (class { - [yuyushiki]() { - } - - [yuyushiki + 40]() { - } - }); - }()); - `); - - expect(scopeManager.scopes).toHaveLength(5); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(scope.block.type).toBe(AST_NODE_TYPES.Program); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(scope.block.type).toBe(AST_NODE_TYPES.FunctionExpression); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(2); - expect(variables[0].name).toBe('arguments'); - expect(variables[1].name).toBe('yuyushiki'); - expect(scope.references).toHaveLength(1); - expect(scope.references[0].identifier.name).toBe('yuyushiki'); - - scope = scopeManager.scopes[2]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.class); - expect(scope.block.type).toBe(AST_NODE_TYPES.ClassExpression); - expect(scope.isStrict).toBe(true); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(2); - expect(scope.references[0].identifier.name).toBe('yuyushiki'); - expect(scope.references[1].identifier.name).toBe('yuyushiki'); - }); - - it('regression #49', () => { - const { scopeManager } = parseAndAnalyze(` - class Shoe { - constructor() { - //Shoe.x = true; - } - } - let shoe = new Shoe(); - `); - - expect(scopeManager.scopes).toHaveLength(3); - - const scope = scopeManager.scopes[0]; - const variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(scope.block.type).toBe(AST_NODE_TYPES.Program); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(2); - expect(variables[0].name).toBe('Shoe'); - expect(variables[1].name).toBe('shoe'); - expect(scope.references).toHaveLength(2); - expect(scope.references[0].identifier.name).toBe('shoe'); - expect(scope.references[1].identifier.name).toBe('Shoe'); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/es6-default-parameters.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/es6-default-parameters.test.ts deleted file mode 100644 index 896aad72..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/es6-default-parameters.test.ts +++ /dev/null @@ -1,395 +0,0 @@ -import { AST_NODE_TYPES } from '@typescript-eslint/types'; - -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('ES6 default parameters:', () => { - describe('a default parameter creates a writable reference for its initialization:', () => { - const patterns = [ - [AST_NODE_TYPES.ArrowFunctionExpression, 'let foo = (a, b = 0) => {};'], - [AST_NODE_TYPES.FunctionDeclaration, 'function foo(a, b = 0) {}'], - [AST_NODE_TYPES.FunctionExpression, 'let foo = function(a, b = 0) {};'], - ] as const; - - it.for(patterns)('%s', ([name, code], { expect }) => { - const numVars = name === AST_NODE_TYPES.ArrowFunctionExpression ? 2 : 3; - const { scopeManager } = parseAndAnalyze(code); - - expect(scopeManager.scopes).toHaveLength(2); // [global, foo] - - const scope = scopeManager.scopes[1]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(numVars); // [arguments?, a, b] - expect(scope.references).toHaveLength(1); - - const reference = scope.references[0]; - - expect(reference.from).toBe(scope); - expect(reference.identifier.name).toBe('b'); - expect(reference.resolved).toBe(variables[numVars - 1]); - - assert.exists(reference.writeExpr); - - expect(reference.isWrite()).toBe(true); - expect(reference.isRead()).toBe(false); - }); - }); - - describe('a default parameter creates a readable reference for references in right:', () => { - const patterns = [ - [ - AST_NODE_TYPES.ArrowFunctionExpression, - ` - let a; - let foo = (b = a) => {}; - `, - ], - [ - AST_NODE_TYPES.FunctionDeclaration, - ` - let a; - function foo(b = a) {} - `, - ], - [ - AST_NODE_TYPES.FunctionExpression, - ` - let a; - let foo = function(b = a) {} - `, - ], - ] as const; - - it.for(patterns)('%s', ([name, code], { expect }) => { - const numVars = name === AST_NODE_TYPES.ArrowFunctionExpression ? 1 : 2; - const { scopeManager } = parseAndAnalyze(code); - - expect(scopeManager.scopes).toHaveLength(2); // [global, foo] - - const scope = scopeManager.scopes[1]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(numVars); // [arguments?, b] - expect(scope.references).toHaveLength(2); // [b, a] - - const reference = scope.references[1]; - - expect(reference.from).toBe(scope); - expect(reference.identifier.name).toBe('a'); - expect(reference.resolved).toBe( - getRealVariables(scopeManager.scopes[0].variables)[0], - ); - - assert.notExists(reference.writeExpr); - - expect(reference.isWrite()).toBe(false); - expect(reference.isRead()).toBe(true); - }); - }); - - describe('a default parameter creates a readable reference for references in right (for const):', () => { - const patterns = [ - [ - AST_NODE_TYPES.ArrowFunctionExpression, - ` - const a = 0; - let foo = (b = a) => {}; - `, - ], - [ - AST_NODE_TYPES.FunctionDeclaration, - ` - const a = 0; - function foo(b = a) {} - `, - ], - [ - AST_NODE_TYPES.FunctionExpression, - ` - const a = 0; - let foo = function(b = a) {} - `, - ], - ] as const; - - it.for(patterns)('%s', ([name, code], { expect }) => { - const numVars = name === AST_NODE_TYPES.ArrowFunctionExpression ? 1 : 2; - const { scopeManager } = parseAndAnalyze(code); - - expect(scopeManager.scopes).toHaveLength(2); // [global, foo] - - const scope = scopeManager.scopes[1]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(numVars); // [arguments?, b] - expect(scope.references).toHaveLength(2); // [b, a] - - const reference = scope.references[1]; - - expect(reference.from).toBe(scope); - expect(reference.identifier.name).toBe('a'); - expect(reference.resolved).toBe( - getRealVariables(scopeManager.scopes[0].variables)[0], - ); - - assert.notExists(reference.writeExpr); - - expect(reference.isWrite()).toBe(false); - expect(reference.isRead()).toBe(true); - }); - }); - - describe('a default parameter creates a readable reference for references in right (partial):', () => { - const patterns = [ - [ - AST_NODE_TYPES.ArrowFunctionExpression, - ` - let a; - let foo = (b = a.c) => {}; - `, - ], - [ - AST_NODE_TYPES.FunctionDeclaration, - ` - let a; - function foo(b = a.c) {} - `, - ], - [ - AST_NODE_TYPES.FunctionExpression, - ` - let a; - let foo = function(b = a.c) {} - `, - ], - ] as const; - - it.for(patterns)('%s', ([name, code], { expect }) => { - const numVars = name === AST_NODE_TYPES.ArrowFunctionExpression ? 1 : 2; - const { scopeManager } = parseAndAnalyze(code); - - expect(scopeManager.scopes).toHaveLength(2); // [global, foo] - - const scope = scopeManager.scopes[1]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(numVars); // [arguments?, b] - expect(scope.references).toHaveLength(2); // [b, a] - - const reference = scope.references[1]; - - expect(reference.from).toBe(scope); - expect(reference.identifier.name).toBe('a'); - expect(reference.resolved).toBe( - getRealVariables(scopeManager.scopes[0].variables)[0], - ); - - assert.notExists(reference.writeExpr); - - expect(reference.isWrite()).toBe(false); - expect(reference.isRead()).toBe(true); - }); - }); - - describe("a default parameter creates a readable reference for references in right's nested scope:", () => { - const patterns = [ - [ - AST_NODE_TYPES.ArrowFunctionExpression, - ` - let a; - let foo = (b = function() { return a; }) => {}; - `, - ], - [ - AST_NODE_TYPES.FunctionDeclaration, - ` - let a; - function foo(b = function() { return a; }) {} - `, - ], - [ - AST_NODE_TYPES.FunctionExpression, - ` - let a; - let foo = function(b = function() { return a; }) {} - `, - ], - ] as const; - - it.for(patterns)('%s', ([, code], { expect }) => { - const { scopeManager } = parseAndAnalyze(code); - - expect(scopeManager.scopes).toHaveLength(3); // [global, foo, anonymous] - - const scope = scopeManager.scopes[2]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(1); // [arguments] - expect(scope.references).toHaveLength(1); // [a] - - const reference = scope.references[0]; - - expect(reference.from).toBe(scope); - expect(reference.identifier.name).toBe('a'); - expect(reference.resolved).toBe( - getRealVariables(scopeManager.scopes[0].variables)[0], - ); - - assert.notExists(reference.writeExpr); - - expect(reference.isWrite()).toBe(false); - expect(reference.isRead()).toBe(true); - }); - }); - - describe("a default parameter creates a readable reference for references in right. It's resolved to outer scope's even if there is the variable in the function body:", () => { - const patterns = [ - [ - AST_NODE_TYPES.ArrowFunctionExpression, - ` - let a; - let foo = (b = a) => { let a; }; - `, - ], - [ - AST_NODE_TYPES.FunctionDeclaration, - ` - let a; - function foo(b = a) { let a; } - `, - ], - [ - AST_NODE_TYPES.FunctionExpression, - ` - let a; - let foo = function(b = a) { let a; } - `, - ], - ] as const; - - it.for(patterns)('%s', ([name, code], { expect }) => { - const numVars = name === AST_NODE_TYPES.ArrowFunctionExpression ? 2 : 3; - const { scopeManager } = parseAndAnalyze(code); - - expect(scopeManager.scopes).toHaveLength(2); // [global, foo] - - const scope = scopeManager.scopes[1]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(numVars); // [arguments?, b, a] - expect(scope.references).toHaveLength(2); // [b, a] - - const reference = scope.references[1]; - - expect(reference.from).toBe(scope); - expect(reference.identifier.name).toBe('a'); - expect(reference.resolved).toBe( - getRealVariables(scopeManager.scopes[0].variables)[0], - ); - - assert.notExists(reference.writeExpr); - - expect(reference.isWrite()).toBe(false); - expect(reference.isRead()).toBe(true); - }); - }); - - describe("a default parameter creates a readable reference for references in right. It's resolved to the parameter:", () => { - const patterns = [ - [ - AST_NODE_TYPES.ArrowFunctionExpression, - ` - let a; - let foo = (b = a, a) => { }; - `, - ], - [ - AST_NODE_TYPES.FunctionDeclaration, - ` - let a; - function foo(b = a, a) { } - `, - ], - [ - AST_NODE_TYPES.FunctionExpression, - ` - let a; - let foo = function(b = a, a) { } - `, - ], - ] as const; - - it.for(patterns)('%s', ([name, code], { expect }) => { - const numVars = name === AST_NODE_TYPES.ArrowFunctionExpression ? 2 : 3; - const { scopeManager } = parseAndAnalyze(code); - - expect(scopeManager.scopes).toHaveLength(2); // [global, foo] - - const scope = scopeManager.scopes[1]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(numVars); // [arguments?, b, a] - expect(scope.references).toHaveLength(2); // [b, a] - - const reference = scope.references[1]; - - expect(reference.from).toBe(scope); - expect(reference.identifier.name).toBe('a'); - expect(reference.resolved).toBe(variables[variables.length - 1]); - - assert.notExists(reference.writeExpr); - - expect(reference.isWrite()).toBe(false); - expect(reference.isRead()).toBe(true); - }); - }); - - describe("a default parameter creates a readable reference for references in right (nested scope). It's resolved to outer scope's even if there is the variable in the function body:", () => { - const patterns = [ - [ - AST_NODE_TYPES.ArrowFunctionExpression, - ` - let a; - let foo = (b = function(){ a }) => { let a; }; - `, - ], - [ - AST_NODE_TYPES.FunctionDeclaration, - ` - let a; - function foo(b = function(){ a }) { let a; } - `, - ], - [ - AST_NODE_TYPES.FunctionExpression, - ` - let a; - let foo = function(b = function(){ a }) { let a; } - `, - ], - ] as const; - - it.for(patterns)('%s', ([, code], { expect }) => { - const { scopeManager } = parseAndAnalyze(code); - - expect(scopeManager.scopes).toHaveLength(3); // [global, foo, anonymous function] - - const scope = scopeManager.scopes[2]; - - expect(scope.references).toHaveLength(1); // [a] - - const reference = scope.references[0]; - - expect(reference.from).toBe(scope); - expect(reference.identifier.name).toBe('a'); - expect(reference.resolved).toBe( - getRealVariables(scopeManager.scopes[0].variables)[0], - ); - - assert.notExists(reference.writeExpr); - - expect(reference.isWrite()).toBe(false); - expect(reference.isRead()).toBe(true); - }); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/es6-destructuring-assignments.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/es6-destructuring-assignments.test.ts deleted file mode 100644 index 74211bd9..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/es6-destructuring-assignments.test.ts +++ /dev/null @@ -1,1345 +0,0 @@ -/* eslint-disable @typescript-eslint/dot-notation -- ['implicit'] is private */ -import { AST_NODE_TYPES } from '@typescript-eslint/types'; - -import type { Reference } from '../../src/index.js'; - -import { DefinitionType, ScopeType } from '../../src/index.js'; -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('ES6 destructuring assignments', () => { - it('Pattern in var in ForInStatement', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - for (var [a, b, c] in array); - }()); - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - expect(scope['implicit'].leftToBeResolved).toHaveLength(1); - expect(scope['implicit'].leftToBeResolved[0].identifier.name).toBe('array'); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(4); - expect(variables[0].name).toBe('arguments'); - expect(variables[1].name).toBe('a'); - expect(variables[2].name).toBe('b'); - expect(variables[3].name).toBe('c'); - expect(scope.references).toHaveLength(4); - expect(scope.references[0].identifier.name).toBe('a'); - expect(scope.references[0].isWrite()).toBe(true); - expect(scope.references[0].resolved).toBe(variables[1]); - expect(scope.references[1].identifier.name).toBe('b'); - expect(scope.references[1].isWrite()).toBe(true); - expect(scope.references[1].resolved).toBe(variables[2]); - expect(scope.references[2].identifier.name).toBe('c'); - expect(scope.references[2].isWrite()).toBe(true); - expect(scope.references[2].resolved).toBe(variables[3]); - expect(scope.references[3].identifier.name).toBe('array'); - expect(scope.references[3].isWrite()).toBe(false); - }); - - it('Pattern in let in ForInStatement', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - for (let [a, b, c] in array); - }()); - `); - - expect(scopeManager.scopes).toHaveLength(3); // [global, function, for] - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - expect(scope['implicit'].leftToBeResolved).toHaveLength(1); - expect(scope['implicit'].leftToBeResolved[0].identifier.name).toBe('array'); - - scope = scopeManager.scopes[2]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.for); - expect(variables).toHaveLength(3); - expect(variables[0].name).toBe('a'); - expect(variables[1].name).toBe('b'); - expect(variables[2].name).toBe('c'); - expect(scope.references).toHaveLength(4); - expect(scope.references[0].identifier.name).toBe('a'); - expect(scope.references[0].isWrite()).toBe(true); - expect(scope.references[0].resolved).toBe(variables[0]); - expect(scope.references[1].identifier.name).toBe('b'); - expect(scope.references[1].isWrite()).toBe(true); - expect(scope.references[1].resolved).toBe(variables[1]); - expect(scope.references[2].identifier.name).toBe('c'); - expect(scope.references[2].isWrite()).toBe(true); - expect(scope.references[2].resolved).toBe(variables[2]); - expect(scope.references[3].identifier.name).toBe('array'); - expect(scope.references[3].isWrite()).toBe(false); - - assert.isNull(scope.references[3].resolved); - }); - - it('Pattern with default values in var in ForInStatement', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - for (var [a, b, c = d] in array); - }()); - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - expect(scope['implicit'].leftToBeResolved).toHaveLength(2); - expect(scope['implicit'].leftToBeResolved[0].identifier.name).toBe('d'); - expect(scope['implicit'].leftToBeResolved[1].identifier.name).toBe('array'); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(4); - expect(variables[0].name).toBe('arguments'); - expect(variables[1].name).toBe('a'); - expect(variables[2].name).toBe('b'); - expect(variables[3].name).toBe('c'); - expect(scope.references).toHaveLength(6); - expect(scope.references[0].identifier.name).toBe('c'); - expect(scope.references[0].isWrite()).toBe(true); - assert.isNodeOfType( - scope.references[0].writeExpr, - AST_NODE_TYPES.Identifier, - ); - expect(scope.references[0].writeExpr.name).toBe('d'); - expect(scope.references[0].resolved).toBe(variables[3]); - expect(scope.references[1].identifier.name).toBe('d'); - expect(scope.references[1].isWrite()).toBe(false); - expect(scope.references[2].identifier.name).toBe('a'); - expect(scope.references[2].isWrite()).toBe(true); - expect(scope.references[2].resolved).toBe(variables[1]); - expect(scope.references[3].identifier.name).toBe('b'); - expect(scope.references[3].isWrite()).toBe(true); - expect(scope.references[3].resolved).toBe(variables[2]); - expect(scope.references[4].identifier.name).toBe('c'); - expect(scope.references[4].isWrite()).toBe(true); - assert.isNodeOfType( - scope.references[4].writeExpr, - AST_NODE_TYPES.Identifier, - ); - expect(scope.references[4].writeExpr.name).toBe('array'); - expect(scope.references[4].resolved).toBe(variables[3]); - expect(scope.references[5].identifier.name).toBe('array'); - expect(scope.references[5].isWrite()).toBe(false); - }); - - it('Pattern with default values in let in ForInStatement', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - for (let [a, b, c = d] in array); - }()); - `); - - expect(scopeManager.scopes).toHaveLength(3); // [global, function, for] - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - expect(scope['implicit'].leftToBeResolved).toHaveLength(2); - expect(scope['implicit'].leftToBeResolved[0].identifier.name).toBe('d'); - assert.isScopeOfType( - scope['implicit'].leftToBeResolved[0].from, - ScopeType.for, - ); - expect(scope['implicit'].leftToBeResolved[1].identifier.name).toBe('array'); - assert.isScopeOfType( - scope['implicit'].leftToBeResolved[1].from, - ScopeType.for, - ); - - scope = scopeManager.scopes[2]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.for); - expect(variables).toHaveLength(3); - expect(variables[0].name).toBe('a'); - expect(variables[1].name).toBe('b'); - expect(variables[2].name).toBe('c'); - expect(scope.references).toHaveLength(6); - expect(scope.references[0].identifier.name).toBe('c'); - expect(scope.references[0].isWrite()).toBe(true); - assert.isNodeOfType( - scope.references[0].writeExpr, - AST_NODE_TYPES.Identifier, - ); - expect(scope.references[0].writeExpr.name).toBe('d'); - expect(scope.references[0].resolved).toBe(variables[2]); - expect(scope.references[1].identifier.name).toBe('d'); - expect(scope.references[1].isWrite()).toBe(false); - expect(scope.references[2].identifier.name).toBe('a'); - expect(scope.references[2].isWrite()).toBe(true); - assert.isNodeOfType( - scope.references[2].writeExpr, - AST_NODE_TYPES.Identifier, - ); - expect(scope.references[2].writeExpr.name).toBe('array'); - expect(scope.references[2].resolved).toBe(variables[0]); - expect(scope.references[3].identifier.name).toBe('b'); - expect(scope.references[3].isWrite()).toBe(true); - assert.isNodeOfType( - scope.references[3].writeExpr, - AST_NODE_TYPES.Identifier, - ); - expect(scope.references[3].writeExpr.name).toBe('array'); - expect(scope.references[3].resolved).toBe(variables[1]); - expect(scope.references[4].identifier.name).toBe('c'); - expect(scope.references[4].isWrite()).toBe(true); - assert.isNodeOfType( - scope.references[4].writeExpr, - AST_NODE_TYPES.Identifier, - ); - expect(scope.references[4].writeExpr.name).toBe('array'); - expect(scope.references[4].resolved).toBe(variables[2]); - expect(scope.references[5].identifier.name).toBe('array'); - expect(scope.references[5].isWrite()).toBe(false); - - assert.isNull(scope.references[5].resolved); - }); - - it('Pattern with nested default values in var in ForInStatement', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - for (var [a, [b, c = d] = e] in array); - }()); - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - expect(scope['implicit'].leftToBeResolved).toHaveLength(3); - expect(scope['implicit'].leftToBeResolved[0].identifier.name).toBe('d'); - expect(scope['implicit'].leftToBeResolved[1].identifier.name).toBe('e'); - expect(scope['implicit'].leftToBeResolved[2].identifier.name).toBe('array'); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(4); - expect(variables[0].name).toBe('arguments'); - expect(variables[1].name).toBe('a'); - expect(variables[2].name).toBe('b'); - expect(variables[3].name).toBe('c'); - expect(scope.references).toHaveLength(9); - expect(scope.references[0].identifier.name).toBe('b'); - expect(scope.references[0].isWrite()).toBe(true); - assert.isNodeOfType( - scope.references[0].writeExpr, - AST_NODE_TYPES.Identifier, - ); - expect(scope.references[0].writeExpr.name).toBe('e'); - expect(scope.references[0].resolved).toBe(variables[2]); - expect(scope.references[1].identifier.name).toBe('c'); - expect(scope.references[1].isWrite()).toBe(true); - assert.isNodeOfType( - scope.references[1].writeExpr, - AST_NODE_TYPES.Identifier, - ); - expect(scope.references[1].writeExpr.name).toBe('e'); - expect(scope.references[1].resolved).toBe(variables[3]); - expect(scope.references[2].identifier.name).toBe('c'); - expect(scope.references[2].isWrite()).toBe(true); - assert.isNodeOfType( - scope.references[2].writeExpr, - AST_NODE_TYPES.Identifier, - ); - expect(scope.references[2].writeExpr.name).toBe('d'); - expect(scope.references[2].resolved).toBe(variables[3]); - expect(scope.references[3].identifier.name).toBe('d'); - expect(scope.references[3].isWrite()).toBe(false); - expect(scope.references[4].identifier.name).toBe('e'); - expect(scope.references[4].isWrite()).toBe(false); - expect(scope.references[5].identifier.name).toBe('a'); - expect(scope.references[5].isWrite()).toBe(true); - assert.isNodeOfType( - scope.references[5].writeExpr, - AST_NODE_TYPES.Identifier, - ); - expect(scope.references[5].writeExpr.name).toBe('array'); - expect(scope.references[5].resolved).toBe(variables[1]); - expect(scope.references[6].identifier.name).toBe('b'); - expect(scope.references[6].isWrite()).toBe(true); - assert.isNodeOfType( - scope.references[6].writeExpr, - AST_NODE_TYPES.Identifier, - ); - expect(scope.references[6].writeExpr.name).toBe('array'); - expect(scope.references[6].resolved).toBe(variables[2]); - expect(scope.references[7].identifier.name).toBe('c'); - expect(scope.references[7].isWrite()).toBe(true); - assert.isNodeOfType( - scope.references[7].writeExpr, - AST_NODE_TYPES.Identifier, - ); - expect(scope.references[7].writeExpr.name).toBe('array'); - expect(scope.references[7].resolved).toBe(variables[3]); - expect(scope.references[8].identifier.name).toBe('array'); - expect(scope.references[8].isWrite()).toBe(false); - }); - - it('Pattern with nested default values in let in ForInStatement', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - for (let [a, [b, c = d] = e] in array); - }()); - `); - - expect(scopeManager.scopes).toHaveLength(3); // [global, function, for] - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - expect(scope['implicit'].leftToBeResolved).toHaveLength(3); - expect(scope['implicit'].leftToBeResolved[0].identifier.name).toBe('d'); - assert.isScopeOfType( - scope['implicit'].leftToBeResolved[0].from, - ScopeType.for, - ); - expect(scope['implicit'].leftToBeResolved[1].identifier.name).toBe('e'); - assert.isScopeOfType( - scope['implicit'].leftToBeResolved[1].from, - ScopeType.for, - ); - expect(scope['implicit'].leftToBeResolved[2].identifier.name).toBe('array'); - assert.isScopeOfType( - scope['implicit'].leftToBeResolved[2].from, - ScopeType.for, - ); - - scope = scopeManager.scopes[2]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.for); - expect(variables).toHaveLength(3); - expect(variables[0].name).toBe('a'); - expect(variables[1].name).toBe('b'); - expect(variables[2].name).toBe('c'); - expect(scope.references).toHaveLength(9); - expect(scope.references[0].identifier.name).toBe('b'); - expect(scope.references[0].isWrite()).toBe(true); - assert.isNodeOfType( - scope.references[0].writeExpr, - AST_NODE_TYPES.Identifier, - ); - expect(scope.references[0].writeExpr.name).toBe('e'); - expect(scope.references[0].resolved).toBe(variables[1]); - expect(scope.references[1].identifier.name).toBe('c'); - expect(scope.references[1].isWrite()).toBe(true); - assert.isNodeOfType( - scope.references[1].writeExpr, - AST_NODE_TYPES.Identifier, - ); - expect(scope.references[1].writeExpr.name).toBe('e'); - expect(scope.references[1].resolved).toBe(variables[2]); - expect(scope.references[2].identifier.name).toBe('c'); - expect(scope.references[2].isWrite()).toBe(true); - assert.isNodeOfType( - scope.references[2].writeExpr, - AST_NODE_TYPES.Identifier, - ); - expect(scope.references[2].writeExpr.name).toBe('d'); - expect(scope.references[2].resolved).toBe(variables[2]); - expect(scope.references[3].identifier.name).toBe('d'); - expect(scope.references[3].isWrite()).toBe(false); - expect(scope.references[4].identifier.name).toBe('e'); - expect(scope.references[4].isWrite()).toBe(false); - expect(scope.references[5].identifier.name).toBe('a'); - expect(scope.references[5].isWrite()).toBe(true); - assert.isNodeOfType( - scope.references[5].writeExpr, - AST_NODE_TYPES.Identifier, - ); - expect(scope.references[5].writeExpr.name).toBe('array'); - expect(scope.references[5].resolved).toBe(variables[0]); - expect(scope.references[6].identifier.name).toBe('b'); - expect(scope.references[6].isWrite()).toBe(true); - assert.isNodeOfType( - scope.references[6].writeExpr, - AST_NODE_TYPES.Identifier, - ); - expect(scope.references[6].writeExpr.name).toBe('array'); - expect(scope.references[6].resolved).toBe(variables[1]); - expect(scope.references[7].identifier.name).toBe('c'); - expect(scope.references[7].isWrite()).toBe(true); - assert.isNodeOfType( - scope.references[7].writeExpr, - AST_NODE_TYPES.Identifier, - ); - expect(scope.references[7].writeExpr.name).toBe('array'); - expect(scope.references[7].resolved).toBe(variables[2]); - expect(scope.references[8].identifier.name).toBe('array'); - expect(scope.references[8].isWrite()).toBe(false); - - assert.isNull(scope.references[8].resolved); - }); - - it('Pattern with default values in var in ForInStatement (separate declarations)', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - var a, b, c; - for ([a, b, c = d] in array); - }()); - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - expect(scope['implicit'].leftToBeResolved).toHaveLength(2); - expect(scope['implicit'].leftToBeResolved[0].identifier.name).toBe('d'); - expect(scope['implicit'].leftToBeResolved[1].identifier.name).toBe('array'); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(4); - expect(variables[0].name).toBe('arguments'); - expect(variables[1].name).toBe('a'); - expect(variables[2].name).toBe('b'); - expect(variables[3].name).toBe('c'); - expect(scope.references).toHaveLength(6); - expect(scope.references[0].identifier.name).toBe('a'); - expect(scope.references[0].isWrite()).toBe(true); - expect(scope.references[0].resolved).toBe(variables[1]); - expect(scope.references[1].identifier.name).toBe('b'); - expect(scope.references[1].isWrite()).toBe(true); - expect(scope.references[1].resolved).toBe(variables[2]); - expect(scope.references[2].identifier.name).toBe('c'); - expect(scope.references[2].isWrite()).toBe(true); - assert.isNodeOfType( - scope.references[2].writeExpr, - AST_NODE_TYPES.Identifier, - ); - expect(scope.references[2].writeExpr.name).toBe('d'); - expect(scope.references[2].resolved).toBe(variables[3]); - expect(scope.references[3].identifier.name).toBe('c'); - expect(scope.references[3].isWrite()).toBe(true); - assert.isNodeOfType( - scope.references[3].writeExpr, - AST_NODE_TYPES.Identifier, - ); - expect(scope.references[3].writeExpr.name).toBe('array'); - expect(scope.references[3].resolved).toBe(variables[3]); - expect(scope.references[4].identifier.name).toBe('d'); - expect(scope.references[4].isWrite()).toBe(false); - expect(scope.references[5].identifier.name).toBe('array'); - expect(scope.references[5].isWrite()).toBe(false); - }); - - it('Pattern with default values in var in ForInStatement (separate declarations and with MemberExpression)', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - var obj; - for ([obj.a, obj.b, obj.c = d] in array); - }()); - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - expect(scope['implicit'].leftToBeResolved).toHaveLength(2); - expect(scope['implicit'].leftToBeResolved[0].identifier.name).toBe('d'); - expect(scope['implicit'].leftToBeResolved[1].identifier.name).toBe('array'); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(2); - expect(variables[0].name).toBe('arguments'); - expect(variables[1].name).toBe('obj'); - expect(scope.references).toHaveLength(5); - expect(scope.references[0].identifier.name).toBe('obj'); // obj.a - expect(scope.references[0].isWrite()).toBe(false); - expect(scope.references[0].isRead()).toBe(true); - expect(scope.references[0].resolved).toBe(variables[1]); - expect(scope.references[1].identifier.name).toBe('obj'); // obj.b - expect(scope.references[1].isWrite()).toBe(false); - expect(scope.references[1].isRead()).toBe(true); - expect(scope.references[1].resolved).toBe(variables[1]); - expect(scope.references[2].identifier.name).toBe('obj'); // obj.c - expect(scope.references[2].isWrite()).toBe(false); - expect(scope.references[2].isRead()).toBe(true); - expect(scope.references[2].resolved).toBe(variables[1]); - expect(scope.references[3].identifier.name).toBe('d'); - expect(scope.references[3].isWrite()).toBe(false); - expect(scope.references[3].isRead()).toBe(true); - expect(scope.references[4].identifier.name).toBe('array'); - expect(scope.references[4].isWrite()).toBe(false); - expect(scope.references[4].isRead()).toBe(true); - }); - - it('ArrayPattern in var', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - var [a, b, c] = array; - }()); - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - expect(scope['implicit'].leftToBeResolved).toHaveLength(1); - expect(scope['implicit'].leftToBeResolved[0].identifier.name).toBe('array'); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(4); - expect(variables[0].name).toBe('arguments'); - expect(variables[1].name).toBe('a'); - expect(variables[2].name).toBe('b'); - expect(variables[3].name).toBe('c'); - expect(scope.references).toHaveLength(4); - expect(scope.references[0].identifier.name).toBe('a'); - expect(scope.references[0].isWrite()).toBe(true); - expect(scope.references[0].resolved).toBe(variables[1]); - expect(scope.references[1].identifier.name).toBe('b'); - expect(scope.references[1].isWrite()).toBe(true); - expect(scope.references[1].resolved).toBe(variables[2]); - expect(scope.references[2].identifier.name).toBe('c'); - expect(scope.references[2].isWrite()).toBe(true); - expect(scope.references[2].resolved).toBe(variables[3]); - expect(scope.references[3].identifier.name).toBe('array'); - expect(scope.references[3].isWrite()).toBe(false); - }); - - it('SpreadElement in var', () => { - let { scopeManager } = parseAndAnalyze(` - (function () { - var [a, b, ...rest] = array; - }()); - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - expect(scope['implicit'].leftToBeResolved).toHaveLength(1); - expect(scope['implicit'].leftToBeResolved[0].identifier.name).toBe('array'); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(4); - expect(variables[0].name).toBe('arguments'); - expect(variables[1].name).toBe('a'); - expect(variables[2].name).toBe('b'); - expect(variables[3].name).toBe('rest'); - expect(scope.references).toHaveLength(4); - expect(scope.references[0].identifier.name).toBe('a'); - expect(scope.references[0].isWrite()).toBe(true); - expect(scope.references[0].resolved).toBe(variables[1]); - expect(scope.references[1].identifier.name).toBe('b'); - expect(scope.references[1].isWrite()).toBe(true); - expect(scope.references[1].resolved).toBe(variables[2]); - expect(scope.references[2].identifier.name).toBe('rest'); - expect(scope.references[2].isWrite()).toBe(true); - expect(scope.references[2].resolved).toBe(variables[3]); - expect(scope.references[3].identifier.name).toBe('array'); - expect(scope.references[3].isWrite()).toBe(false); - - ({ scopeManager } = parseAndAnalyze(` - (function () { - var [a, b, ...[c, d, ...rest]] = array; - }()); - `)); - - expect(scopeManager.scopes).toHaveLength(2); - - scope = scopeManager.scopes[0]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - expect(scope['implicit'].leftToBeResolved).toHaveLength(1); - expect(scope['implicit'].leftToBeResolved[0].identifier.name).toBe('array'); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - - expect(variables).toHaveLength(6); - const expectedVariableNames = ['arguments', 'a', 'b', 'c', 'd', 'rest']; - - for (let index = 0; index < expectedVariableNames.length; index++) { - expect(variables[index].name).toBe(expectedVariableNames[index]); - } - - expect(scope.references).toHaveLength(6); - const expectedReferenceNames = ['a', 'b', 'c', 'd', 'rest']; - - for (let index = 0; index < expectedReferenceNames.length; index++) { - expect(scope.references[index].identifier.name).toBe( - expectedReferenceNames[index], - ); - expect(scope.references[index].isWrite()).toBe(true); - } - expect(scope.references[5].identifier.name).toBe('array'); - expect(scope.references[5].isWrite()).toBe(false); - }); - - it('ObjectPattern in var', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - var { - shorthand, - key: value, - hello: { - world - } - } = object; - }()); - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - expect(scope['implicit'].leftToBeResolved).toHaveLength(1); - expect(scope['implicit'].leftToBeResolved[0].identifier.name).toBe( - 'object', - ); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(4); - expect(variables[0].name).toBe('arguments'); - expect(variables[1].name).toBe('shorthand'); - expect(variables[2].name).toBe('value'); - expect(variables[3].name).toBe('world'); - expect(scope.references).toHaveLength(4); - expect(scope.references[0].identifier.name).toBe('shorthand'); - expect(scope.references[0].isWrite()).toBe(true); - expect(scope.references[0].resolved).toBe(variables[1]); - expect(scope.references[1].identifier.name).toBe('value'); - expect(scope.references[1].isWrite()).toBe(true); - expect(scope.references[1].resolved).toBe(variables[2]); - expect(scope.references[2].identifier.name).toBe('world'); - expect(scope.references[2].isWrite()).toBe(true); - expect(scope.references[2].resolved).toBe(variables[3]); - expect(scope.references[3].identifier.name).toBe('object'); - expect(scope.references[3].isWrite()).toBe(false); - }); - - it('complex pattern in var', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - var { - shorthand, - key: [ a, b, c, d, e ], - hello: { - world - } - } = object; - }()); - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - expect(scope['implicit'].leftToBeResolved).toHaveLength(1); - expect(scope['implicit'].leftToBeResolved[0].identifier.name).toBe( - 'object', - ); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(8); - const expectedVariableNames = [ - 'arguments', - 'shorthand', - 'a', - 'b', - 'c', - 'd', - 'e', - 'world', - ]; - - for (let index = 0; index < expectedVariableNames.length; index++) { - expect(variables[index].name).toBe(expectedVariableNames[index]); - } - expect(scope.references).toHaveLength(8); - const expectedReferenceNames = [ - 'shorthand', - 'a', - 'b', - 'c', - 'd', - 'e', - 'world', - ]; - - for (let index = 0; index < expectedReferenceNames.length; index++) { - expect(scope.references[index].identifier.name).toBe( - expectedReferenceNames[index], - ); - expect(scope.references[index].isWrite()).toBe(true); - } - expect(scope.references[7].identifier.name).toBe('object'); - expect(scope.references[7].isWrite()).toBe(false); - }); - - it('ArrayPattern in AssignmentExpression', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - [a, b, c] = array; - }()); - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - expect(scope['implicit'].leftToBeResolved).toHaveLength(4); - expect( - scope['implicit'].leftToBeResolved.map( - (left: Reference) => left.identifier.name, - ), - ).toStrictEqual(['a', 'b', 'c', 'array']); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('arguments'); - expect(scope.references).toHaveLength(4); - expect(scope.references[0].identifier.name).toBe('a'); - expect(scope.references[0].isWrite()).toBe(true); - - assert.isNull(scope.references[0].resolved); - - expect(scope.references[1].identifier.name).toBe('b'); - expect(scope.references[1].isWrite()).toBe(true); - - assert.isNull(scope.references[1].resolved); - - expect(scope.references[2].identifier.name).toBe('c'); - expect(scope.references[2].isWrite()).toBe(true); - - assert.isNull(scope.references[2].resolved); - - expect(scope.references[3].identifier.name).toBe('array'); - expect(scope.references[3].isWrite()).toBe(false); - }); - - it('ArrayPattern with MemberExpression in AssignmentExpression', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - var obj; - [obj.a, obj.b, obj.c] = array; - }()); - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - expect(scope['implicit'].leftToBeResolved).toHaveLength(1); - expect(scope['implicit'].leftToBeResolved[0].identifier.name).toBe('array'); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(2); - expect(variables[0].name).toBe('arguments'); - expect(variables[1].name).toBe('obj'); - expect(scope.references).toHaveLength(4); - expect(scope.references[0].identifier.name).toBe('obj'); - expect(scope.references[0].isWrite()).toBe(false); - expect(scope.references[0].isRead()).toBe(true); - expect(scope.references[0].resolved).toBe(variables[1]); - expect(scope.references[1].identifier.name).toBe('obj'); - expect(scope.references[1].isWrite()).toBe(false); - expect(scope.references[1].isRead()).toBe(true); - expect(scope.references[1].resolved).toBe(variables[1]); - expect(scope.references[2].identifier.name).toBe('obj'); - expect(scope.references[2].isWrite()).toBe(false); - expect(scope.references[2].isRead()).toBe(true); - expect(scope.references[2].resolved).toBe(variables[1]); - expect(scope.references[3].identifier.name).toBe('array'); - expect(scope.references[3].isWrite()).toBe(false); - expect(scope.references[3].isRead()).toBe(true); - }); - - it('SpreadElement in AssignmentExpression', () => { - let { scopeManager } = parseAndAnalyze(` - (function () { - [a, b, ...rest] = array; - }()); - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - expect(scope['implicit'].leftToBeResolved).toHaveLength(4); - expect( - scope['implicit'].leftToBeResolved.map( - (left: Reference) => left.identifier.name, - ), - ).toStrictEqual(['a', 'b', 'rest', 'array']); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('arguments'); - expect(scope.references).toHaveLength(4); - expect(scope.references[0].identifier.name).toBe('a'); - expect(scope.references[0].isWrite()).toBe(true); - - assert.isNull(scope.references[0].resolved); - - expect(scope.references[1].identifier.name).toBe('b'); - expect(scope.references[1].isWrite()).toBe(true); - - assert.isNull(scope.references[1].resolved); - - expect(scope.references[2].identifier.name).toBe('rest'); - expect(scope.references[2].isWrite()).toBe(true); - - assert.isNull(scope.references[2].resolved); - - expect(scope.references[3].identifier.name).toBe('array'); - expect(scope.references[3].isWrite()).toBe(false); - - ({ scopeManager } = parseAndAnalyze(` - (function () { - [a, b, ...[c, d, ...rest]] = array; - }()); - `)); - - expect(scopeManager.scopes).toHaveLength(2); - - scope = scopeManager.scopes[0]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - expect(scope['implicit'].leftToBeResolved).toHaveLength(6); - expect( - scope['implicit'].leftToBeResolved.map( - (left: Reference) => left.identifier.name, - ), - ).toStrictEqual(['a', 'b', 'c', 'd', 'rest', 'array']); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('arguments'); - - expect(scope.references).toHaveLength(6); - const expectedReferenceNames = ['a', 'b', 'c', 'd', 'rest']; - - for (let index = 0; index < expectedReferenceNames.length; index++) { - expect(scope.references[index].identifier.name).toBe( - expectedReferenceNames[index], - ); - expect(scope.references[index].isWrite()).toBe(true); - - assert.isNull(scope.references[index].resolved); - } - expect(scope.references[5].identifier.name).toBe('array'); - expect(scope.references[5].isWrite()).toBe(false); - }); - - it('SpreadElement with MemberExpression in AssignmentExpression', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - [a, b, ...obj.rest] = array; - }()); - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - expect(scope['implicit'].leftToBeResolved).toHaveLength(4); - expect( - scope['implicit'].leftToBeResolved.map( - (left: Reference) => left.identifier.name, - ), - ).toStrictEqual(['a', 'b', 'obj', 'array']); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('arguments'); - expect(scope.references).toHaveLength(4); - expect(scope.references[0].identifier.name).toBe('a'); - expect(scope.references[0].isWrite()).toBe(true); - - assert.isNull(scope.references[0].resolved); - - expect(scope.references[1].identifier.name).toBe('b'); - expect(scope.references[1].isWrite()).toBe(true); - - assert.isNull(scope.references[1].resolved); - - expect(scope.references[2].identifier.name).toBe('obj'); - expect(scope.references[2].isWrite()).toBe(false); - expect(scope.references[3].identifier.name).toBe('array'); - expect(scope.references[3].isWrite()).toBe(false); - }); - - it('ObjectPattern in AssignmentExpression', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - ({ - shorthand, - key: value, - hello: { - world - } - } = object); - }()); - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - expect(scope['implicit'].leftToBeResolved).toHaveLength(4); - expect( - scope['implicit'].leftToBeResolved.map( - (left: Reference) => left.identifier.name, - ), - ).toStrictEqual(['shorthand', 'value', 'world', 'object']); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('arguments'); - expect(scope.references).toHaveLength(4); - expect(scope.references[0].identifier.name).toBe('shorthand'); - expect(scope.references[0].isWrite()).toBe(true); - - assert.isNull(scope.references[0].resolved); - - expect(scope.references[1].identifier.name).toBe('value'); - expect(scope.references[1].isWrite()).toBe(true); - - assert.isNull(scope.references[1].resolved); - - expect(scope.references[2].identifier.name).toBe('world'); - expect(scope.references[2].isWrite()).toBe(true); - - assert.isNull(scope.references[2].resolved); - - expect(scope.references[3].identifier.name).toBe('object'); - expect(scope.references[3].isWrite()).toBe(false); - }); - - it('complex pattern in AssignmentExpression', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - ({ - shorthand, - key: [ a, b, c, d, e ], - hello: { - world - } - } = object); - }()); - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - expect(scope['implicit'].leftToBeResolved).toHaveLength(8); - expect( - scope['implicit'].leftToBeResolved.map( - (left: Reference) => left.identifier.name, - ), - ).toStrictEqual(['shorthand', 'a', 'b', 'c', 'd', 'e', 'world', 'object']); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('arguments'); - expect(scope.references).toHaveLength(8); - const expectedReferenceNames = [ - 'shorthand', - 'a', - 'b', - 'c', - 'd', - 'e', - 'world', - ]; - - for (let index = 0; index < expectedReferenceNames.length; index++) { - expect(scope.references[index].identifier.name).toBe( - expectedReferenceNames[index], - ); - expect(scope.references[index].isWrite()).toBe(true); - } - expect(scope.references[7].identifier.name).toBe('object'); - expect(scope.references[7].isWrite()).toBe(false); - }); - - it('ArrayPattern in parameters', () => { - const { scopeManager } = parseAndAnalyze(` - (function ([a, b, c]) { - }(array)); - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(1); - expect(scope.references[0].identifier.name).toBe('array'); - expect(scope['implicit'].leftToBeResolved).toHaveLength(1); - expect(scope['implicit'].leftToBeResolved[0].identifier.name).toBe('array'); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(4); - expect(variables[0].name).toBe('arguments'); - expect(variables[1].name).toBe('a'); - expect(variables[2].name).toBe('b'); - expect(variables[3].name).toBe('c'); - expect(scope.references).toHaveLength(0); - }); - - it('SpreadElement in parameters', () => { - const { scopeManager } = parseAndAnalyze(` - (function ([a, b, ...rest], ...rest2) { - }(array)); - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(1); - expect(scope.references[0].identifier.name).toBe('array'); - expect(scope['implicit'].leftToBeResolved).toHaveLength(1); - expect(scope['implicit'].leftToBeResolved[0].identifier.name).toBe('array'); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(5); - expect(variables[0].name).toBe('arguments'); - expect(variables[1].name).toBe('a'); - expect(variables[2].name).toBe('b'); - expect(variables[3].name).toBe('rest'); - assert.isDefinitionOfType(variables[3].defs[0], DefinitionType.Parameter); - expect(variables[3].defs[0].rest).toBe(true); - expect(variables[4].name).toBe('rest2'); - assert.isDefinitionOfType(variables[4].defs[0], DefinitionType.Parameter); - expect(variables[4].defs[0].rest).toBe(true); - expect(scope.references).toHaveLength(0); - }); - - it('ObjectPattern in parameters', () => { - const { scopeManager } = parseAndAnalyze(` - (function ({ - shorthand, - key: value, - hello: { - world - } - }) { } (object)); - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(1); - expect(scope.references[0].identifier.name).toBe('object'); - expect(scope['implicit'].leftToBeResolved).toHaveLength(1); - expect(scope['implicit'].leftToBeResolved[0].identifier.name).toBe( - 'object', - ); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(4); - expect(variables[0].name).toBe('arguments'); - expect(variables[1].name).toBe('shorthand'); - expect(variables[2].name).toBe('value'); - expect(variables[3].name).toBe('world'); - expect(scope.references).toHaveLength(0); - }); - - it('complex pattern in parameters', () => { - const { scopeManager } = parseAndAnalyze(` - (function ({ - shorthand, - key: [ a, b, c, d, e ], - hello: { - world - } - }) { } (object)); - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(1); - expect(scope.references[0].identifier.name).toBe('object'); - expect(scope['implicit'].leftToBeResolved).toHaveLength(1); - expect(scope['implicit'].leftToBeResolved[0].identifier.name).toBe( - 'object', - ); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(8); - const expectedVariableNames = [ - 'arguments', - 'shorthand', - 'a', - 'b', - 'c', - 'd', - 'e', - 'world', - ]; - - for (let index = 0; index < expectedVariableNames.length; index++) { - expect(variables[index].name).toBe(expectedVariableNames[index]); - } - expect(scope.references).toHaveLength(0); - }); - - it('default values and patterns in var', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - var [a, b, c, d = 20 ] = array; - }()); - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(5); - const expectedVariableNames = ['arguments', 'a', 'b', 'c', 'd']; - - for (let index = 0; index < expectedVariableNames.length; index++) { - expect(variables[index].name).toBe(expectedVariableNames[index]); - } - expect(scope.references).toHaveLength(6); - const expectedReferenceNames = [ - 'a', - 'b', - 'c', - 'd', // assign 20 - 'd', // assign array - 'array', - ]; - - for (let index = 0; index < expectedReferenceNames.length; index++) { - expect(scope.references[index].identifier.name).toBe( - expectedReferenceNames[index], - ); - } - }); - - it('default values containing references and patterns in var', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - var [a, b, c, d = e ] = array; - }()); - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(5); - const expectedVariableNames = ['arguments', 'a', 'b', 'c', 'd']; - - for (let index = 0; index < expectedVariableNames.length; index++) { - expect(variables[index].name).toBe(expectedVariableNames[index]); - } - expect(scope.references).toHaveLength(7); - const expectedReferenceNames = [ - 'a', // assign array - 'b', // assign array - 'c', // assign array - 'd', // assign e - 'd', // assign array - 'e', - 'array', - ]; - - for (let index = 0; index < expectedReferenceNames.length; index++) { - expect(scope.references[index].identifier.name).toBe( - expectedReferenceNames[index], - ); - } - }); - - it('nested default values containing references and patterns in var', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - var [a, b, [c, d = e] = f ] = array; - }()); - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(5); - const expectedVariableNames = ['arguments', 'a', 'b', 'c', 'd']; - - for (let index = 0; index < expectedVariableNames.length; index++) { - expect(variables[index].name).toBe(expectedVariableNames[index]); - } - expect(scope.references).toHaveLength(10); - const expectedReferenceNames = [ - 'a', // assign array - 'b', // assign array - 'c', // assign f - 'c', // assign array - 'd', // assign f - 'd', // assign e - 'd', // assign array - 'e', - 'f', - 'array', - ]; - - for (let index = 0; index < expectedReferenceNames.length; index++) { - expect(scope.references[index].identifier.name).toBe( - expectedReferenceNames[index], - ); - } - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/es6-export.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/es6-export.test.ts deleted file mode 100644 index 638f9653..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/es6-export.test.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { DefinitionType, ScopeType } from '../../src/index.js'; -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('export declaration', () => { - // http://people.mozilla.org/~jorendorff/es6-draft.html#sec-static-and-runtme-semantics-module-records - it('should create variable bindings', () => { - const { scopeManager } = parseAndAnalyze('export var v;', 'module'); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.module); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('v'); - assert.isDefinitionOfType(variables[0].defs[0], DefinitionType.Variable); - expect(scope.references).toHaveLength(0); - }); - - it('should create function declaration bindings', () => { - const { scopeManager } = parseAndAnalyze( - 'export default function f(){};', - 'module', - ); - - expect(scopeManager.scopes).toHaveLength(3); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.module); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('f'); - assert.isDefinitionOfType( - variables[0].defs[0], - DefinitionType.FunctionName, - ); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[2]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('arguments'); - expect(scope.references).toHaveLength(0); - }); - - it('should export function expression', () => { - const { scopeManager } = parseAndAnalyze( - 'export default function(){};', - 'module', - ); - - expect(scopeManager.scopes).toHaveLength(3); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.module); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[2]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('arguments'); - expect(scope.references).toHaveLength(0); - }); - - it('should export literal', () => { - const { scopeManager } = parseAndAnalyze('export default 42;', 'module'); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.module); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - }); - - it('should refer exported references#1', () => { - const { scopeManager } = parseAndAnalyze( - 'const x = 1; export {x};', - 'module', - ); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.module); - expect(variables).toHaveLength(1); - expect(scope.references).toHaveLength(2); - expect(scope.references[0].identifier.name).toBe('x'); - expect(scope.references[1].identifier.name).toBe('x'); - }); - - it('should refer exported references#2', () => { - const { scopeManager } = parseAndAnalyze( - 'const v = 1; export {v as x};', - 'module', - ); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.module); - expect(variables).toHaveLength(1); - expect(scope.references).toHaveLength(2); - expect(scope.references[0].identifier.name).toBe('v'); - expect(scope.references[1].identifier.name).toBe('v'); - }); - - it('should not refer exported references from other source#1', () => { - const { scopeManager } = parseAndAnalyze( - 'export {x} from "mod";', - 'module', - ); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.module); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - }); - - it('should not refer exported references from other source#2', () => { - const { scopeManager } = parseAndAnalyze( - 'export {v as x} from "mod";', - 'module', - ); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.module); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - }); - - it('should not refer exported references from other source#3', () => { - const { scopeManager } = parseAndAnalyze('export * from "mod";', 'module'); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.module); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/es6-import.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/es6-import.test.ts deleted file mode 100644 index 5f907517..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/es6-import.test.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { DefinitionType, ScopeType } from '../../src/index.js'; -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('import declaration', () => { - // http://people.mozilla.org/~jorendorff/es6-draft.html#sec-static-and-runtme-semantics-module-records - it('should import names from source', () => { - const { scopeManager } = parseAndAnalyze('import v from "mod";', 'module'); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.module); - expect(scope.isStrict).toBe(true); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('v'); - assert.isDefinitionOfType( - variables[0].defs[0], - DefinitionType.ImportBinding, - ); - expect(scope.references).toHaveLength(0); - }); - - it('should import namespaces', () => { - const { scopeManager } = parseAndAnalyze( - 'import * as ns from "mod";', - 'module', - ); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.module); - expect(scope.isStrict).toBe(true); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('ns'); - assert.isDefinitionOfType( - variables[0].defs[0], - DefinitionType.ImportBinding, - ); - expect(scope.references).toHaveLength(0); - }); - - it('should import insided names#1', () => { - const { scopeManager } = parseAndAnalyze( - 'import {x} from "mod";', - 'module', - ); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.module); - expect(scope.isStrict).toBe(true); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('x'); - assert.isDefinitionOfType( - variables[0].defs[0], - DefinitionType.ImportBinding, - ); - expect(scope.references).toHaveLength(0); - }); - - it('should import insided names#2', () => { - const { scopeManager } = parseAndAnalyze( - 'import {x as v} from "mod";', - 'module', - ); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.module); - expect(scope.isStrict).toBe(true); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('v'); - assert.isDefinitionOfType( - variables[0].defs[0], - DefinitionType.ImportBinding, - ); - expect(scope.references).toHaveLength(0); - }); - - it('should reference imports', () => { - const imports = [ - 'import v from "mod";', - 'import { v } from "mod";', - 'import * as v from "mod";', - ]; - for (const code of imports) { - const { scopeManager } = parseAndAnalyze( - ` - ${code} - const x = v; - `, - 'module', - ); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.module); - expect(scope.isStrict).toBe(true); - expect(variables).toHaveLength(2); - const importV = variables[0]; - expect(importV.name).toBe('v'); - assert.isDefinitionOfType(importV.defs[0], DefinitionType.ImportBinding); - const variableX = variables[1]; - expect(variableX.name).toBe('x'); - assert.isDefinitionOfType(variableX.defs[0], DefinitionType.Variable); - - expect(scope.references).toHaveLength(2); - expect(scope.references[0].resolved).toBe(variableX); - expect(scope.references[1].resolved).toBe(importV); - } - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/es6-iteration-scope.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/es6-iteration-scope.test.ts deleted file mode 100644 index 8b34bbc0..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/es6-iteration-scope.test.ts +++ /dev/null @@ -1,197 +0,0 @@ -import { DefinitionType, ScopeType } from '../../src/index.js'; -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('ES6 iteration scope', () => { - it('let materialize iteration scope for ForInStatement#1', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - let i = 20; - for (let i in i) { - console.log(i); - } - }()); - `); - - expect(scopeManager.scopes).toHaveLength(4); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(2); - expect(variables[0].name).toBe('arguments'); - expect(variables[1].name).toBe('i'); - expect(scope.references).toHaveLength(1); - expect(scope.references[0].identifier.name).toBe('i'); - expect(scope.references[0].resolved).toBe(variables[1]); - - const iterScope = (scope = scopeManager.scopes[2]); - variables = getRealVariables(scope.variables); - const iterVariables = getRealVariables(iterScope.variables); - assert.isScopeOfType(scope, ScopeType.for); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('i'); - expect(scope.references).toHaveLength(2); - expect(scope.references[0].identifier.name).toBe('i'); - expect(scope.references[0].resolved).toBe(variables[0]); - expect(scope.references[1].identifier.name).toBe('i'); - expect(scope.references[1].resolved).toBe(variables[0]); - - scope = scopeManager.scopes[3]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.block); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(2); - expect(scope.references[0].identifier.name).toBe('console'); - - assert.isNull(scope.references[0].resolved); - - expect(scope.references[1].identifier.name).toBe('i'); - expect(scope.references[1].resolved).toBe(iterVariables[0]); - }); - - it('let materialize iteration scope for ForInStatement#2', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - let i = 20; - for (let { i, j, k } in i) { - console.log(i); - } - }()); - `); - - expect(scopeManager.scopes).toHaveLength(4); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(2); - expect(variables[0].name).toBe('arguments'); - expect(variables[1].name).toBe('i'); - expect(scope.references).toHaveLength(1); - expect(scope.references[0].identifier.name).toBe('i'); - expect(scope.references[0].resolved).toBe(variables[1]); - - const iterScope = (scope = scopeManager.scopes[2]); - variables = getRealVariables(scope.variables); - const iterVariables = getRealVariables(iterScope.variables); - - assert.isScopeOfType(scope, ScopeType.for); - expect(variables).toHaveLength(3); - expect(variables[0].name).toBe('i'); - expect(variables[1].name).toBe('j'); - expect(variables[2].name).toBe('k'); - expect(scope.references).toHaveLength(4); - expect(scope.references[0].identifier.name).toBe('i'); - expect(scope.references[0].resolved).toBe(variables[0]); - expect(scope.references[1].identifier.name).toBe('j'); - expect(scope.references[1].resolved).toBe(variables[1]); - expect(scope.references[2].identifier.name).toBe('k'); - expect(scope.references[2].resolved).toBe(variables[2]); - expect(scope.references[3].identifier.name).toBe('i'); - expect(scope.references[3].resolved).toBe(variables[0]); - - scope = scopeManager.scopes[3]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.block); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(2); - expect(scope.references[0].identifier.name).toBe('console'); - - assert.isNull(scope.references[0].resolved); - - expect(scope.references[1].identifier.name).toBe('i'); - expect(scope.references[1].resolved).toBe(iterVariables[0]); - }); - - it('let materialize iteration scope for ForStatement#2', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - let i = 20; - let obj = {}; - for (let { i, j, k } = obj; i < okok; ++i) { - console.log(i, j, k); - } - }()); - `); - - expect(scopeManager.scopes).toHaveLength(4); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - - const functionScope = (scope = scopeManager.scopes[1]); - variables = getRealVariables(scope.variables); - const functionVariables = getRealVariables(functionScope.variables); - - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(3); - expect(variables[0].name).toBe('arguments'); - expect(variables[1].name).toBe('i'); - expect(variables[2].name).toBe('obj'); - expect(scope.references).toHaveLength(2); - expect(scope.references[0].identifier.name).toBe('i'); - expect(scope.references[0].resolved).toBe(variables[1]); - expect(scope.references[1].identifier.name).toBe('obj'); - expect(scope.references[1].resolved).toBe(variables[2]); - - const iterScope = (scope = scopeManager.scopes[2]); - variables = getRealVariables(scope.variables); - const iterVariables = getRealVariables(iterScope.variables); - - assert.isScopeOfType(scope, ScopeType.for); - expect(variables).toHaveLength(3); - expect(variables[0].name).toBe('i'); - assert.isDefinitionOfType(variables[0].defs[0], DefinitionType.Variable); - expect(variables[1].name).toBe('j'); - assert.isDefinitionOfType(variables[1].defs[0], DefinitionType.Variable); - expect(variables[2].name).toBe('k'); - assert.isDefinitionOfType(variables[2].defs[0], DefinitionType.Variable); - expect(scope.references).toHaveLength(7); - expect(scope.references[0].identifier.name).toBe('i'); - expect(scope.references[0].resolved).toBe(variables[0]); - expect(scope.references[1].identifier.name).toBe('j'); - expect(scope.references[1].resolved).toBe(variables[1]); - expect(scope.references[2].identifier.name).toBe('k'); - expect(scope.references[2].resolved).toBe(variables[2]); - expect(scope.references[3].identifier.name).toBe('obj'); - expect(scope.references[3].resolved).toBe(functionVariables[2]); - expect(scope.references[4].identifier.name).toBe('i'); - expect(scope.references[4].resolved).toBe(variables[0]); - expect(scope.references[5].identifier.name).toBe('okok'); - - assert.isNull(scope.references[5].resolved); - - expect(scope.references[6].identifier.name).toBe('i'); - expect(scope.references[6].resolved).toBe(variables[0]); - - scope = scopeManager.scopes[3]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.block); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(4); - expect(scope.references[0].identifier.name).toBe('console'); - - assert.isNull(scope.references[0].resolved); - - expect(scope.references[1].identifier.name).toBe('i'); - expect(scope.references[1].resolved).toBe(iterVariables[0]); - expect(scope.references[2].identifier.name).toBe('j'); - expect(scope.references[2].resolved).toBe(iterVariables[1]); - expect(scope.references[3].identifier.name).toBe('k'); - expect(scope.references[3].resolved).toBe(iterVariables[2]); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/es6-new-target.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/es6-new-target.test.ts deleted file mode 100644 index 0a894e87..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/es6-new-target.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { AST_NODE_TYPES } from '@typescript-eslint/types'; - -import { ScopeType } from '../../src/index.js'; -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('ES6 new.target', () => { - it('should not make references of new.target', () => { - const { scopeManager } = parseAndAnalyze(` - class A { - constructor() { - new.target; - } - } - `); - - expect(scopeManager.scopes).toHaveLength(3); - - const scope = scopeManager.scopes[2]; - const variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.function); - expect(scope.block.type).toBe(AST_NODE_TYPES.FunctionExpression); - expect(scope.isStrict).toBe(true); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('arguments'); - expect(scope.references).toHaveLength(0); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/es6-object.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/es6-object.test.ts deleted file mode 100644 index ffdc8541..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/es6-object.test.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { AST_NODE_TYPES } from '@typescript-eslint/types'; - -import { ScopeType } from '../../src/index.js'; -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('ES6 object', () => { - it('method definition', () => { - const { scopeManager } = parseAndAnalyze(` - ({ - constructor() { - } - }) - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(scope.block.type).toBe(AST_NODE_TYPES.Program); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(scope.block.type).toBe(AST_NODE_TYPES.FunctionExpression); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('arguments'); - expect(scope.references).toHaveLength(0); - }); - - it('computed property key may refer variables', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - var yuyushiki = 42; - ({ - [yuyushiki]() { - }, - - [yuyushiki + 40]() { - } - }) - }()); - `); - - expect(scopeManager.scopes).toHaveLength(4); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(scope.block.type).toBe(AST_NODE_TYPES.Program); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(scope.block.type).toBe(AST_NODE_TYPES.FunctionExpression); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(2); - expect(variables[0].name).toBe('arguments'); - expect(variables[1].name).toBe('yuyushiki'); - expect(scope.references).toHaveLength(3); - expect(scope.references[0].identifier.name).toBe('yuyushiki'); - expect(scope.references[1].identifier.name).toBe('yuyushiki'); - expect(scope.references[2].identifier.name).toBe('yuyushiki'); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/es6-rest-args.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/es6-rest-args.test.ts deleted file mode 100644 index aa91ea96..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/es6-rest-args.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { AST_NODE_TYPES } from '@typescript-eslint/types'; - -import { DefinitionType, ScopeType } from '../../src/index.js'; -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('ES6 rest arguments', () => { - it('materialize rest argument in scope', () => { - const { scopeManager } = parseAndAnalyze(` - function foo(...bar) { - return bar; - } - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(scope.block.type).toBe(AST_NODE_TYPES.Program); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(1); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(2); - expect(variables[0].name).toBe('arguments'); - expect(variables[1].name).toBe('bar'); - assert.isNodeOfType(variables[1].defs[0].name, AST_NODE_TYPES.Identifier); - expect(variables[1].defs[0].name.name).toBe('bar'); - assert.isDefinitionOfType(variables[1].defs[0], DefinitionType.Parameter); - expect(variables[1].defs[0].rest).toBe(true); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/es6-super.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/es6-super.test.ts deleted file mode 100644 index 3fd08354..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/es6-super.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { ScopeType } from '../../src/index.js'; -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('ES6 super', () => { - it('is not handled as reference', () => { - const { scopeManager } = parseAndAnalyze(` - class Foo extends Bar { - constructor() { - super(); - } - - method() { - super.method(); - } - } - `); - - expect(scopeManager.scopes).toHaveLength(4); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('Foo'); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.class); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('Foo'); - expect(scope.references).toHaveLength(1); - expect(scope.references[0].identifier.name).toBe('Bar'); - - scope = scopeManager.scopes[2]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('arguments'); - expect(scope.references).toHaveLength(0); // super is specially handled like `this`. - - scope = scopeManager.scopes[3]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('arguments'); - expect(scope.references).toHaveLength(0); // super is specially handled like `this`. - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/es6-switch.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/es6-switch.test.ts deleted file mode 100644 index 7455b49d..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/es6-switch.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { AST_NODE_TYPES } from '@typescript-eslint/types'; - -import { ScopeType } from '../../src/index.js'; -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('ES6 switch', () => { - it('materialize scope', () => { - const { scopeManager } = parseAndAnalyze(` - switch (ok) { - case hello: - let i = 20; - i; - break; - - default: - let test = 30; - test; - } - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(scope.block.type).toBe(AST_NODE_TYPES.Program); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(1); - expect(scope.references[0].identifier.name).toBe('ok'); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.switch); - expect(scope.block.type).toBe(AST_NODE_TYPES.SwitchStatement); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(2); - expect(variables[0].name).toBe('i'); - expect(variables[1].name).toBe('test'); - expect(scope.references).toHaveLength(5); - expect(scope.references[0].identifier.name).toBe('hello'); - expect(scope.references[1].identifier.name).toBe('i'); - expect(scope.references[2].identifier.name).toBe('i'); - expect(scope.references[3].identifier.name).toBe('test'); - expect(scope.references[4].identifier.name).toBe('test'); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/es6-template-literal.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/es6-template-literal.test.ts deleted file mode 100644 index 050fb5c7..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/es6-template-literal.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { AST_NODE_TYPES } from '@typescript-eslint/types'; - -import { ScopeType } from '../../src/index.js'; -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('ES6 template literal', () => { - it('refer variables', () => { - const { scopeManager } = parseAndAnalyze(` - (function () { - let i, j, k; - function testing() { } - let template = testing\`testing \${i} and \${j}\` - return template; - }()); - `); - - expect(scopeManager.scopes).toHaveLength(3); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(scope.block.type).toBe(AST_NODE_TYPES.Program); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(scope.block.type).toBe(AST_NODE_TYPES.FunctionExpression); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(6); - expect(variables[0].name).toBe('arguments'); - expect(variables[1].name).toBe('i'); - expect(variables[2].name).toBe('j'); - expect(variables[3].name).toBe('k'); - expect(variables[4].name).toBe('testing'); - expect(variables[5].name).toBe('template'); - expect(scope.references).toHaveLength(5); - expect(scope.references[0].identifier.name).toBe('template'); - expect(scope.references[1].identifier.name).toBe('testing'); - expect(scope.references[2].identifier.name).toBe('i'); - expect(scope.references[3].identifier.name).toBe('j'); - expect(scope.references[4].identifier.name).toBe('template'); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/function-expression-name.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/function-expression-name.test.ts deleted file mode 100644 index 22466a07..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/function-expression-name.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { ScopeType } from '../../src/index.js'; -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('function name', () => { - it('should create its special scope', () => { - const { scopeManager } = parseAndAnalyze(` - (function name() { - }()); - `); - - expect(scopeManager.scopes).toHaveLength(3); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - const globalScope = scope; - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - // Function expression name scope - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.functionExpressionName); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('name'); - expect(scope.references).toHaveLength(0); - expect(scope.upper).toBe(globalScope); - - // Function scope - scope = scopeManager.scopes[2]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(1); - expect(scope.variables[0].name).toBe('arguments'); - expect(scope.references).toHaveLength(0); - expect(scope.upper).toBe(scopeManager.scopes[1]); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/get-declared-variables.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/get-declared-variables.test.ts deleted file mode 100644 index 9b14a340..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/get-declared-variables.test.ts +++ /dev/null @@ -1,197 +0,0 @@ -import { AST_NODE_TYPES } from '@typescript-eslint/types'; - -describe('ScopeManager.prototype.getDeclaredVariables', () => { - it('should get variables that declared on `VariableDeclaration`', () => { - expect(` - var {a, x: [b], y: {c = 0}} = foo; - let {d, x: [e], y: {f = 0}} = foo; - const {g, x: [h], y: {i = 0}} = foo, {j, k = function() { let l; }} = bar; - `).toHaveDeclaredVariables({ - astNodeType: AST_NODE_TYPES.VariableDeclaration, - expectedNamesList: [ - ['a', 'b', 'c'], - ['d', 'e', 'f'], - ['g', 'h', 'i', 'j', 'k'], - ['l'], - ], - }); - }); - - it('should get variables that declared on `VariableDeclaration` in for-in/of', () => { - expect(` - for (var {a, x: [b], y: {c = 0}} in foo) { - let g; - } - for (let {d, x: [e], y: {f = 0}} of foo) { - let h; - } - `).toHaveDeclaredVariables({ - astNodeType: AST_NODE_TYPES.VariableDeclaration, - expectedNamesList: [['a', 'b', 'c'], ['g'], ['d', 'e', 'f'], ['h']], - }); - }); - - it('should get variables that declared on `VariableDeclarator`', () => { - expect(` - var {a, x: [b], y: {c = 0}} = foo; - let {d, x: [e], y: {f = 0}} = foo; - const {g, x: [h], y: {i = 0}} = foo, {j, k = function() { let l; }} = bar; - `).toHaveDeclaredVariables({ - astNodeType: AST_NODE_TYPES.VariableDeclarator, - expectedNamesList: [ - ['a', 'b', 'c'], - ['d', 'e', 'f'], - ['g', 'h', 'i'], - ['j', 'k'], - ['l'], - ], - }); - }); - - it('should get variables that declared on `FunctionDeclaration`', () => { - expect(` - function foo({a, x: [b], y: {c = 0}}, [d, e]) { - let z; - } - function bar({f, x: [g], y: {h = 0}}, [i, j = function(q) { let w; }]) { - let z; - } - `).toHaveDeclaredVariables({ - astNodeType: AST_NODE_TYPES.FunctionDeclaration, - expectedNamesList: [ - ['foo', 'a', 'b', 'c', 'd', 'e'], - ['bar', 'f', 'g', 'h', 'i', 'j'], - ], - }); - }); - - it('should get variables that declared on `FunctionExpression`', () => { - expect(` - (function foo({a, x: [b], y: {c = 0}}, [d, e]) { - let z; - }); - (function bar({f, x: [g], y: {h = 0}}, [i, j = function(q) { let w; }]) { - let z; - }); - `).toHaveDeclaredVariables({ - astNodeType: AST_NODE_TYPES.FunctionExpression, - expectedNamesList: [ - ['foo', 'a', 'b', 'c', 'd', 'e'], - ['bar', 'f', 'g', 'h', 'i', 'j'], - ['q'], - ], - }); - }); - - it('should get variables that declared on `ArrowFunctionExpression`', () => { - expect(` - (({a, x: [b], y: {c = 0}}, [d, e]) => { - let z; - }); - (({f, x: [g], y: {h = 0}}, [i, j]) => { - let z; - }); - `).toHaveDeclaredVariables({ - astNodeType: AST_NODE_TYPES.ArrowFunctionExpression, - expectedNamesList: [ - ['a', 'b', 'c', 'd', 'e'], - ['f', 'g', 'h', 'i', 'j'], - ], - }); - }); - - it('should get variables that declared on `ClassDeclaration`', () => { - expect(` - class A { foo(x) { let y; } } - class B { foo(x) { let y; } } - `).toHaveDeclaredVariables({ - astNodeType: AST_NODE_TYPES.ClassDeclaration, - expectedNamesList: [ - ['A', 'A'], // outer scope"s and inner scope"s. - ['B', 'B'], - ], - }); - }); - - it('should get variables that declared on `ClassExpression`', () => { - expect(` - (class A { foo(x) { let y; } }); - (class B { foo(x) { let y; } }); - `).toHaveDeclaredVariables({ - astNodeType: AST_NODE_TYPES.ClassExpression, - expectedNamesList: [['A'], ['B']], - }); - }); - - it('should get variables that declared on `CatchClause`', () => { - expect(` - try {} catch ({a, b}) { - let x; - try {} catch ({c, d}) { - let y; - } - } - `).toHaveDeclaredVariables({ - astNodeType: AST_NODE_TYPES.CatchClause, - expectedNamesList: [ - ['a', 'b'], - ['c', 'd'], - ], - }); - }); - - it('should get variables that declared on `ImportDeclaration`', () => { - expect(` - import "aaa"; - import * as a from "bbb"; - import b, {c, x as d} from "ccc";\ - `).toHaveDeclaredVariables({ - astNodeType: AST_NODE_TYPES.ImportDeclaration, - expectedNamesList: [[], ['a'], ['b', 'c', 'd']], - parserOptions: { sourceType: 'module' }, - }); - }); - - it('should get variables that declared on `ImportSpecifier`', () => { - expect(` - import "aaa"; - import * as a from "bbb"; - import b, {c, x as d} from "ccc"; - `).toHaveDeclaredVariables({ - astNodeType: AST_NODE_TYPES.ImportSpecifier, - expectedNamesList: [['c'], ['d']], - parserOptions: { sourceType: 'module' }, - }); - }); - - it('should get variables that declared on `ImportDefaultSpecifier`', () => { - expect(` - import "aaa"; - import * as a from "bbb"; - import b, {c, x as d} from "ccc"; - `).toHaveDeclaredVariables({ - astNodeType: AST_NODE_TYPES.ImportDefaultSpecifier, - expectedNamesList: [['b']], - parserOptions: { sourceType: 'module' }, - }); - }); - - it('should get variables that declared on `ImportNamespaceSpecifier`', () => { - expect(` - import "aaa"; - import * as a from "bbb"; - import b, {c, x as d} from "ccc"; - `).toHaveDeclaredVariables({ - astNodeType: AST_NODE_TYPES.ImportNamespaceSpecifier, - expectedNamesList: [['a']], - parserOptions: { sourceType: 'module' }, - }); - }); - - it("should not get duplicate even if it's declared twice", () => { - expect('var a = 0, a = 1;').toHaveDeclaredVariables({ - astNodeType: AST_NODE_TYPES.VariableDeclaration, - expectedNamesList: [['a']], - }); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/global-increment.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/global-increment.test.ts deleted file mode 100644 index f52bc15b..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/global-increment.test.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { ScopeType } from '../../src/index.js'; -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('global increment', () => { - it('becomes read/write', () => { - const { scopeManager } = parseAndAnalyze('b++;'); - - expect(scopeManager.scopes).toHaveLength(1); - - const scope = scopeManager.scopes[0]; - const variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(1); - expect(scope.references[0].isReadWrite()).toBe(true); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/global-return.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/global-return.test.ts deleted file mode 100644 index 4c04f61a..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/global-return.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { AST_NODE_TYPES } from '@typescript-eslint/types'; - -import { DefinitionType, ScopeType } from '../../src/index.js'; -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('globalReturn option', () => { - it('creates a function scope following the global scope immediately', () => { - const { scopeManager } = parseAndAnalyze( - ` - "use strict"; - var hello = 20; - `, - { globalReturn: true }, - ); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(scope.block.type).toBe(AST_NODE_TYPES.Program); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(scope.block.type).toBe(AST_NODE_TYPES.Program); - expect(scope.isStrict).toBe(true); - expect(variables).toHaveLength(2); - expect(variables[0].name).toBe('arguments'); - expect(variables[1].name).toBe('hello'); - }); - - it('creates a function scope following the global scope immediately and creates module scope', () => { - const { scopeManager } = parseAndAnalyze("import {x as v} from 'mod';", { - globalReturn: true, - sourceType: 'module', - }); - - expect(scopeManager.scopes).toHaveLength(3); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - - assert.isScopeOfType(scope, ScopeType.global); - expect(scope.block.type).toBe(AST_NODE_TYPES.Program); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(scope.block.type).toBe(AST_NODE_TYPES.Program); - expect(scope.isStrict).toBe(false); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('arguments'); - - scope = scopeManager.scopes[2]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.module); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('v'); - assert.isDefinitionOfType( - variables[0].defs[0], - DefinitionType.ImportBinding, - ); - expect(scope.references).toHaveLength(0); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/implicit-global-reference.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/implicit-global-reference.test.ts deleted file mode 100644 index e7219087..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/implicit-global-reference.test.ts +++ /dev/null @@ -1,238 +0,0 @@ -/* eslint-disable @typescript-eslint/dot-notation -- ['implicit'] is private */ -import type { Variable, Reference } from '../../src/index.js'; - -import { DefinitionType, ScopeType } from '../../src/index.js'; -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('implicit global reference', () => { - it('assignments global scope', () => { - const { scopeManager } = parseAndAnalyze(` - var x = 20; - x = 300; - `); - - const { scopes } = scopeManager; - - expect( - scopes.map(scope => - getRealVariables(scope.variables).map(variable => - variable.defs.map(def => def.type), - ), - ), - ).toStrictEqual([[[DefinitionType.Variable]]]); - - assert.isScopeOfType(scopes[0], ScopeType.global); - expect( - scopes[0]['implicit'].variables.map( - (variable: Variable) => variable.name, - ), - ).toStrictEqual([]); - expect( - scopes[0]['implicit'].leftToBeResolved.map( - (reference: Reference) => reference.identifier.name, - ), - ).toStrictEqual([]); - expect(scopes[0].through).toStrictEqual([]); - }); - - it('assignments global scope without definition', () => { - const { scopeManager } = parseAndAnalyze(` - x = 300; - x = 300; - `); - - const { scopes } = scopeManager; - - expect( - scopes.map(scope => - getRealVariables(scope.variables).map(variable => - variable.defs.map(def => def.type), - ), - ), - ).toStrictEqual([[]]); - - assert.isScopeOfType(scopes[0], ScopeType.global); - expect( - scopes[0]['implicit'].variables.map( - (variable: Variable) => variable.name, - ), - ).toStrictEqual(['x']); - expect( - scopes[0]['implicit'].leftToBeResolved.map( - (reference: Reference) => reference.identifier.name, - ), - ).toStrictEqual(['x', 'x']); - expect( - scopes[0].through.map(reference => reference.identifier.name), - ).toStrictEqual(['x', 'x']); - }); - - it('assignments global scope without definition eval', () => { - const { scopeManager } = parseAndAnalyze(` - function inner() { - eval(str); - x = 300; - } - `); - - const { scopes } = scopeManager; - - expect( - scopes.map(scope => - getRealVariables(scope.variables).map(variable => - variable.defs.map(def => def.type), - ), - ), - ).toStrictEqual([[[DefinitionType.FunctionName]], [[]]]); - - assert.isScopeOfType(scopes[0], ScopeType.global); - expect( - scopes[0]['implicit'].variables.map( - (variable: Variable) => variable.name, - ), - ).toStrictEqual(['x']); - }); - - it('assignment leaks', () => { - const { scopeManager } = parseAndAnalyze(` - function outer() { - x = 20; - } - `); - - const { scopes } = scopeManager; - - expect( - scopes.map(scope => - getRealVariables(scope.variables).map(variable => variable.name), - ), - ).toStrictEqual([['outer'], ['arguments']]); - - assert.isScopeOfType(scopes[0], ScopeType.global); - expect( - scopes[0]['implicit'].variables.map( - (variable: Variable) => variable.name, - ), - ).toStrictEqual(['x']); - expect( - scopes[0]['implicit'].leftToBeResolved.map( - (reference: Reference) => reference.identifier.name, - ), - ).toStrictEqual(['x']); - expect( - scopes[0].through.map( - (reference: Reference) => reference.identifier.name, - ), - ).toStrictEqual(['x']); - }); - - it("assignment doesn't leak", () => { - const { scopeManager } = parseAndAnalyze(` - function outer() { - function inner() { - x = 20; - } - var x; - } - `); - - const { scopes } = scopeManager; - - expect( - scopes.map(scope => - getRealVariables(scope.variables).map(variable => variable.name), - ), - ).toStrictEqual([['outer'], ['arguments', 'inner', 'x'], ['arguments']]); - - assert.isScopeOfType(scopes[0], ScopeType.global); - expect( - scopes[0]['implicit'].variables.map( - (variable: Variable) => variable.name, - ), - ).toStrictEqual([]); - expect( - scopes[0]['implicit'].leftToBeResolved.map( - (reference: Reference) => reference.identifier.name, - ), - ).toStrictEqual([]); - expect( - scopes[0].through.map( - (reference: Reference) => reference.identifier.name, - ), - ).toStrictEqual([]); - }); - - it('for-in-statement leaks', () => { - const { scopeManager } = parseAndAnalyze(` - function outer() { - for (x in y) { } - } - `); - - const { scopes } = scopeManager; - - expect( - scopes.map(scope => - getRealVariables(scope.variables).map(variable => variable.name), - ), - ).toStrictEqual([['outer'], ['arguments'], []]); - - assert.isScopeOfType(scopes[0], ScopeType.global); - expect( - scopes[0]['implicit'].variables.map( - (variable: Variable) => variable.name, - ), - ).toStrictEqual(['x']); - expect( - scopes[0]['implicit'].leftToBeResolved.map( - (reference: Reference) => reference.identifier.name, - ), - ).toStrictEqual(['x', 'y']); - expect( - scopes[0].through.map( - (reference: Reference) => reference.identifier.name, - ), - ).toStrictEqual(['x', 'y']); - }); - - it("for-in-statement doesn't leaks", () => { - const { scopeManager } = parseAndAnalyze(` - function outer() { - function inner() { - for (x in y) { } - } - var x; - } - `); - - const { scopes } = scopeManager; - - expect( - scopes.map(scope => - getRealVariables(scope.variables).map(variable => variable.name), - ), - ).toStrictEqual([ - ['outer'], - ['arguments', 'inner', 'x'], - ['arguments'], - [], - ]); - - assert.isScopeOfType(scopes[0], ScopeType.global); - expect( - scopes[0]['implicit'].variables.map( - (variable: Variable) => variable.name, - ), - ).toStrictEqual([]); - expect( - scopes[0]['implicit'].leftToBeResolved.map( - (reference: Reference) => reference.identifier.name, - ), - ).toStrictEqual(['y']); - expect( - scopes[0].through.map( - (reference: Reference) => reference.identifier.name, - ), - ).toStrictEqual(['y']); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/implied-strict.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/implied-strict.test.ts deleted file mode 100644 index 4d4ae716..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/implied-strict.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { AST_NODE_TYPES } from '@typescript-eslint/types'; - -import { ScopeType } from '../../src/index.js'; -import { parseAndAnalyze } from '../test-utils/index.js'; - -describe('impliedStrict option', () => { - it('ensures all user scopes are strict', () => { - const { scopeManager } = parseAndAnalyze( - ` - function foo() { - function bar() { - "use strict"; - } - } - `, - { - impliedStrict: true, - }, - ); - - expect(scopeManager.scopes).toHaveLength(3); - - let scope = scopeManager.scopes[0]; - - assert.isScopeOfType(scope, ScopeType.global); - expect(scope.block.type).toBe(AST_NODE_TYPES.Program); - expect(scope.isStrict).toBe(true); - - scope = scopeManager.scopes[1]; - assert.isScopeOfType(scope, ScopeType.function); - expect(scope.block.type).toBe(AST_NODE_TYPES.FunctionDeclaration); - expect(scope.isStrict).toBe(true); - - scope = scopeManager.scopes[2]; - assert.isScopeOfType(scope, ScopeType.function); - expect(scope.block.type).toBe(AST_NODE_TYPES.FunctionDeclaration); - expect(scope.isStrict).toBe(true); - }); - - it('omits a nodejs global scope when ensuring all user scopes are strict', () => { - const { scopeManager } = parseAndAnalyze( - ` - function foo() {} - `, - { - globalReturn: true, - impliedStrict: true, - }, - ); - - expect(scopeManager.scopes).toHaveLength(3); - - let scope = scopeManager.scopes[0]; - - assert.isScopeOfType(scope, ScopeType.global); - expect(scope.block.type).toBe(AST_NODE_TYPES.Program); - expect(scope.isStrict).toBe(false); - - scope = scopeManager.scopes[1]; - assert.isScopeOfType(scope, ScopeType.function); - expect(scope.block.type).toBe(AST_NODE_TYPES.Program); - expect(scope.isStrict).toBe(true); - - scope = scopeManager.scopes[2]; - assert.isScopeOfType(scope, ScopeType.function); - expect(scope.block.type).toBe(AST_NODE_TYPES.FunctionDeclaration); - expect(scope.isStrict).toBe(true); - }); - - it('omits a module global scope when ensuring all user scopes are strict', () => { - const { scopeManager } = parseAndAnalyze('function foo() {}', { - impliedStrict: true, - sourceType: 'module', - }); - - expect(scopeManager.scopes).toHaveLength(3); - - let scope = scopeManager.scopes[0]; - - assert.isScopeOfType(scope, ScopeType.global); - expect(scope.block.type).toBe(AST_NODE_TYPES.Program); - expect(scope.isStrict).toBe(false); - - scope = scopeManager.scopes[1]; - assert.isScopeOfType(scope, ScopeType.module); - expect(scope.isStrict).toBe(true); - - scope = scopeManager.scopes[2]; - assert.isScopeOfType(scope, ScopeType.function); - expect(scope.block.type).toBe(AST_NODE_TYPES.FunctionDeclaration); - expect(scope.isStrict).toBe(true); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/label.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/label.test.ts deleted file mode 100644 index 2d673ef8..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/label.test.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { ScopeType } from '../../src/index.js'; -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('label', () => { - it('should not create variables', () => { - const { scopeManager } = parseAndAnalyze( - 'function bar() { q: for(;;) { break q; } }', - ); - - expect(scopeManager.scopes).toHaveLength(3); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('bar'); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('arguments'); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[2]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.block); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - }); - - it('should count child node references', () => { - const { scopeManager } = parseAndAnalyze(` - var foo = 5; - - label: while (true) { - console.log(foo); - break; - } - `); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('foo'); - expect(scope.through).toHaveLength(1); - expect(scope.through[0].identifier.name).toBe('console'); - expect(variables[0].references).toHaveLength(2); - expect(variables[0].references[1].isRead()).toBe(true); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.block); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(2); - expect(scope.references[0].identifier.name).toBe('console'); - expect(scope.references[1].identifier.name).toBe('foo'); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/references.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/references.test.ts deleted file mode 100644 index 84fa2da7..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/references.test.ts +++ /dev/null @@ -1,636 +0,0 @@ -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('References:', () => { - describe('When there is a `let` declaration on global,', () => { - it('the reference on global should be resolved.', () => { - const { scopeManager } = parseAndAnalyze('let a = 0;'); - - expect(scopeManager.scopes).toHaveLength(1); - - const scope = scopeManager.scopes[0]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(1); - expect(scope.references).toHaveLength(1); - - const reference = scope.references[0]; - - expect(reference.from).toBe(scope); - expect(reference.identifier.name).toBe('a'); - expect(reference.resolved).toBe(variables[0]); - - assert.exists(reference.writeExpr); - - expect(reference.isWrite()).toBe(true); - expect(reference.isRead()).toBe(false); - }); - - it('the reference in functions should be resolved.', () => { - const { scopeManager } = parseAndAnalyze(` - let a = 0; - function foo() { - let b = a; - } - `); - - expect(scopeManager.scopes).toHaveLength(2); // [global, foo] - - const scope = scopeManager.scopes[1]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(2); // [arguments, b] - expect(scope.references).toHaveLength(2); // [b, a] - - const reference = scope.references[1]; - - expect(reference.from).toBe(scope); - expect(reference.identifier.name).toBe('a'); - expect(reference.resolved).toBe( - getRealVariables(scopeManager.scopes[0].variables)[0], - ); - - assert.notExists(reference.writeExpr); - - expect(reference.isWrite()).toBe(false); - expect(reference.isRead()).toBe(true); - }); - - it('the reference in default parameters should be resolved.', () => { - const { scopeManager } = parseAndAnalyze(` - let a = 0; - function foo(b = a) { - } - `); - - expect(scopeManager.scopes).toHaveLength(2); // [global, foo] - - const scope = scopeManager.scopes[1]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(2); // [arguments, b] - expect(scope.references).toHaveLength(2); // [b, a] - - const reference = scope.references[1]; - - expect(reference.from).toBe(scope); - expect(reference.identifier.name).toBe('a'); - expect(reference.resolved).toBe( - getRealVariables(scopeManager.scopes[0].variables)[0], - ); - - assert.notExists(reference.writeExpr); - - expect(reference.isWrite()).toBe(false); - expect(reference.isRead()).toBe(true); - }); - }); - - describe('When there is a `const` declaration on global,', () => { - it('the reference on global should be resolved.', () => { - const { scopeManager } = parseAndAnalyze('const a = 0;'); - - expect(scopeManager.scopes).toHaveLength(1); - - const scope = scopeManager.scopes[0]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(1); - expect(scope.references).toHaveLength(1); - - const reference = scope.references[0]; - - expect(reference.from).toBe(scope); - expect(reference.identifier.name).toBe('a'); - expect(reference.resolved).toBe(variables[0]); - - assert.exists(reference.writeExpr); - - expect(reference.isWrite()).toBe(true); - expect(reference.isRead()).toBe(false); - }); - - it('the reference in functions should be resolved.', () => { - const { scopeManager } = parseAndAnalyze(` - const a = 0; - function foo() { - const b = a; - } - `); - - expect(scopeManager.scopes).toHaveLength(2); // [global, foo] - - const scope = scopeManager.scopes[1]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(2); // [arguments, b] - expect(scope.references).toHaveLength(2); // [b, a] - - const reference = scope.references[1]; - - expect(reference.from).toBe(scope); - expect(reference.identifier.name).toBe('a'); - expect(reference.resolved).toBe( - getRealVariables(scopeManager.scopes[0].variables)[0], - ); - - assert.notExists(reference.writeExpr); - - expect(reference.isWrite()).toBe(false); - expect(reference.isRead()).toBe(true); - }); - }); - - describe('When there is a `var` declaration on global,', () => { - it('the reference on global should be resolved.', () => { - const { scopeManager } = parseAndAnalyze('var a = 0;'); - - expect(scopeManager.scopes).toHaveLength(1); - - const scope = scopeManager.scopes[0]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(1); - expect(scope.references).toHaveLength(1); - - const reference = scope.references[0]; - - expect(reference.from).toBe(scope); - expect(reference.identifier.name).toBe('a'); - expect(reference.resolved).toStrictEqual(variables[0]); - - assert.exists(reference.writeExpr); - - expect(reference.isWrite()).toBe(true); - expect(reference.isRead()).toBe(false); - }); - - it('the reference in functions should be resolved.', () => { - const { scopeManager } = parseAndAnalyze(` - var a = 0; - function foo() { - var b = a; - } - `); - - expect(scopeManager.scopes).toHaveLength(2); // [global, foo] - - const scope = scopeManager.scopes[1]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(2); // [arguments, b] - expect(scope.references).toHaveLength(2); // [b, a] - - const reference = scope.references[1]; - - expect(reference.from).toBe(scope); - expect(reference.identifier.name).toBe('a'); - const [outerVariable] = getRealVariables( - scopeManager.scopes[0].variables, - ); - expect(reference.resolved).toStrictEqual(outerVariable); - - assert.notExists(reference.writeExpr); - - expect(reference.isWrite()).toBe(false); - expect(reference.isRead()).toBe(true); - }); - }); - - describe('When there is a `function` declaration on global,', () => { - it('the reference on global should be resolved.', () => { - const { scopeManager } = parseAndAnalyze( - ` - function a() {} - a(); - `, - { sourceType: 'script' }, - ); - - expect(scopeManager.scopes).toHaveLength(2); // [global, a] - - const scope = scopeManager.scopes[0]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(1); - expect(scope.references).toHaveLength(1); - - const reference = scope.references[0]; - - expect(reference.from).toStrictEqual(scope); - expect(reference.identifier.name).toBe('a'); - expect(reference.resolved).toStrictEqual(variables[0]); - expect(reference.writeExpr).toBeUndefined(); - expect(reference.isWrite()).toBe(false); - expect(reference.isRead()).toBe(true); - }); - - it('the reference in functions should be resolved.', () => { - const { scopeManager } = parseAndAnalyze(` - function a() {} - function foo() { - let b = a(); - } - `); - - expect(scopeManager.scopes).toHaveLength(3); // [global, a, foo] - - const scope = scopeManager.scopes[2]; - - expect(scope.variables).toHaveLength(2); // [arguments, b] - expect(scope.references).toHaveLength(2); // [b, a] - - const reference = scope.references[1]; - - expect(reference.from).toStrictEqual(scope); - expect(reference.identifier.name).toBe('a'); - const [variable] = getRealVariables(scopeManager.scopes[0].variables); - expect(reference.resolved).toStrictEqual(variable); - expect(reference.writeExpr).toBeUndefined(); - expect(reference.isWrite()).toBe(false); - expect(reference.isRead()).toBe(true); - }); - }); - - describe('When there is a `class` declaration on global,', () => { - it('the reference on global should be resolved.', () => { - const { scopeManager } = parseAndAnalyze(` - class A {} - let b = new A(); - `); - - expect(scopeManager.scopes).toHaveLength(2); // [global, A] - - const scope = scopeManager.scopes[0]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(2); // [A, b] - expect(scope.references).toHaveLength(2); // [b, A] - - const reference = scope.references[1]; - - expect(reference.from).toBe(scope); - expect(reference.identifier.name).toBe('A'); - expect(reference.resolved).toBe(variables[0]); - - assert.notExists(reference.writeExpr); - - expect(reference.isWrite()).toBe(false); - expect(reference.isRead()).toBe(true); - }); - - it('the reference in functions should be resolved.', () => { - const { scopeManager } = parseAndAnalyze(` - class A {} - function foo() { - let b = new A(); - } - `); - - expect(scopeManager.scopes).toHaveLength(3); // [global, A, foo] - - const scope = scopeManager.scopes[2]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(2); // [arguments, b] - expect(scope.references).toHaveLength(2); // [b, A] - - const reference = scope.references[1]; - - expect(reference.from).toBe(scope); - expect(reference.identifier.name).toBe('A'); - expect(reference.resolved).toBe( - getRealVariables(scopeManager.scopes[0].variables)[0], - ); - - assert.notExists(reference.writeExpr); - - expect(reference.isWrite()).toBe(false); - expect(reference.isRead()).toBe(true); - }); - }); - - describe('When there is a `let` declaration in functions,', () => { - it('the reference on the function should be resolved.', () => { - const { scopeManager } = parseAndAnalyze(` - function foo() { - let a = 0; - } - `); - - expect(scopeManager.scopes).toHaveLength(2); // [global, foo] - - const scope = scopeManager.scopes[1]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(2); // [arguments, a] - expect(scope.references).toHaveLength(1); - - const reference = scope.references[0]; - - expect(reference.from).toBe(scope); - expect(reference.identifier.name).toBe('a'); - expect(reference.resolved).toBe(variables[1]); - - assert.exists(reference.writeExpr); - - expect(reference.isWrite()).toBe(true); - expect(reference.isRead()).toBe(false); - }); - - it('the reference in nested functions should be resolved.', () => { - const { scopeManager } = parseAndAnalyze(` - function foo() { - let a = 0; - function bar() { - let b = a; - } - } - `); - - expect(scopeManager.scopes).toHaveLength(3); // [global, foo, bar] - - const scope = scopeManager.scopes[2]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(2); // [arguments, b] - expect(scope.references).toHaveLength(2); // [b, a] - - const reference = scope.references[1]; - - expect(reference.from).toBe(scope); - expect(reference.identifier.name).toBe('a'); - expect(reference.resolved).toBe( - getRealVariables(scopeManager.scopes[1].variables)[1], - ); - - assert.notExists(reference.writeExpr); - - expect(reference.isWrite()).toBe(false); - expect(reference.isRead()).toBe(true); - }); - }); - - describe('When there is a `var` declaration in functions,', () => { - it('the reference on the function should be resolved.', () => { - const { scopeManager } = parseAndAnalyze(` - function foo() { - var a = 0; - } - `); - - expect(scopeManager.scopes).toHaveLength(2); // [global, foo] - - const scope = scopeManager.scopes[1]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(2); // [arguments, a] - expect(scope.references).toHaveLength(1); - - const reference = scope.references[0]; - - expect(reference.from).toBe(scope); - expect(reference.identifier.name).toBe('a'); - expect(reference.resolved).toBe(variables[1]); - - assert.exists(reference.writeExpr); - - expect(reference.isWrite()).toBe(true); - expect(reference.isRead()).toBe(false); - }); - - it('the reference in nested functions should be resolved.', () => { - const { scopeManager } = parseAndAnalyze(` - function foo() { - var a = 0; - function bar() { - var b = a; - } - } - `); - - expect(scopeManager.scopes).toHaveLength(3); // [global, foo, bar] - - const scope = scopeManager.scopes[2]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(2); // [arguments, b] - expect(scope.references).toHaveLength(2); // [b, a] - - const reference = scope.references[1]; - - expect(reference.from).toBe(scope); - expect(reference.identifier.name).toBe('a'); - expect(reference.resolved).toBe( - getRealVariables(scopeManager.scopes[1].variables)[1], - ); - - assert.notExists(reference.writeExpr); - - expect(reference.isWrite()).toBe(false); - expect(reference.isRead()).toBe(true); - }); - }); - - describe('When there is a `let` declaration with destructuring assignment', () => { - it('"let [a] = [1];", the reference should be resolved.', () => { - const { scopeManager } = parseAndAnalyze('let [a] = [1];'); - - expect(scopeManager.scopes).toHaveLength(1); - - const scope = scopeManager.scopes[0]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(1); - expect(scope.references).toHaveLength(1); - - const reference = scope.references[0]; - - expect(reference.from).toBe(scope); - expect(reference.identifier.name).toBe('a'); - expect(reference.resolved).toBe(variables[0]); - - assert.exists(reference.writeExpr); - - expect(reference.isWrite()).toBe(true); - expect(reference.isRead()).toBe(false); - }); - - it('"let {a} = {a: 1};", the reference should be resolved.', () => { - const { scopeManager } = parseAndAnalyze('let {a} = {a: 1};'); - - expect(scopeManager.scopes).toHaveLength(1); - - const scope = scopeManager.scopes[0]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(1); - expect(scope.references).toHaveLength(1); - - const reference = scope.references[0]; - - expect(reference.from).toBe(scope); - expect(reference.identifier.name).toBe('a'); - expect(reference.resolved).toBe(variables[0]); - - assert.exists(reference.writeExpr); - - expect(reference.isWrite()).toBe(true); - expect(reference.isRead()).toBe(false); - }); - - it('"let {a: {a}} = {a: {a: 1}};", the reference should be resolved.', () => { - const { scopeManager } = parseAndAnalyze('let {a: {a}} = {a: {a: 1}};'); - - expect(scopeManager.scopes).toHaveLength(1); - - const scope = scopeManager.scopes[0]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(1); - expect(scope.references).toHaveLength(1); - - const reference = scope.references[0]; - - expect(reference.from).toBe(scope); - expect(reference.identifier.name).toBe('a'); - expect(reference.resolved).toBe(variables[0]); - - assert.exists(reference.writeExpr); - - expect(reference.isWrite()).toBe(true); - expect(reference.isRead()).toBe(false); - }); - }); - - describe('Reference.init should be a boolean value of whether it is one to initialize or not.', () => { - const trueCodes = [ - 'var a = 0;', - 'let a = 0;', - 'const a = 0;', - 'var [a] = [];', - 'let [a] = [];', - 'const [a] = [];', - 'var [a = 1] = [];', - 'let [a = 1] = [];', - 'const [a = 1] = [];', - 'var {a} = {};', - 'let {a} = {};', - 'const {a} = {};', - 'var {b: a} = {};', - 'let {b: a} = {};', - 'const {b: a} = {};', - 'var {b: a = 0} = {};', - 'let {b: a = 0} = {};', - 'const {b: a = 0} = {};', - 'for (var a in []);', - 'for (let a in []);', - 'for (var [a] in []);', - 'for (let [a] in []);', - 'for (var [a = 0] in []);', - 'for (let [a = 0] in []);', - 'for (var {a} in []);', - 'for (let {a} in []);', - 'for (var {a = 0} in []);', - 'for (let {a = 0} in []);', - 'new function(a = 0) {}', - 'new function([a = 0] = []) {}', - 'new function({b: a = 0} = {}) {}', - ] as const; - - it.for(trueCodes)( - '"%s", all references should be true.', - (code, { expect }) => { - const { scopeManager } = parseAndAnalyze(code); - - expect(scopeManager.scopes.length).toBeGreaterThanOrEqual(1); - - const scope = scopeManager.scopes[scopeManager.scopes.length - 1]; - const variables = getRealVariables(scope.variables); - - expect(variables.length).toBeGreaterThanOrEqual(1); - expect(scope.references.length).toBeGreaterThanOrEqual(1); - - scope.references.forEach(reference => { - expect(reference.identifier.name).toBe('a'); - expect(reference.isWrite()).toBe(true); - expect(reference.init).toBe(true); - }); - }, - ); - - const falseCodes = [ - 'let a; a = 0;', - 'let a; [a] = [];', - 'let a; [a = 1] = [];', - 'let a; ({a} = {});', - 'let a; ({b: a} = {});', - 'let a; ({b: a = 0} = {});', - 'let a; for (a in []);', - 'let a; for ([a] in []);', - 'let a; for ([a = 0] in []);', - 'let a; for ({a} in []);', - 'let a; for ({a = 0} in []);', - ] as const; - - it.for(falseCodes)( - '"%s", all references should be false.', - (code, { expect }) => { - const { scopeManager } = parseAndAnalyze(code); - - expect(scopeManager.scopes.length).toBeGreaterThanOrEqual(1); - - const scope = scopeManager.scopes[scopeManager.scopes.length - 1]; - const variables = getRealVariables(scope.variables); - - expect(variables).toHaveLength(1); - expect(scope.references.length).toBeGreaterThanOrEqual(1); - - scope.references.forEach(reference => { - expect(reference.identifier.name).toBe('a'); - expect(reference.isWrite()).toBe(true); - expect(reference.init).toBe(false); - }); - }, - ); - - it.for([ - 'let a; let b = a;', - 'let a; let [b] = a;', - 'let a; let [b = a] = [];', - 'let a; for (var b in a);', - 'let a; for (var [b = a] in []);', - 'let a; for (let b in a);', - 'let a; for (let [b = a] in []);', - 'let a,b; b = a;', - 'let a,b; [b] = a;', - 'let a,b; [b = a] = [];', - 'let a,b; for (b in a);', - 'let a,b; for ([b = a] in []);', - 'let a; a.foo = 0;', - 'let a,b; b = a.foo;', - ] as const)( - '"%s", readonly references of "a" should be undefined.', - (code, { expect }) => { - const { scopeManager } = parseAndAnalyze(code); - - expect(scopeManager.scopes.length).toBeGreaterThanOrEqual(1); - - const scope = scopeManager.scopes[0]; - const variables = getRealVariables(scope.variables); - - expect(variables.length).toBeGreaterThanOrEqual(1); - expect(variables[0].name).toBe('a'); - - const { references } = variables[0]; - - expect(references.length).toBeGreaterThanOrEqual(1); - - references.forEach(reference => { - expect(reference.isRead()).toBe(true); - - assert.notExists(reference.init); - }); - }, - ); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/typescript.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/typescript.test.ts deleted file mode 100644 index 2edb3e4c..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/typescript.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { AST_NODE_TYPES } from '@typescript-eslint/types'; - -import { ScopeType } from '../../src/index.js'; -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('typescript', () => { - describe('multiple call signatures', () => { - it('should create a function scope', () => { - const { scopeManager } = parseAndAnalyze( - ` - function foo(bar: number): number; - function foo(bar: string): string; - function foo(bar: string | number): string | number { - return bar; - } - `, - 'script', - ); - - expect(scopeManager.scopes).toHaveLength(4); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(scope.references).toHaveLength(0); - expect(variables).toHaveLength(1); - expect(variables[0].defs).toHaveLength(3); - - for (let i = 1; i < 4; i += 1) { - scope = scopeManager.scopes[i]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(2); - expect(variables[0].name).toBe('arguments'); - if (scope.block.type === AST_NODE_TYPES.TSDeclareFunction) { - expect(scope.references).toHaveLength(0); - } else { - expect(scope.references).toHaveLength(1); - } - } - }); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/use-strict-directive.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/use-strict-directive.test.ts deleted file mode 100644 index 8e66ab2f..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/use-strict-directive.test.ts +++ /dev/null @@ -1,191 +0,0 @@ -import { AST_NODE_TYPES } from '@typescript-eslint/types'; - -import { ScopeType } from '../../src/index.js'; -import { parseAndAnalyze } from '../test-utils/index.js'; - -describe('"use strict" directives', () => { - describe('top level', () => { - it('"use strict" at top level makes the global scope strict', () => { - const { scopeManager } = parseAndAnalyze('"use strict";', 'script'); - - expect(scopeManager.scopes).toHaveLength(1); - - const globalScope = scopeManager.scopes[0]; - assert.isScopeOfType(globalScope, ScopeType.global); - expect(globalScope.isStrict).toBe(true); - - const statement = globalScope.block.body[0]; - assert.isNodeOfType(statement, AST_NODE_TYPES.ExpressionStatement); - expect(statement.directive).toBe('use strict'); - }); - - it('"use strict" at top level makes child scopes strict', () => { - const { scopeManager } = parseAndAnalyze( - ` - "use strict"; - { - let x; - } - function f() {} - `, - 'script', - ); - - expect(scopeManager.scopes).toHaveLength(3); - - const globalScope = scopeManager.scopes[0]; - assert.isScopeOfType(globalScope, ScopeType.global); - expect(globalScope.isStrict).toBe(true); - - const blockScope = scopeManager.scopes[1]; - assert.isScopeOfType(blockScope, ScopeType.block); - expect(blockScope.isStrict).toBe(true); - - const functionScope = scopeManager.scopes[2]; - assert.isScopeOfType(functionScope, ScopeType.function); - assert.isNodeOfType( - functionScope.block, - AST_NODE_TYPES.FunctionDeclaration, - ); - expect(functionScope.isStrict).toBe(true); - }); - - it('`ExpressionStatement`s without `directive` property are not directives', () => { - const { scopeManager } = parseAndAnalyze('("use strict");', 'script'); - - expect(scopeManager.scopes).toHaveLength(1); - - const globalScope = scopeManager.scopes[0]; - assert.isScopeOfType(globalScope, ScopeType.global); - expect(globalScope.isStrict).toBe(false); - - const statement = globalScope.block.body[0]; - assert.isNodeOfType(statement, AST_NODE_TYPES.ExpressionStatement); - expect(statement.directive).toBeUndefined(); - }); - - it('other directives at top level do not make the global scope strict', () => { - const { scopeManager } = parseAndAnalyze('"use server";', 'script'); - - expect(scopeManager.scopes).toHaveLength(1); - - const globalScope = scopeManager.scopes[0]; - assert.isScopeOfType(globalScope, ScopeType.global); - expect(globalScope.isStrict).toBe(false); - }); - }); - - describe('function', () => { - it('"use strict" in a function makes that function scope strict', () => { - const { scopeManager } = parseAndAnalyze( - 'function f() { "use strict"; }', - 'script', - ); - - expect(scopeManager.scopes).toHaveLength(2); - - const globalScope = scopeManager.scopes[0]; - assert.isScopeOfType(globalScope, ScopeType.global); - expect(globalScope.isStrict).toBe(false); - - const functionScope = scopeManager.scopes[1]; - assert.isScopeOfType(functionScope, ScopeType.function); - assert.isNodeOfType( - functionScope.block, - AST_NODE_TYPES.FunctionDeclaration, - ); - expect(functionScope.isStrict).toBe(true); - - const statement = functionScope.block.body.body[0]; - assert.isNodeOfType(statement, AST_NODE_TYPES.ExpressionStatement); - expect(statement.directive).toBe('use strict'); - }); - - it('"use strict" in a function makes child scopes strict', () => { - const { scopeManager } = parseAndAnalyze( - ` - function f() { - "use strict"; - { - let x; - } - function g() {} - } - `, - 'script', - ); - - expect(scopeManager.scopes).toHaveLength(4); - - const globalScope = scopeManager.scopes[0]; - assert.isScopeOfType(globalScope, ScopeType.global); - expect(globalScope.isStrict).toBe(false); - - const outerFunctionScope = scopeManager.scopes[1]; - assert.isScopeOfType(outerFunctionScope, ScopeType.function); - assert.isNodeOfType( - outerFunctionScope.block, - AST_NODE_TYPES.FunctionDeclaration, - ); - expect(outerFunctionScope.isStrict).toBe(true); - - const blockScope = scopeManager.scopes[2]; - assert.isScopeOfType(blockScope, ScopeType.block); - expect(blockScope.isStrict).toBe(true); - - const innerFunctionScope = scopeManager.scopes[3]; - assert.isScopeOfType(innerFunctionScope, ScopeType.function); - assert.isNodeOfType( - innerFunctionScope.block, - AST_NODE_TYPES.FunctionDeclaration, - ); - expect(innerFunctionScope.isStrict).toBe(true); - }); - - it('`ExpressionStatement`s without `directive` property are not directives', () => { - const { scopeManager } = parseAndAnalyze( - 'function f() { ("use strict"); }', - 'script', - ); - - expect(scopeManager.scopes).toHaveLength(2); - - const globalScope = scopeManager.scopes[0]; - assert.isScopeOfType(globalScope, ScopeType.global); - expect(globalScope.isStrict).toBe(false); - - const functionScope = scopeManager.scopes[1]; - assert.isScopeOfType(functionScope, ScopeType.function); - assert.isNodeOfType( - functionScope.block, - AST_NODE_TYPES.FunctionDeclaration, - ); - expect(functionScope.isStrict).toBe(false); - - const statement = functionScope.block.body.body[0]; - assert.isNodeOfType(statement, AST_NODE_TYPES.ExpressionStatement); - expect(statement.directive).toBeUndefined(); - }); - - it('other directives do not make the function scope strict', () => { - const { scopeManager } = parseAndAnalyze( - 'function f() { "use server"; }', - 'script', - ); - - expect(scopeManager.scopes).toHaveLength(2); - - const globalScope = scopeManager.scopes[0]; - assert.isScopeOfType(globalScope, ScopeType.global); - expect(globalScope.isStrict).toBe(false); - - const functionScope = scopeManager.scopes[1]; - assert.isScopeOfType(functionScope, ScopeType.function); - assert.isNodeOfType( - functionScope.block, - AST_NODE_TYPES.FunctionDeclaration, - ); - expect(functionScope.isStrict).toBe(false); - }); - }); -}); diff --git a/packages/compat-eslint/test/upstream/eslint-scope/with-scope.test.ts b/packages/compat-eslint/test/upstream/eslint-scope/with-scope.test.ts deleted file mode 100644 index 454de889..00000000 --- a/packages/compat-eslint/test/upstream/eslint-scope/with-scope.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { ScopeType } from '../../src/index.js'; -import { getRealVariables, parseAndAnalyze } from '../test-utils/index.js'; - -describe('with', () => { - it('creates scope', () => { - const { scopeManager } = parseAndAnalyze( - ` - (function () { - with (obj) { - testing; - } - }()); - `, - 'script', - ); - - expect(scopeManager.scopes).toHaveLength(4); - - let scope = scopeManager.scopes[0]; - let variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.global); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[1]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.function); - expect(variables).toHaveLength(1); - expect(variables[0].name).toBe('arguments'); - expect(scope.references).toHaveLength(1); - - assert.isNull(scope.references[0].resolved); - - scope = scopeManager.scopes[2]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.with); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(0); - - scope = scopeManager.scopes[3]; - variables = getRealVariables(scope.variables); - assert.isScopeOfType(scope, ScopeType.block); - expect(variables).toHaveLength(0); - expect(scope.references).toHaveLength(1); - expect(scope.references[0].identifier.name).toBe('testing'); - - assert.isNull(scope.references[0].resolved); - }); -}); diff --git a/packages/compat-eslint/test/upstream/lib.test.ts b/packages/compat-eslint/test/upstream/lib.test.ts deleted file mode 100644 index 4b84b6dc..00000000 --- a/packages/compat-eslint/test/upstream/lib.test.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { ImplicitLibVariable } from '../src'; -import { parseAndAnalyze } from './test-utils'; - -describe('implicit lib definitions', () => { - it('should define no implicit variables if provided an empty array', () => { - const { scopeManager } = parseAndAnalyze('', { - lib: [], - }); - - const variables = scopeManager.variables; - expect(variables).toHaveLength(1); - }); - - it('should define an implicit variable if there is a value reference', () => { - const { scopeManager } = parseAndAnalyze('new ArrayBuffer();', { - lib: ['es2015'], - }); - - const variables = scopeManager.variables; - const arrayBufferVariables = variables.filter( - v => v.name === 'ArrayBuffer', - ); - expect(arrayBufferVariables).toHaveLength(1); - expect(arrayBufferVariables[0]).toBeInstanceOf(ImplicitLibVariable); - }); - - it('should define an implicit variable if there is a type reference', () => { - const { scopeManager } = parseAndAnalyze('type T = ArrayBuffer;', { - lib: ['es2015'], - }); - - const variables = scopeManager.variables; - const arrayBufferVariables = variables.filter( - v => v.name === 'ArrayBuffer', - ); - expect(arrayBufferVariables).toHaveLength(1); - expect(arrayBufferVariables[0]).toBeInstanceOf(ImplicitLibVariable); - }); - - it('should define an implicit variable if there is a nested value reference', () => { - const { scopeManager } = parseAndAnalyze( - 'var f = () => new ArrayBuffer();', - { - lib: ['es2015'], - }, - ); - - const variables = scopeManager.variables; - const arrayBufferVariables = variables.filter( - v => v.name === 'ArrayBuffer', - ); - expect(arrayBufferVariables).toHaveLength(1); - expect(arrayBufferVariables[0]).toBeInstanceOf(ImplicitLibVariable); - }); - - it('should define an implicit variable if there is a nested type reference', () => { - const { scopeManager } = parseAndAnalyze( - 'var f = (): T => undefined as T;', - { - lib: ['es2015'], - }, - ); - - const variables = scopeManager.variables; - const arrayBufferVariables = variables.filter( - v => v.name === 'ArrayBuffer', - ); - expect(arrayBufferVariables).toHaveLength(1); - expect(arrayBufferVariables[0]).toBeInstanceOf(ImplicitLibVariable); - }); - - it('should define an implicit variable if there is a value collision', () => { - const { scopeManager } = parseAndAnalyze('var Symbol = 1;', { - lib: ['es2015'], - }); - - const variables = scopeManager.variables; - const symbolVariables = variables.filter(v => v.name === 'Symbol'); - expect(symbolVariables).toHaveLength(1); - expect(symbolVariables[0]).toBeInstanceOf(ImplicitLibVariable); - }); - - it('should define an implicit variable if there is a type collision', () => { - const { scopeManager } = parseAndAnalyze('type Symbol = 1;', { - lib: ['es2015'], - }); - - const variables = scopeManager.variables; - const symbolVariables = variables.filter(v => v.name === 'Symbol'); - expect(symbolVariables).toHaveLength(1); - expect(symbolVariables[0]).toBeInstanceOf(ImplicitLibVariable); - }); - - it('should define an implicit variable if there is a nested value collision', () => { - const { scopeManager } = parseAndAnalyze('var f = (Symbol) => Symbol;', { - lib: ['es2015'], - }); - - const variables = scopeManager.variables; - const symbolVariables = variables.filter(v => v.name === 'Symbol'); - expect(symbolVariables).toHaveLength(2); - expect(symbolVariables.some(v => v instanceof ImplicitLibVariable)).toBe( - true, - ); - expect(symbolVariables.some(v => !(v instanceof ImplicitLibVariable))).toBe( - true, - ); - }); - - it('should define an implicit variable if there is a nested type collision', () => { - const { scopeManager } = parseAndAnalyze('var f = (a: Symbol) => a;', { - lib: ['es2015'], - }); - - const variables = scopeManager.variables; - const symbolVariables = variables.filter(v => v.name === 'Symbol'); - expect(symbolVariables).toHaveLength(1); - expect(symbolVariables[0]).toBeInstanceOf(ImplicitLibVariable); - }); - - it('should throw if passed an unrecognized lib name', () => { - expect(() => { - parseAndAnalyze('var f = (a: Symbol) => a;', { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - lib: ['invalid+lib' as any], - }); - }).toThrowError('invalid+lib'); - }); -}); diff --git a/packages/compat-eslint/test/upstream/populate-globals-from-lib.test.ts b/packages/compat-eslint/test/upstream/populate-globals-from-lib.test.ts deleted file mode 100644 index 7db7edce..00000000 --- a/packages/compat-eslint/test/upstream/populate-globals-from-lib.test.ts +++ /dev/null @@ -1,206 +0,0 @@ -import { describe, it, expect } from 'vitest'; - -import type { ScopeManager } from '../src'; - -import { analyze } from '../src'; -import { parse } from './test-utils'; - -// Test code that heavily relies on lib-provided globals -const CODE = ` -const arr: Array = [1, 2, 3]; -const map: Map = new Map(); -const set: Set = new Set(); -const promise: Promise = Promise.resolve(); - -function foo(): void {} - -type T1 = Partial<{ a: number }>; -type T2 = Record; -`; - -const ast = parse(CODE, { range: true }); - -function pruneSnapshot(value: unknown): unknown { - if (value == null || value === false) { - return undefined; - } - - if (Array.isArray(value)) { - const prunedItems = value - .map(item => pruneSnapshot(item)) - .filter(item => item != null); - - return prunedItems.length === 0 ? undefined : prunedItems; - } - - if (typeof value === 'object') { - const prunedEntries = Object.entries(value) - .map(([key, childValue]) => [key, pruneSnapshot(childValue)] as const) - .filter( - ([, childValue]) => - childValue != null && - (!Array.isArray(childValue) || childValue.length > 0) && - (typeof childValue !== 'object' || - Object.entries(childValue).length > 0), - ); - - return prunedEntries.length === 0 - ? undefined - : Object.fromEntries(prunedEntries); - } - - return value; -} - -/** - * Extracts a comparable snapshot from analyze() output: - * - focuses on the global scope - * - includes only the minimal information needed for comparison - * - removes ordering dependencies by sorting - */ -function extractGlobalSnapshot(scopeManager: ScopeManager) { - const globalScope = scopeManager.globalScope!; - - return pruneSnapshot({ - variables: Object.fromEntries( - [...globalScope.variables.values()] - .sort((a, b) => a.name.localeCompare(b.name)) - .map(variable => { - const varKind = - variable.isTypeVariable && variable.isValueVariable - ? 'both' - : variable.isTypeVariable - ? 'type' - : 'value'; - const defs = variable.defs.map(def => def.type); - return [ - variable.name, - defs.length === 0 ? varKind : { defs, variable: varKind }, - ]; - }), - ), - }); -} - -describe('populateGlobalsFromLib – behavior snapshot', () => { - it('es2018 globals snapshot stays stable', () => { - const result = analyze(ast, { lib: ['es2018'] }); - const snapshot = extractGlobalSnapshot(result); - - expect(snapshot).toMatchSnapshot(); - }); - - it('esnext.full globals snapshot stays stable', () => { - const result = analyze(ast, { lib: ['esnext.full'] }); - const snapshot = extractGlobalSnapshot(result); - - expect(snapshot).toMatchSnapshot(); - }); - - it('no-lib baseline snapshot stays stable', () => { - const result = analyze(ast, { lib: [] }); - const snapshot = extractGlobalSnapshot(result); - - expect(snapshot).toMatchSnapshot(); - }); -}); - -describe('populateGlobalsFromLib – error handling', () => { - it('throws error for invalid lib name', () => { - expect(() => { - analyze(ast, { lib: ['invalid-lib-name' as never] }); - }).toThrowError('Invalid value for lib provided: invalid-lib-name'); - }); -}); - -describe('populateGlobalsFromLib – lib dependency flattening', () => { - it('es2018 includes all transitive dependencies from es2017, es2016, etc.', () => { - const es2018Result = analyze(ast, { lib: ['es2018'] }); - const es5Result = analyze(ast, { lib: ['es5'] }); - - const es2018Vars = new Set( - [...es2018Result.globalScope!.variables.values()].map(v => v.name), - ); - const es5Vars = new Set( - [...es5Result.globalScope!.variables.values()].map(v => v.name), - ); - - for (const varName of es5Vars) { - expect(es2018Vars.has(varName)).toBe(true); - } - - expect(es2018Vars.size).toBeGreaterThan(es5Vars.size); - }); - - it('multiple libs are merged correctly', () => { - const combinedResult = analyze(ast, { - lib: ['es5', 'es2015.promise', 'es2015.symbol'], - }); - const es5OnlyResult = analyze(ast, { lib: ['es5'] }); - - const combinedVars = new Set( - [...combinedResult.globalScope!.variables.values()].map(v => v.name), - ); - const es5Vars = new Set( - [...es5OnlyResult.globalScope!.variables.values()].map(v => v.name), - ); - - for (const varName of es5Vars) { - expect(combinedVars.has(varName)).toBe(true); - } - expect(combinedVars.has('Promise')).toBe(true); - expect(combinedVars.has('Symbol')).toBe(true); - }); -}); - -describe('populateGlobalsFromLib – deduplication', () => { - it('duplicate libs in array produce same result as single lib', () => { - const singleResult = analyze(ast, { lib: ['es2018'] }); - const duplicateResult = analyze(ast, { - lib: ['es2018', 'es2018', 'es2018'], - }); - - const singleSnapshot = extractGlobalSnapshot(singleResult); - const duplicateSnapshot = extractGlobalSnapshot(duplicateResult); - - expect(duplicateSnapshot).toEqual(singleSnapshot); - }); - - it('overlapping libs are deduplicated correctly', () => { - const es2018Only = analyze(ast, { lib: ['es2018'] }); - const es2018WithEs2017 = analyze(ast, { lib: ['es2018', 'es2017'] }); - - const es2018OnlySnapshot = extractGlobalSnapshot(es2018Only); - const es2018WithEs2017Snapshot = extractGlobalSnapshot(es2018WithEs2017); - - expect(es2018WithEs2017Snapshot).toEqual(es2018OnlySnapshot); - }); -}); - -describe('populateGlobalsFromLib – const assertion global', () => { - it('const type variable is always registered for const assertions', () => { - const noLibResult = analyze(ast, { lib: [] }); - const globalScope = noLibResult.globalScope!; - - const constVar = [...globalScope.variables.values()].find( - v => v.name === 'const', - ); - - expect(constVar).toBeDefined(); - expect(constVar?.isTypeVariable).toBe(true); - expect(constVar?.isValueVariable).toBe(false); - }); - - it('const type variable exists alongside other lib globals', () => { - const result = analyze(ast, { lib: ['es2018'] }); - const globalScope = result.globalScope!; - - const constVar = [...globalScope.variables.values()].find( - v => v.name === 'const', - ); - - expect(constVar).toBeDefined(); - expect(constVar?.isTypeVariable).toBe(true); - expect(constVar?.isValueVariable).toBe(false); - }); -}); diff --git a/packages/compat-eslint/test/upstream/types/reference-type.test.ts b/packages/compat-eslint/test/upstream/types/reference-type.test.ts deleted file mode 100644 index 79a6f317..00000000 --- a/packages/compat-eslint/test/upstream/types/reference-type.test.ts +++ /dev/null @@ -1,391 +0,0 @@ -import type { TSESTree } from '@typescript-eslint/types'; - -import { AST_NODE_TYPES } from '@typescript-eslint/types'; -import { simpleTraverse } from '@typescript-eslint/typescript-estree'; - -import { parseAndAnalyze } from '../test-utils/index.js'; - -describe('referencing a type - positive', () => { - it('records a reference when a type is referenced from a type', () => { - const { ast, scopeManager } = parseAndAnalyze(` - type TypeDecl = string; - type OtherType = TypeDecl; - `); - - const nodes: TSESTree.Node[] = []; - - simpleTraverse( - ast, - { - visitors: { - [AST_NODE_TYPES.TSTypeAliasDeclaration](node) { - nodes.push(node); - }, - }, - }, - true, - ); - - const node = nodes[0]; - - const referencingNode = nodes[1]; - - assert.isNodeOfType(node, AST_NODE_TYPES.TSTypeAliasDeclaration); - - assert.isNodeOfType(referencingNode, AST_NODE_TYPES.TSTypeAliasDeclaration); - - expect(node.id.name).toBe('TypeDecl'); - - expect(referencingNode.id.name).toBe('OtherType'); - - const variable = scopeManager.getDeclaredVariables(node)[0]; - - expect(variable.references).toHaveLength(1); - - expect(variable.references[0].identifier.parent.parent).toBe( - referencingNode, - ); - }); - - it('records a reference when a dual value-type is referenced from a type', () => { - const { ast, scopeManager } = parseAndAnalyze(` - class Class {} - type Type = Class; - `); - - const nodes: TSESTree.Node[] = []; - - simpleTraverse( - ast, - { - visitors: { - [AST_NODE_TYPES.ClassDeclaration](node) { - nodes.push(node); - }, - [AST_NODE_TYPES.TSTypeAliasDeclaration](node) { - nodes.push(node); - }, - }, - }, - true, - ); - - const node = nodes[0]; - - const referencingNode = nodes[1]; - - assert.isNodeOfType(node, AST_NODE_TYPES.ClassDeclaration); - - assert.isNodeOfType(referencingNode, AST_NODE_TYPES.TSTypeAliasDeclaration); - - const variable = scopeManager.getDeclaredVariables(node)[0]; - - expect(variable.references).toHaveLength(1); - - expect(variable.references[0].identifier.parent.parent).toBe( - referencingNode, - ); - }); - - it('records a reference when a generic type parameter is referenced from its type', () => { - const { ast, scopeManager } = parseAndAnalyze(` - type TypeDecl = TypeParam; - `); - - const nodes: TSESTree.Node[] = []; - - simpleTraverse( - ast, - { - visitors: { - [AST_NODE_TYPES.TSTypeParameter](node) { - nodes.push(node); - }, - [AST_NODE_TYPES.TSTypeReference](node) { - nodes.push(node); - }, - }, - }, - true, - ); - - const node = nodes[0]; - - const referencingNode = nodes[1]; - - assert.isNodeOfType(node, AST_NODE_TYPES.TSTypeParameter); - - assert.isNodeOfType(referencingNode, AST_NODE_TYPES.TSTypeReference); - - const variable = scopeManager.getDeclaredVariables(node)[0]; - - expect(variable.references).toHaveLength(1); - - expect(variable.references[0].identifier.parent).toBe(referencingNode); - }); - - it('records references when a name has both a type and a variable definition', () => { - const { ast, scopeManager } = parseAndAnalyze(` - const dual = 1; - type dual = number; - - type reference1 = dual; - const reference2 = dual; - `); - - const nodes: TSESTree.Node[] = []; - - simpleTraverse( - ast, - { - visitors: { - [AST_NODE_TYPES.TSTypeAliasDeclaration](node) { - assert.isNodeOfType(node, AST_NODE_TYPES.TSTypeAliasDeclaration); - - if (node.id.name === 'reference1') { - nodes.push(node); - } - }, - [AST_NODE_TYPES.VariableDeclarator](node) { - assert.isNodeOfType(node, AST_NODE_TYPES.VariableDeclarator); - - assert.isNodeOfType(node.id, AST_NODE_TYPES.Identifier); - - if (node.id.name === 'dual' || node.id.name === 'reference2') { - nodes.push(node); - } - }, - }, - }, - true, - ); - - const node = nodes[0]; - - const referencingTypeNode = nodes[1]; - - const referencingVariableNode = nodes[2]; - - assert.isNodeOfType(node, AST_NODE_TYPES.VariableDeclarator); - - assert.isNodeOfType( - referencingTypeNode, - AST_NODE_TYPES.TSTypeAliasDeclaration, - ); - - assert.isNodeOfType( - referencingVariableNode, - AST_NODE_TYPES.VariableDeclarator, - ); - - assert.isNodeOfType(node.id, AST_NODE_TYPES.Identifier); - - assert.isNodeOfType(referencingVariableNode.id, AST_NODE_TYPES.Identifier); - - expect(node.id.name).toBe('dual'); - - expect(referencingTypeNode.id.name).toBe('reference1'); - - expect(referencingVariableNode.id.name).toBe('reference2'); - - const variable = scopeManager.getDeclaredVariables(node)[0]; - - // it should merge the defs into a single variable - expect(variable.defs).toHaveLength(2); - - expect(variable.references).toHaveLength(3); - - // first ref is the definition of the variable - expect(variable.references[0].identifier.parent).toBe(node); - // second ref is the type reference - expect(variable.references[1].identifier.parent.parent).toBe( - referencingTypeNode, - ); - // third ref is the variable reference - expect(variable.references[2].identifier.parent).toBe( - referencingVariableNode, - ); - }); - - it('records value and type references to imported variables', () => { - const { ast, scopeManager } = parseAndAnalyze( - ` - import { foo } from 'module'; - const test = foo; - type Test = foo; - `, - 'module', - ); - - const nodes: TSESTree.Node[] = []; - - simpleTraverse( - ast, - { - visitors: { - [AST_NODE_TYPES.ImportSpecifier](node) { - nodes.push(node); - }, - [AST_NODE_TYPES.TSTypeReference](node) { - nodes.push(node); - }, - [AST_NODE_TYPES.VariableDeclarator](node) { - nodes.push(node); - }, - }, - }, - true, - ); - - const node = nodes[0]; - - const variableDecl = nodes[1]; - - const typeRef = nodes[2]; - - assert.isNodeOfType(node, AST_NODE_TYPES.ImportSpecifier); - - assert.isNodeOfType(variableDecl, AST_NODE_TYPES.VariableDeclarator); - - assert.isNodeOfType(typeRef, AST_NODE_TYPES.TSTypeReference); - - const variable = scopeManager.getDeclaredVariables(node)[0]; - expect(variable.references).toHaveLength(2); - // first reference is from the variable - expect(variable.references[0].identifier.parent).toBe(variableDecl); - // second reference is from the type - expect(variable.references[1].identifier.parent).toBe(typeRef); - }); - - it('records a reference when a value is referenced from a typeof predicate', () => { - const { ast, scopeManager } = parseAndAnalyze(` - const value = 1; - type Type = typeof value; - `); - - const nodes: TSESTree.Node[] = []; - - simpleTraverse( - ast, - { - visitors: { - [AST_NODE_TYPES.TSTypeAliasDeclaration](node) { - nodes.push(node); - }, - [AST_NODE_TYPES.VariableDeclarator](node) { - nodes.push(node); - }, - }, - }, - true, - ); - - const node = nodes[0]; - - const referencingNode = nodes[1]; - - assert.isNodeOfType(node, AST_NODE_TYPES.VariableDeclarator); - - assert.isNodeOfType(referencingNode, AST_NODE_TYPES.TSTypeAliasDeclaration); - - const variable = scopeManager.getDeclaredVariables(node)[0]; - - expect(variable.references).toHaveLength(2); - // first ref is the definition of the variable - expect(variable.references[0].identifier.parent).toBe(node); - // second ref is the predicate reference - expect(variable.references[1].identifier.parent.parent).toBe( - referencingNode, - ); - }); -}); - -describe('referencing a type - negative', () => { - it('does not record a reference when a value is referenced from a type without a typeof predicate', () => { - const { ast, scopeManager } = parseAndAnalyze(` - const value = 1; - type Type = value; - `); - - const nodes: TSESTree.Node[] = []; - - simpleTraverse( - ast, - { - visitors: { - [AST_NODE_TYPES.VariableDeclarator](node) { - nodes.push(node); - }, - }, - }, - true, - ); - - const node = nodes[0]; - - assert.isNodeOfType(node, AST_NODE_TYPES.VariableDeclarator); - - const variable = scopeManager.getDeclaredVariables(node)[0]; - - // variables declare a reference to themselves if they have an initialization - // so there should be one reference from the declaration itself - expect(variable.references).toHaveLength(1); - expect(variable.references[0].identifier.parent).toBe(node); - }); - - it('does not record a reference when a type is referenced from a value', () => { - const { ast, scopeManager } = parseAndAnalyze(` - type Type = 1; - const value = Type; - `); - - const nodes: TSESTree.Node[] = []; - - simpleTraverse( - ast, - { - visitors: { - [AST_NODE_TYPES.TSTypeAliasDeclaration](node) { - nodes.push(node); - }, - }, - }, - true, - ); - - const node = nodes[0]; - - assert.isNodeOfType(node, AST_NODE_TYPES.TSTypeAliasDeclaration); - - const variable = scopeManager.getDeclaredVariables(node)[0]; - expect(variable.references).toHaveLength(0); - }); - - it('does not record a reference when a type is referenced from outside its declaring type', () => { - const { ast, scopeManager } = parseAndAnalyze(` - type TypeDecl = T; - type Other = TypeParam; - `); - - const nodes: TSESTree.Node[] = []; - - simpleTraverse( - ast, - { - visitors: { - [AST_NODE_TYPES.TSTypeParameter](node) { - nodes.push(node); - }, - }, - }, - true, - ); - - const node = nodes[0]; - - assert.isNodeOfType(node, AST_NODE_TYPES.TSTypeParameter); - - const variable = scopeManager.getDeclaredVariables(node)[0]; - expect(variable.references).toHaveLength(0); - }); -}); diff --git a/packages/compat-eslint/test/upstream/types/variable-definition.test.ts b/packages/compat-eslint/test/upstream/types/variable-definition.test.ts deleted file mode 100644 index 68d1a38b..00000000 --- a/packages/compat-eslint/test/upstream/types/variable-definition.test.ts +++ /dev/null @@ -1,185 +0,0 @@ -import type { TSESTree } from '@typescript-eslint/types'; - -import { AST_NODE_TYPES } from '@typescript-eslint/types'; -import { simpleTraverse } from '@typescript-eslint/typescript-estree'; - -import { parseAndAnalyze } from '../test-utils/index.js'; - -describe('variable definition', () => { - it('defines a variable for a type declaration', () => { - const { ast, scopeManager } = parseAndAnalyze(` - type TypeDecl = string; - `); - - const nodes: TSESTree.Node[] = []; - - simpleTraverse( - ast, - { - visitors: { - [AST_NODE_TYPES.TSTypeAliasDeclaration](node) { - nodes.push(node); - }, - }, - }, - true, - ); - - const node = nodes[0]; - - assert.isNodeOfType(node, AST_NODE_TYPES.TSTypeAliasDeclaration); - - expect(scopeManager.getDeclaredVariables(node)).toMatchInlineSnapshot(` - [ - Variable$2 { - defs: [ - TypeDefinition$1 { - name: Identifier<"TypeDecl">, - node: TSTypeAliasDeclaration$1, - }, - ], - name: "TypeDecl", - references: [], - isValueVariable: false, - isTypeVariable: true, - }, - ] - `); - }); - - it('defines a variable for an interface definition', () => { - const { ast, scopeManager } = parseAndAnalyze(` - interface InterfaceDecl { - prop: string; - } - `); - - const nodes: TSESTree.Node[] = []; - - simpleTraverse( - ast, - { - visitors: { - [AST_NODE_TYPES.TSInterfaceDeclaration](node) { - nodes.push(node); - }, - }, - }, - true, - ); - - const node = nodes[0]; - - assert.isNodeOfType(node, AST_NODE_TYPES.TSInterfaceDeclaration); - - expect(scopeManager.getDeclaredVariables(node)).toMatchInlineSnapshot(` - [ - Variable$2 { - defs: [ - TypeDefinition$1 { - name: Identifier<"InterfaceDecl">, - node: TSInterfaceDeclaration$1, - }, - ], - name: "InterfaceDecl", - references: [], - isValueVariable: false, - isTypeVariable: true, - }, - ] - `); - }); - - it('defines a variable for a generic type', () => { - const { ast, scopeManager } = parseAndAnalyze(` - type TypeDecl = string; - `); - - const nodes: TSESTree.Node[] = []; - - simpleTraverse( - ast, - { - visitors: { - [AST_NODE_TYPES.TSTypeParameter](node) { - nodes.push(node); - }, - }, - }, - true, - ); - - const node = nodes[0]; - - assert.isNodeOfType(node, AST_NODE_TYPES.TSTypeParameter); - - expect(scopeManager.getDeclaredVariables(node)).toMatchInlineSnapshot(` - [ - Variable$3 { - defs: [ - TypeDefinition$2 { - name: Identifier<"TypeParam">, - node: TSTypeParameter$1, - }, - ], - name: "TypeParam", - references: [], - isValueVariable: false, - isTypeVariable: true, - }, - ] - `); - }); - - it('defines a variable for an inferred generic type', () => { - const { ast, scopeManager } = parseAndAnalyze(` - type TypeDecl = TypeParam extends Foo ? Inferred : never; - `); - - const nodes: TSESTree.Node[] = []; - - simpleTraverse( - ast, - { - visitors: { - [AST_NODE_TYPES.TSInferType](node) { - assert.isNodeOfType(node, AST_NODE_TYPES.TSInferType); - - nodes.push(node.typeParameter); - }, - }, - }, - true, - ); - - const node = nodes[0]; - - assert.isNodeOfType(node, AST_NODE_TYPES.TSTypeParameter); - - expect(scopeManager.getDeclaredVariables(node)).toMatchInlineSnapshot(` - [ - Variable$4 { - defs: [ - TypeDefinition$3 { - name: Identifier<"Inferred">, - node: TSTypeParameter$1, - }, - ], - name: "Inferred", - references: [ - Reference$3 { - identifier: Identifier<"Inferred">, - isRead: true, - isTypeReference: true, - isValueReference: false, - isWrite: false, - resolved: Variable$4, - }, - ], - isValueVariable: false, - isTypeVariable: true, - }, - ] - `); - }); -}); From bff83775265f19c5af1820e5cca1f82bd1f35033 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 22 Oct 2026 18:12:15 +0800 Subject: [PATCH 09/52] style: dprint format --- .../{fixture.vue => fixture.mts} | 0 .../compat-eslint/lib/lazy-source-code.ts | 2 +- packages/compat-eslint/lib/ts-ast-scan.ts | 2 +- .../compat-eslint/test/bench/rules.config.ts | 1 - packages/compat-eslint/test/bench/run.ts | 2 +- .../test/compat-pipeline.test.ts | 2 +- ...predicate-materializer-consistency.test.ts | 2 +- packages/config/lib/plugins/diagnostics.ts | 29 +++++++++++-------- packages/core/test/project.ts | 2 +- packages/core/test/skip-rules.test.ts | 10 +++++-- 10 files changed, 31 insertions(+), 21 deletions(-) rename fixtures/multiple-configs/{fixture.vue => fixture.mts} (100%) diff --git a/fixtures/multiple-configs/fixture.vue b/fixtures/multiple-configs/fixture.mts similarity index 100% rename from fixtures/multiple-configs/fixture.vue rename to fixtures/multiple-configs/fixture.mts diff --git a/packages/compat-eslint/lib/lazy-source-code.ts b/packages/compat-eslint/lib/lazy-source-code.ts index a8ee6229..466b3511 100644 --- a/packages/compat-eslint/lib/lazy-source-code.ts +++ b/packages/compat-eslint/lib/lazy-source-code.ts @@ -25,9 +25,9 @@ // method that's missing here, throw a clear error rather than silently // returning undefined. -import * as ts from './ts'; import { materialize } from './lazy-estree'; import { buildCommentObject, walkInnerCommentsOf } from './tokens'; +import * as ts from './ts'; const SK = ts.SyntaxKind; diff --git a/packages/compat-eslint/lib/ts-ast-scan.ts b/packages/compat-eslint/lib/ts-ast-scan.ts index b14c9057..d1192f88 100644 --- a/packages/compat-eslint/lib/ts-ast-scan.ts +++ b/packages/compat-eslint/lib/ts-ast-scan.ts @@ -17,7 +17,6 @@ // already lazy-builds the parent chain when a rule reads `.parent`, so we // don't pre-build ancestors either. -import * as ts from './ts'; import { type ConvertContext, GENERIC_TS_NODE_MARKER, @@ -26,6 +25,7 @@ import { NoESTreeCounterpartError, } from './lazy-estree'; import { UnsupportedSelectorError } from './selector-analysis'; +import * as ts from './ts'; const SK = ts.SyntaxKind; // `import * as ts` lowers to a namespace object guarded by a getter on diff --git a/packages/compat-eslint/test/bench/rules.config.ts b/packages/compat-eslint/test/bench/rules.config.ts index a7d4d6e5..6b2302a3 100644 --- a/packages/compat-eslint/test/bench/rules.config.ts +++ b/packages/compat-eslint/test/bench/rules.config.ts @@ -133,5 +133,4 @@ export const RULES: Array<[string, unknown[]?]> = [ ['no-multi-assign'], ['id-denylist', ['callback']], ['consistent-this'], - ]; diff --git a/packages/compat-eslint/test/bench/run.ts b/packages/compat-eslint/test/bench/run.ts index d9c9d9c1..43d57e8b 100644 --- a/packages/compat-eslint/test/bench/run.ts +++ b/packages/compat-eslint/test/bench/run.ts @@ -18,9 +18,9 @@ // diffs (ideally empty). New unexpected diffs fail; absent // expected diffs also fail (the bug got fixed, baseline is stale). +import * as oracle from '@tsslint/estree-oracle'; import * as fs from 'fs'; import * as path from 'path'; -import * as oracle from '@tsslint/estree-oracle'; import type { SourceFile } from 'typescript/ast'; import type { Checker, Program } from 'typescript/sync'; import { createProjectForFiles } from '../ts7'; diff --git a/packages/compat-eslint/test/compat-pipeline.test.ts b/packages/compat-eslint/test/compat-pipeline.test.ts index b28a1d0d..909be900 100644 --- a/packages/compat-eslint/test/compat-pipeline.test.ts +++ b/packages/compat-eslint/test/compat-pipeline.test.ts @@ -13,8 +13,8 @@ import type * as ESLint from 'eslint'; import type { SourceFile } from 'typescript/ast'; -import { type Checker, type Program } from 'typescript/sync'; import { createIdentifier } from 'typescript/ast/factory'; +import { type Checker, type Program } from 'typescript/sync'; import { createProject } from './ts7'; const compat = require('../index.js') as typeof import('../index.js'); diff --git a/packages/compat-eslint/test/predicate-materializer-consistency.test.ts b/packages/compat-eslint/test/predicate-materializer-consistency.test.ts index eb9b3e89..c3f139a5 100644 --- a/packages/compat-eslint/test/predicate-materializer-consistency.test.ts +++ b/packages/compat-eslint/test/predicate-materializer-consistency.test.ts @@ -28,9 +28,9 @@ // // Run: tsc --build && node packages/compat-eslint/test/predicate-materializer-consistency.test.js +import * as oracle from '@tsslint/estree-oracle'; import * as fs from 'fs'; import * as path from 'path'; -import * as oracle from '@tsslint/estree-oracle'; import * as ts from '../lib/ts'; import { parse } from './ts7'; diff --git a/packages/config/lib/plugins/diagnostics.ts b/packages/config/lib/plugins/diagnostics.ts index 011004f5..8930eeae 100644 --- a/packages/config/lib/plugins/diagnostics.ts +++ b/packages/config/lib/plugins/diagnostics.ts @@ -16,17 +16,19 @@ export function create(mode: CheckMode | CheckMode[] = 'semantic'): Plugin { ? program.getDeclarationDiagnostics(file.fileName) : []; for (const diag of diags) { - diagnostics.push({ - category: diag.category, - code: diag.code, - messageText: flattenMessage(diag), - file, - start: diag.pos, - length: diag.end - diag.pos, - source: diag.source || 'ts', - reportsUnnecessary: diag.reportsUnnecessary, - reportsDeprecated: diag.reportsDeprecated, - } satisfies Diagnostic); + diagnostics.push( + { + category: diag.category, + code: diag.code, + messageText: flattenMessage(diag), + file, + start: diag.pos, + length: diag.end - diag.pos, + source: diag.source || 'ts', + reportsUnnecessary: diag.reportsUnnecessary, + reportsDeprecated: diag.reportsDeprecated, + } satisfies Diagnostic, + ); } } return diagnostics; @@ -34,7 +36,10 @@ export function create(mode: CheckMode | CheckMode[] = 'semantic'): Plugin { }); } -function flattenMessage(diag: { text: string; messageChain?: readonly { text: string; messageChain?: any }[] }, indent = 0): string { +function flattenMessage( + diag: { text: string; messageChain?: readonly { text: string; messageChain?: any }[] }, + indent = 0, +): string { let text = (indent ? '\n' + ' '.repeat(indent) : '') + diag.text; for (const child of diag.messageChain ?? []) { text += flattenMessage(child, indent + 1); diff --git a/packages/core/test/project.ts b/packages/core/test/project.ts index f46f7e00..d8143984 100644 --- a/packages/core/test/project.ts +++ b/packages/core/test/project.ts @@ -1,12 +1,12 @@ // A real on-disk TypeScript 7 project for core tests. One API process // per test run; temp dirs are removed when the process exits. -import type { LintTarget } from '../index.js'; import type { LinterContext } from '@tsslint/types'; import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; import * as ts from 'typescript/ast'; import { API } from 'typescript/sync'; +import type { LintTarget } from '../index.js'; let api: API | undefined; const tempDirs: string[] = []; diff --git a/packages/core/test/skip-rules.test.ts b/packages/core/test/skip-rules.test.ts index 6e840cc5..ad03bac7 100644 --- a/packages/core/test/skip-rules.test.ts +++ b/packages/core/test/skip-rules.test.ts @@ -118,8 +118,14 @@ function check(name: string, cond: boolean, detail?: string) { const out = linter.lint(project.target('a.ts')); const fixable = out.find(d => String(d.code) === 'fixable')!; const plain = out.find(d => String(d.code) === 'plain')!; - check('hasFixForDiagnostic true for fixable diag', linter.hasFixForDiagnostic(project.target('a.ts').file.fileName, fixable) === true); - check('hasFixForDiagnostic false for plain diag', linter.hasFixForDiagnostic(project.target('a.ts').file.fileName, plain) === false); + check( + 'hasFixForDiagnostic true for fixable diag', + linter.hasFixForDiagnostic(project.target('a.ts').file.fileName, fixable) === true, + ); + check( + 'hasFixForDiagnostic false for plain diag', + linter.hasFixForDiagnostic(project.target('a.ts').file.fileName, plain) === false, + ); } // ── Done ──────────────────────────────────────────────────────────────── From 590202e87dfb895ddf7b0884db80b5c70fda2cbd Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 22 Oct 2026 18:12:15 +0800 Subject: [PATCH 10/52] feat(cli)!: run on the TypeScript 7 sync API with content mapper support - lint through typescript/sync snapshots; project file names come from the configured project so content-mapped files (e.g. .vue) are included - replace the BuilderProgram incremental state with a program fingerprint (cache format v3) - map diagnostics back to original files via SourceFile.spanMap; drop diagnostics that only cover generated code - apply --fix only when every edit of a fix maps exactly - render with the TS7 formatDiagnosticsWithColorAndContext --- packages/cli/index.ts | 163 ++++---- packages/cli/lib/cache-flow.ts | 35 +- packages/cli/lib/cache.ts | 35 +- packages/cli/lib/content-mapper.ts | 49 +++ packages/cli/lib/fingerprint.ts | 21 ++ packages/cli/lib/format.ts | 110 ++++++ packages/cli/lib/incremental-state.ts | 183 --------- packages/cli/lib/worker.ts | 389 ++++++-------------- packages/cli/test/cache-flow.test.ts | 241 +++++------- packages/cli/test/cache.test.ts | 48 ++- packages/cli/test/incremental-state.test.ts | 353 ------------------ packages/cli/test/integration.test.ts | 157 +++++++- packages/cli/test/project.ts | 53 +++ 13 files changed, 715 insertions(+), 1122 deletions(-) create mode 100644 packages/cli/lib/content-mapper.ts create mode 100644 packages/cli/lib/fingerprint.ts create mode 100644 packages/cli/lib/format.ts delete mode 100644 packages/cli/lib/incremental-state.ts delete mode 100644 packages/cli/test/incremental-state.test.ts create mode 100644 packages/cli/test/project.ts diff --git a/packages/cli/index.ts b/packages/cli/index.ts index 954c96d4..1a018301 100644 --- a/packages/cli/index.ts +++ b/packages/cli/index.ts @@ -9,7 +9,8 @@ if (process.argv.includes('--debug-estree')) { process.env.TSSLINT_DEBUG_ESTREE = '1'; } -import ts = require('typescript-native-bridge'); +import { version as tsVersion } from 'typescript'; +import { DiagnosticCategory } from 'typescript/sync'; import path = require('path'); import worker = require('./lib/worker.js'); import cache = require('./lib/cache.js'); @@ -17,6 +18,7 @@ import fs = require('fs'); import minimatch = require('minimatch'); import colors = require('./lib/colors.js'); import render = require('./lib/render.js'); +import format = require('./lib/format.js'); const HELP = ` Usage: tsslint [options] @@ -55,16 +57,15 @@ const PROJECT_FLAGS = [ ] as const; class Project { - worker: ReturnType | undefined; + started = false; /** - * file names for passing to ts language service host (getScriptFileNames) + * all file names of the tsconfig, including content-mapped files */ - rawFileNames: string[] = []; + rawFileNames: readonly string[] = []; /** * file names after filter, for linting process */ - fileNames: string[] = []; - options: ts.CompilerOptions = {}; + fileNames: readonly string[] = []; configFile: string | undefined; currentFileIndex = 0; cacheData: cache.CacheData = cache.emptyCache(); @@ -72,11 +73,11 @@ class Project { constructor( public tsconfig: string, - ) {} - - async init(renderer: render.Renderer, filesFilter: string[]) { - this.configFile = ts.findConfigFile(path.dirname(this.tsconfig), ts.sys.fileExists, 'tsslint.config.ts'); + ) { + this.configFile = findConfigFile(path.dirname(tsconfig)); + } + init(renderer: render.Renderer, filesFilter: string[]) { const label = colors.tsColor('TS'); const relPath = path.relative(process.cwd(), this.tsconfig); @@ -87,10 +88,7 @@ class Project { return this; } - const commonLine = await parseCommonLine(this.tsconfig); - - this.rawFileNames = commonLine.fileNames; - this.options = commonLine.options; + this.rawFileNames = worker.getProjectFileNames(this.tsconfig); if (!this.rawFileNames.length) { renderer.info(`${label} ${colors.gray(relPath)} ${colors.gray('(0)')}`); @@ -125,32 +123,29 @@ class Project { // mtime+size — anything that changes the rule set or the toolchain // mints a fresh file. See packages/cli/lib/cache.ts. if (!process.argv.includes('--force')) { - this.cacheData = cache.loadCache( - this.tsconfig, - this.configFile, - ts.version, - ts.sys.createHash, - ); + this.cacheData = cache.loadCache(this.tsconfig, this.configFile, tsVersion); } return this; } } -const formatHost: ts.FormatDiagnosticsHost = { - getCurrentDirectory: ts.sys.getCurrentDirectory, - getCanonicalFileName: ts.sys.useCaseSensitiveFileNames ? x => x : x => x.toLowerCase(), - getNewLine: () => ts.sys.newLine, -}; - (async () => { const renderer = render.createRenderer(); const tsconfigs = new Set(); + function exit(code: number): never { + renderer.dispose(); + try { + worker.close(); + } + catch {} + process.exit(code); + } + function fail(msg: string): never { renderer.error(colors.red(msg)); - renderer.dispose(); - process.exit(1); + exit(1); } let projects: Project[] = []; @@ -202,7 +197,7 @@ const formatHost: ts.FormatDiagnosticsHost = { if (fs.statSync(filterPath, { throwIfNoEntry: false })?.isDirectory()) { filterPath = path.join(filterPath, '**/*'); } - return ts.server.toNormalizedPath(filterPath); + return filterPath.replace(/\\/g, '/'); } const filters: string[] = []; @@ -222,8 +217,13 @@ const formatHost: ts.FormatDiagnosticsHost = { } } - for (const tsconfig of tsconfigs) { - projects.push(await new Project(tsconfig).init(renderer, filters)); + projects = [...tsconfigs].map(tsconfig => new Project(tsconfig)); + const configured = projects.filter(project => project.configFile).map(project => project.tsconfig); + if (configured.length) { + worker.openProjects(configured); + } + for (const project of projects) { + project.init(renderer, filters); } projects = projects.filter(project => project.configFile && project.fileNames.length); @@ -234,11 +234,10 @@ const formatHost: ts.FormatDiagnosticsHost = { if (allFilesNum === 0) { renderer.info(colors.yellow('No input files.')); - renderer.dispose(); - process.exit(1); + exit(1); } - await startWorker(worker.create()); + await startWorker(); const summaryLines: string[] = []; @@ -386,38 +385,35 @@ const formatHost: ts.FormatDiagnosticsHost = { } } - renderer.dispose(); - - process.exit((errors || messages || configErrors) ? 1 : 0); + exit((errors || messages || configErrors) ? 1 : 0); - async function startWorker(linterWorker: ReturnType) { + async function startWorker() { const unfinishedProjects = projects.filter(project => project.currentFileIndex < project.fileNames.length); if (!unfinishedProjects.length) { return; } - const project = unfinishedProjects.find(project => !project.worker); + const project = unfinishedProjects.find(project => !project.started); if (!project) { return; } - project.worker = linterWorker; + project.started = true; if (project.pendingHeader) { renderer.info(project.pendingHeader); project.pendingHeader = undefined; } - const setupResult = await linterWorker.setup( + const setupResult = await worker.setup( project.configFile!, - project.rawFileNames, - project.options, + project.tsconfig, Object.keys(project.cacheData.ruleModes), - project.cacheData.incrementalState, + project.cacheData.programFingerprint, ); if (setupResult !== true) { renderer.diagnostic(formatConfigError(project.configFile!, setupResult)); configErrors++; projects = projects.filter(p => p !== project); - await startWorker(linterWorker); + await startWorker(); return; } @@ -437,14 +433,14 @@ const formatHost: ts.FormatDiagnosticsHost = { else if (fileCache.mtime === fileStat.mtimeMs && Object.keys(fileCache.rules).length) { // File text untouched since the prev session AND we have at // least one rule's diagnostics cached for it — treat as a - // warm hit for the `--force` summary hint. Layer 2's BP - // might still re-run type-aware rules if their deps moved, + // warm hit for the `--force` summary hint. Layer 2 might + // still re-run type-aware rules if the program moved, // but the user-visible signal here is just "cache had // something for this file." cached++; } - const diagnostics = await linterWorker.lint( + const diagnostics = worker.lint( fileName, process.argv.includes('--fix'), fileCache, @@ -452,7 +448,7 @@ const formatHost: ts.FormatDiagnosticsHost = { ); if (diagnostics.length) { - hasFix ||= await linterWorker.hasCodeFixes(fileName); + hasFix ||= worker.hasCodeFixes(fileName); for (const diagnostic of diagnostics) { // Cache-hit diagnostics come back without their rule @@ -461,37 +457,22 @@ const formatHost: ts.FormatDiagnosticsHost = { // per-rule `hasFix` flag for the diagnostic's rule. hasFix ||= !!fileCache.rules[String(diagnostic.code)]?.hasFix; - let output: string; - - if (diagnostic.category === ts.DiagnosticCategory.Suggestion) { - output = ts.formatDiagnosticsWithColorAndContext([{ - ...diagnostic, - category: ts.DiagnosticCategory.Message, - }], formatHost); - output = output.replace(/\[94mmessage/, '[90msuggestion'); - output = output.replace(/\[94m/g, '[90m'); - } - else { - output = ts.formatDiagnosticsWithColorAndContext([diagnostic], formatHost); + const output = format.formatDiagnostic(diagnostic, worker.getApi()); + if (output === undefined) { + continue; } - output = output.trimEnd(); - - if (typeof diagnostic.code === 'string') { - output = output.replace(`TS${diagnostic.code}`, diagnostic.code); - } - - if (diagnostic.category === ts.DiagnosticCategory.Error) { + if (diagnostic.category === DiagnosticCategory.Error) { errors++; renderer.diagnostic(output); } - else if (diagnostic.category === ts.DiagnosticCategory.Warning) { + else if (diagnostic.category === DiagnosticCategory.Warning) { warnings++; if (!failuresOnly) { renderer.diagnostic(output); } } - else if (diagnostic.category === ts.DiagnosticCategory.Message) { + else if (diagnostic.category === DiagnosticCategory.Message) { messages++; renderer.diagnostic(output); } @@ -503,7 +484,7 @@ const formatHost: ts.FormatDiagnosticsHost = { } } } - else if (await linterWorker.hasRules(fileName)) { + else if (worker.hasRules(fileName)) { passed++; } processed++; @@ -513,26 +494,15 @@ const formatHost: ts.FormatDiagnosticsHost = { // file's `ruleModes`. Next session reads this and seeds the linter // via `initialTypeAwareRules` so rules are classified correctly // from the first invocation, before the runtime probe re-runs. - const typeAware = await linterWorker.getTypeAwareRules(); + const typeAware = worker.getTypeAwareRules(); project.cacheData.ruleModes = {}; for (const ruleId of typeAware) { project.cacheData.ruleModes[ruleId] = 'type-aware'; } - // Layer 2: harvest content hashes + transitive deps from a fresh - // BuilderProgram pass over the LS program. Persists alongside the - // per-rule cache so the next `--incremental` session can compute - // affected files. Falls through to `undefined` on layer-1-only - // runs, matching the schema contract. - project.cacheData.incrementalState = await linterWorker.buildIncrementalState(); - cache.saveCache( - project.tsconfig, - project.configFile!, - ts.version, - project.cacheData, - ts.sys.createHash, - ); + project.cacheData.programFingerprint = worker.getFingerprint(); + cache.saveCache(project.tsconfig, project.configFile!, tsVersion, project.cacheData); - await startWorker(linterWorker); + await startWorker(); } function resolvePath(p: string) { @@ -572,13 +542,16 @@ function formatConfigError(configFile: string, errorText: string): string { return out.join('\n'); } -async function parseCommonLine(tsconfig: string) { - const jsonConfigFile = ts.readJsonConfigFile(tsconfig, ts.sys.readFile); - return ts.parseJsonSourceFileConfigFileContent( - jsonConfigFile, - ts.sys, - path.dirname(tsconfig), - {}, - tsconfig, - ); +function findConfigFile(dir: string): string | undefined { + while (true) { + const configFile = path.join(dir, 'tsslint.config.ts'); + if (fs.statSync(configFile, { throwIfNoEntry: false })?.isFile()) { + return configFile; + } + const parent = path.dirname(dir); + if (parent === dir) { + return undefined; + } + dir = parent; + } } diff --git a/packages/cli/lib/cache-flow.ts b/packages/cli/lib/cache-flow.ts index 4dac4c7e..1bd2487d 100644 --- a/packages/cli/lib/cache-flow.ts +++ b/packages/cli/lib/cache-flow.ts @@ -8,32 +8,32 @@ // CLI's responsibility — serialization, mtime invalidation, sticky // type-aware cleanup. -import { type Linter, NO_CACHE } from '@tsslint/core'; -import type * as ts from 'typescript'; +import { type Linter, type LintTarget, NO_CACHE } from '@tsslint/core'; +import type { Diagnostic } from '@tsslint/types'; import type { FileCache, SerializedDiagnostic } from './cache.js'; export function lintWithCache( linter: Linter, - fileName: string, + target: LintTarget, fileCache: FileCache, fileMtime: number, - program: ts.Program, options?: { - // Layer 2 master switch. Driven by the CLI's `--incremental` flag. + // Layer 2 master switch. Always on under the CLI. // When false / undefined (mode A), type-aware rules are never // cached: their entries are deleted after each run. When true // (mode B), type-aware rules' fresh results get persisted so the // next session can reuse them. incremental?: boolean; - // File-level signal: the caller's affected-file tracker says this - // file's type-relevant inputs (own text, transitive deps incl. - // ambient `.d.ts` and lib) haven't moved since the prior session. + // Caller signal: this file's type-relevant inputs (every file in + // the program, plus compiler options) haven't moved since the + // prior session. // Only meaningful in mode B. When true, type-aware rule cache // entries are also cache-hit-eligible (skipped via the run's // skipRules set). typeAwareUnaffected?: boolean; }, -): ts.DiagnosticWithLocation[] { +): Diagnostic[] { + const fileName = target.file.fileName; // File mtime is the layer-1 invalidation key. Anything that touched // the file's text drops every cached rule entry — those rules will // re-run because they vanish from the skip set. @@ -71,16 +71,16 @@ export function lintWithCache( // it would on a cold pass — without this, skipped rules' diagnostics // are missing during plugin filtering and every successful suppression // turns into a warm-only unused-comment false positive. - const restored: ts.DiagnosticWithLocation[] = []; + const restored: Diagnostic[] = []; for (const ruleId of skipRules) { const entry = fileCache.rules[ruleId]; if (!entry) continue; for (const sd of entry.diagnostics) { - restored.push(deserializeDiagnostic(sd, fileName, program)); + restored.push(deserializeDiagnostic(sd, target)); } } - const fresh = linter.lint(fileName, { skipRules, prevDiagnostics: restored }); + const fresh = linter.lint(target, { skipRules, prevDiagnostics: restored }); // Cache writes key off the PRE-suppression rule output (`getRawLintResult`), // not the post-plugin `fresh`. Otherwise a diagnostic the ignore plugin @@ -88,7 +88,7 @@ export function lintWithCache( // warm pass would have nothing to feed back in via `prevDiagnostics` // — re-creating the very FP this is fixing. const raw = linter.getRawLintResult(fileName); - const byRule = new Map(); + const byRule = new Map(); for (const diag of raw) { const ruleId = String(diag.code); let bucket = byRule.get(ruleId); @@ -134,7 +134,7 @@ export function lintWithCache( return fresh; } -function serializeDiagnostic(d: ts.DiagnosticWithLocation): SerializedDiagnostic { +function serializeDiagnostic(d: Diagnostic): SerializedDiagnostic { const { file, relatedInformation, ...rest } = d; void file; return { @@ -148,12 +148,11 @@ function serializeDiagnostic(d: ts.DiagnosticWithLocation): SerializedDiagnostic function deserializeDiagnostic( sd: SerializedDiagnostic, - fileName: string, - program: ts.Program, -): ts.DiagnosticWithLocation { + { program, file }: LintTarget, +): Diagnostic { return { ...sd, - file: program.getSourceFile(fileName)!, + file, relatedInformation: sd.relatedInformation?.map(info => ({ ...info, file: info.file ? program.getSourceFile(info.file.fileName) : undefined, diff --git a/packages/cli/lib/cache.ts b/packages/cli/lib/cache.ts index aa61c7b6..b5fc629b 100644 --- a/packages/cli/lib/cache.ts +++ b/packages/cli/lib/cache.ts @@ -5,8 +5,8 @@ // // Path key includes: // - tsslint version (segment) → bumping the package invalidates everything -// - typescript version (segment) → .tsbuildinfo format / TS internals can -// change across TS majors, so segregate per TS version +// - typescript version (segment) → checker output can change across TS +// versions, so segregate per TS version // - hash of (configFilePath, tsconfig, configFile mtime+size) // → editing tsslint.config.ts mints a fresh cache file // @@ -24,14 +24,13 @@ import fs = require('fs'); import os = require('os'); import crypto = require('crypto'); -import type * as ts from 'typescript'; -import type { IncrementalState } from './incremental-state.js'; +import type { Diagnostic, RelatedInformation } from '@tsslint/types'; const pkg = require('../package.json'); // Bump when the on-disk shape changes incompatibly. Mismatching version // returns an empty cache; we don't migrate (cold start is the safe default). -const CACHE_FORMAT_VERSION = 'v2'; +const CACHE_FORMAT_VERSION = 'v3'; export interface CacheData { version: string; @@ -40,12 +39,11 @@ export interface CacheData { // which currently means the same thing: cache write happens). ruleModes: Record; files: Record; - // Layer 2 cross-session state. Present iff the previous session ran - // with `--incremental`. Lets the next session compute which files' - // type-relevant inputs (incl. ambient `.d.ts`) have changed since. - // See `lib/incremental-state.ts`. Optional so layer-1-only sessions - // stay schema-clean. - incrementalState?: IncrementalState; + // Layer 2: fingerprint of every file in the program (path, mtime, + // size) plus compiler options, from the session that wrote this + // cache. Type-aware rule entries are reused only when the current + // program's fingerprint matches. See `lib/fingerprint.ts`. + programFingerprint?: string; } export interface FileCache { @@ -58,14 +56,14 @@ export interface RuleCache { diagnostics: SerializedDiagnostic[]; } -// `ts.DiagnosticWithLocation` minus the live `file` reference — that gets -// re-attached on load by looking up the SourceFile from the current Program. +// `Diagnostic` minus the live `file` reference — that gets re-attached on +// load by looking up the SourceFile from the current Program. // `relatedInformation` similarly stores `{ fileName }` placeholders. -export type SerializedDiagnostic = Omit & { +export type SerializedDiagnostic = Omit & { relatedInformation?: SerializedRelatedInfo[]; }; -export type SerializedRelatedInfo = Omit & { +export type SerializedRelatedInfo = Omit & { file?: { fileName: string }; }; @@ -140,12 +138,7 @@ function isCacheData(x: unknown): x is CacheData { if (typeof f.mtime !== 'number') return false; if (typeof f.rules !== 'object' || f.rules === null) return false; } - if (o.incrementalState !== undefined) { - if (typeof o.incrementalState !== 'object' || o.incrementalState === null) return false; - const inc = o.incrementalState as Record; - if (typeof inc.version !== 'string') return false; - if (typeof inc.tsBuildInfoText !== 'string') return false; - } + if (o.programFingerprint !== undefined && typeof o.programFingerprint !== 'string') return false; return true; } diff --git a/packages/cli/lib/content-mapper.ts b/packages/cli/lib/content-mapper.ts new file mode 100644 index 00000000..062ec2b0 --- /dev/null +++ b/packages/cli/lib/content-mapper.ts @@ -0,0 +1,49 @@ +// Files produced by a tsconfig `contentMappers` entry (e.g. `.vue`) carry +// the transformed TypeScript in `file.text` and the user's file in +// `file.originalText`; `file.spanMap` relates the two. Rules see and +// report in transformed coordinates; everything the user sees or that +// touches disk goes through here. + +import type { TextChange } from '@tsslint/types'; +import { type SourceFile, SpanMap } from 'typescript/ast'; + +export function getOriginalText(file: SourceFile): string { + return file.spanMap ? file.originalText : file.text; +} + +// Undefined when the range only covers generated code with no counterpart +// in the original file — there is nothing for the user to look at. +export function toOriginalRange( + file: SourceFile, + start: number, + end: number, +): { start: number; end: number } | undefined { + if (!file.spanMap) { + return { start, end }; + } + const { range, fidelity } = file.spanMap.virtualToOriginalSpan({ pos: start, end }); + if (SpanMap.isNone(fidelity)) { + return undefined; + } + return { start: range.pos, end: range.end }; +} + +// Undefined unless every edit maps exactly onto original text; an +// approximate edit could corrupt the user's file. +export function toOriginalTextChanges( + file: SourceFile, + textChanges: readonly TextChange[], +): TextChange[] | undefined { + if (!file.spanMap) { + return [...textChanges]; + } + const result: TextChange[] = []; + for (const { span, newText } of textChanges) { + const { range, fidelity } = file.spanMap.virtualToOriginalSpan({ pos: span.start, end: span.start + span.length }); + if (!SpanMap.isExact(fidelity)) { + return undefined; + } + result.push({ span: { start: range.pos, length: range.end - range.pos }, newText }); + } + return result; +} diff --git a/packages/cli/lib/fingerprint.ts b/packages/cli/lib/fingerprint.ts new file mode 100644 index 00000000..d751bd7f --- /dev/null +++ b/packages/cli/lib/fingerprint.ts @@ -0,0 +1,21 @@ +// Layer 2 cache key. A type-aware rule's result for a file can depend on +// any file the program loaded (imports, ambient `.d.ts`, lib) and on the +// compiler options, so the key covers all of them. Coarse by design: any +// change anywhere in the program re-runs every type-aware rule, which +// keeps the check sound without a dependency graph. + +import crypto = require('crypto'); +import fs = require('fs'); +import type { Program } from 'typescript/sync'; + +export function getProgramFingerprint(program: Program): string { + const hash = crypto.createHash('sha256'); + hash.update(JSON.stringify(program.getCompilerOptions())); + for (const fileName of [...program.getSourceFileNames()].sort()) { + // Bundled lib files have no on-disk path; the TS version in the + // cache path already covers them. + const stat = fs.statSync(fileName, { throwIfNoEntry: false }); + hash.update(`\0${fileName}\0${stat?.mtimeMs ?? -1}\0${stat?.size ?? -1}`); + } + return hash.digest('hex'); +} diff --git a/packages/cli/lib/format.ts b/packages/cli/lib/format.ts new file mode 100644 index 00000000..5b83a613 --- /dev/null +++ b/packages/cli/lib/format.ts @@ -0,0 +1,110 @@ +// Converts core diagnostics into the shape TS7's diagnostic formatter +// renders, in the coordinates of the file the user actually wrote. + +import type { Diagnostic, RelatedInformation } from '@tsslint/types'; +import type { SourceFile } from 'typescript/ast'; +import { + type Diagnostic as TSDiagnostic, + type FormatDiagnosticsHost, + formatDiagnosticsWithColorAndContext, +} from 'typescript/sync'; +import contentMapper = require('./content-mapper.js'); + +const lineStartsCache = new WeakMap(); + +// Undefined when the diagnostic lies entirely in generated code of a +// content-mapped file. +export function formatDiagnostic(diagnostic: Diagnostic, host: FormatDiagnosticsHost): string | undefined { + const converted = convert(diagnostic); + if (!converted) { + return undefined; + } + return formatDiagnosticsWithColorAndContext([converted], host).trimEnd(); +} + +function convert(diagnostic: Diagnostic | RelatedInformation): TSDiagnostic | undefined { + // The formatter prints `${source}${code}`; rule ids go in `source`. + const code = typeof diagnostic.code === 'string' + ? { source: diagnostic.code, code: '' as unknown as number } + : { code: diagnostic.code }; + const base = { + ...code, + category: diagnostic.category, + text: diagnostic.messageText, + }; + const { file, start, length } = diagnostic; + if (!file || start === undefined || length === undefined) { + return { ...base, pos: 0, end: 0 }; + } + const range = contentMapper.toOriginalRange(file, start, start + length); + if (!range) { + return undefined; + } + const text = contentMapper.getOriginalText(file); + const lineStarts = getLineStarts(file, text); + const startPosition = toLineAndCharacter(lineStarts, range.start); + const endPosition = toLineAndCharacter(lineStarts, range.end); + const relatedInformation = 'relatedInformation' in diagnostic + ? diagnostic.relatedInformation?.map(convert).filter(info => !!info) + : undefined; + return { + ...base, + fileName: file.fileName, + pos: range.start, + end: range.end, + startPosition, + endPosition, + sourceLines: getSourceLines(text, lineStarts, startPosition.line, endPosition.line), + reportsUnnecessary: 'reportsUnnecessary' in diagnostic ? diagnostic.reportsUnnecessary : undefined, + reportsDeprecated: 'reportsDeprecated' in diagnostic ? diagnostic.reportsDeprecated : undefined, + relatedInformation, + }; +} + +function getLineStarts(file: SourceFile, text: string): number[] { + let lineStarts = lineStartsCache.get(file); + if (!lineStarts) { + lineStarts = [0]; + for (let i = 0; i < text.length; i++) { + const ch = text.charCodeAt(i); + if (ch === 13 /* \r */) { + if (text.charCodeAt(i + 1) === 10 /* \n */) { + i++; + } + lineStarts.push(i + 1); + } + else if (ch === 10 /* \n */ || ch === 0x2028 || ch === 0x2029) { + lineStarts.push(i + 1); + } + } + lineStartsCache.set(file, lineStarts); + } + return lineStarts; +} + +function toLineAndCharacter(lineStarts: number[], position: number) { + let low = 0; + let high = lineStarts.length - 1; + while (low < high) { + const mid = (low + high + 1) >> 1; + if (lineStarts[mid] <= position) { + low = mid; + } + else { + high = mid - 1; + } + } + return { line: low, character: position - lineStarts[low] }; +} + +// Same context window as `tsc`: every line for short spans, otherwise the +// first two and last two (the formatter prints an ellipsis for the gap). +function getSourceLines(text: string, lineStarts: number[], startLine: number, endLine: number) { + const lines = endLine - startLine >= 4 + ? [startLine, startLine + 1, endLine - 1, endLine] + : Array.from({ length: endLine - startLine + 1 }, (_, i) => startLine + i); + return lines.map(line => { + const lineEnd = line + 1 < lineStarts.length ? lineStarts[line + 1] : text.length; + return { line, text: text.slice(lineStarts[line], lineEnd).replace(/(?:\r\n|[\r\n\u2028\u2029])$/, '') }; + }); +} diff --git a/packages/cli/lib/incremental-state.ts b/packages/cli/lib/incremental-state.ts deleted file mode 100644 index 01556cfa..00000000 --- a/packages/cli/lib/incremental-state.ts +++ /dev/null @@ -1,183 +0,0 @@ -// Layer 2 cross-session state. Wraps TypeScript's internal -// `getBuildInfo` / `createBuilderProgramUsingIncrementalBuildInfo` -// so two `tsslint --incremental` runs can share BuilderProgram state -// across processes via TS's own `tsBuildInfoFile` format. -// -// Why internal API: the public `BuilderProgram` interface only takes -// an `oldProgram` of type `SemanticDiagnosticsBuilderProgram` — there's -// no public path from disk back to BP. Manually serialising via -// `getAllDependencies` + content hash works (we shipped that in -// 68140be) but produces 17MB+ JSON on Dify-scale projects, drops -// node_modules tracking to fit, and uses content hashes where TS -// would use shape signatures. The internal route gives: -// - shape signatures (smarter than content hash — body-only edits -// don't propagate) -// - bit-packed compact format (380 bytes for a 3-file program in -// our spike, vs kilobytes manual) -// - node_modules tracking included -// - all the edge cases TS already debugged: ambient declarations, -// module augmentations, lib changes, project references -// -// Risk surface: -// - `getBuildInfo`, `createBuilderProgramUsingIncrementalBuildInfo` -// are not in `typescript.d.ts`. They've been stable across TS -// 5.x → 6.x but no contract. Cache key already includes -// `ts.version`, so version skew gives a clean miss instead of -// corrupted state. -// - On parse / shape mismatch, treat as cold start (the standard -// "miss is always safe" invariant). - -import type * as ts from 'typescript'; - -// Synthetic path used as the `tsBuildInfoFile` value passed to TS. -// TS uses this only to resolve relative paths inside the buildinfo -// payload — we never actually write here on disk; the buildinfo text -// is captured via the BuilderProgramHost's `writeFile` callback and -// stored in `IncrementalState.tsBuildInfoText`. -export const SYNTHETIC_BUILD_INFO_PATH = '/__tsslint__.tsbuildinfo'; - -// Hard cap on the captured buildinfo text length. The TS internal -// format is compact (~3.6MB on Dify's 5867 files), so 64MB leaves -// ~10–15× headroom on that scale — comfortably below V8's max string -// length and below the size where JSON.stringify of the surrounding -// cache object starts to feel sticky. If the cap fires we warn + skip -// persistence (cold start next session is preferable to a multi-second -// JSON serialise hit on every run). -const MAX_BUILD_INFO_BYTES = 64 * 1024 * 1024; - -export interface IncrementalState { - version: string; - // Raw text TS wrote via `BuilderProgram.emitBuildInfo`. Opaque to - // us — fed straight back to `ts.getBuildInfo` on the next session. - tsBuildInfoText: string; -} - -// Format version. Bump if we ever change what we store alongside -// `tsBuildInfoText`. The internal TS format itself is keyed on -// `ts.version` via the cache file's path component. -export const INCREMENTAL_STATE_VERSION = 'v3'; - -interface IncrementalAccess { - getBuildInfo(file: string, text: string): unknown | undefined; - createBuilderProgramUsingIncrementalBuildInfo( - buildInfo: unknown, - buildInfoPath: string, - host: { useCaseSensitiveFileNames(): boolean; getCurrentDirectory(): string }, - ): ts.BuilderProgram; -} - -// Cast helper. Keeps the unsafe access concentrated and easy to find -// when TS upgrades. -export function asIncremental(ts: typeof import('typescript')): IncrementalAccess { - return ts as unknown as IncrementalAccess; -} - -// Warn the user that layer 2 cache is unavailable this session. -// Goes to stderr (yellow) so it doesn't interleave with the -// renderer's stdout output but stays visible by default. CI logs -// stderr by default; users who pipe stderr to /dev/null know what -// they signed up for. -// -// Each call site phrases its own reason — these are rare enough -// that we don't need rate-limiting. A single CLI invocation can -// emit at most one of each (load fails once at setup, save fails -// once at end-of-project). -function warn(msg: string): void { - process.stderr.write('\x1b[93mwarn\x1b[0m ' + msg + '\n'); -} - -// Reconstruct an old BuilderProgram from a previously captured -// `tsBuildInfoText`. Used as the `oldProgram` argument when creating -// the current session's BP. Returns undefined on any deserialization -// failure (cold-start fallback). -export function reconstructOldBuilder( - ts: typeof import('typescript'), - prev: IncrementalState | undefined, - host: { useCaseSensitiveFileNames(): boolean; getCurrentDirectory(): string }, -): ts.BuilderProgram | undefined { - // Schema-version mismatch and "no prev cache" are normal cold-start - // paths (e.g. cache invalidated by config change, or first run); - // don't warn about either. - if (!prev || prev.version !== INCREMENTAL_STATE_VERSION) return undefined; - const api = asIncremental(ts); - if ( - typeof api.getBuildInfo !== 'function' - || typeof api.createBuilderProgramUsingIncrementalBuildInfo !== 'function' - ) { - warn( - `TypeScript ${ts.version} is missing internal APIs ` - + `(getBuildInfo / createBuilderProgramUsingIncrementalBuildInfo). ` - + `Type-aware cache disabled — every type-aware rule will re-run from cold start.`, - ); - return undefined; - } - try { - const buildInfo = api.getBuildInfo(SYNTHETIC_BUILD_INFO_PATH, prev.tsBuildInfoText); - if (!buildInfo) return undefined; - return api.createBuilderProgramUsingIncrementalBuildInfo( - buildInfo, - SYNTHETIC_BUILD_INFO_PATH, - host, - ); - } - catch (e) { - warn( - `Could not load previous incremental state on TypeScript ${ts.version}: ` - + `${(e as Error).message}. Type-aware cache will rebuild from cold start.`, - ); - return undefined; - } -} - -// Capture a `tsBuildInfoText` from a live BuilderProgram. Triggers a -// `BuilderProgram.emitBuildInfo` with a writeFile callback that -// intercepts what TS would otherwise write to disk. -// -// `emitBuildInfo` is on the runtime `BuilderProgram` shape but not in -// the public d.ts (the public surface only exposes the `emit()` family -// that wraps it). Cast through `unknown` to silence the type error; -// runtime is stable across TS 5.x → 6.x. -// -// On any failure (method missing on a future TS, or it threw), warn + -// return undefined. Caller persists no `incrementalState` for this -// session, which means next session starts cold for layer 2 — the -// layer-1 mtime cache still works. Wrong miss > wrong hit. -export function captureIncrementalState( - tsVersion: string, - builder: ts.BuilderProgram, -): IncrementalState | undefined { - const builderAny = builder as unknown as { - emitBuildInfo?(writeFile: (path: string, content: string) => void): void; - }; - if (typeof builderAny.emitBuildInfo !== 'function') { - warn( - `TypeScript ${tsVersion} BuilderProgram is missing emitBuildInfo. ` - + `Type-aware cache cannot be persisted — next run will start cold.`, - ); - return undefined; - } - let captured: string | undefined; - try { - builderAny.emitBuildInfo((_path, content) => { - captured = content; - }); - } - catch (e) { - warn( - `Could not persist incremental state on TypeScript ${tsVersion}: ` - + `${(e as Error).message}. Type-aware cache cannot be persisted — next run will start cold.`, - ); - return undefined; - } - if (!captured) return undefined; - if (captured.length > MAX_BUILD_INFO_BYTES) { - const mb = (captured.length / 1024 / 1024).toFixed(1); - const capMb = MAX_BUILD_INFO_BYTES / 1024 / 1024; - warn( - `Incremental state too large (${mb}MB > ${capMb}MB cap). ` - + `Type-aware cache cannot be persisted — next run will start cold.`, - ); - return undefined; - } - return { version: INCREMENTAL_STATE_VERSION, tsBuildInfoText: captured }; -} diff --git a/packages/cli/lib/worker.ts b/packages/cli/lib/worker.ts index 044273b3..119d6246 100644 --- a/packages/cli/lib/worker.ts +++ b/packages/cli/lib/worker.ts @@ -1,135 +1,68 @@ -import ts = require('typescript-native-bridge'); import type config = require('@tsslint/config'); import core = require('@tsslint/core'); import url = require('url'); import path = require('path'); import fs = require('fs'); -import crypto = require('crypto'); +import * as ts from 'typescript/ast'; +import { API, type Project, type Snapshot } from 'typescript/sync'; import cacheFlow = require('./cache-flow.js'); -import incrementalState = require('./incremental-state.js'); +import contentMapper = require('./content-mapper.js'); +import type { FileTextChanges } from '@tsslint/types'; import type { FileCache } from './cache.js'; -import type { IncrementalState } from './incremental-state.js'; +import { getProgramFingerprint } from './fingerprint.js'; -// @tsslint/core typings are declared against stock `typescript`; the two -// modules are nominally distinct types. TNB is a drop-in with the same API -// surface. -const tsStock = ts as unknown as typeof import('typescript'); +// One TS7 API process and one snapshot holding every project of the run. +// Projects share parsed files inside the snapshot; `--fix` replaces the +// snapshot after each write. +let api: API | undefined; +let snapshot: Snapshot | undefined; -// Fallback if `ts.sys.createHash` is undefined on this host (Node ≥ 22.6 -// always provides it via crypto, but the type is optional). sha256 hex. -const defaultHash = (s: string) => crypto.createHash('sha256').update(s).digest('hex'); - -let projectVersion = 0; -let typeRootsVersion = 0; -let options: ts.CompilerOptions = {}; -let fileNames: string[] = []; +let tsconfig: string; let linter: core.Linter; -// Layer 2 state. We wrap the LS program in a SemanticDiagnostics- -// BuilderProgram (with the prev session's BP fed back via TS's internal -// `tsBuildInfoText` round-trip) and walk affected files once. cache- -// flow consults this set to decide whether type-aware rules can be -// cache-hit. Always populated under the CLI; `--force` opts out by -// clearing the loaded cache, not by disabling layer 2. -let affectedFiles: Set | undefined; -// The current session's BP — held until end-of-project so we can -// capture its updated buildinfo text for next session's persistence. -let currentBuilder: ts.SemanticDiagnosticsBuilderProgram | undefined; +let programFingerprint: string | undefined; +let wroteFiles = false; +// Layer 2: the program is identical to the one that produced the cache, +// so cached type-aware entries are still valid. +let typeAwareUnaffected = false; + +export function getApi(): API { + // Content mappers run external processes declared by the project's + // tsconfig; the project's own tsslint.config.ts is equally trusted. + return api ??= new API({ cwd: process.cwd(), runExternalCode: true }); +} -const snapshots = new Map(); -const versions = new Map(); -const languageServiceHost: ts.LanguageServiceHost = { - ...ts.sys, - useCaseSensitiveFileNames() { - return ts.sys.useCaseSensitiveFileNames; - }, - getProjectVersion() { - return projectVersion.toString(); - }, - getTypeRootsVersion() { - return typeRootsVersion; - }, - getCompilationSettings() { - return options; - }, - getScriptFileNames() { - return fileNames; - }, - getScriptVersion(fileName) { - // In-session bumps win — `--fix` updates this map after writing - // the file. Otherwise fall back to the on-disk mtime so the - // version reflects content across CLI invocations. Layer 2's - // BuilderProgram diff relies on this — without it, every cross- - // session file looks unchanged (always '0') even when the - // content moved on disk. - const inSession = versions.get(fileName); - if (inSession !== undefined) return inSession.toString(); - const stat = fs.statSync(fileName, { throwIfNoEntry: false }); - return stat ? stat.mtimeMs.toString() : '0'; - }, - getScriptSnapshot(fileName) { - if (!snapshots.has(fileName)) { - snapshots.set(fileName, ts.ScriptSnapshot.fromString(ts.sys.readFile(fileName)!)); - } - return snapshots.get(fileName); - }, - getScriptKind(fileName) { - switch (path.extname(fileName).toLowerCase()) { - case '.js': - case '.mjs': - case '.cjs': - return ts.ScriptKind.JS; - case '.jsx': - return ts.ScriptKind.JSX; - case '.tsx': - return ts.ScriptKind.TSX; - case '.json': - return ts.ScriptKind.JSON; - } - return ts.ScriptKind.TS; - }, - getDefaultLibFileName(options) { - return ts.getDefaultLibFilePath(options); - }, -}; -const languageService = ts.createLanguageService(languageServiceHost); +export function openProjects(tsconfigs: string[]) { + snapshot = getApi().createSnapshot({ openProjects: tsconfigs }); +} + +export function getProjectFileNames(tsconfig: string): readonly string[] { + return getProject(tsconfig).parsedCommandLine.fileNames; +} + +export function close() { + snapshot?.dispose(); + api?.close(); +} + +function getProject(configFileName = tsconfig): Project { + const project = snapshot!.getConfiguredProject(configFileName); + if (!project) { + throw new Error(`Project not loaded: ${configFileName}`); + } + return project; +} -// Linter is single-threaded by design. The previous version split into a -// worker_threads worker for TTY mode (so the spinner could update during a -// file's lint) and a local fallback for non-TTY. Real numbers showed worker -// IPC overhead (JSON.stringify + JSON.parse + structured-clone of diagnostic -// payloads + Worker spawn / teardown) wasn't earning its keep — and a single -// `text` field on a 3 MB checker.ts duplicated across hundreds of diagnostics -// blew JSON.stringify past V8's max string length, crashing the worker. -// Keep a single in-process API; the spinner just updates between files. -export function create() { - return { - setup(...args: Parameters) { - return setup(...args); - }, - lint(...args: Parameters) { - return lint(...args); - }, - hasCodeFixes(...args: Parameters) { - return hasCodeFixes(...args); - }, - hasRules(...args: Parameters) { - return hasRules(...args); - }, - getTypeAwareRules() { - return [...linter.getTypeAwareRules()]; - }, - buildIncrementalState() { - return buildIncrementalState(); - }, - }; +function getTarget(fileName: string): core.LintTarget | undefined { + const { program, checker } = getProject(); + const file = program.getSourceFile(fileName); + return file && { program, checker, file }; } -async function setup( +export async function setup( configFile: string, - _fileNames: string[], - _options: ts.CompilerOptions, + _tsconfig: string, initialTypeAwareRules: readonly string[], - prevIncrementalState: IncrementalState | undefined, + prevProgramFingerprint: string | undefined, ): Promise { let config: config.Config | config.Config[]; try { @@ -142,111 +75,31 @@ async function setup( return String(err); } - // Reset per-project state. Multi-project runs reuse the same worker - // (in-process) — without this, cross-project file paths accumulate in - // `snapshots` / `versions` (memory leak) and `affectedFiles` from a - // prior project would mis-classify this project's files as cache-hit - // candidates if their absolute paths happened to overlap. - snapshots.clear(); - versions.clear(); - affectedFiles = undefined; - currentBuilder = undefined; - - projectVersion++; - typeRootsVersion++; - fileNames = _fileNames; - // Internal API path: BuilderProgram.emitBuildInfo only produces - // content when these options are set. Override the user's values - // (their own tsc --incremental builds shouldn't share this file). - // The synthetic path is never written to disk — captured via - // writeFile callback at end of session. - options = { - ..._options, - incremental: true, - tsBuildInfoFile: incrementalState.SYNTHETIC_BUILD_INFO_PATH, - }; + tsconfig = _tsconfig; linter = core.createLinter( - { - languageService, - languageServiceHost, - typescript: tsStock, - }, + { typescript: ts, getProgram: () => getProject().program }, path.dirname(configFile), config, - () => [], initialTypeAwareRules, ); - - { - const program = languageService.getProgram()!; - // Reconstruct the prev session's BP from cached buildinfo text, - // fall through to undefined on any failure (cold-start path). - const oldBuilder = incrementalState.reconstructOldBuilder(tsStock, prevIncrementalState, { - useCaseSensitiveFileNames: () => ts.sys.useCaseSensitiveFileNames, - getCurrentDirectory: () => ts.sys.getCurrentDirectory(), - }); - currentBuilder = ts.createSemanticDiagnosticsBuilderProgram( - program, - { createHash: ts.sys.createHash ?? defaultHash }, - oldBuilder as ts.SemanticDiagnosticsBuilderProgram | undefined, - ); - affectedFiles = new Set(); - // Drain via `ignoreSourceFile` to record affected files without - // computing their semantic diagnostics. The diagnostic compute is - // the expensive part of the drain (~38s on Dify cold) — TSSLint's - // own lint pass triggers semantic checks lazily for the symbols - // type-aware rules query, not the full program. Doing it twice - // wasted time. The graph-propagation work (which determines - // affected via reference graph) still runs internally. - // `ignoreSourceFile`'s typed param is SourceFile only, but TS - // internally calls it with the same `affected` value the iterator - // returns — which can also be a Program (whole-program affected - // path, e.g. lib flip). Handle both shapes at runtime via the - // `fileName` discriminator. - const recordAffected = (sf: ts.SourceFile) => { - const a = sf as ts.SourceFile | ts.Program; - if ('fileName' in a) { - affectedFiles!.add(a.fileName); - } - else { - for (const f of a.getSourceFiles()) affectedFiles!.add(f.fileName); - } - return true; - }; - while (true) { - const result = currentBuilder.getSemanticDiagnosticsOfNextAffectedFile( - undefined, - recordAffected, - ); - if (!result) break; - // Should not reach here — `ignoreSourceFile` always returns true. - } - } - + programFingerprint = getProgramFingerprint(getProject().program); + typeAwareUnaffected = programFingerprint === prevProgramFingerprint; + wroteFiles = false; return true; } -// Capture the current session's BP state for persistence. Called by -// the CLI at end of project. Returns undefined when not in incremental -// mode or when capture fails. -function buildIncrementalState(): IncrementalState | undefined { - if (!currentBuilder) return undefined; - return incrementalState.captureIncrementalState(ts.version, currentBuilder); +// The fingerprint to persist with this project's cache. After `--fix` +// wrote files, type-aware results cached earlier in the run may predate +// those writes, so none of them can be trusted next session. +export function getFingerprint() { + return wroteFiles ? undefined : programFingerprint; } -function lint(fileName: string, fix: boolean, fileCache: FileCache, fileMtime: number) { - let newSnapshot: ts.IScriptSnapshot | undefined; - let diagnostics!: ts.DiagnosticWithLocation[]; - let shouldCheck = true; - - // Layer 2 signals. `incremental` is always true under the CLI now — - // `--force` opts out by clearing the loaded cache instead. - // typeAwareUnaffected: file's deps haven't moved since prev session, - // so cached type-aware entries can be reused this run. - // False in --fix mode — fixes mutate files mid-session - // and invalidate the setup-time affected snapshot for - // downstream files; we'd rather re-run than serve stale. - const typeAwareUnaffected = !fix && !affectedFiles!.has(fileName); +export function lint(fileName: string, fix: boolean, fileCache: FileCache, fileMtime: number) { + let target = getTarget(fileName); + if (!target) { + return []; + } if (fix) { // Drop cache entries for rules that registered a fix in any prior @@ -264,81 +117,73 @@ function lint(fileName: string, fix: boolean, fileCache: FileCache, fileMtime: n // rewrite to `const x = 1` — without iteration, the user has to // run `--fix` repeatedly. ESLint caps at 10 passes; match that. const MAX_FIX_PASSES = 10; - let pass = 0; - let converged = false; - for (; pass < MAX_FIX_PASSES; pass++) { - const program = languageService.getProgram()!; - diagnostics = cacheFlow.lintWithCache(linter, fileName, fileCache, fileMtime, program, { - incremental: true, - typeAwareUnaffected, - }); - + for (let pass = 0;; pass++) { + // Fixes mutate files mid-session, so the setup-time layer 2 + // signal no longer holds for any file. + const diagnostics = cacheFlow.lintWithCache(linter, target, fileCache, fileMtime, { incremental: true }); + const file = target.file; const fixes = linter .getCodeFixes(fileName, 0, Number.MAX_VALUE, diagnostics) - .filter(fix => fix.fixId === 'tsslint'); - + .filter(fix => fix.fixId === 'tsslint') + .flatMap(fix => { + const changes: FileTextChanges[] = []; + for (const change of fix.changes) { + if (change.fileName !== fileName) { + continue; + } + const textChanges = contentMapper.toOriginalTextChanges(file, change.textChanges); + if (!textChanges) { + return []; + } + changes.push({ ...change, textChanges }); + } + return [{ ...fix, changes }]; + }); const textChanges = core.combineCodeFixes(fileName, fixes); if (!textChanges.length) { - converged = true; - break; + return diagnostics; } - - const oldSnapshot = snapshots.get(fileName)!; - newSnapshot = core.applyTextChanges(oldSnapshot, textChanges); - snapshots.set(fileName, newSnapshot); - versions.set(fileName, (versions.get(fileName) ?? 0) + 1); - projectVersion++; - - // Wipe rule cache so the next pass re-lints the new snapshot. - // (cacheFlow keys on mtime, but disk hasn't been written yet — - // the post-loop block does the single converged write.) - fileCache.rules = {}; - } - if (!converged) { - // Hit MAX_FIX_PASSES without the fix loop settling — likely two - // rules' fixes conflict (each pass undoes the other) or a single - // rule oscillates. Surface it; staying silent leaves the user - // thinking --fix succeeded when partial fixes remain. Mirrors - // ESLint's "Maximum autofix passes exceeded" warning. - console.warn( - `[tsslint] --fix did not converge on ${fileName} after ${MAX_FIX_PASSES} passes; remaining fixable diagnostics may indicate a rule conflict.`, + if (pass === MAX_FIX_PASSES) { + // Two rules' fixes conflict (each pass undoes the other) or a + // single rule oscillates. Surface it; staying silent leaves the + // user thinking --fix succeeded when partial fixes remain. + // Mirrors ESLint's "Maximum autofix passes exceeded" warning. + console.warn( + `[tsslint] --fix did not converge on ${fileName} after ${MAX_FIX_PASSES} passes; remaining fixable diagnostics may indicate a rule conflict.`, + ); + return diagnostics; + } + fs.writeFileSync( + fileName, + core.applyTextChanges(contentMapper.getOriginalText(target.file), textChanges), ); - } - shouldCheck = false; - } - - if (newSnapshot) { - const newText = newSnapshot.getText(0, newSnapshot.getLength()); - const oldText = ts.sys.readFile(fileName); - if (newText !== oldText) { - ts.sys.writeFile(fileName, newSnapshot.getText(0, newSnapshot.getLength())); - // File content moved — refresh mtime so the next lint pass - // invalidates layer-1 cache entries for this file. lintWithCache - // compares fileCache.mtime against the fileMtime we pass in. + wroteFiles = true; fileMtime = fs.statSync(fileName).mtimeMs; - shouldCheck = true; + // The mtime may not move within its resolution. + fileCache.rules = {}; + const prev = snapshot!; + // Listing the project again rebuilds it; otherwise it stays dirty + // and keeps serving the old program. + snapshot = prev.update({ fileNotifications: { changed: [fileName] }, openProjects: [tsconfig] }); + prev.dispose(); + target = getTarget(fileName)!; } } - if (shouldCheck) { - const program = languageService.getProgram()!; - diagnostics = cacheFlow.lintWithCache(linter, fileName, fileCache, fileMtime, program, { - incremental: true, - typeAwareUnaffected, - }); - } - - // Diagnostics are already in the original file's coordinates — the - // program's real `ts.SourceFile`, which shares its `lineMap` cache - // across all diagnostics on the same file (so `formatDiagnosticsWith- - // ColorAndContext` only computes line starts once per file). - return diagnostics; + return cacheFlow.lintWithCache(linter, target, fileCache, fileMtime, { + incremental: true, + typeAwareUnaffected, + }); } -function hasCodeFixes(fileName: string) { +export function hasCodeFixes(fileName: string) { return linter.hasCodeFixes(fileName); } -function hasRules(fileName: string) { +export function hasRules(fileName: string) { return Object.keys(linter.getRules(fileName)).length > 0; } + +export function getTypeAwareRules() { + return [...linter.getTypeAwareRules()]; +} diff --git a/packages/cli/test/cache-flow.test.ts b/packages/cli/test/cache-flow.test.ts index 318f5f2e..f2bd0933 100644 --- a/packages/cli/test/cache-flow.test.ts +++ b/packages/cli/test/cache-flow.test.ts @@ -5,8 +5,9 @@ // node packages/cli/test/cache-flow.test.js import type { Config, RuleContext } from '@tsslint/types'; -import * as ts from 'typescript'; +import { DiagnosticCategory } from 'typescript/sync'; import type { FileCache } from '../lib/cache.js'; +import { createProject } from './project'; const core = require('@tsslint/core') as typeof import('@tsslint/core'); const cacheFlow = require('../lib/cache-flow.js') as typeof import('../lib/cache-flow.js'); @@ -22,37 +23,13 @@ function check(name: string, cond: boolean, detail?: string) { } } -function makeContext(files: Record) { - const realLibPath = ts.getDefaultLibFilePath({ target: ts.ScriptTarget.Latest }); - const realLibContent = ts.sys.readFile(realLibPath) ?? ''; - const host: ts.LanguageServiceHost = { - getCompilationSettings: () => ({ - target: ts.ScriptTarget.Latest, - noEmit: true, - lib: [realLibPath.split(/[\\/]/).pop()!], - }), - getScriptFileNames: () => Object.keys(files), - getScriptVersion: () => '1', - getScriptSnapshot: n => { - if (n in files) return ts.ScriptSnapshot.fromString(files[n]); - if (n === realLibPath) return ts.ScriptSnapshot.fromString(realLibContent); - return undefined; - }, - getCurrentDirectory: () => '/', - getDefaultLibFileName: () => realLibPath, - fileExists: n => n in files || n === realLibPath, - readFile: n => (n in files ? files[n] : (n === realLibPath ? realLibContent : undefined)), - }; - return { typescript: ts, languageServiceHost: host, languageService: ts.createLanguageService(host) }; -} - function emptyFileCache(mtime = 0): FileCache { return { mtime, rules: {} }; } // ── Test 1: syntactic rule writes cache entry ──────────────────────────── { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); const config: Config = { rules: { syntactic: ((rctx: RuleContext) => { @@ -60,9 +37,9 @@ function emptyFileCache(mtime = 0): FileCache { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); + const linter = core.createLinter(project.ctx, project.rootDir, config); const cache = emptyFileCache(1); - cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, ctx.languageService.getProgram()!); + cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1); check('syntactic rule cache entry written', !!cache.rules['syntactic']); check('cache has 1 diagnostic', cache.rules['syntactic']?.diagnostics.length === 1); check('hasFix false (no fix reported)', cache.rules['syntactic']?.hasFix === false); @@ -70,7 +47,7 @@ function emptyFileCache(mtime = 0): FileCache { // ── Test 2: type-aware rule does not write cache entry ─────────────────── { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); const config: Config = { rules: { typed: ((rctx: RuleContext) => { @@ -79,16 +56,16 @@ function emptyFileCache(mtime = 0): FileCache { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); + const linter = core.createLinter(project.ctx, project.rootDir, config); const cache = emptyFileCache(1); - const result = cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, ctx.languageService.getProgram()!); + const result = cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1); check('type-aware rule still produces diagnostics', result.length === 1); check('type-aware rule NOT cached', !cache.rules['typed']); } // ── Test 3: cache hit skips rule, restores diagnostic ──────────────────── { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); let runs = 0; const config: Config = { rules: { @@ -98,14 +75,13 @@ function emptyFileCache(mtime = 0): FileCache { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); + const linter = core.createLinter(project.ctx, project.rootDir, config); const cache = emptyFileCache(1); - const program = ctx.languageService.getProgram()!; - cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, program); + cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1); check('first call ran rule', runs === 1); - const second = cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, program); + const second = cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1); check('second call did NOT re-run rule', runs === 1); check('second call still produced 1 diagnostic', second.length === 1); check( @@ -116,7 +92,7 @@ function emptyFileCache(mtime = 0): FileCache { // ── Test 4: mtime mismatch clears all rule entries ─────────────────────── { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); let runs = 0; const config: Config = { rules: { @@ -126,26 +102,25 @@ function emptyFileCache(mtime = 0): FileCache { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); + const linter = core.createLinter(project.ctx, project.rootDir, config); const cache = emptyFileCache(1); - const program = ctx.languageService.getProgram()!; - cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, program); + cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1); check('first call ran', runs === 1); // Same mtime → cache hit - cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, program); + cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1); check('same mtime → still 1 run', runs === 1); // Different mtime → cache invalidated - cacheFlow.lintWithCache(linter, '/a.ts', cache, 2, program); + cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 2); check('mtime change → re-run', runs === 2); check('cache mtime updated', cache.mtime === 2); } // ── Test 5: report-then-touch deletes any cache entry ──────────────────── { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); const config: Config = { rules: { 'report-then-touch': ((rctx: RuleContext) => { @@ -154,34 +129,33 @@ function emptyFileCache(mtime = 0): FileCache { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); + const linter = core.createLinter(project.ctx, project.rootDir, config); const cache = emptyFileCache(1); - cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, ctx.languageService.getProgram()!); + cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1); check('post-rule cleanup deleted entry', !cache.rules['report-then-touch']); } // ── Test 6: sticky type-aware across files ─────────────────────────────── { - const ctx = makeContext({ - '/a.ts': 'const x = 1;', - '/b.ts': 'const y = 2;', + const project = createProject({ + 'a.ts': 'const x = 1;', + 'b.ts': 'const y = 2;', }); const config: Config = { rules: { 'sometimes-typed': ((rctx: RuleContext) => { - if (rctx.file.fileName === '/a.ts') { + if (rctx.file.fileName.endsWith('/a.ts')) { void rctx.program; } rctx.report('hi', 0, 1); }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); + const linter = core.createLinter(project.ctx, project.rootDir, config); const cacheA = emptyFileCache(1); const cacheB = emptyFileCache(1); - const program = ctx.languageService.getProgram()!; - cacheFlow.lintWithCache(linter, '/a.ts', cacheA, 1, program); - cacheFlow.lintWithCache(linter, '/b.ts', cacheB, 1, program); + cacheFlow.lintWithCache(linter, project.target('a.ts'), cacheA, 1); + cacheFlow.lintWithCache(linter, project.target('b.ts'), cacheB, 1); check('a.ts no cache entry (touched)', !cacheA.rules['sometimes-typed']); check( 'b.ts no cache entry (sticky classification)', @@ -191,7 +165,7 @@ function emptyFileCache(mtime = 0): FileCache { // ── Test 7: stale cache entry for type-aware rule is ignored & cleaned ─── { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); let runs = 0; const config: Config = { rules: { @@ -204,15 +178,15 @@ function emptyFileCache(mtime = 0): FileCache { }; // Linter is seeded with `typed` as type-aware (e.g. from prior session // via cache file's ruleModes). The fileCache also has a stale entry. - const linter = core.createLinter(ctx, '/', config, () => [], ['typed']); + const linter = core.createLinter(project.ctx, project.rootDir, config, ['typed']); const cache: FileCache = { mtime: 1, rules: { typed: { hasFix: false, diagnostics: [{ - category: ts.DiagnosticCategory.Message, - code: 'typed' as any, + category: DiagnosticCategory.Message, + code: 'typed', messageText: 'stale', start: 0, length: 1, @@ -221,7 +195,7 @@ function emptyFileCache(mtime = 0): FileCache { }, }, }; - const result = cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, ctx.languageService.getProgram()!); + const result = cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1); check('rule re-ran (stale cache ignored)', runs === 1); check('result reflects fresh run', result.length === 1); check('stale entry deleted', !cache.rules['typed']); @@ -229,7 +203,7 @@ function emptyFileCache(mtime = 0): FileCache { // ── Test 8: hasFix flag derives from rule fix registration ─────────────── { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); const config: Config = { rules: { fixable: ((rctx: RuleContext) => { @@ -237,15 +211,15 @@ function emptyFileCache(mtime = 0): FileCache { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); + const linter = core.createLinter(project.ctx, project.rootDir, config); const cache = emptyFileCache(1); - cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, ctx.languageService.getProgram()!); + cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1); check('hasFix true after rule registered fix', cache.rules['fixable']?.hasFix === true); } // ── Test 9: multiple rules — only cached ones skipped, others run ──────── { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); let aRuns = 0, bRuns = 0; const config: Config = { rules: { @@ -259,18 +233,17 @@ function emptyFileCache(mtime = 0): FileCache { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); + const linter = core.createLinter(project.ctx, project.rootDir, config); const cache = emptyFileCache(1); - const program = ctx.languageService.getProgram()!; // First call: both run - cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, program); + cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1); check('first call ran both', aRuns === 1 && bRuns === 1); check('a cached', !!cache.rules['a']); check('b cached', !!cache.rules['b']); // Second call: both cached - const result = cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, program); + const result = cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1); check('second call ran neither', aRuns === 1 && bRuns === 1); check('result has both restored diagnostics', result.length === 2); } @@ -281,7 +254,7 @@ function emptyFileCache(mtime = 0): FileCache { // moved (BuilderProgram check in production), type-aware rules become // eligible for cache hits and writes — same path as syntactic rules. { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); let runs = 0; const config: Config = { rules: { @@ -292,11 +265,10 @@ function emptyFileCache(mtime = 0): FileCache { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); + const linter = core.createLinter(project.ctx, project.rootDir, config); const cache = emptyFileCache(1); - const program = ctx.languageService.getProgram()!; - cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, program, { incremental: true, typeAwareUnaffected: true }); + cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1, { incremental: true, typeAwareUnaffected: true }); check('first call ran (no cache yet)', runs === 1); check( 'type-aware entry written under unaffected signal', @@ -311,7 +283,7 @@ function emptyFileCache(mtime = 0): FileCache { // ── Test 11 (layer 2): cache hit on second call with same signal ───────── { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); let runs = 0; const config: Config = { rules: { @@ -322,12 +294,11 @@ function emptyFileCache(mtime = 0): FileCache { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); + const linter = core.createLinter(project.ctx, project.rootDir, config); const cache = emptyFileCache(1); - const program = ctx.languageService.getProgram()!; - cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, program, { incremental: true, typeAwareUnaffected: true }); - const second = cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, program, { + cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1, { incremental: true, typeAwareUnaffected: true }); + const second = cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1, { incremental: true, typeAwareUnaffected: true, }); @@ -345,7 +316,7 @@ function emptyFileCache(mtime = 0): FileCache { // drops the signal (BuilderProgram now considers the file affected). // The type-aware rule must re-run AND its old entry deleted. { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); let runs = 0; const config: Config = { rules: { @@ -356,23 +327,22 @@ function emptyFileCache(mtime = 0): FileCache { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); + const linter = core.createLinter(project.ctx, project.rootDir, config); const cache = emptyFileCache(1); - const program = ctx.languageService.getProgram()!; // Mode B: write the entry. - cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, program, { incremental: true, typeAwareUnaffected: true }); + cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1, { incremental: true, typeAwareUnaffected: true }); check('layer-2 first run cached the entry', !!cache.rules['typed']); // Mode A (default): file is affected. Re-run, drop entry. - cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, program); + cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1); check('mode-A re-run executed rule', runs === 2); check('mode-A re-run dropped the entry', !cache.rules['typed']); } // ── Test 13 (layer 2): mode A (no incremental) never caches type-aware ── { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); const config: Config = { rules: { typed: ((rctx: RuleContext) => { @@ -381,16 +351,15 @@ function emptyFileCache(mtime = 0): FileCache { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); + const linter = core.createLinter(project.ctx, project.rootDir, config); const cache = emptyFileCache(1); - const program = ctx.languageService.getProgram()!; // No options arg → mode A. Type-aware rules never cached. - cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, program); + cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1); check('default (no options) does NOT cache type-aware', !cache.rules['typed']); // Explicit incremental=false also mode A. - cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, program, { incremental: false }); + cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1, { incremental: false }); check('explicit incremental=false does NOT cache type-aware', !cache.rules['typed']); } @@ -401,7 +370,7 @@ function emptyFileCache(mtime = 0): FileCache { // hit. The split between "trust cache" (typeAwareUnaffected) and "write // cache" (incremental) is the gate. { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); let runs = 0; const config: Config = { rules: { @@ -412,13 +381,12 @@ function emptyFileCache(mtime = 0): FileCache { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); + const linter = core.createLinter(project.ctx, project.rootDir, config); const cache = emptyFileCache(1); - const program = ctx.languageService.getProgram()!; // Cold session under --incremental: no prev state, file is "affected" // (unaffected=false). Must run AND write entry. - cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, program, { + cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1, { incremental: true, typeAwareUnaffected: false, }); @@ -438,7 +406,7 @@ function emptyFileCache(mtime = 0): FileCache { // diagnostic; it just isn't written to disk, so the next warm hit on // the same file won't see it. { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); const config: Config = { rules: { env_dependent: ((rctx: RuleContext) => { @@ -447,14 +415,13 @@ function emptyFileCache(mtime = 0): FileCache { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); + const linter = core.createLinter(project.ctx, project.rootDir, config); const cache = emptyFileCache(1); const diags = cacheFlow.lintWithCache( linter, - '/a.ts', + project.target('a.ts'), cache, 1, - ctx.languageService.getProgram()!, ); check('current run returns both diagnostics', diags.length === 2); check( @@ -474,7 +441,7 @@ function emptyFileCache(mtime = 0): FileCache { // ── Test 16: withoutCache() — second run cache-hits with the survivor ─── { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); const config: Config = { rules: { env_dependent: ((rctx: RuleContext) => { @@ -483,19 +450,18 @@ function emptyFileCache(mtime = 0): FileCache { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); + const linter = core.createLinter(project.ctx, project.rootDir, config); const cache = emptyFileCache(1); - cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, ctx.languageService.getProgram()!); + cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1); // Second run with the same mtime — rule cache-hits, only the // persisted diagnostic comes back. - const linter2 = core.createLinter(ctx, '/', config, () => []); + const linter2 = core.createLinter(project.ctx, project.rootDir, config); const diags = cacheFlow.lintWithCache( linter2, - '/a.ts', + project.target('a.ts'), cache, 1, - ctx.languageService.getProgram()!, ); check('warm cache hit returns 1 diagnostic', diags.length === 1); check('warm replay drops the marked one', diags[0]?.messageText === 'plain'); @@ -511,14 +477,14 @@ function emptyFileCache(mtime = 0): FileCache { // function of file text alone — replaying it on a warm hit should match // what re-running the rule would produce. { - const ctx = makeContext({ - '/skip.ts': 'const x = 1;', - '/check.ts': 'const y = 2;', + const project = createProject({ + 'skip.ts': 'const x = 1;', + 'check.ts': 'const y = 2;', }); const config: Config = { rules: { 'mixed-mode': ((rctx: RuleContext) => { - if (rctx.file.fileName === '/skip.ts') return; + if (rctx.file.fileName.endsWith('/skip.ts')) return; void rctx.program; rctx.report('typed', 0, 1); }), @@ -527,12 +493,11 @@ function emptyFileCache(mtime = 0): FileCache { // ── Session 1: cold, both files lint — process the early-return file // FIRST so the rule isn't yet type-aware when its entry gets written. - const linter1 = core.createLinter(ctx, '/', config, () => []); - const program1 = ctx.languageService.getProgram()!; + const linter1 = core.createLinter(project.ctx, project.rootDir, config); const cacheSkip: FileCache = emptyFileCache(1); const cacheCheck: FileCache = emptyFileCache(1); - cacheFlow.lintWithCache(linter1, '/skip.ts', cacheSkip, 1, program1); - cacheFlow.lintWithCache(linter1, '/check.ts', cacheCheck, 1, program1); + cacheFlow.lintWithCache(linter1, project.target('skip.ts'), cacheSkip, 1); + cacheFlow.lintWithCache(linter1, project.target('check.ts'), cacheCheck, 1); check( 'session 1: rule classified type-aware after both files', @@ -550,26 +515,24 @@ function emptyFileCache(mtime = 0): FileCache { // ── Session 2: rule pre-classified type-aware (from session 1). // Both files unchanged. typeAwareUnaffected=true → both should // cache-hit and replay cleanly. - const linter2 = core.createLinter(ctx, '/', config, () => [], ['mixed-mode']); - const program2 = ctx.languageService.getProgram()!; + const linter2 = core.createLinter(project.ctx, project.rootDir, config, ['mixed-mode']); let earlyReturnRanInSession2 = false; const config2: Config = { rules: { 'mixed-mode': ((rctx: RuleContext) => { earlyReturnRanInSession2 = true; - if (rctx.file.fileName === '/skip.ts') return; + if (rctx.file.fileName.endsWith('/skip.ts')) return; void rctx.program; rctx.report('typed', 0, 1); }), }, }; - const linterMonitored = core.createLinter(ctx, '/', config2, () => [], ['mixed-mode']); + const linterMonitored = core.createLinter(project.ctx, project.rootDir, config2, ['mixed-mode']); const session2Skip = cacheFlow.lintWithCache( linterMonitored, - '/skip.ts', + project.target('skip.ts'), cacheSkip, 1, - program2, { incremental: true, typeAwareUnaffected: true }, ); check( @@ -589,7 +552,7 @@ function emptyFileCache(mtime = 0): FileCache { // Symbol-keyed property must stay invisible to JSON.stringify and to // `{...spread}` so the on-disk cache stays clean. { - const ctx = makeContext({ '/a.ts': 'const x = 1;' }); + const project = createProject({ 'a.ts': 'const x = 1;' }); const config: Config = { rules: { r: ((rctx: RuleContext) => { @@ -597,9 +560,9 @@ function emptyFileCache(mtime = 0): FileCache { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); + const linter = core.createLinter(project.ctx, project.rootDir, config); const cache = emptyFileCache(1); - cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, ctx.languageService.getProgram()!); + cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1); // rule entry exists but with 0 diagnostics — no smuggled keys. const json = JSON.stringify(cache); check('NO_CACHE marker not visible in serialised cache', !json.includes('no-cache')); @@ -622,7 +585,7 @@ function emptyFileCache(mtime = 0): FileCache { create: (cmd: string | [string, string], reportsUnused: boolean) => any; }; const code = '// eslint-disable-next-line foo\nconst x = 1;\n'; - const ctx = makeContext({ '/a.ts': code }); + const project = createProject({ 'a.ts': code }); const config: Config = { rules: { foo: ((rctx: RuleContext) => { @@ -633,11 +596,10 @@ function emptyFileCache(mtime = 0): FileCache { }, plugins: [ignorePlugin.create('eslint-disable-next-line', true)], }; - const linter = core.createLinter(ctx, '/', config, () => []); + const linter = core.createLinter(project.ctx, project.rootDir, config); const cache = emptyFileCache(1); - const program = ctx.languageService.getProgram()!; - const cold = cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, program); + const cold = cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1); check( 'cold pass: rule diag suppressed by disable comment, no user-visible diag', cold.length === 0, @@ -649,7 +611,7 @@ function emptyFileCache(mtime = 0): FileCache { cache.rules['foo']?.diagnostics.length === 1, ); - const warm = cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, program); + const warm = cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1); check( 'warm pass: same comment suppresses the same (now-cached) diag — no unused-comment FP', warm.length === 0, @@ -665,7 +627,7 @@ function emptyFileCache(mtime = 0): FileCache { // invariant: across cold + warm passes for a non-suppressed diagnostic, // the user sees exactly one report each pass — never two. { - const ctx = makeContext({ '/a.ts': 'const x = 1;\n' }); + const project = createProject({ 'a.ts': 'const x = 1;\n' }); let runs = 0; const config: Config = { rules: { @@ -675,15 +637,14 @@ function emptyFileCache(mtime = 0): FileCache { }), }, }; - const linter = core.createLinter(ctx, '/', config, () => []); + const linter = core.createLinter(project.ctx, project.rootDir, config); const cache = emptyFileCache(1); - const program = ctx.languageService.getProgram()!; - const cold = cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, program); + const cold = cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1); check('cold: rule ran once', runs === 1); check('cold: 1 diag returned', cold.length === 1); - const warm = cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, program); + const warm = cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1); check('warm: rule did NOT re-run (cached)', runs === 1); check('warm: 1 diag returned (no duplication)', warm.length === 1); check( @@ -702,7 +663,7 @@ function emptyFileCache(mtime = 0): FileCache { create: (cmd: string | [string, string], reportsUnused: boolean) => any; }; const code = '// eslint-disable-next-line foo\nconst x = 1;\n'; - const ctx = makeContext({ '/a.ts': code }); + const project = createProject({ 'a.ts': code }); const config: Config = { rules: { foo: ((rctx: RuleContext) => { @@ -716,11 +677,10 @@ function emptyFileCache(mtime = 0): FileCache { }, plugins: [ignorePlugin.create('eslint-disable-next-line', true)], }; - const linter = core.createLinter(ctx, '/', config, () => []); + const linter = core.createLinter(project.ctx, project.rootDir, config); const cache = emptyFileCache(1); - const program = ctx.languageService.getProgram()!; - const cold = cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, program); + const cold = cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1); const coldCodes = cold.map(d => d.code).sort(); check( 'cold: only `bar` survives (`foo` suppressed by comment for `foo`)', @@ -728,7 +688,7 @@ function emptyFileCache(mtime = 0): FileCache { `cold codes: ${coldCodes.join(',')}`, ); - const warm = cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, program); + const warm = cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1); const warmCodes = warm.map(d => d.code).sort(); check( 'warm: same single `bar` — no duplicate, no resurrected `foo`, no FP unused-comment', @@ -756,7 +716,7 @@ function emptyFileCache(mtime = 0): FileCache { create: (cmd: string | [string, string], reportsUnused: boolean) => any; }; const code = '/* eslint-disable @typescript-eslint/r1 */\n/* eslint-disable @typescript-eslint/r2 */\nconst x = 1;\n'; - const ctx = makeContext({ '/a.ts': code }); + const project = createProject({ 'a.ts': code }); const config: Config = { rules: { '@typescript-eslint/r1': ((rctx: RuleContext) => { @@ -770,11 +730,10 @@ function emptyFileCache(mtime = 0): FileCache { }, plugins: [ignorePlugin.create(['eslint-disable', 'eslint-enable'], true)], }; - const linter = core.createLinter(ctx, '/', config, () => []); + const linter = core.createLinter(project.ctx, project.rootDir, config); const cache = emptyFileCache(1); - const program = ctx.languageService.getProgram()!; - const result = cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, program); + const result = cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1); check( 'scoped block-disables: each independently suppresses its targeted rule (no unused FP on second comment)', result.length === 0, @@ -819,14 +778,14 @@ function emptyFileCache(mtime = 0): FileCache { }, ]; for (const { label, code } of cases) { - const ctx = makeContext({ '/a.ts': code }); + const project = createProject({ 'a.ts': code }); const config: Config = { rules: {}, plugins: [ignorePlugin.create('eslint-disable-next-line', true)], }; - const linter = core.createLinter(ctx, '/', config, () => []); + const linter = core.createLinter(project.ctx, project.rootDir, config); const cache = emptyFileCache(1); - const result = cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, ctx.languageService.getProgram()!); + const result = cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1); check( `scanner: no phantom directive in ${label}`, result.length === 0, @@ -854,14 +813,14 @@ function emptyFileCache(mtime = 0): FileCache { for (let i = 0; i < 20; i++) lines.push(`// eslint-disable-next-line r${i}`); lines.push('const x = 1;'); const code = lines.join('\n') + '\n'; - const ctx = makeContext({ '/a.ts': code }); + const project = createProject({ 'a.ts': code }); const config: Config = { rules: {}, plugins: [ignorePlugin.create('eslint-disable-next-line', true)], }; - const linter = core.createLinter(ctx, '/', config, () => []); + const linter = core.createLinter(project.ctx, project.rootDir, config); const cache = emptyFileCache(1); - const result = cacheFlow.lintWithCache(linter, '/a.ts', cache, 1, ctx.languageService.getProgram()!); + const result = cacheFlow.lintWithCache(linter, project.target('a.ts'), cache, 1); // Each disable targets a non-existent rule → all 20 are unused → 20 diagnostics. check( 'comment-trivia callback: all N stacked comments processed (no truthy-return short-circuit)', diff --git a/packages/cli/test/cache.test.ts b/packages/cli/test/cache.test.ts index 5133a6d9..8bdb7321 100644 --- a/packages/cli/test/cache.test.ts +++ b/packages/cli/test/cache.test.ts @@ -60,7 +60,7 @@ function configWithMarker(marker: string): string { const tsconfig = tmpFile('tsconfig2.json', '{}'); const config = configWithMarker('t2-' + Date.now()); const original: import('../lib/cache.js').CacheData = { - version: 'v2', + version: 'v3', ruleModes: { 'no-undef': 'type-aware' }, files: { '/abs/foo.ts': { @@ -106,7 +106,7 @@ function configWithMarker(marker: string): string { const parsed = JSON.parse(content); // Find the one we just wrote (heuristic: empty data). if ( - parsed.version === 'v2' + parsed.version === 'v3' && Object.keys(parsed.ruleModes).length === 0 && Object.keys(parsed.files).length === 0 ) { @@ -174,7 +174,7 @@ function configWithMarker(marker: string): string { try { const o = JSON.parse(fs.readFileSync(f, 'utf8')); if ( - o.version === 'v2' && Object.keys(o.files).length === 0 + o.version === 'v3' && Object.keys(o.files).length === 0 && Object.keys(o.ruleModes).length === 0 ) { // Could be from a previous test; pick first. @@ -196,7 +196,7 @@ function configWithMarker(marker: string): string { const tsconfig = tmpFile('tsconfig6.json', '{}'); const config = configWithMarker('t6-' + Date.now()); const data: import('../lib/cache.js').CacheData = { - version: 'v2', + version: 'v3', ruleModes: { 'rule6a': 'type-aware' }, files: {}, }; @@ -223,7 +223,7 @@ function configWithMarker(marker: string): string { const config = configWithMarker('t7-' + Date.now()); const data: import('../lib/cache.js').CacheData = { - version: 'v2', + version: 'v3', ruleModes: { 'rule7': 'type-aware' }, files: {}, }; @@ -302,7 +302,7 @@ function configWithMarker(marker: string): string { else if (e.isFile() && full.endsWith('.cache.json')) { try { const o = JSON.parse(fs.readFileSync(full, 'utf8')); - if (o.version === 'v2' && Object.keys(o.files).length === 0) { + if (o.version === 'v3' && Object.keys(o.files).length === 0) { target = full; break; } @@ -315,7 +315,7 @@ function configWithMarker(marker: string): string { if (target) { // mtime is a string, not a number — must reject. const corrupt = { - version: 'v2', + version: 'v3', ruleModes: {}, files: { '/x': { mtime: 'not-a-number', rules: {} } }, }; @@ -325,7 +325,7 @@ function configWithMarker(marker: string): string { // rules is null — must also reject. const corrupt2 = { - version: 'v2', + version: 'v3', ruleModes: {}, files: { '/x': { mtime: 1, rules: null } }, }; @@ -335,16 +335,16 @@ function configWithMarker(marker: string): string { } } -// ── Test 10: incrementalState shape gate ──────────────────────────────── +// ── Test 10: programFingerprint shape gate ────────────────────────────── // -// `incrementalState` is optional, but if present must be a -// `{version, tsBuildInfoText}` object. A stray `incrementalState: 42` -// would otherwise sneak past and crash later when reconstructOldBuilder -// reads `.tsBuildInfoText`. +// `programFingerprint` is optional, but if present must be a string. A +// stray `programFingerprint: 42` must not be compared against a real +// fingerprint as if it were one. { const tsconfig = tmpFile('tsconfig10.json', '{}'); - const config = configWithMarker('t10-' + Date.now()); - cache.saveCache(tsconfig, config, '6.0.0', cache.emptyCache()); + const marker = 't10-' + Date.now(); + const config = configWithMarker(marker); + cache.saveCache(tsconfig, config, '6.0.0', { ...cache.emptyCache(), programFingerprint: marker }); const cacheRoot = path.join(os.tmpdir(), 'tsslint-cache'); let target: string | null = null; const stack = [cacheRoot]; @@ -363,7 +363,7 @@ function configWithMarker(marker: string): string { else if (e.isFile() && full.endsWith('.cache.json')) { try { const o = JSON.parse(fs.readFileSync(full, 'utf8')); - if (o.version === 'v2' && Object.keys(o.files).length === 0) { + if (o.programFingerprint === marker) { target = full; break; } @@ -373,21 +373,17 @@ function configWithMarker(marker: string): string { } if (target) break; } + check('test 10 cache file found', !!target); if (target) { - const corrupt = { version: 'v2', ruleModes: {}, files: {}, incrementalState: 42 }; + const corrupt = { version: 'v3', ruleModes: {}, files: {}, programFingerprint: 42 }; fs.writeFileSync(target, JSON.stringify(corrupt)); const loaded = cache.loadCache(tsconfig, config, '6.0.0'); - check('incrementalState wrong type → empty cache', loaded.incrementalState === undefined); + check('programFingerprint wrong type → empty cache', loaded.programFingerprint === undefined); - const corrupt2 = { - version: 'v2', - ruleModes: {}, - files: {}, - incrementalState: { version: 'v3' /* tsBuildInfoText missing */ }, - }; - fs.writeFileSync(target, JSON.stringify(corrupt2)); + const valid = { version: 'v3', ruleModes: {}, files: {}, programFingerprint: 'abc' }; + fs.writeFileSync(target, JSON.stringify(valid)); const loaded2 = cache.loadCache(tsconfig, config, '6.0.0'); - check('incrementalState missing field → empty cache', loaded2.incrementalState === undefined); + check('programFingerprint string round-trips', loaded2.programFingerprint === 'abc'); } } diff --git a/packages/cli/test/incremental-state.test.ts b/packages/cli/test/incremental-state.test.ts deleted file mode 100644 index b8f55f10..00000000 --- a/packages/cli/test/incremental-state.test.ts +++ /dev/null @@ -1,353 +0,0 @@ -// Round-trip tests for the layer 2 cross-session state. Verifies that -// capturing a BP's state via TS internal `emitBuildInfo` produces a -// text the next session can feed back through -// `createBuilderProgramUsingIncrementalBuildInfo` to get an oldBP that -// correctly diffs the current program. -// -// Run via: -// node packages/cli/test/incremental-state.test.js - -import * as ts from 'typescript'; - -const inc = require('../lib/incremental-state.js') as typeof import('../lib/incremental-state.js'); - -const failures: string[] = []; -function check(name: string, cond: boolean, detail?: string) { - if (cond) { - process.stdout.write('.'); - } - else { - failures.push(name + (detail ? ' — ' + detail : '')); - process.stdout.write('F'); - } -} - -const realLib = ts.getDefaultLibFilePath({ target: ts.ScriptTarget.Latest }); -const libContent = ts.sys.readFile(realLib) ?? ''; -const libName = realLib.split(/[\\/]/).pop()!; - -function buildProgram(files: Record): ts.Program { - const host: ts.CompilerHost = { - getSourceFile(name) { - if (name in files) { - const text = files[name]; - const sf = ts.createSourceFile(name, text, ts.ScriptTarget.Latest, true); - // BP requires a `version` field on each SourceFile. Production - // LS sets this from `host.getScriptVersion`; here we derive - // from content length so edited files get a different version - // (otherwise BP's diff sees no change and skips propagation). - (sf as unknown as { version: string }).version = String(text.length) + ':' + text.charCodeAt(0); - return sf; - } - if (name === realLib) { - const sf = ts.createSourceFile(realLib, libContent, ts.ScriptTarget.Latest, true); - (sf as unknown as { version: string }).version = String(libContent.length); - return sf; - } - return undefined; - }, - getDefaultLibFileName: () => realLib, - writeFile: () => {}, - getCurrentDirectory: () => '/', - getDirectories: () => [], - fileExists: n => n in files || n === realLib, - readFile: n => files[n] ?? (n === realLib ? libContent : undefined), - getCanonicalFileName: n => n, - useCaseSensitiveFileNames: () => true, - getNewLine: () => '\n', - }; - return ts.createProgram({ - rootNames: Object.keys(files), - options: { - target: ts.ScriptTarget.Latest, - noEmit: true, - incremental: true, - tsBuildInfoFile: inc.SYNTHETIC_BUILD_INFO_PATH, - lib: [libName], - }, - host, - }); -} - -function affectedFileNames(builder: ts.SemanticDiagnosticsBuilderProgram): Set { - const set = new Set(); - while (true) { - const r = builder.getSemanticDiagnosticsOfNextAffectedFile(); - if (!r) break; - if ('fileName' in r.affected) set.add(r.affected.fileName); - else for (const sf of r.affected.getSourceFiles()) set.add(sf.fileName); - } - return set; -} - -const hostShim = { - useCaseSensitiveFileNames: () => true, - getCurrentDirectory: () => '/', -}; - -// stderr capture helper for warning-path tests below. -function captureStderr(fn: () => T): { result: T; stderr: string } { - const orig = process.stderr.write.bind(process.stderr); - let buf = ''; - (process.stderr.write as any) = (chunk: any) => { - buf += String(chunk); - return true; - }; - try { - const result = fn(); - return { result, stderr: buf }; - } - finally { - (process.stderr.write as any) = orig; - } -} - -// ── Test 1: captureIncrementalState produces text on a fresh BP ───────── -{ - const program = buildProgram({ '/a.ts': 'export const x: number = 1;' }); - const builder = ts.createSemanticDiagnosticsBuilderProgram( - program, - { createHash: ts.sys.createHash }, - ); - affectedFileNames(builder); // drain - const state = inc.captureIncrementalState(ts.version, builder); - check('state captured', !!state); - check('state version is v3', state?.version === inc.INCREMENTAL_STATE_VERSION); - check('tsBuildInfoText is non-empty', !!state && state.tsBuildInfoText.length > 0); -} - -// ── Test 2: reconstructOldBuilder + diff round-trip — identical program ─ -{ - const program1 = buildProgram({ - '/a.ts': 'export const x: number = 1;', - '/b.ts': "import { x } from './a'; export const y = x + 1;", - }); - const builder1 = ts.createSemanticDiagnosticsBuilderProgram( - program1, - { createHash: ts.sys.createHash }, - ); - affectedFileNames(builder1); - const captured = inc.captureIncrementalState(ts.version, builder1)!; - - const program2 = buildProgram({ - '/a.ts': 'export const x: number = 1;', - '/b.ts': "import { x } from './a'; export const y = x + 1;", - }); - const oldBP = inc.reconstructOldBuilder(ts, captured, hostShim); - check('reconstruct produced an oldBP', !!oldBP); - const builder2 = ts.createSemanticDiagnosticsBuilderProgram( - program2, - { createHash: ts.sys.createHash }, - oldBP as ts.SemanticDiagnosticsBuilderProgram, - ); - const affected = affectedFileNames(builder2); - check( - 'identical program → no user files affected', - !affected.has('/a.ts') && !affected.has('/b.ts'), - `got affected: ${[...affected].join(', ')}`, - ); -} - -// ── Test 3: round-trip catches edits to imported file ─────────────────── -{ - const program1 = buildProgram({ - '/a.ts': 'export const x: number = 1;', - '/b.ts': "import { x } from './a'; export const y = x + 1;", - }); - const builder1 = ts.createSemanticDiagnosticsBuilderProgram( - program1, - { createHash: ts.sys.createHash }, - ); - affectedFileNames(builder1); - const captured = inc.captureIncrementalState(ts.version, builder1)!; - - // /a.ts changes its public type — should propagate to /b.ts. - const program2 = buildProgram({ - '/a.ts': 'export const x: string = "1";', - '/b.ts': "import { x } from './a'; export const y = x + 1;", - }); - const oldBP = inc.reconstructOldBuilder(ts, captured, hostShim); - const builder2 = ts.createSemanticDiagnosticsBuilderProgram( - program2, - { createHash: ts.sys.createHash }, - oldBP as ts.SemanticDiagnosticsBuilderProgram, - ); - const affected = affectedFileNames(builder2); - check('a.ts affected', affected.has('/a.ts')); - check('b.ts affected (importer of a.ts)', affected.has('/b.ts')); -} - -// ── Test 4: undefined prev → cold start (oldBP undefined) ─────────────── -{ - const oldBP = inc.reconstructOldBuilder(ts, undefined, hostShim); - check('undefined prev → undefined oldBP', oldBP === undefined); -} - -// ── Test 5: schema version mismatch → cold start ──────────────────────── -{ - const stale = { version: 'v0', tsBuildInfoText: '' }; - const oldBP = inc.reconstructOldBuilder(ts, stale, hostShim); - check('version mismatch → undefined oldBP', oldBP === undefined); -} - -// ── Test 6: corrupted tsBuildInfoText → cold start ────────────────────── -{ - const corrupted = { version: inc.INCREMENTAL_STATE_VERSION, tsBuildInfoText: '{garbage' }; - const oldBP = inc.reconstructOldBuilder(ts, corrupted, hostShim); - check('corrupted text → undefined oldBP', oldBP === undefined); -} - -// ── Test 7: TS missing internal load APIs → cold start + warn ────────── -// Future TS could rename `getBuildInfo` / -// `createBuilderProgramUsingIncrementalBuildInfo`. We must not throw, -// AND we must surface a stderr warning so users know type-aware cache -// is silently disabled. -{ - const tsStub = new Proxy(ts, { - get(target, prop) { - if (prop === 'getBuildInfo' || prop === 'createBuilderProgramUsingIncrementalBuildInfo') { - return undefined; - } - return (target as any)[prop]; - }, - }); - const valid = { version: inc.INCREMENTAL_STATE_VERSION, tsBuildInfoText: 'irrelevant' }; - const { result, stderr } = captureStderr(() => { - try { - return inc.reconstructOldBuilder(tsStub, valid, hostShim); - } - catch { - return 'THREW' as const; - } - }); - check('missing load APIs → no throw', result !== 'THREW'); - check('missing load APIs → undefined result', result === undefined); - check('missing load APIs → warning printed', /warn/.test(stderr)); - check('missing load APIs → warning names the missing API', /getBuildInfo/.test(stderr)); - check('missing load APIs → warning names TS version', stderr.includes(ts.version)); -} - -// ── Test 8: BuilderProgram missing emitBuildInfo → undefined + warn ──── -// The save path must degrade gracefully if a future TS removes or -// renames `BuilderProgram.emitBuildInfo`. Otherwise the CLI throws -// after lint completes, losing all results. Must also warn the user. -{ - const fakeBuilder = {} as ts.BuilderProgram; - const { result, stderr } = captureStderr(() => { - try { - return inc.captureIncrementalState(ts.version, fakeBuilder); - } - catch { - return 'THREW' as const; - } - }); - check('missing emitBuildInfo → no throw', result !== 'THREW'); - check('missing emitBuildInfo → undefined state', result === undefined); - check('missing emitBuildInfo → warning printed', /warn/.test(stderr)); - check('missing emitBuildInfo → warning names emitBuildInfo', /emitBuildInfo/.test(stderr)); -} - -// ── Test 9: emitBuildInfo throws → undefined + warn, no throw out ────── -{ - const throwingBuilder = { - emitBuildInfo() { - throw new Error('simulated TS internal failure'); - }, - } as unknown as ts.BuilderProgram; - const { result, stderr } = captureStderr(() => { - try { - return inc.captureIncrementalState(ts.version, throwingBuilder); - } - catch { - return 'THREW' as const; - } - }); - check('throwing emitBuildInfo → no throw', result !== 'THREW'); - check('throwing emitBuildInfo → undefined state', result === undefined); - check('throwing emitBuildInfo → warning printed', /warn/.test(stderr)); - check( - 'throwing emitBuildInfo → warning includes underlying error', - /simulated TS internal failure/.test(stderr), - ); -} - -// ── Test 10: corrupted buildInfo on load throws inside getBuildInfo → -// warn, return undefined. Distinct from Test 6 — TS itself may throw on -// some corrupt inputs (vs returning undefined for others). -{ - const throwingTs = new Proxy(ts, { - get(target, prop) { - if (prop === 'getBuildInfo') { - return () => { - throw new Error('synthetic parse failure'); - }; - } - return (target as any)[prop]; - }, - }); - const valid = { version: inc.INCREMENTAL_STATE_VERSION, tsBuildInfoText: 'whatever' }; - const { result, stderr } = captureStderr(() => { - try { - return inc.reconstructOldBuilder(throwingTs, valid, hostShim); - } - catch { - return 'THREW' as const; - } - }); - check('throwing getBuildInfo → no throw', result !== 'THREW'); - check('throwing getBuildInfo → undefined result', result === undefined); - check('throwing getBuildInfo → warning printed', /warn/.test(stderr)); - check( - 'throwing getBuildInfo → warning includes underlying error', - /synthetic parse failure/.test(stderr), - ); -} - -// ── Test 11: silent paths stay silent ────────────────────────────────── -// Cold start (no prev) and version mismatch are normal, not an error -// — must NOT print a warning. -{ - const { stderr: s1 } = captureStderr(() => inc.reconstructOldBuilder(ts, undefined, hostShim)); - check('undefined prev → no warning', s1 === ''); - const stale = { version: 'v0', tsBuildInfoText: '' }; - const { stderr: s2 } = captureStderr(() => inc.reconstructOldBuilder(ts, stale as any, hostShim)); - check('version mismatch → no warning', s2 === ''); -} - -// ── Test 12: oversized buildinfo → undefined + warn ──────────────────── -// Hard cap on `tsBuildInfoText` size protects against pathological -// monorepos where the captured state grows past V8's max-string limit -// or makes JSON.stringify of the surrounding cache feel sticky. Cap -// fires → warn + skip persistence (next run starts cold for layer 2). -{ - // Fabricate a builder that emits a 65MB buildinfo blob. We only - // exercise the size-guard path; the actual TS-emitted text is - // always small in practice (Dify 5867 files = ~3.6MB). - const huge = 'x'.repeat(65 * 1024 * 1024); - const oversizedBuilder = { - emitBuildInfo(write: (path: string, content: string) => void) { - write('whatever', huge); - }, - } as unknown as ts.BuilderProgram; - const { result, stderr } = captureStderr(() => { - try { - return inc.captureIncrementalState(ts.version, oversizedBuilder); - } - catch { - return 'THREW' as const; - } - }); - check('oversized buildinfo → no throw', result !== 'THREW'); - check('oversized buildinfo → undefined state', result === undefined); - check('oversized buildinfo → warning printed', /warn/.test(stderr)); - check('oversized buildinfo → warning mentions cap', /cap/.test(stderr)); -} - -// ── Done ──────────────────────────────────────────────────────────────── -process.stdout.write('\n'); -if (failures.length) { - console.error(`\n${failures.length} failure(s):`); - for (const f of failures) console.error(' - ' + f); - process.exit(1); -} -console.log('OK'); diff --git a/packages/cli/test/integration.test.ts b/packages/cli/test/integration.test.ts index a6796924..c5c134de 100644 --- a/packages/cli/test/integration.test.ts +++ b/packages/cli/test/integration.test.ts @@ -10,6 +10,7 @@ import path = require('path'); import fs = require('fs'); import os = require('os'); +import url = require('url'); import { spawnSync } from 'child_process'; const failures: string[] = []; @@ -26,6 +27,13 @@ function check(name: string, cond: boolean, detail?: string) { const repoRoot = path.resolve(__dirname, '../../..'); const tsslintBin = path.join(repoRoot, 'packages/cli/bin/tsslint.js'); const noConsoleRule = path.join(repoRoot, 'fixtures/noConsoleRule.ts'); +const configModule = path.join(repoRoot, 'packages/config/index.js'); + +// Module specifier for an absolute path; a bare `C:\...` path is not a +// valid ESM import on Windows. +function importPath(fileName: string): string { + return JSON.stringify(url.pathToFileURL(fileName).href); +} function makeFixture(): string { // Resolve symlinks (macOS' /var → /private/var) so paths line up with @@ -46,10 +54,10 @@ function makeFixture(): string { ); fs.writeFileSync( path.join(dir, 'tsslint.config.ts'), - `import { defineConfig } from '${repoRoot}/packages/config/index.js';\n` + `import { defineConfig } from ${importPath(configModule)};\n` + `export default defineConfig({\n` + ` rules: {\n` - + ` 'no-console': (await import('${noConsoleRule}')),\n` + + ` 'no-console': (await import(${importPath(noConsoleRule)})),\n` + ` },\n` + `});\n`, ); @@ -204,7 +212,7 @@ function readCacheForFixture(fixtureDir: string): unknown { } } -// ── Test 6: incrementalState always persisted (layer 2 default-on) ───── +// ── Test 6: programFingerprint always persisted (layer 2 default-on) ─── { const dir = makeFixture(); try { @@ -213,9 +221,8 @@ function readCacheForFixture(fixtureDir: string): unknown { const data = readCacheForFixture(dir) as any; check('cache written', !!data); check( - 'incrementalState persisted to cache file', - !!data?.incrementalState && typeof data.incrementalState.tsBuildInfoText === 'string' - && data.incrementalState.tsBuildInfoText.length > 0, + 'programFingerprint persisted to cache file', + typeof data?.programFingerprint === 'string' && data.programFingerprint.length > 0, ); } finally { @@ -234,10 +241,10 @@ function makeTypeAwareFixture(): { dir: string; markerPath: string; ambient: str const rulePath = path.join(dir, 'type-aware-rule.ts'); fs.writeFileSync( rulePath, - `import { defineRule } from '${repoRoot}/packages/config/index.js';\n` + `import { defineRule } from ${importPath(configModule)};\n` + `import * as fs from 'fs';\n` - + `export default defineRule(({ file, program, report }) => {\n` - + ` void program.getTypeChecker();\n` + + `export default defineRule(({ file, checker, report }) => {\n` + + ` void checker;\n` + ` fs.appendFileSync(${JSON.stringify(markerPath)}, file.fileName + '\\n');\n` + ` report('type-aware ran', 0, 1);\n` + `});\n`, @@ -257,10 +264,10 @@ function makeTypeAwareFixture(): { dir: string; markerPath: string; ambient: str ); fs.writeFileSync( path.join(dir, 'tsslint.config.ts'), - `import { defineConfig } from '${repoRoot}/packages/config/index.js';\n` + `import { defineConfig } from ${importPath(configModule)};\n` + `export default defineConfig({\n` + ` include: ['fixture.ts'],\n` - + ` rules: { 'type-aware': (await import('${rulePath}')) },\n` + + ` rules: { 'type-aware': (await import(${importPath(rulePath)})) },\n` + `});\n`, ); const ambient = path.join(dir, 'ambient.d.ts'); @@ -300,8 +307,8 @@ function markerLineCount(markerPath: string): number { // // The killer case for layer 2: ambient declaration edits don't move the // dependent file's mtime, so layer 1 alone would silently serve stale -// type-aware results. The cross-session affected-file diff (content hash -// + transitive deps) must catch this. +// type-aware results. The program fingerprint covers every file the +// program loaded, so it must catch this. { const { dir, markerPath, ambient } = makeTypeAwareFixture(); try { @@ -355,6 +362,130 @@ function markerLineCount(markerPath: string): number { } } +// A project whose `.vue` files go through a minimal tsconfig content +// mapper: the `')); + return { text: body + '\\nexport {};\\n', extension: '.ts', mappings: [[0, body.length, start, body.length, 0, 1048575]] }; + } + return null; +} +function respond(id, result) { + const body = Buffer.from(JSON.stringify({ jsonrpc: '2.0', id, result })); + process.stdout.write('Content-Length: ' + body.length + '\\r\\n\\r\\n'); + process.stdout.write(body); +} +`; + +function makeContentMapperFixture(newLine: string): { dir: string; vue: string } { + const dir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'tsslint-int-cm-'))); + const mapperDir = path.join(dir, 'node_modules', 'test-mapper'); + fs.mkdirSync(mapperDir, { recursive: true }); + fs.writeFileSync( + path.join(mapperDir, 'package.json'), + JSON.stringify({ + name: 'test-mapper', + version: '1.0.0', + typescript: { contentMapper: { exec: ['node', 'server.js'], dynamicConfig: true } }, + }), + ); + fs.writeFileSync(path.join(mapperDir, 'server.js'), MAPPER_SERVER); + fs.writeFileSync( + path.join(dir, 'tsconfig.json'), + JSON.stringify({ + compilerOptions: { target: 'es2020', strict: true, skipLibCheck: true }, + contentMappers: [{ package: 'test-mapper', extensions: ['.vue'] }], + include: ['*.vue'], + }), + ); + fs.writeFileSync( + path.join(dir, 'tsslint.config.ts'), + `import { defineConfig, defineRule } from ${importPath(configModule)};\n` + + `export default defineConfig({\n` + + ` rules: {\n` + + ` 'no-console': (await import(${importPath(noConsoleRule)})),\n` + + ` 'generated': defineRule(({ file, report }) => {\n` + + ` const start = file.text.lastIndexOf('export {}');\n` + + ` report('in generated code', start, start + 9).withFix('rewrite generated code', () => [{\n` + + ` fileName: file.fileName,\n` + + ` textChanges: [{ span: { start, length: 9 }, newText: 'void 0;' }],\n` + + ` }]);\n` + + ` }),\n` + + ` },\n` + + `});\n`, + ); + const vue = path.join(dir, 'App.vue'); + fs.writeFileSync( + vue, + ['', '', ''].join( + newLine, + ), + ); + return { dir, vue }; +} + +function stripAnsi(text: string): string { + return text.replace(/\x1b\[[0-9;]*m/g, ''); +} + +// ── Test 10 (content mapper): diagnostics land on the original file ──── +{ + const { dir } = makeContentMapperFixture('\n'); + try { + const out = stripAnsi(runCli(dir).stdout); + check('mapped diagnostic points at App.vue:5:1', out.includes('App.vue:5:1'), out); + check('diagnostic in unmapped generated code is dropped', !out.includes('in generated code'), out); + } + finally { + fs.rmSync(dir, { recursive: true, force: true }); + } +} + +// ── Test 11 (content mapper): --fix edits the original text, exactly ─── +// +// CRLF line endings: positions must stay in original-file coordinates. +// The fix touching generated code has no exact original span and must be +// skipped without blocking the exact one. +{ + const { dir, vue } = makeContentMapperFixture('\r\n'); + try { + const before = fs.readFileSync(vue, 'utf8'); + runCli(dir, '--fix'); + const after = fs.readFileSync(vue, 'utf8'); + check( + '--fix rewrote the mapped span in App.vue', + after === before.replace(`console.log('hi')`, '/* deleted */'), + JSON.stringify(after), + ); + const out = stripAnsi(runCli(dir).stdout); + check('no diagnostics after --fix', !out.includes('no-console'), out); + } + finally { + fs.rmSync(dir, { recursive: true, force: true }); + } +} + // ── Done ──────────────────────────────────────────────────────────────── process.stdout.write('\n'); if (failures.length) { diff --git a/packages/cli/test/project.ts b/packages/cli/test/project.ts new file mode 100644 index 00000000..828b3d6c --- /dev/null +++ b/packages/cli/test/project.ts @@ -0,0 +1,53 @@ +// A real on-disk TypeScript 7 project for core tests. One API process +// per test run; temp dirs are removed when the process exits. +import type { LintTarget } from '@tsslint/core'; +import type { LinterContext } from '@tsslint/types'; +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; +import * as ts from 'typescript/ast'; +import { API } from 'typescript/sync'; + +let api: API | undefined; +const tempDirs: string[] = []; + +function getApi(): API { + if (!api) { + api = new API({ cwd: process.cwd() }); + process.once('exit', () => { + // The child may already be gone when exiting via process.exit(). + try { + api?.close(); + } + catch {} + for (const dir of tempDirs) fs.rmSync(dir, { recursive: true, force: true }); + }); + } + return api; +} + +export function createProject(files: Record) { + const dir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'tsslint-cli-test-'))); + tempDirs.push(dir); + for (const [name, text] of Object.entries(files)) { + fs.writeFileSync(path.join(dir, name), text); + } + const tsconfig = path.join(dir, 'tsconfig.json'); + fs.writeFileSync( + tsconfig, + JSON.stringify({ + compilerOptions: { target: 'esnext', lib: ['esnext'], noEmit: true }, + files: Object.keys(files), + }), + ); + const snapshot = getApi().createSnapshot({ openProjects: [tsconfig] }); + const { program, checker } = snapshot.getConfiguredProject(tsconfig)!; + const ctx: LinterContext = { typescript: ts, getProgram: () => program }; + return { + ctx, + rootDir: dir, + target(name: string): LintTarget { + return { program, checker, file: program.getSourceFile(path.join(dir, name))! }; + }, + }; +} From 1c6346cd192c32a9e65ee5636626d84a523bd1f4 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 22 Oct 2026 18:12:15 +0800 Subject: [PATCH 11/52] chore: adapt fixtures and repo lint config to TypeScript 7 --- fixtures/compat-eslint/fixture.ts | 10 ++-------- fixtures/compat-eslint/tsslint.config.ts | 2 +- fixtures/multiple-configs/fixture.mts | 2 -- fixtures/multiple-configs/tsslint.config.ts | 4 ++-- fixtures/noConsoleRule.ts | 4 ++-- tsslint.config.ts | 10 +++++----- 6 files changed, 12 insertions(+), 20 deletions(-) diff --git a/fixtures/compat-eslint/fixture.ts b/fixtures/compat-eslint/fixture.ts index 02b55322..a594fc64 100644 --- a/fixtures/compat-eslint/fixture.ts +++ b/fixtures/compat-eslint/fixture.ts @@ -1,9 +1,3 @@ -// @ts-nocheck let value = 9001; -// ^? let value: number - -// $ExpectError -value = 'over nine thousand'; - -// $ExpectType number -9001; +let unused = 'over nine thousand'; +console.log(value); diff --git a/fixtures/compat-eslint/tsslint.config.ts b/fixtures/compat-eslint/tsslint.config.ts index b8f275d8..d8aa686c 100644 --- a/fixtures/compat-eslint/tsslint.config.ts +++ b/fixtures/compat-eslint/tsslint.config.ts @@ -2,7 +2,7 @@ import { defineConfig, importESLintRules } from '@tsslint/config'; export default defineConfig({ rules: await importESLintRules({ - 'expect-type/expect': true, 'no-unused-vars': 'warn', + 'prefer-const': true, }), }); diff --git a/fixtures/multiple-configs/fixture.mts b/fixtures/multiple-configs/fixture.mts index aa4f600b..e9fe0090 100644 --- a/fixtures/multiple-configs/fixture.mts +++ b/fixtures/multiple-configs/fixture.mts @@ -1,3 +1 @@ - diff --git a/fixtures/multiple-configs/tsslint.config.ts b/fixtures/multiple-configs/tsslint.config.ts index b1af7805..ab10f6ea 100644 --- a/fixtures/multiple-configs/tsslint.config.ts +++ b/fixtures/multiple-configs/tsslint.config.ts @@ -8,9 +8,9 @@ export default defineConfig([ }, }, { - include: ['**/*.vue'], + include: ['**/*.mts'], rules: { - 'no-console-vue': (await import('../noConsoleRule.ts')), + 'no-console-mts': (await import('../noConsoleRule.ts')), }, }, ]); diff --git a/fixtures/noConsoleRule.ts b/fixtures/noConsoleRule.ts index 05fcad43..7079124f 100644 --- a/fixtures/noConsoleRule.ts +++ b/fixtures/noConsoleRule.ts @@ -1,7 +1,7 @@ import { defineRule } from '../packages/config/index.js'; export default defineRule(({ typescript: ts, file, report }) => { - ts.forEachChild(file, function cb(node) { + file.forEachChild(function cb(node): void { if ( ts.isPropertyAccessExpression(node) && ts.isIdentifier(node.expression) @@ -25,6 +25,6 @@ export default defineRule(({ typescript: ts, file, report }) => { }], ); } - ts.forEachChild(node, cb); + node.forEachChild(cb); }); }); diff --git a/tsslint.config.ts b/tsslint.config.ts index 4285e603..a1abe527 100644 --- a/tsslint.config.ts +++ b/tsslint.config.ts @@ -1,13 +1,13 @@ import { defineConfig, importESLintRules } from './packages/config/index.js'; +// typescript-eslint rules cannot run on TypeScript 7, so only ESLint core +// rules are used here. export default defineConfig({ rules: await importESLintRules( { - '@typescript-eslint/consistent-type-imports': [true, { - disallowTypeAnnotations: false, - fixStyle: 'inline-type-imports', - }], - '@typescript-eslint/no-unnecessary-type-assertion': true, + 'no-var': true, + 'prefer-const': true, + 'eqeqeq': [true, 'always', { null: 'ignore' }], }, {}, async () => (await import('./packages/compat-eslint/index.js')).convertRule, From 0bf064e719224a051e20121d538b3da33a77bb32 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 22 Oct 2026 18:13:03 +0800 Subject: [PATCH 12/52] ci: run on Node 24 and the TypeScript 7 test suite --- .github/workflows/test.yml | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 67524d9e..11d85d3a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,7 +16,7 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 24 cache: pnpm - run: pnpm install @@ -28,23 +28,23 @@ jobs: - name: Parity bench run: pnpm -F @tsslint/compat-eslint bench - # Vendored @typescript-eslint/scope-manager test suite. - - name: Upstream scope-manager - run: pnpm -F @tsslint/compat-eslint test:upstream - # Predicate coverage matrix for AST_NODE_TYPES. - name: Predicate coverage run: pnpm -F @tsslint/compat-eslint test:predicate-coverage - # JSX type-aware parity vs ESLint Linter on eslint-plugin-react-x. - - name: JSX react-x parity - run: pnpm -F @tsslint/compat-eslint test:jsx-react-x - # Lazy ESTree shape parity (every .ts under packages/) + identity # invariants + smoke under visitorKeys traversal. - name: Lazy ESTree parity run: node packages/compat-eslint/test/lazy-estree.test.js + # Regression tests for lazy ESTree review findings. + - name: Lazy ESTree review bugs + run: node packages/compat-eslint/test/lazy-estree-review-bugs.test.js + + # Predicate vs materializer agreement. + - name: Predicate materializer consistency + run: node packages/compat-eslint/test/predicate-materializer-consistency.test.js + # Integration: selector dispatch + CPA fallback + per-rule error # isolation through the full convertRule pipeline. - name: Compat pipeline integration @@ -62,10 +62,6 @@ jobs: - name: Core probe run: node packages/core/test/probe.test.js - # Core: BuilderProgram POC for layer 2 cache invalidation. - - name: Core BuilderProgram POC - run: node packages/core/test/builder-program-poc.test.js - # Core: skip-rules option + hasFixForDiagnostic API (the surface # the CLI cache layer uses). - name: Core skip-rules @@ -83,6 +79,10 @@ jobs: - name: CLI integration run: node packages/cli/test/integration.test.js - # CLI: layer 2 cross-session affected-file diff. - - name: CLI incremental state - run: node packages/cli/test/incremental-state.test.js + # CLI: diagnostic rendering. + - name: CLI render + run: node packages/cli/test/render.test.js + + # Repo dogfood. + - name: Lint + run: pnpm run lint --force From f7d69183b8dd9bbc8cf402bfce9902fb938df3b2 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 22 Oct 2026 18:20:30 +0800 Subject: [PATCH 13/52] docs: describe the TypeScript 7 port in the README and v4 migration guide --- README.md | 147 +++++++------------------------ docs/migration-v4.md | 140 ++++++++++++++++++----------- packages/cli/README.md | 2 +- packages/compat-eslint/README.md | 5 +- packages/config/README.md | 12 +-- packages/core/README.md | 2 +- 6 files changed, 134 insertions(+), 174 deletions(-) diff --git a/README.md b/README.md index 26091a1c..d2ea332e 100644 --- a/README.md +++ b/README.md @@ -11,41 +11,13 @@ Ask DeepWiki

-A linter that runs as a `tsserver` plugin. It reuses the TypeChecker your editor already has — no second process, no AST conversion, no duplicated type-checking. +A linter for TypeScript 7. Rules are plain functions over the TypeScript AST, with the TypeScript 7 checker available for type information. -Zero built-in rules. Rules are plain functions over the TypeScript compiler API. - -## Why? - -ESLint runs in its own process and builds its own type information. On large projects this makes "Auto Fix on Save" slow. - -TSSLint piggybacks on `tsserver`. Diagnostics show up in the same path TypeScript errors do, using the same `Program` instance. - -``` - Traditional TSSLint - ─────────── ─────── - - ┌─────┐ ┌─────┐ - │ IDE │ │ IDE │ - └──┬──┘ └──┬──┘ - │ │ - ┌───┴────┐ ▼ - ▼ ▼ ┌─────────────────┐ - ┌──────┐ ┌──────┐ │ tsserver │ - │ ts- │ │linter│ │ ┌───────────┐ │ - │server│ │ │ │ │TypeChecker│ │ - │ │ │ │ │ └─────┬─────┘ │ - │ Type │ │ Type │ │ │ │ - │ Chk. │ │ Chk. │ │ ┌─────▼─────┐ │ - └──────┘ └──────┘ │ │ TSSLint │ │ - │ └───────────┘ │ - ✗ two type-checkers └─────────────────┘ - two parses ✓ one shared pass -``` +Zero built-in rules. ## How it compares -TSLint (TS-AST, deprecated 2019) → ESLint took over via `typescript-eslint` → TSSLint revives the in-process TS-AST approach as a `tsserver` plugin (2023). +TSLint (TS-AST, deprecated 2019) → ESLint took over via `typescript-eslint` → TSSLint revives the TS-AST approach (2023), on the TypeScript 7 API since v4. ``` 2013 2019 2023 @@ -57,24 +29,24 @@ TSLint (TS-AST, deprecated 2019) → ESLint took over via `typescript-eslint` ESLint ─────●━━━━━━━━━━━━━━━━╲━━━━━━━━━━━━━━━━━━━▶ (active) ╲ │ ╲ │ - TSSLint ╲────────────●━━▶ (tsserver plugin, - revives TS-AST) + TSSLint ╲────────────●━━▶ (revives TS-AST, + TypeScript 7 since v4) ``` | | ESLint | TSLint | Oxlint | TSSLint | |---|---|---|---|---| -| Runtime | Node, separate process | Node, separate process | Rust, separate process | Node, in `tsserver` | +| Runtime | Node, separate process | Node, separate process | Rust, separate process | Node + TypeScript 7 | | AST | ESTree | TS AST | Native Rust AST | TS AST | -| Type-aware rules | Yes (its own `Program`) | Yes (its own `Program`) | Yes (via `tsgolint`, alpha) | Yes (shared `TypeChecker`) | -| Built-in rules | Many | Deprecated | Subset of ESLint (+ JS plugins, alpha) | Zero (imports ESLint / TSLint / TSL) | +| Type-aware rules | Yes (its own `Program`) | Yes (its own `Program`) | Yes (via `tsgolint`, alpha) | Yes (TypeScript 7 checker) | +| Built-in rules | Many | Deprecated | Subset of ESLint (+ JS plugins, alpha) | Zero (imports ESLint rules) | | Status | Active standard | Deprecated 2019 | Active | Active | -**Pick by need.** Largest ecosystem → ESLint. Fastest standalone runtime → Oxlint. Type-aware without duplicate type-checking → TSSLint. +**Pick by need.** Largest ecosystem → ESLint. Fastest standalone runtime → Oxlint. Rules on the TypeScript 7 AST and checker → TSSLint. ## Setup ```bash -npm install @tsslint/config --save-dev +npm install @tsslint/cli @tsslint/config typescript@^7.1.0-beta --save-dev ``` `tsslint.config.ts`: @@ -89,40 +61,28 @@ export default defineConfig({ }); ``` -**VSCode**: install [the extension](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.vscode-tsslint). - -**Other editors**: install the plugin and register it in `tsconfig.json`: +Then run `npx tsslint --project tsconfig.json` (see [CLI](#cli)). -```bash -npm install @tsslint/typescript-plugin --save-dev -``` - -```json -{ - "compilerOptions": { - "plugins": [{ "name": "@tsslint/typescript-plugin" }] - } -} -``` +There is no editor integration in v4: TypeScript 7 has no tsserver plugin API. ## Writing rules -A rule is a function. It receives the TypeScript module, the current `Program`, the `SourceFile`, and a `report()` callback. +A rule is a function. It receives the `typescript/ast` module, the current `Program` and `Checker` (from `typescript/sync`), the `SourceFile`, and a `report()` callback. ```ts import { defineRule } from '@tsslint/config'; export default defineRule(({ typescript: ts, file, report }) => { - ts.forEachChild(file, function visit(node) { + file.forEachChild(function visit(node): void { if (node.kind === ts.SyntaxKind.DebuggerStatement) { report('Debugger statement is not allowed.', node.getStart(file), node.getEnd()); } - ts.forEachChild(node, visit); + node.forEachChild(visit); }); }); ``` -Touch `program` only when you need type information — rules that don't are cached aggressively (see [Caching](#caching)). +Touch `program` / `checker` only when you need type information — rules that don't are cached aggressively (see [Caching](#caching)). ### Severity, fixes, refactors @@ -139,7 +99,7 @@ report('No console.', node.getStart(file), node.getEnd()) .withRefactor('Wrap in if (DEBUG)', () => [/* ... */]); ``` -`withFix` runs automatically as a quick fix; `withRefactor` shows up under the editor's refactor menu (user-initiated). +`withFix` is applied by `tsslint --fix`; `withRefactor` registers a user-initiated edit for hosts that expose refactors. ### Real-world example @@ -165,24 +125,24 @@ defineConfig({ Diagnostics are cached on disk under `os.tmpdir()/tsslint-cache/` in two layers, picked per rule: -- **Layer 1** — invalidated by the linted file's mtime. Used for rules that don't read `ctx.program` (purely syntactic). -- **Layer 2** — invalidated by TypeScript's `BuilderProgram` affected-file diff (transitive, includes ambient `.d.ts`). Used for rules that touch `ctx.program`. The first time a rule reads `ctx.program` it's classified type-aware and stays type-aware across sessions. +- **Layer 1** — invalidated by the linted file's mtime. Used for rules that don't read `ctx.program` or `ctx.checker` (purely syntactic). +- **Layer 2** — invalidated by a program fingerprint (compiler options plus the name, mtime, and size of every file in the program, including `.d.ts`). Any change re-runs type-aware rules on every file. Used for rules that touch `ctx.program` or `ctx.checker`. The first time a rule reads either it's classified type-aware and stays type-aware across sessions. A diagnostic whose correctness depends on inputs neither layer tracks — external resources, env vars, sibling files the rule reads directly via `fs` — should opt out per-diagnostic via `.withoutCache()` on the reporter. The diagnostic still surfaces on the current run; it just isn't written to disk, so the next warm hit on this file won't replay it (the rule has to re-run to surface it again). -For diagnostics that depend on cross-file types, prefer reading `ctx.program` once instead — that re-classifies the rule type-aware and layer 2 handles invalidation properly. +For diagnostics that depend on cross-file types, prefer reading `ctx.checker` once instead — that re-classifies the rule type-aware and layer 2 handles invalidation properly. Pass `--force` to the CLI to ignore the cache. `--list-rules` prints each rule's classification (type-aware vs syntactic) after the run. ### Debugging -Every `report()` captures a stack trace. The diagnostic carries a "Related Information" link back to the exact line in your rule that triggered it — ⌘-click in the editor to jump there: +Every `report()` captures a stack trace. The diagnostic carries a "Related Information" entry pointing at the exact line in your rule that triggered it: ``` src/index.ts:3:1 3 │ debugger; │ ~~~~~~~~~ Debugger statement is not allowed. (tsslint) - │ ↳ rules/no-debugger.ts:5:7 ⌘-click to open + │ ↳ rules/no-debugger.ts:5:7 ``` ## CLI @@ -206,35 +166,31 @@ Flags: | `--fix` | Apply fixes | | `--force` | Ignore cache | | `--failures-only` | Only print diagnostics that affect exit code | +| `--list-rules` | Print each rule's classification (syntactic / type-aware) | | `-h`, `--help` | | TSSLint produces diagnostics and edits — it does not format. Run dprint or Prettier after `--fix`. ## Framework support -Since v4, framework files (`.vue`, `.astro`, `.mdx`, `.svelte`, …) are checked through TypeScript 7.1's **content mapper** — the same mechanism `tsc` uses — instead of Volar language plugins. Declare the mappers in `tsconfig.json`; TSSLint picks them up with the project: +Since v4, framework files (`.vue`, `.astro`, `.mdx`, `.svelte`, …) are linted through TypeScript 7.1's **content mapper** — the same mechanism `tsc` uses — instead of Volar language plugins. Declare the mappers in `tsconfig.json`; TSSLint picks them up with the project: ```jsonc { "contentMappers": [ - { "package": "vue-content-mapper", "extensions": [".vue"] } + { "package": "@vue/content-mapper", "extensions": [".vue"] } ] } ``` -Rules run against the transformed TypeScript, and diagnostics and fixes are mapped back to the original file through the mapper's span map. Fixes are only offered on exact (verbatim) spans — the same restriction `tsc` applies to edits. +Rules run against the transformed TypeScript, and diagnostics are mapped back to the original file through the mapper's span map. Diagnostics that only cover generated code are dropped, and `--fix` only applies fixes whose edits all map exactly onto original text. The v3 flags `--vue-project`, `--vue-vine-project`, `--mdx-project`, `--astro-project`, and `--ts-macro-project` are gone; the mapper configuration in `tsconfig.json` replaces them. See [docs/migration-v4.md](docs/migration-v4.md). -> **Availability**: content mapper support activates once `typescript-native-bridge` moves to tsgo 7.1. Until then the CLI checks plain TypeScript files only. - -## Importing ESLint, TSLint, or TSL rules - -### ESLint +## Importing ESLint rules ```bash npm install @tsslint/compat-eslint --save-dev -npm install @typescript-eslint/eslint-plugin --save-dev # for @typescript-eslint/* rules npx tsslint-docgen # generates JSDoc for IDE autocomplete ``` @@ -247,43 +203,13 @@ export default defineConfig({ rules: { ...await importESLintRules({ 'no-unused-vars': true, - '@typescript-eslint/no-explicit-any': 'warn', + 'eqeqeq': 'warn', }), }, }); ``` -### TSLint - -```bash -npm install tslint --save-dev # required for built-in rules -npx tsslint-docgen -``` - -```ts -import { defineConfig, importTSLintRules } from '@tsslint/config'; - -export default defineConfig({ - rules: await importTSLintRules({ - 'no-console': true, - }), -}); -``` - -### TSL - -```bash -npm install tsl --save-dev -``` - -```ts -import { defineConfig, fromTSLRules } from '@tsslint/config'; -import { core } from 'tsl'; - -export default defineConfig({ - rules: fromTSLRules(core.all()), -}); -``` +`@typescript-eslint/eslint-plugin` does not load with `typescript@7` yet (its dependencies read the classic `typescript` API at load time), so `@typescript-eslint/*` rules are unavailable until typescript-eslint supports TypeScript 7. ## Plugins @@ -295,9 +221,8 @@ import { createIgnorePlugin, createCategoryPlugin, createDiagnosticsPlugin, - isCLI, } from '@tsslint/config'; -import ts from 'typescript'; +import { DiagnosticCategory } from 'typescript/sync'; export default defineConfig({ rules: { /* ... */ }, @@ -307,13 +232,11 @@ export default defineConfig({ // Override severity by rule-id pattern createCategoryPlugin({ - 'style/*': ts.DiagnosticCategory.Warning, + 'style/*': DiagnosticCategory.Warning, }), // Forward TypeScript's own diagnostics through the same pipeline. - // Guard with `isCLI()` — tsserver already surfaces these in editors, - // so emitting them again from the plugin would double-report there. - ...(isCLI() ? [createDiagnosticsPlugin('semantic')] : []), + createDiagnosticsPlugin('semantic'), ], }); ``` @@ -322,10 +245,8 @@ Build your own with the `Plugin` type from `@tsslint/types`. ## Requirements -- Node.js **22.6.0+** (uses `--experimental-strip-types` to load `tsslint.config.ts` directly — no transpile step) -- The CLI bundles its own TypeScript engine ([`typescript-native-bridge`](https://github.com/johnsoncodehk/typescript-native-bridge), TS 7 / tsgo) — you no longer install or pin `typescript` to lint -- Not supported on musl-based systems (Alpine) — the bundled engine links a glibc native binary -- `@tsslint/typescript-plugin` still runs against a classic TypeScript tsserver; the TS 7 language server path is being rebuilt (see [docs/migration-v4.md](docs/migration-v4.md)) +- Node.js **22.12.0+** (`tsslint.config.ts` is loaded with `--experimental-strip-types` — no transpile step) +- `typescript@^7.1.0-beta` (peer dependency). TypeScript 5/6 are not supported; see [docs/migration-v4.md](docs/migration-v4.md) ## License diff --git a/docs/migration-v4.md b/docs/migration-v4.md index f621d463..e93bd3e0 100644 --- a/docs/migration-v4.md +++ b/docs/migration-v4.md @@ -1,91 +1,131 @@ # Migrating to TSSLint v4 -v4 moves the lint engine onto TypeScript 7 and replaces the Volar-based -framework support with TypeScript's own content mapper. +v4 runs on TypeScript 7 only. Rules see the TypeScript 7 AST (`typescript/ast`) +and the TypeScript 7 checker (`typescript/sync`), and framework files are linted +through TypeScript's own content mapper instead of Volar language plugins. ## Breaking changes -### 1. The CLI bundles its own TypeScript engine +### 1. TypeScript 7 is a peer dependency -`@tsslint/cli` no longer declares `typescript` as a peer dependency. It depends -on [`typescript-native-bridge`](https://github.com/johnsoncodehk/typescript-native-bridge) -(exact-pinned), which keeps the classic `typescript` API surface but runs the -TS 7 (tsgo) checker in-process. +Install `typescript@^7.1.0-beta` next to TSSLint. There is no TypeScript 5/6 +code path and no compatibility layer. -- You no longer need to install or pin `typescript` for linting, and package - manager overrides that redirected `typescript` to the bridge are no longer - required for TSSLint itself. -- Diagnostics come from the tsgo checker. On a real project the output can - differ from TS 6.x in edge cases — that difference is the engine, not a - TSSLint regression. -- The bridge does not support custom module resolvers or JS custom transformers. - If your setup remaps imports to different files on disk, those remappings are - not honoured. +- Node.js **22.12.0+** is required (the `typescript` 7 package needs it). +- Diagnostics come from the TypeScript 7 checker. Output can differ from + TypeScript 5/6 in edge cases; that difference is the engine, not a TSSLint + regression. -### 2. Framework flags are gone +### 2. Rule context -The Volar integration — and with it the five project flags — has been removed: +| v3 | v4 | +|---|---| +| `typescript` — the classic `typescript` module | `typescript` — the `typescript/ast` module (`SyntaxKind`, `is*` guards, AST utilities) | +| `program` — `ts.Program` | `program` — `Program` from `typescript/sync` | +| `program.getTypeChecker()` | `checker` — `Checker` from `typescript/sync` | +| `ts.forEachChild(node, cb)` | `node.forEachChild(cb)` | +| `ts.sys`, `ts.findConfigFile`, … | use `node:fs` / `node:path` | + +```ts +import { defineRule } from '@tsslint/config'; + +export default defineRule(({ typescript: ts, file, report }) => { + file.forEachChild(function visit(node): void { + if (node.kind === ts.SyntaxKind.DebuggerStatement) { + report('Debugger statement is not allowed.', node.getStart(file), node.getEnd()); + } + node.forEachChild(visit); + }); +}); +``` + +Reading `program` or `checker` classifies a rule as type-aware (see +[Caching](#5-caching)). + +`Diagnostic`, `CodeFixAction`, `FileTextChanges`, and the other result types are +now defined in `@tsslint/types`. A TSSLint diagnostic's `code` is the rule id. + +### 3. Removed packages and importers + +- `@tsslint/typescript-plugin` and the VS Code extension are removed. TypeScript 7 + has no tsserver plugin API. +- `importTSLintRules` and `fromTSLRules` are removed. Both depend on the classic + `typescript` API. + +### 4. Framework flags are replaced by `contentMappers` | v3 | v4 | |---|---| | `tsslint --project tsconfig.json --vue-project tsconfig.json` | declare `contentMappers` in `tsconfig.json`, then `tsslint --project tsconfig.json` | | `--vue-vine-project`, `--mdx-project`, `--astro-project`, `--ts-macro-project` | same — mapper entries in `tsconfig.json` | -There is no replacement flag; the tsconfig is the single source of truth. +See [Framework projects](#framework-projects). + +### 5. Caching -### 3. Platform requirements +The cache format is bumped, so the first v4 run re-lints everything. -- Node.js **22.6.0+** (unchanged) — the bridge itself needs ≥ 20.19. -- **musl-based systems (Alpine) are not supported.** The bridge links a glibc - native binary; run the lint step in a glibc image and copy results into your - Alpine deploy stage. -- Peak memory on checker-heavy runs rises to roughly 1.9× the previous engine. +Layer 2 (type-aware rules) is now keyed on a program fingerprint: compiler +options plus every source file's name, mtime, and size. Any change to a file in +the program re-runs type-aware rules on every file. v3 used TypeScript's +`BuilderProgram` affected-file diff, which TypeScript 7 does not expose. + +### 6. ESLint interop + +`importESLintRules` still works for ESLint core rules and for plugins that +don't import `typescript`. + +`@typescript-eslint/eslint-plugin` does not load with `typescript@7`: its +dependencies read the classic `typescript` API at module load. Remove +`@typescript-eslint/*` rules from the config until typescript-eslint supports +TypeScript 7. + +### 7. `@tsslint/core` API + +Only relevant if you embed the linter directly: + +- `createLinter(ctx, rootDir, config, …)` takes + `ctx: { typescript: typeof import('typescript/ast'), getProgram(): Program }`. +- `linter.lint(target, …)` takes `target: { program, checker, file }` instead of a + file name. +- `applyTextChanges(text, changes)` works on strings. ## Framework projects -Framework files are checked through TypeScript 7.1's content mapper. Add the -mapper package for your framework to `tsconfig.json`: +Framework files are linted through TypeScript 7.1's content mapper. Add the +mapper for your framework to `tsconfig.json`: ```jsonc { "compilerOptions": { }, "contentMappers": [ - { "package": "vue-content-mapper", "extensions": [".vue"], "options": { } } + { "package": "@vue/content-mapper", "extensions": [".vue"], "options": { } } ], "include": ["src"] } ``` -The mapper package declares how it runs in its own `package.json`: +`package` is resolved through `node_modules`. The mapper package declares how it +runs in its own `package.json`: ```jsonc { "typescript": { "contentMapper": { - "exec": ["node", "dist/server.js"], - "compilerOptions": ["module", "jsx", "jsxImportSource"] + "exec": ["node", "server.js"], + "dynamicConfig": true } } } ``` -Notes that affect what you can lint: - -- Rules run on the transformed TypeScript. Diagnostics and fixes are mapped back - to the original file through the mapper's span map; **fixes are only offered - on exact (verbatim) spans**, matching the edit-safety rule `tsc` applies. -- The protocol has no per-diagnostic-code filtering and does not drop diagnostics - on unmapped (synthetic) regions — both are deliberate design choices upstream. -- `tsc` requires `--runExternalCode` to execute mappers; TSSLint runs them as - part of loading the project. - -**Availability**: content mapper support activates once `typescript-native-bridge` -rebases onto tsgo 7.1. Until then the CLI checks plain TypeScript files only. -Mapper packages per framework follow their own ecosystems' adoption. - -## Editors +What this means for rules: -`@tsslint/typescript-plugin` still targets a classic TypeScript tsserver and is -unaffected by the CLI change. The TS 7 language server path (LSP middleware plus -content mapper registration) is being rebuilt on the TS 7.1 APIs; until that -lands, editor diagnostics for framework files keep using the previous setup. +- Rules run on the transformed TypeScript (`file.text`); `file.fileName` is the + original file. Report positions in the transformed text. +- Diagnostics are mapped back to the original file through the mapper's span map. + Diagnostics that only cover generated code are dropped. +- `--fix` applies a fix only when every edit in it maps exactly onto original + text. Other fixes are skipped. +- TSSLint runs mappers as part of loading the project; there is no equivalent of + `tsc --runExternalCode` to pass. diff --git a/packages/cli/README.md b/packages/cli/README.md index 0b25fd85..b68956b4 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -5,7 +5,7 @@ Command-line runner for TSSLint. Lints TypeScript projects in CI or from the ter ## Usage ```bash -npm install @tsslint/cli --save-dev +npm install @tsslint/cli typescript@^7.1.0-beta --save-dev ``` ```bash diff --git a/packages/compat-eslint/README.md b/packages/compat-eslint/README.md index 708cf6fc..2b7a8fd9 100644 --- a/packages/compat-eslint/README.md +++ b/packages/compat-eslint/README.md @@ -6,7 +6,6 @@ Adapter that lets ESLint rules run under TSSLint. Consumed by `importESLintRules ```bash npm install @tsslint/compat-eslint --save-dev -npm install @typescript-eslint/eslint-plugin --save-dev # for @typescript-eslint/* rules ``` ```ts @@ -16,7 +15,7 @@ export default defineConfig({ rules: { ...await importESLintRules({ 'no-unused-vars': true, - '@typescript-eslint/no-explicit-any': 'warn', + 'eqeqeq': 'warn', }), }, }); @@ -24,7 +23,7 @@ export default defineConfig({ For each `/` you reference, install the matching `eslint-plugin-` (or `@scope/eslint-plugin`) package. -See the [root README](../../README.md) for full interop notes, including TSLint and TSL. +`@typescript-eslint/eslint-plugin` does not load with `typescript@7` yet. See the [root README](../../README.md) for full interop notes. --- diff --git a/packages/config/README.md b/packages/config/README.md index d154599f..c545fc6a 100644 --- a/packages/config/README.md +++ b/packages/config/README.md @@ -1,11 +1,11 @@ # @tsslint/config -Public API for `tsslint.config.ts`: `defineConfig`, `defineRule`, `definePlugin`, bundled plugin factories (`createIgnorePlugin`, `createCategoryPlugin`, `createDiagnosticsPlugin`), and importers for ESLint / TSLint / TSL rules. +Public API for `tsslint.config.ts`: `defineConfig`, `defineRule`, `definePlugin`, bundled plugin factories (`createIgnorePlugin`, `createCategoryPlugin`, `createDiagnosticsPlugin`), and `importESLintRules`. ## Usage ```bash -npm install @tsslint/config --save-dev +npm install @tsslint/config typescript@^7.1.0-beta --save-dev ``` ```ts @@ -14,11 +14,11 @@ import { defineConfig } from '@tsslint/config'; export default defineConfig({ rules: { 'no-debugger': ({ typescript: ts, file, report }) => { - ts.forEachChild(file, function visit(node) { + file.forEachChild(function visit(node): void { if (node.kind === ts.SyntaxKind.DebuggerStatement) { report('Debugger statement is not allowed.', node.getStart(file), node.getEnd()); } - ts.forEachChild(node, visit); + node.forEachChild(visit); }); }, }, @@ -27,6 +27,6 @@ export default defineConfig({ `defineRule` / `definePlugin` are available for authoring rules or plugins in their own files, where the context type can't be inferred from the surrounding config. -The package also ships a `tsslint-docgen` binary that generates JSDoc for imported ESLint/TSLint rules so they autocomplete in your config. +The package also ships a `tsslint-docgen` binary that generates JSDoc for imported ESLint rules so they autocomplete in your config. -See the [root README](../../README.md) for rule authoring, ESLint/TSLint/TSL interop, plugins, and caching. +See the [root README](../../README.md) for rule authoring, ESLint interop, plugins, and caching. diff --git a/packages/core/README.md b/packages/core/README.md index 9c25bfb5..104fd8fc 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -2,6 +2,6 @@ The linting engine. Runs rules over TypeScript source files, manages the on-disk cache, and produces diagnostics. -End users don't install this directly — it's a runtime dependency of [`@tsslint/typescript-plugin`](../typescript-plugin) (editor integration) and [`@tsslint/cli`](../cli) (batch linting), so both share one implementation. +End users don't install this directly — it's a runtime dependency of [`@tsslint/cli`](../cli). See the [root README](../../README.md) for TSSLint itself. From ae10369610129b636648ea2313ede203869e24c5 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 22 Oct 2026 19:47:50 +0800 Subject: [PATCH 14/52] v4.0.0-alpha.0 Record the version published from f7d6918 by release run 39772141598, so the next `prerelease` release is 4.0.0-alpha.1. Only workspace specifiers change in pnpm-lock.yaml. --- fixtures/compat-eslint/package.json | 6 +++--- lerna.json | 2 +- package.json | 2 +- packages/cli/package.json | 8 ++++---- packages/compat-eslint/package.json | 4 ++-- packages/config/package.json | 6 +++--- packages/core/package.json | 4 ++-- packages/types/package.json | 2 +- pnpm-lock.yaml | 18 +++++++++--------- tools/estree-oracle/package.json | 2 +- 10 files changed, 27 insertions(+), 27 deletions(-) diff --git a/fixtures/compat-eslint/package.json b/fixtures/compat-eslint/package.json index b112bb6d..9d894260 100644 --- a/fixtures/compat-eslint/package.json +++ b/fixtures/compat-eslint/package.json @@ -1,9 +1,9 @@ { "private": true, "name": "@tsslint-fixtures/compat-eslint", - "version": "3.1.4", + "version": "4.0.0-alpha.0", "devDependencies": { - "@tsslint/compat-eslint": "3.1.4", - "@tsslint/config": "3.1.4" + "@tsslint/compat-eslint": "4.0.0-alpha.0", + "@tsslint/config": "4.0.0-alpha.0" } } diff --git a/lerna.json b/lerna.json index 48c46e3c..d6a09a97 100644 --- a/lerna.json +++ b/lerna.json @@ -7,5 +7,5 @@ "fixtures/*", "tools/*" ], - "version": "3.1.4" + "version": "4.0.0-alpha.0" } diff --git a/package.json b/package.json index f46298bb..c91dff67 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "3.1.4", + "version": "4.0.0-alpha.0", "packageManager": "pnpm@9.3.0", "scripts": { "test": "pnpm -F @tsslint/compat-eslint bench", diff --git a/packages/cli/package.json b/packages/cli/package.json index 85ecc86b..817b6848 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@tsslint/cli", - "version": "3.1.4", + "version": "4.0.0-alpha.0", "license": "MIT", "engines": { "node": ">=22.12.0" @@ -21,9 +21,9 @@ "directory": "packages/cli" }, "dependencies": { - "@tsslint/config": "3.1.4", - "@tsslint/core": "3.1.4", - "@tsslint/types": "3.1.4", + "@tsslint/config": "4.0.0-alpha.0", + "@tsslint/core": "4.0.0-alpha.0", + "@tsslint/types": "4.0.0-alpha.0", "minimatch": "^10.0.1" }, "peerDependencies": { diff --git a/packages/compat-eslint/package.json b/packages/compat-eslint/package.json index d42f111e..05341220 100644 --- a/packages/compat-eslint/package.json +++ b/packages/compat-eslint/package.json @@ -1,6 +1,6 @@ { "name": "@tsslint/compat-eslint", - "version": "3.1.4", + "version": "4.0.0-alpha.0", "license": "MIT", "files": [ "index.js", @@ -28,7 +28,7 @@ "typescript": "^7.1.0-beta" }, "dependencies": { - "@tsslint/types": "3.1.4", + "@tsslint/types": "4.0.0-alpha.0", "@typescript-eslint/scope-manager": "^8.72.0", "esquery": "^1.5.0" } diff --git a/packages/config/package.json b/packages/config/package.json index b3903476..03f8291c 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -1,6 +1,6 @@ { "name": "@tsslint/config", - "version": "3.1.4", + "version": "4.0.0-alpha.0", "license": "MIT", "engines": { "node": ">=22.12.0" @@ -18,11 +18,11 @@ "directory": "packages/config" }, "dependencies": { - "@tsslint/types": "3.1.4", + "@tsslint/types": "4.0.0-alpha.0", "minimatch": "^10.0.1" }, "devDependencies": { - "@tsslint/compat-eslint": "3.1.4", + "@tsslint/compat-eslint": "4.0.0-alpha.0", "typescript": "7.1.0-beta" }, "peerDependencies": { diff --git a/packages/core/package.json b/packages/core/package.json index 27572235..bfe611f7 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@tsslint/core", - "version": "3.1.4", + "version": "4.0.0-alpha.0", "license": "MIT", "engines": { "node": ">=22.12.0" @@ -15,7 +15,7 @@ "directory": "packages/core" }, "dependencies": { - "@tsslint/types": "3.1.4", + "@tsslint/types": "4.0.0-alpha.0", "minimatch": "^10.0.1" }, "peerDependencies": { diff --git a/packages/types/package.json b/packages/types/package.json index 58708219..53caa413 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@tsslint/types", - "version": "3.1.4", + "version": "4.0.0-alpha.0", "license": "MIT", "files": [ "**/*.js", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 92bd7d99..b907ec1f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -27,22 +27,22 @@ importers: fixtures/compat-eslint: devDependencies: '@tsslint/compat-eslint': - specifier: 3.1.4 + specifier: 4.0.0-alpha.0 version: link:../../packages/compat-eslint '@tsslint/config': - specifier: 3.1.4 + specifier: 4.0.0-alpha.0 version: link:../../packages/config packages/cli: dependencies: '@tsslint/config': - specifier: 3.1.4 + specifier: 4.0.0-alpha.0 version: link:../config '@tsslint/core': - specifier: 3.1.4 + specifier: 4.0.0-alpha.0 version: link:../core '@tsslint/types': - specifier: 3.1.4 + specifier: 4.0.0-alpha.0 version: link:../types minimatch: specifier: ^10.0.1 @@ -55,7 +55,7 @@ importers: packages/compat-eslint: dependencies: '@tsslint/types': - specifier: 3.1.4 + specifier: 4.0.0-alpha.0 version: link:../types '@typescript-eslint/scope-manager': specifier: ^8.72.0 @@ -80,14 +80,14 @@ importers: packages/config: dependencies: '@tsslint/types': - specifier: 3.1.4 + specifier: 4.0.0-alpha.0 version: link:../types minimatch: specifier: ^10.0.1 version: 10.2.5 devDependencies: '@tsslint/compat-eslint': - specifier: 3.1.4 + specifier: 4.0.0-alpha.0 version: link:../compat-eslint typescript: specifier: 7.1.0-beta @@ -96,7 +96,7 @@ importers: packages/core: dependencies: '@tsslint/types': - specifier: 3.1.4 + specifier: 4.0.0-alpha.0 version: link:../types minimatch: specifier: ^10.0.1 diff --git a/tools/estree-oracle/package.json b/tools/estree-oracle/package.json index 003df58c..69cc7634 100644 --- a/tools/estree-oracle/package.json +++ b/tools/estree-oracle/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@tsslint/estree-oracle", - "version": "3.1.4", + "version": "4.0.0-alpha.0", "main": "index.js", "dependencies": { "@typescript-eslint/parser": "^8.72.0", From 9ae2792c27d9bcf4ca245314890674332a180cb4 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 22 Oct 2026 20:00:37 +0800 Subject: [PATCH 15/52] fix(packages): depend on typescript 7 directly and declare eslint peers - Every package that loads `typescript/sync` / `typescript/ast` now depends on typescript 7.1.0-beta instead of peering on it. TSSLint's checker is its own engine; as a peer, pnpm resolved it to a host project's TS 6 and `require('typescript/sync')` failed. - @tsslint/config and @tsslint/compat-eslint declare an optional eslint peer (>=9.39.0 <10.0.0). importESLintRules() loads core rules from `node_modules/eslint/lib/rules`, and both packages expose ESLint types. - @tsslint/config peers on @tsslint/compat-eslint via `workspace:*`, so the published range always matches the release (it was `^3.0.0`). --- packages/cli/package.json | 7 +------ packages/compat-eslint/package.json | 13 +++++++++---- packages/config/package.json | 12 ++++++++---- packages/core/package.json | 7 +------ packages/types/package.json | 5 +---- pnpm-lock.yaml | 19 ++++++++++--------- 6 files changed, 30 insertions(+), 33 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 817b6848..9b94ae43 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -24,12 +24,7 @@ "@tsslint/config": "4.0.0-alpha.0", "@tsslint/core": "4.0.0-alpha.0", "@tsslint/types": "4.0.0-alpha.0", - "minimatch": "^10.0.1" - }, - "peerDependencies": { - "typescript": "^7.1.0-beta" - }, - "devDependencies": { + "minimatch": "^10.0.1", "typescript": "7.1.0-beta" } } diff --git a/packages/compat-eslint/package.json b/packages/compat-eslint/package.json index 05341220..701c9fe0 100644 --- a/packages/compat-eslint/package.json +++ b/packages/compat-eslint/package.json @@ -21,15 +21,20 @@ "devDependencies": { "@tsslint/estree-oracle": "workspace:*", "@types/eslint": "^8.56.10", - "eslint": ">=9.39.0 <10.0.0", - "typescript": "7.1.0-beta" + "eslint": ">=9.39.0 <10.0.0" }, "peerDependencies": { - "typescript": "^7.1.0-beta" + "eslint": ">=9.39.0 <10.0.0" }, "dependencies": { "@tsslint/types": "4.0.0-alpha.0", "@typescript-eslint/scope-manager": "^8.72.0", - "esquery": "^1.5.0" + "esquery": "^1.5.0", + "typescript": "7.1.0-beta" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } } } diff --git a/packages/config/package.json b/packages/config/package.json index 03f8291c..e219ee88 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -19,19 +19,23 @@ }, "dependencies": { "@tsslint/types": "4.0.0-alpha.0", - "minimatch": "^10.0.1" + "minimatch": "^10.0.1", + "typescript": "7.1.0-beta" }, "devDependencies": { "@tsslint/compat-eslint": "4.0.0-alpha.0", - "typescript": "7.1.0-beta" + "eslint": ">=9.39.0 <10.0.0" }, "peerDependencies": { - "@tsslint/compat-eslint": "^3.0.0", - "typescript": "^7.1.0-beta" + "@tsslint/compat-eslint": "workspace:*", + "eslint": ">=9.39.0 <10.0.0" }, "peerDependenciesMeta": { "@tsslint/compat-eslint": { "optional": true + }, + "eslint": { + "optional": true } } } diff --git a/packages/core/package.json b/packages/core/package.json index bfe611f7..861f58a9 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -16,12 +16,7 @@ }, "dependencies": { "@tsslint/types": "4.0.0-alpha.0", - "minimatch": "^10.0.1" - }, - "peerDependencies": { - "typescript": "^7.1.0-beta" - }, - "devDependencies": { + "minimatch": "^10.0.1", "typescript": "7.1.0-beta" } } diff --git a/packages/types/package.json b/packages/types/package.json index 53caa413..ddd69c39 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -11,10 +11,7 @@ "url": "https://github.com/johnsoncodehk/tsslint.git", "directory": "packages/types" }, - "peerDependencies": { - "typescript": "^7.1.0-beta" - }, - "devDependencies": { + "dependencies": { "typescript": "7.1.0-beta" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b907ec1f..4683df3b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,7 +47,6 @@ importers: minimatch: specifier: ^10.0.1 version: 10.2.5 - devDependencies: typescript: specifier: 7.1.0-beta version: 7.1.0-beta @@ -63,6 +62,9 @@ importers: esquery: specifier: ^1.5.0 version: 1.7.0 + typescript: + specifier: 7.1.0-beta + version: 7.1.0-beta devDependencies: '@tsslint/estree-oracle': specifier: workspace:* @@ -73,9 +75,6 @@ importers: eslint: specifier: '>=9.39.0 <10.0.0' version: 9.39.4(jiti@2.6.1) - typescript: - specifier: 7.1.0-beta - version: 7.1.0-beta packages/config: dependencies: @@ -85,13 +84,16 @@ importers: minimatch: specifier: ^10.0.1 version: 10.2.5 + typescript: + specifier: 7.1.0-beta + version: 7.1.0-beta devDependencies: '@tsslint/compat-eslint': specifier: 4.0.0-alpha.0 version: link:../compat-eslint - typescript: - specifier: 7.1.0-beta - version: 7.1.0-beta + eslint: + specifier: '>=9.39.0 <10.0.0' + version: 9.39.4(jiti@2.6.1) packages/core: dependencies: @@ -101,13 +103,12 @@ importers: minimatch: specifier: ^10.0.1 version: 10.2.5 - devDependencies: typescript: specifier: 7.1.0-beta version: 7.1.0-beta packages/types: - devDependencies: + dependencies: typescript: specifier: 7.1.0-beta version: 7.1.0-beta From b82aa036e930db861dc8c276446a77cfb2cf88be Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 22 Oct 2026 12:04:03 +0000 Subject: [PATCH 16/52] v4.0.0-alpha.1 --- fixtures/compat-eslint/package.json | 6 +- lerna.json | 2 +- package.json | 2 +- packages/cli/package.json | 8 +- packages/compat-eslint/package.json | 4 +- packages/config/package.json | 6 +- packages/core/package.json | 4 +- packages/types/package.json | 2 +- pnpm-lock.yaml | 728 +++++++++++++++------------- tools/estree-oracle/package.json | 2 +- 10 files changed, 404 insertions(+), 360 deletions(-) diff --git a/fixtures/compat-eslint/package.json b/fixtures/compat-eslint/package.json index 9d894260..aa73c080 100644 --- a/fixtures/compat-eslint/package.json +++ b/fixtures/compat-eslint/package.json @@ -1,9 +1,9 @@ { "private": true, "name": "@tsslint-fixtures/compat-eslint", - "version": "4.0.0-alpha.0", + "version": "4.0.0-alpha.1", "devDependencies": { - "@tsslint/compat-eslint": "4.0.0-alpha.0", - "@tsslint/config": "4.0.0-alpha.0" + "@tsslint/compat-eslint": "4.0.0-alpha.1", + "@tsslint/config": "4.0.0-alpha.1" } } diff --git a/lerna.json b/lerna.json index d6a09a97..67d29c43 100644 --- a/lerna.json +++ b/lerna.json @@ -7,5 +7,5 @@ "fixtures/*", "tools/*" ], - "version": "4.0.0-alpha.0" + "version": "4.0.0-alpha.1" } diff --git a/package.json b/package.json index c91dff67..5442e9bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "4.0.0-alpha.0", + "version": "4.0.0-alpha.1", "packageManager": "pnpm@9.3.0", "scripts": { "test": "pnpm -F @tsslint/compat-eslint bench", diff --git a/packages/cli/package.json b/packages/cli/package.json index 9b94ae43..297419d6 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@tsslint/cli", - "version": "4.0.0-alpha.0", + "version": "4.0.0-alpha.1", "license": "MIT", "engines": { "node": ">=22.12.0" @@ -21,9 +21,9 @@ "directory": "packages/cli" }, "dependencies": { - "@tsslint/config": "4.0.0-alpha.0", - "@tsslint/core": "4.0.0-alpha.0", - "@tsslint/types": "4.0.0-alpha.0", + "@tsslint/config": "4.0.0-alpha.1", + "@tsslint/core": "4.0.0-alpha.1", + "@tsslint/types": "4.0.0-alpha.1", "minimatch": "^10.0.1", "typescript": "7.1.0-beta" } diff --git a/packages/compat-eslint/package.json b/packages/compat-eslint/package.json index 701c9fe0..b6c80ac7 100644 --- a/packages/compat-eslint/package.json +++ b/packages/compat-eslint/package.json @@ -1,6 +1,6 @@ { "name": "@tsslint/compat-eslint", - "version": "4.0.0-alpha.0", + "version": "4.0.0-alpha.1", "license": "MIT", "files": [ "index.js", @@ -27,7 +27,7 @@ "eslint": ">=9.39.0 <10.0.0" }, "dependencies": { - "@tsslint/types": "4.0.0-alpha.0", + "@tsslint/types": "4.0.0-alpha.1", "@typescript-eslint/scope-manager": "^8.72.0", "esquery": "^1.5.0", "typescript": "7.1.0-beta" diff --git a/packages/config/package.json b/packages/config/package.json index e219ee88..ef0e52dc 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -1,6 +1,6 @@ { "name": "@tsslint/config", - "version": "4.0.0-alpha.0", + "version": "4.0.0-alpha.1", "license": "MIT", "engines": { "node": ">=22.12.0" @@ -18,12 +18,12 @@ "directory": "packages/config" }, "dependencies": { - "@tsslint/types": "4.0.0-alpha.0", + "@tsslint/types": "4.0.0-alpha.1", "minimatch": "^10.0.1", "typescript": "7.1.0-beta" }, "devDependencies": { - "@tsslint/compat-eslint": "4.0.0-alpha.0", + "@tsslint/compat-eslint": "workspace:*", "eslint": ">=9.39.0 <10.0.0" }, "peerDependencies": { diff --git a/packages/core/package.json b/packages/core/package.json index 861f58a9..4ae5527b 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@tsslint/core", - "version": "4.0.0-alpha.0", + "version": "4.0.0-alpha.1", "license": "MIT", "engines": { "node": ">=22.12.0" @@ -15,7 +15,7 @@ "directory": "packages/core" }, "dependencies": { - "@tsslint/types": "4.0.0-alpha.0", + "@tsslint/types": "4.0.0-alpha.1", "minimatch": "^10.0.1", "typescript": "7.1.0-beta" } diff --git a/packages/types/package.json b/packages/types/package.json index ddd69c39..3f09337e 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@tsslint/types", - "version": "4.0.0-alpha.0", + "version": "4.0.0-alpha.1", "license": "MIT", "files": [ "**/*.js", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4683df3b..77ceec48 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,16 +10,16 @@ importers: devDependencies: '@lerna-lite/cli': specifier: latest - version: 5.3.0(@lerna-lite/publish@5.3.0(@types/node@25.9.3)(conventional-commits-filter@5.0.0))(@lerna-lite/version@5.3.0(@lerna-lite/publish@5.3.0(@types/node@25.9.3)(conventional-commits-filter@5.0.0))(@types/node@25.9.3)(conventional-commits-filter@5.0.0))(@types/node@25.9.3) + version: 5.6.4(@lerna-lite/publish@5.6.4(@types/node@26.6.5)(conventional-commits-filter@6.0.1))(@lerna-lite/version@5.6.4(@lerna-lite/publish@5.6.4(@types/node@26.6.5)(conventional-commits-filter@6.0.1))(@types/node@26.6.5)(conventional-commits-filter@6.0.1))(@types/node@26.6.5) '@lerna-lite/publish': specifier: latest - version: 5.3.0(@types/node@25.9.3)(conventional-commits-filter@5.0.0) + version: 5.6.4(@types/node@26.6.5)(conventional-commits-filter@6.0.1) '@types/node': specifier: latest - version: 25.9.3 + version: 26.6.5 dprint: specifier: latest - version: 0.54.0 + version: 0.64.1 typescript: specifier: 7.1.0-beta version: 7.1.0-beta @@ -27,22 +27,22 @@ importers: fixtures/compat-eslint: devDependencies: '@tsslint/compat-eslint': - specifier: 4.0.0-alpha.0 + specifier: 4.0.0-alpha.1 version: link:../../packages/compat-eslint '@tsslint/config': - specifier: 4.0.0-alpha.0 + specifier: 4.0.0-alpha.1 version: link:../../packages/config packages/cli: dependencies: '@tsslint/config': - specifier: 4.0.0-alpha.0 + specifier: 4.0.0-alpha.1 version: link:../config '@tsslint/core': - specifier: 4.0.0-alpha.0 + specifier: 4.0.0-alpha.1 version: link:../core '@tsslint/types': - specifier: 4.0.0-alpha.0 + specifier: 4.0.0-alpha.1 version: link:../types minimatch: specifier: ^10.0.1 @@ -54,7 +54,7 @@ importers: packages/compat-eslint: dependencies: '@tsslint/types': - specifier: 4.0.0-alpha.0 + specifier: 4.0.0-alpha.1 version: link:../types '@typescript-eslint/scope-manager': specifier: ^8.72.0 @@ -79,7 +79,7 @@ importers: packages/config: dependencies: '@tsslint/types': - specifier: 4.0.0-alpha.0 + specifier: 4.0.0-alpha.1 version: link:../types minimatch: specifier: ^10.0.1 @@ -89,7 +89,7 @@ importers: version: 7.1.0-beta devDependencies: '@tsslint/compat-eslint': - specifier: 4.0.0-alpha.0 + specifier: workspace:* version: link:../compat-eslint eslint: specifier: '>=9.39.0 <10.0.0' @@ -98,7 +98,7 @@ importers: packages/core: dependencies: '@tsslint/types': - specifier: 4.0.0-alpha.0 + specifier: 4.0.0-alpha.1 version: link:../types minimatch: specifier: ^10.0.1 @@ -133,70 +133,114 @@ importers: packages: - '@conventional-changelog/git-client@2.7.0': - resolution: {integrity: sha512-j7A8/LBEQ+3rugMzPXoKYzyUPpw/0CBQCyvtTR7Lmu4olG4yRC/Tfkq79Mr3yuPs0SUitlO2HwGP3gitMJnRFw==} - engines: {node: '>=18'} + '@conventional-changelog/git-client@3.2.0': + resolution: {integrity: sha512-sD/E0vzpkoDz/dqM1Hhv0ihC4KBw6RXjefeO8SG1XrX+wGkMxe1pKeedkKDPCtr2JjuhbuS9aJNgOvnT8hbeBw==} + engines: {node: '>=22'} peerDependencies: - conventional-commits-filter: ^5.0.0 - conventional-commits-parser: ^6.4.0 + conventional-commits-filter: ^6.0.1 + conventional-commits-parser: ^7.1.3 peerDependenciesMeta: conventional-commits-filter: optional: true conventional-commits-parser: optional: true - '@dprint/darwin-arm64@0.54.0': - resolution: {integrity: sha512-yqRI4enH+BDp+4+ZsPVdZM5h873JK1lN7li9l9A5u4C4cvh1oEsiBWAzEPccRkJ2ctF8LgaizBSxO38sqEVYbw==} + '@conventional-changelog/template@1.4.0': + resolution: {integrity: sha512-aalGyl7dbB5PArRebDIX43ZvBlXrYm9uWzGJ26t+4SzJVPsOuvfILGGbw5X4yX7i50YEmJ8zvbiWnqH/AAnZqg==} + engines: {node: '>=22'} + + '@dprint/android-arm64@0.64.1': + resolution: {integrity: sha512-B1eZzEFbhGg3lIPONQBvceZPJnwo02SUOx7/bt9xVmkCxC1Xei4SB5TTV6rAuOTaU67LHf8k6AXvO8liCXcNZA==} + cpu: [arm64] + os: [android] + + '@dprint/android-x64@0.64.1': + resolution: {integrity: sha512-IBZrUkV8RK6tlOSBK6JzGsq6H+HpEQtcAgLNSmu4SOkhkLPj9BMljUPM4peLHmB9vpd5TXE0m3Ep4HRvJwLJpg==} + cpu: [x64] + os: [android] + + '@dprint/darwin-arm64@0.64.1': + resolution: {integrity: sha512-E3TiTrvp/bbmiAZN5KdiaqIO53qXrAwHPpz6dl0X9Vt2dt82fwWdqWBz+AlXkOoW/4hWzncIKRP8L0ixsYg9qw==} cpu: [arm64] os: [darwin] - '@dprint/darwin-x64@0.54.0': - resolution: {integrity: sha512-W9BARpgHypcQwatg5mnHaCpX6pLX5dBxxiv+tZKruhOmq8MKYOrAYDXlceMuHSowmWREfUF5yL4SRgXDGI6WQw==} + '@dprint/darwin-x64@0.64.1': + resolution: {integrity: sha512-IpJPvJ/knrUFqvMMlpMHMDvmrk3ZXAV8f4PCPjUQZep7OWFt9w8iMRYN9ZSk9uU2DiStfGESjsLEkpXkBLbTZQ==} cpu: [x64] os: [darwin] - '@dprint/linux-arm64-glibc@0.54.0': - resolution: {integrity: sha512-VhM7p70VFuNqxZMdiv1e+nMboPj/hMFlTIBWrRaX7+6VThs9mJr9+94wrUeXgfnfsyaEKSbRFa/dru1PINoSNw==} + '@dprint/freebsd-arm64@0.64.1': + resolution: {integrity: sha512-vj+lZItlQqyd4n/H/Hv0dstPVhniTPw4Ib3oLXQyk2MZTg8CNv32pcPXmkopDyXDa8uLt00LXJtFfNllIDthFw==} + cpu: [arm64] + os: [freebsd] + + '@dprint/freebsd-x64@0.64.1': + resolution: {integrity: sha512-QDtebMG2n7Pm9rBXtWqQsjpmLOgNgbIV+neY7a4PL0ir8J5YZhrah7f3CLL+keFRfInaNuEQUwQYc1nr1aJgxw==} + cpu: [x64] + os: [freebsd] + + '@dprint/linux-arm64-glibc@0.64.1': + resolution: {integrity: sha512-M61BC0QFmO/TrSuaPDZGYhm5NJxwZHvkWTo+i4g6wwj+vR5l++kngg9XT5stQiqOQ5E7iftaJ1VMAzvz8OYMOQ==} cpu: [arm64] os: [linux] - '@dprint/linux-arm64-musl@0.54.0': - resolution: {integrity: sha512-QS1A74Lv60/L9oemHCzbHgOLbV2smSJG5IxS5fjf8ZWetyUt918WDzIHBilz/+uiB+OlW2UVTsm952UG0YOrLw==} + '@dprint/linux-arm64-musl@0.64.1': + resolution: {integrity: sha512-jODO33KWZ0vToGlbvN+TZRErz2DRAPdag6ejYXNexdr1I6CjZ8VKwEPbJUHIVa8gqCKpRduy9vapggqUOWK/pw==} cpu: [arm64] os: [linux] - '@dprint/linux-loong64-glibc@0.54.0': - resolution: {integrity: sha512-8Myka2/0KbhuZnEKL6jagPXTgDKVpd/tfXDRa0oibUBgaqOSku6iRMzHGa/PhqHL+s14Gcp+/cIHz0zU3Tkgug==} + '@dprint/linux-loong64-glibc@0.64.1': + resolution: {integrity: sha512-uDtIDZdFZM+Gz5qq+a12qDS1kgefNytxcW3xoobhhEMzoCxcBQ5P1p3c3aKqOnsmL3VLGpnNzXCAynOU9+gEuA==} cpu: [loong64] os: [linux] - '@dprint/linux-loong64-musl@0.54.0': - resolution: {integrity: sha512-/AN3xCuMhC4PK7Pbj7/4zBuhFGr4m0OHV/5uGTfzpkKX/3+AXoyKl7PbT2VlNMGXAK0kuRThfjtx23gIwlWk7Q==} + '@dprint/linux-loong64-musl@0.64.1': + resolution: {integrity: sha512-XLpOmmChqjIoRVeMwEvh3XD9h0vj0o9/i0d0PJfUIwhf2v9UZGgX60W/8bmpmZoFICl0VnNFDSA0i/B/N9KmPQ==} cpu: [loong64] os: [linux] - '@dprint/linux-riscv64-glibc@0.54.0': - resolution: {integrity: sha512-Aw2vXzzwFDpPbXh6ajsSabVCkCc66C3hCyMKprR/IxYvFtjYX80nh1ox0c7iaw6c4HacHMRLGw7FUSXvomPaEQ==} + '@dprint/linux-ppc64-glibc@0.64.1': + resolution: {integrity: sha512-ukx8KhHx9eVbDM9pV8STBVPeH5KirV4XRolyzBPeoNi5hyk92C3Jfhz+rLrTo/V+46hgZkVoYLAFFzKIDbtIqg==} + cpu: [ppc64] + os: [linux] + + '@dprint/linux-ppc64-musl@0.64.1': + resolution: {integrity: sha512-omr36vzMl4ASD0av8XqMm1RQRiNKfQM7K6dNM2mjK/xUi8zgtq5wa4QmHGurR6K2BP0RkKyoN+DmisPc8LzGew==} + cpu: [ppc64] + os: [linux] + + '@dprint/linux-riscv64-glibc@0.64.1': + resolution: {integrity: sha512-xNwG3RLSy1h3IHy5Fou4HUkJDmqSoRKdLywb7oVQKCAE0/kYD01VhsvaALbThE/30mOp77AdM5v5yJCRkihTTg==} + cpu: [riscv64] + os: [linux] + + '@dprint/linux-riscv64-musl@0.64.1': + resolution: {integrity: sha512-YxpB+2oS2BzYZ9a3GB2JmF3JVeVnz4OsK8Du86VzUg8Xpa/zKaIOGiY8Ksi/f6t0hEEpVlnI/NC7iMCcOsinDQ==} cpu: [riscv64] os: [linux] - '@dprint/linux-x64-glibc@0.54.0': - resolution: {integrity: sha512-zZqj3wQELOX8n6QfT2uuWoMf64Wv0lMXNyam3btm+PKkg0P6a54TPL09Bs9XsViOdxgTcamsiQ7HlErt/LEjIA==} + '@dprint/linux-s390x-glibc@0.64.1': + resolution: {integrity: sha512-HIRsuXWWuQ63Ud0pcGEfwh2LS0oBCxPFRw3y1pevPFhgoRDTUEeasodJeckljmEjix+a//e2XUYJKTmBrLhfEA==} + cpu: [s390x] + os: [linux] + + '@dprint/linux-x64-glibc@0.64.1': + resolution: {integrity: sha512-SFZPwEigcvCx1ZEvUEzbe0SyP61PqJLXUIFKHqRHOSzGWmdEkDTDzxBIZc4VUNIo2MRftCysxFMtKEfSZDHhNw==} cpu: [x64] os: [linux] - '@dprint/linux-x64-musl@0.54.0': - resolution: {integrity: sha512-it6Qdt06dyW2adbAXpOCb7/KQLxlm4i1UphUAWqWsZk4t3EYetyAza9J0g3Vu9itIWSEIo9MnccgANckQJ6+qw==} + '@dprint/linux-x64-musl@0.64.1': + resolution: {integrity: sha512-9MrW8dwV3ZE3ruBLocuVsf4oB3sxyjpFmskRrDPWdolCfa+fONTK5zfuKQwSXGJtu5uksEfky6a0+J4h67ffMQ==} cpu: [x64] os: [linux] - '@dprint/win32-arm64@0.54.0': - resolution: {integrity: sha512-F5kjV/6I9YtNOTDWHUpTqM2HHHS510BPL7z4NJuU0nDnaVeks7GwNEltGr56CcsG8XQYhkiAsqZytPu6AhA2hQ==} + '@dprint/win32-arm64@0.64.1': + resolution: {integrity: sha512-nfY2lvVfIx6gGhEiJadKaHLGzp/ruV4aQWXa4OtRz0YdEGStFMgCTW/GXXxxTWxcpRe1p4O9Ew239hibRFI0tA==} cpu: [arm64] os: [win32] - '@dprint/win32-x64@0.54.0': - resolution: {integrity: sha512-AAr2ye/DtgYXDplRoPS+5U++x7T6W4a3I9FvTFWFxziFmUptvAg5G2c4FcXoAduSruhYZJvjDZrLseR2c3IwXg==} + '@dprint/win32-x64@0.64.1': + resolution: {integrity: sha512-9PQARjMt8p+1SRemGsCnx0LA36JHIiiUacLjias90mKnpyDZ42MPjb6bje371Cs4qRZkwMFLs1JXJgQSwj3CSg==} cpu: [x64] os: [win32] @@ -262,12 +306,12 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@inquirer/ansi@2.0.7': - resolution: {integrity: sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==} + '@inquirer/ansi@2.0.9': + resolution: {integrity: sha512-0Ywt3S8tqM6TWUcsRlBFQ1x+X3/o8kktX0ZiKAnByYb9GFDknnOhse1PVtfiD2BXPI8ar+BWx9PV9R0lQIcygw==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} - '@inquirer/core@11.2.1': - resolution: {integrity: sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==} + '@inquirer/core@12.0.5': + resolution: {integrity: sha512-c0ZSnO9oVk/IlEGoZK3u8b3IZQeu9I6jUkXJSQnQEVRXsjKvtAq3AGqFL2LgfJhlzMZne08pZHWRWasIcI3Rew==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -275,8 +319,8 @@ packages: '@types/node': optional: true - '@inquirer/expand@5.1.1': - resolution: {integrity: sha512-YmQpenjbFSHAK3sOd44puHh3V1KXXr+JiNpUztoSQ4drLh2rTVzTap/YtlAVu/5xavifIlBfNEzJ/neZJ1a/1g==} + '@inquirer/expand@5.1.7': + resolution: {integrity: sha512-eiJ31YFCaB6fLoJCqLIn8S6eqUyME6vwr8KcP5h8cxmA+bV9RRVPO+kywoxb7G63HduDXV6sN/SM7o2R+ahEcQ==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -284,12 +328,12 @@ packages: '@types/node': optional: true - '@inquirer/figures@2.0.7': - resolution: {integrity: sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==} + '@inquirer/figures@2.0.9': + resolution: {integrity: sha512-EAWgUTGQ/Umgga51dE3B2PUHbufuXarDfg86uVgoSgNHNNQnyFKcOrQLWVqYMghuSyHh8+2HUH0Js9cTC1WAdg==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} - '@inquirer/input@5.1.2': - resolution: {integrity: sha512-9K/DDBSQpOyZSkt6sOVP9Vo0TR7atX2kuILsUu0x3wVcVbe97lJwIJKMLdMw25tDYuXl/qp6erT0Xs1rfmcfZg==} + '@inquirer/input@5.1.8': + resolution: {integrity: sha512-psOSTgkL8y/OtgJJYbqJ0oISr2QBE0U1Ro/qQdA/EKjeJiwiUTQgQmq8o4ESNzVOWthIeh3YLD7BCUR9dKnR5g==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -297,8 +341,8 @@ packages: '@types/node': optional: true - '@inquirer/select@5.2.1': - resolution: {integrity: sha512-FlDndEUww8m7BfukO2nJa25vhD+H5jxxCv4oGioKqzyWz3nPHhhw4LKdYRSlXuAx7DsdWia7iyaBPKKS95Evfw==} + '@inquirer/select@5.2.7': + resolution: {integrity: sha512-pRDWO4dTqSAvFQdO8yclKMtXNDm9K9HmvWB5UZmT3clw2OlOBNXveHzhyFmO/0d0yJMqlPcNITus1Jtpm6t8VQ==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -306,8 +350,8 @@ packages: '@types/node': optional: true - '@inquirer/type@4.0.7': - resolution: {integrity: sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==} + '@inquirer/type@4.2.0': + resolution: {integrity: sha512-2rnHX8RPo4glJYqnFc/v3nQeIhDcjyRRyaT7MGcfL/8xv/8K03SXYoA2tX06WVSKywWOE/ncbG3dtuzLjR5TCQ==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' @@ -322,8 +366,8 @@ packages: '@isaacs/string-locale-compare@1.1.0': resolution: {integrity: sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==} - '@lerna-lite/cli@5.3.0': - resolution: {integrity: sha512-gm+XWT2XRR3H8XXDMuWhL1jlqaAt0llwLZCGA/uUwe05HS2QKbEsYeaK0kZZKtt1ff7DAZYadMW4uazz9iHczg==} + '@lerna-lite/cli@5.6.4': + resolution: {integrity: sha512-Oyh/ReScWByM9KxcPHVkeatDeygGuCZtEZgf+o4gZo9KPvG0Xy3WxTlpyzYs4LG869orBi1Ru+G73oCq5+G8bQ==} engines: {node: ^22.17.0 || >=24.0.0} hasBin: true peerDependencies: @@ -347,32 +391,32 @@ packages: '@lerna-lite/watch': optional: true - '@lerna-lite/core@5.3.0': - resolution: {integrity: sha512-3RnmZhOV0PccgrorAzgO33L+UcG7GlpINsGBujafrVW1Qo6yNz+tW1K9bz/WLZilXwcxc15Odg/wTicAaDbU7Q==} + '@lerna-lite/core@5.6.4': + resolution: {integrity: sha512-QvCoTbJ67q0v0ZwL/C6yRDj38PeNBJGeIig8w8Y/9LVQ6ufuvwOj9Z+Ag10YE0UfVSrXLCMlgWlfrGs1J0zC6g==} engines: {node: ^22.17.0 || >=24.0.0} - '@lerna-lite/init@5.3.0': - resolution: {integrity: sha512-RuCFCxjcqtcmi50b3j0/tSVC3LXBhJXhPDucpe3U3sqPFInFhvK3pazLPBRS9bbaIMOG3Xtaz4PoSzsQ9lP1pw==} + '@lerna-lite/init@5.6.4': + resolution: {integrity: sha512-B5I+2VOKjr7cmxt2DDDQoxnL+eergZQ82+8h61IJBb9yt5D60f8R8chkuARJcTTErwhco0RzRSZ/5N16yJeGMg==} engines: {node: ^22.17.0 || >=24.0.0} - '@lerna-lite/npmlog@5.3.0': - resolution: {integrity: sha512-W2Z7ZAJrA4jfuvbDU59fNFVp3hHBAOnO1+LeX8Y2bHNNYNv5EAR9UuyIXikUqewIw7VDWaT6xh+85GjxLLKpcw==} + '@lerna-lite/npmlog@5.4.1': + resolution: {integrity: sha512-APylzmp9eDWsM5ZLnTUwV6uwTshtWAbKIF4sA0/SgBsdbgfJUVoz5VTviBnyODCKRzpjcR1MAlRTcjUmKXSbKA==} engines: {node: ^22.17.0 || >=24.0.0} - '@lerna-lite/publish@5.3.0': - resolution: {integrity: sha512-aLQEMprQJQSeGB8Wdt42zctwo79ybztps0VB/f1pjK2iaD7zBaF9ASacA6FuUt5OsyNujg2mbCZ7kgwAmtjUfg==} + '@lerna-lite/publish@5.6.4': + resolution: {integrity: sha512-kpAWEdt2TBjF2tM7eOEKx/XyL0ukvzqY1QTyfYiiLl6al3K0j7lfQnG1XS8deOekFxgGj2xcUo94K+xwARtWHQ==} engines: {node: ^22.17.0 || >=24.0.0} - '@lerna-lite/version@5.3.0': - resolution: {integrity: sha512-ZZUE/ajuCFj1WLNY08wKFmiSD+RYeT0mPosHKYbXejzUGKIBcuOdTApxvRcBr/G2pAWqQowCtE1sOM7AUbvzsw==} + '@lerna-lite/version@5.6.4': + resolution: {integrity: sha512-K02p93Q3M2Sjqd4PfLsf3aRGG+3W1P5heiQwX4AisE3ffsdbmWNm05O37NF9ifB+PGEfiR+9Ntzrfeu8vtluuA==} engines: {node: ^22.17.0 || >=24.0.0} '@npmcli/agent@4.0.0': resolution: {integrity: sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA==} engines: {node: ^20.17.0 || >=22.9.0} - '@npmcli/arborist@9.8.0': - resolution: {integrity: sha512-bqjei/1+uait6wA30G7IElMs5VCyGpuVPFQYsvzqhTEEAVmDMsHBCFstcT2lLfeQDvi8MeUQHStfHSArvdTQuw==} + '@npmcli/arborist@9.9.3': + resolution: {integrity: sha512-OxRSMNRPzL2/x2zsVrzdMZItfNj3cVXZIYgGNlUaBRUyH16YKdwN1r9z+I13keR1mjVNKelMfi2a7A/0JJ4NCw==} engines: {node: ^20.17.0 || >=22.9.0} hasBin: true @@ -504,17 +548,21 @@ packages: resolution: {integrity: sha512-mNe0Iigql08YupSOGv197YdHpPPr+EzDZmfCgMc7RPNaZTw5aLN01nBl6CHJOh3BGtnMIj83EeN4butBchc8Ag==} engines: {node: ^20.17.0 || >=22.9.0} - '@simple-libs/child-process-utils@1.0.2': - resolution: {integrity: sha512-/4R8QKnd/8agJynkNdJmNw2MBxuFTRcNFnE5Sg/G+jkSsV8/UBgULMzhizWWW42p8L5H7flImV2ATi79Ove2Tw==} - engines: {node: '>=18'} + '@simple-libs/child-process-utils@2.0.0': + resolution: {integrity: sha512-dvNoRKLijXnD0XoJAz94pbNuB5GQgDr55UhpSPhffDkTT0Cmcqh9jSCOtwfT2d4H6MI9E7c4SgtMuJXZ6F3c6A==} + engines: {node: '>=22'} - '@simple-libs/hosted-git-info@1.0.2': - resolution: {integrity: sha512-aAmGQdMH+ZinytKuA2832u0ATeOFNYNk4meBEXtB5xaPotUgggYNhq5tYU/v17wEbmTW5P9iHNqNrFyrhnqBAg==} - engines: {node: '>=18'} + '@simple-libs/hosted-git-info@2.0.0': + resolution: {integrity: sha512-55XwK/GYgV58PuN8lZFhI1qRxdKoMLJocXl/yM1EPqazFDmM4QWY7q6BxPCPDNKTNunj794DSD4h0H7SrqUdMg==} + engines: {node: '>=22'} - '@simple-libs/stream-utils@1.2.0': - resolution: {integrity: sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==} - engines: {node: '>=18'} + '@simple-libs/normalize-package-data@1.0.0': + resolution: {integrity: sha512-/EOmFBekV9tGee8gac/k+5Ox3twkypNqh5TfxA9vTkmcJkjm6f1xqrlstDNOrEhTvnYyVtU6Du2ZhY/IV1+9GA==} + engines: {node: '>=22'} + + '@simple-libs/stream-utils@2.0.0': + resolution: {integrity: sha512-fCTuZK4QBa+39Oz9l4OGfJfz+GpwCp3AqO7Zch3to99xHPgstVsRFpeQ8LNd2o1Gv8raL2mCFwiaHh7bFSp5DQ==} + engines: {node: '>=22'} '@tufjs/canonical-json@2.0.0': resolution: {integrity: sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==} @@ -533,11 +581,8 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@25.9.3': - resolution: {integrity: sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==} - - '@types/normalize-package-data@2.4.4': - resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} + '@types/node@26.6.5': + resolution: {integrity: sha512-uMzsv9stDdofIZBx4UGh4Z9ZgXnGC9seRDscilBcTHAeOFk67MZg/k9bWVfO+bk4qZmG+l52hRnLwUiH2LnJTg==} '@types/parse-path@7.1.0': resolution: {integrity: sha512-EULJ8LApcVEPbrfND0cRQqutIOdiIgJ1Mgrhpy755r14xMohPTEpkV/k28SJvuOs9bHRFW8x+KeDAEPiGQPB9Q==} @@ -742,8 +787,9 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - array-ify@1.0.0: - resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} + argue-cli@3.2.0: + resolution: {integrity: sha512-VipTB0gXgGIFO2Rg9yEVN5wLt2AurJcZqDbgmYSwPwsykhLrQhs240/bfceev4w68lI8JshoOJIk9uW7tFzROw==} + engines: {node: '>=22'} balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -809,42 +855,39 @@ packages: resolution: {integrity: sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==} engines: {node: '>= 18'} - compare-func@2.0.0: - resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} - concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - conventional-changelog-angular@8.3.1: - resolution: {integrity: sha512-6gfI3otXK5Ph5DfCOI1dblr+kN3FAm5a97hYoQkqNZxOaYa5WKfXH+AnpsmS+iUH2mgVC2Cg2Qw9m5OKcmNrIg==} - engines: {node: '>=18'} + conventional-changelog-angular@9.4.0: + resolution: {integrity: sha512-HdxRxuS8bBXVIuo4V82gvSwAXT0vYQUizrjs/izmPg5JdDstr8v8I5hduGL3iQbG+o310dUDxC4+LetuS5hu9w==} + engines: {node: '>=22'} - conventional-changelog-preset-loader@5.0.0: - resolution: {integrity: sha512-SetDSntXLk8Jh1NOAl1Gu5uLiCNSYenB5tm0YVeZKePRIgDW9lQImromTwLa3c/Gae298tsgOM+/CYT9XAl0NA==} - engines: {node: '>=18'} + conventional-changelog-preset-loader@6.0.1: + resolution: {integrity: sha512-GZ8E3RQzXQb3JFy0pKl8oeSf7ENIhvAMvJr+PlWkavZOesLHHdhkfNJ4SvW35eo1Fu2+EyVxWQ1tVvtzAG2iWg==} + engines: {node: '>=22'} - conventional-changelog-writer@8.4.0: - resolution: {integrity: sha512-HHBFkk1EECxxmCi4CTu091iuDpQv5/OavuCUAuZmrkWpmYfyD816nom1CvtfXJ/uYfAAjavgHvXHX291tSLK8g==} - engines: {node: '>=18'} + conventional-changelog-writer@9.3.0: + resolution: {integrity: sha512-lxiECDlvy880e7JIYAtAhxg0wD/CGCVbZCdeua5aOlQVFTEd39zAeOzwK22i/FO4xnEOURK1gbUTETNEYW7zxQ==} + engines: {node: '>=22'} hasBin: true - conventional-changelog@7.2.0: - resolution: {integrity: sha512-BEdgG+vPl53EVlTTk9sZ96aagFp0AQ5pw/ggiQMy2SClLbTo1r0l+8dSg79gkLOO5DS1Lswuhp5fWn6RwE+ivg==} - engines: {node: '>=18'} + conventional-changelog@8.1.3: + resolution: {integrity: sha512-fOMwLCxm46znJKPB08jLxl1FD8SOZOQHktlzCVhvhhVUfq0KgQSryoYXsRRHs1aRlZPR1/QK5wztnY8sBNC2AA==} + engines: {node: '>=22'} hasBin: true - conventional-commits-filter@5.0.0: - resolution: {integrity: sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q==} - engines: {node: '>=18'} + conventional-commits-filter@6.0.1: + resolution: {integrity: sha512-cs+LadpH7Kpw0M3k8wurk+sOVVDAENA0iK4OBOrkL94j5lEVYRJ4j3zd2bhY9qgzyrPqthdcYT3axzRN7AliMg==} + engines: {node: '>=22'} - conventional-commits-parser@6.4.0: - resolution: {integrity: sha512-tvRg7FIBNlyPzjdG8wWRlPHQJJHI7DylhtRGeU9Lq+JuoPh5BKpPRX83ZdLrvXuOSu5Eo/e7SzOQhU4Hd2Miuw==} - engines: {node: '>=18'} + conventional-commits-parser@7.1.3: + resolution: {integrity: sha512-Q9oWm1o9uy/oEsZQIraoFMZ4RuPD+79KgwWj6mJVP356tazolCB0piRLZ5Kz9te9p027uSEcUW+Gu44Eh+X79g==} + engines: {node: '>=22'} hasBin: true - conventional-recommended-bump@11.2.0: - resolution: {integrity: sha512-lqIdmw330QdMBgfL0e6+6q5OMKyIpy4OZNmepit6FS3GldhkG+70drZjuZ0A5NFpze5j85dlYs3GabQXl6sMHw==} - engines: {node: '>=18'} + conventional-recommended-bump@12.1.0: + resolution: {integrity: sha512-HTNG3kEZXOOfKwrEx54ljURU9bG4nVPQzXMyCSeUcA/PE8EpNpQNujSrbXNBI8QZyN35zM+pVw+FuQk4KqHSHg==} + engines: {node: '>=22'} hasBin: true cross-spawn@7.0.6: @@ -872,16 +915,13 @@ packages: resolution: {integrity: sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A==} engines: {node: '>=12.20'} - dot-prop@5.3.0: - resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} - engines: {node: '>=8'} - - dotenv@17.4.2: - resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==} + dotenv@18.0.8: + resolution: {integrity: sha512-2KDeoFoK/zRtcnbunCDlLG5VWVMcyqeQc6iK65o231nfNAcuGrKoqUQfJFUtBI+QlShk5HKIHxwebBtUl3rEfA==} engines: {node: '>=12'} + hasBin: true - dprint@0.54.0: - resolution: {integrity: sha512-sIy25poR2gRP/tWPTgP0MPeJoJcpv0xzYDcsboapvthbEt1Qw3Al252CA0xFyIh2cYEGGKyBJtKokryv4ERlJw==} + dprint@0.64.1: + resolution: {integrity: sha512-cCrmTq1nbZ/j//kkJz3KJxsuq9e1y/NG4JL+zVZM964RPnJKFWjbK1R0AtkKbRLpsfW0NRV0nk7q1kvzZ3zWQQ==} hasBin: true emoji-regex@10.6.0: @@ -918,6 +958,7 @@ packages: eslint@9.39.4: resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true peerDependencies: jiti: '*' @@ -1039,8 +1080,8 @@ packages: graphmatch@1.1.1: resolution: {integrity: sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg==} - handlebars@4.7.9: - resolution: {integrity: sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==} + handlebars@4.7.10: + resolution: {integrity: sha512-P5VJMVM7qgBn6vjXMw8WG9uVI+ncf2pi72j4de4yz5ZULLj2RGqLYaKOYGsgyrViQ0tePOVlN1tDCCXXtFqXKg==} engines: {node: '>=0.4.7'} hasBin: true @@ -1048,10 +1089,6 @@ packages: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - hosted-git-info@8.1.0: - resolution: {integrity: sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==} - engines: {node: ^18.17.0 || >=20.5.0} - hosted-git-info@9.0.2: resolution: {integrity: sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==} engines: {node: ^20.17.0 || >=22.9.0} @@ -1108,10 +1145,6 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-obj@2.0.0: - resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} - engines: {node: '>=8'} - is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} @@ -1179,8 +1212,8 @@ packages: resolution: {integrity: sha512-JPHTfWJxIK+NVPdNMNGnkz4XGX56iijPbe0qFWbdt68HL+kIvSzh+euBL8npLZvl2fpaxo+1eZSdoG15f5YdIQ==} engines: {node: ^20.17.0 || >=22.9.0} - libnpmpublish@11.2.0: - resolution: {integrity: sha512-fAEts7UCM2r1xhI82Dv9PK4gFGZoyD7Z5sfIwBQKoO/f67VNVDC0DeH3uH1Yi+T4kwt5iBuCKkZ5XcpxfvsGHQ==} + libnpmpublish@11.2.1: + resolution: {integrity: sha512-eZTRbsvZnG7tV0b9Hxug9feQt9Z29GyijcWpEKpLrgaJjxuVCkUNPW+w6OcXmlvSaopkyrGN65hgtk+Z0YP3Hg==} engines: {node: ^20.17.0 || >=22.9.0} lilconfig@3.1.3: @@ -1198,9 +1231,6 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.3.5: resolution: {integrity: sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==} engines: {node: 20 || >=22} @@ -1209,10 +1239,6 @@ packages: resolution: {integrity: sha512-uCbIa8jWWmQZt4dSnEStkVC6gdakiinAm4PiGsywIkguF0eWMdcjDz0ECYhUolFU3pFLOev9VNPCEygydXnddg==} engines: {node: ^20.17.0 || >=22.9.0} - meow@13.2.0: - resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} - engines: {node: '>=18'} - minimatch@10.2.5: resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} engines: {node: 18 || 20 || >=22} @@ -1282,10 +1308,6 @@ packages: engines: {node: ^20.17.0 || >=22.9.0} hasBin: true - normalize-package-data@7.0.1: - resolution: {integrity: sha512-linxNAT6M0ebEYZOx2tO6vBEFsVgnPpv+AVjk0wJHfaUIbq31Jm3T6vvZaarnOeWDh8ShnwXuaAyM7WT3RzErA==} - engines: {node: ^18.17.0 || >=20.5.0} - npm-bundled@5.0.0: resolution: {integrity: sha512-JLSpbzh6UUXIEoqPsYBvVNVmyrjVZ1fzEFbqxKkTJQkWBO3xFzFT+KDnSKQWwOQNbuWRwt5LSD6HOTLGIWzfrw==} engines: {node: ^20.17.0 || >=22.9.0} @@ -1342,8 +1364,8 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} - pacote@21.5.0: - resolution: {integrity: sha512-VtZ0SB8mb5Tzw3dXDfVAIjhyVKUHZkS/ZH9/5mpKenwC9sFOXNI0JI7kEF7IMkwOnsWMFrvAZHzx1T5fmrp9FQ==} + pacote@21.5.1: + resolution: {integrity: sha512-KvcJ9iy3crysCsgqc4+PknH/w6jkrp8JN36mpZBPwNaDRwTfMZD37YzRazNstiZUOhuF5pno9f78n9mEJBavwg==} engines: {node: ^20.17.0 || >=22.9.0} hasBin: true @@ -1435,6 +1457,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1471,15 +1498,9 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - spdx-correct@3.2.0: - resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} - spdx-exceptions@2.5.0: resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} - spdx-expression-parse@3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - spdx-expression-parse@4.0.0: resolution: {integrity: sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==} @@ -1494,6 +1515,10 @@ packages: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} + string-width@8.3.0: + resolution: {integrity: sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==} + engines: {node: '>=20'} + strip-ansi@7.2.0: resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} @@ -1510,8 +1535,12 @@ packages: resolution: {integrity: sha512-56adEpPMouktRlBLXiaYFFzZ/3+JXa8P9n7WbR+ibIjtviN55mEaOkiysCnPnWm+7kkui1Dn8J9l+g6zV8731w==} engines: {node: '>=18'} - tinyexec@1.2.4: - resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} + tar@7.5.22: + resolution: {integrity: sha512-MFO/QzvtAOmJbkhOaCTvbGcFN9L9b+JunIsDwaKljSOdcLMea3NJ1k9Usz/rjdfSXTq4dfzfeS7W4p4YOAAHeA==} + engines: {node: '>=18'} + + tinyexec@1.3.1: + resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==} engines: {node: '>=18'} tinyglobby@0.2.16: @@ -1551,8 +1580,8 @@ packages: engines: {node: '>=0.8.0'} hasBin: true - undici-types@7.24.6: - resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} + undici-types@8.9.0: + resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} undici@6.25.0: resolution: {integrity: sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==} @@ -1567,13 +1596,14 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - validate-npm-package-license@3.0.4: - resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - validate-npm-package-name@7.0.2: resolution: {integrity: sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==} engines: {node: ^20.17.0 || >=22.9.0} + verkit@0.5.0: + resolution: {integrity: sha512-u4J1hr6Y+C/hit34GwL3eIcUf3wngT8TlYUjK7tk0jxydKLAq3sYvifjdTDA6U3KCKRcQCQDGf/srjxfZF3l7g==} + engines: {node: '>=18.12.0'} + walk-up-path@4.0.0: resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} engines: {node: 20 || >=22} @@ -1622,8 +1652,8 @@ packages: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} - yaml@2.9.0: - resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + yaml@2.9.1: + resolution: {integrity: sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==} engines: {node: '>= 14.6'} hasBin: true @@ -1631,8 +1661,8 @@ packages: resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} engines: {node: ^20.19.0 || ^22.12.0 || >=23} - yargs@18.0.0: - resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} + yargs@18.2.0: + resolution: {integrity: sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==} engines: {node: ^20.19.0 || ^22.12.0 || >=23} yocto-queue@0.1.0: @@ -1644,46 +1674,72 @@ packages: snapshots: - '@conventional-changelog/git-client@2.7.0(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.4.0)': + '@conventional-changelog/git-client@3.2.0(conventional-commits-filter@6.0.1)(conventional-commits-parser@7.1.3)': dependencies: - '@simple-libs/child-process-utils': 1.0.2 - '@simple-libs/stream-utils': 1.2.0 - semver: 7.8.4 + '@simple-libs/child-process-utils': 2.0.0 + '@simple-libs/stream-utils': 2.0.0 + verkit: 0.5.0 optionalDependencies: - conventional-commits-filter: 5.0.0 - conventional-commits-parser: 6.4.0 + conventional-commits-filter: 6.0.1 + conventional-commits-parser: 7.1.3 - '@dprint/darwin-arm64@0.54.0': + '@conventional-changelog/template@1.4.0': {} + + '@dprint/android-arm64@0.64.1': + optional: true + + '@dprint/android-x64@0.64.1': + optional: true + + '@dprint/darwin-arm64@0.64.1': + optional: true + + '@dprint/darwin-x64@0.64.1': + optional: true + + '@dprint/freebsd-arm64@0.64.1': + optional: true + + '@dprint/freebsd-x64@0.64.1': + optional: true + + '@dprint/linux-arm64-glibc@0.64.1': + optional: true + + '@dprint/linux-arm64-musl@0.64.1': + optional: true + + '@dprint/linux-loong64-glibc@0.64.1': optional: true - '@dprint/darwin-x64@0.54.0': + '@dprint/linux-loong64-musl@0.64.1': optional: true - '@dprint/linux-arm64-glibc@0.54.0': + '@dprint/linux-ppc64-glibc@0.64.1': optional: true - '@dprint/linux-arm64-musl@0.54.0': + '@dprint/linux-ppc64-musl@0.64.1': optional: true - '@dprint/linux-loong64-glibc@0.54.0': + '@dprint/linux-riscv64-glibc@0.64.1': optional: true - '@dprint/linux-loong64-musl@0.54.0': + '@dprint/linux-riscv64-musl@0.64.1': optional: true - '@dprint/linux-riscv64-glibc@0.54.0': + '@dprint/linux-s390x-glibc@0.64.1': optional: true - '@dprint/linux-x64-glibc@0.54.0': + '@dprint/linux-x64-glibc@0.64.1': optional: true - '@dprint/linux-x64-musl@0.54.0': + '@dprint/linux-x64-musl@0.64.1': optional: true - '@dprint/win32-arm64@0.54.0': + '@dprint/win32-arm64@0.64.1': optional: true - '@dprint/win32-x64@0.54.0': + '@dprint/win32-x64@0.64.1': optional: true '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.6.1))': @@ -1750,48 +1806,48 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@inquirer/ansi@2.0.7': {} + '@inquirer/ansi@2.0.9': {} - '@inquirer/core@11.2.1(@types/node@25.9.3)': + '@inquirer/core@12.0.5(@types/node@26.6.5)': dependencies: - '@inquirer/ansi': 2.0.7 - '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@25.9.3) + '@inquirer/ansi': 2.0.9 + '@inquirer/figures': 2.0.9 + '@inquirer/type': 4.2.0(@types/node@26.6.5) cli-width: 4.1.0 fast-wrap-ansi: 0.2.0 mute-stream: 3.0.0 signal-exit: 4.1.0 optionalDependencies: - '@types/node': 25.9.3 + '@types/node': 26.6.5 - '@inquirer/expand@5.1.1(@types/node@25.9.3)': + '@inquirer/expand@5.1.7(@types/node@26.6.5)': dependencies: - '@inquirer/core': 11.2.1(@types/node@25.9.3) - '@inquirer/type': 4.0.7(@types/node@25.9.3) + '@inquirer/core': 12.0.5(@types/node@26.6.5) + '@inquirer/type': 4.2.0(@types/node@26.6.5) optionalDependencies: - '@types/node': 25.9.3 + '@types/node': 26.6.5 - '@inquirer/figures@2.0.7': {} + '@inquirer/figures@2.0.9': {} - '@inquirer/input@5.1.2(@types/node@25.9.3)': + '@inquirer/input@5.1.8(@types/node@26.6.5)': dependencies: - '@inquirer/core': 11.2.1(@types/node@25.9.3) - '@inquirer/type': 4.0.7(@types/node@25.9.3) + '@inquirer/core': 12.0.5(@types/node@26.6.5) + '@inquirer/type': 4.2.0(@types/node@26.6.5) optionalDependencies: - '@types/node': 25.9.3 + '@types/node': 26.6.5 - '@inquirer/select@5.2.1(@types/node@25.9.3)': + '@inquirer/select@5.2.7(@types/node@26.6.5)': dependencies: - '@inquirer/ansi': 2.0.7 - '@inquirer/core': 11.2.1(@types/node@25.9.3) - '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@25.9.3) + '@inquirer/ansi': 2.0.9 + '@inquirer/core': 12.0.5(@types/node@26.6.5) + '@inquirer/figures': 2.0.9 + '@inquirer/type': 4.2.0(@types/node@26.6.5) optionalDependencies: - '@types/node': 25.9.3 + '@types/node': 26.6.5 - '@inquirer/type@4.0.7(@types/node@25.9.3)': + '@inquirer/type@4.2.0(@types/node@26.6.5)': optionalDependencies: - '@types/node': 25.9.3 + '@types/node': 26.6.5 '@isaacs/fs-minipass@4.0.1': dependencies: @@ -1799,70 +1855,70 @@ snapshots: '@isaacs/string-locale-compare@1.1.0': {} - '@lerna-lite/cli@5.3.0(@lerna-lite/publish@5.3.0(@types/node@25.9.3)(conventional-commits-filter@5.0.0))(@lerna-lite/version@5.3.0(@lerna-lite/publish@5.3.0(@types/node@25.9.3)(conventional-commits-filter@5.0.0))(@types/node@25.9.3)(conventional-commits-filter@5.0.0))(@types/node@25.9.3)': + '@lerna-lite/cli@5.6.4(@lerna-lite/publish@5.6.4(@types/node@26.6.5)(conventional-commits-filter@6.0.1))(@lerna-lite/version@5.6.4(@lerna-lite/publish@5.6.4(@types/node@26.6.5)(conventional-commits-filter@6.0.1))(@types/node@26.6.5)(conventional-commits-filter@6.0.1))(@types/node@26.6.5)': dependencies: - '@lerna-lite/core': 5.3.0(@types/node@25.9.3) - '@lerna-lite/init': 5.3.0(@types/node@25.9.3) - '@lerna-lite/npmlog': 5.3.0 - dotenv: 17.4.2 + '@lerna-lite/core': 5.6.4(@types/node@26.6.5) + '@lerna-lite/init': 5.6.4(@types/node@26.6.5) + '@lerna-lite/npmlog': 5.4.1 + dotenv: 18.0.8 import-local: 3.2.0 - yargs: 18.0.0 + yargs: 18.2.0 optionalDependencies: - '@lerna-lite/publish': 5.3.0(@types/node@25.9.3)(conventional-commits-filter@5.0.0) - '@lerna-lite/version': 5.3.0(@lerna-lite/publish@5.3.0(@types/node@25.9.3)(conventional-commits-filter@5.0.0))(@types/node@25.9.3)(conventional-commits-filter@5.0.0) + '@lerna-lite/publish': 5.6.4(@types/node@26.6.5)(conventional-commits-filter@6.0.1) + '@lerna-lite/version': 5.6.4(@lerna-lite/publish@5.6.4(@types/node@26.6.5)(conventional-commits-filter@6.0.1))(@types/node@26.6.5)(conventional-commits-filter@6.0.1) transitivePeerDependencies: - '@types/node' - '@lerna-lite/core@5.3.0(@types/node@25.9.3)': + '@lerna-lite/core@5.6.4(@types/node@26.6.5)': dependencies: - '@inquirer/expand': 5.1.1(@types/node@25.9.3) - '@inquirer/input': 5.1.2(@types/node@25.9.3) - '@inquirer/select': 5.2.1(@types/node@25.9.3) - '@lerna-lite/npmlog': 5.3.0 + '@inquirer/expand': 5.1.7(@types/node@26.6.5) + '@inquirer/input': 5.1.8(@types/node@26.6.5) + '@inquirer/select': 5.2.7(@types/node@26.6.5) + '@lerna-lite/npmlog': 5.4.1 '@npmcli/run-script': 10.0.4 ci-info: 4.4.0 json5: 2.2.3 lilconfig: 3.1.3 npm-package-arg: 13.0.2 - semver: 7.8.4 - tinyexec: 1.2.4 + tinyexec: 1.3.1 + verkit: 0.5.0 write-file-atomic: 7.0.1 write-json-file: 7.0.0 - yaml: 2.9.0 + yaml: 2.9.1 zeptomatch: 2.1.0 transitivePeerDependencies: - '@types/node' - '@lerna-lite/init@5.3.0(@types/node@25.9.3)': + '@lerna-lite/init@5.6.4(@types/node@26.6.5)': dependencies: - '@lerna-lite/core': 5.3.0(@types/node@25.9.3) + '@lerna-lite/core': 5.6.4(@types/node@26.6.5) write-json-file: 7.0.0 transitivePeerDependencies: - '@types/node' - '@lerna-lite/npmlog@5.3.0': + '@lerna-lite/npmlog@5.4.1': dependencies: fast-string-width: 3.0.2 signal-exit: 4.1.0 - '@lerna-lite/publish@5.3.0(@types/node@25.9.3)(conventional-commits-filter@5.0.0)': + '@lerna-lite/publish@5.6.4(@types/node@26.6.5)(conventional-commits-filter@6.0.1)': dependencies: - '@lerna-lite/cli': 5.3.0(@lerna-lite/publish@5.3.0(@types/node@25.9.3)(conventional-commits-filter@5.0.0))(@lerna-lite/version@5.3.0(@lerna-lite/publish@5.3.0(@types/node@25.9.3)(conventional-commits-filter@5.0.0))(@types/node@25.9.3)(conventional-commits-filter@5.0.0))(@types/node@25.9.3) - '@lerna-lite/core': 5.3.0(@types/node@25.9.3) - '@lerna-lite/npmlog': 5.3.0 - '@lerna-lite/version': 5.3.0(@lerna-lite/publish@5.3.0(@types/node@25.9.3)(conventional-commits-filter@5.0.0))(@types/node@25.9.3)(conventional-commits-filter@5.0.0) - '@npmcli/arborist': 9.8.0 + '@lerna-lite/cli': 5.6.4(@lerna-lite/publish@5.6.4(@types/node@26.6.5)(conventional-commits-filter@6.0.1))(@lerna-lite/version@5.6.4(@lerna-lite/publish@5.6.4(@types/node@26.6.5)(conventional-commits-filter@6.0.1))(@types/node@26.6.5)(conventional-commits-filter@6.0.1))(@types/node@26.6.5) + '@lerna-lite/core': 5.6.4(@types/node@26.6.5) + '@lerna-lite/npmlog': 5.4.1 + '@lerna-lite/version': 5.6.4(@lerna-lite/publish@5.6.4(@types/node@26.6.5)(conventional-commits-filter@6.0.1))(@types/node@26.6.5)(conventional-commits-filter@6.0.1) + '@npmcli/arborist': 9.9.3 '@npmcli/package-json': 7.0.5 ci-info: 4.4.0 libnpmaccess: 10.0.3 - libnpmpublish: 11.2.0 + libnpmpublish: 11.2.1 npm-package-arg: 13.0.2 npm-packlist: 10.0.4 npm-registry-fetch: 19.1.1 - pacote: 21.5.0 - semver: 7.8.4 + pacote: 21.5.1 ssri: 13.0.1 - tar: 7.5.16 + tar: 7.5.22 + verkit: 0.5.0 transitivePeerDependencies: - '@lerna-lite/exec' - '@lerna-lite/list' @@ -1872,22 +1928,24 @@ snapshots: - conventional-commits-filter - supports-color - '@lerna-lite/version@5.3.0(@lerna-lite/publish@5.3.0(@types/node@25.9.3)(conventional-commits-filter@5.0.0))(@types/node@25.9.3)(conventional-commits-filter@5.0.0)': + '@lerna-lite/version@5.6.4(@lerna-lite/publish@5.6.4(@types/node@26.6.5)(conventional-commits-filter@6.0.1))(@types/node@26.6.5)(conventional-commits-filter@6.0.1)': dependencies: - '@conventional-changelog/git-client': 2.7.0(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.4.0) - '@lerna-lite/cli': 5.3.0(@lerna-lite/publish@5.3.0(@types/node@25.9.3)(conventional-commits-filter@5.0.0))(@lerna-lite/version@5.3.0(@lerna-lite/publish@5.3.0(@types/node@25.9.3)(conventional-commits-filter@5.0.0))(@types/node@25.9.3)(conventional-commits-filter@5.0.0))(@types/node@25.9.3) - '@lerna-lite/core': 5.3.0(@types/node@25.9.3) - '@lerna-lite/npmlog': 5.3.0 + '@conventional-changelog/git-client': 3.2.0(conventional-commits-filter@6.0.1)(conventional-commits-parser@7.1.3) + '@conventional-changelog/template': 1.4.0 + '@lerna-lite/cli': 5.6.4(@lerna-lite/publish@5.6.4(@types/node@26.6.5)(conventional-commits-filter@6.0.1))(@lerna-lite/version@5.6.4(@lerna-lite/publish@5.6.4(@types/node@26.6.5)(conventional-commits-filter@6.0.1))(@types/node@26.6.5)(conventional-commits-filter@6.0.1))(@types/node@26.6.5) + '@lerna-lite/core': 5.6.4(@types/node@26.6.5) + '@lerna-lite/npmlog': 5.4.1 '@octokit/plugin-enterprise-rest': 6.0.1 '@octokit/rest': 22.0.1 - conventional-changelog: 7.2.0(conventional-commits-filter@5.0.0) - conventional-changelog-angular: 8.3.1 - conventional-changelog-writer: 8.4.0 - conventional-commits-parser: 6.4.0 - conventional-recommended-bump: 11.2.0 + conventional-changelog: 8.1.3 + conventional-changelog-angular: 9.4.0 + conventional-changelog-writer: 9.3.0 + conventional-commits-parser: 7.1.3 + conventional-recommended-bump: 12.1.0 git-url-parse: 16.1.0 + handlebars: 4.7.10 npm-package-arg: 13.0.2 - semver: 7.8.4 + verkit: 0.5.0 write-json-file: 7.0.0 zeptomatch: 2.1.0 transitivePeerDependencies: @@ -1909,7 +1967,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@npmcli/arborist@9.8.0': + '@npmcli/arborist@9.9.3': dependencies: '@gar/promise-retry': 1.0.3 '@isaacs/string-locale-compare': 1.1.0 @@ -1935,7 +1993,7 @@ snapshots: npm-package-arg: 13.0.2 npm-pick-manifest: 11.0.3 npm-registry-fetch: 19.1.1 - pacote: 21.5.0 + pacote: 21.5.1 parse-conflict-json: 5.0.1 proc-log: 6.1.0 proggy: 4.0.0 @@ -1979,7 +2037,7 @@ snapshots: dependencies: cacache: 20.0.4 json-parse-even-better-errors: 5.0.0 - pacote: 21.5.0 + pacote: 21.5.1 proc-log: 6.1.0 semver: 7.8.4 transitivePeerDependencies: @@ -2114,13 +2172,18 @@ snapshots: '@sigstore/core': 3.2.0 '@sigstore/protobuf-specs': 0.5.1 - '@simple-libs/child-process-utils@1.0.2': + '@simple-libs/child-process-utils@2.0.0': dependencies: - '@simple-libs/stream-utils': 1.2.0 + '@simple-libs/stream-utils': 2.0.0 + + '@simple-libs/hosted-git-info@2.0.0': {} - '@simple-libs/hosted-git-info@1.0.2': {} + '@simple-libs/normalize-package-data@1.0.0': + dependencies: + '@simple-libs/hosted-git-info': 2.0.0 + semver: 7.8.5 - '@simple-libs/stream-utils@1.2.0': {} + '@simple-libs/stream-utils@2.0.0': {} '@tufjs/canonical-json@2.0.0': {} @@ -2138,11 +2201,9 @@ snapshots: '@types/json-schema@7.0.15': {} - '@types/node@25.9.3': + '@types/node@26.6.5': dependencies: - undici-types: 7.24.6 - - '@types/normalize-package-data@2.4.4': {} + undici-types: 8.9.0 '@types/parse-path@7.1.0': dependencies: @@ -2290,7 +2351,7 @@ snapshots: argparse@2.0.1: {} - array-ify@1.0.0: {} + argue-cli@3.2.0: {} balanced-match@1.0.2: {} @@ -2357,55 +2418,48 @@ snapshots: common-ancestor-path@2.0.0: {} - compare-func@2.0.0: - dependencies: - array-ify: 1.0.0 - dot-prop: 5.3.0 - concat-map@0.0.1: {} - conventional-changelog-angular@8.3.1: + conventional-changelog-angular@9.4.0: dependencies: - compare-func: 2.0.0 + '@conventional-changelog/template': 1.4.0 - conventional-changelog-preset-loader@5.0.0: {} + conventional-changelog-preset-loader@6.0.1: {} - conventional-changelog-writer@8.4.0: + conventional-changelog-writer@9.3.0: dependencies: - '@simple-libs/stream-utils': 1.2.0 - conventional-commits-filter: 5.0.0 - handlebars: 4.7.9 - meow: 13.2.0 - semver: 7.8.4 + '@conventional-changelog/template': 1.4.0 + '@simple-libs/stream-utils': 2.0.0 + argue-cli: 3.2.0 + conventional-commits-filter: 6.0.1 + verkit: 0.5.0 - conventional-changelog@7.2.0(conventional-commits-filter@5.0.0): + conventional-changelog@8.1.3: dependencies: - '@conventional-changelog/git-client': 2.7.0(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.4.0) - '@simple-libs/hosted-git-info': 1.0.2 - '@types/normalize-package-data': 2.4.4 - conventional-changelog-preset-loader: 5.0.0 - conventional-changelog-writer: 8.4.0 - conventional-commits-parser: 6.4.0 + '@conventional-changelog/git-client': 3.2.0(conventional-commits-filter@6.0.1)(conventional-commits-parser@7.1.3) + '@simple-libs/hosted-git-info': 2.0.0 + '@simple-libs/normalize-package-data': 1.0.0 + argue-cli: 3.2.0 + conventional-changelog-preset-loader: 6.0.1 + conventional-changelog-writer: 9.3.0 + conventional-commits-filter: 6.0.1 + conventional-commits-parser: 7.1.3 fd-package-json: 2.0.0 - meow: 13.2.0 - normalize-package-data: 7.0.1 - transitivePeerDependencies: - - conventional-commits-filter - conventional-commits-filter@5.0.0: {} + conventional-commits-filter@6.0.1: {} - conventional-commits-parser@6.4.0: + conventional-commits-parser@7.1.3: dependencies: - '@simple-libs/stream-utils': 1.2.0 - meow: 13.2.0 + '@simple-libs/stream-utils': 2.0.0 + argue-cli: 3.2.0 - conventional-recommended-bump@11.2.0: + conventional-recommended-bump@12.1.0: dependencies: - '@conventional-changelog/git-client': 2.7.0(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.4.0) - conventional-changelog-preset-loader: 5.0.0 - conventional-commits-filter: 5.0.0 - conventional-commits-parser: 6.4.0 - meow: 13.2.0 + '@conventional-changelog/git-client': 3.2.0(conventional-commits-filter@6.0.1)(conventional-commits-parser@7.1.3) + argue-cli: 3.2.0 + conventional-changelog-preset-loader: 6.0.1 + conventional-commits-filter: 6.0.1 + conventional-commits-parser: 7.1.3 cross-spawn@7.0.6: dependencies: @@ -2423,25 +2477,29 @@ snapshots: detect-indent@7.0.2: {} - dot-prop@5.3.0: - dependencies: - is-obj: 2.0.0 - - dotenv@17.4.2: {} + dotenv@18.0.8: {} - dprint@0.54.0: + dprint@0.64.1: optionalDependencies: - '@dprint/darwin-arm64': 0.54.0 - '@dprint/darwin-x64': 0.54.0 - '@dprint/linux-arm64-glibc': 0.54.0 - '@dprint/linux-arm64-musl': 0.54.0 - '@dprint/linux-loong64-glibc': 0.54.0 - '@dprint/linux-loong64-musl': 0.54.0 - '@dprint/linux-riscv64-glibc': 0.54.0 - '@dprint/linux-x64-glibc': 0.54.0 - '@dprint/linux-x64-musl': 0.54.0 - '@dprint/win32-arm64': 0.54.0 - '@dprint/win32-x64': 0.54.0 + '@dprint/android-arm64': 0.64.1 + '@dprint/android-x64': 0.64.1 + '@dprint/darwin-arm64': 0.64.1 + '@dprint/darwin-x64': 0.64.1 + '@dprint/freebsd-arm64': 0.64.1 + '@dprint/freebsd-x64': 0.64.1 + '@dprint/linux-arm64-glibc': 0.64.1 + '@dprint/linux-arm64-musl': 0.64.1 + '@dprint/linux-loong64-glibc': 0.64.1 + '@dprint/linux-loong64-musl': 0.64.1 + '@dprint/linux-ppc64-glibc': 0.64.1 + '@dprint/linux-ppc64-musl': 0.64.1 + '@dprint/linux-riscv64-glibc': 0.64.1 + '@dprint/linux-riscv64-musl': 0.64.1 + '@dprint/linux-s390x-glibc': 0.64.1 + '@dprint/linux-x64-glibc': 0.64.1 + '@dprint/linux-x64-musl': 0.64.1 + '@dprint/win32-arm64': 0.64.1 + '@dprint/win32-x64': 0.64.1 emoji-regex@10.6.0: {} @@ -2605,7 +2663,7 @@ snapshots: graphmatch@1.1.1: {} - handlebars@4.7.9: + handlebars@4.7.10: dependencies: minimist: 1.2.8 neo-async: 2.6.2 @@ -2616,10 +2674,6 @@ snapshots: has-flag@4.0.0: {} - hosted-git-info@8.1.0: - dependencies: - lru-cache: 10.4.3 - hosted-git-info@9.0.2: dependencies: lru-cache: 11.3.5 @@ -2673,8 +2727,6 @@ snapshots: dependencies: is-extglob: 2.1.1 - is-obj@2.0.0: {} - is-plain-obj@4.1.0: {} is-ssh@1.4.1: @@ -2728,7 +2780,7 @@ snapshots: transitivePeerDependencies: - supports-color - libnpmpublish@11.2.0: + libnpmpublish@11.2.1: dependencies: '@npmcli/package-json': 7.0.5 ci-info: 4.4.0 @@ -2753,8 +2805,6 @@ snapshots: lodash.merge@4.6.2: {} - lru-cache@10.4.3: {} - lru-cache@11.3.5: {} make-fetch-happen@15.0.5: @@ -2774,8 +2824,6 @@ snapshots: transitivePeerDependencies: - supports-color - meow@13.2.0: {} - minimatch@10.2.5: dependencies: brace-expansion: 5.0.5 @@ -2847,12 +2895,6 @@ snapshots: dependencies: abbrev: 4.0.0 - normalize-package-data@7.0.1: - dependencies: - hosted-git-info: 8.1.0 - semver: 7.8.4 - validate-npm-package-license: 3.0.4 - npm-bundled@5.0.0: dependencies: npm-normalize-package-bin: 5.0.0 @@ -2924,7 +2966,7 @@ snapshots: p-try@2.2.0: {} - pacote@21.5.0: + pacote@21.5.1: dependencies: '@gar/promise-retry': 1.0.3 '@npmcli/git': 7.0.2 @@ -2942,7 +2984,7 @@ snapshots: proc-log: 6.1.0 sigstore: 4.1.0 ssri: 13.0.1 - tar: 7.5.16 + tar: 7.5.22 transitivePeerDependencies: - supports-color @@ -3014,6 +3056,8 @@ snapshots: semver@7.8.4: {} + semver@7.8.5: {} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -3054,18 +3098,8 @@ snapshots: source-map@0.6.1: {} - spdx-correct@3.2.0: - dependencies: - spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.23 - spdx-exceptions@2.5.0: {} - spdx-expression-parse@3.0.1: - dependencies: - spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.23 - spdx-expression-parse@4.0.0: dependencies: spdx-exceptions: 2.5.0 @@ -3083,6 +3117,11 @@ snapshots: get-east-asian-width: 1.5.0 strip-ansi: 7.2.0 + string-width@8.3.0: + dependencies: + get-east-asian-width: 1.5.0 + strip-ansi: 7.2.0 + strip-ansi@7.2.0: dependencies: ansi-regex: 6.2.2 @@ -3101,7 +3140,15 @@ snapshots: minizlib: 3.1.0 yallist: 5.0.0 - tinyexec@1.2.4: {} + tar@7.5.22: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.3 + minizlib: 3.1.0 + yallist: 5.0.0 + + tinyexec@1.3.1: {} tinyglobby@0.2.16: dependencies: @@ -3155,7 +3202,7 @@ snapshots: uglify-js@3.19.3: optional: true - undici-types@7.24.6: {} + undici-types@8.9.0: {} undici@6.25.0: {} @@ -3167,13 +3214,10 @@ snapshots: util-deprecate@1.0.2: {} - validate-npm-package-license@3.0.4: - dependencies: - spdx-correct: 3.2.0 - spdx-expression-parse: 3.0.1 - validate-npm-package-name@7.0.2: {} + verkit@0.5.0: {} + walk-up-path@4.0.0: {} which@2.0.2: @@ -3216,16 +3260,16 @@ snapshots: yallist@5.0.0: {} - yaml@2.9.0: {} + yaml@2.9.1: {} yargs-parser@22.0.0: {} - yargs@18.0.0: + yargs@18.2.0: dependencies: cliui: 9.0.1 escalade: 3.2.0 get-caller-file: 2.0.5 - string-width: 7.2.0 + string-width: 8.3.0 y18n: 5.0.8 yargs-parser: 22.0.0 diff --git a/tools/estree-oracle/package.json b/tools/estree-oracle/package.json index 69cc7634..3fea5f82 100644 --- a/tools/estree-oracle/package.json +++ b/tools/estree-oracle/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@tsslint/estree-oracle", - "version": "4.0.0-alpha.0", + "version": "4.0.0-alpha.1", "main": "index.js", "dependencies": { "@typescript-eslint/parser": "^8.72.0", From b8bb41111c73b12f8173c14a4c9bc0083f35473e Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 22 Oct 2026 20:24:56 +0800 Subject: [PATCH 17/52] docs(migration-v4): TypeScript 7 is a direct dependency --- docs/migration-v4.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/migration-v4.md b/docs/migration-v4.md index e93bd3e0..69c4d5c5 100644 --- a/docs/migration-v4.md +++ b/docs/migration-v4.md @@ -6,10 +6,11 @@ through TypeScript's own content mapper instead of Volar language plugins. ## Breaking changes -### 1. TypeScript 7 is a peer dependency +### 1. TypeScript 7 is bundled as a dependency -Install `typescript@^7.1.0-beta` next to TSSLint. There is no TypeScript 5/6 -code path and no compatibility layer. +TSSLint depends on TypeScript 7 directly, so there is nothing extra to +install, and it does not use the `typescript` your project compiles with. +There is no TypeScript 5/6 code path and no compatibility layer. - Node.js **22.12.0+** is required (the `typescript` 7 package needs it). - Diagnostics come from the TypeScript 7 checker. Output can differ from From e479bbf444fd91f536d658710fcdff9a0c5d2615 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 22 Oct 2026 20:29:48 +0800 Subject: [PATCH 18/52] docs: TypeScript 7 is bundled, drop it from install commands --- README.md | 4 ++-- packages/cli/README.md | 2 +- packages/config/README.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d2ea332e..ac720cc7 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ TSLint (TS-AST, deprecated 2019) → ESLint took over via `typescript-eslint` ## Setup ```bash -npm install @tsslint/cli @tsslint/config typescript@^7.1.0-beta --save-dev +npm install @tsslint/cli @tsslint/config --save-dev ``` `tsslint.config.ts`: @@ -246,7 +246,7 @@ Build your own with the `Plugin` type from `@tsslint/types`. ## Requirements - Node.js **22.12.0+** (`tsslint.config.ts` is loaded with `--experimental-strip-types` — no transpile step) -- `typescript@^7.1.0-beta` (peer dependency). TypeScript 5/6 are not supported; see [docs/migration-v4.md](docs/migration-v4.md) +- TypeScript 7 comes bundled as a dependency, separate from the `typescript` your project compiles with. TypeScript 5/6 are not supported; see [docs/migration-v4.md](docs/migration-v4.md) ## License diff --git a/packages/cli/README.md b/packages/cli/README.md index b68956b4..0b25fd85 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -5,7 +5,7 @@ Command-line runner for TSSLint. Lints TypeScript projects in CI or from the ter ## Usage ```bash -npm install @tsslint/cli typescript@^7.1.0-beta --save-dev +npm install @tsslint/cli --save-dev ``` ```bash diff --git a/packages/config/README.md b/packages/config/README.md index c545fc6a..29fe8280 100644 --- a/packages/config/README.md +++ b/packages/config/README.md @@ -5,7 +5,7 @@ Public API for `tsslint.config.ts`: `defineConfig`, `defineRule`, `definePlugin` ## Usage ```bash -npm install @tsslint/config typescript@^7.1.0-beta --save-dev +npm install @tsslint/config --save-dev ``` ```ts From e49875ec55633e10aba04a63256ff18ffc074a5f Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 22 Oct 2026 23:03:44 +0800 Subject: [PATCH 19/52] fix(compat-eslint): enter a parenthesized wrapper head once in every-kind scans ParenthesizedExpression, ParenthesizedType and ComputedPropertyName are pass-through in convertChildInner: materialise returns the inner ESTree. The scan relied on the child's target equalling the parent's to skip the re-entry, but when the inner is a wrapper head (`(a?.b)` -> ChainExpression, `(typeof import('x'))` -> TSTypeQuery) the parens level expands the chain and leaves its tail as parentTarget, so the child's visit entered the same chain again. In CPA and wildcard mode every listener on those nodes fired twice (three times under `((a?.b))`), e.g. no-non-null-asserted-optional-chain reported `(a?.b)!` twice. The three kinds now join the structural pass-through list and recurse with the parent's target unchanged. --- packages/compat-eslint/lib/ts-ast-scan.ts | 18 ++++++---- .../test/compat-pipeline.test.ts | 35 +++++++++++++++++++ 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/packages/compat-eslint/lib/ts-ast-scan.ts b/packages/compat-eslint/lib/ts-ast-scan.ts index d1192f88..57be3ed8 100644 --- a/packages/compat-eslint/lib/ts-ast-scan.ts +++ b/packages/compat-eslint/lib/ts-ast-scan.ts @@ -845,12 +845,8 @@ export function tsScanTraverse( const enterCb = (target: unknown) => visitor.enterNode(target); const leaveCb = (target: unknown) => visitor.leaveNode(target); // `parentTarget` is the most recent ESTree target we entered on the - // way down. ParenthesizedExpression / ComputedPropertyName / ParenthesizedType - // are pass-through in convertChildInner — materialise on those returns - // the INNER ESTree, not a wrapper. Visit then recurses into the - // parens' child (the same inner) and would fire enter/leave a second - // time on the same target. Skip when materialise yields the same - // ESTree we just entered. + // way down. A node whose materialise yields that same target (a TS + // kind that maps back onto its parent's ESTree) is not re-entered. // `embedAfter`: an extra TS child to visit INSIDE this node's enter/ // leave bracket, just before leaving. Used to inject typeAnnotation // from a parent ts.ParameterDeclaration / ts.VariableDeclaration into @@ -897,6 +893,16 @@ export function tsScanTraverse( // Recurse through with the CatchClause as parentTarget so the // inner Identifier visit lands with the right ESTree parent. || (k === SK.VariableDeclaration && node.parent?.kind === SK.CatchClause) + // Pass-through in convertChildInner: materialise returns the + // INNER ESTree, which the child visit enters anyway. When that + // inner is a wrapper head (`(a?.b)` → ChainExpression, + // `(typeof import('x'))` → TSTypeQuery) this level would enter + // the whole chain and leave its tail as parentTarget, so the + // child's own visit — same head, different tail — entered the + // chain a second time. + || k === SK.ParenthesizedExpression + || k === SK.ParenthesizedType + || k === SK.ComputedPropertyName ) { tsForEachChild(node, child => visit(child, parentTarget)); return; diff --git a/packages/compat-eslint/test/compat-pipeline.test.ts b/packages/compat-eslint/test/compat-pipeline.test.ts index 909be900..ac28ff47 100644 --- a/packages/compat-eslint/test/compat-pipeline.test.ts +++ b/packages/compat-eslint/test/compat-pipeline.test.ts @@ -2340,6 +2340,41 @@ type D = Awaited>; check('JSX rule: report points at the first ', reports[0]?.line === 2); } +// Pass-through parens / computed keys around a wrapper head: every-kind +// scans (CPA, wildcard) must enter the wrapper once, not once for the +// parens and again for the inner node. +{ + const code = ` +declare const a: any; +(a?.b)!; +((a?.c)); +const o = { [a?.d]: 1 }; +type T = (typeof import('./x')); +`; + const { program, file, checker } = buildProgram(code); + const hits: string[] = []; + const rule: ESLint.Rule.RuleModule = { + meta: { type: 'problem', schema: [], messages: { x: 'x' } } as any, + create: () => + ({ + onCodePathStart() {}, + ChainExpression(n: any) { + hits.push(`Chain@${n.range}`); + }, + TSTypeQuery(n: any) { + hits.push(`TypeQuery@${n.range}`); + }, + }) as any, + }; + const { threw } = runRule(rule, program, file, checker); + check('CPA pass-through: dispatch did not throw', threw === undefined, String(threw ?? '')); + check( + 'CPA pass-through: each wrapper head enters once', + hits.length === 4 && new Set(hits).size === 4, + hits.join(' '), + ); +} + console.log(); if (failures.length) { console.log('FAILURES:'); From 21d69a68e3b6df15b0c3a1dadf0ab572de256e1d Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Thu, 22 Oct 2026 23:03:44 +0800 Subject: [PATCH 20/52] fix(compat-eslint): wrap an optional chain ending in `!` in ChainExpression typescript-estree treats a non-null assertion that ends an optional chain (`a?.b!`, `a?.()!`, `a?.b.c!`, `a?.b!!`) as the chain's outermost link and wraps it in ChainExpression. The lazy converter only wrapped Member/CallExpression, so these produced a bare TSNonNullExpression and rules keyed on `ChainExpression > TSNonNullExpression` - notably no-non-null-asserted-optional-chain - silently missed them. isOutermostOptionalChain now accepts NonNullExpression (the ts-ast-scan ChainExpression predicate shares it), and TSNonNullExpression goes through wrapChainIfNeeded like the other links. Tests: lazy-estree-review-bugs compares 13 chain/non-null shapes with typescript-estree, plus parent links, bottom-up materialise and dispatch order for `a?.b!`; compat-pipeline runs the real no-non-null-asserted-optional-chain rule and requires the same reports as ESLint on typescript-eslint's parser. The estree-oracle test package now carries @typescript-eslint/eslint-plugin for that. --- packages/compat-eslint/lib/lazy-estree.ts | 24 ++++--- .../test/compat-pipeline.test.ts | 42 +++++++++++ .../test/lazy-estree-review-bugs.test.ts | 62 ++++++++++++++++ pnpm-lock.yaml | 70 +++++++++++++++++++ tools/estree-oracle/index.d.ts | 1 + tools/estree-oracle/index.js | 3 + tools/estree-oracle/package.json | 1 + 7 files changed, 195 insertions(+), 8 deletions(-) diff --git a/packages/compat-eslint/lib/lazy-estree.ts b/packages/compat-eslint/lib/lazy-estree.ts index 3160f424..cc95fd1a 100644 --- a/packages/compat-eslint/lib/lazy-estree.ts +++ b/packages/compat-eslint/lib/lazy-estree.ts @@ -2025,10 +2025,17 @@ defineShape(SK.ConditionalExpression, { alternate: { tsField: 'whenFalse' }, }, }); -defineShape(SK.NonNullExpression, { +// `a?.b!` is itself the outermost chain link, so it runs through +// wrapChainIfNeeded like Member/CallExpression. +const nonNullExpressionShape = makeShapeClass({ type: 'TSNonNullExpression', slots: { expression: { tsField: 'expression' } }, }); +defineShapeRouter( + SK.NonNullExpression, + (tsNode, parent) => + wrapChainIfNeeded(new nonNullExpressionShape(tsNode, parent), tsNode as ts.NonNullExpression, parent), +); defineShape(SK.ExternalModuleReference, { type: 'TSExternalModuleReference', slots: { expression: { tsField: 'expression' } }, @@ -4152,6 +4159,7 @@ export function isOutermostOptionalChain(n: ts.Node): boolean { n.kind !== SK.PropertyAccessExpression && n.kind !== SK.ElementAccessExpression && n.kind !== SK.CallExpression + && n.kind !== SK.NonNullExpression ) { return false; } @@ -4168,16 +4176,16 @@ export function isOutermostOptionalChain(n: ts.Node): boolean { // that the eventual outer build would have to unwrap and re-point. function wrapChainIfNeeded( result: LazyNode, - tsNode: ts.PropertyAccessExpression | ts.ElementAccessExpression | ts.CallExpression, + tsNode: ts.PropertyAccessExpression | ts.ElementAccessExpression | ts.CallExpression | ts.NonNullExpression, parent: LazyNode | null | undefined, ): LazyNode { if (!isOutermostOptionalChain(tsNode)) return result; - // Force the inner `object`/`callee` getter before the wrapper claims the TS - // slot, so the inner access parents to `result` (the Member/CallExpression), - // matching eager — without it a bottom-up `materialize()` of an inner node - // resolves its parent up to the cache-claimed wrapper instead. - const r = result as unknown as { type: string; object?: LazyNode; callee?: LazyNode }; - void (r.type === 'MemberExpression' ? r.object : r.callee); + // Force the inner `object`/`callee`/`expression` getter before the wrapper + // claims the TS slot, so the inner access parents to `result`, matching + // eager — without it a bottom-up `materialize()` of an inner node resolves + // its parent up to the cache-claimed wrapper instead. + const r = result as unknown as { type: string; object?: LazyNode; callee?: LazyNode; expression?: LazyNode }; + void (r.type === 'MemberExpression' ? r.object : r.type === 'CallExpression' ? r.callee : r.expression); return new ChainExpressionWrappingNode(tsNode, parent, result); } diff --git a/packages/compat-eslint/test/compat-pipeline.test.ts b/packages/compat-eslint/test/compat-pipeline.test.ts index ac28ff47..143632c2 100644 --- a/packages/compat-eslint/test/compat-pipeline.test.ts +++ b/packages/compat-eslint/test/compat-pipeline.test.ts @@ -2375,6 +2375,48 @@ type T = (typeof import('./x')); ); } +// typescript-eslint's no-non-null-asserted-optional-chain listens on +// `ChainExpression > TSNonNullExpression` (`a?.b!`) and +// `TSNonNullExpression > ChainExpression` (`(a?.b)!`). Reports must match +// ESLint running the same rule on typescript-eslint's parser. +{ + const oracle = require('@tsslint/estree-oracle') as typeof import('@tsslint/estree-oracle'); + const rule = oracle.eslintPlugin.rules['no-non-null-asserted-optional-chain'] as unknown as ESLint.Rule.RuleModule; + const code = ` +declare const maybe: { a?: { b: number } } | undefined; +declare const fn: (() => { b: number }) | undefined; +maybe?.a!; +maybe?.a?.b!; +fn?.()!; +maybe?.["a"]!; +(maybe?.a)!; +(fn?.())!; +maybe?.a!.b; +maybe!.a; +`; + const linter = new oracle.eslint.Linter({ configType: 'flat' }); + const lines = code.split('\n'); + const offsetOf = (line: number, column: number) => + lines.slice(0, line - 1).reduce((n, l) => n + l.length + 1, 0) + column - 1; + const want = linter.verify(code, [{ + files: ['**/*.ts'], + languageOptions: { parser: oracle.parser as any }, + plugins: { ts: { rules: { r: rule as any } } }, + rules: { 'ts/r': 'error' }, + }], 'test.ts').map(m => `${offsetOf(m.line, m.column)}-${offsetOf(m.endLine!, m.endColumn!)}`).sort(); + + const { program, file, checker } = buildProgram(code); + const { reports, threw } = runRule(rule, program, file, checker); + const got = reports.map(r => `${r.start}-${r.end}`).sort(); + check('no-non-null-asserted-optional-chain: dispatch did not throw', threw === undefined, String(threw ?? '')); + check('no-non-null-asserted-optional-chain: ESLint reports all six', want.length === 6, `ESLint got ${want.length}`); + check( + 'no-non-null-asserted-optional-chain: reports match ESLint', + JSON.stringify(got) === JSON.stringify(want), + `got ${JSON.stringify(got)} want ${JSON.stringify(want)}`, + ); +} + console.log(); if (failures.length) { console.log('FAILURES:'); diff --git a/packages/compat-eslint/test/lazy-estree-review-bugs.test.ts b/packages/compat-eslint/test/lazy-estree-review-bugs.test.ts index 10d07242..54109dff 100644 --- a/packages/compat-eslint/test/lazy-estree-review-bugs.test.ts +++ b/packages/compat-eslint/test/lazy-estree-review-bugs.test.ts @@ -173,6 +173,68 @@ for (const code of ['@dec export class C {}', '@dec export default class C {}', // control: the whole `a?.b.c` tree has exactly one ChainExpression (outermost only). fixed('a?.b.c has exactly one ChainExpression', count(lazyAst('a?.b.c;'), 'ChainExpression') === 1); +// E — a non-null assertion that ends an optional chain (`a?.b!`) is the chain's +// outermost link: typescript-estree wraps it in ChainExpression +// (no-non-null-asserted-optional-chain's `ChainExpression > TSNonNullExpression`). +{ + const shape = (n: any): unknown => { + if (!n || typeof n !== 'object') return n; + if (Array.isArray(n)) return n.map(shape); + const out: Record = { type: n.type, range: n.range }; + if ('optional' in n) out.optional = n.optional; + for (const k of childKeys(n)) if (k !== 'parent' && n[k] != null) out[k] = shape(n[k]); + return out; + }; + for ( + const code of [ + 'a?.b!;', + 'a?.()!;', + 'a?.[0]!;', + 'a?.b.c!;', + 'a?.b!!;', + '(a?.b!);', + 'a?.b!.c;', + 'a?.b!();', + 'a?.b![0];', + '(a?.b)!;', + 'a!?.b;', + 'a?.b!?.c!;', + 'a!;', + ] + ) { + const got = JSON.stringify(shape(firstExpr(lazyAst(code)))); + const want = JSON.stringify(shape(firstExpr(eagerAst(code)))); + fixed(`E ${code} shape matches typescript-estree`, got === want, got === want ? '' : `got ${got} want ${want}`); + } + const chain = firstExpr(lazyAst('a?.b!;')); + fixed( + 'E a?.b! wrapper links parent both ways', + chain.type === 'ChainExpression' && chain.expression.parent === chain + && chain.expression.expression.parent === chain.expression, + ); + const sf = sfOf('a?.b!;'); + const ctx = (lazy.convertLazy(sf) as any).context; + const inner = lazy.materialize(findTs(sf, n => ts.isPropertyAccessExpression(n)), ctx) as any; + fixed( + 'E bottom-up a?.b in a?.b! parents through TSNonNullExpression to ChainExpression', + inner.type === 'MemberExpression' && inner.parent?.type === 'TSNonNullExpression' + && inner.parent.parent?.type === 'ChainExpression', + `got ${inner.type} > ${inner.parent?.type} > ${inner.parent?.parent?.type}`, + ); + const order: string[] = []; + scan.tsScanTraverse(sf, scan.predicateAllKinds(), ctx, { + enterNode(t: any) { + order.push(t.type); + }, + leaveNode() {}, + }); + fixed( + 'E a?.b! dispatches ChainExpression > TSNonNullExpression > MemberExpression', + order.join(' ').includes('ChainExpression TSNonNullExpression MemberExpression'), + order.join(' '), + ); +} + // Guard — materialize(catch variableDeclaration) is the param Identifier or throws; never a phantom VariableDeclarator. { const sf = sfOf('try {} catch (e) {}'); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 77ceec48..3b3c1468 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -115,6 +115,9 @@ importers: tools/estree-oracle: dependencies: + '@typescript-eslint/eslint-plugin': + specifier: ^8.72.0 + version: 8.72.0(@typescript-eslint/parser@8.72.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) '@typescript-eslint/parser': specifier: ^8.72.0 version: 8.72.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) @@ -588,6 +591,14 @@ packages: resolution: {integrity: sha512-EULJ8LApcVEPbrfND0cRQqutIOdiIgJ1Mgrhpy755r14xMohPTEpkV/k28SJvuOs9bHRFW8x+KeDAEPiGQPB9Q==} deprecated: This is a stub types definition. parse-path provides its own type definitions, so you do not need this installed. + '@typescript-eslint/eslint-plugin@8.72.0': + resolution: {integrity: sha512-aysKgysPX9XOv/dk95UkvUDe++Q3n3dJPG9CEWT3RUccxfkPJ+Qn6nhKPDhZXU6Ofo9kgwGWwKyQCnZIXTgngg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.72.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/parser@8.72.0': resolution: {integrity: sha512-0O+x3ynciD6zuXb1CKSu165haQV6ey53DKhHHUnujP7U+XlltJG+/keT0Xhd1HXCze+IHf7Tvt39ovyepX/LNw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -611,6 +622,13 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/type-utils@8.72.0': + resolution: {integrity: sha512-gEXxmmbSTSOVTC37IYYvremOM31v+UN2W772bJxmVYPhAOPSUxtacyb7wkm1EhkYKU6KzFJsdU44DCV4to0LWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/types@8.72.0': resolution: {integrity: sha512-Adfoy59kxNEjT5E5fnyrp7GpHYuWs8ONbdohu4ez6IU46qDq8LltG90tLmkucZB2uhYL8mqBjtXqGd8yhfjXqQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -621,6 +639,13 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/utils@8.72.0': + resolution: {integrity: sha512-0up3oArJZBYnzOaEXKo8DmI43QvMZyPoFEt9LWzRJcyl/RcJjfKKPebknN1VbTgXBf3M/KggUh5uUMdURLnseg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/visitor-keys@8.72.0': resolution: {integrity: sha512-E2mroviasaWecDzTGiGMtuEguuLVOeRggnrIn93CJuxYJzQBHbyymlT08aVQydbBal5538o+JkBKXh2sRuCyIA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1116,6 +1141,10 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} + ignore@7.0.13: + resolution: {integrity: sha512-2E8Q6rh+1RRSq/YdaGsNfXsEZy9FVqv6BSSrkmZJP7BnaoPnRfKL+YaofPvP+5rzdk144qSm6i4+hvPgUcbV4Q==} + engines: {node: '>= 4'} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -2209,6 +2238,22 @@ snapshots: dependencies: parse-path: 7.1.0 + '@typescript-eslint/eslint-plugin@8.72.0(@typescript-eslint/parser@8.72.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3))(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.72.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.72.0 + '@typescript-eslint/type-utils': 8.72.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) + '@typescript-eslint/utils': 8.72.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.72.0 + eslint: 9.39.4(jiti@2.6.1) + ignore: 7.0.13 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/parser@8.72.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3)': dependencies: '@typescript-eslint/scope-manager': 8.72.0 @@ -2239,6 +2284,18 @@ snapshots: dependencies: typescript: 6.0.3 + '@typescript-eslint/type-utils@8.72.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3)': + dependencies: + '@typescript-eslint/types': 8.72.0 + '@typescript-eslint/typescript-estree': 8.72.0(typescript@6.0.3) + '@typescript-eslint/utils': 8.72.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3) + debug: 4.4.3 + eslint: 9.39.4(jiti@2.6.1) + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/types@8.72.0': {} '@typescript-eslint/typescript-estree@8.72.0(typescript@6.0.3)': @@ -2256,6 +2313,17 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/utils@8.72.0(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.72.0 + '@typescript-eslint/types': 8.72.0 + '@typescript-eslint/typescript-estree': 8.72.0(typescript@6.0.3) + eslint: 9.39.4(jiti@2.6.1) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/visitor-keys@8.72.0': dependencies: '@typescript-eslint/types': 8.72.0 @@ -2705,6 +2773,8 @@ snapshots: ignore@5.3.2: {} + ignore@7.0.13: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 diff --git a/tools/estree-oracle/index.d.ts b/tools/estree-oracle/index.d.ts index 11327592..115f2326 100644 --- a/tools/estree-oracle/index.d.ts +++ b/tools/estree-oracle/index.d.ts @@ -2,6 +2,7 @@ export declare const ts: typeof import('typescript'); export declare const typescriptEstree: typeof import('@typescript-eslint/typescript-estree'); export declare const typescriptEstreeInternals: any; +export declare const eslintPlugin: typeof import('@typescript-eslint/eslint-plugin'); export declare const parser: typeof import('@typescript-eslint/parser'); export declare const visitorKeys: typeof import('@typescript-eslint/visitor-keys'); export declare const eslint: typeof import('eslint'); diff --git a/tools/estree-oracle/index.js b/tools/estree-oracle/index.js index 646f0e56..4c07ad24 100644 --- a/tools/estree-oracle/index.js +++ b/tools/estree-oracle/index.js @@ -12,6 +12,9 @@ module.exports = { get typescriptEstreeInternals() { return require('@typescript-eslint/typescript-estree/use-at-your-own-risk'); }, + get eslintPlugin() { + return require('@typescript-eslint/eslint-plugin'); + }, get parser() { return require('@typescript-eslint/parser'); }, diff --git a/tools/estree-oracle/package.json b/tools/estree-oracle/package.json index 3fea5f82..439f76ac 100644 --- a/tools/estree-oracle/package.json +++ b/tools/estree-oracle/package.json @@ -4,6 +4,7 @@ "version": "4.0.0-alpha.1", "main": "index.js", "dependencies": { + "@typescript-eslint/eslint-plugin": "^8.72.0", "@typescript-eslint/parser": "^8.72.0", "@typescript-eslint/typescript-estree": "^8.72.0", "@typescript-eslint/visitor-keys": "^8.72.0", From fb32e5324038c1c579465aa1f123a22ee37a90d5 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sun, 25 Oct 2026 09:39:36 +0800 Subject: [PATCH 21/52] fix(core): suppress rule reports inside content mapper diagnostic directives Ignore and Expect directives from file.diagnosticDirectives both drop rule reports that start inside their virtual range. TypeScript reports unused Expect directives itself, so tsslint adds no unused report. --- packages/cli/test/integration.test.ts | 122 +++++++++++++++++++++++--- packages/core/index.ts | 14 ++- 2 files changed, 122 insertions(+), 14 deletions(-) diff --git a/packages/cli/test/integration.test.ts b/packages/cli/test/integration.test.ts index c5c134de..d2e7009c 100644 --- a/packages/cli/test/integration.test.ts +++ b/packages/cli/test/integration.test.ts @@ -362,10 +362,9 @@ function markerLineCount(markerPath: string): number { } } -// A project whose `.vue` files go through a minimal tsconfig content -// mapper: the `')); - return { text: body + '\\nexport {};\\n', extension: '.ts', mappings: [[0, body.length, start, body.length, 0, 1048575]] }; - } + if (method === 'transform') return transform(params.content); return null; } function respond(id, result) { @@ -396,10 +390,21 @@ function respond(id, result) { process.stdout.write('Content-Length: ' + body.length + '\\r\\n\\r\\n'); process.stdout.write(body); } +${transform} `; -function makeContentMapperFixture(newLine: string): { dir: string; vue: string } { - const dir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'tsslint-int-cm-'))); +// The `')); + return { text: body + '\\nexport {};\\n', extension: '.ts', mappings: [[0, body.length, start, body.length, 0, 1048575]] }; +} +`); + +function writeMapperProject(dir: string, server: string) { const mapperDir = path.join(dir, 'node_modules', 'test-mapper'); fs.mkdirSync(mapperDir, { recursive: true }); fs.writeFileSync( @@ -410,7 +415,7 @@ function makeContentMapperFixture(newLine: string): { dir: string; vue: string } typescript: { contentMapper: { exec: ['node', 'server.js'], dynamicConfig: true } }, }), ); - fs.writeFileSync(path.join(mapperDir, 'server.js'), MAPPER_SERVER); + fs.writeFileSync(path.join(mapperDir, 'server.js'), server); fs.writeFileSync( path.join(dir, 'tsconfig.json'), JSON.stringify({ @@ -419,6 +424,11 @@ function makeContentMapperFixture(newLine: string): { dir: string; vue: string } include: ['*.vue'], }), ); +} + +function makeContentMapperFixture(newLine: string): { dir: string; vue: string } { + const dir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'tsslint-int-cm-'))); + writeMapperProject(dir, MAPPER_SERVER); fs.writeFileSync( path.join(dir, 'tsslint.config.ts'), `import { defineConfig, defineRule } from ${importPath(configModule)};\n` @@ -486,6 +496,92 @@ function stripAnsi(text: string): string { } } +// ── Test 12 (content mapper): diagnostic directives suppress reports ─── +// +// Shaped like Vue's output: template expressions are appended to the +// script inside a generated function, `@vue-ignore` / `@vue-expect-error` +// become Ignore / Expect directives over the next expression, and the +// generated code between the script and the first expression is covered +// by a synthesized Ignore (no original range). `__VLS_template` maps to +// the `template` tag name with no language features, so without the +// synthesized Ignore a report on it would land on `