Skip to content

Commit 3ccdd36

Browse files
authored
Reduce App package sizes (#453)
## Summary - Trim Electron app packaging to only include runtime shell assets and remove unused workspace runtime dependencies. - Copy server resources explicitly, filtering stale build artifacts and pruning known non-runtime dependency files. - Bundle only the Node executable instead of the full Node distribution. ## Validation - Built macOS Electron artifacts with `npm run build:mac --workspace @neuralnomads/codenomad-electron-app`. - Ran packaged server CLI smoke test: `dist/bin.js --version` returned `0.16.0`. - Ran packaged dependency import smoke test for pruned runtime dependencies. ## Size Impact - macOS x64 zip: 161M - macOS arm64 zip: 161M - macOS arm64 app bundle: 435M - Packaged server node_modules: 25M
1 parent f019fcc commit 3ccdd36

10 files changed

Lines changed: 387 additions & 135 deletions

File tree

package-lock.json

Lines changed: 0 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/electron-app/electron.vite.config.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,9 @@ export default defineConfig({
8080
port: 3000,
8181
},
8282
build: {
83-
minify: false,
84-
cssMinify: false,
85-
sourcemap: true,
83+
minify: true,
84+
cssMinify: true,
85+
sourcemap: false,
8686
outDir: resolve(__dirname, "dist/renderer"),
8787
rollupOptions: {
8888
input: {

packages/electron-app/package.json

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,10 @@
3737
"build:all": "node scripts/build.js all",
3838
"package:mac": "node scripts/build.js mac",
3939
"package:win": "node scripts/build.js win",
40-
"package:linux": "node scripts/build.js linux"
40+
"package:linux": "node scripts/build.js linux",
41+
"smoke:resources": "node ../../scripts/smoke-packaged-resources.cjs --resources electron/resources --loading dist/renderer"
4142
},
4243
"dependencies": {
43-
"@neuralnomads/codenomad": "file:../server",
44-
"@codenomad/ui": "file:../ui",
4544
"yaml": "^2.4.2"
4645
},
4746
"devDependencies": {
@@ -66,7 +65,10 @@
6665
"buildResources": "electron/resources"
6766
},
6867
"files": [
69-
"dist/**/*",
68+
"dist/main/**/*",
69+
"dist/preload/**/*",
70+
"dist/renderer/loading.html",
71+
"dist/renderer/assets/**/*",
7072
"package.json"
7173
],
7274
"extraResources": [

packages/electron-app/scripts/build.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,20 @@ async function build(platform) {
141141
env: { NODE_PATH: workspaceNodeModulesPath, CODENOMAD_NODE_TARGET: job.nodeTarget },
142142
})
143143

144+
console.log(`\n🔎 Validating resources for ${job.nodeTarget}...\n`)
145+
await run(process.execPath, [
146+
join(workspaceRoot, "scripts", "smoke-packaged-resources.cjs"),
147+
"--resources",
148+
join(appDir, "electron", "resources"),
149+
"--loading",
150+
join(appDir, "dist", "renderer"),
151+
"--target",
152+
job.nodeTarget,
153+
], {
154+
cwd: workspaceRoot,
155+
shell: false,
156+
})
157+
144158
console.log(`\n📦 Packaging ${job.nodeTarget}...\n`)
145159
await run(npxCmd, ["electron-builder", "--publish=never", ...job.args], {
146160
env: { CODENOMAD_NODE_TARGET: job.nodeTarget },

packages/electron-app/scripts/prepare-resources.js

Lines changed: 2 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ const serverDest = join(resourcesRoot, "server")
1616
const npmExecPath = process.env.npm_execpath
1717
const npmNodeExecPath = process.env.npm_node_execpath
1818
const { prepareBundledNodeRuntime } = require(join(workspaceRoot, "scripts", "prepare-node-runtime.cjs"))
19+
const { copyPackagedServerResources } = require(join(workspaceRoot, "scripts", "desktop-server-resources.cjs"))
1920

20-
const serverSources = ["dist", "public", "node_modules", "package.json"]
2121
const serverDepsMarker = join(serverRoot, "node_modules", "fastify", "package.json")
2222

2323
function log(message) {
@@ -68,65 +68,10 @@ function ensureServerDependencies() {
6868
}
6969
}
7070

71-
function copyServerArtifacts() {
72-
fs.rmSync(serverDest, { recursive: true, force: true })
73-
fs.mkdirSync(serverDest, { recursive: true })
74-
75-
for (const name of serverSources) {
76-
const from = join(serverRoot, name)
77-
const to = join(serverDest, name)
78-
if (!fs.existsSync(from)) {
79-
throw new Error(`Missing required server artifact: ${from}`)
80-
}
81-
fs.cpSync(from, to, { recursive: true, dereference: true })
82-
log(`copied ${name} to Electron resources`)
83-
}
84-
}
85-
86-
function stripNodeModuleBins() {
87-
const root = join(serverDest, "node_modules")
88-
if (!fs.existsSync(root)) {
89-
return
90-
}
91-
92-
const stack = [root]
93-
let removed = 0
94-
95-
while (stack.length > 0) {
96-
const current = stack.pop()
97-
if (!current) break
98-
99-
let entries
100-
try {
101-
entries = fs.readdirSync(current, { withFileTypes: true })
102-
} catch {
103-
continue
104-
}
105-
106-
for (const entry of entries) {
107-
const full = join(current, entry.name)
108-
if (entry.name === ".bin") {
109-
fs.rmSync(full, { recursive: true, force: true })
110-
removed += 1
111-
continue
112-
}
113-
114-
if (entry.isDirectory()) {
115-
stack.push(full)
116-
}
117-
}
118-
}
119-
120-
if (removed > 0) {
121-
log(`removed ${removed} node_modules/.bin directories`)
122-
}
123-
}
124-
12571
async function main() {
12672
ensureServerBuild()
12773
ensureServerDependencies()
128-
copyServerArtifacts()
129-
stripNodeModuleBins()
74+
copyPackagedServerResources({ serverRoot, serverDest, log })
13075
await prepareBundledNodeRuntime({ resourcesRoot })
13176
}
13277

packages/tauri-app/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"sync:version": "node ./scripts/sync-tauri-version.js",
1212
"prebuild": "node ./scripts/prebuild.js",
1313
"bundle:server": "npm run prebuild",
14-
"build": "tauri build"
14+
"build": "tauri build",
15+
"smoke:resources": "node ../../scripts/smoke-packaged-resources.cjs --resources src-tauri/resources --loading src-tauri/resources/ui-loading"
1516
},
1617
"devDependencies": {
1718
"@tauri-apps/cli": "^2.9.4"

packages/tauri-app/scripts/prebuild.js

Lines changed: 13 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ const serverDest = path.resolve(root, "src-tauri", "resources", "server")
1313
const uiLoadingDest = path.resolve(root, "src-tauri", "resources", "ui-loading")
1414
const resourcesRoot = path.resolve(root, "src-tauri", "resources")
1515
const { prepareBundledNodeRuntime } = require(path.join(workspaceRoot, "scripts", "prepare-node-runtime.cjs"))
16-
17-
const sources = ["dist", "public", "node_modules", "package.json"]
16+
const { copyPackagedServerResources } = require(path.join(workspaceRoot, "scripts", "desktop-server-resources.cjs"))
1817

1918
const serverInstallCommand =
2019
"npm install --omit=dev --ignore-scripts --workspaces=false --package-lock=false --install-strategy=shallow --fund=false --audit=false"
@@ -248,60 +247,6 @@ function ensureEsbuildPlatformBinary() {
248247
})
249248
}
250249

251-
function copyServerArtifacts() {
252-
fs.rmSync(serverDest, { recursive: true, force: true })
253-
fs.mkdirSync(serverDest, { recursive: true })
254-
255-
for (const name of sources) {
256-
const from = path.join(serverRoot, name)
257-
const to = path.join(serverDest, name)
258-
if (!fs.existsSync(from)) {
259-
console.warn(`[prebuild] skipped missing ${from}`)
260-
continue
261-
}
262-
fs.cpSync(from, to, { recursive: true, dereference: true })
263-
console.log(`[prebuild] copied ${from} -> ${to}`)
264-
}
265-
}
266-
267-
function stripNodeModuleBins() {
268-
const root = path.join(serverDest, "node_modules")
269-
if (!fs.existsSync(root)) {
270-
return
271-
}
272-
273-
const stack = [root]
274-
let removed = 0
275-
276-
while (stack.length > 0) {
277-
const current = stack.pop()
278-
if (!current) break
279-
280-
let entries
281-
try {
282-
entries = fs.readdirSync(current, { withFileTypes: true })
283-
} catch {
284-
continue
285-
}
286-
287-
for (const entry of entries) {
288-
const full = path.join(current, entry.name)
289-
if (entry.name === ".bin") {
290-
fs.rmSync(full, { recursive: true, force: true })
291-
removed += 1
292-
continue
293-
}
294-
if (entry.isDirectory()) {
295-
stack.push(full)
296-
}
297-
}
298-
}
299-
300-
if (removed > 0) {
301-
console.log(`[prebuild] removed ${removed} node_modules/.bin directories`)
302-
}
303-
}
304-
305250
function copyUiLoadingAssets() {
306251
const loadingSource = path.join(uiDist, "loading.html")
307252
const assetsSource = path.join(uiDist, "assets")
@@ -332,10 +277,20 @@ function copyUiLoadingAssets() {
332277
ensureServerDependencies()
333278
ensureUiBuild()
334279
syncServerUiBundle()
335-
copyServerArtifacts()
336-
stripNodeModuleBins()
280+
copyPackagedServerResources({
281+
serverRoot,
282+
serverDest,
283+
log: (message) => console.log(`[prebuild] ${message}`),
284+
})
337285
copyUiLoadingAssets()
338286
await prepareBundledNodeRuntime({ resourcesRoot })
287+
execSync(
288+
`${JSON.stringify(process.execPath)} ${JSON.stringify(path.join(workspaceRoot, "scripts", "smoke-packaged-resources.cjs"))} --resources ${JSON.stringify(resourcesRoot)} --loading ${JSON.stringify(uiLoadingDest)}`,
289+
{
290+
cwd: workspaceRoot,
291+
stdio: "inherit",
292+
},
293+
)
339294
})().catch((err) => {
340295
console.error("[prebuild] failed:", err)
341296
process.exit(1)

0 commit comments

Comments
 (0)