Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Store): add META_REDUCERS replacement migration #1640

Merged
merged 2 commits into from
Apr 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions modules/store/migrations/8_0_0/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
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('Store Migration 8_0_0', () => {
let appTree: UnitTestTree;
const collectionPath = path.join(__dirname, '../migration.json');
const pkgName = 'store';
beforeEach(() => {
appTree = new UnitTestTree(Tree.empty());
appTree.create(
'/tsconfig.json',
`
{
"include": [**./*.ts"]
}
`
);
createPackageJson('', pkgName, appTree);
});

it(`should replace the meta reducer imports`, () => {
const contents = `
import {
RuntimeChecks,
META_REDUCERS,
Store,
META_REDUCERS,
StoreModule,
META_REDUCERS as foo,
} from '@ngrx/store';`;
const expected = `
import {
RuntimeChecks,
USER_PROVIDED_META_REDUCERS,
Store,
USER_PROVIDED_META_REDUCERS,
StoreModule,
USER_PROVIDED_META_REDUCERS as foo,
} from '@ngrx/store';`;

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 replace the meta reducer assignments`, () => {
const contents = `
@NgModule({
imports: [
CommonModule,
BrowserModule,
BrowserAnimationsModule,
HttpClientModule,
AuthModule,
AppRoutingModule,
StoreModule.forRoot(reducers),
],
providers: [
{
provide: META_REDUCERS,
useValue: [fooReducer, barReducer]
}
]
bootstrap: [AppComponent],
})
export class AppModule {}`;
const expected = `
@NgModule({
imports: [
CommonModule,
BrowserModule,
BrowserAnimationsModule,
HttpClientModule,
AuthModule,
AppRoutingModule,
StoreModule.forRoot(reducers),
],
providers: [
{
provide: USER_PROVIDED_META_REDUCERS,
useValue: [fooReducer, barReducer]
}
]
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);
});
});
120 changes: 120 additions & 0 deletions modules/store/migrations/8_0_0/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import * as ts from 'typescript';
import { Rule, chain, Tree } from '@angular-devkit/schematics';
import { Path } from '@angular-devkit/core';
import { ReplaceChange } from '@ngrx/store/schematics-core';

const META_REDUCERS = 'META_REDUCERS';

function updateMetaReducersToken(): 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;
}

const createChange = (node: ts.Node) =>
new ReplaceChange(
path,
node.getStart(sourceFile),
META_REDUCERS,
'USER_PROVIDED_META_REDUCERS'
);

const changes: ReplaceChange[] = [];
changes.push(
...findMetaReducersImportStatements(sourceFile, createChange)
);
changes.push(...findMetaReducersAssignment(sourceFile, createChange));

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

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

export default function(): Rule {
return chain([updateMetaReducersToken()]);
}

function findMetaReducersImportStatements(
sourceFile: ts.SourceFile,
createChange: (node: ts.Node) => ReplaceChange
) {
const metaReducerImports = sourceFile.statements
.filter(ts.isImportDeclaration)
.filter(isNgRxStoreImport)
.map(p =>
(p.importClause!.namedBindings! as ts.NamedImports).elements.filter(
isMetaReducersImportSpecifier
)
)
.reduce((imports, curr) => imports.concat(curr), []);

const changes = metaReducerImports.map(createChange);
return changes;

function isNgRxStoreImport(importDeclaration: ts.ImportDeclaration) {
return (
importDeclaration.moduleSpecifier.getText(sourceFile) === "'@ngrx/store'"
);
}

function isMetaReducersImportSpecifier(importSpecifier: ts.ImportSpecifier) {
const isImport = () => importSpecifier.name.text === META_REDUCERS;
const isRenamedImport = () =>
importSpecifier.propertyName &&
importSpecifier.propertyName.text === META_REDUCERS;

return (
ts.isImportSpecifier(importSpecifier) && (isImport() || isRenamedImport())
);
}
}

function findMetaReducersAssignment(
sourceFile: ts.SourceFile,
createChange: (node: ts.Node) => ReplaceChange
) {
let changes: ReplaceChange[] = [];
ts.forEachChild(sourceFile, node => findMetaReducers(node, changes));
return changes;

function findMetaReducers(node: ts.Node, changes: ReplaceChange[]) {
if (
ts.isPropertyAssignment(node) &&
node.initializer.getText(sourceFile) === META_REDUCERS
) {
changes.push(createChange(node.initializer));
}

ts.forEachChild(node, childNode => findMetaReducers(childNode, changes));
}
}

function createChangeRecorder(
tree: Tree,
path: Path,
changes: ReplaceChange[]
) {
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;
}
1 change: 1 addition & 0 deletions modules/store/migrations/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ ts_library(
deps = [
"//modules/store/schematics-core",
"@npm//@angular-devkit/schematics",
"@npm//typescript",
],
)

Expand Down
5 changes: 5 additions & 0 deletions modules/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-store-migration-02": {
"description": "The road to v8",
"version": "7.0",
"factory": "./8_0_0/index"
}
}
}