Skip to content

Commit b742a8c

Browse files
timdeschryverbrandonroberts
authored andcommitted
feat(router-store): add migration to add the default serializer (#2291)
1 parent b8a769a commit b742a8c

File tree

32 files changed

+797
-184
lines changed

32 files changed

+797
-184
lines changed

modules/data/schematics-core/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export {
2323
addImportToModule,
2424
addProviderToModule,
2525
replaceImport,
26+
containsProperty,
2627
} from './utility/ast-utils';
2728

2829
export {
@@ -76,4 +77,4 @@ export { addPackageToPackageJson } from './utility/package';
7677

7778
export { platformVersion } from './utility/libs-version';
7879

79-
export { visitTSSourceFiles } from './utility/visit-utils';
80+
export { visitTSSourceFiles, visitNgModuleImports } from './utility/visitors';

modules/data/schematics-core/utility/ast-utils.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,3 +729,18 @@ export function replaceImport(
729729

730730
return changes.reduce((imports, curr) => imports.concat(curr), []);
731731
}
732+
733+
export function containsProperty(
734+
objectLiteral: ts.ObjectLiteralExpression,
735+
propertyName: string
736+
) {
737+
return (
738+
objectLiteral &&
739+
objectLiteral.properties.some(
740+
prop =>
741+
ts.isPropertyAssignment(prop) &&
742+
ts.isIdentifier(prop.name) &&
743+
prop.name.text === propertyName
744+
)
745+
);
746+
}

modules/data/schematics-core/utility/visit-utils.ts renamed to modules/data/schematics-core/utility/visitors.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,31 @@ function* visit(directory: DirEntry): IterableIterator<ts.SourceFile> {
4242
yield* visit(directory.dir(path));
4343
}
4444
}
45+
46+
export function visitNgModuleImports(
47+
sourceFile: ts.SourceFile,
48+
callback: (
49+
importNode: ts.PropertyAssignment,
50+
elementExpressions: ts.NodeArray<ts.Expression>
51+
) => void
52+
) {
53+
ts.forEachChild(sourceFile, function findDecorator(node) {
54+
if (!ts.isDecorator(node)) {
55+
ts.forEachChild(node, findDecorator);
56+
return;
57+
}
58+
59+
ts.forEachChild(node, function findImportsNode(n) {
60+
if (
61+
ts.isPropertyAssignment(n) &&
62+
ts.isArrayLiteralExpression(n.initializer) &&
63+
ts.isIdentifier(n.name) &&
64+
n.name.text === 'imports'
65+
) {
66+
callback(n, n.initializer.elements);
67+
}
68+
69+
ts.forEachChild(n, findImportsNode);
70+
});
71+
});
72+
}

modules/effects/schematics-core/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export {
2323
addImportToModule,
2424
addProviderToModule,
2525
replaceImport,
26+
containsProperty,
2627
} from './utility/ast-utils';
2728

2829
export {
@@ -76,4 +77,4 @@ export { addPackageToPackageJson } from './utility/package';
7677

7778
export { platformVersion } from './utility/libs-version';
7879

79-
export { visitTSSourceFiles } from './utility/visit-utils';
80+
export { visitTSSourceFiles, visitNgModuleImports } from './utility/visitors';

modules/effects/schematics-core/utility/ast-utils.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,3 +729,18 @@ export function replaceImport(
729729

730730
return changes.reduce((imports, curr) => imports.concat(curr), []);
731731
}
732+
733+
export function containsProperty(
734+
objectLiteral: ts.ObjectLiteralExpression,
735+
propertyName: string
736+
) {
737+
return (
738+
objectLiteral &&
739+
objectLiteral.properties.some(
740+
prop =>
741+
ts.isPropertyAssignment(prop) &&
742+
ts.isIdentifier(prop.name) &&
743+
prop.name.text === propertyName
744+
)
745+
);
746+
}

modules/effects/schematics-core/utility/visit-utils.ts renamed to modules/effects/schematics-core/utility/visitors.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,31 @@ function* visit(directory: DirEntry): IterableIterator<ts.SourceFile> {
4242
yield* visit(directory.dir(path));
4343
}
4444
}
45+
46+
export function visitNgModuleImports(
47+
sourceFile: ts.SourceFile,
48+
callback: (
49+
importNode: ts.PropertyAssignment,
50+
elementExpressions: ts.NodeArray<ts.Expression>
51+
) => void
52+
) {
53+
ts.forEachChild(sourceFile, function findDecorator(node) {
54+
if (!ts.isDecorator(node)) {
55+
ts.forEachChild(node, findDecorator);
56+
return;
57+
}
58+
59+
ts.forEachChild(node, function findImportsNode(n) {
60+
if (
61+
ts.isPropertyAssignment(n) &&
62+
ts.isArrayLiteralExpression(n.initializer) &&
63+
ts.isIdentifier(n.name) &&
64+
n.name.text === 'imports'
65+
) {
66+
callback(n, n.initializer.elements);
67+
}
68+
69+
ts.forEachChild(n, findImportsNode);
70+
});
71+
});
72+
}

modules/entity/schematics-core/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export {
2323
addImportToModule,
2424
addProviderToModule,
2525
replaceImport,
26+
containsProperty,
2627
} from './utility/ast-utils';
2728

2829
export {
@@ -76,4 +77,4 @@ export { addPackageToPackageJson } from './utility/package';
7677

7778
export { platformVersion } from './utility/libs-version';
7879

79-
export { visitTSSourceFiles } from './utility/visit-utils';
80+
export { visitTSSourceFiles, visitNgModuleImports } from './utility/visitors';

modules/entity/schematics-core/utility/ast-utils.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,3 +729,18 @@ export function replaceImport(
729729

730730
return changes.reduce((imports, curr) => imports.concat(curr), []);
731731
}
732+
733+
export function containsProperty(
734+
objectLiteral: ts.ObjectLiteralExpression,
735+
propertyName: string
736+
) {
737+
return (
738+
objectLiteral &&
739+
objectLiteral.properties.some(
740+
prop =>
741+
ts.isPropertyAssignment(prop) &&
742+
ts.isIdentifier(prop.name) &&
743+
prop.name.text === propertyName
744+
)
745+
);
746+
}

modules/entity/schematics-core/utility/visit-utils.ts renamed to modules/entity/schematics-core/utility/visitors.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,31 @@ function* visit(directory: DirEntry): IterableIterator<ts.SourceFile> {
4242
yield* visit(directory.dir(path));
4343
}
4444
}
45+
46+
export function visitNgModuleImports(
47+
sourceFile: ts.SourceFile,
48+
callback: (
49+
importNode: ts.PropertyAssignment,
50+
elementExpressions: ts.NodeArray<ts.Expression>
51+
) => void
52+
) {
53+
ts.forEachChild(sourceFile, function findDecorator(node) {
54+
if (!ts.isDecorator(node)) {
55+
ts.forEachChild(node, findDecorator);
56+
return;
57+
}
58+
59+
ts.forEachChild(node, function findImportsNode(n) {
60+
if (
61+
ts.isPropertyAssignment(n) &&
62+
ts.isArrayLiteralExpression(n.initializer) &&
63+
ts.isIdentifier(n.name) &&
64+
n.name.text === 'imports'
65+
) {
66+
callback(n, n.initializer.elements);
67+
}
68+
69+
ts.forEachChild(n, findImportsNode);
70+
});
71+
});
72+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import { Tree } from '@angular-devkit/schematics';
2+
import {
3+
SchematicTestRunner,
4+
UnitTestTree,
5+
} from '@angular-devkit/schematics/testing';
6+
import * as path from 'path';
7+
import { createPackageJson } from '../../../schematics-core/testing/create-package';
8+
9+
describe('Router Store Migration 9_0_0', () => {
10+
let appTree: UnitTestTree;
11+
const collectionPath = path.join(__dirname, '../migration.json');
12+
const pkgName = 'router-store';
13+
14+
beforeEach(() => {
15+
appTree = new UnitTestTree(Tree.empty());
16+
appTree.create(
17+
'/tsconfig.json',
18+
`
19+
{
20+
"include": [**./*.ts"]
21+
}
22+
`
23+
);
24+
createPackageJson('', pkgName, appTree);
25+
});
26+
27+
describe('Adds the default serializer when none is set', () => {
28+
it(`should use the default serializer if none was present (empty)`, () => {
29+
const input = `
30+
import { StoreRouterConnectingModule } from '@ngrx/router-store';
31+
@NgModule({
32+
imports: [
33+
AuthModule,
34+
AppRoutingModule,
35+
StoreRouterConnectingModule.forRoot(),
36+
CoreModule,
37+
],
38+
bootstrap: [AppComponent],
39+
})
40+
export class AppModule {}
41+
`;
42+
const expected = `
43+
import { StoreRouterConnectingModule, DefaultRouterStateSerializer } from '@ngrx/router-store';
44+
@NgModule({
45+
imports: [
46+
AuthModule,
47+
AppRoutingModule,
48+
StoreRouterConnectingModule.forRoot({ serializer: DefaultRouterStateSerializer }),
49+
CoreModule,
50+
],
51+
bootstrap: [AppComponent],
52+
})
53+
export class AppModule {}
54+
`;
55+
56+
test(input, expected);
57+
});
58+
59+
it(`should use the default serializer if none was present (with props)`, () => {
60+
const input = `
61+
import { StoreRouterConnectingModule } from '@ngrx/router-store';
62+
@NgModule({
63+
imports: [
64+
AuthModule,
65+
AppRoutingModule,
66+
StoreRouterConnectingModule.forRoot({ key: 'router' }),
67+
CoreModule,
68+
],
69+
bootstrap: [AppComponent],
70+
})
71+
export class AppModule {}
72+
`;
73+
const expected = `
74+
import { StoreRouterConnectingModule, DefaultRouterStateSerializer } from '@ngrx/router-store';
75+
@NgModule({
76+
imports: [
77+
AuthModule,
78+
AppRoutingModule,
79+
StoreRouterConnectingModule.forRoot({ serializer: DefaultRouterStateSerializer, key: 'router' }),
80+
CoreModule,
81+
],
82+
bootstrap: [AppComponent],
83+
})
84+
export class AppModule {}
85+
`;
86+
87+
test(input, expected);
88+
});
89+
90+
it(`should not run the migration if there was a serializer set`, () => {
91+
const input = `
92+
import { StoreRouterConnectingModule } from '@ngrx/router-store';
93+
@NgModule({
94+
imports: [
95+
AuthModule,
96+
AppRoutingModule,
97+
StoreRouterConnectingModule.forRoot({ serializer: CustomSerializer }),
98+
CoreModule,
99+
],
100+
bootstrap: [AppComponent],
101+
})
102+
export class AppModule {}
103+
`;
104+
const expected = input;
105+
106+
test(input, expected);
107+
});
108+
109+
it(`should not run the migration if there was a routerState set`, () => {
110+
const input = `
111+
import { StoreRouterConnectingModule, RouterState } from '@ngrx/router-store';
112+
@NgModule({
113+
imports: [
114+
AuthModule,
115+
AppRoutingModule,
116+
StoreRouterConnectingModule.forRoot({ routerState: RouterState.Minimal }),
117+
CoreModule,
118+
],
119+
bootstrap: [AppComponent],
120+
})
121+
export class AppModule {}
122+
`;
123+
const expected = input;
124+
125+
test(input, expected);
126+
});
127+
128+
function test(input: string, expected: string) {
129+
appTree.create('./app.module.ts', input);
130+
const runner = new SchematicTestRunner('schematics', collectionPath);
131+
132+
const newTree = runner.runSchematic(
133+
`ngrx-${pkgName}-migration-03`,
134+
{},
135+
appTree
136+
);
137+
const file = newTree.readContent('app.module.ts');
138+
139+
expect(file).toBe(expected);
140+
}
141+
});
142+
});

0 commit comments

Comments
 (0)