Skip to content

fix(desktop): pin @std/http in generated Vite SPA entrypoint + add hermetic compile test#35676

Merged
crowlKats merged 3 commits into
mainfrom
orch/divybot-744
Jul 7, 2026
Merged

fix(desktop): pin @std/http in generated Vite SPA entrypoint + add hermetic compile test#35676
crowlKats merged 3 commits into
mainfrom
orch/divybot-744

Conversation

@divybot

@divybot divybot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary

The Vite SPA framework entrypoint that deno compile . / deno desktop .
generate imported jsr:@std/http/file-server without a version constraint.
A compiled desktop/standalone binary is meant to be a self-contained artifact,
so resolving an unpinned remote dependency at build time makes the output
non-reproducible and lets a future breaking @std/http release silently change
the app on a plain rebuild. This pins it to jsr:@std/http@^1.

It also adds the first end-to-end spec test for the Vite SPA branch. Until
now only the Vite SSR path had a compile spec test; the SPA path (which
generates the serveDir-based entrypoint) was covered only by unit tests that
assert the generated code string — it was never actually compiled. The new
tests/specs/compile/framework_detection/vite_spa test stays hermetic by
remapping the @std/http import to a local stub through the project's import
map, and verifies the whole pipeline: detect Vite → run the build task → resolve
the generated entrypoint's import → include dist/ → produce a binary.

Context / relation to #35590

While investigating the blank-window report in #35590, the root cause of the
empty webview DOM turned out to be the app:// in-process memory-channel
transport added in #35272 (the webview doesn't render that non-standard scheme
as a normal document) — that is being addressed by the transport revert #35670,
which restores TCP-loopback serving. I verified the generated serveDir SPA
entrypoint itself serves the built dist/ correctly over plain HTTP (/
index.html with #app, hashed JS/CSS assets with correct MIME types), so the
serving layer was never the problem.

This PR is the complementary hardening + regression coverage for that same Vite
desktop SPA path, independent of (and non-conflicting with) the transport
revert.

Testing

  • cargo test -p deno --lib framework — 61 passed (incl. the new pin assertion)
  • cargo test --test specs -- framework_detection — 5 passed, incl. the new
    vite_spa and the existing vite_ssr (no regression)

Refs #35590

Closes denoland/divybot#744

…rmetic compile test

The Vite SPA (`vite build` → static `dist/`) framework entrypoint generated by
`deno compile .` / `deno desktop .` imported `jsr:@std/http/file-server` without
a version constraint. A compiled desktop/standalone binary is meant to be a
self-contained artifact, so resolving an unpinned remote dependency at build
time makes the output non-reproducible and lets a future breaking `@std/http`
release change the app on a plain rebuild. Pin it to `jsr:@std/http@^1`.

Also add the first end-to-end spec test for the Vite SPA branch. Previously only
the Vite SSR path had a compile spec; the SPA path (which generates the
`serveDir`-based entrypoint) was covered by unit tests over the generated code
string only, never actually compiled. The new test keeps hermetic by remapping
the `@std/http` import to a local stub via the project's import map, and asserts
that `deno compile .` detects Vite, runs the build task, resolves the generated
entrypoint's import, includes `dist/`, and produces a binary.

Refs #35590

Co-Authored-By: Divy Srivastava <[email protected]>
@divybot
divybot marked this pull request as ready for review July 1, 2026 12:08
@divybot

divybot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

CI note: the only red job is test integration (1/2) debug windows-aarch64, which failed in integration::watcher::serve_watch_parallel_stops_old_workers with Output did not contain "..." after 60 seconds (watcher_tests.rs:116). That's a deno serve --watch output-timing flake on the slow Windows-aarch64 emulated runner — unrelated to this PR, which only touches cli/tools/framework.rs and adds a tests/specs/compile/framework_detection/vite_spa spec.

Everything that actually exercises this change is green on every platform: all test specs shards (the new vite_spa spec runs here), all test unit (the framework unit test), and all lint/lint title. Re-triggering CI to clear the flaky shard.

The only failing job was an unrelated `deno serve --watch` output-timing flake
(`integration::watcher::serve_watch_parallel_stops_old_workers`) on the
Windows-aarch64 runner; all specs/unit/lint jobs are green.

Co-Authored-By: Divy Srivastava <[email protected]>
@bartlomieju

Copy link
Copy Markdown
Member

One gap worth closing: unlike vite_ssr, the new vite_spa test stops at compiling the binary — it never executes it. I understand why (the generated SPA entrypoint blocks on Deno.serve, and serveDir is stubbed out, so there's nothing to assert and no way for a spec step to terminate), but that means the runtime half of the path — the binary actually booting and loading the generated entrypoint + remapped import out of the embedded VFS — is still uncovered, and that's exactly the layer where compile bugs have historically shown up.

There's a cheap way to get that coverage with the existing stub: since _file_server.ts is imported by the generated entrypoint at startup, its module top-level runs before Deno.serve is ever reached. Something like:

// _file_server.ts
console.log("file-server stub loaded");
if (Deno.env.get("EXIT_ON_LOAD")) Deno.exit(0);

export function serveDir(_req: Request): Promise<Response> {
  return Promise.resolve(new Response(null, { status: 404 }));
}

plus a second step per platform mirroring vite_ssr ("commandName": "./main", env EXIT_ON_LOAD: "1", asserting the stub's output). That verifies the compiled binary starts, resolves the import-map-remapped specifier from the VFS, and reaches the entrypoint — without hanging the harness.

Not a blocker, but since this test's whole purpose is end-to-end coverage of the SPA path, running the artifact it produces seems worth the extra step.

Address review feedback: the vite_spa test stopped at compiling the binary and
never ran it, leaving the runtime half — the binary booting and loading the
generated entrypoint + VFS-embedded remapped import — uncovered.

The generated SPA entrypoint imports the (stubbed) file-server module before it
reaches the blocking `Deno.serve`, so the stub's top level runs at startup. Make
it print a marker and, when `EXIT_ON_LOAD` is set, `Deno.exit(0)` before the
server starts. A new run step per platform boots `./main` with `EXIT_ON_LOAD=1`
and asserts the marker, proving the compiled binary starts and resolves the
import-map-remapped specifier out of the embedded VFS — without hanging the
harness on a live server.

Co-Authored-By: Divy Srivastava <[email protected]>
@divybot

divybot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor Author

Good call — done in 8fc2c65. The stub now logs file-server stub loaded at module top level and Deno.exit(0)s when EXIT_ON_LOAD is set (it's imported by the generated entrypoint before that entrypoint reaches Deno.serve, so it runs at startup). Added a run step per platform mirroring vite_ssrcommandName: ./main, envs: { EXIT_ON_LOAD: "1" }, asserting main.out.

That gives the runtime coverage you described: the compiled binary boots, resolves the import-map-remapped @std/http specifier out of the embedded VFS, and reaches the generated entrypoint — without hanging the harness on a live server.

Verified locally: cargo test --test specs -- framework_detection → 5 passed (both steps of vite_spa plus vite_ssr, no regression).

@crowlKats
crowlKats merged commit 816fda3 into main Jul 7, 2026
136 checks passed
@crowlKats
crowlKats deleted the orch/divybot-744 branch July 7, 2026 09:52
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.

3 participants