feat(schematics): improve ngrx code generation

This commit is contained in:
vsavkin 2017-09-12 18:30:40 -04:00
parent a0440688c3
commit c603a834a7
9 changed files with 89 additions and 30 deletions

View File

@ -32,6 +32,7 @@
"@ngrx/store": "4.0.3", "@ngrx/store": "4.0.3",
"@ngrx/router-store": "4.0.3", "@ngrx/router-store": "4.0.3",
"@ngrx/effects": "4.0.3", "@ngrx/effects": "4.0.3",
"@ngrx/store-devtools": "^4.0.0",
"typescript": "2.4.2", "typescript": "2.4.2",
"@types/node": "8.0.7", "@types/node": "8.0.7",
"@types/jasmine": "2.5.53", "@types/jasmine": "2.5.53",

View File

@ -1,7 +1,12 @@
export interface SomeAction { export interface LoadData {
type: 'SOME_ACTION'; type: 'LOAD_DATA';
payload: {}; payload: {};
} }
export type <%= className %>Action = SomeAction; export interface DataLoaded {
type: 'DATA_LOADED';
payload: {};
}
export type <%= className %>Action = LoadData | DataLoaded;

View File

@ -1,16 +1,36 @@
import {TestBed} from '@angular/core/testing';
import {StoreModule} from '@ngrx/store';
import {Actions} from '@ngrx/effects'; import {Actions} from '@ngrx/effects';
import {provideMockActions} from '@ngrx/effects/testing';
import {DataPersistence} from '@nrwl/nx';
import {readAll, hot} from '@nrwl/nx/testing'; import {readAll, hot} from '@nrwl/nx/testing';
import {<%= className %>Effects} from './<%= fileName %>.effects'; import {<%= className %>Effects} from './<%= fileName %>.effects';
import {of} from 'rxjs/observable/of'; import {of} from 'rxjs/observable/of';
describe('<%= className %>Effects', () => { describe('<%= className %>Effects', () => {
let actions;
let effects: <%= className %>Effects;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
StoreModule.forRoot({}),
],
providers: [
<%= className %>Effects,
DataPersistence,
provideMockActions(() => actions)
],
});
effects = TestBed.get(<%= className %>Effects);
});
describe('someEffect', () => { describe('someEffect', () => {
it('should work', async () => { it('should work', async () => {
const actions = new Actions(hot('-a-|', {a: {type: 'SOME_ACTION'}})); actions = hot('-a-|', {a: {type: 'LOAD_DATA'}});
const effects = new <%= className %>Effects(actions); expect(await readAll(effects.loadData)).toEqual([
{type: 'DATA_LOADED', payload: {}}
expect(await readAll(effects.someEffect)).toEqual([
{type: 'OTHER_ACTION'}
]); ]);
}); });
}); });

View File

@ -1,14 +1,25 @@
import {Injectable} from '@angular/core'; import {Injectable} from '@angular/core';
import {Effect, Actions} from '@ngrx/effects'; import {Effect, Actions} from '@ngrx/effects';
import {DataPersistence} from '@nrwl/nx';
import {of} from 'rxjs/observable/of'; import {of} from 'rxjs/observable/of';
import 'rxjs/add/operator/switchMap'; import 'rxjs/add/operator/switchMap';
import {<%= className %>State} from './<%= fileName %>.interfaces';
import {LoadData, DataLoaded} from './<%= fileName %>.actions';
@Injectable() @Injectable()
export class <%= className %>Effects { export class <%= className %>Effects {
@Effect() someEffect = this.actions.ofType('SOME_ACTION').switchMap((a) => { @Effect() loadData = this.d.pessimisticUpdate('LOAD_DATA', {
// process the action run(a: LoadData, state: <%= className %>State) {
return of({type: 'OTHER_ACTION'}); return {
type: 'DATA_LOADED',
payload: {}
};
},
onError(a: LoadData, error) {
console.error('Error', error);
}
}); });
constructor(private actions: Actions) {} constructor(private actions: Actions, private d: DataPersistence<<%= className %>State>) {}
} }

View File

@ -1,12 +1,12 @@
import { <%= propertyName %>Reducer } from './<%= fileName %>.reducer'; import { <%= propertyName %>Reducer } from './<%= fileName %>.reducer';
import { <%= propertyName %>InitialState } from './<%= fileName %>.init'; import { <%= propertyName %>InitialState } from './<%= fileName %>.init';
import { <%= className %> } from './<%= fileName %>.interfaces'; import { <%= className %> } from './<%= fileName %>.interfaces';
import { SomeAction } from './<%= fileName %>.actions'; import { DataLoaded } from './<%= fileName %>.actions';
describe('<%= propertyName %>Reducer', () => { describe('<%= propertyName %>Reducer', () => {
it('should work', () => { it('should work', () => {
const state: <%= className %> = {}; const state: <%= className %> = {};
const action: SomeAction = {type: 'SOME_ACTION', payload: {}}; const action: DataLoaded = {type: 'DATA_LOADED', payload: {}};
const actual = <%= propertyName %>Reducer(state, action); const actual = <%= propertyName %>Reducer(state, action);
expect(actual).toEqual({}); expect(actual).toEqual({});
}); });

View File

@ -3,7 +3,7 @@ import {<%= className %>Action} from './<%= fileName %>.actions';
export function <%= propertyName %>Reducer(state: <%= className %>, action: <%= className %>Action): <%= className %> { export function <%= propertyName %>Reducer(state: <%= className %>, action: <%= className %>Action): <%= className %> {
switch (action.type) { switch (action.type) {
case 'SOME_ACTION': { case 'DATA_LOADED': {
return {...state, ...action.payload}; return {...state, ...action.payload};
} }
default: { default: {

View File

@ -26,13 +26,14 @@ function addImportsToModule(name: string, options: Schema): Rule {
const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true); const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
if (options.onlyEmptyRoot) { if (options.onlyEmptyRoot) {
const reducer = `StoreModule.forRoot({})`;
const effects = `EffectsModule.forRoot([])`;
insert(host, modulePath, [ insert(host, modulePath, [
insertImport(source, modulePath, 'StoreModule', '@ngrx/store'), insertImport(source, modulePath, 'StoreModule', '@ngrx/store'),
insertImport(source, modulePath, 'EffectsModule', '@ngrx/effects'), insertImport(source, modulePath, 'EffectsModule', '@ngrx/effects'),
...addImportToModule(source, modulePath, reducer), ...addImportToModule(source, modulePath, effects) insertImport(source, modulePath, 'StoreDevtoolsModule', '@ngrx/store-devtools'),
insertImport(source, modulePath, 'environment', '../environments/environment'),
...addImportToModule(source, modulePath, `StoreModule.forRoot({})`),
...addImportToModule(source, modulePath, `EffectsModule.forRoot([])`),
...addImportToModule(source, modulePath, `!environment.production ? StoreDevtoolsModule.instrument() : []`)
]); ]);
return host; return host;
@ -45,20 +46,34 @@ function addImportsToModule(name: string, options: Schema): Rule {
const effectsName = `${toClassName(name)}Effects`; const effectsName = `${toClassName(name)}Effects`;
const initName = `${toPropertyName(name)}InitialState`; const initName = `${toPropertyName(name)}InitialState`;
const effects = const common = [
options.root ? `EffectsModule.forRoot([${effectsName}])` : `EffectsModule.forFeature([${effectsName}])`;
const reducer = options.root ?
`StoreModule.forRoot(${reducerName}, {initialState: ${initName}})` :
`StoreModule.forFeature('${toPropertyName(name)}', ${reducerName}, {initialState: ${initName}})`;
insert(host, modulePath, [
insertImport(source, modulePath, 'StoreModule', '@ngrx/store'), insertImport(source, modulePath, 'StoreModule', '@ngrx/store'),
insertImport(source, modulePath, 'EffectsModule', '@ngrx/effects'), insertImport(source, modulePath, 'EffectsModule', '@ngrx/effects'),
insertImport(source, modulePath, reducerName, reducerPath), insertImport(source, modulePath, reducerName, reducerPath),
insertImport(source, modulePath, initName, initPath), insertImport(source, modulePath, initName, initPath),
insertImport(source, modulePath, effectsName, effectsPath), ...addImportToModule(source, modulePath, reducer), insertImport(source, modulePath, effectsName, effectsPath),
...addImportToModule(source, modulePath, effects), ...addProviderToModule(source, modulePath, effectsName) ...addProviderToModule(source, modulePath, effectsName)
]); ];
if (options.root) {
insert(host, modulePath, [
...common,
insertImport(source, modulePath, 'StoreDevtoolsModule', '@ngrx/store-devtools'),
insertImport(source, modulePath, 'environment', '../environments/environment'),
...addImportToModule(source, modulePath, `StoreModule.forRoot(${reducerName}, {initialState: ${initName}})`),
...addImportToModule(source, modulePath, `EffectsModule.forRoot([${effectsName}])`),
...addImportToModule(source, modulePath, `!environment.production ? StoreDevtoolsModule.instrument() : []`),
]);
} else {
insert(host, modulePath, [
...common,
...addImportToModule(
source, modulePath,
`StoreModule.forFeature('${toPropertyName(name)}', ${reducerName}, {initialState: ${initName}})`),
...addImportToModule(source, modulePath, `EffectsModule.forFeature([${effectsName}])`)
]);
}
return host; return host;
} }
}; };
@ -83,6 +98,9 @@ function addNgRxToPackageJson() {
if (!json['dependencies']['@ngrx/effects']) { if (!json['dependencies']['@ngrx/effects']) {
json['dependencies']['@ngrx/effects'] = ngrxVersion; json['dependencies']['@ngrx/effects'] = ngrxVersion;
} }
if (!json['dependencies']['@ngrx/store-devtools']) {
json['dependencies']['@ngrx/store-devtools'] = ngrxVersion;
}
host.overwrite('package.json', JSON.stringify(json, null, 2)); host.overwrite('package.json', JSON.stringify(json, null, 2));
return host; return host;
}; };

View File

@ -1,4 +1,4 @@
export const angularJsVersion = '1.6.6'; export const angularJsVersion = '1.6.6';
export const ngrxVersion = '4.0.3'; export const ngrxVersion = '^4.0.0';
export const nxVersion = 'nrwl/nx-build'; export const nxVersion = 'nrwl/nx-build';
export const schematicsVersion = 'nrwl/schematics-build'; export const schematicsVersion = 'nrwl/schematics-build';

View File

@ -135,6 +135,10 @@
version "4.0.3" version "4.0.3"
resolved "https://registry.yarnpkg.com/@ngrx/router-store/-/router-store-4.0.3.tgz#5f924914a8dda136b049fc6c7ce9fe177f44be97" resolved "https://registry.yarnpkg.com/@ngrx/router-store/-/router-store-4.0.3.tgz#5f924914a8dda136b049fc6c7ce9fe177f44be97"
"@ngrx/store-devtools@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@ngrx/store-devtools/-/store-devtools-4.0.0.tgz#b79c24773217df7fd9735ad21f9cbf2533c96e04"
"@ngrx/store@4.0.3": "@ngrx/store@4.0.3":
version "4.0.3" version "4.0.3"
resolved "https://registry.yarnpkg.com/@ngrx/store/-/store-4.0.3.tgz#36abacdfa19bfb8506e40de80bae06050a1e15e9" resolved "https://registry.yarnpkg.com/@ngrx/store/-/store-4.0.3.tgz#36abacdfa19bfb8506e40de80bae06050a1e15e9"