Skip to content

feat(appkit): report type errors from the dev server - #715

Open
fjakobs wants to merge 1 commit into
mainfrom
fjakobs/la-2186-typecheck-in-build-loop
Open

fjakobs wants to merge 1 commit into
mainfrom
fjakobs/la-2186-typecheck-in-build-loop

Conversation

@fjakobs

@fjakobs fjakobs commented Nov 6, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

In development, the AppKit server builds the app with Vite (client) and tsx watch (server). Both only strip types. An app with type errors still renders in the dev preview, HMR applies the edits, and the server even runs the badly typed code. The dev log says nothing. The errors first appear when databricks apps validate or databricks apps deploy runs the app's typecheck script.

A developer, or a coding agent working in the dev loop, gets no signal until they ask to deploy. Then they have to fix, re-verify and deploy again.

A second gap made this worse: the template's typecheck script was tsc -b tsconfig.server.json && tsc -b tsconfig.client.json. When the server check failed, the && stopped the run, so client errors were never printed. Fixing the server errors and deploying again only revealed the client errors on the next attempt.

Fix

  • Dev server reports type errors. A new Vite plugin, packages/appkit/src/plugins/server/typecheck-vite-plugin.ts, is registered in ViteDevServer and only runs in development. It runs the app's own typecheck script, the same check deployment validation runs, in the background:

    • once at startup (tsx watch restarts the process on server edits, so this covers server changes)
    • again after .ts/.tsx/.mts/.cts changes seen by Vite's watcher, debounced. The watcher also covers shared/, so regenerated appkit-types re-trigger the check.
    • at most one run at a time: a change during a run schedules one more run, and the stale result is not reported
    • errors are logged as [appkit:server:typecheck] Type errors found. The app still runs, but deployment validation fails until they are fixed: followed by the tsc output. No type errors. is logged on the first clean run and after errors are fixed.
    • the child process is stopped when the Vite server closes
    • it does nothing when package.json has no typecheck script

    It never blocks startup or requests, so the preview behaves exactly as before.

  • Template: typecheck is now tsc -b. The root tsconfig.json already references both projects, so one run reports client and server errors together.

  • Docs: local-development.mdx explains the background check. The LLM checklist in llm-guide.md, which ships in the package's CLAUDE.md/llms.txt, now says the typecheck script must pass and that the dev server logs type errors.

Tests

packages/appkit/src/plugins/server/tests/typecheck-vite-plugin.test.ts uses a real temp project with a small stand-in typecheck script and a fake watcher. It covers:

  • the plugin is not applied without a typecheck script
  • failures are logged with the script's output, then "No type errors." after a fix and a .tsx change
  • non-TypeScript changes don't trigger a run
  • closing the server stops an in-flight run without reporting it

Verification

The repro app was scaffolded from this template with databricks apps init --template ./template, plus one type error in client/src/App.tsx and one in server/server.ts.

Before (published AppKit):

  • pnpm run dev: / returns 200 and the server runs the bad value. The dev log has no type errors, including after edits.
  • databricks apps deploy against a Databricks workspace fails validation and reports only the server error.

After (this branch, packed and installed into the same app):

  • pnpm run dev logs both errors about a second after startup, and / still returns 200.
  • Fixing the client error leaves only the server error. Fixing the server error makes tsx restart, and the log says No type errors. Breaking the client again brings the error back.
  • databricks apps validate, and databricks apps deploy against a Databricks workspace, report both client and server errors in one run.
  • After shutdown, no tsc processes are left behind.
  • With the errors fixed, the app (with this build of AppKit) deployed to the workspace: the deployment SUCCEEDED, the app is RUNNING, and the health check passed. The runtime log shows Mode: production (static) and no type-check activity, so the check stays dev-only.

Repo checks: pnpm build && pnpm docs:build, pnpm check:fix && pnpm -r typecheck, and pnpm test all pass.

This pull request and its description were written by Isaac.

Vite and `tsx watch` only strip types, so the preview runs fine with type
errors and they first surface when deployment validation runs the app's
`typecheck` script. In dev, the server now runs that same script in the
background at startup and after TypeScript changes, and logs its errors
as `[appkit:server:typecheck]`. It is inert without a `typecheck` script.

The template's `typecheck` is now `tsc -b`, so one run reports client and
server errors; `&&` hid client errors whenever the server check failed.

Co-authored-by: Isaac <no-reply@databricks.com>
Signed-off-by: Fabian Jakobs <fabian.jakobs@databricks.com>
@fjakobs
fjakobs requested a review from a team as a code owner November 6, 2026 15:28
@fjakobs
fjakobs requested a review from atilafassina November 6, 2026 15:28
@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle size report

Compared against bundle-size-baseline.json (main).

@databricks/appkit

npm tarball (packed): 1.2 MB (+2.8 KB) — gzipped download (dist + bin; excludes release-only docs/NOTICE).

dist raw gzip
JS (runtime) 1.3 MB (+2.8 KB) 461 KB (+1.3 KB)
Type declarations 465 KB 169 KB (-1 B)
Source maps 2.5 MB (+5.1 KB) 861 KB (+2.1 KB)
Other 11 KB 3.7 KB
Total 4.3 MB (+8.0 KB) 1.5 MB (+3.3 KB)
Per-entry composition (own code — deps external (as shipped))
Entry Initial (gz) Lazy (gz) Total (gz) node_modules (min) Own code (min)
. 107 KB (+495 B) 2.5 KB 109 KB (+495 B) external 345 KB (+1.2 KB)
./beta 103 KB 482 B 104 KB external 310 KB
./testing 45 KB (-1 B) 33 KB (+491 B) 78 KB (+490 B) external 225 KB (+1.2 KB)
./tsdown 520 B 0 B 520 B external 813 B
./type-generator 26 KB 0 B 26 KB external 74 KB

Chunks:

Entry Chunk Load Size (gz)
. index.js initial 102 KB
. utils.js initial 4.6 KB
. remote-tunnel-manager.js lazy 2.5 KB
./beta beta.js initial 83 KB
./beta modular.js initial 6.0 KB
./beta stream-manager.js initial 5.9 KB
./beta service-context.js initial 4.2 KB
./beta databricks.js initial 3.3 KB
./beta client.js initial 647 B
./beta index.js initial 20 B
./beta supervisor-api.js lazy 192 B
./beta databricks.js lazy 176 B
./beta index.js lazy 114 B
./testing manifest.js initial 32 KB
./testing index.js initial 10 KB
./testing wide-event-emitter.js initial 2.9 KB
./testing index.js lazy 28 KB
./testing remote-tunnel-manager.js lazy 2.5 KB
./testing utils.js lazy 1.8 KB
./tsdown index.js initial 520 B
./type-generator index.js initial 26 KB

@databricks/appkit-ui

npm tarball (packed): 397 KB (-4 B) — gzipped download (dist + bin; excludes release-only docs/NOTICE).

dist raw gzip
JS (runtime) 429 KB 147 KB
Type declarations 267 KB 98 KB (-1 B)
Source maps 853 KB 286 KB
CSS 16 KB 3.2 KB
Total 1.5 MB 534 KB (-1 B)
Per-entry composition (consumer bundle — deps bundled, peerDeps external)
Entry Initial (gz) Lazy (gz) Total (gz) node_modules (min) Own code (min)
./js 5.3 KB 49 KB 55 KB 208 KB 14 KB
./js/beta 2.1 KB 0 B 2.1 KB 0 B 5.0 KB
./react 433 KB 49 KB 481 KB 1.3 MB 177 KB
./react/beta 4.9 KB 0 B 4.9 KB 0 B 12 KB

Chunks:

Entry Chunk Load Size (gz)
./js index.js initial 5.2 KB
./js chunk initial 120 B
./js apache-arrow lazy 49 KB
./js/beta beta.js initial 2.1 KB
./react index.js initial 431 KB
./react tslib initial 2.1 KB
./react apache-arrow lazy 49 KB
./react/beta beta.js initial 4.9 KB

@github-actions

github-actions Bot commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

🤖 AppKit PR bot

🔬 Run evals

Start an eval for this PR from the evals-monitor app: Go to Evals Monitor →

📦 Try this PR's app template

Scaffolds a new app from this PR's SDK build. Run it in any folder (requires the GitHub CLI — gh auth login — and the Databricks CLI):

gh run download 42243386058 -R databricks/appkit -n appkit-template-0.90.0-pr.d4601a2-fjakobs-la-2186-typecheck-in-build-loop-715 -D appkit-pr-715 \
  && unzip -o "appkit-pr-715/appkit-template-0.90.0-pr.d4601a2-fjakobs-la-2186-typecheck-in-build-loop-715.zip" -d "appkit-pr-715" \
  && databricks apps init --template "appkit-pr-715"

The template pins @databricks/appkit and @databricks/appkit-ui to tarballs built from this branch, so the scaffolded app runs against this PR's code.

This branch has not been deployed

No deployments
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.

2 participants