diff --git a/.changeset/typescript-six.md b/.changeset/typescript-six.md new file mode 100644 index 00000000..9adb7759 --- /dev/null +++ b/.changeset/typescript-six.md @@ -0,0 +1,5 @@ +--- +'uptide': minor +--- + +`check typescript` judges a TypeScript 5 to 6 upgrade with both compilers: every project of the workspace is compiled with the installed TypeScript and with the target's, and what is new at the target is a finding; the bundled compiler never stands in, and a target without a JavaScript API (TypeScript 7) is reported as not compiled. A candidate typescript 5 to 6 pack reports the tsconfig options 6.0 deprecates where they are set, the rootDir default, module-keyword namespaces and import assertions. A peer that rejects the target (typescript-eslint on TypeScript 6) is now reported as a peer conflict even when it is not grouped with the package. diff --git a/README.md b/README.md index d7bcae35..e8c6078d 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ The contract: [migration packs](docs/packs.md). | `@sentry/nextjs` | 10.x → 11.x | 100% | 100% | 8 | cohstats/coh3-stats, xdoubleu/tools.xdoubleu.com, maximunited/yomu | verified | | `@sentry/react` | 10.x → 11.x | 100% | 100% | 2 | owine/youth-activity-scheduler, successible/cleanslate | candidate | | `stripe` | 14 and newer | 100% | 90% | 16 | unkeyed/unkey, nextjs/saas-starter | verified | +| `typescript` | 5.x → 6.x | 100% | 17% | 1399 | m-lyon/recipe, robrechtme/recipes, JosephMaynard/dependency-radar, CroffZ/github-release-notifier, SignalK/signalk-derived-data, shogo82148/typescript-action, Borewit/music-metadata | candidate | | `vitest` | 4.x → 5.x | 100% | 50% | 32 | votingworks/vxsuite, vitorvasc/opentelemetry-ecosystem-explorer | verified | | `zod` | 3.x → 4.x | 100% | 74% | 85 | ugurkocde/AwesomeIntune, howardyang2009/PATH, band-ai/band-sdk-typescript | verified | diff --git a/docs/packs.md b/docs/packs.md index 81b9fb8b..a1b321d3 100644 --- a/docs/packs.md +++ b/docs/packs.md @@ -135,7 +135,12 @@ site is reported is part of the contract (`reported`): - `finding`: `check` lists the site as the pack's own finding (evidence `pack`), found by the note's `detect`; `fix` never edits it unasked. Example: a Stripe client created - without `apiVersion`, whose API version the SDK bump changes at runtime. + without `apiVersion`, whose API version the SDK bump changes at runtime. A pack may + instead create the note's findings with `foldFindings`, which runs once the pack's + findings are claimed and folds compiler errors that one edit elsewhere resolves into a + finding at that edit, as their evidence. The typescript pack folds the "Cannot find + name" errors of the 6.0 `types: []` default into one at the tsconfig that leaves + `types` unset. - `decision`: the pull request lists it under "Decisions for you", with the files and lines. - `test-follow-up`: an assertion is updated only after the migrated code made that test fail, and the test then has to pass (zod's reworded default messages). @@ -185,17 +190,21 @@ repository upgraded to. Where the expected findings come from: two sources that are not Uptide. -1. **The compiler.** The repository's own TypeScript, run on every `tsconfig.json`, with the - target version linked where the package is installed and without it. Every diagnostic +1. **The compiler.** The repository's own TypeScript, run on every project `check` compiles + (the TypeScript project scope: each `tsconfig*.json` and its references, configs that are + only extended left out), with the target version linked where the package is installed + and without it. Every diagnostic new at the target is a site: errors, and deprecations (6385, 6387, what an editor strikes through). A repository on TypeScript 7, which has no JavaScript API, is compiled with the TypeScript of this checkout, and the draft says so. The target is linked with its own dependencies at the versions it declares, as an install would leave it, and `--also @` links the companions the upgrade commit moved with it (the versions in its lockfile): `ai` 7 next to `@ai-sdk/react` 3 is a mix no repository ships, - and its errors are not the upgrade's. + and its errors are not the upgrade's. The tsconfig's own errors (a deprecated option, + TS5101) are listed too, where the compiler points: for an option an extended config sets, + that is the extending config, and the site is where the option is set. 2. **The upgrade commit.** The lines it changed or removed (`git diff -U0 `, - the old side). One site per change: consecutive changed lines of the same change are one + the old side), in source files and `tsconfig*.json`. One site per change: consecutive changed lines of the same change are one site, at the first line of code (a line that is plainly another rule's starts a new one); a run with a diagnostic inside it, or right after a diagnostic of the same rule, is that diagnostic's site. Comment-only lines and changes for another package are not sites. @@ -412,6 +421,17 @@ PR's **Verified by** row. Reverification reruns the required build. Existing pac without this declaration retain their current behavior. Next 16 requires this gate because custom webpack configuration can fail with the default Turbopack build. +### When the package is the compiler + +`typescript` is the one package no overlay can swap: it judges every file, not the ones that +import it. For it, `check` compiles each of the workspace's projects (the TypeScript project +scope: every `tsconfig*.json` and its references) twice: with the repository's installed +TypeScript, and with the target's, loaded from the copy `check` fetched. A diagnostic new at the +target is a finding. The bundled compiler never stands in for either; a target with no +JavaScript API (TypeScript 7) is not compiled, and the report says so. Diagnostics on tsconfig +files are the pack's own findings, from its inventory, in the file that sets the option. The +typescript pack's RESEARCH.md has the details. + ### Release groups Use `releaseGroup` for packages published in lockstep with the pack's lead. Like diff --git a/packages/core/src/adapters/typescript/compile.ts b/packages/core/src/adapters/typescript/compile.ts index a5b2a5c2..abf73957 100644 --- a/packages/core/src/adapters/typescript/compile.ts +++ b/packages/core/src/adapters/typescript/compile.ts @@ -21,10 +21,16 @@ import type { import { satisfies } from '../../fetch/range.js'; import { onReset } from '../../shared-state.js'; import { augmentationCause, findCause, parameterCause } from './cause.js'; -import { type Compiler, repositoryCompiler, unresolvedConfig } from './compiler.js'; +import { + type Compiler, + consumerCompilerDir, + loadCompiler, + repositoryCompiler, + unresolvedConfig, +} from './compiler.js'; import { jsxNamespaceCause } from './config-cause.js'; import { type CompileProject, projectScope } from './project-scope.js'; -import { type LoadedRepo, loadedRepo, loadRepo, ownsFile } from './repo.js'; +import { inheritedTsconfigOf, type LoadedRepo, loadedRepo, loadRepo, ownsFile } from './repo.js'; import { consumerCopySatisfies, type DependencyLinks, @@ -787,6 +793,9 @@ export async function compileAgainstTargets( targets: Target[], options: CompileOptions = {}, ): Promise { + // The compiler itself is the upgrade: both sides compile whole projects (compileCompilerUpgrade). + const compilerTarget = targets.find((t) => t.name === 'typescript'); + if (compilerTarget) return compileCompilerUpgrade(repoRef, compilerTarget); const dir = realpathSync(repoRef.dir); const scope = projectScope({ ...repoRef, dir }); if (scope.projects.length === 0) { @@ -1129,3 +1138,126 @@ async function compileProject( rmSync(overlay, { recursive: true, force: true }); } } + +/** + * The compiler is the upgrade (`typescript` 5 to 6): no overlay can swap it in, since it judges + * every file, not the ones importing it. Each side compiles the workspace's projects whole: the installed side with the repository's own TypeScript, the + * target side with the target's, loaded from the copy `check` fetched (the release `fix` + * installs). Never the bundled compiler: one the repository neither has nor will have reports + * errors nobody sees. A target without a JavaScript API (TypeScript 7) is not compiled, and + * the report says so. Diagnostics on the tsconfig files themselves (TS5101, TS5107) are left to + * the pack, which reports each where the option is set, in the file that sets it. + */ +function compileCompilerUpgrade(repoRef: RepoDir, target: Target): CompileSignal { + const dir = realpathSync(repoRef.dir); + const skip = (skipped: string, compilers: CompileCoverage['compilers'] = []): CompileSignal => ({ + diagnostics: [], + baselineErrors: 0, + skipped, + coverage: { compiled: 0, total: 0, skipped: [{ reason: skipped, count: 0 }], compilers }, + unresolvedInTarget: [], + unresolvedFiles: [], + linkedDependencies: [], + unsatisfiedDependencies: [], + timing: { baselineMs: 0, overlayMs: 0, dependenciesMs: 0 }, + }); + const installedDir = consumerCompilerDir(dir); + if (installedDir === undefined) + return skip('not compiled: the repository installs no TypeScript to compare the target with'); + const before = loadCompiler(installedDir); + let after: typeof ts; + try { + after = loadCompiler(target.dir); + } catch { + return skip(`not compiled: typescript at ${target.dir} cannot be loaded`); + } + const compilers = [ + { version: before.version, own: true }, + { version: after.version ?? '?', own: true }, + ]; + if (typeof after.createProgram !== 'function') + return skip( + `not compiled: typescript ${after.version ?? 'target'} has no JavaScript compiler API, so its diagnostics cannot be computed; tsconfig findings still come from the pack`, + compilers, + ); + // The workspace's projects as the TypeScript project scope finds them (project-scope.ts: every + // tsconfig*.json, their references, configs only extended left out): those inside this + // workspace, or the one it inherits. A project whose `extends` is not installed is not built. + const scope = projectScope({ ...repoRef, dir }); + const inherited = existsSync(join(dir, 'tsconfig.json')) ? undefined : inheritedTsconfigOf(dir); + const configs = scope.projects + .map((p) => p.config) + .filter( + (config) => + (config.startsWith(`${dir}/`) || config === inherited) && + !(repoRef.exclude ?? []).some((x) => config.startsWith(`${x}/`)) && + unresolvedConfig(before, config) === undefined, + ); + if (configs.length === 0) return skip('not compiled: no tsconfig.json', compilers); + + /** The programs of the workspace's projects, built by one compiler. */ + const programs = (tsc: typeof ts): ts.Program[] => + configs.flatMap((config) => { + const parsed = tsc.getParsedCommandLineOfConfigFile( + config, + {}, + { ...tsc.sys, onUnRecoverableConfigFileDiagnostic: () => {} }, + ); + if (!parsed || parsed.fileNames.length === 0) return []; + return [ + tsc.createProgram({ + rootNames: parsed.fileNames, + options: { ...parsed.options, noEmit: true, skipLibCheck: true }, + ...(parsed.projectReferences ? { projectReferences: parsed.projectReferences } : {}), + }), + ]; + }); + // The project scope applies to compiler diagnostics as it does for any package: a file no + // project covers is never a site. Pack findings (the tsconfig files themselves) are not these. + const own = (f: ts.SourceFile): boolean => + !f.isDeclarationFile && + !f.fileName.includes('/node_modules/') && + f.fileName.startsWith(`${dir}/`) && + ownsFile(repoRef, f.fileName) && + scope.covers(f.fileName); + const errorsOf = (program: ts.Program, file: ts.SourceFile): ts.Diagnostic[] => + [...program.getSyntacticDiagnostics(file), ...program.getSemanticDiagnostics(file)].filter( + (d) => isRepoError(d, dir), + ); + + const t0 = Date.now(); + const baseline: ts.Diagnostic[] = []; + for (const program of programs(before)) + for (const file of program.getSourceFiles().filter(own)) + baseline.push(...errorsOf(program, file)); + const baselineMs = Date.now() - t0; + const known = new Set(baseline.map((d) => keyOf(d, dir))); + const knownPlaces = new Set(baseline.map((d) => placeOf(d, dir))); + const t1 = Date.now(); + const fresh = new Map(); + const compiled = new Set(); + for (const program of programs(after)) + for (const file of program.getSourceFiles().filter(own)) { + compiled.add(file.fileName); + for (const d of errorsOf(program, file)) { + if (known.has(keyOf(d, dir)) || knownPlaces.has(placeOf(d, dir))) continue; + const diagnostic = toDiagnostic(d, file, dir); + fresh.set( + `${diagnostic.file}:${diagnostic.line}:${diagnostic.column}:${d.code}`, + diagnostic, + ); + } + } + return { + diagnostics: [...fresh.values()].sort( + (a, b) => a.file.localeCompare(b.file) || a.line - b.line || a.column - b.column, + ), + baselineErrors: baseline.length, + coverage: { compiled: compiled.size, total: compiled.size, skipped: [], compilers }, + unresolvedInTarget: [], + unresolvedFiles: [], + linkedDependencies: [], + unsatisfiedDependencies: [], + timing: { baselineMs, overlayMs: Date.now() - t1, dependenciesMs: 0 }, + }; +} diff --git a/packages/core/src/adapters/typescript/compiler-upgrade.test.ts b/packages/core/src/adapters/typescript/compiler-upgrade.test.ts new file mode 100644 index 00000000..64719207 --- /dev/null +++ b/packages/core/src/adapters/typescript/compiler-upgrade.test.ts @@ -0,0 +1,100 @@ +import { mkdirSync, mkdtempSync, symlinkSync, writeFileSync } from 'node:fs'; +import { createRequire } from 'node:module'; +import { tmpdir } from 'node:os'; +import { dirname, join } from 'node:path'; +import { describe, expect, it } from 'vitest'; +import { createNpmFetcher } from '../../fetch/npm-fetcher.js'; +import { compileAgainstTargets } from './compile.js'; +import { consumerCompilerDir } from './compiler.js'; + +/** + * `typescript` as the upgraded package: both sides compile the workspace's projects whole, + * the installed side with the repository's compiler and the target side with the target's. + */ +const devCompiler = dirname(createRequire(import.meta.url).resolve('typescript/package.json')); + +function repo(files: Record, compiler: string | undefined = devCompiler): string { + const dir = mkdtempSync(join(tmpdir(), 'uptide-compiler-upgrade-')); + for (const [file, text] of Object.entries(files)) { + mkdirSync(dirname(join(dir, file)), { recursive: true }); + writeFileSync(join(dir, file), text); + } + if (compiler) { + mkdirSync(join(dir, 'node_modules'), { recursive: true }); + symlinkSync(compiler, join(dir, 'node_modules', 'typescript'), 'dir'); + } + return dir; +} + +const project = { + 'package.json': JSON.stringify({ name: 'app', devDependencies: { typescript: '5.9.3' } }), + 'tsconfig.json': JSON.stringify({ + compilerOptions: { target: 'es2020', module: 'commonjs', baseUrl: '.', noEmit: true }, + include: ['src'], + }), + // Nothing imports `typescript`: every file is judged by the compiler all the same. + 'src/a.ts': 'module Legacy { export const x = 1; }\nexport const y = Legacy.x;\n', + // A project the root tsconfig never names is still built. + 'tools/tsconfig.json': JSON.stringify({ + compilerOptions: { module: 'commonjs', noEmit: true }, + include: ['*.ts'], + }), + 'tools/b.ts': 'export function f(a, b?: string) { return b.length + a; }\n', + // Outside every project: the project scope (UPT-49) keeps its compiler errors out. + 'examples/c.ts': 'module Outside { export const z = 3; }\n', +}; + +describe('compiling a TypeScript upgrade', () => { + it('compiles every project of the workspace, not only files importing the package', async () => { + // The same compiler on both sides: nothing is new, and every file was compiled. + const signal = await compileAgainstTargets({ dir: repo(project) }, [ + { name: 'typescript', dir: devCompiler }, + ]); + expect(signal.skipped).toBeUndefined(); + expect(signal.diagnostics).toEqual([]); + expect(signal.coverage.compiled).toBe(2); + expect(signal.coverage.compilers?.every((c) => c.own)).toBe(true); + }); + + it('never falls back to the bundled compiler when the repository installs none', async () => { + const dir = repo(project, undefined); + const signal = await compileAgainstTargets({ dir }, [{ name: 'typescript', dir: devCompiler }]); + // A machine may have a TypeScript in a folder above the temp directory, which Node would + // find too; either way it is an installed one, never the bundled compiler. + if (consumerCompilerDir(dir) === undefined) + expect(signal.skipped).toMatch(/installs no TypeScript/); + else expect(signal.coverage.compilers?.every((c) => c.own)).toBe(true); + expect(signal.diagnostics).toEqual([]); + }); + + it('says so, and compiles nothing, when the target has no JavaScript API (TypeScript 7)', async () => { + const seven = mkdtempSync(join(tmpdir(), 'uptide-ts7-')); + writeFileSync( + join(seven, 'package.json'), + JSON.stringify({ name: 'typescript', version: '7.0.2', main: 'version.cjs' }), + ); + writeFileSync(join(seven, 'version.cjs'), "module.exports = { version: '7.0.2' };\n"); + const signal = await compileAgainstTargets({ dir: repo(project) }, [ + { name: 'typescript', dir: seven }, + ]); + expect(signal.skipped).toMatch(/typescript 7\.0\.2 has no JavaScript compiler API/); + expect(signal.diagnostics).toEqual([]); + }); +}); + +describe.skipIf(!process.env.UPTIDE_NETWORK)('compiling against TypeScript 6.0.3', () => { + it('reports what the target compiler rejects, from its own diagnostics', async () => { + const six = await createNpmFetcher().fetch('typescript', '6.0.3'); + const signal = await compileAgainstTargets({ dir: repo(project) }, [ + { name: 'typescript', dir: six.dir }, + ]); + const codes = signal.diagnostics.map((d) => `${d.file}:${d.line} TS${d.code}`); + expect(codes).toContain('src/a.ts:1 TS1540'); + // strict is on by default in 6.0: the nested project's untyped parameter. + expect(codes).toContain('tools/b.ts:1 TS7006'); + expect(codes.some((c) => c.startsWith('examples/'))).toBe(false); + // The tsconfig's own deprecation (baseUrl, TS5101) is the pack's finding, not a compile one. + expect(signal.diagnostics.some((d) => d.file.endsWith('.json'))).toBe(false); + expect(signal.coverage.compilers?.map((c) => c.version)).toEqual(['5.9.3', '6.0.3']); + }); +}); diff --git a/packages/core/src/adapters/typescript/compiler.ts b/packages/core/src/adapters/typescript/compiler.ts index 509b3e19..51abfa0e 100644 --- a/packages/core/src/adapters/typescript/compiler.ts +++ b/packages/core/src/adapters/typescript/compiler.ts @@ -1,4 +1,4 @@ -import { existsSync } from 'node:fs'; +import { existsSync, readFileSync } from 'node:fs'; import { createRequire } from 'node:module'; import { dirname, join, resolve } from 'node:path'; import { ts } from 'ts-morph'; @@ -26,8 +26,25 @@ export function consumerCompilerDir(dir: string): string | undefined { } } -function loadCompiler(packageDir: string): typeof ts { - return createRequire(join(packageDir, 'package.json'))(packageDir) as typeof ts; +/** The version a package directory holds now, from its manifest. */ +function installedVersion(packageDir: string): string | undefined { + try { + return ( + JSON.parse(readFileSync(join(packageDir, 'package.json'), 'utf8')) as { version?: string } + ).version; + } catch { + return undefined; + } +} + +export function loadCompiler(packageDir: string): typeof ts { + const require = createRequire(join(packageDir, 'package.json')); + // Node keeps a loaded module by path: after `fix` installs another TypeScript at the same + // path (the typescript pack upgrades the compiler itself), the old module must not answer. + const main = require.resolve(packageDir); + const cached = require.cache[main]?.exports as { version?: string } | undefined; + if (cached && cached.version !== installedVersion(packageDir)) delete require.cache[main]; + return require(packageDir) as typeof ts; } /** The consumer's compiler is authoritative; bundles carry ts-morph's compiler as fallback. */ @@ -68,7 +85,8 @@ onReset(() => compilers.clear()); */ export function repositoryCompiler(dir: string): Compiler { const own = consumerCompilerDir(dir); - const key = own ?? ''; + // Keyed by version too: an install that replaced the compiler is another compiler. + const key = own === undefined ? '' : `${own}@${installedVersion(own) ?? ''}`; let compiler = compilers.get(key); if (!compiler) { const loaded = own === undefined ? ts : resolveCompiler(dir, ts, loadCompiler, () => own); diff --git a/packages/core/src/adapters/typescript/usages.ts b/packages/core/src/adapters/typescript/usages.ts index 24520931..7214b46f 100644 --- a/packages/core/src/adapters/typescript/usages.ts +++ b/packages/core/src/adapters/typescript/usages.ts @@ -156,7 +156,12 @@ function isPackageSpecifier(specifier: string, pkg: string): boolean { export function resolvePackageDir(repo: LoadedRepo, pkg: string): string | undefined { const files = repo.project.getSourceFiles(); const from = files[0]; - if (!from) return undefined; + if (!from) { + // No file in the scoped program (a package nothing imports, such as the compiler itself): + // where Node would find it. + const hoisted = hoistedPackageDir(repo.dir, pkg); + return hoisted === undefined ? undefined : realpathSync(hoisted); + } const resolved = ts.resolveModuleName( pkg, from.getFilePath(), diff --git a/packages/core/src/check/check.ts b/packages/core/src/check/check.ts index f77bf550..e124f775 100644 --- a/packages/core/src/check/check.ts +++ b/packages/core/src/check/check.ts @@ -477,6 +477,13 @@ export async function check(opts: CheckOptions): Promise { } }, ); + // Errors the pack knows one edit elsewhere resolves (the typescript pack's `types` default). + if (pack.foldFindings) + report.findings = pack.foldFindings({ + root: opts.cwd, + workspace: report.workspace, + findings: report.findings, + }); } const order = new Map(workspaces.map((w, i) => [w, i])); const packages = mergeAcrossWorkspaces( @@ -923,7 +930,13 @@ async function checkWorkspace(ctx: Ctx, job: WorkspaceJob): Promise + packInventory(opts.cwd, workspace, packsOf(opts).find((p) => p.name === name)?.inventory) + .length > 0, + ) ) { return progress(opts.onProgress, { phase: 'resolve', workspace }, () => Object.entries(installedByWorkspace[workspace] ?? {}) diff --git a/packages/core/src/check/companions.test.ts b/packages/core/src/check/companions.test.ts index c267b4ff..222fdac9 100644 --- a/packages/core/src/check/companions.test.ts +++ b/packages/core/src/check/companions.test.ts @@ -285,6 +285,27 @@ describe('companionsOf, react 18 → 19', () => { expect(plan.companions.map((c) => `${c.name} ${c.to}`)).toEqual(['react-dom 19.2.1']); }); + it('reports a peer conflict even when the peer is not grouped with the lead', async () => { + // typescript-eslint peers on typescript, which `list` groups with nothing: a parser whose + // range rejects TypeScript 6 stays where it is, and the report says so. + const parser = { '8.56.0': { peerDependencies: { typescript: '>=4.8.4 <6.0.0' } } }; + const plan = await companionsOf({ + name: 'typescript', + target: '6.0.3', + installed: [ + installed('typescript', '5.9.3', ['.']), + { ...installed('@typescript-eslint/parser', '8.56.0', ['.']), manifest: parser['8.56.0'] }, + ], + manifests: async (name) => + name === '@typescript-eslint/parser' ? parser : { '6.0.3': {}, '5.9.3': {} }, + }); + expect(plan).toEqual({ + companions: [], + conflicts: [], + peerConflicts: ['@typescript-eslint/parser 8.56.0 declares typescript >=4.8.4 <6.0.0'], + }); + }); + it('leaves a named companion whose installed peer range already accepts the target', async () => { // No release of it at the target's version, so only the pack's list moves it. const wrapper = { diff --git a/packages/core/src/check/companions.ts b/packages/core/src/check/companions.ts index f1b752ed..2a4c20ce 100644 --- a/packages/core/src/check/companions.ts +++ b/packages/core/src/check/companions.ts @@ -539,7 +539,14 @@ async function releaseGroupOf(input: CompanionInput): Promise { grew = true; } } - if (inGroup.size < 2) return stays.length ? { ...none, stays: stays.sort() } : none; + // A peer `list` does not group with the lead (typescript-eslint, which peers on typescript) is + // still left in place when it rejects the target, and still reported as a peer conflict. + if (inGroup.size < 2) + return { + ...none, + peerConflicts: [...peerConflicts.values()].sort(), + ...(stays.length ? { stays: stays.sort() } : {}), + }; const companions: Companion[] = []; const conflicts: string[] = []; for (const member of candidates.filter((c) => inGroup.has(c.name))) { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 0cfab5b7..e85aedd9 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,9 +1,10 @@ -export { unresolvedConfig } from './adapters/typescript/compiler.js'; +export { consumerCompilerDir, unresolvedConfig } from './adapters/typescript/compiler.js'; export { createTypescriptAdapter, type TypescriptAdapterOptions, typescriptAdapter, } from './adapters/typescript/index.js'; +export { projectScope } from './adapters/typescript/project-scope.js'; export { createFsSurfaceCache } from './cache/fs-surface-cache.js'; export { defaultCacheDir } from './cache/paths.js'; export { requireFindUsages } from './check/capabilities.js'; diff --git a/packages/core/src/packs/contract.test.ts b/packages/core/src/packs/contract.test.ts index 84fb39ef..fac85bec 100644 --- a/packages/core/src/packs/contract.test.ts +++ b/packages/core/src/packs/contract.test.ts @@ -45,7 +45,9 @@ describe('every registered pack meets the contract', () => { } for (const note of pack.behavior) { expect(note.reported.length, note.id).toBeGreaterThan(0); - if (note.reported.includes('finding')) expect(note.detect, note.id).toBeDefined(); + // A note's findings come from its `detect`, or from the pack folding compiler errors into it. + if (note.reported.includes('finding')) + expect(note.detect ?? pack.foldFindings, note.id).toBeDefined(); } expect(pack.instructions).not.toBe(''); diff --git a/packages/core/src/packs/contract.ts b/packages/core/src/packs/contract.ts index 394f0b65..baab9f00 100644 --- a/packages/core/src/packs/contract.ts +++ b/packages/core/src/packs/contract.ts @@ -429,6 +429,7 @@ export interface PackSpec { requiresBuild?: boolean; releaseGroup?: readonly PackCompanion[]; alternateLeads?: readonly PackCompanion[]; + foldFindings?: MigrationPack['foldFindings']; } /** @@ -449,6 +450,7 @@ export function definePack(spec: PackSpec): Pack { ...(spec.requiresBuild ? { requiresBuild: true } : {}), ...(spec.releaseGroup ? { releaseGroup: spec.releaseGroup } : {}), ...(spec.alternateLeads ? { alternateLeads: spec.alternateLeads } : {}), + ...(spec.foldFindings ? { foldFindings: spec.foldFindings } : {}), defaultTarget: spec.defaultTarget ?? '', supports: (from, to) => satisfies(from, spec.meta.from) && satisfies(to, spec.meta.to), transform(text, finding, context) { @@ -486,10 +488,10 @@ export function definePack(spec: PackSpec): Pack { return pack; } -/** Notes found by `detect`: their findings carry the note's id, as rules' do. */ +/** Notes reported as findings (by `detect`, or by `foldFindings`): their findings carry the note's id, as rules' do. */ function detectingNotes(behavior: readonly BehaviorNote[]): MigrationRule[] { return behavior - .filter((b) => b.detect && b.reported.includes('finding')) + .filter((b) => b.reported.includes('finding')) .map((b) => ({ id: b.id, kinds: [], symbols: /$^/, guide: b.summary })); } diff --git a/packages/core/src/packs/registry.ts b/packages/core/src/packs/registry.ts index 95e44a15..1553058b 100644 --- a/packages/core/src/packs/registry.ts +++ b/packages/core/src/packs/registry.ts @@ -26,6 +26,8 @@ import { sentryReactPack } from './sentry-react/index.js'; import sentryReactVerification from './sentry-react/verification.json' with { type: 'json' }; import { stripePack } from './stripe/index.js'; import stripeVerification from './stripe/verification.json' with { type: 'json' }; +import { typescriptPack } from './typescript/index.js'; +import typescriptVerification from './typescript/verification.json' with { type: 'json' }; import { vitestPack } from './vitest/index.js'; import vitestVerification from './vitest/verification.json' with { type: 'json' }; import { zodPack } from './zod/index.js'; @@ -64,6 +66,11 @@ export const REGISTRY: readonly RegisteredPack[] = [ verification: sentryReactVerification as PackVerification, }, { dir: 'stripe', pack: stripePack, verification: stripeVerification as PackVerification }, + { + dir: 'typescript', + pack: typescriptPack, + verification: typescriptVerification as PackVerification, + }, { dir: 'vitest', pack: vitestPack, verification: vitestVerification as PackVerification }, { dir: 'zod', pack: zodPack, verification: zodVerification as PackVerification }, ]; diff --git a/packages/core/src/packs/tooling/truth-diff.ts b/packages/core/src/packs/tooling/truth-diff.ts index 6d29491e..e06463d2 100644 --- a/packages/core/src/packs/tooling/truth-diff.ts +++ b/packages/core/src/packs/tooling/truth-diff.ts @@ -1,7 +1,7 @@ import { ts } from 'ts-morph'; -/** The source files `packs:truth` reads a diff of. */ -export const SOURCE_FILE = /\.[cm]?[jt]sx?$/; +/** The source files `packs:truth` reads a diff of, tsconfig files included (the compiler's own config). */ +export const SOURCE_FILE = /\.[cm]?[jt]sx?$|(?:^|\/)tsconfig[^/]*\.json$/; /** A line of `` the upgrade changed or removed. */ export interface ChangedLine { @@ -78,6 +78,7 @@ const TRANSPARENT = new Set([ ]); function scriptKind(file: string): ts.ScriptKind { + if (file.endsWith('.json')) return ts.ScriptKind.JSON; if (file.endsWith('.tsx')) return ts.ScriptKind.TSX; if (file.endsWith('.jsx')) return ts.ScriptKind.JSX; return /\.[cm]?js$/.test(file) ? ts.ScriptKind.JS : ts.ScriptKind.TS; diff --git a/packages/core/src/packs/types.ts b/packages/core/src/packs/types.ts index b59a9401..12d6e067 100644 --- a/packages/core/src/packs/types.ts +++ b/packages/core/src/packs/types.ts @@ -270,6 +270,12 @@ export interface MigrationPack { finding: Finding, context: PackContext, ): Pick | undefined; + /** + * After the pack's findings are claimed: compiler errors one edit elsewhere resolves, folded + * into that edit's finding as its evidence (the typescript pack's `types` default). Workspace- + * relative findings in, the same out. + */ + foldFindings?(input: { root: string; workspace: string; findings: Finding[] }): Finding[]; runtimeFindings?(input: { root: string; workspace: string; diff --git a/packages/core/src/packs/typescript/RESEARCH.md b/packages/core/src/packs/typescript/RESEARCH.md new file mode 100644 index 00000000..af1cabda --- /dev/null +++ b/packages/core/src/packs/typescript/RESEARCH.md @@ -0,0 +1,252 @@ +# TypeScript 5 → 6: evidence and verification limits + +Read date for every source below: **2026-10-14**. Scope: `typescript` from 5.x to 6.x, defaulting +to 6.0.3, the latest 6.x. No codemod exists for this upgrade; nothing was downloaded beyond the +npm tarballs of `typescript` 5.9.3, 6.0.3 and 7.0.2 (installed with scripts off into a scratch +directory to read their diagnostics), and no `npx` was used. + +## Source ledger + +| Source | Pinned at | +| --- | --- | +| [TypeScript 6.0 release notes](https://github.com/microsoft/TypeScript-Website/blob/119f931a639c9215a27311fde459c6db0b8718e4/packages/documentation/copy/en/release-notes/TypeScript%206.0.md) (published as [typescriptlang.org/docs/handbook/release-notes/typescript-6-0.html](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-6-0.html)) | TypeScript-Website `119f931a639c9215a27311fde459c6db0b8718e4` (2026-03-31) | +| [Announcing TypeScript 6.0](https://devblogs.microsoft.com/typescript/announcing-typescript-6-0/), 2026-03-23 | blog post, read 2026-10-14 | +| [Announcing TypeScript 7.0](https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/), 2026-07-08 | blog post, read 2026-10-14 | +| `typescript@5.9.3` | tag `v5.9.3`, `c63de15a992d37f0d6cec03ac7631872838602cb` | +| `typescript@6.0.3` | tag `v6.0.3`, `050880ce59e30b356b686bd3144efe24f875ebc8` | + +Every diagnostic code below (TS5101, TS5107, TS5011, TS1540, TS2880, TS2882, TS2591) was read from +6.0.3 itself on a scratch project, not from the notes. + +## One upgrade or two + +Two. 6.0 deprecates options and syntax: each is an error (`TS5101`, `TS5107`) unless +`"ignoreDeprecations": "6.0"` is set, and the notes say 7.0 "will not support any of these +deprecated options". The 7.0 announcement confirms it removes them, so a 5.x repository passes +through 6 whatever it does, and `ignoreDeprecations: "6.0"` (the escape hatch three of the +screened upgrades used) only defers the work to 7. This pack is 5 → 6. `packs/queue.json` carries +6 → 7 as its own entry, **blocked**: `typescript@7.0.2` has no JavaScript compiler API (its main +entry is `lib/version.cjs`; the announcement says a new API comes with 7.1), so `check` cannot +compute a 7.x target's diagnostics (below). + +## How `check` resolves the compiler + +For every other package, Signal B swaps the package into an overlay and compiles the repository +with the repository's own TypeScript. When the upgraded package is `typescript`, that would +compile with the old compiler and judge nothing. `compileCompilerUpgrade` +(`adapters/typescript/compile.ts`) does this instead: + +- **Installed side:** the repository's own `node_modules/typescript`, the nearest one from the + workspace up, as its build finds it (`consumerCompilerDir`). +- **Target side:** the `typescript` copy `check` fetched at the target version, the same release + `fix` installs, loaded from that directory. +- **Never the bundled compiler** (ts-morph's 6.0.2). Without an installed TypeScript the compile + is skipped and the report says why. A target without `createProgram` (7.x) is skipped and named + the same way. The coverage line names both versions: "with the repo's TypeScript 5.9.3 and 6.0.3". +- **What is compiled:** each side builds the workspace's projects as the TypeScript project + scope finds them (UPT-49, `project-scope.ts`: every `tsconfig*.json`, their references, configs + that are only extended left out). Every file of those projects is compiled, not only the files + that import `typescript`, and a diagnostic counts only in a file a project covers, as for any + package. A diagnostic present at 6.0.3 and absent at the installed version is new, matched the + way the overlay matches: file, code and message, or place and message shape. +- **Diagnostics on tsconfig files** (TS5101, TS5107, TS5011) are not compile findings. The + compiler reports an inherited option at the extending config's `compilerOptions`, not where it + is set. The pack reports each one in the file that sets it, as a pack finding from the + inventory (`{tsconfig*.json,**/tsconfig*.json}`). Pack findings are not subject to the + TypeScript project scope (UPT-49, uptide-dev/uptide#138 keeps runtime pack findings out of + it), and `check.test.ts` here pins a tsconfig outside every `include` to that. + +`fix` installs 6.x at the same path the repository's 5.x was loaded from. `loadCompiler` now drops +Node's cached module when the version on disk differs, and the compiler cache is keyed by version, +so verification after the install is judged by 6.x. + +## Classified changes (5 → 6) + +| Change (notes section) | Compiler sees it | Reported as | Rewrite | +| --- | --- | --- | --- | +| `baseUrl` deprecated, no longer a lookup root | TS5101 at the tsconfig | `base-url`, found where set | assisted: the replacement (prefix `paths`) is exact, but bare imports resolved through `baseUrl`, and bundlers that read it (Next.js, vite-tsconfig-paths), can change | +| `moduleResolution: node` / `node10` deprecated | TS5107 | `module-resolution-node10` | assisted: `bundler` vs `nodenext` depends on what runs the code | +| `moduleResolution: classic` removed | TS5107 | `module-resolution-classic` | assisted | +| `target: es5` (and es3) deprecated | TS5107 | `target-es5` | assisted: raising it changes emitted JavaScript | +| `module: amd/umd/system/none` deprecated | TS5107 | `module-format-removed` | assisted: changes what consumers load | +| `outFile` deprecated | TS5101 | `out-file` | assisted: needs a bundler | +| `downlevelIteration` deprecated | TS5101 | `downlevel-iteration` | **mechanical**: remove the line. The notes say it only affected ES5 emit. Declined with an ES3/ES5 target, with `ignoreDeprecations` set, or when the property shares its line | +| `esModuleInterop` / `allowSyntheticDefaultImports` false | TS5107 | `interop-false` | assisted: emit interop changes, and namespace imports that are called need the compiler's sites | +| `alwaysStrict: false` | TS5107 | `always-strict-false` | assisted: emitted scripts gain "use strict" | +| `rootDir` defaults to the tsconfig directory | TS5011 at `outDir` | `root-dir`, found from `outDir` + one-directory `include` | assisted: the inferred directory depends on the file list | +| `module Foo {}` namespaces | TS1540 at the name | `module-keyword` | **mechanical**: `module` → `namespace` (the notes' replacement); declined for a quoted name | +| Import assertions `assert {}` | TS2880 at `assert` | `import-assertions` | **mechanical**: `assert` → `with` (the notes' replacement); declined unless `{` or `:` follows | +| `strict` default true | code errors (TS7006, TS18048, TS2345…) | compiler findings, and the `changed-defaults` decision | assisted; pin only as a decision | +| `types` default `[]` | code errors (TS2591, TS2593, TS2304) | compiler findings, and `changed-defaults` | assisted; the guide says to list `types` | +| `noUncheckedSideEffectImports` default true | TS2882 | compiler findings, and `changed-defaults` | assisted | +| `module` / `target` defaults | code errors where they matter | `changed-defaults` summary | decision | +| `no-default-lib` directive, `tsc file.ts` with a tsconfig (TS5112) | yes | compiler findings (generic) / not in scope | none: no ground-truth site; the CLI change is not code | + +Default changes are not pinned by rule. The issue asks for the old value to be pinned unless the +repository opts in, with the choice under "Decisions for you". A static pin would be predicted in +every tsconfig that leaves `types` or `strict` unset, and in the ground truth most of those +compile cleanly at 6.0 (m-lyon/recipe, robrechtme/recipes), so it would be a false positive +there. So the choice is the `changed-defaults` decision. It names each tsconfig and the defaults +it leaves unset, following `extends` inside the repository; a package's config is not read. The +errors themselves are the compiler's findings. Borewit pinned `"strict": false` and signalk added +`ignoreDeprecations`, both visible in the ground truth as tsconfig sites under `changed-defaults`. + +## Peers + +Tooling that peers on `typescript` is planned like any peer (`companions.ts`). A release whose +range accepts the target stays; one that rejects it stays too and is reported as a peer conflict, +"left where it is, not compiled at another version". Before this change a peer that `list` does +not group with the lead (typescript-eslint, ts-jest) was planned and then dropped from the report. +`companionsOf` now returns its conflict, with a test from `@typescript-eslint/parser` 8.56.0 +(`typescript >=4.8.4 <6.0.0`). Nothing is bumped across a major. The ground-truth repositories +had no such conflict: typescript-eslint 8.68 and tsup accept 6.0. + +## Independent ground truth + +Seven merged public upgrades, 5.x → 6.0.3. Each was drafted with `pnpm packs:truth` (6.0.3 linked +where the repository installs `typescript`, every `tsconfig.json` compiled before and after, and +the upgrade's diff), then assigned by rule and committed (`d128198`, the first commit of this branch) **before the first +`pack test` run**. Nothing was changed after scoring. + +| Repository | Upgrade | Before | What it exercises | +| --- | --- | --- | --- | +| m-lyon/recipe (`client/`) | [#113](https://github.com/m-lyon/recipe/pull/113) | `f6e44b5`, 5.6.2 | `baseUrl` with ten `paths` entries | +| robrechtme/recipes | [#7](https://github.com/robrechtme/recipes/pull/7) | `0853747`, 5.9.3 | `target: es5`, `baseUrl` | +| JosephMaynard/dependency-radar | [#46](https://github.com/JosephMaynard/dependency-radar/pull/46) | `1920eb3`, 5.9.3 | `moduleResolution: node`, `types` default (Node builtins), TS2882 in a nested project | +| CroffZ/github-release-notifier | [#5](https://github.com/CroffZ/github-release-notifier/pull/5) | `d9353f2`, 5.9.3 | `baseUrl` in an extended base config, `rootDir` | +| SignalK/signalk-derived-data | [#239](https://github.com/SignalK/signalk-derived-data/pull/239) | `02f635e`, 5.9.3 | `moduleResolution: node` kept behind `ignoreDeprecations`, `types` default | +| shogo82148/typescript-action | [#487](https://github.com/shogo82148/typescript-action/pull/487) | `0f03405`, 5.9.3 | `rootDir` (TS5011), `types` | +| Borewit/music-metadata | [#2661](https://github.com/Borewit/music-metadata/pull/2661) | `783509b`, 5.9.3 | `baseUrl`, `strict` and `types` defaults across 60 test files | + +How sites were assigned: + +- Every code diagnostic new at 6.0.3 is a site under `generic`, one per line. No pack rule claims + them: they are the compiler's. +- A TS5101/TS5107/TS5011 diagnostic on the line that sets the option takes that option's rule. An + inherited option the compiler reports at an extending config (`packages/action/tsconfig.json:3`, + `lib/tsconfig.json:3`, `test/tsconfig.json:3`) is the same change. Its one site is where the + option is set (`tsconfig.base.json:14`, `tsconfig.json:7`). +- The `paths` entries rewritten when `baseUrl` was removed are part of the `base-url` change. +- A changed line with no diagnostic is a site of the change it belongs to: + - dependency-radar `tsconfig.json:4`, where `module` moved to Node16 for the node16 resolution + (`module-resolution-node10`); + - CroffZ `packages/action/tsconfig.json:5`, where `rootDir` was added (`root-dir`); + - tsconfig lines where `types` was added (`changed-defaults`); + - 17 Borewit test lines the upgrade tightened with no diagnostic at the pinned commit (`generic`). +- Borewit's `lib/common/MetadataCollector.ts:211` is a deprecation (TS6385, `substr`), kept under + `generic`. + +### Screened out + +From about 340 merged pull requests that touched a tsconfig or code with a lockfile, read +2026-10-14: chipster/chipster-web#87 (prepares for 6 without bumping it); AlaskaAirlines/auro-cli#341 +(TypeScript 6 only transitively); vish288/move-prop-types#45 (also moves chalk 5 → 6, @types/node +22 → 26 and @eslint/js 9 → 10, so its errors are not only TypeScript's); several where TypeScript 6 +only arrives through tsup's injected `baseUrl` (`ignoreDeprecations` only); upgrades from 4.x; +forks; and 7.0 or tsgo upgrades. + +## Measured `pack test` + +Measured 2026-10-14 against the ground truth committed before it: + +``` +uptide pack test typescript · verified, recorded candidate +fixtures 6 cases, 13 sites · pass +ground truth + m-lyon/recipe@f6e44b530c4d typescript 5.6.2 → 6.0.3 predicted 1, expected 1, false positives 0, false negatives 0 + robrechtme/recipes@0853747427b7 typescript 5.9.3 → 6.0.3 predicted 2, expected 2, false positives 0, false negatives 0 + JosephMaynard/dependency-radar@1920eb3217b7 typescript 5.9.3 → 6.0.3 predicted 115, expected 116, false positives 0, false negatives 1 + CroffZ/github-release-notifier@d9353f2471c5 typescript 5.9.3 → 6.0.3 predicted 11, expected 12, false positives 0, false negatives 1 + SignalK/signalk-derived-data@02f635e8232a typescript 5.9.3 → 6.0.3 predicted 417, expected 418, false positives 0, false negatives 1 + shogo82148/typescript-action@0f03405c7267 typescript 5.9.3 → 6.0.3 predicted 2, expected 3, false positives 0, false negatives 1 + Borewit/music-metadata@783509b27bac typescript 5.9.3 → 6.0.3 predicted 831, expected 851, false positives 0, false negatives 20 + + rule precision recall tp fp fn + base-url 100% 100% 4 0 0 + changed-defaults n/a 0% 0 0 4 + generic 100% 99% 1371 0 18 + module-resolution-node10 100% 67% 2 0 1 + root-dir 100% 50% 1 0 1 + target-es5 100% 100% 1 0 0 + overall (sites) 100% 98% 1379 0 24 + behavior notes (sites) n/a 0% 0 0 4 + breaking (sites) 100% 99% 1379 0 20 +``` + +The false negatives, all expected: + +- **Second-round compiler errors.** Borewit's 17 tightened test lines and the TS6385 deprecation. + The compile reports errors, and those lines only error once the `types` fix gives `chai` and + mocha their types back. +- **Same-change lines no rule predicts.** dependency-radar's `module` line and CroffZ's `rootDir` + under `noEmit`. `root-dir` reads `outDir`, and CroffZ has none. +- **`changed-defaults` sites.** That note is a decision, not a finding, so its 4 sites are + behavior-only misses, not missed breaks. + +### After rebasing on UPT-49 (#138): the `types` default folds + +On #138's project scope the compile also builds `tsconfig.*.json` projects. The first run there +found 14 breaking false positives in shogo82148/typescript-action `__tests__/`, through +`tsconfig.eslint.json`: `describe`, `it` and `expect` unresolved under `types: []`. The ground +truth, committed before scoring, does not change to make a run pass, so a redraft was reverted +(the `packs:truth` change that drafts on the same scope stays, for packs drafted from now on). + +The engine changed instead. A person fixes the `types` default with one line, `"types": [...]`, +not at each use, as shogo did in `tsconfig.eslint.json`. So `foldTypesDefault` (`fold.ts`, the +pack's `foldFindings` hook, run after #136's option cascades) folds those errors into one +`changed-defaults` finding: + +- **Which errors:** TS2580/2581/2582/2591/2592/2593, and TS2304/TS2503 when an `@types` package + 5.x included declares the missing name. +- **Where:** at the covering project that leaves `types` unset through its `extends`, on the + config's root object line. The errors become its evidence (`anchorOnly`). +- **Severity:** the note's, `deprecated` ("needs a look"). A breaking anchor would be a + breaking false positive wherever the original truth has no site for that tsconfig edit + (dependency-radar's `types` insertion touched a changed line, so it was never anchored). + +Re-scored 2026-10-15 on the original truth (unchanged since `d128198`): + +- 0 breaking false positives; PASS. The run measures verified and the record stays candidate. +- Breaking recall **17% (240 of 1,399)**. 1,159 sites the original truth lists one per error line + under `generic` are now evidence of one tsconfig finding each, so they score as misses. +- 8 `changed-defaults` false positives (deprecated, not breaking): the anchor is on the root + object line, while the truth's lines for those edits follow each diff's shape (`:1` where the + insertion went into an untouched object, the last property where a comma changed). + +The recall figure measures the original truth's convention (every error line a site) against +the engine's (one site per edit). It is not a count of breaks `check` misses: every folded +error is listed under its tsconfig finding. + +## The verified gate, now and under UPT-48 + +Under today's gate the run measures **verified**: seven public repositories, no breaking false +positive, 1,379 breaking sites predicted. The task asked to hold the claim until the stricter gate +(UPT-48) lands, so `verification.json` keeps the candidate record and is not written by this pull +request. After rebasing on UPT-48, `pack test --write` re-scores and records whatever that gate +says. + +Against UPT-48 as specified: + +- ≥2 repositories: 7. +- ≥5 expected breaking sites: 1,399. +- Per-rule real-world true positives: `base-url` 4, `module-resolution-node10` 2, `target-es5` 1, + `root-dir` 1 (all assisted). +- The three mechanical rewrites (`downlevel-iteration`, `module-keyword`, `import-assertions`) + met **no** real-world site. Under UPT-48 they are unexercised and run as assisted. The fixtures + are their only evidence. +- `module-resolution-classic`, `module-format-removed`, `out-file`, `interop-false` and + `always-strict-false` are detection only and also unexercised. Each matches only the exact value + 6.0 reports. + +## Known limits + +- **`extends` to a package** (`@tsconfig/node16`, `expo/tsconfig.base`). Options set there are not + read: neither the deprecated-option rules nor `changed-defaults` see them. The compiler still + reports their code fallout. +- **`root-dir`** reads one file. A `rootDir` set in an extended config is not seen. An `include` + over several directories, or none, is not reported, even when the compiler reports TS5011 + there. CroffZ's `noEmit` case is a miss. +- **Second-round errors** that appear only after the first fixes are not predicted; `fix`'s + verification compile sees them. +- **Deprecation suggestions** (TS6385) are not reported by the compile, which keeps errors. +- **6 → 7** is blocked until TypeScript 7 exposes a compiler API `check` can load. diff --git a/packages/core/src/packs/typescript/check.test.ts b/packages/core/src/packs/typescript/check.test.ts new file mode 100644 index 00000000..da680785 --- /dev/null +++ b/packages/core/src/packs/typescript/check.test.ts @@ -0,0 +1,78 @@ +import { mkdirSync, mkdtempSync, symlinkSync, writeFileSync } from 'node:fs'; +import { createRequire } from 'node:module'; +import { tmpdir } from 'node:os'; +import { dirname, join } from 'node:path'; +import { expect, it } from 'vitest'; +import { createTypescriptAdapter } from '../../adapters/typescript/index.js'; +import { check } from '../../check/check.js'; +import type { PackageFetcher } from '../../domain/io.js'; +import { typescriptPack } from './index.js'; + +process.env.UPTIDE_CACHE_DIR = mkdtempSync(join(tmpdir(), 'uptide-ts-pack-cache-')); + +it('reports tsconfig option sites outside every tsconfig include, where the project scope (UPT-49) never drops them', async () => { + const compiler = dirname(createRequire(import.meta.url).resolve('typescript/package.json')); + const cwd = mkdtempSync(join(tmpdir(), 'uptide-ts-pack-')); + const write = (file: string, text: string) => { + mkdirSync(dirname(join(cwd, file)), { recursive: true }); + writeFileSync(join(cwd, file), text); + }; + const dependencies = { typescript: '5.9.3' }; + write('package.json', JSON.stringify({ name: 'consumer', devDependencies: dependencies })); + write( + 'package-lock.json', + JSON.stringify({ + lockfileVersion: 3, + packages: { + '': { devDependencies: dependencies }, + 'node_modules/typescript': { version: '5.9.3' }, + }, + }), + ); + mkdirSync(join(cwd, 'node_modules')); + symlinkSync(compiler, join(cwd, 'node_modules', 'typescript'), 'dir'); + // No tsconfig file is inside any project's `include`: the compiler reads them, never compiles + // them, and the project scope covers none of them. `config/tsconfig.base.json` is not even a + // project (it is only extended), and its directory is excluded. + write( + 'tsconfig.json', + '{\n "extends": "./config/tsconfig.base.json",\n "compilerOptions": {\n "baseUrl": ".",\n "noEmit": true\n },\n "include": ["src/**/*.ts"],\n "exclude": ["config"]\n}\n', + ); + write( + 'config/tsconfig.base.json', + '{\n "compilerOptions": {\n "target": "es2019",\n "downlevelIteration": true\n }\n}\n', + ); + write( + 'tools/tsconfig.json', + '{\n "compilerOptions": {\n "moduleResolution": "node",\n "noEmit": true\n },\n "include": ["*.ts"]\n}\n', + ); + write('src/index.ts', 'export const answer = 42;\n'); + write('tools/run.ts', 'export {};\n'); + // The target stands in for 6.0.3 with the same compiler: no compiler diagnostic, only the pack's. + const fetcher: PackageFetcher = { + resolve: async () => '6.0.3', + fetch: async () => ({ name: 'typescript', version: '6.0.3', dir: compiler }), + versions: async () => ['5.9.3', '6.0.3'], + manifests: async () => ({ '5.9.3': {}, '6.0.3': {} }), + }; + const result = await check({ + cwd, + only: ['typescript'], + targets: { typescript: '6.0.3' }, + packs: [typescriptPack], + adapter: createTypescriptAdapter(), + fetcher, + runtime: false, + }); + const sites = result.packages + .flatMap((p) => p.findings) + .filter((f) => /(^|\/)tsconfig[^/]*\.json$/.test(f.usage.file)) + .map((f) => [f.usage.file, f.usage.line, f.rule, f.severity]); + expect(sites).toEqual([ + ['config/tsconfig.base.json', 4, 'downlevel-iteration', 'breaking'], + ['tools/tsconfig.json', 3, 'module-resolution-node10', 'breaking'], + ['tsconfig.json', 4, 'base-url', 'breaking'], + ]); + // The compile ran under the project scope (a tsconfig was found), not the no-tsconfig fallback. + expect(result.packages[0]?.compile?.coverage?.noTsconfig).toBeUndefined(); +}, 60_000); diff --git a/packages/core/src/packs/typescript/detect.ts b/packages/core/src/packs/typescript/detect.ts new file mode 100644 index 00000000..f15e03de --- /dev/null +++ b/packages/core/src/packs/typescript/detect.ts @@ -0,0 +1,257 @@ +import { ts } from 'ts-morph'; +import type { Finding } from '../../domain/report.js'; +import { replaceAtSite, type SourceSite } from '../contract.js'; +import type { TransformResult } from '../types.js'; + +/** + * TypeScript 6 sites the compiler reports in a tsconfig file (TS5101, TS5107), found where the + * option is set: in the file that sets it, not the leaf that inherits it, which is where the + * compiler points. Every tsconfig in the repository is read, so an `extends` chain inside the + * repository is covered file by file. Read with the JSON parser, comments and trailing commas + * included, as the compiler reads them. + */ + +export const isTsconfig = (file: string): boolean => /(?:^|\/)tsconfig[^/]*\.json$/.test(file); + +interface Option { + name: string; + /** The value as written: a string lowercased, a boolean, or undefined for anything else. */ + value: string | boolean | undefined; + node: ts.PropertyAssignment; +} + +function compilerOptions( + text: string, + file: string, +): { source: ts.JsonSourceFile; options: Option[] } | undefined { + const source = ts.parseJsonText(file, text); + const root = source.statements[0]?.expression; + if (!root || !ts.isObjectLiteralExpression(root)) return undefined; + const block = root.properties.find( + (p): p is ts.PropertyAssignment => + ts.isPropertyAssignment(p) && nameOf(p) === 'compilerOptions', + )?.initializer; + if (!block || !ts.isObjectLiteralExpression(block)) return { source, options: [] }; + const options = block.properties.filter(ts.isPropertyAssignment).map((node) => { + const init = node.initializer; + const value = ts.isStringLiteral(init) + ? init.text.toLowerCase() + : init.kind === ts.SyntaxKind.TrueKeyword + ? true + : init.kind === ts.SyntaxKind.FalseKeyword + ? false + : undefined; + return { name: nameOf(node), value, node }; + }); + return { source, options }; +} + +function nameOf(p: ts.PropertyAssignment): string { + return ts.isStringLiteral(p.name) || ts.isIdentifier(p.name) ? p.name.text : ''; +} + +function siteOf(source: ts.JsonSourceFile, option: Option): SourceSite { + const { line, character } = source.getLineAndCharacterOfPosition(option.node.getStart(source)); + return { + line: line + 1, + column: character + 1, + snippet: (source.text.split('\n')[line] ?? '').trim(), + name: option.name, + }; +} + +/** The sites of one option in a tsconfig, when its value is one of `values` (any value when omitted). */ +export function optionSites( + name: string, + values?: readonly (string | boolean)[], +): (text: string, file: string) => SourceSite[] { + return (text, file) => { + if (!isTsconfig(file) || !text.includes(name)) return []; + const parsed = compilerOptions(text, file); + if (!parsed) return []; + return parsed.options + .filter( + (o) => + o.name === name && + (values === undefined || (o.value !== undefined && values.includes(o.value))), + ) + .map((o) => siteOf(parsed.source, o)); + }; +} + +/** + * `downlevelIteration` only ever changed ES5 emit (the 6.0 notes), so with a target of ES2015 or + * later in the same file the property goes, with its line. Declined when the file targets ES3 or + * ES5, or silences deprecations: there it still changes the output. + */ +export function removeDownlevelIteration( + text: string, + finding: Pick, +): TransformResult { + const parsed = compilerOptions(text, finding.usage.file); + if (!parsed) return { text, applied: false, reason: 'the tsconfig cannot be read' }; + const target = parsed.options.find((o) => o.name === 'target')?.value; + if (target === 'es3' || target === 'es5') + return { text, applied: false, reason: `target ${target} still uses downlevelIteration` }; + if (parsed.options.some((o) => o.name === 'ignoreDeprecations')) + return { text, applied: false, reason: 'ignoreDeprecations is set: the option still applies' }; + const option = parsed.options.find( + (o) => o.name === 'downlevelIteration' && siteOf(parsed.source, o).line === finding.usage.line, + ); + if (!option) + return { text, applied: false, reason: 'the reported site is not downlevelIteration' }; + const lines = text.split('\n'); + const index = finding.usage.line - 1; + const line = lines[index] ?? ''; + const start = parsed.source.getLineAndCharacterOfPosition(option.node.getStart(parsed.source)); + const end = parsed.source.getLineAndCharacterOfPosition(option.node.end); + // Only a property alone on its line is removed: anything sharing the line is left to a person. + const rest = line.slice(end.character).replace(/^\s*,?\s*(\/\/.*)?$/, ''); + if (start.line !== end.line || line.slice(0, start.character).trim() !== '' || rest !== '') + return { + text, + applied: false, + reason: 'downlevelIteration shares its line with other settings', + }; + lines.splice(index, 1); + // A removed last property leaves the one before it with a trailing comma, which tsconfig allows. + return { + text: lines.join('\n'), + applied: true, + reason: 'downlevelIteration only affected ES5 output', + }; +} + +/** `module Foo {` is `namespace Foo {`: TS1540 points at the name, the keyword is just before it. */ +export function moduleToNamespace(text: string, finding: Pick): TransformResult { + const lines = text.split('\n'); + const index = finding.usage.line - 1; + const line = lines[index]; + if (line === undefined) return { text, applied: false, reason: 'the reported line is gone' }; + const before = line.slice(0, Math.max(0, finding.usage.column - 1)); + // A quoted name (`declare module "x"`) is an ambient module, which stays as it is. + if (!/^[A-Za-z_$]/.test(line.slice(before.length))) + return { text, applied: false, reason: 'the reported name is not an identifier' }; + const keyword = /(?): TransformResult { + const line = text.split('\n')[finding.usage.line - 1] ?? ''; + if (!/^assert\s*[{:]/.test(line.slice(Math.max(0, finding.usage.column - 1)))) + return { text, applied: false, reason: 'the reported site is not an import assertion' }; + return replaceAtSite(text, finding, 'assert', 'with'); +} + +/** The property names a tsconfig sets in `compilerOptions`, and what it `extends`. */ +function declared(text: string, file: string): { names: Set; extends: string[] } { + const parsed = compilerOptions(text, file); + const root = parsed?.source.statements[0]?.expression; + const base = + root && ts.isObjectLiteralExpression(root) + ? root.properties.find( + (p): p is ts.PropertyAssignment => ts.isPropertyAssignment(p) && nameOf(p) === 'extends', + )?.initializer + : undefined; + const extended = !base + ? [] + : ts.isStringLiteral(base) + ? [base.text] + : ts.isArrayLiteralExpression(base) + ? base.elements.filter(ts.isStringLiteral).map((e) => e.text) + : []; + return { names: new Set(parsed?.options.map((o) => o.name)), extends: extended }; +} + +/** + * `rootDir` now defaults to the tsconfig's directory instead of the common directory of the + * inputs, so a project that emits to `outDir` from one source directory (`include: ["src"]`) + * would write `dist/src/index.js`: the compiler stops with TS5011 at `outDir`. Found from this + * file alone: `outDir` set, `rootDir` and `noEmit` not, every `include` entry under the same + * directory. A `rootDir` an extended config sets is not seen (RESEARCH.md, known limits). + */ +export function rootDirSites(text: string, file: string): SourceSite[] { + if (!isTsconfig(file) || !text.includes('outDir')) return []; + const parsed = compilerOptions(text, file); + if (!parsed) return []; + const option = (name: string) => parsed.options.find((o) => o.name === name); + const outDir = option('outDir'); + if (!outDir || option('rootDir') || option('noEmit')?.value === true) return []; + if (option('composite')?.value === true) return []; + const root = parsed.source.statements[0]?.expression; + const include = + root && ts.isObjectLiteralExpression(root) + ? root.properties.find( + (p): p is ts.PropertyAssignment => ts.isPropertyAssignment(p) && nameOf(p) === 'include', + )?.initializer + : undefined; + if (!include || !ts.isArrayLiteralExpression(include)) return []; + const dirs = new Set( + include.elements.map((e) => + ts.isStringLiteral(e) ? (e.text.replace(/^\.\//, '').split('/')[0] ?? '') : '', + ), + ); + const [only] = [...dirs]; + if (dirs.size !== 1 || !only || /[*?.]/.test(only)) return []; + return [siteOf(parsed.source, outDir)]; +} + +const DEFAULTS = [ + ['strict', 'strict is on (it was off): `"strict": false` keeps 5.x checking'], + ['types', 'types is [] (every @types package was included): list the ones the code uses'], + [ + 'noUncheckedSideEffectImports', + 'noUncheckedSideEffectImports is on: `import "./x.css"` needs a declaration', + ], +] as const; + +/** + * The defaults 6.0 changed that a tsconfig of the repository leaves unset, through the + * `extends` chain inside the repository (a package's config, `@tsconfig/node20`, is not read): + * one line per tsconfig, for "Decisions for you". + */ +export function unsetDefaults(inventory: readonly { file: string; text: string }[]): string[] { + const configs = new Map( + inventory.filter((f) => isTsconfig(f.file)).map((f) => [f.file, declared(f.text, f.file)]), + ); + const setIn = (file: string, name: string, seen = new Set()): boolean | undefined => { + const config = configs.get(file); + if (!config || seen.has(file)) return undefined; + seen.add(file); + if (config.names.has(name)) return true; + let unknown = false; + for (const base of config.extends) { + if (!base.startsWith('.')) { + unknown = true; + continue; + } + const dir = file.includes('/') ? file.slice(0, file.lastIndexOf('/')) : '.'; + const target = normalize(`${dir}/${base.endsWith('.json') ? base : `${base}.json`}`); + const found = setIn(target, name, seen); + if (found) return true; + if (found === undefined) unknown = true; + } + return unknown ? undefined : false; + }; + const lines: string[] = []; + for (const file of [...configs.keys()].sort()) { + const unset = DEFAULTS.filter(([name]) => setIn(file, name) === false).map(([, what]) => what); + if (unset.length) lines.push(`${file}: ${unset.join('; ')}`); + } + return lines; +} + +function normalize(path: string): string { + const out: string[] = []; + for (const part of path.split('/')) { + if (part === '.' || part === '') continue; + if (part === '..') out.pop(); + else out.push(part); + } + return out.join('/'); +} diff --git a/packages/core/src/packs/typescript/fixtures/downlevel-iteration/after.ts b/packages/core/src/packs/typescript/fixtures/downlevel-iteration/after.ts new file mode 100644 index 00000000..144f73e5 --- /dev/null +++ b/packages/core/src/packs/typescript/fixtures/downlevel-iteration/after.ts @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "target": "es2019", // @uptide target-es5 keep + "module": "esnext", // @uptide module-format-removed keep + "moduleResolution": "bundler", // @uptide module-resolution-node10 keep + "outDir": "dist", // @uptide out-file keep + "paths": { "@app/*": ["./src/app/*"] }, // @uptide base-url keep + "alwaysStrict": true, // @uptide always-strict-false keep + "esModuleInterop": true, // @uptide module-resolution-classic keep + "declaration": true // @uptide downlevel-iteration keep + } +} diff --git a/packages/core/src/packs/typescript/fixtures/downlevel-iteration/before.ts b/packages/core/src/packs/typescript/fixtures/downlevel-iteration/before.ts new file mode 100644 index 00000000..c5919f2a --- /dev/null +++ b/packages/core/src/packs/typescript/fixtures/downlevel-iteration/before.ts @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "target": "es2019", // @uptide target-es5 keep + "downlevelIteration": true, // @uptide downlevel-iteration + "module": "esnext", // @uptide module-format-removed keep + "moduleResolution": "bundler", // @uptide module-resolution-node10 keep + "outDir": "dist", // @uptide out-file keep + "paths": { "@app/*": ["./src/app/*"] }, // @uptide base-url keep + "alwaysStrict": true, // @uptide always-strict-false keep + "esModuleInterop": true, // @uptide module-resolution-classic keep + "declaration": true // @uptide downlevel-iteration keep + } +} diff --git a/packages/core/src/packs/typescript/fixtures/downlevel-iteration/filename.txt b/packages/core/src/packs/typescript/fixtures/downlevel-iteration/filename.txt new file mode 100644 index 00000000..8f73edb8 --- /dev/null +++ b/packages/core/src/packs/typescript/fixtures/downlevel-iteration/filename.txt @@ -0,0 +1 @@ +tsconfig.build.json diff --git a/packages/core/src/packs/typescript/fixtures/import-assertions/after.ts b/packages/core/src/packs/typescript/fixtures/import-assertions/after.ts new file mode 100644 index 00000000..0c256dc1 --- /dev/null +++ b/packages/core/src/packs/typescript/fixtures/import-assertions/after.ts @@ -0,0 +1,4 @@ +import assert from 'node:assert'; +import data from './data.json' with { type: 'json' }; // @uptide import-assertions at:assert path:TS2880 message:"Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'." +export const lazy = import('./data.json', { with: { type: 'json' } }); // @uptide import-assertions at:assert path:TS2880 message:"Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'." +assert(data); // @uptide import-assertions keep at:assert path:TS2880 message:"Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'." diff --git a/packages/core/src/packs/typescript/fixtures/import-assertions/before.ts b/packages/core/src/packs/typescript/fixtures/import-assertions/before.ts new file mode 100644 index 00000000..2357b3b2 --- /dev/null +++ b/packages/core/src/packs/typescript/fixtures/import-assertions/before.ts @@ -0,0 +1,4 @@ +import assert from 'node:assert'; +import data from './data.json' assert { type: 'json' }; // @uptide import-assertions at:assert path:TS2880 message:"Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'." +export const lazy = import('./data.json', { assert: { type: 'json' } }); // @uptide import-assertions at:assert path:TS2880 message:"Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'." +assert(data); // @uptide import-assertions keep at:assert path:TS2880 message:"Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'." diff --git a/packages/core/src/packs/typescript/fixtures/module-keyword/after.ts b/packages/core/src/packs/typescript/fixtures/module-keyword/after.ts new file mode 100644 index 00000000..2529e037 --- /dev/null +++ b/packages/core/src/packs/typescript/fixtures/module-keyword/after.ts @@ -0,0 +1,12 @@ +namespace Legacy { // @uptide module-keyword at:Legacy path:TS1540 message:"A 'namespace' declaration should not be declared using the 'module' keyword. Please use the 'namespace' keyword instead." + export const x = 1; +} +export namespace Exported.Nested { // @uptide module-keyword at:Exported path:TS1540 message:"A 'namespace' declaration should not be declared using the 'module' keyword. Please use the 'namespace' keyword instead." + export const y = 2; +} +declare module 'ambient-package' { // @uptide module-keyword keep at:'ambient-package' path:TS1540 message:"A 'namespace' declaration should not be declared using the 'module' keyword. Please use the 'namespace' keyword instead." + export const z: number; +} +export namespace Already { + export const module = Legacy.x; +} diff --git a/packages/core/src/packs/typescript/fixtures/module-keyword/before.ts b/packages/core/src/packs/typescript/fixtures/module-keyword/before.ts new file mode 100644 index 00000000..c2bbfb94 --- /dev/null +++ b/packages/core/src/packs/typescript/fixtures/module-keyword/before.ts @@ -0,0 +1,12 @@ +module Legacy { // @uptide module-keyword at:Legacy path:TS1540 message:"A 'namespace' declaration should not be declared using the 'module' keyword. Please use the 'namespace' keyword instead." + export const x = 1; +} +export module Exported.Nested { // @uptide module-keyword at:Exported path:TS1540 message:"A 'namespace' declaration should not be declared using the 'module' keyword. Please use the 'namespace' keyword instead." + export const y = 2; +} +declare module 'ambient-package' { // @uptide module-keyword keep at:'ambient-package' path:TS1540 message:"A 'namespace' declaration should not be declared using the 'module' keyword. Please use the 'namespace' keyword instead." + export const z: number; +} +export namespace Already { + export const module = Legacy.x; +} diff --git a/packages/core/src/packs/typescript/fixtures/module-resolution-classic/after.ts b/packages/core/src/packs/typescript/fixtures/module-resolution-classic/after.ts new file mode 100644 index 00000000..8a9490f9 --- /dev/null +++ b/packages/core/src/packs/typescript/fixtures/module-resolution-classic/after.ts @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "moduleResolution": "Classic", // @uptide module-resolution-classic + "module": "commonjs" // @uptide module-format-removed keep + } +} diff --git a/packages/core/src/packs/typescript/fixtures/module-resolution-classic/before.ts b/packages/core/src/packs/typescript/fixtures/module-resolution-classic/before.ts new file mode 100644 index 00000000..8a9490f9 --- /dev/null +++ b/packages/core/src/packs/typescript/fixtures/module-resolution-classic/before.ts @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "moduleResolution": "Classic", // @uptide module-resolution-classic + "module": "commonjs" // @uptide module-format-removed keep + } +} diff --git a/packages/core/src/packs/typescript/fixtures/module-resolution-classic/filename.txt b/packages/core/src/packs/typescript/fixtures/module-resolution-classic/filename.txt new file mode 100644 index 00000000..15231cfe --- /dev/null +++ b/packages/core/src/packs/typescript/fixtures/module-resolution-classic/filename.txt @@ -0,0 +1 @@ +tsconfig.json diff --git a/packages/core/src/packs/typescript/fixtures/root-dir-set/after.ts b/packages/core/src/packs/typescript/fixtures/root-dir-set/after.ts new file mode 100644 index 00000000..8e527151 --- /dev/null +++ b/packages/core/src/packs/typescript/fixtures/root-dir-set/after.ts @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist", // @uptide root-dir keep + "types": ["node"] + }, + "include": ["src/**/*.ts"] +} diff --git a/packages/core/src/packs/typescript/fixtures/root-dir-set/before.ts b/packages/core/src/packs/typescript/fixtures/root-dir-set/before.ts new file mode 100644 index 00000000..8e527151 --- /dev/null +++ b/packages/core/src/packs/typescript/fixtures/root-dir-set/before.ts @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist", // @uptide root-dir keep + "types": ["node"] + }, + "include": ["src/**/*.ts"] +} diff --git a/packages/core/src/packs/typescript/fixtures/root-dir-set/filename.txt b/packages/core/src/packs/typescript/fixtures/root-dir-set/filename.txt new file mode 100644 index 00000000..0820695c --- /dev/null +++ b/packages/core/src/packs/typescript/fixtures/root-dir-set/filename.txt @@ -0,0 +1 @@ +packages/api/tsconfig.json diff --git a/packages/core/src/packs/typescript/fixtures/root-dir/after.ts b/packages/core/src/packs/typescript/fixtures/root-dir/after.ts new file mode 100644 index 00000000..1cc1ff2a --- /dev/null +++ b/packages/core/src/packs/typescript/fixtures/root-dir/after.ts @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "module": "NodeNext", + "outDir": "./dist" // @uptide root-dir + }, + "include": ["src"] +} diff --git a/packages/core/src/packs/typescript/fixtures/root-dir/before.ts b/packages/core/src/packs/typescript/fixtures/root-dir/before.ts new file mode 100644 index 00000000..1cc1ff2a --- /dev/null +++ b/packages/core/src/packs/typescript/fixtures/root-dir/before.ts @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "module": "NodeNext", + "outDir": "./dist" // @uptide root-dir + }, + "include": ["src"] +} diff --git a/packages/core/src/packs/typescript/fixtures/root-dir/filename.txt b/packages/core/src/packs/typescript/fixtures/root-dir/filename.txt new file mode 100644 index 00000000..15231cfe --- /dev/null +++ b/packages/core/src/packs/typescript/fixtures/root-dir/filename.txt @@ -0,0 +1 @@ +tsconfig.json diff --git a/packages/core/src/packs/typescript/fixtures/tsconfig-deprecated/after.ts b/packages/core/src/packs/typescript/fixtures/tsconfig-deprecated/after.ts new file mode 100644 index 00000000..468c5098 --- /dev/null +++ b/packages/core/src/packs/typescript/fixtures/tsconfig-deprecated/after.ts @@ -0,0 +1,17 @@ +{ + // An older library build: every option TypeScript 6.0 deprecates. + "compilerOptions": { + "target": "es5", // @uptide target-es5 + "lib": ["dom", "es2015"], + "module": "amd", // @uptide module-format-removed + "moduleResolution": "node", // @uptide module-resolution-node10 + "baseUrl": "./src", // @uptide base-url + "paths": { "@app/*": ["app/*"] }, + "outFile": "dist/app.js", // @uptide out-file + "esModuleInterop": false, // @uptide interop-false + "allowSyntheticDefaultImports": true, // @uptide interop-false keep + "alwaysStrict": false, // @uptide always-strict-false + "strict": true, + }, + "include": ["src"], +} diff --git a/packages/core/src/packs/typescript/fixtures/tsconfig-deprecated/before.ts b/packages/core/src/packs/typescript/fixtures/tsconfig-deprecated/before.ts new file mode 100644 index 00000000..468c5098 --- /dev/null +++ b/packages/core/src/packs/typescript/fixtures/tsconfig-deprecated/before.ts @@ -0,0 +1,17 @@ +{ + // An older library build: every option TypeScript 6.0 deprecates. + "compilerOptions": { + "target": "es5", // @uptide target-es5 + "lib": ["dom", "es2015"], + "module": "amd", // @uptide module-format-removed + "moduleResolution": "node", // @uptide module-resolution-node10 + "baseUrl": "./src", // @uptide base-url + "paths": { "@app/*": ["app/*"] }, + "outFile": "dist/app.js", // @uptide out-file + "esModuleInterop": false, // @uptide interop-false + "allowSyntheticDefaultImports": true, // @uptide interop-false keep + "alwaysStrict": false, // @uptide always-strict-false + "strict": true, + }, + "include": ["src"], +} diff --git a/packages/core/src/packs/typescript/fixtures/tsconfig-deprecated/filename.txt b/packages/core/src/packs/typescript/fixtures/tsconfig-deprecated/filename.txt new file mode 100644 index 00000000..15231cfe --- /dev/null +++ b/packages/core/src/packs/typescript/fixtures/tsconfig-deprecated/filename.txt @@ -0,0 +1 @@ +tsconfig.json diff --git a/packages/core/src/packs/typescript/fold.test.ts b/packages/core/src/packs/typescript/fold.test.ts new file mode 100644 index 00000000..ff1eed32 --- /dev/null +++ b/packages/core/src/packs/typescript/fold.test.ts @@ -0,0 +1,165 @@ +import { mkdirSync, mkdtempSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { dirname, join } from 'node:path'; +import { expect, it } from 'vitest'; +import type { Finding } from '../../domain/report.js'; +import { foldTypesDefault } from './fold.js'; + +/** A compiler-only finding as `check` reports it: the code as its path, the message at the site. */ +const error = (file: string, line: number, code: number, message: string): Finding => + ({ + change: { + package: 'typescript', + from: '5.9.3', + to: '6.0.3', + path: `TS${code}`, + kind: 'type', + severity: 'breaking', + source: 'types', + confidence: 1, + }, + usage: { + file, + line, + column: 1, + endLine: line, + endColumn: 1, + symbolPath: `TS${code}`, + access: 'read', + snippet: '', + via: 'inferred', + compileError: message, + compileCode: code, + }, + severity: 'breaking', + confidence: 1, + fixability: 'unknown', + reason: 'compile error not attributed to a known API change', + }) as Finding; + +it('folds the `types` default errors into one finding at each tsconfig that leaves types unset (shogo82148/typescript-action)', () => { + // The shape of shogo82148/typescript-action at 0f03405: src/ through tsconfig.json, tests + // through tsconfig.eslint.json (ts-jest type-checks with it), neither listing `types`. Its + // upgrade added `"types": ["jest", "node"]` to tsconfig.eslint.json: one line, not 14. + const root = mkdtempSync(join(tmpdir(), 'uptide-types-default-')); + const write = (file: string, text: string) => { + mkdirSync(dirname(join(root, file)), { recursive: true }); + writeFileSync(join(root, file), text); + }; + write('package.json', '{ "name": "action" }'); + write('tsconfig.base.json', '{ "compilerOptions": { "strict": true, "module": "NodeNext" } }'); + write( + 'tsconfig.json', + '{\n "extends": "./tsconfig.base.json",\n "compilerOptions": { "outDir": "./dist" },\n "include": ["src"]\n}\n', + ); + write( + 'tsconfig.eslint.json', + '{\n "extends": "./tsconfig.base.json",\n "compilerOptions": { "noEmit": true },\n "include": ["__tests__", "src"]\n}\n', + ); + // A project that already lists its types: its errors are its own, not the default's. + write( + 'tools/tsconfig.json', + '{ "compilerOptions": { "types": ["node"], "noEmit": true }, "include": ["*.ts"] }', + ); + write('node_modules/@types/jest/package.json', '{ "name": "@types/jest" }'); + write( + 'node_modules/@types/jest/index.d.ts', + 'declare var describe: (name: string, fn: () => void) => void;\ndeclare var it: (name: string, fn: () => void) => void;\ndeclare const expect: (value: unknown) => { toBe(v: unknown): void };\n', + ); + write('node_modules/@types/node/package.json', '{ "name": "@types/node" }'); + write( + 'node_modules/@types/node/timers.d.ts', + 'declare global {\n function setTimeout(callback: () => void, ms?: number): number;\n}\nexport {};\n', + ); + write('src/wait.ts', 'export const wait = () => setTimeout(() => {}, 1);\n'); + write( + '__tests__/wait.test.ts', + "describe('wait', () => {\n it('waits', () => {\n expect(1).toBe(1);\n notAGlobal();\n });\n});\n", + ); + write('tools/run.ts', 'process.exit(0);\n'); + + const findings = [ + error( + '__tests__/wait.test.ts', + 1, + 2582, + "Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.", + ), + error( + '__tests__/wait.test.ts', + 2, + 2582, + "Cannot find name 'it'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.", + ), + error('__tests__/wait.test.ts', 3, 2304, "Cannot find name 'expect'."), + // Not a global any @types package declares: a real error of its own. + error('__tests__/wait.test.ts', 4, 2304, "Cannot find name 'notAGlobal'."), + error('src/wait.ts', 1, 2304, "Cannot find name 'setTimeout'."), + // Its tsconfig lists `types`: not the default's doing. + error( + 'tools/run.ts', + 1, + 2591, + "Cannot find name 'process'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.", + ), + ]; + const result = foldTypesDefault({ root, workspace: '.', findings }); + + const anchors = result.filter((f) => f.rule === 'changed-defaults'); + expect( + anchors.map((f) => [ + f.usage.file, + f.usage.line, + f.severity, + f.anchorOnly, + f.downstream?.map((d) => `${d.file}:${d.line}`), + ]), + ).toEqual([ + [ + 'tsconfig.eslint.json', + 1, + 'deprecated', + true, + ['__tests__/wait.test.ts:1', '__tests__/wait.test.ts:2', '__tests__/wait.test.ts:3'], + ], + ['tsconfig.json', 1, 'deprecated', true, ['src/wait.ts:1']], + ]); + // What stands on its own: the undeclared name, and the error under a tsconfig that lists types. + expect( + result.filter((f) => f.rule === undefined).map((f) => `${f.usage.file}:${f.usage.line}`), + ).toEqual(['__tests__/wait.test.ts:4', 'tools/run.ts:1']); +}); + +it('folds into the project that leaves types unset, not a nearer one that lists them (Borewit/music-metadata)', () => { + // test/tsconfig.json lists mocha; the root tsconfig has no `include`, compiles test/ as well + // and lists nothing. The errors are the root project's, and the upgrade added types there. + const root = mkdtempSync(join(tmpdir(), 'uptide-types-default-root-')); + const write = (file: string, text: string) => { + mkdirSync(dirname(join(root, file)), { recursive: true }); + writeFileSync(join(root, file), text); + }; + write('package.json', '{ "name": "lib" }'); + write('tsconfig.json', '{\n "compilerOptions": { "module": "node16" }\n}\n'); + write( + 'test/tsconfig.json', + '{ "extends": "../tsconfig.json", "compilerOptions": { "types": ["mocha"] } }', + ); + write('node_modules/@types/mocha/package.json', '{ "name": "@types/mocha" }'); + write('node_modules/@types/mocha/index.d.ts', 'declare var describe: (n: string) => void;\n'); + write('test/a.test.ts', "describe('a');\n"); + const result = foldTypesDefault({ + root, + workspace: '.', + findings: [ + error( + 'test/a.test.ts', + 1, + 2593, + "Cannot find name 'describe'. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha` and then add 'jest' or 'mocha' to the types field in your tsconfig.", + ), + ], + }); + expect(result.map((f) => [f.rule, f.usage.file, f.downstream?.length])).toEqual([ + ['changed-defaults', 'tsconfig.json', 1], + ]); +}); diff --git a/packages/core/src/packs/typescript/fold.ts b/packages/core/src/packs/typescript/fold.ts new file mode 100644 index 00000000..cfc25c7a --- /dev/null +++ b/packages/core/src/packs/typescript/fold.ts @@ -0,0 +1,185 @@ +import { type Dirent, existsSync, readdirSync, readFileSync, realpathSync } from 'node:fs'; +import { basename, dirname, join, relative, resolve } from 'node:path'; +import { ts } from 'ts-morph'; +import { automaticTypes } from '../../adapters/typescript/legacy-options.js'; +import { type CompileProject, projectScope } from '../../adapters/typescript/project-scope.js'; +import type { DownstreamSite, Finding } from '../../domain/report.js'; + +/** + * TypeScript 6 defaults `types` to `[]`: the `@types` packages 5.x included on its own + * (`@types/node`, `@types/jest`) are gone, and every use of their globals fails (`describe`, + * `process`, `setTimeout`). A person fixes that with one line in the tsconfig, `"types": [...]`, + * not at each use. So, as option cascades fold into their option (check/option-cascades.ts), + * these errors fold into one `changed-defaults` finding at the tsconfig that compiles the file + * and leaves `types` unset, and stand as its evidence. + */ + +const NOTE = 'changed-defaults'; + +/** "Cannot find name 'x'. Do you need to install type definitions for node / a test runner / jQuery?" */ +const TYPE_PACKAGE_HINT = new Set([2580, 2581, 2582, 2591, 2592, 2593]); +/** "Cannot find name 'x'" / "Cannot find namespace 'x'": folded only for a global an `@types` package declares. */ +const MISSING_NAME = new Set([2304, 2503]); + +const codeOf = (f: Finding): number => + f.usage.compileCode ?? Number(/^TS(\d+)$/.exec(f.change.path)?.[1] ?? 0); +const nameOf = (f: Finding): string | undefined => + /'([^']+)'/.exec(f.usage.compileError ?? '')?.[1]; + +interface Config { + /** Absolute path. */ + file: string; + /** `types` unset through the whole `extends` chain. */ + typesUnset: boolean; + /** Globals the `@types` packages 5.x included for it declare. */ + globals: Set; +} + +/** Names declared in a package's `.d.ts` files: a heuristic over the text, not a type check. */ +function declaredNames(dir: string, names: Set, depth = 0): void { + // ponytail: regex over declaration text; a global only a re-export brings in is missed, and its error stays a site. + let entries: Dirent[]; + try { + entries = readdirSync(dir, { withFileTypes: true }); + } catch { + return; + } + for (const entry of entries) { + const path = join(dir, entry.name); + if (entry.isDirectory() && entry.name !== 'node_modules' && depth < 4) + declaredNames(path, names, depth + 1); + else if (entry.isFile() && entry.name.endsWith('.d.ts')) + for (const m of readFileSync(path, 'utf8').matchAll( + /\b(?:function|var|let|const|class|namespace|interface)\s+([A-Za-z_$][\w$]*)/g, + )) + names.add(m[1] as string); + } +} + +function readConfig(file: string): Config | undefined { + const parsed = ts.getParsedCommandLineOfConfigFile( + file, + {}, + { ...ts.sys, onUnRecoverableConfigFileDiagnostic: () => {} }, + ); + if (!parsed) return undefined; + const typesUnset = parsed.options.types === undefined; + const globals = new Set(); + if (typesUnset) + for (const root of ts.getEffectiveTypeRoots(parsed.options, ts.sys) ?? []) + for (const name of automaticTypes(parsed.options)) + if (existsSync(join(root, name))) declaredNames(join(root, name), globals); + return { file, typesUnset, globals }; +} + +/** The projects that compile a file, ranked as the compile ranks owners (compile.ts). */ +function ownersOf(projects: CompileProject[], file: string): CompileProject[] { + const enclosing = (p: CompileProject): boolean => file.startsWith(`${dirname(p.config)}/`); + const rank = (p: CompileProject): number => + enclosing(p) ? (basename(p.config) === 'tsconfig.json' ? 2 : 1) : 0; + return projects + .filter((p) => p.covers(file)) + .sort( + (a, b) => + rank(b) - rank(a) || + (enclosing(b) ? dirname(b.config).length : 0) - + (enclosing(a) ? dirname(a.config).length : 0), + ); +} + +/** The line of the config's root object: where an option is added, as ground truth anchors an insertion. */ +function rootLine(file: string): { line: number; snippet: string } { + const text = readFileSync(file, 'utf8'); + const source = ts.parseJsonText(file, text); + const root = source.statements[0]?.expression; + const line = root ? source.getLineAndCharacterOfPosition(root.getStart(source)).line : 0; + return { line: line + 1, snippet: (text.split('\n')[line] ?? '').trim() }; +} + +export function foldTypesDefault(input: { + root: string; + workspace: string; + findings: Finding[]; +}): Finding[] { + // Real paths, as the project scope keeps them (`/var` is `/private/var` on macOS). + const dir = realpathSync(resolve(input.root, input.workspace)); + const candidates = input.findings.filter((f) => { + const code = codeOf(f); + return f.rule === undefined && (TYPE_PACKAGE_HINT.has(code) || MISSING_NAME.has(code)); + }); + if (candidates.length === 0) return input.findings; + const scope = projectScope({ dir, root: realpathSync(input.root) }); + const configs = new Map(); + const config = (file: string): Config | undefined => { + if (!configs.has(file)) configs.set(file, readConfig(file)); + return configs.get(file); + }; + const folded = new Map(); + for (const f of candidates) { + const name = nameOf(f); + // The project whose compile the default broke: one covering the file that leaves `types` + // unset (and, for a bare missing name, whose old @types declare it). A nearer project + // that lists its types did not produce the error (Borewit's test/ lists mocha; the root + // tsconfig, which compiles test/ too, does not, and is where the upgrade added it). + const owner = ownersOf(scope.projects, resolve(dir, f.usage.file)) + .map((p) => config(p.config)) + .find( + (c) => + c?.typesUnset && + (!MISSING_NAME.has(codeOf(f)) || (name !== undefined && c.globals.has(name))), + ); + if (!owner) continue; + folded.set(owner.file, [...(folded.get(owner.file) ?? []), f]); + } + if (folded.size === 0) return input.findings; + const taken = new Set([...folded.values()].flat()); + const anchors = [...folded].map(([config, errors]): Finding => { + const first = errors[0] as Finding; + const file = relative(dir, config); + const { line, snippet } = rootLine(config); + const downstream: DownstreamSite[] = errors.map((e) => ({ + file: e.usage.file, + line: e.usage.line, + column: e.usage.column, + snippet: e.usage.snippet, + code: codeOf(e), + message: e.usage.compileError ?? e.reason, + })); + const { compileError: _error, compileCode: _code, ...usage } = first.usage; + const path = `${NOTE}:types`; + const names = [...new Set(errors.map(nameOf).filter(Boolean))].slice(0, 6).join(', '); + return { + ...first, + change: { + ...first.change, + path, + kind: 'deprecated', + severity: 'deprecated', + source: 'pack', + confidence: 1, + }, + usage: { + ...usage, + file, + line, + column: 1, + endLine: line, + endColumn: 1, + symbolPath: path, + snippet, + via: 'inferred', + }, + severity: 'deprecated', + confidence: 1, + fixability: 'manual', + rule: NOTE, + anchorOnly: true, + downstream, + reason: `TypeScript 6 includes no @types package unless \`types\` lists it: ${errors.length} error${errors.length === 1 ? '' : 's'} in files ${file} compiles use their globals`, + details: [ + `${errors.length} "Cannot find name" error${errors.length === 1 ? '' : 's'} (${names}) go with one edit here: list the @types packages the code uses in compilerOptions.types, or pin the 5.x behavior with ["*"]`, + ], + }; + }); + return [...input.findings.filter((f) => !taken.has(f)), ...anchors]; +} diff --git a/packages/core/src/packs/typescript/ground-truth.json b/packages/core/src/packs/typescript/ground-truth.json new file mode 100644 index 00000000..d70ac0cf --- /dev/null +++ b/packages/core/src/packs/typescript/ground-truth.json @@ -0,0 +1,7120 @@ +{ + "$comment": "typescript 5 to 6. Expected findings drafted with pnpm packs:truth (the target compiler on every tsconfig.json, and the upgrade commit) on 2026-10-14 and fixed before the first scoring run. Compiler code diagnostics are expected under generic; a deprecated option is expected where it is set, not at the extending config the compiler names. RESEARCH.md says how each kind of site was assigned.", + "package": "typescript", + "repos": [ + { + "repo": "m-lyon/recipe", + "commit": "f6e44b530c4dab5afc9ca6f3505774d5b44fcd33", + "migration": "https://github.com/m-lyon/recipe/pull/113", + "directory": "client", + "from": "5.6.2", + "to": "6.0.3", + "why": "baseUrl \"src\" with ten paths entries: removed, paths rewritten relative (\"./src/graphql/*\")", + "findings": [ + { + "file": "tsconfig.json", + "line": 32, + "rule": "base-url", + "note": "paths entries rewritten relative to the tsconfig in the same change (baseUrl removal)" + } + ] + }, + { + "repo": "robrechtme/recipes", + "commit": "0853747427b71f1d5de275e72aead59979fc1a03", + "migration": "https://github.com/robrechtme/recipes/pull/7", + "from": "5.9.3", + "to": "6.0.3", + "why": "target es5 raised to ES2022; baseUrl \"src\" removed with paths rewritten, including \"../data\" entries", + "findings": [ + { + "file": "tsconfig.json", + "line": 3, + "rule": "target-es5" + }, + { + "file": "tsconfig.json", + "line": 17, + "rule": "base-url", + "note": "paths entries rewritten relative to the tsconfig in the same change (baseUrl removal)" + } + ] + }, + { + "repo": "JosephMaynard/dependency-radar", + "commit": "1920eb3217b701e43afc1651db3f6197028e6f0e", + "migration": "https://github.com/JosephMaynard/dependency-radar/pull/46", + "from": "5.9.3", + "to": "6.0.3", + "why": "moduleResolution node moved to node16 (module CommonJS to Node16 with it); types [] default: node globals and builtin imports stop resolving across src/, and a CSS side-effect import in report-ui/ fails (TS2882)", + "findings": [ + { + "file": "report-ui/main.ts", + "line": 6, + "rule": "generic" + }, + { + "file": "src/aggregator.ts", + "line": 34, + "rule": "generic" + }, + { + "file": "src/aggregator.ts", + "line": 35, + "rule": "generic" + }, + { + "file": "src/aggregator.ts", + "line": 36, + "rule": "generic" + }, + { + "file": "src/aggregator.ts", + "line": 579, + "rule": "generic" + }, + { + "file": "src/aggregator.ts", + "line": 580, + "rule": "generic" + }, + { + "file": "src/aggregator.ts", + "line": 1405, + "rule": "generic" + }, + { + "file": "src/aggregator.ts", + "line": 1406, + "rule": "generic" + }, + { + "file": "src/aggregator.ts", + "line": 2152, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 3, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 4, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 43, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 79, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 80, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 81, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 82, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 83, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 84, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 85, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 86, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 1118, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 1158, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 1178, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 1180, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 1187, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 1189, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 1195, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 1210, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 1214, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 1219, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 1228, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 1232, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 1252, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 1406, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 1429, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 1430, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 1433, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 1439, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 1440, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 1441, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2129, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2130, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2293, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2301, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2320, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2328, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2341, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2359, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2376, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2382, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2399, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2407, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2410, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2434, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2467, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2504, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2506, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2516, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2519, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2528, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2534, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2540, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2543, + "rule": "generic" + }, + { + "file": "src/cli.ts", + "line": 2544, + "rule": "generic" + }, + { + "file": "src/outputFormats.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "src/report.ts", + "line": 1, + "rule": "generic" + }, + { + "file": "src/report.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "src/runners/importGraphRunner.ts", + "line": 1, + "rule": "generic" + }, + { + "file": "src/runners/importGraphRunner.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "src/runners/importGraphRunner.ts", + "line": 3, + "rule": "generic" + }, + { + "file": "src/runners/importGraphRunner.ts", + "line": 203, + "rule": "generic" + }, + { + "file": "src/runners/lockfileGraph.ts", + "line": 1, + "rule": "generic" + }, + { + "file": "src/runners/lockfileGraph.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "src/runners/lockfileSignals.ts", + "line": 1, + "rule": "generic" + }, + { + "file": "src/runners/lockfileSignals.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "src/runners/npmAudit.ts", + "line": 1, + "rule": "generic" + }, + { + "file": "src/runners/npmLs.ts", + "line": 1, + "rule": "generic" + }, + { + "file": "src/runners/npmLs.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "src/runners/npmLs.ts", + "line": 136, + "rule": "generic" + }, + { + "file": "src/runners/npmLs.ts", + "line": 151, + "rule": "generic" + }, + { + "file": "src/runners/npmOutdated.ts", + "line": 1, + "rule": "generic" + }, + { + "file": "src/runners/npmRegistryMetadata.ts", + "line": 380, + "rule": "generic" + }, + { + "file": "src/runners/npmRegistryMetadata.ts", + "line": 381, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 1, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 3, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 4, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 15, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 22, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 27, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 30, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 31, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 40, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 50, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 55, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 60, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 64, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 65, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 67, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 68, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 71, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 76, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 88, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 89, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 99, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 176, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 179, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 180, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 223, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 291, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 299, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 357, + "rule": "generic" + }, + { + "file": "src/utils.ts", + "line": 359, + "rule": "generic" + }, + { + "file": "tsconfig.json", + "line": 4, + "rule": "module-resolution-node10", + "note": "module CommonJS to Node16, required by the node16 resolution the same change chose" + }, + { + "file": "tsconfig.json", + "line": 13, + "rule": "module-resolution-node10" + } + ] + }, + { + "repo": "CroffZ/github-release-notifier", + "commit": "d9353f2471c554f0ce339496f0058a27599f4965", + "migration": "https://github.com/CroffZ/github-release-notifier/pull/5", + "from": "5.9.3", + "to": "6.0.3", + "why": "pnpm monorepo: baseUrl in the shared tsconfig.base.json removed (the compiler reports it at the extending packages/action tsconfig); rootDir \"../..\" added where the action package compiles core sources; types [] default breaks node globals", + "findings": [ + { + "file": "packages/action/src/index.ts", + "line": 8, + "rule": "generic" + }, + { + "file": "packages/action/tsconfig.json", + "line": 5, + "rule": "root-dir", + "note": "rootDir \"../..\" added after this line (the comma is the changed line)" + }, + { + "file": "packages/cli/src/arguments.ts", + "line": 1, + "rule": "generic" + }, + { + "file": "packages/cli/src/index.ts", + "line": 21, + "rule": "generic" + }, + { + "file": "packages/cli/src/index.ts", + "line": 23, + "rule": "generic" + }, + { + "file": "packages/cli/src/index.ts", + "line": 25, + "rule": "generic" + }, + { + "file": "packages/cli/src/index.ts", + "line": 27, + "rule": "generic" + }, + { + "file": "packages/cli/src/index.ts", + "line": 30, + "rule": "generic" + }, + { + "file": "packages/cli/src/index.ts", + "line": 33, + "rule": "generic" + }, + { + "file": "packages/cli/src/index.ts", + "line": 36, + "rule": "generic" + }, + { + "file": "packages/core/src/release-identity.ts", + "line": 28, + "rule": "generic" + }, + { + "file": "tsconfig.base.json", + "line": 14, + "rule": "base-url", + "note": "set here, reported by the compiler at packages/action/tsconfig.json:3; paths entries rewritten relative to the tsconfig in the same change (baseUrl removal)" + } + ] + }, + { + "repo": "SignalK/signalk-derived-data", + "commit": "02f635e8232a433462593ed1192ff22e2e9b6253", + "migration": "https://github.com/SignalK/signalk-derived-data/pull/239", + "from": "5.9.3", + "to": "6.0.3", + "why": "moduleResolution node kept behind ignoreDeprecations \"6.0\" (the compiler error is the site); types [] default: module, require, __dirname and mocha globals stop resolving across src/ and test/", + "findings": [ + { + "file": "src/calcs/airDensity.ts", + "line": 37, + "rule": "generic" + }, + { + "file": "src/calcs/batteryPower.ts", + "line": 24, + "rule": "generic" + }, + { + "file": "src/calcs/courseOverGroundMagnetic.ts", + "line": 58, + "rule": "generic" + }, + { + "file": "src/calcs/courseOverGroundTrue.ts", + "line": 51, + "rule": "generic" + }, + { + "file": "src/calcs/cpa_tcpa.ts", + "line": 516, + "rule": "generic" + }, + { + "file": "src/calcs/depthBelowKeel.ts", + "line": 22, + "rule": "generic" + }, + { + "file": "src/calcs/depthBelowKeel2.ts", + "line": 34, + "rule": "generic" + }, + { + "file": "src/calcs/depthBelowSurface.ts", + "line": 22, + "rule": "generic" + }, + { + "file": "src/calcs/dewPoint.ts", + "line": 62, + "rule": "generic" + }, + { + "file": "src/calcs/eta.ts", + "line": 64, + "rule": "generic" + }, + { + "file": "src/calcs/fuelConsumtion.ts", + "line": 42, + "rule": "generic" + }, + { + "file": "src/calcs/headingTrue.ts", + "line": 46, + "rule": "generic" + }, + { + "file": "src/calcs/heatIndex.ts", + "line": 65, + "rule": "generic" + }, + { + "file": "src/calcs/leeway.ts", + "line": 36, + "rule": "generic" + }, + { + "file": "src/calcs/leewayAngle.ts", + "line": 36, + "rule": "generic" + }, + { + "file": "src/calcs/magneticVariation.ts", + "line": 183, + "rule": "generic" + }, + { + "file": "src/calcs/moon.ts", + "line": 125, + "rule": "generic" + }, + { + "file": "src/calcs/propslip.ts", + "line": 83, + "rule": "generic" + }, + { + "file": "src/calcs/propState.ts", + "line": 105, + "rule": "generic" + }, + { + "file": "src/calcs/setDrift.ts", + "line": 214, + "rule": "generic" + }, + { + "file": "src/calcs/setDrift.ts", + "line": 222, + "rule": "generic" + }, + { + "file": "src/calcs/steer_error.ts", + "line": 60, + "rule": "generic" + }, + { + "file": "src/calcs/suncalc.ts", + "line": 166, + "rule": "generic" + }, + { + "file": "src/calcs/suntime.ts", + "line": 90, + "rule": "generic" + }, + { + "file": "src/calcs/tankVolume.ts", + "line": 114, + "rule": "generic" + }, + { + "file": "src/calcs/tankVolume2.ts", + "line": 32, + "rule": "generic" + }, + { + "file": "src/calcs/transducerToKeel.ts", + "line": 37, + "rule": "generic" + }, + { + "file": "src/calcs/vmg_wind_stw.ts", + "line": 24, + "rule": "generic" + }, + { + "file": "src/calcs/vmg_wind.ts", + "line": 20, + "rule": "generic" + }, + { + "file": "src/calcs/windChill.ts", + "line": 48, + "rule": "generic" + }, + { + "file": "src/calcs/windDirection.ts", + "line": 210, + "rule": "generic" + }, + { + "file": "src/calcs/windGround.ts", + "line": 98, + "rule": "generic" + }, + { + "file": "src/calcs/windShift.ts", + "line": 93, + "rule": "generic" + }, + { + "file": "src/index.ts", + "line": 26, + "rule": "generic" + }, + { + "file": "src/index.ts", + "line": 27, + "rule": "generic" + }, + { + "file": "src/index.ts", + "line": 28, + "rule": "generic" + }, + { + "file": "src/index.ts", + "line": 468, + "rule": "generic" + }, + { + "file": "src/index.ts", + "line": 474, + "rule": "generic" + }, + { + "file": "src/index.ts", + "line": 486, + "rule": "generic" + }, + { + "file": "src/index.ts", + "line": 489, + "rule": "generic" + }, + { + "file": "src/index.ts", + "line": 497, + "rule": "generic" + }, + { + "file": "test/airDensity.ts", + "line": 7, + "rule": "generic" + }, + { + "file": "test/airDensity.ts", + "line": 9, + "rule": "generic" + }, + { + "file": "test/airDensity.ts", + "line": 11, + "rule": "generic" + }, + { + "file": "test/airDensity.ts", + "line": 22, + "rule": "generic" + }, + { + "file": "test/airDensity.ts", + "line": 33, + "rule": "generic" + }, + { + "file": "test/airDensity.ts", + "line": 40, + "rule": "generic" + }, + { + "file": "test/airDensity.ts", + "line": 48, + "rule": "generic" + }, + { + "file": "test/batteryPower.ts", + "line": 10, + "rule": "generic" + }, + { + "file": "test/batteryPower.ts", + "line": 12, + "rule": "generic" + }, + { + "file": "test/batteryPower.ts", + "line": 14, + "rule": "generic" + }, + { + "file": "test/batteryPower.ts", + "line": 26, + "rule": "generic" + }, + { + "file": "test/batteryPower.ts", + "line": 34, + "rule": "generic" + }, + { + "file": "test/courseOverGroundMagnetic.ts", + "line": 6, + "rule": "generic" + }, + { + "file": "test/courseOverGroundMagnetic.ts", + "line": 8, + "rule": "generic" + }, + { + "file": "test/courseOverGroundMagnetic.ts", + "line": 10, + "rule": "generic" + }, + { + "file": "test/courseOverGroundMagnetic.ts", + "line": 19, + "rule": "generic" + }, + { + "file": "test/courseOverGroundMagnetic.ts", + "line": 25, + "rule": "generic" + }, + { + "file": "test/courseOverGroundMagnetic.ts", + "line": 31, + "rule": "generic" + }, + { + "file": "test/courseOverGroundTrue.ts", + "line": 7, + "rule": "generic" + }, + { + "file": "test/courseOverGroundTrue.ts", + "line": 9, + "rule": "generic" + }, + { + "file": "test/courseOverGroundTrue.ts", + "line": 11, + "rule": "generic" + }, + { + "file": "test/courseOverGroundTrue.ts", + "line": 20, + "rule": "generic" + }, + { + "file": "test/courseOverGroundTrue.ts", + "line": 28, + "rule": "generic" + }, + { + "file": "test/courseOverGroundTrue.ts", + "line": 34, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 8, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 10, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 49, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 58, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 66, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 93, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 112, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 137, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 169, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 200, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 226, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 252, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 277, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 304, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 329, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 362, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 418, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 449, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 485, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 487, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 514, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 546, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 574, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 615, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 669, + "rule": "generic" + }, + { + "file": "test/cpa_tcpa.ts", + "line": 716, + "rule": "generic" + }, + { + "file": "test/depthBelowKeel.ts", + "line": 10, + "rule": "generic" + }, + { + "file": "test/depthBelowKeel.ts", + "line": 12, + "rule": "generic" + }, + { + "file": "test/depthBelowKeel.ts", + "line": 14, + "rule": "generic" + }, + { + "file": "test/depthBelowKeel.ts", + "line": 26, + "rule": "generic" + }, + { + "file": "test/depthBelowKeel2.ts", + "line": 7, + "rule": "generic" + }, + { + "file": "test/depthBelowKeel2.ts", + "line": 9, + "rule": "generic" + }, + { + "file": "test/depthBelowKeel2.ts", + "line": 11, + "rule": "generic" + }, + { + "file": "test/depthBelowKeel2.ts", + "line": 20, + "rule": "generic" + }, + { + "file": "test/depthBelowKeel2.ts", + "line": 26, + "rule": "generic" + }, + { + "file": "test/depthBelowKeel2.ts", + "line": 33, + "rule": "generic" + }, + { + "file": "test/depthBelowKeel2.ts", + "line": 43, + "rule": "generic" + }, + { + "file": "test/depthBelowSurface.ts", + "line": 10, + "rule": "generic" + }, + { + "file": "test/depthBelowSurface.ts", + "line": 12, + "rule": "generic" + }, + { + "file": "test/depthBelowSurface.ts", + "line": 14, + "rule": "generic" + }, + { + "file": "test/depthBelowSurface.ts", + "line": 26, + "rule": "generic" + }, + { + "file": "test/dewPoint.ts", + "line": 6, + "rule": "generic" + }, + { + "file": "test/dewPoint.ts", + "line": 8, + "rule": "generic" + }, + { + "file": "test/dewPoint.ts", + "line": 10, + "rule": "generic" + }, + { + "file": "test/eta.ts", + "line": 11, + "rule": "generic" + }, + { + "file": "test/eta.ts", + "line": 13, + "rule": "generic" + }, + { + "file": "test/eta.ts", + "line": 15, + "rule": "generic" + }, + { + "file": "test/eta.ts", + "line": 22, + "rule": "generic" + }, + { + "file": "test/eta.ts", + "line": 28, + "rule": "generic" + }, + { + "file": "test/eta.ts", + "line": 37, + "rule": "generic" + }, + { + "file": "test/fuelConsumtion.ts", + "line": 7, + "rule": "generic" + }, + { + "file": "test/fuelConsumtion.ts", + "line": 9, + "rule": "generic" + }, + { + "file": "test/fuelConsumtion.ts", + "line": 11, + "rule": "generic" + }, + { + "file": "test/fuelConsumtion.ts", + "line": 24, + "rule": "generic" + }, + { + "file": "test/fuelConsumtion.ts", + "line": 31, + "rule": "generic" + }, + { + "file": "test/fuelConsumtion.ts", + "line": 39, + "rule": "generic" + }, + { + "file": "test/fuelConsumtion.ts", + "line": 46, + "rule": "generic" + }, + { + "file": "test/fuelConsumtion.ts", + "line": 52, + "rule": "generic" + }, + { + "file": "test/headingTrue.ts", + "line": 7, + "rule": "generic" + }, + { + "file": "test/headingTrue.ts", + "line": 9, + "rule": "generic" + }, + { + "file": "test/headingTrue.ts", + "line": 11, + "rule": "generic" + }, + { + "file": "test/headingTrue.ts", + "line": 20, + "rule": "generic" + }, + { + "file": "test/headingTrue.ts", + "line": 28, + "rule": "generic" + }, + { + "file": "test/headingTrue.ts", + "line": 34, + "rule": "generic" + }, + { + "file": "test/heatIndex.ts", + "line": 10, + "rule": "generic" + }, + { + "file": "test/heatIndex.ts", + "line": 12, + "rule": "generic" + }, + { + "file": "test/heatIndex.ts", + "line": 21, + "rule": "generic" + }, + { + "file": "test/heatIndex.ts", + "line": 27, + "rule": "generic" + }, + { + "file": "test/heatIndex.ts", + "line": 36, + "rule": "generic" + }, + { + "file": "test/heatIndex.ts", + "line": 41, + "rule": "generic" + }, + { + "file": "test/heatIndex.ts", + "line": 50, + "rule": "generic" + }, + { + "file": "test/heatIndex.ts", + "line": 52, + "rule": "generic" + }, + { + "file": "test/heatIndex.ts", + "line": 58, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 8, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 9, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 11, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 29, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 31, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 33, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 44, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 45, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 54, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 55, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 56, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 82, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 119, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 122, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 130, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 133, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 139, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 142, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 147, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 148, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 169, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 189, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 210, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 226, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 251, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 271, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 286, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 304, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 319, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 341, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 351, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 360, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 373, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 411, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 424, + "rule": "generic" + }, + { + "file": "test/integration/plugin-index.ts", + "line": 426, + "rule": "generic" + }, + { + "file": "test/integration/plugin-loader.ts", + "line": 10, + "rule": "generic" + }, + { + "file": "test/integration/plugin-loader.ts", + "line": 11, + "rule": "generic" + }, + { + "file": "test/integration/plugin-loader.ts", + "line": 16, + "rule": "generic" + }, + { + "file": "test/integration/plugin-loader.ts", + "line": 18, + "rule": "generic" + }, + { + "file": "test/integration/plugin-loader.ts", + "line": 20, + "rule": "generic" + }, + { + "file": "test/integration/plugin-loader.ts", + "line": 26, + "rule": "generic" + }, + { + "file": "test/integration/plugin-loader.ts", + "line": 27, + "rule": "generic" + }, + { + "file": "test/integration/plugin-loader.ts", + "line": 35, + "rule": "generic" + }, + { + "file": "test/integration/plugin-loader.ts", + "line": 36, + "rule": "generic" + }, + { + "file": "test/integration/plugin-loader.ts", + "line": 37, + "rule": "generic" + }, + { + "file": "test/integration/plugin-loader.ts", + "line": 60, + "rule": "generic" + }, + { + "file": "test/integration/plugin-loader.ts", + "line": 61, + "rule": "generic" + }, + { + "file": "test/integration/plugin-loader.ts", + "line": 65, + "rule": "generic" + }, + { + "file": "test/integration/plugin-loader.ts", + "line": 78, + "rule": "generic" + }, + { + "file": "test/integration/plugin-loader.ts", + "line": 83, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 13, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 51, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 52, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 55, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 62, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 68, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 75, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 78, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 89, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 97, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 116, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 119, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 134, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 135, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 137, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 148, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 151, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 163, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 175, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 178, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 194, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 209, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 239, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 257, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 275, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 278, + "rule": "generic" + }, + { + "file": "test/integration/plugin-start.ts", + "line": 300, + "rule": "generic" + }, + { + "file": "test/leeway.ts", + "line": 10, + "rule": "generic" + }, + { + "file": "test/leeway.ts", + "line": 12, + "rule": "generic" + }, + { + "file": "test/leeway.ts", + "line": 14, + "rule": "generic" + }, + { + "file": "test/leeway.ts", + "line": 20, + "rule": "generic" + }, + { + "file": "test/leeway.ts", + "line": 33, + "rule": "generic" + }, + { + "file": "test/leewayAngle.ts", + "line": 6, + "rule": "generic" + }, + { + "file": "test/leewayAngle.ts", + "line": 8, + "rule": "generic" + }, + { + "file": "test/leewayAngle.ts", + "line": 10, + "rule": "generic" + }, + { + "file": "test/leewayAngle.ts", + "line": 23, + "rule": "generic" + }, + { + "file": "test/leewayAngle.ts", + "line": 32, + "rule": "generic" + }, + { + "file": "test/leewayAngle.ts", + "line": 40, + "rule": "generic" + }, + { + "file": "test/leewayAngle.ts", + "line": 47, + "rule": "generic" + }, + { + "file": "test/leewayAngle.ts", + "line": 55, + "rule": "generic" + }, + { + "file": "test/magneticVariation.ts", + "line": 7, + "rule": "generic" + }, + { + "file": "test/magneticVariation.ts", + "line": 9, + "rule": "generic" + }, + { + "file": "test/magneticVariation.ts", + "line": 11, + "rule": "generic" + }, + { + "file": "test/magneticVariation.ts", + "line": 30, + "rule": "generic" + }, + { + "file": "test/magneticVariation.ts", + "line": 32, + "rule": "generic" + }, + { + "file": "test/magneticVariation.ts", + "line": 33, + "rule": "generic" + }, + { + "file": "test/magneticVariation.ts", + "line": 36, + "rule": "generic" + }, + { + "file": "test/magneticVariation.ts", + "line": 40, + "rule": "generic" + }, + { + "file": "test/magneticVariation.ts", + "line": 42, + "rule": "generic" + }, + { + "file": "test/magneticVariation.ts", + "line": 45, + "rule": "generic" + }, + { + "file": "test/magneticVariation.ts", + "line": 49, + "rule": "generic" + }, + { + "file": "test/magneticVariation.ts", + "line": 54, + "rule": "generic" + }, + { + "file": "test/moon.ts", + "line": 7, + "rule": "generic" + }, + { + "file": "test/moon.ts", + "line": 9, + "rule": "generic" + }, + { + "file": "test/moon.ts", + "line": 15, + "rule": "generic" + }, + { + "file": "test/moon.ts", + "line": 34, + "rule": "generic" + }, + { + "file": "test/moon.ts", + "line": 54, + "rule": "generic" + }, + { + "file": "test/moon.ts", + "line": 70, + "rule": "generic" + }, + { + "file": "test/moon.ts", + "line": 96, + "rule": "generic" + }, + { + "file": "test/moon.ts", + "line": 97, + "rule": "generic" + }, + { + "file": "test/moon.ts", + "line": 98, + "rule": "generic" + }, + { + "file": "test/moon.ts", + "line": 99, + "rule": "generic" + }, + { + "file": "test/moon.ts", + "line": 100, + "rule": "generic" + }, + { + "file": "test/moon.ts", + "line": 103, + "rule": "generic" + }, + { + "file": "test/moon.ts", + "line": 117, + "rule": "generic" + }, + { + "file": "test/moon.ts", + "line": 118, + "rule": "generic" + }, + { + "file": "test/moon.ts", + "line": 120, + "rule": "generic" + }, + { + "file": "test/moon.ts", + "line": 164, + "rule": "generic" + }, + { + "file": "test/moon.ts", + "line": 165, + "rule": "generic" + }, + { + "file": "test/moon.ts", + "line": 166, + "rule": "generic" + }, + { + "file": "test/moon.ts", + "line": 167, + "rule": "generic" + }, + { + "file": "test/propslip.ts", + "line": 11, + "rule": "generic" + }, + { + "file": "test/propslip.ts", + "line": 13, + "rule": "generic" + }, + { + "file": "test/propslip.ts", + "line": 15, + "rule": "generic" + }, + { + "file": "test/propslip.ts", + "line": 30, + "rule": "generic" + }, + { + "file": "test/propslip.ts", + "line": 48, + "rule": "generic" + }, + { + "file": "test/propslip.ts", + "line": 61, + "rule": "generic" + }, + { + "file": "test/propState.ts", + "line": 6, + "rule": "generic" + }, + { + "file": "test/propState.ts", + "line": 8, + "rule": "generic" + }, + { + "file": "test/propState.ts", + "line": 10, + "rule": "generic" + }, + { + "file": "test/setDrift.ts", + "line": 10, + "rule": "generic" + }, + { + "file": "test/setDrift.ts", + "line": 12, + "rule": "generic" + }, + { + "file": "test/setDrift.ts", + "line": 21, + "rule": "generic" + }, + { + "file": "test/setDrift.ts", + "line": 34, + "rule": "generic" + }, + { + "file": "test/setDrift.ts", + "line": 47, + "rule": "generic" + }, + { + "file": "test/setDrift.ts", + "line": 49, + "rule": "generic" + }, + { + "file": "test/setDrift.ts", + "line": 53, + "rule": "generic" + }, + { + "file": "test/setDrift.ts", + "line": 58, + "rule": "generic" + }, + { + "file": "test/setDrift.ts", + "line": 62, + "rule": "generic" + }, + { + "file": "test/steer_error.ts", + "line": 6, + "rule": "generic" + }, + { + "file": "test/steer_error.ts", + "line": 8, + "rule": "generic" + }, + { + "file": "test/steer_error.ts", + "line": 10, + "rule": "generic" + }, + { + "file": "test/steer_error.ts", + "line": 29, + "rule": "generic" + }, + { + "file": "test/steer_error.ts", + "line": 37, + "rule": "generic" + }, + { + "file": "test/steer_error.ts", + "line": 45, + "rule": "generic" + }, + { + "file": "test/steer_error.ts", + "line": 56, + "rule": "generic" + }, + { + "file": "test/suncalc.ts", + "line": 6, + "rule": "generic" + }, + { + "file": "test/suncalc.ts", + "line": 8, + "rule": "generic" + }, + { + "file": "test/suncalc.ts", + "line": 10, + "rule": "generic" + }, + { + "file": "test/suncalc.ts", + "line": 23, + "rule": "generic" + }, + { + "file": "test/suntime.ts", + "line": 6, + "rule": "generic" + }, + { + "file": "test/suntime.ts", + "line": 8, + "rule": "generic" + }, + { + "file": "test/suntime.ts", + "line": 13, + "rule": "generic" + }, + { + "file": "test/suntime.ts", + "line": 45, + "rule": "generic" + }, + { + "file": "test/suntime.ts", + "line": 54, + "rule": "generic" + }, + { + "file": "test/tankVolume.ts", + "line": 6, + "rule": "generic" + }, + { + "file": "test/tankVolume.ts", + "line": 31, + "rule": "generic" + }, + { + "file": "test/tankVolume.ts", + "line": 44, + "rule": "generic" + }, + { + "file": "test/tankVolume.ts", + "line": 55, + "rule": "generic" + }, + { + "file": "test/tankVolume.ts", + "line": 64, + "rule": "generic" + }, + { + "file": "test/tankVolume.ts", + "line": 73, + "rule": "generic" + }, + { + "file": "test/tankVolume.ts", + "line": 83, + "rule": "generic" + }, + { + "file": "test/tankVolume.ts", + "line": 98, + "rule": "generic" + }, + { + "file": "test/tankVolume.ts", + "line": 111, + "rule": "generic" + }, + { + "file": "test/tankVolume.ts", + "line": 121, + "rule": "generic" + }, + { + "file": "test/tankVolume.ts", + "line": 131, + "rule": "generic" + }, + { + "file": "test/tankVolume2.ts", + "line": 6, + "rule": "generic" + }, + { + "file": "test/tankVolume2.ts", + "line": 8, + "rule": "generic" + }, + { + "file": "test/tankVolume2.ts", + "line": 10, + "rule": "generic" + }, + { + "file": "test/tankVolume2.ts", + "line": 19, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 1, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 7, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 32, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 35, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 38, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 40, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 45, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 46, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 51, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 56, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 61, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 67, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 72, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 77, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 82, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 87, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 92, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 97, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 102, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 108, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 113, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 118, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 123, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 130, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 133, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 135, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 149, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 162, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 171, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 178, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 184, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 190, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 200, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 201, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 202, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 203, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 204, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 206, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 215, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 216, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 218, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 223, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 224, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 225, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 226, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 232, + "rule": "generic" + }, + { + "file": "test/test.ts", + "line": 239, + "rule": "generic" + }, + { + "file": "test/transducerToKeel.ts", + "line": 11, + "rule": "generic" + }, + { + "file": "test/transducerToKeel.ts", + "line": 13, + "rule": "generic" + }, + { + "file": "test/transducerToKeel.ts", + "line": 21, + "rule": "generic" + }, + { + "file": "test/transducerToKeel.ts", + "line": 32, + "rule": "generic" + }, + { + "file": "test/transducerToKeel.ts", + "line": 41, + "rule": "generic" + }, + { + "file": "test/transducerToKeel.ts", + "line": 46, + "rule": "generic" + }, + { + "file": "test/utils.ts", + "line": 18, + "rule": "generic" + }, + { + "file": "test/utils.ts", + "line": 19, + "rule": "generic" + }, + { + "file": "test/utils.ts", + "line": 23, + "rule": "generic" + }, + { + "file": "test/utils.ts", + "line": 27, + "rule": "generic" + }, + { + "file": "test/utils.ts", + "line": 31, + "rule": "generic" + }, + { + "file": "test/utils.ts", + "line": 39, + "rule": "generic" + }, + { + "file": "test/utils.ts", + "line": 45, + "rule": "generic" + }, + { + "file": "test/utils.ts", + "line": 52, + "rule": "generic" + }, + { + "file": "test/utils.ts", + "line": 58, + "rule": "generic" + }, + { + "file": "test/utils.ts", + "line": 64, + "rule": "generic" + }, + { + "file": "test/utils.ts", + "line": 68, + "rule": "generic" + }, + { + "file": "test/utils.ts", + "line": 72, + "rule": "generic" + }, + { + "file": "test/utils.ts", + "line": 78, + "rule": "generic" + }, + { + "file": "test/utils.ts", + "line": 83, + "rule": "generic" + }, + { + "file": "test/utils.ts", + "line": 88, + "rule": "generic" + }, + { + "file": "test/vmg_wind_stw.ts", + "line": 10, + "rule": "generic" + }, + { + "file": "test/vmg_wind_stw.ts", + "line": 12, + "rule": "generic" + }, + { + "file": "test/vmg_wind_stw.ts", + "line": 14, + "rule": "generic" + }, + { + "file": "test/vmg_wind_stw.ts", + "line": 23, + "rule": "generic" + }, + { + "file": "test/vmg_wind.ts", + "line": 10, + "rule": "generic" + }, + { + "file": "test/vmg_wind.ts", + "line": 12, + "rule": "generic" + }, + { + "file": "test/vmg_wind.ts", + "line": 17, + "rule": "generic" + }, + { + "file": "test/vmg_wind.ts", + "line": 23, + "rule": "generic" + }, + { + "file": "test/windChill.ts", + "line": 7, + "rule": "generic" + }, + { + "file": "test/windChill.ts", + "line": 9, + "rule": "generic" + }, + { + "file": "test/windChill.ts", + "line": 11, + "rule": "generic" + }, + { + "file": "test/windChill.ts", + "line": 18, + "rule": "generic" + }, + { + "file": "test/windChill.ts", + "line": 24, + "rule": "generic" + }, + { + "file": "test/windChill.ts", + "line": 30, + "rule": "generic" + }, + { + "file": "test/windChill.ts", + "line": 37, + "rule": "generic" + }, + { + "file": "test/windChill.ts", + "line": 43, + "rule": "generic" + }, + { + "file": "test/windDirection.ts", + "line": 6, + "rule": "generic" + }, + { + "file": "test/windDirection.ts", + "line": 8, + "rule": "generic" + }, + { + "file": "test/windDirection.ts", + "line": 10, + "rule": "generic" + }, + { + "file": "test/windDirection.ts", + "line": 22, + "rule": "generic" + }, + { + "file": "test/windGround.ts", + "line": 10, + "rule": "generic" + }, + { + "file": "test/windGround.ts", + "line": 12, + "rule": "generic" + }, + { + "file": "test/windGround.ts", + "line": 14, + "rule": "generic" + }, + { + "file": "test/windGround.ts", + "line": 31, + "rule": "generic" + }, + { + "file": "test/windGround.ts", + "line": 38, + "rule": "generic" + }, + { + "file": "test/windGround.ts", + "line": 45, + "rule": "generic" + }, + { + "file": "test/windGround.ts", + "line": 54, + "rule": "generic" + }, + { + "file": "test/windShift.ts", + "line": 11, + "rule": "generic" + }, + { + "file": "test/windShift.ts", + "line": 13, + "rule": "generic" + }, + { + "file": "test/windShift.ts", + "line": 30, + "rule": "generic" + }, + { + "file": "test/windShift.ts", + "line": 36, + "rule": "generic" + }, + { + "file": "test/windShift.ts", + "line": 41, + "rule": "generic" + }, + { + "file": "test/windShift.ts", + "line": 47, + "rule": "generic" + }, + { + "file": "test/windShift.ts", + "line": 56, + "rule": "generic" + }, + { + "file": "test/windShift.ts", + "line": 68, + "rule": "generic" + }, + { + "file": "test/windShift.ts", + "line": 81, + "rule": "generic" + }, + { + "file": "test/windShift.ts", + "line": 91, + "rule": "generic" + }, + { + "file": "test/windShift.ts", + "line": 98, + "rule": "generic" + }, + { + "file": "tsconfig.json", + "line": 1, + "rule": "changed-defaults", + "note": "types [\"node\", \"mocha\"] (and ignoreDeprecations) inserted into the unchanged root object" + }, + { + "file": "tsconfig.json", + "line": 5, + "rule": "module-resolution-node10" + } + ] + }, + { + "repo": "shogo82148/typescript-action", + "commit": "0f03405c7267fafaebee5d57e773c1bb56e28b44", + "migration": "https://github.com/shogo82148/typescript-action/pull/487", + "from": "5.9.3", + "to": "6.0.3", + "why": "rootDir default: outDir ./dist from include [\"src\"] fails with TS5011, rootDir \"./src\" added; types [] default: setTimeout unresolved, types added in tsconfig.json and tsconfig.eslint.json", + "findings": [ + { + "file": "src/wait.ts", + "line": 13, + "rule": "generic" + }, + { + "file": "tsconfig.eslint.json", + "line": 6, + "rule": "changed-defaults", + "note": "types [\"jest\", \"node\"] added after this line" + }, + { + "file": "tsconfig.json", + "line": 7, + "rule": "root-dir", + "note": "rootDir \"./src\" and types [\"node\"] added after outDir" + } + ] + }, + { + "repo": "Borewit/music-metadata", + "commit": "783509b27bac24e244dd75e66c9b4a0828c35a96", + "migration": "https://github.com/Borewit/music-metadata/pull/2661", + "from": "5.9.3", + "to": "6.0.3", + "why": "baseUrl removed from the root tsconfig the lib/, test/ and doc-gen/ configs extend; strict and types defaults: the upgrade pinned strict false and listed node and mocha types, and still tightened 16 test files; the compiler reports mocha globals, node builtins and strict-mode errors across test/", + "findings": [ + { + "file": "doc-gen/gen.ts", + "line": 1, + "rule": "generic" + }, + { + "file": "doc-gen/gen.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "doc-gen/gen.ts", + "line": 3, + "rule": "generic" + }, + { + "file": "doc-gen/MarkDown.ts", + "line": 1, + "rule": "generic" + }, + { + "file": "doc-gen/tsconfig.json", + "line": 2, + "rule": "changed-defaults", + "note": "compilerOptions.types [\"node\"] added next to extends" + }, + { + "file": "lib/common/MetadataCollector.ts", + "line": 211, + "rule": "generic" + }, + { + "file": "lib/core.ts", + "line": 13, + "rule": "generic" + }, + { + "file": "lib/index.ts", + "line": 5, + "rule": "generic" + }, + { + "file": "lib/tsconfig.json", + "line": 1, + "rule": "changed-defaults", + "note": "types [\"node\"] inserted into the unchanged root object" + }, + { + "file": "test/metadata-parsers.ts", + "line": 1, + "rule": "generic" + }, + { + "file": "test/metadata-parsers.ts", + "line": 15, + "rule": "generic" + }, + { + "file": "test/test-async.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-async.ts", + "line": 8, + "rule": "generic" + }, + { + "file": "test/test-async.ts", + "line": 12, + "rule": "generic" + }, + { + "file": "test/test-comment-mapping.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-comment-mapping.ts", + "line": 13, + "rule": "generic" + }, + { + "file": "test/test-comment-mapping.ts", + "line": 15, + "rule": "generic" + }, + { + "file": "test/test-comment-mapping.ts", + "line": 17, + "rule": "generic" + }, + { + "file": "test/test-comment-mapping.ts", + "line": 26, + "rule": "generic" + }, + { + "file": "test/test-comment-mapping.ts", + "line": 36, + "rule": "generic" + }, + { + "file": "test/test-comment-mapping.ts", + "line": 38, + "rule": "generic" + }, + { + "file": "test/test-comment-mapping.ts", + "line": 47, + "rule": "generic" + }, + { + "file": "test/test-comment-mapping.ts", + "line": 57, + "rule": "generic" + }, + { + "file": "test/test-comment-mapping.ts", + "line": 59, + "rule": "generic" + }, + { + "file": "test/test-comment-mapping.ts", + "line": 72, + "rule": "generic" + }, + { + "file": "test/test-comment-mapping.ts", + "line": 85, + "rule": "generic" + }, + { + "file": "test/test-comment-mapping.ts", + "line": 87, + "rule": "generic" + }, + { + "file": "test/test-comment-mapping.ts", + "line": 101, + "rule": "generic" + }, + { + "file": "test/test-comment-mapping.ts", + "line": 118, + "rule": "generic" + }, + { + "file": "test/test-comment-mapping.ts", + "line": 128, + "rule": "generic" + }, + { + "file": "test/test-common.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-common.ts", + "line": 12, + "rule": "generic" + }, + { + "file": "test/test-common.ts", + "line": 16, + "rule": "generic" + }, + { + "file": "test/test-common.ts", + "line": 27, + "rule": "generic" + }, + { + "file": "test/test-common.ts", + "line": 35, + "rule": "generic" + }, + { + "file": "test/test-common.ts", + "line": 37, + "rule": "generic" + }, + { + "file": "test/test-common.ts", + "line": 43, + "rule": "generic" + }, + { + "file": "test/test-common.ts", + "line": 54, + "rule": "generic" + }, + { + "file": "test/test-common.ts", + "line": 56, + "rule": "generic" + }, + { + "file": "test/test-common.ts", + "line": 70, + "rule": "generic" + }, + { + "file": "test/test-common.ts", + "line": 86, + "rule": "generic" + }, + { + "file": "test/test-common.ts", + "line": 97, + "rule": "generic" + }, + { + "file": "test/test-common.ts", + "line": 105, + "rule": "generic" + }, + { + "file": "test/test-common.ts", + "line": 107, + "rule": "generic" + }, + { + "file": "test/test-common.ts", + "line": 113, + "rule": "generic" + }, + { + "file": "test/test-common.ts", + "line": 129, + "rule": "generic" + }, + { + "file": "test/test-concurrent-picture.ts", + "line": 3, + "rule": "generic" + }, + { + "file": "test/test-concurrent-picture.ts", + "line": 4, + "rule": "generic" + }, + { + "file": "test/test-concurrent-picture.ts", + "line": 9, + "rule": "generic" + }, + { + "file": "test/test-concurrent-picture.ts", + "line": 16, + "rule": "generic" + }, + { + "file": "test/test-discogs.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-discogs.ts", + "line": 9, + "rule": "generic" + }, + { + "file": "test/test-discogs.ts", + "line": 27, + "rule": "generic" + }, + { + "file": "test/test-discogs.ts", + "line": 29, + "rule": "generic" + }, + { + "file": "test/test-discogs.ts", + "line": 40, + "rule": "generic" + }, + { + "file": "test/test-discogs.ts", + "line": 51, + "rule": "generic" + }, + { + "file": "test/test-discogs.ts", + "line": 53, + "rule": "generic" + }, + { + "file": "test/test-discogs.ts", + "line": 103, + "rule": "generic" + }, + { + "file": "test/test-discogs.ts", + "line": 120, + "rule": "generic" + }, + { + "file": "test/test-discogs.ts", + "line": 141, + "rule": "generic" + }, + { + "file": "test/test-discogs.ts", + "line": 146, + "rule": "generic" + }, + { + "file": "test/test-discogs.ts", + "line": 168, + "rule": "generic" + }, + { + "file": "test/test-discogs.ts", + "line": 170, + "rule": "generic" + }, + { + "file": "test/test-discogs.ts", + "line": 227, + "rule": "generic" + }, + { + "file": "test/test-dsdiff.ts", + "line": 3, + "rule": "generic" + }, + { + "file": "test/test-dsdiff.ts", + "line": 8, + "rule": "generic" + }, + { + "file": "test/test-dsdiff.ts", + "line": 12, + "rule": "generic" + }, + { + "file": "test/test-file-aac.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-file-aac.ts", + "line": 11, + "rule": "generic" + }, + { + "file": "test/test-file-aac.ts", + "line": 16, + "rule": "generic", + "note": "tightened by the upgrade (non-null assertion or annotation); no diagnostic at the pinned commit" + }, + { + "file": "test/test-file-aac.ts", + "line": 20, + "rule": "generic" + }, + { + "file": "test/test-file-aac.ts", + "line": 22, + "rule": "generic" + }, + { + "file": "test/test-file-aac.ts", + "line": 27, + "rule": "generic" + }, + { + "file": "test/test-file-aac.ts", + "line": 30, + "rule": "generic" + }, + { + "file": "test/test-file-aac.ts", + "line": 31, + "rule": "generic" + }, + { + "file": "test/test-file-aac.ts", + "line": 39, + "rule": "generic" + }, + { + "file": "test/test-file-aac.ts", + "line": 42, + "rule": "generic" + }, + { + "file": "test/test-file-aac.ts", + "line": 43, + "rule": "generic" + }, + { + "file": "test/test-file-aac.ts", + "line": 51, + "rule": "generic" + }, + { + "file": "test/test-file-aac.ts", + "line": 57, + "rule": "generic" + }, + { + "file": "test/test-file-aac.ts", + "line": 58, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 1, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 7, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 16, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 29, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 32, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 35, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 41, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 44, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 47, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 53, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 66, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 68, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 71, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 72, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 78, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 81, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 82, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 88, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 91, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 92, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 98, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 101, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 102, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 108, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 111, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 112, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 118, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 121, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 122, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 131, + "rule": "generic" + }, + { + "file": "test/test-file-aiff.ts", + "line": 146, + "rule": "generic" + }, + { + "file": "test/test-file-ape.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-file-ape.ts", + "line": 9, + "rule": "generic" + }, + { + "file": "test/test-file-ape.ts", + "line": 11, + "rule": "generic" + }, + { + "file": "test/test-file-ape.ts", + "line": 20, + "rule": "generic" + }, + { + "file": "test/test-file-ape.ts", + "line": 31, + "rule": "generic", + "note": "tightened by the upgrade (non-null assertion or annotation); no diagnostic at the pinned commit" + }, + { + "file": "test/test-file-ape.ts", + "line": 45, + "rule": "generic" + }, + { + "file": "test/test-file-ape.ts", + "line": 46, + "rule": "generic" + }, + { + "file": "test/test-file-ape.ts", + "line": 57, + "rule": "generic" + }, + { + "file": "test/test-file-ape.ts", + "line": 59, + "rule": "generic" + }, + { + "file": "test/test-file-ape.ts", + "line": 71, + "rule": "generic" + }, + { + "file": "test/test-file-asf.ts", + "line": 3, + "rule": "generic" + }, + { + "file": "test/test-file-asf.ts", + "line": 14, + "rule": "generic" + }, + { + "file": "test/test-file-asf.ts", + "line": 16, + "rule": "generic" + }, + { + "file": "test/test-file-asf.ts", + "line": 17, + "rule": "generic" + }, + { + "file": "test/test-file-asf.ts", + "line": 27, + "rule": "generic" + }, + { + "file": "test/test-file-asf.ts", + "line": 34, + "rule": "generic" + }, + { + "file": "test/test-file-asf.ts", + "line": 45, + "rule": "generic" + }, + { + "file": "test/test-file-asf.ts", + "line": 87, + "rule": "generic" + }, + { + "file": "test/test-file-asf.ts", + "line": 89, + "rule": "generic" + }, + { + "file": "test/test-file-asf.ts", + "line": 92, + "rule": "generic", + "note": "tightened by the upgrade (non-null assertion or annotation); no diagnostic at the pinned commit" + }, + { + "file": "test/test-file-asf.ts", + "line": 98, + "rule": "generic" + }, + { + "file": "test/test-file-asf.ts", + "line": 116, + "rule": "generic" + }, + { + "file": "test/test-file-asf.ts", + "line": 119, + "rule": "generic" + }, + { + "file": "test/test-file-asf.ts", + "line": 120, + "rule": "generic" + }, + { + "file": "test/test-file-asf.ts", + "line": 131, + "rule": "generic" + }, + { + "file": "test/test-file-asf.ts", + "line": 134, + "rule": "generic" + }, + { + "file": "test/test-file-asf.ts", + "line": 136, + "rule": "generic" + }, + { + "file": "test/test-file-asf.ts", + "line": 149, + "rule": "generic" + }, + { + "file": "test/test-file-asf.ts", + "line": 157, + "rule": "generic" + }, + { + "file": "test/test-file-asf.ts", + "line": 158, + "rule": "generic" + }, + { + "file": "test/test-file-asf.ts", + "line": 164, + "rule": "generic" + }, + { + "file": "test/test-file-dsf.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-file-dsf.ts", + "line": 7, + "rule": "generic" + }, + { + "file": "test/test-file-dsf.ts", + "line": 11, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 3, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 10, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 14, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 26, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 35, + "rule": "generic", + "note": "tightened by the upgrade (non-null assertion or annotation); no diagnostic at the pinned commit" + }, + { + "file": "test/test-file-flac.ts", + "line": 39, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 47, + "rule": "generic", + "note": "tightened by the upgrade (non-null assertion or annotation); no diagnostic at the pinned commit" + }, + { + "file": "test/test-file-flac.ts", + "line": 59, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 62, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 63, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 72, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 80, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 85, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 86, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 93, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 98, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 99, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 100, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 106, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 108, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 117, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 118, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 133, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 157, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 166, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 179, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 189, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 198, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 199, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 220, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 230, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 281, + "rule": "generic" + }, + { + "file": "test/test-file-flac.ts", + "line": 296, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 1, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 11, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 27, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 29, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 38, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 54, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 63, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 67, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 72, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 74, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 83, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 96, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 105, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 117, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 137, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 146, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 157, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 165, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 169, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 177, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 181, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 188, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 197, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 203, + "rule": "generic" + }, + { + "file": "test/test-file-matroska.ts", + "line": 210, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 23, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 27, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 35, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 37, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 64, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 89, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 119, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 124, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 127, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 144, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 145, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 153, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 164, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 181, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 199, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 215, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 218, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 226, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 232, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 238, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 247, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 253, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 255, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 259, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 262, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 263, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 269, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 271, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 274, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 275, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 276, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 286, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 288, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 297, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 317, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 334, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 336, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 345, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 347, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 356, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 363, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 375, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 377, + "rule": "generic" + }, + { + "file": "test/test-file-mp3.ts", + "line": 388, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 3, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 12, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 14, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 16, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 23, + "rule": "generic", + "note": "tightened by the upgrade (non-null assertion or annotation); no diagnostic at the pinned commit" + }, + { + "file": "test/test-file-mp4.ts", + "line": 26, + "rule": "generic", + "note": "tightened by the upgrade (non-null assertion or annotation); no diagnostic at the pinned commit" + }, + { + "file": "test/test-file-mp4.ts", + "line": 31, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 41, + "rule": "generic", + "note": "tightened by the upgrade (non-null assertion or annotation); no diagnostic at the pinned commit" + }, + { + "file": "test/test-file-mp4.ts", + "line": 48, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 69, + "rule": "generic", + "note": "tightened by the upgrade (non-null assertion or annotation); no diagnostic at the pinned commit" + }, + { + "file": "test/test-file-mp4.ts", + "line": 74, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 78, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 91, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 94, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 98, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 121, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 124, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 128, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 150, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 152, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 155, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 159, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 180, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 185, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 192, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 297, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 309, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 321, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 323, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 326, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 330, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 355, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 358, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 368, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 371, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 375, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 385, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 388, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 392, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 411, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 414, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 417, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 438, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 463, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 467, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 468, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 478, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 486, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 492, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 493, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 500, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 511, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 518, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 529, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 536, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 549, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 558, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 563, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 570, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 573, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 576, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 585, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 589, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 601, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 608, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 638, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 649, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 665, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 669, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 688, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 707, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 716, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 821, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 832, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 844, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 855, + "rule": "generic" + }, + { + "file": "test/test-file-mp4.ts", + "line": 865, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 3, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 13, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 15, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 51, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 56, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 63, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 77, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 79, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 89, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 93, + "rule": "generic", + "note": "tightened by the upgrade (non-null assertion or annotation); no diagnostic at the pinned commit" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 101, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 151, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 156, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 160, + "rule": "generic", + "note": "tightened by the upgrade (non-null assertion or annotation); no diagnostic at the pinned commit" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 167, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 204, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 206, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 226, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 245, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 247, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 258, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 267, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 279, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 281, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 297, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 304, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 307, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 314, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 317, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 332, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 334, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 340, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 341, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 348, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 357, + "rule": "generic" + }, + { + "file": "test/test-file-mpeg.ts", + "line": 362, + "rule": "generic" + }, + { + "file": "test/test-file-musepack.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-file-musepack.ts", + "line": 7, + "rule": "generic" + }, + { + "file": "test/test-file-musepack.ts", + "line": 11, + "rule": "generic" + }, + { + "file": "test/test-file-musepack.ts", + "line": 16, + "rule": "generic" + }, + { + "file": "test/test-file-musepack.ts", + "line": 18, + "rule": "generic" + }, + { + "file": "test/test-file-musepack.ts", + "line": 23, + "rule": "generic" + }, + { + "file": "test/test-file-musepack.ts", + "line": 43, + "rule": "generic" + }, + { + "file": "test/test-file-musepack.ts", + "line": 51, + "rule": "generic" + }, + { + "file": "test/test-file-musepack.ts", + "line": 53, + "rule": "generic" + }, + { + "file": "test/test-file-musepack.ts", + "line": 58, + "rule": "generic" + }, + { + "file": "test/test-file-musepack.ts", + "line": 72, + "rule": "generic" + }, + { + "file": "test/test-file-musepack.ts", + "line": 77, + "rule": "generic" + }, + { + "file": "test/test-file-musepack.ts", + "line": 79, + "rule": "generic" + }, + { + "file": "test/test-file-musepack.ts", + "line": 85, + "rule": "generic" + }, + { + "file": "test/test-file-musepack.ts", + "line": 86, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 11, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 13, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 22, + "rule": "generic", + "note": "tightened by the upgrade (non-null assertion or annotation); no diagnostic at the pinned commit" + }, + { + "file": "test/test-file-ogg.ts", + "line": 30, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 39, + "rule": "generic", + "note": "tightened by the upgrade (non-null assertion or annotation); no diagnostic at the pinned commit" + }, + { + "file": "test/test-file-ogg.ts", + "line": 41, + "rule": "generic", + "note": "tightened by the upgrade (non-null assertion or annotation); no diagnostic at the pinned commit" + }, + { + "file": "test/test-file-ogg.ts", + "line": 51, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 53, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 61, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 69, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 77, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 85, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 94, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 96, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 100, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 111, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 112, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 120, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 148, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 171, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 191, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 192, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 193, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 198, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 200, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 207, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 211, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 218, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 220, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 222, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 226, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 231, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 235, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 238, + "rule": "generic", + "note": "tightened by the upgrade (non-null assertion or annotation); no diagnostic at the pinned commit" + }, + { + "file": "test/test-file-ogg.ts", + "line": 247, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 248, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 258, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 260, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 264, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 273, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 274, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 281, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 286, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 287, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 301, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 303, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 311, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 316, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 318, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 333, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 343, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 345, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 355, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 358, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 368, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 376, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 378, + "rule": "generic" + }, + { + "file": "test/test-file-ogg.ts", + "line": 390, + "rule": "generic" + }, + { + "file": "test/test-file-wav.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-file-wav.ts", + "line": 11, + "rule": "generic" + }, + { + "file": "test/test-file-wav.ts", + "line": 26, + "rule": "generic" + }, + { + "file": "test/test-file-wav.ts", + "line": 55, + "rule": "generic" + }, + { + "file": "test/test-file-wav.ts", + "line": 64, + "rule": "generic" + }, + { + "file": "test/test-file-wav.ts", + "line": 113, + "rule": "generic" + }, + { + "file": "test/test-file-wav.ts", + "line": 126, + "rule": "generic" + }, + { + "file": "test/test-file-wav.ts", + "line": 129, + "rule": "generic" + }, + { + "file": "test/test-file-wav.ts", + "line": 169, + "rule": "generic" + }, + { + "file": "test/test-file-wav.ts", + "line": 171, + "rule": "generic" + }, + { + "file": "test/test-file-wav.ts", + "line": 184, + "rule": "generic" + }, + { + "file": "test/test-file-wav.ts", + "line": 188, + "rule": "generic" + }, + { + "file": "test/test-file-wav.ts", + "line": 200, + "rule": "generic" + }, + { + "file": "test/test-file-wav.ts", + "line": 212, + "rule": "generic" + }, + { + "file": "test/test-file-wav.ts", + "line": 224, + "rule": "generic" + }, + { + "file": "test/test-file-wav.ts", + "line": 239, + "rule": "generic" + }, + { + "file": "test/test-file-wav.ts", + "line": 253, + "rule": "generic" + }, + { + "file": "test/test-file-wav.ts", + "line": 266, + "rule": "generic" + }, + { + "file": "test/test-file-wav.ts", + "line": 276, + "rule": "generic" + }, + { + "file": "test/test-file-wav.ts", + "line": 280, + "rule": "generic" + }, + { + "file": "test/test-file-wav.ts", + "line": 289, + "rule": "generic" + }, + { + "file": "test/test-file-wav.ts", + "line": 293, + "rule": "generic" + }, + { + "file": "test/test-file-wav.ts", + "line": 310, + "rule": "generic" + }, + { + "file": "test/test-file-wavpack.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-file-wavpack.ts", + "line": 9, + "rule": "generic" + }, + { + "file": "test/test-file-wavpack.ts", + "line": 11, + "rule": "generic" + }, + { + "file": "test/test-file-wavpack.ts", + "line": 13, + "rule": "generic" + }, + { + "file": "test/test-file-wavpack.ts", + "line": 16, + "rule": "generic", + "note": "tightened by the upgrade (non-null assertion or annotation); no diagnostic at the pinned commit" + }, + { + "file": "test/test-file-wavpack.ts", + "line": 22, + "rule": "generic" + }, + { + "file": "test/test-file-wavpack.ts", + "line": 30, + "rule": "generic" + }, + { + "file": "test/test-file-wavpack.ts", + "line": 31, + "rule": "generic" + }, + { + "file": "test/test-file-wavpack.ts", + "line": 38, + "rule": "generic" + }, + { + "file": "test/test-file-wavpack.ts", + "line": 40, + "rule": "generic" + }, + { + "file": "test/test-file-wavpack.ts", + "line": 52, + "rule": "generic" + }, + { + "file": "test/test-file-wavpack.ts", + "line": 53, + "rule": "generic" + }, + { + "file": "test/test-file-wavpack.ts", + "line": 59, + "rule": "generic" + }, + { + "file": "test/test-file-wavpack.ts", + "line": 61, + "rule": "generic" + }, + { + "file": "test/test-file-wavpack.ts", + "line": 68, + "rule": "generic", + "note": "tightened by the upgrade (non-null assertion or annotation); no diagnostic at the pinned commit" + }, + { + "file": "test/test-file-wavpack.ts", + "line": 74, + "rule": "generic" + }, + { + "file": "test/test-file-wavpack.ts", + "line": 75, + "rule": "generic" + }, + { + "file": "test/test-http.ts", + "line": 7, + "rule": "generic" + }, + { + "file": "test/test-http.ts", + "line": 10, + "rule": "generic" + }, + { + "file": "test/test-http.ts", + "line": 12, + "rule": "generic" + }, + { + "file": "test/test-http.ts", + "line": 16, + "rule": "generic" + }, + { + "file": "test/test-http.ts", + "line": 19, + "rule": "generic" + }, + { + "file": "test/test-http.ts", + "line": 27, + "rule": "generic" + }, + { + "file": "test/test-http.ts", + "line": 30, + "rule": "generic" + }, + { + "file": "test/test-id3v1.1.ts", + "line": 3, + "rule": "generic" + }, + { + "file": "test/test-id3v1.1.ts", + "line": 9, + "rule": "generic" + }, + { + "file": "test/test-id3v1.1.ts", + "line": 13, + "rule": "generic" + }, + { + "file": "test/test-id3v1.1.ts", + "line": 23, + "rule": "generic" + }, + { + "file": "test/test-id3v1.1.ts", + "line": 42, + "rule": "generic" + }, + { + "file": "test/test-id3v1.1.ts", + "line": 43, + "rule": "generic" + }, + { + "file": "test/test-id3v1.1.ts", + "line": 50, + "rule": "generic" + }, + { + "file": "test/test-id3v1.1.ts", + "line": 54, + "rule": "generic" + }, + { + "file": "test/test-id3v1.1.ts", + "line": 55, + "rule": "generic" + }, + { + "file": "test/test-id3v1.1.ts", + "line": 61, + "rule": "generic" + }, + { + "file": "test/test-id3v1.1.ts", + "line": 77, + "rule": "generic" + }, + { + "file": "test/test-id3v1.1.ts", + "line": 78, + "rule": "generic" + }, + { + "file": "test/test-id3v1.1.ts", + "line": 84, + "rule": "generic" + }, + { + "file": "test/test-id3v1.1.ts", + "line": 115, + "rule": "generic" + }, + { + "file": "test/test-id3v1.1.ts", + "line": 116, + "rule": "generic" + }, + { + "file": "test/test-id3v1.1.ts", + "line": 127, + "rule": "generic" + }, + { + "file": "test/test-id3v1.1.ts", + "line": 132, + "rule": "generic" + }, + { + "file": "test/test-id3v1.1.ts", + "line": 133, + "rule": "generic" + }, + { + "file": "test/test-id3v2-duration-allframes.ts", + "line": 5, + "rule": "generic" + }, + { + "file": "test/test-id3v2-duration-allframes.ts", + "line": 10, + "rule": "generic" + }, + { + "file": "test/test-id3v2-duration-allframes.ts", + "line": 28, + "rule": "generic" + }, + { + "file": "test/test-id3v2-duration-allframes.ts", + "line": 39, + "rule": "generic" + }, + { + "file": "test/test-id3v2-lyrics.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-id3v2-lyrics.ts", + "line": 8, + "rule": "generic" + }, + { + "file": "test/test-id3v2-unknownframe.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-id3v2-unknownframe.ts", + "line": 9, + "rule": "generic" + }, + { + "file": "test/test-id3v2-unknownframe.ts", + "line": 13, + "rule": "generic" + }, + { + "file": "test/test-id3v2-utf16encoded.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-id3v2-utf16encoded.ts", + "line": 7, + "rule": "generic" + }, + { + "file": "test/test-id3v2-utf16encoded.ts", + "line": 18, + "rule": "generic" + }, + { + "file": "test/test-id3v2-utf16encoded.ts", + "line": 19, + "rule": "generic" + }, + { + "file": "test/test-id3v2-utf16encoded.ts", + "line": 20, + "rule": "generic" + }, + { + "file": "test/test-id3v2-utf16encoded.ts", + "line": 30, + "rule": "generic" + }, + { + "file": "test/test-id3v2-utf16encoded.ts", + "line": 40, + "rule": "generic" + }, + { + "file": "test/test-id3v2-utf16encoded.ts", + "line": 46, + "rule": "generic" + }, + { + "file": "test/test-id3v2-utf16encoded.ts", + "line": 63, + "rule": "generic" + }, + { + "file": "test/test-id3v2-utf16encoded.ts", + "line": 66, + "rule": "generic" + }, + { + "file": "test/test-id3v2-xheader.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-id3v2-xheader.ts", + "line": 9, + "rule": "generic" + }, + { + "file": "test/test-id3v2-xheader.ts", + "line": 17, + "rule": "generic" + }, + { + "file": "test/test-id3v2.2.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-id3v2.2.ts", + "line": 9, + "rule": "generic" + }, + { + "file": "test/test-id3v2.2.ts", + "line": 13, + "rule": "generic" + }, + { + "file": "test/test-id3v2.2.ts", + "line": 20, + "rule": "generic" + }, + { + "file": "test/test-id3v2.2.ts", + "line": 65, + "rule": "generic" + }, + { + "file": "test/test-id3v2.2.ts", + "line": 78, + "rule": "generic" + }, + { + "file": "test/test-id3v2.2.ts", + "line": 79, + "rule": "generic" + }, + { + "file": "test/test-id3v2.2.ts", + "line": 125, + "rule": "generic" + }, + { + "file": "test/test-id3v2.2.ts", + "line": 134, + "rule": "generic" + }, + { + "file": "test/test-id3v2.2.ts", + "line": 135, + "rule": "generic" + }, + { + "file": "test/test-id3v2.2.ts", + "line": 138, + "rule": "generic" + }, + { + "file": "test/test-id3v2.2.ts", + "line": 140, + "rule": "generic" + }, + { + "file": "test/test-id3v2.2.ts", + "line": 154, + "rule": "generic" + }, + { + "file": "test/test-id3v2.2.ts", + "line": 170, + "rule": "generic" + }, + { + "file": "test/test-id3v2.2.ts", + "line": 172, + "rule": "generic" + }, + { + "file": "test/test-id3v2.2.ts", + "line": 193, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 13, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 15, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 35, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 39, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 51, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 105, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 107, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 144, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 158, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 210, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 223, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 257, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 260, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 270, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 273, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 280, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 283, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 292, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 309, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 316, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 349, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 406, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 410, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 470, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 489, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 495, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 497, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 502, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 514, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 516, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 533, + "rule": "generic" + }, + { + "file": "test/test-id3v2.3.ts", + "line": 553, + "rule": "generic" + }, + { + "file": "test/test-id3v2.4-musicbrainz.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-id3v2.4-musicbrainz.ts", + "line": 8, + "rule": "generic" + }, + { + "file": "test/test-id3v2.4-musicbrainz.ts", + "line": 27, + "rule": "generic" + }, + { + "file": "test/test-id3v2.4-musicbrainz.ts", + "line": 28, + "rule": "generic" + }, + { + "file": "test/test-id3v2.4.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-id3v2.4.ts", + "line": 7, + "rule": "generic" + }, + { + "file": "test/test-id3v2.4.ts", + "line": 9, + "rule": "generic" + }, + { + "file": "test/test-id3v2.4.ts", + "line": 34, + "rule": "generic" + }, + { + "file": "test/test-id3v2.4.ts", + "line": 35, + "rule": "generic" + }, + { + "file": "test/test-id3v2.4.ts", + "line": 36, + "rule": "generic" + }, + { + "file": "test/test-id3v2.4.ts", + "line": 37, + "rule": "generic" + }, + { + "file": "test/test-id3v2.4.ts", + "line": 43, + "rule": "generic" + }, + { + "file": "test/test-id3v2.4.ts", + "line": 71, + "rule": "generic" + }, + { + "file": "test/test-id3v2.4.ts", + "line": 82, + "rule": "generic" + }, + { + "file": "test/test-id3v2.4.ts", + "line": 93, + "rule": "generic" + }, + { + "file": "test/test-id3v2.4.ts", + "line": 103, + "rule": "generic" + }, + { + "file": "test/test-id3v2.4.ts", + "line": 106, + "rule": "generic" + }, + { + "file": "test/test-id3v2.4.ts", + "line": 115, + "rule": "generic" + }, + { + "file": "test/test-invalid-file.ts", + "line": 3, + "rule": "generic" + }, + { + "file": "test/test-invalid-file.ts", + "line": 6, + "rule": "generic" + }, + { + "file": "test/test-invalid-file.ts", + "line": 8, + "rule": "generic" + }, + { + "file": "test/test-invalid-file.ts", + "line": 13, + "rule": "generic" + }, + { + "file": "test/test-merge-tag-headers.ts", + "line": 1, + "rule": "generic" + }, + { + "file": "test/test-merge-tag-headers.ts", + "line": 9, + "rule": "generic" + }, + { + "file": "test/test-merge-tag-headers.ts", + "line": 15, + "rule": "generic" + }, + { + "file": "test/test-merge-tag-headers.ts", + "line": 22, + "rule": "generic" + }, + { + "file": "test/test-merge-tag-headers.ts", + "line": 30, + "rule": "generic" + }, + { + "file": "test/test-merge-tag-headers.ts", + "line": 34, + "rule": "generic" + }, + { + "file": "test/test-merge-tag-headers.ts", + "line": 35, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 3, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 4, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 11, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 32, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 36, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 52, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 54, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 66, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 70, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 79, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 81, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 88, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 95, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 101, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 110, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 122, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 126, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 130, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 135, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 139, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 143, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 147, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 151, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 155, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 159, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 163, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 167, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 171, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 175, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 179, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 183, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 187, + "rule": "generic" + }, + { + "file": "test/test-mime.ts", + "line": 193, + "rule": "generic" + }, + { + "file": "test/test-musicbrainz-picard-vorbis.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-musicbrainz-picard-vorbis.ts", + "line": 7, + "rule": "generic" + }, + { + "file": "test/test-musicbrainz-picard-vorbis.ts", + "line": 12, + "rule": "generic" + }, + { + "file": "test/test-musicbrainz-picard-vorbis.ts", + "line": 19, + "rule": "generic" + }, + { + "file": "test/test-musicbrainz-picard-vorbis.ts", + "line": 56, + "rule": "generic" + }, + { + "file": "test/test-no-metadata.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-no-metadata.ts", + "line": 7, + "rule": "generic" + }, + { + "file": "test/test-no-metadata.ts", + "line": 17, + "rule": "generic" + }, + { + "file": "test/test-nonasciichars.ts", + "line": 5, + "rule": "generic" + }, + { + "file": "test/test-nonasciichars.ts", + "line": 9, + "rule": "generic" + }, + { + "file": "test/test-options.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-options.ts", + "line": 7, + "rule": "generic" + }, + { + "file": "test/test-options.ts", + "line": 15, + "rule": "generic" + }, + { + "file": "test/test-options.ts", + "line": 17, + "rule": "generic" + }, + { + "file": "test/test-options.ts", + "line": 19, + "rule": "generic" + }, + { + "file": "test/test-options.ts", + "line": 29, + "rule": "generic" + }, + { + "file": "test/test-options.ts", + "line": 39, + "rule": "generic" + }, + { + "file": "test/test-options.ts", + "line": 51, + "rule": "generic" + }, + { + "file": "test/test-options.ts", + "line": 53, + "rule": "generic" + }, + { + "file": "test/test-options.ts", + "line": 62, + "rule": "generic" + }, + { + "file": "test/test-options.ts", + "line": 71, + "rule": "generic" + }, + { + "file": "test/test-options.ts", + "line": 82, + "rule": "generic" + }, + { + "file": "test/test-options.ts", + "line": 84, + "rule": "generic" + }, + { + "file": "test/test-options.ts", + "line": 93, + "rule": "generic" + }, + { + "file": "test/test-options.ts", + "line": 102, + "rule": "generic" + }, + { + "file": "test/test-options.ts", + "line": 115, + "rule": "generic" + }, + { + "file": "test/test-options.ts", + "line": 117, + "rule": "generic" + }, + { + "file": "test/test-options.ts", + "line": 126, + "rule": "generic" + }, + { + "file": "test/test-options.ts", + "line": 135, + "rule": "generic" + }, + { + "file": "test/test-options.ts", + "line": 146, + "rule": "generic" + }, + { + "file": "test/test-options.ts", + "line": 148, + "rule": "generic" + }, + { + "file": "test/test-options.ts", + "line": 157, + "rule": "generic" + }, + { + "file": "test/test-options.ts", + "line": 166, + "rule": "generic" + }, + { + "file": "test/test-picard-mappings.ts", + "line": 6, + "rule": "generic" + }, + { + "file": "test/test-picard-mappings.ts", + "line": 19, + "rule": "generic" + }, + { + "file": "test/test-picard-mappings.ts", + "line": 93, + "rule": "generic" + }, + { + "file": "test/test-picard-mappings.ts", + "line": 102, + "rule": "generic" + }, + { + "file": "test/test-picard-mappings.ts", + "line": 128, + "rule": "generic" + }, + { + "file": "test/test-picard-mappings.ts", + "line": 137, + "rule": "generic" + }, + { + "file": "test/test-picard-mappings.ts", + "line": 187, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 3, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 4, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 14, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 108, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 109, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 110, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 113, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 114, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 115, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 129, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 133, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 182, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 186, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 205, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 222, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 277, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 281, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 299, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 303, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 323, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 374, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 378, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 405, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 432, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 497, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 524, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 553, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 568, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 608, + "rule": "generic" + }, + { + "file": "test/test-picard-parsing.ts", + "line": 616, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 12, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 14, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 16, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 25, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 35, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 37, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 46, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 56, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 58, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 68, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 70, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 80, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 82, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 91, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 101, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 103, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 112, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 122, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 124, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 133, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 143, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 145, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 154, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 164, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 166, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 175, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 185, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 187, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 196, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 206, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 208, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 217, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 227, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 229, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 239, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 249, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 251, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 260, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 270, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 272, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 281, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 291, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 293, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 302, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 312, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 314, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 323, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 333, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 335, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 344, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 354, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 356, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 365, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 375, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 377, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 392, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 402, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 404, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 413, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 424, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 426, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 436, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 438, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 448, + "rule": "generic" + }, + { + "file": "test/test-pr-544.ts", + "line": 450, + "rule": "generic" + }, + { + "file": "test/test-radio-streams.ts", + "line": 5, + "rule": "generic" + }, + { + "file": "test/test-radio-streams.ts", + "line": 21, + "rule": "generic" + }, + { + "file": "test/test-radio-streams.ts", + "line": 22, + "rule": "generic" + }, + { + "file": "test/test-radio-streams.ts", + "line": 30, + "rule": "generic" + }, + { + "file": "test/test-radio-streams.ts", + "line": 35, + "rule": "generic" + }, + { + "file": "test/test-radio-streams.ts", + "line": 36, + "rule": "generic" + }, + { + "file": "test/test-radio-streams.ts", + "line": 45, + "rule": "generic" + }, + { + "file": "test/test-radio-streams.ts", + "line": 46, + "rule": "generic" + }, + { + "file": "test/test-radio-streams.ts", + "line": 54, + "rule": "generic" + }, + { + "file": "test/test-radio-streams.ts", + "line": 55, + "rule": "generic" + }, + { + "file": "test/test-radio-streams.ts", + "line": 63, + "rule": "generic" + }, + { + "file": "test/test-radio-streams.ts", + "line": 64, + "rule": "generic" + }, + { + "file": "test/test-radio-streams.ts", + "line": 72, + "rule": "generic" + }, + { + "file": "test/test-radio-streams.ts", + "line": 73, + "rule": "generic" + }, + { + "file": "test/test-regress-GH-56.ts", + "line": 3, + "rule": "generic" + }, + { + "file": "test/test-regress-GH-56.ts", + "line": 9, + "rule": "generic" + }, + { + "file": "test/test-regress-GH-56.ts", + "line": 36, + "rule": "generic" + }, + { + "file": "test/test-regress-GH-56.ts", + "line": 37, + "rule": "generic" + }, + { + "file": "test/test-regress-GH-56.ts", + "line": 41, + "rule": "generic" + }, + { + "file": "test/test-regress-GH-56.ts", + "line": 45, + "rule": "generic" + }, + { + "file": "test/test-regress-GH-56.ts", + "line": 47, + "rule": "generic" + }, + { + "file": "test/test-regress-GH-56.ts", + "line": 51, + "rule": "generic" + }, + { + "file": "test/test-regress-GH-56.ts", + "line": 54, + "rule": "generic" + }, + { + "file": "test/test-regress-GH-56.ts", + "line": 56, + "rule": "generic" + }, + { + "file": "test/test-regress-GH-56.ts", + "line": 60, + "rule": "generic" + }, + { + "file": "test/test-replay-gain.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-replay-gain.ts", + "line": 184, + "rule": "generic" + }, + { + "file": "test/test-replay-gain.ts", + "line": 189, + "rule": "generic" + }, + { + "file": "test/test-replay-gain.ts", + "line": 202, + "rule": "generic" + }, + { + "file": "test/test-replay-gain.ts", + "line": 203, + "rule": "generic" + }, + { + "file": "test/test-replay-gain.ts", + "line": 210, + "rule": "generic" + }, + { + "file": "test/test-replay-gain.ts", + "line": 211, + "rule": "generic" + }, + { + "file": "test/test-replaygain.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-replaygain.ts", + "line": 8, + "rule": "generic" + }, + { + "file": "test/test-replaygain.ts", + "line": 12, + "rule": "generic" + }, + { + "file": "test/test-replaygain.ts", + "line": 17, + "rule": "generic" + }, + { + "file": "test/test-replaygain.ts", + "line": 22, + "rule": "generic" + }, + { + "file": "test/test-replaygain.ts", + "line": 31, + "rule": "generic" + }, + { + "file": "test/test-trackinfo.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-trackinfo.ts", + "line": 10, + "rule": "generic" + }, + { + "file": "test/test-trackinfo.ts", + "line": 12, + "rule": "generic" + }, + { + "file": "test/test-trackinfo.ts", + "line": 14, + "rule": "generic" + }, + { + "file": "test/test-trackinfo.ts", + "line": 18, + "rule": "generic" + }, + { + "file": "test/test-trackinfo.ts", + "line": 30, + "rule": "generic" + }, + { + "file": "test/test-trackinfo.ts", + "line": 48, + "rule": "generic" + }, + { + "file": "test/test-trackinfo.ts", + "line": 50, + "rule": "generic" + }, + { + "file": "test/test-trackinfo.ts", + "line": 89, + "rule": "generic" + }, + { + "file": "test/test-trackinfo.ts", + "line": 242, + "rule": "generic" + }, + { + "file": "test/test-trackinfo.ts", + "line": 244, + "rule": "generic" + }, + { + "file": "test/test-ufid.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-ufid.ts", + "line": 9, + "rule": "generic" + }, + { + "file": "test/test-unknownencoding.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-unknownencoding.ts", + "line": 9, + "rule": "generic" + }, + { + "file": "test/test-unknownencoding.ts", + "line": 22, + "rule": "generic" + }, + { + "file": "test/test-unknownencoding.ts", + "line": 23, + "rule": "generic" + }, + { + "file": "test/test-unknownencoding.ts", + "line": 24, + "rule": "generic" + }, + { + "file": "test/test-utf16bom-encoding.ts", + "line": 2, + "rule": "generic" + }, + { + "file": "test/test-utf16bom-encoding.ts", + "line": 9, + "rule": "generic" + }, + { + "file": "test/test-util.ts", + "line": 10, + "rule": "generic" + }, + { + "file": "test/test-util.ts", + "line": 12, + "rule": "generic" + }, + { + "file": "test/test-util.ts", + "line": 16, + "rule": "generic" + }, + { + "file": "test/test-util.ts", + "line": 21, + "rule": "generic" + }, + { + "file": "test/test-util.ts", + "line": 26, + "rule": "generic" + }, + { + "file": "test/test-util.ts", + "line": 31, + "rule": "generic" + }, + { + "file": "test/test-util.ts", + "line": 36, + "rule": "generic" + }, + { + "file": "test/test-util.ts", + "line": 43, + "rule": "generic" + }, + { + "file": "test/test-util.ts", + "line": 44, + "rule": "generic" + }, + { + "file": "test/test-util.ts", + "line": 70, + "rule": "generic" + }, + { + "file": "test/test-util.ts", + "line": 72, + "rule": "generic" + }, + { + "file": "test/test-util.ts", + "line": 80, + "rule": "generic" + }, + { + "file": "test/util.ts", + "line": 3, + "rule": "generic" + }, + { + "file": "test/util.ts", + "line": 4, + "rule": "generic" + }, + { + "file": "test/util.ts", + "line": 5, + "rule": "generic" + }, + { + "file": "test/util.ts", + "line": 6, + "rule": "generic" + }, + { + "file": "test/util.ts", + "line": 8, + "rule": "generic" + }, + { + "file": "test/util.ts", + "line": 25, + "rule": "generic" + }, + { + "file": "test/util.ts", + "line": 26, + "rule": "generic" + }, + { + "file": "tsconfig.json", + "line": 7, + "rule": "base-url", + "note": "types [\"node\", \"mocha\"] and strict false added in the same change" + } + ] + } + ] +} diff --git a/packages/core/src/packs/typescript/index.ts b/packages/core/src/packs/typescript/index.ts new file mode 100644 index 00000000..eee75842 --- /dev/null +++ b/packages/core/src/packs/typescript/index.ts @@ -0,0 +1,212 @@ +import { definePack } from '../contract.js'; +import { + assertToWith, + moduleToNamespace, + optionSites, + removeDownlevelIteration, + rootDirSites, + unsetDefaults, +} from './detect.js'; +import { foldTypesDefault } from './fold.js'; + +/** + * typescript >=5 <6 → >=6 <7. The upgraded package is the compiler: what breaks is the + * repository's tsconfig (options 6.0 deprecates, errors until `ignoreDeprecations` is set) and + * code the new compiler rejects, much of it because a default changed. Code diagnostics come + * from `check`'s compile of the whole project with the target's own compiler + * (adapters/typescript/compile.ts, compileCompilerUpgrade); the tsconfig sites are this pack's, + * found in every tsconfig*.json of the workspace where the option is set. Sources read + * 2026-10-14; RESEARCH.md has the range decision and the evidence for each rule. + */ +const NOTES = 'https://www.typescriptlang.org/docs/handbook/release-notes/typescript-6-0.html'; + +const deprecated = (option: string, replacement: string): string => + `TypeScript 6.0 deprecates ${option}: \`tsc\` fails on it unless \`"ignoreDeprecations": "6.0"\` is set, and 7.0 removes it. ${replacement} Never add \`ignoreDeprecations\` to make the compile pass: that is a decision for the owner, not a migration.`; + +export const typescriptPack = definePack({ + meta: { + package: 'typescript', + from: '>=5 <6', + to: '>=6 <7', + sources: [ + { title: 'TypeScript 6.0 release notes (read 2026-10-14)', url: NOTES }, + { + title: 'Announcing TypeScript 6.0, 2026-03-23 (read 2026-10-14)', + url: 'https://devblogs.microsoft.com/typescript/announcing-typescript-6-0/', + }, + ], + maintainer: 'uptide-dev', + }, + inventory: ['{tsconfig*.json,**/tsconfig*.json}'], + requiresBuild: true, + foldFindings: foldTypesDefault, + rules: [ + { + id: 'base-url', + summary: 'baseUrl is deprecated and no longer a lookup root for bare imports', + severity: 'breaking', + kinds: [], + symbols: /$^/, + detect: optionSites('baseUrl'), + guide: deprecated( + '`baseUrl`', + 'Remove it and write its directory into each `paths` entry (`"@app/*": ["./src/app/*"]` where it was `baseUrl: "./src"` and `"@app/*": ["app/*"]`). If bare imports resolved through it (`import x from "components/x"`), add `"*": ["./src/*"]` to `paths` only when the compiler reports them unresolved. A bundler or framework that reads `baseUrl` itself (Next.js, vite-tsconfig-paths) resolves the same imports at runtime: keep both in step and say so.', + ), + }, + { + id: 'downlevel-iteration', + summary: 'downlevelIteration is deprecated; it only ever affected ES5 output', + severity: 'breaking', + kinds: [], + symbols: /$^/, + detect: optionSites('downlevelIteration'), + rewrite: removeDownlevelIteration, + guide: deprecated( + '`downlevelIteration`', + 'Remove the option. It changed only ES5 output, so with a target of ES2015 or later removing it changes nothing.', + ), + }, + { + id: 'module-resolution-node10', + summary: 'moduleResolution node (node10) is deprecated: use bundler or nodenext', + severity: 'breaking', + kinds: [], + symbols: /$^/, + detect: optionSites('moduleResolution', ['node', 'node10']), + guide: deprecated( + '`moduleResolution: "node"` (`node10`)', + 'Use `"bundler"` when a bundler (Vite, webpack, Next.js, esbuild) or Bun loads the code, `"nodenext"` (with `"module": "nodenext"`) when Node.js runs the output directly. The choice changes how `exports` maps and extensions resolve: name it in the pull request. Change `module` only when the chosen resolution requires it.', + ), + }, + { + id: 'module-resolution-classic', + summary: 'moduleResolution classic is removed: use bundler or nodenext', + severity: 'breaking', + kinds: [], + symbols: /$^/, + detect: optionSites('moduleResolution', ['classic']), + guide: + 'TypeScript 6.0 removes `moduleResolution: "classic"`. Use `"bundler"` for bundled code or `"nodenext"` for code Node.js runs, and say which and why.', + }, + { + id: 'target-es5', + summary: 'target ES5 (and ES3) is deprecated: the lowest target is ES2015', + severity: 'breaking', + kinds: [], + symbols: /$^/, + detect: optionSites('target', ['es3', 'es5']), + guide: deprecated( + '`target: "es5"`', + 'Raising the target changes the emitted JavaScript (classes, arrow functions and iteration stay native): a decision for the owner when the output runs in old browsers. With `noEmit` (a bundler emits) `target` only selects the default `lib`, and `"es2015"` or later is safe. Otherwise name the browsers the output must run in, or leave ES5 output to the bundler.', + ), + }, + { + id: 'module-format-removed', + summary: 'module amd, umd, system and none are deprecated: emit ES modules or CommonJS', + severity: 'breaking', + kinds: [], + symbols: /$^/, + detect: optionSites('module', ['amd', 'umd', 'system', 'systemjs', 'none']), + guide: deprecated( + '`module` values `amd`, `umd`, `system` and `none`', + 'Emit ES modules (`"esnext"`, `"preserve"`) or CommonJS and let a bundler produce the AMD, UMD or SystemJS output. This changes what consumers load: a decision for the owner.', + ), + }, + { + id: 'out-file', + summary: 'outFile is deprecated: concatenate with a bundler', + severity: 'breaking', + kinds: [], + symbols: /$^/, + detect: optionSites('outFile'), + guide: deprecated( + '`outFile`', + 'A bundler (esbuild, Rollup, webpack, Vite) produces the single file instead. This changes the build: a decision for the owner.', + ), + }, + { + id: 'interop-false', + summary: 'esModuleInterop and allowSyntheticDefaultImports can no longer be false', + severity: 'breaking', + kinds: [], + symbols: /$^/, + detect: (text, file) => [ + ...optionSites('esModuleInterop', [false])(text, file), + ...optionSites('allowSyntheticDefaultImports', [false])(text, file), + ], + guide: deprecated( + '`esModuleInterop: false` and `allowSyntheticDefaultImports: false`', + 'Remove the `false` setting: the safer interop is always on. Emitted CommonJS then goes through the interop helpers, and a namespace import that is called or constructed (`import * as express from "express"; express()`) becomes a default import (`import express from "express"`), at the sites the compiler reports.', + ), + }, + { + id: 'always-strict-false', + summary: 'alwaysStrict false is deprecated: all code is strict mode', + severity: 'breaking', + kinds: [], + symbols: /$^/, + detect: optionSites('alwaysStrict', [false]), + guide: deprecated( + '`alwaysStrict: false`', + 'Remove it. Emitted scripts gain "use strict": reserved words (`await`, `static`, `private`, `public`) cannot be identifiers and sloppy-mode `this` no longer applies. Fix the sites the compiler reports; name any non-module script whose runtime behavior may change.', + ), + }, + { + id: 'root-dir', + summary: + 'rootDir now defaults to the tsconfig directory: set it where outDir relied on inference', + severity: 'breaking', + kinds: [], + symbols: /$^/, + detect: rootDirSites, + guide: + 'TypeScript 6.0 defaults `rootDir` to the directory of the tsconfig, where 5.x inferred the common directory of the inputs; a project emitting from `src/` to `outDir` would write `dist/src/...`, and `tsc` stops with TS5011. Set `"rootDir"` to the directory 5.x inferred (`"./src"` for `include: ["src"]`), so the output layout stays the same. When `include` reaches outside the tsconfig directory, set it to the common parent (`"../src"`).', + }, + { + id: 'module-keyword', + summary: 'a namespace declared with `module` is an error: write `namespace`', + severity: 'breaking', + kinds: ['type'], + symbols: /^TS1540$/, + message: /should not be declared using the 'module' keyword/, + rewrite: moduleToNamespace, + guide: + '`module Foo { }` declares a namespace and is an error in TypeScript 6.0: write `namespace Foo { }`. `declare module "name" { }` (a quoted name) is an ambient module and is unchanged.', + }, + { + id: 'import-assertions', + summary: 'import assertions (`assert { type }`) are errors: write `with { type }`', + severity: 'breaking', + kinds: ['type'], + symbols: /^TS2880$/, + message: /Use 'with' instead of 'assert'/, + rewrite: assertToWith, + guide: + 'Import assertions were replaced by import attributes: `import data from "./data.json" with { type: "json" }`, and `import("./data.json", { with: { type: "json" } })`. Node.js 18.20, 20.10 and later accept `with`; Node.js 22 rejects `assert`.', + }, + ], + behavior: [ + { + id: 'changed-defaults', + summary: + 'TypeScript 6.0 changed defaults a tsconfig may rely on: strict is on, types is [], noUncheckedSideEffectImports is on, rootDir is the tsconfig directory, module is esnext and target is the current ES year. Each option left unset either keeps its new value (the code is fixed where the compiler reports it) or is pinned to its 5.x value in the tsconfig; say which, per option.', + // A finding only where the compiler shows it matters: errors from the `types` default fold + // into one at the tsconfig that leaves `types` unset (fold.ts). Elsewhere a decision. + reported: ['finding', 'decision'], + severity: 'deprecated', + applies: ({ inventory }) => { + const lines = unsetDefaults(inventory ?? []); + return lines.length > 0 + ? `TypeScript 6.0 changed defaults these tsconfig files leave unset; each keeps its new value or is pinned to its 5.x value, say which: ${lines.join(' · ')}` + : false; + }, + }, + ], + instructions: [ + 'TypeScript 6.0 is a compiler upgrade: the errors are the new compiler judging the same code, and each one is fixed in the code or the tsconfig it names.', + 'Never make the compile pass with `any`, `as any`, `@ts-ignore`, `@ts-expect-error`, `@ts-nocheck`, `ignoreDeprecations`, `skipLibCheck`, or by turning off a strictness option the repository had on. A default 6.0 changed (`strict`, `types`, `noUncheckedSideEffectImports`, `rootDir`, `module`, `target`) is pinned to its old value only when the pull request lists it under "Decisions for you".', + '`types` now defaults to `[]`: a "Cannot find name" error for `process`, `describe` or another global of an `@types` package is fixed by listing that package in `compilerOptions.types` of the tsconfig that compiles the file (`"types": ["node"]`), never by declaring the global.', + 'A side-effect import the compiler cannot resolve (`import "./styles.css"`, TS2882) needs a declaration (`declare module "*.css";` in a .d.ts the project includes) or the client types its bundler documents (`"types": ["vite/client"]`), never a cast.', + 'Tooling that peers on typescript (typescript-eslint, ts-node, ts-jest, tsx, framework type checkers) is reported with the range it accepts; never upgrade one across a major to get past it.', + ].join('\n'), +}); diff --git a/packages/core/src/packs/typescript/typescript.test.ts b/packages/core/src/packs/typescript/typescript.test.ts new file mode 100644 index 00000000..742a6a77 --- /dev/null +++ b/packages/core/src/packs/typescript/typescript.test.ts @@ -0,0 +1,86 @@ +import { fileURLToPath } from 'node:url'; +import { describe, expect, it } from 'vitest'; +import type { Finding } from '../../domain/report.js'; +import { runFixtures } from '../tooling/fixtures.js'; +import { removeDownlevelIteration, rootDirSites, unsetDefaults } from './detect.js'; +import { typescriptPack } from './index.js'; + +it('typescript: every fixture rewrites and detects exactly its marked sites', () => { + const result = runFixtures(typescriptPack, fileURLToPath(new URL('.', import.meta.url))); + expect(result.cases.length).toBeGreaterThan(0); + expect(result.unknown).toEqual([]); + expect(result.rewriteFailures).toEqual([]); + expect(result.falsePositives).toEqual([]); + expect(result.falseNegatives).toEqual([]); +}); + +const at = (file: string, line: number) => ({ usage: { file, line, column: 5 } }) as Finding; + +describe('downlevelIteration removal', () => { + const config = (target: string, extra = '') => + `{\n "compilerOptions": {\n "target": "${target}",${extra}\n "downlevelIteration": true\n }\n}\n`; + it('removes the line when the target is ES2015 or later', () => { + const result = removeDownlevelIteration(config('es2017'), at('tsconfig.json', 4)); + expect(result.applied).toBe(true); + expect(result.text).toBe('{\n "compilerOptions": {\n "target": "es2017",\n }\n}\n'); + }); + it('declines where it still changes the output: an ES5 target, or deprecations silenced', () => { + expect(removeDownlevelIteration(config('es5'), at('tsconfig.json', 4)).applied).toBe(false); + const silenced = config('es2017', '\n "ignoreDeprecations": "6.0",'); + expect(removeDownlevelIteration(silenced, at('tsconfig.json', 5)).applied).toBe(false); + }); + it('declines a property that shares its line', () => { + const text = '{ "compilerOptions": { "target": "es2017", "downlevelIteration": true } }\n'; + expect(removeDownlevelIteration(text, at('tsconfig.json', 1)).applied).toBe(false); + }); +}); + +describe('defaults a tsconfig leaves unset', () => { + it('follows extends inside the repository, and leaves a package config unknown', () => { + const lines = unsetDefaults([ + { file: 'tsconfig.base.json', text: '{ "compilerOptions": { "strict": true } }' }, + { + file: 'packages/api/tsconfig.json', + text: '{ "extends": "../../tsconfig.base.json", "compilerOptions": { "types": ["node"] } }', + }, + { file: 'apps/web/tsconfig.json', text: '{ "extends": "@tsconfig/node20/tsconfig.json" }' }, + ]); + expect(lines).toEqual([ + 'packages/api/tsconfig.json: noUncheckedSideEffectImports is on: `import "./x.css"` needs a declaration', + 'tsconfig.base.json: types is [] (every @types package was included): list the ones the code uses; noUncheckedSideEffectImports is on: `import "./x.css"` needs a declaration', + ]); + }); + it('is a decision for the owner, and a finding only where the compiler errors fold into it', () => { + const note = typescriptPack.behavior.find((b) => b.id === 'changed-defaults'); + expect(note?.reported).toEqual(['finding', 'decision']); + expect(note?.severity).toBe('deprecated'); + expect(note?.detect).toBeUndefined(); + const decision = note?.applies?.({ + from: '5.9.3', + to: '6.0.3', + includeDeprecated: false, + inventory: [{ file: 'tsconfig.json', text: '{ "compilerOptions": {} }' }], + }); + expect(decision).toMatch(/tsconfig\.json: strict is on/); + }); +}); + +describe('rootDir default', () => { + it('reports outDir only when the inputs share one directory and rootDir is unset', () => { + const config = (options: string, include: string) => + `{\n "compilerOptions": {\n${options}\n },\n "include": ${include}\n}\n`; + expect(rootDirSites(config(' "outDir": "dist"', '["src"]'), 'tsconfig.json')).toHaveLength( + 1, + ); + expect( + rootDirSites(config(' "outDir": "dist"', '["src", "test"]'), 'tsconfig.json'), + ).toEqual([]); + expect( + rootDirSites(config(' "outDir": "dist",\n "noEmit": true', '["src"]'), 'tsconfig.json'), + ).toEqual([]); + expect(rootDirSites(config(' "outDir": "dist"', '["**/*.ts"]'), 'tsconfig.json')).toEqual( + [], + ); + expect(rootDirSites(config(' "outDir": "dist"', '["src"]'), 'src/config.json')).toEqual([]); + }); +}); diff --git a/packages/core/src/packs/typescript/verification.json b/packages/core/src/packs/typescript/verification.json new file mode 100644 index 00000000..9d225d18 --- /dev/null +++ b/packages/core/src/packs/typescript/verification.json @@ -0,0 +1,23 @@ +{ + "status": "candidate", + "truth": "sha256-b463fcdce83820c229ab46ab51a2afb62c3da5f4f5e6a2b53fee63f302755e68", + "repos": 7, + "breaking": { + "precision": 1, + "recall": 0.1716, + "falsePositives": 0, + "truePositives": 240, + "falseNegatives": 1159 + }, + "behavior": { + "precision": 0.2, + "recall": 0.5, + "falsePositives": 8, + "truePositives": 2, + "falseNegatives": 2 + }, + "class": "breaking", + "breakingSites": 1399, + "behaviorSites": 4, + "unexercised": ["downlevel-iteration", "import-assertions", "module-keyword"] +} diff --git a/packs/queue.json b/packs/queue.json index e6f27456..68a1f123 100644 --- a/packs/queue.json +++ b/packs/queue.json @@ -442,6 +442,23 @@ "total": 65, "distinct": 65 } + }, + { + "package": "typescript", + "members": ["typescript"], + "from": ">=5 <6", + "to": ">=6 <7", + "why": "requested in UPT-56: declared in every repository of the 2026-10-14 OSS sample scan, most on 5.x; the compiler itself, so no type diff measures it: the 6.0 release notes (https://www.typescriptlang.org/docs/handbook/release-notes/typescript-6-0.html) deprecate baseUrl, moduleResolution node and classic, target ES5, module amd/umd/system/none, outFile, downlevelIteration, esModuleInterop and alwaysStrict false, module-keyword namespaces and import assertions, and change the strict, types, rootDir, module, target and noUncheckedSideEffectImports defaults; not measured", + "status": "candidate" + }, + { + "package": "typescript", + "members": ["typescript"], + "from": ">=6 <7", + "to": ">=7 <8", + "why": "split 2026-10-14 from the 5 to 6 entry above: 6.0 deprecates what 7.0 removes (Announcing TypeScript 7.0, 2026-07-08, https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/), so a 5.x repository goes through 6 first; 7.0 also makes stableTypeOrdering permanent, changes template literal inference on Unicode code points and drops JSDoc Closure-style function types; not measured", + "status": "blocked", + "blockedReason": "TypeScript 7.0 ships no JavaScript compiler API (typescript@7.0.2 exports only lib/version.cjs; the announcement says a new API comes with 7.1), so `check` cannot compute the target's diagnostics: compileCompilerUpgrade reports the compile as not done and only tsconfig findings would remain. Retry when typescript 7.x exposes a programmatic API check can load, or when check can run the target's tsc binary on the repository without executing repository code." } ] } diff --git a/scripts/packs-truth-compiler.ts b/scripts/packs-truth-compiler.ts index da471ba1..002cc53d 100644 --- a/scripts/packs-truth-compiler.ts +++ b/scripts/packs-truth-compiler.ts @@ -1,6 +1,7 @@ +import { realpathSync } from 'node:fs'; import { createRequire } from 'node:module'; import { dirname, join, relative } from 'node:path'; -import { unresolvedConfig } from '@uptide/core'; +import { consumerCompilerDir, unresolvedConfig } from '@uptide/core'; export interface Diagnostic { file: string; @@ -25,10 +26,14 @@ export function compilerDiagnostics( notCompiled: { path: string; reason: string }[]; } { const require = createRequire(join(project, 'package.json')); - let ts = require( - require.resolve('typescript', { - paths: [project, ...configs.map((c) => join(project, dirname(c)))], - }), + // Found on disk each time, by its real path: Node caches a resolution, and the typescript pack + // links the target compiler in place of the installed one between two calls. + const found = consumerCompilerDir(configs[0] ? join(project, dirname(configs[0])) : project); + const dir = found && realpathSync(found); + let ts = ( + dir + ? createRequire(join(dir, 'package.json'))(dir) + : require(require.resolve('typescript', { paths: [project] })) ) as typeof import('typescript'); let compiler = `typescript ${ts.version}, the repository's`; // TypeScript 7 has no JavaScript API: the one this checkout builds with stands in, said so. @@ -69,13 +74,19 @@ export function compilerDiagnostics( getDirectories: ts.sys.getDirectories, realpath: ts.sys.realpath, }); - for (const source of parsed.fileNames) { - if (source.includes('/node_modules/')) continue; - const diagnostics = [ - ...service.getSyntacticDiagnostics(source), - ...service.getSemanticDiagnostics(source), - ...service.getSuggestionDiagnostics(source).filter((d) => DEPRECATED.has(d.code)), - ]; + // The tsconfig's own errors (a deprecated option, TS5101): where the compiler points, which + // is the leaf config even for an option an extended one sets. + const configErrors = [...parsed.errors, ...service.getCompilerOptionsDiagnostics()]; + for (const source of [undefined, ...parsed.fileNames]) { + if (source?.includes('/node_modules/')) continue; + const diagnostics = + source === undefined + ? configErrors + : [ + ...service.getSyntacticDiagnostics(source), + ...service.getSemanticDiagnostics(source), + ...service.getSuggestionDiagnostics(source).filter((d) => DEPRECATED.has(d.code)), + ]; for (const d of diagnostics) { if (!d.file || d.start === undefined) continue; if (d.category !== ts.DiagnosticCategory.Error && !DEPRECATED.has(d.code)) continue; diff --git a/scripts/packs-truth.ts b/scripts/packs-truth.ts index e257f901..4b04a892 100644 --- a/scripts/packs-truth.ts +++ b/scripts/packs-truth.ts @@ -41,6 +41,7 @@ import { ensureRepo, insertionAnchors, maxSatisfying, + projectScope, satisfies, validRange, } from '@uptide/core'; @@ -97,17 +98,14 @@ function installs(name: string, dir: string, depth = 0): string[] { found.push(...installs(name, join(dir, entry.name), depth + 1)); return found; } -function tsconfigs(dir: string, depth = 0): string[] { - const found = existsSync(join(dir, 'tsconfig.json')) - ? [relative(project, join(dir, 'tsconfig.json'))] - : []; - if (depth >= 3) return found; - for (const entry of readdirSync(dir, { withFileTypes: true })) - if (entry.isDirectory() && !['node_modules', '.git', 'dist', 'build'].includes(entry.name)) - found.push(...tsconfigs(join(dir, entry.name), depth + 1)); - return found; -} -const configs = tsconfigs(project); +// The projects `check` compiles (the TypeScript project scope, project-scope.ts: every +// tsconfig*.json and its references, configs that are only extended left out), so the draft +// and the run judge the same files: a `tsconfig.eslint.json` ts-jest type-checks tests with is +// as much a build as `tsconfig.json`. +const configs = projectScope({ dir: project }) + .projects.map((p) => relative(project, p.config)) + .filter((config) => !config.startsWith('..')) + .sort(); const fetcher = createNpmFetcher(); const work = mkdtempSync(join(tmpdir(), 'uptide-truth-'));