Skip to content

perf(website/home): defer WASM load until user intent; SW only on /playground - #701

Merged
ajitpratap0 merged 2 commits into
mainfrom
perf/defer-wasm
Oct 14, 2026
Merged

ajitpratap0 merged 2 commits into
mainfrom
perf/defer-wasm

Conversation

@ajitpratap0

Copy link
Copy Markdown
Owner

Summary

  • useWasm(enabled) takes an opt-in flag; MiniPlayground enables it only on focus/pointer-down inside the demo (incl. tab/dialect changes). Until then it shows the precomputed sample AST and a 'Sample output' label.
  • ServiceWorkerRegister moved from the root layout to /playground.
  • fix(website): always ship and serve a current playground WASM #543 no-cache design untouched.

Test Plan

  • website: typecheck, lint (0 errors), build pass
  • Built home HTML has no gosqlx.wasm reference
  • Manual: DevTools network on / shows 0 WASM bytes before interaction; focusing the demo loads WASM once; /playground/ still loads WASM and registers the SW

Breaking Changes

None.

Closes #649
Part of #634

🤖 Generated with Claude Code

https://claude.ai/code/session_01RkzVu3JY9Y6BRWfjesWts9

@vercel

vercel Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
gosqlx-website Ready Ready Preview Oct 14, 2026 4:46pm UTC

@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Claude finished @ajitpratap0's task in 23s —— View job


Review of PR #701: defer WASM load until user intent

The change is small and does what the PR describes. I found no blocking issues and made no code changes. I only read the diff and the surrounding MiniPlayground.tsx. I didn't run the build, typecheck or lint, and I didn't do the manual DevTools check.

What looks good

  • useWasm(enabled = true) defaults to enabled, so existing callers such as the playground keep their behavior.
  • onFocusCapture and onPointerDownCapture on the wrapper cover the textarea, the tabs and the dialect select. The useCallback handler is stable, and calling setState(true) repeatedly is a no-op.
  • Moving ServiceWorkerRegister to /playground matches the goal of keeping the home page lean.
  • Per the PR, the no-cache design from fix(website): always ship and serve a current playground WASM #543 is untouched.

Points to consider

  1. Placeholder fallback (MiniPlayground.tsx:216). The old fallback only showed the sample AST while loading. The new activeTab === 'ast' && !ready also shows it before WASM is requested, which is the intent. It also shows it if WASM fails to load. In that case error from useWasm isn't destructured, so the user sees the sample AST and "Loading..." forever, or an empty label. This was already the case, but it's more visible now that the sample is the default state. Consider handling error and showing a fallback message.
  2. Stale sample output. Before WASM loads, the left pane is editable but the right pane still shows the sample AST for the original SQL. This is fine while the label says "Sample output". The label changes to "Loading..." as soon as focus lands, but the stale sample stays up until parsing finishes, so the output can briefly look as if it belongs to edited SQL. That's minor.
  3. Format and Lint tabs before load. If the user clicks the Format or Lint tab first, the pointer-down starts the load. The panel is empty until WASM is ready, because the placeholder only applies to the AST tab. This is acceptable, but a "Loading…" hint in the panel would be friendlier.
  4. useWasm effect (WasmLoader.tsx:184). initialized is a ref that never resets, which is fine here because the flag only goes false to true. setLoading(true) inside the effect is redundant on first mount when enabled is already true, but it's harmless. Under React StrictMode in dev, the effect's cleanup runs and the re-run exits early because initialized.current is true. That leaves the progress listener unregistered. This predates the PR, but it can make the progress display look stuck in dev.
  5. Keyboard and touch. Keyboard tab-in and touch both trigger focus or pointerdown, so intent detection works for them. Hover alone doesn't trigger a load, which is a reasonable choice. Prefetching on hover or requestIdleCallback could hide the latency if you want faster first interaction.
  6. Tests. There's no automated test for the deferral. A simple test that fetch isn't called for the wasm file before a focus event would guard against regressions. The "no gosqlx.wasm in built home HTML" check from the PR is useful, and could be scripted in CI.

Security and performance

  • No security concerns.
  • Performance should improve: the home page no longer fetches the WASM binary or registers a service worker before the user interacts.

Remaining manual check: the unchecked item in the test plan (DevTools network on /, and /playground/ still loading WASM and registering the SW).
· branch perf/defer-wasm

@ajitpratap0

Copy link
Copy Markdown
Owner Author

Fixed the placeholder: the hand-written AST is gone. The AST panel now shows real output from the current parser for the sample query, with the same shape wasm/main.go returns ({"Statements":[{"With":null,...}]}). It is generated by website/scripts/gen-sample-ast (Go) from sample.sql, run as part of build-wasm.sh and committed at website/src/generated/sample-ast.json (the query text comes from the same file, so SQL and AST cannot drift). Output is the full AST, not trimmed; label now reads 'Precomputed sample output'. typecheck, lint (warnings only, pre-existing) and build pass.

@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Claude finished @ajitpratap0's task in 23s —— View job


Review of PR #701 (after the precomputed-AST update)

I read the diff only. I didn't run the build, typecheck, lint or the manual DevTools check. I found no blocking issues.

What the update fixes

  • The hand-written placeholder AST is gone. sample.sql is now the single source for both the SQL and the AST, and the AST comes from the real parser through gen-sample-ast. Because both come from one generated file, they can't drift apart.
  • The label is now "Precomputed sample output" until the user shows intent. This is more honest than before.
  • useWasm(enabled) defaults to true, so the playground's behavior is unchanged. Calling setLoading(true) inside the effect makes the loading state correct when enabled flips later.
  • ServiceWorkerRegister now lives only on /playground, and the fix(website): always ship and serve a current playground WASM #543 no-cache design is untouched.

Points to consider

  1. Nothing catches a stale sample-ast.json. The file is regenerated only when build-wasm.sh runs. If the AST shape changes and someone commits without rerunning it, the homepage shows an out-of-date AST. The repo's metrics PR (feat(website): metrics single source of truth and drift check #696) added a drift check. Doing the same here would work: regenerate in CI and run git diff --exit-code website/src/generated/. A small Go test that parses sample.sql and compares it to the JSON would also work.
  2. Bundle size. import sample from '@/generated/sample-ast.json' puts the full 287-line AST (including all the null fields) into the client bundle for /. That is likely a few KB gzipped, which is small next to the WASM savings. Trimming it or lazy-loading it would reduce this, but it's optional.
  3. Failure state (MiniPlayground.tsx:~186). If WASM fails after the user interacts, error still isn't read. The label shows "Loading..." or an empty state, and the sample AST stays on screen. I suggest showing an error label and keeping the sample visible as a fallback.
  4. Stale sample after edits. Before WASM is ready, the sample AST is shown even if the user has edited the SQL. The label changes to "Loading..." on focus, but the AST stays up until parsing finishes. It might help to dim it or label it "Sample (for original query)" once sql !== SAMPLE_SQL.
  5. Format and Lint tabs before load. The panel is empty until WASM is ready. A "Loading…" hint there would be nicer.
  6. Capture handlers. Clicking the "Open in playground" link inside the wrapper also triggers a WASM load just before navigating. This is harmless but wasteful, so you could skip it for that link.
  7. useWasm cleanup. The cleanup unregisters the progress listener, but initialized never resets. Under React StrictMode in dev, the effect re-run returns early and leaves the progress listener unregistered. This predates the PR.
  8. build-wasm.sh. It now needs go run from $REPO_ROOT. Since the script already requires Go to build the WASM, this is fine. Make sure the Vercel build environment runs this script, or that the committed JSON is the fallback.
  9. Tests. There is still no automated check that the homepage doesn't fetch gosqlx.wasm before interaction. The "no gosqlx.wasm in built home HTML" check is a good candidate to script in CI.

Security and performance

  • No security concerns. The generator reads a fixed repo file and writes a fixed output path.
  • Performance should improve. The homepage no longer fetches the WASM binary or registers a service worker before interaction.

Remaining manual check: the unchecked test-plan item, which is DevTools network on / and /playground/ still loading WASM and registering the SW.
· branch perf/defer-wasm

Ajit Pratap Singh and others added 2 commits October 14, 2026 23:23
…ly on /playground

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RkzVu3JY9Y6BRWfjesWts9
Replace the hand-written AST placeholder with JSON generated from the
current parser for the sample query (scripts/gen-sample-ast, run by
build-wasm.sh), so the pre-interaction panel matches real output.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RkzVu3JY9Y6BRWfjesWts9
@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Claude finished @ajitpratap0's task in 19s —— View job


Review of PR #701 (latest push)

I read the diff only. I didn't run the build, typecheck or lint, and I didn't do the manual DevTools check. I found no blocking issues.

What works

  • useWasm(enabled = true) keeps the playground's behavior unchanged. The effect returns early while enabled is false and registers the progress listener only once WASM is requested, so the cleanup logic is still correct.
  • onFocusCapture and onPointerDownCapture on the wrapper cover the textarea, the tabs and the dialect select. They also cover keyboard tab-in and touch.
  • sample.sql is now the single source for both the query and the AST. gen-sample-ast handles errors cleanly and uses the same encoding/json output as wasm/main.go.
  • ServiceWorkerRegister now lives only on /playground.

Points to consider

  1. Nothing catches a stale sample-ast.json. It is regenerated only when build-wasm.sh runs. If the AST structs change and someone commits without rerunning it, the homepage shows an outdated AST. You could add a Go test that parses sample.sql and compares the result to the JSON. A CI step that regenerates the file and runs git diff --exit-code website/src/generated/ would also work.
  2. Check the Vercel build. Confirm Vercel's build runs build-wasm.sh, or that the committed JSON is the fallback. Otherwise a stale JSON ships silently.
  3. Error state (MiniPlayground.tsx). error from useWasm is still not read. If the WASM load fails after the user interacts, the label stays on "Loading..." or goes empty, while the sample AST stays on screen. I suggest showing an error label.
  4. Stale sample after edits. Once sql !== SAMPLE_SQL and WASM isn't ready, the sample AST still shows for the original query. The label changes to "Loading..." on focus, so this is minor. You could dim the sample or relabel it as "Sample (original query)".
  5. Format and Lint tabs before load. The panel is empty until WASM is ready. A "Loading…" hint there would be friendlier.
  6. Bundle size. The import puts the full 287-line AST, with its null fields, into the client chunk for /. That is small next to the WASM savings. It could be trimmed or lazy-loaded if you care.
  7. Unneeded loads. Clicking the "Open in playground" link inside the wrapper also triggers a WASM load right before navigating. This is harmless.
  8. useWasm and StrictMode. initialized never resets, so under React StrictMode in dev the effect re-run exits early and leaves the progress listener unregistered. This predates the PR and only affects the dev progress display.
  9. Tests. There is no automated check that the homepage doesn't fetch gosqlx.wasm before interaction. The "no gosqlx.wasm in built home HTML" check from the PR is a good candidate to script in CI.

Security and performance

  • No security concerns. The generator reads a fixed repo file and writes a fixed output path.
  • Performance should improve. The homepage no longer fetches the WASM binary or registers a service worker before interaction.

Remaining manual check: the unchecked test-plan item. That means DevTools network on / showing no WASM bytes before interaction, and /playground/ still loading WASM and registering the SW.
· branch perf/defer-wasm

@ajitpratap0
ajitpratap0 merged commit 20cd6ac into main Oct 14, 2026
28 checks passed
@ajitpratap0
ajitpratap0 deleted the perf/defer-wasm branch October 14, 2026 18:03
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.

perf(website/home): defer WASM load until user intent; SW only on /playground [1.9]

1 participant