@sigx/ssg 0.2.1 → 0.2.3

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.
@@ -1,632 +0,0 @@
1
- import { D as parseFrontmatter, E as extractTitleFromContent, O as defineSSGConfig, T as scanPages, _ as generateNavigationModule, b as generateLazyRoutesModule, c as generateHtmlTemplate, d as RESOLVED_VIRTUAL_LAYOUTS_ID, h as RESOLVED_VIRTUAL_NAVIGATION_ID, k as loadConfig, m as discoverLayouts, n as RESOLVED_VIRTUAL_SERVER_ID, o as detectCustomEntries, p as generateLayoutsModule, s as generateClientEntry, t as RESOLVED_VIRTUAL_CLIENT_ID, u as generateServerEntry, v as RESOLVED_VIRTUAL_ROUTES_ID, x as generateRoutesModule } from "./virtual-entries-TuNN2It1.js";
2
- import path from "node:path";
3
- import fsSync from "node:fs";
4
- import { createHighlighter } from "shiki";
5
- import { visit } from "unist-util-visit";
6
- import { toString } from "hast-util-to-string";
7
- //#region src/mdx/shiki.ts
8
- /**
9
- * Shiki syntax highlighting integration
10
- *
11
- * Provides code block highlighting for Markdown/MDX content.
12
- */
13
- /**
14
- * Cached highlighter instance
15
- */
16
- var highlighterPromise = null;
17
- /**
18
- * Default Shiki configuration
19
- */
20
- var DEFAULT_CONFIG = {
21
- light: "github-light",
22
- dark: "github-dark",
23
- langs: [
24
- "javascript",
25
- "typescript",
26
- "jsx",
27
- "tsx",
28
- "json",
29
- "css",
30
- "html",
31
- "markdown",
32
- "bash",
33
- "shell"
34
- ]
35
- };
36
- /**
37
- * Initialize or get the Shiki highlighter
38
- */
39
- async function getHighlighter(config) {
40
- if (!highlighterPromise) {
41
- const mergedConfig = {
42
- ...DEFAULT_CONFIG,
43
- ...config
44
- };
45
- highlighterPromise = createHighlighter({
46
- themes: [mergedConfig.light, mergedConfig.dark],
47
- langs: mergedConfig.langs
48
- });
49
- }
50
- return highlighterPromise;
51
- }
52
- /**
53
- * Highlight code with Shiki
54
- */
55
- async function highlightCode(code, lang, config, meta) {
56
- const highlighter = await getHighlighter(config);
57
- const mergedConfig = {
58
- ...DEFAULT_CONFIG,
59
- ...config
60
- };
61
- const effectiveLang = highlighter.getLoadedLanguages().includes(lang) ? lang : "text";
62
- const codeHtml = highlighter.codeToHtml(code, {
63
- lang: effectiveLang,
64
- themes: {
65
- light: mergedConfig.light,
66
- dark: mergedConfig.dark
67
- }
68
- });
69
- const filename = meta?.filename ?? "";
70
- const isLive = meta?.live ?? false;
71
- const tabs = meta?.tabs;
72
- const hasTabs = tabs && tabs.length > 0;
73
- const filenameHtml = filename ? `<span class="code-window-filename">${escapeHtml(filename)}</span>` : `<span class="code-window-lang">${getLanguageLabel(effectiveLang)}</span>`;
74
- if (hasTabs) {
75
- const base64Code = encodeBase64(code);
76
- const firstTab = tabs[0];
77
- const tabButtonsHtml = tabs.map((tab, i) => {
78
- const label = tab.charAt(0).toUpperCase() + tab.slice(1);
79
- return `<button class="code-window-tab${i === 0 ? " code-window-tab-active" : ""}">${label}</button>`;
80
- }).join("\n ");
81
- return `<div
82
- class="live-preview-island"
83
- data-island="LivePreview"
84
- data-island-strategy="visible"
85
- data-island-props="${escapeHtml(JSON.stringify({
86
- code: base64Code,
87
- highlightedCode: codeHtml,
88
- language: effectiveLang,
89
- filename,
90
- tabs,
91
- live: isLive
92
- }))}"
93
- >
94
- <div class="code-window code-window-live code-window-preview">
95
- <div class="code-window-header">
96
- <div class="code-window-header-left">
97
- <div class="code-window-dots">
98
- <span class="code-window-dot dot-red"></span>
99
- <span class="code-window-dot dot-yellow"></span>
100
- <span class="code-window-dot dot-green"></span>
101
- </div>
102
- ${filenameHtml}
103
- </div>
104
- <div class="code-window-tabs">
105
- ${tabButtonsHtml}
106
- </div>
107
- <button class="code-window-try-live" disabled>⚡ Try Live</button>
108
- </div>
109
- <div class="code-window-preview-pane"${firstTab !== "preview" ? " style=\"display:none;\"" : ""}>
110
- <div class="code-window-preview-loading">
111
- <span class="code-window-spinner"></span>
112
- Loading preview...
113
- </div>
114
- </div>
115
- <div class="code-window-console-pane"${firstTab !== "console" ? " style=\"display:none;\"" : ""}>
116
- <div class="code-window-console-empty">No console output</div>
117
- </div>
118
- <div class="code-window-content"${firstTab !== "code" ? " style=\"display:none;\"" : ""}>
119
- ${codeHtml}
120
- </div>
121
- </div>
122
- </div>`;
123
- }
124
- const tryLiveButton = isLive ? `<button class="code-window-try-live" data-live-code="${escapeHtml(encodeBase64(code))}" data-lang="${effectiveLang}" data-filename="${escapeHtml(filename)}" title="Open in Live Playground">⚡ Try Live</button>` : "";
125
- return `<div class="code-window${isLive ? " code-window-live" : ""}">
126
- <div class="code-window-header">
127
- <div class="code-window-header-left">
128
- <div class="code-window-dots">
129
- <span class="code-window-dot dot-red"></span>
130
- <span class="code-window-dot dot-yellow"></span>
131
- <span class="code-window-dot dot-green"></span>
132
- </div>
133
- ${filenameHtml}
134
- </div>
135
- ${tryLiveButton}
136
- </div>
137
- <div class="code-window-content">
138
- ${codeHtml}
139
- </div>
140
- </div>`;
141
- }
142
- /**
143
- * Encode string to base64 (works in both Node.js and browser)
144
- */
145
- function encodeBase64(str) {
146
- if (typeof Buffer !== "undefined") return Buffer.from(str, "utf-8").toString("base64");
147
- return btoa(unescape(encodeURIComponent(str)));
148
- }
149
- /**
150
- * Get a display label for a language
151
- */
152
- function getLanguageLabel(lang) {
153
- return {
154
- "tsx": "TSX",
155
- "jsx": "JSX",
156
- "ts": "TypeScript",
157
- "typescript": "TypeScript",
158
- "js": "JavaScript",
159
- "javascript": "JavaScript",
160
- "css": "CSS",
161
- "html": "HTML",
162
- "json": "JSON",
163
- "bash": "Terminal",
164
- "shell": "Terminal",
165
- "sh": "Terminal",
166
- "md": "Markdown",
167
- "markdown": "Markdown",
168
- "python": "Python",
169
- "py": "Python",
170
- "rust": "Rust",
171
- "go": "Go",
172
- "text": ""
173
- }[lang.toLowerCase()] ?? lang.toUpperCase();
174
- }
175
- /**
176
- * Escape HTML special characters
177
- */
178
- function escapeHtml(str) {
179
- return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
180
- }
181
- /**
182
- * Create a rehype plugin for Shiki
183
- */
184
- function rehypeShiki(config) {
185
- return async (tree) => {
186
- const { visit } = await import("unist-util-visit");
187
- const { fromHtml } = await import("hast-util-from-html");
188
- const nodesToProcess = [];
189
- visit(tree, "element", (node, index, parent) => {
190
- if (node.tagName === "pre" && node.children?.[0]?.tagName === "code") nodesToProcess.push({
191
- node,
192
- parent,
193
- index: index ?? 0
194
- });
195
- });
196
- await Promise.all(nodesToProcess.map(async ({ node, parent, index }) => {
197
- const codeNode = node.children[0];
198
- const lang = (codeNode.properties?.className?.[0] || "").replace(/^language-/, "") || "text";
199
- const metaString = codeNode.data?.meta || codeNode.properties?.metastring || "";
200
- const filename = extractMeta(metaString, "filename") || extractMeta(metaString, "title") || "";
201
- const isLive = /\blive\b/i.test(metaString);
202
- const tabKeywords = [
203
- "preview",
204
- "code",
205
- "console"
206
- ];
207
- const tabs = [];
208
- const tabPositions = [];
209
- for (const keyword of tabKeywords) {
210
- const regex = new RegExp(`\\b${keyword}\\b`, "i");
211
- const match = metaString.match(regex);
212
- if (match && match.index !== void 0) tabPositions.push({
213
- tab: keyword,
214
- index: match.index
215
- });
216
- }
217
- tabPositions.sort((a, b) => a.index - b.index);
218
- for (const { tab } of tabPositions) tabs.push(tab);
219
- const fragment = fromHtml(await highlightCode(getTextContent(codeNode).trim(), lang, config, {
220
- filename,
221
- live: isLive,
222
- tabs: tabs.length > 0 ? tabs : void 0
223
- }), { fragment: true });
224
- if (parent && typeof index === "number" && fragment.children.length > 0) parent.children[index] = fragment.children[0];
225
- }));
226
- };
227
- }
228
- /**
229
- * Extract a meta value from a meta string
230
- * Supports: filename="value" or filename='value' or filename=value
231
- */
232
- function extractMeta(metaString, key) {
233
- if (!metaString) return null;
234
- const regex = new RegExp(`${key}=["']?([^"'\\s]+)["']?`, "i");
235
- const match = metaString.match(regex);
236
- return match ? match[1] : null;
237
- }
238
- /**
239
- * Extract text content from an AST node
240
- */
241
- function getTextContent(node) {
242
- if (node.type === "text") return node.value;
243
- if (node.children) return node.children.map(getTextContent).join("");
244
- return "";
245
- }
246
- //#endregion
247
- //#region src/mdx/rehype-headings.ts
248
- /**
249
- * Rehype plugin to extract headings from MDX/MD content
250
- *
251
- * Extracts headings (h2-h6 by default) with their IDs and text content
252
- * for use in table of contents generation.
253
- */
254
- /**
255
- * Rehype plugin to extract headings from the document
256
- *
257
- * Stores extracted headings in `file.data.headings` for later use.
258
- *
259
- * @example
260
- * ```ts
261
- * import { rehypeExtractHeadings } from './rehype-headings';
262
- *
263
- * // Use with unified
264
- * unified()
265
- * .use(rehypeSlug) // First add IDs to headings
266
- * .use(rehypeExtractHeadings, { minLevel: 2, maxLevel: 3 })
267
- * ```
268
- */
269
- function rehypeExtractHeadings(options = {}) {
270
- const { minLevel = 2, maxLevel = 3 } = options;
271
- return (tree, file) => {
272
- const headings = [];
273
- visit(tree, "element", (node) => {
274
- const match = /^h([1-6])$/.exec(node.tagName);
275
- if (!match) return;
276
- const level = parseInt(match[1], 10);
277
- if (level < minLevel || level > maxLevel) return;
278
- const id = node.properties?.id;
279
- if (!id) return;
280
- const text = toString(node).trim();
281
- if (!text) return;
282
- headings.push({
283
- id,
284
- text,
285
- level
286
- });
287
- });
288
- file.data = file.data || {};
289
- file.data.headings = headings;
290
- };
291
- }
292
- //#endregion
293
- //#region src/mdx/plugin.ts
294
- /**
295
- * Create the MDX Vite plugin
296
- */
297
- function mdxPlugin(options = {}) {
298
- const { markdown = {} } = options;
299
- let mdxRollup;
300
- let viteConfig;
301
- return {
302
- name: "sigx-ssg-mdx",
303
- enforce: "pre",
304
- async configResolved(config) {
305
- viteConfig = config;
306
- const mdxModule = await import("@mdx-js/rollup");
307
- const remarkFrontmatter = (await import("remark-frontmatter")).default;
308
- const remarkMdxFrontmatter = (await import("remark-mdx-frontmatter")).default;
309
- const remarkGfm = (await import("remark-gfm")).default;
310
- const rehypeSlug = (await import("rehype-slug")).default;
311
- const rehypeAutolinkHeadings = (await import("rehype-autolink-headings")).default;
312
- const tocConfig = options.ssgConfig?.toc || {
313
- minLevel: 2,
314
- maxLevel: 3
315
- };
316
- const rehypePlugins = [];
317
- rehypePlugins.push(rehypeSlug);
318
- rehypePlugins.push([rehypeAutolinkHeadings, {
319
- behavior: "append",
320
- properties: {
321
- class: "heading-anchor",
322
- ariaHidden: true,
323
- tabIndex: -1
324
- },
325
- content: {
326
- type: "element",
327
- tagName: "span",
328
- properties: { class: "heading-anchor-icon" },
329
- children: [{
330
- type: "text",
331
- value: "#"
332
- }]
333
- }
334
- }]);
335
- rehypePlugins.push([rehypeExtractHeadings, tocConfig]);
336
- if (markdown.shiki !== false) {
337
- const shikiConfig = typeof markdown.shiki === "object" ? markdown.shiki : void 0;
338
- rehypePlugins.push([rehypeShiki, shikiConfig]);
339
- }
340
- if (markdown.rehypePlugins) rehypePlugins.push(...markdown.rehypePlugins);
341
- const remarkPlugins = [
342
- remarkFrontmatter,
343
- [remarkMdxFrontmatter, { name: "frontmatter" }],
344
- remarkGfm
345
- ];
346
- if (markdown.remarkPlugins) remarkPlugins.push(...markdown.remarkPlugins);
347
- mdxRollup = mdxModule.default({
348
- jsx: false,
349
- jsxImportSource: "sigx",
350
- remarkPlugins,
351
- rehypePlugins,
352
- providerImportSource: void 0
353
- });
354
- },
355
- async transform(code, id) {
356
- if (!/\.mdx?$/.test(id)) return null;
357
- const { data: frontmatter, content } = parseFrontmatter(code);
358
- if (!frontmatter.title) {
359
- const extractedTitle = extractTitleFromContent(content);
360
- if (extractedTitle) frontmatter.title = extractedTitle;
361
- }
362
- if (!mdxRollup?.transform) throw new Error("MDX plugin not initialized");
363
- const result = await mdxRollup.transform(code, id);
364
- if (!result) return null;
365
- const headings = await extractHeadingsFromContent(content, options);
366
- const moduleId = id.replace(/\\/g, "/");
367
- return {
368
- code: wrapMDXComponent(result.code, frontmatter, headings, moduleId, viteConfig.command === "serve"),
369
- map: result.map
370
- };
371
- }
372
- };
373
- }
374
- /**
375
- * Wrap MDX output in a SignalX component
376
- *
377
- * Note: remark-mdx-frontmatter already exports `frontmatter`, so we only add the layout export
378
- *
379
- * In dev mode, we:
380
- * 1. Wrap MDXContent in a sigx component() for proper HMR tracking
381
- * 2. Inject HMR registration code so the component updates live
382
- */
383
- function wrapMDXComponent(code, frontmatter, headings, moduleId, isDev) {
384
- if (isDev) {
385
- const fileName = moduleId.split("/").pop()?.replace(/\.mdx?$/, "") || "MDXPage";
386
- const componentName = fileName.charAt(0).toUpperCase() + fileName.slice(1).replace(/[^a-zA-Z0-9]/g, "") + "Page";
387
- return `
388
- import { registerHMRModule } from '@sigx/vite/hmr';
389
- import { component as __component } from 'sigx';
390
- registerHMRModule('${moduleId}');
391
-
392
- ${code.replace(/export\s+default\s+function\s+MDXContent/g, "function _MDXContent").replace(/export\s+{\s*MDXContent\s+as\s+default\s*}/g, "")}
393
-
394
- // Export layout from frontmatter for SSG routing
395
- export const layout = ${frontmatter.layout ? JSON.stringify(frontmatter.layout) : "undefined"};
396
-
397
- // Export headings for table of contents
398
- export const headings = ${JSON.stringify(headings)};
399
-
400
- // Wrap MDXContent in a sigx component for HMR support
401
- const MDXPage = __component(() => {
402
- return () => _MDXContent({});
403
- }, { name: '${componentName}' });
404
-
405
- export default MDXPage;
406
-
407
- if (import.meta.hot) {
408
- // Accept HMR updates with a callback to trigger re-render after module is updated
409
- import.meta.hot.accept((newModule) => {
410
- if (newModule) {
411
- // Notify LayoutRouter to clear its cache and re-render with new module
412
- window.dispatchEvent(new CustomEvent('sigx:mdx-hmr', {
413
- detail: { moduleId: '${moduleId}', newModule }
414
- }));
415
- }
416
- });
417
- }
418
- `;
419
- }
420
- return `
421
- ${code}
422
-
423
- // Export layout from frontmatter for SSG routing
424
- export const layout = ${frontmatter.layout ? JSON.stringify(frontmatter.layout) : "undefined"};
425
-
426
- // Export headings for table of contents
427
- export const headings = ${JSON.stringify(headings)};
428
- `;
429
- }
430
- /**
431
- * Extract headings from markdown/MDX content
432
- */
433
- async function extractHeadingsFromContent(content, options) {
434
- const { unified } = await import("unified");
435
- const remarkParse = (await import("remark-parse")).default;
436
- const remarkRehype = (await import("remark-rehype")).default;
437
- const rehypeSlug = (await import("rehype-slug")).default;
438
- const rehypeStringify = (await import("rehype-stringify")).default;
439
- const tocConfig = options.ssgConfig?.toc || {
440
- minLevel: 2,
441
- maxLevel: 3
442
- };
443
- return (await unified().use(remarkParse).use(remarkRehype).use(rehypeSlug).use(rehypeExtractHeadings, tocConfig).use(rehypeStringify).process(content)).data.headings || [];
444
- }
445
- //#endregion
446
- //#region src/vite/plugin.ts
447
- /**
448
- * Virtual module for SSG config
449
- */
450
- var VIRTUAL_CONFIG_ID = "virtual:ssg-config";
451
- var RESOLVED_VIRTUAL_CONFIG_ID = "\0" + VIRTUAL_CONFIG_ID;
452
- /**
453
- * Create the SSG Vite plugin
454
- */
455
- function ssgPlugin(options = {}) {
456
- let config;
457
- let ssgConfig;
458
- let root;
459
- let entryDetection;
460
- let routesCache = null;
461
- let layoutsCache = null;
462
- let navigationCache = null;
463
- const frontmatterHashCache = /* @__PURE__ */ new Map();
464
- const mainPlugin = {
465
- name: "sigx-ssg",
466
- enforce: "pre",
467
- async configResolved(resolvedConfig) {
468
- config = resolvedConfig;
469
- root = resolvedConfig.root;
470
- ssgConfig = defineSSGConfig({
471
- ...await loadConfig(options.configPath),
472
- ...options
473
- });
474
- entryDetection = detectCustomEntries(root, ssgConfig);
475
- if (entryDetection.useVirtualClient || entryDetection.useVirtualServer) {
476
- console.log("📦 @sigx/ssg: Using zero-config mode");
477
- if (entryDetection.useVirtualClient) console.log(" → Virtual client entry");
478
- if (entryDetection.useVirtualServer) console.log(" → Virtual server entry");
479
- if (entryDetection.globalCssPath) console.log(` → Auto-importing ${entryDetection.globalCssPath}`);
480
- }
481
- },
482
- configureServer(devServer) {
483
- const pagesDir = path.resolve(root, ssgConfig.pages || "src/pages");
484
- const layoutsDir = path.resolve(root, ssgConfig.layouts || "src/layouts");
485
- path.resolve(root, ssgConfig.content || "src/content");
486
- devServer.watcher.on("add", (file) => {
487
- if (file.startsWith(pagesDir)) {
488
- routesCache = null;
489
- navigationCache = null;
490
- invalidateModule(RESOLVED_VIRTUAL_ROUTES_ID);
491
- invalidateModule(RESOLVED_VIRTUAL_NAVIGATION_ID);
492
- } else if (file.startsWith(layoutsDir)) {
493
- layoutsCache = null;
494
- invalidateModule(RESOLVED_VIRTUAL_LAYOUTS_ID);
495
- }
496
- });
497
- devServer.watcher.on("unlink", (file) => {
498
- if (file.startsWith(pagesDir)) {
499
- routesCache = null;
500
- navigationCache = null;
501
- frontmatterHashCache.delete(file);
502
- invalidateModule(RESOLVED_VIRTUAL_ROUTES_ID);
503
- invalidateModule(RESOLVED_VIRTUAL_NAVIGATION_ID);
504
- } else if (file.startsWith(layoutsDir)) {
505
- layoutsCache = null;
506
- invalidateModule(RESOLVED_VIRTUAL_LAYOUTS_ID);
507
- }
508
- });
509
- devServer.watcher.on("change", async (file) => {
510
- if (!file.startsWith(pagesDir)) return;
511
- if (!/\.mdx?$/.test(file)) return;
512
- try {
513
- const { data: newFrontmatter } = parseFrontmatter(await fsSync.promises.readFile(file, "utf-8"));
514
- const newHash = JSON.stringify(newFrontmatter);
515
- const oldHash = frontmatterHashCache.get(file);
516
- frontmatterHashCache.set(file, newHash);
517
- if (oldHash !== void 0 && oldHash !== newHash) {
518
- navigationCache = null;
519
- routesCache = null;
520
- const navMod = devServer.moduleGraph.getModuleById(RESOLVED_VIRTUAL_NAVIGATION_ID);
521
- if (navMod) devServer.moduleGraph.invalidateModule(navMod);
522
- const routesMod = devServer.moduleGraph.getModuleById(RESOLVED_VIRTUAL_ROUTES_ID);
523
- if (routesMod) devServer.moduleGraph.invalidateModule(routesMod);
524
- devServer.ws.send({ type: "full-reload" });
525
- }
526
- } catch (err) {}
527
- });
528
- function invalidateModule(id) {
529
- const mod = devServer.moduleGraph.getModuleById(id);
530
- if (mod) {
531
- devServer.moduleGraph.invalidateModule(mod);
532
- devServer.ws.send({ type: "full-reload" });
533
- }
534
- }
535
- if (entryDetection.useVirtualHtml) devServer.middlewares.use((req, res, next) => {
536
- if (req.url?.startsWith("/@") || req.url?.startsWith("/__") || req.url?.includes("virtual:") || req.url?.includes("node_modules") || req.url?.startsWith("/@vite") || req.url?.startsWith("/@fs")) return next();
537
- if (req.url && (req.url === "/" || !req.url.includes("."))) {
538
- const html = generateHtmlTemplate(ssgConfig);
539
- devServer.transformIndexHtml(req.url, html).then((transformedHtml) => {
540
- res.setHeader("Content-Type", "text/html");
541
- res.end(transformedHtml);
542
- }).catch(next);
543
- return;
544
- }
545
- next();
546
- });
547
- },
548
- resolveId(id) {
549
- if (id === "virtual:ssg-routes") return RESOLVED_VIRTUAL_ROUTES_ID;
550
- if (id === "virtual:generated-layouts") return RESOLVED_VIRTUAL_LAYOUTS_ID;
551
- if (id === VIRTUAL_CONFIG_ID) return RESOLVED_VIRTUAL_CONFIG_ID;
552
- if (id === "virtual:ssg-navigation") return RESOLVED_VIRTUAL_NAVIGATION_ID;
553
- if (id === "virtual:ssg-client" || id === "/@ssg/client.tsx") return RESOLVED_VIRTUAL_CLIENT_ID;
554
- if (id === "virtual:ssg-server") return RESOLVED_VIRTUAL_SERVER_ID;
555
- return null;
556
- },
557
- async load(id) {
558
- if (id === "\0virtual:ssg-routes") {
559
- if (!routesCache) {
560
- const routes = await scanPages(ssgConfig, root);
561
- routesCache = {
562
- routes,
563
- code: config.command === "serve" ? generateLazyRoutesModule(routes, ssgConfig) : generateRoutesModule(routes, ssgConfig)
564
- };
565
- }
566
- return routesCache.code;
567
- }
568
- if (id === "\0virtual:generated-layouts") {
569
- if (!layoutsCache) {
570
- const layouts = await discoverLayouts(ssgConfig, root);
571
- layoutsCache = {
572
- layouts,
573
- code: generateLayoutsModule(layouts, ssgConfig)
574
- };
575
- }
576
- return layoutsCache.code;
577
- }
578
- if (id === "\0virtual:ssg-navigation") {
579
- if (!navigationCache) {
580
- if (!routesCache) {
581
- const routes = await scanPages(ssgConfig, root);
582
- routesCache = {
583
- routes,
584
- code: config.command === "serve" ? generateLazyRoutesModule(routes, ssgConfig) : generateRoutesModule(routes, ssgConfig)
585
- };
586
- }
587
- const isDev = config.command === "serve";
588
- navigationCache = { code: generateNavigationModule(routesCache.routes, ssgConfig, isDev) };
589
- }
590
- return navigationCache.code;
591
- }
592
- if (id === RESOLVED_VIRTUAL_CONFIG_ID) return `export default ${JSON.stringify(ssgConfig)};`;
593
- if (id === "\0virtual:ssg-client.tsx") {
594
- const code = generateClientEntry(ssgConfig, entryDetection);
595
- return (await (await import("esbuild")).transform(code, {
596
- loader: "tsx",
597
- jsx: "automatic",
598
- jsxImportSource: "sigx"
599
- })).code;
600
- }
601
- if (id === "\0virtual:ssg-server.tsx") {
602
- const code = generateServerEntry(ssgConfig);
603
- return (await (await import("esbuild")).transform(code, {
604
- loader: "tsx",
605
- jsx: "automatic",
606
- jsxImportSource: "sigx"
607
- })).code;
608
- }
609
- return null;
610
- },
611
- async handleHotUpdate({ file, server }) {
612
- const layoutsDir = path.resolve(root, ssgConfig.layouts || "src/layouts");
613
- const pagesDir = path.resolve(root, ssgConfig.pages || "src/pages");
614
- if (file.startsWith(layoutsDir)) {
615
- layoutsCache = null;
616
- const mod = server.moduleGraph.getModuleById(RESOLVED_VIRTUAL_LAYOUTS_ID);
617
- if (mod) server.moduleGraph.invalidateModule(mod);
618
- return [];
619
- }
620
- if (file.startsWith(pagesDir) && /\.mdx?$/.test(file)) return;
621
- }
622
- };
623
- if (options.enableMdx !== false) return [mainPlugin, mdxPlugin({
624
- markdown: options.markdown,
625
- ssgConfig: void 0
626
- })];
627
- return [mainPlugin];
628
- }
629
- //#endregion
630
- export { ssgPlugin as t };
631
-
632
- //# sourceMappingURL=plugin-Bik0HMne.js.map