6
6
* found in the LICENSE file at https://angular.io/license
7
7
*/
8
8
9
- import { Migration , TargetVersion } from '@angular/cdk/schematics' ;
10
9
import * as ts from 'typescript' ;
10
+ import * as postcss from 'postcss' ;
11
+ import * as scss from 'postcss-scss' ;
12
+
13
+ import { MIXINS } from './constants' ;
14
+
15
+ import { Migration , ResolvedResource , TargetVersion , WorkspacePath } from '@angular/cdk/schematics' ;
11
16
12
17
export class LegacyComponentsMigration extends Migration < null > {
13
18
enabled = this . targetVersion === TargetVersion . V15 ;
14
19
20
+ override visitStylesheet ( stylesheet : ResolvedResource ) : void {
21
+ let namespace : string | undefined = undefined ;
22
+ const processor = new postcss . Processor ( [
23
+ {
24
+ postcssPlugin : 'legacy-components-v15-plugin' ,
25
+ AtRule : {
26
+ use : node => {
27
+ namespace = namespace ?? this . _parseSassNamespace ( node ) ;
28
+ } ,
29
+ include : node => this . _handleAtInclude ( node , stylesheet . filePath , namespace ) ,
30
+ } ,
31
+ } ,
32
+ ] ) ;
33
+ processor . process ( stylesheet . content , { syntax : scss } ) . sync ( ) ;
34
+ }
35
+
36
+ /** Returns the namespace of the given at-rule if it is importing from @angular/material. */
37
+ private _parseSassNamespace ( node : postcss . AtRule ) : string | undefined {
38
+ if ( node . params . startsWith ( '@angular/material' , 1 ) ) {
39
+ return node . params . split ( / \s + / ) . pop ( ) ;
40
+ }
41
+ return ;
42
+ }
43
+
44
+ /** Handles updating the at-include rules of legacy component mixins. */
45
+ private _handleAtInclude (
46
+ node : postcss . AtRule ,
47
+ filePath : WorkspacePath ,
48
+ namespace ?: string ,
49
+ ) : void {
50
+ if ( ! namespace || ! node . source ?. start ) {
51
+ return ;
52
+ }
53
+ if ( this . _isLegacyMixin ( node , namespace ) ) {
54
+ this . _replaceAt ( filePath , node . source . start . offset , {
55
+ old : `${ namespace } .` ,
56
+ new : `${ namespace } .legacy-` ,
57
+ } ) ;
58
+ }
59
+ }
60
+
61
+ /** Returns true if the given at-include rule is a use of a legacy component mixin. */
62
+ private _isLegacyMixin ( node : postcss . AtRule , namespace : string ) : boolean {
63
+ for ( let i = 0 ; i < MIXINS . length ; i ++ ) {
64
+ if ( node . params . startsWith ( `${ namespace } .${ MIXINS [ i ] } ` ) ) {
65
+ return true ;
66
+ }
67
+ }
68
+ return false ;
69
+ }
70
+
15
71
override visitNode ( node : ts . Node ) : void {
16
72
if ( ts . isImportDeclaration ( node ) ) {
17
73
this . _handleImportDeclaration ( node ) ;
@@ -40,7 +96,7 @@ export class LegacyComponentsMigration extends Migration<null> {
40
96
const newExport = n . propertyName
41
97
? `MatLegacy${ suffix } `
42
98
: `MatLegacy${ suffix } : Mat${ suffix } ` ;
43
- this . _replaceAt ( name , { old : oldExport , new : newExport } ) ;
99
+ this . _tsReplaceAt ( name , { old : oldExport , new : newExport } ) ;
44
100
}
45
101
}
46
102
}
@@ -49,7 +105,7 @@ export class LegacyComponentsMigration extends Migration<null> {
49
105
private _handleImportDeclaration ( node : ts . ImportDeclaration ) : void {
50
106
const moduleSpecifier = node . moduleSpecifier as ts . StringLiteral ;
51
107
if ( moduleSpecifier . text . startsWith ( '@angular/material/' ) ) {
52
- this . _replaceAt ( node , { old : '@angular/material/' , new : '@angular/material/legacy-' } ) ;
108
+ this . _tsReplaceAt ( node , { old : '@angular/material/' , new : '@angular/material/legacy-' } ) ;
53
109
54
110
if ( node . importClause ?. namedBindings && ts . isNamedImports ( node . importClause . namedBindings ) ) {
55
111
this . _handleNamedImportBindings ( node . importClause . namedBindings ) ;
@@ -61,7 +117,7 @@ export class LegacyComponentsMigration extends Migration<null> {
61
117
private _handleImportExpression ( node : ts . CallExpression ) : void {
62
118
const moduleSpecifier = node . arguments [ 0 ] as ts . StringLiteral ;
63
119
if ( moduleSpecifier . text . startsWith ( '@angular/material/' ) ) {
64
- this . _replaceAt ( node , { old : '@angular/material/' , new : '@angular/material/legacy-' } ) ;
120
+ this . _tsReplaceAt ( node , { old : '@angular/material/' , new : '@angular/material/legacy-' } ) ;
65
121
}
66
122
}
67
123
@@ -75,7 +131,7 @@ export class LegacyComponentsMigration extends Migration<null> {
75
131
const newExport = n . propertyName
76
132
? `MatLegacy${ suffix } `
77
133
: `MatLegacy${ suffix } as Mat${ suffix } ` ;
78
- this . _replaceAt ( name , { old : oldExport , new : newExport } ) ;
134
+ this . _tsReplaceAt ( name , { old : oldExport , new : newExport } ) ;
79
135
}
80
136
}
81
137
@@ -108,10 +164,19 @@ export class LegacyComponentsMigration extends Migration<null> {
108
164
) ;
109
165
}
110
166
111
- /** Updates the source file of the given node with the given replacements. */
112
- private _replaceAt ( node : ts . Node , str : { old : string ; new : string } ) : void {
167
+ /** Updates the source file of the given ts node with the given replacements. */
168
+ private _tsReplaceAt ( node : ts . Node , str : { old : string ; new : string } ) : void {
113
169
const filePath = this . fileSystem . resolve ( node . getSourceFile ( ) . fileName ) ;
114
- const index = this . fileSystem . read ( filePath ) ! . indexOf ( str . old , node . pos ) ;
170
+ this . _replaceAt ( filePath , node . pos , str ) ;
171
+ }
172
+
173
+ /** Updates the source file with the given replacements. */
174
+ private _replaceAt (
175
+ filePath : WorkspacePath ,
176
+ offset : number ,
177
+ str : { old : string ; new : string } ,
178
+ ) : void {
179
+ const index = this . fileSystem . read ( filePath ) ! . indexOf ( str . old , offset ) ;
115
180
this . fileSystem . edit ( filePath ) . remove ( index , str . old . length ) . insertRight ( index , str . new ) ;
116
181
}
117
182
}
0 commit comments