@void/md 0.0.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/README.md ADDED
@@ -0,0 +1,152 @@
1
+ # @void/md
2
+
3
+ Markdown pages plugin for Void — `.md` files as first-class routes with zero client JS, islands support, and Shiki syntax highlighting.
4
+
5
+ ## Setup
6
+
7
+ ```ts
8
+ // vite.config.ts
9
+ import { defineConfig } from 'vite';
10
+ import { voidPlugin } from 'void';
11
+ import { voidVue } from '@void/vue/plugin';
12
+ import { voidMarkdown } from '@void/md/plugin';
13
+
14
+ export default defineConfig({
15
+ plugins: [voidPlugin(), voidVue(), voidMarkdown()],
16
+ });
17
+ ```
18
+
19
+ Works with any framework adapter — auto-detects Vue, React, Svelte, or Solid from the plugin list.
20
+
21
+ ## Plugin
22
+
23
+ `voidMarkdown(options?)` returns a Vite plugin array (`Plugin[]`). The plugin runs with `enforce: "pre"`.
24
+
25
+ Options:
26
+
27
+ - `shiki?.themes` — `{ light: string; dark: string }` (default: `github-light` / `github-dark`)
28
+ - `shiki?.langs` — additional Shiki languages to load (default: all bundled languages, lazy-loaded)
29
+
30
+ The plugin exposes an API:
31
+
32
+ - `api.getClientScripts()` — returns `Map<string, string>` of component ID → client code (used by the islands plugin)
33
+
34
+ ## Runtime
35
+
36
+ Import from `@void/md`:
37
+
38
+ - **`useFrontmatter()`** — access the current page's frontmatter object. Uses framework-native context (`provide`/`inject` for Vue, `createContext`/`useContext` for React/Solid, `setContext`/`getContext` for Svelte).
39
+
40
+ ## Virtual Modules
41
+
42
+ - **`@void/md`** — resolves to `useFrontmatter()` runtime
43
+ - **`@void/md/pages`** — flat array of all `MdPage` objects (HMR-aware), useful for building navs, sidebars, and search indexes
44
+
45
+ ```ts
46
+ import pages from '@void/md/pages';
47
+ // MdPage { path, title, frontmatter, headings }
48
+ ```
49
+
50
+ ## Pages
51
+
52
+ `.md` files in the `pages/` directory become routes. They compile to static HTML at build time with zero client JS by default — auto-prerendered like static island pages.
53
+
54
+ ```md
55
+ ---
56
+ title: Getting Started
57
+ author: Jane
58
+ ---
59
+
60
+ # Getting Started
61
+
62
+ Regular markdown content with **full** syntax support.
63
+ ```
64
+
65
+ ### Frontmatter
66
+
67
+ YAML frontmatter between `---` fences. Title is extracted from `frontmatter.title` or the first `<h1>`.
68
+
69
+ ### Islands
70
+
71
+ Embed interactive components via a `<script>` block with `island` import attributes:
72
+
73
+ ```md
74
+ ---
75
+ title: Interactive Page
76
+ ---
77
+
78
+ <script>
79
+ import Counter from "../components/Counter.vue" with { island: "visible" }
80
+ </script>
81
+
82
+ # Page with Islands
83
+
84
+ Static content above, interactive island below:
85
+
86
+ <Counter />
87
+ ```
88
+
89
+ Island strategies: `idle` (default), `visible`, `media(...)`.
90
+
91
+ ### Client Scripts
92
+
93
+ Regular imports and statements in `<script>` blocks (without `island` attribute) are bundled as a client module, code-split per page:
94
+
95
+ ```md
96
+ <script>
97
+ import { initAnalytics } from "../lib/analytics"
98
+ initAnalytics()
99
+ </script>
100
+
101
+ # My Page
102
+
103
+ Content here.
104
+ ```
105
+
106
+ ## Markdown Features
107
+
108
+ Full VitePress-parity feature set:
109
+
110
+ - **Syntax highlighting** — Shiki with dual light/dark themes
111
+ - **Line highlighting** — `{1,3-5}` meta in code fences
112
+ - **Notation transforms** — `[!code ++]`, `[!code --]`, `[!code highlight]`, `[!code focus]`, `[!code error]`, `[!code warning]`
113
+ - **Line numbers** — `:line-numbers` / `:no-line-numbers` per fence, `{=N}` start line
114
+ - **Code snippet imports** — `<<< ./file.ext` with region and line range support
115
+ - **Custom containers** — `:::tip`, `:::info`, `:::warning`, `:::danger`, `:::details`
116
+ - **GitHub alerts** — `> [!NOTE]`, `> [!TIP]`, `> [!WARNING]`, `> [!IMPORTANT]`, `> [!CAUTION]`
117
+ - **Emoji** — `:emoji_name:` shortcodes
118
+ - **Attributes** — `{.class #id}` on elements via `markdown-it-attrs`
119
+ - **Anchor links** — auto-generated heading anchors
120
+ - **Table of contents** — `[[toc]]` directive
121
+ - **Image lazy loading** — automatic `loading="lazy"` on images
122
+ - **Link handling** — external links get `target="_blank"`, internal `.md` links normalized
123
+
124
+ ## Theme
125
+
126
+ Two CSS exports for styling markdown content:
127
+
128
+ - **`@void/md/theme.css`** — full standalone theme (reset + prose + code + containers)
129
+ - **`@void/md/theme-content.css`** — content-only styles scoped to `.void-md` (prose + code + containers)
130
+
131
+ ```ts
132
+ // In your layout or entry
133
+ import '@void/md/theme.css'; // or theme-content.css
134
+ ```
135
+
136
+ ## Architecture
137
+
138
+ - **Compilation pipeline** — `extractScript()` strips `<script>` blocks and collects island imports → `compile()` runs gray-matter + markdown-exit + Shiki → `emit-{framework}()` generates a framework-specific virtual component
139
+ - **Framework-agnostic** — adapter auto-detected from plugin list; emitters for Vue (SFC), React (JSX), Svelte, and Solid
140
+ - **Lightweight metadata extraction** — `extractMetadata()` parses frontmatter + headings without running Shiki, used for `@void/md/pages` scanning
141
+ - **Islands integration** — pages with island imports split HTML into render chunks with `data-island` markers; pages without islands emit static HTML with a frontmatter context provider
142
+ - **Svelte/Solid secondary modules** — emits a JS re-export pointing to a secondary virtual module (`/@void-md-svelte/` or `/@void-md-solid/`) so Vite processes through the framework's own plugin
143
+
144
+ ## Tests
145
+
146
+ - `test/unit/compile.test.ts` — compilation pipeline: basic, frontmatter, title extraction, headings, Shiki, emoji, anchors
147
+ - `test/unit/extract-script.test.ts` — script extraction: island imports, client code, edge cases
148
+ - `test/unit/plugins.test.ts` — markdown-it plugins: containers, GitHub alerts, links, images, line numbers, emoji, TOC
149
+ - `test/unit/emit-vue.test.ts` — Vue component emission with and without islands
150
+ - `test/unit/emit-react.test.ts` — React component emission with and without islands
151
+ - `test/unit/emit-svelte.test.ts` — Svelte component emission with and without islands
152
+ - `test/unit/emit-solid.test.ts` — Solid component emission with and without islands
@@ -0,0 +1,333 @@
1
+ import matter from "gray-matter";
2
+ import MarkdownExit from "markdown-exit";
3
+ import anchor from "markdown-it-anchor";
4
+ import { createJavaScriptRegexEngine } from "@shikijs/engine-javascript";
5
+ import { transformerMetaHighlight, transformerNotationDiff, transformerNotationErrorLevel, transformerNotationFocus, transformerNotationHighlight } from "@shikijs/transformers";
6
+ import attrs from "markdown-it-attrs";
7
+ import { full } from "markdown-it-emoji";
8
+ import { bundledLanguages, createHighlighter, isSpecialLang } from "shiki";
9
+ import container from "markdown-it-container";
10
+ //#region src/plugins/containers.ts
11
+ function createContainer(md, klass, defaultTitle) {
12
+ return [
13
+ container,
14
+ klass,
15
+ { render(tokens, idx, _options, env) {
16
+ const token = tokens[idx];
17
+ if (token.nesting === 1) {
18
+ token.attrJoin("class", `${klass} custom-block`);
19
+ const attrs = md.renderer.renderAttrs(token);
20
+ const info = token.info.trim().slice(klass.length).trim();
21
+ const title = md.renderInline(info || defaultTitle, { references: env.references });
22
+ const titleClass = "custom-block-title" + (info ? "" : " custom-block-title-default");
23
+ if (klass === "details") return `<details${attrs}><summary>${title}</summary>\n`;
24
+ return `<div${attrs}><p class="${titleClass}">${title}</p>\n`;
25
+ }
26
+ return klass === "details" ? `</details>\n` : `</div>\n`;
27
+ } }
28
+ ];
29
+ }
30
+ /**
31
+ * Custom container blocks: :::tip, :::info, :::warning, :::danger, :::details
32
+ * Ported from VitePress.
33
+ */
34
+ function containerPlugin(md, options) {
35
+ md.use(...createContainer(md, "tip", options?.tipLabel ?? "TIP")).use(...createContainer(md, "info", options?.infoLabel ?? "INFO")).use(...createContainer(md, "warning", options?.warningLabel ?? "WARNING")).use(...createContainer(md, "danger", options?.dangerLabel ?? "DANGER")).use(...createContainer(md, "details", options?.detailsLabel ?? "Details"));
36
+ }
37
+ //#endregion
38
+ //#region src/plugins/github-alerts.ts
39
+ const markerRE = /^\[!(TIP|NOTE|INFO|IMPORTANT|WARNING|CAUTION|DANGER)\]([^\n\r]*)/i;
40
+ function capitalize(str) {
41
+ return str.charAt(0).toUpperCase() + str.slice(1);
42
+ }
43
+ /**
44
+ * GitHub-flavored alerts: > [!NOTE], > [!TIP], > [!WARNING], etc.
45
+ * Ported from VitePress.
46
+ */
47
+ function gitHubAlertsPlugin(md, options) {
48
+ const titleMark = {
49
+ tip: options?.tipLabel ?? "TIP",
50
+ note: options?.noteLabel ?? "NOTE",
51
+ info: options?.infoLabel ?? "INFO",
52
+ important: options?.importantLabel ?? "IMPORTANT",
53
+ warning: options?.warningLabel ?? "WARNING",
54
+ caution: options?.cautionLabel ?? "CAUTION",
55
+ danger: options?.dangerLabel ?? "DANGER"
56
+ };
57
+ const rule = (state) => {
58
+ const tokens = state.tokens;
59
+ for (let i = 0; i < tokens.length; i++) if (tokens[i].type === "blockquote_open") {
60
+ const startIndex = i;
61
+ const open = tokens[startIndex];
62
+ let endIndex = i + 1;
63
+ while (endIndex < tokens.length && (tokens[endIndex].type !== "blockquote_close" || tokens[endIndex].level !== open.level)) endIndex++;
64
+ if (endIndex === tokens.length) continue;
65
+ const close = tokens[endIndex];
66
+ const firstContent = tokens.slice(startIndex, endIndex + 1).find((token) => token.type === "inline");
67
+ if (!firstContent) continue;
68
+ const match = firstContent.content.match(markerRE);
69
+ if (!match) continue;
70
+ const type = match[1].toLowerCase();
71
+ const title = match[2].trim() || titleMark[type] || capitalize(type);
72
+ firstContent.content = firstContent.content.slice(match[0].length).trimStart();
73
+ open.type = "github_alert_open";
74
+ open.tag = "div";
75
+ open.meta = {
76
+ title,
77
+ type
78
+ };
79
+ close.type = "github_alert_close";
80
+ close.tag = "div";
81
+ }
82
+ };
83
+ md.core.ruler.after("block", "github-alerts", rule);
84
+ md.renderer.rules.github_alert_open = function(tokens, idx) {
85
+ const { title, type } = tokens[idx].meta;
86
+ return `<div class="${type} custom-block github-alert"><p class="custom-block-title">${title}</p>\n`;
87
+ };
88
+ }
89
+ //#endregion
90
+ //#region src/plugins/image.ts
91
+ const EXTERNAL_URL_RE$1 = /^[a-z]+:/i;
92
+ /**
93
+ * Normalizes relative image paths and optionally adds loading="lazy".
94
+ * Ported from VitePress.
95
+ */
96
+ function imagePlugin(md, options) {
97
+ const imageRule = md.renderer.rules.image;
98
+ md.renderer.rules.image = (tokens, idx, mdOptions, env, self) => {
99
+ const token = tokens[idx];
100
+ let url = token.attrGet("src");
101
+ if (url && !EXTERNAL_URL_RE$1.test(url)) {
102
+ if (!/^\.?\//.test(url)) url = "./" + url;
103
+ token.attrSet("src", decodeURIComponent(url));
104
+ }
105
+ if (options?.lazyLoading) token.attrSet("loading", "lazy");
106
+ return imageRule(tokens, idx, mdOptions, env, self);
107
+ };
108
+ }
109
+ //#endregion
110
+ //#region src/plugins/line-numbers.ts
111
+ /**
112
+ * Adds line number gutter to code blocks.
113
+ * Supports `:line-numbers` / `:no-line-numbers` per-block control and `{=N}` start line.
114
+ * Ported from VitePress.
115
+ */
116
+ function lineNumberPlugin(md, enable = false) {
117
+ const fence = md.renderer.rules.fence;
118
+ md.renderer.rules.fence = async (...args) => {
119
+ const rawCode = await fence(...args);
120
+ const [tokens, idx] = args;
121
+ const info = tokens[idx].info;
122
+ if (!enable && !/:line-numbers\b/.test(info) || enable && /:no-line-numbers\b/.test(info)) return rawCode;
123
+ let startLineNumber = 1;
124
+ const matchStart = info.match(/=(\d+)/);
125
+ if (matchStart?.[1]) startLineNumber = parseInt(matchStart[1]);
126
+ const lines = rawCode.slice(rawCode.indexOf("<code>"), rawCode.indexOf("</code>")).split("\n");
127
+ const lineNumbersWrapper = `<div class="line-numbers-wrapper" aria-hidden="true">${[...Array(lines.length)].map((_, i) => `<span class="line-number">${i + startLineNumber}</span><br>`).join("")}</div>`;
128
+ return rawCode.replace(/<\/div>$/, `${lineNumbersWrapper}</div>`).replace(/"(language-[^"]*?)"/, "\"$1 line-numbers-mode\"");
129
+ };
130
+ }
131
+ //#endregion
132
+ //#region src/plugins/link.ts
133
+ const EXTERNAL_URL_RE = /^[a-z]+:/i;
134
+ /**
135
+ * Adds target="_blank" and rel="noreferrer" to external links.
136
+ * Normalizes internal .md links to route paths.
137
+ * Ported from VitePress.
138
+ */
139
+ function linkPlugin(md, options) {
140
+ const externalAttrs = options?.externalAttrs ?? {
141
+ target: "_blank",
142
+ rel: "noreferrer"
143
+ };
144
+ md.renderer.rules.link_open = (tokens, idx, mdOptions, _env, self) => {
145
+ const token = tokens[idx];
146
+ const hrefIndex = token.attrIndex("href");
147
+ if (hrefIndex < 0) return self.renderToken(tokens, idx, mdOptions);
148
+ const url = token.attrs?.[hrefIndex]?.[1] ?? "";
149
+ if (url.startsWith("#") || token.attrGet("class") === "header-anchor") return self.renderToken(tokens, idx, mdOptions);
150
+ if (EXTERNAL_URL_RE.test(url)) for (const [key, val] of Object.entries(externalAttrs)) token.attrSet(key, val);
151
+ else {
152
+ let normalized = url;
153
+ if (normalized.endsWith(".md")) normalized = normalized.slice(0, -3);
154
+ else if (normalized.includes(".md#")) normalized = normalized.replace(".md#", "#");
155
+ if (normalized !== url) token.attrs[hrefIndex][1] = normalized;
156
+ }
157
+ return self.renderToken(tokens, idx, mdOptions);
158
+ };
159
+ }
160
+ //#endregion
161
+ //#region src/plugins/pre-wrapper.ts
162
+ function extractLang(info) {
163
+ return /^[a-zA-Z0-9-_]+/.exec(info)?.[0].replace(/-vue$/, "").replace(/^vue-html$/, "template").replace(/^ansi$/, "") || "";
164
+ }
165
+ /**
166
+ * Wraps fenced code blocks with a language label and copy button.
167
+ * Ported from VitePress.
168
+ */
169
+ function preWrapperPlugin(md, options) {
170
+ const title = options?.codeCopyButtonTitle ?? "Copy Code";
171
+ const fence = md.renderer.rules.fence;
172
+ md.renderer.rules.fence = async (...args) => {
173
+ const [tokens, idx] = args;
174
+ const token = tokens[idx];
175
+ token.info = token.info.replace(/\[.*\]/, "");
176
+ const active = / active( |$)/.test(token.info) ? " active" : "";
177
+ token.info = token.info.replace(/ active$/, "").replace(/ active /, " ");
178
+ const lang = extractLang(token.info);
179
+ const highlighted = await fence(...args);
180
+ return `<div class="language-${lang}${active}"><button title="${title}" class="copy"></button><span class="lang">${lang}</span>` + highlighted + `</div>`;
181
+ };
182
+ }
183
+ //#endregion
184
+ //#region src/plugins/toc.ts
185
+ function renderHeaders(headers, listTag) {
186
+ if (headers.length === 0) return "";
187
+ let result = `<${listTag}>`;
188
+ for (const h of headers) result += `<li><a href="#${h.slug}">${h.text}</a></li>`;
189
+ result += `</${listTag}>`;
190
+ return result;
191
+ }
192
+ /**
193
+ * [[toc]] directive — renders inline table of contents.
194
+ * Ported from VitePress.
195
+ */
196
+ function tocPlugin(md, options) {
197
+ const pattern = options?.pattern ?? /^\[\[toc\]\]$/i;
198
+ const containerTag = options?.containerTag ?? "nav";
199
+ const containerClass = options?.containerClass ?? "table-of-contents";
200
+ const listTag = options?.listTag ?? "ul";
201
+ const level = options?.level ?? [2, 3];
202
+ md.block.ruler.before("heading", "toc", ((state, startLine, _endLine, silent) => {
203
+ const pos = state.bMarks[startLine] + state.tShift[startLine];
204
+ const max = state.eMarks[startLine];
205
+ const content = state.src.slice(pos, max).trim();
206
+ if (!pattern.test(content)) return false;
207
+ if (silent) return true;
208
+ state.line = startLine + 1;
209
+ const openToken = state.push("toc_open", containerTag, 1);
210
+ openToken.markup = content;
211
+ openToken.attrSet("class", containerClass);
212
+ const bodyToken = state.push("toc_body", "", 0);
213
+ bodyToken.markup = content;
214
+ const closeToken = state.push("toc_close", containerTag, -1);
215
+ closeToken.markup = content;
216
+ return true;
217
+ }), { alt: [
218
+ "paragraph",
219
+ "reference",
220
+ "blockquote"
221
+ ] });
222
+ md.renderer.rules.toc_open = (tokens, idx, options, _env, self) => {
223
+ return self.renderToken(tokens, idx, options);
224
+ };
225
+ md.renderer.rules.toc_body = (tokens) => {
226
+ const headers = [];
227
+ for (let i = 0; i < tokens.length; i++) {
228
+ const token = tokens[i];
229
+ if (token.type === "heading_open") {
230
+ const depth = Number(token.tag.slice(1));
231
+ if (level.includes(depth)) {
232
+ const slug = token.attrGet?.("id") ?? "";
233
+ const text = (tokens[i + 1]?.children?.filter((child) => child.type === "text" || child.type === "code_inline").map((child) => child.content).join("") ?? "").trim();
234
+ headers.push({
235
+ depth,
236
+ slug,
237
+ text
238
+ });
239
+ }
240
+ }
241
+ }
242
+ return renderHeaders(headers, listTag);
243
+ };
244
+ md.renderer.rules.toc_close = (tokens, idx, options, _env, self) => {
245
+ return self.renderToken(tokens, idx, options);
246
+ };
247
+ }
248
+ //#endregion
249
+ //#region src/compile.ts
250
+ let compilerPromise = null;
251
+ async function getCompiler(options) {
252
+ if (compilerPromise) return compilerPromise;
253
+ compilerPromise = createCompiler(options);
254
+ return compilerPromise;
255
+ }
256
+ async function createCompiler(options) {
257
+ const md = new MarkdownExit({
258
+ html: true,
259
+ linkify: true
260
+ });
261
+ const themes = options?.shiki?.themes ?? {
262
+ light: "github-light",
263
+ dark: "github-dark"
264
+ };
265
+ const highlighter = await createHighlighter({
266
+ engine: createJavaScriptRegexEngine(),
267
+ themes: [themes.light, themes.dark],
268
+ langs: []
269
+ });
270
+ const shikiTransformers = [
271
+ transformerMetaHighlight(),
272
+ transformerNotationDiff(),
273
+ transformerNotationHighlight(),
274
+ transformerNotationFocus(),
275
+ transformerNotationErrorLevel()
276
+ ];
277
+ md.renderer.rules.fence = async (tokens, idx) => {
278
+ const token = tokens[idx];
279
+ const code = token.content;
280
+ let lang = token.info ? token.info.split(/\s+/g)[0] : "text";
281
+ if (!isSpecialLang(lang) && !highlighter.getLoadedLanguages().includes(lang)) if (lang in bundledLanguages) await highlighter.loadLanguage(lang);
282
+ else lang = "text";
283
+ return highlighter.codeToHtml(code, {
284
+ lang,
285
+ themes,
286
+ defaultColor: "light",
287
+ transformers: shikiTransformers,
288
+ meta: { __raw: token.info }
289
+ });
290
+ };
291
+ md.use(preWrapperPlugin);
292
+ md.use(containerPlugin);
293
+ md.use(imagePlugin, { lazyLoading: true });
294
+ md.use(linkPlugin);
295
+ md.use(lineNumberPlugin, false);
296
+ md.use(gitHubAlertsPlugin);
297
+ md.use(attrs);
298
+ md.use(full);
299
+ md.use(anchor, { permalink: anchor.permalink.ariaHidden({ placement: "before" }) });
300
+ md.use(tocPlugin);
301
+ return md;
302
+ }
303
+ function extractHeadings(tokens) {
304
+ const headings = [];
305
+ for (let i = 0; i < tokens.length; i++) {
306
+ const token = tokens[i];
307
+ if (token.type === "heading_open") {
308
+ const depth = Number(token.tag.slice(1));
309
+ const slug = token.attrGet?.("id") ?? "";
310
+ const text = (tokens[i + 1]?.children?.filter((child) => child.type === "text" || child.type === "code_inline").map((child) => child.content).join("") ?? "").trim();
311
+ headings.push({
312
+ depth,
313
+ slug,
314
+ text
315
+ });
316
+ }
317
+ }
318
+ return headings;
319
+ }
320
+ async function compile(source, options) {
321
+ const { data: frontmatter, content } = matter(source);
322
+ const compiler = await getCompiler(options);
323
+ const env = {};
324
+ const headings = extractHeadings(compiler.parse(content, env));
325
+ return {
326
+ html: await compiler.renderAsync(content, env),
327
+ frontmatter,
328
+ headings,
329
+ title: frontmatter.title ?? headings.find((h) => h.depth === 1)?.text ?? ""
330
+ };
331
+ }
332
+ //#endregion
333
+ export { compile };
@@ -0,0 +1,3 @@
1
+ import { FrontmatterContext, setFrontmatter, useFrontmatter } from "./runtime/index.mjs";
2
+ import { n as MdPage } from "./plugin-BpyOmjmC.mjs";
3
+ export { FrontmatterContext, type MdPage, setFrontmatter, useFrontmatter };
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import { FrontmatterContext, setFrontmatter, useFrontmatter } from "./runtime/index.mjs";
2
+ export { FrontmatterContext, setFrontmatter, useFrontmatter };
@@ -0,0 +1,56 @@
1
+ import { Plugin } from "vite";
2
+
3
+ //#region src/compile.d.ts
4
+ interface Heading {
5
+ depth: number;
6
+ slug: string;
7
+ text: string;
8
+ }
9
+ interface CompileResult {
10
+ html: string;
11
+ frontmatter: Record<string, unknown>;
12
+ headings: Array<Heading>;
13
+ title: string;
14
+ }
15
+ interface CompileOptions {
16
+ shiki?: {
17
+ themes?: {
18
+ light: string;
19
+ dark: string;
20
+ };
21
+ langs?: Array<string>;
22
+ };
23
+ }
24
+ //#endregion
25
+ //#region src/extract-script.d.ts
26
+ interface IslandImport {
27
+ name: string;
28
+ specifier: string;
29
+ strategy: string;
30
+ /** Root-relative island ID (set during transform when file context is available). */
31
+ islandId?: string;
32
+ }
33
+ interface ScriptExtraction {
34
+ script: string | null;
35
+ body: string;
36
+ islandImports: Array<IslandImport>;
37
+ clientCode: string | null;
38
+ }
39
+ //#endregion
40
+ //#region src/plugin.d.ts
41
+ interface MdPage {
42
+ path: string;
43
+ title: string;
44
+ frontmatter: Record<string, unknown>;
45
+ headings: Array<{
46
+ depth: number;
47
+ slug: string;
48
+ text: string;
49
+ }>;
50
+ }
51
+ interface MarkdownOptions {
52
+ shiki?: CompileOptions["shiki"];
53
+ }
54
+ declare function voidMarkdown(options?: MarkdownOptions): Array<Plugin>;
55
+ //#endregion
56
+ export { ScriptExtraction as a, IslandImport as i, MdPage as n, CompileOptions as o, voidMarkdown as r, CompileResult as s, MarkdownOptions as t };
@@ -0,0 +1,2 @@
1
+ import { a as ScriptExtraction, i as IslandImport, n as MdPage, o as CompileOptions, r as voidMarkdown, s as CompileResult, t as MarkdownOptions } from "./plugin-BpyOmjmC.mjs";
2
+ export { CompileOptions, CompileResult, IslandImport, MarkdownOptions, MdPage, ScriptExtraction, voidMarkdown };