@electron-forge/plugin-vite 8.0.0-alpha.7 → 8.0.0-alpha.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,13 @@
1
+ import { styleText } from 'node:util';
1
2
  import { build } from 'vite';
3
+ import { viteDevServerUrls } from './config/vite.base.config.js';
2
4
  import ViteConfigGenerator from './ViteConfig.js';
3
5
  const projectDir = process.env.FORGE_VITE_PROJECT_DIR;
4
6
  const kind = process.env.FORGE_VITE_KIND;
5
7
  const index = Number(process.env.FORGE_VITE_INDEX);
6
8
  const rawConfig = process.env.FORGE_VITE_CONFIG;
9
+ const watch = process.env.FORGE_VITE_WATCH === '1';
10
+ const rawDevServerUrls = process.env.FORGE_VITE_DEV_SERVER_URLS;
7
11
  if (!projectDir || !kind || !rawConfig || !Number.isInteger(index)) {
8
12
  console.error('subprocess-worker: missing one of FORGE_VITE_PROJECT_DIR, FORGE_VITE_KIND, FORGE_VITE_INDEX, FORGE_VITE_CONFIG');
9
13
  process.exit(1);
@@ -12,7 +16,7 @@ if (!projectDir || !kind || !rawConfig || !Number.isInteger(index)) {
12
16
  // getBuildDefine() reads forgeConfig.renderer to generate ${NAME}_VITE_NAME
13
17
  // defines when building main targets.
14
18
  const pluginConfig = JSON.parse(rawConfig);
15
- const generator = new ViteConfigGenerator(pluginConfig, projectDir, true);
19
+ const generator = new ViteConfigGenerator(pluginConfig, projectDir, !watch);
16
20
  let spec;
17
21
  let target;
18
22
  if (kind === 'build') {
@@ -23,11 +27,93 @@ else {
23
27
  spec = pluginConfig.renderer[index];
24
28
  target = 'renderer';
25
29
  }
30
+ // Seed the module-level URL map so getBuildDefine() produces the same
31
+ // *_VITE_DEV_SERVER_URL defines the in-process path would have.
32
+ if (rawDevServerUrls) {
33
+ Object.assign(viteDevServerUrls, JSON.parse(rawDevServerUrls));
34
+ }
26
35
  const resolved = await generator.resolveConfig(spec, target);
27
- await build({
28
- configFile: false,
29
- logLevel: 'error',
30
- ...resolved,
31
- clearScreen: false,
32
- });
33
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3VicHJvY2Vzcy13b3JrZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvc3VicHJvY2Vzcy13b3JrZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUU3QixPQUFPLG1CQUFtQixNQUFNLGlCQUFpQixDQUFDO0FBUWxELE1BQU0sVUFBVSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsc0JBQXNCLENBQUM7QUFDdEQsTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxlQUF1QyxDQUFDO0FBQ2pFLE1BQU0sS0FBSyxHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLGdCQUFnQixDQUFDLENBQUM7QUFDbkQsTUFBTSxTQUFTLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsQ0FBQztBQUVoRCxJQUFJLENBQUMsVUFBVSxJQUFJLENBQUMsSUFBSSxJQUFJLENBQUMsU0FBUyxJQUFJLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO0lBQ25FLE9BQU8sQ0FBQyxLQUFLLENBQ1gsZ0hBQWdILENBQ2pILENBQUM7SUFDRixPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ2xCLENBQUM7QUFFRCx5RUFBeUU7QUFDekUsNEVBQTRFO0FBQzVFLHNDQUFzQztBQUN0QyxNQUFNLFlBQVksR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBcUIsQ0FBQztBQUUvRCxNQUFNLFNBQVMsR0FBRyxJQUFJLG1CQUFtQixDQUFDLFlBQVksRUFBRSxVQUFVLEVBQUUsSUFBSSxDQUFDLENBQUM7QUFFMUUsSUFBSSxJQUFzRCxDQUFDO0FBQzNELElBQUksTUFBdUMsQ0FBQztBQUM1QyxJQUFJLElBQUksS0FBSyxPQUFPLEVBQUUsQ0FBQztJQUNyQixJQUFJLEdBQUcsWUFBWSxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNqQyxNQUFNLEdBQUksSUFBOEIsQ0FBQyxNQUFNLElBQUksTUFBTSxDQUFDO0FBQzVELENBQUM7S0FBTSxDQUFDO0lBQ04sSUFBSSxHQUFHLFlBQVksQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDcEMsTUFBTSxHQUFHLFVBQVUsQ0FBQztBQUN0QixDQUFDO0FBRUQsTUFBTSxRQUFRLEdBQUcsTUFBTSxTQUFTLENBQUMsYUFBYSxDQUFDLElBQUksRUFBRSxNQUFNLENBQUMsQ0FBQztBQUU3RCxNQUFNLEtBQUssQ0FBQztJQUNWLFVBQVUsRUFBRSxLQUFLO0lBQ2pCLFFBQVEsRUFBRSxPQUFPO0lBQ2pCLEdBQUcsUUFBUTtJQUNYLFdBQVcsRUFBRSxLQUFLO0NBQ25CLENBQUMsQ0FBQyJ9
36
+ if (!watch) {
37
+ await build({
38
+ configFile: false,
39
+ logLevel: 'error',
40
+ ...resolved,
41
+ clearScreen: false,
42
+ });
43
+ }
44
+ else {
45
+ // Matches the format of the default Vite logger
46
+ const timeFormatter = new Intl.DateTimeFormat(undefined, {
47
+ hour: 'numeric',
48
+ minute: 'numeric',
49
+ second: 'numeric',
50
+ });
51
+ // Rollup's `input` can be a string, an array of strings, or an object.
52
+ // https://rollupjs.org/configuration-options/#input
53
+ const input = resolved.build?.rollupOptions?.input ??
54
+ (typeof resolved.build?.lib !== 'boolean'
55
+ ? resolved.build?.lib?.entry
56
+ : undefined);
57
+ const targetDisplay = !input
58
+ ? ''
59
+ : typeof input === 'string'
60
+ ? input
61
+ : Array.isArray(input)
62
+ ? input.join(' ')
63
+ : Object.keys(input).join(' ');
64
+ let firstBuildSent = false;
65
+ const sendOnce = (msg) => {
66
+ if (firstBuildSent)
67
+ return;
68
+ firstBuildSent = true;
69
+ process.send?.(msg);
70
+ };
71
+ const result = await build({
72
+ // Avoid recursive builds caused by users configuring @electron-forge/plugin-vite in Vite config file.
73
+ configFile: false,
74
+ // We suppress Vite output and instead log lines using RollupWatcher events
75
+ logLevel: 'silent',
76
+ ...resolved,
77
+ plugins: [
78
+ // `buildEnd` and `closeBundle` are Rollup output generation hooks.
79
+ // https://rollupjs.org/plugin-development/#output-generation-hooks
80
+ {
81
+ name: '@electron-forge/plugin-vite:build-done',
82
+ buildEnd(err) {
83
+ if (err instanceof Error) {
84
+ sendOnce({ type: 'first-build-error', message: err.message });
85
+ }
86
+ },
87
+ closeBundle() {
88
+ sendOnce({ type: 'first-build-done' });
89
+ if (target === 'preload') {
90
+ // pluginHotRestart('reload') is a no-op here because viteDevServers
91
+ // lives in the parent; ask the parent to fan out the ws full-reload.
92
+ process.send?.({ type: 'reload-renderers' });
93
+ }
94
+ },
95
+ },
96
+ ...(resolved.plugins ?? []),
97
+ ],
98
+ clearScreen: false,
99
+ });
100
+ const isRollupWatcher = (x) => !!x &&
101
+ typeof x === 'object' &&
102
+ 'on' in x &&
103
+ typeof x.on === 'function' &&
104
+ 'close' in x &&
105
+ typeof x.close === 'function';
106
+ if (isRollupWatcher(result)) {
107
+ // The Rollup watcher emits events for subsequent builds.
108
+ result.on('event', (event) => {
109
+ if (event.code === 'ERROR' && resolved.logLevel !== 'silent') {
110
+ console.error(`\n${styleText('dim', timeFormatter.format(new Date()))} ${event.error.message}`);
111
+ }
112
+ else if (event.code === 'BUNDLE_END' &&
113
+ (!resolved.logLevel || resolved.logLevel === 'info')) {
114
+ console.log(`${styleText('dim', timeFormatter.format(new Date()))} ${styleText(['cyan', 'bold'], '[@electron-forge/plugin-vite]')} ${styleText('green', 'target built')} ${styleText('dim', targetDisplay)}`);
115
+ }
116
+ });
117
+ }
118
+ }
119
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3VicHJvY2Vzcy13b3JrZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvc3VicHJvY2Vzcy13b3JrZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUV0QyxPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBRTdCLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBQ2pFLE9BQU8sbUJBQW1CLE1BQU0saUJBQWlCLENBQUM7QUFTbEQsTUFBTSxVQUFVLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxzQkFBc0IsQ0FBQztBQUN0RCxNQUFNLElBQUksR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLGVBQXVDLENBQUM7QUFDakUsTUFBTSxLQUFLLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztBQUNuRCxNQUFNLFNBQVMsR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLGlCQUFpQixDQUFDO0FBQ2hELE1BQU0sS0FBSyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsZ0JBQWdCLEtBQUssR0FBRyxDQUFDO0FBQ25ELE1BQU0sZ0JBQWdCLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQywwQkFBMEIsQ0FBQztBQUVoRSxJQUFJLENBQUMsVUFBVSxJQUFJLENBQUMsSUFBSSxJQUFJLENBQUMsU0FBUyxJQUFJLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO0lBQ25FLE9BQU8sQ0FBQyxLQUFLLENBQ1gsZ0hBQWdILENBQ2pILENBQUM7SUFDRixPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ2xCLENBQUM7QUFFRCx5RUFBeUU7QUFDekUsNEVBQTRFO0FBQzVFLHNDQUFzQztBQUN0QyxNQUFNLFlBQVksR0FBRyxJQUFJLENBQUMsS0FBSyxD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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electron-forge/plugin-vite",
3
- "version": "8.0.0-alpha.7",
3
+ "version": "8.0.0-alpha.9",
4
4
  "type": "module",
5
5
  "description": "Vite plugin for Electron Forge, lets you use Vite directly in your tooling",
6
6
  "repository": {
@@ -18,15 +18,14 @@
18
18
  "./forge-vite-env": "./forge-vite-env.d.ts"
19
19
  },
20
20
  "dependencies": {
21
- "@electron-forge/plugin-base": "8.0.0-alpha.7",
22
- "@electron-forge/shared-types": "8.0.0-alpha.7",
23
- "chalk": "^4.0.0",
21
+ "@electron-forge/plugin-base": "8.0.0-alpha.9",
22
+ "@electron-forge/shared-types": "8.0.0-alpha.9",
24
23
  "debug": "^4.3.1",
25
24
  "fs-extra": "^10.0.0",
26
25
  "listr2": "^7.0.2"
27
26
  },
28
27
  "devDependencies": {
29
- "@electron/packager": "^19.0.1",
28
+ "@electron/packager": "^20.0.0",
30
29
  "@types/node": "^22.10.7",
31
30
  "vite": "^8.0.0",
32
31
  "vitest": "catalog:",
@@ -43,5 +42,5 @@
43
42
  "src",
44
43
  "forge-vite-env.d.ts"
45
44
  ],
46
- "gitHead": "2f61a07d341c7a5b79052f1a3dbe9dab903a1b3e"
45
+ "gitHead": "8d048358c5e5f0b5a09e03740687aa7f0c63d027"
47
46
  }
package/src/VitePlugin.ts CHANGED
@@ -1,13 +1,14 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import path from 'node:path';
3
+ import { styleText } from 'node:util';
3
4
 
4
5
  import { namedHookWithTaskFn, PluginBase } from '@electron-forge/plugin-base';
5
- import chalk from 'chalk';
6
6
  import debug from 'debug';
7
7
  import fs from 'fs-extra';
8
8
  import { Listr, PRESET_TIMER } from 'listr2';
9
9
  import * as vite from 'vite';
10
10
 
11
+ import { viteDevServerUrls } from './config/vite.base.config.js';
11
12
  import ViteConfigGenerator from './ViteConfig.js';
12
13
 
13
14
  import type { VitePluginConfig } from './Config.js';
@@ -16,6 +17,7 @@ import type {
16
17
  ForgeMultiHookMap,
17
18
  ResolvedForgeConfig,
18
19
  } from '@electron-forge/shared-types';
20
+ import type { ChildProcess } from 'node:child_process';
19
21
  import type { AddressInfo } from 'node:net';
20
22
  import type { LibraryOptions } from 'vite';
21
23
 
@@ -76,6 +78,72 @@ function spawnViteBuild(
76
78
  });
77
79
  }
78
80
 
81
+ function spawnViteBuildWatch(
82
+ pluginConfig: Pick<VitePluginConfig, 'build' | 'renderer'>,
83
+ index: number,
84
+ projectDir: string,
85
+ devServerUrls: Record<string, string>,
86
+ onReloadRenderers: () => void,
87
+ ): { child: ChildProcess; firstBuild: Promise<void> } {
88
+ const child = spawn(process.execPath, [subprocessWorkerPath], {
89
+ cwd: projectDir,
90
+ env: {
91
+ ...process.env,
92
+ FORGE_VITE_PROJECT_DIR: projectDir,
93
+ FORGE_VITE_KIND: 'build',
94
+ FORGE_VITE_INDEX: String(index),
95
+ FORGE_VITE_CONFIG: JSON.stringify(pluginConfig),
96
+ FORGE_VITE_WATCH: '1',
97
+ FORGE_VITE_DEV_SERVER_URLS: JSON.stringify(devServerUrls),
98
+ },
99
+ stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
100
+ });
101
+
102
+ let settled = false;
103
+ let stderr = '';
104
+ child.stderr!.setEncoding('utf8');
105
+ child.stderr!.on('data', (chunk) => {
106
+ if (!settled) stderr += chunk;
107
+ process.stderr.write(chunk);
108
+ });
109
+ child.stdout!.setEncoding('utf8');
110
+ child.stdout!.on('data', (chunk) => process.stdout.write(chunk));
111
+
112
+ const firstBuild = new Promise<void>((resolve, reject) => {
113
+ const settle = (fn: () => void) => {
114
+ if (settled) return;
115
+ settled = true;
116
+ stderr = '';
117
+ fn();
118
+ };
119
+
120
+ child.on('message', (msg: { type: string; message?: string }) => {
121
+ if (msg.type === 'first-build-done') {
122
+ settle(resolve);
123
+ } else if (msg.type === 'first-build-error') {
124
+ settle(() => reject(new Error(msg.message)));
125
+ } else if (msg.type === 'reload-renderers') {
126
+ onReloadRenderers();
127
+ }
128
+ });
129
+ child.on('error', (err) => settle(() => reject(err)));
130
+ child.on('close', (code, signal) => {
131
+ const reason = signal
132
+ ? `killed by signal ${signal}`
133
+ : `exited with code ${code}`;
134
+ settle(() =>
135
+ reject(
136
+ new Error(
137
+ `Vite watch subprocess ${reason} before first build completed${stderr ? `:\n${stderr}` : ''}`,
138
+ ),
139
+ ),
140
+ );
141
+ });
142
+ });
143
+
144
+ return { child, firstBuild };
145
+ }
146
+
79
147
  function entryToDisplay(entry: LibraryOptions['entry']): string {
80
148
  if (typeof entry === 'string') return entry;
81
149
  if (Array.isArray(entry)) return entry.join(' ');
@@ -101,17 +169,10 @@ export default class VitePlugin extends PluginBase<VitePluginConfig> {
101
169
 
102
170
  private configGeneratorCache!: ViteConfigGenerator;
103
171
 
104
- private watchers: vite.Rollup.RollupWatcher[] = [];
172
+ private watchChildren: ChildProcess[] = [];
105
173
 
106
174
  private servers: vite.ViteDevServer[] = [];
107
175
 
108
- // Matches the format of the default Vite logger
109
- private timeFormatter = new Intl.DateTimeFormat(undefined, {
110
- hour: 'numeric',
111
- minute: 'numeric',
112
- second: 'numeric',
113
- });
114
-
115
176
  init = (dir: string): void => {
116
177
  this.setDirectories(dir);
117
178
 
@@ -227,9 +288,12 @@ export default class VitePlugin extends PluginBase<VitePluginConfig> {
227
288
  if (forgeConfig.packagerConfig.ignore) {
228
289
  if (typeof forgeConfig.packagerConfig.ignore !== 'function') {
229
290
  console.error(
230
- chalk.yellow(`You have set packagerConfig.ignore, the Electron Forge Vite plugin normally sets this automatically.
291
+ styleText(
292
+ 'yellow',
293
+ `You have set packagerConfig.ignore, the Electron Forge Vite plugin normally sets this automatically.
231
294
 
232
- Your packaged app may be larger than expected if you dont ignore everything other than the '.vite' folder`),
295
+ Your packaged app may be larger than expected if you dont ignore everything other than the '.vite' folder`,
296
+ ),
233
297
  );
234
298
  }
235
299
  return forgeConfig;
@@ -285,13 +349,14 @@ the generated files). Instead, it is ${JSON.stringify(pj.main)}.`);
285
349
 
286
350
  // Main process, Preload scripts and Worker process, etc.
287
351
  build = async (task?: ForgeListrTask<null>): Promise<Listr | void> => {
352
+ const targets = this.config.build
353
+ .map((spec, index) => ({ spec, index }))
354
+ .filter(({ spec }) => spec.config);
355
+
288
356
  if (this.isProd) {
289
- const targets = this.config.build
290
- .map((spec, index) => ({ spec, index }))
291
- .filter(({ spec }) => spec.config);
292
357
  return task?.newListr(
293
358
  targets.map(({ spec, index }) => ({
294
- title: `Building ${chalk.green(entryToDisplay(spec.entry))}`,
359
+ title: `Building ${styleText('green', entryToDisplay(spec.entry))}`,
295
360
  task: async (_ctx, subtask) => {
296
361
  await spawnViteBuild(
297
362
  this.serializableConfig,
@@ -299,7 +364,7 @@ the generated files). Instead, it is ${JSON.stringify(pj.main)}.`);
299
364
  index,
300
365
  this.projectDir,
301
366
  );
302
- subtask.title = `Built target ${chalk.dim(entryToDisplay(spec.entry))}`;
367
+ subtask.title = `Built target ${styleText('dim', entryToDisplay(spec.entry))}`;
303
368
  },
304
369
  })),
305
370
  {
@@ -309,129 +374,28 @@ the generated files). Instead, it is ${JSON.stringify(pj.main)}.`);
309
374
  );
310
375
  }
311
376
 
312
- const configs = await this.configGenerator.getBuildConfigs();
313
- /**
314
- * Checks if the result of the Vite build is a Rollup watcher.
315
- * This should happen iff we're running `electron-forge start`.
316
- */
317
- const isRollupWatcher = (
318
- x:
319
- | vite.Rollup.RollupWatcher
320
- | vite.Rollup.RollupOutput
321
- | vite.Rollup.RollupOutput[],
322
- ): x is vite.Rollup.RollupWatcher =>
323
- x &&
324
- typeof x === 'object' &&
325
- 'on' in x &&
326
- typeof x.on === 'function' &&
327
- 'close' in x &&
328
- typeof x.close === 'function';
329
-
330
- /**
331
- * Rollup's `input` can be a string, an array of strings, or an object.
332
- * This function converts the input to a string for the Forge CLI to consume.
333
- *
334
- * @see https://rollupjs.org/configuration-options/#input
335
- */
336
- const parseInputOptionToString = (input: vite.Rollup.InputOption) => {
337
- if (typeof input === 'string') {
338
- return input;
339
- } else if (Array.isArray(input)) {
340
- return input.join(' ');
341
- } else {
342
- return Object.keys(input).join(' ');
343
- }
344
- };
345
-
346
377
  return task?.newListr(
347
- configs.map((userConfig) => {
348
- let target = '';
349
- const input = userConfig.build?.rollupOptions?.input;
350
- if (input) {
351
- target = parseInputOptionToString(input);
352
- } else if (
353
- typeof userConfig.build?.lib !== 'boolean' &&
354
- userConfig.build?.lib?.entry
355
- ) {
356
- target = parseInputOptionToString(userConfig.build.lib.entry);
357
- }
358
-
359
- return {
360
- title: `Building ${chalk.green(target)} target`,
361
- task: async (_ctx, subtask) => {
362
- // We wrap this function in a Promise to ensure that the task is marked as completed
363
- // only after all bundles are done generated. This is done in the `closeBundle` Rollup hook
364
- // rather than when the `vite.build` promise resolves.
365
- await new Promise<void>((resolve, reject) => {
366
- vite
367
- .build({
368
- // Avoid recursive builds caused by users configuring @electron-forge/plugin-vite in Vite config file.
369
- configFile: false,
370
- // We suppress Vite output and instead log lines using RollupWatcher events
371
- logLevel: 'silent',
372
- ...userConfig,
373
- plugins: [
374
- // This plugin controls the output of the first-time Vite build that happens.
375
- // `buildEnd` and `closeBundle` are Rollup output generation hooks.
376
- // See https://rollupjs.org/plugin-development/#output-generation-hooks
377
- {
378
- name: '@electron-forge/plugin-vite:build-done',
379
- buildEnd(err) {
380
- if (err instanceof Error) {
381
- d(
382
- 'buildEnd rollup hook called with error so build failed',
383
- );
384
- reject(err);
385
- }
386
- },
387
- closeBundle() {
388
- d(
389
- 'no error in buildEnd and reached closeBundle so build succeeded',
390
- );
391
- resolve();
392
- },
393
- },
394
- ...(userConfig.plugins ?? []),
395
- ],
396
- clearScreen: false,
397
- })
398
- .then((result) => {
399
- // When running `start` and enabling watch mode in Vite, the Rollup watcher
400
- // emits events for subsequent builds.
401
- if (isRollupWatcher(result)) {
402
- result.on('event', (event) => {
403
- if (
404
- event.code === 'ERROR' &&
405
- userConfig.logLevel !== 'silent'
406
- ) {
407
- console.error(
408
- `\n${chalk.dim(this.timeFormatter.format(new Date()))} ${event.error.message}`,
409
- );
410
- } else if (
411
- event.code === 'BUNDLE_END' &&
412
- (!userConfig.logLevel || userConfig.logLevel === 'info')
413
- ) {
414
- console.log(
415
- `${chalk.dim(this.timeFormatter.format(new Date()))} ${chalk.cyan.bold('[@electron-forge/plugin-vite]')} ${chalk.green(
416
- 'target built',
417
- )} ${chalk.dim(target)}`,
418
- );
419
- }
420
- });
421
- this.watchers.push(result);
422
- } else {
423
- subtask.title = `Built target ${chalk.dim(target)}`;
424
- }
425
- return result;
426
- })
427
- .catch(reject);
428
- });
429
- },
430
- };
431
- }),
378
+ targets.map(({ spec, index }) => ({
379
+ title: `Building ${styleText('green', entryToDisplay(spec.entry))} target`,
380
+ task: async () => {
381
+ const { child, firstBuild } = spawnViteBuildWatch(
382
+ this.serializableConfig,
383
+ index,
384
+ this.projectDir,
385
+ viteDevServerUrls,
386
+ () => {
387
+ for (const server of this.servers) {
388
+ server.ws.send({ type: 'full-reload' });
389
+ }
390
+ },
391
+ );
392
+ this.watchChildren.push(child);
393
+ await firstBuild;
394
+ },
395
+ })),
432
396
  {
433
397
  concurrent: this.config.concurrent ?? true,
434
- exitOnError: this.isProd,
398
+ exitOnError: false,
435
399
  },
436
400
  );
437
401
  };
@@ -444,7 +408,7 @@ the generated files). Instead, it is ${JSON.stringify(pj.main)}.`);
444
408
  .filter(({ spec }) => spec.config);
445
409
  return task?.newListr(
446
410
  targets.map(({ spec, index }) => ({
447
- title: `Building ${chalk.green(spec.name)}`,
411
+ title: `Building ${styleText('green', spec.name)}`,
448
412
  task: async (_ctx, subtask) => {
449
413
  await spawnViteBuild(
450
414
  this.serializableConfig,
@@ -452,7 +416,7 @@ the generated files). Instead, it is ${JSON.stringify(pj.main)}.`);
452
416
  index,
453
417
  this.projectDir,
454
418
  );
455
- subtask.title = `Built target ${chalk.dim(spec.name)}`;
419
+ subtask.title = `Built target ${styleText('dim', spec.name)}`;
456
420
  },
457
421
  })),
458
422
  {
@@ -471,7 +435,7 @@ the generated files). Instead, it is ${JSON.stringify(pj.main)}.`);
471
435
  logLevel: 'error',
472
436
  ...userConfig,
473
437
  });
474
- subtask.title = `Built target ${chalk.dim(path.basename(userConfig.build?.outDir ?? ''))}`;
438
+ subtask.title = `Built target ${styleText('dim', path.basename(userConfig.build?.outDir ?? ''))}`;
475
439
  },
476
440
  })),
477
441
  {
@@ -484,7 +448,7 @@ the generated files). Instead, it is ${JSON.stringify(pj.main)}.`);
484
448
  const rendererConfigs = await this.configGenerator.getRendererConfig();
485
449
  return task?.newListr(
486
450
  rendererConfigs.map((userConfig) => ({
487
- title: `Target ${chalk.cyan(path.basename(userConfig.build?.outDir ?? ''))}`,
451
+ title: `Target ${styleText('cyan', path.basename(userConfig.build?.outDir ?? ''))}`,
488
452
  task: async (_ctx, subtask) => {
489
453
  const viteDevServer = await vite.createServer({
490
454
  configFile: false,
@@ -524,11 +488,11 @@ the generated files). Instead, it is ${JSON.stringify(pj.main)}.`);
524
488
  ): void => {
525
489
  d('handling process exit with:', options);
526
490
  if (options.cleanup) {
527
- for (const watcher of this.watchers) {
528
- d('cleaning vite watcher');
529
- watcher.close();
491
+ for (const child of this.watchChildren) {
492
+ d('killing vite watch subprocess');
493
+ child.kill();
530
494
  }
531
- this.watchers = [];
495
+ this.watchChildren = [];
532
496
 
533
497
  for (const server of this.servers) {
534
498
  d('cleaning http server');
@@ -549,18 +513,24 @@ the generated files). Instead, it is ${JSON.stringify(pj.main)}.`);
549
513
  function getServerURLs(urls: vite.ResolvedServerUrls) {
550
514
  let output = '';
551
515
  const colorUrl = (url: string) =>
552
- chalk.cyan(url.replace(/:(\d+)\//, (_, port) => `:${chalk.bold(port)}/`));
516
+ styleText(
517
+ 'cyan',
518
+ url.replace(/:(\d+)\//, (_, port) => `:${styleText('bold', port)}/`),
519
+ );
553
520
  for (const url of urls.local) {
554
- output += ` ${chalk.green('➜')} ${chalk.bold('Local')}: ${colorUrl(url)}`;
521
+ output += ` ${styleText('green', '➜')} ${styleText('bold', 'Local')}: ${colorUrl(url)}`;
555
522
  }
556
523
  for (const url of urls.network) {
557
- output += ` \n${chalk.green('➜')} ${chalk.bold('Network')}: ${colorUrl(url)}`;
524
+ output += ` \n${styleText('green', '➜')} ${styleText('bold', 'Network')}: ${colorUrl(url)}`;
558
525
  }
559
526
  if (urls.network.length === 0) {
560
527
  output +=
561
- chalk.dim(` \n${chalk.green('➜')} ${chalk.bold('Network')}: use `) +
562
- chalk.bold('--host') +
563
- chalk.dim(' to expose');
528
+ styleText(
529
+ 'dim',
530
+ ` \n${styleText('green', '➜')} ${styleText('bold', 'Network')}: use `,
531
+ ) +
532
+ styleText('bold', '--host') +
533
+ styleText('dim', ' to expose');
564
534
  }
565
535
 
566
536
  return output;
@@ -11,7 +11,7 @@ export const external = [
11
11
 
12
12
  // Used for hot reload after preload scripts.
13
13
  const viteDevServers: Record<string, ViteDevServer> = {};
14
- const viteDevServerUrls: Record<string, string> = {};
14
+ export const viteDevServerUrls: Record<string, string> = {};
15
15
 
16
16
  export function getBuildConfig(env: ConfigEnv<'build'>): UserConfig {
17
17
  const { root, mode, command } = env;
@@ -24,7 +24,7 @@ export function getBuildConfig(env: ConfigEnv<'build'>): UserConfig {
24
24
  emptyOutDir: false,
25
25
  // 🚧 Multiple builds may conflict.
26
26
  outDir: '.vite/build',
27
- watch: command === 'serve' ? {} : null,
27
+ watch: command === 'serve' ? { exclude: '**/.git/**' } : null,
28
28
  minify: command === 'build',
29
29
  },
30
30
  clearScreen: false,
@@ -20,8 +20,8 @@ export function getConfig(
20
20
  input: forgeConfigSelf.entry,
21
21
  output: {
22
22
  format: 'cjs',
23
- // It should not be split chunks.
24
- inlineDynamicImports: true,
23
+ // Preload scripts require a single entrypoint.
24
+ codeSplitting: false,
25
25
  entryFileNames: '[name].js',
26
26
  chunkFileNames: '[name].js',
27
27
  assetFileNames: '[name].[ext]',