Skip to content

Instantly share code, notes, and snippets.

@ThomasBurleson
Last active June 15, 2023 13:32
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ThomasBurleson/30e45a6bc61f0d53fe07045d69b548e6 to your computer and use it in GitHub Desktop.
Save ThomasBurleson/30e45a6bc61f0d53fe07045d69b548e6 to your computer and use it in GitHub Desktop.
Custom Nx workspace schematic to build a custom 'state' library.

Scenario

We want to build a custom schematic for company Aero that will internally do two things:

  • build a new ngrx library using Nx workspace naming conventions
  • generate ngrx state-management files; include the *.facade.* files

Important: This example is valid ONLY for Nx v6.2.x or higher!


Steps to Build/Use Custom Schematics

  1. Use the following command to generate a custom schematic called ngrx-lib.

This custom workspace schematic will:

  • Generate a Nx library
  • Generate NgRx files inside a folder called +state.
  • Generate NgRx Facade files for the new NgRx state management files
ng g workspace-schematic ngrx-lib
  1. Then open the new empty schematic at /tools/schematics/ngrx-lib/index.ts and replace the contents with your own custom logic.

nx-custom-schematic

The schematic code for our requirements is here :

/tools/schematics/ngrx-lib/index.ts
import {
  chain,
  externalSchematic,
  Rule
} from '@angular-devkit/schematics';
import * as path from 'path';

/**
 * Build a new custom Nx library with ngrx files.
 * Using Nx workspace conventions, these custom libraries are 'state' libraries since 
*  they will manage ngrx state, REST endpoints, etc.
 */
export default function(schema: any): Rule {
  const PREFIX = 'state-';
  if (!schema.name.startsWith('state-') && (schema.name != 'state')) {
    // custom libraries managing state must have name conventions: 'state' or 'state-<name>'
    schema.name = PREFIX + schema.name;
  }

  const name = schema.name.substring(PREFIX.length);
  const libPath = schema.directory ? path.join(schema.directory, schema.name) : schema.name;
  const moduleName = schema.directory ? `${schema.directory}-${schema.name}` :  schema.name;
  const module = path.join('libs',libPath, 'src/lib', `${moduleName}.module.ts`);

  return chain([
    externalSchematic('@nrwl/schematics', 'lib', {
      name: schema.name,
      directory : schema.directory,
      tags :  schema.directory ? `state, ${schema.directory}` : 'state, aero'
    }),
    externalSchematic('@nrwl/schematics', 'ngrx', {
      name,
      module,
      directory: '+state',
      facade : true
    })
  ]);
}
  1. Now you can use the custom workspace schematic (in your workspace).

In our example, we want a new state library called for airports and we want to group that library in a directory called trip-planner:

npm run workspace-schematic ngrx-lib airports -- --directory=trip-planner
# or 
yarn  workspace-schematic ngrx-lib planes --directory=trip-planner

Note the extra -- when using npm. This is required so these options are considered options for the workspace-schematic instead of npm itself.

This would output to the console something similar to:

create libs/trip-planner/state-planes/karma.conf.js (508 bytes)
create libs/trip-planner/state-planes/tsconfig.lib.json (828 bytes)
create libs/trip-planner/state-planes/tsconfig.spec.json (283 bytes)
create libs/trip-planner/state-planes/tslint.json (252 bytes)

create libs/trip-planner/state-planes/src/index.ts (205 bytes)
create libs/trip-planner/state-planes/src/test.ts (700 bytes)

create libs/trip-planner/state-planes/src/lib/trip-planner-state-planes.module.ts (720 bytes)
create libs/trip-planner/state-planes/src/lib/trip-planner-state-planes.module.spec.ts (445 bytes)

create libs/trip-planner/state-planes/src/lib/+state/planes.actions.ts (727 bytes)
create libs/trip-planner/state-planes/src/lib/+state/planes.effects.spec.ts (1158 bytes)
create libs/trip-planner/state-planes/src/lib/+state/planes.effects.ts (833 bytes)
create libs/trip-planner/state-planes/src/lib/+state/planes.facade.spec.ts (2848 bytes)
create libs/trip-planner/state-planes/src/lib/+state/planes.facade.ts (606 bytes)
create libs/trip-planner/state-planes/src/lib/+state/planes.reducer.spec.ts (1130 bytes)
create libs/trip-planner/state-planes/src/lib/+state/planes.reducer.ts (960 bytes)
create libs/trip-planner/state-planes/src/lib/+state/planes.selectors.spec.ts (1596 bytes)
create libs/trip-planner/state-planes/src/lib/+state/planes.selectors.ts (962 bytes)

Notice how trip-planner-state-planes.module.ts has export class TripPlannerStatePlanesModule and uses the <grouping-folder>-<library-name>.module.ts convention. This allows developers to easily determine the folder and library associated with that ngModule.

@CharlieGreenman
Copy link

Does the above support camelCase?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment