Skip to content

Commit d53388d

Browse files
authoredAug 21, 2024··
feat(nx-plugin): add project prefix to plugin (#792)
1 parent 85b3cdb commit d53388d

17 files changed

+269
-40
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
1-
import { mkdir, writeFile } from "node:fs/promises";
2-
import { join } from "node:path";
1+
import { mkdir, writeFile } from 'node:fs/promises';
2+
import { join } from 'node:path';
33

44
export async function createNpmWorkspace(cwd: string) {
55
await mkdir(cwd, { recursive: true });
6-
await writeFile(join(cwd, 'package.json'), JSON.stringify({
7-
name: 'create-npm-workspace',
8-
version: '0.0.1',
9-
scripts: {
10-
test: 'echo "Error: no test specified" && exit 1',
11-
},
12-
keywords: [],
13-
}, null, 2));
6+
await writeFile(
7+
join(cwd, 'package.json'),
8+
JSON.stringify(
9+
{
10+
name: 'create-npm-workspace',
11+
version: '0.0.1',
12+
scripts: {
13+
test: 'echo "Error: no test specified" && exit 1',
14+
},
15+
keywords: [],
16+
},
17+
null,
18+
2,
19+
),
20+
);
1421
}

‎e2e/nx-plugin-e2e/project.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@
1818
}
1919
}
2020
},
21-
"implicitDependencies": ["nx-plugin"],
21+
"implicitDependencies": ["nx-plugin", "test-utils"],
2222
"tags": ["scope:tooling", "type:e2e"]
2323
}

‎e2e/nx-plugin-e2e/tests/nx-plugin.e2e.test.ts ‎e2e/nx-plugin-e2e/tests/create-nodes-plugin.e2e.test.ts

+33-13
Original file line numberDiff line numberDiff line change
@@ -189,30 +189,24 @@ describe('nx-plugin', () => {
189189
)}";`,
190190
plugins: [
191191
{
192-
fileImports: `import jsPackagesPlugin from "${join(
192+
fileImports: `import {customPlugin} from "${join(
193193
relativePathToCwd(cwd),
194194
pathRelativeToPackage,
195-
'dist/packages/plugin-js-packages',
195+
'dist/testing/test-utils',
196196
)}";`,
197-
// @TODO improve formatObjectToJsString to get rid of the "`" hack
198-
codeStrings: 'await jsPackagesPlugin({packageManager: `npm`})',
197+
codeStrings: 'customPlugin()',
199198
},
200199
],
201200
});
201+
202202
await materializeTree(tree, cwd);
203203

204-
const { stdout, stderr } = await executeProcess({
204+
const { stdout } = await executeProcess({
205205
command: 'npx',
206-
args: ['nx', 'run', `${project}:code-pushup -- --dryRun`],
206+
args: ['nx', 'run', `${project}:code-pushup`, '--dryRun'],
207207
cwd,
208208
});
209209

210-
const cleanStderr = removeColorCodes(stderr);
211-
// @TODO create test environment for working plugin. This here misses package-lock.json to execute correctly
212-
expect(cleanStderr).toContain(
213-
'DryRun execution of: npx @code-pushup/cli autorun',
214-
);
215-
216210
const cleanStdout = removeColorCodes(stdout);
217211
expect(cleanStdout).toContain(
218212
'NX Successfully ran target code-pushup for project my-lib',
@@ -242,7 +236,33 @@ describe('nx-plugin', () => {
242236
});
243237
});
244238

245-
it('should NOT add targets dynamically if plugin is NOT registered', async () => {
239+
it('should consider plugin option projectPrefix in executor target', async () => {
240+
const cwd = join(baseDir, 'configuration-option-bin');
241+
registerPluginInWorkspace(tree, {
242+
plugin: join(relativePathToCwd(cwd), 'dist/packages/nx-plugin'),
243+
options: {
244+
projectPrefix: 'cli',
245+
},
246+
});
247+
const { root } = readProjectConfiguration(tree, project);
248+
generateCodePushupConfig(tree, root);
249+
await materializeTree(tree, cwd);
250+
251+
const { code, projectJson } = await nxShowProjectJson(cwd, project);
252+
253+
expect(code).toBe(0);
254+
255+
expect(projectJson.targets).toStrictEqual({
256+
['code-pushup']: expect.objectContaining({
257+
executor: `@code-pushup/nx-plugin:autorun`,
258+
options: {
259+
projectPrefix: 'cli',
260+
},
261+
}),
262+
});
263+
});
264+
265+
it('should NOT add targets dynamically if plugin is not registered', async () => {
246266
const cwd = join(baseDir, 'plugin-not-registered');
247267
await materializeTree(tree, cwd);
248268

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { Tree, updateProjectConfiguration } from '@nx/devkit';
2+
import { rm } from 'node:fs/promises';
3+
import { join, relative } from 'node:path';
4+
import { readProjectConfiguration } from 'nx/src/generators/utils/project-configuration';
5+
import { afterEach, expect } from 'vitest';
6+
import { generateCodePushupConfig } from '@code-pushup/nx-plugin';
7+
import {
8+
generateWorkspaceAndProject,
9+
materializeTree,
10+
} from '@code-pushup/test-nx-utils';
11+
import { removeColorCodes } from '@code-pushup/test-utils';
12+
import { executeProcess } from '@code-pushup/utils';
13+
14+
function relativePathToCwd(testDir: string): string {
15+
return relative(join(process.cwd(), testDir), process.cwd());
16+
}
17+
18+
async function addTargetToWorkspace(
19+
tree: Tree,
20+
options: { cwd: string; project: string },
21+
) {
22+
const { cwd, project } = options;
23+
const pathRelativeToPackage = relative(join(cwd, 'libs', project), cwd);
24+
const projectCfg = readProjectConfiguration(tree, project);
25+
updateProjectConfiguration(tree, project, {
26+
...projectCfg,
27+
targets: {
28+
...projectCfg.targets,
29+
['code-pushup']: {
30+
executor: `${join(
31+
relativePathToCwd(cwd),
32+
'dist/packages/nx-plugin',
33+
)}:autorun`,
34+
},
35+
},
36+
});
37+
const { root } = projectCfg;
38+
generateCodePushupConfig(tree, root, {
39+
fileImports: `import type {CoreConfig} from "${join(
40+
relativePathToCwd(cwd),
41+
pathRelativeToPackage,
42+
'dist/packages/models',
43+
)}";`,
44+
plugins: [
45+
{
46+
fileImports: `import {customPlugin} from "${join(
47+
relativePathToCwd(cwd),
48+
pathRelativeToPackage,
49+
'dist/testing/test-utils',
50+
)}";`,
51+
codeStrings: 'customPlugin()',
52+
},
53+
],
54+
});
55+
await materializeTree(tree, cwd);
56+
}
57+
58+
describe('executor autorun', () => {
59+
let tree: Tree;
60+
const project = 'my-lib';
61+
const baseDir = 'tmp/nx-plugin-e2e/executor';
62+
63+
beforeEach(async () => {
64+
tree = await generateWorkspaceAndProject(project);
65+
});
66+
67+
afterEach(async () => {
68+
await rm(baseDir, { recursive: true, force: true });
69+
});
70+
71+
it('should execute autorun executor', async () => {
72+
const cwd = join(baseDir, 'execute-dynamic-executor');
73+
await addTargetToWorkspace(tree, { cwd, project });
74+
75+
const { stdout, code } = await executeProcess({
76+
command: 'npx',
77+
args: ['nx', 'run', `${project}:code-pushup`, '--dryRun'],
78+
cwd,
79+
});
80+
81+
expect(code).toBe(0);
82+
const cleanStdout = removeColorCodes(stdout);
83+
expect(cleanStdout).toContain('nx run my-lib:code-pushup --dryRun');
84+
});
85+
});
-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
export type DynamicTargetOptions = {
2-
// @TODO add prefix https://github.com/code-pushup/cli/issues/619
32
targetName?: string;
43
bin?: string;
54
};

‎packages/nx-plugin/src/plugin/plugin.unit.test.ts

+66-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ describe('@code-pushup/nx-plugin/plugin', () => {
2020
vol.reset();
2121
});
2222

23-
it('should normalize context and use it to create target on ROOT project', async () => {
23+
it('should normalize context and use it to create the configuration target on ROOT project', async () => {
2424
const projectRoot = '.';
2525
const matchingFilesData = {
2626
[`${projectRoot}/${PROJECT_JSON_FILE_NAME}`]: `${JSON.stringify({
@@ -46,7 +46,7 @@ describe('@code-pushup/nx-plugin/plugin', () => {
4646
});
4747
});
4848

49-
it('should normalize context and use it to create target on PACKAGE project', async () => {
49+
it('should normalize context and use it to create the configuration target on PACKAGE project', async () => {
5050
const projectRoot = 'apps/my-app';
5151
const matchingFilesData = {
5252
[`${projectRoot}/${PROJECT_JSON_FILE_NAME}`]: `${JSON.stringify({
@@ -71,4 +71,68 @@ describe('@code-pushup/nx-plugin/plugin', () => {
7171
},
7272
});
7373
});
74+
75+
it('should create the executor target on ROOT project if configured', async () => {
76+
const projectRoot = '.';
77+
const matchingFilesData = {
78+
[`${projectRoot}/${PROJECT_JSON_FILE_NAME}`]: `${JSON.stringify({
79+
name: '@org/empty-root',
80+
})}`,
81+
[`${projectRoot}/code-pushup.config.ts`]: '{}',
82+
};
83+
84+
await expect(
85+
invokeCreateNodesOnVirtualFiles(
86+
createNodes,
87+
context,
88+
{
89+
projectPrefix: 'cli',
90+
},
91+
{ matchingFilesData },
92+
),
93+
).resolves.toStrictEqual({
94+
[projectRoot]: {
95+
targets: {
96+
[CP_TARGET_NAME]: {
97+
executor: `${PACKAGE_NAME}:autorun`,
98+
options: {
99+
projectPrefix: 'cli',
100+
},
101+
},
102+
},
103+
},
104+
});
105+
});
106+
107+
it('should create the executor target on PACKAGE project if configured', async () => {
108+
const projectRoot = 'apps/my-app';
109+
const matchingFilesData = {
110+
[`${projectRoot}/${PROJECT_JSON_FILE_NAME}`]: `${JSON.stringify({
111+
name: '@org/empty-root',
112+
})}`,
113+
[`${projectRoot}/code-pushup.config.ts`]: '{}',
114+
};
115+
116+
await expect(
117+
invokeCreateNodesOnVirtualFiles(
118+
createNodes,
119+
context,
120+
{
121+
projectPrefix: 'cli',
122+
},
123+
{ matchingFilesData },
124+
),
125+
).resolves.toStrictEqual({
126+
[projectRoot]: {
127+
targets: {
128+
[CP_TARGET_NAME]: {
129+
executor: `${PACKAGE_NAME}:autorun`,
130+
options: {
131+
projectPrefix: 'cli',
132+
},
133+
},
134+
},
135+
},
136+
});
137+
});
74138
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const CODE_PUSHUP_CONFIG_REGEX =
2+
/^code-pushup\.config\.(\w*\.)*(ts|js|mjs)$/;
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
import { TargetConfiguration } from '@nx/devkit';
2-
import { RunCommandsOptions } from 'nx/src/executors/run-commands/run-commands.impl';
32
import { PACKAGE_NAME } from '../../internal/constants';
3+
import { ProjectPrefixOptions } from '../types';
44

55
export function createExecutorTarget(options?: {
66
bin?: string;
7-
}): TargetConfiguration<RunCommandsOptions> {
8-
const { bin = PACKAGE_NAME } = options ?? {};
7+
projectPrefix?: string;
8+
}): TargetConfiguration<ProjectPrefixOptions> {
9+
const { bin = PACKAGE_NAME, projectPrefix } = options ?? {};
910
return {
1011
executor: `${bin}:autorun`,
12+
...(projectPrefix
13+
? {
14+
options: {
15+
projectPrefix,
16+
},
17+
}
18+
: {}),
1119
};
1220
}

‎packages/nx-plugin/src/plugin/target/executor.target.unit.test.ts

+9
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,13 @@ describe('createExecutorTarget', () => {
1313
executor: 'xyz:autorun',
1414
});
1515
});
16+
17+
it('should use projectPrefix if provided', () => {
18+
expect(createExecutorTarget({ projectPrefix: 'cli' })).toStrictEqual({
19+
executor: '@code-pushup/nx-plugin:autorun',
20+
options: {
21+
projectPrefix: 'cli',
22+
},
23+
});
24+
});
1625
});

‎packages/nx-plugin/src/plugin/target/targets.ts

+9-7
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,21 @@ import { readdir } from 'node:fs/promises';
22
import { CP_TARGET_NAME } from '../constants';
33
import type { NormalizedCreateNodesContext } from '../types';
44
import { createConfigurationTarget } from './configuration-target';
5+
import { CODE_PUSHUP_CONFIG_REGEX } from './constants';
56
import { createExecutorTarget } from './executor-target';
67

78
export async function createTargets(
89
normalizedContext: NormalizedCreateNodesContext,
910
) {
10-
const { targetName = CP_TARGET_NAME, bin } = normalizedContext.createOptions;
11+
const {
12+
targetName = CP_TARGET_NAME,
13+
bin,
14+
projectPrefix,
15+
} = normalizedContext.createOptions;
1116
const rootFiles = await readdir(normalizedContext.projectRoot);
12-
return rootFiles.some(filename =>
13-
filename.match(/^code-pushup\.config.(\w*\.)*(ts|js|mjs)$/),
14-
)
15-
? // @TODO return code-pushup cli target https://github.com/code-pushup/cli/issues/619
16-
{
17-
[targetName]: createExecutorTarget({ bin }),
17+
return rootFiles.some(filename => filename.match(CODE_PUSHUP_CONFIG_REGEX))
18+
? {
19+
[targetName]: createExecutorTarget({ bin, projectPrefix }),
1820
}
1921
: // if NO code-pushup.config.*.(ts|js|mjs) is present return configuration target
2022
{

‎packages/nx-plugin/src/plugin/target/targets.unit.test.ts

+25
Original file line numberDiff line numberDiff line change
@@ -161,4 +161,29 @@ describe('createTargets', () => {
161161
},
162162
});
163163
});
164+
165+
it('should include projectPrefix options in executor targets if given', async () => {
166+
const projectName = 'plugin-my-plugin';
167+
vol.fromJSON(
168+
{
169+
[`code-pushup.config.ts`]: `{}`,
170+
},
171+
MEMFS_VOLUME,
172+
);
173+
await expect(
174+
createTargets({
175+
projectRoot: '.',
176+
projectJson: {
177+
name: projectName,
178+
},
179+
createOptions: {
180+
projectPrefix: 'cli',
181+
},
182+
} as NormalizedCreateNodesContext),
183+
).resolves.toStrictEqual({
184+
[DEFAULT_TARGET_NAME]: expect.objectContaining({
185+
options: { projectPrefix: 'cli' },
186+
}),
187+
});
188+
});
164189
});

‎packages/nx-plugin/src/plugin/types.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ import type { CreateNodesContext, ProjectConfiguration } from '@nx/devkit';
22
import { WithRequired } from '@code-pushup/utils';
33
import { DynamicTargetOptions } from '../internal/types';
44

5-
export type CreateNodesOptions = DynamicTargetOptions;
5+
export type ProjectPrefixOptions = {
6+
projectPrefix?: string;
7+
};
8+
9+
export type CreateNodesOptions = DynamicTargetOptions & ProjectPrefixOptions;
610

711
export type ProjectConfigurationWithName = WithRequired<
812
ProjectConfiguration,

‎testing/test-utils/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export * from './lib/utils/commit.mock';
1212
export * from './lib/utils/core-config.mock';
1313
export * from './lib/utils/minimal-config.mock';
1414
export * from './lib/utils/report.mock';
15+
export * from './lib/fixtures/configs/custom-plugin';
1516

1617
// dynamic mocks
1718
export * from './lib/utils/dynamic-mocks/categories.mock';

‎testing/test-utils/src/lib/fixtures/configs/custom-plugin.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const customPlugin = {
1+
const customPluginConfig = {
22
slug: 'good-feels',
33
title: 'Good feels',
44
icon: 'javascript',
@@ -18,4 +18,7 @@ const customPlugin = {
1818
],
1919
};
2020

21-
export default customPlugin;
21+
export function customPlugin() {
22+
return customPluginConfig;
23+
}
24+
export default customPluginConfig;

0 commit comments

Comments
 (0)
Please sign in to comment.