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/bin.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { i as normalizeRelativePath, n as normalizeDeckRoot, r as normalizePath$4, t as publishGeneratedFiles } from "./file-transaction-NjDxX6Gt.js";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
2
4
|
import { existsSync, watch } from "node:fs";
|
|
3
5
|
import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
|
|
4
6
|
import { join, relative, resolve } from "node:path";
|
|
@@ -9,28 +11,28 @@ import remarkParse from "remark-parse";
|
|
|
9
11
|
import { compile } from "@mdx-js/mdx";
|
|
10
12
|
import remarkDirective from "remark-directive";
|
|
11
13
|
import { codeToHtml } from "shiki";
|
|
12
|
-
import { fileURLToPath } from "node:url";
|
|
14
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
13
15
|
import { build } from "esbuild";
|
|
14
16
|
import { lookup } from "node:dns/promises";
|
|
15
17
|
import { isIP } from "node:net";
|
|
16
18
|
//#region src/routing/file-routing.ts
|
|
17
|
-
function resolveDeckFiles(paths, root = "decks") {
|
|
18
|
-
const normalizedRoot = normalizePath$
|
|
19
|
+
function resolveDeckFiles(paths, root = "decks", options = {}) {
|
|
20
|
+
const normalizedRoot = normalizePath$3(root).replace(/\/$/, "");
|
|
19
21
|
const decks = /* @__PURE__ */ new Map();
|
|
20
22
|
const assetsBySlug = /* @__PURE__ */ new Map();
|
|
21
|
-
for (const inputPath of paths.map(normalizePath$
|
|
23
|
+
for (const inputPath of paths.map(normalizePath$3).sort()) {
|
|
22
24
|
assertInsideRoot(inputPath, normalizedRoot);
|
|
23
25
|
const segments = inputPath.slice(normalizedRoot.length + 1).split("/");
|
|
24
|
-
if (segments.length === 1 && segments[0].endsWith(".mdx")) {
|
|
26
|
+
if (segments.length === 1 && (segments[0].endsWith(".mdx") || options.markdown && segments[0].endsWith(".md"))) {
|
|
25
27
|
addDeck(decks, {
|
|
26
|
-
slug: segments[0].replace(/\.mdx
|
|
28
|
+
slug: segments[0].replace(/\.mdx?$/, ""),
|
|
27
29
|
sourcePath: inputPath,
|
|
28
30
|
kind: "single-file",
|
|
29
31
|
assetPaths: []
|
|
30
32
|
});
|
|
31
33
|
continue;
|
|
32
34
|
}
|
|
33
|
-
if (segments.length === 2 && segments[1] === "deck.mdx") {
|
|
35
|
+
if (segments.length === 2 && (segments[1] === "deck.mdx" || options.markdown && segments[1] === "deck.md")) {
|
|
34
36
|
addDeck(decks, {
|
|
35
37
|
slug: segments[0],
|
|
36
38
|
sourcePath: inputPath,
|
|
@@ -48,7 +50,7 @@ function resolveDeckFiles(paths, root = "decks") {
|
|
|
48
50
|
if (deck?.kind === "directory") deck.assetPaths = assetPaths;
|
|
49
51
|
continue;
|
|
50
52
|
}
|
|
51
|
-
if (inputPath.endsWith(".mdx")) throw new Error(`Nested deck slugs are not supported in this slice: ${inputPath}`);
|
|
53
|
+
if (inputPath.endsWith(".mdx") || options.markdown && inputPath.endsWith(".md")) throw new Error(`Nested deck slugs are not supported in this slice: ${inputPath}`);
|
|
52
54
|
}
|
|
53
55
|
return [...decks.values()].map((deck) => ({
|
|
54
56
|
...deck,
|
|
@@ -64,7 +66,7 @@ function assertInsideRoot(path, root) {
|
|
|
64
66
|
if (path.includes("/../") || path.endsWith("/..") || path.startsWith("../")) throw new Error(`Deck path escapes the root: ${path}`);
|
|
65
67
|
if (path !== root && !path.startsWith(`${root}/`)) throw new Error(`Deck path is outside ${root}: ${path}`);
|
|
66
68
|
}
|
|
67
|
-
function normalizePath$
|
|
69
|
+
function normalizePath$3(path) {
|
|
68
70
|
return path.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
|
|
69
71
|
}
|
|
70
72
|
//#endregion
|
|
@@ -370,6 +372,11 @@ function collectFrontmatterAssetCandidates(value) {
|
|
|
370
372
|
if (typeof value === "string") return [value.trim()].filter(Boolean);
|
|
371
373
|
return [];
|
|
372
374
|
}
|
|
375
|
+
function isLocalRelativeAssetCandidate(value) {
|
|
376
|
+
if (assetRefType(value)) return false;
|
|
377
|
+
if (value.startsWith("./") || value.startsWith("../")) return true;
|
|
378
|
+
return /^[^/:?#]+\.(?:png|jpe?g|gif|svg|webp)(?:[?#].*)?$/i.test(value);
|
|
379
|
+
}
|
|
373
380
|
function contentTypeForPath(path) {
|
|
374
381
|
const queryIndex = path.indexOf("?");
|
|
375
382
|
const pathname = (queryIndex === -1 ? path : path.slice(0, queryIndex)).toLowerCase();
|
|
@@ -917,7 +924,7 @@ function rewriteAssetUrls(source, assets) {
|
|
|
917
924
|
}
|
|
918
925
|
function rewriteRelativeMdxImports(source, sourceDir, generatedDir) {
|
|
919
926
|
return source.replace(/(from\s+|import\s+)(["'])(\.[^"']+)\2/g, (_match, prefix, quote, specifier) => {
|
|
920
|
-
return `${prefix}${quote}${toRelativeImportPath(generatedDir, normalizePath$
|
|
927
|
+
return `${prefix}${quote}${toRelativeImportPath(generatedDir, normalizePath$2(`${sourceDir}/${specifier}`))}${quote}`;
|
|
921
928
|
});
|
|
922
929
|
}
|
|
923
930
|
function componentImportPath(outDir, sourcePath) {
|
|
@@ -925,22 +932,22 @@ function componentImportPath(outDir, sourcePath) {
|
|
|
925
932
|
return toRelativeImportPath(outDir, sourcePath.replace(/\/index\.(tsx|ts|jsx|js)$/, ""));
|
|
926
933
|
}
|
|
927
934
|
function dirname$2(path) {
|
|
928
|
-
const normalized = normalizePath$
|
|
935
|
+
const normalized = normalizePath$2(path);
|
|
929
936
|
const index = normalized.lastIndexOf("/");
|
|
930
937
|
return index === -1 ? "." : normalized.slice(0, index);
|
|
931
938
|
}
|
|
932
939
|
function localAssetRelativePath(sourcePath) {
|
|
933
940
|
const marker = "/assets/";
|
|
934
|
-
const normalized = normalizePath$
|
|
941
|
+
const normalized = normalizePath$2(sourcePath);
|
|
935
942
|
const markerIndex = normalized.indexOf(marker);
|
|
936
943
|
return markerIndex === -1 ? normalized.split("/").at(-1) ?? normalized : normalized.slice(markerIndex + 8);
|
|
937
944
|
}
|
|
938
|
-
function normalizePath$
|
|
945
|
+
function normalizePath$2(path) {
|
|
939
946
|
return path.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
|
|
940
947
|
}
|
|
941
948
|
function toRelativeImportPath(fromDir, target) {
|
|
942
|
-
const fromParts = normalizePath$
|
|
943
|
-
const targetParts = normalizePath$
|
|
949
|
+
const fromParts = normalizePath$2(fromDir).split("/").filter(Boolean);
|
|
950
|
+
const targetParts = normalizePath$2(target).split("/").filter(Boolean);
|
|
944
951
|
while (fromParts.length > 0 && targetParts.length > 0 && fromParts[0] === targetParts[0]) {
|
|
945
952
|
fromParts.shift();
|
|
946
953
|
targetParts.shift();
|
|
@@ -963,14 +970,14 @@ async function buildAssetRefs(slug, assetPaths, input) {
|
|
|
963
970
|
})));
|
|
964
971
|
}
|
|
965
972
|
function assetName(sourcePath, root, slug) {
|
|
966
|
-
const normalizedPath = normalizePath$
|
|
967
|
-
const prefix = `${normalizePath$
|
|
973
|
+
const normalizedPath = normalizePath$1(sourcePath);
|
|
974
|
+
const prefix = `${normalizePath$1(root).replace(/\/$/, "")}/${slug}/assets/`;
|
|
968
975
|
return (normalizedPath.startsWith(prefix) ? normalizedPath.slice(prefix.length) : normalizedPath.split("/").at(-1) ?? normalizedPath).split("/").map(encodeURIComponent).join("/");
|
|
969
976
|
}
|
|
970
977
|
function normalizeMountPath(value) {
|
|
971
978
|
return (value.startsWith("/") ? value : `/${value}`).replace(/\/$/, "");
|
|
972
979
|
}
|
|
973
|
-
function normalizePath$
|
|
980
|
+
function normalizePath$1(path) {
|
|
974
981
|
return path.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
|
|
975
982
|
}
|
|
976
983
|
//#endregion
|
|
@@ -986,8 +993,8 @@ import { configureDecks, defineDecks } from ${JSON.stringify(DECKS_ADVANCED_ENTR
|
|
|
986
993
|
import type { ConfiguredDecks, DecksConfig } from ${runtimeEntry};
|
|
987
994
|
import type { Env } from "hono";
|
|
988
995
|
import { decksClientEntry } from "./client-entry";
|
|
989
|
-
${input.decks.flatMap((deck) => deck.slideModules.map((slide, index) => `import ${slideImportName(
|
|
990
|
-
${input.decks.
|
|
996
|
+
${input.decks.flatMap((deck, deckIndex) => deck.slideModules.map((slide, index) => `import ${slideImportName(deckIndex, index)} from ${JSON.stringify(slide.importPath)};`)).join("\n")}
|
|
997
|
+
${input.decks.flatMap((deck, deckIndex) => deck.componentModulePath ? [`import * as ${componentImportName(deckIndex)} from ${JSON.stringify(deck.componentModulePath)};`] : []).join("\n")}
|
|
991
998
|
|
|
992
999
|
function withClientComponentIds(module, clientIds) {
|
|
993
1000
|
const registry = {};
|
|
@@ -1007,7 +1014,7 @@ function withClientComponentIds(module, clientIds) {
|
|
|
1007
1014
|
const generatedDecks = defineDecks({
|
|
1008
1015
|
clientEntryAsset: decksClientEntry,
|
|
1009
1016
|
decks: [
|
|
1010
|
-
${input.decks.map(emitDeckObject).join(",\n")}
|
|
1017
|
+
${input.decks.map((deck, deckIndex) => emitDeckObject(deck, deckIndex)).join(",\n")}
|
|
1011
1018
|
]
|
|
1012
1019
|
});
|
|
1013
1020
|
|
|
@@ -1020,15 +1027,16 @@ function definedDecksFor<E extends Env>() {
|
|
|
1020
1027
|
}
|
|
1021
1028
|
`;
|
|
1022
1029
|
}
|
|
1023
|
-
function emitDeckObject(deck) {
|
|
1030
|
+
function emitDeckObject(deck, deckIndex) {
|
|
1024
1031
|
return ` {
|
|
1025
1032
|
slug: ${JSON.stringify(deck.deck.slug)},
|
|
1026
1033
|
sourcePath: ${JSON.stringify(deck.deck.sourcePath)},
|
|
1027
1034
|
kind: ${JSON.stringify(deck.deck.kind)},
|
|
1028
1035
|
meta: ${serializeValue(deck.deck.meta, 3)},
|
|
1036
|
+
${typeof deck.deck.clientEntryRequired === "boolean" ? ` clientEntryRequired: ${deck.deck.clientEntryRequired},\n` : ""}
|
|
1029
1037
|
${deck.deck.themeStyle ? ` "themeStyle": ${serializeValue(deck.deck.themeStyle, 3)},\n` : ""}${deck.deck.themeSourcePath ? ` "themeSourcePath": ${JSON.stringify(deck.deck.themeSourcePath)},\n` : ""}
|
|
1030
1038
|
assets: ${serializeValue(deck.deck.assets, 3)},
|
|
1031
|
-
componentRegistry: ${deck.componentModulePath ? `withClientComponentIds(${componentImportName(
|
|
1039
|
+
componentRegistry: ${deck.componentModulePath ? `withClientComponentIds(${componentImportName(deckIndex)}, ${serializeValue(deck.clientComponentIds ?? {}, 3)})` : "{}"},
|
|
1032
1040
|
warnings: ${serializeValue(deck.deck.warnings, 3)},
|
|
1033
1041
|
slides: [
|
|
1034
1042
|
${deck.deck.slides.map((slide) => ` {
|
|
@@ -1037,19 +1045,16 @@ ${deck.deck.slides.map((slide) => ` {
|
|
|
1037
1045
|
html: "",
|
|
1038
1046
|
components: [],
|
|
1039
1047
|
notes: ${serializeValue(slide.notes, 5)},
|
|
1040
|
-
render: ${slideImportName(
|
|
1048
|
+
render: ${slideImportName(deckIndex, slide.index)}
|
|
1041
1049
|
}`).join(",\n")}
|
|
1042
1050
|
]
|
|
1043
1051
|
}`;
|
|
1044
1052
|
}
|
|
1045
|
-
function slideImportName(
|
|
1046
|
-
return `Slide_${
|
|
1053
|
+
function slideImportName(deckIndex, slideIndex) {
|
|
1054
|
+
return `Slide_${deckIndex}_${slideIndex}`;
|
|
1047
1055
|
}
|
|
1048
|
-
function componentImportName(
|
|
1049
|
-
return `Components_${
|
|
1050
|
-
}
|
|
1051
|
-
function safeIdentifier$1(value) {
|
|
1052
|
-
return value.replace(/[^A-Za-z0-9_$]+/g, "_").replace(/^[^A-Za-z_$]+/, "_") || "_";
|
|
1056
|
+
function componentImportName(deckIndex) {
|
|
1057
|
+
return `Components_${deckIndex}`;
|
|
1053
1058
|
}
|
|
1054
1059
|
function serializeValue(value, depth) {
|
|
1055
1060
|
const indent = " ".repeat(depth);
|
|
@@ -1501,6 +1506,191 @@ function extractDeckPrelude(source) {
|
|
|
1501
1506
|
};
|
|
1502
1507
|
}
|
|
1503
1508
|
//#endregion
|
|
1509
|
+
//#region src/node/markdown-decks.ts
|
|
1510
|
+
async function compileMarkdownDecks(input, adapter) {
|
|
1511
|
+
const markdownEntries = input.decks.filter((entry) => entry.sourcePath.endsWith(".md"));
|
|
1512
|
+
const mdx = await compileMdxModuleDecks({
|
|
1513
|
+
...input,
|
|
1514
|
+
decks: input.decks.filter((entry) => !entry.sourcePath.endsWith(".md"))
|
|
1515
|
+
});
|
|
1516
|
+
if (!markdownEntries.length) return mdx;
|
|
1517
|
+
const decks = [...mdx.decks];
|
|
1518
|
+
const clients = /* @__PURE__ */ new Set();
|
|
1519
|
+
const markdownIslands = /* @__PURE__ */ new Map();
|
|
1520
|
+
for (const entry of markdownEntries) try {
|
|
1521
|
+
const source = await input.readText(entry.sourcePath);
|
|
1522
|
+
const parsed = adapter.read(source);
|
|
1523
|
+
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.");
|
|
1524
|
+
const warnings = [];
|
|
1525
|
+
const meta = toDeckFrontmatter(parsed.frontmatter, warnings);
|
|
1526
|
+
addUnknownFrontmatterWarnings(warnings, meta.meta, "deck");
|
|
1527
|
+
const assets = [...await buildAssetRefs(entry.slug, entry.assetPaths, input), ...buildExternalAssetRefs([
|
|
1528
|
+
...collectMarkdownAssetCandidates(parsed.content),
|
|
1529
|
+
...collectFrontmatterAssetCandidates(meta.assets),
|
|
1530
|
+
...collectFrontmatterAssetCandidates(parsed.frontmatter.background)
|
|
1531
|
+
])];
|
|
1532
|
+
const candidates = [
|
|
1533
|
+
...collectMarkdownAssetCandidates(parsed.content),
|
|
1534
|
+
...collectFrontmatterAssetCandidates(parsed.frontmatter.assets),
|
|
1535
|
+
...collectFrontmatterAssetCandidates(parsed.frontmatter.background)
|
|
1536
|
+
];
|
|
1537
|
+
if (entry.kind === "single-file" && candidates.some(isLocalRelativeAssetCandidate)) throw new Error("Single-file Markdown decks cannot reference local relative assets; use a directory deck.");
|
|
1538
|
+
let clientEntryRequired = false;
|
|
1539
|
+
const slideModules = [];
|
|
1540
|
+
const slides = [];
|
|
1541
|
+
for (const [index, slideSource] of splitMarkdownSlides(parsed.content).entries()) {
|
|
1542
|
+
const prelude = parsed.script ? `<script>\n${parsed.script}\n<\/script>\n` : "";
|
|
1543
|
+
const slide = adapter.read(prelude + slideSource);
|
|
1544
|
+
const path = `${input.outDir}/decks/${entry.slug}/slide-${index}.ts`;
|
|
1545
|
+
const rewritten = rewriteAssetUrls(`${prelude}---\n${JSON.stringify({
|
|
1546
|
+
...parsed.frontmatter,
|
|
1547
|
+
...slide.frontmatter
|
|
1548
|
+
})}\n---\n${slide.content}`, assets);
|
|
1549
|
+
const result = await adapter.compile(rewritten, { sourcePath: resolve(input.cwd ?? process.cwd(), entry.sourcePath) });
|
|
1550
|
+
clientEntryRequired ||= Boolean(result.clientCode || result.islands?.length);
|
|
1551
|
+
if (result.clientCode) clients.add(result.clientCode);
|
|
1552
|
+
for (const island of result.islands ?? []) markdownIslands.set(island.id, island);
|
|
1553
|
+
const slideMeta = toSlideFrontmatter(slide.frontmatter, warnings, {
|
|
1554
|
+
slideIndex: index,
|
|
1555
|
+
fallbackTitle: result.headings[0]?.text,
|
|
1556
|
+
fallbackTransition: meta.transition,
|
|
1557
|
+
fallbackTransitionDuration: meta.transitionDuration,
|
|
1558
|
+
fallbackTransitionEasing: meta.transitionEasing
|
|
1559
|
+
});
|
|
1560
|
+
addUnknownFrontmatterWarnings(warnings, slideMeta.meta, "slide", index);
|
|
1561
|
+
slideModules.push({
|
|
1562
|
+
path,
|
|
1563
|
+
importPath: `./decks/${entry.slug}/slide-${index}`,
|
|
1564
|
+
code: `// @ts-nocheck\n${rewriteRelativeMdxImports(result.code, dirname$2(entry.sourcePath), dirname$2(path))}`
|
|
1565
|
+
});
|
|
1566
|
+
slides.push({
|
|
1567
|
+
index,
|
|
1568
|
+
meta: slideMeta,
|
|
1569
|
+
html: "",
|
|
1570
|
+
components: [],
|
|
1571
|
+
notes: slideMeta.notes
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
1574
|
+
addExternalAssetWarnings(warnings, assets);
|
|
1575
|
+
const themeStyle = input.themeStyles?.[entry.slug];
|
|
1576
|
+
decks.push({
|
|
1577
|
+
deck: {
|
|
1578
|
+
slug: entry.slug,
|
|
1579
|
+
sourcePath: entry.sourcePath,
|
|
1580
|
+
kind: entry.kind,
|
|
1581
|
+
clientEntryRequired,
|
|
1582
|
+
meta,
|
|
1583
|
+
slides,
|
|
1584
|
+
assets,
|
|
1585
|
+
warnings,
|
|
1586
|
+
themeStyle: themeStyle?.style,
|
|
1587
|
+
themeSourcePath: themeStyle?.sourcePath
|
|
1588
|
+
},
|
|
1589
|
+
slideModules
|
|
1590
|
+
});
|
|
1591
|
+
} catch (error) {
|
|
1592
|
+
throw new CompileError(`Void Markdown compile failed in ${entry.sourcePath}: ${error instanceof Error ? error.message : String(error)}`, "void-markdown-compile-error");
|
|
1593
|
+
}
|
|
1594
|
+
decks.sort((a, b) => a.deck.slug.localeCompare(b.deck.slug));
|
|
1595
|
+
return {
|
|
1596
|
+
decks,
|
|
1597
|
+
routerModule: emitModuleDecksRouter({ decks }),
|
|
1598
|
+
clientCode: [...clients].join("\n"),
|
|
1599
|
+
clientModules: [adapter.client([...markdownIslands.values()])].filter(Boolean),
|
|
1600
|
+
markdownIslands: [...markdownIslands.values()]
|
|
1601
|
+
};
|
|
1602
|
+
}
|
|
1603
|
+
/** Protect fenced code from the deck separator parser. */
|
|
1604
|
+
function splitMarkdownSlides(source) {
|
|
1605
|
+
const fences = [];
|
|
1606
|
+
let prefix = "HONO_DECKS_FENCE_";
|
|
1607
|
+
while (source.includes(prefix)) prefix += "_";
|
|
1608
|
+
const lines = source.replace(/\r\n/g, "\n").split("\n");
|
|
1609
|
+
const protectedLines = [];
|
|
1610
|
+
for (let index = 0; index < lines.length; index++) {
|
|
1611
|
+
const open = /^ {0,3}(`{3,}|~{3,})/.exec(lines[index]);
|
|
1612
|
+
if (/^[ \t]+---[ \t]*$/.test(lines[index])) {
|
|
1613
|
+
protectedLines.push(`${prefix}${fences.push(lines[index]) - 1}_END`);
|
|
1614
|
+
continue;
|
|
1615
|
+
}
|
|
1616
|
+
if (!open) {
|
|
1617
|
+
protectedLines.push(lines[index]);
|
|
1618
|
+
continue;
|
|
1619
|
+
}
|
|
1620
|
+
const block = [lines[index]];
|
|
1621
|
+
const close = new RegExp(`^ {0,3}${open[1][0]}{${open[1].length},}[ \\t]*$`);
|
|
1622
|
+
let closed = false;
|
|
1623
|
+
while (++index < lines.length) {
|
|
1624
|
+
block.push(lines[index]);
|
|
1625
|
+
if (close.test(lines[index])) {
|
|
1626
|
+
closed = true;
|
|
1627
|
+
break;
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
if (!closed) throw new Error("Markdown code fence is not closed.");
|
|
1631
|
+
protectedLines.push(`${prefix}${fences.push(block.join("\n")) - 1}_END`);
|
|
1632
|
+
}
|
|
1633
|
+
return splitSlideSources(protectedLines.join("\n")).map((slide) => slide.replace(new RegExp(`${prefix}(\\d+)_END`, "g"), (_, id) => fences[Number(id)]));
|
|
1634
|
+
}
|
|
1635
|
+
//#endregion
|
|
1636
|
+
//#region src/adapters/hono-markdown.ts
|
|
1637
|
+
/** Node-only bridge. Existing Void configs retain their original compiler/runtime pair. */
|
|
1638
|
+
async function createHonoMarkdownAdapter(cwd, legacy = false, options) {
|
|
1639
|
+
const require = createRequire(resolve(cwd, "package.json"));
|
|
1640
|
+
const entry = legacy ? "void-plugin-hono-jsx/markdown/compiler" : "@ts-76/hono-markdown/compiler";
|
|
1641
|
+
let compiler;
|
|
1642
|
+
try {
|
|
1643
|
+
compiler = await import(pathToFileURL(require.resolve(entry)).href);
|
|
1644
|
+
} catch (cause) {
|
|
1645
|
+
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 });
|
|
1646
|
+
}
|
|
1647
|
+
const read = compiler.readMarkdownModule ?? compiler.readStaticMarkdown;
|
|
1648
|
+
const compile = compiler.compileMarkdownModule ?? compiler.compileStaticMarkdown;
|
|
1649
|
+
if (typeof read !== "function" || typeof compile !== "function") throw new Error("Missing standalone compiler API");
|
|
1650
|
+
const runtime = entry.startsWith("void-plugin") ? "void-plugin-hono-jsx/markdown/client" : "@ts-76/hono-markdown/client";
|
|
1651
|
+
return {
|
|
1652
|
+
read,
|
|
1653
|
+
compile: (source, input) => compile(source, {
|
|
1654
|
+
...input,
|
|
1655
|
+
options
|
|
1656
|
+
}),
|
|
1657
|
+
client: (islands) => emitMarkdownClient(islands, runtime)
|
|
1658
|
+
};
|
|
1659
|
+
}
|
|
1660
|
+
function emitMarkdownClient(islands, runtime) {
|
|
1661
|
+
if (!islands.length) return "";
|
|
1662
|
+
const literal = (value) => JSON.stringify(value).replace(/</g, "\\u003c");
|
|
1663
|
+
return `import { hydrateMarkdownIslands } from ${literal(runtime)};
|
|
1664
|
+
hydrateMarkdownIslands({ ${islands.map((island) => `${literal(island.id)}: () => import(${literal(island.modulePath)})`).join(",")} }, {
|
|
1665
|
+
ready(element, start) {
|
|
1666
|
+
const slide = element.closest('section[data-slide-state]');
|
|
1667
|
+
if (!slide || slide.dataset.slideState === 'active') { start(); return; }
|
|
1668
|
+
const observer = new MutationObserver(() => {
|
|
1669
|
+
if (slide.dataset.slideState === 'active') { observer.disconnect(); start(); }
|
|
1670
|
+
});
|
|
1671
|
+
observer.observe(slide, { attributes: true, attributeFilter: ['data-slide-state'] });
|
|
1672
|
+
}
|
|
1673
|
+
});`;
|
|
1674
|
+
}
|
|
1675
|
+
//#endregion
|
|
1676
|
+
//#region src/node/markdown-adapter.ts
|
|
1677
|
+
async function loadMarkdownAdapter(selection, cwd) {
|
|
1678
|
+
if (selection === "void") return createHonoMarkdownAdapter(cwd, true);
|
|
1679
|
+
if (selection.adapter === "hono-decks/adapters/hono-markdown") return createHonoMarkdownAdapter(cwd, false, selection.options);
|
|
1680
|
+
const module = await import(pathToFileURL(createRequire(resolve(cwd, "package.json")).resolve(selection.adapter)).href);
|
|
1681
|
+
if (typeof module.createMarkdownAdapter !== "function") throw new Error("Markdown adapter must export createMarkdownAdapter.");
|
|
1682
|
+
const adapter = await module.createMarkdownAdapter({
|
|
1683
|
+
cwd,
|
|
1684
|
+
options: selection.options
|
|
1685
|
+
});
|
|
1686
|
+
if (![
|
|
1687
|
+
adapter?.read,
|
|
1688
|
+
adapter?.compile,
|
|
1689
|
+
adapter?.client
|
|
1690
|
+
].every((value) => typeof value === "function")) throw new Error("Markdown adapter must provide read, compile and client functions.");
|
|
1691
|
+
return adapter;
|
|
1692
|
+
}
|
|
1693
|
+
//#endregion
|
|
1504
1694
|
//#region src/node/client-entry.ts
|
|
1505
1695
|
async function discoverClientComponentIds(input) {
|
|
1506
1696
|
const result = {};
|
|
@@ -1521,10 +1711,12 @@ async function emitClientEntryModule(input) {
|
|
|
1521
1711
|
registrations.push(`${JSON.stringify(clientId)}: ${localName}`);
|
|
1522
1712
|
}
|
|
1523
1713
|
}
|
|
1524
|
-
|
|
1525
|
-
const
|
|
1714
|
+
const clientModules = input.clientModules ?? [];
|
|
1715
|
+
if (registrations.length === 0 && clientModules.length === 0) return `export const decksClientEntry = ${JSON.stringify(input.extraCode ?? "")};\n`;
|
|
1716
|
+
const result = await build({
|
|
1526
1717
|
stdin: {
|
|
1527
|
-
contents:
|
|
1718
|
+
contents: `${clientModules.join("\n")}
|
|
1719
|
+
import { hydrateSlideIslands } from "hono-decks/client";
|
|
1528
1720
|
${imports.join("\n")}
|
|
1529
1721
|
|
|
1530
1722
|
hydrateSlideIslands({
|
|
@@ -1539,6 +1731,15 @@ hydrateSlideIslands({
|
|
|
1539
1731
|
},
|
|
1540
1732
|
bundle: true,
|
|
1541
1733
|
write: false,
|
|
1734
|
+
outdir: join(input.cwd, ".hono-decks-client"),
|
|
1735
|
+
loader: {
|
|
1736
|
+
".svg": "dataurl",
|
|
1737
|
+
".png": "dataurl",
|
|
1738
|
+
".jpg": "dataurl",
|
|
1739
|
+
".webp": "dataurl",
|
|
1740
|
+
".woff2": "dataurl",
|
|
1741
|
+
".woff": "dataurl"
|
|
1742
|
+
},
|
|
1542
1743
|
format: "esm",
|
|
1543
1744
|
platform: "browser",
|
|
1544
1745
|
target: "es2022",
|
|
@@ -1548,9 +1749,12 @@ hydrateSlideIslands({
|
|
|
1548
1749
|
alias: { "hono-decks/client": resolveClientRuntimeEntry() },
|
|
1549
1750
|
sourcemap: false,
|
|
1550
1751
|
minify: false
|
|
1551
|
-
})
|
|
1752
|
+
});
|
|
1753
|
+
const output = result.outputFiles.find((file) => file.path.endsWith(".js"));
|
|
1754
|
+
const css = result.outputFiles.filter((file) => file.path.endsWith(".css")).map((file) => file.text).join("\n");
|
|
1755
|
+
input.onCss?.(css);
|
|
1552
1756
|
if (!output) throw new Error("Client entry did not produce output.");
|
|
1553
|
-
return `export const decksClientEntry = ${JSON.stringify(output.text)};\n`;
|
|
1757
|
+
return `export const decksClientEntry = ${JSON.stringify(output.text + (input.extraCode ? "\n" + input.extraCode : ""))};\n`;
|
|
1554
1758
|
}
|
|
1555
1759
|
function resolveClientRuntimeEntry() {
|
|
1556
1760
|
const built = fileURLToPath(new URL("./client.js", import.meta.url));
|
|
@@ -1589,33 +1793,13 @@ function safeIdentifier(value) {
|
|
|
1589
1793
|
return value.replace(/[^A-Za-z0-9_$]+/g, "_").replace(/^[^A-Za-z_$]+/, "_") || "_";
|
|
1590
1794
|
}
|
|
1591
1795
|
//#endregion
|
|
1592
|
-
//#region src/node/path-utils.ts
|
|
1593
|
-
function dirname$1(path) {
|
|
1594
|
-
const normalized = normalizePath$1(path);
|
|
1595
|
-
return normalized.includes("/") ? normalized.slice(0, normalized.lastIndexOf("/")) : ".";
|
|
1596
|
-
}
|
|
1597
|
-
function normalizePath$1(path) {
|
|
1598
|
-
return path.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
|
|
1599
|
-
}
|
|
1600
|
-
function normalizeDeckRoot(root) {
|
|
1601
|
-
const normalized = normalizeRelativePath(root, "Deck root").replace(/\/$/, "");
|
|
1602
|
-
if (normalized === ".") throw new Error("Deck root must be a relative path inside the current working directory");
|
|
1603
|
-
return normalized;
|
|
1604
|
-
}
|
|
1605
|
-
function normalizeRelativePath(path, label) {
|
|
1606
|
-
const normalized = normalizePath$1(path).replace(/\/$/, "");
|
|
1607
|
-
const segments = normalized.split("/");
|
|
1608
|
-
if (normalized === "" || normalized.startsWith("/") || /^[A-Za-z]:\//.test(normalized) || segments.includes("..")) throw new Error(`${label} must be a relative path inside the current working directory`);
|
|
1609
|
-
return normalized;
|
|
1610
|
-
}
|
|
1611
|
-
//#endregion
|
|
1612
1796
|
//#region src/node/local-deck-io.ts
|
|
1613
1797
|
async function listFiles(cwd, dir) {
|
|
1614
1798
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
1615
1799
|
return (await Promise.all(entries.map(async (entry) => {
|
|
1616
1800
|
const fullPath = join(dir, entry.name);
|
|
1617
1801
|
if (entry.isDirectory()) return listFiles(cwd, fullPath);
|
|
1618
|
-
if (entry.isFile()) return [normalizePath$
|
|
1802
|
+
if (entry.isFile()) return [normalizePath$4(relative(cwd, fullPath))];
|
|
1619
1803
|
return [];
|
|
1620
1804
|
}))).flat().sort();
|
|
1621
1805
|
}
|
|
@@ -1806,10 +1990,16 @@ function isHttpUrl(value) {
|
|
|
1806
1990
|
//#endregion
|
|
1807
1991
|
//#region src/node/compile-decks.ts
|
|
1808
1992
|
async function compileDecks(input) {
|
|
1993
|
+
const compiled = await compileDeckModules(input);
|
|
1994
|
+
await publishGeneratedFiles([...compiled.modules, ...compiled.cacheFile ? [compiled.cacheFile] : []]);
|
|
1995
|
+
return compiled.result;
|
|
1996
|
+
}
|
|
1997
|
+
/** Shared by file output and Vite's in-memory module graph. Does not write files. */
|
|
1998
|
+
async function compileDeckModules(input) {
|
|
1809
1999
|
const out = normalizeRelativePath(input.out, "Output directory");
|
|
1810
2000
|
const root = normalizeDeckRoot(input.root);
|
|
1811
2001
|
const paths = await listFiles(input.cwd, join(input.cwd, root));
|
|
1812
|
-
const resolved = resolveDeckFiles(paths, root);
|
|
2002
|
+
const resolved = resolveDeckFiles(paths, root, { markdown: Boolean(input.markdown) });
|
|
1813
2003
|
const componentModulePaths = Object.fromEntries(resolved.flatMap((deck) => {
|
|
1814
2004
|
const path = deck.kind === "directory" ? findComponentModule(paths, root, deck.slug) : void 0;
|
|
1815
2005
|
return path ? [[deck.slug, path]] : [];
|
|
@@ -1837,7 +2027,9 @@ async function compileDecks(input) {
|
|
|
1837
2027
|
refresh: input.refreshOgp,
|
|
1838
2028
|
resolveOgp: input.resolveOgp ?? resolveOgpMetadata
|
|
1839
2029
|
});
|
|
1840
|
-
const
|
|
2030
|
+
const adapter = input.markdown && resolved.some((entry) => entry.sourcePath.endsWith(".md")) ? await loadMarkdownAdapter(input.markdown, input.cwd) : void 0;
|
|
2031
|
+
const generated = await (adapter ? (value) => compileMarkdownDecks(value, adapter) : compileMdxModuleDecks)({
|
|
2032
|
+
cwd: input.cwd,
|
|
1841
2033
|
root,
|
|
1842
2034
|
outDir: out,
|
|
1843
2035
|
mountPath: input.mountPath,
|
|
@@ -1849,20 +2041,57 @@ async function compileDecks(input) {
|
|
|
1849
2041
|
readText: (path) => readFile(join(input.cwd, path), "utf8"),
|
|
1850
2042
|
readBinary: (path) => readFile(join(input.cwd, path))
|
|
1851
2043
|
});
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
await writeTextFile(join(input.cwd, out, "client-entry.ts"), await emitClientEntryModule({
|
|
2044
|
+
let clientCss = "";
|
|
2045
|
+
const clientEntry = await emitClientEntryModule({
|
|
1855
2046
|
cwd: input.cwd,
|
|
1856
2047
|
clientEntries: clientEntryPaths,
|
|
1857
|
-
clientComponentIds
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
2048
|
+
clientComponentIds,
|
|
2049
|
+
extraCode: generated.clientCode,
|
|
2050
|
+
clientModules: generated.clientModules,
|
|
2051
|
+
onCss: (css) => {
|
|
2052
|
+
clientCss = css;
|
|
2053
|
+
}
|
|
2054
|
+
});
|
|
2055
|
+
if (clientCss) {
|
|
2056
|
+
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");
|
|
2057
|
+
generated.routerModule = emitModuleDecksRouter({ decks: generated.decks });
|
|
2058
|
+
}
|
|
2059
|
+
const files = generated.decks.flatMap((deck) => deck.slideModules.map((slide) => ({
|
|
2060
|
+
path: join(input.cwd, slide.path),
|
|
2061
|
+
content: slide.code
|
|
2062
|
+
})));
|
|
2063
|
+
files.push({
|
|
2064
|
+
path: join(input.cwd, out, "decks.ts"),
|
|
2065
|
+
content: generated.routerModule
|
|
2066
|
+
}, {
|
|
2067
|
+
path: join(input.cwd, out, "client-entry.ts"),
|
|
2068
|
+
content: clientEntry
|
|
2069
|
+
});
|
|
2070
|
+
return {
|
|
2071
|
+
modules: files,
|
|
2072
|
+
cacheFile: ogpCache.output(),
|
|
2073
|
+
result: {
|
|
2074
|
+
outDir: out,
|
|
2075
|
+
routerModulePath: `${out}/decks.ts`,
|
|
2076
|
+
decks: generated.decks.map(({ deck, slideModules }) => {
|
|
2077
|
+
const { slides, componentRegistry: _componentRegistry, ...metadata } = deck;
|
|
2078
|
+
return {
|
|
2079
|
+
...metadata,
|
|
2080
|
+
slides: slides.map((slide, index) => ({
|
|
2081
|
+
index: slide.index,
|
|
2082
|
+
meta: slide.meta,
|
|
2083
|
+
notes: slide.notes,
|
|
2084
|
+
modulePath: slideModules[index].path
|
|
2085
|
+
}))
|
|
2086
|
+
};
|
|
2087
|
+
})
|
|
2088
|
+
}
|
|
2089
|
+
};
|
|
1861
2090
|
}
|
|
1862
2091
|
async function createOgpCacheResolver(input) {
|
|
1863
2092
|
if (!input.cacheFile) return {
|
|
1864
2093
|
resolveOgp: input.resolveOgp,
|
|
1865
|
-
|
|
2094
|
+
output: () => void 0
|
|
1866
2095
|
};
|
|
1867
2096
|
const cachePath = join(input.cwd, normalizeRelativePath(input.cacheFile, "OGP cache file"));
|
|
1868
2097
|
const cache = await readOgpCache(cachePath);
|
|
@@ -1878,9 +2107,11 @@ async function createOgpCacheResolver(input) {
|
|
|
1878
2107
|
}
|
|
1879
2108
|
return metadata;
|
|
1880
2109
|
},
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
2110
|
+
output() {
|
|
2111
|
+
return dirty ? {
|
|
2112
|
+
path: cachePath,
|
|
2113
|
+
content: `${JSON.stringify(cache, null, 2)}\n`
|
|
2114
|
+
} : void 0;
|
|
1884
2115
|
}
|
|
1885
2116
|
};
|
|
1886
2117
|
}
|
|
@@ -1910,21 +2141,17 @@ async function readOgpCache(path) {
|
|
|
1910
2141
|
function isNodeError(error) {
|
|
1911
2142
|
return error instanceof Error && "code" in error;
|
|
1912
2143
|
}
|
|
1913
|
-
async function writeTextFile(path, contents) {
|
|
1914
|
-
await mkdir(dirname$1(path), { recursive: true });
|
|
1915
|
-
await writeFile(path, contents, "utf8");
|
|
1916
|
-
}
|
|
1917
2144
|
function findComponentModule(paths, root, slug) {
|
|
1918
|
-
const base = `${normalizePath$
|
|
2145
|
+
const base = `${normalizePath$4(root).replace(/\/$/, "")}/${slug}/components/index`;
|
|
1919
2146
|
return paths.find((path) => {
|
|
1920
|
-
const normalized = normalizePath$
|
|
2147
|
+
const normalized = normalizePath$4(path);
|
|
1921
2148
|
return normalized === `${base}.tsx` || normalized === `${base}.ts` || normalized === `${base}.jsx` || normalized === `${base}.js`;
|
|
1922
2149
|
});
|
|
1923
2150
|
}
|
|
1924
2151
|
function findClientEntryModule(paths, root, slug) {
|
|
1925
|
-
const base = `${normalizePath$
|
|
2152
|
+
const base = `${normalizePath$4(root).replace(/\/$/, "")}/${slug}/components/client/index`;
|
|
1926
2153
|
return paths.find((path) => {
|
|
1927
|
-
const normalized = normalizePath$
|
|
2154
|
+
const normalized = normalizePath$4(path);
|
|
1928
2155
|
return normalized === `${base}.tsx` || normalized === `${base}.ts` || normalized === `${base}.jsx` || normalized === `${base}.js`;
|
|
1929
2156
|
});
|
|
1930
2157
|
}
|
|
@@ -1942,7 +2169,7 @@ async function discoverDeckThemeStyles(input) {
|
|
|
1942
2169
|
return result;
|
|
1943
2170
|
}
|
|
1944
2171
|
function findDeckThemeStyle(paths, root, slug) {
|
|
1945
|
-
const base = `${normalizePath$
|
|
2172
|
+
const base = `${normalizePath$4(root).replace(/\/$/, "")}/${slug}`;
|
|
1946
2173
|
const themePath = `${base}/theme.css`;
|
|
1947
2174
|
const stylesEntryPath = `${base}/styles/index.css`;
|
|
1948
2175
|
const hasTheme = paths.includes(themePath);
|
|
@@ -1953,7 +2180,7 @@ function findDeckThemeStyle(paths, root, slug) {
|
|
|
1953
2180
|
async function loadDecksConfig(input) {
|
|
1954
2181
|
const path = resolve(input.cwd, input.configFile ?? "hono-decks.config.ts");
|
|
1955
2182
|
if (!existsSync(path)) throw new Error(`Config file not found: ${input.configFile ?? "hono-decks.config.ts"}. Run \`hono-decks init\` first.`);
|
|
1956
|
-
const
|
|
2183
|
+
const result = await build({
|
|
1957
2184
|
absWorkingDir: input.cwd,
|
|
1958
2185
|
entryPoints: [path],
|
|
1959
2186
|
bundle: true,
|
|
@@ -1961,16 +2188,20 @@ async function loadDecksConfig(input) {
|
|
|
1961
2188
|
platform: "node",
|
|
1962
2189
|
target: "node20",
|
|
1963
2190
|
write: false,
|
|
2191
|
+
metafile: true,
|
|
1964
2192
|
logLevel: "silent",
|
|
1965
2193
|
alias: { "hono-decks": resolveRuntimeEntry() }
|
|
1966
|
-
})
|
|
2194
|
+
});
|
|
2195
|
+
const source = result.outputFiles[0]?.text;
|
|
1967
2196
|
if (!source) throw new Error(`Could not load config file: ${path}`);
|
|
1968
2197
|
const config = (await import(`data:text/javascript;charset=utf-8,${encodeURIComponent(source)}#${Date.now()}`)).default;
|
|
1969
2198
|
if (!config || typeof config !== "object") throw new Error(`Config must have a default export: ${path}`);
|
|
1970
2199
|
if (typeof config.mountPath !== "string") throw new Error(`Config mountPath must be a string: ${path}`);
|
|
2200
|
+
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.");
|
|
1971
2201
|
return {
|
|
1972
2202
|
path,
|
|
1973
2203
|
config,
|
|
2204
|
+
dependencies: Object.keys(result.metafile.inputs).map((inputPath) => resolve(input.cwd, inputPath)),
|
|
1974
2205
|
root: config.build?.root ?? "decks",
|
|
1975
2206
|
outDir: config.build?.outDir ?? "src/generated",
|
|
1976
2207
|
ogpCacheFile: config.build?.ogpCacheFile
|
|
@@ -1992,6 +2223,10 @@ Commands:
|
|
|
1992
2223
|
compile, build Compile decks using the shared config.
|
|
1993
2224
|
|
|
1994
2225
|
Options:
|
|
2226
|
+
--install Install missing dependencies for --framework void.
|
|
2227
|
+
--adapter <specifier> Compatible Hono Markdown adapter version or tarball; requires --install.
|
|
2228
|
+
--framework <name> Vite integration: hono, honox, or void.
|
|
2229
|
+
--entry <path> Hono app entry for --framework hono; otherwise auto-detect.
|
|
1995
2230
|
--config <path> Config file. Default: hono-decks.config.ts
|
|
1996
2231
|
--out <path> Facade output for init. Default: src/decks.ts
|
|
1997
2232
|
--generated <path> Generated module import for init. Default: ./generated/decks
|
|
@@ -2033,6 +2268,7 @@ async function runHonoDecksCli(input) {
|
|
|
2033
2268
|
out: loaded.outDir,
|
|
2034
2269
|
mountPath: loaded.config.mountPath,
|
|
2035
2270
|
ogpCacheFile: loaded.ogpCacheFile,
|
|
2271
|
+
markdown: loaded.config.build?.markdown,
|
|
2036
2272
|
refreshOgp: parsed.options.refreshOgp
|
|
2037
2273
|
});
|
|
2038
2274
|
stdout(`Compiled ${manifest.decks.length} decks to ${loaded.outDir}`);
|
|
@@ -2113,6 +2349,15 @@ async function runInitCommand(input, args, stdout, stderr) {
|
|
|
2113
2349
|
stderr(USAGE);
|
|
2114
2350
|
return { exitCode: 1 };
|
|
2115
2351
|
}
|
|
2352
|
+
if (parsed.options.framework) try {
|
|
2353
|
+
const { initializeVite } = await import("./init-vite-EA_L4k2H.js");
|
|
2354
|
+
const sampleSlug = await initializeVite(input.cwd, parsed.options.framework, parsed.options.configFile, parsed.options.entry, parsed.options.install ? { adapter: parsed.options.adapter } : void 0);
|
|
2355
|
+
stdout(`Initialized ${parsed.options.framework} decks at /slides/${sampleSlug}. Run your usual dev or build command; no manual compile is needed.`);
|
|
2356
|
+
return { exitCode: 0 };
|
|
2357
|
+
} catch (error) {
|
|
2358
|
+
stderr(error instanceof Error ? error.message : String(error));
|
|
2359
|
+
return { exitCode: 1 };
|
|
2360
|
+
}
|
|
2116
2361
|
const configFile = normalizeOutputFile(parsed.options.configFile ?? "hono-decks.config.ts");
|
|
2117
2362
|
const out = normalizeOutputFile(parsed.options.out ?? "src/decks.ts");
|
|
2118
2363
|
try {
|
|
@@ -2170,13 +2415,26 @@ function parseInitArgs(args) {
|
|
|
2170
2415
|
options,
|
|
2171
2416
|
help: true
|
|
2172
2417
|
};
|
|
2173
|
-
if (arg === "--
|
|
2418
|
+
if (arg === "--install") {
|
|
2419
|
+
options.install = true;
|
|
2420
|
+
continue;
|
|
2421
|
+
}
|
|
2422
|
+
if (arg === "--adapter" || arg === "--config" || arg === "--out" || arg === "--generated" || arg === "--framework" || arg === "--entry") {
|
|
2174
2423
|
const value = args[index + 1];
|
|
2175
2424
|
if (!value || value.startsWith("--")) return {
|
|
2176
2425
|
options,
|
|
2177
2426
|
error: `Missing value for ${arg}`
|
|
2178
2427
|
};
|
|
2428
|
+
if (arg === "--framework") {
|
|
2429
|
+
if (value !== "void" && value !== "hono" && value !== "honox") return {
|
|
2430
|
+
options,
|
|
2431
|
+
error: `Unsupported framework: ${value}. Expected hono, honox, or void.`
|
|
2432
|
+
};
|
|
2433
|
+
options.framework = value;
|
|
2434
|
+
}
|
|
2435
|
+
if (arg === "--adapter") options.adapter = value;
|
|
2179
2436
|
if (arg === "--config") options.configFile = value;
|
|
2437
|
+
if (arg === "--entry") options.entry = value;
|
|
2180
2438
|
if (arg === "--out") options.out = value;
|
|
2181
2439
|
if (arg === "--generated") options.generated = value;
|
|
2182
2440
|
index += 1;
|
|
@@ -2187,14 +2445,26 @@ function parseInitArgs(args) {
|
|
|
2187
2445
|
error: `Unknown option: ${arg}`
|
|
2188
2446
|
};
|
|
2189
2447
|
}
|
|
2448
|
+
if (options.install && options.framework !== "void" || options.adapter && !options.install) return {
|
|
2449
|
+
options,
|
|
2450
|
+
error: "--install requires --framework void; --adapter also requires --install."
|
|
2451
|
+
};
|
|
2452
|
+
if (options.entry && options.framework !== "hono") return {
|
|
2453
|
+
options,
|
|
2454
|
+
error: "--entry requires --framework hono."
|
|
2455
|
+
};
|
|
2456
|
+
if (options.framework && (options.out || options.generated)) return {
|
|
2457
|
+
options,
|
|
2458
|
+
error: "Framework init uses virtual modules; --out and --generated are only supported by generic init."
|
|
2459
|
+
};
|
|
2190
2460
|
return { options };
|
|
2191
2461
|
}
|
|
2192
2462
|
async function writeInitialFiles(input) {
|
|
2193
2463
|
for (const path of [input.configFile, input.out]) if (existsSync(join(input.cwd, path))) throw new Error(`Refusing to overwrite existing file: ${path}`);
|
|
2194
2464
|
const configPath = join(input.cwd, input.configFile);
|
|
2195
2465
|
const facadePath = join(input.cwd, input.out);
|
|
2196
|
-
await mkdir(dirname(configPath), { recursive: true });
|
|
2197
|
-
await mkdir(dirname(facadePath), { recursive: true });
|
|
2466
|
+
await mkdir(dirname$1(configPath), { recursive: true });
|
|
2467
|
+
await mkdir(dirname$1(facadePath), { recursive: true });
|
|
2198
2468
|
await writeFile(configPath, emitDecksConfig(), "utf8");
|
|
2199
2469
|
await writeFile(facadePath, emitDecksFacade(input.generated, relativeImport(input.out, input.configFile)), "utf8");
|
|
2200
2470
|
}
|
|
@@ -2219,7 +2489,7 @@ export const decks = createDecks(config);
|
|
|
2219
2489
|
`;
|
|
2220
2490
|
}
|
|
2221
2491
|
function relativeImport(fromFile, toFile) {
|
|
2222
|
-
const path = normalizePath(relative(dirname(fromFile), toFile)).replace(/\.(?:[cm]?[jt]sx?)$/, "");
|
|
2492
|
+
const path = normalizePath(relative(dirname$1(fromFile), toFile)).replace(/\.(?:[cm]?[jt]sx?)$/, "");
|
|
2223
2493
|
return path.startsWith(".") ? path : `./${path}`;
|
|
2224
2494
|
}
|
|
2225
2495
|
function normalizeOutputFile(path) {
|
|
@@ -2231,7 +2501,7 @@ function normalizeOutputFile(path) {
|
|
|
2231
2501
|
function normalizePath(path) {
|
|
2232
2502
|
return path.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
|
|
2233
2503
|
}
|
|
2234
|
-
function dirname(path) {
|
|
2504
|
+
function dirname$1(path) {
|
|
2235
2505
|
const normalized = normalizePath(path);
|
|
2236
2506
|
return normalized.includes("/") ? normalized.slice(0, normalized.lastIndexOf("/")) : ".";
|
|
2237
2507
|
}
|