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