66 * found in the LICENSE file at https://angular.dev/license
77 */
88
9- import { Rule , SchematicsException } from '@angular-devkit/schematics' ;
10-
11- import { getProjectTsConfigPaths } from '../../utils/project_tsconfig_paths' ;
12- import { DevkitMigrationFilesystem } from '../../utils/tsurge/helpers/angular_devkit/devkit_filesystem' ;
13- import { groupReplacementsByFile } from '../../utils/tsurge/helpers/group_replacements' ;
14- import { setFileSystem } from '@angular/compiler-cli/src/ngtsc/file_system' ;
15- import {
16- CompilationUnitData ,
17- OutputMigration ,
18- } from '../../migrations/output-migration/output-migration' ;
19- import { ProjectRootRelativePath , TextUpdate } from '../../utils/tsurge' ;
20- import { synchronouslyCombineUnitData } from '../../utils/tsurge/helpers/combine_units' ;
9+ import { Rule } from '@angular-devkit/schematics' ;
10+ import { OutputMigration } from '../../migrations/output-migration/output-migration' ;
11+ import { runMigrationInDevkit } from '../../utils/tsurge/helpers/angular_devkit' ;
2112
2213interface Options {
2314 path : string ;
@@ -26,99 +17,53 @@ interface Options {
2617
2718export function migrate ( options : Options ) : Rule {
2819 return async ( tree , context ) => {
29- const { buildPaths, testPaths} = await getProjectTsConfigPaths ( tree ) ;
30-
31- if ( ! buildPaths . length && ! testPaths . length ) {
32- throw new SchematicsException (
33- 'Could not find any tsconfig file. Cannot run output migration.' ,
34- ) ;
35- }
36-
37- const fs = new DevkitMigrationFilesystem ( tree ) ;
38- setFileSystem ( fs ) ;
39-
40- const migration = new OutputMigration ( {
41- shouldMigrate : ( _ , file ) => {
42- return (
43- file . rootRelativePath . startsWith ( fs . normalize ( options . path ) ) &&
44- ! / ( ^ | \/ ) n o d e _ m o d u l e s \/ / . test ( file . rootRelativePath )
45- ) ;
20+ await runMigrationInDevkit ( {
21+ tree,
22+ getMigration : ( fs ) =>
23+ new OutputMigration ( {
24+ shouldMigrate : ( _ , file ) => {
25+ return (
26+ file . rootRelativePath . startsWith ( fs . normalize ( options . path ) ) &&
27+ ! / ( ^ | \/ ) n o d e _ m o d u l e s \/ / . test ( file . rootRelativePath )
28+ ) ;
29+ } ,
30+ } ) ,
31+ beforeProgramCreation : ( tsconfigPath ) => {
32+ context . logger . info ( `Preparing analysis for: ${ tsconfigPath } ...` ) ;
4633 } ,
47- } ) ;
48-
49- const analysisPath = fs . resolve ( options . analysisDir ) ;
50- const unitResults : CompilationUnitData [ ] = [ ] ;
51- const programInfos = [ ...buildPaths , ...testPaths ] . map ( ( tsconfigPath ) => {
52- context . logger . info ( `Preparing analysis for: ${ tsconfigPath } ..` ) ;
53-
54- const baseInfo = migration . createProgram ( tsconfigPath , fs ) ;
55- const info = migration . prepareProgram ( baseInfo ) ;
56-
57- // Support restricting the analysis to subfolders for larger projects.
58- if ( analysisPath !== '/' ) {
59- info . sourceFiles = info . sourceFiles . filter ( ( sf ) => sf . fileName . startsWith ( analysisPath ) ) ;
60- info . fullProgramSourceFiles = info . fullProgramSourceFiles . filter ( ( sf ) =>
61- sf . fileName . startsWith ( analysisPath ) ,
34+ afterProgramCreation : ( info , fs ) => {
35+ const analysisPath = fs . resolve ( options . analysisDir ) ;
36+
37+ // Support restricting the analysis to subfolders for larger projects.
38+ if ( analysisPath !== '/' ) {
39+ info . sourceFiles = info . sourceFiles . filter ( ( sf ) => sf . fileName . startsWith ( analysisPath ) ) ;
40+ info . fullProgramSourceFiles = info . fullProgramSourceFiles . filter ( ( sf ) =>
41+ sf . fileName . startsWith ( analysisPath ) ,
42+ ) ;
43+ }
44+ } ,
45+ beforeUnitAnalysis : ( tsconfigPath ) => {
46+ context . logger . info ( `Scanning for outputs: ${ tsconfigPath } ...` ) ;
47+ } ,
48+ afterAllAnalyzed : ( ) => {
49+ context . logger . info ( `` ) ;
50+ context . logger . info ( `Processing analysis data between targets...` ) ;
51+ context . logger . info ( `` ) ;
52+ } ,
53+ afterAnalysisFailure : ( ) => {
54+ context . logger . error ( 'Migration failed unexpectedly with no analysis data' ) ;
55+ } ,
56+ whenDone : ( { counters} ) => {
57+ const { detectedOutputs, problematicOutputs, successRate} = counters ;
58+ const migratedOutputs = detectedOutputs - problematicOutputs ;
59+ const successRatePercent = ( successRate * 100 ) . toFixed ( 2 ) ;
60+
61+ context . logger . info ( '' ) ;
62+ context . logger . info ( `Successfully migrated to outputs as functions 🎉` ) ;
63+ context . logger . info (
64+ ` -> Migrated ${ migratedOutputs } out of ${ detectedOutputs } detected outputs (${ successRatePercent } %).` ,
6265 ) ;
63- }
64-
65- return { info, tsconfigPath} ;
66+ } ,
6667 } ) ;
67-
68- // Analyze phase. Treat all projects as compilation units as
69- // this allows us to support references between those.
70- for ( const { info, tsconfigPath} of programInfos ) {
71- context . logger . info ( `Scanning for outputs: ${ tsconfigPath } ..` ) ;
72- unitResults . push ( await migration . analyze ( info ) ) ;
73- }
74-
75- context . logger . info ( `` ) ;
76- context . logger . info ( `Processing analysis data between targets..` ) ;
77- context . logger . info ( `` ) ;
78-
79- const combined = await synchronouslyCombineUnitData ( migration , unitResults ) ;
80- if ( combined === null ) {
81- context . logger . error ( 'Migration failed unexpectedly with no analysis data' ) ;
82- return ;
83- }
84-
85- const globalMeta = await migration . globalMeta ( combined ) ;
86- const replacementsPerFile : Map < ProjectRootRelativePath , TextUpdate [ ] > = new Map ( ) ;
87-
88- for ( const { info, tsconfigPath} of programInfos ) {
89- context . logger . info ( `Migrating: ${ tsconfigPath } ..` ) ;
90-
91- const { replacements} = await migration . migrate ( globalMeta ) ;
92- const changesPerFile = groupReplacementsByFile ( replacements ) ;
93-
94- for ( const [ file , changes ] of changesPerFile ) {
95- if ( ! replacementsPerFile . has ( file ) ) {
96- replacementsPerFile . set ( file , changes ) ;
97- }
98- }
99- }
100-
101- context . logger . info ( `Applying changes..` ) ;
102- for ( const [ file , changes ] of replacementsPerFile ) {
103- const recorder = tree . beginUpdate ( file ) ;
104- for ( const c of changes ) {
105- recorder
106- . remove ( c . data . position , c . data . end - c . data . position )
107- . insertLeft ( c . data . position , c . data . toInsert ) ;
108- }
109- tree . commitUpdate ( recorder ) ;
110- }
111-
112- const {
113- counters : { detectedOutputs, problematicOutputs, successRate} ,
114- } = await migration . stats ( globalMeta ) ;
115- const migratedOutputs = detectedOutputs - problematicOutputs ;
116- const successRatePercent = ( successRate * 100 ) . toFixed ( 2 ) ;
117-
118- context . logger . info ( '' ) ;
119- context . logger . info ( `Successfully migrated to outputs as functions 🎉` ) ;
120- context . logger . info (
121- ` -> Migrated ${ migratedOutputs } out of ${ detectedOutputs } detected outputs (${ successRatePercent } %).` ,
122- ) ;
12368 } ;
12469}
0 commit comments