6
6
* found in the LICENSE file at https://angular.dev/license
7
7
*/
8
8
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' ;
21
12
22
13
interface Options {
23
14
path : string ;
@@ -26,99 +17,53 @@ interface Options {
26
17
27
18
export function migrate ( options : Options ) : Rule {
28
19
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 } ...` ) ;
46
33
} ,
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 } %).` ,
62
65
) ;
63
- }
64
-
65
- return { info, tsconfigPath} ;
66
+ } ,
66
67
} ) ;
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
- ) ;
123
68
} ;
124
69
}
0 commit comments