feat(angular): add parent flag to ngrx generator (#14105)
This commit is contained in:
parent
53cffacf6b
commit
16a0891d30
@ -26,6 +26,12 @@
|
||||
"x-prompt": "What name would you like to use for the NgRx feature state? An example would be `users`."
|
||||
},
|
||||
"module": {
|
||||
"type": "string",
|
||||
"description": "The path to the `NgModule` where the feature state will be registered. The host directory will create/use the new state directory.",
|
||||
"x-prompt": "What is the path to the module where this NgRx state should be registered?",
|
||||
"x-deprecated": "This option will be removed in a future version of Nx. Please switch to using --parent instead."
|
||||
},
|
||||
"parent": {
|
||||
"type": "string",
|
||||
"description": "The path to the `NgModule` where the feature state will be registered. The host directory will create/use the new state directory.",
|
||||
"x-prompt": "What is the path to the module where this NgRx state should be registered?"
|
||||
@ -74,7 +80,7 @@
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["module", "name"],
|
||||
"required": ["name"],
|
||||
"presets": []
|
||||
},
|
||||
"description": "Adds NgRx support to an application or library.",
|
||||
|
||||
@ -27,7 +27,7 @@ describe('Angular Package', () => {
|
||||
|
||||
// Generate root ngrx state management
|
||||
runCLI(
|
||||
`generate @nrwl/angular:ngrx users --module=apps/${myapp}/src/app/app.module.ts --root --minimal=false`
|
||||
`generate @nrwl/angular:ngrx users --parent=apps/${myapp}/src/app/app.module.ts --root --minimal=false`
|
||||
);
|
||||
const packageJson = readJson('package.json');
|
||||
expect(packageJson.dependencies['@ngrx/store']).toBeDefined();
|
||||
@ -39,7 +39,7 @@ describe('Angular Package', () => {
|
||||
// Generate feature library and ngrx state within that library
|
||||
runCLI(`g @nrwl/angular:lib ${mylib} --prefix=fl`);
|
||||
runCLI(
|
||||
`generate @nrwl/angular:ngrx flights --module=libs/${mylib}/src/lib/${mylib}.module.ts --facade`
|
||||
`generate @nrwl/angular:ngrx flights --parent=libs/${mylib}/src/lib/${mylib}.module.ts --facade`
|
||||
);
|
||||
|
||||
expect(runCLI(`build ${myapp}`)).toMatch(/main\.[a-z0-9]+\.js/);
|
||||
@ -56,7 +56,40 @@ describe('Angular Package', () => {
|
||||
|
||||
// Generate root ngrx state management
|
||||
runCLI(
|
||||
`generate @nrwl/angular:ngrx users --module=apps/${myapp}/src/app/app.module.ts --root`
|
||||
`generate @nrwl/angular:ngrx users --parent=apps/${myapp}/src/app/app.module.ts --root`
|
||||
);
|
||||
const packageJson = readJson('package.json');
|
||||
expect(packageJson.dependencies['@ngrx/entity']).toBeDefined();
|
||||
expect(packageJson.dependencies['@ngrx/store']).toBeDefined();
|
||||
expect(packageJson.dependencies['@ngrx/effects']).toBeDefined();
|
||||
expect(packageJson.dependencies['@ngrx/router-store']).toBeDefined();
|
||||
expect(packageJson.devDependencies['@ngrx/schematics']).toBeDefined();
|
||||
expect(packageJson.devDependencies['@ngrx/store-devtools']).toBeDefined();
|
||||
|
||||
const mylib = uniq('mylib');
|
||||
// Generate feature library and ngrx state within that library
|
||||
runCLI(`g @nrwl/angular:lib ${mylib} --prefix=fl`);
|
||||
|
||||
const flags = `--facade --barrels`;
|
||||
runCLI(
|
||||
`generate @nrwl/angular:ngrx flights --parent=libs/${mylib}/src/lib/${mylib}.module.ts ${flags}`
|
||||
);
|
||||
|
||||
expect(runCLI(`build ${myapp}`)).toMatch(/main\.[a-z0-9]+\.js/);
|
||||
expectTestsPass(await runCLIAsync(`test ${myapp} --no-watch`));
|
||||
// TODO: remove this condition
|
||||
if (getSelectedPackageManager() !== 'pnpm') {
|
||||
expectTestsPass(await runCLIAsync(`test ${mylib} --no-watch`));
|
||||
}
|
||||
}, 1000000);
|
||||
|
||||
it('should work with creators using --module', async () => {
|
||||
const myapp = uniq('myapp');
|
||||
runCLI(`generate @nrwl/angular:app ${myapp} --routing --no-interactive`);
|
||||
|
||||
// Generate root ngrx state management
|
||||
runCLI(
|
||||
`generate @nrwl/angular:ngrx users --parent=apps/${myapp}/src/app/app.module.ts --root`
|
||||
);
|
||||
const packageJson = readJson('package.json');
|
||||
expect(packageJson.dependencies['@ngrx/entity']).toBeDefined();
|
||||
|
||||
@ -273,6 +273,32 @@ import { StoreRouterConnectingModule } from '@ngrx/router-store';
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ngrx should add a root module with feature module when minimal is set to false using --module 1`] = `
|
||||
"
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { AppComponent } from './app.component';
|
||||
import { StoreModule } from '@ngrx/store';
|
||||
import { EffectsModule } from '@ngrx/effects';
|
||||
import * as fromUsers from './+state/users.reducer';
|
||||
import { UsersEffects } from './+state/users.effects';
|
||||
import { StoreRouterConnectingModule } from '@ngrx/router-store';
|
||||
@NgModule({
|
||||
imports: [BrowserModule, RouterModule.forRoot([]), StoreModule.forRoot({}, {
|
||||
metaReducers: [],
|
||||
runtimeChecks: {
|
||||
strictActionImmutability: true,
|
||||
strictStateImmutability: true
|
||||
}
|
||||
}), EffectsModule.forRoot([UsersEffects]), StoreRouterConnectingModule.forRoot(), StoreModule.forFeature(fromUsers.USERS_FEATURE_KEY, fromUsers.usersReducer)],
|
||||
declarations: [AppComponent],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule {}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`ngrx should add an empty root module when minimal and root are set to true 1`] = `
|
||||
"
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
@ -1,16 +1,15 @@
|
||||
import type { Tree } from '@nrwl/devkit';
|
||||
import { joinPathFragments, names } from '@nrwl/devkit';
|
||||
import { addGlobal } from '@nrwl/workspace/src/utilities/ast-utils';
|
||||
import { dirname } from 'path';
|
||||
import { createSourceFile, ScriptTarget } from 'typescript';
|
||||
import type { NgRxGeneratorOptions } from '../schema';
|
||||
import type { NormalizedNgRxGeneratorOptions } from './normalize-options';
|
||||
|
||||
/**
|
||||
* Add ngrx feature exports to the public barrel in the feature library
|
||||
*/
|
||||
export function addExportsToBarrel(
|
||||
tree: Tree,
|
||||
options: NgRxGeneratorOptions
|
||||
options: NormalizedNgRxGeneratorOptions
|
||||
): void {
|
||||
// Don't update the public barrel for the root state, only for feature states
|
||||
if (options.root) {
|
||||
@ -18,7 +17,7 @@ export function addExportsToBarrel(
|
||||
}
|
||||
|
||||
const indexFilePath = joinPathFragments(
|
||||
dirname(options.module),
|
||||
options.parentDirectory,
|
||||
'..',
|
||||
'index.ts'
|
||||
);
|
||||
|
||||
@ -7,16 +7,16 @@ import {
|
||||
addImportToModule,
|
||||
addProviderToModule,
|
||||
} from '../../../utils/nx-devkit/ast-utils';
|
||||
import type { NgRxGeneratorOptions } from '../schema';
|
||||
import type { NormalizedNgRxGeneratorOptions } from './normalize-options';
|
||||
|
||||
export function addImportsToModule(
|
||||
tree: Tree,
|
||||
options: NgRxGeneratorOptions
|
||||
options: NormalizedNgRxGeneratorOptions
|
||||
): void {
|
||||
const modulePath = options.module;
|
||||
const sourceText = tree.read(modulePath, 'utf-8');
|
||||
const parentPath = options.module ?? options.parent;
|
||||
const sourceText = tree.read(parentPath, 'utf-8');
|
||||
let sourceFile = createSourceFile(
|
||||
modulePath,
|
||||
parentPath,
|
||||
sourceText,
|
||||
ScriptTarget.Latest,
|
||||
true
|
||||
@ -30,7 +30,7 @@ export function addImportsToModule(
|
||||
return insertImport(
|
||||
tree,
|
||||
source,
|
||||
modulePath,
|
||||
parentPath,
|
||||
symbolName,
|
||||
fileName,
|
||||
isDefault
|
||||
@ -72,11 +72,11 @@ export function addImportsToModule(
|
||||
sourceFile = addImport(sourceFile, 'EffectsModule', '@ngrx/effects');
|
||||
|
||||
if (options.minimal && options.root) {
|
||||
sourceFile = addImportToModule(tree, sourceFile, modulePath, storeForRoot);
|
||||
sourceFile = addImportToModule(tree, sourceFile, parentPath, storeForRoot);
|
||||
sourceFile = addImportToModule(
|
||||
tree,
|
||||
sourceFile,
|
||||
modulePath,
|
||||
parentPath,
|
||||
effectsForEmptyRoot
|
||||
);
|
||||
|
||||
@ -89,7 +89,7 @@ export function addImportsToModule(
|
||||
sourceFile = addImportToModule(
|
||||
tree,
|
||||
sourceFile,
|
||||
modulePath,
|
||||
parentPath,
|
||||
storeRouterModule
|
||||
);
|
||||
}
|
||||
@ -103,7 +103,7 @@ export function addImportsToModule(
|
||||
sourceFile = addProviderToModule(
|
||||
tree,
|
||||
sourceFile,
|
||||
modulePath,
|
||||
parentPath,
|
||||
facadeName
|
||||
);
|
||||
}
|
||||
@ -117,13 +117,13 @@ export function addImportsToModule(
|
||||
sourceFile = addImportToModule(
|
||||
tree,
|
||||
sourceFile,
|
||||
modulePath,
|
||||
parentPath,
|
||||
storeForRoot
|
||||
);
|
||||
sourceFile = addImportToModule(
|
||||
tree,
|
||||
sourceFile,
|
||||
modulePath,
|
||||
parentPath,
|
||||
effectsForRoot
|
||||
);
|
||||
|
||||
@ -136,7 +136,7 @@ export function addImportsToModule(
|
||||
sourceFile = addImportToModule(
|
||||
tree,
|
||||
sourceFile,
|
||||
modulePath,
|
||||
parentPath,
|
||||
storeRouterModule
|
||||
);
|
||||
}
|
||||
@ -144,7 +144,7 @@ export function addImportsToModule(
|
||||
sourceFile = addImportToModule(
|
||||
tree,
|
||||
sourceFile,
|
||||
modulePath,
|
||||
parentPath,
|
||||
storeForFeature
|
||||
);
|
||||
} else {
|
||||
@ -153,13 +153,13 @@ export function addImportsToModule(
|
||||
sourceFile = addImportToModule(
|
||||
tree,
|
||||
sourceFile,
|
||||
modulePath,
|
||||
parentPath,
|
||||
storeForFeature
|
||||
);
|
||||
sourceFile = addImportToModule(
|
||||
tree,
|
||||
sourceFile,
|
||||
modulePath,
|
||||
parentPath,
|
||||
effectsForFeature
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,36 +1,39 @@
|
||||
import type { Tree } from '@nrwl/devkit';
|
||||
import { generateFiles, joinPathFragments, names } from '@nrwl/devkit';
|
||||
import { dirname } from 'path';
|
||||
import type { NgRxGeneratorOptions } from '../schema';
|
||||
import { NormalizedNgRxGeneratorOptions } from './normalize-options';
|
||||
|
||||
/**
|
||||
* Generate 'feature' scaffolding: actions, reducer, effects, interfaces, selectors, facade
|
||||
*/
|
||||
export function generateNgrxFilesFromTemplates(
|
||||
tree: Tree,
|
||||
options: NgRxGeneratorOptions
|
||||
options: NormalizedNgRxGeneratorOptions
|
||||
): void {
|
||||
const name = options.name;
|
||||
const moduleDir = dirname(options.module);
|
||||
const projectNames = names(name);
|
||||
|
||||
generateFiles(tree, joinPathFragments(__dirname, '..', 'files'), moduleDir, {
|
||||
...options,
|
||||
...projectNames,
|
||||
tmpl: '',
|
||||
});
|
||||
generateFiles(
|
||||
tree,
|
||||
joinPathFragments(__dirname, '..', 'files'),
|
||||
options.parentDirectory,
|
||||
{
|
||||
...options,
|
||||
...projectNames,
|
||||
tmpl: '',
|
||||
}
|
||||
);
|
||||
|
||||
if (!options.facade) {
|
||||
tree.delete(
|
||||
joinPathFragments(
|
||||
moduleDir,
|
||||
options.parentDirectory,
|
||||
options.directory,
|
||||
`${projectNames.fileName}.facade.ts`
|
||||
)
|
||||
);
|
||||
tree.delete(
|
||||
joinPathFragments(
|
||||
moduleDir,
|
||||
options.parentDirectory,
|
||||
options.directory,
|
||||
`${projectNames.fileName}.facade.spec.ts`
|
||||
)
|
||||
|
||||
@ -1,11 +1,21 @@
|
||||
import { names } from '@nrwl/devkit';
|
||||
import type { NgRxGeneratorOptions } from '../schema';
|
||||
import { dirname } from 'path';
|
||||
|
||||
export type NormalizedNgRxGeneratorOptions = NgRxGeneratorOptions & {
|
||||
parentDirectory: string;
|
||||
};
|
||||
|
||||
export function normalizeOptions(
|
||||
options: NgRxGeneratorOptions
|
||||
): NgRxGeneratorOptions {
|
||||
): NormalizedNgRxGeneratorOptions {
|
||||
return {
|
||||
...options,
|
||||
parentDirectory: options.module
|
||||
? dirname(options.module)
|
||||
: options.parent
|
||||
? dirname(options.parent)
|
||||
: undefined,
|
||||
directory: names(options.directory).fileName,
|
||||
};
|
||||
}
|
||||
|
||||
@ -19,6 +19,13 @@ describe('ngrx', () => {
|
||||
let tree: Tree;
|
||||
|
||||
const defaultOptions: NgRxGeneratorOptions = {
|
||||
directory: '+state',
|
||||
minimal: true,
|
||||
parent: 'apps/myapp/src/app/app.module.ts',
|
||||
name: 'users',
|
||||
};
|
||||
|
||||
const defaultModuleOptions: NgRxGeneratorOptions = {
|
||||
directory: '+state',
|
||||
minimal: true,
|
||||
module: 'apps/myapp/src/app/app.module.ts',
|
||||
@ -49,6 +56,17 @@ describe('ngrx', () => {
|
||||
).rejects.toThrowError(`Module does not exist: ${modulePath}.`);
|
||||
});
|
||||
|
||||
it('should error when the module could not be found using --module', async () => {
|
||||
const modulePath = 'not-existing.module.ts';
|
||||
|
||||
await expect(
|
||||
ngrxGenerator(tree, {
|
||||
...defaultOptions,
|
||||
module: modulePath,
|
||||
})
|
||||
).rejects.toThrowError(`Module does not exist: ${modulePath}.`);
|
||||
});
|
||||
|
||||
it('should add an empty root module when minimal and root are set to true', async () => {
|
||||
await ngrxGenerator(tree, {
|
||||
...defaultOptions,
|
||||
@ -100,6 +118,18 @@ describe('ngrx', () => {
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should add a root module with feature module when minimal is set to false using --module', async () => {
|
||||
await ngrxGenerator(tree, {
|
||||
...defaultModuleOptions,
|
||||
root: true,
|
||||
minimal: false,
|
||||
});
|
||||
|
||||
expect(
|
||||
tree.read('/apps/myapp/src/app/app.module.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should not add RouterStoreModule when the module does not reference the router', async () => {
|
||||
createApp(tree, 'no-router-app', false);
|
||||
|
||||
|
||||
@ -11,29 +11,37 @@ import type { NgRxGeneratorOptions } from './schema';
|
||||
|
||||
export async function ngrxGenerator(
|
||||
tree: Tree,
|
||||
options: NgRxGeneratorOptions
|
||||
schema: NgRxGeneratorOptions
|
||||
): Promise<GeneratorCallback> {
|
||||
const normalizedOptions = normalizeOptions(options);
|
||||
|
||||
if (!tree.exists(normalizedOptions.module)) {
|
||||
throw new Error(`Module does not exist: ${normalizedOptions.module}.`);
|
||||
if (!schema.module && !schema.parent) {
|
||||
throw new Error('Please provide a value for `--parent`!');
|
||||
}
|
||||
|
||||
if (!normalizedOptions.minimal || !normalizedOptions.root) {
|
||||
generateNgrxFilesFromTemplates(tree, normalizedOptions);
|
||||
if (schema.module && !tree.exists(schema.module)) {
|
||||
throw new Error(`Module does not exist: ${schema.module}.`);
|
||||
}
|
||||
|
||||
if (!normalizedOptions.skipImport) {
|
||||
addImportsToModule(tree, normalizedOptions);
|
||||
addExportsToBarrel(tree, normalizedOptions);
|
||||
if (schema.parent && !tree.exists(schema.parent)) {
|
||||
throw new Error(`Parent does not exist: ${schema.parent}.`);
|
||||
}
|
||||
|
||||
const options = normalizeOptions(schema);
|
||||
|
||||
if (!options.minimal || !options.root) {
|
||||
generateNgrxFilesFromTemplates(tree, options);
|
||||
}
|
||||
|
||||
if (!options.skipImport) {
|
||||
addImportsToModule(tree, options);
|
||||
addExportsToBarrel(tree, options);
|
||||
}
|
||||
|
||||
let packageInstallationTask: GeneratorCallback = () => {};
|
||||
if (!normalizedOptions.skipPackageJson) {
|
||||
if (!options.skipPackageJson) {
|
||||
packageInstallationTask = addNgRxToPackageJson(tree);
|
||||
}
|
||||
|
||||
if (!normalizedOptions.skipFormat) {
|
||||
if (!options.skipFormat) {
|
||||
await formatFiles(tree);
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
export interface NgRxGeneratorOptions {
|
||||
directory: string;
|
||||
minimal: boolean;
|
||||
module: string;
|
||||
module?: string;
|
||||
parent?: string;
|
||||
name: string;
|
||||
barrels?: boolean;
|
||||
facade?: boolean;
|
||||
|
||||
@ -26,6 +26,12 @@
|
||||
"x-prompt": "What name would you like to use for the NgRx feature state? An example would be `users`."
|
||||
},
|
||||
"module": {
|
||||
"type": "string",
|
||||
"description": "The path to the `NgModule` where the feature state will be registered. The host directory will create/use the new state directory.",
|
||||
"x-prompt": "What is the path to the module where this NgRx state should be registered?",
|
||||
"x-deprecated": "This option will be removed in a future version of Nx. Please switch to using --parent instead."
|
||||
},
|
||||
"parent": {
|
||||
"type": "string",
|
||||
"description": "The path to the `NgModule` where the feature state will be registered. The host directory will create/use the new state directory.",
|
||||
"x-prompt": "What is the path to the module where this NgRx state should be registered?"
|
||||
@ -74,5 +80,5 @@
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["module", "name"]
|
||||
"required": ["name"]
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user