Skip to content

feat(shiki): add twoslash support - #8132

Merged
ovflowd merged 3 commits into
mainfrom
twoslash
Oct 12, 2025
Merged

feat(shiki): add twoslash support#8132
ovflowd merged 3 commits into
mainfrom
twoslash

Conversation

@avivkeller

@avivkelleravivkeller commented Sep 1, 2025

Copy link
Copy Markdown
Member

Fixes#7932


Introduces @shikijs/twoslash support in @node-core/rehype-shiki. Since twoslash depends on TypeScript declaration files, it cannot be bundled and must instead be listed under serverExternalPackages.

Because TypeScript declarations aren’t imported like regular files (they’re loaded through a TSVFS), they need to be explicitly included in the output bundle.

Because Cloudflare doesn't support WASM or Top-Level awaits, rehype-shiki has been rewritten to not rely on them.

@avivkelleravivkeller self-assigned this Sep 1, 2025
@vercel

vercelBot commented Sep 1, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentPreviewUpdated (UTC)
nodejs-orgReadyReadyPreviewOct 11, 2025 1:26pm

@codecov

codecovBot commented Sep 1, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 45.56962% with 86 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.39%. Comparing base (11fa0e5) to head (b1e201d).
⚠️ Report is 7 commits behind head on main.

Files with missing linesPatch %Lines
packages/rehype-shiki/src/index.mjs11.39%70 Missing ⚠️
packages/rehype-shiki/src/plugin.mjs76.74%10 Missing ⚠️
packages/rehype-shiki/src/highlighter.mjs80.00%6 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #8132 +/- ##
==========================================
- Coverage 76.64% 76.39% -0.26% 
==========================================
Files 115 115 Lines 9623 9643 +20 Branches 322 317 -5 ==========================================
- Hits 7376 7367 -9 - Misses 2246 2275 +29 
Partials 1 1 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@AugustinMauroy

Copy link
Copy Markdown
Member

Hype 😁 !

@avivkeller

Copy link
Copy Markdown
MemberAuthor

Locally:
image

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

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 adds TypeScript TwoSlash support to the syntax highlighting system, enabling interactive TypeScript error displays and type information tooltips in code blocks.

Key Changes:

  • Integrated TwoSlash transformer for TypeScript/JavaScript code highlighting
  • Refactored metadata parsing system from string-based to object-based approach
  • Added CSS styling for TwoSlash popup containers and error displays

Reviewed Changes

Copilot reviewed 10 out of 11 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
packages/rehype-shiki/src/index.mjsAdded TwoSlash transformer import and configuration
packages/rehype-shiki/src/highlighter.mjsUpdated functions to accept metadata parameter and pass transformers
packages/rehype-shiki/src/plugin.mjsReplaced string-based metadata parsing with comprehensive object-based system
packages/rehype-shiki/src/twoslash.cssAdded CSS styling for TwoSlash popup containers
packages/rehype-shiki/package.jsonAdded TwoSlash dependency and exported CSS file
packages/ui-components/src/Common/BaseCodeBox/index.module.cssAdded CSS transforms for proper positioning
apps/site/next.config.mjsConfigured external packages and file tracing for TwoSlash
apps/site/package.jsonAdded twoslash dependency
apps/site/styles/index.cssImported TwoSlash CSS styles
apps/site/pages/en/learn/typescript/transpile.mdUpdated TypeScript example to use TwoSlash error annotations
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment threadapps/site/next.config.mjs Outdated
Comment threadpackages/rehype-shiki/src/plugin.mjs
@jakebailey

Copy link
Copy Markdown
Member

Is this all server side?

I'm asking because given the TS port to Go, I'm not sure what the deal will be for shiki on the web; probably having to include a large Go binary in Wasm to work with all of the fancy stuff if not prerendered...

@avivkeller

Copy link
Copy Markdown
MemberAuthor

@nodejs/nodejs-website @nodejs/web-infra for reviews. Currently, this is disabled on CF, but there's a TODO to enable it.

@AugustinMauroyAugustinMauroy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Light/dark theme didn't work well but I didn't see why
Capture d’écran 2025-09-25 à 08 53 20
Capture d’écran 2025-09-25 à 08 53 35

Comment threadapps/site/pages/en/learn/typescript/introduction.md
@avivkeller

Copy link
Copy Markdown
MemberAuthor

I'll get to Augustin's review today, @nodejs/web any other comments / concerns

@flakey5flakey5 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

thank you!

@avivkeller

Copy link
Copy Markdown
MemberAuthor

I need an @nodejs/nodejs-website approval, and then I'll rebase

Comment threadpackages/rehype-shiki/src/index.mjs Outdated
Comment threadpackages/ui-components/src/MDX/Tooltip.tsx Outdated
Comment threadpackages/rehype-shiki/src/plugin.mjs
return parameter !== undefined && parameter.length > 0
? parameter
: undefined;
while ((match = rMeta.exec(meta)) !== null) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This feels like some voodoo wizardry and so prone of going wrong I guess

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

How so? The RegEx (AFAICT) is pretty robust.

@ovflowdovflowdOct 11, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This regex pattern seems to be vulnerable to ReDoS.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I don't think it is:
image
image
image

return transformers;
}

export const LANGS = [

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Does this need to be on the same file?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Ideally yes, the index.mjsLANGS and the minimal.mjsLANGS

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

IDK, feels like this should have a dedicated langs.mjs file tho

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I don't think so. The index.mjs exports all of the highlighting utilities needed for our server, and the minimal.mjs exports all for our client. A langs.mjs file for index.mjs doesn't make sense to me.

WDYT @nodejs/nodejs-website

Comment threadapps/site/next.config.mjs Outdated
Comment threadpackages/rehype-shiki/src/transformers/twoslash/index.mjs
Comment threadpackages/rehype-shiki/src/transformers/twoslash/index.mjs Outdated
Signed-off-by: Aviv Keller <me@aviv.sh>
import BadgeGroup from '@node-core/ui-components/Common/BadgeGroup';
import Blockquote from '@node-core/ui-components/Common/Blockquote';
import MDXCodeTabs from '@node-core/ui-components/MDX/CodeTabs';
import {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

These should all be separated files

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also are these client components? This file should only contain server components. There's another file for client components no?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

No, we merged them into a single file in #8167

Comment threadpackages/rehype-shiki/src/highlighter.mjs

@ovflowdovflowd left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I have a few reservations, would love some comments to be addressed and some benchmark images of build times / bundle sizes with this addition.

This is a cool visual addition but if it's to heavy, might not be worth it.

@avivkeller

avivkeller commented Oct 11, 2025

Copy link
Copy Markdown
MemberAuthor

I have a few reservations, would love some comments to be addressed and some benchmark images of build times / bundle sizes with this addition.

The bundle is a bit bigger, since it needs to load the extra JS (See #8160). The most recent Twoslash build took ~1:33, almost identical to the most recent main build (~1:29)

@ovflowd
ovflowd merged commit 0aa1281 into mainOct 12, 2025
12 checks passed
@ovflowd
ovflowd deleted the twoslash branch October 12, 2025 17:06
@ovflowd

Copy link
Copy Markdown
Member

Bypassing ownership from typescript team as it is a false positive.

Sign up for freeto 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.

Type Info on the Docs & Guides

8 participants

@avivkeller@AugustinMauroy@jakebailey@canerakdas@dario-piotrowicz@ovflowd@flakey5