From 664a09c4471f46a5b88be0b8e26f24b1a0b2bcc1 Mon Sep 17 00:00:00 2001 From: Lutz Roeder Date: Wed, 13 Dec 2023 16:16:36 -0500 Subject: [PATCH] feat: Enable ESM support for hooks by using dynamic `import()` when `package.json` is set to type `module` (#7936) --- .changeset/quick-rocks-cover.md | 5 +++++ packages/app-builder-lib/src/appInfo.ts | 2 ++ .../src/codeSign/windowsCodeSign.ts | 2 +- packages/app-builder-lib/src/index.ts | 2 +- .../app-builder-lib/src/options/metadata.ts | 2 ++ packages/app-builder-lib/src/packager.ts | 12 ++++++------ .../app-builder-lib/src/platformPackager.ts | 18 +++++++++++++----- .../src/util/NodeModuleCopyHelper.ts | 2 +- 8 files changed, 31 insertions(+), 14 deletions(-) create mode 100644 .changeset/quick-rocks-cover.md diff --git a/.changeset/quick-rocks-cover.md b/.changeset/quick-rocks-cover.md new file mode 100644 index 00000000000..c4735e484cb --- /dev/null +++ b/.changeset/quick-rocks-cover.md @@ -0,0 +1,5 @@ +--- +"app-builder-lib": minor +--- + +feat: Enable ESM support for hooks by using dynamic `import()` when `package.json` is set to type `module`. diff --git a/packages/app-builder-lib/src/appInfo.ts b/packages/app-builder-lib/src/appInfo.ts index 33e8b134809..9c3e0fed5a9 100644 --- a/packages/app-builder-lib/src/appInfo.ts +++ b/packages/app-builder-lib/src/appInfo.ts @@ -22,6 +22,7 @@ export function smarten(s: string): string { export class AppInfo { readonly description = smarten(this.info.metadata.description || "") readonly version: string + readonly type: string | undefined readonly shortVersion: string | undefined readonly shortVersionWindows: string | undefined @@ -39,6 +40,7 @@ export class AppInfo { normalizeNfd = false ) { this.version = info.metadata.version! + this.type = info.metadata.type if (buildVersion == null) { buildVersion = info.config.buildVersion diff --git a/packages/app-builder-lib/src/codeSign/windowsCodeSign.ts b/packages/app-builder-lib/src/codeSign/windowsCodeSign.ts index 7095735872f..739834994f3 100644 --- a/packages/app-builder-lib/src/codeSign/windowsCodeSign.ts +++ b/packages/app-builder-lib/src/codeSign/windowsCodeSign.ts @@ -52,7 +52,7 @@ export async function sign(options: WindowsSignOptions, packager: WinPackager): hashes = Array.isArray(hashes) ? hashes : [hashes] } - const executor = resolveFunction(options.options.sign, "sign") || doSign + const executor = (await resolveFunction(packager.appInfo.type, options.options.sign, "sign")) || doSign let isNest = false for (const hash of hashes) { const taskConfiguration: WindowsSignTaskConfiguration = { ...options, hash, isNest } diff --git a/packages/app-builder-lib/src/index.ts b/packages/app-builder-lib/src/index.ts index 31cbca39645..1f03ee50bff 100644 --- a/packages/app-builder-lib/src/index.ts +++ b/packages/app-builder-lib/src/index.ts @@ -76,7 +76,7 @@ export function build(options: PackagerOptions & PublishOptions, packager: Packa process.once("SIGINT", sigIntHandler) const promise = packager.build().then(async buildResult => { - const afterAllArtifactBuild = resolveFunction(buildResult.configuration.afterAllArtifactBuild, "afterAllArtifactBuild") + const afterAllArtifactBuild = await resolveFunction(packager.appInfo.type, buildResult.configuration.afterAllArtifactBuild, "afterAllArtifactBuild") if (afterAllArtifactBuild != null) { const newArtifacts = asArray(await Promise.resolve(afterAllArtifactBuild(buildResult))) if (newArtifacts.length === 0 || !publishManager.isPublish) { diff --git a/packages/app-builder-lib/src/options/metadata.ts b/packages/app-builder-lib/src/options/metadata.ts index 0f2663b50fb..cda553185cd 100644 --- a/packages/app-builder-lib/src/options/metadata.ts +++ b/packages/app-builder-lib/src/options/metadata.ts @@ -41,6 +41,8 @@ export interface Metadata { /** @private */ readonly version?: string /** @private */ + readonly type?: string + /** @private */ readonly shortVersion?: string | null /** @private */ readonly shortVersionWindows?: string | null diff --git a/packages/app-builder-lib/src/packager.ts b/packages/app-builder-lib/src/packager.ts index ebb9a2e6bd6..8e5db68faac 100644 --- a/packages/app-builder-lib/src/packager.ts +++ b/packages/app-builder-lib/src/packager.ts @@ -257,7 +257,7 @@ export class Packager { }, "building" ) - const handler = resolveFunction(this.config.artifactBuildStarted, "artifactBuildStarted") + const handler = await resolveFunction(this.appInfo.type, this.config.artifactBuildStarted, "artifactBuildStarted") if (handler != null) { await Promise.resolve(handler(event)) } @@ -271,7 +271,7 @@ export class Packager { } async callArtifactBuildCompleted(event: ArtifactCreated): Promise { - const handler = resolveFunction(this.config.artifactBuildCompleted, "artifactBuildCompleted") + const handler = await resolveFunction(this.appInfo.type, this.config.artifactBuildCompleted, "artifactBuildCompleted") if (handler != null) { await Promise.resolve(handler(event)) } @@ -280,14 +280,14 @@ export class Packager { } async callAppxManifestCreated(path: string): Promise { - const handler = resolveFunction(this.config.appxManifestCreated, "appxManifestCreated") + const handler = await resolveFunction(this.appInfo.type, this.config.appxManifestCreated, "appxManifestCreated") if (handler != null) { await Promise.resolve(handler(path)) } } async callMsiProjectCreated(path: string): Promise { - const handler = resolveFunction(this.config.msiProjectCreated, "msiProjectCreated") + const handler = await resolveFunction(this.appInfo.type, this.config.msiProjectCreated, "msiProjectCreated") if (handler != null) { await Promise.resolve(handler(path)) } @@ -503,7 +503,7 @@ export class Packager { return } - const beforeBuild = resolveFunction(config.beforeBuild, "beforeBuild") + const beforeBuild = await resolveFunction(this.appInfo.type, config.beforeBuild, "beforeBuild") if (beforeBuild != null) { const performDependenciesInstallOrRebuild = await beforeBuild({ appDir: this.appDir, @@ -532,7 +532,7 @@ export class Packager { } async afterPack(context: AfterPackContext): Promise { - const afterPack = resolveFunction(this.config.afterPack, "afterPack") + const afterPack = await resolveFunction(this.appInfo.type, this.config.afterPack, "afterPack") const handlers = this.afterPackHandlers.slice() if (afterPack != null) { // user handler should be last diff --git a/packages/app-builder-lib/src/platformPackager.ts b/packages/app-builder-lib/src/platformPackager.ts index d7d38ca1611..adfadfa047c 100644 --- a/packages/app-builder-lib/src/platformPackager.ts +++ b/packages/app-builder-lib/src/platformPackager.ts @@ -204,7 +204,7 @@ export abstract class PlatformPackager // Due to node-gyp rewriting GYP_MSVS_VERSION when reused across the same session, we must reset the env var: https://github.com/electron-userland/electron-builder/issues/7256 delete process.env.GYP_MSVS_VERSION - const beforePack = resolveFunction(this.config.beforePack, "beforePack") + const beforePack = await resolveFunction(this.appInfo.type, this.config.beforePack, "beforePack") if (beforePack != null) { await beforePack({ appOutDir, @@ -330,7 +330,7 @@ export abstract class PlatformPackager electronPlatformName: platformName, } const didSign = await this.signApp(packContext, isAsar) - const afterSign = resolveFunction(this.config.afterSign, "afterSign") + const afterSign = await resolveFunction(this.appInfo.type, this.config.afterSign, "afterSign") if (afterSign != null) { if (didSign) { await Promise.resolve(afterSign(packContext)) @@ -753,7 +753,16 @@ export function normalizeExt(ext: string) { return ext.startsWith(".") ? ext.substring(1) : ext } -export function resolveFunction(executor: T | string, name: string): T { +async function resolveModule(type: string | undefined, name: string): Promise { + const extension = path.extname(name).toLowerCase() + const isModuleType = type === "module" + if (extension === ".mjs" || (extension === ".js" && isModuleType)) { + return await eval("import('" + name + "')") + } + return require(name) +} + +export async function resolveFunction(type: string | undefined, executor: T | string, name: string): Promise { if (executor == null || typeof executor !== "string") { return executor } @@ -770,8 +779,7 @@ export function resolveFunction(executor: T | string, name: string): T { p = path.resolve(p) } - // eslint-disable-next-line @typescript-eslint/no-var-requires - const m = require(p) + const m: any = await resolveModule(type, p) const namedExport = m[name] if (namedExport == null) { return m.default || m diff --git a/packages/app-builder-lib/src/util/NodeModuleCopyHelper.ts b/packages/app-builder-lib/src/util/NodeModuleCopyHelper.ts index e7856a14089..beb3523f0db 100644 --- a/packages/app-builder-lib/src/util/NodeModuleCopyHelper.ts +++ b/packages/app-builder-lib/src/util/NodeModuleCopyHelper.ts @@ -41,7 +41,7 @@ export class NodeModuleCopyHelper extends FileCopyHelper { const filter = this.filter const metadata = this.metadata - const onNodeModuleFile = resolveFunction(this.packager.config.onNodeModuleFile, "onNodeModuleFile") + const onNodeModuleFile = await resolveFunction(this.packager.appInfo.type, this.packager.config.onNodeModuleFile, "onNodeModuleFile") const result: Array = [] const queue: Array = []