build: compile 4 webpackRendererConfigs instead of N (#3101)

* compile 4 webpackRendererConfigs instead of N.

Co-authored-by: Samuel Attard <marshallofsound@electronjs.org>
Co-authored-by: Keeley Hammond <vertedinde@electronjs.org>

* modify enum

* promise all

* refactor

* fix typing

* remove some tests +  more

* fix bug

* add [name]/preload.js

* fix some test

* alter preload tests

* packages now use asset relocator patch

* fix test

* add additional config per entrypoint config

* apply fixes

* chore: add webpack tests

* clean up assetRelocatorPatch path

* add test and fix merge order prioritization

* revert webpack merge priority change

---------

Co-authored-by: Samuel Attard <marshallofsound@electronjs.org>
Co-authored-by: Keeley Hammond <vertedinde@electronjs.org>
Co-authored-by: Erick Zhao <erick@hotmail.ca>
This commit is contained in:
George Xu 2023-08-30 16:00:25 -07:00 коммит произвёл GitHub
Родитель 9ab274d3e6
Коммит ed45e79c35
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 350 добавлений и 281 удалений

Просмотреть файл

@ -8,7 +8,7 @@
"main": "dist/WebpackPlugin.js",
"typings": "dist/WebpackPlugin.d.ts",
"scripts": {
"test": "xvfb-maybe mocha --config ../../../.mocharc.js test/**/*_spec.ts"
"test": "xvfb-maybe mocha --config ../../../.mocharc.js test/**/*_spec.ts test/*_spec.ts"
},
"devDependencies": {
"@malept/cross-spawn-promise": "^2.0.0",

Просмотреть файл

@ -8,7 +8,7 @@ import { merge as webpackMerge } from 'webpack-merge';
import { WebpackPluginConfig, WebpackPluginEntryPoint, WebpackPluginEntryPointLocalWindow, WebpackPluginEntryPointPreloadOnly } from './Config';
import AssetRelocatorPatch from './util/AssetRelocatorPatch';
import processConfig from './util/processConfig';
import { isLocalWindow, isNoWindow, isPreloadOnly } from './util/rendererTypeUtils';
import { isLocalOrNoWindowEntries, isLocalWindow, isNoWindow, isPreloadOnly, isPreloadOnlyEntries } from './util/rendererTypeUtils';
type EntryType = string | string[] | Record<string, string | string[]>;
type WebpackMode = 'production' | 'development';
@ -20,6 +20,35 @@ export type ConfigurationFactory = (
args: Record<string, unknown>
) => Configuration | Promise<Configuration>;
enum RendererTarget {
Web,
ElectronRenderer,
ElectronPreload,
SandboxedPreload,
}
enum WebpackTarget {
Web = 'web',
ElectronPreload = 'electron-preload',
ElectronRenderer = 'electron-renderer',
}
function isNotNull<T>(item: T | null): item is T {
return item !== null;
}
function rendererTargetToWebpackTarget(target: RendererTarget): WebpackTarget {
switch (target) {
case RendererTarget.Web:
case RendererTarget.SandboxedPreload:
return WebpackTarget.Web;
case RendererTarget.ElectronPreload:
return WebpackTarget.ElectronPreload;
case RendererTarget.ElectronRenderer:
return WebpackTarget.ElectronRenderer;
}
}
export default class WebpackConfigGenerator {
private isProd: boolean;
@ -69,10 +98,6 @@ export default class WebpackConfigGenerator {
return this.isProd ? 'source-map' : 'eval-source-map';
}
rendererTarget(entryPoint: WebpackPluginEntryPoint): string {
return entryPoint.nodeIntegration ?? this.pluginConfig.renderer.nodeIntegration ? 'electron-renderer' : 'web';
}
rendererEntryPoint(entryPoint: WebpackPluginEntryPoint, inRendererDir: boolean, basename: string): string {
if (this.isProd) {
return `\`file://$\{require('path').resolve(__dirname, '..', '${inRendererDir ? 'renderer' : '.'}', '${entryPoint.name}', '${basename}')}\``;
@ -163,114 +188,151 @@ export default class WebpackConfigGenerator {
);
}
async getPreloadConfigForEntryPoint(entryPoint: WebpackPluginEntryPointLocalWindow | WebpackPluginEntryPointPreloadOnly): Promise<Configuration> {
if (!entryPoint.preload) {
return {};
async getRendererConfig(entryPoints: WebpackPluginEntryPoint[]): Promise<Configuration[]> {
const entryPointsForTarget = {
web: [] as (WebpackPluginEntryPointLocalWindow | WebpackPluginEntryPoint)[],
electronRenderer: [] as (WebpackPluginEntryPointLocalWindow | WebpackPluginEntryPoint)[],
electronPreload: [] as WebpackPluginEntryPointPreloadOnly[],
sandboxedPreload: [] as WebpackPluginEntryPointPreloadOnly[],
};
for (const entry of entryPoints) {
const target = entry.nodeIntegration ?? this.pluginConfig.renderer.nodeIntegration ? 'electronRenderer' : 'web';
const preloadTarget = entry.nodeIntegration ?? this.pluginConfig.renderer.nodeIntegration ? 'electronPreload' : 'sandboxedPreload';
if (isPreloadOnly(entry)) {
entryPointsForTarget[preloadTarget].push(entry);
} else {
entryPointsForTarget[target].push(entry);
if (isLocalWindow(entry) && entry.preload) {
entryPointsForTarget[preloadTarget].push({ ...entry, preload: entry.preload });
}
}
}
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.preload.js]),
output: {
path: path.resolve(this.webpackDir, 'renderer', entryPoint.name),
filename: 'preload.js',
},
node: {
__dirname: false,
__filename: false,
},
},
rendererConfig || {},
{ target: 'electron-preload' }
const rendererConfigs = await Promise.all(
[
await this.buildRendererConfigs(entryPointsForTarget.web, RendererTarget.Web),
await this.buildRendererConfigs(entryPointsForTarget.electronRenderer, RendererTarget.ElectronRenderer),
await this.buildRendererConfigs(entryPointsForTarget.electronPreload, RendererTarget.ElectronPreload),
await this.buildRendererConfigs(entryPointsForTarget.sandboxedPreload, RendererTarget.SandboxedPreload),
].reduce((configs, allConfigs) => allConfigs.concat(configs))
);
return rendererConfigs.filter(isNotNull);
}
async getRendererConfig(entryPoints: WebpackPluginEntryPoint[]): Promise<Configuration[]> {
buildRendererBaseConfig(target: RendererTarget): webpack.Configuration {
return {
target: rendererTargetToWebpackTarget(target),
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 AssetRelocatorPatch(this.isProd, target === RendererTarget.ElectronRenderer || target === RendererTarget.ElectronPreload)],
};
}
async buildRendererConfigForWebOrRendererTarget(
entryPoints: WebpackPluginEntryPoint[],
target: RendererTarget.Web | RendererTarget.ElectronRenderer
): Promise<Configuration | null> {
if (!isLocalOrNoWindowEntries(entryPoints)) {
throw new Error('Invalid renderer entry point detected.');
}
const entry: webpack.Entry = {};
const baseConfig: webpack.Configuration = this.buildRendererBaseConfig(target);
const rendererConfig = await this.resolveConfig(this.pluginConfig.renderer.config);
return entryPoints.map((entryPoint) => {
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 AssetRelocatorPatch(this.isProd, !!this.pluginConfig.renderer.nodeIntegration)],
};
const output = {
path: path.resolve(this.webpackDir, 'renderer'),
filename: '[name]/index.js',
globalObject: 'self',
...(this.isProd ? {} : { publicPath: '/' }),
};
const plugins: webpack.WebpackPluginInstance[] = [];
for (const entryPoint of entryPoints) {
entry[entryPoint.name] = (entryPoint.prefixedEntries || []).concat([entryPoint.js]);
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,
],
},
rendererConfig || {}
plugins.push(
new HtmlWebpackPlugin({
title: entryPoint.name,
template: entryPoint.html,
filename: `${entryPoint.name}/index.html`,
chunks: [entryPoint.name].concat(entryPoint.additionalChunks || []),
}) as WebpackPluginInstance
);
} 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: '/' }),
},
},
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: '/' }),
},
},
rendererConfig || {}
);
} else {
}
}
return webpackMerge(baseConfig, rendererConfig || {}, { entry, output, plugins });
}
async buildRendererConfigForPreloadOrSandboxedPreloadTarget(
entryPoints: WebpackPluginEntryPointPreloadOnly[],
target: RendererTarget.ElectronPreload | RendererTarget.SandboxedPreload
): Promise<Configuration | null> {
if (entryPoints.length === 0) {
return null;
}
const externals = ['electron', 'electron/renderer', 'electron/common', 'events', 'timers', 'url'];
const entry: webpack.Entry = {};
const baseConfig: webpack.Configuration = this.buildRendererBaseConfig(target);
const rendererConfig = await this.resolveConfig(entryPoints[0].preload?.config || this.pluginConfig.renderer.config);
for (const entryPoint of entryPoints) {
entry[entryPoint.name] = (entryPoint.prefixedEntries || []).concat([entryPoint.preload.js]);
}
const config: Configuration = {
target: rendererTargetToWebpackTarget(target),
entry,
output: {
path: path.resolve(this.webpackDir, 'renderer'),
filename: '[name]/preload.js',
globalObject: 'self',
...(this.isProd ? {} : { publicPath: '/' }),
},
plugins: target === RendererTarget.ElectronPreload ? [] : [new webpack.ExternalsPlugin('commonjs2', externals)],
};
return webpackMerge(baseConfig, rendererConfig || {}, config);
}
async buildRendererConfigs(entryPoints: WebpackPluginEntryPoint[], target: RendererTarget): Promise<Promise<webpack.Configuration | null>[]> {
if (entryPoints.length === 0) {
return [];
}
const rendererConfigs = [];
if (target === RendererTarget.Web || target === RendererTarget.ElectronRenderer) {
rendererConfigs.push(this.buildRendererConfigForWebOrRendererTarget(entryPoints, target));
return rendererConfigs;
} else if (target === RendererTarget.ElectronPreload || target === RendererTarget.SandboxedPreload) {
if (!isPreloadOnlyEntries(entryPoints)) {
throw new Error('Invalid renderer entry point detected.');
}
});
const entryPointsWithPreloadConfig: WebpackPluginEntryPointPreloadOnly[] = [],
entryPointsWithoutPreloadConfig: WebpackPluginEntryPointPreloadOnly[] = [];
entryPoints.forEach((entryPoint) => (entryPoint.preload.config ? entryPointsWithPreloadConfig : entryPointsWithoutPreloadConfig).push(entryPoint));
rendererConfigs.push(this.buildRendererConfigForPreloadOrSandboxedPreloadTarget(entryPointsWithoutPreloadConfig, target));
entryPointsWithPreloadConfig.forEach((entryPoint) => {
rendererConfigs.push(this.buildRendererConfigForPreloadOrSandboxedPreloadTarget([entryPoint], target));
});
return rendererConfigs;
} else {
throw new Error('Invalid renderer entry point detected.');
}
}
}

Просмотреть файл

@ -15,7 +15,6 @@ import { merge } from 'webpack-merge';
import { WebpackPluginConfig } from './Config';
import ElectronForgeLoggingPlugin from './util/ElectronForgeLogging';
import once from './util/once';
import { isLocalWindow, isPreloadOnly } from './util/rendererTypeUtils';
import WebpackConfigGenerator from './WebpackConfig';
const d = debug('electron-forge:plugin:webpack');
@ -281,34 +280,17 @@ the generated files). Instead, it is ${JSON.stringify(pj.main)}`);
if (!watch && stats?.hasErrors()) {
throw new Error(`Compilation errors in the renderer: ${stats.toString()}`);
}
for (const entryPoint of this.config.renderer.entryPoints) {
if ((isLocalWindow(entryPoint) && !!entryPoint.preload) || isPreloadOnly(entryPoint)) {
const stats = await this.runWebpack(
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
[await this.configGenerator.getPreloadConfigForEntryPoint(entryPoint)]
);
if (stats?.hasErrors()) {
throw new Error(`Compilation errors in the preload (${entryPoint.name}): ${stats.toString()}`);
}
}
}
};
launchRendererDevServers = async (logger: Logger): Promise<void> => {
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);
entryConfig.plugins.push(new ElectronForgeLoggingPlugin(logger.createTab(`Renderer Target Bundle (${entryConfig.target})`)));
entryConfig.infrastructureLogging = {
level: 'none',
@ -320,35 +302,6 @@ the generated files). Instead, it is ${JSON.stringify(pj.main)}`);
const webpackDevServer = new WebpackDevServer(this.devServerOptions(), compiler);
await webpackDevServer.start();
this.servers.push(webpackDevServer.server!);
for (const entryPoint of this.config.renderer.entryPoints) {
if ((isLocalWindow(entryPoint) && !!entryPoint.preload) || isPreloadOnly(entryPoint)) {
const config = await this.configGenerator.getPreloadConfigForEntryPoint(entryPoint);
config.infrastructureLogging = {
level: 'none',
};
config.stats = 'none';
await new Promise((resolve, reject) => {
const tab = logger.createTab(`${entryPoint.name} - Preload`);
const [onceResolve, onceReject] = once(resolve, reject);
this.watchers.push(
webpack(config).watch({}, (err, stats) => {
if (stats) {
tab.log(
stats.toString({
colors: true,
})
);
}
if (err) return onceReject(err);
return onceResolve(undefined);
})
);
});
}
}
};
devServerOptions(): WebpackDevServer.Configuration {

Просмотреть файл

@ -15,3 +15,27 @@ export const isPreloadOnly = (entry: WebpackPluginEntryPoint): entry is WebpackP
export const isNoWindow = (entry: WebpackPluginEntryPoint): entry is WebpackPluginEntryPointNoWindow => {
return !(entry as any).html && !!(entry as any).js;
};
export const hasPreloadScript = (entry: WebpackPluginEntryPoint): entry is WebpackPluginEntryPointPreloadOnly => {
return 'preload' in entry;
};
export const isLocalOrNoWindowEntries = (
entries: WebpackPluginEntryPoint[]
): entries is (WebpackPluginEntryPointLocalWindow | WebpackPluginEntryPointNoWindow)[] => {
for (const entry of entries) {
if (!isLocalWindow(entry) && !isNoWindow(entry)) {
return false;
}
}
return true;
};
export const isPreloadOnlyEntries = (entries: WebpackPluginEntryPoint[]): entries is WebpackPluginEntryPointPreloadOnly[] => {
for (const entry of entries) {
if (!hasPreloadScript(entry)) {
return false;
}
}
return true;
};

Просмотреть файл

@ -145,14 +145,13 @@ describe('AssetRelocatorPatch', () => {
});
it('builds preload', async () => {
const entryPoint = config.renderer.entryPoints[0] as WebpackPluginEntryPointLocalWindow;
const preloadConfig = await generator.getPreloadConfigForEntryPoint(entryPoint);
await asyncWebpack(preloadConfig);
const preloadConfig = await generator.getRendererConfig(config.renderer.entryPoints);
await asyncWebpack(preloadConfig[0]);
await expectOutputFileToHaveTheCorrectNativeModulePath({
outDir: path.join(rendererOut, 'main_window'),
outDir: rendererOut,
jsPath: path.join(rendererOut, 'main_window/preload.js'),
nativeModulesString: '__webpack_require__.ab = __dirname + "/native_modules/"',
nativeModulesString: `__webpack_require__.ab = ${JSON.stringify(rendererOut)} + "/native_modules/"`,
nativePathString: `require(__webpack_require__.ab + \\"${nativePathSuffix}\\")`,
});
});
@ -199,13 +198,13 @@ describe('AssetRelocatorPatch', () => {
it('builds preload', async () => {
const entryPoint = config.renderer.entryPoints[0] as WebpackPluginEntryPointLocalWindow;
const preloadConfig = await generator.getPreloadConfigForEntryPoint(entryPoint);
await asyncWebpack(preloadConfig);
const preloadConfig = await generator.getRendererConfig([entryPoint]);
await asyncWebpack(preloadConfig[0]);
await expectOutputFileToHaveTheCorrectNativeModulePath({
outDir: path.join(rendererOut, 'main_window'),
outDir: rendererOut,
jsPath: path.join(rendererOut, 'main_window/preload.js'),
nativeModulesString: '.ab=__dirname+"/native_modules/"',
nativeModulesString: '.ab=require("path").resolve(__dirname,"..")+"/native_modules/"',
nativePathString: `.ab+"${nativePathSuffix}"`,
});
});

Просмотреть файл

@ -3,7 +3,7 @@ import path from 'path';
import { expect } from 'chai';
import { Compiler, Configuration, Entry, WebpackPluginInstance } from 'webpack';
import { WebpackConfiguration, WebpackPluginConfig, WebpackPluginEntryPoint, WebpackPluginEntryPointLocalWindow } from '../src/Config';
import { WebpackConfiguration, WebpackPluginConfig, WebpackPluginEntryPoint } from '../src/Config';
import AssetRelocatorPatch from '../src/util/AssetRelocatorPatch';
import WebpackConfigGenerator, { ConfigurationFactory } from '../src/WebpackConfig';
@ -28,17 +28,18 @@ const sampleWebpackConfig = {
describe('WebpackConfigGenerator', () => {
describe('rendererTarget', () => {
it('is web if undefined', () => {
it('is web if undefined', async () => {
const config = {
renderer: {
entryPoints: [{ name: 'foo', js: 'foo/index.js' }],
},
} as WebpackPluginConfig;
const generator = new WebpackConfigGenerator(config, '/', false, 3000);
expect(generator.rendererTarget(config.renderer.entryPoints[0])).to.equal('web');
const webpackConfig = await generator.getRendererConfig(config.renderer.entryPoints);
expect(webpackConfig[0].target).to.equal('web');
});
it('is web if false', () => {
it('is web if false', async () => {
const config = {
renderer: {
entryPoints: [{ name: 'foo', js: 'foo/index.js' }],
@ -46,10 +47,11 @@ describe('WebpackConfigGenerator', () => {
},
} as WebpackPluginConfig;
const generator = new WebpackConfigGenerator(config, '/', false, 3000);
expect(generator.rendererTarget(config.renderer.entryPoints[0])).to.equal('web');
const webpackConfig = await generator.getRendererConfig(config.renderer.entryPoints);
expect(webpackConfig[0].target).to.equal('web');
});
it('is electron-renderer if true', () => {
it('is electron-renderer if true', async () => {
const config = {
renderer: {
entryPoints: [{ name: 'foo', js: 'foo/index.js' }],
@ -57,10 +59,11 @@ describe('WebpackConfigGenerator', () => {
},
} as WebpackPluginConfig;
const generator = new WebpackConfigGenerator(config, '/', false, 3000);
expect(generator.rendererTarget(config.renderer.entryPoints[0])).to.equal('electron-renderer');
const webpackConfig = await generator.getRendererConfig(config.renderer.entryPoints);
expect(webpackConfig[0].target).to.equal('electron-renderer');
});
it('is web if entry nodeIntegration is false', () => {
it('is web if entry nodeIntegration is false', async () => {
const config = {
renderer: {
entryPoints: [{ name: 'foo', js: 'foo/index.js', nodeIntegration: false }],
@ -68,7 +71,8 @@ describe('WebpackConfigGenerator', () => {
},
} as WebpackPluginConfig;
const generator = new WebpackConfigGenerator(config, '/', false, 3000);
expect(generator.rendererTarget(config.renderer.entryPoints[0])).to.equal('web');
const webpackConfig = await generator.getRendererConfig(config.renderer.entryPoints);
expect(webpackConfig[0].target).to.equal('web');
});
});
@ -308,113 +312,6 @@ describe('WebpackConfigGenerator', () => {
});
});
describe('getPreloadConfigForEntryPoint', () => {
it('generates a development config', async () => {
const config = {
renderer: {
entryPoints: [
{
name: 'main',
preload: {
js: 'preloadScript.js',
},
},
],
},
} as WebpackPluginConfig;
const generator = new WebpackConfigGenerator(config, mockProjectDir, false, 3000);
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']);
expect(webpackConfig.output).to.deep.equal({
path: path.join(mockProjectDir, '.webpack', 'renderer', 'main'),
filename: 'preload.js',
});
expect(hasAssetRelocatorPatchPlugin(webpackConfig.plugins)).to.equal(false);
});
it('generates a production config', async () => {
const config = {
renderer: {
entryPoints: [
{
name: 'main',
preload: {
js: 'preload.js',
},
},
],
},
} as WebpackPluginConfig;
const generator = new WebpackConfigGenerator(config, mockProjectDir, true, 3000);
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']);
expect(webpackConfig.output).to.deep.equal({
path: path.join(mockProjectDir, '.webpack', 'renderer', 'main'),
filename: 'preload.js',
});
expect(hasAssetRelocatorPatchPlugin(webpackConfig.plugins)).to.equal(false);
});
it('prevents the preload target from being overridden', async () => {
const config = {
renderer: {
config: {
target: 'web',
},
entryPoints: [
{
name: 'main',
preload: {
js: 'preload.js',
},
},
],
},
} as WebpackPluginConfig;
const generator = new WebpackConfigGenerator(config, mockProjectDir, true, 3000);
const entryPoint = config.renderer.entryPoints[0] as WebpackPluginEntryPointLocalWindow;
const webpackConfig = await generator.getPreloadConfigForEntryPoint(entryPoint);
expect(webpackConfig.target).to.equal('electron-preload');
});
it('allows you to specify a preload webpack config', async () => {
const config = {
renderer: {
config: {
name: 'renderer',
target: 'web',
entry: 'renderer',
},
entryPoints: [
{
name: 'main',
preload: {
js: 'preload.js',
config: {
name: 'preload',
target: 'electron-preload',
entry: 'preload',
},
},
},
],
},
} as WebpackPluginConfig;
const generator = new WebpackConfigGenerator(config, mockProjectDir, true, 3000);
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');
expect(rendererWebpackConfig[0].name).to.equal('renderer');
});
});
describe('getRendererConfig', () => {
it('generates a development config', async () => {
const config = {
@ -483,12 +380,19 @@ describe('WebpackConfigGenerator', () => {
} 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].target).to.equal('web');
expect(webpackConfig[0].mode).to.equal('development');
expect(webpackConfig[0].entry).to.deep.equal({
main: ['rendererScript.js'],
});
expect(webpackConfig[0].output).to.deep.equal({
path: path.join(mockProjectDir, '.webpack', 'renderer'),
filename: '[name]/preload.js',
globalObject: 'self',
publicPath: '/',
});
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
expect(webpackConfig[0].plugins!.length).to.equal(1);
expect(webpackConfig[0].plugins!.length).to.equal(2);
expect(hasAssetRelocatorPatchPlugin(webpackConfig[0].plugins)).to.equal(true);
});
@ -520,6 +424,34 @@ describe('WebpackConfigGenerator', () => {
expect(hasAssetRelocatorPatchPlugin(webpackConfig[0].plugins)).to.equal(true);
});
it('generates a production config with entryPoint preload', async () => {
const config = {
renderer: {
entryPoints: [
{
name: 'main',
preload: {
js: 'preload.js',
},
},
],
},
} as WebpackPluginConfig;
const generator = new WebpackConfigGenerator(config, mockProjectDir, true, 3000);
const webpackConfig = await generator.getRendererConfig(config.renderer.entryPoints);
expect(webpackConfig[0].target).to.equal('web');
expect(webpackConfig[0].mode).to.equal('production');
expect(webpackConfig[0].entry).to.deep.equal({ main: ['preload.js'] });
expect(webpackConfig[0].output).to.deep.equal({
path: path.join(mockProjectDir, '.webpack', 'renderer'),
filename: '[name]/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('generates a preload-only production config', async () => {
const config = {
renderer: {
@ -535,18 +467,18 @@ describe('WebpackConfigGenerator', () => {
} 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].target).to.deep.equal('web');
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',
filename: '[name]/preload.js',
globalObject: 'self',
});
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
expect(webpackConfig[0].plugins!.length).to.equal(1);
expect(webpackConfig[0].plugins!.length).to.equal(2);
expect(hasAssetRelocatorPatchPlugin(webpackConfig[0].plugins)).to.equal(true);
});
@ -569,6 +501,105 @@ describe('WebpackConfigGenerator', () => {
expect(webpackConfig[0].target).to.equal('web');
});
it('prevents the preload target from being overridden', async () => {
const config = {
renderer: {
config: {
target: 'web',
},
entryPoints: [
{
name: 'main',
preload: {
js: 'preload.js',
},
nodeIntegration: true,
},
],
},
} as WebpackPluginConfig;
const generator = new WebpackConfigGenerator(config, mockProjectDir, true, 3000);
const webpackConfig = await generator.getRendererConfig(config.renderer.entryPoints);
expect(webpackConfig[0].target).to.equal('electron-preload');
});
it('allows you to specify a preload webpack config', async () => {
const config = {
renderer: {
config: {
target: 'web',
name: 'renderer',
entry: 'renderer',
},
entryPoints: [
{
name: 'main',
preload: {
js: 'preload.js',
config: {
name: 'preload',
target: 'electron-preload',
entry: 'preload',
},
},
},
],
},
} as WebpackPluginConfig;
const generator = new WebpackConfigGenerator(config, mockProjectDir, true, 3000);
const webpackConfig = await generator.getRendererConfig(config.renderer.entryPoints);
expect(webpackConfig[0].target).to.equal('web');
expect(webpackConfig[0].name).to.equal('preload');
});
it('generates up to 4 rendererConfigs instead of 1 per entrypoint', async () => {
const config = {
renderer: {
config: {
target: 'web',
},
entryPoints: [
{
name: '1',
preload: {
js: 'preload.js',
},
},
{
name: '2',
preload: {
js: 'preload.js',
},
nodeIntegration: true,
},
{
html: './src/mediaPlayer/index.html',
js: './src/mediaPlayer/index.tsx',
name: '3',
},
{
html: './src/mediaPlayer/index.html',
js: './src/mediaPlayer/index.tsx',
name: '4',
nodeIntegration: true,
},
{
js: './src/background/background.ts',
name: '5',
},
{
js: './src/background/background.ts',
name: '6',
nodeIntegration: true,
},
],
},
} as WebpackPluginConfig;
const generator = new WebpackConfigGenerator(config, mockProjectDir, true, 3000);
const webpackConfig = await generator.getRendererConfig(config.renderer.entryPoints);
expect(webpackConfig.length).to.equal(4);
});
it('generates a config from function', async () => {
const generateWebpackConfig = (webpackConfig: WebpackConfiguration) => {
const config = {