Skip to content

fix(compositor): PR #170's asset-path fix was a no-op in the packaged app — bypass asar-transparent fs#171

Merged
EtienneLescot merged 1 commit into
feat/ai-editionfrom
claude/asar-existsSync-fastfollow
Jul 27, 2026
Merged

fix(compositor): PR #170's asset-path fix was a no-op in the packaged app — bypass asar-transparent fs#171
EtienneLescot merged 1 commit into
feat/ai-editionfrom
claude/asar-existsSync-fastfollow

Conversation

@EtienneLescot

@EtienneLescot EtienneLescot commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Fast-follow on #170 (already merged). Rebuilt and relaunched the packaged app to visually confirm that fix, and the exact same bug came right back: a bundled wallpaper still rendered as a flat colour, a themed cursor still fell back to default art, and the main-process log still showed

[compositor] wallpaper image "...\resources\app.asar\dist\wallpapers\wallpaper5.jpg" : ... Le chemin d'accès spécifié est introuvable. (os error 3)

— the exact path #170 was supposed to stop sending.

Root cause

resolveSceneAssetPath's candidate-probe loop used plain fs.existsSync. Electron patches fs to read through an asar archive transparently — from JS's point of view, .../app.asar/dist/wallpapers/wallpaper5.jpg "exists". So the probe locked onto the first candidate (VITE_PUBLIC, pointing into the asar) on every single call and never reached the second, real one (resourcesPath). #170's fix compiled correctly, shipped correctly, and never actually resolved anything, because the existence check itself was lying.

Confirmed the mechanism directly rather than guessing: ran a probe script inside the packaged binary via ELECTRON_RUN_AS_NODE=1.

patched fs.existsSync (asar-transparent) on asar path: true
original-fs.existsSync on asar path:                   false
original-fs.existsSync on real resourcesPath:           true

The native addon calls raw fopen/CreateFile with no concept of asar, so it agrees with original-fs, not with patched fs — which is exactly why the wrong candidate kept winning.

Fix

original-fs is Electron's own escape hatch for precisely this: same API, unpatched. Route the existence check through it, falling back to plain fs where original-fs doesn't exist — outside Electron, i.e. the vitest suite, where plain Node's fs was never patched in the first place, so no fallback behavior changes.

function realExistsSync(candidate: string): boolean {
	try {
		return (localRequire("original-fs") as typeof fs).existsSync(candidate);
	} catch {
		return fs.existsSync(candidate);
	}
}

Verification

  • Rebuilt (tsc && vite build && electron-builder, native addon unchanged), grepped the packaged app.asar bytes directly to confirm realExistsSync/original-fs actually compiled in.
  • Relaunched the packaged app against the same project that reproduced the original bug report. Confirmed live:
    • The bundled wallpaper now renders (padding/roundness/shadow all visible around it) instead of falling back to a flat colour.
    • Switching cursor themes now visibly changes the rendered sprite on the recording.
    • The [compositor] wallpaper image ... introuvable log line is gone.
  • tsc --noEmit clean, biome clean, existing 15 tests in compositorViewService.test.ts still pass (they run under plain Node, so they exercise the fs fallback branch, not the asar-bypass itself — that half is Electron-runtime-specific and is what the live verification above covers).

Note for review

I did not add a mocked unit test simulating Electron's asar-fs patching — a test that only asserts localRequire("original-fs") gets called would be closer to tautological than to a real regression guard, since the actual failure mode (patched fs.existsSync lying about an asar-internal path) can't be faithfully reproduced outside a real Electron + real asar. The live verification above is the real check for this one.

…or real asset files

`resolveSceneAssetPath`'s candidate-probe loop (from the previous commit) used
plain `fs.existsSync`, which Electron patches to read *through* an asar
archive — from JS's point of view, `.../app.asar/dist/wallpapers/x.jpg`
"exists". So the probe locked onto the VITE_PUBLIC candidate (pointing into
the asar) on every call and never reached the real `resourcesPath` files,
making the previous fix a no-op in the actual packaged app: rebuilt and
relaunched it, and the exact same "wallpaper image ... introuvable" errors
came right back.

Confirmed the mechanism by running a probe script inside the packaged binary
via `ELECTRON_RUN_AS_NODE=1`: patched `fs.existsSync` on the asar-internal
path answered `true`; the native addon (raw `fopen`/`CreateFile`, no
knowledge of asar) still gets ENOENT on that same path.

`original-fs` is Electron's own escape hatch for this — same API, unpatched.
Route the existence check through it, falling back to plain `fs` where
`original-fs` doesn't exist (outside Electron, i.e. the vitest suite), since
plain Node's `fs` was never patched in the first place there.

Re-verified live after rebuilding: the same project's bundled wallpaper and
themed cursor sprite now render in the packaged app, and the
"introuvable"/ENOENT log lines are gone.
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bf6cfd59-ed5c-47fa-b21e-5bc0ab1417b9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/asar-existsSync-fastfollow

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@EtienneLescot
EtienneLescot merged commit e0c06ce into feat/ai-edition Jul 27, 2026
8 checks passed
@EtienneLescot
EtienneLescot deleted the claude/asar-existsSync-fastfollow branch July 27, 2026 07:38
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