feat(marko-virtual): post-release follow-ups: full option parity, streaming SSR Support with Resume, typed handles, e2e coverage, examples#1219
Conversation
📝 WalkthroughWalkthroughThe Marko adapter now exposes returned virtualizer handles, forwards the full virtual-core option surface, supports SSR slices and streaming, modernizes existing examples, adds chat and SSR demos, and introduces extensive unit, type-check, and Playwright coverage. ChangesMarko virtual adapter
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install timed out. The project may have too many dependencies for the sandbox. 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. Comment |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
|
@coderabbitai help |
ChatThere are 3 ways to chat with CodeRabbit:
CodeRabbit commands
Other keywords and placeholders
CodeRabbit configuration file (
|
|
@coderabbitai summary |
✅ Action performedSummary regeneration triggered. |
4bd71a2 to
fb05f90
Compare
To buildpnpm install
pnpm --filter @tanstack/virtual-core build
pnpm --filter @tanstack/marko-virtual build
pnpm --filter @tanstack/marko-virtual test:types To Test## runs unit tests
pnpm --filter @tanstack/marko-virtual exec vitest run --reporter=verbose
## runs e2e
pnpm -r --workspace-concurrency=1 --filter "./examples/marko/*" run test:e2eTo run examplespnpm --filter tanstack-marko-virtual-example-fixed dev
pnpm --filter tanstack-marko-virtual-example-variable dev
pnpm --filter tanstack-marko-virtual-example-dynamic dev
pnpm --filter tanstack-marko-virtual-example-grid dev
pnpm --filter tanstack-marko-virtual-example-smooth-scroll dev
pnpm --filter tanstack-marko-virtual-example-infinite-scroll dev
pnpm --filter tanstack-marko-virtual-example-window dev
pnpm --filter tanstack-marko-virtual-example-ssr dev
pnpm --filter tanstack-marko-virtual-example-ssr-fetch dev
pnpm --filter tanstack-marko-virtual-example-ssr-slice dev
pnpm --filter tanstack-marko-virtual-example-ssr-restore dev
pnpm --filter tanstack-marko-virtual-example-window-ssr-slice dev
pnpm --filter tanstack-marko-virtual-example-chat dev
pnpm --filter tanstack-marko-virtual-example-scroll-padding dev
pnpm --filter tanstack-marko-virtual-example-padding dev
pnpm --filter tanstack-marko-virtual-example-pretext dev
pnpm --filter tanstack-marko-virtual-example-sticky dev
pnpm --filter tanstack-marko-virtual-example-table dev
pnpm --filter tanstack-marko-virtual-example-chat-pretext dev |
c4e8d8f to
e5c653c
Compare
… type-strict examples
…am guidance packaging changed to the canonical Marko library shape per Marko team guidance (marko.json exports → built dist/tags, no tags-dir/script-lang, mtc-built declarations, in-repo consumers resolve tags from source via nested marko.json), plus the sideEffects fix
|
@copilot resolve the merge conflicts in this pull request |
|
View your CI Pipeline Execution ↗ for commit 99185f0 ☁️ Nx Cloud last updated this comment at |
There was a problem hiding this comment.
Pull request overview
This PR significantly hardens and expands the @tanstack/marko-virtual Marko v6 adapter without touching packages/virtual-core, focusing on SSR/streaming support, full option parity, typed tag handles, packaging to a canonical Marko library layout, and broad example + e2e coverage.
Changes:
- Reworks the Marko tags to be SSR-safe (including server “slice” rendering via
initialRect) and to resume cleanly on the client. - Adds full option parity for both tags and validates behavior with a dedicated Playwright “option-gates” harness.
- Modernizes packaging/build/type-check workflows and adds/updates many Marko examples, route typings, and docs wiring.
Reviewed changes
Copilot reviewed 175 out of 197 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| pnpm-workspace.yaml | Adds Marko option-gates workspace + Marko compiler override |
| packages/marko-virtual/tsconfig.typecheck.json | Adds mtc-based package typecheck project |
| packages/marko-virtual/tsconfig.tags.json | Adds tags build emit config into dist |
| packages/marko-virtual/tests/marko.json | Points tests at source tags via tags-dir |
| packages/marko-virtual/tests/fixtures/window-virtualizer-fixture.marko | Updates fixtures to tag-variable return API |
| packages/marko-virtual/tests/fixtures/virtualizer-fixture.marko | Updates fixtures to tag-variable return API |
| packages/marko-virtual/tests/fixtures/surface-fixture.marko | Adds fixture for expanded handle surface |
| packages/marko-virtual/tests/fixtures/count-update-fixture.marko | Updates reactive-count fixture to new handle API |
| packages/marko-virtual/src/tags/virtualizer/index.marko | Adds SSR seeding + typed handle return surface |
| packages/marko-virtual/package.json | Updates exports/files/sideEffects + build/type scripts |
| packages/marko-virtual/marko.json | Switches to canonical Marko package exports shape |
| packages/marko-virtual/e2e/option-gates/vite.config.ts | Adds option-gates Vite config + virtual-core alias |
| packages/marko-virtual/e2e/option-gates/tsconfig.json | Adds strict TS config for option-gates harness |
| packages/marko-virtual/e2e/option-gates/src/routes/window-initial-offset/+page.marko | Adds initialOffset SSR gate route |
| packages/marko-virtual/e2e/option-gates/src/routes/window-horizontal/+page.marko | Adds horizontal window virtualizer gate route |
| packages/marko-virtual/e2e/option-gates/src/routes/window-example/+page.marko | Adds window example page into gate harness |
| packages/marko-virtual/e2e/option-gates/src/routes/scroll-margin/+page.marko | Adds scrollMargin behavior gate route |
| packages/marko-virtual/e2e/option-gates/src/routes/scroll-events/+page.marko | Adds scroll event option smoke gate route |
| packages/marko-virtual/e2e/option-gates/src/routes/rtl/+page.marko | Adds RTL scrolling gate route |
| packages/marko-virtual/e2e/option-gates/src/routes/measure-element/+page.marko | Adds custom measureElement gate route |
| packages/marko-virtual/e2e/option-gates/src/routes/lanes-mode/+page.marko | Adds laneAssignmentMode gate route |
| packages/marko-virtual/e2e/option-gates/src/routes/enabled/+page.marko | Adds enabled toggle gate route |
| packages/marko-virtual/e2e/option-gates/src/routes/debug/+page.marko | Adds debug logging gate route |
| packages/marko-virtual/e2e/option-gates/src/routes/cached/+page.marko | Adds cached measurements gate route |
| packages/marko-virtual/e2e/option-gates/src/routes/+page.marko | Adds option-gates index route |
| packages/marko-virtual/e2e/option-gates/README.md | Documents option-gates harness usage/constraints |
| packages/marko-virtual/e2e/option-gates/playwright.config.ts | Adds Playwright config for option-gates |
| packages/marko-virtual/e2e/option-gates/package.json | Adds package manifest for option-gates harness |
| packages/marko-virtual/e2e/option-gates/marko.json | Points option-gates at source tags via tags-dir |
| examples/marko/window/tsconfig.json | Adds strict TS config for example app |
| examples/marko/window/src/routes/+page.marko | Updates window example to handle API + scrollMargin |
| examples/marko/window/playwright.config.ts | Adds Playwright config for example |
| examples/marko/window/package.json | Modernizes deps + adds e2e/types scripts |
| examples/marko/window/marko.json | Switches example to source tags via tags-dir |
| examples/marko/window/e2e/window.spec.ts | Adds browser gates for window scrollMargin behavior |
| examples/marko/window/.marko-run/routes.d.ts | Commits generated route typings |
| examples/marko/window-ssr-slice/vite.config.ts | Adds Vite config for SSR slice example |
| examples/marko/window-ssr-slice/tsconfig.json | Adds strict TS config for example app |
| examples/marko/window-ssr-slice/src/routes/+page.marko | Adds window SSR slice example route |
| examples/marko/window-ssr-slice/src/data.ts | Adds server-side data source for SSR demo |
| examples/marko/window-ssr-slice/playwright.config.ts | Adds Playwright config for example |
| examples/marko/window-ssr-slice/package.json | Adds example manifest + scripts/deps |
| examples/marko/window-ssr-slice/marko.json | Points example at source tags via tags-dir |
| examples/marko/window-ssr-slice/e2e/window-ssr-slice.spec.ts | Adds SSR slice + resume browser gates |
| examples/marko/window-ssr-slice/.marko-run/routes.d.ts | Commits generated route typings |
| examples/marko/variable/tsconfig.json | Adds strict TS config for example app |
| examples/marko/variable/src/routes/+page.marko | Migrates to handle API + removes mounted guards |
| examples/marko/variable/playwright.config.ts | Adds Playwright config for example |
| examples/marko/variable/package.json | Modernizes deps + adds e2e/types scripts |
| examples/marko/variable/marko.json | Switches example to source tags via tags-dir |
| examples/marko/variable/e2e/variable.spec.ts | Adds behavioral gates for formula-sized items |
| examples/marko/variable/.marko-run/routes.d.ts | Commits generated route typings |
| examples/marko/table/vite.config.ts | Adds Vite config for table example |
| examples/marko/table/tsconfig.json | Adds strict TS config for example app |
| examples/marko/table/playwright.config.ts | Adds Playwright config for example |
| examples/marko/table/package.json | Adds example manifest + scripts/deps |
| examples/marko/table/marko.json | Points example at source tags via tags-dir |
| examples/marko/table/.marko-run/routes.d.ts | Commits generated route typings |
| examples/marko/sticky/vite.config.ts | Adds Vite config for sticky example |
| examples/marko/sticky/tsconfig.json | Adds strict TS config for example app |
| examples/marko/sticky/playwright.config.ts | Adds Playwright config for example |
| examples/marko/sticky/package.json | Adds example manifest + scripts/deps |
| examples/marko/sticky/marko.json | Points example at source tags via tags-dir |
| examples/marko/sticky/e2e/sticky.spec.ts | Adds sticky-header rangeExtractor gates |
| examples/marko/sticky/.marko-run/routes.d.ts | Commits generated route typings |
| examples/marko/ssr/vite.config.ts | Adds Vite config for SSR baseline example |
| examples/marko/ssr/tsconfig.json | Adds strict TS config for example app |
| examples/marko/ssr/src/routes/+page.marko | Adds SSR baseline (empty server container) example |
| examples/marko/ssr/playwright.config.ts | Adds Playwright config for example |
| examples/marko/ssr/package.json | Adds example manifest + scripts/deps |
| examples/marko/ssr/marko.json | Points example at source tags via tags-dir |
| examples/marko/ssr/e2e/ssr.spec.ts | Adds SSR baseline view-source + resume gates |
| examples/marko/ssr/.marko-run/routes.d.ts | Commits generated route typings |
| examples/marko/ssr-slice/vite.config.ts | Adds Vite config for SSR slice example |
| examples/marko/ssr-slice/tsconfig.json | Adds strict TS config for example app |
| examples/marko/ssr-slice/src/routes/+page.marko | Adds SSR slice (server painted rows) example |
| examples/marko/ssr-slice/src/data.ts | Adds server-side data source for SSR demo |
| examples/marko/ssr-slice/playwright.config.ts | Adds Playwright config for example |
| examples/marko/ssr-slice/package.json | Adds example manifest + scripts/deps |
| examples/marko/ssr-slice/marko.json | Points example at source tags via tags-dir |
| examples/marko/ssr-slice/.marko-run/routes.d.ts | Commits generated route typings |
| examples/marko/ssr-restore/vite.config.ts | Adds Vite config for SSR restore example |
| examples/marko/ssr-restore/tsconfig.json | Adds strict TS config for example app |
| examples/marko/ssr-restore/src/routes/+page.marko | Adds SSR initialOffset restore example route |
| examples/marko/ssr-restore/src/data.ts | Adds server-side data source for SSR demo |
| examples/marko/ssr-restore/playwright.config.ts | Adds Playwright config for example |
| examples/marko/ssr-restore/package.json | Adds example manifest + scripts/deps |
| examples/marko/ssr-restore/marko.json | Points example at source tags via tags-dir |
| examples/marko/ssr-restore/e2e/ssr-restore.spec.ts | Adds SSR offset slice + restore gates |
| examples/marko/ssr-restore/.marko-run/routes.d.ts | Commits generated route typings |
| examples/marko/ssr-fetch/vite.config.ts | Adds Vite config for SSR fetch example |
| examples/marko/ssr-fetch/tsconfig.json | Adds strict TS config for example app |
| examples/marko/ssr-fetch/src/routes/+page.marko | Adds SSR fetch+serialize (client-rendered rows) |
| examples/marko/ssr-fetch/src/data.ts | Adds server-side data source for SSR demo |
| examples/marko/ssr-fetch/playwright.config.ts | Adds Playwright config for example |
| examples/marko/ssr-fetch/package.json | Adds example manifest + scripts/deps |
| examples/marko/ssr-fetch/marko.json | Points example at source tags via tags-dir |
| examples/marko/ssr-fetch/e2e/ssr-fetch.spec.ts | Adds SSR serialized data/no-refetch gates |
| examples/marko/ssr-fetch/.marko-run/routes.d.ts | Commits generated route typings |
| examples/marko/smooth-scroll/tsconfig.json | Adds strict TS config for example app |
| examples/marko/smooth-scroll/src/routes/+page.marko | Migrates smooth-scroll example to handle API |
| examples/marko/smooth-scroll/playwright.config.ts | Adds Playwright config for example |
| examples/marko/smooth-scroll/package.json | Modernizes deps + adds e2e/types scripts |
| examples/marko/smooth-scroll/marko.json | Switches example to source tags via tags-dir |
| examples/marko/smooth-scroll/e2e/smooth-scroll.spec.ts | Adds smooth scrollToIndex gates |
| examples/marko/smooth-scroll/.marko-run/routes.d.ts | Commits generated route typings |
| examples/marko/scroll-padding/vite.config.ts | Adds Vite config for scroll-padding example |
| examples/marko/scroll-padding/tsconfig.json | Adds strict TS config for example app |
| examples/marko/scroll-padding/src/routes/+page.marko | Adds scrollPaddingStart/paddingStart demo |
| examples/marko/scroll-padding/playwright.config.ts | Adds Playwright config for example |
| examples/marko/scroll-padding/package.json | Adds example manifest + scripts/deps |
| examples/marko/scroll-padding/marko.json | Points example at source tags via tags-dir |
| examples/marko/scroll-padding/e2e/scroll-padding.spec.ts | Adds scroll padding + sticky header gates |
| examples/marko/scroll-padding/.marko-run/routes.d.ts | Commits generated route typings |
| examples/marko/pretext/vite.config.ts | Adds Vite config for pretext example |
| examples/marko/pretext/tsconfig.json | Adds strict TS config for example app |
| examples/marko/pretext/playwright.config.ts | Adds Playwright config for example |
| examples/marko/pretext/package.json | Adds example manifest + pretext dependency |
| examples/marko/pretext/marko.json | Points example at source tags via tags-dir |
| examples/marko/pretext/e2e/pretext.spec.ts | Adds calculated-height behavior gates |
| examples/marko/pretext/.marko-run/routes.d.ts | Commits generated route typings |
| examples/marko/padding/vite.config.ts | Adds Vite config for padding example |
| examples/marko/padding/tsconfig.json | Adds strict TS config for example app |
| examples/marko/padding/playwright.config.ts | Adds Playwright config for example |
| examples/marko/padding/package.json | Adds example manifest + scripts/deps |
| examples/marko/padding/marko.json | Points example at source tags via tags-dir |
| examples/marko/padding/.marko-run/routes.d.ts | Commits generated route typings |
| examples/marko/infinite-scroll/tsconfig.json | Adds strict TS config for example app |
| examples/marko/infinite-scroll/src/routes/+page.marko | Migrates infinite-scroll to handle API |
| examples/marko/infinite-scroll/playwright.config.ts | Adds Playwright config for example |
| examples/marko/infinite-scroll/package.json | Modernizes deps + adds e2e/types scripts |
| examples/marko/infinite-scroll/marko.json | Switches example to source tags via tags-dir |
| examples/marko/infinite-scroll/e2e/infinite-scroll.spec.ts | Adds paging/placeholder behavior gates |
| examples/marko/infinite-scroll/.marko-run/routes.d.ts | Commits generated route typings |
| examples/marko/grid/tsconfig.json | Adds strict TS config for example app |
| examples/marko/grid/src/routes/+page.marko | Migrates grid example to handle API |
| examples/marko/grid/playwright.config.ts | Adds Playwright config for example |
| examples/marko/grid/package.json | Modernizes deps + adds e2e/types scripts |
| examples/marko/grid/marko.json | Switches example to source tags via tags-dir |
| examples/marko/grid/e2e/grid.spec.ts | Adds shared-scroller 2D virtualization gates |
| examples/marko/grid/.marko-run/routes.d.ts | Commits generated route typings |
| examples/marko/fixed/tsconfig.json | Adds strict TS config for example app |
| examples/marko/fixed/playwright.config.ts | Adds Playwright config for example |
| examples/marko/fixed/package.json | Modernizes deps + adds e2e/types scripts |
| examples/marko/fixed/marko.json | Points example at source tags via tags-dir |
| examples/marko/fixed/e2e/fixed.spec.ts | Adds fixed-size virtualization gates |
| examples/marko/fixed/.marko-run/routes.d.ts | Commits generated route typings |
| examples/marko/dynamic/tsconfig.json | Adds strict TS config for example app |
| examples/marko/dynamic/src/routes/+page.marko | Migrates dynamic-measure example to handle API |
| examples/marko/dynamic/playwright.config.ts | Adds Playwright config for example |
| examples/marko/dynamic/package.json | Modernizes deps + adds e2e/types scripts |
| examples/marko/dynamic/marko.json | Switches example to source tags via tags-dir |
| examples/marko/dynamic/e2e/dynamic.spec.ts | Adds dynamic measurement behavior gates |
| examples/marko/dynamic/.marko-run/routes.d.ts | Commits generated route typings |
| examples/marko/chat/vite.config.ts | Adds Vite config for chat example |
| examples/marko/chat/tsconfig.json | Adds strict TS config for example app |
| examples/marko/chat/src/routes/api/reply/+handler.ts | Adds streamed reply handler for chat demo |
| examples/marko/chat/playwright.config.ts | Adds Playwright config for example |
| examples/marko/chat/package.json | Adds chat example manifest + scripts/deps |
| examples/marko/chat/marko.json | Points example at source tags via tags-dir |
| examples/marko/chat/e2e/README.md | Documents running chat e2e locally |
| examples/marko/chat/.marko-run/routes.d.ts | Commits generated route typings |
| examples/marko/chat-pretext/vite.config.ts | Adds Vite config for chat-pretext example |
| examples/marko/chat-pretext/tsconfig.json | Adds strict TS config for example app |
| examples/marko/chat-pretext/src/routes/api/reply/+handler.ts | Adds streamed reply handler for chat-pretext |
| examples/marko/chat-pretext/playwright.config.ts | Adds Playwright config for example |
| examples/marko/chat-pretext/package.json | Adds chat-pretext manifest + pretext dependency |
| examples/marko/chat-pretext/marko.json | Points example at source tags via tags-dir |
| examples/marko/chat-pretext/e2e/README.md | Documents running chat-pretext e2e locally |
| examples/marko/chat-pretext/.marko-run/routes.d.ts | Commits generated route typings |
| docs/installation.md | Adds Marko adapter install instructions |
| docs/config.json | Adds Marko example routes to docs nav |
| .gitignore | Ignores TS incremental build info files |
| .changeset/marko-virtual-followups.md | Adds release notes for Marko adapter follow-ups |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| Publishing: not affected — the package's `files` field publishes only dist, | ||
| src/tags, marko.json, and README.md, so nothing under e2e/ reaches npm. |
| named handle types (VirtualizerHandle / WindowVirtualizerHandle) with committed | ||
| .d.marko declarations verified by marko-type-check in CI, a new Chat + Pretext |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (7)
examples/marko/ssr-slice/vite.config.ts (1)
5-7: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove the Vite 5 cast and comment.
examples/marko/ssr-slice/package.jsonalready uses Vite 6, somarko() as anyand the surrounding note are obsolete.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/marko/ssr-slice/vite.config.ts` around lines 5 - 7, Remove the obsolete Vite compatibility comment and the any cast from the plugins configuration, using the direct marko() plugin value. Keep the existing plugin configuration otherwise unchanged.examples/marko/chat-pretext/src/routes/+page.marko (1)
46-55: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winUnbounded module-scoped cache growth on the server.
preparedCacheis astatic const(module scope, shared across all requests/instances) that only grows — entries are added ingetPreparedbut the only eviction (preparedCache.clear()) happens inside a client-onlyonMount/document.fonts.readycallback (Lines 292-297), which never fires during SSR. On a long-running server process, unique message text (e.g., "Add message" clicks, growing streamed-reply substrings) accumulates in this map forever.♻️ Suggested bound on cache size
+static const PREPARED_CACHE_LIMIT = 1000 static const preparedCache = new Map<string, ReturnType<typeof prepare>>() static function getPrepared(text: string) { const body = text || PLACEHOLDER const cached = preparedCache.get(body) if (cached) return cached const prepared = prepare(body, BODY_FONT, { letterSpacing: 0, whiteSpace: 'normal' }) + if (preparedCache.size >= PREPARED_CACHE_LIMIT) { + preparedCache.delete(preparedCache.keys().next().value) + } preparedCache.set(body, prepared) return prepared }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/marko/chat-pretext/src/routes/`+page.marko around lines 46 - 55, Bound the module-scoped preparedCache used by getPrepared so unique server-side message text cannot grow it indefinitely. Add eviction when inserting a new prepared value, preserving cache hits and retaining only a fixed maximum number of entries; keep the existing client-side font readiness clearing behavior unchanged.examples/marko/chat-pretext/vite.config.ts (1)
4-6: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAdd a comment explaining the
as anycast.To improve maintainability, consider adding a comment explaining why
marko()is cast toany(e.g., due to Vite version type mismatches), similar to the comment found inexamples/marko/ssr-fetch/vite.config.ts.💡 Proposed refactor
export default defineConfig({ + // Cast to any: `@marko/run/vite` types are built against Vite 6 but this + // workspace uses Vite 5. The plugin works correctly at runtime. plugins: [marko() as any], })🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/marko/chat-pretext/vite.config.ts` around lines 4 - 6, Add a concise inline comment next to the `marko() as any` cast in the `defineConfig` plugin configuration, explaining the Vite/Marko type mismatch and why the cast is required, consistent with the existing explanation in the related configuration.examples/marko/ssr-slice/e2e/ssr-slice.spec.ts (1)
111-111: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueHardcoded URL duplicates
playwright.config.ts'sbaseURL.Deriving the URL from config (e.g.
test.info().project.use.baseURL) would avoid drift if the port changes.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/marko/ssr-slice/e2e/ssr-slice.spec.ts` at line 111, Update the fetch call in the SSR slice test to derive its URL from Playwright’s configured baseURL via test.info().project.use.baseURL, rather than hardcoding localhost and the port. Preserve the existing root-path request.examples/marko/scroll-padding/e2e/scroll-padding.spec.ts (1)
25-28: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUse a top-level type import for
Page.For improved readability, consider using a top-level
import type { Page }instead of an inlineimport()type assertion.💅 Proposed refactor
Update the top-level import to include
Page:import { expect, test, type Page } from '`@playwright/test`'And update the function signature:
-async function headerBottom(page: import('`@playwright/test`').Page) { +async function headerBottom(page: Page) { const box = await page.locator('thead').boundingBox() return box!.y + box!.height }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/marko/scroll-padding/e2e/scroll-padding.spec.ts` around lines 25 - 28, Update the top-level `@playwright/test` import to include the type-only Page symbol, then change headerBottom’s page parameter to use Page directly instead of the inline import() type. Preserve the existing function behavior.examples/marko/pretext/vite.config.ts (1)
4-8: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove stale comment and verify type cast.
The comment mentions that this workspace uses Vite 5, but the
package.jsonfor this example specifies"vite": "^6.4.2". Consider removing the stale comment and verifying if theas anycast is still necessary.🧹 Proposed refactor
export default defineConfig({ - // Cast to any: `@marko/run/vite` types are built against Vite 6 but this - // workspace uses Vite 5. The plugin works correctly at runtime. - plugins: [marko() as any], + plugins: [marko()], })🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/marko/pretext/vite.config.ts` around lines 4 - 8, Update the plugins configuration in defineConfig: remove the stale Vite 5 compatibility comment, verify whether the marko() result now type-checks with the configured Vite 6 dependency, and remove the as any cast if it is no longer required.examples/marko/window-ssr-slice/vite.config.ts (1)
5-7: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider verifying if the
as anycast is still required.The comment indicates a Vite 5 vs Vite 6 type mismatch, but this specific example's
package.jsonexplicitly specifies"vite": "^6.4.2". If the workspace's Vite version has since been updated or if TypeScript resolves the types correctly, you may be able to remove theas anycast and this comment.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@examples/marko/window-ssr-slice/vite.config.ts` around lines 5 - 7, Recheck the Vite plugin typing in the plugins configuration for marko(); since this example declares Vite 6.4.2, remove the as any cast and its explanatory comment if TypeScript accepts the plugin directly. Retain the cast only if the current resolved types still require it.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@examples/marko/chat/src/routes/`+page.marko:
- Around line 141-168: Handle non-abort errors inside the fire-and-forget IIFE
around the fetch in the reply stream flow instead of rethrowing them as an
unhandled rejection. Surface the failure through the existing status or message
state, while preserving silent handling for intentional aborts and the existing
cleanup of state.streamAbort in finally.
In `@examples/marko/scroll-padding/vite.config.ts`:
- Around line 5-7: Update the plugin configuration around marko() to reflect the
workspace’s Vite 6 dependency: remove the obsolete Vite 5 comment and drop the
as any cast if the Vite 6 types now align; otherwise retain the cast only with a
rationale describing the current type incompatibility.
---
Nitpick comments:
In `@examples/marko/chat-pretext/src/routes/`+page.marko:
- Around line 46-55: Bound the module-scoped preparedCache used by getPrepared
so unique server-side message text cannot grow it indefinitely. Add eviction
when inserting a new prepared value, preserving cache hits and retaining only a
fixed maximum number of entries; keep the existing client-side font readiness
clearing behavior unchanged.
In `@examples/marko/chat-pretext/vite.config.ts`:
- Around line 4-6: Add a concise inline comment next to the `marko() as any`
cast in the `defineConfig` plugin configuration, explaining the Vite/Marko type
mismatch and why the cast is required, consistent with the existing explanation
in the related configuration.
In `@examples/marko/pretext/vite.config.ts`:
- Around line 4-8: Update the plugins configuration in defineConfig: remove the
stale Vite 5 compatibility comment, verify whether the marko() result now
type-checks with the configured Vite 6 dependency, and remove the as any cast if
it is no longer required.
In `@examples/marko/scroll-padding/e2e/scroll-padding.spec.ts`:
- Around line 25-28: Update the top-level `@playwright/test` import to include the
type-only Page symbol, then change headerBottom’s page parameter to use Page
directly instead of the inline import() type. Preserve the existing function
behavior.
In `@examples/marko/ssr-slice/e2e/ssr-slice.spec.ts`:
- Line 111: Update the fetch call in the SSR slice test to derive its URL from
Playwright’s configured baseURL via test.info().project.use.baseURL, rather than
hardcoding localhost and the port. Preserve the existing root-path request.
In `@examples/marko/ssr-slice/vite.config.ts`:
- Around line 5-7: Remove the obsolete Vite compatibility comment and the any
cast from the plugins configuration, using the direct marko() plugin value. Keep
the existing plugin configuration otherwise unchanged.
In `@examples/marko/window-ssr-slice/vite.config.ts`:
- Around line 5-7: Recheck the Vite plugin typing in the plugins configuration
for marko(); since this example declares Vite 6.4.2, remove the as any cast and
its explanatory comment if TypeScript accepts the plugin directly. Retain the
cast only if the current resolved types still require it.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 57b0ab6f-f658-4adf-afd1-86c6897d00b2
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (196)
.changeset/marko-virtual-followups.md.gitignoredocs/config.jsondocs/framework/marko/marko-virtual.mddocs/installation.mdexamples/marko/chat-pretext/.marko-run/routes.d.tsexamples/marko/chat-pretext/e2e/README.mdexamples/marko/chat-pretext/e2e/chat-pretext.spec.tsexamples/marko/chat-pretext/marko.jsonexamples/marko/chat-pretext/package.jsonexamples/marko/chat-pretext/playwright.config.tsexamples/marko/chat-pretext/src/routes/+page.markoexamples/marko/chat-pretext/src/routes/api/reply/+handler.tsexamples/marko/chat-pretext/tsconfig.jsonexamples/marko/chat-pretext/vite.config.tsexamples/marko/chat/.marko-run/routes.d.tsexamples/marko/chat/e2e/README.mdexamples/marko/chat/e2e/chat.spec.tsexamples/marko/chat/marko.jsonexamples/marko/chat/package.jsonexamples/marko/chat/playwright.config.tsexamples/marko/chat/src/routes/+page.markoexamples/marko/chat/src/routes/api/reply/+handler.tsexamples/marko/chat/tsconfig.jsonexamples/marko/chat/vite.config.tsexamples/marko/dynamic/.marko-run/routes.d.tsexamples/marko/dynamic/e2e/dynamic.spec.tsexamples/marko/dynamic/marko.jsonexamples/marko/dynamic/package.jsonexamples/marko/dynamic/playwright.config.tsexamples/marko/dynamic/src/routes/+page.markoexamples/marko/dynamic/tsconfig.jsonexamples/marko/fixed/.marko-run/routes.d.tsexamples/marko/fixed/e2e/fixed.spec.tsexamples/marko/fixed/marko.jsonexamples/marko/fixed/package.jsonexamples/marko/fixed/playwright.config.tsexamples/marko/fixed/src/routes/+page.markoexamples/marko/fixed/tsconfig.jsonexamples/marko/grid/.marko-run/routes.d.tsexamples/marko/grid/e2e/grid.spec.tsexamples/marko/grid/marko.jsonexamples/marko/grid/package.jsonexamples/marko/grid/playwright.config.tsexamples/marko/grid/src/routes/+page.markoexamples/marko/grid/tsconfig.jsonexamples/marko/infinite-scroll/.marko-run/routes.d.tsexamples/marko/infinite-scroll/e2e/infinite-scroll.spec.tsexamples/marko/infinite-scroll/marko.jsonexamples/marko/infinite-scroll/package.jsonexamples/marko/infinite-scroll/playwright.config.tsexamples/marko/infinite-scroll/src/routes/+page.markoexamples/marko/infinite-scroll/tsconfig.jsonexamples/marko/padding/.marko-run/routes.d.tsexamples/marko/padding/e2e/padding.spec.tsexamples/marko/padding/marko.jsonexamples/marko/padding/package.jsonexamples/marko/padding/playwright.config.tsexamples/marko/padding/src/routes/+page.markoexamples/marko/padding/tsconfig.jsonexamples/marko/padding/vite.config.tsexamples/marko/pretext/.marko-run/routes.d.tsexamples/marko/pretext/e2e/pretext.spec.tsexamples/marko/pretext/marko.jsonexamples/marko/pretext/package.jsonexamples/marko/pretext/playwright.config.tsexamples/marko/pretext/src/routes/+page.markoexamples/marko/pretext/tsconfig.jsonexamples/marko/pretext/vite.config.tsexamples/marko/scroll-padding/.marko-run/routes.d.tsexamples/marko/scroll-padding/e2e/scroll-padding.spec.tsexamples/marko/scroll-padding/marko.jsonexamples/marko/scroll-padding/package.jsonexamples/marko/scroll-padding/playwright.config.tsexamples/marko/scroll-padding/src/routes/+page.markoexamples/marko/scroll-padding/tsconfig.jsonexamples/marko/scroll-padding/vite.config.tsexamples/marko/smooth-scroll/.marko-run/routes.d.tsexamples/marko/smooth-scroll/e2e/smooth-scroll.spec.tsexamples/marko/smooth-scroll/marko.jsonexamples/marko/smooth-scroll/package.jsonexamples/marko/smooth-scroll/playwright.config.tsexamples/marko/smooth-scroll/src/routes/+page.markoexamples/marko/smooth-scroll/tsconfig.jsonexamples/marko/ssr-fetch/.marko-run/routes.d.tsexamples/marko/ssr-fetch/e2e/ssr-fetch.spec.tsexamples/marko/ssr-fetch/marko.jsonexamples/marko/ssr-fetch/package.jsonexamples/marko/ssr-fetch/playwright.config.tsexamples/marko/ssr-fetch/src/data.tsexamples/marko/ssr-fetch/src/routes/+page.markoexamples/marko/ssr-fetch/tsconfig.jsonexamples/marko/ssr-fetch/vite.config.tsexamples/marko/ssr-restore/.marko-run/routes.d.tsexamples/marko/ssr-restore/e2e/ssr-restore.spec.tsexamples/marko/ssr-restore/marko.jsonexamples/marko/ssr-restore/package.jsonexamples/marko/ssr-restore/playwright.config.tsexamples/marko/ssr-restore/src/data.tsexamples/marko/ssr-restore/src/routes/+page.markoexamples/marko/ssr-restore/tsconfig.jsonexamples/marko/ssr-restore/vite.config.tsexamples/marko/ssr-slice/.marko-run/routes.d.tsexamples/marko/ssr-slice/e2e/ssr-slice.spec.tsexamples/marko/ssr-slice/marko.jsonexamples/marko/ssr-slice/package.jsonexamples/marko/ssr-slice/playwright.config.tsexamples/marko/ssr-slice/src/data.tsexamples/marko/ssr-slice/src/routes/+page.markoexamples/marko/ssr-slice/tsconfig.jsonexamples/marko/ssr-slice/vite.config.tsexamples/marko/ssr/.marko-run/routes.d.tsexamples/marko/ssr/e2e/ssr.spec.tsexamples/marko/ssr/marko.jsonexamples/marko/ssr/package.jsonexamples/marko/ssr/playwright.config.tsexamples/marko/ssr/src/routes/+page.markoexamples/marko/ssr/tsconfig.jsonexamples/marko/ssr/vite.config.tsexamples/marko/sticky/.marko-run/routes.d.tsexamples/marko/sticky/e2e/sticky.spec.tsexamples/marko/sticky/marko.jsonexamples/marko/sticky/package.jsonexamples/marko/sticky/playwright.config.tsexamples/marko/sticky/src/routes/+page.markoexamples/marko/sticky/tsconfig.jsonexamples/marko/sticky/vite.config.tsexamples/marko/table/.marko-run/routes.d.tsexamples/marko/table/e2e/table.spec.tsexamples/marko/table/marko.jsonexamples/marko/table/package.jsonexamples/marko/table/playwright.config.tsexamples/marko/table/src/routes/+page.markoexamples/marko/table/tsconfig.jsonexamples/marko/table/vite.config.tsexamples/marko/variable/.marko-run/routes.d.tsexamples/marko/variable/e2e/variable.spec.tsexamples/marko/variable/marko.jsonexamples/marko/variable/package.jsonexamples/marko/variable/playwright.config.tsexamples/marko/variable/src/routes/+page.markoexamples/marko/variable/tsconfig.jsonexamples/marko/window-ssr-slice/.marko-run/routes.d.tsexamples/marko/window-ssr-slice/e2e/window-ssr-slice.spec.tsexamples/marko/window-ssr-slice/marko.jsonexamples/marko/window-ssr-slice/package.jsonexamples/marko/window-ssr-slice/playwright.config.tsexamples/marko/window-ssr-slice/src/data.tsexamples/marko/window-ssr-slice/src/routes/+page.markoexamples/marko/window-ssr-slice/tsconfig.jsonexamples/marko/window-ssr-slice/vite.config.tsexamples/marko/window/.marko-run/routes.d.tsexamples/marko/window/e2e/window.spec.tsexamples/marko/window/marko.jsonexamples/marko/window/package.jsonexamples/marko/window/playwright.config.tsexamples/marko/window/src/routes/+page.markoexamples/marko/window/tsconfig.jsonpackages/marko-virtual/README.mdpackages/marko-virtual/e2e/option-gates/.marko-run/routes.d.tspackages/marko-virtual/e2e/option-gates/README.mdpackages/marko-virtual/e2e/option-gates/e2e/option-gates.spec.tspackages/marko-virtual/e2e/option-gates/marko.jsonpackages/marko-virtual/e2e/option-gates/package.jsonpackages/marko-virtual/e2e/option-gates/playwright.config.tspackages/marko-virtual/e2e/option-gates/src/routes/+page.markopackages/marko-virtual/e2e/option-gates/src/routes/cached/+page.markopackages/marko-virtual/e2e/option-gates/src/routes/debug/+page.markopackages/marko-virtual/e2e/option-gates/src/routes/enabled/+page.markopackages/marko-virtual/e2e/option-gates/src/routes/lanes-mode/+page.markopackages/marko-virtual/e2e/option-gates/src/routes/measure-element/+page.markopackages/marko-virtual/e2e/option-gates/src/routes/rtl/+page.markopackages/marko-virtual/e2e/option-gates/src/routes/scroll-events/+page.markopackages/marko-virtual/e2e/option-gates/src/routes/scroll-margin/+page.markopackages/marko-virtual/e2e/option-gates/src/routes/window-example/+page.markopackages/marko-virtual/e2e/option-gates/src/routes/window-horizontal/+page.markopackages/marko-virtual/e2e/option-gates/src/routes/window-initial-offset/+page.markopackages/marko-virtual/e2e/option-gates/tsconfig.jsonpackages/marko-virtual/e2e/option-gates/vite.config.tspackages/marko-virtual/marko.jsonpackages/marko-virtual/package.jsonpackages/marko-virtual/src/tags/virtualizer/index.markopackages/marko-virtual/src/tags/virtualizer/options.tspackages/marko-virtual/src/tags/window-virtualizer/index.markopackages/marko-virtual/src/tags/window-virtualizer/options.tspackages/marko-virtual/tests/fixtures/count-update-fixture.markopackages/marko-virtual/tests/fixtures/surface-fixture.markopackages/marko-virtual/tests/fixtures/virtualizer-fixture.markopackages/marko-virtual/tests/fixtures/window-virtualizer-fixture.markopackages/marko-virtual/tests/marko.jsonpackages/marko-virtual/tests/options.test.tspackages/marko-virtual/tests/render-slice.test.tspackages/marko-virtual/tests/tags.test.tspackages/marko-virtual/tsconfig.tags.jsonpackages/marko-virtual/tsconfig.typecheck.jsonpnpm-workspace.yaml
| ;(async () => { | ||
| try { | ||
| const response = await fetch('/api/reply', { | ||
| method: 'POST', | ||
| body: 'demo prompt', | ||
| signal: abort.signal, | ||
| }) | ||
| if (!response.ok || !response.body) { | ||
| throw new Error(`reply failed: ${response.status}`) | ||
| } | ||
| const reader = response.body.getReader() | ||
| const decoder = new TextDecoder() | ||
| for (;;) { | ||
| const { done, value } = await reader.read() | ||
| if (done) break | ||
| const chunk = decoder.decode(value, { stream: true }) | ||
| messages = messages.map((message) => | ||
| message.id === id | ||
| ? { ...message, text: message.text + chunk } | ||
| : message, | ||
| ) | ||
| } | ||
| } catch (error) { | ||
| if (!abort.signal.aborted) throw error | ||
| } finally { | ||
| state.streamAbort = null | ||
| } | ||
| })() |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Unhandled rejection risk on a non-2xx /api/reply response.
The fire-and-forget IIFE rethrows on any error that isn't an intentional abort (if (!abort.signal.aborted) throw error), but nothing awaits or catches the IIFE's returned promise — a real fetch failure becomes an unhandled promise rejection in the browser. Since the e2e suite asserts zero console/page errors, this would also fail CI under a transient network hiccup. As a reference example, it's worth handling explicitly (e.g., surface the failure in status/message text) rather than letting it escape uncaught.
🐛 Proposed fix
} catch (error) {
if (!abort.signal.aborted) throw error
+ } catch (error) {
+ // swallow after abort-check to avoid an unhandled rejection; a real app
+ // would surface this to the UI.
} finally {
state.streamAbort = null
}
})()
+ .catch(() => {})🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/marko/chat/src/routes/`+page.marko around lines 141 - 168, Handle
non-abort errors inside the fire-and-forget IIFE around the fetch in the reply
stream flow instead of rethrowing them as an unhandled rejection. Surface the
failure through the existing status or message state, while preserving silent
handling for intentional aborts and the existing cleanup of state.streamAbort in
finally.
| // Cast to any: @marko/run/vite types are built against Vite 6 but this | ||
| // workspace uses Vite 5. The plugin works correctly at runtime. | ||
| plugins: [marko() as any], |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Confirm the actual vite version resolved for this example and whether `@marko/run/vite`'s
# types compile cleanly against it without the cast.
cat examples/marko/scroll-padding/package.json | grep -i '"vite"'
fd -p 'node_modules/@marko/run' --max-depth 4 -x cat {}/package.json \; 2>/dev/null | grep -i viteRepository: TanStack/virtual
Length of output: 175
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== vite.config.ts =="
cat -n examples/marko/scroll-padding/vite.config.ts
echo
echo "== package.json =="
cat -n examples/marko/scroll-padding/package.json
echo
echo "== search for marko/vite refs =="
rg -n "`@marko/`(run/)?vite|vite" examples/marko/scroll-padding -g '!**/node_modules/**'Repository: TanStack/virtual
Length of output: 1873
Update the Vite comment or drop the cast
This example now pins Vite 6, so the note about Vite 5 is stale. If the as any cast is still required, rewrite the rationale; otherwise remove the cast and comment.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@examples/marko/scroll-padding/vite.config.ts` around lines 5 - 7, Update the
plugin configuration around marko() to reflect the workspace’s Vite 6
dependency: remove the obsolete Vite 5 comment and drop the as any cast if the
Vite 6 types now align; otherwise retain the cast only with a rationale
describing the current type incompatibility.
|
I'll fix this shortly and update here. Thanks |
marko-virtual:
Follow-up to the initial
@tanstack/marko-virtual[PR#1156](#1156), covering the review follow-upsand everything found while hardening.
packages/virtual-coreis untouched: thisPR is Marko-adapter-only. The file count (~260) is dominated by per-example
scaffolding (test suites, tsconfigs, generated route typings, per-example type-check
wiring) across 19 examples; the source-logic changes are concentrated in the two tags
and the example pages. Two commits: the follow-up feature/fix work, and the
packaging restructure to the canonical Marko library shape (details in section 4).
1. SSR support (new architecture in the tags)
The tags are now SSR-safe by construction. On the server, a tag renders either an
empty container with the correct total height, or, when given
initialRect, theactual initial rows into the HTML, computed by a lightweight
renderSlicepass(no live virtualizer instance ever runs on the server). The live, observing instance
is built client-only on mount and takes over on resume ([MarkoJS](https://markojs.com/) v6 does not replay).
Consumer-facing consequence, visible in the 7 pre-existing examples' diffs: the
<let/mounted>+<lifecycle onMount>+<if=mounted>guard scaffolding isdeleted. Nobody needs to hide the virtualizer from the server anymore. Those
pages also migrate to the [tag-variable API](https://markojs.com/docs/guide/marko-5-interop#tags-api-syntax) (
<virtualizer/v .../>, self-closing),the documented shape.
Streaming composes for free: a virtualizer inside a streamed
<await>(with<try>/<@placeholder>) renders its server slice into the mid-stream chunk andresumes independently when that chunk arrives. This is a gated fact, not a claim:
the
ssr-slicesuite includes a raw-HTTP wire test asserting the placeholder flushesfirst and the server-painted rows arrive in a later chunk. Documented with a full
pattern sample in the README and docs (including the out-of-order no-JS caveat).
2. Full option parity
Both tags accept the remaining core options:
scrollMargin,enabled,isRtl,isScrollingResetDelay,useScrollendEvent,useAnimationFrameWithResizeObserver,laneAssignmentMode,useCachedMeasurements,debug, custommeasureElement; thewindow tag adds
horizontalandinitialOffset. Each option is proven by areal-Chromium behavioral test in
packages/marko-virtual/e2e/option-gates(a testrig, excluded from publish via the
filesfield).3. Typed tag variables + type verification
The tag variables (
/v) are typed by exported interfaces,VirtualizerHandleandWindowVirtualizerHandle. The.d.markodeclarations consumers see are buildoutput (see section 4), generated by
[marko-type-check](https://github.com/marko-js/language-server/tree/main/packages/type-check),
never hand-written, never committed.
pnpm test:typesruns mtc in check-only modeover the whole package (sources, tags, tests, fixtures). Every example additionally
has its own
test:typesscript, sopnpm -r --filter "./examples/marko/*" run test:typestype-checks all 19 exampleapps from the CLI.
4. Canonical Marko library packaging (per Marko team guidance)
The package now follows the structure of
marko-js/examples
library, replacing theinitial hand-rolled setup:
marko.jsonis the one-liner{ "exports": "./dist/tags" }. Notags-dir, noscript-lang(both flagged in review as app-style config in a package).pnpm buildruns two tools:vite buildfor the plain-TS entry, thenmarko-type-check -p tsconfig.tags.jsonbuilding the tags intodist/tags,which contains each tag's
.markowith the TypeScript stripped, its generated.d.marko, and compiled helper.js/.d.ts. The tag build's incremental cachelives inside
distso any clean forces a re-emit (a stale cache outside thecleaned dir silently emits nothing).
package.jsonexportsadditionally maps"./dist/tags/*": when a consumer'sapp compiles, Marko emits each discovered tag as a bare specifier through the
package (
@tanstack/marko-virtual/dist/tags/<tag>/index.marko), and theconsumer's bundler resolves that through the exports map. Without the subpath,
every real consumer build fails; verified on packed tarballs in fresh consumer
apps (npm and pnpm layouts, @marko/vite 5 and 6).
filesshipsdist+marko.json+ README only; rawsrc/tagsno longerpublishes; the committed
.d.markofiles and their generator script are retired.the tags from source via a small nested
marko.jsonwithtags-dir, givingfull types with no build-order dependency, with the examples' tsconfigs
include-ing the source tags (build-mode type-checking requires listingcross-package files).
language-toolsissuemakes installed-package tag types fail to load under pnpm's symlinked layout
(falls back to a permissive read-only default); npm/yarn consumers and runtime
behavior everywhere are unaffected; consumer stopgap
node-linker=hoisted.5. Twelve new examples (7 to 19)
padding,scroll-padding,sticky,table,pretext: one per corefeature area (padding offsets, scroll padding, sticky headers, table semantics,
calculated text heights via
@chenglou/pretext).chat: bottom-anchored messaging withanchorTo="end",followOnAppend,history prepends that keep the visible message anchored, near-top auto-load with
a load-ahead trigger, and a real streamed reply over the network (marko-run
+handlerreturning a chunkedReadableStream).chat-pretext: chat rebuilt on calculated heights. Prepends land withzero scroll correction at any width (the suite asserts the prepend landed AND
produced at most the single compensation jump), and the streamed reply pushes its
growing height through
v.resizeItemper chunk (that API's first real consumer).behavior and raw view-source HTML in its suite:
ssr(no fetch, empty container, rows on client),ssr-fetch(server fetch,client rows),
ssr-slice(server fetch, rows in the server HTML viainitialRect, plus the streaming wire gate),ssr-restore(server rows at ascroll offset),
window-ssr-slice(server rows for the window tag).6. Tests
tags.test.tsexpanded; newoptions.test.ts(option plumbing)and
render-slice.test.ts(SSR seed math); fixtures updated plus a new surfacefixture.
share port 4173, so they run sequentially (command below).
7. Fixes
sideEffectscorrected to["**/*.marko"](wasfalse): Marko templatesregister their renderers and resume effects via module side effects, so
sideEffects: falselet production bundlers tree-shake a tag's client module onany page that uses the tag render-only (no function references). The resume
payload then points at registrations that never ran:
TypeError: effects[(i++)] is not a function, zero hydration. Invisible in dev and todev-mode suites; only surfaced by production-previewing a simple example.
Note for maintainers: the published 3.14.x carries this bug. Render-only
consumers' prod builds are broken until this ships.
(rows in a keyed loop lose their subscription to a page-level signal) which broke
the chat example's streamed reply in prod builds (text arrived and was stored,
but never painted). Reproduced in a 25-line page with no TanStack code, bisected
to the fix in marko 6.1.10, verified end-to-end here.
ahead of the top (prepends land above the viewport; no hard-stop jolt), and
overflow-anchor: noneon the scroller (the virtualizer owns prependcompensation; native browser anchoring could double-compensate).
symlink formed a directory cycle that crashed
marko-run build(ENAMETOOLONG)via
@marko/vite's production template scan.tag-variable inference cycles,
String(item.key)for<for by>, thesecond-parameter event-handler idiom instead of
currentTarget, which Marko'sdelegated events reject at runtime).
8. Toolchain & workspace modernization
@marko/run^0.7 to ^0.10,markoto ^6.2.2; package devDeps to@marko/vite^6.1.0.workspace:*to^3.14.0) soany example folder works when copied out of the monorepo; each example also
declares the
@marko/type-check+@marko/language-toolspair (both are neededfor the CLI type checks: pnpm exposes only a project's own bins, and
language-toolsneeds itsmarkopeer resolved from the declaring project).@playwright/testaligned to the root's^1.53.1everywhere;sherifpasses.pnpm-workspace.yaml: single@marko/compiler(5.40.0) enforced via override(two compiler instances in the graph break taglib discovery; comment inline),
plus the option-gates workspace entry.
.marko-run/routes.d.ts(marko-run's generated routetypings), the same convention as marko-run's own examples: typed routes on a
fresh clone, and route-surface changes show up in review.
.gitignore:**/*.tsbuildinfo(incremental-build cache from the newper-example tsconfigs).
9. Docs
docs/framework/marko/marko-virtual.mdsubstantially expanded (full inputreference for both tags, SSR guide including streaming via
<await>and theno-JS caveat, TypeScript guide); the package README adds the build/packaging
story, the streaming section, the pnpm consumer limitation, and a
production-sanity note naming both manual canaries (a render-only example AND
the interactive chat);
docs/installation.mdgains the Marko section;docs/config.jsonlists all 19 examples.How to verify
✅ Checklist
pnpm run test:pr.🚀 Release Impact
Summary by CodeRabbit
New Features
Documentation
Tests