solid-marked
v0.6.3
Published
Markdown Compiler for SolidJS
Downloads
846
Readme
solid-marked
MDX/Markdown compiler for SolidJS
Install
npm i solid-js solid-marked
yarn add solid-js solid-marked
pnpm add solid-js solid-marked
Integrations
Using the compiler
import { compile } from 'solid-marked/compiler';
const { map, code } = await compile(
'my-file.md', // Name of the file
'# Hello World', // Markdown code
{
// Where to import the builtin components
// Default: solid-marked
mdxImportSource: 'mdx-provider',
// If solid-marked should use the Dynamic component
// or not.
// Possible values:
// - true: completely disables Dynamic component
// - false: enables Dynamic component
// - 'only-mdx': Dynamic component is only enabled for MDX
noDynamicComponents: 'only-mdx',
}
);
console.log(code);
useMDX
and MDXProvider
Components generated by solid-marked
uses the fundamental components from an MDX provider, this is through the use of useMDX
which is imported from the module. By default, useMDX
comes from solid-marked
and needs to be used in conjunction with <MDXProvider>
import { MDXProvider } from 'solid-marked';
// ...
<MDXProvider
builtins={{
Heading(props) {
return (
<Dynamic component={`h${props.depth}`}>
{props.children}
</Dynamic>
);
},
}}
>
<App />
</MDXProvider>
Example custom module
export function useMDX() {
return {
builtins: {
Link(props) {
return (
<a href={props.url} title={props.title}>
{props.children}
</a>
);
},
},
};
}
Features
<Root>
<Root>
is the top-most component in a Markdown component. It serves as the container for the markdown content.
Markdown
The following components (and their prop definitions) are derived from mdast
<Paragraph>
Lorem ipsum dolor.
import { useMDX as _useMDX$ } from 'solid-marked';
export default function Component(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.Root}>
<Dynamic component={_ctx$.builtins.Paragraph}>
Lorem ipsum dolor.
</Dynamic>
</Dynamic>
);
}
<Heading>
[!INFO] Presence of a
<Heading>
allows the Markdown component to generate a<TableOfContents>
.
# Heading 1
## Heading 2
### Heading 3
#### Heading 4
##### Heading 5
###### Heading 6
import { useMDX as _useMDX$ } from 'solid-marked';
export default function Component(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.Root}>
<Dynamic component={_ctx$.builtins.Heading} depth={1} id={"heading-1"}>Heading 1</Dynamic>
<Dynamic component={_ctx$.builtins.Heading} depth={2} id={"heading-2"}>Heading 2</Dynamic>
<Dynamic component={_ctx$.builtins.Heading} depth={3} id={"heading-3"}>Heading 3</Dynamic>
<Dynamic component={_ctx$.builtins.Heading} depth={4} id={"heading-4"}>Heading 4</Dynamic>
<Dynamic component={_ctx$.builtins.Heading} depth={5} id={"heading-5"}>Heading 5</Dynamic>
<Dynamic component={_ctx$.builtins.Heading} depth={6} id={"heading-6"}>Heading 6</Dynamic>
</Dynamic>
);
}
<ThematicBreak>
Lorem
***
Ipsum
***
Dolor
import { useMDX as _useMDX$ } from 'solid-marked';
export default function Component(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.Root}>
<Dynamic component={_ctx$.builtins.Paragraph}>Lorem</Dynamic>
<Dynamic component={_ctx$.builtins.ThematicBreak} />
<Dynamic component={_ctx$.builtins.Paragraph}>Ipsum</Dynamic>
<Dynamic component={_ctx$.builtins.ThematicBreak} />
<Dynamic component={_ctx$.builtins.Paragraph}>Dolor</Dynamic>
</Dynamic>
);
}
<Blockquote>
> Lorem ipsum dolor.
import { useMDX as _useMDX$ } from 'solid-marked';
export default function Component(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.Root}>
<Dynamic component={_ctx$.builtins.Blockquote}>
<Dynamic component={_ctx$.builtins.Paragraph}>
Lorem ipsum dolor.
</Dynamic>
</Dynamic>
</Dynamic>
);
}
<List>
and <ListItem>
Ordered lists
1. Lorem
2. Ipsum
3. Dolor
import { useMDX as _useMDX$ } from 'solid-marked';
export default function Component(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.Root}>
<Dynamic component={_ctx$.builtins.List} ordered={true} spread={false} start={1}>
<Dynamic component={_ctx$.builtins.ListItem} spread={false}>
<Dynamic component={_ctx$.builtins.Paragraph}>Lorem</Dynamic>
</Dynamic>
<Dynamic component={_ctx$.builtins.ListItem} spread={false}>
<Dynamic component={_ctx$.builtins.Paragraph}>Ipsum</Dynamic>
</Dynamic>
<Dynamic component={_ctx$.builtins.ListItem} spread={false}>
<Dynamic component={_ctx$.builtins.Paragraph}>Dolor</Dynamic>
</Dynamic>
</Dynamic>
</Dynamic>
);
}
Unordered lists
- Lorem
- Ipsum
- Dolor
import { useMDX as _useMDX$ } from 'solid-marked';
export default function Component(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.Root}>
<Dynamic component={_ctx$.builtins.List} ordered={false} spread={false}>
<Dynamic component={_ctx$.builtins.ListItem} spread={false}>
<Dynamic component={_ctx$.builtins.Paragraph}>Lorem</Dynamic>
</Dynamic>
<Dynamic component={_ctx$.builtins.ListItem} spread={false}>
<Dynamic component={_ctx$.builtins.Paragraph}>Ipsum</Dynamic>
</Dynamic>
<Dynamic component={_ctx$.builtins.ListItem} spread={false}>
<Dynamic component={_ctx$.builtins.Paragraph}>Dolor</Dynamic>
</Dynamic>
</Dynamic>
</Dynamic>
);
}
<Code>
```js highlight-line="2"
foo()
bar()
baz()
```
import { useMDX as _useMDX$ } from 'solid-marked';
export default function Component(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.Root}>
<Dynamic component={_ctx$.builtins.Code} lang={"js"} meta={"highlight-line=\"2\""}>
{"foo()\nbar()\nbaz()"}
</Dynamic>
</Dynamic>
);
}
<Definition>
[Github]: https://github.com
import { useMDX as _useMDX$ } from 'solid-marked';
export default function Component(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.Root}>
<Dynamic component={_ctx$.builtins.Definition}
url={"https://github.com"}
identifier={"github"}
label={"Github"}
/>
</Dynamic>
);
}
<Emphasis>
*alpha* _bravo_
import { useMDX as _useMDX$ } from 'solid-marked';
export default function Component(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.Root}>
<Dynamic component={_ctx$.builtins.Paragraph}>
<Dynamic component={_ctx$.builtins.Emphasis}>alpha</Dynamic> <Dynamic component={_ctx$.builtins.Emphasis}>bravo</Dynamic>
</Dynamic>
</Dynamic>
);
}
<Strong>
**alpha** __bravo__
import { useMDX as _useMDX$ } from 'solid-marked';
export default function Component(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.Root}>
<Dynamic component={_ctx$.builtins.Paragraph}>
<Dynamic component={_ctx$.builtins.Strong}>alpha</Dynamic> <Dynamic component={_ctx$.builtins.Strong}>bravo</Dynamic>
</Dynamic>
</Dynamic>
);
}
<InlineCode>
`foo()`
import { useMDX as _useMDX$ } from 'solid-marked';
export default function Component(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.Root}>
<Dynamic component={_ctx$.builtins.Paragraph}>
<Dynamic component={_ctx$.builtins.InlineCode}>{"foo()"}</Dynamic>
</Dynamic>
</Dynamic>
);
}
<Break>
a
b
import { useMDX as _useMDX$ } from 'solid-marked';
export default function Component(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.Root}>
<Dynamic component={_ctx$.builtins.Paragraph}>
a<Dynamic component={_ctx$.builtins.Break} />b
</Dynamic>
</Dynamic>
);
}
<Link>
[alpha](https://example.com "bravo")
import { useMDX as _useMDX$ } from 'solid-marked';
export default function Component(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.Root}>
<Dynamic component={_ctx$.builtins.Paragraph}>
<Dynamic
component={_ctx$.builtins.Link}
url={"https://example.com"}
title={"bravo"}
>
alpha
</Dynamic>
</Dynamic>
</Dynamic>
);
}
<Image>
![alpha](https://example.com/favicon.ico "bravo")
import { useMDX as _useMDX$ } from 'solid-marked';
export default function Component(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.Root}>
<Dynamic component={_ctx$.builtins.Paragraph}>
<Dynamic
component={_ctx$.builtins.Image}
url={"https://example.com/favicon.ico"}
title={"bravo"}
alt={"alpha"}
/>
</Dynamic>
</Dynamic>
);
}
<LinkReference>
Must be have an associated <Definition>
.
[This is an example][alpha]
[alpha]: bravo
import { useMDX as _useMDX$ } from 'solid-marked';
export default function Component(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.Root}>
<Dynamic component={_ctx$.builtins.Paragraph}>
<Dynamic
component={_ctx$.builtins.LinkReference}
identifier={"alpha"}
label={"alpha"}
referenceType={"full"}
>
This is an example
</Dynamic>
</Dynamic>
<Dynamic
component={_ctx$.builtins.Definition}
url={"bravo"}
identifier={"alpha"}
label={"alpha"}
/>
</Dynamic>
);
}
<ImageReference>
Must be have an associated <Definition>
.
![This is an example][alpha]
[alpha]: bravo
import { useMDX as _useMDX$ } from 'solid-marked';
export default function Component(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.Root}>
<Dynamic component={_ctx$.builtins.Paragraph}>
<Dynamic
component={_ctx$.builtins.ImageReference}
identifier={"alpha"}
label={"alpha"}
referenceType={"full"}
alt={"This is an example"}
/>
</Dynamic>
<Dynamic
component={_ctx$.builtins.Definition}
url={"bravo"}
identifier={"alpha"}
label={"alpha"}
/>
</Dynamic>
);
}
Github-flavored Markdown
Based on Github-flavored Markdown
<FootnoteDefinition>
[^alpha]: bravo and charlie.
import { useMDX as _useMDX$ } from 'solid-marked';
export default function Component(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.Root}>
<Dynamic component={_ctx$.builtins.FootnoteDefinition} identifier={"alpha"} label={"alpha"}>
<Dynamic component={_ctx$.builtins.Paragraph}>bravo and charlie.</Dynamic>
</Dynamic>
</Dynamic>
);
}
<FootnoteReference>
Must have an associated <FootnoteDefinition>
[^alpha]
[^alpha]: bravo and charlie.
import { useMDX as _useMDX$ } from 'solid-marked';
export default function Component(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.Root}>
<Dynamic component={_ctx$.builtins.Paragraph}>
<Dynamic
component={_ctx$.builtins.FootnoteReference}
identifier={"alpha"}
label={"alpha"}
/>
</Dynamic>
<Dynamic component={_ctx$.builtins.FootnoteDefinition} identifier={"alpha"} label={"alpha"}>
<Dynamic component={_ctx$.builtins.Paragraph}>bravo and charlie.</Dynamic>
</Dynamic>
</Dynamic>
);
}
<Table>
, <TableRow>
and <TableCell>
| first | second | third |
| :---- | :----: | ----: |
| foo | bar | baz |
import { useMDX as _useMDX$ } from 'solid-marked';
export default function Component(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.Root}>
<Dynamic component={_ctx$.builtins.Table} align={["left","center","right"]}>
<Dynamic component={_ctx$.builtins.TableRow} isHead={true}>
<Dynamic component={_ctx$.builtins.TableCell}>first</Dynamic>
<Dynamic component={_ctx$.builtins.TableCell}>second</Dynamic>
<Dynamic component={_ctx$.builtins.TableCell}>third</Dynamic>
</Dynamic>
<Dynamic component={_ctx$.builtins.TableRow}>
<Dynamic component={_ctx$.builtins.TableCell}>foo</Dynamic>
<Dynamic component={_ctx$.builtins.TableCell}>bar</Dynamic>
<Dynamic component={_ctx$.builtins.TableCell}>baz</Dynamic>
</Dynamic>
</Dynamic>
</Dynamic>
);
}
<Delete>
~~alpha~~
import { useMDX as _useMDX$ } from 'solid-marked';
export default function Component(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.Root}>
<Dynamic component={_ctx$.builtins.Paragraph}>
<Dynamic component={_ctx$.builtins.Delete}>alpha</Dynamic>
</Dynamic>
</Dynamic>
);
}
MDX
solid-marked
supports MDX, with three compilation modes for the HTML/JSX expressions based on the noDynamicImports
option for the compiler.
Let's take this example code:
<Example>Hello World</Example>
- if set to
true
, the output JSX will not use SolidJS'<Dynamic>
component and instead outputs the target JSX component directly to the markup.import { useMDX as _useMDX$ } from 'solid-marked'; export default function Component(props) { const _ctx$ = _useMDX$(); return ( <_ctx$.builtins.Root> <_ctx$.builtins.Paragraph> <Example>Hello World</Example> </_ctx$.builtins.Paragraph> </_ctx$.builtins.Root> ); }
- if set to
false
, the output will use<Dynamic>
. If an HTML or JSX expression is encountered, the element will be checked if it's not declared, in which it will use its counterpart component fromuseMDX
, otherwise it will use the component/element directly.import { useMDX as _useMDX$ } from 'solid-marked'; export default function Component(props) { const _ctx$ = _useMDX$(); return ( <Dynamic component={_ctx$.builtins.Root}> <Dynamic component={_ctx$.builtins.Paragraph}> <Dynamic component={typeof Example === 'undefined' ? _ctx$.components.Example : Example}> Hello World </Dynamic> </Dynamic> </Dynamic> ); }
- if set to
only-mdx
,<Dynamic>
is only used for builtin components while HTML/JSX expressions will be used directly.import { useMDX as _useMDX$ } from 'solid-marked'; export default function Component(props) { const _ctx$ = _useMDX$(); return ( <Dynamic component={_ctx$.builtins.Root}> <Dynamic component={_ctx$.builtins.Paragraph}> <Example>Hello World</Example> </Dynamic> </Dynamic> ); }
<TableOfContents>
<TableOfContents>
is automatically generated when <Heading>
elements are detected. It is automatically exported and can also be used in the Markdown/MDX.
# foo
## bar
### baz
# alpha
## bravo
### charlie
import { useMDX as _useMDX$ } from 'solid-marked';
export function TableOfContents(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.List} ordered={false} spread={true}>
<Dynamic component={_ctx$.builtins.ListItem} spread={true}>
<Dynamic component={_ctx$.builtins.Paragraph}>
<Dynamic component={_ctx$.builtins.Link} url={"#foo"}>foo</Dynamic>
</Dynamic>
<Dynamic component={_ctx$.builtins.List} ordered={false} spread={true}>
<Dynamic component={_ctx$.builtins.ListItem} spread={true}>
<Dynamic component={_ctx$.builtins.Paragraph}>
<Dynamic component={_ctx$.builtins.Link} url={"#bar"}>bar</Dynamic>
</Dynamic>
<Dynamic component={_ctx$.builtins.List} ordered={false} spread={false}>
<Dynamic component={_ctx$.builtins.ListItem} spread={false}>
<Dynamic component={_ctx$.builtins.Paragraph}>
<Dynamic component={_ctx$.builtins.Link} url={"#baz"}>baz</Dynamic>
</Dynamic>
</Dynamic>
</Dynamic>
</Dynamic>
</Dynamic>
</Dynamic>
<Dynamic component={_ctx$.builtins.ListItem} spread={true}>
<Dynamic component={_ctx$.builtins.Paragraph}>
<Dynamic component={_ctx$.builtins.Link} url={"#alpha"}>alpha</Dynamic>
</Dynamic>
<Dynamic component={_ctx$.builtins.List} ordered={false} spread={true}>
<Dynamic component={_ctx$.builtins.ListItem} spread={true}>
<Dynamic component={_ctx$.builtins.Paragraph}>
<Dynamic component={_ctx$.builtins.Link} url={"#bravo"}>bravo</Dynamic>
</Dynamic>
<Dynamic component={_ctx$.builtins.List} ordered={false} spread={false}>
<Dynamic component={_ctx$.builtins.ListItem} spread={false}>
<Dynamic component={_ctx$.builtins.Paragraph}>
<Dynamic component={_ctx$.builtins.Link} url={"#charlie"}>charlie</Dynamic>
</Dynamic>
</Dynamic>
</Dynamic>
</Dynamic>
</Dynamic>
</Dynamic>
</Dynamic>
);
}
export default function Component(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.Root}>
<Dynamic component={_ctx$.builtins.Heading} depth={1} id={"foo"}>foo</Dynamic>
<Dynamic component={_ctx$.builtins.Heading} depth={2} id={"bar"}>bar</Dynamic>
<Dynamic component={_ctx$.builtins.Heading} depth={3} id={"baz"}>baz</Dynamic>
<Dynamic component={_ctx$.builtins.Heading} depth={1} id={"alpha"}>alpha</Dynamic>
<Dynamic component={_ctx$.builtins.Heading} depth={2} id={"bravo"}>bravo</Dynamic>
<Dynamic component={_ctx$.builtins.Heading} depth={3} id={"charlie"}>charlie</Dynamic>
</Dynamic>
);
}
Frontmatter
solid-marked
supports YAML and TOML frontmatter for Markdown/MDX. The data can be accessed through the frontmatter
variable and also exported automatically. Frontmatter can only be used directly at the first line of the file.
YAML
---
title: Hi, World!
---
# {frontmatter.title}
export const frontmatter = ({title:"Hi, World!"});
import { useMDX as _useMDX$ } from 'solid-marked';
export function TableOfContents(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.List} ordered={false} spread={false}>
<Dynamic component={_ctx$.builtins.ListItem} spread={false}>
<Dynamic component={_ctx$.builtins.Paragraph}>
<Dynamic component={_ctx$.builtins.Link} url={"#frontmattertitle"}>
{frontmatter.title}
</Dynamic>
</Dynamic>
</Dynamic>
</Dynamic>
);
}
export default function Component(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.Root}>
<Dynamic component={_ctx$.builtins.Heading} depth={1} id={"frontmattertitle"}>
{frontmatter.title}
</Dynamic>
</Dynamic>
);
}
TOML
+++
title = "Hi, World!"
+++
# {frontmatter.title}
export const frontmatter = Object.assign(Object.create(null),{title:"Hi, World!"});
import { useMDX as _useMDX$ } from 'solid-marked';
export function TableOfContents(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.List} ordered={false} spread={false}>
<Dynamic component={_ctx$.builtins.ListItem} spread={false}>
<Dynamic component={_ctx$.builtins.Paragraph}>
<Dynamic component={_ctx$.builtins.Link} url={"#frontmattertitle"}>
{frontmatter.title}
</Dynamic>
</Dynamic>
</Dynamic>
</Dynamic>
);
}
export default function Component(props) {
const _ctx$ = _useMDX$();
return (
<Dynamic component={_ctx$.builtins.Root}>
<Dynamic component={_ctx$.builtins.Heading} depth={1} id={"frontmattertitle"}>
{frontmatter.title}
</Dynamic>
</Dynamic>
);
}
Typescript
/// <reference types="solid-marked/env">
Component
The features above are compile-time Markdown rendering. For runtime rendering, solid-marked
provides the Markdown
component:
import Markdown from 'solid-marked/component';
const content = (
<Markdown
builtins={{
Root(props) {
return (
<div>
{props.children}
</div>
);
},
Blockquote(props) {
return (
<blockquote>
{props.children}
</blockquote>
);
},
}}
>
{'> This is a blockquote.'}
</Markdown>
);
[!INFO] Markdown component only supports CommonMark and GFM. MDX and Frontmatter are considered "invalid".
Sponsors
License
MIT © lxsmnsyc