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/router-store": "4.0.3",
"@ngrx/effects": "4.0.3",
"@ngrx/store-devtools": "^4.0.0",
"typescript": "2.4.2",
"@types/node": "8.0.7",
"@types/jasmine": "2.5.53",

View File

@ -1,7 +1,12 @@
export interface SomeAction {
type: 'SOME_ACTION';
export interface LoadData {
type: 'LOAD_DATA';
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 {provideMockActions} from '@ngrx/effects/testing';
import {DataPersistence} from '@nrwl/nx';
import {readAll, hot} from '@nrwl/nx/testing';
import {<%= className %>Effects} from './<%= fileName %>.effects';
import {of} from 'rxjs/observable/of';
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', () => {
it('should work', async () => {
const actions = new Actions(hot('-a-|', {a: {type: 'SOME_ACTION'}}));
const effects = new <%= className %>Effects(actions);
expect(await readAll(effects.someEffect)).toEqual([
{type: 'OTHER_ACTION'}
actions = hot('-a-|', {a: {type: 'LOAD_DATA'}});
expect(await readAll(effects.loadData)).toEqual([
{type: 'DATA_LOADED', payload: {}}
]);
});
});

View File

@ -1,14 +1,25 @@
import {Injectable} from '@angular/core';
import {Effect, Actions} from '@ngrx/effects';
import {DataPersistence} from '@nrwl/nx';
import {of} from 'rxjs/observable/of';
import 'rxjs/add/operator/switchMap';
import {<%= className %>State} from './<%= fileName %>.interfaces';
import {LoadData, DataLoaded} from './<%= fileName %>.actions';
@Injectable()
export class <%= className %>Effects {
@Effect() someEffect = this.actions.ofType('SOME_ACTION').switchMap((a) => {
// process the action
return of({type: 'OTHER_ACTION'});
@Effect() loadData = this.d.pessimisticUpdate('LOAD_DATA', {
run(a: LoadData, state: <%= className %>State) {
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 %>InitialState } from './<%= fileName %>.init';
import { <%= className %> } from './<%= fileName %>.interfaces';
import { SomeAction } from './<%= fileName %>.actions';
import { DataLoaded } from './<%= fileName %>.actions';
describe('<%= propertyName %>Reducer', () => {
it('should work', () => {
const state: <%= className %> = {};
const action: SomeAction = {type: 'SOME_ACTION', payload: {}};
const action: DataLoaded = {type: 'DATA_LOADED', payload: {}};
const actual = <%= propertyName %>Reducer(state, action);
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 %> {
switch (action.type) {
case 'SOME_ACTION': {
case 'DATA_LOADED': {
return {...state, ...action.payload};
}
default: {

View File

@ -26,13 +26,14 @@ function addImportsToModule(name: string, options: Schema): Rule {
const source = ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true);
if (options.onlyEmptyRoot) {
const reducer = `StoreModule.forRoot({})`;
const effects = `EffectsModule.forRoot([])`;
insert(host, modulePath, [
insertImport(source, modulePath, 'StoreModule', '@ngrx/store'),
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;
@ -45,20 +46,34 @@ function addImportsToModule(name: string, options: Schema): Rule {
const effectsName = `${toClassName(name)}Effects`;
const initName = `${toPropertyName(name)}InitialState`;
const effects =
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, [
const common = [
insertImport(source, modulePath, 'StoreModule', '@ngrx/store'),
insertImport(source, modulePath, 'EffectsModule', '@ngrx/effects'),
insertImport(source, modulePath, reducerName, reducerPath),
insertImport(source, modulePath, initName, initPath),
insertImport(source, modulePath, effectsName, effectsPath), ...addImportToModule(source, modulePath, reducer),
...addImportToModule(source, modulePath, effects), ...addProviderToModule(source, modulePath, effectsName)
]);
insertImport(source, modulePath, effectsName, effectsPath),
...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;
}
};
@ -83,6 +98,9 @@ function addNgRxToPackageJson() {
if (!json['dependencies']['@ngrx/effects']) {
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));
return host;
};

View File

@ -1,4 +1,4 @@
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 schematicsVersion = 'nrwl/schematics-build';

View File

@ -135,6 +135,10 @@
version "4.0.3"
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":
version "4.0.3"
resolved "https://registry.yarnpkg.com/@ngrx/store/-/store-4.0.3.tgz#36abacdfa19bfb8506e40de80bae06050a1e15e9"