@rooted/markdown 1.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/markdown.d.mts +71 -0
- package/dist/markdown.mjs +47 -0
- package/dist/vite.d.mts +43 -0
- package/dist/vite.mjs +65 -0
- package/package.json +76 -0
- package/plugins/markdown.d.ts +9 -0
- package/readme.md +38 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Marvin Brouwer
|
|
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.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { CssClasses } from "@rooted/components";
|
|
2
|
+
//#region src/markdown.d.mts
|
|
3
|
+
/**
|
|
4
|
+
* What the Vite plugin turns a `.md` file into.
|
|
5
|
+
*
|
|
6
|
+
* A module namespace from `import('./about.md')` matches this shape, so it can
|
|
7
|
+
* be passed as {@link MarkdownOptions.source} directly.
|
|
8
|
+
*/
|
|
9
|
+
type MarkdownModule = {
|
|
10
|
+
/** The parsed YAML frontmatter block. Cast it to your own shape. */
|
|
11
|
+
frontmatter: Record<string, unknown>;
|
|
12
|
+
/** The body, rendered to HTML at build time. */
|
|
13
|
+
html: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Content for {@link Markdown}: anything with an `html` property (a transformed
|
|
17
|
+
* `.md` module), or an HTML string.
|
|
18
|
+
*
|
|
19
|
+
* A bare string is **HTML, not markdown**. There's no markdown parser in the
|
|
20
|
+
* browser bundle, on purpose. Rendering happens at build time in the Vite
|
|
21
|
+
* plugin.
|
|
22
|
+
*/
|
|
23
|
+
type MarkdownSource = {
|
|
24
|
+
html: string;
|
|
25
|
+
} | string;
|
|
26
|
+
/**
|
|
27
|
+
* Options for {@link Markdown}.
|
|
28
|
+
*/
|
|
29
|
+
type MarkdownOptions = {
|
|
30
|
+
/** The HTML to render, or a `.md` module to take it from. */
|
|
31
|
+
source: MarkdownSource;
|
|
32
|
+
/** Tag for the wrapping element. Defaults to `div`. */
|
|
33
|
+
tag?: keyof HTMLElementTagNameMap;
|
|
34
|
+
/** CSS classes applied to the wrapping element. */
|
|
35
|
+
classes?: CssClasses;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Renders pre-rendered HTML into the DOM.
|
|
39
|
+
*
|
|
40
|
+
* Pair it with the Vite plugin (`@rooted/markdown/vite`), which turns `.md`
|
|
41
|
+
* files into modules carrying an `html` export:
|
|
42
|
+
*
|
|
43
|
+
* ```ts
|
|
44
|
+
* import * as about from './about.md'
|
|
45
|
+
*
|
|
46
|
+
* append(create(Markdown, {
|
|
47
|
+
* source: about
|
|
48
|
+
* }))
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* For content that differs per locale, hand it whichever module
|
|
52
|
+
* `localization.branch` picked:
|
|
53
|
+
*
|
|
54
|
+
* ```ts
|
|
55
|
+
* const source = await localization.branch({
|
|
56
|
+
* 'en-GB': () => import('./about.en-GB.md'),
|
|
57
|
+
* 'nl-NL': () => import('./about.nl-NL.md'),
|
|
58
|
+
* })
|
|
59
|
+
* append(create(Markdown, {
|
|
60
|
+
* source
|
|
61
|
+
* }))
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* The HTML is assigned as-is and is **not sanitised**. Treat `source` the way
|
|
65
|
+
* you'd treat a `<script>` tag: fine for content you control (files in your
|
|
66
|
+
* repo, your own CMS), not for anything a visitor can influence.
|
|
67
|
+
*/
|
|
68
|
+
declare const Markdown: import("@rooted/components").Component<MarkdownOptions>;
|
|
69
|
+
//#endregion
|
|
70
|
+
export { Markdown, type MarkdownModule, type MarkdownOptions, type MarkdownSource };
|
|
71
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFya2Rvd24uZC5tdHMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vc3JjL21hcmtkb3duLm10cyJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7S0FVWTs7RUFFWCxhQUFhOztFQUViOzs7Ozs7Ozs7O0tBV1c7RUFBbUI7Ozs7O0tBS25COztFQUVYLFFBQVE7O0VBRVIsWUFBWTs7RUFFWixVQUFVOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Y0FrQ0UsdUNBQVEsVUFBQSJ9
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { component } from "@rooted/components";
|
|
2
|
+
//#region src/markdown.mts
|
|
3
|
+
/**
|
|
4
|
+
* Renders pre-rendered HTML into the DOM.
|
|
5
|
+
*
|
|
6
|
+
* Pair it with the Vite plugin (`@rooted/markdown/vite`), which turns `.md`
|
|
7
|
+
* files into modules carrying an `html` export:
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import * as about from './about.md'
|
|
11
|
+
*
|
|
12
|
+
* append(create(Markdown, {
|
|
13
|
+
* source: about
|
|
14
|
+
* }))
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* For content that differs per locale, hand it whichever module
|
|
18
|
+
* `localization.branch` picked:
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* const source = await localization.branch({
|
|
22
|
+
* 'en-GB': () => import('./about.en-GB.md'),
|
|
23
|
+
* 'nl-NL': () => import('./about.nl-NL.md'),
|
|
24
|
+
* })
|
|
25
|
+
* append(create(Markdown, {
|
|
26
|
+
* source
|
|
27
|
+
* }))
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* The HTML is assigned as-is and is **not sanitised**. Treat `source` the way
|
|
31
|
+
* you'd treat a `<script>` tag: fine for content you control (files in your
|
|
32
|
+
* repo, your own CMS), not for anything a visitor can influence.
|
|
33
|
+
*/
|
|
34
|
+
const Markdown = component({
|
|
35
|
+
name: "@rooted/markdown",
|
|
36
|
+
onMount({ options, append, element }) {
|
|
37
|
+
const { source, tag, classes } = options;
|
|
38
|
+
append(element(tag ?? "div", {
|
|
39
|
+
classes,
|
|
40
|
+
innerHTML: typeof source === "string" ? source : source.html
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
//#endregion
|
|
45
|
+
export { Markdown };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFya2Rvd24ubWpzIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbIi4uL3NyYy9tYXJrZG93bi5tdHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgY29tcG9uZW50IH0gZnJvbSAnQHJvb3RlZC9jb21wb25lbnRzJ1xuXG5pbXBvcnQgdHlwZSB7IENzc0NsYXNzZXMgfSBmcm9tICdAcm9vdGVkL2NvbXBvbmVudHMnXG5cbi8qKlxuICogV2hhdCB0aGUgVml0ZSBwbHVnaW4gdHVybnMgYSBgLm1kYCBmaWxlIGludG8uXG4gKlxuICogQSBtb2R1bGUgbmFtZXNwYWNlIGZyb20gYGltcG9ydCgnLi9hYm91dC5tZCcpYCBtYXRjaGVzIHRoaXMgc2hhcGUsIHNvIGl0IGNhblxuICogYmUgcGFzc2VkIGFzIHtAbGluayBNYXJrZG93bk9wdGlvbnMuc291cmNlfSBkaXJlY3RseS5cbiAqL1xuZXhwb3J0IHR5cGUgTWFya2Rvd25Nb2R1bGUgPSB7XG5cdC8qKiBUaGUgcGFyc2VkIFlBTUwgZnJvbnRtYXR0ZXIgYmxvY2suIENhc3QgaXQgdG8geW91ciBvd24gc2hhcGUuICovXG5cdGZyb250bWF0dGVyOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPlxuXHQvKiogVGhlIGJvZHksIHJlbmRlcmVkIHRvIEhUTUwgYXQgYnVpbGQgdGltZS4gKi9cblx0aHRtbDogc3RyaW5nXG59XG5cbi8qKlxuICogQ29udGVudCBmb3Ige0BsaW5rIE1hcmtkb3dufTogYW55dGhpbmcgd2l0aCBhbiBgaHRtbGAgcHJvcGVydHkgKGEgdHJhbnNmb3JtZWRcbiAqIGAubWRgIG1vZHVsZSksIG9yIGFuIEhUTUwgc3RyaW5nLlxuICpcbiAqIEEgYmFyZSBzdHJpbmcgaXMgKipIVE1MLCBub3QgbWFya2Rvd24qKi4gVGhlcmUncyBubyBtYXJrZG93biBwYXJzZXIgaW4gdGhlXG4gKiBicm93c2VyIGJ1bmRsZSwgb24gcHVycG9zZS4gUmVuZGVyaW5nIGhhcHBlbnMgYXQgYnVpbGQgdGltZSBpbiB0aGUgVml0ZVxuICogcGx1Z2luLlxuICovXG5leHBvcnQgdHlwZSBNYXJrZG93blNvdXJjZSA9IHsgaHRtbDogc3RyaW5nIH0gfCBzdHJpbmdcblxuLyoqXG4gKiBPcHRpb25zIGZvciB7QGxpbmsgTWFya2Rvd259LlxuICovXG5leHBvcnQgdHlwZSBNYXJrZG93bk9wdGlvbnMgPSB7XG5cdC8qKiBUaGUgSFRNTCB0byByZW5kZXIsIG9yIGEgYC5tZGAgbW9kdWxlIHRvIHRha2UgaXQgZnJvbS4gKi9cblx0c291cmNlOiBNYXJrZG93blNvdXJjZVxuXHQvKiogVGFnIGZvciB0aGUgd3JhcHBpbmcgZWxlbWVudC4gRGVmYXVsdHMgdG8gYGRpdmAuICovXG5cdHRhZz86IGtleW9mIEhUTUxFbGVtZW50VGFnTmFtZU1hcFxuXHQvKiogQ1NTIGNsYXNzZXMgYXBwbGllZCB0byB0aGUgd3JhcHBpbmcgZWxlbWVudC4gKi9cblx0Y2xhc3Nlcz86IENzc0NsYXNzZXNcbn1cblxuLyoqXG4gKiBSZW5kZXJzIHByZS1yZW5kZXJlZCBIVE1MIGludG8gdGhlIERPTS5cbiAqXG4gKiBQYWlyIGl0IHdpdGggdGhlIFZpdGUgcGx1Z2luIChgQHJvb3RlZC9tYXJrZG93bi92aXRlYCksIHdoaWNoIHR1cm5zIGAubWRgXG4gKiBmaWxlcyBpbnRvIG1vZHVsZXMgY2FycnlpbmcgYW4gYGh0bWxgIGV4cG9ydDpcbiAqXG4gKiBgYGB0c1xuICogaW1wb3J0ICogYXMgYWJvdXQgZnJvbSAnLi9hYm91dC5tZCdcbiAqXG4gKiBhcHBlbmQoY3JlYXRlKE1hcmtkb3duLCB7XG4gKiAgIHNvdXJjZTogYWJvdXRcbiAqIH0pKVxuICogYGBgXG4gKlxuICogRm9yIGNvbnRlbnQgdGhhdCBkaWZmZXJzIHBlciBsb2NhbGUsIGhhbmQgaXQgd2hpY2hldmVyIG1vZHVsZVxuICogYGxvY2FsaXphdGlvbi5icmFuY2hgIHBpY2tlZDpcbiAqXG4gKiBgYGB0c1xuICogY29uc3Qgc291cmNlID0gYXdhaXQgbG9jYWxpemF0aW9uLmJyYW5jaCh7XG4gKiAgICdlbi1HQic6ICgpID0+IGltcG9ydCgnLi9hYm91dC5lbi1HQi5tZCcpLFxuICogICAnbmwtTkwnOiAoKSA9PiBpbXBvcnQoJy4vYWJvdXQubmwtTkwubWQnKSxcbiAqIH0pXG4gKiBhcHBlbmQoY3JlYXRlKE1hcmtkb3duLCB7XG4gKiAgIHNvdXJjZVxuICogfSkpXG4gKiBgYGBcbiAqXG4gKiBUaGUgSFRNTCBpcyBhc3NpZ25lZCBhcy1pcyBhbmQgaXMgKipub3Qgc2FuaXRpc2VkKiouIFRyZWF0IGBzb3VyY2VgIHRoZSB3YXlcbiAqIHlvdSdkIHRyZWF0IGEgYDxzY3JpcHQ+YCB0YWc6IGZpbmUgZm9yIGNvbnRlbnQgeW91IGNvbnRyb2wgKGZpbGVzIGluIHlvdXJcbiAqIHJlcG8sIHlvdXIgb3duIENNUyksIG5vdCBmb3IgYW55dGhpbmcgYSB2aXNpdG9yIGNhbiBpbmZsdWVuY2UuXG4gKi9cbmV4cG9ydCBjb25zdCBNYXJrZG93biA9IGNvbXBvbmVudDxNYXJrZG93bk9wdGlvbnM+KHtcblx0bmFtZTogJ0Byb290ZWQvbWFya2Rvd24nLFxuXHRvbk1vdW50KHsgb3B0aW9ucywgYXBwZW5kLCBlbGVtZW50IH0pIHtcblx0XHRjb25zdCB7IHNvdXJjZSwgdGFnLCBjbGFzc2VzIH0gPSBvcHRpb25zXG5cblx0XHRhcHBlbmQoXG5cdFx0XHRlbGVtZW50KHRhZyA/PyAnZGl2Jywge1xuXHRcdFx0XHRjbGFzc2VzLFxuXHRcdFx0XHQvLyBUcnVzdGVkIGJ5IGNvbnRyYWN0OiB0aGUgY2FsbGVyIG93bnMgdGhpcyBIVE1MLCBzZWUgdGhlIFRTRG9jXG5cdFx0XHRcdGlubmVySFRNTDogdHlwZW9mIHNvdXJjZSA9PT0gJ3N0cmluZycgPyBzb3VyY2UgOiBzb3VyY2UuaHRtbCxcblx0XHRcdH0pLFxuXHRcdClcblx0fSxcbn0pXG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQXNFQSxNQUFhLFdBQVcsVUFBMkI7Q0FDbEQsTUFBTTtDQUNOLFFBQVEsRUFBRSxTQUFTLFFBQVEsV0FBVztFQUNyQyxNQUFNLEVBQUUsUUFBUSxLQUFLLFlBQVk7RUFFakMsT0FDQyxRQUFRLE9BQU8sT0FBTztHQUNyQjtHQUVBLFdBQVcsT0FBTyxXQUFXLFdBQVcsU0FBUyxPQUFPO0VBQ3pELENBQUMsQ0FDRjtDQUNEO0FBQ0QsQ0FBQyJ9
|
package/dist/vite.d.mts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Plugin } from "vite";
|
|
2
|
+
//#region plugins/markdown.d.mts
|
|
3
|
+
/**
|
|
4
|
+
* Options for {@link rootedMarkdown}.
|
|
5
|
+
*/
|
|
6
|
+
type MarkdownPluginOptions = {
|
|
7
|
+
/**
|
|
8
|
+
* Minify the rendered HTML. Defaults to `true` for builds and `false` for
|
|
9
|
+
* the dev server, so what you read in devtools matches the source.
|
|
10
|
+
*/
|
|
11
|
+
minify?: boolean;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Turns `.md` files into modules at build time, so no markdown parser reaches
|
|
15
|
+
* the browser bundle.
|
|
16
|
+
*
|
|
17
|
+
* Each file becomes `frontmatter` (whatever the YAML block contained) and
|
|
18
|
+
* `html` (everything after it, rendered by `marked`), exported both by name
|
|
19
|
+
* and as a default:
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* import { frontmatter, html } from './about.md'
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* Because `html` is a named export, the module itself satisfies the `Markdown`
|
|
26
|
+
* component's `source`, so you can hand a dynamic import straight over:
|
|
27
|
+
*
|
|
28
|
+
* ```ts
|
|
29
|
+
* append(create(Markdown, {
|
|
30
|
+
* source: await import('./about.md')
|
|
31
|
+
* }))
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* Imports carrying a query (`./about.md?raw`, `?url`) are left alone for Vite
|
|
35
|
+
* to handle.
|
|
36
|
+
*
|
|
37
|
+
* Reference `@rooted/markdown/vite/types` from your env declarations so
|
|
38
|
+
* TypeScript knows what a `.md` import is.
|
|
39
|
+
*/
|
|
40
|
+
declare function rootedMarkdown(options?: MarkdownPluginOptions): Plugin;
|
|
41
|
+
//#endregion
|
|
42
|
+
export { type MarkdownPluginOptions, rootedMarkdown };
|
|
43
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidml0ZS5kLm10cyIsIm5hbWVzIjpbXSwic291cmNlcyI6WyIuLi9wbHVnaW5zL21hcmtkb3duLm10cyJdLCJtYXBwaW5ncyI6Ijs7Ozs7S0FTWTs7Ozs7RUFLWDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7aUJBcUNlLGVBQWUsVUFBUyx3QkFBNkIifQ==
|
package/dist/vite.mjs
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import matter from "gray-matter";
|
|
2
|
+
import { Marked } from "marked";
|
|
3
|
+
//#region plugins/markdown.mts
|
|
4
|
+
const minifyOptions = {
|
|
5
|
+
collapseWhitespace: true,
|
|
6
|
+
removeComments: true,
|
|
7
|
+
removeOptionalTags: true,
|
|
8
|
+
decodeEntities: true
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Turns `.md` files into modules at build time, so no markdown parser reaches
|
|
12
|
+
* the browser bundle.
|
|
13
|
+
*
|
|
14
|
+
* Each file becomes `frontmatter` (whatever the YAML block contained) and
|
|
15
|
+
* `html` (everything after it, rendered by `marked`), exported both by name
|
|
16
|
+
* and as a default:
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* import { frontmatter, html } from './about.md'
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* Because `html` is a named export, the module itself satisfies the `Markdown`
|
|
23
|
+
* component's `source`, so you can hand a dynamic import straight over:
|
|
24
|
+
*
|
|
25
|
+
* ```ts
|
|
26
|
+
* append(create(Markdown, {
|
|
27
|
+
* source: await import('./about.md')
|
|
28
|
+
* }))
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* Imports carrying a query (`./about.md?raw`, `?url`) are left alone for Vite
|
|
32
|
+
* to handle.
|
|
33
|
+
*
|
|
34
|
+
* Reference `@rooted/markdown/vite/types` from your env declarations so
|
|
35
|
+
* TypeScript knows what a `.md` import is.
|
|
36
|
+
*/
|
|
37
|
+
function rootedMarkdown(options = {}) {
|
|
38
|
+
const marked = new Marked();
|
|
39
|
+
let config;
|
|
40
|
+
return {
|
|
41
|
+
name: "vite-plugin:rooted-markdown",
|
|
42
|
+
configResolved(resolvedConfig) {
|
|
43
|
+
config = resolvedConfig;
|
|
44
|
+
},
|
|
45
|
+
async transform(code, id) {
|
|
46
|
+
const [file, query] = id.split("?");
|
|
47
|
+
if (query !== void 0 || !file.endsWith(".md")) return;
|
|
48
|
+
const { data, content } = matter(code);
|
|
49
|
+
const rendered = await marked.parse(content);
|
|
50
|
+
const html = options.minify ?? config.command === "build" ? await (await import("html-minifier-terser")).minify(rendered, minifyOptions) : rendered;
|
|
51
|
+
return {
|
|
52
|
+
code: [
|
|
53
|
+
`export const frontmatter = ${JSON.stringify(data)}`,
|
|
54
|
+
`export const html = ${JSON.stringify(html)}`,
|
|
55
|
+
"export default { frontmatter, html }"
|
|
56
|
+
].join("\n"),
|
|
57
|
+
map: void 0
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
export { rootedMarkdown };
|
|
64
|
+
|
|
65
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidml0ZS5tanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsiLi4vcGx1Z2lucy9tYXJrZG93bi5tdHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IG1hdHRlciBmcm9tICdncmF5LW1hdHRlcidcbmltcG9ydCB7IE1hcmtlZCB9IGZyb20gJ21hcmtlZCdcblxuaW1wb3J0IHR5cGUgeyBPcHRpb25zIGFzIE1pbmlmeU9wdGlvbnMgfSBmcm9tICdodG1sLW1pbmlmaWVyLXRlcnNlcidcbmltcG9ydCB0eXBlIHsgUGx1Z2luLCBSZXNvbHZlZENvbmZpZyB9IGZyb20gJ3ZpdGUnXG5cbi8qKlxuICogT3B0aW9ucyBmb3Ige0BsaW5rIHJvb3RlZE1hcmtkb3dufS5cbiAqL1xuZXhwb3J0IHR5cGUgTWFya2Rvd25QbHVnaW5PcHRpb25zID0ge1xuXHQvKipcblx0ICogTWluaWZ5IHRoZSByZW5kZXJlZCBIVE1MLiBEZWZhdWx0cyB0byBgdHJ1ZWAgZm9yIGJ1aWxkcyBhbmQgYGZhbHNlYCBmb3Jcblx0ICogdGhlIGRldiBzZXJ2ZXIsIHNvIHdoYXQgeW91IHJlYWQgaW4gZGV2dG9vbHMgbWF0Y2hlcyB0aGUgc291cmNlLlxuXHQgKi9cblx0bWluaWZ5PzogYm9vbGVhblxufVxuXG5jb25zdCBtaW5pZnlPcHRpb25zOiBNaW5pZnlPcHRpb25zID0ge1xuXHRjb2xsYXBzZVdoaXRlc3BhY2U6IHRydWUsXG5cdHJlbW92ZUNvbW1lbnRzOiB0cnVlLFxuXHRyZW1vdmVPcHRpb25hbFRhZ3M6IHRydWUsXG5cdGRlY29kZUVudGl0aWVzOiB0cnVlLFxufVxuXG4vKipcbiAqIFR1cm5zIGAubWRgIGZpbGVzIGludG8gbW9kdWxlcyBhdCBidWlsZCB0aW1lLCBzbyBubyBtYXJrZG93biBwYXJzZXIgcmVhY2hlc1xuICogdGhlIGJyb3dzZXIgYnVuZGxlLlxuICpcbiAqIEVhY2ggZmlsZSBiZWNvbWVzIGBmcm9udG1hdHRlcmAgKHdoYXRldmVyIHRoZSBZQU1MIGJsb2NrIGNvbnRhaW5lZCkgYW5kXG4gKiBgaHRtbGAgKGV2ZXJ5dGhpbmcgYWZ0ZXIgaXQsIHJlbmRlcmVkIGJ5IGBtYXJrZWRgKSwgZXhwb3J0ZWQgYm90aCBieSBuYW1lXG4gKiBhbmQgYXMgYSBkZWZhdWx0OlxuICpcbiAqIGBgYHRzXG4gKiBpbXBvcnQgeyBmcm9udG1hdHRlciwgaHRtbCB9IGZyb20gJy4vYWJvdXQubWQnXG4gKiBgYGBcbiAqXG4gKiBCZWNhdXNlIGBodG1sYCBpcyBhIG5hbWVkIGV4cG9ydCwgdGhlIG1vZHVsZSBpdHNlbGYgc2F0aXNmaWVzIHRoZSBgTWFya2Rvd25gXG4gKiBjb21wb25lbnQncyBgc291cmNlYCwgc28geW91IGNhbiBoYW5kIGEgZHluYW1pYyBpbXBvcnQgc3RyYWlnaHQgb3ZlcjpcbiAqXG4gKiBgYGB0c1xuICogYXBwZW5kKGNyZWF0ZShNYXJrZG93biwge1xuICogICBzb3VyY2U6IGF3YWl0IGltcG9ydCgnLi9hYm91dC5tZCcpXG4gKiB9KSlcbiAqIGBgYFxuICpcbiAqIEltcG9ydHMgY2FycnlpbmcgYSBxdWVyeSAoYC4vYWJvdXQubWQ/cmF3YCwgYD91cmxgKSBhcmUgbGVmdCBhbG9uZSBmb3IgVml0ZVxuICogdG8gaGFuZGxlLlxuICpcbiAqIFJlZmVyZW5jZSBgQHJvb3RlZC9tYXJrZG93bi92aXRlL3R5cGVzYCBmcm9tIHlvdXIgZW52IGRlY2xhcmF0aW9ucyBzb1xuICogVHlwZVNjcmlwdCBrbm93cyB3aGF0IGEgYC5tZGAgaW1wb3J0IGlzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcm9vdGVkTWFya2Rvd24ob3B0aW9uczogTWFya2Rvd25QbHVnaW5PcHRpb25zID0ge30pOiBQbHVnaW4ge1xuXHQvLyBBIHByaXZhdGUgaW5zdGFuY2U6IGBtYXJrZWQudXNlKC4uLilgIG11dGF0ZXMgYSBzaGFyZWQgc2luZ2xldG9uLCB3aGljaFxuXHQvLyB3b3VsZCBsZWFrIGludG8gYW55dGhpbmcgZWxzZSBpbiB0aGUgYXBwIHRoYXQgdXNlcyBtYXJrZWQuXG5cdGNvbnN0IG1hcmtlZCA9IG5ldyBNYXJrZWQoKVxuXHRsZXQgY29uZmlnOiBSZXNvbHZlZENvbmZpZ1xuXG5cdHJldHVybiB7XG5cdFx0bmFtZTogJ3ZpdGUtcGx1Z2luOnJvb3RlZC1tYXJrZG93bicsXG5cdFx0Y29uZmlnUmVzb2x2ZWQocmVzb2x2ZWRDb25maWcpIHtcblx0XHRcdGNvbmZpZyA9IHJlc29sdmVkQ29uZmlnXG5cdFx0fSxcblx0XHRhc3luYyB0cmFuc2Zvcm0oY29kZSwgaWQpIHtcblx0XHRcdGNvbnN0IFtmaWxlLCBxdWVyeV0gPSBpZC5zcGxpdCgnPycpXG5cdFx0XHQvLyBBIHF1ZXJ5IG1lYW5zIHRoZSBjYWxsZXIgYXNrZWQgVml0ZSBmb3Igc29tZXRoaW5nIHNwZWNpZmljICg/cmF3LCA/dXJsKVxuXHRcdFx0aWYgKHF1ZXJ5ICE9PSB1bmRlZmluZWQgfHwgIWZpbGUuZW5kc1dpdGgoJy5tZCcpKSByZXR1cm5cblxuXHRcdFx0Y29uc3QgeyBkYXRhLCBjb250ZW50IH0gPSBtYXR0ZXIoY29kZSlcblx0XHRcdGNvbnN0IHJlbmRlcmVkID0gYXdhaXQgbWFya2VkLnBhcnNlKGNvbnRlbnQpXG5cdFx0XHRjb25zdCBzaG91bGRNaW5pZnkgPSBvcHRpb25zLm1pbmlmeSA/PyBjb25maWcuY29tbWFuZCA9PT0gJ2J1aWxkJ1xuXHRcdFx0Y29uc3QgaHRtbCA9IHNob3VsZE1pbmlmeVxuXHRcdFx0XHQ/IGF3YWl0IChhd2FpdCBpbXBvcnQoJ2h0bWwtbWluaWZpZXItdGVyc2VyJykpLm1pbmlmeShyZW5kZXJlZCwgbWluaWZ5T3B0aW9ucylcblx0XHRcdFx0OiByZW5kZXJlZFxuXG5cdFx0XHRjb25zdCBmcm9udG1hdHRlciA9IEpTT04uc3RyaW5naWZ5KGRhdGEpXG5cdFx0XHRyZXR1cm4ge1xuXHRcdFx0XHRjb2RlOiBbXG5cdFx0XHRcdFx0YGV4cG9ydCBjb25zdCBmcm9udG1hdHRlciA9ICR7ZnJvbnRtYXR0ZXJ9YCxcblx0XHRcdFx0XHRgZXhwb3J0IGNvbnN0IGh0bWwgPSAke0pTT04uc3RyaW5naWZ5KGh0bWwpfWAsXG5cdFx0XHRcdFx0J2V4cG9ydCBkZWZhdWx0IHsgZnJvbnRtYXR0ZXIsIGh0bWwgfScsXG5cdFx0XHRcdF0uam9pbignXFxuJyksXG5cdFx0XHRcdG1hcDogdW5kZWZpbmVkLFxuXHRcdFx0fVxuXHRcdH0sXG5cdH1cbn1cbiJdLCJtYXBwaW5ncyI6Ijs7O0FBaUJBLE1BQU0sZ0JBQStCO0NBQ3BDLG9CQUFvQjtDQUNwQixnQkFBZ0I7Q0FDaEIsb0JBQW9CO0NBQ3BCLGdCQUFnQjtBQUNqQjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQTZCQSxTQUFnQixlQUFlLFVBQWlDLENBQUMsR0FBVztDQUczRSxNQUFNLFNBQVMsSUFBSSxPQUFPO0NBQzFCLElBQUk7Q0FFSixPQUFPO0VBQ04sTUFBTTtFQUNOLGVBQWUsZ0JBQWdCO0dBQzlCLFNBQVM7RUFDVjtFQUNBLE1BQU0sVUFBVSxNQUFNLElBQUk7R0FDekIsTUFBTSxDQUFDLE1BQU0sU0FBUyxHQUFHLE1BQU0sR0FBRztHQUVsQyxJQUFJLFVBQVUsS0FBQSxLQUFhLENBQUMsS0FBSyxTQUFTLEtBQUssR0FBRztHQUVsRCxNQUFNLEVBQUUsTUFBTSxZQUFZLE9BQU8sSUFBSTtHQUNyQyxNQUFNLFdBQVcsTUFBTSxPQUFPLE1BQU0sT0FBTztHQUUzQyxNQUFNLE9BRGUsUUFBUSxVQUFVLE9BQU8sWUFBWSxVQUV2RCxPQUFPLE1BQU0sT0FBTyx3QkFBQSxDQUF5QixPQUFPLFVBQVUsYUFBYSxJQUMzRTtHQUdILE9BQU87SUFDTixNQUFNO0tBQ0wsOEJBSGtCLEtBQUssVUFBVSxJQUdPO0tBQ3hDLHVCQUF1QixLQUFLLFVBQVUsSUFBSTtLQUMxQztJQUNELENBQUMsQ0FBQyxLQUFLLElBQUk7SUFDWCxLQUFLLEtBQUE7R0FDTjtFQUNEO0NBQ0Q7QUFDRCJ9
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rooted/markdown",
|
|
3
|
+
"version": "1.0.0-alpha.0",
|
|
4
|
+
"description": "Markdown content for the rooted framework",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"markdown",
|
|
7
|
+
"marked",
|
|
8
|
+
"frontmatter",
|
|
9
|
+
"vite-plugin",
|
|
10
|
+
"web-components",
|
|
11
|
+
"typescript",
|
|
12
|
+
"rooted"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/Marvin-Brouwer/rooted.git",
|
|
18
|
+
"directory": "packages/markdown"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://github.com/Marvin-Brouwer/rooted#readme",
|
|
21
|
+
"bugs": "https://github.com/Marvin-Brouwer/rooted/issues",
|
|
22
|
+
"sideEffects": false,
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"registry": "https://registry.npmjs.org/",
|
|
25
|
+
"access": "public",
|
|
26
|
+
"provenance": true
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"plugins/markdown.d.ts",
|
|
31
|
+
"readme.md"
|
|
32
|
+
],
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=22.0.0",
|
|
35
|
+
"pnpm": ">=10.0.0"
|
|
36
|
+
},
|
|
37
|
+
"exports": {
|
|
38
|
+
".": {
|
|
39
|
+
"source": "./src/_module/markdown.mts",
|
|
40
|
+
"import": "./dist/markdown.mjs",
|
|
41
|
+
"types": "./dist/markdown.d.mts"
|
|
42
|
+
},
|
|
43
|
+
"./vite": {
|
|
44
|
+
"source": "./plugins/_module/vite.mts",
|
|
45
|
+
"import": "./dist/vite.mjs",
|
|
46
|
+
"types": "./dist/vite.d.mts"
|
|
47
|
+
},
|
|
48
|
+
"./vite/types": {
|
|
49
|
+
"types": "./plugins/markdown.d.ts"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"gray-matter": "^4.0.3",
|
|
54
|
+
"html-minifier-terser": "^7.2.0",
|
|
55
|
+
"marked": "^18.0.5"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"vite": ">=8.0.1",
|
|
59
|
+
"@rooted/components": "^1.0.0-alpha.0"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@types/html-minifier-terser": "^7.0.2",
|
|
63
|
+
"@types/node": "^26.2.0",
|
|
64
|
+
"vite": "^8.2.1",
|
|
65
|
+
"@rooted/development": "0.1.0",
|
|
66
|
+
"@rooted/components": "^1.0.0-alpha.0"
|
|
67
|
+
},
|
|
68
|
+
"scripts": {
|
|
69
|
+
"build": "pnpm build:ci",
|
|
70
|
+
"build:dev": "pnpm build:ci && rooted-development extract-api",
|
|
71
|
+
"build:ci": "tsdown",
|
|
72
|
+
"watch": "tsdown --watch --no-clean",
|
|
73
|
+
"clean": "rm -rf dist",
|
|
74
|
+
"extract-api": "rooted-development extract-api"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
declare module '*.md' {
|
|
2
|
+
/** The parsed YAML frontmatter block. Cast it to your own shape. */
|
|
3
|
+
export const frontmatter: Record<string, unknown>
|
|
4
|
+
/** The body, rendered to HTML at build time. */
|
|
5
|
+
export const html: string
|
|
6
|
+
|
|
7
|
+
const markdown: import('@rooted/markdown').MarkdownModule
|
|
8
|
+
export default markdown
|
|
9
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# [`@rooted/markdown`](https://www.npmjs.com/package/@rooted/markdown)
|
|
2
|
+
|
|
3
|
+
Markdown content for the [`@rooted/*`](https://github.com/Marvin-Brouwer/rooted#rooted) framework. A Vite plugin renders `.md` files to HTML at build time, and a component puts that HTML in the DOM. No markdown parser reaches the browser bundle.
|
|
4
|
+
|
|
5
|
+
> [!IMPORTANT]
|
|
6
|
+
> This package is still in alpha.
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
pnpm add @rooted/markdown
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Register the plugin:
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { rootedMarkdown } from '@rooted/markdown/vite'
|
|
16
|
+
|
|
17
|
+
export default rootedManifest({
|
|
18
|
+
plugins: [rootedMarkdown()],
|
|
19
|
+
})
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Add the ambient types to your env declarations so `.md` imports typecheck:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
/// <reference types="@rooted/markdown/vite/types" />
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Then import a file and render it:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import * as about from './about.md'
|
|
32
|
+
|
|
33
|
+
append(create(Markdown, { source: about }))
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`about.frontmatter` holds the YAML block. The component assigns `html` as-is and does not sanitise it, so only pass content you control.
|
|
37
|
+
|
|
38
|
+
More in the [markdown guide](https://github.com/Marvin-Brouwer/rooted/blob/main/docs/guide/markdown.md).
|