66 * found in the LICENSE file at https://angular.io/license
77 */
88
9- import { Migration , TargetVersion } from '@angular/cdk/schematics' ;
109import * 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' ;
1116
1217export class LegacyComponentsMigration extends Migration < null > {
1318 enabled = this . targetVersion === TargetVersion . V15 ;
1419
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+
1571 override visitNode ( node : ts . Node ) : void {
1672 if ( ts . isImportDeclaration ( node ) ) {
1773 this . _handleImportDeclaration ( node ) ;
@@ -40,7 +96,7 @@ export class LegacyComponentsMigration extends Migration<null> {
4096 const newExport = n . propertyName
4197 ? `MatLegacy${ suffix } `
4298 : `MatLegacy${ suffix } : Mat${ suffix } ` ;
43- this . _replaceAt ( name , { old : oldExport , new : newExport } ) ;
99+ this . _tsReplaceAt ( name , { old : oldExport , new : newExport } ) ;
44100 }
45101 }
46102 }
@@ -49,7 +105,7 @@ export class LegacyComponentsMigration extends Migration<null> {
49105 private _handleImportDeclaration ( node : ts . ImportDeclaration ) : void {
50106 const moduleSpecifier = node . moduleSpecifier as ts . StringLiteral ;
51107 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-' } ) ;
53109
54110 if ( node . importClause ?. namedBindings && ts . isNamedImports ( node . importClause . namedBindings ) ) {
55111 this . _handleNamedImportBindings ( node . importClause . namedBindings ) ;
@@ -61,7 +117,7 @@ export class LegacyComponentsMigration extends Migration<null> {
61117 private _handleImportExpression ( node : ts . CallExpression ) : void {
62118 const moduleSpecifier = node . arguments [ 0 ] as ts . StringLiteral ;
63119 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-' } ) ;
65121 }
66122 }
67123
@@ -75,7 +131,7 @@ export class LegacyComponentsMigration extends Migration<null> {
75131 const newExport = n . propertyName
76132 ? `MatLegacy${ suffix } `
77133 : `MatLegacy${ suffix } as Mat${ suffix } ` ;
78- this . _replaceAt ( name , { old : oldExport , new : newExport } ) ;
134+ this . _tsReplaceAt ( name , { old : oldExport , new : newExport } ) ;
79135 }
80136 }
81137
@@ -108,10 +164,19 @@ export class LegacyComponentsMigration extends Migration<null> {
108164 ) ;
109165 }
110166
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 {
113169 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 ) ;
115180 this . fileSystem . edit ( filePath ) . remove ( index , str . old . length ) . insertRight ( index , str . new ) ;
116181 }
117182}
0 commit comments