hono-decks 0.2.0 → 0.2.2

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/dist/bin.js CHANGED
@@ -1,974 +1,983 @@
1
1
  #!/usr/bin/env node
2
-
3
- // src/node/cli.ts
4
- import { existsSync as existsSync4, watch as watch2 } from "fs";
5
- import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
6
- import { join as join4, relative as relative3 } from "path";
7
-
8
- // src/node/compile-decks.ts
9
- import { mkdir, readFile as readFile3, writeFile } from "fs/promises";
10
- import { join as join3 } from "path";
11
-
12
- // src/routing/file-routing.ts
2
+ import { existsSync, watch } from "node:fs";
3
+ import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
4
+ import { join, relative, resolve } from "node:path";
5
+ import { unified } from "unified";
6
+ import remarkGfm from "remark-gfm";
7
+ import remarkMdx from "remark-mdx";
8
+ import remarkParse from "remark-parse";
9
+ import { compile } from "@mdx-js/mdx";
10
+ import remarkDirective from "remark-directive";
11
+ import { codeToHtml } from "shiki";
12
+ import { fileURLToPath } from "node:url";
13
+ import { build } from "esbuild";
14
+ import { lookup } from "node:dns/promises";
15
+ import { isIP } from "node:net";
16
+ //#region src/routing/file-routing.ts
13
17
  function resolveDeckFiles(paths, root = "decks") {
14
- const normalizedRoot = normalizePath(root).replace(/\/$/, "");
15
- const decks = /* @__PURE__ */ new Map();
16
- const assetsBySlug = /* @__PURE__ */ new Map();
17
- for (const inputPath of paths.map(normalizePath).sort()) {
18
- assertInsideRoot(inputPath, normalizedRoot);
19
- const relative4 = inputPath.slice(normalizedRoot.length + 1);
20
- const segments = relative4.split("/");
21
- if (segments.length === 1 && segments[0].endsWith(".mdx")) {
22
- const slug = segments[0].replace(/\.mdx$/, "");
23
- addDeck(decks, {
24
- slug,
25
- sourcePath: inputPath,
26
- kind: "single-file",
27
- assetPaths: []
28
- });
29
- continue;
30
- }
31
- if (segments.length === 2 && segments[1] === "deck.mdx") {
32
- addDeck(decks, {
33
- slug: segments[0],
34
- sourcePath: inputPath,
35
- kind: "directory",
36
- assetPaths: assetsBySlug.get(segments[0]) ?? []
37
- });
38
- continue;
39
- }
40
- if (segments.length >= 3 && segments[1] === "assets") {
41
- const slug = segments[0];
42
- const assetPaths = assetsBySlug.get(slug) ?? [];
43
- assetPaths.push(inputPath);
44
- assetsBySlug.set(slug, assetPaths);
45
- const deck = decks.get(slug);
46
- if (deck?.kind === "directory") deck.assetPaths = assetPaths;
47
- continue;
48
- }
49
- if (inputPath.endsWith(".mdx")) {
50
- throw new Error(`Nested deck slugs are not supported in this slice: ${inputPath}`);
51
- }
52
- }
53
- return [...decks.values()].map((deck) => ({
54
- ...deck,
55
- assetPaths: [...deck.assetPaths].sort()
56
- }));
18
+ const normalizedRoot = normalizePath$4(root).replace(/\/$/, "");
19
+ const decks = /* @__PURE__ */ new Map();
20
+ const assetsBySlug = /* @__PURE__ */ new Map();
21
+ for (const inputPath of paths.map(normalizePath$4).sort()) {
22
+ assertInsideRoot(inputPath, normalizedRoot);
23
+ const segments = inputPath.slice(normalizedRoot.length + 1).split("/");
24
+ if (segments.length === 1 && segments[0].endsWith(".mdx")) {
25
+ addDeck(decks, {
26
+ slug: segments[0].replace(/\.mdx$/, ""),
27
+ sourcePath: inputPath,
28
+ kind: "single-file",
29
+ assetPaths: []
30
+ });
31
+ continue;
32
+ }
33
+ if (segments.length === 2 && segments[1] === "deck.mdx") {
34
+ addDeck(decks, {
35
+ slug: segments[0],
36
+ sourcePath: inputPath,
37
+ kind: "directory",
38
+ assetPaths: assetsBySlug.get(segments[0]) ?? []
39
+ });
40
+ continue;
41
+ }
42
+ if (segments.length >= 3 && segments[1] === "assets") {
43
+ const slug = segments[0];
44
+ const assetPaths = assetsBySlug.get(slug) ?? [];
45
+ assetPaths.push(inputPath);
46
+ assetsBySlug.set(slug, assetPaths);
47
+ const deck = decks.get(slug);
48
+ if (deck?.kind === "directory") deck.assetPaths = assetPaths;
49
+ continue;
50
+ }
51
+ if (inputPath.endsWith(".mdx")) throw new Error(`Nested deck slugs are not supported in this slice: ${inputPath}`);
52
+ }
53
+ return [...decks.values()].map((deck) => ({
54
+ ...deck,
55
+ assetPaths: [...deck.assetPaths].sort()
56
+ }));
57
57
  }
58
58
  function addDeck(decks, next) {
59
- const current = decks.get(next.slug);
60
- if (current) {
61
- throw new Error(`Deck slug conflict for "${next.slug}": ${current.sourcePath} and ${next.sourcePath}`);
62
- }
63
- decks.set(next.slug, next);
59
+ const current = decks.get(next.slug);
60
+ if (current) throw new Error(`Deck slug conflict for "${next.slug}": ${current.sourcePath} and ${next.sourcePath}`);
61
+ decks.set(next.slug, next);
64
62
  }
65
63
  function assertInsideRoot(path, root) {
66
- if (path.includes("/../") || path.endsWith("/..") || path.startsWith("../")) {
67
- throw new Error(`Deck path escapes the root: ${path}`);
68
- }
69
- if (path !== root && !path.startsWith(`${root}/`)) {
70
- throw new Error(`Deck path is outside ${root}: ${path}`);
71
- }
72
- }
73
- function normalizePath(path) {
74
- return path.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
75
- }
76
-
77
- // src/deck/model.ts
78
- var SLIDE_TRANSITIONS = [
79
- "none",
80
- "fade",
81
- "fade-out",
82
- "slide-left",
83
- "slide-right",
84
- "slide-up",
85
- "slide-down",
86
- "view-transition"
64
+ if (path.includes("/../") || path.endsWith("/..") || path.startsWith("../")) throw new Error(`Deck path escapes the root: ${path}`);
65
+ if (path !== root && !path.startsWith(`${root}/`)) throw new Error(`Deck path is outside ${root}: ${path}`);
66
+ }
67
+ function normalizePath$4(path) {
68
+ return path.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
69
+ }
70
+ //#endregion
71
+ //#region src/deck/model.ts
72
+ const SLIDE_TRANSITIONS = [
73
+ "none",
74
+ "fade",
75
+ "fade-out",
76
+ "slide-left",
77
+ "slide-right",
78
+ "slide-up",
79
+ "slide-down",
80
+ "view-transition"
87
81
  ];
88
82
  var CompileError = class extends Error {
89
- constructor(message, code) {
90
- super(message);
91
- this.code = code;
92
- this.name = "CompileError";
93
- }
83
+ code;
84
+ constructor(message, code) {
85
+ super(message);
86
+ this.code = code;
87
+ this.name = "CompileError";
88
+ }
94
89
  };
95
-
96
- // src/deck/frontmatter.ts
90
+ //#endregion
91
+ //#region src/deck/frontmatter.ts
97
92
  function readFrontmatter(source) {
98
- const normalized = source.replace(/\r\n/g, "\n").trimStart();
99
- if (!normalized.startsWith("---\n")) return { attrs: {}, body: source.trim() };
100
- const end = normalized.indexOf("\n---", 4);
101
- if (end === -1) throw new CompileError("Frontmatter block is not closed.", "frontmatter-unclosed");
102
- const rawAttrs = normalized.slice(4, end).trim();
103
- const body = normalized.slice(end + 4).replace(/^\n/, "").trim();
104
- return { attrs: parseFrontmatterAttrs(rawAttrs), body };
93
+ const normalized = source.replace(/\r\n/g, "\n").trimStart();
94
+ if (!normalized.startsWith("---\n")) return {
95
+ attrs: {},
96
+ body: source.trim()
97
+ };
98
+ const end = normalized.indexOf("\n---", 4);
99
+ if (end === -1) throw new CompileError("Frontmatter block is not closed.", "frontmatter-unclosed");
100
+ const rawAttrs = normalized.slice(4, end).trim();
101
+ const body = normalized.slice(end + 4).replace(/^\n/, "").trim();
102
+ return {
103
+ attrs: parseFrontmatterAttrs(rawAttrs),
104
+ body
105
+ };
105
106
  }
106
107
  function toDeckFrontmatter(attrs, warnings) {
107
- const meta = { ...attrs };
108
- const deck = { meta };
109
- deck.title = takeString(meta, "title");
110
- deck.description = takeString(meta, "description");
111
- deck.author = takeString(meta, "author");
112
- deck.date = takeString(meta, "date");
113
- deck.theme = takeString(meta, "theme");
114
- deck.transition = takeKnownStringWithWarning(meta, "transition", SLIDE_TRANSITIONS, "none", warnings, "unknown-transition");
115
- deck.transitionDuration = takeTransitionDuration(meta, warnings);
116
- deck.transitionEasing = takeTransitionEasing(meta, warnings);
117
- deck.assets = takeStringOrStringArray(meta, "assets");
118
- deck.draft = takeBoolean(meta, "draft");
119
- deck.presenter = takeBoolean(meta, "presenter");
120
- const tags = meta.tags;
121
- if (Array.isArray(tags)) {
122
- deck.tags = tags.map(String);
123
- delete meta.tags;
124
- }
125
- return deck;
108
+ const meta = { ...attrs };
109
+ const deck = { meta };
110
+ deck.title = takeString(meta, "title");
111
+ deck.description = takeString(meta, "description");
112
+ deck.author = takeString(meta, "author");
113
+ deck.date = takeString(meta, "date");
114
+ deck.theme = takeString(meta, "theme");
115
+ deck.transition = takeKnownStringWithWarning(meta, "transition", SLIDE_TRANSITIONS, "none", warnings, "unknown-transition");
116
+ deck.transitionDuration = takeTransitionDuration(meta, warnings);
117
+ deck.transitionEasing = takeTransitionEasing(meta, warnings);
118
+ deck.assets = takeStringOrStringArray(meta, "assets");
119
+ deck.draft = takeBoolean(meta, "draft");
120
+ deck.presenter = takeBoolean(meta, "presenter");
121
+ const tags = meta.tags;
122
+ if (Array.isArray(tags)) {
123
+ deck.tags = tags.map(String);
124
+ delete meta.tags;
125
+ }
126
+ return deck;
126
127
  }
127
128
  function toSlideFrontmatter(attrs, warnings, input) {
128
- const meta = { ...attrs };
129
- const slide = {
130
- title: takeString(meta, "title") ?? input.fallbackTitle,
131
- layout: takeString(meta, "layout") ?? input.fallbackLayout,
132
- className: takeString(meta, "class") ?? input.fallbackClassName,
133
- notes: takeString(meta, "notes"),
134
- background: takeString(meta, "background"),
135
- transition: takeKnownStringWithWarning(
136
- meta,
137
- "transition",
138
- SLIDE_TRANSITIONS,
139
- "none",
140
- warnings,
141
- "unknown-transition",
142
- input.slideIndex
143
- ) ?? input.fallbackTransition,
144
- transitionDuration: takeTransitionDuration(meta, warnings, input.slideIndex) ?? input.fallbackTransitionDuration,
145
- transitionEasing: takeTransitionEasing(meta, warnings, input.slideIndex) ?? input.fallbackTransitionEasing,
146
- meta
147
- };
148
- return slide;
129
+ const meta = { ...attrs };
130
+ return {
131
+ title: takeString(meta, "title") ?? input.fallbackTitle,
132
+ layout: takeString(meta, "layout") ?? input.fallbackLayout,
133
+ className: takeString(meta, "class") ?? input.fallbackClassName,
134
+ notes: takeString(meta, "notes"),
135
+ background: takeString(meta, "background"),
136
+ transition: takeKnownStringWithWarning(meta, "transition", SLIDE_TRANSITIONS, "none", warnings, "unknown-transition", input.slideIndex) ?? input.fallbackTransition,
137
+ transitionDuration: takeTransitionDuration(meta, warnings, input.slideIndex) ?? input.fallbackTransitionDuration,
138
+ transitionEasing: takeTransitionEasing(meta, warnings, input.slideIndex) ?? input.fallbackTransitionEasing,
139
+ meta
140
+ };
149
141
  }
150
142
  function addUnknownFrontmatterWarnings(warnings, meta, scope, slideIndex) {
151
- for (const key of Object.keys(meta)) {
152
- warnings.push({
153
- code: "unknown-frontmatter-key",
154
- message: `Unknown ${scope} frontmatter key "${key}" is preserved in meta.`,
155
- ...slideIndex !== void 0 ? { slideIndex } : {}
156
- });
157
- }
143
+ for (const key of Object.keys(meta)) warnings.push({
144
+ code: "unknown-frontmatter-key",
145
+ message: `Unknown ${scope} frontmatter key "${key}" is preserved in meta.`,
146
+ ...slideIndex !== void 0 ? { slideIndex } : {}
147
+ });
158
148
  }
159
149
  function splitSlideSources(source) {
160
- const lines = source.replace(/\r\n/g, "\n").trim().split("\n");
161
- const slides = [];
162
- let current = [];
163
- let cursor = 0;
164
- while (cursor < lines.length) {
165
- if (isFence(lines[cursor])) {
166
- if (looksLikeFrontmatterFence(lines, cursor) && findFrontmatterEnd(lines, cursor) === -1) {
167
- throw new CompileError("Frontmatter block is not closed.", "frontmatter-unclosed");
168
- }
169
- if (isFrontmatterStart(lines, cursor)) {
170
- if (hasMeaningfulLines(current)) {
171
- slides.push(current.join("\n").trim());
172
- }
173
- current = [];
174
- const frontmatterEnd = findFrontmatterEnd(lines, cursor);
175
- current.push(...lines.slice(cursor, frontmatterEnd + 1));
176
- cursor = frontmatterEnd + 1;
177
- continue;
178
- }
179
- if (hasMeaningfulLines(current)) {
180
- slides.push(current.join("\n").trim());
181
- current = [];
182
- }
183
- cursor += 1;
184
- continue;
185
- }
186
- current.push(lines[cursor]);
187
- cursor += 1;
188
- }
189
- if (hasMeaningfulLines(current)) {
190
- slides.push(current.join("\n").trim());
191
- }
192
- return slides;
150
+ const lines = source.replace(/\r\n/g, "\n").trim().split("\n");
151
+ const slides = [];
152
+ let current = [];
153
+ let cursor = 0;
154
+ while (cursor < lines.length) {
155
+ if (isFence(lines[cursor])) {
156
+ if (looksLikeFrontmatterFence(lines, cursor) && findFrontmatterEnd(lines, cursor) === -1) throw new CompileError("Frontmatter block is not closed.", "frontmatter-unclosed");
157
+ if (isFrontmatterStart(lines, cursor)) {
158
+ if (hasMeaningfulLines(current)) slides.push(current.join("\n").trim());
159
+ current = [];
160
+ const frontmatterEnd = findFrontmatterEnd(lines, cursor);
161
+ current.push(...lines.slice(cursor, frontmatterEnd + 1));
162
+ cursor = frontmatterEnd + 1;
163
+ continue;
164
+ }
165
+ if (hasMeaningfulLines(current)) {
166
+ slides.push(current.join("\n").trim());
167
+ current = [];
168
+ }
169
+ cursor += 1;
170
+ continue;
171
+ }
172
+ current.push(lines[cursor]);
173
+ cursor += 1;
174
+ }
175
+ if (hasMeaningfulLines(current)) slides.push(current.join("\n").trim());
176
+ return slides;
193
177
  }
194
178
  function parseFrontmatterAttrs(source) {
195
- const attrs = {};
196
- const lines = source.split("\n");
197
- for (let index = 0; index < lines.length; index += 1) {
198
- const line = lines[index];
199
- const match = /^([A-Za-z_][A-Za-z0-9_-]*):\s*(.*)$/.exec(line);
200
- if (!match) {
201
- if (line.trim()) {
202
- throw new CompileError(`Invalid frontmatter line ${index + 1}: "${line.trim()}"`, "frontmatter-invalid-line");
203
- }
204
- continue;
205
- }
206
- const key = match[1];
207
- const value = match[2];
208
- if (value === "|") {
209
- const block = [];
210
- index += 1;
211
- while (index < lines.length && /^\s+/.test(lines[index])) {
212
- block.push(lines[index].trim());
213
- index += 1;
214
- }
215
- index -= 1;
216
- attrs[key] = block.join("\n").trim();
217
- continue;
218
- }
219
- if (value.trim() === "") {
220
- const nested = [];
221
- index += 1;
222
- while (index < lines.length && /^\s+/.test(lines[index])) {
223
- nested.push(lines[index]);
224
- index += 1;
225
- }
226
- index -= 1;
227
- attrs[key] = parseNestedFrontmatterValue(nested);
228
- continue;
229
- }
230
- attrs[key] = parseScalar(value);
231
- }
232
- return attrs;
179
+ const attrs = {};
180
+ const lines = source.split("\n");
181
+ for (let index = 0; index < lines.length; index += 1) {
182
+ const line = lines[index];
183
+ const match = /^([A-Za-z_][A-Za-z0-9_-]*):\s*(.*)$/.exec(line);
184
+ if (!match) {
185
+ if (line.trim()) throw new CompileError(`Invalid frontmatter line ${index + 1}: "${line.trim()}"`, "frontmatter-invalid-line");
186
+ continue;
187
+ }
188
+ const key = match[1];
189
+ const value = match[2];
190
+ if (value === "|") {
191
+ const block = [];
192
+ index += 1;
193
+ while (index < lines.length && /^\s+/.test(lines[index])) {
194
+ block.push(lines[index].trim());
195
+ index += 1;
196
+ }
197
+ index -= 1;
198
+ attrs[key] = block.join("\n").trim();
199
+ continue;
200
+ }
201
+ if (value.trim() === "") {
202
+ const nested = [];
203
+ index += 1;
204
+ while (index < lines.length && /^\s+/.test(lines[index])) {
205
+ nested.push(lines[index]);
206
+ index += 1;
207
+ }
208
+ index -= 1;
209
+ attrs[key] = parseNestedFrontmatterValue(nested);
210
+ continue;
211
+ }
212
+ attrs[key] = parseScalar(value);
213
+ }
214
+ return attrs;
233
215
  }
234
216
  function isFrontmatterStart(lines, index) {
235
- return looksLikeFrontmatterFence(lines, index) && findFrontmatterEnd(lines, index) > index;
217
+ return looksLikeFrontmatterFence(lines, index) && findFrontmatterEnd(lines, index) > index;
236
218
  }
237
219
  function looksLikeFrontmatterFence(lines, index) {
238
- if (!isFence(lines[index])) return false;
239
- const next = lines[index + 1];
240
- return next != null && /^([A-Za-z_][A-Za-z0-9_-]*):\s*/.test(next);
220
+ if (!isFence(lines[index])) return false;
221
+ const next = lines[index + 1];
222
+ return next != null && /^([A-Za-z_][A-Za-z0-9_-]*):\s*/.test(next);
241
223
  }
242
224
  function findFrontmatterEnd(lines, start) {
243
- for (let index = start + 1; index < lines.length; index += 1) {
244
- if (isFence(lines[index])) return index;
245
- }
246
- return -1;
225
+ for (let index = start + 1; index < lines.length; index += 1) if (isFence(lines[index])) return index;
226
+ return -1;
247
227
  }
248
228
  function isFence(line) {
249
- return /^---\s*$/.test(line);
229
+ return /^---\s*$/.test(line);
250
230
  }
251
231
  function hasMeaningfulLines(lines) {
252
- return lines.some((line) => line.trim() !== "");
232
+ return lines.some((line) => line.trim() !== "");
253
233
  }
254
234
  function parseNestedFrontmatterValue(lines) {
255
- const meaningful = lines.map((line) => line.trim()).filter(Boolean);
256
- if (meaningful.every((line) => line.startsWith("- "))) {
257
- return meaningful.map((line) => parseScalar(line.slice(2)));
258
- }
259
- const object = {};
260
- for (const line of meaningful) {
261
- const match = /^([A-Za-z_][A-Za-z0-9_-]*):\s*(.*)$/.exec(line);
262
- if (!match) {
263
- throw new CompileError(`Invalid nested frontmatter line: "${line}"`, "frontmatter-invalid-line");
264
- }
265
- object[match[1]] = parseScalar(match[2]);
266
- }
267
- return object;
235
+ const meaningful = lines.map((line) => line.trim()).filter(Boolean);
236
+ if (meaningful.every((line) => line.startsWith("- "))) return meaningful.map((line) => parseScalar(line.slice(2)));
237
+ const object = {};
238
+ for (const line of meaningful) {
239
+ const match = /^([A-Za-z_][A-Za-z0-9_-]*):\s*(.*)$/.exec(line);
240
+ if (!match) throw new CompileError(`Invalid nested frontmatter line: "${line}"`, "frontmatter-invalid-line");
241
+ object[match[1]] = parseScalar(match[2]);
242
+ }
243
+ return object;
268
244
  }
269
245
  function parseScalar(value) {
270
- const trimmed = value.trim().replace(/^['"]|['"]$/g, "");
271
- if (trimmed === "true") return true;
272
- if (trimmed === "false") return false;
273
- if (/^\[.*\]$/.test(trimmed)) {
274
- return trimmed.slice(1, -1).split(",").map((item) => item.trim().replace(/^['"]|['"]$/g, "")).filter(Boolean);
275
- }
276
- return trimmed;
246
+ const trimmed = value.trim().replace(/^['"]|['"]$/g, "");
247
+ if (trimmed === "true") return true;
248
+ if (trimmed === "false") return false;
249
+ if (/^\[.*\]$/.test(trimmed)) return trimmed.slice(1, -1).split(",").map((item) => item.trim().replace(/^['"]|['"]$/g, "")).filter(Boolean);
250
+ return trimmed;
277
251
  }
278
252
  function takeString(attrs, key) {
279
- const value = attrs[key];
280
- if (typeof value !== "string") return void 0;
281
- delete attrs[key];
282
- return value;
253
+ const value = attrs[key];
254
+ if (typeof value !== "string") return void 0;
255
+ delete attrs[key];
256
+ return value;
283
257
  }
284
258
  function takeKnownStringWithWarning(attrs, key, values, fallback, warnings, code, slideIndex) {
285
- const value = attrs[key];
286
- delete attrs[key];
287
- if (value === void 0) return void 0;
288
- if (typeof value === "string" && values.includes(value)) return value;
289
- warnings.push({
290
- code,
291
- message: `Unknown ${key} value "${String(value)}"; using ${fallback}.`,
292
- ...slideIndex !== void 0 ? { slideIndex } : {}
293
- });
294
- return fallback;
259
+ const value = attrs[key];
260
+ delete attrs[key];
261
+ if (value === void 0) return void 0;
262
+ if (typeof value === "string" && values.includes(value)) return value;
263
+ warnings.push({
264
+ code,
265
+ message: `Unknown ${key} value "${String(value)}"; using ${fallback}.`,
266
+ ...slideIndex !== void 0 ? { slideIndex } : {}
267
+ });
268
+ return fallback;
295
269
  }
296
270
  function takeTransitionDuration(attrs, warnings, slideIndex) {
297
- const value = attrs.transitionDuration;
298
- delete attrs.transitionDuration;
299
- if (value === void 0) return void 0;
300
- if (typeof value === "string" && isValidTransitionDuration(value)) return value;
301
- warnings.push({
302
- code: "invalid-transition-duration",
303
- message: `Invalid transitionDuration value "${String(value)}"; ignoring it.`,
304
- ...slideIndex !== void 0 ? { slideIndex } : {}
305
- });
306
- return void 0;
271
+ const value = attrs.transitionDuration;
272
+ delete attrs.transitionDuration;
273
+ if (value === void 0) return void 0;
274
+ if (typeof value === "string" && isValidTransitionDuration(value)) return value;
275
+ warnings.push({
276
+ code: "invalid-transition-duration",
277
+ message: `Invalid transitionDuration value "${String(value)}"; ignoring it.`,
278
+ ...slideIndex !== void 0 ? { slideIndex } : {}
279
+ });
307
280
  }
308
281
  function takeTransitionEasing(attrs, warnings, slideIndex) {
309
- const value = attrs.transitionEasing;
310
- delete attrs.transitionEasing;
311
- if (value === void 0) return void 0;
312
- if (typeof value === "string" && isValidTransitionEasing(value)) return value;
313
- warnings.push({
314
- code: "invalid-transition-easing",
315
- message: `Invalid transitionEasing value "${String(value)}"; ignoring it.`,
316
- ...slideIndex !== void 0 ? { slideIndex } : {}
317
- });
318
- return void 0;
282
+ const value = attrs.transitionEasing;
283
+ delete attrs.transitionEasing;
284
+ if (value === void 0) return void 0;
285
+ if (typeof value === "string" && isValidTransitionEasing(value)) return value;
286
+ warnings.push({
287
+ code: "invalid-transition-easing",
288
+ message: `Invalid transitionEasing value "${String(value)}"; ignoring it.`,
289
+ ...slideIndex !== void 0 ? { slideIndex } : {}
290
+ });
319
291
  }
320
292
  function isValidTransitionDuration(value) {
321
- return value.split(",").map((item) => item.trim()).every((item) => /^(?:\d+|\d*\.\d+)(?:ms|s)$/.test(item));
293
+ return value.split(",").map((item) => item.trim()).every((item) => /^(?:\d+|\d*\.\d+)(?:ms|s)$/.test(item));
322
294
  }
323
295
  function isValidTransitionEasing(value) {
324
- const easing = value.trim();
325
- return ["linear", "ease", "ease-in", "ease-out", "ease-in-out", "step-start", "step-end"].includes(easing) || /^cubic-bezier\(\s*-?(?:\d+|\d*\.\d+)\s*,\s*-?(?:\d+|\d*\.\d+)\s*,\s*-?(?:\d+|\d*\.\d+)\s*,\s*-?(?:\d+|\d*\.\d+)\s*\)$/.test(
326
- easing
327
- ) || /^steps\(\s*\d+\s*(?:,\s*(?:jump-start|jump-end|jump-none|jump-both|start|end))?\s*\)$/.test(easing) || /^linear\([^)]+\)$/.test(easing);
296
+ const easing = value.trim();
297
+ return [
298
+ "linear",
299
+ "ease",
300
+ "ease-in",
301
+ "ease-out",
302
+ "ease-in-out",
303
+ "step-start",
304
+ "step-end"
305
+ ].includes(easing) || /^cubic-bezier\(\s*-?(?:\d+|\d*\.\d+)\s*,\s*-?(?:\d+|\d*\.\d+)\s*,\s*-?(?:\d+|\d*\.\d+)\s*,\s*-?(?:\d+|\d*\.\d+)\s*\)$/.test(easing) || /^steps\(\s*\d+\s*(?:,\s*(?:jump-start|jump-end|jump-none|jump-both|start|end))?\s*\)$/.test(easing) || /^linear\([^)]+\)$/.test(easing);
328
306
  }
329
307
  function takeStringOrStringArray(attrs, key) {
330
- const value = attrs[key];
331
- if (typeof value === "string") {
332
- delete attrs[key];
333
- return value;
334
- }
335
- if (Array.isArray(value)) {
336
- delete attrs[key];
337
- return value.map(String);
338
- }
339
- return void 0;
308
+ const value = attrs[key];
309
+ if (typeof value === "string") {
310
+ delete attrs[key];
311
+ return value;
312
+ }
313
+ if (Array.isArray(value)) {
314
+ delete attrs[key];
315
+ return value.map(String);
316
+ }
340
317
  }
341
318
  function takeBoolean(attrs, key) {
342
- const value = attrs[key];
343
- if (typeof value !== "boolean") return void 0;
344
- delete attrs[key];
345
- return value;
319
+ const value = attrs[key];
320
+ if (typeof value !== "boolean") return void 0;
321
+ delete attrs[key];
322
+ return value;
346
323
  }
347
-
348
- // src/deck/assets.ts
324
+ //#endregion
325
+ //#region src/deck/assets.ts
349
326
  function buildExternalAssetRefs(candidates) {
350
- const refs = /* @__PURE__ */ new Map();
351
- for (const candidate of candidates) {
352
- const type = assetRefType(candidate);
353
- if (!type || refs.has(candidate)) continue;
354
- const contentType = contentTypeForPath(candidate);
355
- refs.set(candidate, {
356
- sourcePath: candidate,
357
- publicPath: candidate,
358
- type,
359
- ...contentType ? { contentType } : {}
360
- });
361
- }
362
- return [...refs.values()];
327
+ const refs = /* @__PURE__ */ new Map();
328
+ for (const candidate of candidates) {
329
+ const type = assetRefType(candidate);
330
+ if (!type || refs.has(candidate)) continue;
331
+ const contentType = contentTypeForPath(candidate);
332
+ refs.set(candidate, {
333
+ sourcePath: candidate,
334
+ publicPath: candidate,
335
+ type,
336
+ ...contentType ? { contentType } : {}
337
+ });
338
+ }
339
+ return [...refs.values()];
363
340
  }
364
341
  function addExternalAssetWarnings(warnings, assets) {
365
- for (const asset of assets) {
366
- if (asset.type !== "remote" && asset.type !== "r2") continue;
367
- const label = asset.type === "r2" ? "R2" : "Remote";
368
- warnings.push({
369
- code: "external-asset-unverified",
370
- message: `${label} asset existence cannot be verified at compile time: ${asset.sourcePath}`
371
- });
372
- }
342
+ for (const asset of assets) {
343
+ if (asset.type !== "remote" && asset.type !== "r2") continue;
344
+ const label = asset.type === "r2" ? "R2" : "Remote";
345
+ warnings.push({
346
+ code: "external-asset-unverified",
347
+ message: `${label} asset existence cannot be verified at compile time: ${asset.sourcePath}`
348
+ });
349
+ }
373
350
  }
374
351
  function collectMarkdownAssetCandidates(markdown) {
375
- const candidates = [];
376
- for (const match of markdown.matchAll(/^\s*(?:background|image|src|asset):\s*['"]?([^'"\n]+)['"]?\s*$/gim)) {
377
- candidates.push(match[1].trim());
378
- }
379
- for (const match of markdown.matchAll(/!\[[^\]]*\]\(([^)\s]+)(?:\s+["'][^"']*["'])?\)/g)) {
380
- candidates.push(match[1].trim());
381
- }
382
- for (const match of markdown.matchAll(/\b(?:src|href|image|background)=["']([^"']+)["']/g)) {
383
- candidates.push(match[1].trim());
384
- }
385
- return candidates;
352
+ const candidates = [];
353
+ candidates.push(...collectDirectiveAssetCandidates(markdown));
354
+ candidates.push(...collectMarkdownImageCandidates(markdown));
355
+ for (const match of markdown.matchAll(/\b(?:src|href|image|background)=["']([^"']+)["']/g)) candidates.push(match[1].trim());
356
+ return candidates;
386
357
  }
387
358
  function collectFrontmatterAssetCandidates(value) {
388
- if (Array.isArray(value)) return value.map(String).map((item) => item.trim()).filter(Boolean);
389
- if (typeof value === "string") return [value.trim()].filter(Boolean);
390
- return [];
359
+ if (Array.isArray(value)) return value.map(String).map((item) => item.trim()).filter(Boolean);
360
+ if (typeof value === "string") return [value.trim()].filter(Boolean);
361
+ return [];
391
362
  }
392
363
  function contentTypeForPath(path) {
393
- const pathname = path.replace(/\?.*$/, "").toLowerCase();
394
- if (/\.png(?:#.*)?$/.test(pathname)) return "image/png";
395
- if (/\.jpe?g(?:#.*)?$/.test(pathname)) return "image/jpeg";
396
- if (/\.gif(?:#.*)?$/.test(pathname)) return "image/gif";
397
- if (/\.svg(?:#.*)?$/.test(pathname)) return "image/svg+xml";
398
- if (/\.webp(?:#.*)?$/.test(pathname)) return "image/webp";
399
- return void 0;
364
+ const queryIndex = path.indexOf("?");
365
+ const pathname = (queryIndex === -1 ? path : path.slice(0, queryIndex)).toLowerCase();
366
+ if (hasExtension(pathname, ".png")) return "image/png";
367
+ if (hasExtension(pathname, ".jpg") || hasExtension(pathname, ".jpeg")) return "image/jpeg";
368
+ if (hasExtension(pathname, ".gif")) return "image/gif";
369
+ if (hasExtension(pathname, ".svg")) return "image/svg+xml";
370
+ if (hasExtension(pathname, ".webp")) return "image/webp";
371
+ }
372
+ function hasExtension(path, extension) {
373
+ return path.endsWith(extension) || path.includes(`${extension}#`);
374
+ }
375
+ function collectDirectiveAssetCandidates(markdown) {
376
+ const candidates = [];
377
+ for (const sourceLine of markdown.split("\n")) {
378
+ const line = sourceLine.endsWith("\r") ? sourceLine.slice(0, -1).trim() : sourceLine.trim();
379
+ const separator = line.indexOf(":");
380
+ if (separator === -1) continue;
381
+ const key = line.slice(0, separator).toLowerCase();
382
+ if (key !== "background" && key !== "image" && key !== "src" && key !== "asset") continue;
383
+ let value = line.slice(separator + 1).trim();
384
+ if (value.startsWith("\"") || value.startsWith("'")) value = value.slice(1);
385
+ if (value.endsWith("\"") || value.endsWith("'")) value = value.slice(0, -1);
386
+ value = value.trim();
387
+ if (value && !value.includes("\"") && !value.includes("'")) candidates.push(value);
388
+ }
389
+ return candidates;
390
+ }
391
+ function collectMarkdownImageCandidates(markdown) {
392
+ const candidates = [];
393
+ let index = 0;
394
+ while (index < markdown.length - 1) {
395
+ const imageStart = markdown.indexOf("![", index);
396
+ if (imageStart === -1) break;
397
+ const labelEnd = markdown.indexOf("]", imageStart + 2);
398
+ if (labelEnd === -1) break;
399
+ if (markdown[labelEnd + 1] !== "(") {
400
+ index = labelEnd + 1;
401
+ continue;
402
+ }
403
+ let cursor = labelEnd + 2;
404
+ const destinationStart = cursor;
405
+ while (cursor < markdown.length && markdown[cursor] !== ")" && !isMarkdownWhitespace(markdown[cursor])) cursor += 1;
406
+ if (cursor === destinationStart) {
407
+ index = cursor + 1;
408
+ continue;
409
+ }
410
+ const destination = markdown.slice(destinationStart, cursor);
411
+ while (cursor < markdown.length && isMarkdownWhitespace(markdown[cursor])) cursor += 1;
412
+ if (markdown[cursor] !== ")") {
413
+ const quote = markdown[cursor];
414
+ if (quote !== "\"" && quote !== "'") {
415
+ index = cursor + 1;
416
+ continue;
417
+ }
418
+ cursor += 1;
419
+ while (cursor < markdown.length && markdown[cursor] !== "\"" && markdown[cursor] !== "'") cursor += 1;
420
+ if (cursor >= markdown.length) break;
421
+ cursor += 1;
422
+ while (cursor < markdown.length && isMarkdownWhitespace(markdown[cursor])) cursor += 1;
423
+ }
424
+ if (markdown[cursor] === ")") {
425
+ candidates.push(destination);
426
+ index = cursor + 1;
427
+ } else index = cursor + 1;
428
+ }
429
+ return candidates;
430
+ }
431
+ function isMarkdownWhitespace(value) {
432
+ return value === " " || value === " " || value === "\r" || value === "\n";
400
433
  }
401
434
  function assetRefType(value) {
402
- if (/^https?:\/\//i.test(value)) return "remote";
403
- if (/^r2:\/\//i.test(value)) return "r2";
404
- if (value.startsWith("/")) return "public";
405
- return void 0;
435
+ if (/^https?:\/\//i.test(value)) return "remote";
436
+ if (/^r2:\/\//i.test(value)) return "r2";
437
+ if (value.startsWith("/")) return "public";
406
438
  }
407
-
408
- // src/deck/speaker-notes.ts
439
+ //#endregion
440
+ //#region src/deck/speaker-notes.ts
409
441
  function extractMdxCommentSpeakerNotes(source) {
410
- const notes = [];
411
- const body = source.replace(/\{\/\*([\s\S]*?)\*\/\}/g, (_match, rawNote) => {
412
- const note = normalizeSpeakerNote(rawNote);
413
- if (note) notes.push(note);
414
- return "";
415
- });
416
- return {
417
- body,
418
- ...notes.length ? { notes: notes.join("\n\n") } : {}
419
- };
442
+ const notes = [];
443
+ return {
444
+ body: source.replace(/\{\/\*([\s\S]*?)\*\/\}/g, (_match, rawNote) => {
445
+ const note = normalizeSpeakerNote(rawNote);
446
+ if (note) notes.push(note);
447
+ return "";
448
+ }),
449
+ ...notes.length ? { notes: notes.join("\n\n") } : {}
450
+ };
420
451
  }
421
452
  function combineSpeakerNotes(...notes) {
422
- const combined = notes.map((note) => note?.trim()).filter((note) => Boolean(note));
423
- return combined.length ? combined.join("\n\n") : void 0;
453
+ const combined = notes.map((note) => note?.trim()).filter((note) => Boolean(note));
454
+ return combined.length ? combined.join("\n\n") : void 0;
424
455
  }
425
456
  function normalizeSpeakerNote(value) {
426
- return value.replace(/\r\n/g, "\n").split("\n").map((line) => line.trim()).join("\n").trim();
457
+ return value.replace(/\r\n/g, "\n").split("\n").map((line) => line.trim()).join("\n").trim();
427
458
  }
428
-
429
- // src/parser/parser.ts
430
- import { unified } from "unified";
431
- import remarkGfm from "remark-gfm";
432
- import remarkMdx from "remark-mdx";
433
- import remarkParse from "remark-parse";
434
- var slideSeparator = /^---\s*$/m;
459
+ //#endregion
460
+ //#region src/parser/parser.ts
461
+ const slideSeparator = /^---\s*$/m;
435
462
  function parseDeckWithWarnings(markdown) {
436
- const normalized = markdown.replace(/\r\n/g, "\n").trim();
437
- const chunks = normalized.length > 0 ? normalized.split(slideSeparator) : [""];
438
- const parsedSlides = chunks.map((chunk, index) => parseSlide(chunk.trim(), index)).filter(({ slide }) => slide.raw.length > 0 || slide.blocks.length > 0 || slide.nodes.length > 0);
439
- return {
440
- slides: parsedSlides.map(({ slide }) => slide),
441
- warnings: parsedSlides.flatMap(({ warnings }) => warnings)
442
- };
463
+ const normalized = markdown.replace(/\r\n/g, "\n").trim();
464
+ const parsedSlides = (normalized.length > 0 ? normalized.split(slideSeparator) : [""]).map((chunk, index) => parseSlide(chunk.trim(), index)).filter(({ slide }) => slide.raw.length > 0 || slide.blocks.length > 0 || slide.nodes.length > 0);
465
+ return {
466
+ slides: parsedSlides.map(({ slide }) => slide),
467
+ warnings: parsedSlides.flatMap(({ warnings }) => warnings)
468
+ };
443
469
  }
444
470
  function parseSlide(source, index) {
445
- const { attrs, body } = readSlideAttributes(source);
446
- const parsed = parseContent(body, index);
447
- const firstHeading = parsed.blocks.find((block) => block.type === "heading");
448
- return {
449
- slide: {
450
- index,
451
- title: stringAttr(attrs.title) ?? firstHeading?.text,
452
- layout: stringAttr(attrs.layout) ?? (index === 0 ? "cover" : "default"),
453
- className: stringAttr(attrs.class),
454
- blocks: parsed.blocks,
455
- nodes: parsed.nodes,
456
- raw: source
457
- },
458
- warnings: parsed.warnings
459
- };
471
+ const { attrs, body } = readSlideAttributes(source);
472
+ const parsed = parseContent(body, index);
473
+ const firstHeading = parsed.blocks.find((block) => block.type === "heading");
474
+ return {
475
+ slide: {
476
+ index,
477
+ title: stringAttr(attrs.title) ?? firstHeading?.text,
478
+ layout: stringAttr(attrs.layout) ?? (index === 0 ? "cover" : "default"),
479
+ className: stringAttr(attrs.class),
480
+ blocks: parsed.blocks,
481
+ nodes: parsed.nodes,
482
+ raw: source
483
+ },
484
+ warnings: parsed.warnings
485
+ };
460
486
  }
461
487
  function readSlideAttributes(source) {
462
- const lines = source.split("\n");
463
- const attrs = {};
464
- let cursor = 0;
465
- while (cursor < lines.length) {
466
- const line = lines[cursor];
467
- const match = /^(title|layout|class):\s*(.+)$/.exec(line);
468
- if (!match) break;
469
- attrs[match[1]] = match[2].trim().replace(/^['"]|['"]$/g, "");
470
- cursor += 1;
471
- }
472
- return { attrs, body: lines.slice(cursor).join("\n").trim() };
488
+ const lines = source.split("\n");
489
+ const attrs = {};
490
+ let cursor = 0;
491
+ while (cursor < lines.length) {
492
+ const line = lines[cursor];
493
+ const match = /^(title|layout|class):\s*(.+)$/.exec(line);
494
+ if (!match) break;
495
+ attrs[match[1]] = match[2].trim().replace(/^['"]|['"]$/g, "");
496
+ cursor += 1;
497
+ }
498
+ return {
499
+ attrs,
500
+ body: lines.slice(cursor).join("\n").trim()
501
+ };
473
502
  }
474
503
  function parseContent(body, slideIndex) {
475
- const { root, warnings: parseWarnings } = parseMarkdownTree(body, slideIndex);
476
- const blockResults = (root.children ?? []).map((child) => toBlocks(child, slideIndex, body));
477
- const nodeResults = (root.children ?? []).map((child) => toNodes(child, slideIndex, body));
478
- return {
479
- blocks: blockResults.flatMap(({ blocks }) => blocks),
480
- nodes: nodeResults.flatMap(({ nodes }) => nodes),
481
- warnings: [
482
- ...fenceWarningsFor(body, slideIndex),
483
- ...parseWarnings,
484
- ...blockResults.flatMap(({ warnings }) => warnings),
485
- ...nodeResults.flatMap(({ warnings }) => warnings)
486
- ]
487
- };
504
+ const { root, warnings: parseWarnings } = parseMarkdownTree(body, slideIndex);
505
+ const blockResults = (root.children ?? []).map((child) => toBlocks(child, slideIndex, body));
506
+ const nodeResults = (root.children ?? []).map((child) => toNodes(child, slideIndex, body));
507
+ return {
508
+ blocks: blockResults.flatMap(({ blocks }) => blocks),
509
+ nodes: nodeResults.flatMap(({ nodes }) => nodes),
510
+ warnings: [
511
+ ...fenceWarningsFor(body, slideIndex),
512
+ ...parseWarnings,
513
+ ...blockResults.flatMap(({ warnings }) => warnings),
514
+ ...nodeResults.flatMap(({ warnings }) => warnings)
515
+ ]
516
+ };
488
517
  }
489
518
  function parseMarkdownTree(body, slideIndex) {
490
- try {
491
- return {
492
- root: unified().use(remarkParse).use(remarkGfm).use(remarkMdx).parse(body),
493
- warnings: []
494
- };
495
- } catch (error) {
496
- const message = error instanceof Error ? error.message : "Unknown MDX parse error";
497
- return {
498
- root: unified().use(remarkParse).use(remarkGfm).parse(body),
499
- warnings: [parserWarning("mdx-parse-error", `Slide ${slideIndex + 1}: ${message}`, slideIndex)]
500
- };
501
- }
519
+ try {
520
+ return {
521
+ root: unified().use(remarkParse).use(remarkGfm).use(remarkMdx).parse(body),
522
+ warnings: []
523
+ };
524
+ } catch (error) {
525
+ const message = error instanceof Error ? error.message : "Unknown MDX parse error";
526
+ return {
527
+ root: unified().use(remarkParse).use(remarkGfm).parse(body),
528
+ warnings: [parserWarning("mdx-parse-error", `Slide ${slideIndex + 1}: ${message}`, slideIndex)]
529
+ };
530
+ }
502
531
  }
503
532
  function fenceWarningsFor(body, slideIndex) {
504
- let openFence;
505
- for (const line of body.split("\n")) {
506
- const match = /^(```|~~~)/.exec(line);
507
- if (!match) continue;
508
- if (!openFence) {
509
- openFence = match[1];
510
- continue;
511
- }
512
- if (line.startsWith(openFence)) openFence = void 0;
513
- }
514
- if (!openFence) return [];
515
- return [parserWarning("code-fence-unclosed", `Slide ${slideIndex + 1}: code fence is not closed.`, slideIndex)];
533
+ let openFence;
534
+ for (const line of body.split("\n")) {
535
+ const match = /^(```|~~~)/.exec(line);
536
+ if (!match) continue;
537
+ if (!openFence) {
538
+ openFence = match[1];
539
+ continue;
540
+ }
541
+ if (line.startsWith(openFence)) openFence = void 0;
542
+ }
543
+ if (!openFence) return [];
544
+ return [parserWarning("code-fence-unclosed", `Slide ${slideIndex + 1}: code fence is not closed.`, slideIndex)];
516
545
  }
517
546
  function toBlocks(node, slideIndex, source) {
518
- switch (node.type) {
519
- case "heading":
520
- return blockResult([{ type: "heading", depth: headingDepth(node.depth), text: textContent(node) }]);
521
- case "paragraph": {
522
- const image = soleImage(node);
523
- if (image) {
524
- return blockResult([
525
- {
526
- type: "image",
527
- alt: image.alt ?? "",
528
- src: image.url ?? "",
529
- ...image.title ? { title: image.title } : {}
530
- }
531
- ]);
532
- }
533
- return blockResult([{ type: "paragraph", text: textContent(node).replace(/\s+/g, " ").trim() }]);
534
- }
535
- case "list":
536
- return blockResult([
537
- {
538
- type: "list",
539
- ordered: node.ordered === true,
540
- items: (node.children ?? []).map((item) => taskListItemText(item)).filter(Boolean)
541
- }
542
- ]);
543
- case "code":
544
- return blockResult([{ type: "code", lang: node.lang ?? void 0, code: node.value ?? "" }]);
545
- case "blockquote":
546
- return blockResult([{ type: "blockquote", text: textContent(node).trim() }]);
547
- case "table":
548
- return blockResult([tableBlock(node)]);
549
- case "mdxJsxFlowElement":
550
- case "mdxJsxTextElement":
551
- return jsxElementBlock(node, slideIndex, source);
552
- case "mdxjsEsm":
553
- case "mdxFlowExpression":
554
- case "mdxTextExpression":
555
- case "thematicBreak":
556
- return blockResult([]);
557
- default:
558
- return blockResult(textContent(node).trim() ? [{ type: "paragraph", text: textContent(node).trim() }] : []);
559
- }
547
+ switch (node.type) {
548
+ case "heading": return blockResult([{
549
+ type: "heading",
550
+ depth: headingDepth(node.depth),
551
+ text: textContent(node)
552
+ }]);
553
+ case "paragraph": {
554
+ const image = soleImage(node);
555
+ if (image) return blockResult([{
556
+ type: "image",
557
+ alt: image.alt ?? "",
558
+ src: image.url ?? "",
559
+ ...image.title ? { title: image.title } : {}
560
+ }]);
561
+ return blockResult([{
562
+ type: "paragraph",
563
+ text: textContent(node).replace(/\s+/g, " ").trim()
564
+ }]);
565
+ }
566
+ case "list": return blockResult([{
567
+ type: "list",
568
+ ordered: node.ordered === true,
569
+ items: (node.children ?? []).map((item) => taskListItemText(item)).filter(Boolean)
570
+ }]);
571
+ case "code": return blockResult([{
572
+ type: "code",
573
+ lang: node.lang ?? void 0,
574
+ code: node.value ?? ""
575
+ }]);
576
+ case "blockquote": return blockResult([{
577
+ type: "blockquote",
578
+ text: textContent(node).trim()
579
+ }]);
580
+ case "table": return blockResult([tableBlock(node)]);
581
+ case "mdxJsxFlowElement":
582
+ case "mdxJsxTextElement": return jsxElementBlock(node, slideIndex, source);
583
+ case "mdxjsEsm":
584
+ case "mdxFlowExpression":
585
+ case "mdxTextExpression":
586
+ case "thematicBreak": return blockResult([]);
587
+ default: return blockResult(textContent(node).trim() ? [{
588
+ type: "paragraph",
589
+ text: textContent(node).trim()
590
+ }] : []);
591
+ }
560
592
  }
561
593
  function jsxElementBlock(node, slideIndex, source) {
562
- const name = node.name ?? "";
563
- if (!name) return blockResult([]);
564
- if (isComponentName(name)) {
565
- const parsedProps = parseProps(node.attributes ?? [], slideIndex, name);
566
- return blockResult(
567
- [
568
- {
569
- type: "component",
570
- name,
571
- props: parsedProps.props,
572
- raw: sourceForNode(node, source) ?? `<${name} />`
573
- }
574
- ],
575
- parsedProps.warnings
576
- );
577
- }
578
- return blockResult([{ type: "paragraph", text: textContent(node).trim() }]);
594
+ const name = node.name ?? "";
595
+ if (!name) return blockResult([]);
596
+ if (isComponentName(name)) {
597
+ const parsedProps = parseProps(node.attributes ?? [], slideIndex, name);
598
+ return blockResult([{
599
+ type: "component",
600
+ name,
601
+ props: parsedProps.props,
602
+ raw: sourceForNode(node, source) ?? `<${name} />`
603
+ }], parsedProps.warnings);
604
+ }
605
+ return blockResult([{
606
+ type: "paragraph",
607
+ text: textContent(node).trim()
608
+ }]);
579
609
  }
580
610
  function toNodes(node, slideIndex, source) {
581
- switch (node.type) {
582
- case "text":
583
- return nodeResult(node.value ? [{ type: "text", value: node.value }] : []);
584
- case "emphasis":
585
- return elementNodeResult(node, "em", slideIndex, source);
586
- case "strong":
587
- return elementNodeResult(node, "strong", slideIndex, source);
588
- case "delete":
589
- return elementNodeResult(node, "del", slideIndex, source);
590
- case "inlineCode":
591
- return nodeResult([{ type: "element", tag: "code", props: {}, children: [{ type: "text", value: node.value ?? "" }] }]);
592
- case "break":
593
- return nodeResult([{ type: "element", tag: "br", props: {}, children: [] }]);
594
- case "heading":
595
- return elementNodeResult(node, `h${headingDepth(node.depth)}`, slideIndex, source);
596
- case "paragraph": {
597
- if (soleImage(node)) return childrenToNodes(node, slideIndex, source);
598
- if (isJsxOnlyParagraph(node)) return childrenToNodes(node, slideIndex, source);
599
- return elementNodeResult(node, "p", slideIndex, source);
600
- }
601
- case "list": {
602
- const tag = node.ordered === true ? "ol" : "ul";
603
- const items = node.children ?? [];
604
- const childResults = items.map((child) => childrenToNodes(child, slideIndex, source));
605
- return nodeResult(
606
- [
607
- {
608
- type: "element",
609
- tag,
610
- props: {},
611
- children: childResults.map(({ nodes }, itemIndex) => listItemNode(nodes, items[itemIndex]?.checked))
612
- }
613
- ],
614
- childResults.flatMap(({ warnings }) => warnings)
615
- );
616
- }
617
- case "listItem":
618
- return childrenToNodes(node, slideIndex, source);
619
- case "code":
620
- return nodeResult([{ type: "code", lang: node.lang ?? void 0, value: node.value ?? "" }]);
621
- case "blockquote":
622
- return elementNodeResult(node, "blockquote", slideIndex, source);
623
- case "image":
624
- return nodeResult([
625
- {
626
- type: "element",
627
- tag: "img",
628
- props: { src: node.url ?? "", alt: node.alt ?? "", ...node.title ? { title: node.title } : {} },
629
- children: []
630
- }
631
- ]);
632
- case "link":
633
- return elementNodeResult(node, "a", slideIndex, source, { href: node.url ?? "" });
634
- case "table":
635
- return tableNodes(node, slideIndex, source);
636
- case "mdxJsxFlowElement":
637
- case "mdxJsxTextElement":
638
- return jsxElementNodes(node, slideIndex, source);
639
- case "mdxjsEsm":
640
- return nodeResult(
641
- [],
642
- [
643
- parserWarning(
644
- "mdx-import-export-ignored",
645
- `Slide ${slideIndex + 1}: MDX import/export syntax is ignored.`,
646
- slideIndex
647
- )
648
- ]
649
- );
650
- case "mdxFlowExpression":
651
- case "mdxTextExpression":
652
- return nodeResult(
653
- [],
654
- [
655
- parserWarning(
656
- "mdx-expression-ignored",
657
- `Slide ${slideIndex + 1}: MDX JavaScript expressions are ignored.`,
658
- slideIndex
659
- )
660
- ]
661
- );
662
- case "thematicBreak":
663
- return nodeResult([{ type: "element", tag: "hr", props: {}, children: [] }]);
664
- default:
665
- return childrenToNodes(node, slideIndex, source);
666
- }
611
+ switch (node.type) {
612
+ case "text": return nodeResult(node.value ? [{
613
+ type: "text",
614
+ value: node.value
615
+ }] : []);
616
+ case "emphasis": return elementNodeResult(node, "em", slideIndex, source);
617
+ case "strong": return elementNodeResult(node, "strong", slideIndex, source);
618
+ case "delete": return elementNodeResult(node, "del", slideIndex, source);
619
+ case "inlineCode": return nodeResult([{
620
+ type: "element",
621
+ tag: "code",
622
+ props: {},
623
+ children: [{
624
+ type: "text",
625
+ value: node.value ?? ""
626
+ }]
627
+ }]);
628
+ case "break": return nodeResult([{
629
+ type: "element",
630
+ tag: "br",
631
+ props: {},
632
+ children: []
633
+ }]);
634
+ case "heading": return elementNodeResult(node, `h${headingDepth(node.depth)}`, slideIndex, source);
635
+ case "paragraph":
636
+ if (soleImage(node)) return childrenToNodes(node, slideIndex, source);
637
+ if (isJsxOnlyParagraph(node)) return childrenToNodes(node, slideIndex, source);
638
+ return elementNodeResult(node, "p", slideIndex, source);
639
+ case "list": {
640
+ const tag = node.ordered === true ? "ol" : "ul";
641
+ const items = node.children ?? [];
642
+ const childResults = items.map((child) => childrenToNodes(child, slideIndex, source));
643
+ return nodeResult([{
644
+ type: "element",
645
+ tag,
646
+ props: {},
647
+ children: childResults.map(({ nodes }, itemIndex) => listItemNode(nodes, items[itemIndex]?.checked))
648
+ }], childResults.flatMap(({ warnings }) => warnings));
649
+ }
650
+ case "listItem": return childrenToNodes(node, slideIndex, source);
651
+ case "code": return nodeResult([{
652
+ type: "code",
653
+ lang: node.lang ?? void 0,
654
+ value: node.value ?? ""
655
+ }]);
656
+ case "blockquote": return elementNodeResult(node, "blockquote", slideIndex, source);
657
+ case "image": return nodeResult([{
658
+ type: "element",
659
+ tag: "img",
660
+ props: {
661
+ src: node.url ?? "",
662
+ alt: node.alt ?? "",
663
+ ...node.title ? { title: node.title } : {}
664
+ },
665
+ children: []
666
+ }]);
667
+ case "link": return elementNodeResult(node, "a", slideIndex, source, { href: node.url ?? "" });
668
+ case "table": return tableNodes(node, slideIndex, source);
669
+ case "mdxJsxFlowElement":
670
+ case "mdxJsxTextElement": return jsxElementNodes(node, slideIndex, source);
671
+ case "mdxjsEsm": return nodeResult([], [parserWarning("mdx-import-export-ignored", `Slide ${slideIndex + 1}: MDX import/export syntax is ignored.`, slideIndex)]);
672
+ case "mdxFlowExpression":
673
+ case "mdxTextExpression": return nodeResult([], [parserWarning("mdx-expression-ignored", `Slide ${slideIndex + 1}: MDX JavaScript expressions are ignored.`, slideIndex)]);
674
+ case "thematicBreak": return nodeResult([{
675
+ type: "element",
676
+ tag: "hr",
677
+ props: {},
678
+ children: []
679
+ }]);
680
+ default: return childrenToNodes(node, slideIndex, source);
681
+ }
667
682
  }
668
683
  function elementNodeResult(node, tag, slideIndex, source, props = {}) {
669
- const children = childrenToNodes(node, slideIndex, source);
670
- return nodeResult(
671
- [
672
- {
673
- type: "element",
674
- tag,
675
- props,
676
- children: children.nodes
677
- }
678
- ],
679
- children.warnings
680
- );
684
+ const children = childrenToNodes(node, slideIndex, source);
685
+ return nodeResult([{
686
+ type: "element",
687
+ tag,
688
+ props,
689
+ children: children.nodes
690
+ }], children.warnings);
681
691
  }
682
692
  function listItemNode(children, checked) {
683
- if (typeof checked !== "boolean") return { type: "element", tag: "li", props: {}, children };
684
- return {
685
- type: "element",
686
- tag: "li",
687
- props: { class: "task-list-item" },
688
- children: [
689
- {
690
- type: "element",
691
- tag: "input",
692
- props: { type: "checkbox", disabled: true, ...checked ? { checked: true } : {} },
693
- children: []
694
- },
695
- { type: "text", value: " " },
696
- ...children
697
- ]
698
- };
693
+ if (typeof checked !== "boolean") return {
694
+ type: "element",
695
+ tag: "li",
696
+ props: {},
697
+ children
698
+ };
699
+ return {
700
+ type: "element",
701
+ tag: "li",
702
+ props: { class: "task-list-item" },
703
+ children: [
704
+ {
705
+ type: "element",
706
+ tag: "input",
707
+ props: {
708
+ type: "checkbox",
709
+ disabled: true,
710
+ ...checked ? { checked: true } : {}
711
+ },
712
+ children: []
713
+ },
714
+ {
715
+ type: "text",
716
+ value: " "
717
+ },
718
+ ...children
719
+ ]
720
+ };
699
721
  }
700
722
  function tableNodes(node, slideIndex, source) {
701
- const align = node.align ?? [];
702
- const [headerRow, ...bodyRows] = node.children ?? [];
703
- const headerResult = headerRow ? tableRowCells(headerRow, "th", align, slideIndex, source) : cellsResult([]);
704
- const bodyResults = bodyRows.map((row) => tableRowCells(row, "td", align, slideIndex, source));
705
- return nodeResult(
706
- [
707
- {
708
- type: "element",
709
- tag: "table",
710
- props: {},
711
- children: [
712
- {
713
- type: "element",
714
- tag: "thead",
715
- props: {},
716
- children: [{ type: "element", tag: "tr", props: {}, children: headerResult.cells }]
717
- },
718
- {
719
- type: "element",
720
- tag: "tbody",
721
- props: {},
722
- children: bodyResults.map(({ cells }) => ({ type: "element", tag: "tr", props: {}, children: cells }))
723
- }
724
- ]
725
- }
726
- ],
727
- [...headerResult.warnings, ...bodyResults.flatMap(({ warnings }) => warnings)]
728
- );
723
+ const align = node.align ?? [];
724
+ const [headerRow, ...bodyRows] = node.children ?? [];
725
+ const headerResult = headerRow ? tableRowCells(headerRow, "th", align, slideIndex, source) : cellsResult([]);
726
+ const bodyResults = bodyRows.map((row) => tableRowCells(row, "td", align, slideIndex, source));
727
+ return nodeResult([{
728
+ type: "element",
729
+ tag: "table",
730
+ props: {},
731
+ children: [{
732
+ type: "element",
733
+ tag: "thead",
734
+ props: {},
735
+ children: [{
736
+ type: "element",
737
+ tag: "tr",
738
+ props: {},
739
+ children: headerResult.cells
740
+ }]
741
+ }, {
742
+ type: "element",
743
+ tag: "tbody",
744
+ props: {},
745
+ children: bodyResults.map(({ cells }) => ({
746
+ type: "element",
747
+ tag: "tr",
748
+ props: {},
749
+ children: cells
750
+ }))
751
+ }]
752
+ }], [...headerResult.warnings, ...bodyResults.flatMap(({ warnings }) => warnings)]);
729
753
  }
730
754
  function cellsResult(cells, emittedWarnings = []) {
731
- return { cells, warnings: emittedWarnings };
755
+ return {
756
+ cells,
757
+ warnings: emittedWarnings
758
+ };
732
759
  }
733
760
  function tableRowCells(row, tag, align, slideIndex, source) {
734
- const results = (row.children ?? []).map((cell, cellIndex) => {
735
- const children = childrenToNodes(cell, slideIndex, source);
736
- const cellAlign = toTableAlign(align[cellIndex]);
737
- return {
738
- cell: {
739
- type: "element",
740
- tag,
741
- props: cellAlign ? { style: `text-align:${cellAlign}` } : {},
742
- children: children.nodes
743
- },
744
- warnings: children.warnings
745
- };
746
- });
747
- return cellsResult(
748
- results.map(({ cell }) => cell),
749
- results.flatMap(({ warnings }) => warnings)
750
- );
761
+ const results = (row.children ?? []).map((cell, cellIndex) => {
762
+ const children = childrenToNodes(cell, slideIndex, source);
763
+ const cellAlign = toTableAlign(align[cellIndex]);
764
+ return {
765
+ cell: {
766
+ type: "element",
767
+ tag,
768
+ props: cellAlign ? { style: `text-align:${cellAlign}` } : {},
769
+ children: children.nodes
770
+ },
771
+ warnings: children.warnings
772
+ };
773
+ });
774
+ return cellsResult(results.map(({ cell }) => cell), results.flatMap(({ warnings }) => warnings));
751
775
  }
752
776
  function jsxElementNodes(node, slideIndex, source) {
753
- const name = node.name ?? "";
754
- if (!name) return childrenToNodes(node, slideIndex, source);
755
- if (isUnsafeHtmlElement(name)) {
756
- return nodeResult([{ type: "text", value: sourceForNode(node, source) ?? textContent(node) }]);
757
- }
758
- const parsedProps = parseProps(node.attributes ?? [], slideIndex, name);
759
- const parsedChildren = childrenToNodes(node, slideIndex, source);
760
- const children = parsedChildren.nodes.filter((child) => child.type !== "text" || child.value.trim() !== "");
761
- const warnings = [...parsedProps.warnings, ...parsedChildren.warnings];
762
- if (isComponentName(name)) return nodeResult([{ type: "component", name, props: parsedProps.props, children }], warnings);
763
- return nodeResult([{ type: "element", tag: name, props: parsedProps.props, children }], warnings);
777
+ const name = node.name ?? "";
778
+ if (!name) return childrenToNodes(node, slideIndex, source);
779
+ if (isUnsafeHtmlElement(name)) return nodeResult([{
780
+ type: "text",
781
+ value: sourceForNode(node, source) ?? textContent(node)
782
+ }]);
783
+ const parsedProps = parseProps(node.attributes ?? [], slideIndex, name);
784
+ const parsedChildren = childrenToNodes(node, slideIndex, source);
785
+ const children = parsedChildren.nodes.filter((child) => child.type !== "text" || child.value.trim() !== "");
786
+ const warnings = [...parsedProps.warnings, ...parsedChildren.warnings];
787
+ if (isComponentName(name)) return nodeResult([{
788
+ type: "component",
789
+ name,
790
+ props: parsedProps.props,
791
+ children
792
+ }], warnings);
793
+ return nodeResult([{
794
+ type: "element",
795
+ tag: name,
796
+ props: parsedProps.props,
797
+ children
798
+ }], warnings);
764
799
  }
765
800
  function childrenToNodes(node, slideIndex, source) {
766
- const results = (node.children ?? []).map((child) => toNodes(child, slideIndex, source));
767
- return nodeResult(
768
- results.flatMap(({ nodes }) => nodes),
769
- results.flatMap(({ warnings }) => warnings)
770
- );
801
+ const results = (node.children ?? []).map((child) => toNodes(child, slideIndex, source));
802
+ return nodeResult(results.flatMap(({ nodes }) => nodes), results.flatMap(({ warnings }) => warnings));
771
803
  }
772
804
  function parseProps(attributes, slideIndex, componentName) {
773
- const props = {};
774
- const warnings = attributes.flatMap((attr) => {
775
- if (attr.type !== "mdxJsxAttribute" || !attr.name) return [];
776
- if (attr.value === null || attr.value === void 0) {
777
- props[attr.name] = true;
778
- return [];
779
- }
780
- if (typeof attr.value === "string" || typeof attr.value === "number" || typeof attr.value === "boolean") {
781
- props[attr.name] = attr.value;
782
- return [];
783
- }
784
- return [
785
- parserWarning(
786
- "mdx-expression-prop-ignored",
787
- `Slide ${slideIndex + 1}: MDX JavaScript expression props are ignored on ${ignoredPropTarget(componentName, attr.name)}.`,
788
- slideIndex
789
- )
790
- ];
791
- });
792
- return { props, warnings };
805
+ const props = {};
806
+ return {
807
+ props,
808
+ warnings: attributes.flatMap((attr) => {
809
+ if (attr.type !== "mdxJsxAttribute" || !attr.name) return [];
810
+ if (attr.value === null || attr.value === void 0) {
811
+ props[attr.name] = true;
812
+ return [];
813
+ }
814
+ if (typeof attr.value === "string" || typeof attr.value === "number" || typeof attr.value === "boolean") {
815
+ props[attr.name] = attr.value;
816
+ return [];
817
+ }
818
+ return [parserWarning("mdx-expression-prop-ignored", `Slide ${slideIndex + 1}: MDX JavaScript expression props are ignored on ${ignoredPropTarget(componentName, attr.name)}.`, slideIndex)];
819
+ })
820
+ };
793
821
  }
794
822
  function blockResult(blocks, emittedWarnings = []) {
795
- return { blocks, warnings: emittedWarnings };
823
+ return {
824
+ blocks,
825
+ warnings: emittedWarnings
826
+ };
796
827
  }
797
828
  function nodeResult(nodes, emittedWarnings = []) {
798
- return { nodes, warnings: emittedWarnings };
829
+ return {
830
+ nodes,
831
+ warnings: emittedWarnings
832
+ };
799
833
  }
800
834
  function parserWarning(code, message, slideIndex) {
801
- return { code, message, slideIndex };
835
+ return {
836
+ code,
837
+ message,
838
+ slideIndex
839
+ };
802
840
  }
803
841
  function ignoredPropTarget(componentName, propName) {
804
- if (propName.startsWith("$")) return `${componentName} dynamic prop`;
805
- return `${componentName}.${propName}`;
842
+ if (propName.startsWith("$")) return `${componentName} dynamic prop`;
843
+ return `${componentName}.${propName}`;
806
844
  }
807
845
  function textContent(node) {
808
- if (typeof node.value === "string") return node.value;
809
- if (node.type === "strong") return `**${(node.children ?? []).map(textContent).join("")}**`;
810
- if (node.type === "emphasis") return `*${(node.children ?? []).map(textContent).join("")}*`;
811
- if (node.type === "delete") return `~~${(node.children ?? []).map(textContent).join("")}~~`;
812
- if (node.type === "inlineCode") return `\`${node.value ?? ""}\``;
813
- if ((node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement") && node.name && !isComponentName(node.name)) {
814
- return `<${node.name}>${(node.children ?? []).map(textContent).join("")}</${node.name}>`;
815
- }
816
- return (node.children ?? []).map(textContent).join("");
846
+ if (typeof node.value === "string") return node.value;
847
+ if (node.type === "strong") return `**${(node.children ?? []).map(textContent).join("")}**`;
848
+ if (node.type === "emphasis") return `*${(node.children ?? []).map(textContent).join("")}*`;
849
+ if (node.type === "delete") return `~~${(node.children ?? []).map(textContent).join("")}~~`;
850
+ if (node.type === "inlineCode") return `\`${node.value ?? ""}\``;
851
+ if ((node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement") && node.name && !isComponentName(node.name)) return `<${node.name}>${(node.children ?? []).map(textContent).join("")}</${node.name}>`;
852
+ return (node.children ?? []).map(textContent).join("");
817
853
  }
818
854
  function taskListItemText(item) {
819
- const text = textContent(item).replace(/\s+/g, " ").trim();
820
- if (typeof item.checked === "boolean") return `${item.checked ? "[x]" : "[ ]"} ${text}`.trim();
821
- return text;
855
+ const text = textContent(item).replace(/\s+/g, " ").trim();
856
+ if (typeof item.checked === "boolean") return `${item.checked ? "[x]" : "[ ]"} ${text}`.trim();
857
+ return text;
822
858
  }
823
859
  function tableBlock(node) {
824
- const rows = (node.children ?? []).map(
825
- (row) => (row.children ?? []).map((cell) => textContent(cell).replace(/\s+/g, " ").trim())
826
- );
827
- const [header = [], ...bodyRows] = rows;
828
- return {
829
- type: "table",
830
- align: (node.align ?? []).map(toTableAlign),
831
- header,
832
- rows: bodyRows
833
- };
860
+ const [header = [], ...bodyRows] = (node.children ?? []).map((row) => (row.children ?? []).map((cell) => textContent(cell).replace(/\s+/g, " ").trim()));
861
+ return {
862
+ type: "table",
863
+ align: (node.align ?? []).map(toTableAlign),
864
+ header,
865
+ rows: bodyRows
866
+ };
834
867
  }
835
868
  function toTableAlign(value) {
836
- return value === "left" || value === "center" || value === "right" ? value : void 0;
869
+ return value === "left" || value === "center" || value === "right" ? value : void 0;
837
870
  }
838
871
  function soleImage(node) {
839
- const meaningful = (node.children ?? []).filter((child) => child.type !== "text" || child.value?.trim());
840
- return meaningful.length === 1 && meaningful[0].type === "image" ? meaningful[0] : void 0;
872
+ const meaningful = (node.children ?? []).filter((child) => child.type !== "text" || child.value?.trim());
873
+ return meaningful.length === 1 && meaningful[0].type === "image" ? meaningful[0] : void 0;
841
874
  }
842
875
  function isJsxOnlyParagraph(node) {
843
- const children = node.children ?? [];
844
- return children.length > 0 && children.every(
845
- (child) => child.type === "mdxJsxTextElement" || child.type === "mdxJsxFlowElement" || child.type === "text" && child.value?.trim() === ""
846
- );
876
+ const children = node.children ?? [];
877
+ return children.length > 0 && children.every((child) => child.type === "mdxJsxTextElement" || child.type === "mdxJsxFlowElement" || child.type === "text" && child.value?.trim() === "");
847
878
  }
848
879
  function sourceForNode(node, source) {
849
- const start = node.position?.start?.offset;
850
- const end = node.position?.end?.offset;
851
- return typeof start === "number" && typeof end === "number" ? source.slice(start, end) : void 0;
880
+ const start = node.position?.start?.offset;
881
+ const end = node.position?.end?.offset;
882
+ return typeof start === "number" && typeof end === "number" ? source.slice(start, end) : void 0;
852
883
  }
853
884
  function isComponentName(name) {
854
- return /^[A-Z]/.test(name);
885
+ return /^[A-Z]/.test(name);
855
886
  }
856
887
  function isUnsafeHtmlElement(name) {
857
- return /^(script|style|iframe|object|embed)$/i.test(name);
888
+ return /^(script|style|iframe|object|embed)$/i.test(name);
858
889
  }
859
890
  function headingDepth(value) {
860
- if (value === 1 || value === 2 || value === 3) return value;
861
- return 3;
891
+ if (value === 1 || value === 2 || value === 3) return value;
892
+ return 3;
862
893
  }
863
894
  function stringAttr(value) {
864
- return value && value.length > 0 ? value : void 0;
895
+ return value && value.length > 0 ? value : void 0;
865
896
  }
866
-
867
- // src/generator/mdx/assets.ts
897
+ //#endregion
898
+ //#region src/generator/mdx/assets.ts
868
899
  function rewriteAssetUrls(source, assets) {
869
- let result2 = source;
870
- for (const asset of assets) {
871
- const assetPath = localAssetRelativePath(asset.sourcePath);
872
- result2 = result2.replaceAll(`./assets/${assetPath}`, asset.publicPath);
873
- result2 = result2.replace(new RegExp(`(?<!/)assets/${escapeRegExp(assetPath)}`, "g"), asset.publicPath);
874
- }
875
- return result2;
900
+ let result = source;
901
+ for (const asset of assets) {
902
+ const assetPath = localAssetRelativePath(asset.sourcePath);
903
+ result = result.replaceAll(`./assets/${assetPath}`, asset.publicPath);
904
+ result = result.replace(new RegExp(`(?<!/)assets/${escapeRegExp(assetPath)}`, "g"), asset.publicPath);
905
+ }
906
+ return result;
876
907
  }
877
908
  function rewriteRelativeMdxImports(source, sourceDir, generatedDir) {
878
- return source.replace(/(from\s+|import\s+)(["'])(\.[^"']+)\2/g, (_match, prefix, quote, specifier) => {
879
- const target = normalizePath2(`${sourceDir}/${specifier}`);
880
- return `${prefix}${quote}${toRelativeImportPath(generatedDir, target)}${quote}`;
881
- });
909
+ return source.replace(/(from\s+|import\s+)(["'])(\.[^"']+)\2/g, (_match, prefix, quote, specifier) => {
910
+ return `${prefix}${quote}${toRelativeImportPath(generatedDir, normalizePath$3(`${sourceDir}/${specifier}`))}${quote}`;
911
+ });
882
912
  }
883
913
  function componentImportPath(outDir, sourcePath) {
884
- if (!sourcePath) return void 0;
885
- const source = sourcePath.replace(/\/index\.(tsx|ts|jsx|js)$/, "");
886
- return toRelativeImportPath(outDir, source);
914
+ if (!sourcePath) return void 0;
915
+ return toRelativeImportPath(outDir, sourcePath.replace(/\/index\.(tsx|ts|jsx|js)$/, ""));
887
916
  }
888
- function dirname(path) {
889
- const normalized = normalizePath2(path);
890
- const index = normalized.lastIndexOf("/");
891
- return index === -1 ? "." : normalized.slice(0, index);
917
+ function dirname$2(path) {
918
+ const normalized = normalizePath$3(path);
919
+ const index = normalized.lastIndexOf("/");
920
+ return index === -1 ? "." : normalized.slice(0, index);
892
921
  }
893
922
  function localAssetRelativePath(sourcePath) {
894
- const marker = "/assets/";
895
- const normalized = normalizePath2(sourcePath);
896
- const markerIndex = normalized.indexOf(marker);
897
- return markerIndex === -1 ? normalized.split("/").at(-1) ?? normalized : normalized.slice(markerIndex + marker.length);
923
+ const marker = "/assets/";
924
+ const normalized = normalizePath$3(sourcePath);
925
+ const markerIndex = normalized.indexOf(marker);
926
+ return markerIndex === -1 ? normalized.split("/").at(-1) ?? normalized : normalized.slice(markerIndex + 8);
898
927
  }
899
- function normalizePath2(path) {
900
- return path.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
928
+ function normalizePath$3(path) {
929
+ return path.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
901
930
  }
902
931
  function toRelativeImportPath(fromDir, target) {
903
- const fromParts = normalizePath2(fromDir).split("/").filter(Boolean);
904
- const targetParts = normalizePath2(target).split("/").filter(Boolean);
905
- while (fromParts.length > 0 && targetParts.length > 0 && fromParts[0] === targetParts[0]) {
906
- fromParts.shift();
907
- targetParts.shift();
908
- }
909
- const relative4 = [...fromParts.map(() => ".."), ...targetParts].join("/");
910
- return relative4.startsWith(".") ? relative4 : `./${relative4 || "."}`;
932
+ const fromParts = normalizePath$3(fromDir).split("/").filter(Boolean);
933
+ const targetParts = normalizePath$3(target).split("/").filter(Boolean);
934
+ while (fromParts.length > 0 && targetParts.length > 0 && fromParts[0] === targetParts[0]) {
935
+ fromParts.shift();
936
+ targetParts.shift();
937
+ }
938
+ const relative = [...fromParts.map(() => ".."), ...targetParts].join("/");
939
+ return relative.startsWith(".") ? relative : `./${relative || "."}`;
911
940
  }
912
941
  function escapeRegExp(value) {
913
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
942
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
914
943
  }
915
-
916
- // src/generator/assets.ts
944
+ //#endregion
945
+ //#region src/generator/assets.ts
917
946
  async function buildAssetRefs(slug, assetPaths, input) {
918
- return Promise.all(
919
- assetPaths.map(async (sourcePath) => ({
920
- sourcePath,
921
- publicPath: `${normalizeMountPath(input.mountPath ?? `/${input.root}`)}/${encodeURIComponent(slug)}/assets/${assetName(
922
- sourcePath,
923
- input.root,
924
- slug
925
- )}`,
926
- type: "local",
927
- contentType: contentTypeForPath(sourcePath),
928
- body: input.readBinary ? await input.readBinary(sourcePath) : void 0
929
- }))
930
- );
947
+ return Promise.all(assetPaths.map(async (sourcePath) => ({
948
+ sourcePath,
949
+ publicPath: `${normalizeMountPath(input.mountPath ?? `/${input.root}`)}/${encodeURIComponent(slug)}/assets/${assetName(sourcePath, input.root, slug)}`,
950
+ type: "local",
951
+ contentType: contentTypeForPath(sourcePath),
952
+ body: input.readBinary ? await input.readBinary(sourcePath) : void 0
953
+ })));
931
954
  }
932
955
  function assetName(sourcePath, root, slug) {
933
- const normalizedPath = normalizePath3(sourcePath);
934
- const prefix = `${normalizePath3(root).replace(/\/$/, "")}/${slug}/assets/`;
935
- const relative4 = normalizedPath.startsWith(prefix) ? normalizedPath.slice(prefix.length) : normalizedPath.split("/").at(-1) ?? normalizedPath;
936
- return relative4.split("/").map(encodeURIComponent).join("/");
956
+ const normalizedPath = normalizePath$2(sourcePath);
957
+ const prefix = `${normalizePath$2(root).replace(/\/$/, "")}/${slug}/assets/`;
958
+ return (normalizedPath.startsWith(prefix) ? normalizedPath.slice(prefix.length) : normalizedPath.split("/").at(-1) ?? normalizedPath).split("/").map(encodeURIComponent).join("/");
937
959
  }
938
960
  function normalizeMountPath(value) {
939
- const withLeadingSlash = value.startsWith("/") ? value : `/${value}`;
940
- return withLeadingSlash.replace(/\/$/, "");
961
+ return (value.startsWith("/") ? value : `/${value}`).replace(/\/$/, "");
941
962
  }
942
- function normalizePath3(path) {
943
- return path.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
963
+ function normalizePath$2(path) {
964
+ return path.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
944
965
  }
945
-
946
- // src/generator/package-entry.ts
947
- var DECKS_RUNTIME_ENTRY = "hono-decks";
948
- var DECKS_ADVANCED_ENTRY = "hono-decks/advanced";
949
-
950
- // src/generator/mdx-module-generator.ts
951
- import { compile } from "@mdx-js/mdx";
952
- import remarkDirective from "remark-directive";
953
- import remarkGfm2 from "remark-gfm";
954
-
955
- // src/generator/mdx/emit.ts
966
+ //#endregion
967
+ //#region src/generator/package-entry.ts
968
+ const DECKS_RUNTIME_ENTRY = "hono-decks";
969
+ const DECKS_ADVANCED_ENTRY = "hono-decks/advanced";
970
+ //#endregion
971
+ //#region src/generator/mdx/emit.ts
956
972
  function emitModuleDecksRouter(input) {
957
- const runtimeEntry = JSON.stringify(DECKS_RUNTIME_ENTRY);
958
- const advancedEntry = JSON.stringify(DECKS_ADVANCED_ENTRY);
959
- const slideImports = input.decks.flatMap(
960
- (deck) => deck.slideModules.map(
961
- (slide, index) => `import ${slideImportName(deck.deck.slug, index)} from ${JSON.stringify(slide.importPath)};`
962
- )
963
- ).join("\n");
964
- const componentImports = input.decks.filter((deck) => deck.componentModulePath).map((deck) => `import * as ${componentImportName(deck.deck.slug)} from ${JSON.stringify(deck.componentModulePath)};`).join("\n");
965
- return `// @ts-nocheck
966
- import { configureDecks, defineDecks } from ${advancedEntry};
973
+ const runtimeEntry = JSON.stringify(DECKS_RUNTIME_ENTRY);
974
+ return `// @ts-nocheck
975
+ import { configureDecks, defineDecks } from ${JSON.stringify(DECKS_ADVANCED_ENTRY)};
967
976
  import type { ConfiguredDecks, DecksConfig } from ${runtimeEntry};
968
977
  import type { Env } from "hono";
969
978
  import { decksClientEntry } from "./client-entry";
970
- ${slideImports}
971
- ${componentImports}
979
+ ${input.decks.flatMap((deck) => deck.slideModules.map((slide, index) => `import ${slideImportName(deck.deck.slug, index)} from ${JSON.stringify(slide.importPath)};`)).join("\n")}
980
+ ${input.decks.filter((deck) => deck.componentModulePath).map((deck) => `import * as ${componentImportName(deck.deck.slug)} from ${JSON.stringify(deck.componentModulePath)};`).join("\n")}
972
981
 
973
982
  function withClientComponentIds(module, clientIds) {
974
983
  const registry = {};
@@ -1002,589 +1011,510 @@ function definedDecksFor<E extends Env>() {
1002
1011
  `;
1003
1012
  }
1004
1013
  function emitDeckObject(deck) {
1005
- return ` {
1014
+ return ` {
1006
1015
  slug: ${JSON.stringify(deck.deck.slug)},
1007
1016
  sourcePath: ${JSON.stringify(deck.deck.sourcePath)},
1008
1017
  kind: ${JSON.stringify(deck.deck.kind)},
1009
1018
  meta: ${serializeValue(deck.deck.meta, 3)},
1010
- ${deck.deck.themeStyle ? ` "themeStyle": ${serializeValue(deck.deck.themeStyle, 3)},
1011
- ` : ""}${deck.deck.themeSourcePath ? ` "themeSourcePath": ${JSON.stringify(deck.deck.themeSourcePath)},
1012
- ` : ""}
1019
+ ${deck.deck.themeStyle ? ` "themeStyle": ${serializeValue(deck.deck.themeStyle, 3)},\n` : ""}${deck.deck.themeSourcePath ? ` "themeSourcePath": ${JSON.stringify(deck.deck.themeSourcePath)},\n` : ""}
1013
1020
  assets: ${serializeValue(deck.deck.assets, 3)},
1014
1021
  componentRegistry: ${deck.componentModulePath ? `withClientComponentIds(${componentImportName(deck.deck.slug)}, ${serializeValue(deck.clientComponentIds ?? {}, 3)})` : "{}"},
1015
1022
  warnings: ${serializeValue(deck.deck.warnings, 3)},
1016
1023
  slides: [
1017
- ${deck.deck.slides.map(
1018
- (slide) => ` {
1024
+ ${deck.deck.slides.map((slide) => ` {
1019
1025
  index: ${slide.index},
1020
1026
  meta: ${serializeValue(slide.meta, 5)},
1021
1027
  html: "",
1022
1028
  components: [],
1023
1029
  notes: ${serializeValue(slide.notes, 5)},
1024
1030
  render: ${slideImportName(deck.deck.slug, slide.index)}
1025
- }`
1026
- ).join(",\n")}
1031
+ }`).join(",\n")}
1027
1032
  ]
1028
1033
  }`;
1029
1034
  }
1030
1035
  function slideImportName(slug, index) {
1031
- return `Slide_${safeIdentifier(slug)}_${index}`;
1036
+ return `Slide_${safeIdentifier$1(slug)}_${index}`;
1032
1037
  }
1033
1038
  function componentImportName(slug) {
1034
- return `Components_${safeIdentifier(slug)}`;
1039
+ return `Components_${safeIdentifier$1(slug)}`;
1035
1040
  }
1036
- function safeIdentifier(value) {
1037
- return value.replace(/[^A-Za-z0-9_$]+/g, "_").replace(/^[^A-Za-z_$]+/, "_") || "_";
1041
+ function safeIdentifier$1(value) {
1042
+ return value.replace(/[^A-Za-z0-9_$]+/g, "_").replace(/^[^A-Za-z_$]+/, "_") || "_";
1038
1043
  }
1039
1044
  function serializeValue(value, depth) {
1040
- const indent = " ".repeat(depth);
1041
- const nextIndent = " ".repeat(depth + 1);
1042
- if (value === void 0) return "undefined";
1043
- if (value instanceof Uint8Array) return `new Uint8Array([${[...value].join(", ")}])`;
1044
- if (Array.isArray(value)) {
1045
- if (value.length === 0) return "[]";
1046
- return `[
1047
- ${value.map((item) => `${nextIndent}${serializeValue(item, depth + 1)}`).join(",\n")}
1048
- ${indent}]`;
1049
- }
1050
- if (typeof value === "object" && value !== null) {
1051
- const entries = Object.entries(value).filter(([, item]) => item !== void 0);
1052
- if (entries.length === 0) return "{}";
1053
- return `{
1054
- ${entries.map(([key, item]) => `${nextIndent}${JSON.stringify(key)}: ${serializeValue(item, depth + 1)}`).join(",\n")}
1055
- ${indent}}`;
1056
- }
1057
- return JSON.stringify(value);
1058
- }
1059
-
1060
- // src/generator/mdx/ogp.ts
1045
+ const indent = " ".repeat(depth);
1046
+ const nextIndent = " ".repeat(depth + 1);
1047
+ if (value === void 0) return "undefined";
1048
+ if (value instanceof Uint8Array) return `new Uint8Array([${[...value].join(", ")}])`;
1049
+ if (Array.isArray(value)) {
1050
+ if (value.length === 0) return "[]";
1051
+ return `[\n${value.map((item) => `${nextIndent}${serializeValue(item, depth + 1)}`).join(",\n")}\n${indent}]`;
1052
+ }
1053
+ if (typeof value === "object" && value !== null) {
1054
+ const entries = Object.entries(value).filter(([, item]) => item !== void 0);
1055
+ if (entries.length === 0) return "{}";
1056
+ return `{\n${entries.map(([key, item]) => `${nextIndent}${JSON.stringify(key)}: ${serializeValue(item, depth + 1)}`).join(",\n")}\n${indent}}`;
1057
+ }
1058
+ return JSON.stringify(value);
1059
+ }
1060
+ //#endregion
1061
+ //#region src/generator/mdx/ogp.ts
1061
1062
  async function resolveLinkCardMetadataByUrl(source, resolveOgp) {
1062
- const result2 = /* @__PURE__ */ new Map();
1063
- if (!resolveOgp) return result2;
1064
- for (const url of collectLinkCardUrls(source)) {
1065
- try {
1066
- const metadata = await resolveOgp(url);
1067
- if (metadata) result2.set(url, metadata);
1068
- } catch {
1069
- }
1070
- }
1071
- return result2;
1063
+ const result = /* @__PURE__ */ new Map();
1064
+ if (!resolveOgp) return result;
1065
+ for (const url of collectLinkCardUrls(source)) try {
1066
+ const metadata = await resolveOgp(url);
1067
+ if (metadata) result.set(url, metadata);
1068
+ } catch {}
1069
+ return result;
1072
1070
  }
1073
1071
  function collectLinkCardUrls(source) {
1074
- const urls = /* @__PURE__ */ new Set();
1075
- const pattern = /@\[card\]\(([^)\s]+)\)/g;
1076
- for (const match of source.matchAll(pattern)) {
1077
- urls.add(match[1]);
1078
- }
1079
- return [...urls];
1072
+ const urls = /* @__PURE__ */ new Set();
1073
+ for (const match of source.matchAll(/@\[card\]\(([^)\s]+)\)/g)) urls.add(match[1]);
1074
+ return [...urls];
1080
1075
  }
1081
-
1082
- // src/generator/mdx/syntax.ts
1083
- import { codeToHtml } from "shiki";
1076
+ //#endregion
1077
+ //#region src/generator/mdx/syntax.ts
1084
1078
  function remarkDeckSyntax(input = {}) {
1085
- return () => (tree) => {
1086
- transformDeckSyntaxChildren(tree, input);
1087
- };
1079
+ return () => (tree) => {
1080
+ transformDeckSyntaxChildren(tree, input);
1081
+ };
1088
1082
  }
1089
1083
  function remarkCodeHighlight() {
1090
- return async (tree) => {
1091
- await highlightMarkdownNode(tree);
1092
- };
1084
+ return async (tree) => {
1085
+ await highlightMarkdownNode(tree);
1086
+ };
1093
1087
  }
1094
1088
  function transformDeckSyntaxChildren(node, input) {
1095
- if (!Array.isArray(node.children)) return;
1096
- const children = [];
1097
- for (const child of node.children) {
1098
- transformDeckSyntaxChildren(child, input);
1099
- rejectRemovedFireAuthoring(child);
1100
- children.push(
1101
- fireAttributeNode(child) ?? zennEmbedNode(child, input) ?? plainUrlLinkNode(child) ?? fireDirectiveNode(child) ?? unknownDirectiveFallback(child) ?? child
1102
- );
1103
- }
1104
- node.children = children;
1089
+ if (!Array.isArray(node.children)) return;
1090
+ const children = [];
1091
+ for (const child of node.children) {
1092
+ transformDeckSyntaxChildren(child, input);
1093
+ rejectRemovedFireAuthoring(child);
1094
+ children.push(fireAttributeNode(child) ?? zennEmbedNode(child, input) ?? plainUrlLinkNode(child) ?? fireDirectiveNode(child) ?? unknownDirectiveFallback(child) ?? child);
1095
+ }
1096
+ node.children = children;
1105
1097
  }
1106
1098
  function rejectRemovedFireAuthoring(node) {
1107
- if (node.type !== "mdxJsxFlowElement" && node.type !== "mdxJsxTextElement") return;
1108
- if (!Array.isArray(node.attributes)) return;
1109
- if (node.attributes.some((attribute) => attribute.name === "$fire")) {
1110
- throw new Error('The "$fire" prop is not supported. Use fire or fire="effect" on a block-level custom component.');
1111
- }
1112
- if (node.name === "Fire" && node.attributes.some((attribute) => attribute.name === "order")) {
1113
- throw new Error('The Fire "order" prop is not supported. Fires reveal in source order.');
1114
- }
1115
- if (node.name === "Fire") {
1116
- const atAttribute = node.attributes.find((attribute) => attribute.name === "at");
1117
- if (atAttribute) fireAtAttributeValue(atAttribute.value);
1118
- }
1099
+ if (node.type !== "mdxJsxFlowElement" && node.type !== "mdxJsxTextElement") return;
1100
+ if (!Array.isArray(node.attributes)) return;
1101
+ if (node.attributes.some((attribute) => attribute.name === "$fire")) throw new Error("The \"$fire\" prop is not supported. Use fire or fire=\"effect\" on a block-level custom component.");
1102
+ if (node.name === "Fire" && node.attributes.some((attribute) => attribute.name === "order")) throw new Error("The Fire \"order\" prop is not supported. Fires reveal in source order.");
1103
+ if (node.name === "Fire") {
1104
+ const atAttribute = node.attributes.find((attribute) => attribute.name === "at");
1105
+ if (atAttribute) fireAtAttributeValue(atAttribute.value);
1106
+ }
1119
1107
  }
1120
1108
  function fireAttributeNode(node) {
1121
- if (node.type !== "mdxJsxFlowElement" && node.type !== "mdxJsxTextElement") return void 0;
1122
- if (!Array.isArray(node.attributes)) return void 0;
1123
- const fireAttribute = node.attributes.find(
1124
- (attribute) => attribute.type === "mdxJsxAttribute" && attribute.name === "fire"
1125
- );
1126
- if (!fireAttribute) return void 0;
1127
- const atAttribute = node.attributes.find(
1128
- (attribute) => attribute.type === "mdxJsxAttribute" && attribute.name === "at"
1129
- );
1130
- if (node.name === "Fire") {
1131
- throw new Error('The "fire" attribute is not supported on <Fire>. Use the effect prop instead.');
1132
- }
1133
- if (!node.name || !/^[A-Z]/.test(node.name)) {
1134
- throw new Error('The "fire" attribute is only supported on custom components. Use :::fire for Markdown content.');
1135
- }
1136
- if (node.type !== "mdxJsxFlowElement") {
1137
- throw new Error('The "fire" attribute is only supported on block-level custom components. Move the component to its own line.');
1138
- }
1139
- if (fireAttribute.value !== null && fireAttribute.value !== void 0 && typeof fireAttribute.value !== "string") {
1140
- throw new Error('The "fire" attribute accepts no value or a static effect name such as fire="fade-up".');
1141
- }
1142
- const effect = typeof fireAttribute.value === "string" ? fireAttribute.value.trim() : "";
1143
- const at = atAttribute ? fireAtAttributeValue(atAttribute.value) : void 0;
1144
- node.attributes = node.attributes.filter((attribute) => attribute !== fireAttribute && attribute !== atAttribute);
1145
- return mdxElement(
1146
- "Fire",
1147
- [
1148
- ...effect ? [mdxAttribute("effect", effect)] : [],
1149
- ...at ? [mdxAttribute("at", at)] : []
1150
- ],
1151
- [node]
1152
- );
1109
+ if (node.type !== "mdxJsxFlowElement" && node.type !== "mdxJsxTextElement") return void 0;
1110
+ if (!Array.isArray(node.attributes)) return void 0;
1111
+ const fireAttribute = node.attributes.find((attribute) => attribute.type === "mdxJsxAttribute" && attribute.name === "fire");
1112
+ if (!fireAttribute) return void 0;
1113
+ const atAttribute = node.attributes.find((attribute) => attribute.type === "mdxJsxAttribute" && attribute.name === "at");
1114
+ if (node.name === "Fire") throw new Error("The \"fire\" attribute is not supported on <Fire>. Use the effect prop instead.");
1115
+ if (!node.name || !/^[A-Z]/.test(node.name)) throw new Error("The \"fire\" attribute is only supported on custom components. Use :::fire for Markdown content.");
1116
+ if (node.type !== "mdxJsxFlowElement") throw new Error("The \"fire\" attribute is only supported on block-level custom components. Move the component to its own line.");
1117
+ if (fireAttribute.value !== null && fireAttribute.value !== void 0 && typeof fireAttribute.value !== "string") throw new Error("The \"fire\" attribute accepts no value or a static effect name such as fire=\"fade-up\".");
1118
+ const effect = typeof fireAttribute.value === "string" ? fireAttribute.value.trim() : "";
1119
+ const at = atAttribute ? fireAtAttributeValue(atAttribute.value) : void 0;
1120
+ node.attributes = node.attributes.filter((attribute) => attribute !== fireAttribute && attribute !== atAttribute);
1121
+ return mdxElement("Fire", [...effect ? [mdxAttribute("effect", effect)] : [], ...at ? [mdxAttribute("at", at)] : []], [node]);
1153
1122
  }
1154
1123
  function fireAtAttributeValue(value) {
1155
- if (typeof value === "string") {
1156
- const at = value.trim();
1157
- if (/^(?:\d+|[+-]\d+)$/.test(at)) return at;
1158
- }
1159
- throw new Error('The fire "at" attribute accepts a non-negative integer or a relative value such as "+1".');
1124
+ if (typeof value === "string") {
1125
+ const at = value.trim();
1126
+ if (/^(?:\d+|[+-]\d+)$/.test(at)) return at;
1127
+ }
1128
+ throw new Error("The fire \"at\" attribute accepts a non-negative integer or a relative value such as \"+1\".");
1160
1129
  }
1161
1130
  function zennEmbedNode(node, input) {
1162
- if (node.type !== "paragraph" || !Array.isArray(node.children) || node.children.length !== 2) return void 0;
1163
- const [prefix, link] = node.children;
1164
- if (prefix?.type !== "text" || String(prefix.value ?? "").trim() !== "@") return void 0;
1165
- if (link?.type !== "link" || typeof link.url !== "string") return void 0;
1166
- const name = collectMarkdownText(link).trim().toLowerCase();
1167
- if (name === "youtube") {
1168
- return mdxElement(
1169
- "EmbedFrame",
1170
- [
1171
- mdxAttribute("provider", "youtube"),
1172
- mdxAttribute("src", toYoutubeEmbedUrl(link.url)),
1173
- mdxAttribute("fallbackHref", link.url),
1174
- mdxAttribute("title", "YouTube embed example")
1175
- ],
1176
- [{ type: "text", value: "Open YouTube embed" }]
1177
- );
1178
- }
1179
- if (name === "x") {
1180
- return mdxElement("TweetEmbed", [mdxAttribute("href", link.url), mdxAttribute("label", "Open post on X")], []);
1181
- }
1182
- if (name === "card") {
1183
- const metadata = input.linkCardMetadata?.get(link.url);
1184
- return mdxElement(
1185
- "LinkCard",
1186
- [
1187
- mdxAttribute("href", link.url),
1188
- ...metadataAttributes(metadata)
1189
- ],
1190
- []
1191
- );
1192
- }
1193
- if (name === "embed" || name === "iframe") {
1194
- return mdxElement(
1195
- "EmbedFrame",
1196
- [mdxAttribute("src", link.url), mdxAttribute("title", "Embedded content")],
1197
- [{ type: "text", value: "Open embed" }]
1198
- );
1199
- }
1200
- return void 0;
1131
+ if (node.type !== "paragraph" || !Array.isArray(node.children) || node.children.length !== 2) return void 0;
1132
+ const [prefix, link] = node.children;
1133
+ if (prefix?.type !== "text" || String(prefix.value ?? "").trim() !== "@") return void 0;
1134
+ if (link?.type !== "link" || typeof link.url !== "string") return void 0;
1135
+ const name = collectMarkdownText(link).trim().toLowerCase();
1136
+ if (name === "youtube") return mdxElement("EmbedFrame", [
1137
+ mdxAttribute("provider", "youtube"),
1138
+ mdxAttribute("src", toYoutubeEmbedUrl(link.url)),
1139
+ mdxAttribute("fallbackHref", link.url),
1140
+ mdxAttribute("title", "YouTube embed example")
1141
+ ], [{
1142
+ type: "text",
1143
+ value: "Open YouTube embed"
1144
+ }]);
1145
+ if (name === "x") return mdxElement("TweetEmbed", [mdxAttribute("href", link.url), mdxAttribute("label", "Open post on X")], []);
1146
+ if (name === "card") {
1147
+ const metadata = input.linkCardMetadata?.get(link.url);
1148
+ return mdxElement("LinkCard", [mdxAttribute("href", link.url), ...metadataAttributes(metadata)], []);
1149
+ }
1150
+ if (name === "embed" || name === "iframe") return mdxElement("EmbedFrame", [mdxAttribute("src", link.url), mdxAttribute("title", "Embedded content")], [{
1151
+ type: "text",
1152
+ value: "Open embed"
1153
+ }]);
1201
1154
  }
1202
1155
  function metadataAttributes(metadata) {
1203
- if (!metadata) return [];
1204
- return [
1205
- ...metadata.title ? [mdxAttribute("title", metadata.title)] : [],
1206
- ...metadata.description ? [mdxAttribute("description", metadata.description)] : [],
1207
- ...metadata.image ? [mdxAttribute("image", metadata.image)] : [],
1208
- ...metadata.siteName ? [mdxAttribute("siteName", metadata.siteName)] : []
1209
- ];
1156
+ if (!metadata) return [];
1157
+ return [
1158
+ ...metadata.title ? [mdxAttribute("title", metadata.title)] : [],
1159
+ ...metadata.description ? [mdxAttribute("description", metadata.description)] : [],
1160
+ ...metadata.image ? [mdxAttribute("image", metadata.image)] : [],
1161
+ ...metadata.siteName ? [mdxAttribute("siteName", metadata.siteName)] : []
1162
+ ];
1210
1163
  }
1211
1164
  function plainUrlLinkNode(node) {
1212
- if (node.type !== "paragraph" || !Array.isArray(node.children) || node.children.length !== 1) return void 0;
1213
- const [child] = node.children;
1214
- if (child?.type !== "text" || typeof child.value !== "string") return void 0;
1215
- const value = child.value.trim();
1216
- if (!isHttpUrl(value)) return void 0;
1217
- return {
1218
- type: "paragraph",
1219
- children: [
1220
- {
1221
- type: "link",
1222
- url: value,
1223
- children: [{ type: "text", value }]
1224
- }
1225
- ]
1226
- };
1227
- }
1228
- function isHttpUrl(value) {
1229
- try {
1230
- const url = new URL(value);
1231
- return url.protocol === "https:" || url.protocol === "http:";
1232
- } catch {
1233
- return false;
1234
- }
1165
+ if (node.type !== "paragraph" || !Array.isArray(node.children) || node.children.length !== 1) return void 0;
1166
+ const [child] = node.children;
1167
+ if (child?.type !== "text" || typeof child.value !== "string") return void 0;
1168
+ const value = child.value.trim();
1169
+ if (!isHttpUrl$1(value)) return void 0;
1170
+ return {
1171
+ type: "paragraph",
1172
+ children: [{
1173
+ type: "link",
1174
+ url: value,
1175
+ children: [{
1176
+ type: "text",
1177
+ value
1178
+ }]
1179
+ }]
1180
+ };
1181
+ }
1182
+ function isHttpUrl$1(value) {
1183
+ try {
1184
+ const url = new URL(value);
1185
+ return url.protocol === "https:" || url.protocol === "http:";
1186
+ } catch {
1187
+ return false;
1188
+ }
1235
1189
  }
1236
1190
  function fireDirectiveNode(node) {
1237
- if (node.type !== "containerDirective" || node.name !== "fire") return void 0;
1238
- const attributes = directiveAttributes(node);
1239
- if (attributes.order !== void 0) {
1240
- throw new Error('The fire "order" attribute is not supported. Fires reveal in source order.');
1241
- }
1242
- if (attributes.each === void 0 && (attributes.depth !== void 0 || attributes.every !== void 0)) {
1243
- throw new Error('The fire "depth" and "every" attributes require each="item".');
1244
- }
1245
- if (attributes.each !== void 0) return fireEachItemNode(node, attributes);
1246
- const at = attributes.at !== void 0 ? fireAtAttributeValue(attributes.at) : void 0;
1247
- const fireAttributes = [
1248
- ...typeof attributes.effect === "string" ? [mdxAttribute("effect", attributes.effect)] : [],
1249
- ...at ? [mdxAttribute("at", at)] : []
1250
- ];
1251
- return mdxElement("Fire", fireAttributes, node.children ?? []);
1191
+ if (node.type !== "containerDirective" || node.name !== "fire") return void 0;
1192
+ const attributes = directiveAttributes(node);
1193
+ if (attributes.order !== void 0) throw new Error("The fire \"order\" attribute is not supported. Fires reveal in source order.");
1194
+ if (attributes.each === void 0 && (attributes.depth !== void 0 || attributes.every !== void 0)) throw new Error("The fire \"depth\" and \"every\" attributes require each=\"item\".");
1195
+ if (attributes.each !== void 0) return fireEachItemNode(node, attributes);
1196
+ const at = attributes.at !== void 0 ? fireAtAttributeValue(attributes.at) : void 0;
1197
+ return mdxElement("Fire", [...typeof attributes.effect === "string" ? [mdxAttribute("effect", attributes.effect)] : [], ...at ? [mdxAttribute("at", at)] : []], node.children ?? []);
1252
1198
  }
1253
1199
  function fireEachItemNode(node, attributes) {
1254
- if (attributes.each !== "item") {
1255
- throw new Error('The fire "each" attribute only accepts "item".');
1256
- }
1257
- const children = node.children ?? [];
1258
- const list = children.length === 1 && children[0]?.type === "list" ? children[0] : void 0;
1259
- if (!list) {
1260
- throw new Error('fire each="item" must contain exactly one Markdown list.');
1261
- }
1262
- const depth = positiveFireInteger(attributes.depth, "depth", 1);
1263
- const every = positiveFireInteger(attributes.every, "every", 1);
1264
- const at = attributes.at !== void 0 ? fireAtAttributeValue(attributes.at) : void 0;
1265
- const effect = attributes.effect ? fireEffectToken(attributes.effect) : void 0;
1266
- const items = fireListItems(list, depth);
1267
- for (const [itemIndex, item] of items.entries()) {
1268
- const itemAt = fireListItemAt(at, every, itemIndex);
1269
- item.data = {
1270
- ...item.data,
1271
- hProperties: {
1272
- ...item.data?.hProperties,
1273
- "data-hono-decks-fire": "true",
1274
- ...itemAt ? { "data-fire-at": itemAt } : {},
1275
- ...effect ? { "data-fire-effect": effect } : {}
1276
- }
1277
- };
1278
- }
1279
- return list;
1200
+ if (attributes.each !== "item") throw new Error("The fire \"each\" attribute only accepts \"item\".");
1201
+ const children = node.children ?? [];
1202
+ const list = children.length === 1 && children[0]?.type === "list" ? children[0] : void 0;
1203
+ if (!list) throw new Error("fire each=\"item\" must contain exactly one Markdown list.");
1204
+ const depth = positiveFireInteger(attributes.depth, "depth", 1);
1205
+ const every = positiveFireInteger(attributes.every, "every", 1);
1206
+ const at = attributes.at !== void 0 ? fireAtAttributeValue(attributes.at) : void 0;
1207
+ const effect = attributes.effect ? fireEffectToken(attributes.effect) : void 0;
1208
+ const items = fireListItems(list, depth);
1209
+ for (const [itemIndex, item] of items.entries()) {
1210
+ const itemAt = fireListItemAt(at, every, itemIndex);
1211
+ item.data = {
1212
+ ...item.data,
1213
+ hProperties: {
1214
+ ...item.data?.hProperties,
1215
+ "data-hono-decks-fire": "true",
1216
+ ...itemAt ? { "data-fire-at": itemAt } : {},
1217
+ ...effect ? { "data-fire-effect": effect } : {}
1218
+ }
1219
+ };
1220
+ }
1221
+ return list;
1280
1222
  }
1281
1223
  function positiveFireInteger(value, name, fallback) {
1282
- if (value === void 0) return fallback;
1283
- const parsed = Number(value);
1284
- if (Number.isInteger(parsed) && parsed > 0) return parsed;
1285
- throw new Error(`The fire "${name}" attribute accepts a positive integer.`);
1224
+ if (value === void 0) return fallback;
1225
+ const parsed = Number(value);
1226
+ if (Number.isInteger(parsed) && parsed > 0) return parsed;
1227
+ throw new Error(`The fire "${name}" attribute accepts a positive integer.`);
1286
1228
  }
1287
1229
  function fireListItems(list, maxDepth, depth = 1) {
1288
- const items = [];
1289
- for (const item of list.children ?? []) {
1290
- if (item.type !== "listItem") continue;
1291
- items.push(item);
1292
- if (depth >= maxDepth) continue;
1293
- for (const child of item.children ?? []) {
1294
- if (child.type === "list") items.push(...fireListItems(child, maxDepth, depth + 1));
1295
- }
1296
- }
1297
- return items;
1230
+ const items = [];
1231
+ for (const item of list.children ?? []) {
1232
+ if (item.type !== "listItem") continue;
1233
+ items.push(item);
1234
+ if (depth >= maxDepth) continue;
1235
+ for (const child of item.children ?? []) if (child.type === "list") items.push(...fireListItems(child, maxDepth, depth + 1));
1236
+ }
1237
+ return items;
1298
1238
  }
1299
1239
  function fireListItemAt(at, every, itemIndex) {
1300
- if (at && /^\d+$/.test(at)) return String(Number(at) + Math.floor(itemIndex / every));
1301
- if (!at && every === 1) return void 0;
1302
- if (itemIndex === 0) return at;
1303
- return itemIndex % every === 0 ? "+1" : "+0";
1240
+ if (at && /^\d+$/.test(at)) return String(Number(at) + Math.floor(itemIndex / every));
1241
+ if (!at && every === 1) return void 0;
1242
+ if (itemIndex === 0) return at;
1243
+ return itemIndex % every === 0 ? "+1" : "+0";
1304
1244
  }
1305
1245
  function fireEffectToken(value) {
1306
- return value.trim().replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase() || "fade";
1246
+ return value.trim().replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase() || "fade";
1307
1247
  }
1308
1248
  function unknownDirectiveFallback(node) {
1309
- if (node.type === "textDirective") return { type: "text", value: `:${node.name ?? ""}` };
1310
- if (node.type === "leafDirective") return { type: "text", value: `::${node.name ?? ""}` };
1311
- if (node.type === "containerDirective") {
1312
- return {
1313
- type: "paragraph",
1314
- children: [{ type: "text", value: `:::${node.name ?? ""}` }, ...node.children ?? []]
1315
- };
1316
- }
1317
- return void 0;
1249
+ if (node.type === "textDirective") return {
1250
+ type: "text",
1251
+ value: `:${node.name ?? ""}`
1252
+ };
1253
+ if (node.type === "leafDirective") return {
1254
+ type: "text",
1255
+ value: `::${node.name ?? ""}`
1256
+ };
1257
+ if (node.type === "containerDirective") return {
1258
+ type: "paragraph",
1259
+ children: [{
1260
+ type: "text",
1261
+ value: `:::${node.name ?? ""}`
1262
+ }, ...node.children ?? []]
1263
+ };
1318
1264
  }
1319
1265
  function directiveAttributes(node) {
1320
- if (!node.attributes || Array.isArray(node.attributes)) return {};
1321
- const result2 = {};
1322
- for (const [key, value] of Object.entries(node.attributes)) {
1323
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
1324
- result2[key] = String(value);
1325
- }
1326
- }
1327
- return result2;
1266
+ if (!node.attributes || Array.isArray(node.attributes)) return {};
1267
+ const result = {};
1268
+ for (const [key, value] of Object.entries(node.attributes)) if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") result[key] = String(value);
1269
+ return result;
1328
1270
  }
1329
1271
  function mdxElement(name, attributes, children) {
1330
- return {
1331
- type: "mdxJsxFlowElement",
1332
- name,
1333
- attributes,
1334
- children
1335
- };
1272
+ return {
1273
+ type: "mdxJsxFlowElement",
1274
+ name,
1275
+ attributes,
1276
+ children
1277
+ };
1336
1278
  }
1337
1279
  function toYoutubeEmbedUrl(value) {
1338
- try {
1339
- const url = new URL(value);
1340
- const host = url.hostname.replace(/^www\./, "");
1341
- if (host === "youtu.be") {
1342
- const id = url.pathname.split("/").filter(Boolean)[0];
1343
- return id ? `https://www.youtube.com/embed/${id}` : value;
1344
- }
1345
- if (host === "youtube.com" || host === "m.youtube.com") {
1346
- if (url.pathname.startsWith("/embed/")) return value;
1347
- const id = url.searchParams.get("v");
1348
- return id ? `https://www.youtube.com/embed/${id}` : value;
1349
- }
1350
- return value;
1351
- } catch {
1352
- return value;
1353
- }
1280
+ try {
1281
+ const url = new URL(value);
1282
+ const host = url.hostname.replace(/^www\./, "");
1283
+ if (host === "youtu.be") {
1284
+ const id = url.pathname.split("/").filter(Boolean)[0];
1285
+ return id ? `https://www.youtube.com/embed/${id}` : value;
1286
+ }
1287
+ if (host === "youtube.com" || host === "m.youtube.com") {
1288
+ if (url.pathname.startsWith("/embed/")) return value;
1289
+ const id = url.searchParams.get("v");
1290
+ return id ? `https://www.youtube.com/embed/${id}` : value;
1291
+ }
1292
+ return value;
1293
+ } catch {
1294
+ return value;
1295
+ }
1354
1296
  }
1355
1297
  async function highlightMarkdownNode(node) {
1356
- if (node.type === "code") {
1357
- const code = typeof node.value === "string" ? node.value : "";
1358
- const lang = typeof node.lang === "string" && node.lang ? node.lang : void 0;
1359
- const highlightedHtml = await highlightCodeBlock(code, lang);
1360
- node.type = "mdxJsxFlowElement";
1361
- node.name = "CodeBlock";
1362
- node.attributes = [
1363
- ...lang ? [mdxAttribute("lang", lang)] : [],
1364
- mdxAttribute("highlightedHtml", highlightedHtml)
1365
- ];
1366
- node.children = [{ type: "text", value: code }];
1367
- delete node.value;
1368
- delete node.lang;
1369
- delete node.meta;
1370
- return;
1371
- }
1372
- if (node.type === "mdxJsxFlowElement" && node.name === "CodeBlock") {
1373
- const code = collectMarkdownText(node);
1374
- if (code.trim()) {
1375
- const lang = getMdxStringAttribute(node, "lang");
1376
- const highlightedHtml = await highlightCodeBlock(code, lang);
1377
- node.attributes = upsertMdxStringAttribute(node.attributes, "highlightedHtml", highlightedHtml);
1378
- }
1379
- return;
1380
- }
1381
- if (!Array.isArray(node.children)) return;
1382
- await Promise.all(node.children.map((child) => highlightMarkdownNode(child)));
1298
+ if (node.type === "code") {
1299
+ const code = typeof node.value === "string" ? node.value : "";
1300
+ const lang = typeof node.lang === "string" && node.lang ? node.lang : void 0;
1301
+ const highlightedHtml = await highlightCodeBlock(code, lang);
1302
+ node.type = "mdxJsxFlowElement";
1303
+ node.name = "CodeBlock";
1304
+ node.attributes = [...lang ? [mdxAttribute("lang", lang)] : [], mdxAttribute("highlightedHtml", highlightedHtml)];
1305
+ node.children = [{
1306
+ type: "text",
1307
+ value: code
1308
+ }];
1309
+ delete node.value;
1310
+ delete node.lang;
1311
+ delete node.meta;
1312
+ return;
1313
+ }
1314
+ if (node.type === "mdxJsxFlowElement" && node.name === "CodeBlock") {
1315
+ const code = collectMarkdownText(node);
1316
+ if (code.trim()) {
1317
+ const highlightedHtml = await highlightCodeBlock(code, getMdxStringAttribute(node, "lang"));
1318
+ node.attributes = upsertMdxStringAttribute(node.attributes, "highlightedHtml", highlightedHtml);
1319
+ }
1320
+ return;
1321
+ }
1322
+ if (!Array.isArray(node.children)) return;
1323
+ await Promise.all(node.children.map((child) => highlightMarkdownNode(child)));
1383
1324
  }
1384
1325
  async function highlightCodeBlock(code, lang) {
1385
- const language = lang && /^[A-Za-z0-9_#+.-]+$/.test(lang) ? lang : "text";
1386
- try {
1387
- return await codeToHtml(code, { lang: language, theme: "github-dark" });
1388
- } catch (error) {
1389
- if (language === "text") throw error;
1390
- return codeToHtml(code, { lang: "text", theme: "github-dark" });
1391
- }
1326
+ const language = lang && /^[A-Za-z0-9_#+.-]+$/.test(lang) ? lang : "text";
1327
+ try {
1328
+ return await codeToHtml(code, {
1329
+ lang: language,
1330
+ theme: "github-dark"
1331
+ });
1332
+ } catch (error) {
1333
+ if (language === "text") throw error;
1334
+ return codeToHtml(code, {
1335
+ lang: "text",
1336
+ theme: "github-dark"
1337
+ });
1338
+ }
1392
1339
  }
1393
1340
  function mdxAttribute(name, value) {
1394
- return { type: "mdxJsxAttribute", name, value };
1341
+ return {
1342
+ type: "mdxJsxAttribute",
1343
+ name,
1344
+ value
1345
+ };
1395
1346
  }
1396
1347
  function getMdxStringAttribute(node, name) {
1397
- const attributes = Array.isArray(node.attributes) ? node.attributes : [];
1398
- const attribute = attributes.find((item) => item.type === "mdxJsxAttribute" && item.name === name);
1399
- return typeof attribute?.value === "string" ? attribute.value : void 0;
1348
+ const attribute = (Array.isArray(node.attributes) ? node.attributes : []).find((item) => item.type === "mdxJsxAttribute" && item.name === name);
1349
+ return typeof attribute?.value === "string" ? attribute.value : void 0;
1400
1350
  }
1401
1351
  function upsertMdxStringAttribute(attributes, name, value) {
1402
- const next = Array.isArray(attributes) ? [...attributes] : [];
1403
- const index = next.findIndex((item) => item.type === "mdxJsxAttribute" && item.name === name);
1404
- const attribute = mdxAttribute(name, value);
1405
- if (index === -1) return [...next, attribute];
1406
- next[index] = attribute;
1407
- return next;
1352
+ const next = Array.isArray(attributes) ? [...attributes] : [];
1353
+ const index = next.findIndex((item) => item.type === "mdxJsxAttribute" && item.name === name);
1354
+ const attribute = mdxAttribute(name, value);
1355
+ if (index === -1) return [...next, attribute];
1356
+ next[index] = attribute;
1357
+ return next;
1408
1358
  }
1409
1359
  function collectMarkdownText(node) {
1410
- if (typeof node.value === "string") return node.value;
1411
- if (!Array.isArray(node.children)) return "";
1412
- return node.children.map((child) => collectMarkdownText(child)).join(node.type === "paragraph" ? "\n" : "");
1360
+ if (typeof node.value === "string") return node.value;
1361
+ if (!Array.isArray(node.children)) return "";
1362
+ return node.children.map((child) => collectMarkdownText(child)).join(node.type === "paragraph" ? "\n" : "");
1413
1363
  }
1414
-
1415
- // src/generator/mdx-module-generator.ts
1364
+ //#endregion
1365
+ //#region src/generator/mdx-module-generator.ts
1416
1366
  async function compileMdxModuleDecks(input) {
1417
- const decks = await Promise.all(input.decks.map((entry) => compileMdxModuleDeck(input, entry)));
1418
- return {
1419
- decks,
1420
- routerModule: emitModuleDecksRouter({ decks })
1421
- };
1367
+ const decks = await Promise.all(input.decks.map((entry) => compileMdxModuleDeck(input, entry)));
1368
+ return {
1369
+ decks,
1370
+ routerModule: emitModuleDecksRouter({ decks })
1371
+ };
1422
1372
  }
1423
1373
  async function compileMdxModuleDeck(input, entry) {
1424
- const source = await input.readText(entry.sourcePath);
1425
- const { attrs, body } = readFrontmatter(source);
1426
- const { prelude, body: contentBody } = extractDeckPrelude(body);
1427
- const slideSources = splitSlideSources(contentBody);
1428
- let warnings = [];
1429
- const deckMeta = toDeckFrontmatter(attrs, warnings);
1430
- addUnknownFrontmatterWarnings(warnings, deckMeta.meta, "deck");
1431
- const assets = [
1432
- ...await buildAssetRefs(entry.slug, entry.assetPaths, input),
1433
- ...collectGeneratedExternalAssetRefs(contentBody, deckMeta)
1434
- ];
1435
- const componentModulePath = componentImportPath(input.outDir, input.componentModulePaths?.[entry.slug]);
1436
- const themeStyle = entry.kind === "directory" ? input.themeStyles?.[entry.slug] : void 0;
1437
- const slideModules = [];
1438
- const slides = [];
1439
- for (let index = 0; index < slideSources.length; index += 1) {
1440
- const { attrs: slideAttrs, body: slideBody } = readFrontmatter(slideSources[index]);
1441
- const speakerNotes = extractMdxCommentSpeakerNotes(slideBody);
1442
- const parsed = parseDeckWithWarnings(speakerNotes.body);
1443
- warnings = warnings.concat(toGeneratedParserCompileWarnings(parsed.warnings, index));
1444
- const firstParsedSlide = parsed.slides[0];
1445
- const slideModulePath = `${input.outDir}/decks/${entry.slug}/slide-${index}.ts`;
1446
- const slideMeta = toSlideFrontmatter(slideAttrs, warnings, {
1447
- slideIndex: index,
1448
- fallbackTransition: deckMeta.transition,
1449
- fallbackTransitionDuration: deckMeta.transitionDuration,
1450
- fallbackTransitionEasing: deckMeta.transitionEasing,
1451
- fallbackTitle: firstParsedSlide?.title,
1452
- fallbackLayout: firstParsedSlide?.layout,
1453
- fallbackClassName: firstParsedSlide?.className
1454
- });
1455
- addUnknownFrontmatterWarnings(warnings, slideMeta.meta, "slide", index);
1456
- const moduleSource = [prelude, rewriteAssetUrls(speakerNotes.body, assets)].filter(Boolean).join("\n\n");
1457
- const rewrittenSource = rewriteRelativeMdxImports(moduleSource, dirname(entry.sourcePath), dirname(slideModulePath));
1458
- const code = await compileMdxModule(rewrittenSource, entry.sourcePath, index, input.resolveOgp);
1459
- slideModules.push({
1460
- path: slideModulePath,
1461
- importPath: `./decks/${entry.slug}/slide-${index}`,
1462
- code
1463
- });
1464
- slides.push({
1465
- index,
1466
- meta: slideMeta,
1467
- html: "",
1468
- components: [],
1469
- notes: combineSpeakerNotes(slideMeta.notes, speakerNotes.notes)
1470
- });
1471
- }
1472
- addExternalAssetWarnings(warnings, assets);
1473
- return {
1474
- componentModulePath,
1475
- clientComponentIds: input.clientComponentIds?.[entry.slug],
1476
- deck: {
1477
- slug: entry.slug,
1478
- sourcePath: entry.sourcePath,
1479
- kind: entry.kind,
1480
- meta: deckMeta,
1481
- themeStyle: themeStyle?.style,
1482
- themeSourcePath: themeStyle?.sourcePath,
1483
- slides,
1484
- assets,
1485
- warnings
1486
- },
1487
- slideModules
1488
- };
1374
+ const { attrs, body } = readFrontmatter(await input.readText(entry.sourcePath));
1375
+ const { prelude, body: contentBody } = extractDeckPrelude(body);
1376
+ const slideSources = splitSlideSources(contentBody);
1377
+ let warnings = [];
1378
+ const deckMeta = toDeckFrontmatter(attrs, warnings);
1379
+ addUnknownFrontmatterWarnings(warnings, deckMeta.meta, "deck");
1380
+ const assets = [...await buildAssetRefs(entry.slug, entry.assetPaths, input), ...collectGeneratedExternalAssetRefs(contentBody, deckMeta)];
1381
+ const componentModulePath = componentImportPath(input.outDir, input.componentModulePaths?.[entry.slug]);
1382
+ const themeStyle = entry.kind === "directory" ? input.themeStyles?.[entry.slug] : void 0;
1383
+ const slideModules = [];
1384
+ const slides = [];
1385
+ for (let index = 0; index < slideSources.length; index += 1) {
1386
+ const { attrs: slideAttrs, body: slideBody } = readFrontmatter(slideSources[index]);
1387
+ const speakerNotes = extractMdxCommentSpeakerNotes(slideBody);
1388
+ const parsed = parseDeckWithWarnings(speakerNotes.body);
1389
+ warnings = warnings.concat(toGeneratedParserCompileWarnings(parsed.warnings, index));
1390
+ const firstParsedSlide = parsed.slides[0];
1391
+ const slideModulePath = `${input.outDir}/decks/${entry.slug}/slide-${index}.ts`;
1392
+ const slideMeta = toSlideFrontmatter(slideAttrs, warnings, {
1393
+ slideIndex: index,
1394
+ fallbackTransition: deckMeta.transition,
1395
+ fallbackTransitionDuration: deckMeta.transitionDuration,
1396
+ fallbackTransitionEasing: deckMeta.transitionEasing,
1397
+ fallbackTitle: firstParsedSlide?.title,
1398
+ fallbackLayout: firstParsedSlide?.layout,
1399
+ fallbackClassName: firstParsedSlide?.className
1400
+ });
1401
+ addUnknownFrontmatterWarnings(warnings, slideMeta.meta, "slide", index);
1402
+ const code = await compileMdxModule(rewriteRelativeMdxImports([prelude, rewriteAssetUrls(speakerNotes.body, assets)].filter(Boolean).join("\n\n"), dirname$2(entry.sourcePath), dirname$2(slideModulePath)), entry.sourcePath, index, input.resolveOgp);
1403
+ slideModules.push({
1404
+ path: slideModulePath,
1405
+ importPath: `./decks/${entry.slug}/slide-${index}`,
1406
+ code
1407
+ });
1408
+ slides.push({
1409
+ index,
1410
+ meta: slideMeta,
1411
+ html: "",
1412
+ components: [],
1413
+ notes: combineSpeakerNotes(slideMeta.notes, speakerNotes.notes)
1414
+ });
1415
+ }
1416
+ addExternalAssetWarnings(warnings, assets);
1417
+ return {
1418
+ componentModulePath,
1419
+ clientComponentIds: input.clientComponentIds?.[entry.slug],
1420
+ deck: {
1421
+ slug: entry.slug,
1422
+ sourcePath: entry.sourcePath,
1423
+ kind: entry.kind,
1424
+ meta: deckMeta,
1425
+ themeStyle: themeStyle?.style,
1426
+ themeSourcePath: themeStyle?.sourcePath,
1427
+ slides,
1428
+ assets,
1429
+ warnings
1430
+ },
1431
+ slideModules
1432
+ };
1489
1433
  }
1490
1434
  function collectGeneratedExternalAssetRefs(contentBody, deckMeta) {
1491
- return buildExternalAssetRefs([
1492
- ...collectMarkdownAssetCandidates(contentBody),
1493
- ...collectFrontmatterAssetCandidates(deckMeta.assets)
1494
- ]);
1435
+ return buildExternalAssetRefs([...collectMarkdownAssetCandidates(contentBody), ...collectFrontmatterAssetCandidates(deckMeta.assets)]);
1495
1436
  }
1496
1437
  function generatedMdxParserWarnings(warnings) {
1497
- return warnings.filter((warning) => warning.code === "code-fence-unclosed");
1438
+ return warnings.filter((warning) => warning.code === "code-fence-unclosed");
1498
1439
  }
1499
1440
  function toGeneratedParserCompileWarnings(warnings, slideIndex) {
1500
- return generatedMdxParserWarnings(warnings).map((warning) => ({
1501
- code: "parse-warning",
1502
- message: warning.message,
1503
- slideIndex
1504
- }));
1441
+ return generatedMdxParserWarnings(warnings).map((warning) => ({
1442
+ code: "parse-warning",
1443
+ message: warning.message,
1444
+ slideIndex
1445
+ }));
1505
1446
  }
1506
1447
  async function compileMdxModule(source, sourcePath, slideIndex, resolveOgp) {
1507
- try {
1508
- const linkCardMetadata = await resolveLinkCardMetadataByUrl(source, resolveOgp);
1509
- const compiled = String(
1510
- await compile(
1511
- { path: `${sourcePath}#slide-${slideIndex + 1}`, value: source },
1512
- {
1513
- jsxRuntime: "automatic",
1514
- jsxImportSource: "hono/jsx",
1515
- format: "mdx",
1516
- elementAttributeNameCase: "html",
1517
- remarkPlugins: [
1518
- remarkGfm2,
1519
- remarkDirective,
1520
- remarkDeckSyntax({ linkCardMetadata }),
1521
- remarkCodeHighlight
1522
- ]
1523
- }
1524
- )
1525
- );
1526
- return `// @ts-nocheck
1527
- ${compiled}`;
1528
- } catch (error) {
1529
- const message = error instanceof Error ? error.message : String(error);
1530
- throw new CompileError(
1531
- `MDX compile failed in ${sourcePath} slide ${slideIndex + 1}: ${message}`,
1532
- "mdx-compile-error"
1533
- );
1534
- }
1448
+ try {
1449
+ const linkCardMetadata = await resolveLinkCardMetadataByUrl(source, resolveOgp);
1450
+ return `// @ts-nocheck\n${String(await compile({
1451
+ path: `${sourcePath}#slide-${slideIndex + 1}`,
1452
+ value: source
1453
+ }, {
1454
+ jsxRuntime: "automatic",
1455
+ jsxImportSource: "hono/jsx",
1456
+ format: "mdx",
1457
+ elementAttributeNameCase: "html",
1458
+ remarkPlugins: [
1459
+ remarkGfm,
1460
+ remarkDirective,
1461
+ remarkDeckSyntax({ linkCardMetadata }),
1462
+ remarkCodeHighlight
1463
+ ]
1464
+ }))}`;
1465
+ } catch (error) {
1466
+ const message = error instanceof Error ? error.message : String(error);
1467
+ throw new CompileError(`MDX compile failed in ${sourcePath} slide ${slideIndex + 1}: ${message}`, "mdx-compile-error");
1468
+ }
1535
1469
  }
1536
1470
  function extractDeckPrelude(source) {
1537
- const lines = source.replace(/\r\n/g, "\n").split("\n");
1538
- const prelude = [];
1539
- let cursor = 0;
1540
- while (cursor < lines.length) {
1541
- const line = lines[cursor];
1542
- if (line.trim() === "") {
1543
- prelude.push(line);
1544
- cursor += 1;
1545
- continue;
1546
- }
1547
- if (/^\s*(import|export)\s/.test(line)) {
1548
- prelude.push(line);
1549
- cursor += 1;
1550
- continue;
1551
- }
1552
- break;
1553
- }
1554
- return {
1555
- prelude: prelude.join("\n").trim(),
1556
- body: lines.slice(cursor).join("\n").trim()
1557
- };
1558
- }
1559
-
1560
- // src/node/client-entry.ts
1561
- import { existsSync } from "fs";
1562
- import { readFile } from "fs/promises";
1563
- import { join } from "path";
1564
- import { fileURLToPath } from "url";
1565
- import { build as buildBrowserBundle } from "esbuild";
1471
+ const lines = source.replace(/\r\n/g, "\n").split("\n");
1472
+ const prelude = [];
1473
+ let cursor = 0;
1474
+ while (cursor < lines.length) {
1475
+ const line = lines[cursor];
1476
+ if (line.trim() === "") {
1477
+ prelude.push(line);
1478
+ cursor += 1;
1479
+ continue;
1480
+ }
1481
+ if (/^\s*(import|export)\s/.test(line)) {
1482
+ prelude.push(line);
1483
+ cursor += 1;
1484
+ continue;
1485
+ }
1486
+ break;
1487
+ }
1488
+ return {
1489
+ prelude: prelude.join("\n").trim(),
1490
+ body: lines.slice(cursor).join("\n").trim()
1491
+ };
1492
+ }
1493
+ //#endregion
1494
+ //#region src/node/client-entry.ts
1566
1495
  async function discoverClientComponentIds(input) {
1567
- const result2 = {};
1568
- for (const entry of input.clientEntries) {
1569
- const source = await readFile(join(input.cwd, entry.sourcePath), "utf8");
1570
- const exports = extractComponentExportNames(source);
1571
- result2[entry.slug] = Object.fromEntries(exports.map((name) => [name, clientComponentId(entry.slug, name)]));
1572
- }
1573
- return result2;
1496
+ const result = {};
1497
+ for (const entry of input.clientEntries) {
1498
+ const exports = extractComponentExportNames(await readFile(join(input.cwd, entry.sourcePath), "utf8"));
1499
+ result[entry.slug] = Object.fromEntries(exports.map((name) => [name, clientComponentId(entry.slug, name)]));
1500
+ }
1501
+ return result;
1574
1502
  }
1575
1503
  async function emitClientEntryModule(input) {
1576
- const imports = [];
1577
- const registrations = [];
1578
- for (const entry of input.clientEntries) {
1579
- const ids = input.clientComponentIds[entry.slug] ?? {};
1580
- for (const [exportName, clientId] of Object.entries(ids)) {
1581
- const localName = clientImportName(entry.slug, exportName);
1582
- imports.push(`import { ${exportName} as ${localName} } from ${JSON.stringify(join(input.cwd, entry.sourcePath))};`);
1583
- registrations.push(`${JSON.stringify(clientId)}: ${localName}`);
1584
- }
1585
- }
1586
- if (registrations.length === 0) return 'export const decksClientEntry = "";\n';
1587
- const entryContents = `import { hydrateSlideIslands } from "hono-decks/client";
1504
+ const imports = [];
1505
+ const registrations = [];
1506
+ for (const entry of input.clientEntries) {
1507
+ const ids = input.clientComponentIds[entry.slug] ?? {};
1508
+ for (const [exportName, clientId] of Object.entries(ids)) {
1509
+ const localName = clientImportName(entry.slug, exportName);
1510
+ imports.push(`import { ${exportName} as ${localName} } from ${JSON.stringify(join(input.cwd, entry.sourcePath))};`);
1511
+ registrations.push(`${JSON.stringify(clientId)}: ${localName}`);
1512
+ }
1513
+ }
1514
+ if (registrations.length === 0) return "export const decksClientEntry = \"\";\n";
1515
+ const output = (await build({
1516
+ stdin: {
1517
+ contents: `import { hydrateSlideIslands } from "hono-decks/client";
1588
1518
  ${imports.join("\n")}
1589
1519
 
1590
1520
  hydrateSlideIslands({
@@ -1592,496 +1522,458 @@ hydrateSlideIslands({
1592
1522
  ${registrations.join(",\n ")}
1593
1523
  }
1594
1524
  });
1595
- `;
1596
- const result2 = await buildBrowserBundle({
1597
- stdin: {
1598
- contents: entryContents,
1599
- resolveDir: input.cwd,
1600
- sourcefile: "hono-decks-client-entry.tsx",
1601
- loader: "tsx"
1602
- },
1603
- bundle: true,
1604
- write: false,
1605
- format: "esm",
1606
- platform: "browser",
1607
- target: "es2022",
1608
- jsx: "automatic",
1609
- jsxImportSource: "hono/jsx/dom",
1610
- nodePaths: nodeModuleFallbackPaths(input.cwd),
1611
- alias: { "hono-decks/client": resolveClientRuntimeEntry() },
1612
- sourcemap: false,
1613
- minify: false
1614
- });
1615
- const output = result2.outputFiles[0];
1616
- if (!output) throw new Error("Client entry did not produce output.");
1617
- return `export const decksClientEntry = ${JSON.stringify(output.text)};
1618
- `;
1525
+ `,
1526
+ resolveDir: input.cwd,
1527
+ sourcefile: "hono-decks-client-entry.tsx",
1528
+ loader: "tsx"
1529
+ },
1530
+ bundle: true,
1531
+ write: false,
1532
+ format: "esm",
1533
+ platform: "browser",
1534
+ target: "es2022",
1535
+ jsx: "automatic",
1536
+ jsxImportSource: "hono/jsx/dom",
1537
+ nodePaths: nodeModuleFallbackPaths(input.cwd),
1538
+ alias: { "hono-decks/client": resolveClientRuntimeEntry() },
1539
+ sourcemap: false,
1540
+ minify: false
1541
+ })).outputFiles[0];
1542
+ if (!output) throw new Error("Client entry did not produce output.");
1543
+ return `export const decksClientEntry = ${JSON.stringify(output.text)};\n`;
1619
1544
  }
1620
1545
  function resolveClientRuntimeEntry() {
1621
- const built = fileURLToPath(new URL("./client.js", import.meta.url));
1622
- if (existsSync(built)) return built;
1623
- return fileURLToPath(new URL("../client.ts", import.meta.url));
1546
+ const built = fileURLToPath(new URL("./client.js", import.meta.url));
1547
+ if (existsSync(built)) return built;
1548
+ return fileURLToPath(new URL("../client.ts", import.meta.url));
1624
1549
  }
1625
1550
  function extractComponentExportNames(source) {
1626
- const names = /* @__PURE__ */ new Set();
1627
- for (const match of source.matchAll(/\bexport\s+function\s+([A-Z][A-Za-z0-9_]*)\b/g)) {
1628
- names.add(match[1]);
1629
- }
1630
- for (const match of source.matchAll(/\bexport\s+const\s+([A-Z][A-Za-z0-9_]*)\b/g)) {
1631
- names.add(match[1]);
1632
- }
1633
- return [...names].sort();
1551
+ const names = /* @__PURE__ */ new Set();
1552
+ for (const match of source.matchAll(/\bexport\s+function\s+([A-Z][A-Za-z0-9_]*)\b/g)) names.add(match[1]);
1553
+ for (const match of source.matchAll(/\bexport\s+const\s+([A-Z][A-Za-z0-9_]*)\b/g)) names.add(match[1]);
1554
+ return [...names].sort();
1634
1555
  }
1635
1556
  function nodeModuleFallbackPaths(cwd) {
1636
- const current = process.cwd();
1637
- return [
1638
- join(cwd, "node_modules"),
1639
- join(cwd, "..", "node_modules"),
1640
- join(cwd, "..", "..", "node_modules"),
1641
- join(current, "node_modules"),
1642
- join(current, "..", "node_modules"),
1643
- join(current, "..", "..", "node_modules")
1644
- ];
1557
+ const current = process.cwd();
1558
+ return [
1559
+ join(cwd, "node_modules"),
1560
+ join(cwd, "..", "node_modules"),
1561
+ join(cwd, "..", "..", "node_modules"),
1562
+ join(current, "node_modules"),
1563
+ join(current, "..", "node_modules"),
1564
+ join(current, "..", "..", "node_modules")
1565
+ ];
1645
1566
  }
1646
1567
  function clientComponentId(slug, exportName) {
1647
- const base = `${exportName}__${safeIdentifier2(slug)}`;
1648
- return `${base}_${hashString(`${slug}:${exportName}`).slice(0, 8)}`;
1568
+ return `${`${exportName}__${safeIdentifier(slug)}`}_${hashString(`${slug}:${exportName}`).slice(0, 8)}`;
1649
1569
  }
1650
1570
  function clientImportName(slug, exportName) {
1651
- return `${safeIdentifier2(exportName)}__${safeIdentifier2(slug)}_${hashString(`${slug}:${exportName}`).slice(0, 8)}`;
1571
+ return `${safeIdentifier(exportName)}__${safeIdentifier(slug)}_${hashString(`${slug}:${exportName}`).slice(0, 8)}`;
1652
1572
  }
1653
1573
  function hashString(value) {
1654
- let hash = 5381;
1655
- for (let index = 0; index < value.length; index += 1) {
1656
- hash = hash * 33 ^ value.charCodeAt(index);
1657
- }
1658
- return (hash >>> 0).toString(36);
1574
+ let hash = 5381;
1575
+ for (let index = 0; index < value.length; index += 1) hash = hash * 33 ^ value.charCodeAt(index);
1576
+ return (hash >>> 0).toString(36);
1659
1577
  }
1660
- function safeIdentifier2(value) {
1661
- return value.replace(/[^A-Za-z0-9_$]+/g, "_").replace(/^[^A-Za-z_$]+/, "_") || "_";
1578
+ function safeIdentifier(value) {
1579
+ return value.replace(/[^A-Za-z0-9_$]+/g, "_").replace(/^[^A-Za-z_$]+/, "_") || "_";
1662
1580
  }
1663
-
1664
- // src/node/local-deck-io.ts
1665
- import { existsSync as existsSync2, watch } from "fs";
1666
- import { readdir, readFile as readFile2 } from "fs/promises";
1667
- import { join as join2, relative as relative2 } from "path";
1668
-
1669
- // src/node/path-utils.ts
1670
- import { relative } from "path";
1671
- function dirname2(path) {
1672
- const normalized = normalizePath4(path);
1673
- return normalized.includes("/") ? normalized.slice(0, normalized.lastIndexOf("/")) : ".";
1581
+ //#endregion
1582
+ //#region src/node/path-utils.ts
1583
+ function dirname$1(path) {
1584
+ const normalized = normalizePath$1(path);
1585
+ return normalized.includes("/") ? normalized.slice(0, normalized.lastIndexOf("/")) : ".";
1674
1586
  }
1675
- function normalizePath4(path) {
1676
- return path.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
1587
+ function normalizePath$1(path) {
1588
+ return path.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
1677
1589
  }
1678
1590
  function normalizeDeckRoot(root) {
1679
- const normalized = normalizeRelativePath(root, "Deck root").replace(/\/$/, "");
1680
- if (normalized === ".") {
1681
- throw new Error("Deck root must be a relative path inside the current working directory");
1682
- }
1683
- return normalized;
1591
+ const normalized = normalizeRelativePath(root, "Deck root").replace(/\/$/, "");
1592
+ if (normalized === ".") throw new Error("Deck root must be a relative path inside the current working directory");
1593
+ return normalized;
1684
1594
  }
1685
1595
  function normalizeRelativePath(path, label) {
1686
- const normalized = normalizePath4(path).replace(/\/$/, "");
1687
- const segments = normalized.split("/");
1688
- if (normalized === "" || normalized.startsWith("/") || /^[A-Za-z]:\//.test(normalized) || segments.includes("..")) {
1689
- throw new Error(`${label} must be a relative path inside the current working directory`);
1690
- }
1691
- return normalized;
1596
+ const normalized = normalizePath$1(path).replace(/\/$/, "");
1597
+ const segments = normalized.split("/");
1598
+ 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`);
1599
+ return normalized;
1692
1600
  }
1693
-
1694
- // src/node/local-deck-io.ts
1601
+ //#endregion
1602
+ //#region src/node/local-deck-io.ts
1695
1603
  async function listFiles(cwd, dir) {
1696
- const entries = await readdir(dir, { withFileTypes: true });
1697
- const paths = await Promise.all(
1698
- entries.map(async (entry) => {
1699
- const fullPath = join2(dir, entry.name);
1700
- if (entry.isDirectory()) return listFiles(cwd, fullPath);
1701
- if (entry.isFile()) return [normalizePath4(relative2(cwd, fullPath))];
1702
- return [];
1703
- })
1704
- );
1705
- return paths.flat().sort();
1706
- }
1707
-
1708
- // src/node/ogp.ts
1709
- import { lookup } from "dns/promises";
1710
- import { isIP } from "net";
1711
- var MAX_OGP_BYTES = 256 * 1024;
1712
- var MAX_OGP_REDIRECTS = 3;
1604
+ const entries = await readdir(dir, { withFileTypes: true });
1605
+ return (await Promise.all(entries.map(async (entry) => {
1606
+ const fullPath = join(dir, entry.name);
1607
+ if (entry.isDirectory()) return listFiles(cwd, fullPath);
1608
+ if (entry.isFile()) return [normalizePath$1(relative(cwd, fullPath))];
1609
+ return [];
1610
+ }))).flat().sort();
1611
+ }
1612
+ //#endregion
1613
+ //#region src/node/ogp.ts
1614
+ const MAX_OGP_BYTES = 256 * 1024;
1615
+ const MAX_OGP_REDIRECTS = 3;
1713
1616
  async function resolveOgpMetadata(url) {
1714
- if (!isHttpUrl2(url) || typeof fetch === "undefined") return void 0;
1715
- try {
1716
- const response = await fetchPublicHttpUrl(url);
1717
- if (!response) return void 0;
1718
- const contentType = response.headers.get("content-type") ?? "";
1719
- if (!response.ok || !contentType.toLowerCase().includes("text/html") || isTooLarge(response)) return void 0;
1720
- const html = await readTextWithLimit(response, MAX_OGP_BYTES);
1721
- if (!html) return void 0;
1722
- return parseOgpHtml(html, response.url || url);
1723
- } catch {
1724
- return void 0;
1725
- }
1617
+ if (!isHttpUrl(url) || typeof fetch === "undefined") return void 0;
1618
+ try {
1619
+ const response = await fetchPublicHttpUrl(url);
1620
+ if (!response) return void 0;
1621
+ const contentType = response.headers.get("content-type") ?? "";
1622
+ if (!response.ok || !contentType.toLowerCase().includes("text/html") || isTooLarge(response)) return void 0;
1623
+ const html = await readTextWithLimit(response, MAX_OGP_BYTES);
1624
+ if (!html) return void 0;
1625
+ return parseOgpHtml(html, response.url || url);
1626
+ } catch {
1627
+ return;
1628
+ }
1726
1629
  }
1727
1630
  async function fetchPublicHttpUrl(url) {
1728
- let current = url;
1729
- for (let redirect = 0; redirect <= MAX_OGP_REDIRECTS; redirect += 1) {
1730
- if (!await isPublicHttpUrl(current)) return void 0;
1731
- const response = await fetch(current, {
1732
- headers: { accept: "text/html,application/xhtml+xml" },
1733
- redirect: "manual",
1734
- signal: AbortSignal.timeout(1500)
1735
- });
1736
- if (!isRedirect(response.status)) return response;
1737
- await response.body?.cancel();
1738
- const location = response.headers.get("location");
1739
- if (!location) return void 0;
1740
- current = new URL(location, current).toString();
1741
- }
1742
- return void 0;
1631
+ let current = url;
1632
+ for (let redirect = 0; redirect <= MAX_OGP_REDIRECTS; redirect += 1) {
1633
+ if (!await isPublicHttpUrl(current)) return void 0;
1634
+ const response = await fetch(current, {
1635
+ headers: { accept: "text/html,application/xhtml+xml" },
1636
+ redirect: "manual",
1637
+ signal: AbortSignal.timeout(1500)
1638
+ });
1639
+ if (!isRedirect(response.status)) return response;
1640
+ await response.body?.cancel();
1641
+ const location = response.headers.get("location");
1642
+ if (!location) return void 0;
1643
+ current = new URL(location, current).toString();
1644
+ }
1743
1645
  }
1744
1646
  async function isPublicHttpUrl(value) {
1745
- let url;
1746
- try {
1747
- url = new URL(value);
1748
- } catch {
1749
- return false;
1750
- }
1751
- if (url.protocol !== "http:" && url.protocol !== "https:") return false;
1752
- if (isLocalHostname(url.hostname)) return false;
1753
- if (isPrivateIpAddress(url.hostname)) return false;
1754
- const addresses = await lookup(url.hostname, { all: true, verbatim: true });
1755
- return addresses.length > 0 && addresses.every(({ address }) => !isPrivateIpAddress(address));
1647
+ let url;
1648
+ try {
1649
+ url = new URL(value);
1650
+ } catch {
1651
+ return false;
1652
+ }
1653
+ if (url.protocol !== "http:" && url.protocol !== "https:") return false;
1654
+ if (isLocalHostname(url.hostname)) return false;
1655
+ if (isPrivateIpAddress(url.hostname)) return false;
1656
+ const addresses = await lookup(url.hostname, {
1657
+ all: true,
1658
+ verbatim: true
1659
+ });
1660
+ return addresses.length > 0 && addresses.every(({ address }) => !isPrivateIpAddress(address));
1756
1661
  }
1757
1662
  function isLocalHostname(hostname) {
1758
- const normalized = hostname.toLowerCase().replace(/\.$/, "");
1759
- return normalized === "localhost" || normalized.endsWith(".localhost");
1663
+ const normalized = hostname.toLowerCase().replace(/\.$/, "");
1664
+ return normalized === "localhost" || normalized.endsWith(".localhost");
1760
1665
  }
1761
1666
  function isPrivateIpAddress(value) {
1762
- const normalized = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
1763
- const version = isIP(normalized);
1764
- if (version === 4) return isPrivateIpv4(normalized);
1765
- if (version === 6) return isPrivateIpv6(normalized);
1766
- return false;
1667
+ const normalized = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
1668
+ const version = isIP(normalized);
1669
+ if (version === 4) return isPrivateIpv4(normalized);
1670
+ if (version === 6) return isPrivateIpv6(normalized);
1671
+ return false;
1767
1672
  }
1768
1673
  function isPrivateIpv4(address) {
1769
- const parts = address.split(".").map((part) => Number.parseInt(part, 10));
1770
- if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) return true;
1771
- const [a, b] = parts;
1772
- return a === 0 || a === 10 || a === 127 || a === 100 && b >= 64 && b <= 127 || a === 169 && b === 254 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a === 198 && (b === 18 || b === 19) || a >= 224;
1674
+ const parts = address.split(".").map((part) => Number.parseInt(part, 10));
1675
+ if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) return true;
1676
+ const [a, b] = parts;
1677
+ return a === 0 || a === 10 || a === 127 || a === 100 && b >= 64 && b <= 127 || a === 169 && b === 254 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a === 198 && (b === 18 || b === 19) || a >= 224;
1773
1678
  }
1774
1679
  function isPrivateIpv6(address) {
1775
- const normalized = address.toLowerCase();
1776
- if (normalized === "::" || normalized === "::1" || normalized.startsWith("fe80:") || normalized.startsWith("fec0:") || normalized.startsWith("fc") || normalized.startsWith("fd") || normalized.startsWith("ff")) {
1777
- return true;
1778
- }
1779
- if (normalized.startsWith("::ffff:")) {
1780
- const tail = normalized.slice("::ffff:".length);
1781
- const dotted = tail.match(/^(?:0:)?(\d+\.\d+\.\d+\.\d+)$/);
1782
- if (dotted) return isPrivateIpv4(dotted[1]);
1783
- const hex = tail.match(/^(?:0:)?([0-9a-f]{1,4}):([0-9a-f]{1,4})$/);
1784
- if (hex) {
1785
- const hi = Number.parseInt(hex[1], 16);
1786
- const lo = Number.parseInt(hex[2], 16);
1787
- const value32 = hi << 16 | lo;
1788
- const ipv4 = [value32 >>> 24 & 255, value32 >>> 16 & 255, value32 >>> 8 & 255, value32 & 255].join(".");
1789
- return isPrivateIpv4(ipv4);
1790
- }
1791
- }
1792
- return false;
1680
+ const normalized = address.toLowerCase();
1681
+ if (normalized === "::" || normalized === "::1" || normalized.startsWith("fe80:") || normalized.startsWith("fec0:") || normalized.startsWith("fc") || normalized.startsWith("fd") || normalized.startsWith("ff")) return true;
1682
+ if (normalized.startsWith("::ffff:")) {
1683
+ const tail = normalized.slice(7);
1684
+ const dotted = tail.match(/^(?:0:)?(\d+\.\d+\.\d+\.\d+)$/);
1685
+ if (dotted) return isPrivateIpv4(dotted[1]);
1686
+ const hex = tail.match(/^(?:0:)?([0-9a-f]{1,4}):([0-9a-f]{1,4})$/);
1687
+ if (hex) {
1688
+ const hi = Number.parseInt(hex[1], 16);
1689
+ const lo = Number.parseInt(hex[2], 16);
1690
+ const value32 = hi << 16 | lo;
1691
+ return isPrivateIpv4([
1692
+ value32 >>> 24 & 255,
1693
+ value32 >>> 16 & 255,
1694
+ value32 >>> 8 & 255,
1695
+ value32 & 255
1696
+ ].join("."));
1697
+ }
1698
+ }
1699
+ return false;
1793
1700
  }
1794
1701
  function isRedirect(status) {
1795
- return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
1702
+ return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
1796
1703
  }
1797
1704
  function isTooLarge(response) {
1798
- const contentLength = response.headers.get("content-length");
1799
- return contentLength != null && Number(contentLength) > MAX_OGP_BYTES;
1705
+ const contentLength = response.headers.get("content-length");
1706
+ return contentLength != null && Number(contentLength) > MAX_OGP_BYTES;
1800
1707
  }
1801
1708
  async function readTextWithLimit(response, maxBytes) {
1802
- const reader = response.body?.getReader();
1803
- if (!reader) {
1804
- const text = await response.text();
1805
- return new TextEncoder().encode(text).byteLength > maxBytes ? void 0 : text;
1806
- }
1807
- const chunks = [];
1808
- let bytes = 0;
1809
- while (true) {
1810
- const { done, value } = await reader.read();
1811
- if (done) break;
1812
- bytes += value.byteLength;
1813
- if (bytes > maxBytes) {
1814
- await reader.cancel();
1815
- return void 0;
1816
- }
1817
- chunks.push(value);
1818
- }
1819
- const buffer = new Uint8Array(bytes);
1820
- let offset = 0;
1821
- for (const chunk of chunks) {
1822
- buffer.set(chunk, offset);
1823
- offset += chunk.byteLength;
1824
- }
1825
- return new TextDecoder().decode(buffer);
1709
+ const reader = response.body?.getReader();
1710
+ if (!reader) {
1711
+ const text = await response.text();
1712
+ return new TextEncoder().encode(text).byteLength > maxBytes ? void 0 : text;
1713
+ }
1714
+ const chunks = [];
1715
+ let bytes = 0;
1716
+ while (true) {
1717
+ const { done, value } = await reader.read();
1718
+ if (done) break;
1719
+ bytes += value.byteLength;
1720
+ if (bytes > maxBytes) {
1721
+ await reader.cancel();
1722
+ return;
1723
+ }
1724
+ chunks.push(value);
1725
+ }
1726
+ const buffer = new Uint8Array(bytes);
1727
+ let offset = 0;
1728
+ for (const chunk of chunks) {
1729
+ buffer.set(chunk, offset);
1730
+ offset += chunk.byteLength;
1731
+ }
1732
+ return new TextDecoder().decode(buffer);
1826
1733
  }
1827
1734
  function parseOgpHtml(html, pageUrl) {
1828
- const meta = collectHtmlMeta(html);
1829
- const title = firstValue(meta, ["og:title", "twitter:title"]) ?? htmlTitle(html);
1830
- const description = firstValue(meta, ["og:description", "twitter:description", "description"]);
1831
- const image = absoluteUrl(firstValue(meta, ["og:image", "og:image:url", "twitter:image"]), pageUrl);
1832
- const siteName = firstValue(meta, ["og:site_name", "application-name"]);
1833
- const result2 = { title, description, image, siteName };
1834
- return Object.values(result2).some(Boolean) ? result2 : void 0;
1735
+ const meta = collectHtmlMeta(html);
1736
+ const result = {
1737
+ title: firstValue(meta, ["og:title", "twitter:title"]) ?? htmlTitle(html),
1738
+ description: firstValue(meta, [
1739
+ "og:description",
1740
+ "twitter:description",
1741
+ "description"
1742
+ ]),
1743
+ image: absoluteUrl(firstValue(meta, [
1744
+ "og:image",
1745
+ "og:image:url",
1746
+ "twitter:image"
1747
+ ]), pageUrl),
1748
+ siteName: firstValue(meta, ["og:site_name", "application-name"])
1749
+ };
1750
+ return Object.values(result).some(Boolean) ? result : void 0;
1835
1751
  }
1836
1752
  function collectHtmlMeta(html) {
1837
- const meta = /* @__PURE__ */ new Map();
1838
- const tagPattern = /<meta\b[^>]*>/gi;
1839
- for (const match of html.matchAll(tagPattern)) {
1840
- const attributes = parseHtmlAttributes(match[0]);
1841
- const key = attributes.property ?? attributes.name;
1842
- const content = attributes.content;
1843
- if (key && content && !meta.has(key.toLowerCase())) {
1844
- meta.set(key.toLowerCase(), decodeHtmlEntities(content.trim()));
1845
- }
1846
- }
1847
- return meta;
1753
+ const meta = /* @__PURE__ */ new Map();
1754
+ for (const match of html.matchAll(/<meta\b[^>]*>/gi)) {
1755
+ const attributes = parseHtmlAttributes(match[0]);
1756
+ const key = attributes.property ?? attributes.name;
1757
+ const content = attributes.content;
1758
+ if (key && content && !meta.has(key.toLowerCase())) meta.set(key.toLowerCase(), decodeHtmlEntities(content.trim()));
1759
+ }
1760
+ return meta;
1848
1761
  }
1849
1762
  function parseHtmlAttributes(tag) {
1850
- const attributes = {};
1851
- const attributePattern = /([^\s"'<>/=]+)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+))/g;
1852
- for (const match of tag.matchAll(attributePattern)) {
1853
- attributes[match[1].toLowerCase()] = match[2] ?? match[3] ?? match[4] ?? "";
1854
- }
1855
- return attributes;
1763
+ const attributes = {};
1764
+ for (const match of tag.matchAll(/([^\s"'<>/=]+)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+))/g)) attributes[match[1].toLowerCase()] = match[2] ?? match[3] ?? match[4] ?? "";
1765
+ return attributes;
1856
1766
  }
1857
1767
  function firstValue(meta, keys) {
1858
- for (const key of keys) {
1859
- const value = meta.get(key);
1860
- if (value) return value;
1861
- }
1862
- return void 0;
1768
+ for (const key of keys) {
1769
+ const value = meta.get(key);
1770
+ if (value) return value;
1771
+ }
1863
1772
  }
1864
1773
  function htmlTitle(html) {
1865
- const match = html.match(/<title\b[^>]*>([\s\S]*?)<\/title>/i);
1866
- return match ? decodeHtmlEntities(match[1].replace(/\s+/g, " ").trim()) : void 0;
1774
+ const match = html.match(/<title\b[^>]*>([\s\S]*?)<\/title>/i);
1775
+ return match ? decodeHtmlEntities(match[1].replace(/\s+/g, " ").trim()) : void 0;
1867
1776
  }
1868
1777
  function absoluteUrl(value, base) {
1869
- if (!value) return void 0;
1870
- try {
1871
- return new URL(value, base).toString();
1872
- } catch {
1873
- return value;
1874
- }
1778
+ if (!value) return void 0;
1779
+ try {
1780
+ return new URL(value, base).toString();
1781
+ } catch {
1782
+ return value;
1783
+ }
1875
1784
  }
1876
1785
  function decodeHtmlEntities(value) {
1877
- return value.replaceAll("&amp;", "&").replaceAll("&quot;", '"').replaceAll("&#39;", "'").replaceAll("&lt;", "<").replaceAll("&gt;", ">");
1878
- }
1879
- function isHttpUrl2(value) {
1880
- try {
1881
- const url = new URL(value);
1882
- return url.protocol === "http:" || url.protocol === "https:";
1883
- } catch {
1884
- return false;
1885
- }
1786
+ return value.replaceAll("&quot;", "\"").replaceAll("&#39;", "'").replaceAll("&lt;", "<").replaceAll("&gt;", ">").replaceAll("&amp;", "&");
1886
1787
  }
1887
-
1888
- // src/node/compile-decks.ts
1788
+ function isHttpUrl(value) {
1789
+ try {
1790
+ const url = new URL(value);
1791
+ return url.protocol === "http:" || url.protocol === "https:";
1792
+ } catch {
1793
+ return false;
1794
+ }
1795
+ }
1796
+ //#endregion
1797
+ //#region src/node/compile-decks.ts
1889
1798
  async function compileDecks(input) {
1890
- const out = normalizeRelativePath(input.out, "Output directory");
1891
- const root = normalizeDeckRoot(input.root);
1892
- const paths = await listFiles(input.cwd, join3(input.cwd, root));
1893
- const resolved = resolveDeckFiles(paths, root);
1894
- const componentModulePaths = Object.fromEntries(
1895
- resolved.flatMap((deck) => {
1896
- const path = deck.kind === "directory" ? findComponentModule(paths, root, deck.slug) : void 0;
1897
- return path ? [[deck.slug, path]] : [];
1898
- })
1899
- );
1900
- const clientEntryPaths = resolved.flatMap((deck) => {
1901
- const path = deck.kind === "directory" ? findClientEntryModule(paths, root, deck.slug) : void 0;
1902
- return path ? [{ slug: deck.slug, sourcePath: path }] : [];
1903
- });
1904
- const themeStyles = await discoverDeckThemeStyles({ cwd: input.cwd, root, paths, decks: resolved });
1905
- const clientComponentIds = await discoverClientComponentIds({
1906
- cwd: input.cwd,
1907
- clientEntries: clientEntryPaths
1908
- });
1909
- const ogpCache = await createOgpCacheResolver({
1910
- cwd: input.cwd,
1911
- cacheFile: input.ogpCacheFile,
1912
- refresh: input.refreshOgp,
1913
- resolveOgp: input.resolveOgp ?? resolveOgpMetadata
1914
- });
1915
- const generated = await compileMdxModuleDecks({
1916
- root,
1917
- outDir: out,
1918
- mountPath: input.mountPath,
1919
- decks: resolved,
1920
- componentModulePaths,
1921
- clientComponentIds,
1922
- themeStyles,
1923
- resolveOgp: ogpCache.resolveOgp,
1924
- readText: (path) => readFile3(join3(input.cwd, path), "utf8"),
1925
- readBinary: (path) => readFile3(join3(input.cwd, path))
1926
- });
1927
- for (const deck of generated.decks) {
1928
- for (const slide of deck.slideModules) {
1929
- await writeTextFile(join3(input.cwd, slide.path), slide.code);
1930
- }
1931
- }
1932
- await writeTextFile(join3(input.cwd, out, "decks.ts"), generated.routerModule);
1933
- await writeTextFile(
1934
- join3(input.cwd, out, "client-entry.ts"),
1935
- await emitClientEntryModule({ cwd: input.cwd, clientEntries: clientEntryPaths, clientComponentIds })
1936
- );
1937
- await ogpCache.write();
1938
- return { decks: generated.decks.map((deck) => deck.deck) };
1799
+ const out = normalizeRelativePath(input.out, "Output directory");
1800
+ const root = normalizeDeckRoot(input.root);
1801
+ const paths = await listFiles(input.cwd, join(input.cwd, root));
1802
+ const resolved = resolveDeckFiles(paths, root);
1803
+ const componentModulePaths = Object.fromEntries(resolved.flatMap((deck) => {
1804
+ const path = deck.kind === "directory" ? findComponentModule(paths, root, deck.slug) : void 0;
1805
+ return path ? [[deck.slug, path]] : [];
1806
+ }));
1807
+ const clientEntryPaths = resolved.flatMap((deck) => {
1808
+ const path = deck.kind === "directory" ? findClientEntryModule(paths, root, deck.slug) : void 0;
1809
+ return path ? [{
1810
+ slug: deck.slug,
1811
+ sourcePath: path
1812
+ }] : [];
1813
+ });
1814
+ const themeStyles = await discoverDeckThemeStyles({
1815
+ cwd: input.cwd,
1816
+ root,
1817
+ paths,
1818
+ decks: resolved
1819
+ });
1820
+ const clientComponentIds = await discoverClientComponentIds({
1821
+ cwd: input.cwd,
1822
+ clientEntries: clientEntryPaths
1823
+ });
1824
+ const ogpCache = await createOgpCacheResolver({
1825
+ cwd: input.cwd,
1826
+ cacheFile: input.ogpCacheFile,
1827
+ refresh: input.refreshOgp,
1828
+ resolveOgp: input.resolveOgp ?? resolveOgpMetadata
1829
+ });
1830
+ const generated = await compileMdxModuleDecks({
1831
+ root,
1832
+ outDir: out,
1833
+ mountPath: input.mountPath,
1834
+ decks: resolved,
1835
+ componentModulePaths,
1836
+ clientComponentIds,
1837
+ themeStyles,
1838
+ resolveOgp: ogpCache.resolveOgp,
1839
+ readText: (path) => readFile(join(input.cwd, path), "utf8"),
1840
+ readBinary: (path) => readFile(join(input.cwd, path))
1841
+ });
1842
+ for (const deck of generated.decks) for (const slide of deck.slideModules) await writeTextFile(join(input.cwd, slide.path), slide.code);
1843
+ await writeTextFile(join(input.cwd, out, "decks.ts"), generated.routerModule);
1844
+ await writeTextFile(join(input.cwd, out, "client-entry.ts"), await emitClientEntryModule({
1845
+ cwd: input.cwd,
1846
+ clientEntries: clientEntryPaths,
1847
+ clientComponentIds
1848
+ }));
1849
+ await ogpCache.write();
1850
+ return { decks: generated.decks.map((deck) => deck.deck) };
1939
1851
  }
1940
1852
  async function createOgpCacheResolver(input) {
1941
- if (!input.cacheFile) {
1942
- return {
1943
- resolveOgp: input.resolveOgp,
1944
- write: async () => void 0
1945
- };
1946
- }
1947
- const cachePath = join3(input.cwd, normalizeRelativePath(input.cacheFile, "OGP cache file"));
1948
- const cache = await readOgpCache(cachePath);
1949
- let dirty = false;
1950
- return {
1951
- async resolveOgp(url) {
1952
- if (!input.refresh && cache[url]) return cache[url];
1953
- if (!input.refresh) return void 0;
1954
- const metadata = await input.resolveOgp(url);
1955
- if (metadata) {
1956
- cache[url] = metadata;
1957
- dirty = true;
1958
- }
1959
- return metadata;
1960
- },
1961
- async write() {
1962
- if (!dirty) return;
1963
- await writeTextFile(cachePath, `${JSON.stringify(cache, null, 2)}
1964
- `);
1965
- }
1966
- };
1853
+ if (!input.cacheFile) return {
1854
+ resolveOgp: input.resolveOgp,
1855
+ write: async () => void 0
1856
+ };
1857
+ const cachePath = join(input.cwd, normalizeRelativePath(input.cacheFile, "OGP cache file"));
1858
+ const cache = await readOgpCache(cachePath);
1859
+ let dirty = false;
1860
+ return {
1861
+ async resolveOgp(url) {
1862
+ if (!input.refresh && cache[url]) return cache[url];
1863
+ if (!input.refresh) return void 0;
1864
+ const metadata = await input.resolveOgp(url);
1865
+ if (metadata) {
1866
+ cache[url] = metadata;
1867
+ dirty = true;
1868
+ }
1869
+ return metadata;
1870
+ },
1871
+ async write() {
1872
+ if (!dirty) return;
1873
+ await writeTextFile(cachePath, `${JSON.stringify(cache, null, 2)}\n`);
1874
+ }
1875
+ };
1967
1876
  }
1968
1877
  async function readOgpCache(path) {
1969
- let raw;
1970
- try {
1971
- raw = await readFile3(path, "utf8");
1972
- } catch (error) {
1973
- if (isNodeError(error) && error.code === "ENOENT") return {};
1974
- throw error;
1975
- }
1976
- const parsed = JSON.parse(raw);
1977
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1978
- throw new Error("OGP cache file must contain a JSON object");
1979
- }
1980
- const cache = {};
1981
- for (const [url, value] of Object.entries(parsed)) {
1982
- if (!value || typeof value !== "object" || Array.isArray(value)) continue;
1983
- const metadata = value;
1984
- cache[url] = {
1985
- ...typeof metadata.title === "string" ? { title: metadata.title } : {},
1986
- ...typeof metadata.description === "string" ? { description: metadata.description } : {},
1987
- ...typeof metadata.image === "string" ? { image: metadata.image } : {},
1988
- ...typeof metadata.siteName === "string" ? { siteName: metadata.siteName } : {}
1989
- };
1990
- }
1991
- return cache;
1878
+ let raw;
1879
+ try {
1880
+ raw = await readFile(path, "utf8");
1881
+ } catch (error) {
1882
+ if (isNodeError(error) && error.code === "ENOENT") return {};
1883
+ throw error;
1884
+ }
1885
+ const parsed = JSON.parse(raw);
1886
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("OGP cache file must contain a JSON object");
1887
+ const cache = {};
1888
+ for (const [url, value] of Object.entries(parsed)) {
1889
+ if (!value || typeof value !== "object" || Array.isArray(value)) continue;
1890
+ const metadata = value;
1891
+ cache[url] = {
1892
+ ...typeof metadata.title === "string" ? { title: metadata.title } : {},
1893
+ ...typeof metadata.description === "string" ? { description: metadata.description } : {},
1894
+ ...typeof metadata.image === "string" ? { image: metadata.image } : {},
1895
+ ...typeof metadata.siteName === "string" ? { siteName: metadata.siteName } : {}
1896
+ };
1897
+ }
1898
+ return cache;
1992
1899
  }
1993
1900
  function isNodeError(error) {
1994
- return error instanceof Error && "code" in error;
1901
+ return error instanceof Error && "code" in error;
1995
1902
  }
1996
1903
  async function writeTextFile(path, contents) {
1997
- await mkdir(dirname2(path), { recursive: true });
1998
- await writeFile(path, contents, "utf8");
1904
+ await mkdir(dirname$1(path), { recursive: true });
1905
+ await writeFile(path, contents, "utf8");
1999
1906
  }
2000
1907
  function findComponentModule(paths, root, slug) {
2001
- const base = `${normalizePath4(root).replace(/\/$/, "")}/${slug}/components/index`;
2002
- return paths.find((path) => {
2003
- const normalized = normalizePath4(path);
2004
- return normalized === `${base}.tsx` || normalized === `${base}.ts` || normalized === `${base}.jsx` || normalized === `${base}.js`;
2005
- });
1908
+ const base = `${normalizePath$1(root).replace(/\/$/, "")}/${slug}/components/index`;
1909
+ return paths.find((path) => {
1910
+ const normalized = normalizePath$1(path);
1911
+ return normalized === `${base}.tsx` || normalized === `${base}.ts` || normalized === `${base}.jsx` || normalized === `${base}.js`;
1912
+ });
2006
1913
  }
2007
1914
  function findClientEntryModule(paths, root, slug) {
2008
- const base = `${normalizePath4(root).replace(/\/$/, "")}/${slug}/components/client/index`;
2009
- return paths.find((path) => {
2010
- const normalized = normalizePath4(path);
2011
- return normalized === `${base}.tsx` || normalized === `${base}.ts` || normalized === `${base}.jsx` || normalized === `${base}.js`;
2012
- });
1915
+ const base = `${normalizePath$1(root).replace(/\/$/, "")}/${slug}/components/client/index`;
1916
+ return paths.find((path) => {
1917
+ const normalized = normalizePath$1(path);
1918
+ return normalized === `${base}.tsx` || normalized === `${base}.ts` || normalized === `${base}.jsx` || normalized === `${base}.js`;
1919
+ });
2013
1920
  }
2014
1921
  async function discoverDeckThemeStyles(input) {
2015
- const result2 = {};
2016
- for (const deck of input.decks) {
2017
- if (deck.kind !== "directory") continue;
2018
- const sourcePath = findDeckThemeStyle(input.paths, input.root, deck.slug);
2019
- if (!sourcePath) continue;
2020
- result2[deck.slug] = {
2021
- sourcePath,
2022
- style: await readFile3(join3(input.cwd, sourcePath), "utf8")
2023
- };
2024
- }
2025
- return result2;
1922
+ const result = {};
1923
+ for (const deck of input.decks) {
1924
+ if (deck.kind !== "directory") continue;
1925
+ const sourcePath = findDeckThemeStyle(input.paths, input.root, deck.slug);
1926
+ if (!sourcePath) continue;
1927
+ result[deck.slug] = {
1928
+ sourcePath,
1929
+ style: await readFile(join(input.cwd, sourcePath), "utf8")
1930
+ };
1931
+ }
1932
+ return result;
2026
1933
  }
2027
1934
  function findDeckThemeStyle(paths, root, slug) {
2028
- const base = `${normalizePath4(root).replace(/\/$/, "")}/${slug}`;
2029
- const themePath = `${base}/theme.css`;
2030
- const stylesEntryPath = `${base}/styles/index.css`;
2031
- const hasTheme = paths.includes(themePath);
2032
- const hasStylesEntry = paths.includes(stylesEntryPath);
2033
- if (hasTheme && hasStylesEntry) {
2034
- throw new Error(`Deck ${slug} has both ${themePath} and ${stylesEntryPath}. Use only one theme CSS entry.`);
2035
- }
2036
- return hasTheme ? themePath : hasStylesEntry ? stylesEntryPath : void 0;
1935
+ const base = `${normalizePath$1(root).replace(/\/$/, "")}/${slug}`;
1936
+ const themePath = `${base}/theme.css`;
1937
+ const stylesEntryPath = `${base}/styles/index.css`;
1938
+ const hasTheme = paths.includes(themePath);
1939
+ const hasStylesEntry = paths.includes(stylesEntryPath);
1940
+ if (hasTheme && hasStylesEntry) throw new Error(`Deck ${slug} has both ${themePath} and ${stylesEntryPath}. Use only one theme CSS entry.`);
1941
+ return hasTheme ? themePath : hasStylesEntry ? stylesEntryPath : void 0;
2037
1942
  }
2038
-
2039
- // src/node/config.ts
2040
- import { existsSync as existsSync3 } from "fs";
2041
- import { resolve } from "path";
2042
- import { fileURLToPath as fileURLToPath2 } from "url";
2043
- import { build } from "esbuild";
2044
- var DEFAULT_DECKS_CONFIG_FILE = "hono-decks.config.ts";
2045
1943
  async function loadDecksConfig(input) {
2046
- const path = resolve(input.cwd, input.configFile ?? DEFAULT_DECKS_CONFIG_FILE);
2047
- if (!existsSync3(path)) {
2048
- throw new Error(`Config file not found: ${input.configFile ?? DEFAULT_DECKS_CONFIG_FILE}. Run \`hono-decks init\` first.`);
2049
- }
2050
- const result2 = await build({
2051
- absWorkingDir: input.cwd,
2052
- entryPoints: [path],
2053
- bundle: true,
2054
- format: "esm",
2055
- platform: "node",
2056
- target: "node20",
2057
- write: false,
2058
- logLevel: "silent",
2059
- alias: {
2060
- "hono-decks": resolveRuntimeEntry()
2061
- }
2062
- });
2063
- const source = result2.outputFiles[0]?.text;
2064
- if (!source) throw new Error(`Could not load config file: ${path}`);
2065
- const module = await import(`data:text/javascript;charset=utf-8,${encodeURIComponent(source)}#${Date.now()}`);
2066
- const config = module.default;
2067
- if (!config || typeof config !== "object") throw new Error(`Config must have a default export: ${path}`);
2068
- if (typeof config.mountPath !== "string") throw new Error(`Config mountPath must be a string: ${path}`);
2069
- return {
2070
- path,
2071
- config,
2072
- root: config.build?.root ?? "decks",
2073
- outDir: config.build?.outDir ?? "src/generated",
2074
- ogpCacheFile: config.build?.ogpCacheFile
2075
- };
1944
+ const path = resolve(input.cwd, input.configFile ?? "hono-decks.config.ts");
1945
+ if (!existsSync(path)) throw new Error(`Config file not found: ${input.configFile ?? "hono-decks.config.ts"}. Run \`hono-decks init\` first.`);
1946
+ const source = (await build({
1947
+ absWorkingDir: input.cwd,
1948
+ entryPoints: [path],
1949
+ bundle: true,
1950
+ format: "esm",
1951
+ platform: "node",
1952
+ target: "node20",
1953
+ write: false,
1954
+ logLevel: "silent",
1955
+ alias: { "hono-decks": resolveRuntimeEntry() }
1956
+ })).outputFiles[0]?.text;
1957
+ if (!source) throw new Error(`Could not load config file: ${path}`);
1958
+ const config = (await import(`data:text/javascript;charset=utf-8,${encodeURIComponent(source)}#${Date.now()}`)).default;
1959
+ if (!config || typeof config !== "object") throw new Error(`Config must have a default export: ${path}`);
1960
+ if (typeof config.mountPath !== "string") throw new Error(`Config mountPath must be a string: ${path}`);
1961
+ return {
1962
+ path,
1963
+ config,
1964
+ root: config.build?.root ?? "decks",
1965
+ outDir: config.build?.outDir ?? "src/generated",
1966
+ ogpCacheFile: config.build?.ogpCacheFile
1967
+ };
2076
1968
  }
2077
1969
  function resolveRuntimeEntry() {
2078
- const built = fileURLToPath2(new URL("./mod.js", import.meta.url));
2079
- if (existsSync3(built)) return built;
2080
- return fileURLToPath2(new URL("../mod.ts", import.meta.url));
1970
+ const built = fileURLToPath(new URL("./mod.js", import.meta.url));
1971
+ if (existsSync(built)) return built;
1972
+ return fileURLToPath(new URL("../mod.ts", import.meta.url));
2081
1973
  }
2082
-
2083
- // src/node/cli.ts
2084
- var USAGE = `Usage:
1974
+ //#endregion
1975
+ //#region src/node/cli.ts
1976
+ const USAGE = `Usage:
2085
1977
  hono-decks init [--config hono-decks.config.ts] [--out src/decks.ts]
2086
1978
  hono-decks compile [--config hono-decks.config.ts] [--watch] [--refresh-ogp]
2087
1979
 
@@ -2097,179 +1989,207 @@ Options:
2097
1989
  --refresh-ogp Refresh OGP cache entries from the network.
2098
1990
  -h, --help Show this help.`;
2099
1991
  async function runHonoDecksCli(input) {
2100
- const stdout = input.stdout ?? (() => void 0);
2101
- const stderr = input.stderr ?? (() => void 0);
2102
- const [command, ...args] = input.argv;
2103
- if (!command || command === "--help" || command === "-h") {
2104
- stdout(USAGE);
2105
- return { exitCode: 0 };
2106
- }
2107
- if (command === "init") return runInitCommand(input, args, stdout, stderr);
2108
- if (command !== "compile" && command !== "build") {
2109
- stderr(`Unknown command: ${command}`);
2110
- stderr(USAGE);
2111
- return { exitCode: 1 };
2112
- }
2113
- const parsed = parseCompileArgs(args);
2114
- if (parsed.help) {
2115
- stdout(USAGE);
2116
- return { exitCode: 0 };
2117
- }
2118
- if (parsed.error) {
2119
- stderr(parsed.error);
2120
- stderr(USAGE);
2121
- return { exitCode: 1 };
2122
- }
2123
- const compile2 = async () => {
2124
- const loaded = await loadDecksConfig({ cwd: input.cwd, configFile: parsed.options.configFile });
2125
- const manifest = await compileDecks({
2126
- cwd: input.cwd,
2127
- root: loaded.root,
2128
- out: loaded.outDir,
2129
- mountPath: loaded.config.mountPath,
2130
- ogpCacheFile: loaded.ogpCacheFile,
2131
- refreshOgp: parsed.options.refreshOgp
2132
- });
2133
- stdout(`Compiled ${manifest.decks.length} decks to ${loaded.outDir}`);
2134
- return { root: loaded.root, configPath: loaded.path };
2135
- };
2136
- try {
2137
- const initial = await compile2();
2138
- if (!parsed.options.watch) return { exitCode: 0 };
2139
- stdout(`Watching ${initial.root} and ${relative3(input.cwd, initial.configPath)}`);
2140
- await watchAndCompile({ ...input, root: initial.root, configPath: initial.configPath, compile: compile2, stderr });
2141
- return { exitCode: 0 };
2142
- } catch (error) {
2143
- stderr(error instanceof Error ? error.message : String(error));
2144
- return { exitCode: 1 };
2145
- }
1992
+ const stdout = input.stdout ?? (() => void 0);
1993
+ const stderr = input.stderr ?? (() => void 0);
1994
+ const [command, ...args] = input.argv;
1995
+ if (!command || command === "--help" || command === "-h") {
1996
+ stdout(USAGE);
1997
+ return { exitCode: 0 };
1998
+ }
1999
+ if (command === "init") return runInitCommand(input, args, stdout, stderr);
2000
+ if (command !== "compile" && command !== "build") {
2001
+ stderr(`Unknown command: ${command}`);
2002
+ stderr(USAGE);
2003
+ return { exitCode: 1 };
2004
+ }
2005
+ const parsed = parseCompileArgs(args);
2006
+ if (parsed.help) {
2007
+ stdout(USAGE);
2008
+ return { exitCode: 0 };
2009
+ }
2010
+ if (parsed.error) {
2011
+ stderr(parsed.error);
2012
+ stderr(USAGE);
2013
+ return { exitCode: 1 };
2014
+ }
2015
+ const compile = async () => {
2016
+ const loaded = await loadDecksConfig({
2017
+ cwd: input.cwd,
2018
+ configFile: parsed.options.configFile
2019
+ });
2020
+ const manifest = await compileDecks({
2021
+ cwd: input.cwd,
2022
+ root: loaded.root,
2023
+ out: loaded.outDir,
2024
+ mountPath: loaded.config.mountPath,
2025
+ ogpCacheFile: loaded.ogpCacheFile,
2026
+ refreshOgp: parsed.options.refreshOgp
2027
+ });
2028
+ stdout(`Compiled ${manifest.decks.length} decks to ${loaded.outDir}`);
2029
+ return {
2030
+ root: loaded.root,
2031
+ configPath: loaded.path
2032
+ };
2033
+ };
2034
+ try {
2035
+ const initial = await compile();
2036
+ if (!parsed.options.watch) return { exitCode: 0 };
2037
+ stdout(`Watching ${initial.root} and ${relative(input.cwd, initial.configPath)}`);
2038
+ await watchAndCompile({
2039
+ ...input,
2040
+ root: initial.root,
2041
+ configPath: initial.configPath,
2042
+ compile,
2043
+ stderr
2044
+ });
2045
+ return { exitCode: 0 };
2046
+ } catch (error) {
2047
+ stderr(error instanceof Error ? error.message : String(error));
2048
+ return { exitCode: 1 };
2049
+ }
2146
2050
  }
2147
2051
  async function watchAndCompile(input) {
2148
- const watchFileSystem = input.watchFileSystem ?? watch2;
2149
- let watchedRoot = input.root;
2150
- let deckWatcher;
2151
- let timer;
2152
- let compiling = false;
2153
- let queued = false;
2154
- const run = async () => {
2155
- if (compiling) {
2156
- queued = true;
2157
- return;
2158
- }
2159
- compiling = true;
2160
- try {
2161
- const next = await input.compile();
2162
- if (next.root !== watchedRoot) {
2163
- deckWatcher.close();
2164
- watchedRoot = next.root;
2165
- deckWatcher = watchFileSystem(join4(input.cwd, watchedRoot), { recursive: true }, schedule);
2166
- }
2167
- } catch (error) {
2168
- input.stderr(error instanceof Error ? error.message : String(error));
2169
- } finally {
2170
- compiling = false;
2171
- if (queued) {
2172
- queued = false;
2173
- await run();
2174
- }
2175
- }
2176
- };
2177
- const schedule = () => {
2178
- if (timer) clearTimeout(timer);
2179
- timer = setTimeout(() => void run(), 75);
2180
- };
2181
- deckWatcher = watchFileSystem(join4(input.cwd, watchedRoot), { recursive: true }, schedule);
2182
- const configWatcher = watchFileSystem(input.configPath, { recursive: false }, schedule);
2183
- await new Promise((resolve2) => {
2184
- if (input.signal?.aborted) return resolve2();
2185
- input.signal?.addEventListener("abort", () => resolve2(), { once: true });
2186
- });
2187
- if (timer) clearTimeout(timer);
2188
- deckWatcher.close();
2189
- configWatcher.close();
2052
+ const watchFileSystem = input.watchFileSystem ?? watch;
2053
+ let watchedRoot = input.root;
2054
+ let deckWatcher;
2055
+ let timer;
2056
+ let compiling = false;
2057
+ let queued = false;
2058
+ const run = async () => {
2059
+ if (compiling) {
2060
+ queued = true;
2061
+ return;
2062
+ }
2063
+ compiling = true;
2064
+ try {
2065
+ const next = await input.compile();
2066
+ if (next.root !== watchedRoot) {
2067
+ deckWatcher.close();
2068
+ watchedRoot = next.root;
2069
+ deckWatcher = watchFileSystem(join(input.cwd, watchedRoot), { recursive: true }, schedule);
2070
+ }
2071
+ } catch (error) {
2072
+ input.stderr(error instanceof Error ? error.message : String(error));
2073
+ } finally {
2074
+ compiling = false;
2075
+ if (queued) {
2076
+ queued = false;
2077
+ await run();
2078
+ }
2079
+ }
2080
+ };
2081
+ const schedule = () => {
2082
+ if (timer) clearTimeout(timer);
2083
+ timer = setTimeout(() => void run(), 75);
2084
+ };
2085
+ deckWatcher = watchFileSystem(join(input.cwd, watchedRoot), { recursive: true }, schedule);
2086
+ const configWatcher = watchFileSystem(input.configPath, { recursive: false }, schedule);
2087
+ await new Promise((resolve) => {
2088
+ if (input.signal?.aborted) return resolve();
2089
+ input.signal?.addEventListener("abort", () => resolve(), { once: true });
2090
+ });
2091
+ if (timer) clearTimeout(timer);
2092
+ deckWatcher.close();
2093
+ configWatcher.close();
2190
2094
  }
2191
2095
  async function runInitCommand(input, args, stdout, stderr) {
2192
- const parsed = parseInitArgs(args);
2193
- if (parsed.help) {
2194
- stdout(USAGE);
2195
- return { exitCode: 0 };
2196
- }
2197
- if (parsed.error) {
2198
- stderr(parsed.error);
2199
- stderr(USAGE);
2200
- return { exitCode: 1 };
2201
- }
2202
- const configFile = normalizeOutputFile(parsed.options.configFile ?? DEFAULT_DECKS_CONFIG_FILE);
2203
- const out = normalizeOutputFile(parsed.options.out ?? "src/decks.ts");
2204
- try {
2205
- await writeInitialFiles({
2206
- cwd: input.cwd,
2207
- configFile,
2208
- out,
2209
- generated: parsed.options.generated ?? "./generated/decks"
2210
- });
2211
- stdout(`Initialized ${configFile} and ${out}`);
2212
- return { exitCode: 0 };
2213
- } catch (error) {
2214
- stderr(error instanceof Error ? error.message : String(error));
2215
- return { exitCode: 1 };
2216
- }
2096
+ const parsed = parseInitArgs(args);
2097
+ if (parsed.help) {
2098
+ stdout(USAGE);
2099
+ return { exitCode: 0 };
2100
+ }
2101
+ if (parsed.error) {
2102
+ stderr(parsed.error);
2103
+ stderr(USAGE);
2104
+ return { exitCode: 1 };
2105
+ }
2106
+ const configFile = normalizeOutputFile(parsed.options.configFile ?? "hono-decks.config.ts");
2107
+ const out = normalizeOutputFile(parsed.options.out ?? "src/decks.ts");
2108
+ try {
2109
+ await writeInitialFiles({
2110
+ cwd: input.cwd,
2111
+ configFile,
2112
+ out,
2113
+ generated: parsed.options.generated ?? "./generated/decks"
2114
+ });
2115
+ stdout(`Initialized ${configFile} and ${out}`);
2116
+ return { exitCode: 0 };
2117
+ } catch (error) {
2118
+ stderr(error instanceof Error ? error.message : String(error));
2119
+ return { exitCode: 1 };
2120
+ }
2217
2121
  }
2218
2122
  function parseCompileArgs(args) {
2219
- const options = {};
2220
- for (let index = 0; index < args.length; index += 1) {
2221
- const arg = args[index];
2222
- if (arg === "--help" || arg === "-h") return { options, help: true };
2223
- if (arg === "--watch") {
2224
- options.watch = true;
2225
- continue;
2226
- }
2227
- if (arg === "--refresh-ogp") {
2228
- options.refreshOgp = true;
2229
- continue;
2230
- }
2231
- if (arg === "--config") {
2232
- const value = args[index + 1];
2233
- if (!value || value.startsWith("--")) return { options, error: `Missing value for ${arg}` };
2234
- options.configFile = value;
2235
- index += 1;
2236
- continue;
2237
- }
2238
- return { options, error: `Unknown option: ${arg}` };
2239
- }
2240
- return { options };
2123
+ const options = {};
2124
+ for (let index = 0; index < args.length; index += 1) {
2125
+ const arg = args[index];
2126
+ if (arg === "--help" || arg === "-h") return {
2127
+ options,
2128
+ help: true
2129
+ };
2130
+ if (arg === "--watch") {
2131
+ options.watch = true;
2132
+ continue;
2133
+ }
2134
+ if (arg === "--refresh-ogp") {
2135
+ options.refreshOgp = true;
2136
+ continue;
2137
+ }
2138
+ if (arg === "--config") {
2139
+ const value = args[index + 1];
2140
+ if (!value || value.startsWith("--")) return {
2141
+ options,
2142
+ error: `Missing value for ${arg}`
2143
+ };
2144
+ options.configFile = value;
2145
+ index += 1;
2146
+ continue;
2147
+ }
2148
+ return {
2149
+ options,
2150
+ error: `Unknown option: ${arg}`
2151
+ };
2152
+ }
2153
+ return { options };
2241
2154
  }
2242
2155
  function parseInitArgs(args) {
2243
- const options = {};
2244
- for (let index = 0; index < args.length; index += 1) {
2245
- const arg = args[index];
2246
- if (arg === "--help" || arg === "-h") return { options, help: true };
2247
- if (arg === "--config" || arg === "--out" || arg === "--generated") {
2248
- const value = args[index + 1];
2249
- if (!value || value.startsWith("--")) return { options, error: `Missing value for ${arg}` };
2250
- if (arg === "--config") options.configFile = value;
2251
- if (arg === "--out") options.out = value;
2252
- if (arg === "--generated") options.generated = value;
2253
- index += 1;
2254
- continue;
2255
- }
2256
- return { options, error: `Unknown option: ${arg}` };
2257
- }
2258
- return { options };
2156
+ const options = {};
2157
+ for (let index = 0; index < args.length; index += 1) {
2158
+ const arg = args[index];
2159
+ if (arg === "--help" || arg === "-h") return {
2160
+ options,
2161
+ help: true
2162
+ };
2163
+ if (arg === "--config" || arg === "--out" || arg === "--generated") {
2164
+ const value = args[index + 1];
2165
+ if (!value || value.startsWith("--")) return {
2166
+ options,
2167
+ error: `Missing value for ${arg}`
2168
+ };
2169
+ if (arg === "--config") options.configFile = value;
2170
+ if (arg === "--out") options.out = value;
2171
+ if (arg === "--generated") options.generated = value;
2172
+ index += 1;
2173
+ continue;
2174
+ }
2175
+ return {
2176
+ options,
2177
+ error: `Unknown option: ${arg}`
2178
+ };
2179
+ }
2180
+ return { options };
2259
2181
  }
2260
2182
  async function writeInitialFiles(input) {
2261
- for (const path of [input.configFile, input.out]) {
2262
- if (existsSync4(join4(input.cwd, path))) throw new Error(`Refusing to overwrite existing file: ${path}`);
2263
- }
2264
- const configPath = join4(input.cwd, input.configFile);
2265
- const facadePath = join4(input.cwd, input.out);
2266
- await mkdir2(dirname3(configPath), { recursive: true });
2267
- await mkdir2(dirname3(facadePath), { recursive: true });
2268
- await writeFile2(configPath, emitDecksConfig(), "utf8");
2269
- await writeFile2(facadePath, emitDecksFacade(input.generated, relativeImport(input.out, input.configFile)), "utf8");
2183
+ for (const path of [input.configFile, input.out]) if (existsSync(join(input.cwd, path))) throw new Error(`Refusing to overwrite existing file: ${path}`);
2184
+ const configPath = join(input.cwd, input.configFile);
2185
+ const facadePath = join(input.cwd, input.out);
2186
+ await mkdir(dirname(configPath), { recursive: true });
2187
+ await mkdir(dirname(facadePath), { recursive: true });
2188
+ await writeFile(configPath, emitDecksConfig(), "utf8");
2189
+ await writeFile(facadePath, emitDecksFacade(input.generated, relativeImport(input.out, input.configFile)), "utf8");
2270
2190
  }
2271
2191
  function emitDecksConfig() {
2272
- return `import { defineDecksConfig } from "hono-decks";
2192
+ return `import { defineDecksConfig } from "hono-decks";
2273
2193
 
2274
2194
  export default defineDecksConfig({
2275
2195
  mountPath: "/decks",
@@ -2281,7 +2201,7 @@ export default defineDecksConfig({
2281
2201
  `;
2282
2202
  }
2283
2203
  function emitDecksFacade(generated, configImport) {
2284
- return `// App-owned facade. Files under the generated directory are overwritten.
2204
+ return `// App-owned facade. Files under the generated directory are overwritten.
2285
2205
  import config from ${JSON.stringify(configImport)};
2286
2206
  import { createDecks } from ${JSON.stringify(generated)};
2287
2207
 
@@ -2289,43 +2209,39 @@ export const decks = createDecks(config);
2289
2209
  `;
2290
2210
  }
2291
2211
  function relativeImport(fromFile, toFile) {
2292
- const path = normalizePath5(relative3(dirname3(fromFile), toFile)).replace(/\.(?:[cm]?[jt]sx?)$/, "");
2293
- return path.startsWith(".") ? path : `./${path}`;
2212
+ const path = normalizePath(relative(dirname(fromFile), toFile)).replace(/\.(?:[cm]?[jt]sx?)$/, "");
2213
+ return path.startsWith(".") ? path : `./${path}`;
2294
2214
  }
2295
2215
  function normalizeOutputFile(path) {
2296
- const normalized = normalizePath5(path).replace(/\/$/, "");
2297
- const segments = normalized.split("/");
2298
- if (!normalized || normalized.startsWith("/") || /^[A-Za-z]:\//.test(normalized) || segments.includes("..")) {
2299
- throw new Error("File must be a relative path inside the current working directory");
2300
- }
2301
- return normalized;
2216
+ const normalized = normalizePath(path).replace(/\/$/, "");
2217
+ const segments = normalized.split("/");
2218
+ if (!normalized || normalized.startsWith("/") || /^[A-Za-z]:\//.test(normalized) || segments.includes("..")) throw new Error("File must be a relative path inside the current working directory");
2219
+ return normalized;
2302
2220
  }
2303
- function normalizePath5(path) {
2304
- return path.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
2221
+ function normalizePath(path) {
2222
+ return path.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
2305
2223
  }
2306
- function dirname3(path) {
2307
- const normalized = normalizePath5(path);
2308
- return normalized.includes("/") ? normalized.slice(0, normalized.lastIndexOf("/")) : ".";
2224
+ function dirname(path) {
2225
+ const normalized = normalizePath(path);
2226
+ return normalized.includes("/") ? normalized.slice(0, normalized.lastIndexOf("/")) : ".";
2309
2227
  }
2310
2228
  if (typeof process !== "undefined" && process.argv[1]?.endsWith("/node/cli.ts")) {
2311
- const result2 = await runHonoDecksCli({
2312
- argv: process.argv.slice(2),
2313
- cwd: process.cwd(),
2314
- stdout: (line) => process.stdout.write(`${line}
2315
- `),
2316
- stderr: (line) => process.stderr.write(`${line}
2317
- `)
2318
- });
2319
- process.exitCode = result2.exitCode;
2320
- }
2321
-
2322
- // src/bin.ts
2323
- var result = await runHonoDecksCli({
2324
- argv: process.argv.slice(2),
2325
- cwd: process.cwd(),
2326
- stdout: (line) => process.stdout.write(`${line}
2327
- `),
2328
- stderr: (line) => process.stderr.write(`${line}
2329
- `)
2229
+ const result = await runHonoDecksCli({
2230
+ argv: process.argv.slice(2),
2231
+ cwd: process.cwd(),
2232
+ stdout: (line) => process.stdout.write(`${line}\n`),
2233
+ stderr: (line) => process.stderr.write(`${line}\n`)
2234
+ });
2235
+ process.exitCode = result.exitCode;
2236
+ }
2237
+ //#endregion
2238
+ //#region src/bin.ts
2239
+ const result = await runHonoDecksCli({
2240
+ argv: process.argv.slice(2),
2241
+ cwd: process.cwd(),
2242
+ stdout: (line) => process.stdout.write(`${line}\n`),
2243
+ stderr: (line) => process.stderr.write(`${line}\n`)
2330
2244
  });
2331
2245
  process.exitCode = result.exitCode;
2246
+ //#endregion
2247
+ export {};