Skip to content

Commit

Permalink
feat(router-store): add v8 migration schematic (#1699)
Browse files Browse the repository at this point in the history
  • Loading branch information
timdeschryver authored and brandonroberts committed Apr 9, 2019
1 parent 214316f commit 0b794ce
Show file tree
Hide file tree
Showing 19 changed files with 415 additions and 18 deletions.
2 changes: 2 additions & 0 deletions modules/effects/schematics-core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export {
InsertChange,
RemoveChange,
ReplaceChange,
createReplaceChange,
createChangeRecorder,
} from './utility/change';

export { AppConfig, getWorkspace, getWorkspacePath } from './utility/config';
Expand Down
28 changes: 28 additions & 0 deletions modules/effects/schematics-core/utility/change.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import * as ts from 'typescript';
import { Tree, UpdateRecorder } from '@angular-devkit/schematics';
import { Path } from '@angular-devkit/core';

/* istanbul ignore file */
/**
* @license
Expand Down Expand Up @@ -133,3 +137,27 @@ export class ReplaceChange implements Change {
});
}
}

export function createReplaceChange(
sourceFile: ts.SourceFile,
path: Path,
node: ts.Node,
oldText: string,
newText: string
): ReplaceChange {
return new ReplaceChange(path, node.getStart(sourceFile), oldText, newText);
}

export function createChangeRecorder(
tree: Tree,
path: Path,
changes: ReplaceChange[]
): UpdateRecorder {
const recorder = tree.beginUpdate(path);
for (const change of changes) {
const action = <any>change;
recorder.remove(action.pos, action.oldText.length);
recorder.insertLeft(action.pos, action.newText);
}
return recorder;
}
2 changes: 2 additions & 0 deletions modules/entity/schematics-core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export {
InsertChange,
RemoveChange,
ReplaceChange,
createReplaceChange,
createChangeRecorder,
} from './utility/change';

export { AppConfig, getWorkspace, getWorkspacePath } from './utility/config';
Expand Down
28 changes: 28 additions & 0 deletions modules/entity/schematics-core/utility/change.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import * as ts from 'typescript';
import { Tree, UpdateRecorder } from '@angular-devkit/schematics';
import { Path } from '@angular-devkit/core';

/* istanbul ignore file */
/**
* @license
Expand Down Expand Up @@ -133,3 +137,27 @@ export class ReplaceChange implements Change {
});
}
}

export function createReplaceChange(
sourceFile: ts.SourceFile,
path: Path,
node: ts.Node,
oldText: string,
newText: string
): ReplaceChange {
return new ReplaceChange(path, node.getStart(sourceFile), oldText, newText);
}

export function createChangeRecorder(
tree: Tree,
path: Path,
changes: ReplaceChange[]
): UpdateRecorder {
const recorder = tree.beginUpdate(path);
for (const change of changes) {
const action = <any>change;
recorder.remove(action.pos, action.oldText.length);
recorder.insertLeft(action.pos, action.newText);
}
return recorder;
}
120 changes: 120 additions & 0 deletions modules/router-store/migrations/8_0_0/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
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 8_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);
});

it(`should import StoreRouterConnectingModule as StoreRouterConnectingModule.forRoot()`, () => {
const contents = `
import { StoreRouterConnectingModule } from '@ngrx/router-store';
@NgModule({
imports: [
AuthModule,
AppRoutingModule,
/**
* @ngrx/router-store keeps router state up-to-date in the store.
*/
StoreRouterConnectingModule,
CoreModule,
],
bootstrap: [AppComponent],
})
export class AppModule {}
`;
const expected = `
import { StoreRouterConnectingModule } from '@ngrx/router-store';
@NgModule({
imports: [
AuthModule,
AppRoutingModule,
/**
* @ngrx/router-store keeps router state up-to-date in the store.
*/
StoreRouterConnectingModule.forRoot(),
CoreModule,
],
bootstrap: [AppComponent],
})
export class AppModule {}
`;

appTree.create('./app.module.ts', contents);
const runner = new SchematicTestRunner('schematics', collectionPath);

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

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

it(`should not replace StoreRouterConnectingModule.forRoot()`, () => {
const contents = `
import { StoreRouterConnectingModule } from '@ngrx/router-store';
@NgModule({
imports: [
AuthModule,
AppRoutingModule,
/**
* @ngrx/router-store keeps router state up-to-date in the store.
*/
StoreRouterConnectingModule.forRoot(),
CoreModule,
],
bootstrap: [AppComponent],
})
export class AppModule {}
`;
const expected = `
import { StoreRouterConnectingModule } from '@ngrx/router-store';
@NgModule({
imports: [
AuthModule,
AppRoutingModule,
/**
* @ngrx/router-store keeps router state up-to-date in the store.
*/
StoreRouterConnectingModule.forRoot(),
CoreModule,
],
bootstrap: [AppComponent],
})
export class AppModule {}
`;

appTree.create('./app.module.ts', contents);
const runner = new SchematicTestRunner('schematics', collectionPath);

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

expect(file).toBe(expected);
});
});
71 changes: 71 additions & 0 deletions modules/router-store/migrations/8_0_0/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import * as ts from 'typescript';
import { Rule, chain, Tree } from '@angular-devkit/schematics';
import {
ReplaceChange,
createChangeRecorder,
createReplaceChange,
} from '@ngrx/router-store/schematics-core';

function updateRouterStoreImport(): Rule {
return (tree: Tree) => {
tree.visit(path => {
if (!path.endsWith('.ts')) {
return;
}

const sourceFile = ts.createSourceFile(
path,
tree.read(path)!.toString(),
ts.ScriptTarget.Latest
);

if (sourceFile.isDeclarationFile) {
return;
}
let changes: ReplaceChange[] = [];
ts.forEachChild(sourceFile, function findDecorator(node) {
if (!ts.isDecorator(node)) {
ts.forEachChild(node, findDecorator);
return;
}

ts.forEachChild(node, function findImports(node) {
if (
ts.isPropertyAssignment(node) &&
ts.isArrayLiteralExpression(node.initializer) &&
ts.isIdentifier(node.name) &&
node.name.text === 'imports'
) {
node.initializer.elements
.filter(ts.isIdentifier)
.filter(element => element.text === 'StoreRouterConnectingModule')
.forEach(element => {
changes.push(
createReplaceChange(
sourceFile,
path,
element,
'StoreRouterConnectingModule',
'StoreRouterConnectingModule.forRoot()'
)
);
});
}

ts.forEachChild(node, findImports);
});
});

if (changes.length < 1) {
return;
}

const recorder = createChangeRecorder(tree, path, changes);
tree.commitUpdate(recorder);
});
};
}

export default function(): Rule {
return chain([updateRouterStoreImport()]);
}
1 change: 1 addition & 0 deletions modules/router-store/migrations/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ ts_library(
deps = [
"//modules/router-store/schematics-core",
"@npm//@angular-devkit/schematics",
"@npm//typescript",
],
)

Expand Down
5 changes: 5 additions & 0 deletions modules/router-store/migrations/migration.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
"description": "The road to v6",
"version": "5.2",
"factory": "./6_0_0/index"
},
"ngrx-router-store-migration-02": {
"description": "The road to v8",
"version": "8-beta",
"factory": "./8_0_0/index"
}
}
}
2 changes: 2 additions & 0 deletions modules/router-store/schematics-core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export {
InsertChange,
RemoveChange,
ReplaceChange,
createReplaceChange,
createChangeRecorder,
} from './utility/change';

export { AppConfig, getWorkspace, getWorkspacePath } from './utility/config';
Expand Down
28 changes: 28 additions & 0 deletions modules/router-store/schematics-core/utility/change.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import * as ts from 'typescript';
import { Tree, UpdateRecorder } from '@angular-devkit/schematics';
import { Path } from '@angular-devkit/core';

/* istanbul ignore file */
/**
* @license
Expand Down Expand Up @@ -133,3 +137,27 @@ export class ReplaceChange implements Change {
});
}
}

export function createReplaceChange(
sourceFile: ts.SourceFile,
path: Path,
node: ts.Node,
oldText: string,
newText: string
): ReplaceChange {
return new ReplaceChange(path, node.getStart(sourceFile), oldText, newText);
}

export function createChangeRecorder(
tree: Tree,
path: Path,
changes: ReplaceChange[]
): UpdateRecorder {
const recorder = tree.beginUpdate(path);
for (const change of changes) {
const action = <any>change;
recorder.remove(action.pos, action.oldText.length);
recorder.insertLeft(action.pos, action.newText);
}
return recorder;
}
2 changes: 2 additions & 0 deletions modules/schematics-core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export {
InsertChange,
RemoveChange,
ReplaceChange,
createReplaceChange,
createChangeRecorder,
} from './utility/change';

export { AppConfig, getWorkspace, getWorkspacePath } from './utility/config';
Expand Down
28 changes: 28 additions & 0 deletions modules/schematics-core/utility/change.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import * as ts from 'typescript';
import { Tree, UpdateRecorder } from '@angular-devkit/schematics';
import { Path } from '@angular-devkit/core';

/* istanbul ignore file */
/**
* @license
Expand Down Expand Up @@ -133,3 +137,27 @@ export class ReplaceChange implements Change {
});
}
}

export function createReplaceChange(
sourceFile: ts.SourceFile,
path: Path,
node: ts.Node,
oldText: string,
newText: string
): ReplaceChange {
return new ReplaceChange(path, node.getStart(sourceFile), oldText, newText);
}

export function createChangeRecorder(
tree: Tree,
path: Path,
changes: ReplaceChange[]
): UpdateRecorder {
const recorder = tree.beginUpdate(path);
for (const change of changes) {
const action = <any>change;
recorder.remove(action.pos, action.oldText.length);
recorder.insertLeft(action.pos, action.newText);
}
return recorder;
}
Loading

0 comments on commit 0b794ce

Please sign in to comment.