Skip to content

Commit a494e48

Browse files
authoredAug 28, 2024··
feat(workspace-plugin): implement clean executor (#32401)

File tree

5 files changed

+155
-14
lines changed

5 files changed

+155
-14
lines changed
 

‎tools/workspace-plugin/executors.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"clean": {
2424
"implementation": "./src/executors/clean/executor",
2525
"schema": "./src/executors/clean/schema.json",
26-
"description": "clean executor"
26+
"description": "clean executor - remove build artifacts."
2727
}
2828
}
2929
}
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,103 @@
1-
import { ExecutorContext } from '@nx/devkit';
1+
import { ExecutorContext, logger } from '@nx/devkit';
22

33
import { CleanExecutorSchema } from './schema';
44
import executor from './executor';
5+
import { join } from 'node:path';
6+
import { rm } from 'node:fs/promises';
7+
import { mkdirSync, rmSync, writeFileSync, existsSync } from 'node:fs';
8+
9+
jest.mock('node:fs/promises', () => {
10+
return {
11+
...jest.requireActual('node:fs/promises'),
12+
rm: jest.fn(() => Promise.resolve()),
13+
};
14+
});
515

616
const options: CleanExecutorSchema = {};
717
const context: ExecutorContext = {
8-
root: '',
18+
root: join(__dirname, '__fixtures__'),
19+
projectName: 'proj',
20+
projectsConfigurations: {
21+
projects: { proj: { root: 'proj' } },
22+
version: 2,
23+
},
924
cwd: process.cwd(),
10-
isVerbose: false,
25+
isVerbose: true,
1126
};
1227

28+
const noop = () => {
29+
return;
30+
};
31+
const fixtureRoot = context.root;
32+
const projRoot = join(fixtureRoot, 'proj');
33+
34+
function prepareFixture() {
35+
if (!existsSync(fixtureRoot)) {
36+
mkdirSync(fixtureRoot);
37+
mkdirSync(projRoot);
38+
mkdirSync(join(projRoot, 'dist'));
39+
writeFileSync(join(projRoot, 'dist', 'file.txt'), 'file', 'utf-8');
40+
mkdirSync(join(projRoot, 'lib'));
41+
writeFileSync(join(projRoot, 'lib', 'file.txt'), 'file', 'utf-8');
42+
}
43+
return () => {
44+
rmSync(fixtureRoot, { recursive: true, force: true });
45+
};
46+
}
47+
1348
describe('Clean Executor', () => {
49+
let cleanup = noop;
50+
51+
beforeAll(() => {
52+
cleanup = prepareFixture();
53+
});
54+
afterAll(() => {
55+
cleanup();
56+
});
57+
58+
beforeEach(() => {
59+
jest.spyOn(logger, 'info').mockImplementation(noop);
60+
jest.spyOn(logger, 'error').mockImplementation(noop);
61+
});
62+
1463
it('can run', async () => {
64+
const rmMock = rm as jest.Mock;
1565
const output = await executor(options, context);
1666
expect(output.success).toBe(true);
67+
68+
expect(rmMock.mock.calls.flat()).toEqual([
69+
expect.stringContaining('tools/workspace-plugin/src/executors/clean/__fixtures__/proj/dist'),
70+
{
71+
force: true,
72+
recursive: true,
73+
},
74+
expect.stringContaining('tools/workspace-plugin/src/executors/clean/__fixtures__/proj/lib'),
75+
{
76+
force: true,
77+
recursive: true,
78+
},
79+
]);
80+
});
81+
82+
it('accepts custom paths that override default ones', async () => {
83+
const rmMock = rm as jest.Mock;
84+
mkdirSync(join(projRoot, 'foo-bar'));
85+
mkdirSync(join(projRoot, 'mr-wick'));
86+
87+
const output = await executor({ ...options, paths: ['foo-bar', 'mr-wick'] }, context);
88+
expect(output.success).toBe(true);
89+
90+
expect(rmMock.mock.calls.flat()).toEqual([
91+
expect.stringContaining('tools/workspace-plugin/src/executors/clean/__fixtures__/proj/foo-bar'),
92+
{
93+
force: true,
94+
recursive: true,
95+
},
96+
expect.stringContaining('tools/workspace-plugin/src/executors/clean/__fixtures__/proj/mr-wick'),
97+
{
98+
force: true,
99+
recursive: true,
100+
},
101+
]);
17102
});
18103
});
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,54 @@
1-
import { PromiseExecutor } from '@nx/devkit';
2-
import { CleanExecutorSchema } from './schema';
1+
import { type ExecutorContext, type PromiseExecutor, logger } from '@nx/devkit';
2+
import { type CleanExecutorSchema } from './schema';
3+
import { join } from 'node:path';
4+
import { rm } from 'node:fs/promises';
5+
import { existsSync } from 'node:fs';
36

4-
const runExecutor: PromiseExecutor<CleanExecutorSchema> = async options => {
5-
console.log('Executor ran for Clean', options);
6-
return {
7-
success: true,
8-
};
7+
const runExecutor: PromiseExecutor<CleanExecutorSchema> = async (schema, context) => {
8+
const options = normalizeOptions(schema, context);
9+
10+
const success = await runClean(options, context);
11+
12+
return { success };
913
};
1014

15+
interface NormalizedOptions extends ReturnType<typeof normalizeOptions> {}
16+
17+
async function runClean(options: NormalizedOptions, context: ExecutorContext): Promise<boolean> {
18+
const projectAbsoluteRootPath = join(context.root, options.project.root);
19+
20+
const results = options.paths.map(dir => {
21+
const dirPath = join(projectAbsoluteRootPath, dir);
22+
if (existsSync(dirPath)) {
23+
verboseLog(`removing "${dirPath}"`);
24+
return rm(dirPath, { force: true, recursive: true });
25+
}
26+
return Promise.resolve();
27+
});
28+
29+
return Promise.all(results)
30+
.then(() => {
31+
return true;
32+
})
33+
.catch(err => {
34+
logger.error(err);
35+
return false;
36+
});
37+
}
38+
1139
export default runExecutor;
40+
41+
function normalizeOptions(schema: CleanExecutorSchema, context: ExecutorContext) {
42+
const defaults = {
43+
paths: ['temp', 'dist', 'dist-storybook', 'storybook-static', 'lib', 'lib-amd', 'lib-commonjs', 'coverage'],
44+
};
45+
const project = context.projectsConfigurations!.projects[context.projectName!];
46+
47+
return { ...defaults, ...schema, project };
48+
}
49+
50+
function verboseLog(message: string, kind: keyof typeof logger = 'info') {
51+
if (process.env.NX_VERBOSE_LOGGING === 'true') {
52+
logger[kind](message);
53+
}
54+
}
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
1-
export interface CleanExecutorSchema {} // eslint-disable-line
1+
export interface CleanExecutorSchema {
2+
/**
3+
* Files/Directories to remove (provide relative paths to project root)
4+
*/
5+
paths?: string[];
6+
}

‎tools/workspace-plugin/src/executors/clean/schema.json

+10-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,16 @@
22
"$schema": "https://json-schema.org/schema",
33
"version": 2,
44
"title": "Clean executor",
5-
"description": "",
5+
"description": "Remove build artifacts.",
66
"type": "object",
7-
"properties": {},
7+
"properties": {
8+
"paths": {
9+
"type": "array",
10+
"items": {
11+
"type": "string"
12+
},
13+
"description": "Files/Directories to remove (provide relative paths to project root)"
14+
}
15+
},
816
"required": []
917
}

0 commit comments

Comments
 (0)
Please sign in to comment.