From 9b217731f51c86616a097a5ffa193f3c8125a1aa Mon Sep 17 00:00:00 2001 From: Felix Rieseberg Date: Sat, 11 Apr 2026 12:37:57 -0700 Subject: [PATCH] Replace Parcel 1 with Vite (#353) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Swap the unmaintained parcel-bundler@1.x for Vite's build API, called from the same generateAssets hook. Output layout is unchanged (dist/src/main/main.js + dist/static/index.html + dist/renderer.{js,css}) so no runtime path changes — __dirname-based asset lookup, loadFile, and packagerConfig.ignore all keep working. Renderer is built in lib/CJS mode with Node builtins + electron externalized; a one-line banner aliases exports=module.exports since the Electron + \ No newline at end of file diff --git a/tools/generateAssets.js b/tools/generateAssets.js index 241d2e0..e66bfe0 100644 --- a/tools/generateAssets.js +++ b/tools/generateAssets.js @@ -1,7 +1,7 @@ /* tslint:disable */ -const { compileParcel } = require('./parcel-build') +const { compileVite } = require('./vite-build') module.exports = async () => { - await Promise.all([compileParcel()]) + await compileVite() } diff --git a/tools/parcel-build.js b/tools/parcel-build.js deleted file mode 100644 index bfbddc9..0000000 --- a/tools/parcel-build.js +++ /dev/null @@ -1,91 +0,0 @@ -/* tslint:disable */ - -const Bundler = require('parcel-bundler') -const path = require('path') -const fs = require('fs') - -// libv86 checks `typeof module.exports` before `typeof window` when deciding -// where to export V86. In an Electron renderer with nodeIntegration both exist, -// so it ends up on module.exports instead of window. This shim copies it over. -const LIBV86_SHIM = ` -` - -// v86's node-path file loader used `await import("node:...")` until d4c5fa86 -// switched it to require(). Dynamic import of node: URLs doesn't work in an -// Electron renderer — only require() does. The literals are stable across -// Closure builds; if they're absent the build is post-d4c5fa86 and already -// uses require, so a no-op is correct. -const V86_NODE_IMPORTS = [ - ['await import("node:fs/promises")', 'require("fs").promises'], - ['await import("node:"+"fs/promises")', 'require("fs").promises'], - ['await import("node:crypto")', 'require("crypto")'], -]; - -async function copyLib() { - const target = path.join(__dirname, '../dist/static') - const lib = path.join(__dirname, '../src/renderer/lib') - const index = path.join(target, 'index.html') - - await fs.promises.cp(lib, target, { recursive: true }); - - const libv86path = path.join(target, 'libv86.js') - let libv86 = fs.readFileSync(libv86path, 'utf-8') - let patchCount = 0; - for (const [from, to] of V86_NODE_IMPORTS) { - const next = libv86.split(from).join(to); - if (next !== libv86) { patchCount++; libv86 = next; } - } - if (patchCount > 0) { - fs.writeFileSync(libv86path, libv86) - console.log(`libv86: ${patchCount} dynamic-import → require`) - } - - const indexContents = fs.readFileSync(index, 'utf-8'); - const replacedContents = indexContents.replace('', LIBV86_SHIM) - fs.writeFileSync(index, replacedContents) -} - -async function compileParcel (options = {}) { - const entryFiles = [ - path.join(__dirname, '../static/index.html'), - path.join(__dirname, '../src/main/main.ts') - ] - - const bundlerOptions = { - outDir: './dist', // The out directory to put the build files in, defaults to dist - outFile: undefined, // The name of the outputFile - publicUrl: '../', // The url to server on, defaults to dist - watch: false, // whether to watch the files and rebuild them on change, defaults to process.env.NODE_ENV !== 'production' - cache: false, // Enabled or disables caching, defaults to true - cacheDir: '.cache', // The directory cache gets put in, defaults to .cache - contentHash: false, // Disable content hash from being included on the filename - minify: false, // Minify files, enabled if process.env.NODE_ENV === 'production' - scopeHoist: false, // turn on experimental scope hoisting/tree shaking flag, for smaller production bundles - target: 'electron', // browser/node/electron, defaults to browser - // https: { // Define a custom {key, cert} pair, use true to generate one or false to use http - // cert: './ssl/c.crt', // path to custom certificate - // key: './ssl/k.key' // path to custom key - // }, - logLevel: 3, // 3 = log everything, 2 = log warnings & errors, 1 = log errors - hmr: false, // Enable or disable HMR while watching - hmrPort: 0, // The port the HMR socket runs on, defaults to a random free port (0 in node.js resolves to a random free port) - sourceMaps: false, // Enable or disable sourcemaps, defaults to enabled (minified builds currently always create sourcemaps) - hmrHostname: '', // A hostname for hot module reload, default to '' - detailedReport: false, // Prints a detailed report of the bundles, assets, filesizes and times, defaults to false, reports are only printed if watch is disabled, - ...options - } - - const bundler = new Bundler(entryFiles, bundlerOptions) - - // Run the bundler, this returns the main bundle - // Use the events if you're using watch mode as this promise will only trigger once and not for every rebuild - await bundler.bundle() - - await copyLib(); -} - -module.exports = { - compileParcel -} - -if (require.main === module) compileParcel() diff --git a/tools/parcel-watch.js b/tools/parcel-watch.js deleted file mode 100644 index 1eda750..0000000 --- a/tools/parcel-watch.js +++ /dev/null @@ -1,11 +0,0 @@ -const { compileParcel } = require('./parcel-build') - -async function watchParcel () { - return compileParcel({ watch: true }) -} - -module.exports = { - watchParcel -} - -if (require.main === module) watchParcel() diff --git a/tools/probe-boot.sh b/tools/probe-boot.sh index 27994e8..a534dd0 100755 --- a/tools/probe-boot.sh +++ b/tools/probe-boot.sh @@ -20,10 +20,10 @@ rm -f "$STATUS" "$DONE" "$SCREEN" pkill -f "windows95/node_modules/electron" 2>/dev/null || true sleep 1 -# build (parcel only — forge's generateAssets does this too but we want +# build (vite only — forge's generateAssets does this too but we want # direct control without the forge startup overhead) -rm -rf dist .cache -node tools/parcel-build.js > /tmp/win95-build.log 2>&1 +rm -rf dist +node tools/vite-build.js > /tmp/win95-build.log 2>&1 if [ $? -ne 0 ]; then echo "BUILD FAILED" tail -20 /tmp/win95-build.log diff --git a/tools/update-v86.js b/tools/update-v86.js index 6a8680a..c6a6f98 100644 --- a/tools/update-v86.js +++ b/tools/update-v86.js @@ -126,9 +126,9 @@ function sanity_check() { [/this\.master\.features_reg=\(this\.master\.features_reg/.test(js), 'libv86.js ide.js did not get the shared-register fix — is the windows95-base branch still in sync?'], - // Export pattern still shims the way parcel-build expects + // Export pattern still shims the way vite-build expects [js.includes('module.exports') && js.includes('window'), - 'libv86.js export pattern changed — check the runtime shim in parcel-build.js'], + 'libv86.js export pattern changed — check the runtime shim in vite-build.js'], // SMB integration needs the tcp-connection bus event (new API path in index.ts) [js.includes('tcp-connection'), diff --git a/tools/vite-build.js b/tools/vite-build.js new file mode 100644 index 0000000..c4ef3c2 --- /dev/null +++ b/tools/vite-build.js @@ -0,0 +1,113 @@ +const { build } = require('vite') +const { builtinModules } = require('module') +const path = require('path') +const fs = require('fs') + +const root = path.join(__dirname, '..') +const nodeExternals = ['electron', ...builtinModules, ...builtinModules.map(m => `node:${m}`)] + +// libv86 checks `typeof module.exports` before `typeof window` when deciding +// where to export V86. In an Electron renderer with nodeIntegration both exist, +// so it ends up on module.exports instead of window. This shim copies it over. +const LIBV86_SHIM = ` +` + +// v86's node-path file loader used `await import("node:...")` until d4c5fa86 +// switched it to require(). Dynamic import of node: URLs doesn't work in an +// Electron renderer — only require() does. The literals are stable across +// Closure builds; if they're absent the build is post-d4c5fa86 and already +// uses require, so a no-op is correct. +const V86_NODE_IMPORTS = [ + ['await import("node:fs/promises")', 'require("fs").promises'], + ['await import("node:"+"fs/promises")', 'require("fs").promises'], + ['await import("node:crypto")', 'require("crypto")'], +]; + +async function copyLib() { + const target = path.join(root, 'dist/static') + const lib = path.join(root, 'src/renderer/lib') + const indexSrc = path.join(root, 'static/index.html') + const indexOut = path.join(target, 'index.html') + + await fs.promises.cp(lib, target, { recursive: true }); + + const libv86path = path.join(target, 'libv86.js') + let libv86 = fs.readFileSync(libv86path, 'utf-8') + let patchCount = 0; + for (const [from, to] of V86_NODE_IMPORTS) { + const next = libv86.split(from).join(to); + if (next !== libv86) { patchCount++; libv86 = next; } + } + if (patchCount > 0) { + fs.writeFileSync(libv86path, libv86) + console.log(`libv86: ${patchCount} dynamic-import → require`) + } + + const indexContents = fs.readFileSync(indexSrc, 'utf-8'); + const replacedContents = indexContents.replace('', LIBV86_SHIM) + fs.writeFileSync(indexOut, replacedContents) +} + +function mainConfig(watch) { + return { + root, + configFile: false, + build: { + outDir: 'dist/src/main', + emptyOutDir: false, + minify: false, + sourcemap: false, + target: 'node22', + lib: { entry: 'src/main/main.ts', formats: ['cjs'], fileName: () => 'main.js' }, + rollupOptions: { + external: [...nodeExternals, 'electron-squirrel-startup', 'update-electron-app'], + }, + watch: watch ? {} : undefined, + }, + } +} + +function rendererConfig(watch) { + return { + root, + configFile: false, + build: { + outDir: 'dist', + emptyOutDir: false, + minify: false, + sourcemap: false, + target: 'es2023', + lib: { + entry: 'src/renderer/app.tsx', + formats: ['cjs'], + fileName: () => 'renderer.js', + cssFileName: 'renderer', + }, + rollupOptions: { + external: nodeExternals, + output: { + inlineDynamicImports: true, + assetFileNames: '[name][extname]', + // Electron renderer