hono-decks 1.1.0 → 1.2.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.
@@ -6,6 +6,19 @@ import { dirname, join, relative, resolve } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { parse } from "@babel/parser";
8
8
  import { execFile } from "node:child_process";
9
+ //#region src/adapters/void.ts
10
+ /** Dependency policy of the Void preset, outside the deck compiler. */
11
+ const voidDependencies = [
12
+ ["void", "void@>=1.1.11 <1.2.0"],
13
+ ["vite", "vite@^8.0.16"],
14
+ ["hono", "hono@^4.13.13"],
15
+ ["markdown-exit", "markdown-exit@^1.3.0"],
16
+ ["yaml", "yaml@^2.9.1"],
17
+ ["markdown-it-container", "markdown-it-container@^4.0.0"],
18
+ ["shiki", "shiki@^4.5.0"],
19
+ ["@shikijs/transformers", "@shikijs/transformers@^4.5.0"]
20
+ ];
21
+ //#endregion
9
22
  //#region src/node/install-void.ts
10
23
  async function installVoidDependencies(cwd, options = {}) {
11
24
  if (options.adapter?.startsWith("-")) throw new Error("--adapter must be a package specifier or tarball path.");
@@ -33,16 +46,7 @@ async function installVoidDependencies(cwd, options = {}) {
33
46
  return true;
34
47
  }
35
48
  };
36
- const required = [
37
- ["void", "void@>=1.1.11 <1.2.0"],
38
- ["vite", "vite@^8.0.16"],
39
- ["hono", "hono@^4.13.13"],
40
- ["markdown-exit", "markdown-exit@^1.3.0"],
41
- ["yaml", "yaml@^2.9.1"],
42
- ["markdown-it-container", "markdown-it-container@^4.0.0"],
43
- ["shiki", "shiki@^4.5.0"],
44
- ["@shikijs/transformers", "@shikijs/transformers@^4.5.0"]
45
- ];
49
+ const required = voidDependencies;
46
50
  for (const [id, spec] of required) {
47
51
  if (missing(id)) continue;
48
52
  const version = await installedVersion(require.resolve(id), id);
@@ -77,7 +81,7 @@ async function installVoidDependencies(cwd, options = {}) {
77
81
  specs.push(`hono-decks@${requested}`);
78
82
  }
79
83
  if (options.adapter || missing("void-plugin-hono-jsx/markdown/compiler")) {
80
- let adapter = options.adapter ?? "void-plugin-hono-jsx@^0.4.0";
84
+ let adapter = options.adapter ?? "void-plugin-hono-jsx@^0.5.0";
81
85
  if (/^\d+\./.test(adapter)) adapter = "void-plugin-hono-jsx@" + adapter;
82
86
  else if (modernYarn && (adapter.endsWith(".tgz") || adapter.startsWith("file:"))) adapter = "void-plugin-hono-jsx@file:" + resolve(cwd, adapter.replace(/^file:/, ""));
83
87
  specs.push(adapter);
@@ -127,26 +131,30 @@ function compare(a, b) {
127
131
  return 0;
128
132
  }
129
133
  //#endregion
134
+ //#region src/adapters/vite-presets.ts
135
+ /** Initialization policies belong to the host integration, not the compiler. */
136
+ const vitePresets = {
137
+ void: { dependencies: [...voidDependencies.map(([id]) => id), "void-plugin-hono-jsx/markdown/compiler"] },
138
+ hono: {
139
+ dependencies: ["vite", "hono"],
140
+ clientEntryAssetPath: "/_assets/client"
141
+ },
142
+ honox: {
143
+ dependencies: [
144
+ "vite",
145
+ "hono",
146
+ "honox"
147
+ ],
148
+ clientEntryAssetPath: "/_assets/client"
149
+ }
150
+ };
151
+ //#endregion
130
152
  //#region src/node/init-vite.ts
131
153
  /** Configure an existing Vite app without changing its package scripts. */
132
154
  async function initializeVite(cwd, framework, configFile = "hono-decks.config.ts", entry, installation) {
133
155
  if (!configFile || configFile.startsWith("/") || configFile.includes("\\") || /^[A-Za-z]:/.test(configFile) || configFile.split("/").includes("..") || !/\.[cm]?[jt]s$/.test(configFile)) throw new Error("--config must be a JS/TS file inside the current working directory.");
134
156
  const require = createRequire(join(cwd, "package.json"));
135
- const dependencies = framework === "void" ? [
136
- "void",
137
- "vite",
138
- "hono",
139
- "void-plugin-hono-jsx/markdown/compiler",
140
- "markdown-exit",
141
- "yaml",
142
- "markdown-it-container",
143
- "shiki",
144
- "@shikijs/transformers"
145
- ] : [
146
- "vite",
147
- "hono",
148
- ...framework === "honox" ? ["honox"] : []
149
- ];
157
+ const dependencies = vitePresets[framework].dependencies;
150
158
  for (const dependency of installation ? [] : dependencies) try {
151
159
  require.resolve(dependency);
152
160
  } catch {
@@ -177,7 +185,7 @@ async function initializeVite(cwd, framework, configFile = "hono-decks.config.ts
177
185
 
178
186
  export default defineDecksConfig({
179
187
  mountPath: "/slides",
180
- build: { root: "decks"${framework === "void" ? ", markdown: \"void\"" : ""} },
188
+ build: { root: "decks"${framework === "void" ? ", markdown: \"void\"" : ""} },${vitePresets[framework].clientEntryAssetPath ? `\n router: { clientEntryAssetPath: ${JSON.stringify(vitePresets[framework].clientEntryAssetPath)} },` : ""}
181
189
  });
182
190
  `
183
191
  },
package/dist/mod.d.ts CHANGED
@@ -463,8 +463,11 @@ type DecksRouterOverrides<E extends Env = any> = Partial<Omit<DecksRouterOptions
463
463
  type DecksRouterConfig<E extends Env = any> = Omit<DecksRouterOverrides<E>, "source">;
464
464
  /** File-system settings consumed by the CLI. */
465
465
  interface DecksBuildConfig {
466
- /** Opt into .md decks compiled by void-plugin-hono-jsx/markdown/compiler. */
467
- markdown?: "void";
466
+ /** Build-only Markdown adapter reference. "void" retains the compatible preset. */
467
+ markdown?: "void" | {
468
+ adapter: string;
469
+ options?: Record<string, unknown>;
470
+ };
468
471
  /** Deck source directory. @default "decks" */
469
472
  root?: string;
470
473
  /** CLI output directory. Vite keeps modules in memory. @default "src/generated" */
package/dist/node.d.ts CHANGED
@@ -236,116 +236,6 @@ declare class RenderError extends Error {
236
236
  constructor(message: string, code: string, cause?: unknown);
237
237
  }
238
238
  //#endregion
239
- //#region src/routing/file-routing.d.ts
240
- interface ResolvedDeckFile {
241
- slug: string;
242
- sourcePath: string;
243
- kind: DeckKind;
244
- assetPaths: string[];
245
- }
246
- declare function resolveDeckFiles(paths: string[], root?: string, options?: {
247
- markdown?: boolean;
248
- }): ResolvedDeckFile[];
249
- //#endregion
250
- //#region src/generator/mdx/ogp.d.ts
251
- interface LinkCardOgpMetadata {
252
- title?: string;
253
- description?: string;
254
- image?: string;
255
- siteName?: string;
256
- }
257
- //#endregion
258
- //#region src/generator/component-registry.d.ts
259
- interface DeckComponentExport {
260
- slug: string;
261
- sourcePath: string;
262
- modulePath: string;
263
- exportName: string;
264
- }
265
- interface ResolvedDeckComponentExport extends DeckComponentExport {
266
- internalName: string;
267
- }
268
- interface ApplyDeckComponentRegistryInput {
269
- manifest: DeckManifest;
270
- components: DeckComponentExport[];
271
- }
272
- interface ApplyDeckComponentRegistryResult {
273
- manifest: DeckManifest;
274
- components: ResolvedDeckComponentExport[];
275
- }
276
- declare function applyDeckComponentRegistry(input: ApplyDeckComponentRegistryInput): ApplyDeckComponentRegistryResult;
277
- declare function emitDeckComponentRegistryModule(components: ResolvedDeckComponentExport[]): string;
278
- //#endregion
279
- //#region src/node/compile-decks.d.ts
280
- interface BuildDeckManifestFromFileSystemInput {
281
- cwd: string;
282
- root: string;
283
- mountPath?: string;
284
- }
285
- interface WriteDeckManifestModuleInput {
286
- manifest: DeckManifest;
287
- outFile: string;
288
- }
289
- interface WriteDeckComponentRegistryModuleInput {
290
- components: ResolvedDeckComponentExport[];
291
- outFile: string;
292
- }
293
- interface WriteDecksRouterModuleInput {
294
- manifestModulePath: string;
295
- componentRegistryModulePath?: string;
296
- outFile: string;
297
- }
298
- interface CompileDecksInput extends BuildDeckManifestFromFileSystemInput {
299
- markdown?: "void";
300
- out: string;
301
- ogpCacheFile?: string;
302
- refreshOgp?: boolean;
303
- resolveOgp?(this: void, url: string): Promise<LinkCardOgpMetadata | undefined>;
304
- }
305
- /** Build information only; render using createDecks() from routerModulePath. */
306
- interface CompileDecksResult {
307
- outDir: string;
308
- routerModulePath: string;
309
- decks: DeckBuildSummary[];
310
- }
311
- interface DeckBuildSummary extends Omit<CompiledDeck, "slides" | "componentRegistry"> {
312
- slides: (Pick<CompiledSlide, "index" | "meta" | "notes"> & {
313
- modulePath: string;
314
- })[];
315
- }
316
- declare function compileDecks(input: CompileDecksInput): Promise<CompileDecksResult>;
317
- declare function buildDeckManifestFromFileSystem(input: BuildDeckManifestFromFileSystemInput): Promise<DeckManifest>;
318
- declare function writeDeckManifestModule(input: WriteDeckManifestModuleInput): Promise<void>;
319
- declare function writeDeckComponentRegistryModule(input: WriteDeckComponentRegistryModuleInput): Promise<void>;
320
- declare function writeDecksRouterModule(input: WriteDecksRouterModuleInput): Promise<void>;
321
- //#endregion
322
- //#region src/node/local-deck-io.d.ts
323
- interface CreateLocalDeckIOInput {
324
- cwd: string;
325
- root: string;
326
- pathExists?(this: void, path: string): boolean;
327
- watchFileSystem?(this: void, path: string, options: {
328
- recursive: boolean;
329
- }, listener: (eventType: "rename" | "change", filename: string | null) => void): {
330
- close(): void;
331
- };
332
- }
333
- declare function createLocalDeckIO(input: CreateLocalDeckIOInput): LocalDeckIO;
334
- //#endregion
335
- //#region src/node/local-dev-app.d.ts
336
- interface CreateLocalDevSlidesAppInput {
337
- cwd: string;
338
- root: string;
339
- mountPath?: string;
340
- watchFileSystem?: CreateLocalDeckIOInput["watchFileSystem"];
341
- }
342
- interface LocalDevSlidesApp {
343
- app: Hono;
344
- localDeckIO: LocalDeckIO;
345
- stop: () => void;
346
- }
347
- declare function createLocalDevSlidesApp(input: CreateLocalDevSlidesAppInput): Promise<LocalDevSlidesApp>;
348
- //#endregion
349
239
  //#region src/renderer/control-icons.d.ts
350
240
  type DeckControlIconName = "deck-list" | "home" | "viewer" | "projection" | "presenter" | "previous" | "next" | "fullscreen" | "external-link" | "print" | "details" | "export-pdf" | "export-png";
351
241
  //#endregion
@@ -646,8 +536,11 @@ type DecksRouterOverrides<E extends Env = any> = Partial<Omit<DecksRouterOptions
646
536
  type DecksRouterConfig<E extends Env = any> = Omit<DecksRouterOverrides<E>, "source">;
647
537
  /** File-system settings consumed by the CLI. */
648
538
  interface DecksBuildConfig {
649
- /** Opt into .md decks compiled by void-plugin-hono-jsx/markdown/compiler. */
650
- markdown?: "void";
539
+ /** Build-only Markdown adapter reference. "void" retains the compatible preset. */
540
+ markdown?: "void" | {
541
+ adapter: string;
542
+ options?: Record<string, unknown>;
543
+ };
651
544
  /** Deck source directory. @default "decks" */
652
545
  root?: string;
653
546
  /** CLI output directory. Vite keeps modules in memory. @default "src/generated" */
@@ -664,6 +557,116 @@ interface DecksConfig<E extends Env = any> {
664
557
  router?: DecksRouterConfig<E>;
665
558
  }
666
559
  //#endregion
560
+ //#region src/routing/file-routing.d.ts
561
+ interface ResolvedDeckFile {
562
+ slug: string;
563
+ sourcePath: string;
564
+ kind: DeckKind;
565
+ assetPaths: string[];
566
+ }
567
+ declare function resolveDeckFiles(paths: string[], root?: string, options?: {
568
+ markdown?: boolean;
569
+ }): ResolvedDeckFile[];
570
+ //#endregion
571
+ //#region src/generator/mdx/ogp.d.ts
572
+ interface LinkCardOgpMetadata {
573
+ title?: string;
574
+ description?: string;
575
+ image?: string;
576
+ siteName?: string;
577
+ }
578
+ //#endregion
579
+ //#region src/generator/component-registry.d.ts
580
+ interface DeckComponentExport {
581
+ slug: string;
582
+ sourcePath: string;
583
+ modulePath: string;
584
+ exportName: string;
585
+ }
586
+ interface ResolvedDeckComponentExport extends DeckComponentExport {
587
+ internalName: string;
588
+ }
589
+ interface ApplyDeckComponentRegistryInput {
590
+ manifest: DeckManifest;
591
+ components: DeckComponentExport[];
592
+ }
593
+ interface ApplyDeckComponentRegistryResult {
594
+ manifest: DeckManifest;
595
+ components: ResolvedDeckComponentExport[];
596
+ }
597
+ declare function applyDeckComponentRegistry(input: ApplyDeckComponentRegistryInput): ApplyDeckComponentRegistryResult;
598
+ declare function emitDeckComponentRegistryModule(components: ResolvedDeckComponentExport[]): string;
599
+ //#endregion
600
+ //#region src/node/compile-decks.d.ts
601
+ interface BuildDeckManifestFromFileSystemInput {
602
+ cwd: string;
603
+ root: string;
604
+ mountPath?: string;
605
+ }
606
+ interface WriteDeckManifestModuleInput {
607
+ manifest: DeckManifest;
608
+ outFile: string;
609
+ }
610
+ interface WriteDeckComponentRegistryModuleInput {
611
+ components: ResolvedDeckComponentExport[];
612
+ outFile: string;
613
+ }
614
+ interface WriteDecksRouterModuleInput {
615
+ manifestModulePath: string;
616
+ componentRegistryModulePath?: string;
617
+ outFile: string;
618
+ }
619
+ interface CompileDecksInput extends BuildDeckManifestFromFileSystemInput {
620
+ markdown?: DecksBuildConfig["markdown"];
621
+ out: string;
622
+ ogpCacheFile?: string;
623
+ refreshOgp?: boolean;
624
+ resolveOgp?(this: void, url: string): Promise<LinkCardOgpMetadata | undefined>;
625
+ }
626
+ /** Build information only; render using createDecks() from routerModulePath. */
627
+ interface CompileDecksResult {
628
+ outDir: string;
629
+ routerModulePath: string;
630
+ decks: DeckBuildSummary[];
631
+ }
632
+ interface DeckBuildSummary extends Omit<CompiledDeck, "slides" | "componentRegistry"> {
633
+ slides: (Pick<CompiledSlide, "index" | "meta" | "notes"> & {
634
+ modulePath: string;
635
+ })[];
636
+ }
637
+ declare function compileDecks(input: CompileDecksInput): Promise<CompileDecksResult>;
638
+ declare function buildDeckManifestFromFileSystem(input: BuildDeckManifestFromFileSystemInput): Promise<DeckManifest>;
639
+ declare function writeDeckManifestModule(input: WriteDeckManifestModuleInput): Promise<void>;
640
+ declare function writeDeckComponentRegistryModule(input: WriteDeckComponentRegistryModuleInput): Promise<void>;
641
+ declare function writeDecksRouterModule(input: WriteDecksRouterModuleInput): Promise<void>;
642
+ //#endregion
643
+ //#region src/node/local-deck-io.d.ts
644
+ interface CreateLocalDeckIOInput {
645
+ cwd: string;
646
+ root: string;
647
+ pathExists?(this: void, path: string): boolean;
648
+ watchFileSystem?(this: void, path: string, options: {
649
+ recursive: boolean;
650
+ }, listener: (eventType: "rename" | "change", filename: string | null) => void): {
651
+ close(): void;
652
+ };
653
+ }
654
+ declare function createLocalDeckIO(input: CreateLocalDeckIOInput): LocalDeckIO;
655
+ //#endregion
656
+ //#region src/node/local-dev-app.d.ts
657
+ interface CreateLocalDevSlidesAppInput {
658
+ cwd: string;
659
+ root: string;
660
+ mountPath?: string;
661
+ watchFileSystem?: CreateLocalDeckIOInput["watchFileSystem"];
662
+ }
663
+ interface LocalDevSlidesApp {
664
+ app: Hono;
665
+ localDeckIO: LocalDeckIO;
666
+ stop: () => void;
667
+ }
668
+ declare function createLocalDevSlidesApp(input: CreateLocalDevSlidesAppInput): Promise<LocalDevSlidesApp>;
669
+ //#endregion
667
670
  //#region src/node/config.d.ts
668
671
  declare const DEFAULT_DECKS_CONFIG_FILE = "hono-decks.config.ts";
669
672
  interface LoadedDecksConfig<E extends Env = any> {
package/dist/node.js CHANGED
@@ -5,10 +5,10 @@ import { unified } from "unified";
5
5
  import remarkGfm from "remark-gfm";
6
6
  import remarkMdx from "remark-mdx";
7
7
  import remarkParse from "remark-parse";
8
- import { fileURLToPath, pathToFileURL } from "node:url";
9
8
  import { compile } from "@mdx-js/mdx";
10
9
  import remarkDirective from "remark-directive";
11
10
  import { codeToHtml } from "shiki";
11
+ import { fileURLToPath, pathToFileURL } from "node:url";
12
12
  import { existsSync, watch } from "node:fs";
13
13
  import { build } from "esbuild";
14
14
  import { lookup } from "node:dns/promises";
@@ -1698,29 +1698,20 @@ function extractDeckPrelude(source) {
1698
1698
  };
1699
1699
  }
1700
1700
  //#endregion
1701
- //#region src/node/void-markdown.ts
1702
- /** Load only for an explicit Void Markdown build; ordinary MDX has no new peers. */
1703
- async function compileVoidMarkdownDecks(input) {
1701
+ //#region src/node/markdown-decks.ts
1702
+ async function compileMarkdownDecks(input, adapter) {
1704
1703
  const markdownEntries = input.decks.filter((entry) => entry.sourcePath.endsWith(".md"));
1705
1704
  const mdx = await compileMdxModuleDecks({
1706
1705
  ...input,
1707
1706
  decks: input.decks.filter((entry) => !entry.sourcePath.endsWith(".md"))
1708
1707
  });
1709
1708
  if (!markdownEntries.length) return mdx;
1710
- let compiler;
1711
- try {
1712
- compiler = await import(pathToFileURL(createRequire(resolve(input.cwd ?? process.cwd(), "package.json")).resolve("void-plugin-hono-jsx/markdown/compiler")).href);
1713
- if (typeof (compiler.compileMarkdownModule ?? compiler.compileStaticMarkdown) !== "function" || typeof (compiler.readMarkdownModule ?? compiler.readStaticMarkdown) !== "function") throw new Error("Missing standalone compiler API");
1714
- } catch (error) {
1715
- throw new Error("Void Markdown requires void-plugin-hono-jsx with the public /markdown/compiler API and its Markdown peers installed in the app.", { cause: error });
1716
- }
1717
- const readModule = compiler.readMarkdownModule ?? compiler.readStaticMarkdown;
1718
- const compileModule = compiler.compileMarkdownModule ?? compiler.compileStaticMarkdown;
1719
1709
  const decks = [...mdx.decks];
1720
1710
  const clients = /* @__PURE__ */ new Set();
1721
1711
  const markdownIslands = /* @__PURE__ */ new Map();
1722
1712
  for (const entry of markdownEntries) try {
1723
- const parsed = readModule(await input.readText(entry.sourcePath));
1713
+ const source = await input.readText(entry.sourcePath);
1714
+ const parsed = adapter.read(source);
1724
1715
  if (input.clientComponentIds?.[entry.slug] && Object.keys(input.clientComponentIds[entry.slug]).length) throw new Error("Void Markdown slides support static components only; use MDX for hono-decks client components.");
1725
1716
  const warnings = [];
1726
1717
  const meta = toDeckFrontmatter(parsed.frontmatter, warnings);
@@ -1741,12 +1732,13 @@ async function compileVoidMarkdownDecks(input) {
1741
1732
  const slides = [];
1742
1733
  for (const [index, slideSource] of splitMarkdownSlides(parsed.content).entries()) {
1743
1734
  const prelude = parsed.script ? `<script>\n${parsed.script}\n<\/script>\n` : "";
1744
- const slide = readModule(prelude + slideSource);
1735
+ const slide = adapter.read(prelude + slideSource);
1745
1736
  const path = `${input.outDir}/decks/${entry.slug}/slide-${index}.ts`;
1746
- const result = await compileModule(rewriteAssetUrls(`${prelude}---\n${JSON.stringify({
1737
+ const rewritten = rewriteAssetUrls(`${prelude}---\n${JSON.stringify({
1747
1738
  ...parsed.frontmatter,
1748
1739
  ...slide.frontmatter
1749
- })}\n---\n${slide.content}`, assets), { sourcePath: resolve(input.cwd ?? process.cwd(), entry.sourcePath) });
1740
+ })}\n---\n${slide.content}`, assets);
1741
+ const result = await adapter.compile(rewritten, { sourcePath: resolve(input.cwd ?? process.cwd(), entry.sourcePath) });
1750
1742
  clientEntryRequired ||= Boolean(result.clientCode || result.islands?.length);
1751
1743
  if (result.clientCode) clients.add(result.clientCode);
1752
1744
  for (const island of result.islands ?? []) markdownIslands.set(island.id, island);
@@ -1796,6 +1788,7 @@ async function compileVoidMarkdownDecks(input) {
1796
1788
  decks,
1797
1789
  routerModule: emitModuleDecksRouter({ decks }),
1798
1790
  clientCode: [...clients].join("\n"),
1791
+ clientModules: [adapter.client([...markdownIslands.values()])].filter(Boolean),
1799
1792
  markdownIslands: [...markdownIslands.values()]
1800
1793
  };
1801
1794
  }
@@ -1832,6 +1825,64 @@ function splitMarkdownSlides(source) {
1832
1825
  return splitSlideSources(protectedLines.join("\n")).map((slide) => slide.replace(new RegExp(`${prefix}(\\d+)_END`, "g"), (_, id) => fences[Number(id)]));
1833
1826
  }
1834
1827
  //#endregion
1828
+ //#region src/adapters/hono-markdown.ts
1829
+ /** Node-only bridge. Existing Void configs retain their original compiler/runtime pair. */
1830
+ async function createHonoMarkdownAdapter(cwd, legacy = false, options) {
1831
+ const require = createRequire(resolve(cwd, "package.json"));
1832
+ const entry = legacy ? "void-plugin-hono-jsx/markdown/compiler" : "@ts-76/hono-markdown/compiler";
1833
+ let compiler;
1834
+ try {
1835
+ compiler = await import(pathToFileURL(require.resolve(entry)).href);
1836
+ } catch (cause) {
1837
+ throw new Error(`Hono Markdown requires ${legacy ? "its public /markdown/compiler API" : "@ts-76/hono-markdown/compiler"} and Markdown peers installed in the app.`, { cause });
1838
+ }
1839
+ const read = compiler.readMarkdownModule ?? compiler.readStaticMarkdown;
1840
+ const compile = compiler.compileMarkdownModule ?? compiler.compileStaticMarkdown;
1841
+ if (typeof read !== "function" || typeof compile !== "function") throw new Error("Missing standalone compiler API");
1842
+ const runtime = entry.startsWith("void-plugin") ? "void-plugin-hono-jsx/markdown/client" : "@ts-76/hono-markdown/client";
1843
+ return {
1844
+ read,
1845
+ compile: (source, input) => compile(source, {
1846
+ ...input,
1847
+ options
1848
+ }),
1849
+ client: (islands) => emitMarkdownClient(islands, runtime)
1850
+ };
1851
+ }
1852
+ function emitMarkdownClient(islands, runtime) {
1853
+ if (!islands.length) return "";
1854
+ const literal = (value) => JSON.stringify(value).replace(/</g, "\\u003c");
1855
+ return `import { hydrateMarkdownIslands } from ${literal(runtime)};
1856
+ hydrateMarkdownIslands({ ${islands.map((island) => `${literal(island.id)}: () => import(${literal(island.modulePath)})`).join(",")} }, {
1857
+ ready(element, start) {
1858
+ const slide = element.closest('section[data-slide-state]');
1859
+ if (!slide || slide.dataset.slideState === 'active') { start(); return; }
1860
+ const observer = new MutationObserver(() => {
1861
+ if (slide.dataset.slideState === 'active') { observer.disconnect(); start(); }
1862
+ });
1863
+ observer.observe(slide, { attributes: true, attributeFilter: ['data-slide-state'] });
1864
+ }
1865
+ });`;
1866
+ }
1867
+ //#endregion
1868
+ //#region src/node/markdown-adapter.ts
1869
+ async function loadMarkdownAdapter(selection, cwd) {
1870
+ if (selection === "void") return createHonoMarkdownAdapter(cwd, true);
1871
+ if (selection.adapter === "hono-decks/adapters/hono-markdown") return createHonoMarkdownAdapter(cwd, false, selection.options);
1872
+ const module = await import(pathToFileURL(createRequire(resolve(cwd, "package.json")).resolve(selection.adapter)).href);
1873
+ if (typeof module.createMarkdownAdapter !== "function") throw new Error("Markdown adapter must export createMarkdownAdapter.");
1874
+ const adapter = await module.createMarkdownAdapter({
1875
+ cwd,
1876
+ options: selection.options
1877
+ });
1878
+ if (![
1879
+ adapter?.read,
1880
+ adapter?.compile,
1881
+ adapter?.client
1882
+ ].every((value) => typeof value === "function")) throw new Error("Markdown adapter must provide read, compile and client functions.");
1883
+ return adapter;
1884
+ }
1885
+ //#endregion
1835
1886
  //#region src/generator/component-registry.ts
1836
1887
  function applyDeckComponentRegistry(input) {
1837
1888
  const components = input.components.map((component) => ({
@@ -1942,12 +1993,11 @@ async function emitClientEntryModule(input) {
1942
1993
  registrations.push(`${JSON.stringify(clientId)}: ${localName}`);
1943
1994
  }
1944
1995
  }
1945
- const markdown = input.markdownIslands ?? [];
1946
- if (registrations.length === 0 && markdown.length === 0) return `export const decksClientEntry = ${JSON.stringify(input.extraCode ?? "")};\n`;
1996
+ const clientModules = input.clientModules ?? [];
1997
+ if (registrations.length === 0 && clientModules.length === 0) return `export const decksClientEntry = ${JSON.stringify(input.extraCode ?? "")};\n`;
1947
1998
  const result = await build({
1948
1999
  stdin: {
1949
- contents: `${markdown.length ? `import { hydrateMarkdownIslands } from "void-plugin-hono-jsx/markdown/client";
1950
- hydrateMarkdownIslands({ ${markdown.map((entry) => `${javascriptString(entry.id)}: () => import(${javascriptString(entry.modulePath)})`).join(",")} });` : ""}
2000
+ contents: `${clientModules.join("\n")}
1951
2001
  import { hydrateSlideIslands } from "hono-decks/client";
1952
2002
  ${imports.join("\n")}
1953
2003
 
@@ -2024,9 +2074,6 @@ function hashString(value) {
2024
2074
  function safeIdentifier(value) {
2025
2075
  return value.replace(/[^A-Za-z0-9_$]+/g, "_").replace(/^[^A-Za-z_$]+/, "_") || "_";
2026
2076
  }
2027
- function javascriptString(value) {
2028
- return JSON.stringify(value).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
2029
- }
2030
2077
  //#endregion
2031
2078
  //#region src/node/path-utils.ts
2032
2079
  function dirname(path) {
@@ -2369,7 +2416,7 @@ async function compileDeckModules(input) {
2369
2416
  const out = normalizeRelativePath(input.out, "Output directory");
2370
2417
  const root = normalizeDeckRoot(input.root);
2371
2418
  const paths = await listFiles(input.cwd, join(input.cwd, root));
2372
- const resolved = resolveDeckFiles(paths, root, { markdown: input.markdown === "void" });
2419
+ const resolved = resolveDeckFiles(paths, root, { markdown: Boolean(input.markdown) });
2373
2420
  const componentModulePaths = Object.fromEntries(resolved.flatMap((deck) => {
2374
2421
  const path = deck.kind === "directory" ? findComponentModule(paths, root, deck.slug) : void 0;
2375
2422
  return path ? [[deck.slug, path]] : [];
@@ -2397,7 +2444,8 @@ async function compileDeckModules(input) {
2397
2444
  refresh: input.refreshOgp,
2398
2445
  resolveOgp: input.resolveOgp ?? resolveOgpMetadata
2399
2446
  });
2400
- const generated = await (input.markdown === "void" ? compileVoidMarkdownDecks : compileMdxModuleDecks)({
2447
+ const adapter = input.markdown && resolved.some((entry) => entry.sourcePath.endsWith(".md")) ? await loadMarkdownAdapter(input.markdown, input.cwd) : void 0;
2448
+ const generated = await (adapter ? (value) => compileMarkdownDecks(value, adapter) : compileMdxModuleDecks)({
2401
2449
  cwd: input.cwd,
2402
2450
  root,
2403
2451
  outDir: out,
@@ -2416,7 +2464,7 @@ async function compileDeckModules(input) {
2416
2464
  clientEntries: clientEntryPaths,
2417
2465
  clientComponentIds,
2418
2466
  extraCode: generated.clientCode,
2419
- markdownIslands: generated.markdownIslands,
2467
+ clientModules: generated.clientModules,
2420
2468
  onCss: (css) => {
2421
2469
  clientCss = css;
2422
2470
  }
@@ -5701,7 +5749,7 @@ async function loadDecksConfig(input) {
5701
5749
  const config = (await import(`data:text/javascript;charset=utf-8,${encodeURIComponent(source)}#${Date.now()}`)).default;
5702
5750
  if (!config || typeof config !== "object") throw new Error(`Config must have a default export: ${path}`);
5703
5751
  if (typeof config.mountPath !== "string") throw new Error(`Config mountPath must be a string: ${path}`);
5704
- if (config.build?.markdown !== void 0 && config.build.markdown !== "void") throw new Error("Config build.markdown must be \"void\".");
5752
+ if (config.build?.markdown !== void 0 && config.build.markdown !== "void" && (typeof config.build.markdown !== "object" || !config.build.markdown || typeof config.build.markdown.adapter !== "string" || !config.build.markdown.adapter.trim())) throw new Error("Config build.markdown must be \"void\" or an adapter reference.");
5705
5753
  return {
5706
5754
  path,
5707
5755
  config,