Skip to content

chore(ailab): migrate to vite + vitest + typescript toolchain#73010

Merged
sanchitmalhotra126 merged 2 commits into
stagingfrom
ailab-vite-toolchain
Jun 3, 2026
Merged

chore(ailab): migrate to vite + vitest + typescript toolchain#73010
sanchitmalhotra126 merged 2 commits into
stagingfrom
ailab-vite-toolchain

Conversation

@sanchitmalhotra126

@sanchitmalhotra126 sanchitmalhotra126 commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Why

Follow-up #1 to the mechanical migration (#72976, landed on staging via the redone #73025 ailab-migration-v2), which inlined @code-dot-org/ml-playground into frontend/packages/labs/ailab/ but deliberately left its standalone webpack + babel + jest toolchain in place (and therefore out of the turbo build/typecheck pipeline).

This PR swaps that toolchain for the monorepo-standard vite + vitest + tsc setup (mirroring frontend/packages/labs/oceans), restoring a green build and typecheck and wiring the package into turbo so CI sees it.

The migration is now merged to staging, so this PR targets staging directly (one commit on top). It was originally drafted stacked on the ailab-migration branch.

What this PR does

  • Build — a vite library build emits dist/index.{mjs,cjs,d.ts} (replacing the old webpack multi-entry output). package.json gains exports/main/module/types pointing at dist/, plus build/dev/typecheck scripts so the package joins the turbo pipeline.
  • Externalizationreact, react-dom, redux, react-redux move to peerDependencies and are externalized (vite-plugin-externalize-deps), so the consumer's single instances are used at runtime — preserving the redux "one instance per page" invariant the old webpack externals documented. Everything else (chart.js, ml-knn, papaparse, messageformat, …) is bundled.
  • Assets — the datasets (132 files) and the five UI images are emitted to dist/assets/{datasets,images}/ by a small rollup plugin and fetched at runtime from <setAssetPath base>/<dir>/<file> (the host serves them; the Gruntfile copies dist/assets/** into media/skins/ailab/). A dev-only middleware serves both trees for the standalone harness, with a ../-traversal guard. index.html moved to the package root (vite's dev entry); main.tsx is the dev harness. (Images were initially inlined as data-URIs; per review they're emitted instead — library mode always inlines imported assets, which added ~626 KB to index.mjs and lost separate caching. Now index.mjs is ~805 KB, down from ~1430 KB.)
  • setAssetPath simplification — deleted setPublicPath.ts and the __webpack_public_path__ machinery. setAssetPath now governs the runtime asset base for the emitted datasets and images, and its call-ordering contract relaxes from "before importing the bundle" to "before initAll()."
  • Tests — jest → vitest, 108 passing. .test.js files get vitest globals via the shared config.
  • Lint/format — adopt the shared flat eslint.config.mjs + prettier config; deleted .eslintrc.js, babel.config.json, the local prettier index.mjs, webpack.config.js, src/indexProd.tsx.

Decisions worth a look

  • Strictness deferral. The migrated source predates a few shared-config checks. To keep this swap mechanical, they're relaxed in this package only, with comments pointing at the cleanup follow-up:
    • tsconfig.app.json: verbatimModuleSyntax, noUnusedLocals, noUnusedParameters off (~110 import type + ~16 unused-binding violations).
    • eslint.config.mjs: import-x/no-cycle (real store↔index↔component cycles), @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, and two import-x CJS-default-interop false positives on react/papaparse. import-x/order stays on (auto-fixed across the source — most of the source diff is this reordering).
  • The one genuine type error the migration deferred was a real papaparse parse() overload mismatch (the source imports papaparse directly but only react-papaparse was declared). Fixed properly: declare papaparse + @types/papaparse, drop the dead react-papaparse shim, and split the call by the download flag so each branch matches a real overload.

What this PR does NOT do

  • No consumer rewiring. apps/ still imports the npm @code-dot-org/ml-playground and the Gruntfile still copies datasets from it. Nothing in the studio build consumes this package yet → near-zero risk. Repointing AilabView.tsx + the Gruntfile to @code-dot-org/ailab (and sequencing the frontend build before the apps build) is follow-up store sprite.value again, use it properly in setSpriteSize #2.
  • No e2e/Playwright suite — deferred to a later update (see follow-ups).

Test plan

  • CI: turbo run build typecheck test lint prettier --filter=@code-dot-org/ailab green (verified locally — 8/8 tasks).
  • yarn workspace @code-dot-org/ailab dev serves the standalone harness; datasets load and AI-bot images render (verified locally: /datasets/*.csv and /images/* 200 via middleware, traversal blocked).
  • dist/ contains index.{mjs,cjs,d.ts} exporting initAll/instructionsDismissed/setAssetPath, plus assets/datasets/ (132 files).
  • Studio unaffected — apps/ still on npm @code-dot-org/ml-playground.

Follow-ups

  1. Consumer rewire (store sprite.value again, use it properly in setSpriteSize #2): point apps/ at the workspace package; ensure the frontend build runs before the apps Grunt copy.
  2. Strictness cleanup: restore the relaxed tsconfig flags + eslint rules and untangle the import cycles.
  3. Playwright e2e suite against the standalone dev harness, following oceans (refactor(oceans-lab): swap toolchain to vite + lint-config + tsx + Playwright (stacked on #72539) #72579): a smoke spec per sample mode (boots clean, datasets load, expected panel renders), then 1–2 train→predict happy paths, plus an axe a11y pass. Infra is already present (playwright + @axe-core/playwright in the catalog, a test:ui:ci turbo task). Tracked for a later update.
  4. Consider folding assetPath into initAll(...) options to retire the globalThis write entirely (kills the last ordering caveat).

🤖 Generated with Claude Code

@sanchitmalhotra126 sanchitmalhotra126 requested review from a team, stephenliang and wilkie June 2, 2026 23:28

@stephenliang stephenliang left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A friendly pass over the vite + vitest + typescript migration 👋

Thanks for taking this on — toolchain swaps are fiddly, thankless work, and the overall shape here is really solid. I went through it three times wearing different hats (Vite/bundling, Vitest, TypeScript) and wanted to share notes in the spirit of "here's what I'd polish with you," not a gate. I've left inline comments with concrete, applyable suggestions wherever I could.

Tests — nicely done. I actually ran the suite (vitest --run): 108 passing across 13 files, clean exit, no hangs. I also poked it by injecting a deliberately-failing assertion to confirm the tests genuinely assert (they do). The only changes to the *.test.js files are import reordering — you carried the behavior over faithfully, which is exactly what you want in a migration like this. Lovely and disciplined.

TypeScript — also in good shape. strict is still on, the typecheck is genuinely green, and the papaparse fix is correct: the original error was real (the non-literal download flag matched no overload, and it only compiled before because papaparse was an untyped phantom dep), and your branch split lines up with the real @types/papaparse overloads with no casts. I re-enabled the deferred tsconfig flags in a throwaway copy just to be sure nothing scary was hiding — it's all cosmetic (≈111 import type-style + 16 unused bindings), no real bugs. The deferral-plus-follow-up plan reads as very reasonable.

A few Vite details I'd love to look at together (all inline, with fixes):

  1. The "five UI images are small" comment isn't quite holding up in practice — about 626 KB of base64 is landing in the bundle. The oceans package has a nice pattern we can borrow.
  2. vite build is exiting 0 even though the dts step logs a couple of TS6307 errors — easy one-line fix, and I left the exact suggestion.
  3. The dev /datasets/ middleware can be walked out of its directory with ../ — dev-only, but a quick guard closes it.
  4. A tiny stale lodash mention in a comment (it rode along from oceans).

None of these are a knock on the work — they're exactly the kind of thing that's easy to miss in a big mechanical swap. Happy to pair on any of them!

🤖 Written by Claude Code.

Comment thread frontend/packages/labs/ailab/vite.config.ts Outdated
Comment thread frontend/packages/labs/ailab/vite.config.ts
Comment thread frontend/packages/labs/ailab/tsconfig.app.json Outdated
Comment thread frontend/packages/labs/ailab/vite.config.ts Outdated
Comment thread frontend/packages/labs/ailab/vite.config.ts Outdated

@stephenliang stephenliang left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes look amazing! Love seeing the old dependencies get replaced with catalog. I had Claude generate some comments, and then I added my own here. They mainly have to do with ensuring we follow up.

Comment on lines +18 to +23
// The migrated ml-playground source predates the shared config. These rules
// fire on pre-existing patterns: real circular imports between the store
// singleton and its consumers, unused bindings, a couple of `any`s, and
// false-positive CJS-default-interop reports on react/papaparse. Relaxed so
// the toolchain swap stays mechanical; untangling the cycles and tightening
// the rest is a tracked follow-up cleanup. (`import-x/order` stays on.)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's flag as a follow up clean up task. I've been putting these on Github Issues under the label "Next Generation Frontend Platform".

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +26 to +32
rules: {
'import-x/no-cycle': 'off',
'import-x/default': 'off',
'import-x/no-named-as-default-member': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same with these, all fine as follow ups.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread frontend/packages/labs/ailab/index.html Outdated
// turns on: `verbatimModuleSyntax` (would require `import type` on ~110
// type-only imports) and `noUnusedLocals`/`noUnusedParameters` (~16
// pre-existing unused bindings). Relaxed here so the toolchain swap stays
// mechanical; restoring them is a tracked follow-up cleanup.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make sure to track it

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread frontend/packages/labs/ailab/vite.config.ts Outdated
Base automatically changed from ailab-migration to staging June 3, 2026 17:55
Follow-up #1 to the mechanical migration (#72976). Replace the package's
webpack + babel + jest toolchain with the monorepo-standard vite + vitest +
tsc setup, restoring a green build/typecheck and wiring the package into the
turbo pipeline. No consumer rewiring: apps/ still pulls npm
@code-dot-org/ml-playground, so nothing in the studio build changes yet.

Build: a vite library build emits dist/index.{mjs,cjs,d.ts}. peerDependencies
(react, react-dom, redux, react-redux) are externalized so the consumer's
single instances are used; everything else is bundled. The five UI images are
inlined as data-URIs (no runtime path), and the 132 dataset files are emitted
to dist/assets/datasets/ for the host to serve. A dev-only middleware serves
them at /datasets/ for the standalone harness (index.html moved to the package
root as the vite entry). Deleted the webpack __webpack_public_path__ machinery
(setPublicPath.ts): with images inlined, setAssetPath now governs only the
runtime dataset URLs.

Tests: jest -> vitest, 108 passing. Lint/prettier adopt the shared flat
eslint + prettier config; the .test.js suite gets vitest globals.

The migrated source predates some shared-config strictness. Relaxed in this
package with a documented, tracked cleanup follow-up: tsconfig
verbatimModuleSyntax + noUnusedLocals/Parameters off; eslint import-x/no-cycle,
no-unused-vars, no-explicit-any, and two react/papaparse CJS-interop
false-positives off. The one genuine type error (a papaparse parse() overload)
is fixed properly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

🖼️ Storybook Visual Comparison Report

✅ No Storybook eyes differences detected!

From Stephen's review on the vite/vitest toolchain PR:

- Emit the 5 UI images to dist/assets/images/ and resolve them off
  setAssetPath at runtime (new imageUrl helper) instead of inlining. Library
  mode always inlines imported assets as base-64, which put ~626 KB into the
  entry and lost separate caching; index.mjs drops 1430 KB -> 805 KB. Images
  now travel like the datasets (oceans pattern): emitted under dist/assets/,
  copied by the Gruntfile, fetched under the setAssetPath base.
- tsconfig.app.json: include the two JSON files imported from outside src
  (public/datasets-manifest.json, i18n/mlPlayground.json) so vite-plugin-dts
  stops logging TS6307 (the build exited 0 but the dts step was unhappy).
- dev asset middleware: decode + resolve + confirm the path stays under
  public/ before serving via /@fs (closes a dev-only ../ traversal); generalized
  to serve /images/ alongside /datasets/.
- rename the standalone dev entry indexDev.tsx -> main.tsx for consistency
  with oceans; drop the now-dead @public alias and trim a stale lodash mention.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sanchitmalhotra126 sanchitmalhotra126 merged commit 015ac0b into staging Jun 3, 2026
16 checks passed
@sanchitmalhotra126 sanchitmalhotra126 deleted the ailab-vite-toolchain branch June 3, 2026 22:07
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