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

feat: add support for forge.config.ts et. al #2993

Merged
merged 1 commit into from
Oct 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
}
},
{
"files": ["packages/*/*/test/**/*_spec*.ts"],
"files": ["packages/*/*/test/**/*_spec*.ts", "packages/*/*/test/fixture/**/*.ts"],
"rules": {
"global-require": "off",
"import/no-dynamic-require": "off",
Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
"got": "^11.8.5",
"html-webpack-plugin": "^5.3.1",
"inquirer": "^8.0.0",
"interpret": "^3.1.1",
"lodash": "^4.17.20",
"log-symbols": "^4.0.0",
"mime-types": "^2.1.25",
Expand All @@ -73,6 +74,7 @@
"parse-author": "^2.0.0",
"pretty-ms": "^7.0.0",
"progress": "^2.0.3",
"rechoir": "^0.8.0",
"resolve-package": "^1.0.1",
"semver": "^7.2.1",
"source-map-support": "^0.5.13",
Expand Down Expand Up @@ -102,6 +104,7 @@
"@types/fetch-mock": "^7.3.1",
"@types/fs-extra": "^9.0.6",
"@types/inquirer": "^8.1.1",
"@types/interpret": "^1.1.1",
"@types/listr": "^0.14.2",
"@types/lodash": "^4.14.166",
"@types/mime-types": "^2.1.0",
Expand All @@ -111,6 +114,7 @@
"@types/node-fetch": "^2.5.5",
"@types/progress": "^2.0.5",
"@types/proxyquire": "^1.3.28",
"@types/rechoir": "^0.6.1",
"@types/semver": "^7.3.4",
"@types/sinon": "^10.0.0",
"@types/sinon-chai": "^3.2.5",
Expand Down
4 changes: 4 additions & 0 deletions packages/api/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
"@electron-forge/maker-wix": "6.0.0-beta.68",
"@electron-forge/maker-zip": "6.0.0-beta.68",
"@electron-forge/test-utils": "6.0.0-beta.68",
"@types/interpret": "^1.1.1",
"@types/progress": "^2.0.5",
"@types/rechoir": "^0.6.1",
"chai": "^4.3.3",
"chai-as-promised": "^7.0.0",
"cross-env": "^7.0.2",
Expand Down Expand Up @@ -56,10 +58,12 @@
"find-up": "^5.0.0",
"fs-extra": "^10.0.0",
"got": "^11.8.5",
"interpret": "^3.1.1",
"lodash": "^4.17.20",
"log-symbols": "^4.0.0",
"node-fetch": "^2.6.7",
"progress": "^2.0.3",
"rechoir": "^0.8.0",
"resolve-package": "^1.0.1",
"semver": "^7.2.1",
"source-map-support": "^0.5.13",
Expand Down
6 changes: 3 additions & 3 deletions packages/api/core/src/api/make.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import path from 'path';

import { asyncOra } from '@electron-forge/async-ora';
import { MakerBase } from '@electron-forge/maker-base';
import { ForgeArch, ForgeConfig, ForgeConfigMaker, ForgeMakeResult, ForgePlatform, IForgeResolvableMaker } from '@electron-forge/shared-types';
import { ForgeArch, ForgeConfigMaker, ForgeMakeResult, ForgePlatform, IForgeResolvableMaker, ResolvedForgeConfig } from '@electron-forge/shared-types';
import { getHostArch } from '@electron/get';
import chalk from 'chalk';
import filenamify from 'filenamify';
Expand All @@ -29,7 +29,7 @@ class MakerImpl extends MakerBase<any> {

type MakeTargets = ForgeConfigMaker[] | string[];

function generateTargets(forgeConfig: ForgeConfig, overrideTargets?: MakeTargets) {
function generateTargets(forgeConfig: ResolvedForgeConfig, overrideTargets?: MakeTargets) {
if (overrideTargets) {
return overrideTargets.map((target) => {
if (typeof target === 'string') {
Expand Down Expand Up @@ -90,7 +90,7 @@ export default async ({
}: MakeOptions): Promise<ForgeMakeResult[]> => {
asyncOra.interactive = interactive;

let forgeConfig!: ForgeConfig;
let forgeConfig!: ResolvedForgeConfig;
await asyncOra('Resolving Forge Config', async () => {
const resolvedDir = await resolveDir(dir);
if (!resolvedDir) {
Expand Down
35 changes: 23 additions & 12 deletions packages/api/core/src/util/forge-config.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import path from 'path';

import { ForgeConfig, IForgeResolvableMaker } from '@electron-forge/shared-types';
import { ForgeConfig, IForgeResolvableMaker, ResolvedForgeConfig } from '@electron-forge/shared-types';
import fs from 'fs-extra';
import * as interpret from 'interpret';
import { template } from 'lodash';
import * as rechoir from 'rechoir';

import { runMutatingHook } from './hook';
import PluginInterface from './plugin-interface';
Expand Down Expand Up @@ -119,22 +121,29 @@ export function renderConfigTemplate(dir: string, templateObj: any, obj: any): v
}
}

export default async (dir: string): Promise<ForgeConfig> => {
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;

if (!forgeConfig) {
if (await fs.pathExists(path.resolve(dir, 'forge.config.js'))) {
forgeConfig = 'forge.config.js';
} else {
forgeConfig = {} as ForgeConfig;
for (const extension of ['.js', ...Object.keys(interpret.extensions)]) {
const pathToConfig = path.resolve(dir, `forge.config${extension}`);
if (await fs.pathExists(pathToConfig)) {
rechoir.prepare(interpret.extensions, pathToConfig, dir);
forgeConfig = `forge.config${extension}`;
break;
}
}
}
forgeConfig = forgeConfig || ({} as ForgeConfig);

if (await forgeConfigIsValidFilePath(dir, forgeConfig)) {
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
forgeConfig = require(path.resolve(dir, forgeConfig as string)) as ForgeConfig;
const loaded = require(path.resolve(dir, forgeConfig as string)) as MaybeESM<ForgeConfig>;
forgeConfig = 'default' in loaded ? loaded.default : loaded;
} catch (err) {
console.error(`Failed to load: ${path.resolve(dir, forgeConfig as string)}`);
throw err;
Expand All @@ -149,17 +158,19 @@ export default async (dir: string): Promise<ForgeConfig> => {
publishers: [],
plugins: [],
};
forgeConfig = {
let resolvedForgeConfig: ResolvedForgeConfig = {
...defaultForgeConfig,
...forgeConfig,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
pluginInterface: null as any,
};

const templateObj = { ...packageJSON, year: new Date().getFullYear() };
renderConfigTemplate(dir, templateObj, forgeConfig);
renderConfigTemplate(dir, templateObj, resolvedForgeConfig);

forgeConfig.pluginInterface = new PluginInterface(dir, forgeConfig);
resolvedForgeConfig.pluginInterface = new PluginInterface(dir, resolvedForgeConfig);

forgeConfig = await runMutatingHook(forgeConfig, 'resolveForgeConfig', forgeConfig);
resolvedForgeConfig = await runMutatingHook(resolvedForgeConfig, 'resolveForgeConfig', resolvedForgeConfig);

return proxify<ForgeConfig>(forgeConfig.buildIdentifier || '', forgeConfig, 'ELECTRON_FORGE');
return proxify<ResolvedForgeConfig>(resolvedForgeConfig.buildIdentifier || '', resolvedForgeConfig, 'ELECTRON_FORGE');
};
6 changes: 3 additions & 3 deletions packages/api/core/src/util/hook.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { ForgeConfig } from '@electron-forge/shared-types';
import { ResolvedForgeConfig } from '@electron-forge/shared-types';
import debug from 'debug';

const d = debug('electron-forge:hook');

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const runHook = async (forgeConfig: ForgeConfig, hookName: string, ...hookArgs: any[]): Promise<void> => {
export const runHook = async (forgeConfig: ResolvedForgeConfig, hookName: string, ...hookArgs: any[]): Promise<void> => {
const { hooks } = forgeConfig;
if (hooks) {
d(`hook triggered: ${hookName}`);
Expand All @@ -16,7 +16,7 @@ export const runHook = async (forgeConfig: ForgeConfig, hookName: string, ...hoo
await forgeConfig.pluginInterface.triggerHook(hookName, hookArgs);
};

export async function runMutatingHook<T>(forgeConfig: ForgeConfig, hookName: string, item: T): Promise<T> {
export async function runMutatingHook<T>(forgeConfig: ResolvedForgeConfig, hookName: string, item: T): Promise<T> {
const { hooks } = forgeConfig;
if (hooks) {
d(`hook triggered: ${hookName}`);
Expand Down
4 changes: 2 additions & 2 deletions packages/api/core/src/util/out-dir.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import path from 'path';

import { ForgeConfig } from '@electron-forge/shared-types';
import { ResolvedForgeConfig } from '@electron-forge/shared-types';

const BASE_OUT_DIR = 'out';

export default (baseDir: string, forgeConfig: ForgeConfig): string => {
export default (baseDir: string, forgeConfig: ResolvedForgeConfig): string => {
if (forgeConfig.buildIdentifier) {
let identifier = forgeConfig.buildIdentifier;
if (typeof identifier === 'function') {
Expand Down
6 changes: 3 additions & 3 deletions packages/api/core/src/util/plugin-interface.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import PluginBase from '@electron-forge/plugin-base';
import { ForgeConfig, IForgePlugin, IForgePluginInterface, StartResult } from '@electron-forge/shared-types';
import { IForgePlugin, IForgePluginInterface, ResolvedForgeConfig, StartResult } from '@electron-forge/shared-types';
import debug from 'debug';

import { StartOptions } from '../api';
Expand All @@ -15,9 +15,9 @@ function isForgePlugin(plugin: IForgePlugin | unknown): plugin is IForgePlugin {
export default class PluginInterface implements IForgePluginInterface {
private plugins: IForgePlugin[];

private config: ForgeConfig;
private config: ResolvedForgeConfig;

constructor(dir: string, forgeConfig: ForgeConfig) {
constructor(dir: string, forgeConfig: ResolvedForgeConfig) {
this.plugins = forgeConfig.plugins.map((plugin) => {
if (isForgePlugin(plugin)) {
return plugin;
Expand Down
4 changes: 2 additions & 2 deletions packages/api/core/src/util/read-package-json.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from 'path';

import { ForgeConfig } from '@electron-forge/shared-types';
import { ResolvedForgeConfig } from '@electron-forge/shared-types';
import fs from 'fs-extra';

import { runMutatingHook } from './hook';
Expand All @@ -9,5 +9,5 @@ import { runMutatingHook } from './hook';
export const readRawPackageJson = async (dir: string): Promise<any> => fs.readJson(path.resolve(dir, 'package.json'));

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const readMutatedPackageJson = async (dir: string, forgeConfig: ForgeConfig): Promise<any> =>
export const readMutatedPackageJson = async (dir: string, forgeConfig: ResolvedForgeConfig): Promise<any> =>
runMutatingHook(forgeConfig, 'readPackageJson', await readRawPackageJson(dir));
24 changes: 15 additions & 9 deletions packages/api/core/test/fast/forge-config_spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from 'path';

import { ForgeConfig } from '@electron-forge/shared-types';
import { ResolvedForgeConfig } from '@electron-forge/shared-types';
import { expect } from 'chai';

import findConfig, {
Expand Down Expand Up @@ -106,21 +106,27 @@ describe('forge-config', () => {
});

it('should resolve the JS file exports in config.forge points to a JS file and maintain functions', async () => {
type MagicFunctionConfig = ForgeConfig & { magicFn: () => string };
type MagicFunctionConfig = ResolvedForgeConfig & { magicFn: () => string };
const conf = (await findConfig(path.resolve(__dirname, '../fixture/dummy_js_conf'))) as MagicFunctionConfig;
expect(conf.magicFn).to.be.a('function');
expect(conf.magicFn()).to.be.equal('magic result');
});

it('should resolve the JS file exports of forge.config.js if config.forge does not exist points', async () => {
type DefaultResolvedConfig = ForgeConfig & { defaultResolved: boolean };
it('should resolve the JS file exports of forge.config.js if config.forge does not exist ', async () => {
type DefaultResolvedConfig = ResolvedForgeConfig & { defaultResolved: boolean };
const conf = (await findConfig(path.resolve(__dirname, '../fixture/dummy_default_js_conf'))) as DefaultResolvedConfig;
expect(conf.buildIdentifier).to.equal('default');
expect(conf.defaultResolved).to.equal(true);
});

it('should resolve the TS file exports of forge.config.ts if config.forge does not exist and the TS config exists', async () => {
type DefaultResolvedConfig = ResolvedForgeConfig;
const conf = (await findConfig(path.resolve(__dirname, '../fixture/dummy_default_ts_conf'))) as DefaultResolvedConfig;
expect(conf.buildIdentifier).to.equal('typescript');
});

it('should magically map properties to environment variables', async () => {
type MappedConfig = ForgeConfig & {
type MappedConfig = ResolvedForgeConfig & {
s3: {
secretAccessKey?: string;
};
Expand All @@ -140,7 +146,7 @@ describe('forge-config', () => {
});

it('should resolve values fromBuildIdentifier', async () => {
type ResolveBIConfig = ForgeConfig & {
type ResolveBIConfig = ResolvedForgeConfig & {
topLevelProp: string;
sub: {
prop: {
Expand All @@ -164,13 +170,13 @@ describe('forge-config', () => {
});

it('should resolve undefined from fromBuildIdentifier if no value is provided', async () => {
type ResolveUndefConfig = ForgeConfig & { topLevelUndef?: string };
type ResolveUndefConfig = ResolvedForgeConfig & { topLevelUndef?: string };
const conf = (await findConfig(path.resolve(__dirname, '../fixture/dummy_js_conf'))) as ResolveUndefConfig;
expect(conf.topLevelUndef).to.equal(undefined);
});

it('should leave arrays intact', async () => {
type NestedConfig = ForgeConfig & {
type NestedConfig = ResolvedForgeConfig & {
sub: {
prop: {
inArray: string[];
Expand All @@ -182,7 +188,7 @@ describe('forge-config', () => {
});

it('should leave regexps intact', async () => {
type RegExpConfig = ForgeConfig & { regexp: RegExp };
type RegExpConfig = ResolvedForgeConfig & { regexp: RegExp };
const conf = (await findConfig(path.resolve(__dirname, '../fixture/dummy_js_conf'))) as RegExpConfig;
expect(conf.regexp).to.be.instanceOf(RegExp);
expect(conf.regexp.test('foo')).to.equal(true, 'regexp should match foo');
Expand Down
4 changes: 2 additions & 2 deletions packages/api/core/test/fast/hook_spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ForgeConfig, ForgeHookFn } from '@electron-forge/shared-types';
import { ForgeHookFn, ResolvedForgeConfig } from '@electron-forge/shared-types';
import { expect } from 'chai';
import { SinonStub, stub } from 'sinon';

Expand All @@ -9,7 +9,7 @@ const fakeConfig = {
triggerHook: async () => false,
triggerMutatingHook: async (_hookName: string, item: unknown) => item,
},
} as unknown as ForgeConfig;
} as unknown as ResolvedForgeConfig;

describe('hooks', () => {
describe('runHook', () => {
Expand Down
8 changes: 4 additions & 4 deletions packages/api/core/test/fast/out-dir_spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from 'path';

import { ForgeConfig } from '@electron-forge/shared-types';
import { ResolvedForgeConfig } from '@electron-forge/shared-types';
import { expect } from 'chai';

import getCurrentOutDir from '../../src/util/out-dir';
Expand All @@ -10,22 +10,22 @@ describe('out-dir', () => {

describe('getCurrentOutDir', () => {
it('resolves to the default out directory when nothing extra is declared', () => {
expect(getCurrentOutDir(DIR, {} as ForgeConfig)).to.equal(`${DIR}${path.sep}out`);
expect(getCurrentOutDir(DIR, {} as ResolvedForgeConfig)).to.equal(`${DIR}${path.sep}out`);
});

it('resolves to the provided identifier', () => {
expect(
getCurrentOutDir(DIR, {
buildIdentifier: 'bar',
} as ForgeConfig)
} as ResolvedForgeConfig)
).to.equal(`${DIR}${path.sep}out${path.sep}bar`);
});

it('resolves to the return value of provided identifier getter', () => {
expect(
getCurrentOutDir(DIR, {
buildIdentifier: () => 'thing',
} as ForgeConfig)
} as ResolvedForgeConfig)
).to.equal(`${DIR}${path.sep}out${path.sep}thing`);
});
});
Expand Down
8 changes: 4 additions & 4 deletions packages/api/core/test/fast/read-package-json_spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import path from 'path';

import { ForgeConfig } from '@electron-forge/shared-types';
import { ResolvedForgeConfig } from '@electron-forge/shared-types';
import { expect } from 'chai';

import { readMutatedPackageJson, readRawPackageJson } from '../../src/util/read-package-json';

const emptyForgeConfig: Partial<ForgeConfig> = {
const emptyForgeConfig: Partial<ResolvedForgeConfig> = {
packagerConfig: {},
rebuildConfig: {},
makers: [],
Expand All @@ -31,7 +31,7 @@ describe('read-package-json', () => {
triggerHook: () => Promise.resolve(),
overrideStartLogic: () => Promise.resolve(false),
},
} as ForgeConfig)
} as ResolvedForgeConfig)
).to.deep.equal(require('../../package.json'));
});

Expand All @@ -44,7 +44,7 @@ describe('read-package-json', () => {
triggerHook: () => Promise.resolve(),
overrideStartLogic: () => Promise.resolve(false),
},
} as ForgeConfig)
} as ResolvedForgeConfig)
).to.deep.equal('test_mut');
});
});
Expand Down
Loading