Skip to content

Commit 307a37b

Browse files
committed
feat(migrations): add migration to convert ngStyle to use style
Add migration to convert ngStyle to use style
1 parent 85994fb commit 307a37b

15 files changed

Lines changed: 1541 additions & 1 deletion

File tree

adev/src/app/routing/sub-navigation-data.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,6 +1497,12 @@ const REFERENCE_SUB_NAVIGATION_DATA: NavigationItem[] = [
14971497
contentPath: 'reference/migrations/ngclass-to-class',
14981498
status: 'new',
14991499
},
1500+
{
1501+
label: 'NgStyle to Style',
1502+
path: 'reference/migrations/ngstyle-to-style',
1503+
contentPath: 'reference/migrations/ngstyle-to-style',
1504+
status: 'new',
1505+
},
15001506
],
15011507
},
15021508
];
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Migration from NgStyle to style bindings
2+
3+
This schematic migrates NgStyle directive usages to style bindings in your application.
4+
It will only migrate usages that are considered safe to migrate.
5+
6+
Run the schematic using the following command:
7+
8+
```bash
9+
ng generate @angular/core:ngstyle-to-style
10+
```
11+
12+
13+
#### Before
14+
15+
```html
16+
<div [ngStyle]="{'background-color': 'red'}">
17+
```
18+
19+
20+
#### After
21+
22+
```html
23+
<div [style]="{'background-color': 'red'}">
24+
```
25+
26+
## Configuration options
27+
28+
The migration supports a few options for fine tuning the migration to your specific needs.
29+
30+
### `--best-effort-mode`
31+
32+
By default, the migration avoids migrating object references usages of `NgStyle`
33+
When the `--best-effort-mode` flag is enabled, `ngStyle` instances binded to object references are also migrated.
34+
This can be unsafe to migrate, for example if the binded object is mutated.
35+
36+
37+
```html
38+
<div [ngStyle]="styleObject"></div>
39+
```
40+
41+
to
42+
43+
```html
44+
<div [style]="styleObject"></div>
45+
```

adev/src/content/reference/migrations/overview.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,7 @@ Learn about how you can migrate your existing angular project to the latest feat
3333
<docs-card title="NgClass to Class Bindings" link="Migrate now" href="reference/migrations/ngclass-to-class">
3434
Convert component templates to prefer class bindings over the `NgClass` directives when possible.
3535
</docs-card>
36+
<docs-card title="NgStyle to Style Bindings" link="Migrate now" href="reference/migrations/ngstyle-to-style">
37+
Convert component templates to prefer style bindings over the `NgStyle` directives when possible.
38+
</docs-card>
3639
</docs-card-container>

packages/core/schematics/BUILD.bazel

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ npm_package(
4444
":bundles",
4545
"//packages/core/schematics/migrations/control-flow-migration:static_files",
4646
"//packages/core/schematics/migrations/ngclass-to-class-migration:static_files",
47+
"//packages/core/schematics/migrations/ngstyle-to-style-migration:static_files",
4748
"//packages/core/schematics/ng-generate/cleanup-unused-imports:static_files",
4849
"//packages/core/schematics/ng-generate/inject-migration:static_files",
4950
"//packages/core/schematics/ng-generate/output-migration:static_files",
@@ -102,6 +103,10 @@ bundle_entrypoints = [
102103
"ngclass-to-class-migration",
103104
"packages/core/schematics/migrations/ngclass-to-class-migration/index.js",
104105
],
106+
[
107+
"ngstyle-to-style-migration",
108+
"packages/core/schematics/migrations/ngstyle-to-style-migration/index.js",
109+
],
105110
[
106111
"router-current-navigation",
107112
"packages/core/schematics/migrations/router-current-navigation/index.js",
@@ -133,6 +138,7 @@ rollup.rollup(
133138
"//packages/core/schematics/migrations/application-config-core",
134139
"//packages/core/schematics/migrations/control-flow-migration",
135140
"//packages/core/schematics/migrations/ngclass-to-class-migration",
141+
"//packages/core/schematics/migrations/ngstyle-to-style-migration",
136142
"//packages/core/schematics/migrations/router-current-navigation",
137143
"//packages/core/schematics/migrations/router-last-successful-navigation",
138144
"//packages/core/schematics/ng-generate/cleanup-unused-imports",

packages/core/schematics/collection.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,12 @@
6363
"factory": "./bundles/ngclass-to-class-migration.cjs#migrate",
6464
"schema": "./migrations/ngclass-to-class-migration/schema.json",
6565
"aliases": ["ngclass-to-class"]
66+
},
67+
"ngstyle-to-style-migration": {
68+
"description": "Updates usages of `ngStyle` directives to the `style` bindings where possible",
69+
"factory": "./bundles/ngstyle-to-style-migration.cjs#migrate",
70+
"schema": "./migrations/ngstyle-to-style-migration/schema.json",
71+
"aliases": ["ngstyle-to-style"]
6672
}
6773
}
6874
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
load("//tools:defaults.bzl", "copy_to_bin", "ts_project")
2+
3+
package(
4+
default_visibility = [
5+
"//packages/core/schematics:__pkg__",
6+
"//packages/core/schematics/test:__pkg__",
7+
],
8+
)
9+
10+
copy_to_bin(
11+
name = "static_files",
12+
srcs = ["schema.json"],
13+
)
14+
15+
ts_project(
16+
name = "ngstyle-to-style-migration",
17+
srcs = glob(["**/*.ts"]),
18+
data = ["schema.json"],
19+
deps = [
20+
"//:node_modules/@angular-devkit/schematics",
21+
"//:node_modules/@types/node",
22+
"//:node_modules/typescript",
23+
"//packages/compiler",
24+
"//packages/compiler-cli",
25+
"//packages/compiler-cli/private",
26+
"//packages/compiler-cli/src/ngtsc/annotations",
27+
"//packages/compiler-cli/src/ngtsc/annotations/directive",
28+
"//packages/compiler-cli/src/ngtsc/file_system",
29+
"//packages/compiler-cli/src/ngtsc/imports",
30+
"//packages/compiler-cli/src/ngtsc/metadata",
31+
"//packages/compiler-cli/src/ngtsc/reflection",
32+
"//packages/core/schematics/utils",
33+
"//packages/core/schematics/utils/tsurge",
34+
"//packages/core/schematics/utils/tsurge/helpers/angular_devkit",
35+
],
36+
)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
# ngStyle to style migration
3+
This schematic helps developers to convert ngStyle directive usages to style bindings where possible.
4+
5+
## How to run this migration?
6+
The migration can be run using the following command:
7+
8+
```bash
9+
ng generate @angular/core:ngstyle-to-style
10+
```
11+
12+
By default, the migration will go over the entire application. If you want to apply this migration to a subset of the files, you can pass the path argument as shown below:
13+
14+
```bash
15+
ng generate @angular/core:ngstyle-to-style --path src/app/sub-component
16+
```
17+
18+
### How does it work?
19+
The schematic will attempt to find all the places in the templates where the directive is used and check if it can be converted to [style].
20+
21+
Example:
22+
23+
```html
24+
<!-- Before -->
25+
<div [ngStyle]="{'background-color': 'red'}">
26+
27+
<!-- After -->
28+
<div [style]="{'background-color': 'red'}">
29+
```
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
import {Rule} from '@angular-devkit/schematics';
10+
import {NgStyleMigration} from './ngstyle-to-style-migration';
11+
import {MigrationStage, runMigrationInDevkit} from '../../utils/tsurge/helpers/angular_devkit';
12+
13+
interface Options {
14+
path: string;
15+
analysisDir: string;
16+
bestEffortMode?: boolean;
17+
}
18+
19+
export function migrate(options: Options): Rule {
20+
return async (tree, context) => {
21+
await runMigrationInDevkit({
22+
tree,
23+
getMigration: (fs) =>
24+
new NgStyleMigration({
25+
bestEffortMode: options.bestEffortMode,
26+
shouldMigrate: (file) => {
27+
return (
28+
file.rootRelativePath.startsWith(fs.normalize(options.path)) &&
29+
!/(^|\/)node_modules\//.test(file.rootRelativePath)
30+
);
31+
},
32+
}),
33+
beforeProgramCreation: (tsconfigPath: string, stage: MigrationStage) => {
34+
if (stage === MigrationStage.Analysis) {
35+
context.logger.info(`Preparing analysis for: ${tsconfigPath}...`);
36+
} else {
37+
context.logger.info(`Running migration for: ${tsconfigPath}...`);
38+
}
39+
},
40+
beforeUnitAnalysis: (tsconfigPath: string) => {
41+
context.logger.info(`Scanning for component tags: ${tsconfigPath}...`);
42+
},
43+
afterAllAnalyzed: () => {
44+
context.logger.info(``);
45+
context.logger.info(`Processing analysis data between targets...`);
46+
context.logger.info(``);
47+
},
48+
afterAnalysisFailure: () => {
49+
context.logger.error('Migration failed unexpectedly with no analysis data');
50+
},
51+
whenDone: ({
52+
touchedFilesCount,
53+
replacementCount,
54+
}: {
55+
touchedFilesCount: number;
56+
replacementCount: number;
57+
}) => {
58+
context.logger.info('');
59+
context.logger.info(`Successfully migrated to style bindings from ngStyle 🎉`);
60+
context.logger.info(
61+
` -> Migrated ${replacementCount} ngStyle to style bindings in ${touchedFilesCount} files.`,
62+
);
63+
},
64+
});
65+
};
66+
}

0 commit comments

Comments
 (0)