| 1 | # Configure Pre-Parser |
| 2 | |
| 3 | ::: info |
| 4 | Custom pre-parsers are not supposed to be used too often. Usually you can use [Transformers](./config-transformers) for custom syntaxes. |
| 5 | ::: |
| 6 | |
| 7 | Slidev parses your presentation file (e.g. `slides.md`) in three steps: |
| 8 | |
| 9 | 1. A "preparsing" step is carried out: the file is split into slides using the `---` separator, and considering the possible frontmatter blocks. |
| 10 | 2. Each slide is parsed with an external library. |
| 11 | 3. Slidev resolves the special frontmatter property `src: ....`, which allows to include other md files. |
| 12 | |
| 13 | ## Markdown Parser |
| 14 | |
| 15 | Configuring the markdown parser used in step 2 can be done by [configuring Vite internal plugins](/custom/config-vite#configure-internal-plugins). |
| 16 | |
| 17 | ## Preparser Extensions |
| 18 | |
| 19 | > Available since v0.37.0. |
| 20 | |
| 21 | ::: warning |
| 22 | Important: when modifying the preparser configuration, you need to stop and start Slidev again (restart might not be sufficient). |
| 23 | ::: |
| 24 | |
| 25 | The preparser (step 1 above) is highly extensible and allows you to implement custom syntaxes for your md files. Extending the preparser is considered **an advanced feature** and is susceptible to breaking [editor integrations](../features/side-editor) due to implicit changes in the syntax. |
| 26 | |
| 27 | To customize it, create a `./setup/preparser.ts` file with the following content: |
| 28 | |
| 29 | ```ts twoslash [./setup/preparser.ts] |
| 30 | import { definePreparserSetup } from '@slidev/types' |
| 31 | |
| 32 | export default definePreparserSetup(({ filepath, headmatter, mode }) => { |
| 33 | return [ |
| 34 | { |
| 35 | transformRawLines(lines) { |
| 36 | for (const i in lines) { |
| 37 | if (lines[i] === '@@@') |
| 38 | lines[i] = 'HELLO' |
| 39 | } |
| 40 | }, |
| 41 | } |
| 42 | ] |
| 43 | }) |
| 44 | ``` |
| 45 | |
| 46 | This example systematically replaces any `@@@` line with a line with `hello`. It illustrates the structure of a preparser configuration file and some of the main concepts the preparser involves: |
| 47 | |
| 48 | - `definePreparserSetup` must be called with a function as parameter. |
| 49 | - The function receives the file path (of the root presentation file), the headmatter (from the md file) and, since v0.48.0, a mode (dev, build or export). It could use this information (e.g., enable extensions based on the presentation file or whether we are exporting a PDF). |
| 50 | - The function must return a list of preparser extensions. |
| 51 | - An extension can contain: |
| 52 | - a `transformRawLines(lines)` function that runs just after parsing the headmatter of the md file and receives a list of all lines (from the md file). The function can mutate the list arbitrarily. |
| 53 | - a `transformSlide(content, frontmatter)` function that is called for each slide, just after splitting the file, and receives the slide content as a string and the frontmatter of the slide as an object. The function can mutate the frontmatter and must return the content string (possibly modified, possibly `undefined` if no modifications have been done). |
| 54 | - a `transformNote(note, frontmatter)` function that is called for each slide, just after splitting the file, and receives the slide note as a string or undefined and the frontmatter of the slide as an object. The function can mutate the frontmatter and must return the note string (possibly modified, possibly `undefined` if no modifications have been done). |
| 55 | - a `name` |
| 56 | |
| 57 | ## Example Preparser Extensions |
| 58 | |
| 59 | ### Use case 1: compact syntax top-level presentation |
| 60 | |
| 61 | Imagine a situation where (part of) your presentation is mainly showing cover images and including other md files. You might want a compact notation where for instance (part of) `slides.md` is as follows: |
| 62 | |
| 63 | <!-- eslint-skip --> |
| 64 | |
| 65 | ```md |
| 66 | @cover: /nice.jpg |
| 67 | # Welcome |
| 68 | @src: page1.md |
| 69 | @src: page2.md |
| 70 | @cover: /break.jpg |
| 71 | @src: pages3-4.md |
| 72 | @cover: https://cover.sli.dev |
| 73 | # Questions? |
| 74 | see you next time |
| 75 | ``` |
| 76 | |
| 77 | To allow these `@src:` and `@cover:` syntaxes, create a `./setup/preparser.ts` file with the following content: |
| 78 | |
| 79 | ```ts twoslash [./setup/preparser.ts] |
| 80 | import { definePreparserSetup } from '@slidev/types' |
| 81 | |
| 82 | export default definePreparserSetup(() => { |
| 83 | return [ |
| 84 | { |
| 85 | transformRawLines(lines) { |
| 86 | let i = 0 |
| 87 | while (i < lines.length) { |
| 88 | const l = lines[i] |
| 89 | if (/^@cover:/i.test(l)) { |
| 90 | lines.splice( |
| 91 | i, |
| 92 | 1, |
| 93 | '---', |
| 94 | 'layout: cover', |
| 95 | `background: ${l.replace(/^@cover: */i, '')}`, |
| 96 | '---', |
| 97 | '' |
| 98 | ) |
| 99 | continue |
| 100 | } |
| 101 | if (/^@src:/i.test(l)) { |
| 102 | lines.splice( |
| 103 | i, |
| 104 | 1, |
| 105 | '---', |
| 106 | `src: ${l.replace(/^@src: */i, '')}`, |
| 107 | '---', |
| 108 | '' |
| 109 | ) |
| 110 | continue |
| 111 | } |
| 112 | i++ |
| 113 | } |
| 114 | } |
| 115 | }, |
| 116 | ] |
| 117 | }) |
| 118 | ``` |
| 119 | |
| 120 | And that's it. |
| 121 | |
| 122 | ### Use case 2: using custom frontmatter to wrap slides |
| 123 | |
| 124 | Imagine a case where you often want to scale some of your slides but still want to use a variety of existing layouts so creating a new layout would not be suited. |
| 125 | For instance, you might want to write your `slides.md` as follows: |
| 126 | |
| 127 | <!-- eslint-skip --> |
| 128 | |
| 129 | ```md |
| 130 | --- |
| 131 | layout: quote |
| 132 | _scale: 0.75 |
| 133 | --- |
| 134 | |
| 135 | # Welcome |
| 136 | |
| 137 | > great! |
| 138 | |
| 139 | --- |
| 140 | _scale: 4 |
| 141 | --- |
| 142 | # Break |
| 143 | |
| 144 | --- |
| 145 | |
| 146 | # Ok |
| 147 | |
| 148 | --- |
| 149 | layout: center |
| 150 | _scale: 2.5 |
| 151 | --- |
| 152 | # Questions? |
| 153 | see you next time |
| 154 | ``` |
| 155 | |
| 156 | Here we used an underscore in `_scale` to avoid possible conflicts with existing frontmatter properties (indeed, the case of `scale`, without underscore would cause potential problems). |
| 157 | |
| 158 | To handle this `_scale: ...` syntax in the frontmatter, create a `./setup/preparser.ts` file with the following content: |
| 159 | |
| 160 | ```ts twoslash [./setup/preparser.ts] |
| 161 | import { definePreparserSetup } from '@slidev/types' |
| 162 | |
| 163 | export default definePreparserSetup(() => { |
| 164 | return [ |
| 165 | { |
| 166 | async transformSlide(content, frontmatter) { |
| 167 | if ('_scale' in frontmatter) { |
| 168 | return [ |
| 169 | `<Transform :scale=${frontmatter._scale}>`, |
| 170 | '', |
| 171 | content, |
| 172 | '', |
| 173 | '</Transform>' |
| 174 | ].join('\n') |
| 175 | } |
| 176 | }, |
| 177 | }, |
| 178 | ] |
| 179 | }) |
| 180 | ``` |
| 181 | |
| 182 | And that's it. |
| 183 | |
| 184 | ### Use case 3: using custom frontmatter to transform note |
| 185 | |
| 186 | Imagine a case where you want to replace the slides default notes with custom notes. |
| 187 | For instance, you might want to write your `slides.md` as follows: |
| 188 | |
| 189 | <!-- eslint-skip --> |
| 190 | |
| 191 | ```md |
| 192 | --- |
| 193 | layout: quote |
| 194 | _note: notes/note.md |
| 195 | --- |
| 196 | |
| 197 | # Welcome |
| 198 | |
| 199 | > great! |
| 200 | |
| 201 | <!-- |
| 202 | Default slide notes |
| 203 | --> |
| 204 | ``` |
| 205 | |
| 206 | Here we used an underscore in `_note` to avoid possible conflicts with existing frontmatter properties. |
| 207 | |
| 208 | To handle this `_note: ...` syntax in the frontmatter, create a `./setup/preparser.ts` file with the following content: |
| 209 | |
| 210 | ```ts twoslash [./setup/preparser.ts] |
| 211 | import fs, { promises as fsp } from 'node:fs' |
| 212 | import { definePreparserSetup } from '@slidev/types' |
| 213 | |
| 214 | export default definePreparserSetup(() => { |
| 215 | return [ |
| 216 | { |
| 217 | async transformNote(note, frontmatter) { |
| 218 | if ('_note' in frontmatter && fs.existsSync(frontmatter._note)) { |
| 219 | try { |
| 220 | const newNote = await fsp.readFile(frontmatter._note, 'utf8') |
| 221 | return newNote |
| 222 | } |
| 223 | catch (err) { |
| 224 | } |
| 225 | } |
| 226 | |
| 227 | return note |
| 228 | }, |
| 229 | }, |
| 230 | ] |
| 231 | }) |
| 232 | ``` |
| 233 |