From 389933f0797fa54ce33da2db181fdd278cd01765 Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Wed, 28 Aug 2024 11:48:56 +0000 Subject: [PATCH] refactor(migrations): allow tsurge migrations to run with just `NgCompiler` (#57562) This is important so that migrations can easily be wired up in the language service where only `NgCompiler` is available. PR Close #57562 --- .../signal-migration/src/analysis_deps.ts | 2 +- .../signal-migration/src/migration.ts | 37 +++++++++---------- .../schematics/utils/tsurge/base_migration.ts | 21 +++-------- .../utils/tsurge/executors/analyze_exec.ts | 10 +---- .../utils/tsurge/executors/merge_exec.ts | 10 +---- .../utils/tsurge/executors/migrate_exec.ts | 10 +---- .../utils/tsurge/helpers/ngtsc_program.ts | 5 ++- .../core/schematics/utils/tsurge/migration.ts | 36 ++++-------------- .../schematics/utils/tsurge/program_info.ts | 10 +++-- .../utils/tsurge/test/output_helpers.ts | 3 +- .../utils/tsurge/test/output_migration.ts | 8 ++-- 11 files changed, 52 insertions(+), 100 deletions(-) diff --git a/packages/core/schematics/migrations/signal-migration/src/analysis_deps.ts b/packages/core/schematics/migrations/signal-migration/src/analysis_deps.ts index 8949324d4e2..d99034dfb96 100644 --- a/packages/core/schematics/migrations/signal-migration/src/analysis_deps.ts +++ b/packages/core/schematics/migrations/signal-migration/src/analysis_deps.ts @@ -25,7 +25,7 @@ import {TemplateTypeChecker} from '../../../../../compiler-cli/src/ngtsc/typeche * Interface containing the analysis information * for an Angular program to be migrated. */ -export interface AnalysisProgramInfo extends ProgramInfo { +export interface AnalysisProgramInfo extends ProgramInfo { // List of source files in the program. sourceFiles: ts.SourceFile[]; // List of all files in the program, including external `d.ts`. diff --git a/packages/core/schematics/migrations/signal-migration/src/migration.ts b/packages/core/schematics/migrations/signal-migration/src/migration.ts index 98ca7165e7f..bd97ec0a755 100644 --- a/packages/core/schematics/migrations/signal-migration/src/migration.ts +++ b/packages/core/schematics/migrations/signal-migration/src/migration.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {NgtscProgram} from '../../../../../compiler-cli/src/ngtsc/program'; import {FileSystem} from '../../../../../compiler-cli/src/ngtsc/file_system'; import {confirmAsSerializable, Serializable} from '../../../utils/tsurge/helpers/serializable'; import {BaseProgramInfo, ProgramInfo} from '../../../utils/tsurge/program_info'; @@ -26,6 +25,7 @@ import {InheritanceGraph} from './utils/inheritance_graph'; import {executeMigrationPhase} from './phase_migrate'; import {filterIncompatibilitiesForBestEffortMode} from './best_effort_mode'; import {createNgtscProgram} from '../../../utils/tsurge/helpers/ngtsc_program'; +import assert from 'assert'; /** * Tsurge migration for migrating Angular `@Input()` declarations to @@ -33,9 +33,7 @@ import {createNgtscProgram} from '../../../utils/tsurge/helpers/ngtsc_program'; */ export class SignalInputMigration extends TsurgeComplexMigration< CompilationUnitData, - CompilationUnitData, - NgtscProgram, - AnalysisProgramInfo + CompilationUnitData > { upgradeAnalysisPhaseToAvoidBatch = false; upgradedAnalysisPhaseResults: Replacement[] | null = null; @@ -43,7 +41,7 @@ export class SignalInputMigration extends TsurgeComplexMigration< bestEffortMode = false; // Override the default ngtsc program creation, to add extra flags. - override createProgram(tsconfigAbsPath: string, fs?: FileSystem): BaseProgramInfo { + override createProgram(tsconfigAbsPath: string, fs?: FileSystem): BaseProgramInfo { return createNgtscProgram(tsconfigAbsPath, fs, { _enableTemplateTypeChecker: true, _compilePoisonedComponents: true, @@ -57,23 +55,20 @@ export class SignalInputMigration extends TsurgeComplexMigration< } // Extend the program info with the analysis information we need in every phase. - override prepareProgram(baseInfo: BaseProgramInfo): AnalysisProgramInfo { - const info = super.prepareProgram(baseInfo); + prepareAnalysisDeps(info: ProgramInfo): AnalysisProgramInfo { + assert(info.ngCompiler !== null, 'Expected `NgCompiler` to be configured.'); return { ...info, - ...prepareAnalysisInfo( - info.program.getTsProgram(), - info.program.compiler, - info.programAbsoluteRootPaths, - ), + ...prepareAnalysisInfo(info.program, info.ngCompiler, info.programAbsoluteRootPaths), }; } - override async analyze(analysisDeps: AnalysisProgramInfo) { + override async analyze(info: ProgramInfo) { + const analysisDeps = this.prepareAnalysisDeps(info); const {metaRegistry} = analysisDeps; const knownInputs = new KnownInputs(); const result = new MigrationResult(); - const host = createMigrationHost(analysisDeps); + const host = createMigrationHost(info); const {inheritanceGraph} = executeAnalysisPhase(host, knownInputs, result, analysisDeps); pass4__checkInheritanceOfInputs(host, inheritanceGraph, metaRegistry, knownInputs); @@ -86,11 +81,12 @@ export class SignalInputMigration extends TsurgeComplexMigration< // Non-batch mode! if (this.upgradeAnalysisPhaseToAvoidBatch) { const merged = await this.merge([unitData]); - const replacements = await this.migrate(merged, analysisDeps, { + const replacements = await this.migrate(merged, info, { knownInputs, result, host, inheritanceGraph, + analysisDeps, }); // Expose the upgraded analysis stage results. @@ -106,18 +102,19 @@ export class SignalInputMigration extends TsurgeComplexMigration< override async migrate( globalMetadata: CompilationUnitData, - analysisDeps: AnalysisProgramInfo, + info: ProgramInfo, nonBatchData?: { knownInputs: KnownInputs; result: MigrationResult; host: MigrationHost; inheritanceGraph: InheritanceGraph; + analysisDeps: AnalysisProgramInfo; }, ): Promise { const knownInputs = nonBatchData?.knownInputs ?? new KnownInputs(); const result = nonBatchData?.result ?? new MigrationResult(); - const host = nonBatchData?.host ?? createMigrationHost(analysisDeps); - const {metaRegistry} = analysisDeps; + const host = nonBatchData?.host ?? createMigrationHost(info); + const analysisDeps = nonBatchData?.analysisDeps ?? this.prepareAnalysisDeps(info); let inheritanceGraph: InheritanceGraph; // Can't re-use analysis structures, so re-build them. @@ -129,7 +126,7 @@ export class SignalInputMigration extends TsurgeComplexMigration< inheritanceGraph = nonBatchData.inheritanceGraph; } - pass4__checkInheritanceOfInputs(host, inheritanceGraph, metaRegistry, knownInputs); + pass4__checkInheritanceOfInputs(host, inheritanceGraph, analysisDeps.metaRegistry, knownInputs); if (this.bestEffortMode) { filterIncompatibilitiesForBestEffortMode(knownInputs); } @@ -140,7 +137,7 @@ export class SignalInputMigration extends TsurgeComplexMigration< } } -function createMigrationHost(info: ProgramInfo): MigrationHost { +function createMigrationHost(info: ProgramInfo): MigrationHost { return new MigrationHost( /* projectDir */ info.projectDirAbsPath, /* isMigratingCore */ false, diff --git a/packages/core/schematics/utils/tsurge/base_migration.ts b/packages/core/schematics/utils/tsurge/base_migration.ts index 73eb6f92f63..139a8417fb5 100644 --- a/packages/core/schematics/utils/tsurge/base_migration.ts +++ b/packages/core/schematics/utils/tsurge/base_migration.ts @@ -6,11 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import assert from 'assert'; import path from 'path'; -import ts from 'typescript'; import {FileSystem} from '../../../../compiler-cli/src/ngtsc/file_system'; -import {NgtscProgram} from '../../../../compiler-cli/src/ngtsc/program'; import {isShim} from '../../../../compiler-cli/src/ngtsc/shims'; import {createNgtscProgram} from './helpers/ngtsc_program'; import {BaseProgramInfo, ProgramInfo} from './program_info'; @@ -21,22 +18,16 @@ import {BaseProgramInfo, ProgramInfo} from './program_info'; * For example, this class exposes methods to conveniently create * TypeScript programs, while also allowing migration authors to override. */ -export abstract class TsurgeBaseMigration< - TsProgramType extends ts.Program | NgtscProgram = NgtscProgram, - PreparationInfo = ProgramInfo, -> { +export abstract class TsurgeBaseMigration { // By default, ngtsc programs are being created. - createProgram(tsconfigAbsPath: string, fs?: FileSystem): BaseProgramInfo { - return createNgtscProgram(tsconfigAbsPath, fs) as BaseProgramInfo; + createProgram(tsconfigAbsPath: string, fs?: FileSystem): BaseProgramInfo { + return createNgtscProgram(tsconfigAbsPath, fs); } // Optional function to prepare the base `ProgramInfo` even further, // for the analyze and migrate phases. E.g. determining source files. - prepareProgram(info: BaseProgramInfo): PreparationInfo { - assert(info.program instanceof NgtscProgram); - - const userProgram = info.program.getTsProgram(); - const fullProgramSourceFiles = userProgram.getSourceFiles(); + prepareProgram(info: BaseProgramInfo): ProgramInfo { + const fullProgramSourceFiles = [...info.program.getSourceFiles()]; const sourceFiles = fullProgramSourceFiles.filter( (f) => !f.isDeclarationFile && @@ -53,6 +44,6 @@ export abstract class TsurgeBaseMigration< sourceFiles, fullProgramSourceFiles, projectDirAbsPath, - } as PreparationInfo; + }; } } diff --git a/packages/core/schematics/utils/tsurge/executors/analyze_exec.ts b/packages/core/schematics/utils/tsurge/executors/analyze_exec.ts index 63c9e2801ed..59714aff232 100644 --- a/packages/core/schematics/utils/tsurge/executors/analyze_exec.ts +++ b/packages/core/schematics/utils/tsurge/executors/analyze_exec.ts @@ -8,8 +8,6 @@ import {TsurgeMigration} from '../migration'; import {Serializable} from '../helpers/serializable'; -import ts from 'typescript'; -import {NgtscProgram} from '../../../../../compiler-cli/src/ngtsc/program'; /** * Executes the analyze phase of the given migration against @@ -17,12 +15,8 @@ import {NgtscProgram} from '../../../../../compiler-cli/src/ngtsc/program'; * * @returns the serializable migration unit data. */ -export async function executeAnalyzePhase< - UnitData, - GlobalData, - TsProgramType extends ts.Program | NgtscProgram, ->( - migration: TsurgeMigration, +export async function executeAnalyzePhase( + migration: TsurgeMigration, tsconfigAbsolutePath: string, ): Promise> { const baseInfo = migration.createProgram(tsconfigAbsolutePath); diff --git a/packages/core/schematics/utils/tsurge/executors/merge_exec.ts b/packages/core/schematics/utils/tsurge/executors/merge_exec.ts index b07f1c46a2b..7dbe5e037d0 100644 --- a/packages/core/schematics/utils/tsurge/executors/merge_exec.ts +++ b/packages/core/schematics/utils/tsurge/executors/merge_exec.ts @@ -6,10 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import ts from 'typescript'; import {Serializable} from '../helpers/serializable'; import {TsurgeMigration} from '../migration'; -import {NgtscProgram} from '../../../../../compiler-cli/src/ngtsc/program'; /** * Executes the merge phase for the given migration against @@ -17,12 +15,8 @@ import {NgtscProgram} from '../../../../../compiler-cli/src/ngtsc/program'; * * @returns the serializable migration global data. */ -export async function executeMergePhase< - UnitData, - GlobalData, - TsProgramType extends ts.Program | NgtscProgram, ->( - migration: TsurgeMigration, +export async function executeMergePhase( + migration: TsurgeMigration, units: UnitData[], ): Promise> { return await migration.merge(units); diff --git a/packages/core/schematics/utils/tsurge/executors/migrate_exec.ts b/packages/core/schematics/utils/tsurge/executors/migrate_exec.ts index 1af78f89813..48c9f441330 100644 --- a/packages/core/schematics/utils/tsurge/executors/migrate_exec.ts +++ b/packages/core/schematics/utils/tsurge/executors/migrate_exec.ts @@ -8,8 +8,6 @@ import {TsurgeMigration} from '../migration'; import {Replacement} from '../replacement'; -import {NgtscProgram} from '../../../../../compiler-cli/src/ngtsc/program'; -import ts from 'typescript'; /** * Executes the migrate phase of the given migration against @@ -20,12 +18,8 @@ import ts from 'typescript'; * * @returns a list of text replacements to apply to disk. */ -export async function executeMigratePhase< - UnitData, - GlobalData, - TsProgramType extends ts.Program | NgtscProgram, ->( - migration: TsurgeMigration, +export async function executeMigratePhase( + migration: TsurgeMigration, globalMetadata: GlobalData, tsconfigAbsolutePath: string, ): Promise { diff --git a/packages/core/schematics/utils/tsurge/helpers/ngtsc_program.ts b/packages/core/schematics/utils/tsurge/helpers/ngtsc_program.ts index 2d1eec04538..1caae37d26b 100644 --- a/packages/core/schematics/utils/tsurge/helpers/ngtsc_program.ts +++ b/packages/core/schematics/utils/tsurge/helpers/ngtsc_program.ts @@ -25,7 +25,7 @@ export function createNgtscProgram( absoluteTsconfigPath: string, fs?: FileSystem, optionOverrides: NgCompilerOptions = {}, -): BaseProgramInfo { +): BaseProgramInfo { if (fs === undefined) { fs = new NodeJSFileSystem(); setFileSystem(fs); @@ -57,7 +57,8 @@ export function createNgtscProgram( ); return { - program: ngtscProgram, + ngCompiler: ngtscProgram.compiler, + program: ngtscProgram.getTsProgram(), userOptions: tsconfig.options, programAbsoluteRootPaths: tsconfig.rootNames, tsconfigAbsolutePath: absoluteTsconfigPath, diff --git a/packages/core/schematics/utils/tsurge/migration.ts b/packages/core/schematics/utils/tsurge/migration.ts index 0f670c79381..e7bfa33184d 100644 --- a/packages/core/schematics/utils/tsurge/migration.ts +++ b/packages/core/schematics/utils/tsurge/migration.ts @@ -6,8 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import ts from 'typescript'; -import {NgtscProgram} from '../../../../compiler-cli/src/ngtsc/program'; import {TsurgeBaseMigration} from './base_migration'; import {Serializable} from './helpers/serializable'; import {ProgramInfo} from './program_info'; @@ -37,24 +35,9 @@ import {Replacement} from './replacement'; * * TODO: Link design doc */ -export type TsurgeMigration< - UnitAnalysisMetadata, - CombinedGlobalMetadata, - TsProgramType extends ts.Program | NgtscProgram = NgtscProgram, - PreparationInfo = ProgramInfo, -> = - | TsurgeComplexMigration< - UnitAnalysisMetadata, - CombinedGlobalMetadata, - TsProgramType, - PreparationInfo - > - | TsurgeFunnelMigration< - UnitAnalysisMetadata, - CombinedGlobalMetadata, - TsProgramType, - PreparationInfo - >; +export type TsurgeMigration = + | TsurgeComplexMigration + | TsurgeFunnelMigration; /** * A simpler variant of a {@link TsurgeComplexMigration} that does not @@ -71,9 +54,8 @@ export type TsurgeMigration< export abstract class TsurgeFunnelMigration< UnitAnalysisMetadata, CombinedGlobalMetadata, - TsProgramType extends ts.Program | NgtscProgram = NgtscProgram, - PreparationInfo = ProgramInfo, -> extends TsurgeBaseMigration { + PreparationInfo = ProgramInfo, +> extends TsurgeBaseMigration { /** Analyzes the given TypeScript project and returns serializable compilation unit data. */ abstract analyze(info: PreparationInfo): Promise>; @@ -104,11 +86,9 @@ export abstract class TsurgeFunnelMigration< export abstract class TsurgeComplexMigration< UnitAnalysisMetadata, CombinedGlobalMetadata, - TsProgramType extends ts.Program | NgtscProgram = NgtscProgram, - PreparationInfo = ProgramInfo, -> extends TsurgeBaseMigration { +> extends TsurgeBaseMigration { /** Analyzes the given TypeScript project and returns serializable compilation unit data. */ - abstract analyze(info: PreparationInfo): Promise>; + abstract analyze(info: ProgramInfo): Promise>; /** Merges all compilation unit data from previous analysis phases into a global result. */ abstract merge(units: UnitAnalysisMetadata[]): Promise>; @@ -122,6 +102,6 @@ export abstract class TsurgeComplexMigration< */ abstract migrate( globalMetadata: CombinedGlobalMetadata, - info: PreparationInfo, + info: ProgramInfo, ): Promise; } diff --git a/packages/core/schematics/utils/tsurge/program_info.ts b/packages/core/schematics/utils/tsurge/program_info.ts index cc68c23ae83..ed5a844ef8f 100644 --- a/packages/core/schematics/utils/tsurge/program_info.ts +++ b/packages/core/schematics/utils/tsurge/program_info.ts @@ -10,14 +10,16 @@ import {NgtscProgram} from '../../../../compiler-cli/src/ngtsc/program'; import {NgCompilerOptions} from '../../../../compiler-cli/src/ngtsc/core/api'; import ts from 'typescript'; +import {NgCompiler} from '../../../../compiler-cli/src/ngtsc/core'; /** * Base information for a TypeScript project, including an instantiated * TypeScript program. Base information may be extended by user-overridden * migration preparation methods to extend the stages with more data. */ -export interface BaseProgramInfo { - program: T; +export interface BaseProgramInfo { + ngCompiler: NgCompiler | null; + program: ts.Program; userOptions: NgCompilerOptions; programAbsoluteRootPaths: string[]; tsconfigAbsolutePath: string; @@ -30,8 +32,8 @@ export interface BaseProgramInfo { * A different interface may be used as full program information, configured via a * {@link TsurgeMigration.prepareProgram} override. */ -export interface ProgramInfo extends BaseProgramInfo { +export interface ProgramInfo extends BaseProgramInfo { sourceFiles: ts.SourceFile[]; - fullProgramSourceFiles: ts.SourceFile[]; + fullProgramSourceFiles: readonly ts.SourceFile[]; projectDirAbsPath: string; } diff --git a/packages/core/schematics/utils/tsurge/test/output_helpers.ts b/packages/core/schematics/utils/tsurge/test/output_helpers.ts index f0e90afdf8f..2d2a456d26b 100644 --- a/packages/core/schematics/utils/tsurge/test/output_helpers.ts +++ b/packages/core/schematics/utils/tsurge/test/output_helpers.ts @@ -10,7 +10,6 @@ import path from 'path'; import {UniqueID} from '../helpers/unique_id'; import ts from 'typescript'; import {ProgramInfo} from '../program_info'; -import {NgtscProgram} from '../../../../../compiler-cli/src/ngtsc/program'; import {DtsMetadataReader} from '../../../../../compiler-cli/src/ngtsc/metadata'; import {ClassDeclaration, ReflectionHost} from '../../../../../compiler-cli/src/ngtsc/reflection'; import {Reference} from '../../../../../compiler-cli/src/ngtsc/imports'; @@ -24,7 +23,7 @@ export function getIdOfOutput(projectDirAbsPath: string, prop: ts.PropertyDeclar } export function findOutputDeclarationsAndReferences( - {sourceFiles, projectDirAbsPath}: ProgramInfo, + {sourceFiles, projectDirAbsPath}: ProgramInfo, checker: ts.TypeChecker, reflector: ReflectionHost, dtsReader: DtsMetadataReader, diff --git a/packages/core/schematics/utils/tsurge/test/output_migration.ts b/packages/core/schematics/utils/tsurge/test/output_migration.ts index af6907cfffe..e65ac89c7e2 100644 --- a/packages/core/schematics/utils/tsurge/test/output_migration.ts +++ b/packages/core/schematics/utils/tsurge/test/output_migration.ts @@ -27,8 +27,8 @@ type GlobalMetadata = {[id: OutputID]: {canBeMigrated: boolean}}; * framework works as expected. This is **not a full migration**, but rather an example. */ export class OutputMigration extends TsurgeComplexMigration { - override async analyze(info: ProgramInfo) { - const program = info.program.getTsProgram(); + override async analyze(info: ProgramInfo) { + const program = info.program; const typeChecker = program.getTypeChecker(); const reflector = new TypeScriptReflectionHost(typeChecker, false); const dtsReader = new DtsMetadataReader(typeChecker, reflector); @@ -74,8 +74,8 @@ export class OutputMigration extends TsurgeComplexMigration) { - const program = info.program.getTsProgram(); + override async migrate(globalAnalysisData: GlobalMetadata, info: ProgramInfo) { + const program = info.program; const typeChecker = program.getTypeChecker(); const reflector = new TypeScriptReflectionHost(typeChecker, false); const dtsReader = new DtsMetadataReader(typeChecker, reflector);