hono-decks 1.0.4 → 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.
- package/README.ja.md +221 -3
- package/README.md +241 -3
- package/dist/adapters/hono-markdown.d.ts +39 -0
- package/dist/adapters/hono-markdown.js +47 -0
- package/dist/advanced.d.ts +8 -1
- package/dist/advanced.js +165 -118
- package/dist/bin.js +356 -86
- package/dist/cli.js +356 -86
- package/dist/file-transaction-NjDxX6Gt.js +80 -0
- package/dist/honox.d.ts +21 -0
- package/dist/honox.js +47 -0
- package/dist/init-vite-EA_L4k2H.js +441 -0
- package/dist/mod.d.ts +8 -1
- package/dist/mod.js +7 -2
- package/dist/node.d.ts +121 -98
- package/dist/node.js +474 -157
- package/dist/vite.d.ts +4 -2
- package/dist/vite.js +441 -53
- package/package.json +15 -2
- package/skills/hono-decks/references/getting-started.md +40 -1
- package/skills/hono-decks/references/integrating.md +12 -0
- package/skills/hono-decks/references/styling.md +10 -0
- package/vite-env.d.ts +7 -0
package/dist/vite.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, writeFile } from "node:fs/promises";
|
|
1
3
|
import { join, relative, resolve } from "node:path";
|
|
2
|
-
import {
|
|
4
|
+
import { mergeConfig } from "vite";
|
|
5
|
+
import { build, transform } from "esbuild";
|
|
3
6
|
import { unified } from "unified";
|
|
4
7
|
import remarkGfm from "remark-gfm";
|
|
5
8
|
import remarkMdx from "remark-mdx";
|
|
@@ -7,29 +10,28 @@ import remarkParse from "remark-parse";
|
|
|
7
10
|
import { compile } from "@mdx-js/mdx";
|
|
8
11
|
import remarkDirective from "remark-directive";
|
|
9
12
|
import { codeToHtml } from "shiki";
|
|
13
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
10
14
|
import { existsSync } from "node:fs";
|
|
11
|
-
import { fileURLToPath } from "node:url";
|
|
12
|
-
import { build } from "esbuild";
|
|
13
15
|
import { lookup } from "node:dns/promises";
|
|
14
16
|
import { isIP } from "node:net";
|
|
15
17
|
//#region src/routing/file-routing.ts
|
|
16
|
-
function resolveDeckFiles(paths, root = "decks") {
|
|
18
|
+
function resolveDeckFiles(paths, root = "decks", options = {}) {
|
|
17
19
|
const normalizedRoot = normalizePath$3(root).replace(/\/$/, "");
|
|
18
20
|
const decks = /* @__PURE__ */ new Map();
|
|
19
21
|
const assetsBySlug = /* @__PURE__ */ new Map();
|
|
20
22
|
for (const inputPath of paths.map(normalizePath$3).sort()) {
|
|
21
23
|
assertInsideRoot(inputPath, normalizedRoot);
|
|
22
24
|
const segments = inputPath.slice(normalizedRoot.length + 1).split("/");
|
|
23
|
-
if (segments.length === 1 && segments[0].endsWith(".mdx")) {
|
|
25
|
+
if (segments.length === 1 && (segments[0].endsWith(".mdx") || options.markdown && segments[0].endsWith(".md"))) {
|
|
24
26
|
addDeck(decks, {
|
|
25
|
-
slug: segments[0].replace(/\.mdx
|
|
27
|
+
slug: segments[0].replace(/\.mdx?$/, ""),
|
|
26
28
|
sourcePath: inputPath,
|
|
27
29
|
kind: "single-file",
|
|
28
30
|
assetPaths: []
|
|
29
31
|
});
|
|
30
32
|
continue;
|
|
31
33
|
}
|
|
32
|
-
if (segments.length === 2 && segments[1] === "deck.mdx") {
|
|
34
|
+
if (segments.length === 2 && (segments[1] === "deck.mdx" || options.markdown && segments[1] === "deck.md")) {
|
|
33
35
|
addDeck(decks, {
|
|
34
36
|
slug: segments[0],
|
|
35
37
|
sourcePath: inputPath,
|
|
@@ -47,7 +49,7 @@ function resolveDeckFiles(paths, root = "decks") {
|
|
|
47
49
|
if (deck?.kind === "directory") deck.assetPaths = assetPaths;
|
|
48
50
|
continue;
|
|
49
51
|
}
|
|
50
|
-
if (inputPath.endsWith(".mdx")) throw new Error(`Nested deck slugs are not supported in this slice: ${inputPath}`);
|
|
52
|
+
if (inputPath.endsWith(".mdx") || options.markdown && inputPath.endsWith(".md")) throw new Error(`Nested deck slugs are not supported in this slice: ${inputPath}`);
|
|
51
53
|
}
|
|
52
54
|
return [...decks.values()].map((deck) => ({
|
|
53
55
|
...deck,
|
|
@@ -369,6 +371,11 @@ function collectFrontmatterAssetCandidates(value) {
|
|
|
369
371
|
if (typeof value === "string") return [value.trim()].filter(Boolean);
|
|
370
372
|
return [];
|
|
371
373
|
}
|
|
374
|
+
function isLocalRelativeAssetCandidate(value) {
|
|
375
|
+
if (assetRefType(value)) return false;
|
|
376
|
+
if (value.startsWith("./") || value.startsWith("../")) return true;
|
|
377
|
+
return /^[^/:?#]+\.(?:png|jpe?g|gif|svg|webp)(?:[?#].*)?$/i.test(value);
|
|
378
|
+
}
|
|
372
379
|
function contentTypeForPath(path) {
|
|
373
380
|
const queryIndex = path.indexOf("?");
|
|
374
381
|
const pathname = (queryIndex === -1 ? path : path.slice(0, queryIndex)).toLowerCase();
|
|
@@ -985,8 +992,8 @@ import { configureDecks, defineDecks } from ${JSON.stringify(DECKS_ADVANCED_ENTR
|
|
|
985
992
|
import type { ConfiguredDecks, DecksConfig } from ${runtimeEntry};
|
|
986
993
|
import type { Env } from "hono";
|
|
987
994
|
import { decksClientEntry } from "./client-entry";
|
|
988
|
-
${input.decks.flatMap((deck) => deck.slideModules.map((slide, index) => `import ${slideImportName(
|
|
989
|
-
${input.decks.
|
|
995
|
+
${input.decks.flatMap((deck, deckIndex) => deck.slideModules.map((slide, index) => `import ${slideImportName(deckIndex, index)} from ${JSON.stringify(slide.importPath)};`)).join("\n")}
|
|
996
|
+
${input.decks.flatMap((deck, deckIndex) => deck.componentModulePath ? [`import * as ${componentImportName(deckIndex)} from ${JSON.stringify(deck.componentModulePath)};`] : []).join("\n")}
|
|
990
997
|
|
|
991
998
|
function withClientComponentIds(module, clientIds) {
|
|
992
999
|
const registry = {};
|
|
@@ -1006,7 +1013,7 @@ function withClientComponentIds(module, clientIds) {
|
|
|
1006
1013
|
const generatedDecks = defineDecks({
|
|
1007
1014
|
clientEntryAsset: decksClientEntry,
|
|
1008
1015
|
decks: [
|
|
1009
|
-
${input.decks.map(emitDeckObject).join(",\n")}
|
|
1016
|
+
${input.decks.map((deck, deckIndex) => emitDeckObject(deck, deckIndex)).join(",\n")}
|
|
1010
1017
|
]
|
|
1011
1018
|
});
|
|
1012
1019
|
|
|
@@ -1019,15 +1026,16 @@ function definedDecksFor<E extends Env>() {
|
|
|
1019
1026
|
}
|
|
1020
1027
|
`;
|
|
1021
1028
|
}
|
|
1022
|
-
function emitDeckObject(deck) {
|
|
1029
|
+
function emitDeckObject(deck, deckIndex) {
|
|
1023
1030
|
return ` {
|
|
1024
1031
|
slug: ${JSON.stringify(deck.deck.slug)},
|
|
1025
1032
|
sourcePath: ${JSON.stringify(deck.deck.sourcePath)},
|
|
1026
1033
|
kind: ${JSON.stringify(deck.deck.kind)},
|
|
1027
1034
|
meta: ${serializeValue(deck.deck.meta, 3)},
|
|
1035
|
+
${typeof deck.deck.clientEntryRequired === "boolean" ? ` clientEntryRequired: ${deck.deck.clientEntryRequired},\n` : ""}
|
|
1028
1036
|
${deck.deck.themeStyle ? ` "themeStyle": ${serializeValue(deck.deck.themeStyle, 3)},\n` : ""}${deck.deck.themeSourcePath ? ` "themeSourcePath": ${JSON.stringify(deck.deck.themeSourcePath)},\n` : ""}
|
|
1029
1037
|
assets: ${serializeValue(deck.deck.assets, 3)},
|
|
1030
|
-
componentRegistry: ${deck.componentModulePath ? `withClientComponentIds(${componentImportName(
|
|
1038
|
+
componentRegistry: ${deck.componentModulePath ? `withClientComponentIds(${componentImportName(deckIndex)}, ${serializeValue(deck.clientComponentIds ?? {}, 3)})` : "{}"},
|
|
1031
1039
|
warnings: ${serializeValue(deck.deck.warnings, 3)},
|
|
1032
1040
|
slides: [
|
|
1033
1041
|
${deck.deck.slides.map((slide) => ` {
|
|
@@ -1036,19 +1044,16 @@ ${deck.deck.slides.map((slide) => ` {
|
|
|
1036
1044
|
html: "",
|
|
1037
1045
|
components: [],
|
|
1038
1046
|
notes: ${serializeValue(slide.notes, 5)},
|
|
1039
|
-
render: ${slideImportName(
|
|
1047
|
+
render: ${slideImportName(deckIndex, slide.index)}
|
|
1040
1048
|
}`).join(",\n")}
|
|
1041
1049
|
]
|
|
1042
1050
|
}`;
|
|
1043
1051
|
}
|
|
1044
|
-
function slideImportName(
|
|
1045
|
-
return `Slide_${
|
|
1046
|
-
}
|
|
1047
|
-
function componentImportName(slug) {
|
|
1048
|
-
return `Components_${safeIdentifier$1(slug)}`;
|
|
1052
|
+
function slideImportName(deckIndex, slideIndex) {
|
|
1053
|
+
return `Slide_${deckIndex}_${slideIndex}`;
|
|
1049
1054
|
}
|
|
1050
|
-
function
|
|
1051
|
-
return
|
|
1055
|
+
function componentImportName(deckIndex) {
|
|
1056
|
+
return `Components_${deckIndex}`;
|
|
1052
1057
|
}
|
|
1053
1058
|
function serializeValue(value, depth) {
|
|
1054
1059
|
const indent = " ".repeat(depth);
|
|
@@ -1500,6 +1505,191 @@ function extractDeckPrelude(source) {
|
|
|
1500
1505
|
};
|
|
1501
1506
|
}
|
|
1502
1507
|
//#endregion
|
|
1508
|
+
//#region src/node/markdown-decks.ts
|
|
1509
|
+
async function compileMarkdownDecks(input, adapter) {
|
|
1510
|
+
const markdownEntries = input.decks.filter((entry) => entry.sourcePath.endsWith(".md"));
|
|
1511
|
+
const mdx = await compileMdxModuleDecks({
|
|
1512
|
+
...input,
|
|
1513
|
+
decks: input.decks.filter((entry) => !entry.sourcePath.endsWith(".md"))
|
|
1514
|
+
});
|
|
1515
|
+
if (!markdownEntries.length) return mdx;
|
|
1516
|
+
const decks = [...mdx.decks];
|
|
1517
|
+
const clients = /* @__PURE__ */ new Set();
|
|
1518
|
+
const markdownIslands = /* @__PURE__ */ new Map();
|
|
1519
|
+
for (const entry of markdownEntries) try {
|
|
1520
|
+
const source = await input.readText(entry.sourcePath);
|
|
1521
|
+
const parsed = adapter.read(source);
|
|
1522
|
+
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.");
|
|
1523
|
+
const warnings = [];
|
|
1524
|
+
const meta = toDeckFrontmatter(parsed.frontmatter, warnings);
|
|
1525
|
+
addUnknownFrontmatterWarnings(warnings, meta.meta, "deck");
|
|
1526
|
+
const assets = [...await buildAssetRefs(entry.slug, entry.assetPaths, input), ...buildExternalAssetRefs([
|
|
1527
|
+
...collectMarkdownAssetCandidates(parsed.content),
|
|
1528
|
+
...collectFrontmatterAssetCandidates(meta.assets),
|
|
1529
|
+
...collectFrontmatterAssetCandidates(parsed.frontmatter.background)
|
|
1530
|
+
])];
|
|
1531
|
+
const candidates = [
|
|
1532
|
+
...collectMarkdownAssetCandidates(parsed.content),
|
|
1533
|
+
...collectFrontmatterAssetCandidates(parsed.frontmatter.assets),
|
|
1534
|
+
...collectFrontmatterAssetCandidates(parsed.frontmatter.background)
|
|
1535
|
+
];
|
|
1536
|
+
if (entry.kind === "single-file" && candidates.some(isLocalRelativeAssetCandidate)) throw new Error("Single-file Markdown decks cannot reference local relative assets; use a directory deck.");
|
|
1537
|
+
let clientEntryRequired = false;
|
|
1538
|
+
const slideModules = [];
|
|
1539
|
+
const slides = [];
|
|
1540
|
+
for (const [index, slideSource] of splitMarkdownSlides(parsed.content).entries()) {
|
|
1541
|
+
const prelude = parsed.script ? `<script>\n${parsed.script}\n<\/script>\n` : "";
|
|
1542
|
+
const slide = adapter.read(prelude + slideSource);
|
|
1543
|
+
const path = `${input.outDir}/decks/${entry.slug}/slide-${index}.ts`;
|
|
1544
|
+
const rewritten = rewriteAssetUrls(`${prelude}---\n${JSON.stringify({
|
|
1545
|
+
...parsed.frontmatter,
|
|
1546
|
+
...slide.frontmatter
|
|
1547
|
+
})}\n---\n${slide.content}`, assets);
|
|
1548
|
+
const result = await adapter.compile(rewritten, { sourcePath: resolve(input.cwd ?? process.cwd(), entry.sourcePath) });
|
|
1549
|
+
clientEntryRequired ||= Boolean(result.clientCode || result.islands?.length);
|
|
1550
|
+
if (result.clientCode) clients.add(result.clientCode);
|
|
1551
|
+
for (const island of result.islands ?? []) markdownIslands.set(island.id, island);
|
|
1552
|
+
const slideMeta = toSlideFrontmatter(slide.frontmatter, warnings, {
|
|
1553
|
+
slideIndex: index,
|
|
1554
|
+
fallbackTitle: result.headings[0]?.text,
|
|
1555
|
+
fallbackTransition: meta.transition,
|
|
1556
|
+
fallbackTransitionDuration: meta.transitionDuration,
|
|
1557
|
+
fallbackTransitionEasing: meta.transitionEasing
|
|
1558
|
+
});
|
|
1559
|
+
addUnknownFrontmatterWarnings(warnings, slideMeta.meta, "slide", index);
|
|
1560
|
+
slideModules.push({
|
|
1561
|
+
path,
|
|
1562
|
+
importPath: `./decks/${entry.slug}/slide-${index}`,
|
|
1563
|
+
code: `// @ts-nocheck\n${rewriteRelativeMdxImports(result.code, dirname$1(entry.sourcePath), dirname$1(path))}`
|
|
1564
|
+
});
|
|
1565
|
+
slides.push({
|
|
1566
|
+
index,
|
|
1567
|
+
meta: slideMeta,
|
|
1568
|
+
html: "",
|
|
1569
|
+
components: [],
|
|
1570
|
+
notes: slideMeta.notes
|
|
1571
|
+
});
|
|
1572
|
+
}
|
|
1573
|
+
addExternalAssetWarnings(warnings, assets);
|
|
1574
|
+
const themeStyle = input.themeStyles?.[entry.slug];
|
|
1575
|
+
decks.push({
|
|
1576
|
+
deck: {
|
|
1577
|
+
slug: entry.slug,
|
|
1578
|
+
sourcePath: entry.sourcePath,
|
|
1579
|
+
kind: entry.kind,
|
|
1580
|
+
clientEntryRequired,
|
|
1581
|
+
meta,
|
|
1582
|
+
slides,
|
|
1583
|
+
assets,
|
|
1584
|
+
warnings,
|
|
1585
|
+
themeStyle: themeStyle?.style,
|
|
1586
|
+
themeSourcePath: themeStyle?.sourcePath
|
|
1587
|
+
},
|
|
1588
|
+
slideModules
|
|
1589
|
+
});
|
|
1590
|
+
} catch (error) {
|
|
1591
|
+
throw new CompileError(`Void Markdown compile failed in ${entry.sourcePath}: ${error instanceof Error ? error.message : String(error)}`, "void-markdown-compile-error");
|
|
1592
|
+
}
|
|
1593
|
+
decks.sort((a, b) => a.deck.slug.localeCompare(b.deck.slug));
|
|
1594
|
+
return {
|
|
1595
|
+
decks,
|
|
1596
|
+
routerModule: emitModuleDecksRouter({ decks }),
|
|
1597
|
+
clientCode: [...clients].join("\n"),
|
|
1598
|
+
clientModules: [adapter.client([...markdownIslands.values()])].filter(Boolean),
|
|
1599
|
+
markdownIslands: [...markdownIslands.values()]
|
|
1600
|
+
};
|
|
1601
|
+
}
|
|
1602
|
+
/** Protect fenced code from the deck separator parser. */
|
|
1603
|
+
function splitMarkdownSlides(source) {
|
|
1604
|
+
const fences = [];
|
|
1605
|
+
let prefix = "HONO_DECKS_FENCE_";
|
|
1606
|
+
while (source.includes(prefix)) prefix += "_";
|
|
1607
|
+
const lines = source.replace(/\r\n/g, "\n").split("\n");
|
|
1608
|
+
const protectedLines = [];
|
|
1609
|
+
for (let index = 0; index < lines.length; index++) {
|
|
1610
|
+
const open = /^ {0,3}(`{3,}|~{3,})/.exec(lines[index]);
|
|
1611
|
+
if (/^[ \t]+---[ \t]*$/.test(lines[index])) {
|
|
1612
|
+
protectedLines.push(`${prefix}${fences.push(lines[index]) - 1}_END`);
|
|
1613
|
+
continue;
|
|
1614
|
+
}
|
|
1615
|
+
if (!open) {
|
|
1616
|
+
protectedLines.push(lines[index]);
|
|
1617
|
+
continue;
|
|
1618
|
+
}
|
|
1619
|
+
const block = [lines[index]];
|
|
1620
|
+
const close = new RegExp(`^ {0,3}${open[1][0]}{${open[1].length},}[ \\t]*$`);
|
|
1621
|
+
let closed = false;
|
|
1622
|
+
while (++index < lines.length) {
|
|
1623
|
+
block.push(lines[index]);
|
|
1624
|
+
if (close.test(lines[index])) {
|
|
1625
|
+
closed = true;
|
|
1626
|
+
break;
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
if (!closed) throw new Error("Markdown code fence is not closed.");
|
|
1630
|
+
protectedLines.push(`${prefix}${fences.push(block.join("\n")) - 1}_END`);
|
|
1631
|
+
}
|
|
1632
|
+
return splitSlideSources(protectedLines.join("\n")).map((slide) => slide.replace(new RegExp(`${prefix}(\\d+)_END`, "g"), (_, id) => fences[Number(id)]));
|
|
1633
|
+
}
|
|
1634
|
+
//#endregion
|
|
1635
|
+
//#region src/adapters/hono-markdown.ts
|
|
1636
|
+
/** Node-only bridge. Existing Void configs retain their original compiler/runtime pair. */
|
|
1637
|
+
async function createHonoMarkdownAdapter(cwd, legacy = false, options) {
|
|
1638
|
+
const require = createRequire(resolve(cwd, "package.json"));
|
|
1639
|
+
const entry = legacy ? "void-plugin-hono-jsx/markdown/compiler" : "@ts-76/hono-markdown/compiler";
|
|
1640
|
+
let compiler;
|
|
1641
|
+
try {
|
|
1642
|
+
compiler = await import(pathToFileURL(require.resolve(entry)).href);
|
|
1643
|
+
} catch (cause) {
|
|
1644
|
+
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 });
|
|
1645
|
+
}
|
|
1646
|
+
const read = compiler.readMarkdownModule ?? compiler.readStaticMarkdown;
|
|
1647
|
+
const compile = compiler.compileMarkdownModule ?? compiler.compileStaticMarkdown;
|
|
1648
|
+
if (typeof read !== "function" || typeof compile !== "function") throw new Error("Missing standalone compiler API");
|
|
1649
|
+
const runtime = entry.startsWith("void-plugin") ? "void-plugin-hono-jsx/markdown/client" : "@ts-76/hono-markdown/client";
|
|
1650
|
+
return {
|
|
1651
|
+
read,
|
|
1652
|
+
compile: (source, input) => compile(source, {
|
|
1653
|
+
...input,
|
|
1654
|
+
options
|
|
1655
|
+
}),
|
|
1656
|
+
client: (islands) => emitMarkdownClient(islands, runtime)
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
1659
|
+
function emitMarkdownClient(islands, runtime) {
|
|
1660
|
+
if (!islands.length) return "";
|
|
1661
|
+
const literal = (value) => JSON.stringify(value).replace(/</g, "\\u003c");
|
|
1662
|
+
return `import { hydrateMarkdownIslands } from ${literal(runtime)};
|
|
1663
|
+
hydrateMarkdownIslands({ ${islands.map((island) => `${literal(island.id)}: () => import(${literal(island.modulePath)})`).join(",")} }, {
|
|
1664
|
+
ready(element, start) {
|
|
1665
|
+
const slide = element.closest('section[data-slide-state]');
|
|
1666
|
+
if (!slide || slide.dataset.slideState === 'active') { start(); return; }
|
|
1667
|
+
const observer = new MutationObserver(() => {
|
|
1668
|
+
if (slide.dataset.slideState === 'active') { observer.disconnect(); start(); }
|
|
1669
|
+
});
|
|
1670
|
+
observer.observe(slide, { attributes: true, attributeFilter: ['data-slide-state'] });
|
|
1671
|
+
}
|
|
1672
|
+
});`;
|
|
1673
|
+
}
|
|
1674
|
+
//#endregion
|
|
1675
|
+
//#region src/node/markdown-adapter.ts
|
|
1676
|
+
async function loadMarkdownAdapter(selection, cwd) {
|
|
1677
|
+
if (selection === "void") return createHonoMarkdownAdapter(cwd, true);
|
|
1678
|
+
if (selection.adapter === "hono-decks/adapters/hono-markdown") return createHonoMarkdownAdapter(cwd, false, selection.options);
|
|
1679
|
+
const module = await import(pathToFileURL(createRequire(resolve(cwd, "package.json")).resolve(selection.adapter)).href);
|
|
1680
|
+
if (typeof module.createMarkdownAdapter !== "function") throw new Error("Markdown adapter must export createMarkdownAdapter.");
|
|
1681
|
+
const adapter = await module.createMarkdownAdapter({
|
|
1682
|
+
cwd,
|
|
1683
|
+
options: selection.options
|
|
1684
|
+
});
|
|
1685
|
+
if (![
|
|
1686
|
+
adapter?.read,
|
|
1687
|
+
adapter?.compile,
|
|
1688
|
+
adapter?.client
|
|
1689
|
+
].every((value) => typeof value === "function")) throw new Error("Markdown adapter must provide read, compile and client functions.");
|
|
1690
|
+
return adapter;
|
|
1691
|
+
}
|
|
1692
|
+
//#endregion
|
|
1503
1693
|
//#region src/node/client-entry.ts
|
|
1504
1694
|
async function discoverClientComponentIds(input) {
|
|
1505
1695
|
const result = {};
|
|
@@ -1520,10 +1710,12 @@ async function emitClientEntryModule(input) {
|
|
|
1520
1710
|
registrations.push(`${JSON.stringify(clientId)}: ${localName}`);
|
|
1521
1711
|
}
|
|
1522
1712
|
}
|
|
1523
|
-
|
|
1524
|
-
const
|
|
1713
|
+
const clientModules = input.clientModules ?? [];
|
|
1714
|
+
if (registrations.length === 0 && clientModules.length === 0) return `export const decksClientEntry = ${JSON.stringify(input.extraCode ?? "")};\n`;
|
|
1715
|
+
const result = await build({
|
|
1525
1716
|
stdin: {
|
|
1526
|
-
contents:
|
|
1717
|
+
contents: `${clientModules.join("\n")}
|
|
1718
|
+
import { hydrateSlideIslands } from "hono-decks/client";
|
|
1527
1719
|
${imports.join("\n")}
|
|
1528
1720
|
|
|
1529
1721
|
hydrateSlideIslands({
|
|
@@ -1538,6 +1730,15 @@ hydrateSlideIslands({
|
|
|
1538
1730
|
},
|
|
1539
1731
|
bundle: true,
|
|
1540
1732
|
write: false,
|
|
1733
|
+
outdir: join(input.cwd, ".hono-decks-client"),
|
|
1734
|
+
loader: {
|
|
1735
|
+
".svg": "dataurl",
|
|
1736
|
+
".png": "dataurl",
|
|
1737
|
+
".jpg": "dataurl",
|
|
1738
|
+
".webp": "dataurl",
|
|
1739
|
+
".woff2": "dataurl",
|
|
1740
|
+
".woff": "dataurl"
|
|
1741
|
+
},
|
|
1541
1742
|
format: "esm",
|
|
1542
1743
|
platform: "browser",
|
|
1543
1744
|
target: "es2022",
|
|
@@ -1547,9 +1748,12 @@ hydrateSlideIslands({
|
|
|
1547
1748
|
alias: { "hono-decks/client": resolveClientRuntimeEntry() },
|
|
1548
1749
|
sourcemap: false,
|
|
1549
1750
|
minify: false
|
|
1550
|
-
})
|
|
1751
|
+
});
|
|
1752
|
+
const output = result.outputFiles.find((file) => file.path.endsWith(".js"));
|
|
1753
|
+
const css = result.outputFiles.filter((file) => file.path.endsWith(".css")).map((file) => file.text).join("\n");
|
|
1754
|
+
input.onCss?.(css);
|
|
1551
1755
|
if (!output) throw new Error("Client entry did not produce output.");
|
|
1552
|
-
return `export const decksClientEntry = ${JSON.stringify(output.text)};\n`;
|
|
1756
|
+
return `export const decksClientEntry = ${JSON.stringify(output.text + (input.extraCode ? "\n" + input.extraCode : ""))};\n`;
|
|
1553
1757
|
}
|
|
1554
1758
|
function resolveClientRuntimeEntry() {
|
|
1555
1759
|
const built = fileURLToPath(new URL("./client.js", import.meta.url));
|
|
@@ -1619,6 +1823,63 @@ async function listFiles(cwd, dir) {
|
|
|
1619
1823
|
}))).flat().sort();
|
|
1620
1824
|
}
|
|
1621
1825
|
//#endregion
|
|
1826
|
+
//#region src/node/file-transaction.ts
|
|
1827
|
+
/** Stage every file before publishing; restore the previous files on an I/O error.
|
|
1828
|
+
* Individual renames are atomic. This is rollback on failure, not a cross-process
|
|
1829
|
+
* or crash-atomic filesystem transaction. Failed rollback retains recovery files.
|
|
1830
|
+
*/
|
|
1831
|
+
async function publishGeneratedFiles(files, renameFile = rename) {
|
|
1832
|
+
const staged = [];
|
|
1833
|
+
const published = [];
|
|
1834
|
+
let keepRecovery = false;
|
|
1835
|
+
try {
|
|
1836
|
+
if (new Set(files.map((file) => file.path)).size !== files.length) throw new Error("Generated output paths must be unique.");
|
|
1837
|
+
for (const file of files) {
|
|
1838
|
+
const parent = dirname(file.path);
|
|
1839
|
+
await mkdir(parent, { recursive: true });
|
|
1840
|
+
const dir = await mkdtemp(join(parent, ".hono-decks-"));
|
|
1841
|
+
const entry = {
|
|
1842
|
+
path: file.path,
|
|
1843
|
+
dir,
|
|
1844
|
+
next: join(dir, "next")
|
|
1845
|
+
};
|
|
1846
|
+
staged.push(entry);
|
|
1847
|
+
await writeFile(entry.next, file.content, "utf8");
|
|
1848
|
+
let previous;
|
|
1849
|
+
try {
|
|
1850
|
+
previous = await readFile(file.path);
|
|
1851
|
+
} catch (error) {
|
|
1852
|
+
if (error.code === "ENOENT") continue;
|
|
1853
|
+
throw error;
|
|
1854
|
+
}
|
|
1855
|
+
entry.backup = join(dir, "previous");
|
|
1856
|
+
await writeFile(entry.backup, previous);
|
|
1857
|
+
}
|
|
1858
|
+
for (const entry of staged) {
|
|
1859
|
+
await renameFile(entry.next, entry.path);
|
|
1860
|
+
published.push(entry);
|
|
1861
|
+
}
|
|
1862
|
+
} catch (error) {
|
|
1863
|
+
const failures = [];
|
|
1864
|
+
for (const entry of published.reverse()) try {
|
|
1865
|
+
if (entry.backup) await renameFile(entry.backup, entry.path);
|
|
1866
|
+
else await rm(entry.path, { force: true });
|
|
1867
|
+
} catch (failure) {
|
|
1868
|
+
failures.push(failure);
|
|
1869
|
+
}
|
|
1870
|
+
if (failures.length) {
|
|
1871
|
+
keepRecovery = true;
|
|
1872
|
+
throw new AggregateError([error, ...failures], `Generated output rollback failed. Recovery files remain in: ${staged.map((entry) => entry.dir).join(", ")}`);
|
|
1873
|
+
}
|
|
1874
|
+
throw error;
|
|
1875
|
+
} finally {
|
|
1876
|
+
if (!keepRecovery) await Promise.allSettled(staged.map((entry) => rm(entry.dir, {
|
|
1877
|
+
recursive: true,
|
|
1878
|
+
force: true
|
|
1879
|
+
})));
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
//#endregion
|
|
1622
1883
|
//#region src/node/ogp.ts
|
|
1623
1884
|
const MAX_OGP_BYTES = 256 * 1024;
|
|
1624
1885
|
const MAX_OGP_REDIRECTS = 3;
|
|
@@ -1804,11 +2065,12 @@ function isHttpUrl(value) {
|
|
|
1804
2065
|
}
|
|
1805
2066
|
//#endregion
|
|
1806
2067
|
//#region src/node/compile-decks.ts
|
|
1807
|
-
|
|
2068
|
+
/** Shared by file output and Vite's in-memory module graph. Does not write files. */
|
|
2069
|
+
async function compileDeckModules(input) {
|
|
1808
2070
|
const out = normalizeRelativePath(input.out, "Output directory");
|
|
1809
2071
|
const root = normalizeDeckRoot(input.root);
|
|
1810
2072
|
const paths = await listFiles(input.cwd, join(input.cwd, root));
|
|
1811
|
-
const resolved = resolveDeckFiles(paths, root);
|
|
2073
|
+
const resolved = resolveDeckFiles(paths, root, { markdown: Boolean(input.markdown) });
|
|
1812
2074
|
const componentModulePaths = Object.fromEntries(resolved.flatMap((deck) => {
|
|
1813
2075
|
const path = deck.kind === "directory" ? findComponentModule(paths, root, deck.slug) : void 0;
|
|
1814
2076
|
return path ? [[deck.slug, path]] : [];
|
|
@@ -1836,7 +2098,9 @@ async function compileDecks(input) {
|
|
|
1836
2098
|
refresh: input.refreshOgp,
|
|
1837
2099
|
resolveOgp: input.resolveOgp ?? resolveOgpMetadata
|
|
1838
2100
|
});
|
|
1839
|
-
const
|
|
2101
|
+
const adapter = input.markdown && resolved.some((entry) => entry.sourcePath.endsWith(".md")) ? await loadMarkdownAdapter(input.markdown, input.cwd) : void 0;
|
|
2102
|
+
const generated = await (adapter ? (value) => compileMarkdownDecks(value, adapter) : compileMdxModuleDecks)({
|
|
2103
|
+
cwd: input.cwd,
|
|
1840
2104
|
root,
|
|
1841
2105
|
outDir: out,
|
|
1842
2106
|
mountPath: input.mountPath,
|
|
@@ -1848,20 +2112,57 @@ async function compileDecks(input) {
|
|
|
1848
2112
|
readText: (path) => readFile(join(input.cwd, path), "utf8"),
|
|
1849
2113
|
readBinary: (path) => readFile(join(input.cwd, path))
|
|
1850
2114
|
});
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
await writeTextFile(join(input.cwd, out, "client-entry.ts"), await emitClientEntryModule({
|
|
2115
|
+
let clientCss = "";
|
|
2116
|
+
const clientEntry = await emitClientEntryModule({
|
|
1854
2117
|
cwd: input.cwd,
|
|
1855
2118
|
clientEntries: clientEntryPaths,
|
|
1856
|
-
clientComponentIds
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
2119
|
+
clientComponentIds,
|
|
2120
|
+
extraCode: generated.clientCode,
|
|
2121
|
+
clientModules: generated.clientModules,
|
|
2122
|
+
onCss: (css) => {
|
|
2123
|
+
clientCss = css;
|
|
2124
|
+
}
|
|
2125
|
+
});
|
|
2126
|
+
if (clientCss) {
|
|
2127
|
+
for (const deck of generated.decks) if (deck.deck.clientEntryRequired || Object.keys(deck.clientComponentIds ?? {}).length) deck.deck.themeStyle = [deck.deck.themeStyle, clientCss].filter(Boolean).join("\n");
|
|
2128
|
+
generated.routerModule = emitModuleDecksRouter({ decks: generated.decks });
|
|
2129
|
+
}
|
|
2130
|
+
const files = generated.decks.flatMap((deck) => deck.slideModules.map((slide) => ({
|
|
2131
|
+
path: join(input.cwd, slide.path),
|
|
2132
|
+
content: slide.code
|
|
2133
|
+
})));
|
|
2134
|
+
files.push({
|
|
2135
|
+
path: join(input.cwd, out, "decks.ts"),
|
|
2136
|
+
content: generated.routerModule
|
|
2137
|
+
}, {
|
|
2138
|
+
path: join(input.cwd, out, "client-entry.ts"),
|
|
2139
|
+
content: clientEntry
|
|
2140
|
+
});
|
|
2141
|
+
return {
|
|
2142
|
+
modules: files,
|
|
2143
|
+
cacheFile: ogpCache.output(),
|
|
2144
|
+
result: {
|
|
2145
|
+
outDir: out,
|
|
2146
|
+
routerModulePath: `${out}/decks.ts`,
|
|
2147
|
+
decks: generated.decks.map(({ deck, slideModules }) => {
|
|
2148
|
+
const { slides, componentRegistry: _componentRegistry, ...metadata } = deck;
|
|
2149
|
+
return {
|
|
2150
|
+
...metadata,
|
|
2151
|
+
slides: slides.map((slide, index) => ({
|
|
2152
|
+
index: slide.index,
|
|
2153
|
+
meta: slide.meta,
|
|
2154
|
+
notes: slide.notes,
|
|
2155
|
+
modulePath: slideModules[index].path
|
|
2156
|
+
}))
|
|
2157
|
+
};
|
|
2158
|
+
})
|
|
2159
|
+
}
|
|
2160
|
+
};
|
|
1860
2161
|
}
|
|
1861
2162
|
async function createOgpCacheResolver(input) {
|
|
1862
2163
|
if (!input.cacheFile) return {
|
|
1863
2164
|
resolveOgp: input.resolveOgp,
|
|
1864
|
-
|
|
2165
|
+
output: () => void 0
|
|
1865
2166
|
};
|
|
1866
2167
|
const cachePath = join(input.cwd, normalizeRelativePath(input.cacheFile, "OGP cache file"));
|
|
1867
2168
|
const cache = await readOgpCache(cachePath);
|
|
@@ -1877,9 +2178,11 @@ async function createOgpCacheResolver(input) {
|
|
|
1877
2178
|
}
|
|
1878
2179
|
return metadata;
|
|
1879
2180
|
},
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
2181
|
+
output() {
|
|
2182
|
+
return dirty ? {
|
|
2183
|
+
path: cachePath,
|
|
2184
|
+
content: `${JSON.stringify(cache, null, 2)}\n`
|
|
2185
|
+
} : void 0;
|
|
1883
2186
|
}
|
|
1884
2187
|
};
|
|
1885
2188
|
}
|
|
@@ -1909,10 +2212,6 @@ async function readOgpCache(path) {
|
|
|
1909
2212
|
function isNodeError(error) {
|
|
1910
2213
|
return error instanceof Error && "code" in error;
|
|
1911
2214
|
}
|
|
1912
|
-
async function writeTextFile(path, contents) {
|
|
1913
|
-
await mkdir(dirname(path), { recursive: true });
|
|
1914
|
-
await writeFile(path, contents, "utf8");
|
|
1915
|
-
}
|
|
1916
2215
|
function findComponentModule(paths, root, slug) {
|
|
1917
2216
|
const base = `${normalizePath(root).replace(/\/$/, "")}/${slug}/components/index`;
|
|
1918
2217
|
return paths.find((path) => {
|
|
@@ -1952,7 +2251,7 @@ function findDeckThemeStyle(paths, root, slug) {
|
|
|
1952
2251
|
async function loadDecksConfig(input) {
|
|
1953
2252
|
const path = resolve(input.cwd, input.configFile ?? "hono-decks.config.ts");
|
|
1954
2253
|
if (!existsSync(path)) throw new Error(`Config file not found: ${input.configFile ?? "hono-decks.config.ts"}. Run \`hono-decks init\` first.`);
|
|
1955
|
-
const
|
|
2254
|
+
const result = await build({
|
|
1956
2255
|
absWorkingDir: input.cwd,
|
|
1957
2256
|
entryPoints: [path],
|
|
1958
2257
|
bundle: true,
|
|
@@ -1960,16 +2259,20 @@ async function loadDecksConfig(input) {
|
|
|
1960
2259
|
platform: "node",
|
|
1961
2260
|
target: "node20",
|
|
1962
2261
|
write: false,
|
|
2262
|
+
metafile: true,
|
|
1963
2263
|
logLevel: "silent",
|
|
1964
2264
|
alias: { "hono-decks": resolveRuntimeEntry() }
|
|
1965
|
-
})
|
|
2265
|
+
});
|
|
2266
|
+
const source = result.outputFiles[0]?.text;
|
|
1966
2267
|
if (!source) throw new Error(`Could not load config file: ${path}`);
|
|
1967
2268
|
const config = (await import(`data:text/javascript;charset=utf-8,${encodeURIComponent(source)}#${Date.now()}`)).default;
|
|
1968
2269
|
if (!config || typeof config !== "object") throw new Error(`Config must have a default export: ${path}`);
|
|
1969
2270
|
if (typeof config.mountPath !== "string") throw new Error(`Config mountPath must be a string: ${path}`);
|
|
2271
|
+
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.");
|
|
1970
2272
|
return {
|
|
1971
2273
|
path,
|
|
1972
2274
|
config,
|
|
2275
|
+
dependencies: Object.keys(result.metafile.inputs).map((inputPath) => resolve(input.cwd, inputPath)),
|
|
1973
2276
|
root: config.build?.root ?? "decks",
|
|
1974
2277
|
outDir: config.build?.outDir ?? "src/generated",
|
|
1975
2278
|
ogpCacheFile: config.build?.ogpCacheFile
|
|
@@ -1992,6 +2295,7 @@ function resolveRuntimeEntry() {
|
|
|
1992
2295
|
function honoDecks(options = {}) {
|
|
1993
2296
|
let projectRoot = process.cwd();
|
|
1994
2297
|
let configPath = resolve(projectRoot, options.configFile ?? "hono-decks.config.ts");
|
|
2298
|
+
let configDependencies = /* @__PURE__ */ new Set();
|
|
1995
2299
|
let deckRoot = resolve(projectRoot, "decks");
|
|
1996
2300
|
let resolvedConfig;
|
|
1997
2301
|
let server;
|
|
@@ -1999,9 +2303,17 @@ function honoDecks(options = {}) {
|
|
|
1999
2303
|
let timer;
|
|
2000
2304
|
let compiling = false;
|
|
2001
2305
|
let queued = false;
|
|
2306
|
+
let compiledMountPath;
|
|
2307
|
+
let mountChanged = false;
|
|
2308
|
+
let modules = /* @__PURE__ */ new Map();
|
|
2309
|
+
let routerModulePath = "";
|
|
2002
2310
|
const syncWatchedPaths = async () => {
|
|
2003
2311
|
if (!server) return;
|
|
2004
|
-
const next = /* @__PURE__ */ new Set([
|
|
2312
|
+
const next = /* @__PURE__ */ new Set([
|
|
2313
|
+
deckRoot,
|
|
2314
|
+
configPath,
|
|
2315
|
+
...configDependencies
|
|
2316
|
+
]);
|
|
2005
2317
|
const removed = [...watchedPaths].filter((path) => !next.has(path));
|
|
2006
2318
|
if (removed.length > 0) server.watcher.unwatch(removed);
|
|
2007
2319
|
server.watcher.add([...next]);
|
|
@@ -2012,18 +2324,26 @@ function honoDecks(options = {}) {
|
|
|
2012
2324
|
cwd: projectRoot,
|
|
2013
2325
|
configFile: options.configFile
|
|
2014
2326
|
});
|
|
2015
|
-
|
|
2327
|
+
configDependencies = new Set(loaded.dependencies);
|
|
2328
|
+
await syncWatchedPaths();
|
|
2329
|
+
const compiled = await compileDeckModules({
|
|
2016
2330
|
cwd: projectRoot,
|
|
2017
2331
|
root: loaded.root,
|
|
2018
2332
|
out: loaded.outDir,
|
|
2019
2333
|
mountPath: loaded.config.mountPath,
|
|
2020
2334
|
ogpCacheFile: loaded.ogpCacheFile,
|
|
2335
|
+
markdown: loaded.config.build?.markdown,
|
|
2021
2336
|
refreshOgp: options.refreshOgp
|
|
2022
2337
|
});
|
|
2338
|
+
if (compiled.cacheFile) await publishGeneratedFiles([compiled.cacheFile]);
|
|
2339
|
+
modules = new Map(compiled.modules.map((file) => [resolve(file.path), file.content]));
|
|
2340
|
+
routerModulePath = resolve(projectRoot, compiled.result.routerModulePath);
|
|
2341
|
+
mountChanged ||= compiledMountPath !== void 0 && compiledMountPath !== loaded.config.mountPath;
|
|
2342
|
+
compiledMountPath = loaded.config.mountPath;
|
|
2023
2343
|
configPath = loaded.path;
|
|
2024
2344
|
deckRoot = resolve(projectRoot, loaded.root);
|
|
2025
2345
|
await syncWatchedPaths();
|
|
2026
|
-
resolvedConfig?.logger.info(`[hono-decks] Compiled ${
|
|
2346
|
+
resolvedConfig?.logger.info(`[hono-decks] Compiled ${compiled.result.decks.length} decks as virtual modules`);
|
|
2027
2347
|
};
|
|
2028
2348
|
const compile = async (fatal) => {
|
|
2029
2349
|
if (compiling) {
|
|
@@ -2044,7 +2364,18 @@ function honoDecks(options = {}) {
|
|
|
2044
2364
|
resolvedConfig?.logger.error(`[hono-decks] ${error instanceof Error ? error.message : String(error)}`);
|
|
2045
2365
|
}
|
|
2046
2366
|
} while (queued);
|
|
2047
|
-
if (latestCompileSucceeded && server)
|
|
2367
|
+
if (latestCompileSucceeded && server) {
|
|
2368
|
+
if (mountChanged) {
|
|
2369
|
+
mountChanged = false;
|
|
2370
|
+
await server.restart();
|
|
2371
|
+
return;
|
|
2372
|
+
}
|
|
2373
|
+
for (const environment of Object.values(server.environments)) {
|
|
2374
|
+
environment.moduleGraph.invalidateAll();
|
|
2375
|
+
if (environment.name !== "client") environment.hot.send({ type: "full-reload" });
|
|
2376
|
+
}
|
|
2377
|
+
server.ws.send({ type: "full-reload" });
|
|
2378
|
+
}
|
|
2048
2379
|
} finally {
|
|
2049
2380
|
compiling = false;
|
|
2050
2381
|
}
|
|
@@ -2056,14 +2387,56 @@ function honoDecks(options = {}) {
|
|
|
2056
2387
|
const onWatcherEvent = (event, path) => {
|
|
2057
2388
|
if (event !== "add" && event !== "change" && event !== "unlink") return;
|
|
2058
2389
|
const absolutePath = resolve(projectRoot, path);
|
|
2059
|
-
if (absolutePath === configPath || isWithin(deckRoot, absolutePath)) scheduleCompile();
|
|
2390
|
+
if (absolutePath === configPath || configDependencies.has(absolutePath) || isWithin(deckRoot, absolutePath)) scheduleCompile();
|
|
2060
2391
|
};
|
|
2061
2392
|
return {
|
|
2062
2393
|
name: "hono-decks",
|
|
2063
2394
|
enforce: "pre",
|
|
2395
|
+
async resolveId(id, importer) {
|
|
2396
|
+
if (id === "virtual:hono-decks") return VIRTUAL_ENTRY;
|
|
2397
|
+
if (id.startsWith(PUBLIC_MODULE_PREFIX)) {
|
|
2398
|
+
const path = decodeURIComponent(id.slice(PUBLIC_MODULE_PREFIX.length));
|
|
2399
|
+
return modules.has(path) ? MODULE_PREFIX + path : null;
|
|
2400
|
+
}
|
|
2401
|
+
if (importer?.startsWith(MODULE_PREFIX)) {
|
|
2402
|
+
const parent = importer.slice(MODULE_PREFIX.length);
|
|
2403
|
+
if (id.startsWith(".")) {
|
|
2404
|
+
const path = resolve(dirname(parent), id);
|
|
2405
|
+
const generated = findModule(modules, path);
|
|
2406
|
+
if (generated) return MODULE_PREFIX + generated;
|
|
2407
|
+
return this.resolve(path, void 0, { skipSelf: true });
|
|
2408
|
+
}
|
|
2409
|
+
return this.resolve(id, resolve(projectRoot, "package.json"), { skipSelf: true });
|
|
2410
|
+
}
|
|
2411
|
+
if (id.startsWith(".") || id.startsWith("/")) {
|
|
2412
|
+
const path = id.startsWith(".") ? resolve(importer ? dirname(importer.split("?")[0]) : projectRoot, id) : id;
|
|
2413
|
+
const generated = findModule(modules, path);
|
|
2414
|
+
if (generated) return MODULE_PREFIX + generated;
|
|
2415
|
+
}
|
|
2416
|
+
return null;
|
|
2417
|
+
},
|
|
2418
|
+
async load(id) {
|
|
2419
|
+
if (id === VIRTUAL_ENTRY) return `import config from ${JSON.stringify(configPath)};
|
|
2420
|
+
import { createDecks } from ${JSON.stringify(PUBLIC_MODULE_PREFIX + encodeURIComponent(routerModulePath))};
|
|
2421
|
+
export { createDecks };
|
|
2422
|
+
export const decks = createDecks(config);`;
|
|
2423
|
+
if (!id.startsWith(MODULE_PREFIX)) return null;
|
|
2424
|
+
const path = id.slice(MODULE_PREFIX.length);
|
|
2425
|
+
const code = modules.get(path);
|
|
2426
|
+
if (code === void 0) return null;
|
|
2427
|
+
return (await transform(code, {
|
|
2428
|
+
loader: "ts",
|
|
2429
|
+
format: "esm",
|
|
2430
|
+
sourcefile: path,
|
|
2431
|
+
sourcemap: "inline"
|
|
2432
|
+
})).code;
|
|
2433
|
+
},
|
|
2434
|
+
config() {
|
|
2435
|
+
return { server: { watch: { ignored: [/(?:^|[/\\])\.hono-decks-[^/\\]+(?:[/\\]|$)/] } } };
|
|
2436
|
+
},
|
|
2064
2437
|
async configResolved(config) {
|
|
2065
2438
|
resolvedConfig = config;
|
|
2066
|
-
projectRoot = config.root;
|
|
2439
|
+
projectRoot = config.resolve?.preserveSymlinks ? config.root : await realpath(config.root);
|
|
2067
2440
|
configPath = resolve(projectRoot, options.configFile ?? "hono-decks.config.ts");
|
|
2068
2441
|
await compile(true);
|
|
2069
2442
|
},
|
|
@@ -2081,9 +2454,24 @@ function honoDecks(options = {}) {
|
|
|
2081
2454
|
}
|
|
2082
2455
|
};
|
|
2083
2456
|
}
|
|
2457
|
+
const VIRTUAL_ENTRY = "\0hono-decks:entry";
|
|
2458
|
+
const PUBLIC_MODULE_PREFIX = "virtual:hono-decks/module/";
|
|
2459
|
+
const MODULE_PREFIX = "\0hono-decks:module:";
|
|
2460
|
+
function findModule(modules, path) {
|
|
2461
|
+
return [
|
|
2462
|
+
path,
|
|
2463
|
+
path.replace(/\.js$/, ".ts"),
|
|
2464
|
+
path + ".ts",
|
|
2465
|
+
path + "/index.ts"
|
|
2466
|
+
].find((candidate) => modules.has(candidate));
|
|
2467
|
+
}
|
|
2084
2468
|
function isWithin(root, path) {
|
|
2085
2469
|
const child = relative(root, path);
|
|
2086
2470
|
return child === "" || child !== ".." && !child.startsWith("../") && !child.startsWith("..\\");
|
|
2087
2471
|
}
|
|
2472
|
+
/** Add deck compilation to an existing object, promise, or config function. */
|
|
2473
|
+
function withHonoDecks(config, options = {}) {
|
|
2474
|
+
return async (env) => mergeConfig(await (typeof config === "function" ? config(env) : config), { plugins: [honoDecks(options)] });
|
|
2475
|
+
}
|
|
2088
2476
|
//#endregion
|
|
2089
|
-
export { honoDecks };
|
|
2477
|
+
export { honoDecks, withHonoDecks };
|