- Understand what an internal plugin is
- Understanding generators in internal plugin, including:
- How to create them
- How to invoke them
- How to use one to simplify usages of other, more powerful generators
Add the @nx/plugin plugin with nx add.
🐳 Hint
```bash
npx nx add @nx/plugin
```
Generate a new plugin called internal-plugin in the libs directory. This step initializes the necessary setup for your custom generator.
🐳 Hint
```bash
nx generate @nx/plugin:plugin libs/internal-plugin
```
Use the @nx/plugin:generator generator to generate a new generator called util-lib. Inspect the files that got generated and then commit everything.
🐳 Hint: See list of plugins
Run npx nx list to see the list of installed plugins. Then run npx nx list @nx/plugin to see what generators are available.
🐳 Solution
To generate a generator into our `@nx-workshop\internal-plugin` plugin, we can run:```bash
npx nx generate @nx/plugin:generator libs/internal-plugin/src/generators/util-lib --name=util-lib
```
Try to run your generator to see what changes are being made (you can append --dry-run to avoid reverting using Git).
We can call other generators inside of our custom generator. Import the @nx/js:library generator and call it inside of the default exported function of libs/internal-plugin/src/generators/util-lib/generator.ts
🐳 Hint
import { libraryGenerator } from '@nx/js';
export default async function (tree: Tree, options: UtilLibGeneratorSchema) {
await libraryGenerator(tree, {
directory: options.name,
});
// comment the rest of the code
await formatFiles(tree);
}In libs/internal-plugin/src/generators/util-lib/generator.ts try to make it console.log() the value of the --name property you passed to it (can use --dry-run again to test it)
The generator should prefix any name you give to your lib with util-. For example:
nx generate @nx-workshop/internal-plugin:util-lib dates- Should generate a lib with the name
util-dates
--dry-run flag.️
Add a new property to its schema called directory. It should have only 3 possible values:
"movies", "api", "shared". If you do not pass --directory as an option when invoking the
schema it should prompt the user to select from the 3 different values (similar to when you got
asked about which bundler to use when creating libs). The generator should generate the lib in the directory you pass to it:
movies->libs/movies/{lib name}
🐳 Hint
Because it's a util lib, it should automatically be generated with the type:util tags. We also need to add scope tag to it. We can use the directory value for this, since it signifies our scope e.g. scope:movies.
🐳 Hint
Consult the @nx/js:lib docs
for possible options you can pass to it.
Let's add some functionality to the lib you just created:
- Generate a new lib called
util-notificationsin theapidirectory using our new generator - In
libs/api/util-notifications/src/lib/api-util-notifications.ts - Add:
export function sendNotification(clientId: string) { console.log('sending notification to client: ', clientId); }
Now try to import the above function in apps/movies-api/src/main.ts
- Try to lint all the apps
- It should work because everything is in the
apiscope
Try to import it in apps/movies-app/src/app/app.component.ts
- It should fail because it's not within the same scope
A generator.spec.ts file was created when we ran our generator. Try writing some meaningful tests for this generator. Use createTreeWithEmptyWorkspace from @nx/devkit/testing to generate in memory Tree and readProjectConfiguration from @nx/devkit to parse the contents of your project configuration.
🐳 Solution
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { Tree, readProjectConfiguration } from '@nx/devkit';
import generator from './generator';
import { UtilLibGeneratorSchema } from './schema';
describe('util-lib generator', () => {
let appTree: Tree;
const options: UtilLibGeneratorSchema = { name: 'foo', directory: 'movies' };
beforeEach(() => {
appTree = createTreeWithEmptyWorkspace();
});
it('should add util to the name and add appropriate tags', async () => {
await generator(appTree, options);
const config = readProjectConfiguration(appTree, 'movies-util-foo');
expect(config).toBeDefined();
expect(config.tags).toEqual(['type:util', 'scope:movies']);
});
});You learned how to generate plugis and create your own generators. In the next step we will learn how to build even more complex generators.