Add schematics to Angular library

Alain Boudard
6 min readFeb 16, 2022

And publish it and use it.

This article is an attempt to simplify an existing documentation page from Angular, about how we can add schematics to a library.

We will go through the whole process up until publishing, so there will be very little code about code scaffolding itself. If you’re looking for details about possible code actions offered by Angular schematics, check some articles at the bottom of this page.

The most important part of this is about packaging the schematics properly along with the library, so you simply have to be carefull with the relative paths you are working with.

Create workspace

We start with an empty workspace, it will contain a sandbox app and a library.

ng new ng-library-schematics --create-application false --skip-install true

Create the library

We create a simple Angular library and add a script to build it :

ng g library @aboudard/ng-lib

In root package.json :

"scripts": {
"build:lib": "ng build @aboudard/ng-lib",
Build Angular library
Build Angular library

We need to build the library before we use any of its components, since root tsconfig.json is refering to /dist folder :

"paths": {
"@aboudard/ng-lib": [
"dist/aboudard/ng-lib/aboudard-ng-lib",
"dist/aboudard/ng-lib"
]

Create the application

Let’s consume the library in an application in order to check if everything is fine.

ng g application sandbox --routing --style scss

In our app, we can simply import the library exported Module and add the component in our app :

Import library Module in Angular app
<h1>{{title}}</h1>
<lib-ng-lib></lib-ng-lib>

The library component is displayed in our app, so everything works fine. We can start adding the schematics.

Create the library schematics

We fill follow the process described in this Angular doc page.

  • Install the proper version of the tools :
@angular-devkit/schematics
  • Add a /schematics folder in your library
  • Create collection.json in this folder (this is the most important file of this process)
Relative path depending on your library

Beware of the relative path where your library is located, this must be reflected in the collection.json $schema variable.

  • Create a /ng-add folder
  • Create an index.ts file within this folder, use the name declared in previous factory variable, here it’s ngAdd (very simple, since we won’t make relevant work here) :
export function ngAdd(): Rule {
return (tree: Tree, context: SchematicContext) => {
context.logger.log('info', `✅️ Running Schematics`);
context.addTask(new NodePackageInstallTask());
return tree;
};
}

Note that the NodePackageInstallTask is pretty useless here, because we don’t install nothing except the library itself, and ng-add command is already installing like the npm i would do.

  • Reference the collection.json in the package.json of your library : this is very important because during the execution of the schematics, it will be needed. And note that for now, the file is not where we declare it :
Reference the collection.json file in library package.json

Configure the build of the schematics

The point is to build it in the same folder as the library. It means we want the .js files in the library /dist folder, but also the collection.json, just like we mentioned.

  • Create the tsconfig.schematics.json file in the library root folder, this is the instruction to the typescript compiler, and most important is outDir :
{
"compilerOptions": {
"baseUrl": ".",
"lib": ["es2018","dom"],
"declaration": true,
"module": "commonjs",
"moduleResolution": "node",
"noEmitOnError": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noImplicitThis": true,
"noUnusedParameters": true,
"noUnusedLocals": true,
"rootDir": "schematics",
"outDir": "../../../dist/aboudard/ng-lib/schematics",
"skipDefaultLibCheck": true,
"skipLibCheck": true,
"sourceMap": true,
"strictNullChecks": true,
"target": "es6",
"types": ["jasmine","node"]
},
"include": ["schematics/**/*"],
"exclude": ["schematics/*/files/**/*"]
}

You should test the build with a script in your library to see if your paths are correct (and later call it from your main workspace package.json) :

  • Install in your main project copyfiles via npm.
  • Use it to copy relevant files to /dist folder especially collection.json, only after the build, with a “post” script.

So we run the build script and see if the files end up in the right folder.

Generate the schematics build files in lib dist folder.

Note: you could skip this copyfiles part by simply exporting the schematics folder files with the assets configuration of your library as follows :

In your ng-package.json file, add the reference to the new schematics folder :

"assets": [
"./styles/*.*",
"./assets/**/*.*",
"./schematics/**/*.*"
]

In this case, you will copy all files present in the folder at build time, unless you target more specific files than **/*.*

Also note that if you do that, you could change the outDir of your tsconfig file, and leave the compiled .js files in the ./schematics folder, since they will be copied in the dist folder during the library build :

"outDir": "./schematics",

Ok, the files are where they are supposed to be. The last part of the package.json tells what kind of install you want for your library through the ng add command. If you expose components, it probably should end up in dependencies, but if it’s just scripting, it would be devDependencies, and maybe you don’t even want to reference it at all but only use it one time.

Test your library locally

A good way to test a library, with schematics or not, is a sandbox application. With the help of npm link command, we can pretend the library is installed and call the ng-add from it.

Here are the scripts that will help us :

"link:lib": "cd dist/aboudard/ng-lib && npm link",
"link:app": "cd projects/sandbox && npm link @aboudard/ng-lib",
"link:all": "npm run link:lib && npm run link:app",
"link:rm": "npm rm -g @aboudard/ng-lib",
"sandbox:ng-add": "cd projects/sandbox && ng g @aboudard/ng-lib:ng-add",

Publish your library and test schematics

Now it is time to see if it all works in real situation, meaning when we try to add the library to a blank project.

The build process should be :

  • Bump the library version if needed (better if you plan to publish)
  • Build the library
  • Build the schematics
  • Pack the library dist folder
  • Login with your registry (npm login)
  • Publish your library
"build:lib": "ng build @aboudard/ng-lib",
"build:schematics": "tsc -p projects/aboudard/ng-lib/tsconfig.schematics.json",
"postbuild:schematics": "copyfiles projects/aboudard/ng-lib/schematics/collection.json dist/aboudard/ng-lib/schematics/ -f",
"build:all": "npm run build:lib && npm run build:schematics",
"pack:lib": "cd dist/aboudard/ng-lib && npm pack",
"lib:all": "npm run build:all && npm run pack:lib",
"publish:all": "npm run lib:all && npm publish dist/aboudard/ng-lib",

The end result is published indeed here on npmjs.com.

Test the library, install schematics

And now let’s just test this with a new project.

Runnning ng add schematics with an Angular library

All seems to be fine, note :

  • The logger is ok, this is what we wrote in the ngAdd function
  • The package was installed twice, because of the task we mentioned earlier

Here is the repo, but you figured it already from the npmjs page ;)

https://github.com/aboudard/ng-library-schematics

References

I walked through these articles to gather some of informations :

--

--