├── .editorconfig
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── config.yml
│ ├── feature_request.md
│ └── question.md
└── workflows
│ └── ci.yml
├── .gitignore
├── .npmrc
├── CHANGELOG.md
├── LICENSE
├── README.md
├── docs
├── .eslintignore
├── .eslintrc.cjs
├── .gitignore
├── .npmrc
├── .nuxtrc
├── README.md
├── app.config.ts
├── content
│ └── 0.index.md
├── nuxt.config.ts
├── package.json
├── pnpm-lock.yaml
├── public
│ ├── cover.png
│ └── favicon.ico
├── renovate.json
├── tokens.config.ts
└── tsconfig.json
├── eslint.config.mjs
├── package.json
├── playground
├── assets
│ └── css
│ │ └── main.css
├── composables
│ ├── useMarkdownGenerator.ts
│ └── useMarkdownParser.ts
├── nuxt.config.ts
├── pages
│ └── index.vue
├── server
│ └── api
│ │ └── parse.ts
└── tsconfig.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── renovate.json
├── scripts
├── bump-edge.ts
├── release-edge.sh
└── release.sh
├── src
├── from-markdown.ts
├── frontmatter.ts
├── index.ts
├── mdast-util-to-markdown.ts
├── micromark-extension
│ ├── constants.ts
│ ├── factory-attributes.ts
│ ├── factory-label.ts
│ ├── factory-name.ts
│ ├── index.ts
│ ├── tokenize-attribute.ts
│ ├── tokenize-binding.ts
│ ├── tokenize-container-indented.ts
│ ├── tokenize-container-suger.ts
│ ├── tokenize-container.ts
│ ├── tokenize-frontmatter.ts
│ ├── tokenize-inline.ts
│ ├── tokenize-span.ts
│ ├── types.ts
│ └── utils.ts
├── to-markdown.ts
├── types.d.ts
└── utils.ts
├── test
├── __snapshots__
│ ├── attributes.test.ts.snap
│ ├── basic.test.ts.snap
│ ├── block-component.test.ts.snap
│ ├── codeblock-props.test.ts.snap
│ ├── inline-component.test.ts.snap
│ ├── label.test.ts.snap
│ ├── span.test.ts.snap
│ └── unwrap.test.ts.snap
├── attributes.test.ts
├── basic.test.ts
├── block-component.test.ts
├── codeblock-props.test.ts
├── inline-component.test.ts
├── label.test.ts
├── span.test.ts
├── unwrap.test.ts
└── utils
│ └── index.ts
└── tsconfig.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_size = 2
6 | indent_style = space
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Report a bug report to help us improve the module.
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | ### Version
11 | module:
12 | nuxt:
13 |
14 | ### Nuxt configuration
15 | #### [mode](https://nuxtjs.org/api/configuration-mode):
16 | - [ ] universal
17 | - [ ] spa
18 |
19 | ### Nuxt configuration
20 |
26 |
27 | ## Reproduction
28 | > :warning: without a minimal reproduction we wont be able to look into your issue
29 |
30 | **Link:**
31 | - [ ] https://codesandbox.io/
32 | - [ ] GitHub repository
33 |
34 | #### What is expected?
35 | #### What is actually happening?
36 | #### Steps to reproduce
37 | ## Additional information
38 | ## Checklist
39 | * [ ] I have tested with the latest Nuxt version and the issue still occurs
40 | * [ ] I have tested with the latest module version and the issue still occurs
41 | * [ ] I have searched the issue tracker and this issue hasn't been reported yet
42 |
43 | ### Steps to reproduce
44 |
45 |
46 | ### What is expected?
47 |
48 |
49 | ### What is actually happening?
50 |
51 |
52 | ### Performance analysis?
53 |
54 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Nuxt Community Discord
4 | url: https://discord.nuxtjs.org/
5 | about: Consider asking questions about the module here.
6 | # - name: Documentation
7 | # url: /README.md
8 | # about: Check our documentation before reporting issues or questions.
9 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea or enhancement for this project.
4 | title: ''
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | ### Is your feature request related to a problem? Please describe.
11 |
12 |
13 | ### Describe the solution you'd like to see
14 |
15 |
16 | ### Describe alternatives you've considered
17 |
18 |
19 | ### Additional context
20 |
21 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/question.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Question
3 | about: Ask a question about the module.
4 | title: ''
5 | labels: question
6 | assignees: ''
7 |
8 | ---
9 |
10 |
21 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: ci
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | pull_request:
8 | branches:
9 | - main
10 |
11 | jobs:
12 | ci:
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - uses: actions/checkout@v4
17 | - uses: actions/setup-node@v4
18 | with:
19 | node-version: '20'
20 | - run: corepack enable
21 | - run: pnpm install
22 | - run: pnpm dev:prepare
23 | - run: pnpm lint
24 | - run: pnpm test
25 | - run: pnpm build
26 | - uses: codecov/codecov-action@v2
27 | - name: Release Edge
28 | if: |
29 | github.event_name == 'push' &&
30 | !contains(github.event.head_commit.message, '[skip-release]') &&
31 | !startsWith(github.event.head_commit.message, 'docs')
32 | run: ./scripts/release-edge.sh
33 | env:
34 | NODE_AUTH_TOKEN: ${{secrets.NODE_AUTH_TOKEN}}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependencies
2 | node_modules
3 | jspm_packages
4 |
5 | package-lock.json
6 | # */**/yarn.lock
7 |
8 | # Logs
9 | *.log
10 |
11 | # Temp directories
12 | .temp
13 | .tmp
14 | .cache
15 |
16 | # Yarn
17 | **/.yarn/cache
18 | **/.yarn/*state*
19 |
20 | # Generated dirs
21 | dist
22 | .nuxt
23 | .nuxt-*
24 | .output
25 | .gen
26 | nuxt.d.ts
27 |
28 | # Junit reports
29 | reports
30 |
31 | # Coverage reports
32 | coverage
33 | *.lcov
34 | .nyc_output
35 |
36 | # VSCode
37 | .vscode
38 |
39 | # Intellij idea
40 | *.iml
41 | .idea
42 |
43 | # OSX
44 | .DS_Store
45 | .AppleDouble
46 | .LSOverride
47 |
48 | # Files that might appear in the root of a volume
49 | .DocumentRevisions-V100
50 | .fseventsd
51 | .Spotlight-V100
52 | .TemporaryItems
53 | .Trashes
54 | .VolumeIcon.icns
55 | .com.apple.timemachine.donotpresent
56 |
57 | # Directories potentially created on remote AFP share
58 | .AppleDB
59 | .AppleDesktop
60 | Network Trash Folder
61 | Temporary Items
62 | .apdisk
63 |
64 | .vercel_build_output
65 | .build-*
66 | .env
67 | .netlify
68 | .data
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | shamefully-hoist=true
2 | strict-peer-dependencies=false
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) NuxtLabs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Remark MDC
2 |
3 | [![npm version][npm-version-src]][npm-version-href]
4 | [![npm downloads][npm-downloads-src]][npm-downloads-href]
5 | [![License][license-src]][license-href]
6 |
7 | Remark plugin to parse Markdown Components syntax.
8 |
9 | For MDC syntax highlight on VS Code, checkout [vscode-mdc](https://github.com/nuxtlabs/vscode-mdc).
10 |
11 | ## Setup
12 |
13 | Add the `remark-mdc` dependency to your project:
14 |
15 | ```bash
16 | # yarn
17 | yarn add --dev remark-mdc
18 | # npm
19 | npm install --save-dev remark-mdc
20 | # pnpm
21 | pnpm add --save-dev remark-mdc
22 | ```
23 |
24 | Then, add `remark-mdc` to the `unified` streams:
25 |
26 | ```ts
27 | import { unified } from 'unified'
28 | import remarkParse from 'remark-parse'
29 | import remarkMDC from 'remark-mdc'
30 |
31 | function parse(md: string) {
32 | const processor = unified()
33 | processor.use(remarkParse)
34 |
35 | // Use `remark-mdc` plugin to parse MDC syntax
36 | processor.use(remarkMDC)
37 |
38 | // ...
39 |
40 | return processor.process({ value: content, data: frontmatter })
41 | }
42 | ```
43 |
44 | That's it! ✨
45 |
46 | ## Syntax
47 |
48 | ### `^-` Frontmatter
49 |
50 | Front-matter is a convention of Markdown-based CMS to provide metadata to documents, like description or title. Remark MDC uses the YAML syntax with `key: value` pairs.
51 |
52 | To define frontmatter, start your document with `---\n---` section and put your desired data in YAML format within this section.
53 |
54 | ```md
55 | ---
56 | title: 'Title of the page'
57 | description: 'meta description of the page'
58 | ---
59 |
60 |
61 | ```
62 |
63 | ### `:` Inline Components
64 |
65 | Inline components are entries that will stick inside the parent paragraph. Like spans, emojis, icons, etc. Inline components can be defined by a single `:` followed by the component name.
66 |
67 | ```md
68 | A simple :inline-component
69 | ```
70 |
71 | You may want to pass some text into an inline component; you can do it using the `[TEXT]` syntax.
72 |
73 | ```md
74 | A simple :inline-component[John Doe]
75 | ```
76 |
77 | If you want to use an inline component followed by specific characters like `-`, `_`, or `:`, you can use a dummy props specifier after it.
78 |
79 | ```md
80 | How to say :hello{}-world in Markdown
81 | ```
82 |
83 | In this example, `:hello{}` will search for the `` component, and `-world` will be plain text.
84 |
85 | > Note: If you put an inline component alone in a single line, it will be transformed into a block component. This is sugar syntax for block components.
86 | > ```md
87 | > Paragraph a
88 | >
89 | > :block-component
90 | >
91 | > Paragraph b
92 | > ```
93 |
94 |
95 | ### `::` Block Components
96 |
97 | Block components are components that accept Markdown content or another component as a slot.
98 |
99 | Block components are defined by the `::` identifier.
100 |
101 | ```md
102 | ::card
103 | The content of the card
104 | ::
105 | ```
106 |
107 | Block components can be used without any content.
108 |
109 | ```md
110 | ::card
111 | ::
112 | ```
113 |
114 | Or with sugar syntax. Note that in sugar syntax, it is important to put the component alone on a separate line.
115 |
116 | ```md
117 | A paragraph
118 |
119 | :card
120 | ```
121 |
122 | ### `#` Slots
123 |
124 | Block components can accept slots (like Vue slots) with different names. The content of these slots can be anything from a normal markdown paragraph to a nested block component.
125 |
126 | - The `default` slot renders the top-level content inside the block component.
127 | - Named slots use the `#` identifier to render the corresponding content.
128 |
129 | ```md
130 | ::hero
131 | Default slot text
132 |
133 | #description
134 | This will be rendered inside the `description` slot.
135 | ```
136 |
137 | ### `:::` Nesting
138 |
139 | MDC supports nested components inside slots by indenting them. To make nested components visually distinguishable, you can indent nested components and add more `:` when you define them.
140 |
141 | ```md
142 | ::hero
143 | :::card
144 | A nested card
145 |
146 | ::::card
147 | A super nested card
148 | ::::
149 | :::
150 | ::
151 | ```
152 |
153 | ### `[]` Span
154 |
155 | To create inline spans in your text, you can use the `[]` identifier.
156 |
157 | ```md
158 | Hello [World]
159 | ```
160 |
161 | This syntax is useful in combination with inline props to make text visually different from the rest of the paragraph. Check out the inline props section to read more about props.
162 |
163 | ```md
164 | Hello [World]{.bg-blue-500}!
165 | ```
166 |
167 | ### `{}` Inline Props
168 |
169 | Using the inline props syntax, you can pass props and attributes to your components. MDC goes a step further and allows you to pass attributes to markdown native elements like images, links, bold texts, and more.
170 |
171 | To define properties for a component or a markdown element, you need to create a props scope `{}` exactly after the component/element definition. Then you can define the properties inline within this scope using a `key=value` syntax.
172 |
173 | ```md
174 | Inline :component{key="value" key2=value2}
175 |
176 | ::block-component{no-border title="My Component"}
177 | ::
178 |
179 | [Link](https://nuxt.com){class="nuxt"}
180 |
181 | {class=".nuxt-logo"}
182 |
183 | `code`{style="color: red"}
184 |
185 | _italic_{style="color: blue"}
186 |
187 | **bold**{style="color: blue"}
188 | ```
189 |
190 | There are also a couple of sugar syntaxes for common use-cases:
191 |
192 | - `id` attribute: `_italic_{#the_italic_text}`
193 | - `class` attribute: `**bold**{.bold .text.with_attribute}`
194 | - No value (boolean props): `:component{no-border}`
195 | - Single string without any space: `**bold**{class=red}`
196 |
197 | If you want to pass arrays or objects as props to components, you can pass them as a JSON string and prefix the prop key with a colon to automatically decode the JSON string. Note that in this case, you should use single quotes for the value string so you can use double quotes to pass a valid JSON string:
198 |
199 | ```md
200 | ::dropdown{:items='["Nuxt", "Vue", "React"]'}
201 | String Array
202 | ::
203 |
204 | ::dropdown{:items='[1,2,3.5]'}
205 | Number Array
206 | ::
207 |
208 | ::chart{:options='{"responsive": true, "scales": {"y": {"beginAtZero": true}}}'}
209 | Object
210 | ::
211 | ```
212 |
213 | > **Escape Character Behavior:** In MDC, the escape character (backslash `\`) can be used to escape special characters within attribute values. However, this behavior only applies to bound attributes. For example, in a bound attribute like `:list="[\"item 1\"]"`, the escape character will allow you to include quotes within the value. In contrast, for non-bound attributes such as `class="my-class"`, the escape character will not have any effect, and the value will be rendered as is. Therefore, it's important to use binding when you need to include special characters in attribute values.
214 |
215 | ### `---` Yaml Props
216 |
217 | The YAML method uses the `---` identifier to declare one prop per line, which can be useful for readability.
218 |
219 | ```md
220 | ::icon-card
221 | ---
222 | icon: IconNuxt
223 | description: Harness the full power of Nuxt and the Nuxt ecosystem.
224 | title: Nuxt Architecture.
225 | ---
226 | ::
227 | ```
228 |
229 | ### `{{}}` Binding Variables
230 |
231 | The `{{ $doc.variable || 'defaultValue' }}` syntax allows you to bind variables in your Markdown content. This is especially useful when you want to dynamically insert values into your document.
232 |
233 | To use this syntax, simply enclose the variable name within double curly braces, like so:
234 |
235 | ```md
236 | ---
237 | color: blue
238 | ---
239 |
240 | # The color is {{ $doc.color || 'red' }}.
241 | ```
242 |
243 | ## Contributing
244 |
245 | You can contribute to this module online with CodeSandbox:
246 |
247 | [](https://codesandbox.io/s/github/nuxtlabs/remark-mdc/tree/main/?fontsize=14&hidenavigation=1&theme=dark)
248 |
249 | Or locally:
250 |
251 | 1. Clone this repository
252 | 2. Install dependencies using `pnpm install`
253 | 3. Start the development server using `pnpm dev`
254 |
255 | ## License
256 |
257 | [MIT License](https://github.com/nuxtlabs/remark-mdc/blob/main/LICENSE)
258 |
259 | Copyright (c) NuxtLabs
260 |
261 |
262 |
263 | [npm-version-src]: https://img.shields.io/npm/v/remark-mdc/latest.svg?style=flat&colorA=020420&colorB=28CF8D
264 | [npm-version-href]: https://npmjs.com/package/remark-mdc
265 |
266 | [npm-downloads-src]: https://img.shields.io/npm/dm/remark-mdc.svg?style=flat&colorA=020420&colorB=28CF8D
267 | [npm-downloads-href]: https://npmjs.com/package/remark-mdc
268 |
269 | [license-src]: https://img.shields.io/github/license/nuxtlabs/remark-mdc.svg?style=flat&colorA=020420&colorB=28CF8D
270 | [license-href]: https://github.com/nuxtlabs/remark-mdc/blob/main/LICENSE
271 |
--------------------------------------------------------------------------------
/docs/.eslintignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 | .output
4 | .nuxt
--------------------------------------------------------------------------------
/docs/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: '@nuxt/eslint-config',
4 | rules: {
5 | 'vue/max-attributes-per-line': 'off',
6 | 'vue/multi-word-component-names': 'off',
7 | },
8 | }
9 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.iml
3 | .idea
4 | *.log*
5 | .nuxt
6 | .vscode
7 | .DS_Store
8 | coverage
9 | dist
10 | sw.*
11 | .env
12 | .output
13 |
--------------------------------------------------------------------------------
/docs/.npmrc:
--------------------------------------------------------------------------------
1 | shamefully-hoist=true
2 | strict-peer-dependencies=false
3 |
--------------------------------------------------------------------------------
/docs/.nuxtrc:
--------------------------------------------------------------------------------
1 | imports.autoImport=true
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Docus Starter
2 |
3 | Starter template for [Docus](https://docus.dev).
4 |
5 | ## Clone
6 |
7 | Clone the repository (using `nuxi`):
8 |
9 | ```bash
10 | npx nuxi init -t themes/docus
11 | ```
12 |
13 | ## Setup
14 |
15 | Install dependencies:
16 |
17 | ```bash
18 | yarn install
19 | ```
20 |
21 | ## Development
22 |
23 | ```bash
24 | yarn dev
25 | ```
26 |
27 | ## Edge Side Rendering
28 |
29 | Can be deployed to Vercel Functions, Netlify Functions, AWS, and most Node-compatible environments.
30 |
31 | Look at all the available presets [here](https://v3.nuxtjs.org/guide/deploy/presets).
32 |
33 | ```bash
34 | yarn build
35 | ```
36 |
37 | ## Static Generation
38 |
39 | Use the `generate` command to build your application.
40 |
41 | The HTML files will be generated in the .output/public directory and ready to be deployed to any static compatible hosting.
42 |
43 | ```bash
44 | yarn generate
45 | ```
46 |
47 | ## Preview build
48 |
49 | You might want to preview the result of your build locally, to do so, run the following command:
50 |
51 | ```bash
52 | yarn preview
53 | ```
54 |
55 | ---
56 |
57 | For a detailed explanation of how things work, check out [Docus](https://docus.dev).
58 |
--------------------------------------------------------------------------------
/docs/app.config.ts:
--------------------------------------------------------------------------------
1 | export default defineAppConfig({
2 | docus: {
3 | title: 'Remark MDC',
4 | description: 'Remark plugin to parse Markdown Components syntax.',
5 | image: 'https://remark-mdc.nuxt.space/cover.png',
6 |
7 | socials: {
8 | twitter: 'nuxt_js',
9 | github: 'nuxtlabs/remark-mdc',
10 | nuxt: {
11 | label: 'Nuxt',
12 | icon: 'simple-icons:nuxtdotjs',
13 | href: 'https://nuxt.com',
14 | },
15 | },
16 |
17 | github: {
18 | dir: 'docs/content',
19 | branch: 'main',
20 | repo: 'remark-mdc',
21 | owner: 'nuxtlabs',
22 | edit: true,
23 | },
24 |
25 | aside: {
26 | level: 0,
27 | collapsed: false,
28 | exclude: [],
29 | },
30 |
31 | main: {
32 | padded: true,
33 | fluid: false,
34 | },
35 |
36 | header: {
37 | logo: false,
38 | showLinkIcon: true,
39 | exclude: [],
40 | fluid: false,
41 | title: 'Remark MDC',
42 | },
43 | },
44 | })
45 |
--------------------------------------------------------------------------------
/docs/content/0.index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Remark MDC
3 | ---
4 |
5 |
6 | # Remark MDC
7 |
8 | Remark plugin to parse Markdown Components syntax.
9 |
10 | ## Setup
11 |
12 | Add the `remark-mdc` dependency to your project:
13 |
14 | ::code-group
15 | ```bash [Yarn]
16 | yarn add --dev remark-mdc
17 | ```
18 | ```bash [NPM]
19 | npm install --save-dev remark-mdc
20 | ```
21 | ```bash [PNPM]
22 | pnpm add --save-dev remark-mdc
23 | ```
24 | ::
25 |
26 | Then, add `remark-mdc` to the `unified` streams:
27 |
28 | ```ts
29 | import { unified } from 'unified'
30 | import remarkParse from 'remark-parse'
31 | import remarkMDC from 'remark-mdc'
32 |
33 | function parse(md: string) {
34 | const processor = unified()
35 | processor.use(remarkParse)
36 |
37 | // Use `remark-mdc` plugin to parse MDC syntax
38 | processor.use(remarkMDC)
39 |
40 | // ...
41 |
42 | return processor.process({ value: content, data: frontmatter })
43 | }
44 | ```
45 |
46 | That's it! ✨
47 |
48 | ## Syntax
49 |
50 | ### `^-` Frontmatter
51 |
52 | Front-matter is a convention of Markdown-based CMS to provide metadata to documents, like description or title. Remark MDC uses the YAML syntax with `key: value` pairs.
53 |
54 | To define frontmatter, start your document with `---\n---` section and put your desired data in YAML format within this section.
55 |
56 | ```md
57 | ---
58 | title: 'Title of the page'
59 | description: 'meta description of the page'
60 | ---
61 |
62 |
63 | ```
64 |
65 | ### `:` Inline Components
66 |
67 | Inline components are entries that will stick inside the parent paragraph. Like spans, emojis, icons, etc. Inline components can be defined by a single `:` followed by the component name.
68 |
69 | ```md
70 | A simple :inline-component
71 | ```
72 |
73 | You may want to pass some text into an inline component; you can do it using the `[TEXT]` syntax.
74 |
75 | ```md
76 | A simple :inline-component[John Doe]
77 | ```
78 |
79 | If you want to use an inline component followed by specific characters like `-`, `_`, or `:`, you can use a dummy props specifier after it.
80 |
81 | ```md
82 | How to say :hello{}-world in Markdown
83 | ```
84 |
85 | In this example, `:hello{}` will search for the `` component, and `-world` will be plain text.
86 |
87 | ::alert
88 | Note: If you put an inline component alone in a single line, it will be transformed into a block component. This is sugar syntax for block components.
89 | ```md
90 | Paragraph a
91 |
92 | :block-component
93 |
94 | Paragraph b
95 | ```
96 | ::
97 |
98 | ### `::` Block Components
99 |
100 | Block components are components that accept Markdown content or another component as a slot.
101 |
102 | Block components are defined by the `::` identifier.
103 |
104 | ```md
105 | ::card
106 | The content of the card
107 | ::
108 | ```
109 |
110 | Block components can be used without any content.
111 |
112 | ```md
113 | ::card
114 | ::
115 | ```
116 |
117 | Or with sugar syntax. Note that in sugar syntax, it is important to put the component alone on a separate line.
118 |
119 | ```md
120 | A paragraph
121 |
122 | :card
123 | ```
124 |
125 | ### `#` Slots
126 |
127 | Block components can accept slots (like Vue slots) with different names. The content of these slots can be anything from a normal markdown paragraph to a nested block component.
128 |
129 | - The `default` slot renders the top-level content inside the block component.
130 | - Named slots use the `#` identifier to render the corresponding content.
131 |
132 | ```md
133 | ::hero
134 | Default slot text
135 |
136 | #description
137 | This will be rendered inside the `description` slot.
138 | ```
139 |
140 | ### `:::` Nesting
141 |
142 | MDC supports nested components inside slots by indenting them. To make nested components visually distinguishable, you can indent nested components and add more `:` when you define them.
143 |
144 | ```md
145 | ::hero
146 | :::card
147 | A nested card
148 |
149 | ::::card
150 | A super nested card
151 | ::::
152 | :::
153 | ::
154 | ```
155 |
156 | ### `[]` Span
157 |
158 | To create inline spans in your text, you can use the `[]` identifier.
159 |
160 | ```md
161 | Hello [World]
162 | ```
163 |
164 | This syntax is useful in combination with inline props to make text visually different from the rest of the paragraph. Check out the inline props section to read more about props.
165 |
166 | ```md
167 | Hello [World]{.bg-blue-500}!
168 | ```
169 |
170 | ### `{}` Inline Props
171 |
172 | Using the inline props syntax, you can pass props and attributes to your components. MDC goes a step further and allows you to pass attributes to markdown native elements like images, links, bold texts, and more.
173 |
174 | To define properties for a component or a markdown element, you need to create a props scope `{}` exactly after the component/element definition. Then you can define the properties inline within this scope using a `key=value` syntax.
175 |
176 | ```md
177 | Inline :component{key="value" key2=value2}
178 |
179 | ::block-component{no-border title="My Component"}
180 | ::
181 |
182 | [Link](https://nuxt.com){class="nuxt"}
183 |
184 | {class=".nuxt-logo"}
185 |
186 | `code`{style="color: red"}
187 |
188 | _italic_{style="color: blue"}
189 |
190 | **bold**{style="color: blue"}
191 | ```
192 |
193 | There are also a couple of sugar syntaxes for common use-cases:
194 |
195 | - `id` attribute: `_italic_{#the_italic_text}`
196 | - `class` attribute: `**bold**{.bold .text.with_attribute}`
197 | - No value (boolean props): `:component{no-border}`
198 | - Single string without any space: `**bold**{class=red}`
199 |
200 | If you want to pass arrays or objects as props to components, you can pass them as a JSON string and prefix the prop key with a colon to automatically decode the JSON string. Note that in this case, you should use single quotes for the value string so you can use double quotes to pass a valid JSON string:
201 |
202 | ```md
203 | ::dropdown{:items='["Nuxt", "Vue", "React"]'}
204 | String Array
205 | ::
206 |
207 | ::dropdown{:items='[1,2,3.5]'}
208 | Number Array
209 | ::
210 |
211 | ::chart{:options='{"responsive": true, "scales": {"y": {"beginAtZero": true}}}'}
212 | Object
213 | ::
214 | ```
215 |
216 | ### `---` Yaml Props
217 |
218 | The YAML method uses the `---` identifier to declare one prop per line, which can be useful for readability.
219 |
220 | ```md
221 | ::icon-card
222 | ---
223 | icon: IconNuxt
224 | description: Harness the full power of Nuxt and the Nuxt ecosystem.
225 | title: Nuxt Architecture.
226 | ---
227 | ::
228 | ```
229 |
230 | ### `{{}}` Binding Variables
231 |
232 | The `{{ $doc.variable || 'defaultValue' }}` syntax allows you to bind variables in your Markdown content. This is especially useful when you want to dynamically insert values into your document.
233 |
234 | To use this syntax, simply enclose the variable name within double curly braces, like so:
235 |
236 | ```md
237 | ---
238 | color: blue
239 | ---
240 |
241 | # The color is {{ $doc.color || 'red' }}.
242 | ```
243 |
244 | ## Contributing
245 |
246 | You can contribute to this module online with CodeSandbox:
247 |
248 | [](https://codesandbox.io/s/github/nuxtlabs/remark-mdc/tree/main/?fontsize=14&hidenavigation=1&theme=dark)
249 |
250 | Or locally:
251 |
252 | 1. Clone this repository
253 | 2. Install dependencies using `pnpm install`
254 | 3. Start the development server using `pnpm dev`
255 |
256 | ## License
257 |
258 | [MIT License](https://github.com/nuxtlabs/remark-mdc/blob/main/LICENSE)
259 |
260 | Copyright (c) NuxtLabs
261 |
--------------------------------------------------------------------------------
/docs/nuxt.config.ts:
--------------------------------------------------------------------------------
1 | export default defineNuxtConfig({
2 | // https://github.com/nuxt-themes/docus
3 | extends: '@nuxt-themes/docus',
4 |
5 | modules: [
6 | // https://github.com/nuxt-modules/plausible
7 | '@nuxtjs/plausible',
8 | // https://github.com/nuxt/devtools
9 | '@nuxt/devtools',
10 | ],
11 | })
12 |
--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "remark-mdc-docs",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "nuxi dev",
7 | "build": "nuxi build",
8 | "generate": "nuxi generate",
9 | "preview": "nuxi preview",
10 | "lint": "eslint ."
11 | },
12 | "devDependencies": {
13 | "@nuxt-themes/docus": "^1.15.1",
14 | "@nuxt/devtools": "^2.4.0",
15 | "@nuxt/eslint-config": "^1.3.0",
16 | "@nuxtjs/plausible": "^1.2.0",
17 | "@types/node": "^22.14.1",
18 | "eslint": "^9.25.1",
19 | "nuxt": "^3.16.2"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/docs/public/cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuxtlabs/remark-mdc/72dc99ca2a87511ad9d9415f02a4c6a78f389855/docs/public/cover.png
--------------------------------------------------------------------------------
/docs/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nuxtlabs/remark-mdc/72dc99ca2a87511ad9d9415f02a4c6a78f389855/docs/public/favicon.ico
--------------------------------------------------------------------------------
/docs/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "@nuxtjs"
4 | ],
5 | "lockFileMaintenance": {
6 | "enabled": true
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/docs/tokens.config.ts:
--------------------------------------------------------------------------------
1 | import { defineTheme } from 'pinceau'
2 |
3 | export default defineTheme({
4 | })
5 |
--------------------------------------------------------------------------------
/docs/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./.nuxt/tsconfig.json"
3 | }
4 |
--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | import { createConfigForNuxt } from '@nuxt/eslint-config/flat'
3 |
4 | // Run `npx @eslint/config-inspector` to inspect the resolved config interactively
5 | export default createConfigForNuxt({
6 | features: {
7 | // Rules for module authors
8 | tooling: true,
9 | // Rules for formatting
10 | stylistic: true,
11 | },
12 | dirs: {
13 | src: [
14 | './playground',
15 | './examples/blog',
16 | ],
17 | },
18 | })
19 | .append(
20 | {
21 | rules: {
22 | '@typescript-eslint/no-explicit-any': 'off',
23 | 'vue/multi-word-component-names': 'off',
24 | '@typescript-eslint/no-empty-object-type': 'off',
25 | },
26 | },
27 | )
28 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "remark-mdc",
3 | "version": "3.6.0",
4 | "description": "Remark plugin to support MDC syntax",
5 | "keywords": [
6 | "remark",
7 | "mdc"
8 | ],
9 | "repository": "https://github.com/nuxtlabs/remark-mdc",
10 | "license": "MIT",
11 | "exports": "./dist/index.mjs",
12 | "types": "./dist/index.d.ts",
13 | "type": "module",
14 | "files": [
15 | "dist"
16 | ],
17 | "scripts": {
18 | "build": "unbuild",
19 | "dev": "nuxi dev playground",
20 | "dev:prepare": "nuxi prepare playground",
21 | "generate": "nuxi generate playground",
22 | "lint": "eslint",
23 | "test:ui": "pnpm lint && vitest --ui --open=false",
24 | "test": "vitest run",
25 | "prepack": "pnpm build",
26 | "release": "release-it",
27 | "typecheck": "nuxt typecheck",
28 | "verify": "npm run dev:prepare && npm run lint && npm run test && npm run typecheck"
29 | },
30 | "dependencies": {
31 | "@types/mdast": "^4.0.4",
32 | "@types/unist": "^3.0.3",
33 | "flat": "^6.0.1",
34 | "mdast-util-from-markdown": "^2.0.2",
35 | "mdast-util-to-markdown": "^2.1.2",
36 | "micromark": "^4.0.2",
37 | "micromark-core-commonmark": "^2.0.3",
38 | "micromark-factory-space": "^2.0.1",
39 | "micromark-factory-whitespace": "^2.0.1",
40 | "micromark-util-character": "^2.1.1",
41 | "micromark-util-types": "^2.0.2",
42 | "parse-entities": "^4.0.2",
43 | "scule": "^1.3.0",
44 | "stringify-entities": "^4.0.4",
45 | "unified": "^11.0.5",
46 | "unist-util-visit": "^5.0.0",
47 | "unist-util-visit-parents": "^6.0.1",
48 | "yaml": "^2.7.1"
49 | },
50 | "devDependencies": {
51 | "@nuxt/eslint-config": "^1.3.0",
52 | "@nuxt/kit": "^3.16.2",
53 | "@nuxt/ui": "3.0.2",
54 | "@nuxthub/core": "^0.8.25",
55 | "@nuxtjs/eslint-config-typescript": "latest",
56 | "@types/flat": "^5.0.5",
57 | "@types/js-yaml": "^4.0.9",
58 | "@types/node": "^22.14.1",
59 | "eslint": "^9.25.1",
60 | "eslint-plugin-nuxt": "latest",
61 | "jiti": "^2.4.2",
62 | "nuxt": "^3.16.2",
63 | "release-it": "^19.0.1",
64 | "remark-gfm": "^4.0.1",
65 | "remark-parse": "^11.0.0",
66 | "remark-stringify": "^11.0.0",
67 | "remark-wiki-link": "^2.0.1",
68 | "unbuild": "^3.5.0",
69 | "vitest": "latest"
70 | },
71 | "release-it": {
72 | "git": {
73 | "commitMessage": "chore(release): release v${version}"
74 | },
75 | "github": {
76 | "release": true,
77 | "releaseName": "v${version}"
78 | },
79 | "hooks": {
80 | "after:bump": "npx changelogen@latest --no-commit --no-tag --output --r $(node -p \"require('./package.json').version\")"
81 | }
82 | },
83 | "packageManager": "pnpm@10.9.0"
84 | }
85 |
--------------------------------------------------------------------------------
/playground/assets/css/main.css:
--------------------------------------------------------------------------------
1 | @import "tailwindcss";
2 | @import "@nuxt/ui";
3 |
--------------------------------------------------------------------------------
/playground/composables/useMarkdownGenerator.ts:
--------------------------------------------------------------------------------
1 | import type { Root, Node } from 'mdast'
2 | import type { Processor } from 'unified'
3 | import type { Ref } from 'vue'
4 |
5 | // workaround for kleur
6 | process.env = process.env || {}
7 |
8 | function jsonParser(this: any) {
9 | this.Parser = function (root: any) {
10 | return JSON.parse(root)
11 | }
12 | }
13 | export function useMarkdownGenerator(input: Ref