Skip to content

feat(marko-virtual): post-release follow-ups: full option parity, streaming SSR Support with Resume, typed handles, e2e coverage, examples#1219

Open
defunkt-dev wants to merge 3 commits into
TanStack:mainfrom
defunkt-dev:main
Open

feat(marko-virtual): post-release follow-ups: full option parity, streaming SSR Support with Resume, typed handles, e2e coverage, examples#1219
defunkt-dev wants to merge 3 commits into
TanStack:mainfrom
defunkt-dev:main

Conversation

@defunkt-dev

@defunkt-dev defunkt-dev commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

marko-virtual:

SSR + Streaming support, full option parity, typed handles, 12 new examples, e2e coverage, canonical Marko packaging

Follow-up to the initial @tanstack/marko-virtual [PR#1156](#1156), covering the review follow-ups
and everything found while hardening. packages/virtual-core is untouched: this
PR 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, the
actual initial rows into the HTML, computed by a lightweight renderSlice pass
(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 is
deleted. 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 and
resumes independently when that chunk arrives. This is a gated fact, not a claim:
the ssr-slice suite includes a raw-HTTP wire test asserting the placeholder flushes
first 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, custom measureElement; the
window tag adds horizontal and initialOffset. Each option is proven by a
real-Chromium behavioral test in packages/marko-virtual/e2e/option-gates (a test
rig, excluded from publish via the files field).

3. Typed tag variables + type verification

The tag variables (/v) are typed by exported interfaces, VirtualizerHandle and
WindowVirtualizerHandle. The .d.marko declarations consumers see are build
output
(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:types runs mtc in check-only mode
over the whole package (sources, tags, tests, fixtures). Every example additionally
has its own test:types script, so
pnpm -r --filter "./examples/marko/*" run test:types type-checks all 19 example
apps from the CLI.

4. Canonical Marko library packaging (per Marko team guidance)

The package now follows the structure of
marko-js/examples library, replacing the
initial hand-rolled setup:

  • marko.json is the one-liner { "exports": "./dist/tags" }. No tags-dir, no
    script-lang (both flagged in review as app-style config in a package).
  • pnpm build runs two tools: vite build for the plain-TS entry, then
    marko-type-check -p tsconfig.tags.json building the tags into dist/tags,
    which contains each tag's .marko with the TypeScript stripped, its generated
    .d.marko, and compiled helper .js/.d.ts. The tag build's incremental cache
    lives inside dist so any clean forces a re-emit (a stale cache outside the
    cleaned dir silently emits nothing).
  • package.json exports additionally maps "./dist/tags/*": when a consumer's
    app compiles, Marko emits each discovered tag as a bare specifier through the
    package (@tanstack/marko-virtual/dist/tags/<tag>/index.marko), and the
    consumer'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).
  • files ships dist + marko.json + README only; raw src/tags no longer
    publishes; the committed .d.marko files and their generator script are retired.
  • In-repo consumers (the package's own test fixtures, and every example) resolve
    the tags from source via a small nested marko.json with tags-dir, giving
    full types with no build-order dependency, with the examples' tsconfigs
    include-ing the source tags (build-mode type-checking requires listing
    cross-package files).
  • Known limitation documented in the README: an open Marko language-tools issue
    makes 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 core
    feature area (padding offsets, scroll padding, sticky headers, table semantics,
    calculated text heights via @chenglou/pretext).
  • chat: bottom-anchored messaging with anchorTo="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
    +handler returning a chunked ReadableStream).
  • chat-pretext: chat rebuilt on calculated heights. Prepends land with
    zero 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.resizeItem per chunk (that API's first real consumer).
  • Five SSR examples covering the server-rendering matrix, each asserting resume
    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 via
    initialRect, plus the streaming wire gate), ssr-restore (server rows at a
    scroll offset
    ), window-ssr-slice (server rows for the window tag).

6. Tests

  • Unit (66): tags.test.ts expanded; new options.test.ts (option plumbing)
    and render-slice.test.ts (SSR seed math); fixtures updated plus a new surface
    fixture.
  • Browser e2e for every example (19 suites) plus the option-gate proofs. Suites
    share port 4173, so they run sequentially (command below).
  • Per-example CLI type checks (see section 3).

7. Fixes

  • sideEffects corrected to ["**/*.marko"] (was false): Marko templates
    register their renderers and resume effects via module side effects, so
    sideEffects: false let production bundlers tree-shake a tag's client module on
    any 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 to
    dev-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.
  • marko bumped to ^6.2.2: marko 6.1.9 and below had a production-only bug
    (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.
  • Chat: measured size estimate (74 to 88), history loads about 1.5 viewports
    ahead of the top (prepends land above the viewport; no hard-stop jolt), and
    overflow-anchor: none on the scroller (the virtualizer owns prepend
    compensation; native browser anchoring could double-compensate).
  • option-gates no longer depends on its ancestor package: the pnpm workspace
    symlink formed a directory cycle that crashed marko-run build (ENAMETOOLONG)
    via @marko/vite's production template scan.
  • TypeScript-strict cleanup across example pages (annotated closures to break
    tag-variable inference cycles, String(item.key) for <for by>, the
    second-parameter event-handler idiom instead of currentTarget, which Marko's
    delegated events reject at runtime).

8. Toolchain & workspace modernization

  • Examples moved to the current Marko stack: @marko/run ^0.7 to ^0.10,
    marko to ^6.2.2; package devDeps to @marko/vite ^6.1.0.
  • Example dependencies pinned to published versions (workspace:* to ^3.14.0) so
    any example folder works when copied out of the monorepo; each example also
    declares the @marko/type-check + @marko/language-tools pair (both are needed
    for the CLI type checks: pnpm exposes only a project's own bins, and
    language-tools needs its marko peer resolved from the declaring project).
  • @playwright/test aligned to the root's ^1.53.1 everywhere; sherif passes.
  • 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.
  • Each example commits its .marko-run/routes.d.ts (marko-run's generated route
    typings), 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 new
    per-example tsconfigs).

9. Docs

docs/framework/marko/marko-virtual.md substantially expanded (full input
reference for both tags, SSR guide including streaming via <await> and the
no-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.md gains the Marko section;
docs/config.json lists all 19 examples.

How to verify

pnpm install
pnpm --filter @tanstack/virtual-core build
pnpm --filter @tanstack/marko-virtual build
ls packages/marko-virtual/dist/tags   # stripped .marko + generated .d.marko per tag

# unit tests (66)
pnpm --filter @tanstack/marko-virtual exec vitest run

# type verification: the package, then every example app
pnpm --filter @tanstack/marko-virtual run test:types
pnpm -r --workspace-concurrency=1 --filter "./examples/marko/*" run test:types

# all 19 example browser suites (sequential, shared port)
npx playwright install chromium   # one-time
pnpm -r --workspace-concurrency=1 --filter "./examples/marko/*" run test:e2e

# option-gate behavioral proofs
cd packages/marko-virtual/e2e/option-gates && npm run test:e2e && cd -

# workspace lint + published-shape check
pnpm run test:sherif
pnpm --filter @tanstack/marko-virtual run test:build

# production sanity, BOTH canaries:
# render-only (the sideEffects fix, section 7; rows must appear):
pnpm --filter tanstack-marko-virtual-example-fixed build
PORT=3000 pnpm --filter tanstack-marko-virtual-example-fixed preview
# interactive (the marko 6.2.2 fix; reply streams into the bubble live):
pnpm --filter tanstack-marko-virtual-example-chat build
PORT=3000 pnpm --filter tanstack-marko-virtual-example-chat preview

✅ Checklist

🚀 Release Impact

Summary by CodeRabbit

  • New Features

    • Marko virtualization now supports broader option coverage, including scrolling, anchoring, measurement, RTL, caching, lanes, and window virtualization.
    • Added tag handles exposing virtual items, sizing, ranges, scrolling controls, and end-of-list status.
    • Added SSR rendering modes, initial slices, scroll restoration, and streaming support.
    • Added examples for chat, variable sizing, sticky lists, tables, grids, padding, and SSR scenarios.
  • Documentation

    • Expanded installation, API, SSR, streaming, and example guidance.
  • Tests

    • Added comprehensive browser and behavior coverage across supported options and examples.

@defunkt-dev
defunkt-dev requested a review from a team as a code owner July 5, 2026 23:50
@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The 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.

Changes

Marko virtual adapter

Layer / File(s) Summary
Returned handles and option parity
packages/marko-virtual/src/tags/*
Virtualizer and window-virtualizer tags return typed handles, expose range and scrolling methods, forward additional options, and seed server-rendered slices.
Build and type-check pipeline
packages/marko-virtual/package.json, packages/marko-virtual/tsconfig.*, pnpm-workspace.yaml
Package outputs use built tags, Marko type checking is added, and option-gate projects are included in the workspace.
Examples and browser coverage
examples/marko/*, packages/marko-virtual/e2e/option-gates/*
Examples use tag variables and callback scroll-element access, while Playwright suites cover virtualization, SSR, scrolling, measurement, and option gates.
Tests and documentation
packages/marko-virtual/tests/*, docs/*, packages/marko-virtual/README.md
Fixtures and unit tests validate returned handles and SSR slices; documentation describes the expanded API and usage patterns.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested reviewers: copilot, piecyk

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is specific and matches the main change set: Marko parity, SSR, typed handles, tests, and examples.
Description check ✅ Passed The description covers the PR goals, checklist, and release impact, though it does not follow the template headings exactly.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@defunkt-dev defunkt-dev changed the title feat(marko-virtual): post-release follow-ups — full option parity, typed handles, e2e coverage, new Chat + Pretext example feat(marko-virtual): post-release follow-ups: full option parity, typed handles, e2e coverage, new Chat + Pretext example Jul 5, 2026
@defunkt-dev defunkt-dev changed the title feat(marko-virtual): post-release follow-ups: full option parity, typed handles, e2e coverage, new Chat + Pretext example feat(marko-virtual): post-release follow-ups: full option parity, SSR Support, typed handles, e2e coverage, examples Jul 5, 2026
@defunkt-dev defunkt-dev changed the title feat(marko-virtual): post-release follow-ups: full option parity, SSR Support, typed handles, e2e coverage, examples feat(marko-virtual): post-release follow-ups: full option parity, Streaming SSR Support, typed handles, e2e coverage, examples Jul 5, 2026
@socket-security

socket-security Bot commented Jul 5, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​marko/​language-tools@​2.6.38010073100100
Added@​marko/​type-check@​3.1.17610090100100
Added@​marko/​vite@​6.1.27710010099100
Added@​marko/​run@​0.10.08010010098100
Addedmarko@​6.3.38110094100100

View full report

@socket-security

socket-security Bot commented Jul 5, 2026

Copy link
Copy Markdown

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.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Obfuscated code: npm @emnapi/runtime is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: pnpm-lock.yamlnpm/@marko/run@0.10.0npm/@emnapi/runtime@1.11.1

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@emnapi/runtime@1.11.1. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm @marko/compiler is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: pnpm-lock.yamlnpm/@marko/run@0.10.0npm/@marko/type-check@3.1.1npm/@marko/language-tools@2.6.3npm/marko@6.3.3npm/@marko/vite@6.1.2npm/@marko/compiler@5.40.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@marko/compiler@5.40.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn Medium
Low adoption: npm @marko/language-tools

Location: Package overview

From: examples/marko/chat-pretext/package.jsonnpm/@marko/language-tools@2.6.3

ℹ Read more on: This package | This alert | What are unpopular packages?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Unpopular packages may have less maintenance and contain other problems.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@marko/language-tools@2.6.3. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn Medium
Low adoption: npm @marko/run

Location: Package overview

From: examples/marko/chat-pretext/package.jsonnpm/@marko/run@0.10.0

ℹ Read more on: This package | This alert | What are unpopular packages?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Unpopular packages may have less maintenance and contain other problems.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@marko/run@0.10.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn Medium
Low adoption: npm @marko/type-check

Location: Package overview

From: examples/marko/chat-pretext/package.jsonnpm/@marko/type-check@3.1.1

ℹ Read more on: This package | This alert | What are unpopular packages?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Unpopular packages may have less maintenance and contain other problems.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@marko/type-check@3.1.1. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@defunkt-dev

Copy link
Copy Markdown
Contributor Author

@coderabbitai help

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

CodeRabbit commands

These commands are invoked using PR/Issue comments.

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai rate limit to show your current review rate limit status.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai evaluate custom pre-merge check --instructions <custom-checks-instructions> --name <custom-checks-title> [--mode <error|warning>] to validate the custom pre-merge checks instructions. Defaults to error when --mode is omitted.
  • @coderabbitai ignore pre-merge checks to override pre-merge checks and get an approval on PR.
  • @coderabbitai run pre-merge checks to run pre-merge checks on the pull request.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai emit path instructions to emit generated path-specific review instructions for this repository.
  • @coderabbitai resolve merge conflict to automatically resolve merge conflicts.
  • @coderabbitai autofix to automatically fix issues identified in unresolved review comments.
  • @coderabbitai resolve to resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai generate configuration to create a PR that adds the current resolved configuration as .coderabbit.yaml (or show it if already present).
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit configuration file (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, support, documentation and community

  • Visit our status page to check the current availability of CodeRabbit.
  • Create a ticket on our support page for assistance with any issues or questions.
  • Visit our documentation site for detailed information on how to use CodeRabbit.
  • Join our Discord community to connect with other users and get help from the community.
  • Follow us on X/Twitter for updates and announcements.

@defunkt-dev

Copy link
Copy Markdown
Contributor Author

@coderabbitai summary

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
✅ Action performed

Summary regeneration triggered.

@defunkt-dev defunkt-dev changed the title feat(marko-virtual): post-release follow-ups: full option parity, Streaming SSR Support, typed handles, e2e coverage, examples feat(marko-virtual): post-release follow-ups: full option parity, streaming SSR Support with Resume, typed handles, e2e coverage, examples Jul 6, 2026
@defunkt-dev
defunkt-dev force-pushed the main branch 2 times, most recently from 4bd71a2 to fb05f90 Compare July 6, 2026 00:27
@defunkt-dev

Copy link
Copy Markdown
Contributor Author

To build

pnpm 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:e2e

To run examples

pnpm --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

…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
@piecyk

piecyk commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

@copilot resolve the merge conflicts in this pull request

@nx-cloud

nx-cloud Bot commented Jul 20, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 99185f0


☁️ Nx Cloud last updated this comment at 2026-07-20 10:32:47 UTC

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Comment on lines +34 to +35
Publishing: not affected — the package's `files` field publishes only dist,
src/tags, marko.json, and README.md, so nothing under e2e/ reaches npm.
Comment on lines +9 to +10
named handle types (VirtualizerHandle / WindowVirtualizerHandle) with committed
.d.marko declarations verified by marko-type-check in CI, a new Chat + Pretext

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (7)
examples/marko/ssr-slice/vite.config.ts (1)

5-7: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the Vite 5 cast and comment. examples/marko/ssr-slice/package.json already uses Vite 6, so marko() as any and 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 win

Unbounded module-scoped cache growth on the server.

preparedCache is a static const (module scope, shared across all requests/instances) that only grows — entries are added in getPrepared but the only eviction (preparedCache.clear()) happens inside a client-only onMount/document.fonts.ready callback (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 value

Add a comment explaining the as any cast.

To improve maintainability, consider adding a comment explaining why marko() is cast to any (e.g., due to Vite version type mismatches), similar to the comment found in examples/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 value

Hardcoded URL duplicates playwright.config.ts's baseURL.

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 value

Use a top-level type import for Page.

For improved readability, consider using a top-level import type { Page } instead of an inline import() 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 value

Remove stale comment and verify type cast.

The comment mentions that this workspace uses Vite 5, but the package.json for this example specifies "vite": "^6.4.2". Consider removing the stale comment and verifying if the as any cast 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 value

Consider verifying if the as any cast is still required.

The comment indicates a Vite 5 vs Vite 6 type mismatch, but this specific example's package.json explicitly 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 the as any cast 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

📥 Commits

Reviewing files that changed from the base of the PR and between e2cb096 and 99185f0.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (196)
  • .changeset/marko-virtual-followups.md
  • .gitignore
  • docs/config.json
  • docs/framework/marko/marko-virtual.md
  • docs/installation.md
  • examples/marko/chat-pretext/.marko-run/routes.d.ts
  • examples/marko/chat-pretext/e2e/README.md
  • examples/marko/chat-pretext/e2e/chat-pretext.spec.ts
  • examples/marko/chat-pretext/marko.json
  • examples/marko/chat-pretext/package.json
  • examples/marko/chat-pretext/playwright.config.ts
  • examples/marko/chat-pretext/src/routes/+page.marko
  • examples/marko/chat-pretext/src/routes/api/reply/+handler.ts
  • examples/marko/chat-pretext/tsconfig.json
  • examples/marko/chat-pretext/vite.config.ts
  • examples/marko/chat/.marko-run/routes.d.ts
  • examples/marko/chat/e2e/README.md
  • examples/marko/chat/e2e/chat.spec.ts
  • examples/marko/chat/marko.json
  • examples/marko/chat/package.json
  • examples/marko/chat/playwright.config.ts
  • examples/marko/chat/src/routes/+page.marko
  • examples/marko/chat/src/routes/api/reply/+handler.ts
  • examples/marko/chat/tsconfig.json
  • examples/marko/chat/vite.config.ts
  • examples/marko/dynamic/.marko-run/routes.d.ts
  • examples/marko/dynamic/e2e/dynamic.spec.ts
  • examples/marko/dynamic/marko.json
  • examples/marko/dynamic/package.json
  • examples/marko/dynamic/playwright.config.ts
  • examples/marko/dynamic/src/routes/+page.marko
  • examples/marko/dynamic/tsconfig.json
  • examples/marko/fixed/.marko-run/routes.d.ts
  • examples/marko/fixed/e2e/fixed.spec.ts
  • examples/marko/fixed/marko.json
  • examples/marko/fixed/package.json
  • examples/marko/fixed/playwright.config.ts
  • examples/marko/fixed/src/routes/+page.marko
  • examples/marko/fixed/tsconfig.json
  • examples/marko/grid/.marko-run/routes.d.ts
  • examples/marko/grid/e2e/grid.spec.ts
  • examples/marko/grid/marko.json
  • examples/marko/grid/package.json
  • examples/marko/grid/playwright.config.ts
  • examples/marko/grid/src/routes/+page.marko
  • examples/marko/grid/tsconfig.json
  • examples/marko/infinite-scroll/.marko-run/routes.d.ts
  • examples/marko/infinite-scroll/e2e/infinite-scroll.spec.ts
  • examples/marko/infinite-scroll/marko.json
  • examples/marko/infinite-scroll/package.json
  • examples/marko/infinite-scroll/playwright.config.ts
  • examples/marko/infinite-scroll/src/routes/+page.marko
  • examples/marko/infinite-scroll/tsconfig.json
  • examples/marko/padding/.marko-run/routes.d.ts
  • examples/marko/padding/e2e/padding.spec.ts
  • examples/marko/padding/marko.json
  • examples/marko/padding/package.json
  • examples/marko/padding/playwright.config.ts
  • examples/marko/padding/src/routes/+page.marko
  • examples/marko/padding/tsconfig.json
  • examples/marko/padding/vite.config.ts
  • examples/marko/pretext/.marko-run/routes.d.ts
  • examples/marko/pretext/e2e/pretext.spec.ts
  • examples/marko/pretext/marko.json
  • examples/marko/pretext/package.json
  • examples/marko/pretext/playwright.config.ts
  • examples/marko/pretext/src/routes/+page.marko
  • examples/marko/pretext/tsconfig.json
  • examples/marko/pretext/vite.config.ts
  • examples/marko/scroll-padding/.marko-run/routes.d.ts
  • examples/marko/scroll-padding/e2e/scroll-padding.spec.ts
  • examples/marko/scroll-padding/marko.json
  • examples/marko/scroll-padding/package.json
  • examples/marko/scroll-padding/playwright.config.ts
  • examples/marko/scroll-padding/src/routes/+page.marko
  • examples/marko/scroll-padding/tsconfig.json
  • examples/marko/scroll-padding/vite.config.ts
  • examples/marko/smooth-scroll/.marko-run/routes.d.ts
  • examples/marko/smooth-scroll/e2e/smooth-scroll.spec.ts
  • examples/marko/smooth-scroll/marko.json
  • examples/marko/smooth-scroll/package.json
  • examples/marko/smooth-scroll/playwright.config.ts
  • examples/marko/smooth-scroll/src/routes/+page.marko
  • examples/marko/smooth-scroll/tsconfig.json
  • examples/marko/ssr-fetch/.marko-run/routes.d.ts
  • examples/marko/ssr-fetch/e2e/ssr-fetch.spec.ts
  • examples/marko/ssr-fetch/marko.json
  • examples/marko/ssr-fetch/package.json
  • examples/marko/ssr-fetch/playwright.config.ts
  • examples/marko/ssr-fetch/src/data.ts
  • examples/marko/ssr-fetch/src/routes/+page.marko
  • examples/marko/ssr-fetch/tsconfig.json
  • examples/marko/ssr-fetch/vite.config.ts
  • examples/marko/ssr-restore/.marko-run/routes.d.ts
  • examples/marko/ssr-restore/e2e/ssr-restore.spec.ts
  • examples/marko/ssr-restore/marko.json
  • examples/marko/ssr-restore/package.json
  • examples/marko/ssr-restore/playwright.config.ts
  • examples/marko/ssr-restore/src/data.ts
  • examples/marko/ssr-restore/src/routes/+page.marko
  • examples/marko/ssr-restore/tsconfig.json
  • examples/marko/ssr-restore/vite.config.ts
  • examples/marko/ssr-slice/.marko-run/routes.d.ts
  • examples/marko/ssr-slice/e2e/ssr-slice.spec.ts
  • examples/marko/ssr-slice/marko.json
  • examples/marko/ssr-slice/package.json
  • examples/marko/ssr-slice/playwright.config.ts
  • examples/marko/ssr-slice/src/data.ts
  • examples/marko/ssr-slice/src/routes/+page.marko
  • examples/marko/ssr-slice/tsconfig.json
  • examples/marko/ssr-slice/vite.config.ts
  • examples/marko/ssr/.marko-run/routes.d.ts
  • examples/marko/ssr/e2e/ssr.spec.ts
  • examples/marko/ssr/marko.json
  • examples/marko/ssr/package.json
  • examples/marko/ssr/playwright.config.ts
  • examples/marko/ssr/src/routes/+page.marko
  • examples/marko/ssr/tsconfig.json
  • examples/marko/ssr/vite.config.ts
  • examples/marko/sticky/.marko-run/routes.d.ts
  • examples/marko/sticky/e2e/sticky.spec.ts
  • examples/marko/sticky/marko.json
  • examples/marko/sticky/package.json
  • examples/marko/sticky/playwright.config.ts
  • examples/marko/sticky/src/routes/+page.marko
  • examples/marko/sticky/tsconfig.json
  • examples/marko/sticky/vite.config.ts
  • examples/marko/table/.marko-run/routes.d.ts
  • examples/marko/table/e2e/table.spec.ts
  • examples/marko/table/marko.json
  • examples/marko/table/package.json
  • examples/marko/table/playwright.config.ts
  • examples/marko/table/src/routes/+page.marko
  • examples/marko/table/tsconfig.json
  • examples/marko/table/vite.config.ts
  • examples/marko/variable/.marko-run/routes.d.ts
  • examples/marko/variable/e2e/variable.spec.ts
  • examples/marko/variable/marko.json
  • examples/marko/variable/package.json
  • examples/marko/variable/playwright.config.ts
  • examples/marko/variable/src/routes/+page.marko
  • examples/marko/variable/tsconfig.json
  • examples/marko/window-ssr-slice/.marko-run/routes.d.ts
  • examples/marko/window-ssr-slice/e2e/window-ssr-slice.spec.ts
  • examples/marko/window-ssr-slice/marko.json
  • examples/marko/window-ssr-slice/package.json
  • examples/marko/window-ssr-slice/playwright.config.ts
  • examples/marko/window-ssr-slice/src/data.ts
  • examples/marko/window-ssr-slice/src/routes/+page.marko
  • examples/marko/window-ssr-slice/tsconfig.json
  • examples/marko/window-ssr-slice/vite.config.ts
  • examples/marko/window/.marko-run/routes.d.ts
  • examples/marko/window/e2e/window.spec.ts
  • examples/marko/window/marko.json
  • examples/marko/window/package.json
  • examples/marko/window/playwright.config.ts
  • examples/marko/window/src/routes/+page.marko
  • examples/marko/window/tsconfig.json
  • packages/marko-virtual/README.md
  • packages/marko-virtual/e2e/option-gates/.marko-run/routes.d.ts
  • packages/marko-virtual/e2e/option-gates/README.md
  • packages/marko-virtual/e2e/option-gates/e2e/option-gates.spec.ts
  • packages/marko-virtual/e2e/option-gates/marko.json
  • packages/marko-virtual/e2e/option-gates/package.json
  • packages/marko-virtual/e2e/option-gates/playwright.config.ts
  • packages/marko-virtual/e2e/option-gates/src/routes/+page.marko
  • packages/marko-virtual/e2e/option-gates/src/routes/cached/+page.marko
  • packages/marko-virtual/e2e/option-gates/src/routes/debug/+page.marko
  • packages/marko-virtual/e2e/option-gates/src/routes/enabled/+page.marko
  • packages/marko-virtual/e2e/option-gates/src/routes/lanes-mode/+page.marko
  • packages/marko-virtual/e2e/option-gates/src/routes/measure-element/+page.marko
  • packages/marko-virtual/e2e/option-gates/src/routes/rtl/+page.marko
  • packages/marko-virtual/e2e/option-gates/src/routes/scroll-events/+page.marko
  • packages/marko-virtual/e2e/option-gates/src/routes/scroll-margin/+page.marko
  • packages/marko-virtual/e2e/option-gates/src/routes/window-example/+page.marko
  • packages/marko-virtual/e2e/option-gates/src/routes/window-horizontal/+page.marko
  • packages/marko-virtual/e2e/option-gates/src/routes/window-initial-offset/+page.marko
  • packages/marko-virtual/e2e/option-gates/tsconfig.json
  • packages/marko-virtual/e2e/option-gates/vite.config.ts
  • packages/marko-virtual/marko.json
  • packages/marko-virtual/package.json
  • packages/marko-virtual/src/tags/virtualizer/index.marko
  • packages/marko-virtual/src/tags/virtualizer/options.ts
  • packages/marko-virtual/src/tags/window-virtualizer/index.marko
  • packages/marko-virtual/src/tags/window-virtualizer/options.ts
  • packages/marko-virtual/tests/fixtures/count-update-fixture.marko
  • packages/marko-virtual/tests/fixtures/surface-fixture.marko
  • packages/marko-virtual/tests/fixtures/virtualizer-fixture.marko
  • packages/marko-virtual/tests/fixtures/window-virtualizer-fixture.marko
  • packages/marko-virtual/tests/marko.json
  • packages/marko-virtual/tests/options.test.ts
  • packages/marko-virtual/tests/render-slice.test.ts
  • packages/marko-virtual/tests/tags.test.ts
  • packages/marko-virtual/tsconfig.tags.json
  • packages/marko-virtual/tsconfig.typecheck.json
  • pnpm-workspace.yaml

Comment on lines +141 to +168
;(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
}
})()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 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.

Comment on lines +5 to +7
// 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],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 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 vite

Repository: 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.

@defunkt-dev

Copy link
Copy Markdown
Contributor Author

I'll fix this shortly and update here. Thanks

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