Skip to content

Commit 134ed97

Browse files
committed
feat(hook): add beforeMerge, afterMerge, afterOutput hooks
- `afterOutput` will be executed after the file is output to the directory BREAKING CHANGE: `afterGenerate` will be executed after the build is complete and before the file is output
1 parent aa5f08d commit 134ed97

File tree

2 files changed

+81
-10
lines changed

2 files changed

+81
-10
lines changed

src/Generator.ts

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { GeneratorSource } from './Source';
66
import { Hook, HookHelper } from './Hook';
77
import { DirInfo } from './Dir';
88
import np from 'normalize-path';
9+
import { isDir } from './utils';
10+
import { getConfirm } from './qa';
911

1012
import type { Ora } from 'ora';
1113
import type { SourceInfo } from './Source';
@@ -189,13 +191,41 @@ export class Generator<N extends string = string> {
189191
}
190192
}
191193

192-
public async clearTemp() {
193-
await fs.rm(this.tempPathname, { recursive: true });
194+
private async before() {
195+
if (!fs.existsSync(this.target.pathname)) return;
196+
let confirm = false;
197+
if (isDir(this.target.pathname))
198+
if (fs.readdirSync(this.target.pathname).length)
199+
confirm = await getConfirm(
200+
`The content in folder "${this.target.name}" will be deleted, whether to continue`,
201+
);
202+
else
203+
confirm = await getConfirm(
204+
`Existing file "${this.target.name}" will be deleted, whether to continue`,
205+
);
206+
if (!confirm) process.exit(0);
207+
fs.rmSync(this.target.pathname, { recursive: true });
208+
fs.ensureDirSync(this.target.pathname);
194209
}
195210

196-
private async output() {
197-
this.spinner.start('Copy Files');
198-
await fs.copy(this.tempPathname, this.target.pathname, { overwrite: true });
211+
private async after() {
212+
const pkg = this.targetDirInfo.get('/package.json', { type: 'file' });
213+
if (pkg?.getJson()) {
214+
const pkgJson = pkg.getJson();
215+
pkgJson.name = this.target.name;
216+
pkg.setContent(JSON.stringify(pkgJson));
217+
}
218+
}
219+
220+
private async output(map = this.targetDirInfo.getMap()) {
221+
this.spinner.start('Output Files');
222+
for (const item of Object.values(map)) {
223+
if (item.isDir) this.output(item.getMap());
224+
else {
225+
fs.ensureFileSync(item.pathname);
226+
fs.writeFileSync(item.pathname, (item as FileInfo).getContent());
227+
}
228+
}
199229
}
200230

201231
private async installDeps() {
@@ -208,11 +238,20 @@ export class Generator<N extends string = string> {
208238

209239
public async generate() {
210240
try {
241+
await this.before();
242+
await this.callHooks('beforeGenerate');
211243

212244
await this.generateBase();
213245
await this.generateInject();
246+
$.verbose = false;
247+
248+
await this.callHooks('afterGenerate', { targetDir: this.targetDirInfo });
249+
await this.after();
250+
214251
await this.output();
215252

253+
await this.callHooks('afterOutput');
254+
216255
await this.installDeps();
217256

218257
this.spinner.succeed(
@@ -225,8 +264,6 @@ export class Generator<N extends string = string> {
225264
chalk.redBright(this.spinner.text || 'Something error when generating'),
226265
);
227266
console.error(chalk.redBright((e as Error).stack));
228-
} finally {
229-
await this.clearTemp();
230267
}
231268
}
232269
}

src/Hook.ts

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import VError from 'verror';
1212
import type { FileInfo } from './File';
1313
import type { SourceInfo, SOURCE_TYPE } from './Source';
1414
import type { AsyncOrCommon } from './utils';
15+
import type { DirInfo } from './Dir';
1516

1617
export type HookHelpers = typeof ex & {
1718
inquirer: typeof inquirer;
@@ -22,7 +23,22 @@ export type HookHelpers = typeof ex & {
2223
export interface HookBase<P extends unknown[] = unknown[], R = unknown> {
2324
(...args: P): AsyncOrCommon<R>;
2425
}
26+
27+
/**
28+
* beforeGenerate -> beforeMerge -> onMerging -> afterMerge -> afterGenerate -> afterOutput
29+
*/
2530
export interface Hooks {
31+
beforeGenerate?: HookBase;
32+
beforeMerge?: HookBase<
33+
[
34+
options: {
35+
srcDir: DirInfo;
36+
destDir: DirInfo;
37+
src: FileInfo;
38+
dest: FileInfo;
39+
},
40+
]
41+
>;
2642
onMerging?: HookBase<
2743
[
2844
options: {
@@ -32,8 +48,24 @@ export interface Hooks {
3248
],
3349
string
3450
>;
35-
beforeMerge?: HookBase;
36-
afterMerge?: HookBase;
51+
afterMerge?: HookBase<
52+
[
53+
options: {
54+
srcDir: DirInfo;
55+
destDir: DirInfo;
56+
src: FileInfo;
57+
dest: FileInfo;
58+
},
59+
]
60+
>;
61+
afterGenerate?: HookBase<
62+
[
63+
options: {
64+
targetDir: DirInfo;
65+
},
66+
]
67+
>;
68+
afterOutput?: HookBase;
3769
}
3870

3971
interface HookConstructorOptions {
@@ -161,7 +193,9 @@ export class HookHelper {
161193
keys: ['__dir_src__', '__pathname_entry__'],
162194
onHasNot(_, key) {
163195
throw new VError(
164-
`Base template ${base.name} is missing the "${String(key)}" key in meta`,
196+
`Base template ${base.name} is missing the "${String(
197+
key,
198+
)}" key in meta`,
165199
);
166200
},
167201
});

0 commit comments

Comments
 (0)