Skip to content

perf(web): load Shiki language grammars lazily instead of in the eager core chunk - #3496

Open
fanzeyi wants to merge 1 commit into
omnigent-ai:mainfrom
fanzeyi:perf/web-shiki-lazy-langs
Open

perf(web): load Shiki language grammars lazily instead of in the eager core chunk#3496
fanzeyi wants to merge 1 commit into
omnigent-ai:mainfrom
fanzeyi:perf/web-shiki-lazy-langs

Conversation

@fanzeyi

@fanzeyi fanzeyi commented Jul 29, 2026

Copy link
Copy Markdown
Member

Related issue

N/A

Summary

  • The manualChunks guard that keeps Shiki in one chunk (to avoid a cyclic
    import crash — the language-index ↔ alias-map split that throws "Cannot read
    properties of undefined (reading 'flatMap')" and blanks the Monaco/file
    viewer) matched both /shiki and /@shikijs/. That also swept every
    @shikijs/langs/<lang> grammar — which Shiki loads via dynamic import as
    per-language chunks — into the single, eagerly modulepreloaded core chunk.
    So ~200 language grammars (~1.68 MB gzip) were downloaded on every initial
    page load, even though a session uses only a few languages.
  • Exclude @shikijs/langs/<lang> from the shiki chunk so grammars stay lazy
    per-language chunks. Keep Shiki's core, engines, and bundle glue together so
    the cyclic core stays intra-chunk — the engines must stay too: excluding them
    re-splits the cycle across chunks and reintroduces the flatMap crash.
  • Initial-load eager JS drops from ~11.8 MB to ~4.37 MB (Shiki 1.68 MB → 466 KB
    gzip); grammars become 427 on-demand chunks. Layout-independent (same result
    under pnpm hoisted and isolated).

Test Plan

  • pnpm --filter web run build succeeds.
  • Verified the emitted shiki chunk statically imports only the rolldown
    runtime (no cross-chunk cycle) and contains bundledLanguagesAlias
    co-located with its reader — under both hoisted and isolated node_modules.
  • Verified per-language grammar chunks (python, rust, typescript, …) are
    emitted separately and are NOT modulepreloaded by index.html.
  • Recommended pre-merge smoke test: open the file viewer / Monaco editor and a
    markdown code block and confirm syntax highlighting renders.

Demo

N/A

Type of change

  • Bug fix
  • Feature
  • UI / frontend change
  • Refactor / chore
  • Docs
  • Test / CI
  • Breaking change

Test coverage

  • Unit tests added / updated
  • Integration tests added / updated
  • E2E tests added / updated
  • Manual verification completed
  • Existing tests cover this change
  • Not applicable

Coverage notes

Verified via build output analysis: the shiki core chunk is acyclic with the
alias map co-located (cycle fix preserved), and language grammars are emitted as
separate, non-preloaded chunks. Existing Shiki/code-block tests exercise the
runtime highlighting path; this change only affects chunk grouping, not module
behavior.

Changelog

Faster initial load — syntax-highlighting language grammars now load on demand instead of all upfront.

Signed-off-by: Zeyi (Rice) Fan zeyi.f@databricks.com

…r core chunk

## Related issue

N/A

## Summary

- The `manualChunks` guard that keeps Shiki in one chunk (to avoid a cyclic
  import crash — the language-index ↔ alias-map split that throws "Cannot read
  properties of undefined (reading 'flatMap')" and blanks the Monaco/file
  viewer) matched both `/shiki` and `/@shikijs/`. That also swept every
  `@shikijs/langs/<lang>` grammar — which Shiki loads via dynamic import as
  per-language chunks — into the single, eagerly `modulepreload`ed core chunk.
  So ~200 language grammars (~1.68 MB gzip) were downloaded on every initial
  page load, even though a session uses only a few languages.
- Exclude `@shikijs/langs/<lang>` from the `shiki` chunk so grammars stay lazy
  per-language chunks. Keep Shiki's core, engines, and bundle glue together so
  the cyclic core stays intra-chunk — the engines must stay too: excluding them
  re-splits the cycle across chunks and reintroduces the `flatMap` crash.
- Initial-load eager JS drops from ~11.8 MB to ~4.37 MB (Shiki 1.68 MB → 466 KB
  gzip); grammars become 427 on-demand chunks. Layout-independent (same result
  under pnpm hoisted and isolated).

## Test Plan

- `pnpm --filter web run build` succeeds.
- Verified the emitted `shiki` chunk statically imports only the rolldown
  runtime (no cross-chunk cycle) and contains `bundledLanguagesAlias`
  co-located with its reader — under both hoisted and isolated node_modules.
- Verified per-language grammar chunks (python, rust, typescript, …) are
  emitted separately and are NOT `modulepreload`ed by `index.html`.
- Recommended pre-merge smoke test: open the file viewer / Monaco editor and a
  markdown code block and confirm syntax highlighting renders.

## Demo

N/A

## Type of change

- [ ] Bug fix
- [ ] Feature
- [ ] UI / frontend change
- [x] Refactor / chore
- [ ] Docs
- [ ] Test / CI
- [ ] Breaking change

## Test coverage

- [ ] Unit tests added / updated
- [ ] Integration tests added / updated
- [ ] E2E tests added / updated
- [x] Manual verification completed
- [x] Existing tests cover this change
- [ ] Not applicable

## Coverage notes

Verified via build output analysis: the `shiki` core chunk is acyclic with the
alias map co-located (cycle fix preserved), and language grammars are emitted as
separate, non-preloaded chunks. Existing Shiki/code-block tests exercise the
runtime highlighting path; this change only affects chunk grouping, not module
behavior.

## Changelog

Faster initial load — syntax-highlighting language grammars now load on demand instead of all upfront.

Signed-off-by: Zeyi (Rice) Fan <zeyi.f@databricks.com>
@github-actions github-actions Bot added the size/S Pull request size: S label Jul 29, 2026
@fanzeyi fanzeyi changed the title perf/web shiki lazy langs perf(web): load Shiki language grammars lazily instead of in the eager core chunk Jul 29, 2026
@fanzeyi fanzeyi added the automerge Automatically Run Merge CI label Jul 29, 2026
@github-actions
github-actions Bot enabled auto-merge (squash) July 29, 2026 08:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automerge Automatically Run Merge CI size/S Pull request size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant