From 81d844f39c58156c7f6942058680f00e59b9f71c Mon Sep 17 00:00:00 2001 From: Ghoshan Jaganathamani <175198591+ghostyfreak@users.noreply.github.com> Date: Wed, 22 Jul 2026 16:03:13 +0530 Subject: [PATCH] fix: stop requiring shiki for type-checking streamdown Re-exported BundledLanguage, BundledTheme, and ThemeRegistrationAny from local structural types instead of importing them from shiki. Consumers no longer need to install shiki solely to satisfy TypeScript when using the published declarations. Runtime highlighting remains in @streamdown/code, which still depends on shiki. CodeHighlighterPlugin methods use method syntax so plugins with narrower Shiki unions stay assignable. Fixes #562 --- .changeset/fix-shiki-type-declarations.md | 7 +++ packages/streamdown/index.tsx | 8 ++-- .../lib/code-block/highlighted-body.tsx | 3 +- packages/streamdown/lib/plugin-types.ts | 24 ++++++++--- packages/streamdown/lib/shiki-types.ts | 43 +++++++++++++++++++ packages/streamdown/package.json | 1 - pnpm-lock.yaml | 11 ++--- 7 files changed, 75 insertions(+), 22 deletions(-) create mode 100644 .changeset/fix-shiki-type-declarations.md create mode 100644 packages/streamdown/lib/shiki-types.ts diff --git a/.changeset/fix-shiki-type-declarations.md b/.changeset/fix-shiki-type-declarations.md new file mode 100644 index 00000000..38fb891c --- /dev/null +++ b/.changeset/fix-shiki-type-declarations.md @@ -0,0 +1,7 @@ +--- +"streamdown": patch +--- + +Fix type declarations requiring a `shiki` install. + +`BundledLanguage`, `BundledTheme`, and `ThemeRegistrationAny` are now defined locally and re-exported from `streamdown`, so consumers can type-check without installing `shiki`. Runtime highlighting remains in `@streamdown/code`, which still depends on `shiki`. diff --git a/packages/streamdown/index.tsx b/packages/streamdown/index.tsx index be32d9fd..736bd597 100644 --- a/packages/streamdown/index.tsx +++ b/packages/streamdown/index.tsx @@ -46,11 +46,6 @@ import { } from "./lib/translations-context"; import { createCn } from "./lib/utils"; -export type { - BundledLanguage, - BundledTheme, - ThemeRegistrationAny, -} from "shiki"; export type { AnimateOptions } from "./lib/animate"; // biome-ignore lint/performance/noBarrelFile: "required" export { createAnimatePlugin } from "./lib/animate"; @@ -73,6 +68,8 @@ export type { export { defaultUrlTransform } from "./lib/markdown"; export { parseMarkdownIntoBlocks } from "./lib/parse-blocks"; export type { + BundledLanguage, + BundledTheme, CjkPlugin, CodeHighlighterPlugin, CustomRenderer, @@ -82,6 +79,7 @@ export type { MathPlugin, PluginConfig, ThemeInput, + ThemeRegistrationAny, } from "./lib/plugin-types"; export { TableCopyDropdown, diff --git a/packages/streamdown/lib/code-block/highlighted-body.tsx b/packages/streamdown/lib/code-block/highlighted-body.tsx index 87ef2924..fe68d013 100644 --- a/packages/streamdown/lib/code-block/highlighted-body.tsx +++ b/packages/streamdown/lib/code-block/highlighted-body.tsx @@ -1,8 +1,7 @@ import { type HTMLAttributes, useContext, useEffect, useState } from "react"; -import type { BundledLanguage } from "shiki"; import { StreamdownContext } from "../../index"; import { useCodePlugin } from "../plugin-context"; -import type { HighlightResult } from "../plugin-types"; +import type { BundledLanguage, HighlightResult } from "../plugin-types"; import { CodeBlockBody } from "./body"; type HighlightedCodeBlockBodyProps = HTMLAttributes & { diff --git a/packages/streamdown/lib/plugin-types.ts b/packages/streamdown/lib/plugin-types.ts index 95130362..01c45b64 100644 --- a/packages/streamdown/lib/plugin-types.ts +++ b/packages/streamdown/lib/plugin-types.ts @@ -1,11 +1,17 @@ import type { MermaidConfig } from "mermaid"; import type React from "react"; +import type { Pluggable } from "unified"; import type { BundledLanguage, BundledTheme, ThemeRegistrationAny, -} from "shiki"; -import type { Pluggable } from "unified"; +} from "./shiki-types"; + +export type { + BundledLanguage, + BundledTheme, + ThemeRegistrationAny, +} from "./shiki-types"; export type ThemeInput = BundledTheme | ThemeRegistrationAny; @@ -42,30 +48,34 @@ export interface HighlightOptions { /** * Plugin for code syntax highlighting (Shiki) + * + * Method syntax is intentional: parameter types stay bivariant so plugins + * from `@streamdown/code` (which use Shiki's narrower language/theme unions) + * remain assignable without requiring a `shiki` type dependency here. */ export interface CodeHighlighterPlugin { /** * Get list of supported languages */ - getSupportedLanguages: () => BundledLanguage[]; + getSupportedLanguages(): BundledLanguage[]; /** * Get the configured themes */ - getThemes: () => [ThemeInput, ThemeInput]; + getThemes(): [ThemeInput, ThemeInput]; /** * Highlight code and return tokens * Returns null if highlighting not ready yet (async loading) * Use callback for async result */ - highlight: ( + highlight( options: HighlightOptions, callback?: (result: HighlightResult) => void - ) => HighlightResult | null; + ): HighlightResult | null; name: "shiki"; /** * Check if language is supported */ - supportsLanguage: (language: BundledLanguage) => boolean; + supportsLanguage(language: BundledLanguage): boolean; type: "code-highlighter"; } diff --git a/packages/streamdown/lib/shiki-types.ts b/packages/streamdown/lib/shiki-types.ts new file mode 100644 index 00000000..5acd6d66 --- /dev/null +++ b/packages/streamdown/lib/shiki-types.ts @@ -0,0 +1,43 @@ +/** + * Types compatible with Shiki, defined locally so consumers of `streamdown` + * can type-check without installing the `shiki` package. + * + * Runtime highlighting lives in `@streamdown/code`, which depends on `shiki`. + * Streamdown only needs these types for its plugin API surface (`shikiTheme`, + * `CodeHighlighterPlugin`, and related re-exports). + * + * @see https://shiki.style/languages + * @see https://shiki.style/themes + */ + +/** + * Language identifier for syntax highlighting. + * Compatible with Shiki's `BundledLanguage`. + */ +export type BundledLanguage = string; + +/** + * Built-in theme name for syntax highlighting. + * Compatible with Shiki's `BundledTheme`. + */ +export type BundledTheme = string; + +/** + * Custom theme registration object compatible with Shiki's + * `ThemeRegistrationAny` (raw, resolved, or intermediate forms). + * + * Kept structural and permissive so custom theme objects from + * `@streamdown/code` / Shiki remain assignable without a type dependency. + */ +export interface ThemeRegistrationAny { + bg?: string; + colors?: Record; + displayName?: string; + fg?: string; + name?: string; + semanticHighlighting?: boolean; + semanticTokenColors?: Record; + settings?: unknown[]; + tokenColors?: unknown[]; + type?: "light" | "dark"; +} diff --git a/packages/streamdown/package.json b/packages/streamdown/package.json index b44b3474..cecaba75 100644 --- a/packages/streamdown/package.json +++ b/packages/streamdown/package.json @@ -52,7 +52,6 @@ "react-markdown": "^10.1.0", "rehype-parse": "^9.0.1", "rehype-stringify": "^10.0.1", - "shiki": "^3.19.0", "tsup": "^8.5.1", "vitest": "^4.0.15" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a055b22c..2e7ac6d0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -158,7 +158,7 @@ importers: version: 1.6.1(next@16.2.6(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3) '@vercel/geistdocs': specifier: 1.11.0 - version: 1.11.0(@svta/cml-cta@1.0.1(@svta/cml-structured-field-values@1.0.1(@svta/cml-utils@1.0.1))(@svta/cml-utils@1.0.1))(@svta/cml-structured-field-values@1.0.1(@svta/cml-utils@1.0.1))(@svta/cml-utils@1.0.1)(@tanstack/react-router@1.151.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@types/mdast@4.0.4)(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(micromark-util-types@2.0.2)(micromark@4.0.2)(next@16.2.6(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18)(unified@11.0.5)(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + version: 1.11.0(@svta/cml-cta@1.0.1(@svta/cml-structured-field-values@1.0.1(@svta/cml-utils@1.0.1))(@svta/cml-utils@1.0.1))(@svta/cml-structured-field-values@1.0.1(@svta/cml-utils@1.0.1))(@svta/cml-utils@1.0.1)(@tanstack/react-router@1.151.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@types/mdast@4.0.4)(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(next@16.2.6(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18)(unified@11.0.5)(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) '@vercel/speed-insights': specifier: ^1.3.1 version: 1.3.1(next@16.2.6(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3) @@ -377,9 +377,6 @@ importers: rehype-stringify: specifier: ^10.0.1 version: 10.0.1 - shiki: - specifier: ^3.19.0 - version: 3.21.0 tsup: specifier: ^8.5.1 version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3) @@ -8933,7 +8930,7 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@streamdown/cjk@1.0.3(@types/mdast@4.0.4)(micromark-util-types@2.0.2)(micromark@4.0.2)(react@19.2.3)(unified@11.0.5)': + '@streamdown/cjk@1.0.3(@types/mdast@4.0.4)(react@19.2.3)(unified@11.0.5)': dependencies: react: 19.2.3 remark-cjk-friendly: 2.0.1(@types/mdast@4.0.4)(micromark-util-types@2.0.2)(micromark@4.0.2)(unified@11.0.5) @@ -9347,13 +9344,13 @@ snapshots: next: 16.2.6(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react: 19.2.3 - '@vercel/geistdocs@1.11.0(@svta/cml-cta@1.0.1(@svta/cml-structured-field-values@1.0.1(@svta/cml-utils@1.0.1))(@svta/cml-utils@1.0.1))(@svta/cml-structured-field-values@1.0.1(@svta/cml-utils@1.0.1))(@svta/cml-utils@1.0.1)(@tanstack/react-router@1.151.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@types/mdast@4.0.4)(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(micromark-util-types@2.0.2)(micromark@4.0.2)(next@16.2.6(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18)(unified@11.0.5)(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': + '@vercel/geistdocs@1.11.0(@svta/cml-cta@1.0.1(@svta/cml-structured-field-values@1.0.1(@svta/cml-utils@1.0.1))(@svta/cml-utils@1.0.1))(@svta/cml-structured-field-values@1.0.1(@svta/cml-utils@1.0.1))(@svta/cml-utils@1.0.1)(@tanstack/react-router@1.151.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(@types/mdast@4.0.4)(@types/react-dom@19.2.3(@types/react@19.2.8))(@types/react@19.2.8)(next@16.2.6(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18)(unified@11.0.5)(vite@7.3.1(@types/node@24.10.10)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': dependencies: '@ai-sdk/react': 3.0.201(react@19.2.3)(zod@4.3.5) '@clack/prompts': 0.11.0 '@icons-pack/react-simple-icons': 13.8.0(react@19.2.3) '@orama/tokenizers': 3.1.18 - '@streamdown/cjk': 1.0.3(@types/mdast@4.0.4)(micromark-util-types@2.0.2)(micromark@4.0.2)(react@19.2.3)(unified@11.0.5) + '@streamdown/cjk': 1.0.3(@types/mdast@4.0.4)(react@19.2.3)(unified@11.0.5) '@streamdown/code': 1.1.1(react@19.2.3) '@vercel/agent-readability': 0.2.1(next@16.2.6(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)) ai: 6.0.199(zod@4.3.5)