🚡 Nx Targets Elevated (Part One)

Unlocking the Full Potential of Nx: A Comprehensive Guide to Centralizing Targets for Enhanced Efficiency

Jonathan Gelin
3 min readJul 23, 2023

That version of the Nx plugin will be deprecated in v18. Please check the new way of doing in my article 💎 Discovering Nx Project Crystal’s Magic

If you’ve ever worked with Nx, you know how invaluable it can be for maintaining large-scale codebases within an organization. From applications and libraries to executors, generators, and plugins, Nx offers a powerful set of tools to streamline development.

However, as projects grow, maintaining hundreds of libraries and managing thousands of lines of duplicated configurations can become cumbersome.

In this article, we’ll explore a lesser-known aspect of the Nx architecture that addresses this issue — centralizing target configurations. By leveraging this approach, we can eliminate duplication and simplify our development process significantly.

How does it work?

When initiating a target build on the “my-app” project, the run command will not only consider targets specified in the related project.json but also check for targets provided by registered Nx plugins. By merging all relevant targets, it efficiently calls the build target.

This means that we can define all reusable targets within a local plugin instead of cluttering our project.json.

How to do it?

Create an Nx plugin

To begin centralizing targets, the first step is to create an Nx plugin. Execute the following command:

$ nx g @nx/plugin:plugin my-plugin

Don’t forget to register your plugin in your nx.json configuration file:

{
"$schema": "./node_modules/nx/schemas/nx-schema.json",
...
"plugins": ["@my-org/my-plugin"]
}

Export your targets from the plugin

To enable dynamic targets, we need to implement and export the registerProjectTargets function in the plugin's index.ts:

import { TargetConfiguration } from '@nx/devkit';

export const projectFilePatterns = ['project.json'];

export function registerProjectTargets(projectFilePath: string): Record<string, TargetConfiguration> {
return {
build: {
"executor": "@nx/vite:build",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "production",
"options": {
"outputPath": "dist/packages/my-app"
},
"configurations": {
"development": {
"mode": "development"
},
"production": {
"mode": "production"
}
}
},
};
}

As you can see, the configuration mirrors that of your project.json target configuration.

Don’t forget to export the projectFilePatterns function, which is essential for project inference.

Customize your configuration

Since this target will be invoked for all projects, customization may be necessary. To do this, modify the registerProjectTargets function:

export function registerProjectTargets(projectPath: string): Record<string, TargetConfiguration> {

const normalizedPath = normalizePath(projectPath);
const projectRoot = normalizedPath.replace('/project.json','');
const projectName = projectRoot.split('/').pop();

return {
build: {
"executor": "@nx/vite:build",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "production",
"options": {
"outputPath": `dist/packages/${projectName}`
},
"configurations": {
"development": {
"mode": "development"
},
"production": {
"mode": "production"
}
}
},
};
}

In this example, we’ve customized the output path, but any other configurations can be tailored to your specific needs.

Clean up your project.json

With your targets centralized, you can now remove the build target from all application configurations:

{
"name": "my-app",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"sourceRoot": "packages/my-app/src",
"targets": {
// build target can be removed here
"serve": {
...
},
"preview": {
...
},
"test": {
...
},
"lint": {
...
}
}
}

Iterate again for the other targets

Apply the same principles to other targets such as lint, test, serve, and so on.

Conclusion

Incorporating Nx’s centralized target approach offers more than just code generation and execution capabilities. It provides a powerful way to streamline your configuration management.

By adopting this method, you can effortlessly maintain your architecture without duplicating configurations. Any modifications made to configurations will only need to be applied once, benefiting all projects using them.

Additionally, this streamlined approach simplifies generators, as they’ll generate fewer configurations.

Example

You can find an example on my repository here: https://github.com/jogelin/register-nx-targets

Looking for some help? 🤝
Connect with me on TwitterLinkedIn Github

--

--

Jonathan Gelin

Write & Share about SDLC/Architecture in Ts/Js, Nx, Angular, DevOps, XP, Micro Frontends, NodeJs, Cypress/Playwright, Storybook, Tailwind • Based in 🇪🇺