import { debug, deepAssign, FileTransformer, log } from "builder-util" import { readFile } from "fs/promises" import * as path from "path" import { Configuration } from "./configuration" import { Packager } from "./packager" /** @internal */ export const NODE_MODULES_PATTERN = `${path.sep}node_modules${path.sep}` /** @internal */ export function isElectronCompileUsed(info: Packager): boolean { if (info.config.electronCompile != null) { return info.config.electronCompile } // if in devDependencies - it means that babel is used for precompilation or for some reason user decided to not use electron-compile for production return hasDep("electron-compile", info) } /** @internal */ export function hasDep(name: string, info: Packager) { const deps = info.metadata.dependencies return deps != null && name in deps } /** @internal */ export function createTransformer(srcDir: string, configuration: Configuration, extraMetadata: any, extraTransformer: FileTransformer | null): FileTransformer { const mainPackageJson = path.join(srcDir, "package.json") const isRemovePackageScripts = configuration.removePackageScripts !== false const isRemovePackageKeywords = configuration.removePackageKeywords !== false const packageJson = path.sep + "package.json" return file => { if (file === mainPackageJson) { return modifyMainPackageJson(file, extraMetadata, isRemovePackageScripts, isRemovePackageKeywords) } if (file.endsWith(packageJson) && file.includes(NODE_MODULES_PATTERN)) { return readFile(file, "utf-8") .then(it => cleanupPackageJson(JSON.parse(it), { isMain: false, isRemovePackageScripts, isRemovePackageKeywords, }) ) .catch((e: any) => log.warn(e)) } else if (extraTransformer != null) { return extraTransformer(file) } else { return null } } } /** @internal */ export interface CompilerHost { compile(file: string): any saveConfiguration(): Promise } /** @internal */ export function createElectronCompilerHost(projectDir: string, cacheDir: string): Promise { const electronCompilePath = path.join(projectDir, "node_modules", "electron-compile", "lib") return require(path.join(electronCompilePath, "config-parser")).createCompilerHostFromProjectRoot(projectDir, cacheDir) } const ignoredPackageMetadataProperties = new Set(["dist", "gitHead", "build", "jspm", "ava", "xo", "nyc", "eslintConfig", "contributors", "bundleDependencies", "tags"]) interface CleanupPackageFileOptions { readonly isRemovePackageScripts: boolean readonly isRemovePackageKeywords: boolean readonly isMain: boolean } function cleanupPackageJson(data: any, options: CleanupPackageFileOptions): any { const deps = data.dependencies // https://github.com/electron-userland/electron-builder/issues/507#issuecomment-312772099 const isRemoveBabel = deps != null && typeof deps === "object" && !Object.getOwnPropertyNames(deps).some(it => it.startsWith("babel")) try { let changed = false for (const prop of Object.getOwnPropertyNames(data)) { // removing devDependencies from package.json breaks levelup in electron, so, remove it only from main package.json if ( prop[0] === "_" || ignoredPackageMetadataProperties.has(prop) || (options.isRemovePackageScripts && prop === "scripts") || (options.isRemovePackageKeywords && prop === "keywords") || (options.isMain && prop === "devDependencies") || (!options.isMain && prop === "bugs") || (isRemoveBabel && prop === "babel") ) { delete data[prop] changed = true } } if (changed) { return JSON.stringify(data, null, 2) } } catch (e: any) { debug(e) } return null } async function modifyMainPackageJson(file: string, extraMetadata: any, isRemovePackageScripts: boolean, isRemovePackageKeywords: boolean) { const mainPackageData = JSON.parse(await readFile(file, "utf-8")) if (extraMetadata != null) { deepAssign(mainPackageData, extraMetadata) } // https://github.com/electron-userland/electron-builder/issues/1212 const serializedDataIfChanged = cleanupPackageJson(mainPackageData, { isMain: true, isRemovePackageScripts, isRemovePackageKeywords, }) if (serializedDataIfChanged != null) { return serializedDataIfChanged } else if (extraMetadata != null) { return JSON.stringify(mainPackageData, null, 2) } return null }