diff --git a/.changeset/fix-mermaid-hard-dep.md b/.changeset/fix-mermaid-hard-dep.md new file mode 100644 index 00000000..fe47f067 --- /dev/null +++ b/.changeset/fix-mermaid-hard-dep.md @@ -0,0 +1,9 @@ +--- +"streamdown": patch +--- + +fix(deps): remove `mermaid` as a hard runtime dependency + +This patch replaces the type import with a local structural type for `MermaidConfig` so no type-level coupling to the `mermaid` package remains in the distributed typings. Users who want fully-typed mermaid config can still `import type { MermaidConfig } from 'mermaid'` themselves; the structural type is compatible. + +Fixes #501 diff --git a/packages/streamdown/__tests__/mermaid.test.tsx b/packages/streamdown/__tests__/mermaid.test.tsx index 7af696fd..0cfaff03 100644 --- a/packages/streamdown/__tests__/mermaid.test.tsx +++ b/packages/streamdown/__tests__/mermaid.test.tsx @@ -1,12 +1,11 @@ import { render, waitFor } from "@testing-library/react"; -import type { MermaidConfig } from "mermaid"; import { act } from "react"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { Mermaid } from "../lib/mermaid"; import { MermaidDownloadDropdown } from "../lib/mermaid/download-button"; import { MermaidFullscreenButton } from "../lib/mermaid/fullscreen-button"; import { PluginContext } from "../lib/plugin-context"; -import type { DiagramPlugin } from "../lib/plugin-types"; +import type { DiagramPlugin, MermaidConfig } from "../lib/plugin-types"; const { saveMock, mockInitialize, mockRender, mockMermaid } = vi.hoisted(() => { const mockInitialize = vi.fn(); diff --git a/packages/streamdown/__tests__/streamdown.test.tsx b/packages/streamdown/__tests__/streamdown.test.tsx index 06c3c8b4..491c29dd 100644 --- a/packages/streamdown/__tests__/streamdown.test.tsx +++ b/packages/streamdown/__tests__/streamdown.test.tsx @@ -1,7 +1,7 @@ import { render } from "@testing-library/react"; -import type { MermaidConfig } from "mermaid"; import { describe, expect, it, vi } from "vitest"; import { Streamdown } from "../index"; +import type { MermaidConfig } from "../lib/plugin-types"; // Mock the dependencies vi.mock("../lib/markdown", () => ({ diff --git a/packages/streamdown/index.tsx b/packages/streamdown/index.tsx index 5b8cc920..e36692da 100644 --- a/packages/streamdown/index.tsx +++ b/packages/streamdown/index.tsx @@ -1,6 +1,5 @@ "use client"; -import type { MermaidConfig } from "mermaid"; import { type ComponentProps, type CSSProperties, @@ -33,7 +32,11 @@ import { hasIncompleteCodeFence, hasTable } from "./lib/incomplete-code-utils"; import { type ExtraProps, Markdown, type Options } from "./lib/markdown"; import { parseMarkdownIntoBlocks } from "./lib/parse-blocks"; import { PluginContext } from "./lib/plugin-context"; -import type { PluginConfig, ThemeInput } from "./lib/plugin-types"; +import type { + MermaidConfig, + PluginConfig, + ThemeInput, +} from "./lib/plugin-types"; import { PrefixContext } from "./lib/prefix-context"; import { preprocessCustomTags } from "./lib/preprocess-custom-tags"; import { preprocessLiteralTagContent } from "./lib/preprocess-literal-tag-content"; diff --git a/packages/streamdown/lib/mermaid/download-button.tsx b/packages/streamdown/lib/mermaid/download-button.tsx index 7f287f2d..c46c94e5 100644 --- a/packages/streamdown/lib/mermaid/download-button.tsx +++ b/packages/streamdown/lib/mermaid/download-button.tsx @@ -1,8 +1,8 @@ -import type { MermaidConfig } from "mermaid"; import { useContext, useEffect, useRef, useState } from "react"; import { StreamdownContext } from "../../index"; import { useIcons } from "../icon-context"; import { useMermaidPlugin } from "../plugin-context"; +import type { MermaidConfig } from "../plugin-types"; import { useCn } from "../prefix-context"; import { useTranslations } from "../translations-context"; import { save } from "../utils"; diff --git a/packages/streamdown/lib/mermaid/fullscreen-button.tsx b/packages/streamdown/lib/mermaid/fullscreen-button.tsx index 316095da..8491fd67 100644 --- a/packages/streamdown/lib/mermaid/fullscreen-button.tsx +++ b/packages/streamdown/lib/mermaid/fullscreen-button.tsx @@ -1,8 +1,8 @@ -import type { MermaidConfig } from "mermaid"; import { type ComponentProps, useContext, useEffect, useState } from "react"; import { createPortal } from "react-dom"; import { StreamdownContext } from "../../index"; import { useIcons } from "../icon-context"; +import type { MermaidConfig } from "../plugin-types"; import { useCn } from "../prefix-context"; import { lockBodyScroll, unlockBodyScroll } from "../scroll-lock"; import { useTranslations } from "../translations-context"; diff --git a/packages/streamdown/lib/mermaid/index.tsx b/packages/streamdown/lib/mermaid/index.tsx index 3f483863..4f4408cf 100644 --- a/packages/streamdown/lib/mermaid/index.tsx +++ b/packages/streamdown/lib/mermaid/index.tsx @@ -1,8 +1,8 @@ -import type { MermaidConfig } from "mermaid"; import { useContext, useEffect, useState } from "react"; import { useDeferredRender } from "../../hooks/use-deferred-render"; import { StreamdownContext } from "../../index"; import { useMermaidPlugin } from "../plugin-context"; +import type { MermaidConfig } from "../plugin-types"; import { useCn } from "../prefix-context"; import { PanZoom } from "./pan-zoom"; diff --git a/packages/streamdown/lib/plugin-types.ts b/packages/streamdown/lib/plugin-types.ts index 01c45b64..807341f7 100644 --- a/packages/streamdown/lib/plugin-types.ts +++ b/packages/streamdown/lib/plugin-types.ts @@ -1,4 +1,3 @@ -import type { MermaidConfig } from "mermaid"; import type React from "react"; import type { Pluggable } from "unified"; import type { @@ -80,21 +79,44 @@ export interface CodeHighlighterPlugin { } /** - * Mermaid instance interface + * Structural type for Mermaid configuration pass-through. + * Avoids a hard dependency on the "mermaid" package in the core bundle. + */ +export type MermaidConfig = { + fontFamily?: string; + securityLevel?: string; + startOnLoad?: boolean; + suppressErrorRendering?: boolean; + theme?: string; + themeCSS?: string; + themeVariables?: Record; + // biome-ignore lint/suspicious/noExplicitAny: open pass-through for mermaid options +} & Record; + +/** + * Mermaid instance interface. + * + * Method syntax is intentional: parameter types stay bivariant so + * `@streamdown/mermaid` (which uses mermaid's narrower `MermaidConfig`) remains + * assignable without a core type dependency on the mermaid package. */ export interface MermaidInstance { - initialize: (config: MermaidConfig) => void; - render: (id: string, source: string) => Promise<{ svg: string }>; + initialize(config: MermaidConfig): void; + render(id: string, source: string): Promise<{ svg: string }>; } /** - * Plugin for diagram rendering (Mermaid) + * Plugin for diagram rendering (Mermaid). + * + * Method syntax on `getMermaid` matches `CodeHighlighterPlugin` — keeps plugin + * implementations with narrower config types assignable under + * `strictFunctionTypes`. */ export interface DiagramPlugin { /** * Get the mermaid instance (initialized with optional config) */ - getMermaid: (config?: MermaidConfig) => MermaidInstance; + getMermaid(config?: MermaidConfig): MermaidInstance; /** * Language identifier for code blocks */ diff --git a/packages/streamdown/package.json b/packages/streamdown/package.json index cecaba75..f54d7152 100644 --- a/packages/streamdown/package.json +++ b/packages/streamdown/package.json @@ -73,7 +73,6 @@ "remend": "workspace:*", "tailwind-merge": "^3.4.0", "unified": "^11.0.5", - "mermaid": "^11.12.2", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2e7ac6d0..c10a209e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -292,9 +292,6 @@ importers: marked: specifier: ^17.0.1 version: 17.0.1 - mermaid: - specifier: ^11.12.2 - version: 11.12.2 react: specifier: ^18.0.0 || ^19.0.0 version: 19.2.3