Skip to content
Draft
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
21 changes: 21 additions & 0 deletions scripts/dev/tauri-dev-processes.cjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
const http = require("node:http");
const https = require("node:https");

const LIGHT_DEV_WEBPACK_MAX_OLD_SPACE_MIB = 1792;

function applyLightDevWebpackMemoryLimit(env, { lightDev = false } = {}) {
const nextEnv = { ...env };
if (!lightDev) return nextEnv;

const nodeOptions = nextEnv.NODE_OPTIONS?.trim() || "";
const hasExplicitLimit =
/(?:^|\s)--max[-_]old[-_]space[-_]size(?:=|\s)/u.test(nodeOptions);
if (hasExplicitLimit) return nextEnv;

nextEnv.NODE_OPTIONS = [
nodeOptions,
`--max-old-space-size=${LIGHT_DEV_WEBPACK_MAX_OLD_SPACE_MIB}`,
]
.filter(Boolean)
.join(" ");
return nextEnv;
}

function createTauriArgs({ features = [], devUrl } = {}) {
const args = ["dev"];
if (features.length > 0) {
Expand Down Expand Up @@ -138,6 +158,7 @@ async function waitForDevServerAsset(
}

module.exports = {
applyLightDevWebpackMemoryLimit,
createDevUrl,
createFrontendScriptName,
createTauriArgs,
Expand Down
38 changes: 36 additions & 2 deletions scripts/dev/tauri-dev-processes.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const fs = require("node:fs");
const test = require("node:test");

const {
applyLightDevWebpackMemoryLimit,
createDevUrl,
createTauriArgs,
formatElapsedMs,
Expand All @@ -12,6 +13,33 @@ const {
waitForDevServerAsset,
} = require("./tauri-dev-processes.cjs");

test("light webpack gets a bounded default heap without overriding user options", () => {
assert.deepEqual(
applyLightDevWebpackMemoryLimit(
{ NODE_OPTIONS: "--trace-warnings", KEEP: "yes" },
{ lightDev: true }
),
{
NODE_OPTIONS: "--trace-warnings --max-old-space-size=1792",
KEEP: "yes",
}
);
assert.deepEqual(
applyLightDevWebpackMemoryLimit(
{ NODE_OPTIONS: "--max_old_space_size=3072" },
{ lightDev: true }
),
{ NODE_OPTIONS: "--max_old_space_size=3072" }
);
assert.deepEqual(
applyLightDevWebpackMemoryLimit(
{ NODE_OPTIONS: "--trace-warnings" },
{ lightDev: false }
),
{ NODE_OPTIONS: "--trace-warnings" }
);
});

const tauriDevSource = fs.readFileSync("scripts/dev/tauri.js", "utf8");
const tauriLauncherSource = fs.readFileSync(
"scripts/dev/tauri-launcher.cjs",
Expand Down Expand Up @@ -81,8 +109,14 @@ test("tauri dev npm scripts use a cross-platform launcher", () => {
});

test("tauri dev launcher detaches Unix stdin without requiring setsid", () => {
assert.match(tauriLauncherSource, /detached:\s*process\.platform !== "win32"/);
assert.match(tauriLauncherSource, /stdio:\s*\["ignore",\s*"inherit",\s*"inherit"\]/);
assert.match(
tauriLauncherSource,
/detached:\s*process\.platform !== "win32"/
);
assert.match(
tauriLauncherSource,
/stdio:\s*\["ignore",\s*"inherit",\s*"inherit"\]/
);
assert.match(tauriLauncherSource, /env\.ORGII_LIGHT_DEV = "true"/);
assert.match(tauriLauncherSource, /SIGINT:\s*130/);
assert.match(tauriLauncherSource, /SIGTERM:\s*143/);
Expand Down
8 changes: 4 additions & 4 deletions scripts/dev/tauri.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const {
applyDefaultDiagnosticsEndpoint,
} = require("../tauri/diagnostics-endpoint.cjs");
const {
applyLightDevWebpackMemoryLimit,
createDevUrl,
createFrontendScriptName,
createTauriArgs,
Expand Down Expand Up @@ -494,9 +495,8 @@ function pipeProcessLines(childProcess, onLine) {
}

function startFrontendDev() {
const scriptName = createFrontendScriptName({
lightDev: process.env.ORGII_LIGHT_DEV === "true",
});
const lightDev = process.env.ORGII_LIGHT_DEV === "true";
const scriptName = createFrontendScriptName({ lightDev });
const pnpmCli = createPnpmCliCommand();

setWebpackStatus("starting dev server...");
Expand All @@ -508,7 +508,7 @@ function startFrontendDev() {
{
stdio: ["ignore", "pipe", "pipe"],
cwd: rootDir,
env: cleanChildEnv(),
env: applyLightDevWebpackMemoryLimit(cleanChildEnv(), { lightDev }),
}
);

Expand Down
44 changes: 44 additions & 0 deletions scripts/dev/webpack-config-light.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,50 @@ test("light dev disables webpack dev-server browser client", () => {
assert.equal(htmlPlugin?.userOptions?.retryMainScriptLoad, false);
});

test("light development cache stays memory-only and bounded", () => {
const config = withEnv(
{
ORGII_LIGHT_DEV: "true",
FAST_DEV: "true",
DEV_SOURCEMAPS: "false",
},
() => createWebpackConfig({}, { mode: "development" })
);

assert.deepEqual(config.cache, {
type: "memory",
maxGenerations: 1,
});
});

test("standard development keeps its filesystem cache", () => {
const config = withEnv(
{
ORGII_LIGHT_DEV: null,
FAST_DEV: null,
DEV_SOURCEMAPS: null,
},
() => createWebpackConfig({}, { mode: "development" })
);

assert.equal(config.cache.type, "filesystem");
assert.equal(config.cache.version, "dev-11");
});

test("production keeps its isolated filesystem cache", () => {
for (const fastProd of [false, true]) {
const config = withEnv(
{
FAST_PROD: fastProd ? "true" : null,
},
() => createWebpackConfig({}, { mode: "production" })
);

assert.equal(config.cache.type, "filesystem");
assert.equal(config.cache.version, `${fastProd ? "prod-fast" : "prod"}-11`);
}
});

test("retrying main script loader disables static HTML injection in dev", () => {
const config = withEnv(
{
Expand Down
45 changes: 27 additions & 18 deletions webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,24 +62,33 @@ module.exports = (env, argv) => {
chunkFilename: isProduction ? "[name].[contenthash].js" : "[name].js",
clean: true,
},
cache: {
type: "filesystem",
// FAST_PROD swaps both the transpiler and minimizer. Keep it in a
// separate filesystem-cache namespace so webpack cannot reuse cached
// runtime-condition code generated by the regular production pipeline.
// Mixing those caches can leave async chunks guarded by a stale runtime
// id (for example `__webpack_require__.j == 9121` while the emitted
// runtime id is `49121`), which turns otherwise valid imports into
// `undefined` only in the packaged app.
version: `${
isProduction ? (useFastProd ? "prod-fast" : "prod") : "dev"
}-11`,
buildDependencies: {
config: [__filename],
},
// Don't compress - avoids sass serialization issues
compression: false,
},
cache: isLightDev
? {
// Webpack's filesystem cache can serialize gigabyte-scale pack files
// for this app and raises the light-dev rebuild RSS ceiling. Retain
// only one unused generation and never persist the light-mode
// compilation graph across restarts.
type: "memory",
maxGenerations: 1,
}
: {
type: "filesystem",
// FAST_PROD swaps both the transpiler and minimizer. Keep it in a
// separate filesystem-cache namespace so webpack cannot reuse cached
// runtime-condition code generated by the regular production pipeline.
// Mixing those caches can leave async chunks guarded by a stale runtime
// id (for example `__webpack_require__.j == 9121` while the emitted
// runtime id is `49121`), which turns otherwise valid imports into
// `undefined` only in the packaged app.
version: isProduction
? `${useFastProd ? "prod-fast" : "prod"}-11`
: "dev-11",
buildDependencies: {
config: [__filename],
},
// Don't compress - avoids sass serialization issues
compression: false,
},
// Snapshot: use timestamps for node_modules instead of content hashing.
// node_modules rarely change during a dev session; timestamp checks are much faster.
snapshot: {
Expand Down
Loading