diff --git a/.yarn/versions/a859e3b4.yml b/.yarn/versions/a859e3b4.yml new file mode 100644 index 0000000..b212163 --- /dev/null +++ b/.yarn/versions/a859e3b4.yml @@ -0,0 +1,2 @@ +releases: + ts-overrides-plugin: patch diff --git a/packages/plugin/src/cli/index.ts b/packages/plugin/src/cli/index.ts index c196d37..2a3539d 100644 --- a/packages/plugin/src/cli/index.ts +++ b/packages/plugin/src/cli/index.ts @@ -1,14 +1,70 @@ import * as path from 'node:path'; +import outmatch from 'outmatch'; import type { PluginConfig, ProgramTransformer } from 'ts-patch'; import type ts from 'typescript'; import { type Override } from '../types/Override'; -import { getDiagnosticForFile, getDiagnosticsForProject, getOverridePrograms } from './utils'; interface CliPluginConfig extends PluginConfig { overrides: Override[]; } +export const getOverridePrograms = ( + rootPath: string, + typescript: typeof ts, + overridesFromConfig: Override[], + rootFileNames: readonly string[], + defaultCompilerOptions: ts.CompilerOptions, + host?: ts.CompilerHost, +): ts.Program[] => { + const dTsFiles = rootFileNames.filter(fileName => fileName.endsWith(`.d.ts`)); + + return overridesFromConfig.map(override => { + const isMatch = outmatch(override.files); + const filesToCurrentOverrideDiagnostic: string[] = rootFileNames.filter(fileName => + isMatch(path.relative(rootPath, fileName)), + ); + + return typescript.createProgram( + [...filesToCurrentOverrideDiagnostic, ...dTsFiles], + { + ...defaultCompilerOptions, + ...typescript.convertCompilerOptionsFromJson(override.compilerOptions, rootPath).options, + }, + host, + ); + }); +}; + +export const getDiagnosticForFile = ( + overridePrograms: ts.Program[], + target: ts.Program, + sourceFile: ts.SourceFile, + method: 'getSemanticDiagnostics' | 'getBindAndCheckDiagnostics', + cancellationToken?: ts.CancellationToken, +): readonly ts.Diagnostic[] => { + const { fileName } = sourceFile; + + const overrideProgramForFile = overridePrograms.find(overrideProgram => + overrideProgram.getRootFileNames().includes(fileName), + ); + + return overrideProgramForFile + ? overrideProgramForFile[method](sourceFile, cancellationToken) + : target[method](sourceFile, cancellationToken); +}; + +export const getDiagnosticsForProject = ( + program: ts.Program, + overridePrograms: ts.Program[], + cancellationToken?: ts.CancellationToken, +): ts.Diagnostic[] => + program + .getSourceFiles() + .flatMap(sourceFile => + getDiagnosticForFile(overridePrograms, program, sourceFile, `getSemanticDiagnostics`, cancellationToken), + ); + const plugin: ProgramTransformer = (program, host, pluginConfig, extras) => { const { overrides: overridesFromConfig } = pluginConfig as CliPluginConfig; const { plugins, ...defaultCompilerOptions } = program.getCompilerOptions(); diff --git a/packages/plugin/src/cli/utils.ts b/packages/plugin/src/cli/utils.ts deleted file mode 100644 index 56aa8c1..0000000 --- a/packages/plugin/src/cli/utils.ts +++ /dev/null @@ -1,116 +0,0 @@ -import path from 'node:path'; -import outmatch from 'outmatch'; -import type ts from 'typescript'; - -import { type Override } from '../types/Override'; - -const getOverrideProgram = ( - rootPath: string, - typescript: typeof ts, - override: Override, - filesToOriginalDiagnostic: string[], - defaultCompilerOptions: ts.CompilerOptions, - dTsFiles: string[], - host?: ts.CompilerHost, -): { - overrideProgram: ts.Program; - filesToCurrentOverrideDiagnostic: string[]; -} => { - const isMatch = outmatch(override.files); - const filesToCurrentOverrideDiagnostic: string[] = filesToOriginalDiagnostic.filter(fileName => - isMatch(path.relative(rootPath, fileName)), - ); - - const overrideProgram = typescript.createProgram( - [...filesToCurrentOverrideDiagnostic, ...dTsFiles], - { - ...defaultCompilerOptions, - ...typescript.convertCompilerOptionsFromJson(override.compilerOptions, rootPath).options, - }, - host, - ); - - return { overrideProgram, filesToCurrentOverrideDiagnostic }; -}; - -export const getDiagnosticsForProject = ( - program: ts.Program, - overridePrograms: OverridePrograms, - cancellationToken?: ts.CancellationToken, -): ts.Diagnostic[] => { - const resultDiagnostic: ts.Diagnostic[] = overridePrograms.resultOverrides.flatMap(override => - override - .getRootFileNames() - .flatMap(fileName => override.getSemanticDiagnostics(override.getSourceFile(fileName), cancellationToken)), - ); - - const originalDiagnostics = overridePrograms.filesToOriginalDiagnostic.flatMap(fileName => { - const sourceFile = program.getSourceFile(fileName); - - return sourceFile ? program.getSemanticDiagnostics(sourceFile, cancellationToken) : []; - }); - - return [...resultDiagnostic, ...originalDiagnostics]; -}; - -export const getOverridePrograms = ( - rootPath: string, - typescript: typeof ts, - overridesFromConfig: Override[], - rootFileNames: readonly string[], - defaultCompilerOptions: ts.CompilerOptions, - host?: ts.CompilerHost, -): { - resultOverrides: ts.Program[]; - filesToOriginalDiagnostic: string[]; -} => { - let filesToOriginalDiagnostic: string[] = [...rootFileNames]; - const dTsFiles = rootFileNames.filter(fileName => fileName.endsWith(`.d.ts`)); - - const resultOverrides: ts.Program[] = overridesFromConfig.map(override => { - const { overrideProgram, filesToCurrentOverrideDiagnostic } = getOverrideProgram( - rootPath, - typescript, - override, - filesToOriginalDiagnostic, - defaultCompilerOptions, - dTsFiles, - host, - ); - - filesToOriginalDiagnostic = filesToOriginalDiagnostic.filter( - fileName => !filesToCurrentOverrideDiagnostic.includes(fileName), - ); - - return overrideProgram; - }); - - return { resultOverrides, filesToOriginalDiagnostic }; -}; - -export interface OverridePrograms { - filesToOriginalDiagnostic: string[]; - resultOverrides: ts.Program[]; -} - -export const getDiagnosticForFile = ( - overridePrograms: OverridePrograms, - target: ts.Program, - sourceFile: ts.SourceFile, - method: 'getSemanticDiagnostics' | 'getBindAndCheckDiagnostics', - cancellationToken?: ts.CancellationToken, -): readonly ts.Diagnostic[] => { - const { fileName } = sourceFile; - - if (overridePrograms.filesToOriginalDiagnostic.includes(fileName)) { - return target[method](sourceFile, cancellationToken); - } - - const overrideProgramForFile = overridePrograms.resultOverrides.find(overrideProgram => - overrideProgram.getRootFileNames().includes(fileName), - ); - - return overrideProgramForFile - ? overrideProgramForFile[method](sourceFile, cancellationToken) - : target[method](sourceFile, cancellationToken); -};