Skip to content

Commit cede393

Browse files
brandonrobertswesleygrimes
authored andcommitted
feat(schematics): add support for minimal setup option for store and effects
1 parent 12202a7 commit cede393

File tree

9 files changed

+154
-22
lines changed

9 files changed

+154
-22
lines changed

modules/effects/schematics/ng-add/schema.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@
4848
"default": false,
4949
"description": "Group effects file within 'effects' folder",
5050
"aliases": ["g"]
51+
},
52+
"minimal": {
53+
"type": "boolean",
54+
"default": false,
55+
"description":
56+
"Setup root effects module without registering initial effects."
5157
}
5258
},
5359
"required": []

modules/schematics/src/effect/index.spec.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,39 @@ describe('Effect Schematic', () => {
126126
expect(content).toMatch(/EffectsModule\.forRoot\(\[FooEffects\]\)/);
127127
});
128128

129+
it('should register the root effect module without effect with the minimal flag', () => {
130+
const options = {
131+
...defaultOptions,
132+
root: true,
133+
name: undefined,
134+
module: 'app.module.ts',
135+
minimal: true,
136+
};
137+
138+
const tree = schematicRunner.runSchematic('effect', options, appTree);
139+
const content = tree.readContent(`${projectPath}/src/app/app.module.ts`);
140+
141+
expect(content).toMatch(/EffectsModule\.forRoot\(\[\]\)/);
142+
expect(content).not.toMatch(/FooEffects/);
143+
});
144+
145+
it('should still register the feature effect module with an effect with the minimal flag', () => {
146+
const options = {
147+
...defaultOptions,
148+
root: false,
149+
module: 'app.module.ts',
150+
minimal: true,
151+
};
152+
153+
const tree = schematicRunner.runSchematic('effect', options, appTree);
154+
const content = tree.readContent(`${projectPath}/src/app/app.module.ts`);
155+
156+
expect(content).toMatch(/EffectsModule\.forFeature\(\[FooEffects\]\)/);
157+
expect(
158+
tree.files.indexOf(`${projectPath}/src/app/foo/foo.effects.ts`)
159+
).toBeGreaterThanOrEqual(0);
160+
});
161+
129162
it('should register the feature effect in the provided module', () => {
130163
const options = { ...defaultOptions, module: 'app.module.ts' };
131164

modules/schematics/src/effect/index.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
mergeWith,
1212
move,
1313
noop,
14-
template,
1514
url,
1615
} from '@angular-devkit/schematics';
1716
import {
@@ -74,13 +73,22 @@ function addImportToNgModule(options: EffectOptions): Rule {
7473
effectsName,
7574
relativePath
7675
);
76+
77+
const effectsSetup =
78+
options.root && options.minimal ? `[]` : `[${effectsName}]`;
7779
const [effectsNgModuleImport] = addImportToModule(
7880
source,
7981
modulePath,
80-
`EffectsModule.for${options.root ? 'Root' : 'Feature'}([${effectsName}])`,
82+
`EffectsModule.for${options.root ? 'Root' : 'Feature'}(${effectsSetup})`,
8183
relativePath
8284
);
83-
const changes = [effectsModuleImport, effectsImport, effectsNgModuleImport];
85+
86+
let changes = [effectsModuleImport, effectsNgModuleImport];
87+
88+
if (!options.root || (options.root && !options.minimal)) {
89+
changes = changes.concat([effectsImport]);
90+
}
91+
8492
const recorder = host.beginUpdate(modulePath);
8593
for (const change of changes) {
8694
if (change instanceof InsertChange) {
@@ -116,14 +124,15 @@ export default function(options: EffectOptions): Rule {
116124
options.module = findModuleFromOptions(host, options);
117125
}
118126

119-
const parsedPath = parseName(options.path, options.name);
127+
const parsedPath = parseName(options.path, options.name || '');
120128
options.name = parsedPath.name;
121129
options.path = parsedPath.path;
122130

123131
const templateSource = apply(url('./files'), [
124132
options.spec
125133
? noop()
126134
: filter(path => !path.endsWith('.spec.ts.template')),
135+
options.root && options.minimal ? filter(_ => false) : noop(),
127136
applyTemplates({
128137
...stringUtils,
129138
'if-flat': (s: string) =>

modules/schematics/src/effect/schema.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@
7070
"description":
7171
"Specifies whether to use creator functions for handling actions, reducers, and effects.",
7272
"aliases": ["c"]
73+
},
74+
"minimal": {
75+
"type": "boolean",
76+
"default": false,
77+
"description":
78+
"Setup root effects module without registering initial effects."
7379
}
7480
},
7581
"required": []

modules/schematics/src/effect/schema.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,9 @@ export interface Schema {
5454
* handling actions, reducers, and effects.
5555
*/
5656
creators?: boolean;
57+
58+
/**
59+
* Setup root effects module without registering initial effects.
60+
*/
61+
minimal?: boolean;
5762
}

modules/schematics/src/store/index.spec.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,48 @@ describe('Store Schematic', () => {
4545
).toBeGreaterThanOrEqual(0);
4646
});
4747

48+
it('should skip the initial store setup files if the minimal flag is provided', async () => {
49+
const options = {
50+
...defaultOptions,
51+
module: 'app.module.ts',
52+
minimal: true,
53+
};
54+
55+
const tree = await schematicRunner
56+
.runSchematicAsync('store', options, appTree)
57+
.toPromise();
58+
const content = tree.readContent(`${projectPath}/src/app/app.module.ts`);
59+
const files = tree.files;
60+
61+
expect(content).not.toMatch(
62+
/import { reducers, metaReducers } from '\.\/reducers';/
63+
);
64+
expect(content).toMatch(/StoreModule.forRoot\({}/);
65+
66+
expect(files.indexOf(`${projectPath}/src/app/reducers/index.ts`)).toBe(-1);
67+
});
68+
69+
it('should not skip the initial store setup files if the minimal flag is provided with a feature', async () => {
70+
const options = {
71+
...defaultOptions,
72+
root: false,
73+
module: 'app.module.ts',
74+
minimal: true,
75+
};
76+
77+
const tree = await schematicRunner
78+
.runSchematicAsync('store', options, appTree)
79+
.toPromise();
80+
const content = tree.readContent(`${projectPath}/src/app/app.module.ts`);
81+
82+
expect(content).toMatch(
83+
/StoreModule\.forFeature\('foo', fromFoo\.reducers, { metaReducers: fromFoo.metaReducers }\)/
84+
);
85+
expect(
86+
tree.files.indexOf(`${projectPath}/src/app/reducers/index.ts`)
87+
).toBeGreaterThanOrEqual(0);
88+
});
89+
4890
it('should create the initial store to specified project if provided', () => {
4991
const options = {
5092
...defaultOptions,
@@ -154,6 +196,7 @@ describe('Store Schematic', () => {
154196

155197
const tree = schematicRunner.runSchematic('store', options, appTree);
156198
const content = tree.readContent(`${projectPath}/src/app/app.module.ts`);
199+
157200
expect(content).toMatch(
158201
/StoreModule\.forFeature\('foo', fromFoo\.reducers, { metaReducers: fromFoo.metaReducers }\)/
159202
);
@@ -244,7 +287,7 @@ describe('Store Schematic', () => {
244287

245288
const tree = schematicRunner.runSchematic('store', options, appTree);
246289
const content = tree.readContent(`${projectPath}/src/app/app.module.ts`);
247-
expect(content).toMatch(/, runtimeChecks: {/);
290+
expect(content).toMatch(/runtimeChecks: {/);
248291
expect(content).toMatch(/strictStateImmutability: true,/);
249292
expect(content).toMatch(/strictActionImmutability: true/);
250293
});

modules/schematics/src/store/index.ts

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import {
1111
template,
1212
url,
1313
move,
14+
filter,
15+
noop,
1416
} from '@angular-devkit/schematics';
1517
import { Path, dirname } from '@angular-devkit/core';
1618
import * as ts from 'typescript';
@@ -61,16 +63,25 @@ function addImportToNgModule(options: StoreOptions): Rule {
6163
`${options.path}/environments/environment`
6264
);
6365

64-
const runtimeChecks = `runtimeChecks: {
66+
const runtimeChecks = `
67+
runtimeChecks: {
6568
strictStateImmutability: true,
6669
strictActionImmutability: true,
67-
}`;
70+
}
71+
`;
72+
73+
const rootStoreReducers = options.minimal ? `{}` : `reducers`;
74+
75+
const rootStoreConfig = options.minimal
76+
? `{ ${runtimeChecks} }`
77+
: `{
78+
metaReducers, ${runtimeChecks} }`;
6879

6980
const storeNgModuleImport = addImportToModule(
7081
source,
7182
modulePath,
7283
options.root
73-
? `StoreModule.forRoot(reducers, { metaReducers, ${runtimeChecks} })`
84+
? `StoreModule.forRoot(${rootStoreReducers}, ${rootStoreConfig})`
7485
: `StoreModule.forFeature('${stringUtils.camelize(
7586
options.name
7687
)}', from${stringUtils.classify(
@@ -83,22 +94,30 @@ function addImportToNgModule(options: StoreOptions): Rule {
8394

8495
let commonImports = [
8596
insertImport(source, modulePath, 'StoreModule', '@ngrx/store'),
86-
options.root
87-
? insertImport(
88-
source,
89-
modulePath,
90-
'reducers, metaReducers',
91-
relativePath
92-
)
93-
: insertImport(
94-
source,
95-
modulePath,
96-
`* as from${stringUtils.classify(options.name)}`,
97-
relativePath,
98-
true
99-
),
10097
storeNgModuleImport,
10198
];
99+
100+
if (options.root && !options.minimal) {
101+
commonImports = commonImports.concat([
102+
insertImport(
103+
source,
104+
modulePath,
105+
'reducers, metaReducers',
106+
relativePath
107+
),
108+
]);
109+
} else if (!options.root) {
110+
commonImports = commonImports.concat([
111+
insertImport(
112+
source,
113+
modulePath,
114+
`* as from${stringUtils.classify(options.name)}`,
115+
relativePath,
116+
true
117+
),
118+
]);
119+
}
120+
102121
let rootImports: (Change | undefined)[] = [];
103122

104123
if (options.root) {
@@ -166,6 +185,7 @@ export default function(options: StoreOptions): Rule {
166185
}
167186

168187
const templateSource = apply(url('./files'), [
188+
options.root && options.minimal ? filter(_ => false) : noop(),
169189
applyTemplates({
170190
...stringUtils,
171191
...(options as object),

modules/schematics/src/store/schema.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@
5454
"default": "State",
5555
"description": "Specifies the interface for the state.",
5656
"alias": "si"
57+
},
58+
"minimal": {
59+
"type": "boolean",
60+
"default": false,
61+
"description":
62+
"Setup root state management without registering initial reducers."
5763
}
5864
},
5965
"required": []

modules/schematics/src/store/schema.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,8 @@ export interface Schema {
3939
* Specifies the interface for the state
4040
*/
4141
stateInterface?: string;
42+
/**
43+
* Setup state management without registering initial reducers.
44+
*/
45+
minimal?: boolean;
4246
}

0 commit comments

Comments
 (0)