Markdown
Import markdown files directly in HTML templates with your custom processing
pipeline or presets.
Extracts the table of contents, frontmatter, excerpt and title.
Warning
This API is subject to changes
Installation
npm i @gracile/markdown
# Presets
npm i @gracile/markdown-preset-marked
Tip
You can use this extension with any Vite+Lit setup!
It’s totally decoupled from the framework.
📄 /vite.config.ts
import { defineConfigfunction defineConfig(config: UserConfig): UserConfig (+5 overloads)Type helper to make it easier to use vite.config.ts
accepts a direct
{@link
UserConfig
}
object, or a function that returns it.
The function receives a
{@link
ConfigEnv
}
object.
} from 'vite';
import { viteMarkdownPluginimport viteMarkdownPlugin } from '@gracile/markdown/vite';
import { MarkdownRendererimport MarkdownRenderer } from '@gracile/markdown-preset-marked';
export default defineConfigfunction defineConfig(config: UserConfig): UserConfig (+5 overloads)Type helper to make it easier to use vite.config.ts
accepts a direct
{@link
UserConfig
}
object, or a function that returns it.
The function receives a
{@link
ConfigEnv
}
object.
({
// ...
pluginsUserConfig.plugins?: PluginOption[] | undefinedArray of vite plugins to use.
: [
viteMarkdownPluginimport viteMarkdownPlugin ({ MarkdownRenderertype MarkdownRenderer: any }),
// ...
],
});
Usage
📄 /src/modules/my-partial.ts
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
import myDocumentmodule "*.md" from './my-document.md';
export const myPartialconst myPartial: TemplateResult<1> = htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`
<article>
<h1>${myDocumentmodule "*.md" .meta.title}</h1>
<div class="content">${myDocumentmodule "*.md" .body.lit}</div>
</article>
<details>
<div>${myDocumentmodule "*.md" .excerptconst excerpt: TemplateResult<1> .lit}</div>
<pre>${JSONvar JSON: JSONAn intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.
.stringifyJSON.stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string (+1 overload)Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
(myDocumentmodule "*.md" .meta.tableOfContents)}</pre>
<pre>${JSONvar JSON: JSONAn intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.
.stringifyJSON.stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string (+1 overload)Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
(myDocumentmodule "*.md" .meta.frontmatter)}</pre>
<pre>${JSONvar JSON: JSONAn intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.
.stringifyJSON.stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string (+1 overload)Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
(myDocumentmodule "*.md" .source.yaml)}</pre>
<pre>${JSONvar JSON: JSONAn intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.
.stringifyJSON.stringify(value: any, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string (+1 overload)Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
(myDocumentmodule "*.md" .path.relative)}</pre>
</details>
`;
📄 /src/types.ts
You can use these types to flesh out yours.
import type {
MarkdownModuleimport MarkdownModule ,
TocLevelimport TocLevel ,
Headingimport Heading ,
} from '@gracile/markdown/module';
With Vite’s glob import
📄 /src/content/content.ts
import type { MarkdownModuleimport MarkdownModule } from '@gracile/markdown/module';
export const blogPostsconst blogPosts: Record<string, MarkdownModule> = import.meta.globImportMeta.glob: ImportGlobFunction
<MarkdownModule>(glob: string | string[], options: ImportGlobOptions<true, string>) => Record<string, MarkdownModule> (+2 overloads)
Import a list of files with a glob pattern.
Overload 3: Module generic provided, infer the type from eager: true
<MarkdownModuleimport MarkdownModule >(
'/src/content/blog/**/*.md',
{ eagerImportGlobOptions<true, string>.eager?: true | undefinedImport as static or dynamic
: true, importImportGlobOptions<Eager extends boolean, AsType extends string>.import?: string | undefinedImport only the specific named export. Set to default to import the default export.
: 'default' },
);
📄 /src/routes/blog/[slug].ts
import { defineRouteimport defineRoute } from '@gracile/gracile/route';
import { htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
} from 'lit';
import { blogPostsconst blogPosts: Record<string, MarkdownModule> } from '../../content/content.js';
import { documentimport document } from '../../document.js';
export default defineRouteimport defineRoute ({
staticPathsstaticPaths: () => {
params: {
slug: any;
};
props: {
title: any;
content: any;
toc: any;
};
}[]
: () =>
Objectvar Object: ObjectConstructorProvides functionality common to all JavaScript objects.
.valuesObjectConstructor.values<MarkdownModule>(o: {
[s: string]: MarkdownModule;
} | ArrayLike<MarkdownModule>): MarkdownModule[] (+1 overload)
Returns an array of values of the enumerable own properties of an object
(blogPostsconst blogPosts: Record<string, MarkdownModule> ).mapArray<MarkdownModule>.map<{
params: {
slug: any;
};
props: {
title: any;
content: any;
toc: any;
};
}>(callbackfn: (value: MarkdownModule, index: number, array: MarkdownModule[]) => {
params: {
slug: any;
};
props: {
title: any;
content: any;
toc: any;
};
}, thisArg?: any): {
params: {
slug: any;
};
props: {
title: any;
content: any;
toc: any;
};
}[]
Calls a defined callback function on each element of an array, and returns an array that contains the results.
((modulemodule: MarkdownModule ) => ({
paramsparams: {
slug: any;
}
: { slugslug: any : modulemodule: MarkdownModule .meta.slug },
propsprops: {
title: any;
content: any;
toc: any;
}
: {
titletitle: any : modulemodule: MarkdownModule .meta.title,
contentcontent: any : modulemodule: MarkdownModule .body.lit,
toctoc: any : modulemodule: MarkdownModule .meta.tableOfContents,
},
})),
documentdocument: (context: any) => any : (contextcontext: any ) => documentimport document ({ ...contextcontext: any , titletitle: any : contextcontext: any .props.title }),
templatetemplate: (context: any) => TemplateResult<1> : (contextcontext: any ) => htmlconst html: (strings: TemplateStringsArray, ...values: unknown[]) => TemplateResult<1>Interprets a template literal as an HTML template that can efficiently
render to and update a container.
const header = (title: string) => html`<h1>${title}</h1>`;
The html tag returns a description of the DOM to render as a value. It is
lazy, meaning no work is done until the template is rendered. When rendering,
if a template comes from the same expression as a previously rendered result,
it's efficiently updated instead of replaced.
`
<pre>${JSONvar JSON: JSONAn intrinsic object that provides functions to convert JavaScript values to and from the JavaScript Object Notation (JSON) format.
.stringifyJSON.stringify(value: any, replacer?: (number | string)[] | null, space?: string | number): string (+1 overload)Converts a JavaScript value to a JavaScript Object Notation (JSON) string.
(contextcontext: any .props, null, 2)}</pre>
...
`,
});
Build your preset
The MarkdownRenderer class is used to produce a ready-to-consume, standardized
MarkdownModule.
See the “Marked” preset
for a simple implementation of the MarkdownRenderer abstract class. It’s under
100 lines of code!
Also, it uses very few, light dependencies for achieving all tasks, like
transforming the MD, extracting the ToC, and other metadata.
As everyone has different needs, it’s up to you to plug your custom pipeline;
Gracile may offer a basic preset with remark at some point, which could be
expanded or overridden.
Server-side rendering
As with any lit-html templates, you can use Web Components inside Markdown,
they will be server-side-rendered and hydrated (if needed) once inside
the client!