diff --git a/packages/core/schematics/migrations/document-core/document_core_migration.ts b/packages/core/schematics/migrations/document-core/document_core_migration.ts index 63a2253af1d..4fb71f3b473 100644 --- a/packages/core/schematics/migrations/document-core/document_core_migration.ts +++ b/packages/core/schematics/migrations/document-core/document_core_migration.ts @@ -91,6 +91,6 @@ export class DocumentCoreMigration extends TsurgeFunnelMigration< } override async stats() { - return {counters: {}}; + return confirmAsSerializable({}); } } diff --git a/packages/core/schematics/migrations/inject-flags/inject_flags_migration.ts b/packages/core/schematics/migrations/inject-flags/inject_flags_migration.ts index 9de9d3a39cd..ce46ebefdcb 100644 --- a/packages/core/schematics/migrations/inject-flags/inject_flags_migration.ts +++ b/packages/core/schematics/migrations/inject-flags/inject_flags_migration.ts @@ -153,7 +153,7 @@ export class InjectFlagsMigration extends TsurgeFunnelMigration< } override async stats() { - return {counters: {}}; + return confirmAsSerializable({}); } } diff --git a/packages/core/schematics/migrations/output-migration/output-migration.spec.ts b/packages/core/schematics/migrations/output-migration/output-migration.spec.ts index fc9f059c8ee..fd29b798d56 100644 --- a/packages/core/schematics/migrations/output-migration/output-migration.spec.ts +++ b/packages/core/schematics/migrations/output-migration/output-migration.spec.ts @@ -678,9 +678,9 @@ describe('outputs', () => { ]); const stats = await runResults.getStatistics(); - expect(stats.counters['detectedOutputs']).toBe(4); - expect(stats.counters['problematicOutputs']).toBe(2); - expect(stats.counters['successRate']).toBe(0.5); + expect(stats['detectedOutputs']).toBe(4); + expect(stats['problematicOutputs']).toBe(2); + expect(stats['successRate']).toBe(0.5); }); it('should capture migration statistics without problematic usages', async () => { @@ -696,9 +696,9 @@ describe('outputs', () => { ]); const stats = await runResults.getStatistics(); - expect(stats.counters['detectedOutputs']).toBe(2); - expect(stats.counters['problematicOutputs']).toBe(0); - expect(stats.counters['successRate']).toBe(1); + expect(stats['detectedOutputs']).toBe(2); + expect(stats['problematicOutputs']).toBe(0); + expect(stats['successRate']).toBe(1); }); }); diff --git a/packages/core/schematics/migrations/output-migration/output-migration.ts b/packages/core/schematics/migrations/output-migration/output-migration.ts index 3a34dca85b6..fd2c376f17d 100644 --- a/packages/core/schematics/migrations/output-migration/output-migration.ts +++ b/packages/core/schematics/migrations/output-migration/output-migration.ts @@ -9,7 +9,6 @@ import ts from 'typescript'; import { confirmAsSerializable, - MigrationStats, ProgramInfo, projectFile, ProjectFile, @@ -384,7 +383,7 @@ export class OutputMigration extends TsurgeFunnelMigration< return confirmAsSerializable(combinedData); } - override async stats(globalMetadata: CompilationUnitData): Promise { + override async stats(globalMetadata: CompilationUnitData) { const detectedOutputs = new Set(Object.keys(globalMetadata.outputFields)).size + globalMetadata.problematicDeclarationCount; @@ -395,13 +394,11 @@ export class OutputMigration extends TsurgeFunnelMigration< const successRate = detectedOutputs > 0 ? (detectedOutputs - problematicOutputs) / detectedOutputs : 1; - return { - counters: { - detectedOutputs, - problematicOutputs, - successRate, - }, - }; + return confirmAsSerializable({ + detectedOutputs, + problematicOutputs, + successRate, + }); } override async migrate(globalData: CompilationUnitData) { diff --git a/packages/core/schematics/migrations/self-closing-tags-migration/self-closing-tags-migration.ts b/packages/core/schematics/migrations/self-closing-tags-migration/self-closing-tags-migration.ts index e3ffe0f5e10..8191c066cbc 100644 --- a/packages/core/schematics/migrations/self-closing-tags-migration/self-closing-tags-migration.ts +++ b/packages/core/schematics/migrations/self-closing-tags-migration/self-closing-tags-migration.ts @@ -9,7 +9,6 @@ import ts from 'typescript'; import { confirmAsSerializable, - MigrationStats, ProgramInfo, projectFile, ProjectFile, @@ -128,21 +127,17 @@ export class SelfClosingTagsMigration extends TsurgeFunnelMigration< return confirmAsSerializable(globalMeta); } - override async stats( - globalMetadata: SelfClosingTagsCompilationUnitData, - ): Promise { + override async stats(globalMetadata: SelfClosingTagsCompilationUnitData) { const touchedFilesCount = globalMetadata.tagReplacements.length; const replacementCount = globalMetadata.tagReplacements.reduce( (acc, cur) => acc + cur.replacementCount, 0, ); - return { - counters: { - touchedFilesCount, - replacementCount, - }, - }; + return confirmAsSerializable({ + touchedFilesCount, + replacementCount, + }); } override async migrate(globalData: SelfClosingTagsCompilationUnitData) { diff --git a/packages/core/schematics/migrations/signal-migration/src/migration.ts b/packages/core/schematics/migrations/signal-migration/src/migration.ts index 476b7d0eedc..ccd5c48ac7d 100644 --- a/packages/core/schematics/migrations/signal-migration/src/migration.ts +++ b/packages/core/schematics/migrations/signal-migration/src/migration.ts @@ -231,15 +231,13 @@ export class SignalInputMigration extends TsurgeComplexMigration< } } - return { - counters: { - fullCompilationInputs, - sourceInputs, - incompatibleInputs, - ...fieldIncompatibleCounts, - ...classIncompatibleCounts, - }, - }; + return confirmAsSerializable({ + fullCompilationInputs, + sourceInputs, + incompatibleInputs, + ...fieldIncompatibleCounts, + ...classIncompatibleCounts, + }); } } diff --git a/packages/core/schematics/migrations/signal-queries-migration/migration.spec.ts b/packages/core/schematics/migrations/signal-queries-migration/migration.spec.ts index a6bdc549ef2..93f017b285e 100644 --- a/packages/core/schematics/migrations/signal-queries-migration/migration.spec.ts +++ b/packages/core/schematics/migrations/signal-queries-migration/migration.spec.ts @@ -1586,14 +1586,14 @@ describe('signal queries migration', () => { ], ); - expect(await getStatistics()).toEqual({ - counters: { - queriesCount: 3, - multiQueries: 2, - incompatibleQueries: 2, - 'incompat-field-Accessor': 1, - 'incompat-field-WriteAssignment': 1, - }, + // Cast as we dynamically add fields to the stats. This can be improved in follow-ups + // when stats for this migration are leveraging more complex data structures. + expect((await getStatistics()) as object).toEqual({ + queriesCount: 3, + multiQueries: 2, + incompatibleQueries: 2, + 'incompat-field-Accessor': 1, + 'incompat-field-WriteAssignment': 1, }); }); diff --git a/packages/core/schematics/migrations/signal-queries-migration/migration.ts b/packages/core/schematics/migrations/signal-queries-migration/migration.ts index 94473c36360..0defafc6110 100644 --- a/packages/core/schematics/migrations/signal-queries-migration/migration.ts +++ b/packages/core/schematics/migrations/signal-queries-migration/migration.ts @@ -644,15 +644,13 @@ export class SignalQueriesMigration extends TsurgeComplexMigration< } } - return { - counters: { - queriesCount, - multiQueries, - incompatibleQueries, - ...fieldIncompatibleCounts, - ...classIncompatibleCounts, - }, - }; + return confirmAsSerializable({ + queriesCount, + multiQueries, + incompatibleQueries, + ...fieldIncompatibleCounts, + ...classIncompatibleCounts, + }); } } diff --git a/packages/core/schematics/migrations/test-bed-get/test_bed_get_migration.ts b/packages/core/schematics/migrations/test-bed-get/test_bed_get_migration.ts index e252eae2dc8..64e8554733f 100644 --- a/packages/core/schematics/migrations/test-bed-get/test_bed_get_migration.ts +++ b/packages/core/schematics/migrations/test-bed-get/test_bed_get_migration.ts @@ -110,6 +110,6 @@ export class TestBedGetMigration extends TsurgeFunnelMigration< } override async stats() { - return {counters: {}}; + return confirmAsSerializable({}); } } diff --git a/packages/core/schematics/ng-generate/cleanup-unused-imports/index.ts b/packages/core/schematics/ng-generate/cleanup-unused-imports/index.ts index b04e3b2cf01..9f35647d9a4 100644 --- a/packages/core/schematics/ng-generate/cleanup-unused-imports/index.ts +++ b/packages/core/schematics/ng-generate/cleanup-unused-imports/index.ts @@ -28,8 +28,7 @@ export function migrate(): Rule { afterAnalysisFailure: () => { context.logger.error('Schematic failed unexpectedly with no analysis data'); }, - whenDone: (stats) => { - const {removedImports, changedFiles} = stats.counters; + whenDone: ({removedImports, changedFiles}) => { let statsMessage: string; if (removedImports === 0) { diff --git a/packages/core/schematics/ng-generate/cleanup-unused-imports/unused_imports_migration.ts b/packages/core/schematics/ng-generate/cleanup-unused-imports/unused_imports_migration.ts index 924ed5380be..e17a6ebc6f4 100644 --- a/packages/core/schematics/ng-generate/cleanup-unused-imports/unused_imports_migration.ts +++ b/packages/core/schematics/ng-generate/cleanup-unused-imports/unused_imports_migration.ts @@ -10,7 +10,6 @@ import ts from 'typescript'; import { BaseProgramInfo, confirmAsSerializable, - MigrationStats, ProgramInfo, projectFile, ProjectFileID, @@ -163,13 +162,11 @@ export class UnusedImportsMigration extends TsurgeFunnelMigration< return confirmAsSerializable(combinedData); } - override async stats(globalMetadata: CompilationUnitData): Promise { - return { - counters: { - removedImports: globalMetadata.removedIdentifiers.length, - changedFiles: globalMetadata.changedFiles, - }, - }; + override async stats(globalMetadata: CompilationUnitData) { + return confirmAsSerializable({ + removedImports: globalMetadata.removedIdentifiers.length, + changedFiles: globalMetadata.changedFiles, + }); } /** Gets an ID that can be used to look up a node based on its location. */ diff --git a/packages/core/schematics/ng-generate/output-migration/index.ts b/packages/core/schematics/ng-generate/output-migration/index.ts index 9f7c2613c01..9293b4daa5a 100644 --- a/packages/core/schematics/ng-generate/output-migration/index.ts +++ b/packages/core/schematics/ng-generate/output-migration/index.ts @@ -57,8 +57,7 @@ export function migrate(options: Options): Rule { afterAnalysisFailure: () => { context.logger.error('Migration failed unexpectedly with no analysis data'); }, - whenDone: ({counters}) => { - const {detectedOutputs, problematicOutputs, successRate} = counters; + whenDone: ({detectedOutputs, problematicOutputs, successRate}) => { const migratedOutputs = detectedOutputs - problematicOutputs; const successRatePercent = (successRate * 100).toFixed(2); diff --git a/packages/core/schematics/ng-generate/self-closing-tags-migration/index.ts b/packages/core/schematics/ng-generate/self-closing-tags-migration/index.ts index 5b9109b12ff..789852800f6 100644 --- a/packages/core/schematics/ng-generate/self-closing-tags-migration/index.ts +++ b/packages/core/schematics/ng-generate/self-closing-tags-migration/index.ts @@ -46,8 +46,7 @@ export function migrate(options: Options): Rule { afterAnalysisFailure: () => { context.logger.error('Migration failed unexpectedly with no analysis data'); }, - whenDone: ({counters}) => { - const {touchedFilesCount, replacementCount} = counters; + whenDone: ({touchedFilesCount, replacementCount}) => { context.logger.info(''); context.logger.info(`Successfully migrated to self-closing tags 🎉`); context.logger.info( diff --git a/packages/core/schematics/ng-generate/signal-input-migration/index.ts b/packages/core/schematics/ng-generate/signal-input-migration/index.ts index e6a49fd8a1b..8330d732a9d 100644 --- a/packages/core/schematics/ng-generate/signal-input-migration/index.ts +++ b/packages/core/schematics/ng-generate/signal-input-migration/index.ts @@ -61,8 +61,7 @@ export function migrate(options: Options): Rule { afterAnalysisFailure: () => { context.logger.error('Migration failed unexpectedly with no analysis data'); }, - whenDone: ({counters}) => { - const {sourceInputs, incompatibleInputs} = counters; + whenDone: ({sourceInputs, incompatibleInputs}) => { const migratedInputs = sourceInputs - incompatibleInputs; context.logger.info(''); diff --git a/packages/core/schematics/ng-generate/signal-queries-migration/index.ts b/packages/core/schematics/ng-generate/signal-queries-migration/index.ts index b13fa85aa15..ac0b63515bc 100644 --- a/packages/core/schematics/ng-generate/signal-queries-migration/index.ts +++ b/packages/core/schematics/ng-generate/signal-queries-migration/index.ts @@ -61,11 +61,10 @@ export function migrate(options: Options): Rule { context.logger.info(`Processing analysis data between targets...`); context.logger.info(``); }, - whenDone: ({counters}) => { + whenDone: ({queriesCount, incompatibleQueries}) => { context.logger.info(''); context.logger.info(`Successfully migrated to signal queries 🎉`); - const {queriesCount, incompatibleQueries} = counters; const migratedQueries = queriesCount - incompatibleQueries; context.logger.info(''); diff --git a/packages/core/schematics/test/BUILD.bazel b/packages/core/schematics/test/BUILD.bazel index 6d26d3f3f53..d41b8309bcc 100644 --- a/packages/core/schematics/test/BUILD.bazel +++ b/packages/core/schematics/test/BUILD.bazel @@ -32,6 +32,7 @@ jasmine_node_test( "//packages/core/schematics/ng-generate/signals:static_files", "//packages/core/schematics/ng-generate/standalone-migration:static_files", ], + shard_count = 4, deps = [ ":test_lib", "@npm//shelljs", diff --git a/packages/core/schematics/utils/tsurge/base_migration.ts b/packages/core/schematics/utils/tsurge/base_migration.ts index 516d5cf9d9f..f01dbc83e73 100644 --- a/packages/core/schematics/utils/tsurge/base_migration.ts +++ b/packages/core/schematics/utils/tsurge/base_migration.ts @@ -14,15 +14,9 @@ import {BaseProgramInfo, ProgramInfo} from './program_info'; import {Serializable} from './helpers/serializable'; import {createBaseProgramInfo} from './helpers/create_program'; -/** - * Type describing statistics that could be tracked - * by migrations. - * - * Statistics may be tracked depending on the runner. - */ -export interface MigrationStats { - counters: Record; -} +/** Type helper extracting the stats type of a migration. */ +export type MigrationStats = + T extends TsurgeBaseMigration ? Stats : never; /** * @private @@ -32,7 +26,12 @@ export interface MigrationStats { * For example, this class exposes methods to conveniently create * TypeScript programs, while also allowing migration authors to override. */ -export abstract class TsurgeBaseMigration { +export abstract class TsurgeBaseMigration< + UnitAnalysisMetadata, + CombinedGlobalMetadata, + // Note: Even when optional, they can be inferred from implementations. + Stats = unknown, +> { /** * Advanced Tsurge users can override this method, but most of the time, * overriding {@link prepareProgram} is more desirable. @@ -103,5 +102,5 @@ export abstract class TsurgeBaseMigration>; /** Extract statistics based on the global metadata. */ - abstract stats(globalMetadata: CombinedGlobalMetadata): Promise; + abstract stats(globalMetadata: CombinedGlobalMetadata): Promise>; } diff --git a/packages/core/schematics/utils/tsurge/executors/analyze_exec.ts b/packages/core/schematics/utils/tsurge/executors/analyze_exec.ts index 892dece28a7..5f51b302cc6 100644 --- a/packages/core/schematics/utils/tsurge/executors/analyze_exec.ts +++ b/packages/core/schematics/utils/tsurge/executors/analyze_exec.ts @@ -16,7 +16,7 @@ import {Serializable} from '../helpers/serializable'; * @returns the serializable migration unit data. */ export async function executeAnalyzePhase( - migration: TsurgeMigration, + migration: TsurgeMigration, tsconfigAbsolutePath: string, ): Promise> { const baseInfo = migration.createProgram(tsconfigAbsolutePath); diff --git a/packages/core/schematics/utils/tsurge/executors/combine_exec.ts b/packages/core/schematics/utils/tsurge/executors/combine_exec.ts index 09c21477997..bc354e83f29 100644 --- a/packages/core/schematics/utils/tsurge/executors/combine_exec.ts +++ b/packages/core/schematics/utils/tsurge/executors/combine_exec.ts @@ -16,7 +16,7 @@ import {TsurgeMigration} from '../migration'; * @returns the serializable combined unit data. */ export async function executeCombinePhase( - migration: TsurgeMigration, + migration: TsurgeMigration, unitA: UnitData, unitB: UnitData, ): Promise> { diff --git a/packages/core/schematics/utils/tsurge/executors/global_meta_exec.ts b/packages/core/schematics/utils/tsurge/executors/global_meta_exec.ts index 950f0ebcb3a..0566b85ad68 100644 --- a/packages/core/schematics/utils/tsurge/executors/global_meta_exec.ts +++ b/packages/core/schematics/utils/tsurge/executors/global_meta_exec.ts @@ -16,7 +16,7 @@ import {TsurgeMigration} from '../migration'; * @returns the serializable global meta. */ export async function executeGlobalMetaPhase( - migration: TsurgeMigration, + migration: TsurgeMigration, combinedUnitData: UnitData, ): Promise> { return await migration.globalMeta(combinedUnitData); diff --git a/packages/core/schematics/utils/tsurge/executors/migrate_exec.ts b/packages/core/schematics/utils/tsurge/executors/migrate_exec.ts index 3a64e820415..268eba95069 100644 --- a/packages/core/schematics/utils/tsurge/executors/migrate_exec.ts +++ b/packages/core/schematics/utils/tsurge/executors/migrate_exec.ts @@ -21,7 +21,7 @@ import {Replacement} from '../replacement'; * absolute project directory path (to allow for applying). */ export async function executeMigratePhase( - migration: TsurgeMigration, + migration: TsurgeMigration, globalMetadata: GlobalData, tsconfigAbsolutePath: string, ): Promise<{replacements: Replacement[]; projectRoot: AbsoluteFsPath}> { diff --git a/packages/core/schematics/utils/tsurge/helpers/angular_devkit/run_in_devkit.ts b/packages/core/schematics/utils/tsurge/helpers/angular_devkit/run_in_devkit.ts index 8b4f51566e8..8c231969db9 100644 --- a/packages/core/schematics/utils/tsurge/helpers/angular_devkit/run_in_devkit.ts +++ b/packages/core/schematics/utils/tsurge/helpers/angular_devkit/run_in_devkit.ts @@ -27,9 +27,9 @@ export enum MigrationStage { } /** Information necessary to run a Tsurge migration in the devkit. */ -export interface TsurgeDevkitMigration { +export interface TsurgeDevkitMigration { /** Instantiates the migration. */ - getMigration: (fs: FileSystem) => TsurgeMigration; + getMigration: (fs: FileSystem) => TsurgeMigration; /** File tree of the schematic. */ tree: Tree; @@ -53,11 +53,13 @@ export interface TsurgeDevkitMigration { afterAnalysisFailure?: () => void; /** Called when the migration is done running and stats are available. Useful for logging. */ - whenDone?: (stats: MigrationStats) => void; + whenDone?: (stats: Stats) => void; } /** Runs a Tsurge within an Angular Devkit context. */ -export async function runMigrationInDevkit(config: TsurgeDevkitMigration): Promise { +export async function runMigrationInDevkit( + config: TsurgeDevkitMigration, +): Promise { const {buildPaths, testPaths} = await getProjectTsConfigPaths(config.tree); if (!buildPaths.length && !testPaths.length) { diff --git a/packages/core/schematics/utils/tsurge/helpers/combine_units.ts b/packages/core/schematics/utils/tsurge/helpers/combine_units.ts index 2405dfb6a39..10ae8b280dd 100644 --- a/packages/core/schematics/utils/tsurge/helpers/combine_units.ts +++ b/packages/core/schematics/utils/tsurge/helpers/combine_units.ts @@ -16,7 +16,7 @@ import {TsurgeMigration} from '../migration'; * prefer parallel execution of combining via e.g. Beam combiners. */ export async function synchronouslyCombineUnitData( - migration: TsurgeMigration, + migration: TsurgeMigration, unitDatas: UnitData[], ): Promise { if (unitDatas.length === 0) { diff --git a/packages/core/schematics/utils/tsurge/migration.ts b/packages/core/schematics/utils/tsurge/migration.ts index 3787977d921..3a074841d29 100644 --- a/packages/core/schematics/utils/tsurge/migration.ts +++ b/packages/core/schematics/utils/tsurge/migration.ts @@ -39,9 +39,9 @@ interface MigrateResult { * * TODO: Link design doc */ -export type TsurgeMigration = - | TsurgeComplexMigration - | TsurgeFunnelMigration; +export type TsurgeMigration = + | TsurgeComplexMigration + | TsurgeFunnelMigration; /** * A simpler variant of a {@link TsurgeComplexMigration} that does not @@ -58,7 +58,8 @@ export type TsurgeMigration = export abstract class TsurgeFunnelMigration< UnitAnalysisMetadata, CombinedGlobalMetadata, -> extends TsurgeBaseMigration { + Stats = unknown, +> extends TsurgeBaseMigration { /** * Finalizes the migration result. * @@ -83,7 +84,8 @@ export abstract class TsurgeFunnelMigration< export abstract class TsurgeComplexMigration< UnitAnalysisMetadata, CombinedGlobalMetadata, -> extends TsurgeBaseMigration { + Stats = unknown, +> extends TsurgeBaseMigration { /** * Migration phase. Workers will be started for every compilation unit again, * instantiating a new program for every unit to compute the final migration diff --git a/packages/core/schematics/utils/tsurge/test/output_migration.spec.ts b/packages/core/schematics/utils/tsurge/test/output_migration.spec.ts index 7ad37460af5..39a766243ab 100644 --- a/packages/core/schematics/utils/tsurge/test/output_migration.spec.ts +++ b/packages/core/schematics/utils/tsurge/test/output_migration.spec.ts @@ -97,10 +97,8 @@ describe('output migration', () => { ]); expect(await getStatistics()).toEqual({ - counters: { - allOutputs: 2, - migratedOutputs: 1, - }, + allOutputs: 2, + migratedOutputs: 1, }); }); }); diff --git a/packages/core/schematics/utils/tsurge/test/output_migration.ts b/packages/core/schematics/utils/tsurge/test/output_migration.ts index 24f6d4be6ac..00296110d75 100644 --- a/packages/core/schematics/utils/tsurge/test/output_migration.ts +++ b/packages/core/schematics/utils/tsurge/test/output_migration.ts @@ -14,7 +14,6 @@ import {ProgramInfo} from '../program_info'; import {Replacement, TextUpdate} from '../replacement'; import {findOutputDeclarationsAndReferences, OutputID} from './output_helpers'; import {projectFile} from '../project_paths'; -import {MigrationStats} from '../base_migration'; type AnalysisUnit = {[id: OutputID]: {seenProblematicUsage: boolean}}; type GlobalMetadata = {[id: OutputID]: {canBeMigrated: boolean}}; @@ -120,7 +119,7 @@ export class OutputMigration extends TsurgeComplexMigration { + override async stats(globalMetadata: GlobalMetadata) { let allOutputs = 0; let migratedOutputs = 0; @@ -131,11 +130,9 @@ export class OutputMigration extends TsurgeComplexMigration( - migration: TsurgeMigration, +export async function runTsurgeMigration( + migration: TsurgeMigration, files: {name: AbsoluteFsPath; contents: string; isProgramRootFile?: boolean}[], compilerOptions: ts.CompilerOptions = {}, -): Promise { +): Promise> { const mockFs = getFileSystem(); if (!(mockFs instanceof MockFileSystem)) { throw new Error('Expected a mock file system for `runTsurgeMigration`.'); diff --git a/packages/core/schematics/utils/tsurge/testing/test_run.ts b/packages/core/schematics/utils/tsurge/testing/test_run.ts index 45a0eebfe0d..3be526ce6f9 100644 --- a/packages/core/schematics/utils/tsurge/testing/test_run.ts +++ b/packages/core/schematics/utils/tsurge/testing/test_run.ts @@ -7,12 +7,11 @@ */ import {MockFileSystem} from '../../../../../compiler-cli/src/ngtsc/file_system/testing'; -import {MigrationStats} from '../base_migration'; /** Type describing results of a Tsurge migration test run. */ -export interface TestRun { +export interface TestRun { /** File system that can be used to read migrated file contents. */ fs: MockFileSystem; /** Function that can be invoked to compute migration statistics. */ - getStatistics: () => Promise; + getStatistics: () => Promise; }