mirror of
https://github.com/felixrieseberg/windows95.git
synced 2026-05-09 00:24:09 +00:00
Replace Parcel 1 with Vite (#353)
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 <script> context provides module/require but not a bare exports
global. CSS (98.css + root.css) is now imported from app.tsx so Vite
emits a single renderer.css with fonts inlined.
Drops parcel-bundler and rimraf (vite-build clears dist/ itself).
~800ms full build.
This commit is contained in:
committed by
Felix Rieseberg
parent
148f8e4874
commit
9b217731f5
12021
package-lock.json
generated
12021
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@
|
||||
"description": "Windows 95, in an app. I'm sorry.",
|
||||
"main": "./dist/src/main/main.js",
|
||||
"scripts": {
|
||||
"start": "rimraf ./dist && electron-forge start",
|
||||
"start": "electron-forge start",
|
||||
"package": "electron-forge package",
|
||||
"make": "electron-forge make",
|
||||
"publish": "electron-forge publish",
|
||||
@@ -39,10 +39,9 @@
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"dotenv": "^17.3.1",
|
||||
"electron": "41.2.0",
|
||||
"parcel-bundler": "^1.12.5",
|
||||
"patch-package": "^8.0.1",
|
||||
"prettier": "^3.8.1",
|
||||
"rimraf": "^6.1.3",
|
||||
"typescript": "^6.0.2"
|
||||
"typescript": "^6.0.2",
|
||||
"vite": "^7.3.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import "../css/vendor/98.css";
|
||||
import "../css/root.css";
|
||||
|
||||
export interface Win95Window extends Window {
|
||||
emulator: any;
|
||||
win95: {
|
||||
|
||||
1
src/renderer/styles.d.ts
vendored
Normal file
1
src/renderer/styles.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
declare module "*.css";
|
||||
@@ -5,12 +5,11 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>windows95</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="../src/css/vendor/98.css">
|
||||
<link rel="stylesheet" href="../src/css/root.css">
|
||||
<link rel="stylesheet" href="../renderer.css">
|
||||
<!-- libv86 -->
|
||||
</head>
|
||||
<body class="paused windows95">
|
||||
<div id="app"></div>
|
||||
<script src="../src/renderer/app.tsx"></script>
|
||||
<script src="../renderer.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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 = `<script src="libv86.js"></script>
|
||||
<script>if (typeof module !== "undefined" && module.exports && module.exports.V86) window.V86 = module.exports.V86;</script>`
|
||||
|
||||
// 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 -->', 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()
|
||||
@@ -1,11 +0,0 @@
|
||||
const { compileParcel } = require('./parcel-build')
|
||||
|
||||
async function watchParcel () {
|
||||
return compileParcel({ watch: true })
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
watchParcel
|
||||
}
|
||||
|
||||
if (require.main === module) watchParcel()
|
||||
@@ -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
|
||||
|
||||
@@ -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'),
|
||||
|
||||
113
tools/vite-build.js
Normal file
113
tools/vite-build.js
Normal file
@@ -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 = `<script src="libv86.js"></script>
|
||||
<script>if (typeof module !== "undefined" && module.exports && module.exports.V86) window.V86 = module.exports.V86;</script>`
|
||||
|
||||
// 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 -->', 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 <script> with nodeIntegration has module/require
|
||||
// but not a bare `exports` global; alias it so Rollup's CJS prelude works.
|
||||
banner: 'var exports = module.exports;',
|
||||
},
|
||||
},
|
||||
watch: watch ? {} : undefined,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
async function compileVite(options = {}) {
|
||||
if (!options.watch) {
|
||||
await fs.promises.rm(path.join(root, 'dist'), { recursive: true, force: true })
|
||||
}
|
||||
await fs.promises.mkdir(path.join(root, 'dist/static'), { recursive: true })
|
||||
await copyLib()
|
||||
await build(mainConfig(options.watch))
|
||||
await build(rendererConfig(options.watch))
|
||||
}
|
||||
|
||||
module.exports = { compileVite }
|
||||
|
||||
if (require.main === module) compileVite()
|
||||
3
tools/vite-watch.js
Normal file
3
tools/vite-watch.js
Normal file
@@ -0,0 +1,3 @@
|
||||
const { compileVite } = require('./vite-build')
|
||||
|
||||
if (require.main === module) compileVite({ watch: true })
|
||||
Reference in New Issue
Block a user