MelonyParser
Low-level parser class for extracting and parsing Melony components from content strings.
Import
import { MelonyParser } from "melony";Overview
MelonyParser is the underlying parser used by MelonyMarkdown andMelonyWidget. You typically don't need to use it directly, but it's available for advanced use cases like custom renderers, custom component registration, or building your own parsing logic.
Basic Usage
import { MelonyParser } from "melony";
const parser = new MelonyParser();
const content = `
<card title="Weather">
<text value="Sunny" />
</card>
`;
const blocks = parser.parseContentAsBlocks(content);
console.log(blocks);Methods
parseContentAsBlocks
(content: string) => Block[]
Parses content string into an array of blocks (text or component blocks).
const content = `
Some text here.
<card title="Hello">
<text value="World" />
</card>
More text.
`;
const blocks = parser.parseContentAsBlocks(content);
// Returns:
// [
// { type: "text", content: "Some text here." },
// { type: "component", component: {...} },
// { type: "text", content: "More text." }
// ]registerComponent
(tag: string, componentName: string) => void
Register a custom component tag mapping.
const parser = new MelonyParser();
// Register custom component
parser.registerComponent("mywidget", "MyWidget");
// Now the parser recognizes <mywidget> tags
const content = `<mywidget prop="value" />`;
const blocks = parser.parseContentAsBlocks(content);registerComponents
(mappings: Record<string, string>) => void
Register multiple component tag mappings at once.
const parser = new MelonyParser();
parser.registerComponents({
customcard: "CustomCard",
specialbutton: "SpecialButton",
mywidget: "MyWidget",
});Block Types
The parser returns an array of blocks with these types:
type Block = TextBlock | ComponentBlock;
interface TextBlock {
type: "text";
content: string;
}
interface ComponentBlock {
type: "component";
component: ComponentDef;
}
interface ComponentDef {
component: string;
props?: Record<string, any>;
children?: (ComponentDef | string)[];
}Custom Component Registration
Register custom component tags for specialized use cases:
import { MelonyParser } from "melony";
// Create parser with custom components
const parser = new MelonyParser();
parser.registerComponents({
"weather-card": "WeatherCard",
"stock-ticker": "StockTicker",
"custom-chart": "CustomChart",
});
const content = `
<weather-card location="SF" temp="72" />
<stock-ticker symbol="AAPL" />
`;
const blocks = parser.parseContentAsBlocks(content);
// Blocks will reference "WeatherCard" and "StockTicker" componentsBuilding Custom Renderers
Use the parser to build your own custom rendering logic:
import { MelonyParser, renderComponent } from "melony";
function customRenderer(content: string) {
const parser = new MelonyParser();
const blocks = parser.parseContentAsBlocks(content);
return blocks.map((block, index) => {
if (block.type === "text") {
return <div key={index}>{block.content}</div>;
} else if (block.type === "component") {
// Custom component rendering logic
return (
<div key={index} className="component-wrapper">
{renderComponent(block.component)}
</div>
);
}
});
}Progressive Parsing
The parser is designed for progressive/streaming content:
import { MelonyParser } from "melony";
import { useState, useEffect } from "react";
function StreamingRenderer({ stream }: { stream: ReadableStream }) {
const [content, setContent] = useState("");
const parser = new MelonyParser();
useEffect(() => {
const reader = stream.getReader();
async function read() {
const { done, value } = await reader.read();
if (done) return;
const chunk = new TextDecoder().decode(value);
setContent((prev) => prev + chunk);
read();
}
read();
}, [stream]);
const blocks = parser.parseContentAsBlocks(content);
return (
<div>
{blocks.map((block, i) => (
<div key={i}>
{block.type === "text" ? block.content : renderComponent(block.component)}
</div>
))}
</div>
);
}Complete Example
Advanced usage with custom parsing and rendering:
import { MelonyParser, renderComponent, ComponentDef } from "melony";
import { useState } from "react";
// Custom parser with additional components
const createCustomParser = () => {
const parser = new MelonyParser();
// Register custom components
parser.registerComponents({
"analytics-card": "AnalyticsCard",
"chart-widget": "ChartWidget",
"data-table": "DataTable",
});
return parser;
};
function CustomMelonyRenderer({ content }: { content: string }) {
const parser = createCustomParser();
const blocks = parser.parseContentAsBlocks(content);
return (
<div className="custom-renderer">
{blocks.map((block, index) => {
if (block.type === "text") {
// Custom text rendering
return (
<div key={index} className="text-block">
{block.content}
</div>
);
} else {
// Custom component rendering with wrapper
return (
<div key={index} className="component-block">
<div className="component-header">
Component: {block.component.component}
</div>
<div className="component-content">
{renderComponent(block.component)}
</div>
</div>
);
}
})}
</div>
);
}
export default CustomMelonyRenderer;When to Use MelonyParser
Use MelonyParser directly when:
- Building custom rendering logic
- Need to register custom component tags
- Building integrations with other libraries
- Need low-level access to parsed blocks
- Implementing custom progressive rendering
Use MelonyMarkdown or MelonyWidget instead when:
- Building standard chat interfaces
- Rendering AI responses with default components
- No need for custom parsing logic
- Standard use cases with built-in components
Notes
MelonyParseris used internally byMelonyMarkdownandMelonyWidget- Component registration is per-parser instance
- The parser handles incomplete/streaming content gracefully
- Parsed blocks can be cached for performance
- Use
renderComponenthelper to render ComponentDef objects
TypeScript Types
// Import types
import type {
MelonyParser,
Block,
TextBlock,
ComponentBlock,
ComponentDef
} from "melony";
// Block type
type Block = TextBlock | ComponentBlock;
interface TextBlock {
type: "text";
content: string;
}
interface ComponentBlock {
type: "component";
component: ComponentDef;
}
// Component definition
interface ComponentDef {
component: string;
props?: Record<string, any>;
children?: (ComponentDef | string)[];
}See Also
MelonyMarkdown - High-level markdown rendering component
MelonyWidget - High-level widget rendering component
MelonyProvider - Root provider component