-
Notifications
You must be signed in to change notification settings - Fork 0
Nx Generators
Generators can be used with Nx to perform a number of tasks that can help build towards automation in our code. For example, Nx Generators can be used to quickly create scaffolded files for different necessary infrastructure in a project and/or update that infrastructure as the project continues and as necessary. Nx Generators can also be used for tasks such as quickly creating components with the CLI for our React repo, or for creating our own Generators to help work towards automating tasks that are specific to our project.
Sticking to a set of Patterns in our Code - By creating templates, it is possible to ensure a set of standards for each set of files generated with an Nx generator.
Speed and Time Management in our Process - Nx generators will enable us to strive towards automation in our projects. Consider for instance, repetitive code processes that are the same in a wide range of files or components. It should be possible to create layouts through templates to speed up this process of writing repetitive code. Or consider possible updates to our infrastructure that could perhaps be automated through Nx generators to update without having to do so manually for certain tasks.
Consistency between Projects in our Monorepo - One potential issue with a monorepo setup could be issues with code from one project affecting code in another one of our projects. But with generators it is possible to create tags that can help to specify each project and the specifications for each. For instance, using a JavaScript/TypeScript project, you can use a .eslintrc.json
file for configuring linting rules, using specific tags to point to which project each set of linting rules applies for.
To create generators, you first must create a plugin folder to store your generators. Nx generators are always located with an Nx Plugin folder.
Plugins - these allow users to customize and add new features to their core application without modifying the original codebase. Within a project, you create a Plugin file, which will hold your generators in a unique location. They are designed to be independent of the core project while also enhancing the project by adding new, unique capabilities that can be utilized.
Creating a Plugin for Nx Generators:
Format:
nx add @nx/plugin
nx generate [plugin]:[generator-name] [options]
Example:
nx add @nx/plugin
nx g @nx/plugin:plugin my-plugin
Then you are able to create your own generators as necessary, depending on your project layout.
Reference - (https://nx.dev/features/generate-code)
You can use the nx generate
command to generate 1.) Plugin generators, 2.) Local generators, and 3.) Update generators:
(If you do not have
nx
installed globally, it is possible to usenpx nx generate
,yarn nx generate
, orpnpx nx generate
).
Plugin Generators - are tools used for automating the process of creating plugins for a particular existing framework or platform. (For example, you might use plugin generators to create frameworks for Angular applications, React components, or using Express.js to generate a proxy server).
- Nx Plugins Registry:
To see a list of available community plugins visit (https://nx.dev/plugin-registry)
- Examples of Plugin Generator Commands:
-Generating a React Component:
nx generate @nx/react:component mycmp --project=myapp
-Generating a new Angular application:
nx generate @nx/angular:app myapp
-Generating a Node application:
nx generate @nx/node:app myapp
- List of Generator References available specifically for React:
eg.) To create a React application, library, component, etc. (https://nx.dev/nx-api/react/generators)
- Example of Using a Plugin Generator in the Something Fishy Project:
1.) Ran
yarn install
.On 'Yarn':
Like
npm
("node package manager"),yarn
is a package manager that is widely used with JavaScript. It is a popular choice for managing dependencies in Node.js projects due to its speed, reliability, and features.
2.) Ran
nvm use 18.18.0
to update what node version the project was using.
3.) Ran
yarn add -D ts-jest
to make sure that ts-jest was installed as a dependency.
4.) Ran
yarn nx generate @nx/node:application cli - -directory=app
to generate the @nx/node plugin with a package named cli that resides within a directory called app.
5.) Changed the module value to
"module": "NodeNext"
in the tsconfig.app.json file.
6.) Removed the app/cli-e2e directory that was generated and unnecessary for our project.
Reference - (https://nx.dev/extending-nx/recipes/local-generators)
Local Generators - Typically refers to creating tools and scripts that generate code or scaffold project structures that are unique to your own project and workspaces. For instance, you could create a Local Generator to automate repetitive coding processes and set up new projects more quickly, using unique customizations that are meant specifically for your project.
a.) If you require a Local Plugin, use Nx to generate one:
nx add @nx/plugin
nx g @nx/plugin:plugin my-plugin
b.) Next you can use the CLI to generate your Nx generator files, which should be created in the plugin/generators folder:
nx generate @nx/plugin:generator my-generator --project=my-plugin
c.) Now several files should be added to your generator folder: 1.) schema.json, 2.) schema.d.ts, 3.) generator.spec.ts and 4.) generator.ts.
- schema.json - contains the JSON schema that defines structure and validation rules that make up the generator’s configuration (e.g. “properties” such as “name”, “description”, “x-prompt” for questions to prompt a user with when creating the generator, and “required” for required entry fields).
- schema.d.ts - serves as a TypeScript declaration file (.d.ts) that defines the TypeScript type definitions for the generator schema. It specifies the structure and properties of the input options and/or configuration that the generator accepts from users (see the options object within the generator.ts file). By defining TypeScript types for the schema, this file provides type safety and IDE support during generator configuration (e.g. auto-completion and type checking).
- generator.ts - entry point for the generator. Here you will find a function that performs manipulations on a tree object (a virtual representation of the file system), as well as an options object that refers to the schema.d.ts file (a TypeScript definition of the schema.json file).
- generator.spec.ts - contains unit tests for the generator.
d.) In the generator.ts file, you should see the 'Generator Function', which can be used to create new libraries:
import { Tree, formatFiles, installPackagesTask } from '@nx/devkit';
import { libraryGenerator } from '@nx/js';
export default async function (tree: Tree, schema: any) {
await libraryGenerator(tree, { name: schema.name });
await formatFiles(tree);
return () => {
installPackagesTask(tree);
};
}
e.) The 'Generator Function' Details:
1.) Imports:
- As you can see, the Tree object, formatFiles and installPackagesTask are imported from the ‘@nx/devkit’ library. These utilities provided by Nx allow us to work with the project’s file system (‘Tree’), to format files, and install packages.
- ’@nx/devkit’ - this library contains utility functions for reading and writing files, updating configuration, and working with Abstract Syntax Trees(ASTs) in Nx projects. (Check out Enumerations, Classes, Interfaces, Type Aliases, Variables and Functions included in the ‘@nx/devkit’ at https://nx.dev/nx-api/devkit/documents/nx_devkit).
- The libraryGenerator is also imported from the ‘@nx/js’ plugin, and, as the name implies, is responsible for generating new libraries within the Nx workspace.
- ‘@nx/js’ - a JS plugin, including executors and generators for JavaScript and TypeScript projects in an Nx workspace. With ‘@nx/js’, we can create, build, test, and lint libraries, amongst other things. (For more information checkout ‘@nx/js’ at https://nx.dev/nx-api/js).
On Scope: In npm package names, the "@" symbol denotes a scoped package. Scoped packages are a way to group related npm packages under a common namespace, often associated with a particular organization or project. In the case of Nx, the ‘@nx’ scope is used to group all packages related to the Nx ecosystem. This helps to organize and distinguish Nx-related packages from other npm packages.
2.) Exported Function/Function Body:
- We can use this 'Generator Function' to invoke other generators. To use the functionality of another generator within our 'Generator Function', import the entry point function from said generator and run it against the project's file tree system.
- This means we will be able to perform specific tasks or create new files or scaffold new packages by leveraging the 'Generator Function'.
- By running an imported generator function against the tree, we can trigger that generator's logic and manipulate the file system defined by that generator's implementation.
- On Async/Await: Asynchronous operations, used here, will make the code flow more like procedural code, while enhancing readability and maintainability.
- Optionally, we will be able to return a callback function. This callback function, if returned, is executed after the changes to the file system are applied. This will provide a way to perform additional tasks or actions as necessary, after the main generator logic is completed.
- In the provided code snippet, the callback function returned is responsible for installing any required packages after the file system manipulations have been performed. This ensures that once the new library from libraryGenerator is generated and the new file tree system has been formatted (Tree), the required packages are installed.
f.) The 'schema.json' File:
{
"cli": "nx",
"id": "test",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Library name",
"$default": {
"$source": "argv",
"index": 0
}
}
},
"required": ["name"]
}
Reference - (https://nx.dev/recipes/adopting-nx)
Update Generators - (a.k.a. migration or update scripts) - used to automate the process of updating and/or moving existing code/data to newer versions. Update generators can analyze your existing code or data, identify differences between the current and target/updated versions, then apply the necessary modifications to the code/data to ensure compatibility.