UPDATE 2024–04-03:
An official way now exists to exclude or include plugins for a specific project:
feat(core): add ability to scope plugins #22379
There is no way to users to filter where plugins are applied. Only by the plugin author.
This means that errors thrown make the plugin unusable and more modifications to the project graph are made than desired.
Plugins can be scoped via include
and exclude
which are both lists of file patterns.
{
plugins: [
{
plugin: '@nx/jest/plugin',
include: ['packages/**/*'],
exclude: ['e2e/**/*']
}
]
}
This will scope running the plugin to processing only those config files.
Fixes #
If we take the example below, you can now use the approach:
{
"plugins": [
{
"plugin": "@nx/jest/plugin",
"options": {
"targetName": "test",
"exclude": ["my-lib-e2e/**/*"]
}
}
]
}
Since the release of Nx 18, many repositories have started the adoption of Nx Project Crystal. As it’s still the beginning, the feature to exclude some projects from the dynamic target assignment is not supported yet.
In this article, I want to share workarounds and ideas until the Nx team implements something internally.
If you’re looking to understand how Nx assigns targets to projects dynamically, I first recommend reading my article:
Workarounds
Let’s specify the context by mentioning that you have a project named my-lib-e2e
and you don't want that project to have a test target even if it contains a jest.config.ts
. However, you still want to get the benefit of the @nx/jest/plugin
for your other 1000 Nx projects 😋.
Manually Override the “test” Target with nx:noop
In your my-lib-e2e/project.json
:
{
"name": "my-lib-e2e",
"targets": {
"test": {}
}
}
Given that project.json
takes precedence over all plugins, the inferred test
target from the @nx/jest/plugin
will be overridden.
Dynamically Override Any Target with nx:noop
You can also develop a plugin to automatically assign nx:noop
on any target. For instance, I crafted a plugin located at tools/nx-plugins/targets-noopify.ts
:
import { CreateNodes, ProjectConfiguration, readJsonFile } from '@nx/devkit';
import { dirname, join } from 'node:path';
interface TargetsNoopifyOptions {
projectName: string;
targets: string[];
}
export const createNodes: CreateNodes<TargetsNoopifyOptions> = [
'**/project.json',
async (configFilePath, options, { workspaceRoot }) => {
const projectRoot = dirname(configFilePath);
// get project name from porject.json
const projectJson = readJsonFile<ProjectConfiguration>(
join(workspaceRoot, configFilePath)
);
const projectName = projectJson.name;
// check if the target of the project should be noopified
const targetsToStub = options[projectName];
if (!targetsToStub) {
return {};
}
// override the targets
const targets = targetsToStub.reduce(
(acc, targetName) => ({
...acc,
[targetName]: {},
}),
{}
);
return {
projects: {
[projectRoot]: {
root: projectRoot,
targets: targets,
},
},
};
},
];
This plugin should then be listed last in nx.json
:
{
"plugins": [
{
"plugin": "@nx/jest/plugin",
"options": {
"targetName": "test"
}
},
// MAKE SURE THIS COMES AFTER THE PLUGIN YOU WISH TO OVERRIDE
{
"plugin": "./tools/nx-plugins/targets-noopify.ts",
"options": {
"my-lib-e2e": ["test"]
}
}
]
}
Create Your Custom @nx/jest/plugin
and Skip the Projects
Alternatively, you could implement your custom Jest plugin that filters projects. Here’s an example plugin tools/nx-plugins/skip-jest-plugin.ts
:
import {
CreateNodes,
joinPathFragments,
ProjectConfiguration,
readJsonFile,
} from '@nx/devkit';
import { dirname, join } from 'node:path';
import {
JestPluginOptions,
createNodes as createJestNodes,
} from '@nx/jest/plugin';
import { readdirSync } from 'fs';
export const createNodes: CreateNodes<
JestPluginOptions & { skipProjects: string[] }
> = [
createJestNodes[0],
async (configFilePath, options, context) => {
const projectRoot = dirname(configFilePath);
const siblingFiles = readdirSync(join(context.workspaceRoot, projectRoot));
if (!siblingFiles.includes('project.json')) {
return {};
}
const path = joinPathFragments(projectRoot, 'project.json');
const projectJson = readJsonFile<ProjectConfiguration>(path);
const projectName = projectJson.name;
if (options.skipProjects.includes(projectName)) {
return {};
}
return createJestNodes[1](configFilePath, options, context);
},
];
And then, in the nx.json
, it should replace @nx/jest/plugin
with:
{
"plugins": [
// HERE, REPLACE "@nx/jest/plugin" WITH YOUR CUSTOM PLUGIN
{
"plugin": "./tools/nx-plugins/skip-jest-plugin.ts",
"options": {
"targetName": "test",
"skipProjects": ["my-lib-e2e"]
}
}
]
}
Ideas
As Max Kless commented, they are open to ideas, so I jumped into it 🙂
Skip projects per plugin
This is related to the last workaround described above but integrated into Project Crystal. For example, you would have an nx.json
like:
{
"plugins": [
{
"plugin": "@nx/jest/plugin",
"options": {
"targetName": "test",
"skipProjects": ["my-lib-e2e"] // Could be a pattern instead
}
}
]
}
✅ Specific per plugin
⛔ Duplication in each plugin
⛔ If the plugin adds multiple targets, it is not possible to filter only one
Add “projects” property on targetDefaults
This strategy takes a leaf from the Nx Release workflow, applies it to targetDefaults
for more granular control:
{
"$schema": "./node_modules/nx/schemas/nx-schema.json",
"targetDefaults": {
"test": {
"cache": true,
"inputs": ["production", "^production"],
"projects": ["*", "!my-lib-e2e"]
},
}
}
✅ Specific per target
✅ Centralized
✅ Re-use existing from Release
⛔ Cannot be specified by executor because the uses Nx Command
⛔ It does not reflect what you can specify in your Project Configuration
In Conclusion
Got more ideas? Feel free to share, and I’ll eagerly incorporate them. Let’s keep the conversation going and make Nx Project Crystal even more versatile.
🚀 Stay Tuned!
Looking for some help?🤝
Connect with me on Twitter • LinkedIn • Github
Top comments (0)