Skip to content

Commit

Permalink
feat(nx-plugin): add project prefix to plugin (#792)
Browse files Browse the repository at this point in the history
  • Loading branch information
BioPhoton authored Aug 21, 2024
1 parent 85b3cdb commit d53388d
Show file tree
Hide file tree
Showing 17 changed files with 269 additions and 40 deletions.
27 changes: 17 additions & 10 deletions e2e/create-cli-e2e/mocks/create-npm-workshpace.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import { mkdir, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { mkdir, writeFile } from 'node:fs/promises';
import { join } from 'node:path';

export async function createNpmWorkspace(cwd: string) {
await mkdir(cwd, { recursive: true });
await writeFile(join(cwd, 'package.json'), JSON.stringify({
name: 'create-npm-workspace',
version: '0.0.1',
scripts: {
test: 'echo "Error: no test specified" && exit 1',
},
keywords: [],
}, null, 2));
await writeFile(
join(cwd, 'package.json'),
JSON.stringify(
{
name: 'create-npm-workspace',
version: '0.0.1',
scripts: {
test: 'echo "Error: no test specified" && exit 1',
},
keywords: [],
},
null,
2,
),
);
}
2 changes: 1 addition & 1 deletion e2e/nx-plugin-e2e/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
}
}
},
"implicitDependencies": ["nx-plugin"],
"implicitDependencies": ["nx-plugin", "test-utils"],
"tags": ["scope:tooling", "type:e2e"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -189,30 +189,24 @@ describe('nx-plugin', () => {
)}";`,
plugins: [
{
fileImports: `import jsPackagesPlugin from "${join(
fileImports: `import {customPlugin} from "${join(
relativePathToCwd(cwd),
pathRelativeToPackage,
'dist/packages/plugin-js-packages',
'dist/testing/test-utils',
)}";`,
// @TODO improve formatObjectToJsString to get rid of the "`" hack
codeStrings: 'await jsPackagesPlugin({packageManager: `npm`})',
codeStrings: 'customPlugin()',
},
],
});

await materializeTree(tree, cwd);

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

const cleanStderr = removeColorCodes(stderr);
// @TODO create test environment for working plugin. This here misses package-lock.json to execute correctly
expect(cleanStderr).toContain(
'DryRun execution of: npx @code-pushup/cli autorun',
);

const cleanStdout = removeColorCodes(stdout);
expect(cleanStdout).toContain(
'NX Successfully ran target code-pushup for project my-lib',
Expand Down Expand Up @@ -242,7 +236,33 @@ describe('nx-plugin', () => {
});
});

it('should NOT add targets dynamically if plugin is NOT registered', async () => {
it('should consider plugin option projectPrefix in executor target', async () => {
const cwd = join(baseDir, 'configuration-option-bin');
registerPluginInWorkspace(tree, {
plugin: join(relativePathToCwd(cwd), 'dist/packages/nx-plugin'),
options: {
projectPrefix: 'cli',
},
});
const { root } = readProjectConfiguration(tree, project);
generateCodePushupConfig(tree, root);
await materializeTree(tree, cwd);

const { code, projectJson } = await nxShowProjectJson(cwd, project);

expect(code).toBe(0);

expect(projectJson.targets).toStrictEqual({
['code-pushup']: expect.objectContaining({
executor: `@code-pushup/nx-plugin:autorun`,
options: {
projectPrefix: 'cli',
},
}),
});
});

it('should NOT add targets dynamically if plugin is not registered', async () => {
const cwd = join(baseDir, 'plugin-not-registered');
await materializeTree(tree, cwd);

Expand Down
85 changes: 85 additions & 0 deletions e2e/nx-plugin-e2e/tests/executor-autorun.e2e.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { Tree, updateProjectConfiguration } from '@nx/devkit';
import { rm } from 'node:fs/promises';
import { join, relative } from 'node:path';
import { readProjectConfiguration } from 'nx/src/generators/utils/project-configuration';
import { afterEach, expect } from 'vitest';
import { generateCodePushupConfig } from '@code-pushup/nx-plugin';
import {
generateWorkspaceAndProject,
materializeTree,
} from '@code-pushup/test-nx-utils';
import { removeColorCodes } from '@code-pushup/test-utils';
import { executeProcess } from '@code-pushup/utils';

function relativePathToCwd(testDir: string): string {
return relative(join(process.cwd(), testDir), process.cwd());
}

async function addTargetToWorkspace(
tree: Tree,
options: { cwd: string; project: string },
) {
const { cwd, project } = options;
const pathRelativeToPackage = relative(join(cwd, 'libs', project), cwd);
const projectCfg = readProjectConfiguration(tree, project);
updateProjectConfiguration(tree, project, {
...projectCfg,
targets: {
...projectCfg.targets,
['code-pushup']: {
executor: `${join(
relativePathToCwd(cwd),
'dist/packages/nx-plugin',
)}:autorun`,
},
},
});
const { root } = projectCfg;
generateCodePushupConfig(tree, root, {
fileImports: `import type {CoreConfig} from "${join(
relativePathToCwd(cwd),
pathRelativeToPackage,
'dist/packages/models',
)}";`,
plugins: [
{
fileImports: `import {customPlugin} from "${join(
relativePathToCwd(cwd),
pathRelativeToPackage,
'dist/testing/test-utils',
)}";`,
codeStrings: 'customPlugin()',
},
],
});
await materializeTree(tree, cwd);
}

describe('executor autorun', () => {
let tree: Tree;
const project = 'my-lib';
const baseDir = 'tmp/nx-plugin-e2e/executor';

beforeEach(async () => {
tree = await generateWorkspaceAndProject(project);
});

afterEach(async () => {
await rm(baseDir, { recursive: true, force: true });
});

it('should execute autorun executor', async () => {
const cwd = join(baseDir, 'execute-dynamic-executor');
await addTargetToWorkspace(tree, { cwd, project });

const { stdout, code } = await executeProcess({
command: 'npx',
args: ['nx', 'run', `${project}:code-pushup`, '--dryRun'],
cwd,
});

expect(code).toBe(0);
const cleanStdout = removeColorCodes(stdout);
expect(cleanStdout).toContain('nx run my-lib:code-pushup --dryRun');
});
});
File renamed without changes.
1 change: 0 additions & 1 deletion packages/nx-plugin/src/internal/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
export type DynamicTargetOptions = {
// @TODO add prefix https://github.com/code-pushup/cli/issues/619
targetName?: string;
bin?: string;
};
68 changes: 66 additions & 2 deletions packages/nx-plugin/src/plugin/plugin.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ describe('@code-pushup/nx-plugin/plugin', () => {
vol.reset();
});

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

it('should normalize context and use it to create target on PACKAGE project', async () => {
it('should normalize context and use it to create the configuration target on PACKAGE project', async () => {
const projectRoot = 'apps/my-app';
const matchingFilesData = {
[`${projectRoot}/${PROJECT_JSON_FILE_NAME}`]: `${JSON.stringify({
Expand All @@ -71,4 +71,68 @@ describe('@code-pushup/nx-plugin/plugin', () => {
},
});
});

it('should create the executor target on ROOT project if configured', async () => {
const projectRoot = '.';
const matchingFilesData = {
[`${projectRoot}/${PROJECT_JSON_FILE_NAME}`]: `${JSON.stringify({
name: '@org/empty-root',
})}`,
[`${projectRoot}/code-pushup.config.ts`]: '{}',
};

await expect(
invokeCreateNodesOnVirtualFiles(
createNodes,
context,
{
projectPrefix: 'cli',
},
{ matchingFilesData },
),
).resolves.toStrictEqual({
[projectRoot]: {
targets: {
[CP_TARGET_NAME]: {
executor: `${PACKAGE_NAME}:autorun`,
options: {
projectPrefix: 'cli',
},
},
},
},
});
});

it('should create the executor target on PACKAGE project if configured', async () => {
const projectRoot = 'apps/my-app';
const matchingFilesData = {
[`${projectRoot}/${PROJECT_JSON_FILE_NAME}`]: `${JSON.stringify({
name: '@org/empty-root',
})}`,
[`${projectRoot}/code-pushup.config.ts`]: '{}',
};

await expect(
invokeCreateNodesOnVirtualFiles(
createNodes,
context,
{
projectPrefix: 'cli',
},
{ matchingFilesData },
),
).resolves.toStrictEqual({
[projectRoot]: {
targets: {
[CP_TARGET_NAME]: {
executor: `${PACKAGE_NAME}:autorun`,
options: {
projectPrefix: 'cli',
},
},
},
},
});
});
});
2 changes: 2 additions & 0 deletions packages/nx-plugin/src/plugin/target/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const CODE_PUSHUP_CONFIG_REGEX =
/^code-pushup\.config\.(\w*\.)*(ts|js|mjs)$/;
14 changes: 11 additions & 3 deletions packages/nx-plugin/src/plugin/target/executor-target.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { TargetConfiguration } from '@nx/devkit';
import { RunCommandsOptions } from 'nx/src/executors/run-commands/run-commands.impl';
import { PACKAGE_NAME } from '../../internal/constants';
import { ProjectPrefixOptions } from '../types';

export function createExecutorTarget(options?: {
bin?: string;
}): TargetConfiguration<RunCommandsOptions> {
const { bin = PACKAGE_NAME } = options ?? {};
projectPrefix?: string;
}): TargetConfiguration<ProjectPrefixOptions> {
const { bin = PACKAGE_NAME, projectPrefix } = options ?? {};
return {
executor: `${bin}:autorun`,
...(projectPrefix
? {
options: {
projectPrefix,
},
}
: {}),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,13 @@ describe('createExecutorTarget', () => {
executor: 'xyz:autorun',
});
});

it('should use projectPrefix if provided', () => {
expect(createExecutorTarget({ projectPrefix: 'cli' })).toStrictEqual({
executor: '@code-pushup/nx-plugin:autorun',
options: {
projectPrefix: 'cli',
},
});
});
});
16 changes: 9 additions & 7 deletions packages/nx-plugin/src/plugin/target/targets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@ import { readdir } from 'node:fs/promises';
import { CP_TARGET_NAME } from '../constants';
import type { NormalizedCreateNodesContext } from '../types';
import { createConfigurationTarget } from './configuration-target';
import { CODE_PUSHUP_CONFIG_REGEX } from './constants';
import { createExecutorTarget } from './executor-target';

export async function createTargets(
normalizedContext: NormalizedCreateNodesContext,
) {
const { targetName = CP_TARGET_NAME, bin } = normalizedContext.createOptions;
const {
targetName = CP_TARGET_NAME,
bin,
projectPrefix,
} = normalizedContext.createOptions;
const rootFiles = await readdir(normalizedContext.projectRoot);
return rootFiles.some(filename =>
filename.match(/^code-pushup\.config.(\w*\.)*(ts|js|mjs)$/),
)
? // @TODO return code-pushup cli target https://github.com/code-pushup/cli/issues/619
{
[targetName]: createExecutorTarget({ bin }),
return rootFiles.some(filename => filename.match(CODE_PUSHUP_CONFIG_REGEX))
? {
[targetName]: createExecutorTarget({ bin, projectPrefix }),
}
: // if NO code-pushup.config.*.(ts|js|mjs) is present return configuration target
{
Expand Down
25 changes: 25 additions & 0 deletions packages/nx-plugin/src/plugin/target/targets.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,29 @@ describe('createTargets', () => {
},
});
});

it('should include projectPrefix options in executor targets if given', async () => {
const projectName = 'plugin-my-plugin';
vol.fromJSON(
{
[`code-pushup.config.ts`]: `{}`,
},
MEMFS_VOLUME,
);
await expect(
createTargets({
projectRoot: '.',
projectJson: {
name: projectName,
},
createOptions: {
projectPrefix: 'cli',
},
} as NormalizedCreateNodesContext),
).resolves.toStrictEqual({
[DEFAULT_TARGET_NAME]: expect.objectContaining({
options: { projectPrefix: 'cli' },
}),
});
});
});
Loading

0 comments on commit d53388d

Please sign in to comment.