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(plugin-vite): upgrade to vite@5 #3468

Merged
merged 19 commits into from
Feb 7, 2024
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.DS_Store
.nyc_output
*.lcov
/coverage
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@
"lint:fix": "prettier --write .",
"link:prepare": "lerna exec -- node ../../../tools/silent.js yarn link --silent --no-bin-links --link-folder ../../../.links",
"link:remove": "lerna exec -- node ../../../tools/silent.js yarn unlink --silent --no-bin-links --link-folder ../../../.links",
"test": "xvfb-maybe cross-env NODE_ENV=test TS_NODE_PROJECT='./tsconfig.test.json' TS_NODE_FILES=1 mocha",
"test": "npm run test:clear && xvfb-maybe cross-env NODE_ENV=test TS_NODE_PROJECT='./tsconfig.test.json' TS_NODE_FILES=1 mocha",
"test:fast": "npm run test -- --suite=fast",
"test:slow": "npm run test -- --suite=slow",
"test:clear": "ts-node tools/test-clear",
"postinstall": "rimraf node_modules/.bin/*.ps1 && ts-node ./tools/gen-tsconfigs.ts && ts-node ./tools/gen-ts-glue.ts",
"prepare": "husky install",
"preversion": "yarn build"
Expand Down Expand Up @@ -74,7 +75,7 @@
"source-map-support": "^0.5.13",
"sudo-prompt": "^9.1.1",
"username": "^5.1.0",
"vite": "^4.5.1",
erickzhao marked this conversation as resolved.
Show resolved Hide resolved
"vite": "^5.0.12",
"webpack": "^5.69.1",
"webpack-dev-server": "^4.0.0",
"webpack-merge": "^5.7.3",
Expand Down
20 changes: 10 additions & 10 deletions packages/plugin/vite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,28 @@
"scripts": {
"test": "xvfb-maybe mocha --config ../../../.mocharc.js test/**/*_spec.ts test/*_spec.ts"
},
"dependencies": {
"@electron-forge/core-utils": "7.2.0",
"@electron-forge/plugin-base": "7.2.0",
"@electron-forge/shared-types": "7.2.0",
"@electron-forge/web-multi-logger": "7.2.0",
"chalk": "^4.0.0",
"debug": "^4.3.1",
"fs-extra": "^10.0.0"
},
"devDependencies": {
"@electron/packager": "^18.1.2",
"@malept/cross-spawn-promise": "^2.0.0",
"@types/node": "^18.0.3",
"chai": "^4.3.3",
"mocha": "^9.0.1",
"vite": "^5.0.12",
"which": "^2.0.2",
"xvfb-maybe": "^0.2.1"
},
"engines": {
"node": ">= 16.4.0"
},
"dependencies": {
"@electron-forge/core-utils": "7.2.0",
"@electron-forge/plugin-base": "7.2.0",
"@electron-forge/shared-types": "7.2.0",
"@electron-forge/web-multi-logger": "7.2.0",
"chalk": "^4.0.0",
"debug": "^4.3.1",
"fs-extra": "^10.0.0",
"vite": "^4.5.1"
},
"publishConfig": {
"access": "public"
},
Expand Down
13 changes: 9 additions & 4 deletions packages/plugin/vite/src/Config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// eslint-disable-next-line node/no-unpublished-import
import type { LibraryOptions } from 'vite';

export interface VitePluginBuildConfig {
BlackHole1 marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -8,27 +9,31 @@ export interface VitePluginBuildConfig {
/**
* Vite config file path.
*/
config?: string;
config: string;
}

export interface VitePluginRendererConfig {
/**
* Human friendly name of your entry point
* Human friendly name of your entry point.
*/
name: string;
name?: string;
/**
* Vite config file path.
*/
config: string;
}

export interface VitePluginConfig {
// Reserved option, may support modification in the future.
// @defaultValue '.vite'
// baseDir?: string;

/**
* Build anything such as Main process, Preload scripts and Worker process, etc.
*/
build: VitePluginBuildConfig[];
/**
* Renderer process.
* Renderer process Vite configs.
*/
renderer: VitePluginRendererConfig[];
}
110 changes: 25 additions & 85 deletions packages/plugin/vite/src/ViteConfig.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,33 @@
import path from 'node:path';

import debug from 'debug';
import { ConfigEnv, loadConfigFromFile, mergeConfig, UserConfig } from 'vite';

import { VitePluginConfig } from './Config';
import { externalBuiltins } from './util/plugins';
// eslint-disable-next-line node/no-unpublished-import
import { loadConfigFromFile } from 'vite';

const d = debug('electron-forge:plugin:vite:viteconfig');
import type { VitePluginBuildConfig, VitePluginConfig, VitePluginRendererConfig } from './Config';
// eslint-disable-next-line node/no-unpublished-import
import type { ConfigEnv, UserConfig } from 'vite';

/**
* Vite allows zero-config runs, if the user does not provide `vite.config.js`,
* then the value of `LoadResult` will become `null`.
*/
export type LoadResult = Awaited<ReturnType<typeof loadConfigFromFile>>;
const d = debug('@electron-forge/plugin-vite:ViteConfig');

export default class ViteConfigGenerator {
private readonly baseDir: string;

private rendererConfigCache!: Promise<UserConfig>[];

constructor(private readonly pluginConfig: VitePluginConfig, private readonly projectDir: string, private readonly isProd: boolean) {
this.baseDir = path.join(projectDir, '.vite');
d('Config mode:', this.mode);
}

resolveConfig(config: string, configEnv: Partial<ConfigEnv> = {}) {
// `command` is to be passed as an arguments when the user export a function in `vite.config.js`.
resolveConfig(buildConfig: VitePluginBuildConfig | VitePluginRendererConfig, configEnv: Partial<ConfigEnv> = {}) {
// @see - https://vitejs.dev/config/#conditional-config
configEnv.command ??= this.isProd ? 'build' : 'serve';
// `mode` affects `.env.[mode]` file loading.
// `mode` affects `.env.[mode]` file load.
configEnv.mode ??= this.mode;
return loadConfigFromFile(configEnv as ConfigEnv, config);

// Hack! Pass the forge runtime config to the vite config file in the template.
Object.assign(configEnv, {
root: this.projectDir,
forgeConfig: this.pluginConfig,
forgeConfigSelf: buildConfig,
});

// `configEnv` is to be passed as an arguments when the user export a function in `vite.config.js`.
return loadConfigFromFile(configEnv as ConfigEnv, buildConfig.config);
}

get mode(): string {
Expand All @@ -40,61 +37,15 @@ export default class ViteConfigGenerator {
return this.isProd ? 'production' : 'development';
}

async getDefines(): Promise<Record<string, string>> {
const defines: Record<string, any> = {};
const rendererConfigs = await this.getRendererConfig();
for (const [index, userConfig] of rendererConfigs.entries()) {
const name = this.pluginConfig.renderer[index].name;
if (!name) {
continue;
}
const NAME = name.toUpperCase().replace(/ /g, '_');
// `server.port` is set in `launchRendererDevServers` in `VitePlugin.ts`.
defines[`${NAME}_VITE_DEV_SERVER_URL`] = this.isProd ? undefined : JSON.stringify(`http://localhost:${userConfig?.server?.port}`);
defines[`${NAME}_VITE_NAME`] = JSON.stringify(name);
}
return defines;
}

async getBuildConfig(watch = false): Promise<UserConfig[]> {
async getBuildConfig(): Promise<UserConfig[]> {
if (!Array.isArray(this.pluginConfig.build)) {
throw new Error('"config.build" must be an Array');
}

const define = await this.getDefines();
const plugins = [externalBuiltins()];
const configs = this.pluginConfig.build
.filter(({ entry, config }) => entry || config)
.map<Promise<UserConfig>>(async ({ entry, config }) => {
const defaultConfig: UserConfig = {
// Ensure that each build config loads the .env file correctly.
mode: this.mode,
build: {
lib: entry
? {
entry,
// Electron can only support cjs.
formats: ['cjs'],
fileName: () => '[name].js',
}
: undefined,
// Prevent multiple builds from interfering with each other.
emptyOutDir: false,
// 🚧 Multiple builds may conflict.
outDir: path.join(this.baseDir, 'build'),
watch: watch ? {} : undefined,
minify: this.isProd,
},
clearScreen: false,
define,
plugins,
};
if (config) {
const loadResult = await this.resolveConfig(config);
return mergeConfig(defaultConfig, loadResult?.config ?? {});
}
return defaultConfig;
});
// Prevent load the default `vite.config.js` file.
.filter(({ config }) => config)
.map<Promise<UserConfig>>(async (buildConfig) => (await this.resolveConfig(buildConfig))?.config ?? {});

return await Promise.all(configs);
}
Expand All @@ -104,20 +55,9 @@ export default class ViteConfigGenerator {
throw new Error('"config.renderer" must be an Array');
}

const configs = (this.rendererConfigCache ??= this.pluginConfig.renderer.map(async ({ name, config }) => {
const defaultConfig: UserConfig = {
// Ensure that each build config loads the .env file correctly.
mode: this.mode,
// Make sure that Electron can be loaded into the local file using `loadFile` after packaging.
base: './',
build: {
outDir: path.join(this.baseDir, 'renderer', name),
},
clearScreen: false,
};
const loadResult = (await this.resolveConfig(config)) ?? { path: '', config: {}, dependencies: [] };
return mergeConfig(defaultConfig, loadResult.config);
}));
const configs = this.pluginConfig.renderer
.filter(({ config }) => config)
.map<Promise<UserConfig>>(async (buildConfig) => (await this.resolveConfig(buildConfig))?.config ?? {});

return await Promise.all(configs);
}
Expand Down
79 changes: 37 additions & 42 deletions packages/plugin/vite/src/VitePlugin.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import { AddressInfo } from 'node:net';
import path from 'node:path';

import { namedHookWithTaskFn, PluginBase } from '@electron-forge/plugin-base';
import { ForgeMultiHookMap, ResolvedForgeConfig, StartResult } from '@electron-forge/shared-types';
import chalk from 'chalk';
import debug from 'debug';
import fs from 'fs-extra';
// eslint-disable-next-line node/no-extraneous-import
import { RollupWatcher } from 'rollup';
// eslint-disable-next-line node/no-unpublished-import
import { default as vite } from 'vite';

import { VitePluginConfig } from './Config';
import { getFlatDependencies } from './util/package';
import { onBuildDone } from './util/plugins';
import ViteConfigGenerator from './ViteConfig';

import type { VitePluginConfig } from './Config';
import type { ForgeMultiHookMap, ResolvedForgeConfig, StartResult } from '@electron-forge/shared-types';
import type { AddressInfo } from 'node:net';
// eslint-disable-next-line node/no-extraneous-import
import type { RollupWatcher } from 'rollup';

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

export default class VitePlugin extends PluginBase<VitePluginConfig> {
Expand Down Expand Up @@ -144,7 +147,7 @@ the generated files). Instead, it is ${JSON.stringify(pj.main)}`);
{
title: 'Compiling main process code',
task: async () => {
await this.build(true);
await this.build();
},
options: {
showTimer: true,
Expand All @@ -156,42 +159,34 @@ the generated files). Instead, it is ${JSON.stringify(pj.main)}`);
};

// Main process, Preload scripts and Worker process, etc.
build = async (watch = false): Promise<void> => {
await Promise.all(
(
await this.configGenerator.getBuildConfig(watch)
).map((userConfig) => {
return new Promise<void>((resolve, reject) => {
vite
.build({
// Avoid recursive builds caused by users configuring @electron-forge/plugin-vite in Vite config file.
configFile: false,
...userConfig,
plugins: [
{
name: '@electron-forge/plugin-vite:start',
closeBundle() {
resolve();

// TODO: implement hot-restart here
},
},
...(userConfig.plugins ?? []),
],
})
.then((result) => {
const isWatcher = (x: any): x is RollupWatcher => typeof x?.close === 'function';

if (isWatcher(result)) {
this.watchers.push(result);
}

return result;
})
.catch(reject);
});
})
);
build = async (): Promise<void> => {
const configs = await this.configGenerator.getBuildConfig();
const buildTasks: Promise<void>[] = [];
const isWatcher = (x: any): x is RollupWatcher => typeof x?.close === 'function';

for (const userConfig of configs) {
const buildTask = new Promise<void>((resolve, reject) => {
vite
.build({
// Avoid recursive builds caused by users configuring @electron-forge/plugin-vite in Vite config file.
configFile: false,
...userConfig,
plugins: [onBuildDone(resolve), ...(userConfig.plugins ?? [])],
})
.then((result) => {
if (isWatcher(result)) {
this.watchers.push(result);
}

return result;
})
.catch(reject);
});

buildTasks.push(buildTask);
}

await Promise.all(buildTasks);
};

// Renderer process
Expand Down
Loading