diff --git a/packages/api/core/test/fixture/app-with-scoped-name/out/make/zip/linux/x64/@scope-package-linux-x64-1.0.0.zip b/packages/api/core/test/fixture/app-with-scoped-name/out/make/zip/linux/x64/@scope-package-linux-x64-1.0.0.zip index 818053ed76..ac3631a134 100644 Binary files a/packages/api/core/test/fixture/app-with-scoped-name/out/make/zip/linux/x64/@scope-package-linux-x64-1.0.0.zip and b/packages/api/core/test/fixture/app-with-scoped-name/out/make/zip/linux/x64/@scope-package-linux-x64-1.0.0.zip differ diff --git a/packages/plugin/webpack/src/Config.ts b/packages/plugin/webpack/src/Config.ts index f0621176fc..9d956eb2d1 100644 --- a/packages/plugin/webpack/src/Config.ts +++ b/packages/plugin/webpack/src/Config.ts @@ -2,18 +2,7 @@ import { Configuration as RawWebpackConfiguration } from 'webpack'; import WebpackDevServer from 'webpack-dev-server'; import { ConfigurationFactory as WebpackConfigurationFactory } from './WebpackConfig'; -export interface WebpackPluginEntryPoint { - /** - * Relative or absolute path to the HTML template file for this entry point - * - * If this is a window, you MUST provide this. Only leave it unset for things - * like WebWorker scripts. - */ - html?: string; - /** - * Relative or absolute path to the main JS file for this entry point - */ - js: string; +export interface WebpackPluginEntryPointBase { /** * Human friendly name of your entry point */ @@ -25,24 +14,19 @@ export interface WebpackPluginEntryPoint { */ prefixedEntries?: string[]; /** - * Additional chunks to include in the outputted HTML file, use this if you - * set up some custom chunking. E.g. CommonChunksPlugin + * Additional chunks to include in the outputted HTML file. Use this if you + * set up some custom chunking (e.g. using SplitChunksPlugin). */ additionalChunks?: string[]; /** - * Information about the preload script for this entry point, if you don't use - * preload scripts you don't need to set this. - */ - preload?: WebpackPreloadEntryPoint; - /** - * Override the Webpack config for this renderer based on whether `nodeIntegration` for - * the `BrowserWindow` is enabled. Namely, for Webpack's `target` option: + * Override the webpack config for this renderer based on whether `nodeIntegration` for + * the `BrowserWindow` is enabled. For webpack's `target` option: * * * When `nodeIntegration` is true, the `target` is `electron-renderer`. * * When `nodeIntegration` is false, the `target` is `web`. * - * Unfortunately, we cannot derive the value from the main process code as it can be a - * dynamically generated value at runtime, and Webpack processes at build-time. + * Unfortunately, we cannot derive the value from the main process code as it can be + * dynamically generated at run-time, and webpack processes at build-time. * * Defaults to `false` (as it is disabled by default in Electron \>= 5) or the value set * for all entries. @@ -50,9 +34,41 @@ export interface WebpackPluginEntryPoint { nodeIntegration?: boolean; } +export interface WebpackPluginEntryPointLocalWindow extends WebpackPluginEntryPointBase { + /** + * Relative or absolute path to the HTML template file for this entry point. + */ + html: string; + /** + * Relative or absolute path to the main JS file for this entry point. + */ + js: string; + /** + * Information about the preload script for this entry point. If you don't use + * preload scripts, you don't need to set this. + */ + preload?: WebpackPreloadEntryPoint; +} + +export interface WebpackPluginEntryPointPreloadOnly extends WebpackPluginEntryPointBase { + /** + * Information about the preload script for this entry point. + */ + preload: WebpackPreloadEntryPoint; +} + +export interface WebpackPluginEntryPointNoWindow extends WebpackPluginEntryPointBase { + /** + * Relative or absolute path to the main JS file for this entry point. + */ + js: string; +} + +export type WebpackPluginEntryPoint = WebpackPluginEntryPointLocalWindow | WebpackPluginEntryPointNoWindow | WebpackPluginEntryPointPreloadOnly; + export interface WebpackPreloadEntryPoint { /** - * Relative or absolute path to the preload JS file + * Relative or absolute path to the preload JS file. */ js: string; /** @@ -62,12 +78,16 @@ export interface WebpackPreloadEntryPoint { */ prefixedEntries?: string[]; /** - * The optional webpack config for your preload process, defaults to the - * renderer webpack config if blank + * The optional webpack config for your preload process. + * Defaults to the renderer webpack config if blank. */ config?: WebpackConfiguration | string; } +export interface StandaloneWebpackPreloadEntryPoint extends WebpackPreloadEntryPoint { + name: string; +} + export interface WebpackPluginRendererConfig { /** * The webpack config for your renderer process @@ -81,14 +101,14 @@ export interface WebpackPluginRendererConfig { */ jsonStats?: boolean; /** - * Adjusts the Webpack config for all renderer entries based on whether `nodeIntegration` - * for the `BrowserWindow` is enabled. Namely, for Webpack's `target` option: + * Override the webpack config for this renderer based on whether `nodeIntegration` for + * the `BrowserWindow` is enabled. For webpack's `target` option: * * * When `nodeIntegration` is true, the `target` is `electron-renderer`. * * When `nodeIntegration` is false, the `target` is `web`. * - * Unfortunately, we cannot derive the value from the main process code as it can be a - * dynamically generated value at runtime, and Webpack processes at build-time. + * Unfortunately, we cannot derive the value from the main process code as it can be + * dynamically generated at run-time, and webpack processes at build-time. * * Defaults to `false` (as it is disabled by default in Electron \>= 5). */ diff --git a/packages/plugin/webpack/src/WebpackConfig.ts b/packages/plugin/webpack/src/WebpackConfig.ts index af481b11b0..2dd1cf7dc3 100644 --- a/packages/plugin/webpack/src/WebpackConfig.ts +++ b/packages/plugin/webpack/src/WebpackConfig.ts @@ -3,9 +3,10 @@ import HtmlWebpackPlugin from 'html-webpack-plugin'; import path from 'path'; import webpack, { Configuration, WebpackPluginInstance } from 'webpack'; import { merge as webpackMerge } from 'webpack-merge'; -import { WebpackPluginConfig, WebpackPluginEntryPoint, WebpackPreloadEntryPoint } from './Config'; +import { WebpackPluginConfig, WebpackPluginEntryPoint, WebpackPluginEntryPointLocalWindow, WebpackPluginEntryPointPreloadOnly } from './Config'; import AssetRelocatorPatch from './util/AssetRelocatorPatch'; import processConfig from './util/processConfig'; +import { isLocalWindow, isNoWindow, isPreloadOnly } from './util/rendererTypeUtils'; type EntryType = string | string[] | Record; type WebpackMode = 'production' | 'development'; @@ -87,15 +88,16 @@ export default class WebpackConfigGenerator { } getPreloadDefine(entryPoint: WebpackPluginEntryPoint): string { - if (entryPoint.preload) { + if (!isNoWindow(entryPoint)) { if (this.isProd) { return `require('path').resolve(__dirname, '../renderer', '${entryPoint.name}', 'preload.js')`; } return `'${path.resolve(this.webpackDir, 'renderer', entryPoint.name, 'preload.js').replace(/\\/g, '\\\\')}'`; + } else { + // If this entry-point has no configured preload script just map this constant to `undefined` + // so that any code using it still works. This makes quick-start / docs simpler. + return 'undefined'; } - // If this entry-point has no configured preload script just map this constant to `undefined` - // so that any code using it still works. This makes quick-start / docs simpler. - return 'undefined'; } getDefines(inRendererDir = true): Record { @@ -105,7 +107,7 @@ export default class WebpackConfigGenerator { } for (const entryPoint of this.pluginConfig.renderer.entryPoints) { const entryKey = this.toEnvironmentVariable(entryPoint); - if (entryPoint.html) { + if (isLocalWindow(entryPoint)) { defines[entryKey] = this.rendererEntryPoint(entryPoint, inRendererDir, 'index.html'); } else { defines[entryKey] = this.rendererEntryPoint(entryPoint, inRendererDir, 'index.js'); @@ -116,6 +118,7 @@ export default class WebpackConfigGenerator { defines[preloadDefineKey] = this.getPreloadDefine(entryPoint); defines[`process.env.${preloadDefineKey}`] = defines[preloadDefineKey]; } + return defines; } @@ -158,17 +161,21 @@ export default class WebpackConfigGenerator { ); } - async getPreloadRendererConfig(parentPoint: WebpackPluginEntryPoint, entryPoint: WebpackPreloadEntryPoint): Promise { - const rendererConfig = await this.resolveConfig(entryPoint.config || this.pluginConfig.renderer.config); + async getPreloadConfigForEntryPoint(entryPoint: WebpackPluginEntryPointLocalWindow | WebpackPluginEntryPointPreloadOnly): Promise { + if (!entryPoint.preload) { + return {}; + } + + const rendererConfig = await this.resolveConfig(entryPoint.preload.config || this.pluginConfig.renderer.config); const prefixedEntries = entryPoint.prefixedEntries || []; return webpackMerge( { devtool: this.rendererSourceMapOption, mode: this.mode, - entry: prefixedEntries.concat([entryPoint.js]), + entry: prefixedEntries.concat([entryPoint.preload.js]), output: { - path: path.resolve(this.webpackDir, 'renderer', parentPoint.name), + path: path.resolve(this.webpackDir, 'renderer', entryPoint.name), filename: 'preload.js', }, node: { @@ -186,43 +193,83 @@ export default class WebpackConfigGenerator { const defines = this.getDefines(false); return entryPoints.map((entryPoint) => { - const config = webpackMerge( - { - entry: { - [entryPoint.name]: (entryPoint.prefixedEntries || []).concat([entryPoint.js]), + const baseConfig: webpack.Configuration = { + target: this.rendererTarget(entryPoint), + devtool: this.rendererSourceMapOption, + mode: this.mode, + output: { + path: path.resolve(this.webpackDir, 'renderer'), + filename: '[name]/index.js', + globalObject: 'self', + ...(this.isProd ? {} : { publicPath: '/' }), + }, + node: { + __dirname: false, + __filename: false, + }, + plugins: [new webpack.DefinePlugin(defines), new AssetRelocatorPatch(this.isProd, !!this.pluginConfig.renderer.nodeIntegration)], + }; + + if (isLocalWindow(entryPoint)) { + return webpackMerge( + baseConfig, + { + entry: { + [entryPoint.name]: (entryPoint.prefixedEntries || []).concat([entryPoint.js]), + }, + output: { + path: path.resolve(this.webpackDir, 'renderer'), + filename: '[name]/index.js', + globalObject: 'self', + ...(this.isProd ? {} : { publicPath: '/' }), + }, + plugins: [ + new HtmlWebpackPlugin({ + title: entryPoint.name, + template: entryPoint.html, + filename: `${entryPoint.name}/index.html`, + chunks: [entryPoint.name].concat(entryPoint.additionalChunks || []), + }) as WebpackPluginInstance, + ], }, - target: this.rendererTarget(entryPoint), - devtool: this.rendererSourceMapOption, - mode: this.mode, - output: { - path: path.resolve(this.webpackDir, 'renderer'), - filename: '[name]/index.js', - globalObject: 'self', - ...(this.isProd ? {} : { publicPath: '/' }), + rendererConfig || {} + ); + } else if (isNoWindow(entryPoint)) { + return webpackMerge( + baseConfig, + { + entry: { + [entryPoint.name]: (entryPoint.prefixedEntries || []).concat([entryPoint.js]), + }, + output: { + path: path.resolve(this.webpackDir, 'renderer'), + filename: '[name]/index.js', + globalObject: 'self', + ...(this.isProd ? {} : { publicPath: '/' }), + }, }, - node: { - __dirname: false, - __filename: false, + rendererConfig || {} + ); + } else if (isPreloadOnly(entryPoint)) { + return webpackMerge( + baseConfig, + { + target: 'electron-preload', + entry: { + [entryPoint.name]: (entryPoint.prefixedEntries || []).concat([entryPoint.preload.js]), + }, + output: { + path: path.resolve(this.webpackDir, 'renderer'), + filename: 'preload.js', + globalObject: 'self', + ...(this.isProd ? {} : { publicPath: '/' }), + }, }, - plugins: [ - ...(entryPoint.html - ? [ - new HtmlWebpackPlugin({ - title: entryPoint.name, - template: entryPoint.html, - filename: `${entryPoint.name}/index.html`, - chunks: [entryPoint.name].concat(entryPoint.additionalChunks || []), - }) as WebpackPluginInstance, - ] - : []), - new webpack.DefinePlugin(defines), - new AssetRelocatorPatch(this.isProd, !!this.pluginConfig.renderer.nodeIntegration), - ], - }, - rendererConfig || {} - ); - - return config; + rendererConfig || {} + ); + } else { + throw new Error('Invalid renderer entry point detected.'); + } }); } } diff --git a/packages/plugin/webpack/src/WebpackPlugin.ts b/packages/plugin/webpack/src/WebpackPlugin.ts index 4710cdf136..b66fea270f 100644 --- a/packages/plugin/webpack/src/WebpackPlugin.ts +++ b/packages/plugin/webpack/src/WebpackPlugin.ts @@ -17,6 +17,7 @@ import { WebpackPluginConfig } from './Config'; import ElectronForgeLoggingPlugin from './util/ElectronForgeLogging'; import once from './util/once'; import WebpackConfigGenerator from './WebpackConfig'; +import { isLocalWindow, isPreloadOnly } from './util/rendererTypeUtils'; const d = debug('electron-forge:plugin:webpack'); const DEFAULT_PORT = 3000; @@ -292,11 +293,11 @@ the generated files). Instead, it is ${JSON.stringify(pj.main)}`); }); for (const entryPoint of this.config.renderer.entryPoints) { - if (entryPoint.preload) { - await asyncOra(`Compiling Renderer Preload: ${entryPoint.name}`, async () => { + if ((isLocalWindow(entryPoint) && !!entryPoint.preload) || isPreloadOnly(entryPoint)) { + await asyncOra(`Compiling Renderer Preload: ${chalk.cyan(entryPoint.name)}`, async () => { const stats = await this.runWebpack( // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - [await this.configGenerator.getPreloadRendererConfig(entryPoint, entryPoint.preload!)] + [await this.configGenerator.getPreloadConfigForEntryPoint(entryPoint)] ); if (stats?.hasErrors()) { @@ -307,12 +308,17 @@ the generated files). Instead, it is ${JSON.stringify(pj.main)}`); } }; - launchDevServers = async (logger: Logger): Promise => { - await asyncOra('Launch Dev Servers', async () => { + launchRendererDevServers = async (logger: Logger): Promise => { + await asyncOra('Launching Dev Servers for Renderer Process Code', async () => { const tab = logger.createTab('Renderers'); const pluginLogs = new ElectronForgeLoggingPlugin(tab); const config = await this.configGenerator.getRendererConfig(this.config.renderer.entryPoints); + + if (config.length === 0) { + return; + } + for (const entryConfig of config) { if (!entryConfig.plugins) entryConfig.plugins = []; entryConfig.plugins.push(pluginLogs); @@ -326,12 +332,8 @@ the generated files). Instead, it is ${JSON.stringify(pj.main)}`); await asyncOra('Compiling Preload Scripts', async () => { for (const entryPoint of this.config.renderer.entryPoints) { - if (entryPoint.preload) { - const config = await this.configGenerator.getPreloadRendererConfig( - entryPoint, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - entryPoint.preload! - ); + if ((isLocalWindow(entryPoint) && !!entryPoint.preload) || isPreloadOnly(entryPoint)) { + const config = await this.configGenerator.getPreloadConfigForEntryPoint(entryPoint); await new Promise((resolve, reject) => { const tab = logger.createTab(`${entryPoint.name} - Preload`); const [onceResolve, onceReject] = once(resolve, reject); @@ -390,7 +392,7 @@ the generated files). Instead, it is ${JSON.stringify(pj.main)}`); const logger = new Logger(this.loggerPort); this.loggers.push(logger); await this.compileMain(true, logger); - await this.launchDevServers(logger); + await this.launchRendererDevServers(logger); await logger.start(); return false; } diff --git a/packages/plugin/webpack/src/util/rendererTypeUtils.ts b/packages/plugin/webpack/src/util/rendererTypeUtils.ts new file mode 100644 index 0000000000..2b9d0ff008 --- /dev/null +++ b/packages/plugin/webpack/src/util/rendererTypeUtils.ts @@ -0,0 +1,17 @@ +import { WebpackPluginEntryPoint, WebpackPluginEntryPointLocalWindow, WebpackPluginEntryPointNoWindow, WebpackPluginEntryPointPreloadOnly } from '../Config'; + +/** + * Reusable type predicate functions to narrow down the type of the WebpackPluginEntryPoint + */ + +export const isLocalWindow = (entry: WebpackPluginEntryPoint): entry is WebpackPluginEntryPointLocalWindow => { + return !!(entry as any).html; +}; + +export const isPreloadOnly = (entry: WebpackPluginEntryPoint): entry is WebpackPluginEntryPointPreloadOnly => { + return !(entry as any).html && !(entry as any).js && !!(entry as any).preload; +}; + +export const isNoWindow = (entry: WebpackPluginEntryPoint): entry is WebpackPluginEntryPointNoWindow => { + return !(entry as any).html && !!(entry as any).js; +}; diff --git a/packages/plugin/webpack/test/AssetRelocatorPatch_spec.ts b/packages/plugin/webpack/test/AssetRelocatorPatch_spec.ts index 448d1710a3..96a4baf5aa 100644 --- a/packages/plugin/webpack/test/AssetRelocatorPatch_spec.ts +++ b/packages/plugin/webpack/test/AssetRelocatorPatch_spec.ts @@ -4,7 +4,7 @@ import { expect } from 'chai'; import http from 'http'; import { pathExists, readFile } from 'fs-extra'; import { spawn } from '@malept/cross-spawn-promise'; -import { WebpackPluginConfig } from '../src/Config'; +import { WebpackPluginConfig, WebpackPluginEntryPointLocalWindow } from '../src/Config'; import WebpackConfigGenerator from '../src/WebpackConfig'; import which from 'which'; @@ -143,12 +143,8 @@ describe('AssetRelocatorPatch', () => { }); it('builds preload', async () => { - const entryPoint = config.renderer.entryPoints[0]; - const preloadConfig = await generator.getPreloadRendererConfig( - entryPoint, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - entryPoint.preload! - ); + const entryPoint = config.renderer.entryPoints[0] as WebpackPluginEntryPointLocalWindow; + const preloadConfig = await generator.getPreloadConfigForEntryPoint(entryPoint); await asyncWebpack(preloadConfig); await expectOutputFileToHaveTheCorrectNativeModulePath({ @@ -200,12 +196,8 @@ describe('AssetRelocatorPatch', () => { }); it('builds preload', async () => { - const entryPoint = config.renderer.entryPoints[0]; - const preloadConfig = await generator.getPreloadRendererConfig( - entryPoint, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - entryPoint.preload! - ); + const entryPoint = config.renderer.entryPoints[0] as WebpackPluginEntryPointLocalWindow; + const preloadConfig = await generator.getPreloadConfigForEntryPoint(entryPoint); await asyncWebpack(preloadConfig); await expectOutputFileToHaveTheCorrectNativeModulePath({ diff --git a/packages/plugin/webpack/test/WebpackConfig_spec.ts b/packages/plugin/webpack/test/WebpackConfig_spec.ts index 95b9ce60fa..0aaccf46a9 100644 --- a/packages/plugin/webpack/test/WebpackConfig_spec.ts +++ b/packages/plugin/webpack/test/WebpackConfig_spec.ts @@ -3,7 +3,7 @@ import { expect } from 'chai'; import path from 'path'; import WebpackConfigGenerator, { ConfigurationFactory } from '../src/WebpackConfig'; -import { WebpackPluginConfig, WebpackPluginEntryPoint, WebpackConfiguration } from '../src/Config'; +import { WebpackPluginConfig, WebpackPluginEntryPoint, WebpackConfiguration, WebpackPluginEntryPointLocalWindow } from '../src/Config'; import AssetRelocatorPatch from '../src/util/AssetRelocatorPatch'; const mockProjectDir = process.platform === 'win32' ? 'C:\\path' : '/path'; @@ -149,6 +149,7 @@ describe('WebpackConfigGenerator', () => { config: {}, entryPoints: [ { + html: 'index.html', js: 'window.js', name: 'window', preload: { @@ -306,7 +307,7 @@ describe('WebpackConfigGenerator', () => { }); }); - describe('getPreloadRendererConfig', () => { + describe('getPreloadConfigForEntryPoint', () => { it('generates a development config', async () => { const config = { renderer: { @@ -321,12 +322,8 @@ describe('WebpackConfigGenerator', () => { }, } as WebpackPluginConfig; const generator = new WebpackConfigGenerator(config, mockProjectDir, false, 3000); - const entryPoint = config.renderer.entryPoints[0]; - const webpackConfig = await generator.getPreloadRendererConfig( - entryPoint, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - entryPoint.preload! - ); + const entryPoint = config.renderer.entryPoints[0] as WebpackPluginEntryPointLocalWindow; + const webpackConfig = await generator.getPreloadConfigForEntryPoint(entryPoint); expect(webpackConfig.target).to.equal('electron-preload'); expect(webpackConfig.mode).to.equal('development'); expect(webpackConfig.entry).to.deep.equal(['preloadScript.js']); @@ -351,12 +348,8 @@ describe('WebpackConfigGenerator', () => { }, } as WebpackPluginConfig; const generator = new WebpackConfigGenerator(config, mockProjectDir, true, 3000); - const entryPoint = config.renderer.entryPoints[0]; - const webpackConfig = await generator.getPreloadRendererConfig( - entryPoint, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - entryPoint.preload! - ); + const entryPoint = config.renderer.entryPoints[0] as WebpackPluginEntryPointLocalWindow; + const webpackConfig = await generator.getPreloadConfigForEntryPoint(entryPoint); expect(webpackConfig.target).to.equal('electron-preload'); expect(webpackConfig.mode).to.equal('production'); expect(webpackConfig.entry).to.deep.equal(['preload.js']); @@ -383,12 +376,8 @@ describe('WebpackConfigGenerator', () => { }, } as WebpackPluginConfig; const generator = new WebpackConfigGenerator(config, mockProjectDir, true, 3000); - const entryPoint = config.renderer.entryPoints[0]; - const webpackConfig = await generator.getPreloadRendererConfig( - entryPoint, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - entryPoint.preload! - ); + const entryPoint = config.renderer.entryPoints[0] as WebpackPluginEntryPointLocalWindow; + const webpackConfig = await generator.getPreloadConfigForEntryPoint(entryPoint); expect(webpackConfig.target).to.equal('electron-preload'); }); @@ -416,12 +405,8 @@ describe('WebpackConfigGenerator', () => { }, } as WebpackPluginConfig; const generator = new WebpackConfigGenerator(config, mockProjectDir, true, 3000); - const entryPoint = config.renderer.entryPoints[0]; - const preloadWebpackConfig = await generator.getPreloadRendererConfig( - entryPoint, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - entryPoint.preload! - ); + const entryPoint = config.renderer.entryPoints[0] as WebpackPluginEntryPointLocalWindow; + const preloadWebpackConfig = await generator.getPreloadConfigForEntryPoint(entryPoint); const rendererWebpackConfig = await generator.getRendererConfig(config.renderer.entryPoints); // Our preload config plugins is an empty list while our renderer config plugins has a member expect(preloadWebpackConfig.name).to.equal('preload'); @@ -482,6 +467,30 @@ describe('WebpackConfigGenerator', () => { expect(hasAssetRelocatorPatchPlugin(webpackConfig[0].plugins)).to.equal(true); }); + it('generates a preload-only development config', async () => { + const config = { + renderer: { + entryPoints: [ + { + name: 'main', + preload: { + js: 'rendererScript.js', + }, + }, + ], + }, + } as WebpackPluginConfig; + const generator = new WebpackConfigGenerator(config, mockProjectDir, false, 3000); + const webpackConfig = await generator.getRendererConfig(config.renderer.entryPoints); + expect(webpackConfig[0].target).to.equal('electron-preload'); + expect(webpackConfig[0].entry).to.deep.equal({ + main: ['rendererScript.js'], + }); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + expect(webpackConfig[0].plugins!.length).to.equal(2); + expect(hasAssetRelocatorPatchPlugin(webpackConfig[0].plugins)).to.equal(true); + }); + it('generates a production config', async () => { const config = { renderer: { @@ -510,6 +519,36 @@ describe('WebpackConfigGenerator', () => { expect(hasAssetRelocatorPatchPlugin(webpackConfig[0].plugins)).to.equal(true); }); + it('generates a preload-only production config', async () => { + const config = { + renderer: { + entryPoints: [ + { + name: 'main', + preload: { + js: 'rendererScript.js', + }, + }, + ], + }, + } as WebpackPluginConfig; + const generator = new WebpackConfigGenerator(config, mockProjectDir, true, 3000); + const webpackConfig = await generator.getRendererConfig(config.renderer.entryPoints); + expect(webpackConfig[0].target).to.deep.equal('electron-preload'); + expect(webpackConfig[0].mode).to.equal('production'); + expect(webpackConfig[0].entry).to.deep.equal({ + main: ['rendererScript.js'], + }); + expect(webpackConfig[0].output).to.deep.equal({ + path: path.join(mockProjectDir, '.webpack', 'renderer'), + filename: 'preload.js', + globalObject: 'self', + }); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + expect(webpackConfig[0].plugins!.length).to.equal(2); + expect(hasAssetRelocatorPatchPlugin(webpackConfig[0].plugins)).to.equal(true); + }); + it('can override the renderer target', async () => { const config = { renderer: {