diff --git a/lib/common/services/hooks-service.ts b/lib/common/services/hooks-service.ts index 4879ae4a5b..4b04e9b7a9 100644 --- a/lib/common/services/hooks-service.ts +++ b/lib/common/services/hooks-service.ts @@ -24,7 +24,10 @@ import { color } from "../../color"; import { memoize } from "../decorators"; class Hook implements IHook { - constructor(public name: string, public fullPath: string) {} + constructor( + public name: string, + public fullPath: string, + ) {} } export class HooksService implements IHooksService { @@ -45,7 +48,7 @@ export class HooksService implements IHooksService { private $projectHelper: IProjectHelper, private $options: IOptions, private $performanceService: IPerformanceService, - private $projectConfigService: IProjectConfigService + private $projectConfigService: IProjectConfigService, ) {} public get hookArgsName(): string { @@ -69,12 +72,12 @@ export class HooksService implements IHooksService { if (projectDir) { this.hooksDirectories.push( - path.join(projectDir, HooksService.HOOKS_DIRECTORY_NAME) + path.join(projectDir, HooksService.HOOKS_DIRECTORY_NAME), ); } this.$logger.trace( - "Hooks directories: " + util.inspect(this.hooksDirectories) + "Hooks directories: " + util.inspect(this.hooksDirectories), ); const customHooks = this.$projectConfigService.getValue("hooks", []); @@ -91,7 +94,7 @@ export class HooksService implements IHooksService { public executeBeforeHooks( commandName: string, - hookArguments?: IDictionary + hookArguments?: IDictionary, ): Promise { const beforeHookName = `before-${HooksService.formatHookName(commandName)}`; const traceMessage = `BeforeHookName for command ${commandName} is ${beforeHookName}`; @@ -100,7 +103,7 @@ export class HooksService implements IHooksService { public executeAfterHooks( commandName: string, - hookArguments?: IDictionary + hookArguments?: IDictionary, ): Promise { const afterHookName = `after-${HooksService.formatHookName(commandName)}`; const traceMessage = `AfterHookName for command ${commandName} is ${afterHookName}`; @@ -110,7 +113,7 @@ export class HooksService implements IHooksService { private async executeHooks( hookName: string, traceMessage: string, - hookArguments?: IDictionary + hookArguments?: IDictionary, ): Promise { if (this.$config.DISABLE_HOOKS || !this.$options.hooks) { return; @@ -135,8 +138,8 @@ export class HooksService implements IHooksService { await this.executeHooksInDirectory( hooksDirectory, hookName, - hookArguments - ) + hookArguments, + ), ); } @@ -148,8 +151,8 @@ export class HooksService implements IHooksService { this.$projectHelper.projectDir, hookName, hook, - hookArguments - ) + hookArguments, + ), ); } } catch (err) { @@ -160,11 +163,16 @@ export class HooksService implements IHooksService { return _.flatten(results); } + private isESModule(hook: IHook): boolean { + const ext = path.extname(hook.fullPath).toLowerCase(); + return ext === ".mjs"; + } + private async executeHook( directoryPath: string, hookName: string, hook: IHook, - hookArguments?: IDictionary + hookArguments?: IDictionary, ): Promise { hookArguments = hookArguments || {}; @@ -173,15 +181,18 @@ export class HooksService implements IHooksService { const relativePath = path.relative(directoryPath, hook.fullPath); const trackId = relativePath.replace( new RegExp("\\" + path.sep, "g"), - AnalyticsEventLabelDelimiter + AnalyticsEventLabelDelimiter, ); + const isESM = this.isESModule(hook); let command = this.getSheBangInterpreter(hook); let inProc = false; if (!command) { command = hook.fullPath; - if (path.extname(hook.fullPath).toLowerCase() === ".js") { + if ([".mjs", ".js"].includes(path.extname(hook.fullPath).toLowerCase())) { command = process.argv[0]; - inProc = this.shouldExecuteInProcess(this.$fs.readText(hook.fullPath)); + inProc = isESM + ? true + : this.shouldExecuteInProcess(this.$fs.readText(hook.fullPath)); } } @@ -190,15 +201,21 @@ export class HooksService implements IHooksService { this.$logger.trace( "Executing %s hook at location %s in-process", hookName, - hook.fullPath + hook.fullPath, ); - const hookEntryPoint = require(hook.fullPath); + let hookEntryPoint; + if (isESM) { + const { default: hookFn } = await import(hook.fullPath); + hookEntryPoint = hookFn; + } else { + hookEntryPoint = require(hook.fullPath); + } this.$logger.trace(`Validating ${hookName} arguments.`); const invalidArguments = this.validateHookArguments( hookEntryPoint, - hook.fullPath + hook.fullPath, ); if (invalidArguments.length) { @@ -206,8 +223,8 @@ export class HooksService implements IHooksService { `${ hook.fullPath } will NOT be executed because it has invalid arguments - ${color.grey( - invalidArguments.join(", ") - )}.` + invalidArguments.join(", "), + )}.`, ); return; } @@ -220,14 +237,13 @@ export class HooksService implements IHooksService { const projectDataHookArg = hookArguments["hookArgs"] && hookArguments["hookArgs"]["projectData"]; if (projectDataHookArg) { - hookArguments["projectData"] = hookArguments[ - "$projectData" - ] = projectDataHookArg; + hookArguments["projectData"] = hookArguments["$projectData"] = + projectDataHookArg; } const maybePromise = this.$injector.resolve( hookEntryPoint, - hookArguments + hookArguments, ); if (maybePromise) { this.$logger.trace("Hook promises to signal completion"); @@ -255,7 +271,7 @@ export class HooksService implements IHooksService { "Executing %s hook at location %s with environment ", hookName, hook.fullPath, - environment + environment, ); const output = await this.$childProcess.spawnFromEvent( @@ -263,7 +279,7 @@ export class HooksService implements IHooksService { [hook.fullPath], "close", environment, - { throwError: false } + { throwError: false }, ); result = output; @@ -275,7 +291,7 @@ export class HooksService implements IHooksService { "Finished executing %s hook at location %s with environment ", hookName, hook.fullPath, - environment + environment, ); } const endTime = this.$performanceService.now(); @@ -289,7 +305,7 @@ export class HooksService implements IHooksService { private async executeHooksInDirectory( directoryPath: string, hookName: string, - hookArguments?: IDictionary + hookArguments?: IDictionary, ): Promise { hookArguments = hookArguments || {}; const results: any[] = []; @@ -301,7 +317,7 @@ export class HooksService implements IHooksService { directoryPath, hookName, hook, - hookArguments + hookArguments, ); if (result) { @@ -316,14 +332,14 @@ export class HooksService implements IHooksService { const hooks: IHook[] = []; const customHooks: INsConfigHooks[] = this.$projectConfigService.getValue( "hooks", - [] + [], ); for (const cHook of customHooks) { if (cHook.type === hookName) { const fullPath = path.join( this.$projectHelper.projectDir, - cHook.script + cHook.script, ); const isFile = this.$fs.getFsStats(fullPath).isFile(); @@ -332,8 +348,8 @@ export class HooksService implements IHooksService { hooks.push( new Hook( this.getBaseFilename(fileNameParts[fileNameParts.length - 1]), - fullPath - ) + fullPath, + ), ); } } @@ -346,10 +362,10 @@ export class HooksService implements IHooksService { const allBaseHooks = this.getHooksInDirectory(directoryPath); const baseHooks = _.filter( allBaseHooks, - (hook) => hook.name.toLowerCase() === hookName.toLowerCase() + (hook) => hook.name.toLowerCase() === hookName.toLowerCase(), ); const moreHooks = this.getHooksInDirectory( - path.join(directoryPath, hookName) + path.join(directoryPath, hookName), ); return baseHooks.concat(moreHooks); } @@ -385,13 +401,11 @@ export class HooksService implements IHooksService { const clientName = this.$staticConfig.CLIENT_NAME.toUpperCase(); const environment: IStringDictionary = {}; - environment[util.format("%s-COMMANDLINE", clientName)] = process.argv.join( - " " - ); + environment[util.format("%s-COMMANDLINE", clientName)] = + process.argv.join(" "); environment[util.format("%s-HOOK_FULL_PATH", clientName)] = hookFullPath; - environment[ - util.format("%s-VERSION", clientName) - ] = this.$staticConfig.version; + environment[util.format("%s-VERSION", clientName)] = + this.$staticConfig.version; return { cwd: this.$projectHelper.projectDir, @@ -463,7 +477,7 @@ export class HooksService implements IHooksService { private validateHookArguments( hookConstructor: any, - hookFullPath: string + hookFullPath: string, ): string[] { const invalidArguments: string[] = []; @@ -477,7 +491,7 @@ export class HooksService implements IHooksService { } } catch (err) { this.$logger.trace( - `Cannot resolve ${argument} of hook ${hookFullPath}, reason: ${err}` + `Cannot resolve ${argument} of hook ${hookFullPath}, reason: ${err}`, ); invalidArguments.push(argument); } diff --git a/package.json b/package.json index 8dbd629ed2..addfe5724b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "nativescript", "main": "./lib/nativescript-cli-lib.js", - "version": "8.9.4", + "version": "9.0.0-alpha.0", "author": "NativeScript ", "description": "Command-line interface for building NativeScript projects", "bin": { @@ -50,7 +50,7 @@ }, "keywords": [ "nativescript", - "telerik", + "typescript", "mobile" ], "dependencies": {