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