Skip to content

Commit

Permalink
feat!: prefer forge.config.js over package.json config (#2991)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: templates that mutated the Forge config within `package.json` will need to instantiate their own `forge.config.js`
  • Loading branch information
erickzhao committed Oct 27, 2022
1 parent 25a0a63 commit 777197e
Show file tree
Hide file tree
Showing 11 changed files with 172 additions and 117 deletions.
43 changes: 23 additions & 20 deletions packages/api/core/src/api/import.ts
Expand Up @@ -83,17 +83,20 @@ export default async ({
let importExactDevDeps = ([] as string[]).concat(exactDevDeps);

let packageJSON = await readRawPackageJson(dir);
if (!packageJSON.version) {
warn(interactive, chalk.yellow(`Please set the ${chalk.green('"version"')} in your application's package.json`));
}
if (packageJSON.config && packageJSON.config.forge) {
if (packageJSON.config.forge.makers) {
warn(interactive, chalk.green('It looks like this project is already configured for Electron Forge'));
warn(interactive, chalk.green('Existing Electron Forge configuration detected'));
if (typeof shouldContinueOnExisting === 'function') {
if (!(await shouldContinueOnExisting())) {
// TODO: figure out if we can just return early here
// eslint-disable-next-line no-process-exit
process.exit(0);
}
}
} else if (typeof packageJSON.config.forge === 'string') {
} else if (!(typeof packageJSON.config.forge === 'object')) {
warn(
interactive,
chalk.yellow(
Expand Down Expand Up @@ -187,23 +190,23 @@ export default async ({
await installDepList(dir, importExactDevDeps, DepType.DEV, DepVersionRestriction.EXACT);
});

packageJSON = await readRawPackageJson(dir);

if (!packageJSON.version) {
warn(interactive, chalk.yellow('Please set the "version" in your application\'s package.json'));
}

packageJSON.config = packageJSON.config || {};
const templatePackageJSON = await readRawPackageJson(baseTemplate.templateDir);
if (packageJSON.config.forge) {
if (typeof packageJSON.config.forge !== 'string') {
packageJSON.config.forge = merge(templatePackageJSON.config.forge, packageJSON.config.forge);
await asyncOra('Copying base template Forge configuration', async () => {
const pathToTemplateConfig = path.resolve(baseTemplate.templateDir, 'forge.config.js');

// if there's an existing config.forge object in package.json
if (packageJSON?.config?.forge && typeof packageJSON.config.forge === 'object') {
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
await writeChanges();
// otherwise, write to forge.config.js
} else {
d('writing new forge.config.js');
await fs.copyFile(pathToTemplateConfig, path.resolve(dir, 'forge.config.js'));
}
} else {
packageJSON.config.forge = templatePackageJSON.config.forge;
}

await writeChanges();
});

await asyncOra('Fixing .gitignore', async () => {
if (await fs.pathExists(path.resolve(dir, '.gitignore'))) {
Expand All @@ -218,8 +221,8 @@ export default async ({
interactive,
`
We have ATTEMPTED to convert your app to be in a format that electron-forge understands.
We have attempted to convert your app to be in a format that Electron Forge understands.
Thanks for using ${chalk.green('Electron Forge')}!!!`
Thanks for using ${chalk.green('Electron Forge')}!`
);
};
2 changes: 1 addition & 1 deletion packages/api/core/src/api/init-scripts/init-git.ts
Expand Up @@ -6,7 +6,7 @@ import debug from 'debug';
const d = debug('electron-forge:init:git');

export default async (dir: string): Promise<void> => {
await asyncOra('Initializing Git Repository', async () => {
await asyncOra('Initializing Git repository', async () => {
await new Promise<void>((resolve, reject) => {
exec(
'git rev-parse --show-toplevel',
Expand Down
11 changes: 1 addition & 10 deletions packages/api/core/src/util/forge-config.ts
@@ -1,6 +1,6 @@
import path from 'path';

import { ForgeConfig, IForgeResolvableMaker, ResolvedForgeConfig } from '@electron-forge/shared-types';
import { ForgeConfig, ResolvedForgeConfig } from '@electron-forge/shared-types';
import fs from 'fs-extra';
import * as interpret from 'interpret';
import { template } from 'lodash';
Expand All @@ -16,15 +16,6 @@ const underscoreCase = (str: string) =>
.replace(/([a-z0-9])([A-Z])/g, '$1_$2')
.toUpperCase();

export type PackageJSONForInitialForgeConfig = {
name?: string;
config: {
forge: {
makers: Pick<IForgeResolvableMaker, 'name' | 'config'>[];
};
};
};

// Why: needs access to Object methods and also needs to be able to match any interface.
// eslint-disable-next-line @typescript-eslint/ban-types
type ProxiedObject = object;
Expand Down
18 changes: 15 additions & 3 deletions packages/api/core/test/slow/api_spec_slow.ts
Expand Up @@ -79,6 +79,10 @@ for (const nodeInstaller of ['npm', 'yarn']) {
expect(await fs.pathExists(path.resolve(dir, 'node_modules/@electron-forge/cli')), '@electron-forge/cli should exist').to.equal(true);
});

it('should create a forge.config.js', async () => {
await expectProjectPathExists(dir, 'forge.config.js', 'file');
});

describe('lint', () => {
it('should initially pass the linting process', () => expectLintToPass(dir));
});
Expand Down Expand Up @@ -230,13 +234,21 @@ describe('Electron Forge API', () => {
packageJSON.name = 'testapp';
packageJSON.version = '1.0.0-beta.1';
packageJSON.productName = 'Test-App';
assert(packageJSON.config.forge.packagerConfig);
packageJSON.config.forge.packagerConfig.asar = false;
packageJSON.config = packageJSON.config || {};
packageJSON.config.forge = {
...packageJSON.config.forge,
packagerConfig: {
asar: false,
},
};
if (process.platform === 'win32') {
await fs.copy(path.join(__dirname, '..', 'fixture', 'bogus-private-key.pvk'), path.join(dir, 'default.pvk'));
devCert = await createDefaultCertificate('CN=Test Author', { certFilePath: dir });
} else if (process.platform === 'linux') {
packageJSON.config.forge.packagerConfig.executableName = 'testapp';
packageJSON.config.forge.packagerConfig = {
...packageJSON.config.forge.packagerConfig,
executableName: 'testapp',
};
}
packageJSON.homepage = 'http://www.example.com/';
packageJSON.author = 'Test Author';
Expand Down
2 changes: 1 addition & 1 deletion packages/template/base/src/BaseTemplate.ts
Expand Up @@ -39,7 +39,7 @@ export class BaseTemplate implements ForgeTemplate {
await asyncOra('Copying Starter Files', async () => {
d('creating directory:', path.resolve(directory, 'src'));
await fs.mkdirs(path.resolve(directory, 'src'));
const rootFiles = ['_gitignore'];
const rootFiles = ['_gitignore', 'forge.config.js'];
if (copyCIFiles) rootFiles.push(...['_travis.yml', '_appveyor.yml']);
const srcFiles = ['index.css', 'index.js', 'index.html', 'preload.js'];

Expand Down
22 changes: 22 additions & 0 deletions packages/template/base/tmpl/forge.config.js
@@ -0,0 +1,22 @@
module.exports = {
packagerConfig: {},
rebuildConfig: {},
makers: [
{
name: '@electron-forge/maker-squirrel',
config: {},
},
{
name: '@electron-forge/maker-zip',
platforms: ['darwin'],
},
{
name: '@electron-forge/maker-deb',
config: {},
},
{
name: '@electron-forge/maker-rpm',
config: {},
},
],
};
27 changes: 1 addition & 26 deletions packages/template/base/tmpl/package.json
Expand Up @@ -12,30 +12,5 @@
},
"keywords": [],
"author": "",
"license": "MIT",
"config": {
"forge": {
"packagerConfig": {},
"makers": [
{
"name": "@electron-forge/maker-squirrel",
"config": {}
},
{
"name": "@electron-forge/maker-zip",
"platforms": [
"darwin"
]
},
{
"name": "@electron-forge/maker-deb",
"config": {}
},
{
"name": "@electron-forge/maker-rpm",
"config": {}
}
]
}
}
"license": "MIT"
}
Expand Up @@ -11,37 +11,8 @@ class TypeScriptWebpackTemplate extends BaseTemplate {
async initializeTemplate(directory: string, options: InitTemplateOptions) {
await super.initializeTemplate(directory, options);
await asyncOra('Setting up Forge configuration', async () => {
const packageJSONPath = path.resolve(directory, 'package.json');
const packageJSON = await fs.readJson(packageJSONPath);

packageJSON.main = '.webpack/main';
packageJSON.config.forge.plugins = packageJSON.config.forge.plugins || [];
packageJSON.config.forge.plugins.push({
name: '@electron-forge/plugin-webpack',
config: {
mainConfig: './webpack.main.config.js',
renderer: {
config: './webpack.renderer.config.js',
entryPoints: [
{
html: './src/index.html',
js: './src/renderer.ts',
name: 'main_window',
preload: {
js: './src/preload.ts',
},
},
],
},
},
});

// Configure scripts for TS template
packageJSON.scripts.lint = 'eslint --ext .ts,.tsx .';

await fs.writeJson(packageJSONPath, packageJSON, { spaces: 2 });
await this.copyTemplateFile(directory, 'forge.config.ts');
});

await asyncOra('Setting up TypeScript configuration', async () => {
const filePath = (fileName: string) => path.join(directory, 'src', fileName);

Expand Down Expand Up @@ -71,6 +42,18 @@ class TypeScriptWebpackTemplate extends BaseTemplate {
// Remove preload.js and replace with preload.ts
await fs.remove(filePath('preload.js'));
await this.copyTemplateFile(path.join(directory, 'src'), 'preload.ts');

// update package.json
const packageJSONPath = path.resolve(directory, 'package.json');
const packageJSON = await fs.readJson(packageJSONPath);
packageJSON.main = '.webpack/main';
// Configure scripts for TS template
packageJSON.scripts.lint = 'eslint --ext .ts,.tsx .';
await fs.writeJson(packageJSONPath, packageJSON, {
spaces: 2,
});

await fs.writeJson(packageJSONPath, packageJSON, { spaces: 2 });
});
}
}
Expand Down
43 changes: 43 additions & 0 deletions packages/template/typescript-webpack/tmpl/forge.config.ts
@@ -0,0 +1,43 @@
module.exports = {
packagerConfig: {},
rebuildConfig: {},
makers: [
{
name: '@electron-forge/maker-squirrel',
config: {},
},
{
name: '@electron-forge/maker-zip',
platforms: ['darwin'],
},
{
name: '@electron-forge/maker-deb',
config: {},
},
{
name: '@electron-forge/maker-rpm',
config: {},
},
],
plugins: [
{
name: '@electron-forge/plugin-webpack',
config: {
mainConfig: './webpack.main.config.js',
renderer: {
config: './webpack.renderer.config.js',
entryPoints: [
{
html: './src/index.html',
js: './src/renderer.js',
name: 'main_window',
preload: {
js: './src/preload.js',
},
},
],
},
},
},
],
};
35 changes: 9 additions & 26 deletions packages/template/webpack/src/WebpackTemplate.ts
Expand Up @@ -11,32 +11,7 @@ class WebpackTemplate extends BaseTemplate {
public async initializeTemplate(directory: string, options: InitTemplateOptions) {
await super.initializeTemplate(directory, options);
await asyncOra('Setting up Forge configuration', async () => {
const pjPath = path.resolve(directory, 'package.json');
const currentPJ = await fs.readJson(pjPath);
currentPJ.main = '.webpack/main';
currentPJ.config.forge.plugins = currentPJ.config.forge.plugins || [];
currentPJ.config.forge.plugins.push({
name: '@electron-forge/plugin-webpack',
config: {
mainConfig: './webpack.main.config.js',
renderer: {
config: './webpack.renderer.config.js',
entryPoints: [
{
html: './src/index.html',
js: './src/renderer.js',
name: 'main_window',
preload: {
js: './src/preload.js',
},
},
],
},
},
});
await fs.writeJson(pjPath, currentPJ, {
spaces: 2,
});
await this.copyTemplateFile(directory, 'forge.config.js');
});
await asyncOra('Setting up webpack configuration', async () => {
await this.copyTemplateFile(directory, 'webpack.main.config.js');
Expand All @@ -59,6 +34,14 @@ class WebpackTemplate extends BaseTemplate {
if (line.includes('link rel="stylesheet"')) return '';
return line;
});

// update package.json entry point
const pjPath = path.resolve(directory, 'package.json');
const currentPJ = await fs.readJson(pjPath);
currentPJ.main = '.webpack/main';
await fs.writeJson(pjPath, currentPJ, {
spaces: 2,
});
});
}
}
Expand Down

0 comments on commit 777197e

Please sign in to comment.