Skip to content

Commit 4b9e451

Browse files
committed
feat(hooks): support esm
1 parent cbfe8c2 commit 4b9e451

File tree

1 file changed

+56
-42
lines changed

1 file changed

+56
-42
lines changed

lib/common/services/hooks-service.ts

Lines changed: 56 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ import { color } from "../../color";
2424
import { memoize } from "../decorators";
2525

2626
class Hook implements IHook {
27-
constructor(public name: string, public fullPath: string) {}
27+
constructor(
28+
public name: string,
29+
public fullPath: string,
30+
) {}
2831
}
2932

3033
export class HooksService implements IHooksService {
@@ -45,7 +48,7 @@ export class HooksService implements IHooksService {
4548
private $projectHelper: IProjectHelper,
4649
private $options: IOptions,
4750
private $performanceService: IPerformanceService,
48-
private $projectConfigService: IProjectConfigService
51+
private $projectConfigService: IProjectConfigService,
4952
) {}
5053

5154
public get hookArgsName(): string {
@@ -69,12 +72,12 @@ export class HooksService implements IHooksService {
6972

7073
if (projectDir) {
7174
this.hooksDirectories.push(
72-
path.join(projectDir, HooksService.HOOKS_DIRECTORY_NAME)
75+
path.join(projectDir, HooksService.HOOKS_DIRECTORY_NAME),
7376
);
7477
}
7578

7679
this.$logger.trace(
77-
"Hooks directories: " + util.inspect(this.hooksDirectories)
80+
"Hooks directories: " + util.inspect(this.hooksDirectories),
7881
);
7982

8083
const customHooks = this.$projectConfigService.getValue("hooks", []);
@@ -91,7 +94,7 @@ export class HooksService implements IHooksService {
9194

9295
public executeBeforeHooks(
9396
commandName: string,
94-
hookArguments?: IDictionary<any>
97+
hookArguments?: IDictionary<any>,
9598
): Promise<void> {
9699
const beforeHookName = `before-${HooksService.formatHookName(commandName)}`;
97100
const traceMessage = `BeforeHookName for command ${commandName} is ${beforeHookName}`;
@@ -100,7 +103,7 @@ export class HooksService implements IHooksService {
100103

101104
public executeAfterHooks(
102105
commandName: string,
103-
hookArguments?: IDictionary<any>
106+
hookArguments?: IDictionary<any>,
104107
): Promise<void> {
105108
const afterHookName = `after-${HooksService.formatHookName(commandName)}`;
106109
const traceMessage = `AfterHookName for command ${commandName} is ${afterHookName}`;
@@ -110,7 +113,7 @@ export class HooksService implements IHooksService {
110113
private async executeHooks(
111114
hookName: string,
112115
traceMessage: string,
113-
hookArguments?: IDictionary<any>
116+
hookArguments?: IDictionary<any>,
114117
): Promise<any> {
115118
if (this.$config.DISABLE_HOOKS || !this.$options.hooks) {
116119
return;
@@ -135,8 +138,8 @@ export class HooksService implements IHooksService {
135138
await this.executeHooksInDirectory(
136139
hooksDirectory,
137140
hookName,
138-
hookArguments
139-
)
141+
hookArguments,
142+
),
140143
);
141144
}
142145

@@ -148,8 +151,8 @@ export class HooksService implements IHooksService {
148151
this.$projectHelper.projectDir,
149152
hookName,
150153
hook,
151-
hookArguments
152-
)
154+
hookArguments,
155+
),
153156
);
154157
}
155158
} catch (err) {
@@ -160,11 +163,16 @@ export class HooksService implements IHooksService {
160163
return _.flatten(results);
161164
}
162165

166+
private isESModule(hook: IHook): boolean {
167+
const ext = path.extname(hook.fullPath).toLowerCase();
168+
return ext === ".mjs";
169+
}
170+
163171
private async executeHook(
164172
directoryPath: string,
165173
hookName: string,
166174
hook: IHook,
167-
hookArguments?: IDictionary<any>
175+
hookArguments?: IDictionary<any>,
168176
): Promise<any> {
169177
hookArguments = hookArguments || {};
170178

@@ -173,15 +181,18 @@ export class HooksService implements IHooksService {
173181
const relativePath = path.relative(directoryPath, hook.fullPath);
174182
const trackId = relativePath.replace(
175183
new RegExp("\\" + path.sep, "g"),
176-
AnalyticsEventLabelDelimiter
184+
AnalyticsEventLabelDelimiter,
177185
);
186+
const isESM = this.isESModule(hook);
178187
let command = this.getSheBangInterpreter(hook);
179188
let inProc = false;
180189
if (!command) {
181190
command = hook.fullPath;
182191
if ([".mjs", ".js"].includes(path.extname(hook.fullPath).toLowerCase())) {
183192
command = process.argv[0];
184-
inProc = this.shouldExecuteInProcess(this.$fs.readText(hook.fullPath));
193+
inProc = isESM
194+
? true
195+
: this.shouldExecuteInProcess(this.$fs.readText(hook.fullPath));
185196
}
186197
}
187198

@@ -190,24 +201,30 @@ export class HooksService implements IHooksService {
190201
this.$logger.trace(
191202
"Executing %s hook at ___location %s in-process",
192203
hookName,
193-
hook.fullPath
204+
hook.fullPath,
194205
);
195-
const hookEntryPoint = require(hook.fullPath);
206+
let hookEntryPoint;
207+
if (isESM) {
208+
const { default: hookFn } = await import(hook.fullPath);
209+
hookEntryPoint = hookFn;
210+
} else {
211+
hookEntryPoint = require(hook.fullPath);
212+
}
196213

197214
this.$logger.trace(`Validating ${hookName} arguments.`);
198215

199216
const invalidArguments = this.validateHookArguments(
200217
hookEntryPoint,
201-
hook.fullPath
218+
hook.fullPath,
202219
);
203220

204221
if (invalidArguments.length) {
205222
this.$logger.warn(
206223
`${
207224
hook.fullPath
208225
} will NOT be executed because it has invalid arguments - ${color.grey(
209-
invalidArguments.join(", ")
210-
)}.`
226+
invalidArguments.join(", "),
227+
)}.`,
211228
);
212229
return;
213230
}
@@ -220,14 +237,13 @@ export class HooksService implements IHooksService {
220237
const projectDataHookArg =
221238
hookArguments["hookArgs"] && hookArguments["hookArgs"]["projectData"];
222239
if (projectDataHookArg) {
223-
hookArguments["projectData"] = hookArguments[
224-
"$projectData"
225-
] = projectDataHookArg;
240+
hookArguments["projectData"] = hookArguments["$projectData"] =
241+
projectDataHookArg;
226242
}
227243

228244
const maybePromise = this.$injector.resolve(
229245
hookEntryPoint,
230-
hookArguments
246+
hookArguments,
231247
);
232248
if (maybePromise) {
233249
this.$logger.trace("Hook promises to signal completion");
@@ -255,15 +271,15 @@ export class HooksService implements IHooksService {
255271
"Executing %s hook at ___location %s with environment ",
256272
hookName,
257273
hook.fullPath,
258-
environment
274+
environment,
259275
);
260276

261277
const output = await this.$childProcess.spawnFromEvent(
262278
command,
263279
[hook.fullPath],
264280
"close",
265281
environment,
266-
{ throwError: false }
282+
{ throwError: false },
267283
);
268284
result = output;
269285

@@ -275,7 +291,7 @@ export class HooksService implements IHooksService {
275291
"Finished executing %s hook at ___location %s with environment ",
276292
hookName,
277293
hook.fullPath,
278-
environment
294+
environment,
279295
);
280296
}
281297
const endTime = this.$performanceService.now();
@@ -289,7 +305,7 @@ export class HooksService implements IHooksService {
289305
private async executeHooksInDirectory(
290306
directoryPath: string,
291307
hookName: string,
292-
hookArguments?: IDictionary<any>
308+
hookArguments?: IDictionary<any>,
293309
): Promise<any[]> {
294310
hookArguments = hookArguments || {};
295311
const results: any[] = [];
@@ -301,7 +317,7 @@ export class HooksService implements IHooksService {
301317
directoryPath,
302318
hookName,
303319
hook,
304-
hookArguments
320+
hookArguments,
305321
);
306322

307323
if (result) {
@@ -316,14 +332,14 @@ export class HooksService implements IHooksService {
316332
const hooks: IHook[] = [];
317333
const customHooks: INsConfigHooks[] = this.$projectConfigService.getValue(
318334
"hooks",
319-
[]
335+
[],
320336
);
321337

322338
for (const cHook of customHooks) {
323339
if (cHook.type === hookName) {
324340
const fullPath = path.join(
325341
this.$projectHelper.projectDir,
326-
cHook.script
342+
cHook.script,
327343
);
328344
const isFile = this.$fs.getFsStats(fullPath).isFile();
329345

@@ -332,8 +348,8 @@ export class HooksService implements IHooksService {
332348
hooks.push(
333349
new Hook(
334350
this.getBaseFilename(fileNameParts[fileNameParts.length - 1]),
335-
fullPath
336-
)
351+
fullPath,
352+
),
337353
);
338354
}
339355
}
@@ -346,10 +362,10 @@ export class HooksService implements IHooksService {
346362
const allBaseHooks = this.getHooksInDirectory(directoryPath);
347363
const baseHooks = _.filter(
348364
allBaseHooks,
349-
(hook) => hook.name.toLowerCase() === hookName.toLowerCase()
365+
(hook) => hook.name.toLowerCase() === hookName.toLowerCase(),
350366
);
351367
const moreHooks = this.getHooksInDirectory(
352-
path.join(directoryPath, hookName)
368+
path.join(directoryPath, hookName),
353369
);
354370
return baseHooks.concat(moreHooks);
355371
}
@@ -385,13 +401,11 @@ export class HooksService implements IHooksService {
385401
const clientName = this.$staticConfig.CLIENT_NAME.toUpperCase();
386402

387403
const environment: IStringDictionary = {};
388-
environment[util.format("%s-COMMANDLINE", clientName)] = process.argv.join(
389-
" "
390-
);
404+
environment[util.format("%s-COMMANDLINE", clientName)] =
405+
process.argv.join(" ");
391406
environment[util.format("%s-HOOK_FULL_PATH", clientName)] = hookFullPath;
392-
environment[
393-
util.format("%s-VERSION", clientName)
394-
] = this.$staticConfig.version;
407+
environment[util.format("%s-VERSION", clientName)] =
408+
this.$staticConfig.version;
395409

396410
return {
397411
cwd: this.$projectHelper.projectDir,
@@ -463,7 +477,7 @@ export class HooksService implements IHooksService {
463477

464478
private validateHookArguments(
465479
hookConstructor: any,
466-
hookFullPath: string
480+
hookFullPath: string,
467481
): string[] {
468482
const invalidArguments: string[] = [];
469483

@@ -477,7 +491,7 @@ export class HooksService implements IHooksService {
477491
}
478492
} catch (err) {
479493
this.$logger.trace(
480-
`Cannot resolve ${argument} of hook ${hookFullPath}, reason: ${err}`
494+
`Cannot resolve ${argument} of hook ${hookFullPath}, reason: ${err}`,
481495
);
482496
invalidArguments.push(argument);
483497
}

0 commit comments

Comments
 (0)