Skip to content

Commit

Permalink
feat(angular): add parent flag to ngrx generator (#14105)
Browse files Browse the repository at this point in the history
  • Loading branch information
Coly010 committed Jan 3, 2023
1 parent 53cffac commit 16a0891
Show file tree
Hide file tree
Showing 11 changed files with 171 additions and 49 deletions.
8 changes: 7 additions & 1 deletion docs/generated/packages/angular/generators/ngrx.json
Original file line number Diff line number Diff line change
Expand Up @@ -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?"
Expand Down Expand Up @@ -74,7 +80,7 @@
}
},
"additionalProperties": false,
"required": ["module", "name"],
"required": ["name"],
"presets": []
},
"description": "Adds NgRx support to an application or library.",
Expand Down
39 changes: 36 additions & 3 deletions e2e/angular-extensions/src/ngrx.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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/);
Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
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) {
return;
}

const indexFilePath = joinPathFragments(
dirname(options.module),
options.parentDirectory,
'..',
'index.ts'
);
Expand Down
32 changes: 16 additions & 16 deletions packages/angular/src/generators/ngrx/lib/add-imports-to-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -30,7 +30,7 @@ export function addImportsToModule(
return insertImport(
tree,
source,
modulePath,
parentPath,
symbolName,
fileName,
isDefault
Expand Down Expand Up @@ -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
);

Expand All @@ -89,7 +89,7 @@ export function addImportsToModule(
sourceFile = addImportToModule(
tree,
sourceFile,
modulePath,
parentPath,
storeRouterModule
);
}
Expand All @@ -103,7 +103,7 @@ export function addImportsToModule(
sourceFile = addProviderToModule(
tree,
sourceFile,
modulePath,
parentPath,
facadeName
);
}
Expand All @@ -117,13 +117,13 @@ export function addImportsToModule(
sourceFile = addImportToModule(
tree,
sourceFile,
modulePath,
parentPath,
storeForRoot
);
sourceFile = addImportToModule(
tree,
sourceFile,
modulePath,
parentPath,
effectsForRoot
);

Expand All @@ -136,15 +136,15 @@ export function addImportsToModule(
sourceFile = addImportToModule(
tree,
sourceFile,
modulePath,
parentPath,
storeRouterModule
);
}

sourceFile = addImportToModule(
tree,
sourceFile,
modulePath,
parentPath,
storeForFeature
);
} else {
Expand All @@ -153,13 +153,13 @@ export function addImportsToModule(
sourceFile = addImportToModule(
tree,
sourceFile,
modulePath,
parentPath,
storeForFeature
);
sourceFile = addImportToModule(
tree,
sourceFile,
modulePath,
parentPath,
effectsForFeature
);
}
Expand Down
25 changes: 14 additions & 11 deletions packages/angular/src/generators/ngrx/lib/generate-files.ts
Original file line number Diff line number Diff line change
@@ -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`
)
Expand Down
12 changes: 11 additions & 1 deletion packages/angular/src/generators/ngrx/lib/normalize-options.ts
Original file line number Diff line number Diff line change
@@ -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,
};
}
30 changes: 30 additions & 0 deletions packages/angular/src/generators/ngrx/ngrx.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);

Expand Down
Loading

1 comment on commit 16a0891

@vercel
Copy link

@vercel vercel bot commented on 16a0891 Jan 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

nx-dev – ./

nx-dev-git-master-nrwl.vercel.app
nx-five.vercel.app
nx-dev-nrwl.vercel.app
nx.dev

Please sign in to comment.