Skip to content

Commit

Permalink
feat(router-store): add migration to add the default serializer (#2291)
Browse files Browse the repository at this point in the history
  • Loading branch information
timdeschryver authored and brandonroberts committed Jan 7, 2020
1 parent b8a769a commit b742a8c
Show file tree
Hide file tree
Showing 32 changed files with 797 additions and 184 deletions.
3 changes: 2 additions & 1 deletion modules/data/schematics-core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export {
addImportToModule,
addProviderToModule,
replaceImport,
containsProperty,
} from './utility/ast-utils';

export {
Expand Down Expand Up @@ -76,4 +77,4 @@ export { addPackageToPackageJson } from './utility/package';

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

export { visitTSSourceFiles } from './utility/visit-utils';
export { visitTSSourceFiles, visitNgModuleImports } from './utility/visitors';
15 changes: 15 additions & 0 deletions modules/data/schematics-core/utility/ast-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -729,3 +729,18 @@ export function replaceImport(

return changes.reduce((imports, curr) => imports.concat(curr), []);
}

export function containsProperty(
objectLiteral: ts.ObjectLiteralExpression,
propertyName: string
) {
return (
objectLiteral &&
objectLiteral.properties.some(
prop =>
ts.isPropertyAssignment(prop) &&
ts.isIdentifier(prop.name) &&
prop.name.text === propertyName
)
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,31 @@ function* visit(directory: DirEntry): IterableIterator<ts.SourceFile> {
yield* visit(directory.dir(path));
}
}

export function visitNgModuleImports(
sourceFile: ts.SourceFile,
callback: (
importNode: ts.PropertyAssignment,
elementExpressions: ts.NodeArray<ts.Expression>
) => void
) {
ts.forEachChild(sourceFile, function findDecorator(node) {
if (!ts.isDecorator(node)) {
ts.forEachChild(node, findDecorator);
return;
}

ts.forEachChild(node, function findImportsNode(n) {
if (
ts.isPropertyAssignment(n) &&
ts.isArrayLiteralExpression(n.initializer) &&
ts.isIdentifier(n.name) &&
n.name.text === 'imports'
) {
callback(n, n.initializer.elements);
}

ts.forEachChild(n, findImportsNode);
});
});
}
3 changes: 2 additions & 1 deletion modules/effects/schematics-core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export {
addImportToModule,
addProviderToModule,
replaceImport,
containsProperty,
} from './utility/ast-utils';

export {
Expand Down Expand Up @@ -76,4 +77,4 @@ export { addPackageToPackageJson } from './utility/package';

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

export { visitTSSourceFiles } from './utility/visit-utils';
export { visitTSSourceFiles, visitNgModuleImports } from './utility/visitors';
15 changes: 15 additions & 0 deletions modules/effects/schematics-core/utility/ast-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -729,3 +729,18 @@ export function replaceImport(

return changes.reduce((imports, curr) => imports.concat(curr), []);
}

export function containsProperty(
objectLiteral: ts.ObjectLiteralExpression,
propertyName: string
) {
return (
objectLiteral &&
objectLiteral.properties.some(
prop =>
ts.isPropertyAssignment(prop) &&
ts.isIdentifier(prop.name) &&
prop.name.text === propertyName
)
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,31 @@ function* visit(directory: DirEntry): IterableIterator<ts.SourceFile> {
yield* visit(directory.dir(path));
}
}

export function visitNgModuleImports(
sourceFile: ts.SourceFile,
callback: (
importNode: ts.PropertyAssignment,
elementExpressions: ts.NodeArray<ts.Expression>
) => void
) {
ts.forEachChild(sourceFile, function findDecorator(node) {
if (!ts.isDecorator(node)) {
ts.forEachChild(node, findDecorator);
return;
}

ts.forEachChild(node, function findImportsNode(n) {
if (
ts.isPropertyAssignment(n) &&
ts.isArrayLiteralExpression(n.initializer) &&
ts.isIdentifier(n.name) &&
n.name.text === 'imports'
) {
callback(n, n.initializer.elements);
}

ts.forEachChild(n, findImportsNode);
});
});
}
3 changes: 2 additions & 1 deletion modules/entity/schematics-core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export {
addImportToModule,
addProviderToModule,
replaceImport,
containsProperty,
} from './utility/ast-utils';

export {
Expand Down Expand Up @@ -76,4 +77,4 @@ export { addPackageToPackageJson } from './utility/package';

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

export { visitTSSourceFiles } from './utility/visit-utils';
export { visitTSSourceFiles, visitNgModuleImports } from './utility/visitors';
15 changes: 15 additions & 0 deletions modules/entity/schematics-core/utility/ast-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -729,3 +729,18 @@ export function replaceImport(

return changes.reduce((imports, curr) => imports.concat(curr), []);
}

export function containsProperty(
objectLiteral: ts.ObjectLiteralExpression,
propertyName: string
) {
return (
objectLiteral &&
objectLiteral.properties.some(
prop =>
ts.isPropertyAssignment(prop) &&
ts.isIdentifier(prop.name) &&
prop.name.text === propertyName
)
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,31 @@ function* visit(directory: DirEntry): IterableIterator<ts.SourceFile> {
yield* visit(directory.dir(path));
}
}

export function visitNgModuleImports(
sourceFile: ts.SourceFile,
callback: (
importNode: ts.PropertyAssignment,
elementExpressions: ts.NodeArray<ts.Expression>
) => void
) {
ts.forEachChild(sourceFile, function findDecorator(node) {
if (!ts.isDecorator(node)) {
ts.forEachChild(node, findDecorator);
return;
}

ts.forEachChild(node, function findImportsNode(n) {
if (
ts.isPropertyAssignment(n) &&
ts.isArrayLiteralExpression(n.initializer) &&
ts.isIdentifier(n.name) &&
n.name.text === 'imports'
) {
callback(n, n.initializer.elements);
}

ts.forEachChild(n, findImportsNode);
});
});
}
142 changes: 142 additions & 0 deletions modules/router-store/migrations/9_0_0/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { Tree } from '@angular-devkit/schematics';
import {
SchematicTestRunner,
UnitTestTree,
} from '@angular-devkit/schematics/testing';
import * as path from 'path';
import { createPackageJson } from '../../../schematics-core/testing/create-package';

describe('Router Store Migration 9_0_0', () => {
let appTree: UnitTestTree;
const collectionPath = path.join(__dirname, '../migration.json');
const pkgName = 'router-store';

beforeEach(() => {
appTree = new UnitTestTree(Tree.empty());
appTree.create(
'/tsconfig.json',
`
{
"include": [**./*.ts"]
}
`
);
createPackageJson('', pkgName, appTree);
});

describe('Adds the default serializer when none is set', () => {
it(`should use the default serializer if none was present (empty)`, () => {
const input = `
import { StoreRouterConnectingModule } from '@ngrx/router-store';
@NgModule({
imports: [
AuthModule,
AppRoutingModule,
StoreRouterConnectingModule.forRoot(),
CoreModule,
],
bootstrap: [AppComponent],
})
export class AppModule {}
`;
const expected = `
import { StoreRouterConnectingModule, DefaultRouterStateSerializer } from '@ngrx/router-store';
@NgModule({
imports: [
AuthModule,
AppRoutingModule,
StoreRouterConnectingModule.forRoot({ serializer: DefaultRouterStateSerializer }),
CoreModule,
],
bootstrap: [AppComponent],
})
export class AppModule {}
`;

test(input, expected);
});

it(`should use the default serializer if none was present (with props)`, () => {
const input = `
import { StoreRouterConnectingModule } from '@ngrx/router-store';
@NgModule({
imports: [
AuthModule,
AppRoutingModule,
StoreRouterConnectingModule.forRoot({ key: 'router' }),
CoreModule,
],
bootstrap: [AppComponent],
})
export class AppModule {}
`;
const expected = `
import { StoreRouterConnectingModule, DefaultRouterStateSerializer } from '@ngrx/router-store';
@NgModule({
imports: [
AuthModule,
AppRoutingModule,
StoreRouterConnectingModule.forRoot({ serializer: DefaultRouterStateSerializer, key: 'router' }),
CoreModule,
],
bootstrap: [AppComponent],
})
export class AppModule {}
`;

test(input, expected);
});

it(`should not run the migration if there was a serializer set`, () => {
const input = `
import { StoreRouterConnectingModule } from '@ngrx/router-store';
@NgModule({
imports: [
AuthModule,
AppRoutingModule,
StoreRouterConnectingModule.forRoot({ serializer: CustomSerializer }),
CoreModule,
],
bootstrap: [AppComponent],
})
export class AppModule {}
`;
const expected = input;

test(input, expected);
});

it(`should not run the migration if there was a routerState set`, () => {
const input = `
import { StoreRouterConnectingModule, RouterState } from '@ngrx/router-store';
@NgModule({
imports: [
AuthModule,
AppRoutingModule,
StoreRouterConnectingModule.forRoot({ routerState: RouterState.Minimal }),
CoreModule,
],
bootstrap: [AppComponent],
})
export class AppModule {}
`;
const expected = input;

test(input, expected);
});

function test(input: string, expected: string) {
appTree.create('./app.module.ts', input);
const runner = new SchematicTestRunner('schematics', collectionPath);

const newTree = runner.runSchematic(
`ngrx-${pkgName}-migration-03`,
{},
appTree
);
const file = newTree.readContent('app.module.ts');

expect(file).toBe(expected);
}
});
});
Loading

0 comments on commit b742a8c

Please sign in to comment.