Custom schematics with nx workspace

Publiziert am 19 Juli 2018 unter Angular, Javascript von David

Angular CLI is great for scaffolding components, services, pipes and so on. But often you find yourself changing a lot of stuff after scaffolding because of company coding conventions, best practices or simply because of your preferences. 

Wouldn't it be great if you could scaffold components with Angular CLI using your custom templates?

Following the excellent blog post by Tomas Trajan, we found that some things have changed with the current tool versions. So here is my updated step-by-step instruction on how to get a custom component up and running with Angular CLI 6.0.8.

For simplicity, I also used the default Angular component as a template and modified it.

Step 1: Add global dependencies

npm i -g @angular-devkit/schematics @angular-devkit/schematics-cli @angular-devkit/core @schematics/schematics

also, install Nrwl schematics

npm i -g @nrwl/schematics

Step 2: Create a new NX workspace and add a new app

ng new schematicsdemo --collection=@nrwl/schematics
ng g app demo-app

Step 3: Add project dependencies

npm i @schematics/angular --save
npm i typescript@2.7.2 --save

Step 4: Go to the tools folder of your nx workspace and create a new schematic collection:

schematics @schematics/schematics:schematic --name demo-collection .

This creates a collection template with some examples.

Delete all the demo folders “my-full-schematic”, “my-other-schematic” and “my-schematic”, delete the contents under “schematics” in collection.json and replace it with your new schematics path:

collection.json

{
  "$schema": "./node_modules/@angular-devkit/schematics/collection-schema.json",

  "schematics": {
    "custom-component": {
      "factory": "./custom-component",
      "schema": "./custom-component/schema.json",
      "description": "Create a custom Angular component."
    }
  }
}

Step 5: Update .gitignore

The default .gitignore file of the schematics collection contains *.js, *.js.map and *.d.ts. Remove these entries to allow git to check in the transpiled js files.


# Outputs
# src/**/*.js
# src/**/*.js.map
# src/**/*.d.ts

# IDEs
.idea/
jsconfig.json
.vscode/

# Misc
node_modules/
npm-debug.log*
yarn-error.log*

# Mac OSX Finder files.
**/.DS_Store
.DS_Store
  

Next, create an empty directory with your component name in the “src” directory (custom-component in my case).

Step 6:Download the default angular schematics files as a starting template

I used these, they work with CLI 6.0.8

Note that in the newest Angular CLI beta, they moved the devkit stuff back to the main cli Repo, so these may work in the future: https://github.com/angular/angular-cli/tree/master/packages/schematics/angular

Once extracted, you can copy the contents of the “Component” Directory and paste it in the “custom-component” directory of your collection. Your structure should now look like this:

Folder structure

Step 7: Fix imports

The index.ts and index_spec.ts in custom-component contain relative imports that don't work anymore. Instead of

../utility/

use

@schematics/angular

Result:

index.ts

import * as ts from 'typescript';
import { addDeclarationToModule, addExportToModule } from '@schematics/angular/utility/ast-utils';
import { InsertChange } from '@schematics/angular/utility/change';
import { getWorkspace } from '@schematics/angular/utility/config';
import { buildRelativePath, findModuleFromOptions } from '@schematics/angular/utility/find-module';
import { parseName } from '@schematics/angular/utility/parse-name';
import { validateHtmlSelector, validateName } from '@schematics/angular/utility/validation';
import { Schema as ComponentOptions } from './schema';

index.spec.ts

import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing';
import * as path from 'path';
import { Schema as ApplicationOptions } from '@schematics/angular/application/schema';
import { createAppModule } from '@schematics/angular/utility/test';
import { Schema as WorkspaceOptions } from '@schematics/angular/workspace/schema';
import { Schema as ComponentOptions } from './schema';

Step 8: Transpile

After you fix the imports, test if you got everything right by running

npm run build

in the tools/demo-collection directory.

Step 9: Modify the templates and transpile again

Now to the most important part!

You can now modify the files under custom-component/files according to your needs. I simply added the message “This is a custom component!” in __name@dasherize__.component.html Don't forget to build again after you have changed anything.

Modified __name@dasherize__.component.html template

Html template

Or here I modified __name@dasherize__.component.ts to always use ChangeDetection.OnPush

Tstemplate

Step 10: Time to use the schematics in a project!

You can reference your collection in package.json using file syntax:

package.json

"demo-collection": "file:./tools/demo-collection" 

Then run

npm i

in the nx workspace. This will install your new schematics to node_modules and you can use them without typing the full path.

You should now be able to create a new custom component:

ng g demo-collection:custom-component DemoPage --app=demo-app

It works!

> C:\customschematicsdemo> ng g demo-collection:custom-component DemoPage --app=demo-app
CREATE apps/demo-app/src/app/demo-page/demo-page.component.html (73 bytes)
CREATE apps/demo-app/src/app/demo-page/demo-page.component.spec.ts (668 bytes)
CREATE apps/demo-app/src/app/demo-page/demo-page.component.ts (306 bytes)
CREATE apps/demo-app/src/app/demo-page/demo-page.component.css (0 bytes)
UPDATE apps/demo-app/src/app/app.module.ts (453 bytes)
> C:\customschematicsdemo>

To prove that it indeed worked, I used the component in app.component.html

Run npm start to serve the app on http://localhost:4200

ng serve on localhost

Additional Tipps

Of course you could publish your collection to a separate repository and reference it in package.json. Useful for company wide schematics.

Also, you don’t need to add it to package.json. But then you have to give the full path to your collection when generating a component:

ng g path/to/demo-collection:custom-component DemoPage

To generate a component into an nx workspace library project:

ng g demo-collection:custom-component DemoPage –-project=lib1

You don’t have to use an nx workspace, to generate a component in a normal angular app, run

ng g demo-collection:custom-component DemoPage

Links

This complete example is available on Github: https://github.com/Lambda-IT/customschematicsdemo