Skip to content

Commit d4ec6c0

Browse files
authored
feat: add meta prop to custom renderer props (#447)
Custom renderers now receive the raw metastring from code fences via a new optional `meta` prop. Everything after the language identifier is passed through (e.g. ```rust {1} title="foo"``` → meta = '{1} title="foo"'). - Added `meta?: string` to `CustomRendererProps` in `plugin-types.ts` - Pass `meta={metastring}` to custom renderers in `components.tsx` - Added tests for meta prop presence and absence - Added minor changeset Closes #443 Co-authored-by: Dmitrii Troitskii <jsleitor@gmail.com>
1 parent 8b1c262 commit d4ec6c0

File tree

4 files changed

+53
-0
lines changed

4 files changed

+53
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"streamdown": minor
3+
---
4+
5+
Add `meta` prop to `CustomRendererProps`. Custom renderers now receive the raw metastring from the code fence (everything after the language identifier, e.g. ` ```rust {1} title="foo" ``meta = '{1} title="foo"'`). The prop is optional (`meta?: string`) and is `undefined` when no metastring is present. Existing custom renderers are unaffected.

packages/streamdown/__tests__/custom-renderer.test.tsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,4 +143,47 @@ describe("Custom Renderers", () => {
143143
expect(codeBlock).toBeTruthy();
144144
});
145145
});
146+
147+
it("passes meta prop to custom renderer when metastring is present", async () => {
148+
const MetaRenderer = ({ meta }: CustomRendererProps) => (
149+
<div data-meta={meta ?? ""} data-testid="meta-renderer" />
150+
);
151+
const { container } = render(
152+
<Streamdown
153+
plugins={{
154+
renderers: [{ language: "rust", component: MetaRenderer }],
155+
}}
156+
>
157+
{'```rust {1} title="foo"\nlet x = 1;\n```'}
158+
</Streamdown>
159+
);
160+
161+
await waitFor(() => {
162+
const el = container.querySelector('[data-testid="meta-renderer"]');
163+
expect(el?.getAttribute("data-meta")).toBe('{1} title="foo"');
164+
});
165+
});
166+
167+
it("passes undefined meta when no metastring present", async () => {
168+
const MetaRenderer = ({ meta }: CustomRendererProps) => (
169+
<div
170+
data-has-meta={meta !== undefined ? "true" : "false"}
171+
data-testid="meta-renderer"
172+
/>
173+
);
174+
const { container } = render(
175+
<Streamdown
176+
plugins={{
177+
renderers: [{ language: "rust", component: MetaRenderer }],
178+
}}
179+
>
180+
{"```rust\nlet x = 1;\n```"}
181+
</Streamdown>
182+
);
183+
184+
await waitFor(() => {
185+
const el = container.querySelector('[data-testid="meta-renderer"]');
186+
expect(el?.getAttribute("data-has-meta")).toBe("false");
187+
});
188+
});
146189
});

packages/streamdown/lib/components.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -843,6 +843,7 @@ const CodeComponent = ({
843843
code={code}
844844
isIncomplete={isBlockIncomplete}
845845
language={language}
846+
meta={metastring}
846847
/>
847848
</Suspense>
848849
);

packages/streamdown/lib/plugin-types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@ export interface CustomRendererProps {
149149
code: string;
150150
isIncomplete: boolean;
151151
language: string;
152+
/** Raw metastring from the code fence (everything after the language identifier).
153+
* e.g. ```rust {1} title="foo" → meta = '{1} title="foo"'
154+
* Undefined when no metastring is present. */
155+
meta?: string;
152156
}
153157

154158
export interface CustomRenderer {

0 commit comments

Comments
 (0)