Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: improve typing of package.json usages #3139

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 5 additions & 2 deletions packages/api/cli/src/electron-forge.ts
Original file line number Diff line number Diff line change
@@ -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[]) => {
Expand Down
9 changes: 5 additions & 4 deletions packages/api/cli/src/util/check-system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -17,11 +17,12 @@ async function getGitVersion(): Promise<string | null> {
}

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;
Expand Down
16 changes: 9 additions & 7 deletions packages/api/core/src/api/import.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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') {
Expand All @@ -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<string, unknown>);
importDevDeps = updateUpgradedForgeDevDeps(packageJSON, importDevDeps);
}
}
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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')}!`;
},
},
Expand Down
6 changes: 3 additions & 3 deletions packages/api/core/src/api/init-scripts/init-npm.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
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';

import installDepList, { DepType, DepVersionRestriction } from '../../util/install-dependencies';
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}`;
Expand Down Expand Up @@ -40,7 +40,7 @@ export const initNPM = async (dir: string, task: ForgeListrTask<any>): Promise<v
if (process.env.LINK_FORGE_DEPENDENCIES_ON_INIT) {
const packageJson = await readRawPackageJson(dir);
const linkFolder = path.resolve(__dirname, '..', '..', '..', '..', '..', '..', '.links');
for (const packageName of Object.keys(packageJson.devDependencies)) {
for (const packageName of Object.keys(packageJson.devDependencies ?? {})) {
if (packageName.startsWith('@electron-forge/')) {
task.output = `${packageManager} link --link-folder ${linkFolder} ${packageName}`;
await yarnOrNpmSpawn(['link', '--link-folder', linkFolder, packageName], {
Expand Down
2 changes: 1 addition & 1 deletion packages/api/core/src/api/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ async function validateTemplate(template: string, templateModule: ForgeTemplate)
}

const forgeVersion = (await readRawPackageJson(path.join(__dirname, '..', '..'))).version;
if (!semver.satisfies(forgeVersion, templateModule.requiredForgeVersion)) {
if (!forgeVersion || !semver.satisfies(forgeVersion, templateModule.requiredForgeVersion)) {
throw new Error(
`Template (${template}) is not compatible with this version of Electron Forge (${forgeVersion}), it requires ${templateModule.requiredForgeVersion}`
);
Expand Down
4 changes: 2 additions & 2 deletions packages/api/core/src/api/package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import path from 'path';
import { promisify } from 'util';

import { getElectronVersion, listrCompatibleRebuildHook } from '@electron-forge/core-utils';
import { ForgeArch, ForgeListrTask, ForgeListrTaskDefinition, ForgePlatform, ResolvedForgeConfig } from '@electron-forge/shared-types';
import { ForgeArch, ForgeListrTask, ForgeListrTaskDefinition, ForgePlatform, PackageJSON, ResolvedForgeConfig } from '@electron-forge/shared-types';
import { getHostArch } from '@electron/get';
import chalk from 'chalk';
import debug from 'debug';
Expand Down Expand Up @@ -73,7 +73,7 @@ function sequentialFinalizePackageTargetsHooks(hooks: FinalizePackageTargetsHook
type PackageContext = {
dir: string;
forgeConfig: ResolvedForgeConfig;
packageJSON: any;
packageJSON: PackageJSON;
calculatedOutDir: string;
packagerPromise: Promise<string[]>;
targets: InternalTargetDefinition[];
Expand Down
4 changes: 2 additions & 2 deletions packages/api/core/src/api/start.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -19,7 +19,7 @@ export { StartOptions };
type StartContext = {
dir: string;
forgeConfig: ResolvedForgeConfig;
packageJSON: any;
packageJSON: PackageJSON;
spawned: ElectronProcess;
};

Expand Down
8 changes: 3 additions & 5 deletions packages/api/core/src/util/electron-executable.ts
Original file line number Diff line number Diff line change
@@ -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<string, unknown>;
type Dependencies = Record<string, string>;

export function pluginCompileExists(packageJSON: PackageJSON): boolean {
if (!packageJSON.devDependencies) {
return false;
Expand All @@ -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;
}
Expand Down
6 changes: 3 additions & 3 deletions packages/api/core/src/util/forge-config.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -106,8 +106,8 @@ export function renderConfigTemplate(dir: string, templateObj: any, obj: any): v
type MaybeESM<T> = T | { default: T };

export default async (dir: string): Promise<ResolvedForgeConfig> => {
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)]) {
Expand Down
10 changes: 4 additions & 6 deletions packages/api/core/src/util/read-package-json.ts
Original file line number Diff line number Diff line change
@@ -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<any> => fs.readJson(path.resolve(dir, 'package.json'));
export const readRawPackageJson = async (dir: string): Promise<PackageJSON> => 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<any> =>
runMutatingHook(forgeConfig, 'readPackageJson', await readRawPackageJson(dir));
export const readMutatedPackageJson = async (dir: string, forgeConfig: ResolvedForgeConfig): Promise<ForgeAppPackageJSON> =>
runMutatingHook(forgeConfig, 'readPackageJson', await readRawPackageJson(dir)) as Promise<ForgeAppPackageJSON>;
5 changes: 3 additions & 2 deletions packages/api/core/src/util/resolve-dir.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -22,7 +23,7 @@ export default async (dir: string): Promise<string | null> => {
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
Expand All @@ -34,7 +35,7 @@ export default async (dir: string): Promise<string | null> => {
}
}

if (packageJSON.config && packageJSON.config.forge) {
if (packageJSON.config?.forge) {
d('electron-forge compatible package.json found in', testPath);
return mDir;
}
Expand Down
19 changes: 6 additions & 13 deletions packages/api/core/src/util/upgrade-forge-config.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -33,13 +33,6 @@ type Forge5Config = {

type Forge5ConfigKey = keyof Forge5Config;

type ForgePackageJSON = Record<string, unknown> & {
config: {
forge: ForgeConfig;
};
devDependencies: Record<string, string>;
};

function mapMakeTargets(forge5Config: Forge5Config): Map<string, ForgePlatform[]> {
const makeTargets = new Map<string, ForgePlatform[]>();
if (forge5Config.make_targets) {
Expand Down Expand Up @@ -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'));
}

Expand Down