Skip to content

Shrink install footprint: strip prebuilts (-90%) + drop unused deps#485

Merged
romgrk merged 2 commits into
masterfrom
startup-deps-strip
Jul 4, 2026
Merged

Shrink install footprint: strip prebuilts (-90%) + drop unused deps#485
romgrk merged 2 commits into
masterfrom
startup-deps-strip

Conversation

@romgrk

@romgrk romgrk commented Jul 4, 2026

Copy link
Copy Markdown
Owner

Context

Investigated startup time for the hello-world example. Key finding: node-gtk's controllable startup is only ~55ms of the ~385ms real hello-world startup — the rest is Node itself (~17ms), GTK/libadwaita init (~55ms), and first-frame/compositor rendering (~180ms). The binding layer is already well-optimized by the prior lazy-GIR work, and the one thing that looked like free time — the ~31ms of eager GType registration in GetInfoEntries — is load-bearing: skipping it made real startup ~20ms slower (it just moves the cost onto widget construction). So there's no meaningful startup win left to take there.

What the investigation did surface are two real footprint wins, which is what this PR does. Neither changes runtime behavior or measurably affects startup — they reduce install/download size and dependency surface.

1. Strip prebuilt binaries (ci: …)

The Linux Release build ships unstripped with full DWARF (binding.gyp passes -g so local crashes stay debuggable), so the published addon is ~5.1 MB. This strips the packaged copy in publish() — only that copy, right before node-pre-gyp package — so local npm run build keeps its symbols while the prebuilt users download shrinks:

before after
.node binary 5,096,472 B 537,600 B (−89%)
download (.tar.gz) 1,885,792 B 163,225 B (−91%)

DWARF lives in non-loaded .debug_* sections, so this is a pure download/install win — dlopen is unchanged (~2.5→2.2ms). A post-strip smoke test loads the stripped addon so a bad strip aborts the publish instead of shipping something unloadable.

Scope note: only Linux/macOS (scripts/ci.sh). The Windows build is MSVC (debug info → separate .pdb, not shipped) and bundles the full GTK runtime, so the .node strip there is negligible — left as a possible follow-up.

2. Drop unused runtime dependencies (build: …)

Verified by grepping every require() across lib/, bin/, src/, tools/, tests/:

  • unindent — entirely unused; the cairo generators use a local src/modules/cairo/indent.js, not this package. Removed.
  • lodash.isequal — only the test harness used it. Replaced with node:util's built-in isDeepStrictEqual. Removed.
  • remove-trailing-spaces — only the build-time cairo code generators use it (and their output is committed). Moved to devDependencies.

Removes 3 packages (+ transitive) from every consumer install, zero runtime behavior change. (Only lodash.camelcase/lodash.snakecase actually load at startup — ~1.3ms combined — so deps were never a startup factor.)

Verification

  • Full suite: 96 passing. The single require.js failure ("some modules failed to load") is pre-existing and environment-specific (system Peas/PeasGtk typelibs) — reproduces identically on clean master, unrelated to these changes.
  • isDeepStrictEqual swap: all expect()-based tests pass.
  • Strip: built from source → stripped → addon still loads (Gtk.Box/Window resolve) → node-pre-gyp package succeeds; tarball 11.5× smaller.
  • bash -n scripts/ci.sh clean; strip_binaries verified idempotent with smoke test.

🤖 Generated with Claude Code

romgrk and others added 2 commits July 4, 2026 16:50
The Linux Release build embeds full DWARF debug info (binding.gyp passes
-g so local `npm run build` stays debuggable), leaving the published
addon unstripped at ~5.1 MB. Strip the packaged copy in publish(), right
before `node-pre-gyp package` archives it — local builds keep their
symbols, but the prebuilt users download shrinks dramatically:

  binary:   5,096,472 -> 537,600 bytes  (-89%)
  download: 1,885,792 ->  163,225 bytes  (-91%, tar.gz)

A post-strip smoke test loads the stripped addon so a bad strip aborts
the publish instead of shipping an unloadable binary.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
None of these are needed at runtime by lib/:
- unindent: entirely unused (the cairo generators use a local
  src/modules/cairo/indent.js, not this package)
- lodash.isequal: only used by the test harness — replaced with
  node:util's isDeepStrictEqual (built in)
- remove-trailing-spaces: only used by the build-time cairo code
  generators (whose output is committed) — moved to devDependencies

Shrinks what every consumer installs; no runtime behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@romgrk
romgrk merged commit d49dc1f into master Jul 4, 2026
9 checks passed
@romgrk
romgrk deleted the startup-deps-strip branch July 4, 2026 21:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant