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/cli.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { i as normalizeRelativePath, n as normalizeDeckRoot, r as normalizePath$4, t as publishGeneratedFiles } from "./file-transaction-NjDxX6Gt.js";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
1
3
|
import { existsSync, watch } from "node:fs";
|
|
2
4
|
import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
|
|
3
5
|
import { join, relative, resolve } from "node:path";
|
|
@@ -8,28 +10,28 @@ import remarkParse from "remark-parse";
|
|
|
8
10
|
import { compile } from "@mdx-js/mdx";
|
|
9
11
|
import remarkDirective from "remark-directive";
|
|
10
12
|
import { codeToHtml } from "shiki";
|
|
11
|
-
import { fileURLToPath } from "node:url";
|
|
13
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
12
14
|
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") {
|
|
17
|
-
const normalizedRoot = normalizePath$
|
|
18
|
+
function resolveDeckFiles(paths, root = "decks", options = {}) {
|
|
19
|
+
const normalizedRoot = normalizePath$3(root).replace(/\/$/, "");
|
|
18
20
|
const decks = /* @__PURE__ */ new Map();
|
|
19
21
|
const assetsBySlug = /* @__PURE__ */ new Map();
|
|
20
|
-
for (const inputPath of paths.map(normalizePath$
|
|
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,
|
|
@@ -63,7 +65,7 @@ function assertInsideRoot(path, root) {
|
|
|
63
65
|
if (path.includes("/../") || path.endsWith("/..") || path.startsWith("../")) throw new Error(`Deck path escapes the root: ${path}`);
|
|
64
66
|
if (path !== root && !path.startsWith(`${root}/`)) throw new Error(`Deck path is outside ${root}: ${path}`);
|
|
65
67
|
}
|
|
66
|
-
function normalizePath$
|
|
68
|
+
function normalizePath$3(path) {
|
|
67
69
|
return path.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
|
|
68
70
|
}
|
|
69
71
|
//#endregion
|
|
@@ -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();
|
|
@@ -916,7 +923,7 @@ function rewriteAssetUrls(source, assets) {
|
|
|
916
923
|
}
|
|
917
924
|
function rewriteRelativeMdxImports(source, sourceDir, generatedDir) {
|
|
918
925
|
return source.replace(/(from\s+|import\s+)(["'])(\.[^"']+)\2/g, (_match, prefix, quote, specifier) => {
|
|
919
|
-
return `${prefix}${quote}${toRelativeImportPath(generatedDir, normalizePath$
|
|
926
|
+
return `${prefix}${quote}${toRelativeImportPath(generatedDir, normalizePath$2(`${sourceDir}/${specifier}`))}${quote}`;
|
|
920
927
|
});
|
|
921
928
|
}
|
|
922
929
|
function componentImportPath(outDir, sourcePath) {
|
|
@@ -924,22 +931,22 @@ function componentImportPath(outDir, sourcePath) {
|
|
|
924
931
|
return toRelativeImportPath(outDir, sourcePath.replace(/\/index\.(tsx|ts|jsx|js)$/, ""));
|
|
925
932
|
}
|
|
926
933
|
function dirname$2(path) {
|
|
927
|
-
const normalized = normalizePath$
|
|
934
|
+
const normalized = normalizePath$2(path);
|
|
928
935
|
const index = normalized.lastIndexOf("/");
|
|
929
936
|
return index === -1 ? "." : normalized.slice(0, index);
|
|
930
937
|
}
|
|
931
938
|
function localAssetRelativePath(sourcePath) {
|
|
932
939
|
const marker = "/assets/";
|
|
933
|
-
const normalized = normalizePath$
|
|
940
|
+
const normalized = normalizePath$2(sourcePath);
|
|
934
941
|
const markerIndex = normalized.indexOf(marker);
|
|
935
942
|
return markerIndex === -1 ? normalized.split("/").at(-1) ?? normalized : normalized.slice(markerIndex + 8);
|
|
936
943
|
}
|
|
937
|
-
function normalizePath$
|
|
944
|
+
function normalizePath$2(path) {
|
|
938
945
|
return path.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
|
|
939
946
|
}
|
|
940
947
|
function toRelativeImportPath(fromDir, target) {
|
|
941
|
-
const fromParts = normalizePath$
|
|
942
|
-
const targetParts = normalizePath$
|
|
948
|
+
const fromParts = normalizePath$2(fromDir).split("/").filter(Boolean);
|
|
949
|
+
const targetParts = normalizePath$2(target).split("/").filter(Boolean);
|
|
943
950
|
while (fromParts.length > 0 && targetParts.length > 0 && fromParts[0] === targetParts[0]) {
|
|
944
951
|
fromParts.shift();
|
|
945
952
|
targetParts.shift();
|
|
@@ -962,14 +969,14 @@ async function buildAssetRefs(slug, assetPaths, input) {
|
|
|
962
969
|
})));
|
|
963
970
|
}
|
|
964
971
|
function assetName(sourcePath, root, slug) {
|
|
965
|
-
const normalizedPath = normalizePath$
|
|
966
|
-
const prefix = `${normalizePath$
|
|
972
|
+
const normalizedPath = normalizePath$1(sourcePath);
|
|
973
|
+
const prefix = `${normalizePath$1(root).replace(/\/$/, "")}/${slug}/assets/`;
|
|
967
974
|
return (normalizedPath.startsWith(prefix) ? normalizedPath.slice(prefix.length) : normalizedPath.split("/").at(-1) ?? normalizedPath).split("/").map(encodeURIComponent).join("/");
|
|
968
975
|
}
|
|
969
976
|
function normalizeMountPath(value) {
|
|
970
977
|
return (value.startsWith("/") ? value : `/${value}`).replace(/\/$/, "");
|
|
971
978
|
}
|
|
972
|
-
function normalizePath$
|
|
979
|
+
function normalizePath$1(path) {
|
|
973
980
|
return path.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
|
|
974
981
|
}
|
|
975
982
|
//#endregion
|
|
@@ -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_${
|
|
1052
|
+
function slideImportName(deckIndex, slideIndex) {
|
|
1053
|
+
return `Slide_${deckIndex}_${slideIndex}`;
|
|
1046
1054
|
}
|
|
1047
|
-
function componentImportName(
|
|
1048
|
-
return `Components_${
|
|
1049
|
-
}
|
|
1050
|
-
function safeIdentifier$1(value) {
|
|
1051
|
-
return value.replace(/[^A-Za-z0-9_$]+/g, "_").replace(/^[^A-Za-z_$]+/, "_") || "_";
|
|
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$2(entry.sourcePath), dirname$2(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));
|
|
@@ -1588,33 +1792,13 @@ function safeIdentifier(value) {
|
|
|
1588
1792
|
return value.replace(/[^A-Za-z0-9_$]+/g, "_").replace(/^[^A-Za-z_$]+/, "_") || "_";
|
|
1589
1793
|
}
|
|
1590
1794
|
//#endregion
|
|
1591
|
-
//#region src/node/path-utils.ts
|
|
1592
|
-
function dirname$1(path) {
|
|
1593
|
-
const normalized = normalizePath$1(path);
|
|
1594
|
-
return normalized.includes("/") ? normalized.slice(0, normalized.lastIndexOf("/")) : ".";
|
|
1595
|
-
}
|
|
1596
|
-
function normalizePath$1(path) {
|
|
1597
|
-
return path.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
|
|
1598
|
-
}
|
|
1599
|
-
function normalizeDeckRoot(root) {
|
|
1600
|
-
const normalized = normalizeRelativePath(root, "Deck root").replace(/\/$/, "");
|
|
1601
|
-
if (normalized === ".") throw new Error("Deck root must be a relative path inside the current working directory");
|
|
1602
|
-
return normalized;
|
|
1603
|
-
}
|
|
1604
|
-
function normalizeRelativePath(path, label) {
|
|
1605
|
-
const normalized = normalizePath$1(path).replace(/\/$/, "");
|
|
1606
|
-
const segments = normalized.split("/");
|
|
1607
|
-
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`);
|
|
1608
|
-
return normalized;
|
|
1609
|
-
}
|
|
1610
|
-
//#endregion
|
|
1611
1795
|
//#region src/node/local-deck-io.ts
|
|
1612
1796
|
async function listFiles(cwd, dir) {
|
|
1613
1797
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
1614
1798
|
return (await Promise.all(entries.map(async (entry) => {
|
|
1615
1799
|
const fullPath = join(dir, entry.name);
|
|
1616
1800
|
if (entry.isDirectory()) return listFiles(cwd, fullPath);
|
|
1617
|
-
if (entry.isFile()) return [normalizePath$
|
|
1801
|
+
if (entry.isFile()) return [normalizePath$4(relative(cwd, fullPath))];
|
|
1618
1802
|
return [];
|
|
1619
1803
|
}))).flat().sort();
|
|
1620
1804
|
}
|
|
@@ -1805,10 +1989,16 @@ function isHttpUrl(value) {
|
|
|
1805
1989
|
//#endregion
|
|
1806
1990
|
//#region src/node/compile-decks.ts
|
|
1807
1991
|
async function compileDecks(input) {
|
|
1992
|
+
const compiled = await compileDeckModules(input);
|
|
1993
|
+
await publishGeneratedFiles([...compiled.modules, ...compiled.cacheFile ? [compiled.cacheFile] : []]);
|
|
1994
|
+
return compiled.result;
|
|
1995
|
+
}
|
|
1996
|
+
/** Shared by file output and Vite's in-memory module graph. Does not write files. */
|
|
1997
|
+
async function compileDeckModules(input) {
|
|
1808
1998
|
const out = normalizeRelativePath(input.out, "Output directory");
|
|
1809
1999
|
const root = normalizeDeckRoot(input.root);
|
|
1810
2000
|
const paths = await listFiles(input.cwd, join(input.cwd, root));
|
|
1811
|
-
const resolved = resolveDeckFiles(paths, root);
|
|
2001
|
+
const resolved = resolveDeckFiles(paths, root, { markdown: Boolean(input.markdown) });
|
|
1812
2002
|
const componentModulePaths = Object.fromEntries(resolved.flatMap((deck) => {
|
|
1813
2003
|
const path = deck.kind === "directory" ? findComponentModule(paths, root, deck.slug) : void 0;
|
|
1814
2004
|
return path ? [[deck.slug, path]] : [];
|
|
@@ -1836,7 +2026,9 @@ async function compileDecks(input) {
|
|
|
1836
2026
|
refresh: input.refreshOgp,
|
|
1837
2027
|
resolveOgp: input.resolveOgp ?? resolveOgpMetadata
|
|
1838
2028
|
});
|
|
1839
|
-
const
|
|
2029
|
+
const adapter = input.markdown && resolved.some((entry) => entry.sourcePath.endsWith(".md")) ? await loadMarkdownAdapter(input.markdown, input.cwd) : void 0;
|
|
2030
|
+
const generated = await (adapter ? (value) => compileMarkdownDecks(value, adapter) : compileMdxModuleDecks)({
|
|
2031
|
+
cwd: input.cwd,
|
|
1840
2032
|
root,
|
|
1841
2033
|
outDir: out,
|
|
1842
2034
|
mountPath: input.mountPath,
|
|
@@ -1848,20 +2040,57 @@ async function compileDecks(input) {
|
|
|
1848
2040
|
readText: (path) => readFile(join(input.cwd, path), "utf8"),
|
|
1849
2041
|
readBinary: (path) => readFile(join(input.cwd, path))
|
|
1850
2042
|
});
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
await writeTextFile(join(input.cwd, out, "client-entry.ts"), await emitClientEntryModule({
|
|
2043
|
+
let clientCss = "";
|
|
2044
|
+
const clientEntry = await emitClientEntryModule({
|
|
1854
2045
|
cwd: input.cwd,
|
|
1855
2046
|
clientEntries: clientEntryPaths,
|
|
1856
|
-
clientComponentIds
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
2047
|
+
clientComponentIds,
|
|
2048
|
+
extraCode: generated.clientCode,
|
|
2049
|
+
clientModules: generated.clientModules,
|
|
2050
|
+
onCss: (css) => {
|
|
2051
|
+
clientCss = css;
|
|
2052
|
+
}
|
|
2053
|
+
});
|
|
2054
|
+
if (clientCss) {
|
|
2055
|
+
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");
|
|
2056
|
+
generated.routerModule = emitModuleDecksRouter({ decks: generated.decks });
|
|
2057
|
+
}
|
|
2058
|
+
const files = generated.decks.flatMap((deck) => deck.slideModules.map((slide) => ({
|
|
2059
|
+
path: join(input.cwd, slide.path),
|
|
2060
|
+
content: slide.code
|
|
2061
|
+
})));
|
|
2062
|
+
files.push({
|
|
2063
|
+
path: join(input.cwd, out, "decks.ts"),
|
|
2064
|
+
content: generated.routerModule
|
|
2065
|
+
}, {
|
|
2066
|
+
path: join(input.cwd, out, "client-entry.ts"),
|
|
2067
|
+
content: clientEntry
|
|
2068
|
+
});
|
|
2069
|
+
return {
|
|
2070
|
+
modules: files,
|
|
2071
|
+
cacheFile: ogpCache.output(),
|
|
2072
|
+
result: {
|
|
2073
|
+
outDir: out,
|
|
2074
|
+
routerModulePath: `${out}/decks.ts`,
|
|
2075
|
+
decks: generated.decks.map(({ deck, slideModules }) => {
|
|
2076
|
+
const { slides, componentRegistry: _componentRegistry, ...metadata } = deck;
|
|
2077
|
+
return {
|
|
2078
|
+
...metadata,
|
|
2079
|
+
slides: slides.map((slide, index) => ({
|
|
2080
|
+
index: slide.index,
|
|
2081
|
+
meta: slide.meta,
|
|
2082
|
+
notes: slide.notes,
|
|
2083
|
+
modulePath: slideModules[index].path
|
|
2084
|
+
}))
|
|
2085
|
+
};
|
|
2086
|
+
})
|
|
2087
|
+
}
|
|
2088
|
+
};
|
|
1860
2089
|
}
|
|
1861
2090
|
async function createOgpCacheResolver(input) {
|
|
1862
2091
|
if (!input.cacheFile) return {
|
|
1863
2092
|
resolveOgp: input.resolveOgp,
|
|
1864
|
-
|
|
2093
|
+
output: () => void 0
|
|
1865
2094
|
};
|
|
1866
2095
|
const cachePath = join(input.cwd, normalizeRelativePath(input.cacheFile, "OGP cache file"));
|
|
1867
2096
|
const cache = await readOgpCache(cachePath);
|
|
@@ -1877,9 +2106,11 @@ async function createOgpCacheResolver(input) {
|
|
|
1877
2106
|
}
|
|
1878
2107
|
return metadata;
|
|
1879
2108
|
},
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
2109
|
+
output() {
|
|
2110
|
+
return dirty ? {
|
|
2111
|
+
path: cachePath,
|
|
2112
|
+
content: `${JSON.stringify(cache, null, 2)}\n`
|
|
2113
|
+
} : void 0;
|
|
1883
2114
|
}
|
|
1884
2115
|
};
|
|
1885
2116
|
}
|
|
@@ -1909,21 +2140,17 @@ async function readOgpCache(path) {
|
|
|
1909
2140
|
function isNodeError(error) {
|
|
1910
2141
|
return error instanceof Error && "code" in error;
|
|
1911
2142
|
}
|
|
1912
|
-
async function writeTextFile(path, contents) {
|
|
1913
|
-
await mkdir(dirname$1(path), { recursive: true });
|
|
1914
|
-
await writeFile(path, contents, "utf8");
|
|
1915
|
-
}
|
|
1916
2143
|
function findComponentModule(paths, root, slug) {
|
|
1917
|
-
const base = `${normalizePath$
|
|
2144
|
+
const base = `${normalizePath$4(root).replace(/\/$/, "")}/${slug}/components/index`;
|
|
1918
2145
|
return paths.find((path) => {
|
|
1919
|
-
const normalized = normalizePath$
|
|
2146
|
+
const normalized = normalizePath$4(path);
|
|
1920
2147
|
return normalized === `${base}.tsx` || normalized === `${base}.ts` || normalized === `${base}.jsx` || normalized === `${base}.js`;
|
|
1921
2148
|
});
|
|
1922
2149
|
}
|
|
1923
2150
|
function findClientEntryModule(paths, root, slug) {
|
|
1924
|
-
const base = `${normalizePath$
|
|
2151
|
+
const base = `${normalizePath$4(root).replace(/\/$/, "")}/${slug}/components/client/index`;
|
|
1925
2152
|
return paths.find((path) => {
|
|
1926
|
-
const normalized = normalizePath$
|
|
2153
|
+
const normalized = normalizePath$4(path);
|
|
1927
2154
|
return normalized === `${base}.tsx` || normalized === `${base}.ts` || normalized === `${base}.jsx` || normalized === `${base}.js`;
|
|
1928
2155
|
});
|
|
1929
2156
|
}
|
|
@@ -1941,7 +2168,7 @@ async function discoverDeckThemeStyles(input) {
|
|
|
1941
2168
|
return result;
|
|
1942
2169
|
}
|
|
1943
2170
|
function findDeckThemeStyle(paths, root, slug) {
|
|
1944
|
-
const base = `${normalizePath$
|
|
2171
|
+
const base = `${normalizePath$4(root).replace(/\/$/, "")}/${slug}`;
|
|
1945
2172
|
const themePath = `${base}/theme.css`;
|
|
1946
2173
|
const stylesEntryPath = `${base}/styles/index.css`;
|
|
1947
2174
|
const hasTheme = paths.includes(themePath);
|
|
@@ -1952,7 +2179,7 @@ function findDeckThemeStyle(paths, root, slug) {
|
|
|
1952
2179
|
async function loadDecksConfig(input) {
|
|
1953
2180
|
const path = resolve(input.cwd, input.configFile ?? "hono-decks.config.ts");
|
|
1954
2181
|
if (!existsSync(path)) throw new Error(`Config file not found: ${input.configFile ?? "hono-decks.config.ts"}. Run \`hono-decks init\` first.`);
|
|
1955
|
-
const
|
|
2182
|
+
const result = await build({
|
|
1956
2183
|
absWorkingDir: input.cwd,
|
|
1957
2184
|
entryPoints: [path],
|
|
1958
2185
|
bundle: true,
|
|
@@ -1960,16 +2187,20 @@ async function loadDecksConfig(input) {
|
|
|
1960
2187
|
platform: "node",
|
|
1961
2188
|
target: "node20",
|
|
1962
2189
|
write: false,
|
|
2190
|
+
metafile: true,
|
|
1963
2191
|
logLevel: "silent",
|
|
1964
2192
|
alias: { "hono-decks": resolveRuntimeEntry() }
|
|
1965
|
-
})
|
|
2193
|
+
});
|
|
2194
|
+
const source = result.outputFiles[0]?.text;
|
|
1966
2195
|
if (!source) throw new Error(`Could not load config file: ${path}`);
|
|
1967
2196
|
const config = (await import(`data:text/javascript;charset=utf-8,${encodeURIComponent(source)}#${Date.now()}`)).default;
|
|
1968
2197
|
if (!config || typeof config !== "object") throw new Error(`Config must have a default export: ${path}`);
|
|
1969
2198
|
if (typeof config.mountPath !== "string") throw new Error(`Config mountPath must be a string: ${path}`);
|
|
2199
|
+
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
2200
|
return {
|
|
1971
2201
|
path,
|
|
1972
2202
|
config,
|
|
2203
|
+
dependencies: Object.keys(result.metafile.inputs).map((inputPath) => resolve(input.cwd, inputPath)),
|
|
1973
2204
|
root: config.build?.root ?? "decks",
|
|
1974
2205
|
outDir: config.build?.outDir ?? "src/generated",
|
|
1975
2206
|
ogpCacheFile: config.build?.ogpCacheFile
|
|
@@ -1991,6 +2222,10 @@ Commands:
|
|
|
1991
2222
|
compile, build Compile decks using the shared config.
|
|
1992
2223
|
|
|
1993
2224
|
Options:
|
|
2225
|
+
--install Install missing dependencies for --framework void.
|
|
2226
|
+
--adapter <specifier> Compatible Hono Markdown adapter version or tarball; requires --install.
|
|
2227
|
+
--framework <name> Vite integration: hono, honox, or void.
|
|
2228
|
+
--entry <path> Hono app entry for --framework hono; otherwise auto-detect.
|
|
1994
2229
|
--config <path> Config file. Default: hono-decks.config.ts
|
|
1995
2230
|
--out <path> Facade output for init. Default: src/decks.ts
|
|
1996
2231
|
--generated <path> Generated module import for init. Default: ./generated/decks
|
|
@@ -2032,6 +2267,7 @@ async function runHonoDecksCli(input) {
|
|
|
2032
2267
|
out: loaded.outDir,
|
|
2033
2268
|
mountPath: loaded.config.mountPath,
|
|
2034
2269
|
ogpCacheFile: loaded.ogpCacheFile,
|
|
2270
|
+
markdown: loaded.config.build?.markdown,
|
|
2035
2271
|
refreshOgp: parsed.options.refreshOgp
|
|
2036
2272
|
});
|
|
2037
2273
|
stdout(`Compiled ${manifest.decks.length} decks to ${loaded.outDir}`);
|
|
@@ -2112,6 +2348,15 @@ async function runInitCommand(input, args, stdout, stderr) {
|
|
|
2112
2348
|
stderr(USAGE);
|
|
2113
2349
|
return { exitCode: 1 };
|
|
2114
2350
|
}
|
|
2351
|
+
if (parsed.options.framework) try {
|
|
2352
|
+
const { initializeVite } = await import("./init-vite-EA_L4k2H.js");
|
|
2353
|
+
const sampleSlug = await initializeVite(input.cwd, parsed.options.framework, parsed.options.configFile, parsed.options.entry, parsed.options.install ? { adapter: parsed.options.adapter } : void 0);
|
|
2354
|
+
stdout(`Initialized ${parsed.options.framework} decks at /slides/${sampleSlug}. Run your usual dev or build command; no manual compile is needed.`);
|
|
2355
|
+
return { exitCode: 0 };
|
|
2356
|
+
} catch (error) {
|
|
2357
|
+
stderr(error instanceof Error ? error.message : String(error));
|
|
2358
|
+
return { exitCode: 1 };
|
|
2359
|
+
}
|
|
2115
2360
|
const configFile = normalizeOutputFile(parsed.options.configFile ?? "hono-decks.config.ts");
|
|
2116
2361
|
const out = normalizeOutputFile(parsed.options.out ?? "src/decks.ts");
|
|
2117
2362
|
try {
|
|
@@ -2169,13 +2414,26 @@ function parseInitArgs(args) {
|
|
|
2169
2414
|
options,
|
|
2170
2415
|
help: true
|
|
2171
2416
|
};
|
|
2172
|
-
if (arg === "--
|
|
2417
|
+
if (arg === "--install") {
|
|
2418
|
+
options.install = true;
|
|
2419
|
+
continue;
|
|
2420
|
+
}
|
|
2421
|
+
if (arg === "--adapter" || arg === "--config" || arg === "--out" || arg === "--generated" || arg === "--framework" || arg === "--entry") {
|
|
2173
2422
|
const value = args[index + 1];
|
|
2174
2423
|
if (!value || value.startsWith("--")) return {
|
|
2175
2424
|
options,
|
|
2176
2425
|
error: `Missing value for ${arg}`
|
|
2177
2426
|
};
|
|
2427
|
+
if (arg === "--framework") {
|
|
2428
|
+
if (value !== "void" && value !== "hono" && value !== "honox") return {
|
|
2429
|
+
options,
|
|
2430
|
+
error: `Unsupported framework: ${value}. Expected hono, honox, or void.`
|
|
2431
|
+
};
|
|
2432
|
+
options.framework = value;
|
|
2433
|
+
}
|
|
2434
|
+
if (arg === "--adapter") options.adapter = value;
|
|
2178
2435
|
if (arg === "--config") options.configFile = value;
|
|
2436
|
+
if (arg === "--entry") options.entry = value;
|
|
2179
2437
|
if (arg === "--out") options.out = value;
|
|
2180
2438
|
if (arg === "--generated") options.generated = value;
|
|
2181
2439
|
index += 1;
|
|
@@ -2186,14 +2444,26 @@ function parseInitArgs(args) {
|
|
|
2186
2444
|
error: `Unknown option: ${arg}`
|
|
2187
2445
|
};
|
|
2188
2446
|
}
|
|
2447
|
+
if (options.install && options.framework !== "void" || options.adapter && !options.install) return {
|
|
2448
|
+
options,
|
|
2449
|
+
error: "--install requires --framework void; --adapter also requires --install."
|
|
2450
|
+
};
|
|
2451
|
+
if (options.entry && options.framework !== "hono") return {
|
|
2452
|
+
options,
|
|
2453
|
+
error: "--entry requires --framework hono."
|
|
2454
|
+
};
|
|
2455
|
+
if (options.framework && (options.out || options.generated)) return {
|
|
2456
|
+
options,
|
|
2457
|
+
error: "Framework init uses virtual modules; --out and --generated are only supported by generic init."
|
|
2458
|
+
};
|
|
2189
2459
|
return { options };
|
|
2190
2460
|
}
|
|
2191
2461
|
async function writeInitialFiles(input) {
|
|
2192
2462
|
for (const path of [input.configFile, input.out]) if (existsSync(join(input.cwd, path))) throw new Error(`Refusing to overwrite existing file: ${path}`);
|
|
2193
2463
|
const configPath = join(input.cwd, input.configFile);
|
|
2194
2464
|
const facadePath = join(input.cwd, input.out);
|
|
2195
|
-
await mkdir(dirname(configPath), { recursive: true });
|
|
2196
|
-
await mkdir(dirname(facadePath), { recursive: true });
|
|
2465
|
+
await mkdir(dirname$1(configPath), { recursive: true });
|
|
2466
|
+
await mkdir(dirname$1(facadePath), { recursive: true });
|
|
2197
2467
|
await writeFile(configPath, emitDecksConfig(), "utf8");
|
|
2198
2468
|
await writeFile(facadePath, emitDecksFacade(input.generated, relativeImport(input.out, input.configFile)), "utf8");
|
|
2199
2469
|
}
|
|
@@ -2218,7 +2488,7 @@ export const decks = createDecks(config);
|
|
|
2218
2488
|
`;
|
|
2219
2489
|
}
|
|
2220
2490
|
function relativeImport(fromFile, toFile) {
|
|
2221
|
-
const path = normalizePath(relative(dirname(fromFile), toFile)).replace(/\.(?:[cm]?[jt]sx?)$/, "");
|
|
2491
|
+
const path = normalizePath(relative(dirname$1(fromFile), toFile)).replace(/\.(?:[cm]?[jt]sx?)$/, "");
|
|
2222
2492
|
return path.startsWith(".") ? path : `./${path}`;
|
|
2223
2493
|
}
|
|
2224
2494
|
function normalizeOutputFile(path) {
|
|
@@ -2230,7 +2500,7 @@ function normalizeOutputFile(path) {
|
|
|
2230
2500
|
function normalizePath(path) {
|
|
2231
2501
|
return path.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
|
|
2232
2502
|
}
|
|
2233
|
-
function dirname(path) {
|
|
2503
|
+
function dirname$1(path) {
|
|
2234
2504
|
const normalized = normalizePath(path);
|
|
2235
2505
|
return normalized.includes("/") ? normalized.slice(0, normalized.lastIndexOf("/")) : ".";
|
|
2236
2506
|
}
|