From af001990447dc3a2f96566037c305866dab56626 Mon Sep 17 00:00:00 2001 From: David Sanders Date: Wed, 11 Jan 2023 17:26:11 -0800 Subject: [PATCH] refactor: improve typing of package.json usages --- packages/api/cli/src/electron-forge.ts | 7 ++- packages/api/cli/src/util/check-system.ts | 9 +-- packages/api/core/src/api/import.ts | 16 +++--- .../api/core/src/api/init-scripts/init-npm.ts | 6 +- packages/api/core/src/api/init.ts | 2 +- packages/api/core/src/api/package.ts | 4 +- packages/api/core/src/api/start.ts | 4 +- .../api/core/src/util/electron-executable.ts | 8 +-- packages/api/core/src/util/forge-config.ts | 6 +- .../api/core/src/util/read-package-json.ts | 10 ++-- packages/api/core/src/util/resolve-dir.ts | 5 +- .../api/core/src/util/upgrade-forge-config.ts | 19 ++----- .../test/fast/electron-executable_spec.ts | 38 ++++++++++--- packages/api/core/test/fast/hook_spec.ts | 14 +++-- packages/api/core/test/fast/make_spec.ts | 7 ++- .../core/test/fast/read-package-json_spec.ts | 9 ++- packages/api/core/test/fast/start_spec.ts | 9 ++- .../test/fast/upgrade-forge-config_spec.ts | 8 ++- packages/api/core/test/slow/api_spec_slow.ts | 24 ++++---- .../slow/install-dependencies_spec_slow.ts | 3 +- packages/maker/base/src/Maker.ts | 4 +- packages/maker/deb/test/MakerDeb_spec.ts | 4 +- packages/maker/dmg/test/MakerDMG_spec.ts | 4 +- .../maker/flatpak/test/MakerFlatpak_spec.ts | 4 +- packages/maker/pkg/test/MakerPKG_spec.ts | 4 +- packages/maker/rpm/test/MakerRpm_spec.ts | 4 +- packages/maker/snap/test/MakerSnap_spec.ts | 4 +- .../plugin/compile/src/lib/compile-hook.ts | 4 +- .../test/PublisherERS_spec.ts | 4 ++ packages/template/base/src/BaseTemplate.ts | 10 ++-- .../src/WebpackTypeScriptTemplate.ts | 5 +- .../test/WebpackTypeScript_spec_slow.ts | 4 +- .../utils/core-utils/src/electron-version.ts | 27 ++++----- .../core-utils/test/electron-version_spec.ts | 57 ++++++++++++------- packages/utils/types/src/index.ts | 32 ++++++++++- 35 files changed, 226 insertions(+), 153 deletions(-) diff --git a/packages/api/cli/src/electron-forge.ts b/packages/api/cli/src/electron-forge.ts index b4730fb8c5..e4ad02955a 100755 --- a/packages/api/cli/src/electron-forge.ts +++ b/packages/api/cli/src/electron-forge.ts @@ -1,16 +1,19 @@ #!/usr/bin/env node // This file requires a shebang above. If it is missing, this is an error. +import path from 'path'; + +import { ElectronForgePackageJSON } from '@electron-forge/shared-types'; import chalk from 'chalk'; import program from 'commander'; +import fs from 'fs-extra'; import { Listr } from 'listr2'; import './util/terminate'; import { checkSystem } from './util/check-system'; -// eslint-disable-next-line @typescript-eslint/no-var-requires -const metadata = require('../package.json'); +const metadata: ElectronForgePackageJSON = fs.readJsonSync(path.join(__dirname, '../package.json')); const originalSC = program.executeSubCommand.bind(program); program.executeSubCommand = (argv: string[], args: string[], unknown: string[]) => { diff --git a/packages/api/cli/src/util/check-system.ts b/packages/api/cli/src/util/check-system.ts index c64f8c4b70..e05672d07c 100644 --- a/packages/api/cli/src/util/check-system.ts +++ b/packages/api/cli/src/util/check-system.ts @@ -3,7 +3,7 @@ import os from 'os'; import path from 'path'; import { utils as forgeUtils } from '@electron-forge/core'; -import { ForgeListrTask } from '@electron-forge/shared-types'; +import { ElectronForgePackageJSON, ForgeListrTask } from '@electron-forge/shared-types'; import debug from 'debug'; import fs from 'fs-extra'; import semver from 'semver'; @@ -17,11 +17,12 @@ async function getGitVersion(): Promise { } async function checkNodeVersion() { - const { engines } = await fs.readJson(path.resolve(__dirname, '..', '..', 'package.json')); - const versionSatisfied = semver.satisfies(process.versions.node, engines.node); + const packageJSON: ElectronForgePackageJSON = await fs.readJson(path.resolve(__dirname, '..', '..', 'package.json')); + + const versionSatisfied = semver.satisfies(process.versions.node, packageJSON.engines.node); if (!versionSatisfied) { - throw new Error(`You are running Node.js version ${process.versions.node}, but Electron Forge requires Node.js ${engines.node}.`); + throw new Error(`You are running Node.js version ${process.versions.node}, but Electron Forge requires Node.js ${packageJSON.engines.node}.`); } return process.versions.node; diff --git a/packages/api/core/src/api/import.ts b/packages/api/core/src/api/import.ts index d1db4e98b3..6217efecde 100644 --- a/packages/api/core/src/api/import.ts +++ b/packages/api/core/src/api/import.ts @@ -1,6 +1,7 @@ import path from 'path'; import { safeYarnOrNpm, updateElectronDependency } from '@electron-forge/core-utils'; +import { ForgeAppPackageJSON } from '@electron-forge/shared-types'; import baseTemplate from '@electron-forge/template-base'; import chalk from 'chalk'; import debug from 'debug'; @@ -103,11 +104,11 @@ export default async ({ let importDevDeps = ([] as string[]).concat(devDeps); let importExactDevDeps = ([] as string[]).concat(exactDevDeps); - let packageJSON = await readRawPackageJson(dir); + let packageJSON = (await readRawPackageJson(dir)) as ForgeAppPackageJSON; if (!packageJSON.version) { task.output = chalk.yellow(`Please set the ${chalk.green('"version"')} in your application's package.json`); } - if (packageJSON.config && packageJSON.config.forge) { + if (packageJSON.config?.forge) { if (packageJSON.config.forge.makers) { task.output = chalk.green('Existing Electron Forge configuration detected'); if (typeof shouldContinueOnExisting === 'function') { @@ -123,7 +124,7 @@ export default async ({ ); } else { d('Upgrading an Electron Forge < 6 project'); - packageJSON.config.forge = upgradeForgeConfig(packageJSON.config.forge); + packageJSON.config.forge = upgradeForgeConfig(packageJSON.config.forge as Record); importDevDeps = updateUpgradedForgeDevDeps(packageJSON, importDevDeps); } } @@ -168,12 +169,13 @@ export default async ({ d('reading current scripts object:', packageJSON.scripts); const updatePackageScript = async (scriptName: string, newValue: string) => { - if (packageJSON.scripts[scriptName] !== newValue) { + if (packageJSON.scripts?.[scriptName] !== newValue) { let update = true; if (typeof shouldUpdateScript === 'function') { update = await shouldUpdateScript(scriptName, newValue); } if (update) { + packageJSON.scripts = packageJSON.scripts ?? {}; packageJSON.scripts[scriptName] = newValue; } } @@ -224,8 +226,8 @@ export default async ({ d('detected existing Forge config in package.json, merging with base template Forge config'); // eslint-disable-next-line @typescript-eslint/no-var-requires const templateConfig = require(path.resolve(baseTemplate.templateDir, 'forge.config.js')); - packageJSON = await readRawPackageJson(dir); - merge(templateConfig, packageJSON.config.forge); // mutates the templateConfig object + packageJSON = (await readRawPackageJson(dir)) as ForgeAppPackageJSON; + merge(templateConfig, packageJSON.config?.forge); // mutates the templateConfig object await writeChanges(); // otherwise, write to forge.config.js } else { @@ -258,7 +260,7 @@ export default async ({ }, task: (_, task) => { task.output = `We have attempted to convert your app to be in a format that Electron Forge understands. - + Thanks for using ${chalk.green('Electron Forge')}!`; }, }, diff --git a/packages/api/core/src/api/init-scripts/init-npm.ts b/packages/api/core/src/api/init-scripts/init-npm.ts index 4cced9fe41..c5becffdb6 100644 --- a/packages/api/core/src/api/init-scripts/init-npm.ts +++ b/packages/api/core/src/api/init-scripts/init-npm.ts @@ -1,7 +1,7 @@ import path from 'path'; import { safeYarnOrNpm, yarnOrNpmSpawn } from '@electron-forge/core-utils'; -import { ForgeListrTask } from '@electron-forge/shared-types'; +import { ForgeListrTask, PackageJSON } from '@electron-forge/shared-types'; import debug from 'debug'; import fs from 'fs-extra'; @@ -9,7 +9,7 @@ import installDepList, { DepType, DepVersionRestriction } from '../../util/insta import { readRawPackageJson } from '../../util/read-package-json'; const d = debug('electron-forge:init:npm'); -const corePackage = fs.readJsonSync(path.resolve(__dirname, '../../../package.json')); +const corePackage: PackageJSON = fs.readJsonSync(path.resolve(__dirname, '../../../package.json')); export function siblingDep(name: string): string { return `@electron-forge/${name}@^${corePackage.version}`; @@ -40,7 +40,7 @@ export const initNPM = async (dir: string, task: ForgeListrTask): Promise; targets: InternalTargetDefinition[]; diff --git a/packages/api/core/src/api/start.ts b/packages/api/core/src/api/start.ts index 6c5482e8b5..1675db16b6 100644 --- a/packages/api/core/src/api/start.ts +++ b/packages/api/core/src/api/start.ts @@ -1,7 +1,7 @@ import { spawn, SpawnOptions } from 'child_process'; import { getElectronVersion, listrCompatibleRebuildHook } from '@electron-forge/core-utils'; -import { ElectronProcess, ForgeArch, ForgeListrTask, ForgePlatform, ResolvedForgeConfig, StartOptions } from '@electron-forge/shared-types'; +import { ElectronProcess, ForgeArch, ForgeListrTask, ForgePlatform, PackageJSON, ResolvedForgeConfig, StartOptions } from '@electron-forge/shared-types'; import chalk from 'chalk'; import debug from 'debug'; import { Listr } from 'listr2'; @@ -19,7 +19,7 @@ export { StartOptions }; type StartContext = { dir: string; forgeConfig: ResolvedForgeConfig; - packageJSON: any; + packageJSON: PackageJSON; spawned: ElectronProcess; }; diff --git a/packages/api/core/src/util/electron-executable.ts b/packages/api/core/src/util/electron-executable.ts index d0e7f435bc..16746f3a43 100644 --- a/packages/api/core/src/util/electron-executable.ts +++ b/packages/api/core/src/util/electron-executable.ts @@ -1,12 +1,10 @@ import path from 'path'; import { getElectronModulePath } from '@electron-forge/core-utils'; +import { PackageJSON } from '@electron-forge/shared-types'; import chalk from 'chalk'; import logSymbols from 'log-symbols'; -type PackageJSON = Record; -type Dependencies = Record; - export function pluginCompileExists(packageJSON: PackageJSON): boolean { if (!packageJSON.devDependencies) { return false; @@ -15,11 +13,11 @@ export function pluginCompileExists(packageJSON: PackageJSON): boolean { const pluginCompileName = '@electron-forge/plugin-compile'; const findPluginCompile = (packageName: string): boolean => packageName === pluginCompileName; - if (Object.keys(packageJSON.devDependencies as Dependencies).find(findPluginCompile)) { + if (Object.keys(packageJSON.devDependencies).find(findPluginCompile)) { return true; } - if (Object.keys((packageJSON.dependencies as Dependencies) || {}).find(findPluginCompile)) { + if (Object.keys(packageJSON.dependencies || {}).find(findPluginCompile)) { console.warn(logSymbols.warning, chalk.yellow(`${pluginCompileName} was detected in dependencies, it should be in devDependencies`)); return true; } diff --git a/packages/api/core/src/util/forge-config.ts b/packages/api/core/src/util/forge-config.ts index 03b9a229cf..5ce184d1eb 100644 --- a/packages/api/core/src/util/forge-config.ts +++ b/packages/api/core/src/util/forge-config.ts @@ -1,6 +1,6 @@ import path from 'path'; -import { ForgeConfig, ResolvedForgeConfig } from '@electron-forge/shared-types'; +import { ForgeAppPackageJSON, ForgeConfig, ResolvedForgeConfig } from '@electron-forge/shared-types'; import fs from 'fs-extra'; import * as interpret from 'interpret'; import { template } from 'lodash'; @@ -106,8 +106,8 @@ export function renderConfigTemplate(dir: string, templateObj: any, obj: any): v type MaybeESM = T | { default: T }; export default async (dir: string): Promise => { - const packageJSON = await readRawPackageJson(dir); - let forgeConfig: ForgeConfig | string | null = packageJSON.config && packageJSON.config.forge ? packageJSON.config.forge : null; + const packageJSON = (await readRawPackageJson(dir)) as ForgeAppPackageJSON; + let forgeConfig: ForgeConfig | string | null = packageJSON.config?.forge ?? null; if (!forgeConfig || typeof forgeConfig === 'string') { for (const extension of ['.js', ...Object.keys(interpret.extensions)]) { diff --git a/packages/api/core/src/util/read-package-json.ts b/packages/api/core/src/util/read-package-json.ts index edd180e6bc..f9e310e038 100644 --- a/packages/api/core/src/util/read-package-json.ts +++ b/packages/api/core/src/util/read-package-json.ts @@ -1,13 +1,11 @@ import path from 'path'; -import { ResolvedForgeConfig } from '@electron-forge/shared-types'; +import { ForgeAppPackageJSON, PackageJSON, ResolvedForgeConfig } from '@electron-forge/shared-types'; import fs from 'fs-extra'; import { runMutatingHook } from './hook'; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const readRawPackageJson = async (dir: string): Promise => fs.readJson(path.resolve(dir, 'package.json')); +export const readRawPackageJson = async (dir: string): Promise => fs.readJson(path.resolve(dir, 'package.json')); -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const readMutatedPackageJson = async (dir: string, forgeConfig: ResolvedForgeConfig): Promise => - runMutatingHook(forgeConfig, 'readPackageJson', await readRawPackageJson(dir)); +export const readMutatedPackageJson = async (dir: string, forgeConfig: ResolvedForgeConfig): Promise => + runMutatingHook(forgeConfig, 'readPackageJson', await readRawPackageJson(dir)) as Promise; diff --git a/packages/api/core/src/util/resolve-dir.ts b/packages/api/core/src/util/resolve-dir.ts index f023a5ec0c..eccfe9d0cd 100644 --- a/packages/api/core/src/util/resolve-dir.ts +++ b/packages/api/core/src/util/resolve-dir.ts @@ -1,6 +1,7 @@ import path from 'path'; import { getElectronVersion } from '@electron-forge/core-utils'; +import { ForgeAppPackageJSON } from '@electron-forge/shared-types'; import debug from 'debug'; import fs from 'fs-extra'; @@ -22,7 +23,7 @@ export default async (dir: string): Promise => { const testPath = path.resolve(mDir, 'package.json'); d('searching for project in:', mDir); if (await fs.pathExists(testPath)) { - const packageJSON = await readRawPackageJson(mDir); + const packageJSON = (await readRawPackageJson(mDir)) as ForgeAppPackageJSON; // TODO: Move this check to inside the forge config resolver and use // mutatedPackageJson reader @@ -34,7 +35,7 @@ export default async (dir: string): Promise => { } } - if (packageJSON.config && packageJSON.config.forge) { + if (packageJSON.config?.forge) { d('electron-forge compatible package.json found in', testPath); return mDir; } diff --git a/packages/api/core/src/util/upgrade-forge-config.ts b/packages/api/core/src/util/upgrade-forge-config.ts index a8daddd553..298f842cfb 100644 --- a/packages/api/core/src/util/upgrade-forge-config.ts +++ b/packages/api/core/src/util/upgrade-forge-config.ts @@ -1,6 +1,6 @@ import path from 'path'; -import { ForgeConfig, ForgePlatform, IForgeResolvableMaker, IForgeResolvablePublisher } from '@electron-forge/shared-types'; +import { ForgeAppPackageJSON, ForgeConfig, ForgePlatform, IForgeResolvableMaker, IForgeResolvablePublisher } from '@electron-forge/shared-types'; import { siblingDep } from '../api/init-scripts/init-npm'; @@ -33,13 +33,6 @@ type Forge5Config = { type Forge5ConfigKey = keyof Forge5Config; -type ForgePackageJSON = Record & { - config: { - forge: ForgeConfig; - }; - devDependencies: Record; -}; - function mapMakeTargets(forge5Config: Forge5Config): Map { const makeTargets = new Map(); if (forge5Config.make_targets) { @@ -160,15 +153,15 @@ export default function upgradeForgeConfig(forge5Config: Forge5Config): ForgeCon return forgeConfig; } -export function updateUpgradedForgeDevDeps(packageJSON: ForgePackageJSON, devDeps: string[]): string[] { - const forgeConfig = packageJSON.config.forge; +export function updateUpgradedForgeDevDeps(packageJSON: ForgeAppPackageJSON, devDeps: string[]): string[] { + const forgeConfig = packageJSON.config?.forge; devDeps = devDeps.filter((dep) => !dep.startsWith('@electron-forge/maker-')); - devDeps = devDeps.concat((forgeConfig.makers as IForgeResolvableMaker[]).map((maker: IForgeResolvableMaker) => siblingDep(path.basename(maker.name)))); + devDeps = devDeps.concat((forgeConfig?.makers as IForgeResolvableMaker[]).map((maker: IForgeResolvableMaker) => siblingDep(path.basename(maker.name)))); devDeps = devDeps.concat( - (forgeConfig.publishers as IForgeResolvablePublisher[]).map((publisher: IForgeResolvablePublisher) => siblingDep(path.basename(publisher.name))) + (forgeConfig?.publishers as IForgeResolvablePublisher[]).map((publisher: IForgeResolvablePublisher) => siblingDep(path.basename(publisher.name))) ); - if (Object.keys(packageJSON.devDependencies).find((dep: string) => dep === 'electron-prebuilt-compile')) { + if (Object.keys(packageJSON.devDependencies ?? {}).find((dep: string) => dep === 'electron-prebuilt-compile')) { devDeps = devDeps.concat(siblingDep('plugin-compile')); } diff --git a/packages/api/core/test/fast/electron-executable_spec.ts b/packages/api/core/test/fast/electron-executable_spec.ts index ffd72f91b9..7a575d08ca 100644 --- a/packages/api/core/test/fast/electron-executable_spec.ts +++ b/packages/api/core/test/fast/electron-executable_spec.ts @@ -1,5 +1,6 @@ import path from 'path'; +import { PackageJSON } from '@electron-forge/shared-types'; import chai, { expect } from 'chai'; import { createSandbox } from 'sinon'; import sinonChai from 'sinon-chai'; @@ -23,7 +24,9 @@ describe('locateElectronExecutable', () => { it('returns the correct path to electron', async () => { const appFixture = path.join(fixtureDir, 'electron_app'); - const packageJSON = { + const packageJSON: PackageJSON = { + name: 'foo', + version: '0.0.0-development', devDependencies: { electron: '^100.0.0' }, }; @@ -33,7 +36,9 @@ describe('locateElectronExecutable', () => { it('warns and returns a hardcoded path to electron if another electron module does not export a string', async () => { const appFixture = path.join(fixtureDir, 'bad-export'); - const packageJSON = { + const packageJSON: PackageJSON = { + name: 'foo', + version: '0.0.0-development', devDependencies: { '@electron-forge/plugin-compile': '^6.0.0-beta.1', 'electron-prebuilt-compile': '^1.4.0', @@ -45,7 +50,9 @@ describe('locateElectronExecutable', () => { }); it('warns if prebuilt-compile exists without the corresponding plugin', async () => { - const packageJSON = { + const packageJSON: PackageJSON = { + name: 'foo', + version: '0.0.0-development', devDependencies: { 'electron-prebuilt-compile': '1.0.0' }, }; const compileFixture = path.join(fixtureDir, 'prebuilt-compile'); @@ -55,7 +62,9 @@ describe('locateElectronExecutable', () => { }); it('does not warn if prebuilt-compile exists with the corresponding plugin', async () => { - const packageJSON = { + const packageJSON: PackageJSON = { + name: 'foo', + version: '0.0.0-development', devDependencies: { '@electron-forge/plugin-compile': '^6.0.0-beta.1', 'electron-prebuilt-compile': '1.0.0', @@ -80,15 +89,26 @@ describe('pluginCompileExists', () => { }); it('returns false if there is no devDependencies', () => { - expect(pluginCompileExists({})).to.equal(false); + const packageJSON: PackageJSON = { + name: 'foo', + version: '0.0.0-development', + }; + expect(pluginCompileExists(packageJSON)).to.equal(false); }); it('returns false if the plugin is not found in devDependencies', () => { - expect(pluginCompileExists({ devDependencies: {} })).to.equal(false); + const packageJSON: PackageJSON = { + name: 'foo', + version: '0.0.0-development', + devDependencies: {}, + }; + expect(pluginCompileExists(packageJSON)).to.equal(false); }); it('returns true if the plugin is found in devDependencies', () => { - const packageJSON = { + const packageJSON: PackageJSON = { + name: 'foo', + version: '0.0.0-development', devDependencies: { '@electron-forge/plugin-compile': '^6.0.0-beta.1' }, }; @@ -97,7 +117,9 @@ describe('pluginCompileExists', () => { }); it('warns and returns true if the plugin is found in dependencies', () => { - const packageJSON = { + const packageJSON: PackageJSON = { + name: 'foo', + version: '0.0.0-development', dependencies: { '@electron-forge/plugin-compile': '^6.0.0-beta.1' }, devDependencies: {}, }; diff --git a/packages/api/core/test/fast/hook_spec.ts b/packages/api/core/test/fast/hook_spec.ts index c54c9ce2db..b8892257bb 100644 --- a/packages/api/core/test/fast/hook_spec.ts +++ b/packages/api/core/test/fast/hook_spec.ts @@ -1,4 +1,4 @@ -import { ForgeHookFn, ResolvedForgeConfig } from '@electron-forge/shared-types'; +import { ForgeHookFn, PackageJSON, ResolvedForgeConfig } from '@electron-forge/shared-types'; import { expect } from 'chai'; import { SinonStub, stub } from 'sinon'; @@ -31,10 +31,12 @@ describe('hooks', () => { describe('runMutatingHook', () => { it('should return the input when running non existent hooks', async () => { - const info = { + const packageJSON: PackageJSON = { + name: 'foo', + version: '0.0.0-development', foo: 'bar', }; - expect(await runMutatingHook({ ...fakeConfig }, 'readPackageJson', info)).to.equal(info); + expect(await runMutatingHook({ ...fakeConfig }, 'readPackageJson', packageJSON)).to.equal(packageJSON); }); it('should return the mutated input when returned from a hook', async () => { @@ -45,10 +47,12 @@ describe('hooks', () => { mutated: 'foo', }) ); - const info = { + const packageJSON: PackageJSON = { + name: 'foo', + version: '0.0.0-development', foo: 'bar', }; - const output = await runMutatingHook({ hooks: { readPackageJson: myStub }, ...fakeConfig }, 'readPackageJson', info); + const output = await runMutatingHook({ hooks: { readPackageJson: myStub }, ...fakeConfig }, 'readPackageJson', packageJSON); expect(output).to.deep.equal({ mutated: 'foo', }); diff --git a/packages/api/core/test/fast/make_spec.ts b/packages/api/core/test/fast/make_spec.ts index 47cca20bf7..e9ae82c31d 100644 --- a/packages/api/core/test/fast/make_spec.ts +++ b/packages/api/core/test/fast/make_spec.ts @@ -1,7 +1,8 @@ import * as path from 'path'; -import { ForgeMakeResult } from '@electron-forge/shared-types'; +import { ForgeMakeResult, PackageJSON } from '@electron-forge/shared-types'; import { expect } from 'chai'; +import fs from 'fs-extra'; import proxyquire from 'proxyquire'; import { MakeOptions } from '../../src/api'; @@ -13,7 +14,7 @@ describe('make', () => { it('works with scoped package names', async () => { const stubbedMake: (opts: MakeOptions) => Promise = proxyquire.noCallThru().load('../../src/api/make', { '../util/read-package-json': { - readMutatedPackageJson: () => Promise.resolve(require('../fixture/app-with-scoped-name/package.json')), + readMutatedPackageJson: () => Promise.resolve(fs.readJsonSync(path.join(__dirname, '../fixture/app-with-scoped-name/package.json'))), }, }).default; await stubbedMake({ @@ -81,7 +82,7 @@ describe('make', () => { it('can skip makers via config', async () => { const stubbedMake = proxyquire.noCallThru().load('../../src/api/make', { '../util/read-package-json': { - readMutatedPackageJson: () => Promise.resolve(require('../fixture/app-with-maker-disable/package.json')), + readMutatedPackageJson: () => Promise.resolve(fs.readJsonSync(path.join(__dirname, '../fixture/app-with-maker-disable/package.json'))), }, }).default; await expect( diff --git a/packages/api/core/test/fast/read-package-json_spec.ts b/packages/api/core/test/fast/read-package-json_spec.ts index 50ae2f1fe1..099004de25 100644 --- a/packages/api/core/test/fast/read-package-json_spec.ts +++ b/packages/api/core/test/fast/read-package-json_spec.ts @@ -1,7 +1,8 @@ import path from 'path'; -import { ResolvedForgeConfig } from '@electron-forge/shared-types'; +import { PackageJSON, ResolvedForgeConfig } from '@electron-forge/shared-types'; import { expect } from 'chai'; +import fs from 'fs-extra'; import { readMutatedPackageJson, readRawPackageJson } from '../../src/util/read-package-json'; @@ -16,7 +17,9 @@ const emptyForgeConfig: Partial = { describe('read-package-json', () => { describe('readRawPackageJson', () => { it('should find a package.json file from the given directory', async () => { - expect(await readRawPackageJson(path.resolve(__dirname, '../..'))).to.deep.equal(require('../../package.json')); + expect(await readRawPackageJson(path.resolve(__dirname, '../..'))).to.deep.equal( + fs.readJsonSync(path.join(__dirname, '../../package.json')) as PackageJSON + ); }); }); @@ -33,7 +36,7 @@ describe('read-package-json', () => { getHookListrTasks: () => Promise.resolve([]), }, } as ResolvedForgeConfig) - ).to.deep.equal(require('../../package.json')); + ).to.deep.equal(fs.readJsonSync(path.join(__dirname, '../../package.json')) as PackageJSON); }); it('should allow mutations from hooks', async () => { diff --git a/packages/api/core/test/fast/start_spec.ts b/packages/api/core/test/fast/start_spec.ts index 1fd42cfd12..d688c8afd2 100644 --- a/packages/api/core/test/fast/start_spec.ts +++ b/packages/api/core/test/fast/start_spec.ts @@ -1,5 +1,8 @@ -import { ElectronProcess } from '@electron-forge/shared-types'; +import path from 'path'; + +import { ElectronProcess, PackageJSON } from '@electron-forge/shared-types'; import { expect } from 'chai'; +import fs from 'fs-extra'; import proxyquire from 'proxyquire'; import { SinonStub, stub } from 'sinon'; @@ -7,7 +10,7 @@ import { StartOptions } from '../../src/api'; describe('start', () => { let start: (opts: StartOptions) => Promise; - let packageJSON: Record; + let packageJSON: PackageJSON; let resolveStub: SinonStub; let spawnStub: SinonStub; let shouldOverride: false | { on: () => void }; @@ -17,7 +20,7 @@ describe('start', () => { resolveStub = stub(); spawnStub = stub(); shouldOverride = false; - packageJSON = require('../fixture/dummy_app/package.json'); + packageJSON = fs.readJsonSync(path.join(__dirname, '../fixture/dummy_app/package.json')); start = proxyquire.noCallThru().load('../../src/api/start', { '../util/electron-executable': () => Promise.resolve('fake_electron_path'), diff --git a/packages/api/core/test/fast/upgrade-forge-config_spec.ts b/packages/api/core/test/fast/upgrade-forge-config_spec.ts index ef04adb0c6..818f80d39c 100644 --- a/packages/api/core/test/fast/upgrade-forge-config_spec.ts +++ b/packages/api/core/test/fast/upgrade-forge-config_spec.ts @@ -1,6 +1,6 @@ import assert from 'assert'; -import { ForgeConfig, IForgeResolvableMaker, IForgeResolvablePublisher } from '@electron-forge/shared-types'; +import { ForgeAppPackageJSON, ForgeConfig, IForgeResolvableMaker, IForgeResolvablePublisher } from '@electron-forge/shared-types'; import { expect } from 'chai'; import { merge } from 'lodash'; @@ -115,7 +115,9 @@ describe('upgradeForgeConfig', () => { }); describe('updateUpgradedForgeDevDeps', () => { - const skeletonPackageJSON = { + const skeletonPackageJSON: ForgeAppPackageJSON = { + name: 'foo', + version: '0.0.0-development', config: { forge: { packagerConfig: {}, @@ -141,6 +143,7 @@ describe('updateUpgradedForgeDevDeps', () => { it('adds makers to devDependencies', () => { const packageJSON = merge({}, skeletonPackageJSON); + assert(packageJSON.config?.forge); packageJSON.config.forge.makers = [ { name: '@electron-forge/maker-zip', @@ -161,6 +164,7 @@ describe('updateUpgradedForgeDevDeps', () => { it('adds publishers to devDependencies', () => { const packageJSON = merge({}, skeletonPackageJSON); + assert(packageJSON.config?.forge); packageJSON.config.forge.publishers = [{ name: '@electron-forge/publisher-github' }, { name: '@electron-forge/publisher-snapcraft' }]; const actual = updateUpgradedForgeDevDeps(packageJSON, []); diff --git a/packages/api/core/test/slow/api_spec_slow.ts b/packages/api/core/test/slow/api_spec_slow.ts index a0198ebe8b..ceeb09f4c2 100644 --- a/packages/api/core/test/slow/api_spec_slow.ts +++ b/packages/api/core/test/slow/api_spec_slow.ts @@ -3,7 +3,7 @@ import { execSync } from 'child_process'; import path from 'path'; import { createDefaultCertificate } from '@electron-forge/maker-appx'; -import { ForgeConfig, IForgeResolvableMaker } from '@electron-forge/shared-types'; +import { ForgeAppPackageJSON, IForgeResolvableMaker } from '@electron-forge/shared-types'; import { ensureTestDirIsNonexistent, expectLintToPass, expectProjectPathExists } from '@electron-forge/test-utils'; import { expect } from 'chai'; import { readMetadata } from 'electron-installer-common'; @@ -21,15 +21,9 @@ const forge = proxyquire.noCallThru().load('../../src/api', { }).api; type BeforeInitFunction = () => void; -type PackageJSON = Record & { - config: { - forge: ForgeConfig; - }; - dependencies: Record; -}; - -async function updatePackageJSON(dir: string, packageJSONUpdater: (packageJSON: PackageJSON) => Promise) { - const packageJSON = await readRawPackageJson(dir); + +async function updatePackageJSON(dir: string, packageJSONUpdater: (packageJSON: ForgeAppPackageJSON) => Promise) { + const packageJSON = (await readRawPackageJson(dir)) as ForgeAppPackageJSON; await packageJSONUpdater(packageJSON); await fs.writeJson(path.resolve(dir, 'package.json'), packageJSON); } @@ -257,12 +251,12 @@ describe('Electron Forge API', () => { it('throws an error when all is set', async () => { await updatePackageJSON(dir, async (packageJSON) => { - assert(packageJSON.config.forge.packagerConfig); + assert(packageJSON.config?.forge?.packagerConfig); packageJSON.config.forge.packagerConfig.all = true; }); await expect(forge.package({ dir })).to.eventually.be.rejectedWith(/packagerConfig\.all is not supported by Electron Forge/); await updatePackageJSON(dir, async (packageJSON) => { - assert(packageJSON.config.forge.packagerConfig); + assert(packageJSON.config?.forge?.packagerConfig); delete packageJSON.config.forge.packagerConfig.all; }); }); @@ -279,6 +273,7 @@ describe('Electron Forge API', () => { it('can make from custom outDir without errors', async () => { await updatePackageJSON(dir, async (packageJSON) => { + assert(packageJSON.config?.forge); // eslint-disable-next-line node/no-missing-require packageJSON.config.forge.makers = [{ name: require.resolve('@electron-forge/maker-zip') } as IForgeResolvableMaker]; }); @@ -305,14 +300,14 @@ describe('Electron Forge API', () => { after(async () => { await fs.remove(path.resolve(dir, 'node_modules/ref-napi')); await updatePackageJSON(dir, async (packageJSON) => { - delete packageJSON.dependencies['ref-napi']; + delete packageJSON.dependencies?.['ref-napi']; }); }); }); it('can package without errors', async () => { await updatePackageJSON(dir, async (packageJSON) => { - assert(packageJSON.config.forge.packagerConfig); + assert(packageJSON.config?.forge?.packagerConfig); packageJSON.config.forge.packagerConfig.asar = true; }); @@ -385,6 +380,7 @@ describe('Electron Forge API', () => { describe(`make (with target=${path.basename(path.dirname(target().name))})`, async () => { before(async () => { await updatePackageJSON(dir, async (packageJSON) => { + assert(packageJSON.config?.forge); packageJSON.config.forge.makers = [target() as IForgeResolvableMaker]; }); }); diff --git a/packages/api/core/test/slow/install-dependencies_spec_slow.ts b/packages/api/core/test/slow/install-dependencies_spec_slow.ts index 8f5b71a99d..cdb33a27f3 100644 --- a/packages/api/core/test/slow/install-dependencies_spec_slow.ts +++ b/packages/api/core/test/slow/install-dependencies_spec_slow.ts @@ -1,6 +1,7 @@ import os from 'os'; import path from 'path'; +import { PackageJSON } from '@electron-forge/shared-types'; import { expect } from 'chai'; import fs from 'fs-extra'; @@ -17,7 +18,7 @@ if (!(process.platform === 'linux' && process.env.CI)) { it('should install the latest minor version when the dependency has a caret', async () => { await installDeps(installDir, ['debug@^2.0.0']); - const packageJSON = require(path.resolve(installDir, 'node_modules', 'debug', 'package.json')); + const packageJSON: PackageJSON = fs.readJsonSync(path.resolve(installDir, 'node_modules', 'debug', 'package.json')); expect(packageJSON.version).to.not.equal('2.0.0'); }); diff --git a/packages/maker/base/src/Maker.ts b/packages/maker/base/src/Maker.ts index 10882d2c92..a9eb7231d1 100644 --- a/packages/maker/base/src/Maker.ts +++ b/packages/maker/base/src/Maker.ts @@ -1,6 +1,6 @@ import path from 'path'; -import { ForgeArch, ForgePlatform, IForgeMaker, ResolvedForgeConfig } from '@electron-forge/shared-types'; +import { ForgeAppPackageJSON, ForgeArch, ForgePlatform, IForgeMaker, ResolvedForgeConfig } from '@electron-forge/shared-types'; import fs from 'fs-extra'; import which from 'which'; @@ -35,7 +35,7 @@ export interface MakerOptions { /** * The application's package.json file */ - packageJSON: any; // eslint-disable-line @typescript-eslint/no-explicit-any + packageJSON: ForgeAppPackageJSON; } export default abstract class Maker implements IForgeMaker { diff --git a/packages/maker/deb/test/MakerDeb_spec.ts b/packages/maker/deb/test/MakerDeb_spec.ts index f4a568a6cf..9eebabe948 100644 --- a/packages/maker/deb/test/MakerDeb_spec.ts +++ b/packages/maker/deb/test/MakerDeb_spec.ts @@ -1,7 +1,7 @@ import path from 'path'; import { MakerBase, MakerOptions } from '@electron-forge/maker-base'; -import { ForgeArch } from '@electron-forge/shared-types'; +import { ForgeAppPackageJSON, ForgeArch } from '@electron-forge/shared-types'; import { expect } from 'chai'; import proxyquire from 'proxyquire'; import { SinonStub, stub } from 'sinon'; @@ -29,7 +29,7 @@ describe('MakerDeb', () => { const makeDir = path.resolve('/foo/bar/make'); const appName = 'My Test App'; const targetArch = process.arch; - const packageJSON = { version: '1.2.3' }; + const packageJSON: ForgeAppPackageJSON = { name: 'foo', version: '1.2.3' }; beforeEach(() => { ensureDirectoryStub = stub().returns(Promise.resolve()); diff --git a/packages/maker/dmg/test/MakerDMG_spec.ts b/packages/maker/dmg/test/MakerDMG_spec.ts index 71fd34d405..52e12590e0 100644 --- a/packages/maker/dmg/test/MakerDMG_spec.ts +++ b/packages/maker/dmg/test/MakerDMG_spec.ts @@ -1,7 +1,7 @@ import path from 'path'; import { MakerBase, MakerOptions } from '@electron-forge/maker-base'; -import { ForgeArch } from '@electron-forge/shared-types'; +import { ForgeAppPackageJSON, ForgeArch } from '@electron-forge/shared-types'; import { expect } from 'chai'; import proxyquire from 'proxyquire'; import { SinonStub, stub } from 'sinon'; @@ -29,7 +29,7 @@ describe('MakerDMG', () => { const makeDir = '/my/test/dir/make'; const appName = 'My Test App'; const targetArch = process.arch; - const packageJSON = { version: '1.2.3' }; + const packageJSON: ForgeAppPackageJSON = { name: 'foo', version: '1.2.3' }; beforeEach(() => { ensureFileStub = stub().returns(Promise.resolve()); diff --git a/packages/maker/flatpak/test/MakerFlatpak_spec.ts b/packages/maker/flatpak/test/MakerFlatpak_spec.ts index 3fb793b02e..a568c74f5a 100644 --- a/packages/maker/flatpak/test/MakerFlatpak_spec.ts +++ b/packages/maker/flatpak/test/MakerFlatpak_spec.ts @@ -1,7 +1,7 @@ import path from 'path'; import { MakerBase, MakerOptions } from '@electron-forge/maker-base'; -import { ForgeArch } from '@electron-forge/shared-types'; +import { ForgeAppPackageJSON, ForgeArch } from '@electron-forge/shared-types'; import { expect } from 'chai'; import 'chai-as-promised'; import proxyquire from 'proxyquire'; @@ -30,7 +30,7 @@ describe('MakerFlatpak', () => { const makeDir = path.resolve('/make/dir'); const appName = 'My Test App'; const targetArch = process.arch; - const packageJSON = { version: '1.2.3' }; + const packageJSON: ForgeAppPackageJSON = { name: 'foo', version: '1.2.3' }; beforeEach(() => { ensureDirectoryStub = stub().returns(Promise.resolve()); diff --git a/packages/maker/pkg/test/MakerPKG_spec.ts b/packages/maker/pkg/test/MakerPKG_spec.ts index 59aff54dd3..b29114d544 100644 --- a/packages/maker/pkg/test/MakerPKG_spec.ts +++ b/packages/maker/pkg/test/MakerPKG_spec.ts @@ -1,7 +1,7 @@ import path from 'path'; import { MakerBase, MakerOptions } from '@electron-forge/maker-base'; -import { ForgeArch } from '@electron-forge/shared-types'; +import { ForgeAppPackageJSON, ForgeArch } from '@electron-forge/shared-types'; import { expect } from 'chai'; import proxyquire from 'proxyquire'; import { SinonStub, stub } from 'sinon'; @@ -29,7 +29,7 @@ describe('MakerPKG', () => { const makeDir = '/my/test/dir/make'; const appName = 'My Test App'; const targetArch = process.arch; - const packageJSON = { version: '1.2.3' }; + const packageJSON: ForgeAppPackageJSON = { name: 'foo', version: '1.2.3' }; beforeEach(() => { ensureFileStub = stub().returns(Promise.resolve()); diff --git a/packages/maker/rpm/test/MakerRpm_spec.ts b/packages/maker/rpm/test/MakerRpm_spec.ts index 3c237a6c6c..9775599d8a 100644 --- a/packages/maker/rpm/test/MakerRpm_spec.ts +++ b/packages/maker/rpm/test/MakerRpm_spec.ts @@ -1,7 +1,7 @@ import path from 'path'; import { MakerBase, MakerOptions } from '@electron-forge/maker-base'; -import { ForgeArch } from '@electron-forge/shared-types'; +import { ForgeAppPackageJSON, ForgeArch } from '@electron-forge/shared-types'; import { expect } from 'chai'; import proxyquire from 'proxyquire'; import { SinonStub, stub } from 'sinon'; @@ -29,7 +29,7 @@ describe('MakerRpm', () => { const makeDir = path.resolve('/make/dir'); const appName = 'My Test App'; const targetArch = process.arch; - const packageJSON = { version: '1.2.3' }; + const packageJSON: ForgeAppPackageJSON = { name: 'foo', version: '1.2.3' }; beforeEach(() => { ensureDirectoryStub = stub().returns(Promise.resolve()); diff --git a/packages/maker/snap/test/MakerSnap_spec.ts b/packages/maker/snap/test/MakerSnap_spec.ts index bfc5427ad8..5c078d3309 100644 --- a/packages/maker/snap/test/MakerSnap_spec.ts +++ b/packages/maker/snap/test/MakerSnap_spec.ts @@ -1,7 +1,7 @@ import path from 'path'; import { MakerBase, MakerOptions } from '@electron-forge/maker-base'; -import { ForgeArch } from '@electron-forge/shared-types'; +import { ForgeAppPackageJSON, ForgeArch } from '@electron-forge/shared-types'; import { expect } from 'chai'; import proxyquire from 'proxyquire'; import { SinonStub, stub } from 'sinon'; @@ -28,7 +28,7 @@ describe('MakerSnap', () => { const makeDir = path.resolve('/make/dir'); const appName = 'My Test App'; const targetArch = process.arch; - const packageJSON = { version: '1.2.3' }; + const packageJSON: ForgeAppPackageJSON = { name: 'foo', version: '1.2.3' }; beforeEach(() => { ensureDirectoryStub = stub().returns(Promise.resolve()); diff --git a/packages/plugin/compile/src/lib/compile-hook.ts b/packages/plugin/compile/src/lib/compile-hook.ts index 88265502e2..824af62305 100644 --- a/packages/plugin/compile/src/lib/compile-hook.ts +++ b/packages/plugin/compile/src/lib/compile-hook.ts @@ -1,6 +1,6 @@ import path from 'path'; -import { ForgeHookFn } from '@electron-forge/shared-types'; +import { ForgeHookFn, PackageJSON } from '@electron-forge/shared-types'; import fs from 'fs-extra'; export const createCompileHook = @@ -25,7 +25,7 @@ export const createCompileHook = } } - const packageJSON = await fs.readJson(path.resolve(appDir, 'package.json')); + const packageJSON: PackageJSON = await fs.readJson(path.resolve(appDir, 'package.json')); const index = packageJSON.main || 'index.js'; packageJSON.originalMain = index; diff --git a/packages/publisher/electron-release-server/test/PublisherERS_spec.ts b/packages/publisher/electron-release-server/test/PublisherERS_spec.ts index ccc36d3f5e..8429902bee 100644 --- a/packages/publisher/electron-release-server/test/PublisherERS_spec.ts +++ b/packages/publisher/electron-release-server/test/PublisherERS_spec.ts @@ -52,6 +52,7 @@ describe('PublisherERS', () => { { artifacts: ['/path/to/artifact'], packageJSON: { + name: 'foo', version, }, platform: 'linux', @@ -99,6 +100,7 @@ describe('PublisherERS', () => { { artifacts: ['/path/to/artifact'], packageJSON: { + name: 'foo', version, }, platform: 'linux', @@ -136,6 +138,7 @@ describe('PublisherERS', () => { { artifacts: ['/path/to/existing-artifact'], packageJSON: { + name: 'foo', version, }, platform: 'linux', @@ -175,6 +178,7 @@ describe('PublisherERS', () => { { artifacts: ['/path/to/artifact'], packageJSON: { + name: 'foo', version, }, platform: 'linux', diff --git a/packages/template/base/src/BaseTemplate.ts b/packages/template/base/src/BaseTemplate.ts index dd4818e86c..530c836000 100644 --- a/packages/template/base/src/BaseTemplate.ts +++ b/packages/template/base/src/BaseTemplate.ts @@ -1,13 +1,12 @@ import path from 'path'; -import { ForgeListrTaskDefinition, ForgeTemplate, InitTemplateOptions } from '@electron-forge/shared-types'; +import { ForgeListrTaskDefinition, ForgeTemplate, InitTemplateOptions, PackageJSON } from '@electron-forge/shared-types'; import debug from 'debug'; import fs from 'fs-extra'; import determineAuthor from './determine-author'; -// eslint-disable-next-line @typescript-eslint/no-var-requires -const currentForgeVersion = require('../package.json').version; +const currentForgeVersion = (fs.readJsonSync(path.join(__dirname, '../package.json')) as PackageJSON).version; const d = debug('electron-forge:template:base'); const tmplDir = path.resolve(__dirname, '../tmpl'); @@ -20,7 +19,7 @@ export class BaseTemplate implements ForgeTemplate { get devDependencies(): string[] { const packageJSONPath = path.join(this.templateDir, 'package.json'); if (fs.pathExistsSync(packageJSONPath)) { - const packageDevDeps = fs.readJsonSync(packageJSONPath).devDependencies; + const packageDevDeps = (fs.readJsonSync(packageJSONPath) as PackageJSON).devDependencies; if (packageDevDeps) { return Object.entries(packageDevDeps).map(([packageName, version]) => { if (version === 'ELECTRON_FORGE/VERSION') { @@ -75,10 +74,11 @@ export class BaseTemplate implements ForgeTemplate { } async initializePackageJSON(directory: string): Promise { - const packageJSON = await fs.readJson(path.resolve(__dirname, '../tmpl/package.json')); + const packageJSON: PackageJSON = await fs.readJson(path.resolve(__dirname, '../tmpl/package.json')); packageJSON.productName = packageJSON.name = path.basename(directory).toLowerCase(); packageJSON.author = await determineAuthor(directory); + packageJSON.scripts = packageJSON.scripts ?? {}; packageJSON.scripts.lint = 'echo "No linting configured"'; d('writing package.json to:', directory); diff --git a/packages/template/webpack-typescript/src/WebpackTypeScriptTemplate.ts b/packages/template/webpack-typescript/src/WebpackTypeScriptTemplate.ts index 749ac3ec8f..a95d8cf03f 100644 --- a/packages/template/webpack-typescript/src/WebpackTypeScriptTemplate.ts +++ b/packages/template/webpack-typescript/src/WebpackTypeScriptTemplate.ts @@ -1,6 +1,6 @@ import path from 'path'; -import { ForgeListrTaskDefinition, InitTemplateOptions } from '@electron-forge/shared-types'; +import { ForgeListrTaskDefinition, InitTemplateOptions, PackageJSON } from '@electron-forge/shared-types'; import { BaseTemplate } from '@electron-forge/template-base'; import fs from 'fs-extra'; @@ -52,9 +52,10 @@ class WebpackTypeScriptTemplate extends BaseTemplate { // update package.json const packageJSONPath = path.resolve(directory, 'package.json'); - const packageJSON = await fs.readJson(packageJSONPath); + const packageJSON: PackageJSON = await fs.readJson(packageJSONPath); packageJSON.main = '.webpack/main'; // Configure scripts for TS template + packageJSON.scripts = packageJSON.scripts ?? {}; packageJSON.scripts.lint = 'eslint --ext .ts,.tsx .'; await fs.writeJson(packageJSONPath, packageJSON, { spaces: 2, diff --git a/packages/template/webpack-typescript/test/WebpackTypeScript_spec_slow.ts b/packages/template/webpack-typescript/test/WebpackTypeScript_spec_slow.ts index f323b2d2c2..3097901e73 100644 --- a/packages/template/webpack-typescript/test/WebpackTypeScript_spec_slow.ts +++ b/packages/template/webpack-typescript/test/WebpackTypeScript_spec_slow.ts @@ -1,6 +1,7 @@ import path from 'path'; import { yarnOrNpmSpawn } from '@electron-forge/core-utils'; +import { PackageJSON } from '@electron-forge/shared-types'; import * as testUtils from '@electron-forge/test-utils'; import { expect } from 'chai'; import glob from 'fast-glob'; @@ -67,8 +68,7 @@ describe('WebpackTypeScriptTemplate', () => { // typescript type-resolution. In prod no one has to worry about things like this const pj = await fs.readJson(path.resolve(dir, 'package.json')); pj.resolutions = { - // eslint-disable-next-line @typescript-eslint/no-var-requires - webpack: `${require('../../../../node_modules/webpack/package.json').version}`, + webpack: `${(fs.readJsonSync(path.join(__dirname, '../../../../node_modules/webpack/package.json')) as PackageJSON).version}`, }; await fs.writeJson(path.resolve(dir, 'package.json'), pj); await yarnOrNpmSpawn(['install'], { diff --git a/packages/utils/core-utils/src/electron-version.ts b/packages/utils/core-utils/src/electron-version.ts index 2709800a98..e6d6e66b89 100644 --- a/packages/utils/core-utils/src/electron-version.ts +++ b/packages/utils/core-utils/src/electron-version.ts @@ -1,5 +1,6 @@ import path from 'path'; +import { PackageJSON } from '@electron-forge/shared-types'; import debug from 'debug'; import findUp from 'find-up'; import fs from 'fs-extra'; @@ -11,9 +12,8 @@ const d = debug('electron-forge:electron-version'); const electronPackageNames = ['electron-prebuilt-compile', 'electron-prebuilt', 'electron-nightly', 'electron']; -type PackageJSONWithDeps = { - devDependencies?: Record; - dependencies?: Record; +type ElectronPackageJSON = PackageJSON & { + version: string; }; function findElectronDep(dep: string): boolean { @@ -48,14 +48,12 @@ export class PackageNotFoundError extends Error { } } -function getElectronModuleName(packageJSON: PackageJSONWithDeps): string { +function getElectronModuleName(packageJSON: PackageJSON): string { if (!packageJSON.devDependencies) { throw new Error('package.json for app does not have any devDependencies'); } - // Why: checked above - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const packageName = electronPackageNames.find((pkg) => packageJSON.devDependencies![pkg]); + const packageName = electronPackageNames.find((pkg) => packageJSON.devDependencies?.[pkg]); if (packageName === undefined) { throw new Error('Could not find any Electron packages in devDependencies'); } @@ -77,7 +75,7 @@ async function getElectronPackageJSONPath(dir: string, packageName: string): Pro return undefined; } -export async function getElectronModulePath(dir: string, packageJSON: PackageJSONWithDeps): Promise { +export async function getElectronModulePath(dir: string, packageJSON: PackageJSON): Promise { const moduleName = getElectronModuleName(packageJSON); const packageJSONPath = await getElectronPackageJSONPath(dir, moduleName); if (packageJSONPath) { @@ -87,17 +85,16 @@ export async function getElectronModulePath(dir: string, packageJSON: PackageJSO return undefined; } -export async function getElectronVersion(dir: string, packageJSON: PackageJSONWithDeps): Promise { +export async function getElectronVersion(dir: string, packageJSON: PackageJSON): Promise { const packageName = getElectronModuleName(packageJSON); // Why: checked in getElectronModuleName - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - let version = packageJSON.devDependencies![packageName]; + let version = (packageJSON.devDependencies as Record)[packageName]; if (!semver.valid(version)) { // It's not an exact version, find it in the actual module const electronPackageJSONPath = await getElectronPackageJSONPath(dir, packageName); if (electronPackageJSONPath) { - const electronPackageJSON = await fs.readJson(electronPackageJSONPath); + const electronPackageJSON: ElectronPackageJSON = await fs.readJson(electronPackageJSONPath); version = electronPackageJSON.version; } else { throw new PackageNotFoundError(packageName, dir); @@ -107,12 +104,10 @@ export async function getElectronVersion(dir: string, packageJSON: PackageJSONWi return version; } -export function updateElectronDependency(packageJSON: PackageJSONWithDeps, dev: string[], exact: string[]): [string[], string[]] { +export function updateElectronDependency(packageJSON: PackageJSON, dev: string[], exact: string[]): [string[], string[]] { const alteredDev = ([] as string[]).concat(dev); let alteredExact = ([] as string[]).concat(exact); - // Why: checked in getElectronModuleName - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - if (Object.keys(packageJSON.devDependencies!).find(findElectronDep)) { + if (Object.keys(packageJSON.devDependencies ?? {}).find(findElectronDep)) { alteredExact = alteredExact.filter((dep) => dep !== 'electron'); } else if (packageJSON.dependencies) { const electronKey = Object.keys(packageJSON.dependencies).find(findElectronDep); diff --git a/packages/utils/core-utils/test/electron-version_spec.ts b/packages/utils/core-utils/test/electron-version_spec.ts index 26838e426d..3660fb1e8f 100644 --- a/packages/utils/core-utils/test/electron-version_spec.ts +++ b/packages/utils/core-utils/test/electron-version_spec.ts @@ -1,6 +1,7 @@ import os from 'os'; import path from 'path'; +import { PackageJSON } from '@electron-forge/shared-types'; import { expect } from 'chai'; import fs from 'fs-extra'; @@ -11,14 +12,15 @@ const fixturePath = path.resolve(__dirname, '..', '..', '..', 'api', 'core', 'te describe('updateElectronDependency', () => { it('adds an Electron dep if one does not already exist', () => { - const packageJSON = { dependencies: {}, devDependencies: {} }; + const packageJSON: PackageJSON = { name: 'foo', dependencies: {}, devDependencies: {} }; const [dev, exact] = updateElectronDependency(packageJSON, devDeps, exactDevDeps); expect(dev).to.deep.equal(devDeps); expect(exact).to.deep.equal(exactDevDeps); }); it('does not add an Electron dep if one already exists', () => { - const packageJSON = { + const packageJSON: PackageJSON = { + name: 'foo', dependencies: {}, devDependencies: { electron: '0.37.0' }, }; @@ -28,7 +30,8 @@ describe('updateElectronDependency', () => { }); it('moves an Electron dependency from dependencies to devDependencies', () => { - const packageJSON = { + const packageJSON: PackageJSON = { + name: 'foo', dependencies: { electron: '0.37.0' }, devDependencies: {}, }; @@ -39,44 +42,50 @@ describe('updateElectronDependency', () => { }); describe('getElectronVersion', () => { - it('fails without devDependencies', () => expect(getElectronVersion('', {})).to.eventually.be.rejectedWith('does not have any devDependencies')); + it('fails without devDependencies', () => expect(getElectronVersion('', { name: 'foo' })).to.eventually.be.rejectedWith('does not have any devDependencies')); it('fails without electron devDependencies', () => - expect(getElectronVersion('', { devDependencies: {} })).to.eventually.be.rejectedWith('Electron packages in devDependencies')); + expect(getElectronVersion('', { name: 'foo', devDependencies: {} })).to.eventually.be.rejectedWith('Electron packages in devDependencies')); it('fails with a non-exact version and no electron installed', () => { const fixtureDir = path.resolve(fixturePath, 'dummy_app'); - return expect(getElectronVersion(fixtureDir, { devDependencies: { electron: '^4.0.2' } })).to.eventually.be.rejectedWith('Cannot find the package'); + return expect(getElectronVersion(fixtureDir, { name: 'foo', devDependencies: { electron: '^4.0.2' } })).to.eventually.be.rejectedWith( + 'Cannot find the package' + ); }); it('works with a non-exact version with electron installed', () => { const fixtureDir = path.resolve(fixturePath, 'non-exact'); - return expect(getElectronVersion(fixtureDir, { devDependencies: { electron: '^4.0.2' } })).to.eventually.equal('4.0.9'); + return expect(getElectronVersion(fixtureDir, { name: 'foo', devDependencies: { electron: '^4.0.2' } })).to.eventually.equal('4.0.9'); }); it('works with electron-prebuilt-compile', () => { - const packageJSON = { + const packageJSON: PackageJSON = { + name: 'foo', devDependencies: { 'electron-prebuilt-compile': '1.0.0' }, }; return expect(getElectronVersion('', packageJSON)).to.eventually.equal('1.0.0'); }); it('works with electron-prebuilt', async () => { - const packageJSON = { + const packageJSON: PackageJSON = { + name: 'foo', devDependencies: { 'electron-prebuilt': '1.0.0' }, }; return expect(await getElectronVersion('', packageJSON)).to.be.equal('1.0.0'); }); it('works with electron-nightly', async () => { - const packageJSON = { + const packageJSON: PackageJSON = { + name: 'foo', devDependencies: { 'electron-nightly': '5.0.0-nightly.20190107' }, }; return expect(await getElectronVersion('', packageJSON)).to.be.equal('5.0.0-nightly.20190107'); }); it('works with electron', async () => { - const packageJSON = { + const packageJSON: PackageJSON = { + name: 'foo', devDependencies: { electron: '1.0.0' }, }; return expect(await getElectronVersion('', packageJSON)).to.be.equal('1.0.0'); @@ -89,7 +98,8 @@ describe('getElectronVersion', () => { it('works with a non-exact version', async () => { const fixtureDir = path.resolve(fixturePath, 'yarn-workspace', 'packages', 'subpackage'); - const packageJSON = { + const packageJSON: PackageJSON = { + name: 'foo', devDependencies: { electron: '^4.0.4' }, }; @@ -103,10 +113,11 @@ describe('getElectronVersion', () => { }); describe('getElectronModulePath', () => { - it('fails without devDependencies', () => expect(getElectronModulePath('', {})).to.eventually.be.rejectedWith('does not have any devDependencies')); + it('fails without devDependencies', () => + expect(getElectronModulePath('', { name: 'foo' })).to.eventually.be.rejectedWith('does not have any devDependencies')); it('fails without electron devDependencies', () => - expect(getElectronModulePath('', { devDependencies: {} })).to.eventually.be.rejectedWith('Electron packages in devDependencies')); + expect(getElectronModulePath('', { name: 'foo', devDependencies: {} })).to.eventually.be.rejectedWith('Electron packages in devDependencies')); describe('with no electron installed', () => { let tempDir: string; @@ -117,7 +128,9 @@ describe('getElectronModulePath', () => { it('throws an error saying it cannot find electron', async () => { const fixtureDir = path.resolve(fixturePath, 'dummy_app'); await fs.copy(fixtureDir, tempDir); - return expect(getElectronModulePath(tempDir, { devDependencies: { electron: '^4.0.2' } })).to.eventually.be.rejectedWith('Cannot find the package'); + return expect(getElectronModulePath(tempDir, { name: 'foo', devDependencies: { electron: '^4.0.2' } })).to.eventually.be.rejectedWith( + 'Cannot find the package' + ); }); after(async () => { @@ -127,7 +140,7 @@ describe('getElectronModulePath', () => { it('works with electron', () => { const fixtureDir = path.resolve(fixturePath, 'non-exact'); - return expect(getElectronModulePath(fixtureDir, { devDependencies: { electron: '^4.0.2' } })).to.eventually.equal( + return expect(getElectronModulePath(fixtureDir, { name: 'foo', devDependencies: { electron: '^4.0.2' } })).to.eventually.equal( path.join(fixtureDir, 'node_modules', 'electron') ); }); @@ -140,7 +153,8 @@ describe('getElectronModulePath', () => { it('finds the top-level electron module', async () => { const workspaceDir = path.resolve(fixturePath, 'npm-workspace'); const fixtureDir = path.join(workspaceDir, 'packages', 'subpackage'); - const packageJSON = { + const packageJSON: PackageJSON = { + name: 'foo', devDependencies: { electron: '^4.0.4' }, }; @@ -160,7 +174,8 @@ describe('getElectronModulePath', () => { it('finds the top-level electron module', async () => { const workspaceDir = path.resolve(fixturePath, 'yarn-workspace'); const fixtureDir = path.join(workspaceDir, 'packages', 'subpackage'); - const packageJSON = { + const packageJSON: PackageJSON = { + name: 'foo', devDependencies: { electron: '^4.0.4' }, }; @@ -170,7 +185,8 @@ describe('getElectronModulePath', () => { it('finds the top-level electron module despite the additional node_modules folder inside the package', async () => { const workspaceDir = path.resolve(fixturePath, 'yarn-workspace'); const fixtureDir = path.join(workspaceDir, 'packages', 'with-node-modules'); - const packageJSON = { + const packageJSON: PackageJSON = { + name: 'foo', devDependencies: { electron: '^4.0.4' }, }; @@ -180,7 +196,8 @@ describe('getElectronModulePath', () => { it('finds the correct electron module in nohoist mode', async () => { const workspaceDir = path.resolve(fixturePath, 'yarn-workspace'); const fixtureDir = path.join(workspaceDir, 'packages', 'electron-folder-in-node-modules'); - const packageJSON = { + const packageJSON: PackageJSON = { + name: 'foo', devDependencies: { electron: '^13.0.0' }, }; diff --git a/packages/utils/types/src/index.ts b/packages/utils/types/src/index.ts index c30e21588e..6b9796df3e 100644 --- a/packages/utils/types/src/index.ts +++ b/packages/utils/types/src/index.ts @@ -33,8 +33,7 @@ export interface ForgeSimpleHookSignatures { export interface ForgeMutatingHookSignatures { postMake: [makeResults: ForgeMakeResult[]]; resolveForgeConfig: [currentConfig: ResolvedForgeConfig]; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - readPackageJson: [packageJson: Record]; + readPackageJson: [packageJson: PackageJSON]; } export type ForgeHookName = keyof (ForgeSimpleHookSignatures & ForgeMutatingHookSignatures); @@ -105,7 +104,7 @@ export interface ForgeMakeResult { /** * The state of the package.json file when the make happened */ - packageJSON: any; // eslint-disable-line @typescript-eslint/no-explicit-any + packageJSON: ForgeAppPackageJSON; /** * The platform this make run was for */ @@ -217,3 +216,30 @@ export type PackagePerson = email?: string; url?: string; }; + +export type PackageJSON = Record & { + author?: PackagePerson; + name: string; + description?: string; + version?: string; + main?: string; + engines?: Record; + devDependencies?: Record; + dependencies?: Record; + scripts?: Record; + productName?: string; +}; + +export type ForgeAppPackageJSON = PackageJSON & { + version: string; + config?: { + forge?: ForgeConfig; + }; +}; + +export type ElectronForgePackageJSON = PackageJSON & { + version: string; + engines: { + node: string; + }; +};