@sigx/ssg 0.4.1 → 0.4.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.
- package/README.md +49 -208
- package/dist/{build-DP9zez3B.js → build-C9YKGjXk.js} +6 -2
- package/dist/build-C9YKGjXk.js.map +1 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +1 -1
- package/dist/dev.js +1 -1
- package/dist/errors.d.ts +11 -11
- package/dist/errors.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/mdx/rehype-headings.d.ts.map +1 -1
- package/dist/mdx/shiki.d.ts.map +1 -1
- package/dist/{plugin-EIAzPLvE.js → plugin-aT6WUDzk.js} +5 -3
- package/dist/{plugin-EIAzPLvE.js.map → plugin-aT6WUDzk.js.map} +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/vite/plugin.d.ts.map +1 -1
- package/dist/vite/plugin.js +1 -1
- package/package.json +3 -3
- package/src/client.d.ts +58 -0
- package/dist/build-DP9zez3B.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,46 +1,28 @@
|
|
|
1
1
|
# @sigx/ssg
|
|
2
2
|
|
|
3
|
-
Static
|
|
3
|
+
Static site generator for [SignalX](https://github.com/signalxjs/core) with file-based routing, MDX, and pluggable themes.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- 🗂 **File-based routing**
|
|
8
|
-
- 📑 **Layout system**
|
|
9
|
-
- 📝 **MDX
|
|
10
|
-
- 🎨 **Pluggable themes**
|
|
11
|
-
-
|
|
12
|
-
- ⚡ **Fast builds**
|
|
13
|
-
- 🚀 **Zero-config mode**
|
|
14
|
-
- 🗺️ **Sitemap generation**
|
|
15
|
-
- 🛠️ **CLI tools** - `ssg dev`, `ssg build`, and `ssg preview` commands
|
|
7
|
+
- 🗂 **File-based routing** — `src/pages/` becomes routes automatically
|
|
8
|
+
- 📑 **Layout system** — wrap pages with reusable layouts
|
|
9
|
+
- 📝 **MDX** — write content with Markdown + SignalX components
|
|
10
|
+
- 🎨 **Pluggable themes** — install a theme package (e.g. `@sigx/ssg-theme-daisyui`) or build your own
|
|
11
|
+
- 🔥 **Vite HMR** — instant updates during development
|
|
12
|
+
- ⚡ **Fast builds** — parallel rendering with streaming
|
|
13
|
+
- 🚀 **Zero-config mode** — works out of the box with sensible defaults
|
|
14
|
+
- 🗺️ **Sitemap generation** — automatic `sitemap.xml` and `robots.txt`
|
|
16
15
|
|
|
17
16
|
## Installation
|
|
18
17
|
|
|
19
18
|
```bash
|
|
20
|
-
npm install @sigx/ssg sigx @sigx/router
|
|
19
|
+
npm install @sigx/ssg sigx @sigx/router
|
|
20
|
+
npm install -D vite @sigx/vite
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
##
|
|
23
|
+
## Usage
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
The simplest way to get started - just create pages and run:
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
# Create a page
|
|
31
|
-
mkdir -p src/pages
|
|
32
|
-
echo 'export default () => <h1>Hello World</h1>' > src/pages/index.tsx
|
|
33
|
-
|
|
34
|
-
# Start development
|
|
35
|
-
npx ssg dev
|
|
36
|
-
|
|
37
|
-
# Build for production
|
|
38
|
-
npx ssg build
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
### With configuration
|
|
42
|
-
|
|
43
|
-
Create `ssg.config.ts`:
|
|
25
|
+
`@sigx/ssg` ships as a [`@sigx/cli`](https://github.com/signalxjs/cli) plugin and a Vite plugin you can use directly. Drop a `ssg.config.ts` in your project root to opt in:
|
|
44
26
|
|
|
45
27
|
```ts
|
|
46
28
|
// ssg.config.ts
|
|
@@ -51,49 +33,32 @@ export default defineSSGConfig({
|
|
|
51
33
|
title: 'My Site',
|
|
52
34
|
description: 'Built with SignalX SSG',
|
|
53
35
|
url: 'https://example.com',
|
|
54
|
-
fonts: ['Inter:wght@400;500;600;700'],
|
|
55
36
|
},
|
|
56
37
|
});
|
|
57
38
|
```
|
|
58
39
|
|
|
59
|
-
###
|
|
40
|
+
### Via `@sigx/cli`
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npx sigx ssg dev # start dev server
|
|
44
|
+
npx sigx ssg build # build static site
|
|
45
|
+
npx sigx ssg preview # preview the production build
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Via the Vite plugin
|
|
60
49
|
|
|
61
50
|
```ts
|
|
62
51
|
// vite.config.ts
|
|
63
52
|
import { defineConfig } from 'vite';
|
|
64
|
-
import
|
|
65
|
-
import
|
|
53
|
+
import sigx from '@sigx/vite';
|
|
54
|
+
import ssg from '@sigx/ssg/vite';
|
|
66
55
|
|
|
67
56
|
export default defineConfig({
|
|
68
|
-
plugins: [
|
|
69
|
-
sigxPlugin(),
|
|
70
|
-
ssgPlugin(),
|
|
71
|
-
],
|
|
72
|
-
});
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### Create pages
|
|
76
|
-
|
|
77
|
-
```tsx
|
|
78
|
-
// src/pages/index.tsx
|
|
79
|
-
import { component } from 'sigx';
|
|
80
|
-
|
|
81
|
-
export default component(() => {
|
|
82
|
-
return () => (
|
|
83
|
-
<div>
|
|
84
|
-
<h1>Welcome to my site!</h1>
|
|
85
|
-
</div>
|
|
86
|
-
);
|
|
57
|
+
plugins: [sigx(), ssg()],
|
|
87
58
|
});
|
|
88
59
|
```
|
|
89
60
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
```bash
|
|
93
|
-
npx ssg build
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
## File-based Routing
|
|
61
|
+
## File-based routing
|
|
97
62
|
|
|
98
63
|
Pages in `src/pages/` are automatically converted to routes:
|
|
99
64
|
|
|
@@ -105,16 +70,13 @@ Pages in `src/pages/` are automatically converted to routes:
|
|
|
105
70
|
| `src/pages/blog/[slug].tsx` | `/blog/:slug` |
|
|
106
71
|
| `src/pages/docs/[...path].tsx` | `/docs/*path` |
|
|
107
72
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
For dynamic routes, export a `getStaticPaths` function:
|
|
73
|
+
Dynamic routes export `getStaticPaths`:
|
|
111
74
|
|
|
112
75
|
```tsx
|
|
113
76
|
// src/pages/blog/[slug].tsx
|
|
114
77
|
import { component } from 'sigx';
|
|
115
78
|
|
|
116
79
|
export async function getStaticPaths() {
|
|
117
|
-
// Fetch your data from any source
|
|
118
80
|
const posts = await fetchBlogPosts();
|
|
119
81
|
return posts.map(post => ({
|
|
120
82
|
params: { slug: post.slug },
|
|
@@ -122,13 +84,11 @@ export async function getStaticPaths() {
|
|
|
122
84
|
}));
|
|
123
85
|
}
|
|
124
86
|
|
|
125
|
-
export default component(({ props }) =>
|
|
126
|
-
|
|
127
|
-
<
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
);
|
|
131
|
-
});
|
|
87
|
+
export default component(({ props }) => () => (
|
|
88
|
+
<article>
|
|
89
|
+
<h1>{props.post.title}</h1>
|
|
90
|
+
</article>
|
|
91
|
+
));
|
|
132
92
|
```
|
|
133
93
|
|
|
134
94
|
## Layouts
|
|
@@ -139,22 +99,16 @@ Create layouts in `src/layouts/`:
|
|
|
139
99
|
// src/layouts/default.tsx
|
|
140
100
|
import { component } from 'sigx';
|
|
141
101
|
|
|
142
|
-
export default component(({ slots
|
|
143
|
-
|
|
144
|
-
<
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
{slots.default()}
|
|
150
|
-
</main>
|
|
151
|
-
<footer>© 2024</footer>
|
|
152
|
-
</div>
|
|
153
|
-
);
|
|
154
|
-
});
|
|
102
|
+
export default component(({ slots }) => () => (
|
|
103
|
+
<div class="layout">
|
|
104
|
+
<header><nav>My Site</nav></header>
|
|
105
|
+
<main>{slots.default()}</main>
|
|
106
|
+
<footer>©</footer>
|
|
107
|
+
</div>
|
|
108
|
+
));
|
|
155
109
|
```
|
|
156
110
|
|
|
157
|
-
Specify layout in frontmatter or
|
|
111
|
+
Specify the layout in frontmatter or by exporting it:
|
|
158
112
|
|
|
159
113
|
```mdx
|
|
160
114
|
---
|
|
@@ -165,149 +119,57 @@ layout: docs
|
|
|
165
119
|
# Content here
|
|
166
120
|
```
|
|
167
121
|
|
|
168
|
-
Or in TSX:
|
|
169
|
-
|
|
170
122
|
```tsx
|
|
171
123
|
export const layout = 'docs';
|
|
172
124
|
```
|
|
173
125
|
|
|
174
126
|
## MDX
|
|
175
127
|
|
|
176
|
-
Write content with MDX:
|
|
177
|
-
|
|
178
128
|
```mdx
|
|
179
129
|
---
|
|
180
130
|
title: Hello World
|
|
181
|
-
date:
|
|
131
|
+
date: 2026-05-10
|
|
182
132
|
---
|
|
183
133
|
|
|
184
134
|
# {frontmatter.title}
|
|
185
135
|
|
|
186
|
-
This is **markdown** with SignalX components
|
|
136
|
+
This is **markdown** with SignalX components mixed in:
|
|
187
137
|
|
|
188
138
|
<Counter initial={5} />
|
|
189
139
|
```
|
|
190
140
|
|
|
191
141
|
## Themes
|
|
192
142
|
|
|
193
|
-
Install a theme
|
|
143
|
+
Install a theme:
|
|
194
144
|
|
|
195
145
|
```bash
|
|
196
146
|
npm install @sigx/ssg-theme-daisyui
|
|
197
147
|
```
|
|
198
148
|
|
|
199
|
-
Configure in ssg.config.ts:
|
|
200
|
-
|
|
201
149
|
```ts
|
|
150
|
+
// ssg.config.ts
|
|
202
151
|
export default defineSSGConfig({
|
|
203
152
|
theme: '@sigx/ssg-theme-daisyui',
|
|
204
153
|
});
|
|
205
154
|
```
|
|
206
155
|
|
|
207
|
-
|
|
208
|
-
- Pre-built layouts (default, docs, blog, etc.)
|
|
209
|
-
- UI components
|
|
210
|
-
- CSS styles
|
|
211
|
-
|
|
212
|
-
## Hydration
|
|
213
|
-
|
|
214
|
-
SSG pages are pre-rendered at build time as static HTML. Interactive components are "hydrated" on the client side to become reactive.
|
|
215
|
-
|
|
216
|
-
### Islands Architecture
|
|
156
|
+
A theme bundles layouts, components, and CSS so you don't have to.
|
|
217
157
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
```tsx
|
|
221
|
-
import { Counter } from './components/Counter';
|
|
222
|
-
|
|
223
|
-
export default component(() => {
|
|
224
|
-
return () => (
|
|
225
|
-
<div>
|
|
226
|
-
{/* Static content - no JS needed */}
|
|
227
|
-
<h1>Welcome to my site</h1>
|
|
228
|
-
|
|
229
|
-
{/* Interactive islands with different hydration strategies */}
|
|
230
|
-
<Counter client:load />
|
|
231
|
-
<LazyWidget client:idle />
|
|
232
|
-
<OffscreenChart client:visible />
|
|
233
|
-
<MobileMenu client:media="(max-width: 768px)" />
|
|
234
|
-
<BrowserOnlyWidget client:only />
|
|
235
|
-
</div>
|
|
236
|
-
);
|
|
237
|
-
});
|
|
238
|
-
```
|
|
158
|
+
## Islands hydration
|
|
239
159
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
| Directive | When it Hydrates | Use Case |
|
|
243
|
-
|-----------|------------------|----------|
|
|
244
|
-
| `client:load` | Immediately on page load | Critical interactive elements |
|
|
245
|
-
| `client:idle` | When browser is idle (`requestIdleCallback`) | Non-critical interactivity |
|
|
246
|
-
| `client:visible` | When scrolled into viewport (`IntersectionObserver`) | Below-the-fold components |
|
|
247
|
-
| `client:media="query"` | When media query matches | Mobile-only components |
|
|
248
|
-
| `client:only` | Not SSR'd, renders only on client | Browser API dependencies |
|
|
249
|
-
|
|
250
|
-
### Example: Interactive Counter
|
|
251
|
-
|
|
252
|
-
```tsx
|
|
253
|
-
// src/components/Counter.tsx
|
|
254
|
-
import { component, signal } from 'sigx';
|
|
255
|
-
|
|
256
|
-
export const Counter = component(() => {
|
|
257
|
-
const count = signal(0);
|
|
258
|
-
|
|
259
|
-
return () => (
|
|
260
|
-
<div class="counter">
|
|
261
|
-
<button onClick={() => count.value--}>-</button>
|
|
262
|
-
<span>{count.value}</span>
|
|
263
|
-
<button onClick={() => count.value++}>+</button>
|
|
264
|
-
</div>
|
|
265
|
-
);
|
|
266
|
-
}, { name: 'Counter' });
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
```tsx
|
|
270
|
-
// src/pages/index.tsx
|
|
271
|
-
import { Counter } from '../components/Counter';
|
|
272
|
-
|
|
273
|
-
export default component(() => {
|
|
274
|
-
return () => (
|
|
275
|
-
<main>
|
|
276
|
-
<h1>Counter Demo</h1>
|
|
277
|
-
<Counter client:load />
|
|
278
|
-
</main>
|
|
279
|
-
);
|
|
280
|
-
});
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
### How It Works
|
|
284
|
-
|
|
285
|
-
1. **Build time**: Pages are rendered to static HTML with component markers
|
|
286
|
-
2. **Page load**: Browser receives pre-rendered HTML (fast First Contentful Paint)
|
|
287
|
-
3. **Hydration**: JavaScript attaches event handlers and reactivity to existing DOM
|
|
288
|
-
4. **Interactivity**: Components become fully interactive without re-rendering
|
|
289
|
-
|
|
290
|
-
### Benefits
|
|
291
|
-
|
|
292
|
-
- **Fast initial load** - HTML is ready immediately, no JavaScript blocking
|
|
293
|
-
- **SEO friendly** - Search engines see full content
|
|
294
|
-
- **Progressive enhancement** - Content is visible before JS loads
|
|
295
|
-
- **Reduced JavaScript** - Only hydrate what needs to be interactive
|
|
296
|
-
- **Fine-grained control** - Choose when each component becomes interactive
|
|
160
|
+
`@sigx/ssg` works with [`@sigx/ssr-islands`](https://github.com/signalxjs/ssr-islands) for selective hydration via `client:*` directives. See the islands README for details.
|
|
297
161
|
|
|
298
162
|
## Configuration
|
|
299
163
|
|
|
300
|
-
Full
|
|
164
|
+
Full options:
|
|
301
165
|
|
|
302
166
|
```ts
|
|
303
167
|
defineSSGConfig({
|
|
304
|
-
// Directories
|
|
305
168
|
pages: 'src/pages',
|
|
306
169
|
layouts: 'src/layouts',
|
|
307
170
|
content: 'src/content',
|
|
308
171
|
outDir: 'dist',
|
|
309
172
|
|
|
310
|
-
// Site metadata
|
|
311
173
|
site: {
|
|
312
174
|
title: 'My Site',
|
|
313
175
|
description: 'Site description',
|
|
@@ -320,11 +182,9 @@ defineSSGConfig({
|
|
|
320
182
|
themeColor: '#000000',
|
|
321
183
|
},
|
|
322
184
|
|
|
323
|
-
// Theme
|
|
324
185
|
theme: '@sigx/ssg-theme-daisyui',
|
|
325
186
|
defaultLayout: 'default',
|
|
326
187
|
|
|
327
|
-
// Markdown options
|
|
328
188
|
markdown: {
|
|
329
189
|
shiki: {
|
|
330
190
|
light: 'github-light',
|
|
@@ -334,25 +194,6 @@ defineSSGConfig({
|
|
|
334
194
|
});
|
|
335
195
|
```
|
|
336
196
|
|
|
337
|
-
## CLI
|
|
338
|
-
|
|
339
|
-
```bash
|
|
340
|
-
# Start development server
|
|
341
|
-
npx ssg dev
|
|
342
|
-
|
|
343
|
-
# Build static site (generates sitemap.xml and robots.txt)
|
|
344
|
-
npx ssg build
|
|
345
|
-
|
|
346
|
-
# Preview production build
|
|
347
|
-
npx ssg preview
|
|
348
|
-
|
|
349
|
-
# With custom config
|
|
350
|
-
npx ssg build --config=./my-config.ts
|
|
351
|
-
|
|
352
|
-
# With options
|
|
353
|
-
npx ssg dev --port=3000 --open
|
|
354
|
-
```
|
|
355
|
-
|
|
356
197
|
## License
|
|
357
198
|
|
|
358
|
-
MIT
|
|
199
|
+
MIT © Andreas Ekdahl
|
|
@@ -62,6 +62,10 @@ async function b(t = {}) {
|
|
|
62
62
|
let n = Date.now(), a = process.cwd(), c = [], l = [];
|
|
63
63
|
console.log("\n🚀 @sigx/ssg - Building static site...\n"), console.log("📦 Loading configuration...");
|
|
64
64
|
let u = e(await i(t.configPath), a);
|
|
65
|
+
if (!u.base || u.base === "/") try {
|
|
66
|
+
let e = await (await import("vite")).resolveConfig({ root: a }, "build");
|
|
67
|
+
e.base && e.base !== "/" && (u.base = e.base);
|
|
68
|
+
} catch {}
|
|
65
69
|
console.log("🔍 Scanning pages...");
|
|
66
70
|
let h = await r(u, a);
|
|
67
71
|
console.log(` Found ${h.length} page(s)`), console.log("📐 Discovering layouts...");
|
|
@@ -214,7 +218,7 @@ function S(e, t) {
|
|
|
214
218
|
}
|
|
215
219
|
function C(e, t) {
|
|
216
220
|
let n = e.replace(/^\//, "").replace(/\/$/, "");
|
|
217
|
-
return n
|
|
221
|
+
return n ? n.endsWith(".html") ? d.join(t, n) : d.join(t, n, "index.html") : d.join(t, "index.html");
|
|
218
222
|
}
|
|
219
223
|
async function w(e, t) {
|
|
220
224
|
let n = s(t, e);
|
|
@@ -251,4 +255,4 @@ function k(e) {
|
|
|
251
255
|
//#endregion
|
|
252
256
|
export { v as a, _ as i, g as n, h as r, b as t };
|
|
253
257
|
|
|
254
|
-
//# sourceMappingURL=build-
|
|
258
|
+
//# sourceMappingURL=build-C9YKGjXk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-C9YKGjXk.js","names":[],"sources":["../src/sitemap.ts","../src/build.ts"],"sourcesContent":["/**\n * Sitemap Generation\n *\n * Generates XML sitemaps for SSG sites following the sitemap protocol.\n * https://www.sitemaps.org/protocol.html\n */\n\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport type { SSGConfig, PageBuildResult } from './types';\n\n/**\n * Sitemap entry with optional metadata\n */\nexport interface SitemapEntry {\n /** URL path (relative to site base) */\n path: string;\n /** Last modification date */\n lastmod?: Date | string;\n /** Change frequency hint */\n changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';\n /** Priority relative to other pages (0.0 to 1.0) */\n priority?: number;\n}\n\n/**\n * Sitemap generation options\n */\nexport interface SitemapOptions {\n /** Include all built pages automatically */\n includePages?: boolean;\n /** Additional URLs to include */\n additionalUrls?: SitemapEntry[];\n /** URLs to exclude (glob patterns or exact matches) */\n exclude?: string[];\n /** Default change frequency */\n defaultChangefreq?: SitemapEntry['changefreq'];\n /** Default priority */\n defaultPriority?: number;\n}\n\n/**\n * Generate sitemap XML content\n */\nexport function generateSitemap(\n entries: SitemapEntry[],\n config: SSGConfig\n): string {\n const siteUrl = config.site?.url?.replace(/\\/$/, '') || '';\n const base = config.base?.replace(/\\/$/, '') || '';\n\n const urlEntries = entries.map((entry) => {\n const loc = `${siteUrl}${base}${entry.path}`;\n const lastmod = entry.lastmod\n ? typeof entry.lastmod === 'string'\n ? entry.lastmod\n : entry.lastmod.toISOString().split('T')[0]\n : undefined;\n\n return ` <url>\n <loc>${escapeXml(loc)}</loc>${lastmod ? `\n <lastmod>${lastmod}</lastmod>` : ''}${entry.changefreq ? `\n <changefreq>${entry.changefreq}</changefreq>` : ''}${entry.priority !== undefined ? `\n <priority>${entry.priority.toFixed(1)}</priority>` : ''}\n </url>`;\n });\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n${urlEntries.join('\\n')}\n</urlset>`;\n}\n\n/**\n * Generate robots.txt content\n */\nexport function generateRobotsTxt(config: SSGConfig, sitemapPath = '/sitemap.xml'): string {\n const siteUrl = config.site?.url?.replace(/\\/$/, '') || '';\n const base = config.base?.replace(/\\/$/, '') || '';\n\n return `User-agent: *\nAllow: /\n\nSitemap: ${siteUrl}${base}${sitemapPath}\n`;\n}\n\n/**\n * Convert page build results to sitemap entries\n */\nexport function pagesToSitemapEntries(\n pages: PageBuildResult[],\n options: SitemapOptions = {}\n): SitemapEntry[] {\n const {\n exclude = [],\n defaultChangefreq = 'weekly',\n defaultPriority = 0.5,\n } = options;\n\n return pages\n .filter((page) => {\n // Filter out excluded paths\n for (const pattern of exclude) {\n if (pattern.includes('*')) {\n // Simple glob matching\n const regex = new RegExp(\n '^' + pattern.replace(/\\*/g, '.*').replace(/\\?/g, '.') + '$'\n );\n if (regex.test(page.path)) return false;\n } else if (page.path === pattern) {\n return false;\n }\n }\n return true;\n })\n .map((page) => {\n // Determine priority based on path depth\n const depth = page.path.split('/').filter(Boolean).length;\n let priority = defaultPriority;\n \n if (page.path === '/') {\n priority = 1.0; // Homepage highest priority\n } else if (depth === 1) {\n priority = 0.8; // Top-level pages\n } else if (depth === 2) {\n priority = 0.6; // Second-level pages\n }\n\n return {\n path: page.path,\n changefreq: defaultChangefreq,\n priority,\n };\n });\n}\n\n/**\n * Write sitemap and robots.txt to output directory\n */\nexport async function writeSitemap(\n pages: PageBuildResult[],\n config: SSGConfig,\n outDir: string,\n options: SitemapOptions = {}\n): Promise<{ sitemapPath: string; robotsPath: string }> {\n // Generate entries from pages\n const entries = pagesToSitemapEntries(pages, options);\n\n // Add additional URLs if provided\n if (options.additionalUrls) {\n entries.push(...options.additionalUrls);\n }\n\n // Generate sitemap XML\n const sitemapContent = generateSitemap(entries, config);\n const sitemapPath = path.join(outDir, 'sitemap.xml');\n await fs.writeFile(sitemapPath, sitemapContent, 'utf-8');\n\n // Generate robots.txt\n const robotsContent = generateRobotsTxt(config);\n const robotsPath = path.join(outDir, 'robots.txt');\n await fs.writeFile(robotsPath, robotsContent, 'utf-8');\n\n return { sitemapPath, robotsPath };\n}\n\n/**\n * Escape special XML characters\n */\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n","/**\n * SSG Build CLI\n *\n * Static site generation build process:\n * 1. Load configuration\n * 2. Scan routes and expand dynamic paths\n * 3. Build with Vite for production\n * 4. Render each route to static HTML\n * 5. Write output files\n * 6. Generate sitemap and robots.txt\n */\n\nimport path from 'node:path';\nimport fs from 'node:fs/promises';\nimport fsSync from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { pathToFileURL } from 'node:url';\nimport type { SSGConfig, BuildOptions, BuildResult, PageBuildResult, SSGRoute, PageModule } from './types';\nimport { loadConfig, resolveConfigPaths } from './config';\nimport { scanPages, isDynamicRoute, extractParams, expandDynamicRoute } from './routing/index';\nimport { discoverLayouts } from './layouts/index';\nimport { writeSitemap } from './sitemap';\nimport {\n detectCustomEntries,\n generateClientEntry,\n generateServerEntry,\n generateProductionHtmlTemplate,\n VIRTUAL_CLIENT_ID,\n VIRTUAL_SERVER_ID,\n} from './vite/virtual-entries';\n\n/**\n * Build static site\n */\nexport async function build(options: BuildOptions = {}): Promise<BuildResult> {\n const startTime = Date.now();\n const root = process.cwd();\n const warnings: string[] = [];\n const pages: PageBuildResult[] = [];\n\n console.log('\\n🚀 @sigx/ssg - Building static site...\\n');\n\n // Step 1: Load configuration\n console.log('📦 Loading configuration...');\n const config = await loadConfig(options.configPath);\n const resolvedConfig = resolveConfigPaths(config, root);\n\n // Inherit Vite's `base` when the SSG config doesn't set one, so the\n // sitemap and post-build steps share the prefix Vite uses for assets.\n if (!resolvedConfig.base || resolvedConfig.base === '/') {\n try {\n const viteForBase = await import('vite');\n const viteResolved = await viteForBase.resolveConfig({ root }, 'build');\n if (viteResolved.base && viteResolved.base !== '/') {\n resolvedConfig.base = viteResolved.base;\n }\n } catch {\n // Vite config not loadable here — continue with default '/'.\n }\n }\n\n // Step 2: Scan routes\n console.log('🔍 Scanning pages...');\n const routes = await scanPages(resolvedConfig, root);\n console.log(` Found ${routes.length} page(s)`);\n\n // Step 3: Discover layouts\n console.log('📐 Discovering layouts...');\n const layouts = await discoverLayouts(resolvedConfig, root);\n console.log(` Found ${layouts.length} layout(s)`);\n\n // Step 4: Detect entry points\n const entryDetection = detectCustomEntries(root, resolvedConfig);\n if (entryDetection.useVirtualClient || entryDetection.useVirtualServer) {\n console.log('📦 Using zero-config mode');\n if (entryDetection.useVirtualClient) console.log(' → Virtual client entry');\n if (entryDetection.useVirtualServer) console.log(' → Virtual server entry');\n if (entryDetection.useVirtualHtml) console.log(' → Virtual HTML template');\n }\n\n // Get entry points (may create temp files for virtual entries)\n const clientEntry = await getClientEntryPoint(resolvedConfig, root);\n const ssrEntry = await getSSREntryPoint(resolvedConfig, root);\n\n // Always write HTML template for the build (either generated or updated from custom)\n // This ensures Vite processes it and outputs index.html\n const htmlTemplatePath = path.join(root, 'index.html');\n let cleanupHtml = false;\n let originalHtmlContent: string | null = null;\n \n // Save original HTML content if we're modifying a custom one\n if (!entryDetection.useVirtualHtml && fsSync.existsSync(htmlTemplatePath)) {\n originalHtmlContent = fsSync.readFileSync(htmlTemplatePath, 'utf-8');\n }\n \n const htmlContent = await getHtmlTemplate(resolvedConfig, root, clientEntry);\n fsSync.writeFileSync(htmlTemplatePath, htmlContent, 'utf-8');\n cleanupHtml = entryDetection.useVirtualHtml; // Only cleanup (delete) if we generated it\n\n // Step 5: Build with Vite\n console.log('🔨 Building with Vite...');\n const vite = await import('vite');\n\n try {\n // Build client bundle\n // Note: We don't empty the outDir since vite build may have already run\n // Always use HTML as input so Vite outputs index.html properly\n const clientInput = htmlTemplatePath;\n \n await vite.build({\n root,\n mode: 'production',\n build: {\n outDir: resolvedConfig.outDir,\n emptyOutDir: false,\n ssrManifest: true,\n rollupOptions: {\n input: clientInput,\n },\n },\n logLevel: options.verbose ? 'info' : 'warn',\n });\n\n // Build SSR bundle\n const ssrOutDir = path.join(resolvedConfig.outDir!, '.ssg');\n await vite.build({\n root,\n mode: 'production',\n build: {\n outDir: ssrOutDir,\n ssr: true,\n rollupOptions: {\n input: ssrEntry,\n },\n },\n logLevel: options.verbose ? 'info' : 'warn',\n });\n\n // Step 6: Collect all paths to render\n console.log('📝 Collecting paths to render...');\n const pathsToRender = await collectPaths(routes, root, warnings);\n console.log(` ${pathsToRender.length} path(s) to render`);\n\n // Pre-create all output directories to avoid mkdir contention during parallel rendering\n const outputDirs = new Set<string>();\n for (const pathInfo of pathsToRender) {\n const outputPath = getOutputPath(pathInfo.path, resolvedConfig.outDir!);\n outputDirs.add(path.dirname(outputPath));\n }\n await Promise.all(\n Array.from(outputDirs).map(dir => fs.mkdir(dir, { recursive: true }))\n );\n\n // Step 7: Render each path to HTML\n console.log('🎨 Rendering pages...');\n\n // Determine the SSR entry output name (based on input file name)\n const ssrEntryBasename = path.basename(ssrEntry, path.extname(ssrEntry));\n const ssrEntryName = ssrEntryBasename + '.js';\n\n // Pre-load the SSR module once for all pages\n const entryPath = path.join(ssrOutDir, ssrEntryName);\n const entryModule = await import(pathToFileURL(entryPath).href);\n\n // Load HTML template once\n const templatePath = path.join(resolvedConfig.outDir!, 'index.html');\n const template = await fs.readFile(templatePath, 'utf-8');\n\n // Parallel rendering configuration - higher concurrency since rendering is CPU-bound\n const CONCURRENCY = options.concurrency ?? 20; // Number of pages to render in parallel\n const verbose = options.verbose ?? false;\n\n interface RenderResult {\n pathInfo: PathToRender;\n html: string;\n outputPath: string;\n renderTime: number;\n }\n\n // Render a single page (CPU-bound, no I/O)\n async function renderPage(pathInfo: PathToRender): Promise<RenderResult | null> {\n const renderStart = Date.now();\n\n try {\n // Render the app\n const appHtml = await entryModule.render(pathInfo.path, {\n params: pathInfo.params,\n props: pathInfo.props,\n });\n\n // Inject into template\n let html = template.replace('<!--app-html-->', appHtml);\n const headTags = generateHeadTags(pathInfo, resolvedConfig);\n html = html.replace('<!--head-tags-->', headTags);\n\n const outputPath = getOutputPath(pathInfo.path, resolvedConfig.outDir!);\n const renderTime = Date.now() - renderStart;\n\n return { pathInfo, html, outputPath, renderTime };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n console.error(` ❌ ${pathInfo.path}: ${errorMessage}`);\n warnings.push(`Failed to render ${pathInfo.path}: ${errorMessage}`);\n return null;\n }\n }\n\n // Render all pages in parallel batches (CPU-bound, no I/O)\n console.log(' Phase 1: Rendering...');\n const renderPhaseStart = Date.now();\n const renderResults: RenderResult[] = [];\n \n for (let i = 0; i < pathsToRender.length; i += CONCURRENCY) {\n const batch = pathsToRender.slice(i, i + CONCURRENCY);\n const results = await Promise.all(batch.map(renderPage));\n \n for (const result of results) {\n if (result) {\n renderResults.push(result);\n }\n }\n }\n const renderPhaseDuration = Date.now() - renderPhaseStart;\n console.log(` Phase 1 complete: ${renderResults.length} pages in ${renderPhaseDuration}ms (${Math.round(renderPhaseDuration / renderResults.length)}ms avg)`);\n\n // Write all files in parallel with limited concurrency\n console.log(' Phase 2: Writing files...');\n const writePhaseStart = Date.now();\n const WRITE_CONCURRENCY = 10; // Limit parallel writes to reduce I/O contention\n \n for (let i = 0; i < renderResults.length; i += WRITE_CONCURRENCY) {\n const batch = renderResults.slice(i, i + WRITE_CONCURRENCY);\n await Promise.all(batch.map(async (result) => {\n await fs.writeFile(result.outputPath, result.html, 'utf-8');\n const size = Buffer.byteLength(result.html, 'utf-8');\n\n pages.push({\n path: result.pathInfo.path,\n file: result.outputPath,\n time: result.renderTime,\n size,\n });\n \n if (verbose) {\n console.log(` ✓ ${result.pathInfo.path} (${result.renderTime}ms, ${formatBytes(size)})`);\n }\n }));\n }\n const writePhaseDuration = Date.now() - writePhaseStart;\n console.log(` Phase 2 complete: ${renderResults.length} files in ${writePhaseDuration}ms`);\n \n // Summary (non-verbose)\n if (!verbose) {\n console.log(` ✓ Rendered ${renderResults.length} pages`);\n }\n\n // Step 8: Clean up SSR build\n await fs.rm(ssrOutDir, { recursive: true, force: true });\n\n // Step 9: Generate sitemap and robots.txt\n if (pages.length > 0) {\n console.log('🗺️ Generating sitemap...');\n await writeSitemap(pages, resolvedConfig, resolvedConfig.outDir!);\n console.log(' ✓ sitemap.xml');\n console.log(' ✓ robots.txt');\n }\n\n } finally {\n // Clean up temporary entry files\n await cleanupTempEntries(root);\n \n // Clean up or restore HTML template\n if (cleanupHtml) {\n // We generated a virtual HTML, remove it\n try {\n await fs.unlink(htmlTemplatePath);\n } catch {\n // Ignore\n }\n } else if (originalHtmlContent !== null) {\n // We modified a custom HTML, restore the original\n try {\n await fs.writeFile(htmlTemplatePath, originalHtmlContent, 'utf-8');\n } catch {\n // Ignore\n }\n }\n }\n\n // Done\n const totalTime = Date.now() - startTime;\n\n console.log(`\\n✅ Built ${pages.length} page(s) in ${totalTime}ms`);\n\n if (warnings.length > 0) {\n console.log(`\\n⚠️ ${warnings.length} warning(s):`);\n for (const warning of warnings) {\n console.log(` - ${warning}`);\n }\n }\n\n console.log(`\\n📁 Output: ${resolvedConfig.outDir}\\n`);\n\n return {\n pages,\n totalTime,\n warnings,\n };\n}\n\n/**\n * Path information for rendering\n */\ninterface PathToRender {\n path: string;\n route: SSGRoute;\n params: Record<string, string>;\n props?: Record<string, unknown>;\n}\n\n/**\n * Collect all paths to render, expanding dynamic routes\n */\nasync function collectPaths(\n routes: SSGRoute[],\n root: string,\n warnings: string[]\n): Promise<PathToRender[]> {\n const paths: PathToRender[] = [];\n\n for (const route of routes) {\n if (isDynamicRoute(route)) {\n // Load module and call getStaticPaths\n try {\n const moduleUrl = pathToFileURL(route.file).href;\n const pageModule = (await import(moduleUrl)) as PageModule;\n\n if (!pageModule.getStaticPaths) {\n const params = extractParams(route.path).join(', ');\n console.warn(\n `\\n⚠️ SSG102: Dynamic route missing getStaticPaths()\\n` +\n ` 📁 ${route.file}\\n` +\n ` Route: ${route.path} (params: ${params})\\n` +\n ` 💡 Export getStaticPaths() to generate static pages:\\n\\n` +\n ` export async function getStaticPaths() {\\n` +\n ` return [{ params: { ${params.split(', ')[0]}: 'value' } }];\\n` +\n ` }\\n`\n );\n warnings.push(\n `Route ${route.path} has dynamic segments [${params}] but no getStaticPaths() export. Skipping.`\n );\n continue;\n }\n\n const staticPaths = await pageModule.getStaticPaths();\n\n for (const staticPath of staticPaths) {\n const expandedPaths = expandDynamicRoute(route, [staticPath]);\n for (const expandedPath of expandedPaths) {\n paths.push({\n path: expandedPath,\n route,\n params: staticPath.params,\n props: staticPath.props,\n });\n }\n }\n } catch (err) {\n warnings.push(`Failed to load ${route.file}: ${err}`);\n }\n } else {\n paths.push({\n path: route.path,\n route,\n params: {},\n });\n }\n }\n\n return paths;\n}\n\n/**\n * Render a page to HTML\n */\nasync function renderPage(\n pathInfo: PathToRender,\n config: SSGConfig,\n ssrOutDir: string,\n ssrEntryName: string\n): Promise<string> {\n // Load the SSR entry module\n // This should export a render function that returns HTML string\n const entryPath = path.join(ssrOutDir, ssrEntryName);\n const entryModule = await import(pathToFileURL(entryPath).href);\n\n // Render the app - the entry module's render function handles SSR\n const appHtml = await entryModule.render(pathInfo.path, {\n params: pathInfo.params,\n props: pathInfo.props,\n });\n\n // Load HTML template\n const templatePath = path.join(config.outDir!, 'index.html');\n let template = await fs.readFile(templatePath, 'utf-8');\n\n // Inject app HTML\n template = template.replace('<!--app-html-->', appHtml);\n\n // Inject head tags if any\n const headTags = generateHeadTags(pathInfo, config);\n template = template.replace('<!--head-tags-->', headTags);\n\n return template;\n}\n\n/**\n * Generate head tags for a page\n */\nfunction generateHeadTags(pathInfo: PathToRender, config: SSGConfig): string {\n const tags: string[] = [];\n const meta = pathInfo.route.meta || {};\n\n // Title\n const title = meta.title || config.site?.title;\n if (title) {\n tags.push(`<title>${escapeHtml(title)}</title>`);\n }\n\n // Description\n const description = meta.description || config.site?.description;\n if (description) {\n tags.push(`<meta name=\"description\" content=\"${escapeHtml(description)}\">`);\n }\n\n // Canonical URL\n if (config.site?.url) {\n const canonical = new URL(pathInfo.path, config.site.url).href;\n tags.push(`<link rel=\"canonical\" href=\"${canonical}\">`);\n }\n\n return tags.join('\\n ');\n}\n\n/**\n * Get output file path for a URL path.\n *\n * - `/` → `<outDir>/index.html`\n * - `/about` → `<outDir>/about/index.html`\n * - `/foo.html` → `<outDir>/foo.html`\n */\nfunction getOutputPath(urlPath: string, outDir: string): string {\n const normalized = urlPath.replace(/^\\//, '').replace(/\\/$/, '');\n\n if (!normalized) {\n return path.join(outDir, 'index.html');\n }\n\n if (normalized.endsWith('.html')) {\n return path.join(outDir, normalized);\n }\n\n return path.join(outDir, normalized, 'index.html');\n}\n\n/**\n * Get SSR entry point\n * Returns virtual module ID if no custom entry exists\n */\nasync function getSSREntryPoint(config: SSGConfig, root: string): Promise<string> {\n // Check for custom entry points\n const detection = detectCustomEntries(root, config);\n \n if (!detection.useVirtualServer && detection.customServerPath) {\n return detection.customServerPath;\n }\n\n // Use virtual server entry - we need to write a temp file for the build\n // because Rollup input must be a real file path\n const virtualServerCode = generateServerEntry(config);\n const tempServerPath = path.join(root, '.ssg-temp-entry-server.tsx');\n fsSync.writeFileSync(tempServerPath, virtualServerCode, 'utf-8');\n \n return tempServerPath;\n}\n\n/**\n * Get client entry point\n * Returns virtual module ID if no custom entry exists\n */\nasync function getClientEntryPoint(config: SSGConfig, root: string): Promise<string> {\n const detection = detectCustomEntries(root, config);\n \n if (!detection.useVirtualClient && detection.customClientPath) {\n return detection.customClientPath;\n }\n\n // Use virtual client entry - write a temp file\n const virtualClientCode = generateClientEntry(config, detection);\n const tempClientPath = path.join(root, '.ssg-temp-entry-client.tsx');\n fsSync.writeFileSync(tempClientPath, virtualClientCode, 'utf-8');\n \n return tempClientPath;\n}\n\n/**\n * Clean up temporary entry files\n */\nasync function cleanupTempEntries(root: string): Promise<void> {\n const tempFiles = [\n path.join(root, '.ssg-temp-entry-server.tsx'),\n path.join(root, '.ssg-temp-entry-client.tsx'),\n ];\n \n for (const file of tempFiles) {\n try {\n await fs.unlink(file);\n } catch {\n // Ignore if file doesn't exist\n }\n }\n}\n\n/**\n * Get or generate HTML template\n */\nasync function getHtmlTemplate(config: SSGConfig, root: string, clientEntryPath: string): Promise<string> {\n const detection = detectCustomEntries(root, config);\n \n if (!detection.useVirtualHtml && detection.customHtmlPath) {\n // Read custom HTML and update the script src to point to the temp entry file\n let html = await fs.readFile(detection.customHtmlPath, 'utf-8');\n // Replace any virtual SSG client paths with the actual temp entry path\n // Use relative path from root for the script src\n const relativePath = './' + path.relative(root, clientEntryPath).replace(/\\\\/g, '/');\n html = html.replace(\n /<script([^>]*)\\s+src=[\"']?\\/@ssg\\/client\\.tsx[\"']?/g,\n `<script$1 src=\"${relativePath}\"`\n );\n return html;\n }\n\n // Generate virtual HTML template\n return generateProductionHtmlTemplate(config, clientEntryPath);\n}\n\n/**\n * Format bytes to human-readable string\n */\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n}\n\n/**\n * Escape HTML special characters\n */\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n"],"mappings":"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"}
|
package/dist/build.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,OAAO,KAAK,EAAa,YAAY,EAAE,WAAW,EAAyC,MAAM,SAAS,CAAC;AAc3G;;GAEG;AACH,wBAAsB,KAAK,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,CAAC,
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,OAAO,KAAK,EAAa,YAAY,EAAE,WAAW,EAAyC,MAAM,SAAS,CAAC;AAc3G;;GAEG;AACH,wBAAsB,KAAK,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,CAAC,CAkR5E"}
|
package/dist/build.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as e } from "./build-
|
|
1
|
+
import { t as e } from "./build-C9YKGjXk.js";
|
|
2
2
|
export { e as build };
|
package/dist/dev.js
CHANGED
package/dist/errors.d.ts
CHANGED
|
@@ -28,17 +28,17 @@ export declare class SSGError extends Error {
|
|
|
28
28
|
* Error codes for categorization
|
|
29
29
|
*/
|
|
30
30
|
export declare const ErrorCodes: {
|
|
31
|
-
readonly CONFIG_NOT_FOUND:
|
|
32
|
-
readonly CONFIG_INVALID:
|
|
33
|
-
readonly CONFIG_THEME_NOT_FOUND:
|
|
34
|
-
readonly PAGE_NOT_FOUND:
|
|
35
|
-
readonly PAGE_INVALID_EXPORT:
|
|
36
|
-
readonly DYNAMIC_ROUTE_NO_PATHS:
|
|
37
|
-
readonly LAYOUT_NOT_FOUND:
|
|
38
|
-
readonly BUILD_RENDER_FAILED:
|
|
39
|
-
readonly BUILD_VITE_FAILED:
|
|
40
|
-
readonly MDX_PARSE_ERROR:
|
|
41
|
-
readonly MDX_FRONTMATTER_ERROR:
|
|
31
|
+
readonly CONFIG_NOT_FOUND: 'SSG001';
|
|
32
|
+
readonly CONFIG_INVALID: 'SSG002';
|
|
33
|
+
readonly CONFIG_THEME_NOT_FOUND: 'SSG003';
|
|
34
|
+
readonly PAGE_NOT_FOUND: 'SSG100';
|
|
35
|
+
readonly PAGE_INVALID_EXPORT: 'SSG101';
|
|
36
|
+
readonly DYNAMIC_ROUTE_NO_PATHS: 'SSG102';
|
|
37
|
+
readonly LAYOUT_NOT_FOUND: 'SSG103';
|
|
38
|
+
readonly BUILD_RENDER_FAILED: 'SSG300';
|
|
39
|
+
readonly BUILD_VITE_FAILED: 'SSG301';
|
|
40
|
+
readonly MDX_PARSE_ERROR: 'SSG400';
|
|
41
|
+
readonly MDX_FRONTMATTER_ERROR: 'SSG401';
|
|
42
42
|
};
|
|
43
43
|
export declare function configNotFoundError(searchedPaths: string[]): SSGError;
|
|
44
44
|
export declare function themeNotFoundError(themeName: string): SSGError;
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;GAEG;AACH,qBAAa,QAAS,SAAQ,KAAK;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAE7B,YACI,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,KAAK,CAAC,EAAE,KAAK,CAAC;KACjB,EASJ;IAED;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAuB5B;CACJ;AAED;;GAEG;AACH,eAAO,MAAM,UAAU
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;GAEG;AACH,qBAAa,QAAS,SAAQ,KAAK;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAE7B,YACI,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,KAAK,CAAC,EAAE,KAAK,CAAC;KACjB,EASJ;IAED;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAuB5B;CACJ;AAED;;GAEG;AACH,eAAO,MAAM,UAAU;aAEnB,gBAAgB,EAAE,QAAQ;aAC1B,cAAc,EAAE,QAAQ;aACxB,sBAAsB,EAAE,QAAQ;aAGhC,cAAc,EAAE,QAAQ;aACxB,mBAAmB,EAAE,QAAQ;aAC7B,sBAAsB,EAAE,QAAQ;aAChC,gBAAgB,EAAE,QAAQ;aAG1B,mBAAmB,EAAE,QAAQ;aAC7B,iBAAiB,EAAE,QAAQ;aAG3B,eAAe,EAAE,QAAQ;aACzB,qBAAqB,EAAE,QAAQ;CACzB,CAAC;AAMX,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,QAAQ,CAUrE;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,CAQ9D;AAMD,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,GAAG,QAAQ,CAa9G;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,QAAQ,CAYjF;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAY5D;AAMD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,QAAQ,CAYrE;AAMD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,CAcjF;AAMD;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAgBhE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAC/B,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,IAAI,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,CAAC,CAAC,CAIZ"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { O as e } from "./virtual-entries-CH-0HuqJ.js";
|
|
2
|
-
import { a as t, i as n, n as r, r as i, t as a } from "./build-
|
|
2
|
+
import { a as t, i as n, n as r, r as i, t as a } from "./build-C9YKGjXk.js";
|
|
3
3
|
import { dev as o, preview as s } from "./dev.js";
|
|
4
4
|
import c from "node:path";
|
|
5
5
|
//#region src/errors.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rehype-headings.d.ts","sourceRoot":"","sources":["../../src/mdx/rehype-headings.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH;;GAEG;AACH,MAAM,WAAW,qBAAqB;IAClC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,qBAA0B,
|
|
1
|
+
{"version":3,"file":"rehype-headings.d.ts","sourceRoot":"","sources":["../../src/mdx/rehype-headings.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH;;GAEG;AACH,MAAM,WAAW,qBAAqB;IAClC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,qBAA0B,UAGvD,GAAG,QAAQ,GAAG,UA4B/B;eAEc,qBAAqB"}
|
package/dist/mdx/shiki.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shiki.d.ts","sourceRoot":"","sources":["../../src/mdx/shiki.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAqB,KAAK,WAAW,EAA2C,MAAM,OAAO,CAAC;AACrG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAgB5C;;GAEG;AACH,wBAAsB,cAAc,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAW/E;AAED;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;AAErD;;GAEG;AACH,wBAAsB,aAAa,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,WAAW,EACpB,IAAI,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAA;CAAE,GAC/D,OAAO,CAAC,MAAM,CAAC,CA6GjB;AAsDD;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"shiki.d.ts","sourceRoot":"","sources":["../../src/mdx/shiki.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAqB,KAAK,WAAW,EAA2C,MAAM,OAAO,CAAC;AACrG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAgB5C;;GAEG;AACH,wBAAsB,cAAc,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAW/E;AAED;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;AAErD;;GAEG;AACH,wBAAsB,aAAa,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,WAAW,EACpB,IAAI,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAA;CAAE,GAC/D,OAAO,CAAC,MAAM,CAAC,CA6GjB;AAsDD;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,WAAW,UACxB,GAAG,mBA+E1B;AA8BD;;GAEG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAW9D"}
|
|
@@ -329,10 +329,12 @@ function B(t = {}) {
|
|
|
329
329
|
name: "sigx-ssg",
|
|
330
330
|
enforce: "pre",
|
|
331
331
|
async configResolved(e) {
|
|
332
|
-
x = e, C = e.root
|
|
332
|
+
x = e, C = e.root;
|
|
333
|
+
let r = {
|
|
333
334
|
...await l(t.configPath),
|
|
334
335
|
...t
|
|
335
|
-
}
|
|
336
|
+
};
|
|
337
|
+
(r.base == null || r.base === "/") && e.base && e.base !== "/" && (r.base = e.base), S = n(r), w = f(C, S), (w.useVirtualClient || w.useVirtualServer) && (console.log("📦 @sigx/ssg: Using zero-config mode"), w.useVirtualClient && console.log(" → Virtual client entry"), w.useVirtualServer && console.log(" → Virtual server entry"), w.globalCssPath && console.log(` → Auto-importing ${w.globalCssPath}`));
|
|
336
338
|
},
|
|
337
339
|
configureServer(t) {
|
|
338
340
|
let n = y.resolve(C, S.pages || "src/pages"), r = y.resolve(C, S.layouts || "src/layouts");
|
|
@@ -443,4 +445,4 @@ function B(t = {}) {
|
|
|
443
445
|
//#endregion
|
|
444
446
|
export { B as t };
|
|
445
447
|
|
|
446
|
-
//# sourceMappingURL=plugin-
|
|
448
|
+
//# sourceMappingURL=plugin-aT6WUDzk.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-EIAzPLvE.js","names":[],"sources":["../src/mdx/shiki.ts","../src/mdx/rehype-headings.ts","../src/mdx/plugin.ts","../src/vite/plugin.ts"],"sourcesContent":["/**\n * Shiki syntax highlighting integration\n *\n * Provides code block highlighting for Markdown/MDX content.\n */\n\nimport { createHighlighter, type Highlighter, type BundledLanguage, type BundledTheme } from 'shiki';\nimport type { ShikiConfig } from '../types';\n\n/**\n * Cached highlighter instance\n */\nlet highlighterPromise: Promise<Highlighter> | null = null;\n\n/**\n * Default Shiki configuration\n */\nconst DEFAULT_CONFIG: Required<ShikiConfig> = {\n light: 'github-light',\n dark: 'github-dark',\n langs: ['javascript', 'typescript', 'jsx', 'tsx', 'json', 'css', 'html', 'markdown', 'bash', 'shell'],\n};\n\n/**\n * Initialize or get the Shiki highlighter\n */\nexport async function getHighlighter(config?: ShikiConfig): Promise<Highlighter> {\n if (!highlighterPromise) {\n const mergedConfig = { ...DEFAULT_CONFIG, ...config };\n\n highlighterPromise = createHighlighter({\n themes: [mergedConfig.light as BundledTheme, mergedConfig.dark as BundledTheme],\n langs: mergedConfig.langs as BundledLanguage[],\n });\n }\n\n return highlighterPromise;\n}\n\n/**\n * Tab types for preview blocks\n */\nexport type TabType = 'preview' | 'code' | 'console';\n\n/**\n * Highlight code with Shiki\n */\nexport async function highlightCode(\n code: string,\n lang: string,\n config?: ShikiConfig,\n meta?: { filename?: string; live?: boolean; tabs?: TabType[] }\n): Promise<string> {\n const highlighter = await getHighlighter(config);\n const mergedConfig = { ...DEFAULT_CONFIG, ...config };\n\n // Check if language is loaded\n const loadedLangs = highlighter.getLoadedLanguages();\n const effectiveLang = loadedLangs.includes(lang as BundledLanguage) ? lang : 'text';\n\n // Generate HTML with both themes for CSS-based switching\n const codeHtml = highlighter.codeToHtml(code, {\n lang: effectiveLang as BundledLanguage,\n themes: {\n light: mergedConfig.light as BundledTheme,\n dark: mergedConfig.dark as BundledTheme,\n },\n });\n\n // Wrap in code-window structure\n const filename = meta?.filename ?? '';\n const isLive = meta?.live ?? false;\n const tabs = meta?.tabs; // undefined means no tabbed view, just code\n const hasTabs = tabs && tabs.length > 0;\n \n const filenameHtml = filename \n ? `<span class=\"code-window-filename\">${escapeHtml(filename)}</span>`\n : `<span class=\"code-window-lang\">${getLanguageLabel(effectiveLang)}</span>`;\n\n // For preview blocks (has tabs), render a LivePreview island component\n if (hasTabs) {\n const base64Code = encodeBase64(code);\n \n // Generate tab buttons HTML based on tabs array\n const firstTab = tabs[0];\n const tabButtonsHtml = tabs.map((tab, i) => {\n const label = tab.charAt(0).toUpperCase() + tab.slice(1);\n const isActive = i === 0;\n return `<button class=\"code-window-tab${isActive ? ' code-window-tab-active' : ''}\">${label}</button>`;\n }).join('\\n ');\n \n // Return an island marker that will be hydrated on the client\n // Use code-window styling for consistency with the nice terminal look\n const html = `<div \n class=\"live-preview-island\" \n data-island=\"LivePreview\" \n data-island-strategy=\"visible\"\n data-island-props=\"${escapeHtml(JSON.stringify({\n code: base64Code,\n highlightedCode: codeHtml,\n language: effectiveLang,\n filename: filename,\n tabs: tabs,\n live: isLive\n }))}\"\n>\n <div class=\"code-window code-window-live code-window-preview\">\n <div class=\"code-window-header\">\n <div class=\"code-window-header-left\">\n <div class=\"code-window-dots\">\n <span class=\"code-window-dot dot-red\"></span>\n <span class=\"code-window-dot dot-yellow\"></span>\n <span class=\"code-window-dot dot-green\"></span>\n </div>\n ${filenameHtml}\n </div>\n <div class=\"code-window-tabs\">\n ${tabButtonsHtml}\n </div>\n <button class=\"code-window-try-live\" disabled>⚡ Try Live</button>\n </div>\n <div class=\"code-window-preview-pane\"${firstTab !== 'preview' ? ' style=\"display:none;\"' : ''}>\n <div class=\"code-window-preview-loading\">\n <span class=\"code-window-spinner\"></span>\n Loading preview...\n </div>\n </div>\n <div class=\"code-window-console-pane\"${firstTab !== 'console' ? ' style=\"display:none;\"' : ''}>\n <div class=\"code-window-console-empty\">No console output</div>\n </div>\n <div class=\"code-window-content\"${firstTab !== 'code' ? ' style=\"display:none;\"' : ''}>\n ${codeHtml}\n </div>\n </div>\n</div>`;\n return html;\n }\n\n // Add \"Try Live\" button for live code blocks\n const tryLiveButton = isLive \n ? `<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>`\n : '';\n\n const html = `<div class=\"code-window${isLive ? ' code-window-live' : ''}\">\n <div class=\"code-window-header\">\n <div class=\"code-window-header-left\">\n <div class=\"code-window-dots\">\n <span class=\"code-window-dot dot-red\"></span>\n <span class=\"code-window-dot dot-yellow\"></span>\n <span class=\"code-window-dot dot-green\"></span>\n </div>\n ${filenameHtml}\n </div>\n ${tryLiveButton}\n </div>\n <div class=\"code-window-content\">\n ${codeHtml}\n </div>\n </div>`;\n\n return html;\n}\n\n/**\n * Encode string to base64 (works in both Node.js and browser)\n */\nfunction encodeBase64(str: string): string {\n // Use Buffer in Node.js\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(str, 'utf-8').toString('base64');\n }\n // Fallback for browser (shouldn't be needed during SSG build)\n return btoa(unescape(encodeURIComponent(str)));\n}\n\n/**\n * Get a display label for a language\n */\nfunction getLanguageLabel(lang: string): string {\n const labels: Record<string, string> = {\n 'tsx': 'TSX',\n 'jsx': 'JSX',\n 'ts': 'TypeScript',\n 'typescript': 'TypeScript',\n 'js': 'JavaScript',\n 'javascript': 'JavaScript',\n 'css': 'CSS',\n 'html': 'HTML',\n 'json': 'JSON',\n 'bash': 'Terminal',\n 'shell': 'Terminal',\n 'sh': 'Terminal',\n 'md': 'Markdown',\n 'markdown': 'Markdown',\n 'python': 'Python',\n 'py': 'Python',\n 'rust': 'Rust',\n 'go': 'Go',\n 'text': '',\n };\n return labels[lang.toLowerCase()] ?? lang.toUpperCase();\n}\n\n/**\n * Escape HTML special characters\n */\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\n/**\n * Create a rehype plugin for Shiki\n */\nexport function rehypeShiki(config?: ShikiConfig) {\n return async (tree: any) => {\n const { visit } = await import('unist-util-visit');\n const { fromHtml } = await import('hast-util-from-html');\n\n const nodesToProcess: Array<{ node: any; parent: any; index: number }> = [];\n\n visit(tree, 'element', (node: any, index: number | undefined, parent: any) => {\n // Look for <pre><code> elements\n if (\n node.tagName === 'pre' &&\n node.children?.[0]?.tagName === 'code'\n ) {\n nodesToProcess.push({ node, parent, index: index ?? 0 });\n }\n });\n\n // Process nodes in parallel\n await Promise.all(\n nodesToProcess.map(async ({ node, parent, index }) => {\n const codeNode = node.children[0];\n\n // Extract language from class (e.g., \"language-typescript\")\n const className = codeNode.properties?.className?.[0] || '';\n const lang = className.replace(/^language-/, '') || 'text';\n\n // Extract filename from meta string if present\n // Supports: ```tsx filename=\"Counter.tsx\" or ```tsx title=\"Counter.tsx\"\n const metaString = codeNode.data?.meta || codeNode.properties?.metastring || '';\n const filename = extractMeta(metaString, 'filename') || extractMeta(metaString, 'title') || '';\n \n // Check for \"live\" flag in meta string (e.g., ```tsx live)\n const isLive = /\\blive\\b/i.test(metaString);\n \n // Parse tabs from meta string - order determines tab order\n // Supported keywords: preview, code, console\n // Example: ```tsx code preview console live -> tabs: [code, preview, console], with live button\n const tabKeywords = ['preview', 'code', 'console'] as const;\n const tabs: TabType[] = [];\n \n // Find all tab keywords and their positions\n const tabPositions: Array<{ tab: TabType; index: number }> = [];\n for (const keyword of tabKeywords) {\n const regex = new RegExp(`\\\\b${keyword}\\\\b`, 'i');\n const match = metaString.match(regex);\n if (match && match.index !== undefined) {\n tabPositions.push({ tab: keyword, index: match.index });\n }\n }\n \n // Sort by position (order of appearance in meta string)\n tabPositions.sort((a, b) => a.index - b.index);\n \n // Extract just the tab names in order\n for (const { tab } of tabPositions) {\n tabs.push(tab);\n }\n\n // Get raw code content\n const code = getTextContent(codeNode);\n\n // Highlight with Shiki (with meta info)\n // Only pass tabs if at least one tab keyword was found\n const html = await highlightCode(code.trim(), lang, config, { \n filename, \n live: isLive, \n tabs: tabs.length > 0 ? tabs : undefined \n });\n\n // Parse the HTML string into HAST nodes\n const fragment = fromHtml(html, { fragment: true });\n \n // Replace node with the parsed HAST fragment's children\n if (parent && typeof index === 'number' && fragment.children.length > 0) {\n // Use the first element from the fragment (should be the code-window div)\n parent.children[index] = fragment.children[0];\n }\n })\n );\n };\n}\n\n/**\n * Extract a meta value from a meta string\n * Supports: filename=\"value\" or filename='value' or filename=value\n */\nfunction extractMeta(metaString: string, key: string): string | null {\n if (!metaString) return null;\n \n // Match: key=\"value\" or key='value' or key=value\n const regex = new RegExp(`${key}=[\"']?([^\"'\\\\s]+)[\"']?`, 'i');\n const match = metaString.match(regex);\n return match ? match[1] : null;\n}\n\n/**\n * Extract text content from an AST node\n */\nfunction getTextContent(node: any): string {\n if (node.type === 'text') {\n return node.value;\n }\n\n if (node.children) {\n return node.children.map(getTextContent).join('');\n }\n\n return '';\n}\n\n/**\n * Load additional language on demand\n */\nexport async function loadLanguage(lang: string): Promise<void> {\n const highlighter = await getHighlighter();\n const loadedLangs = highlighter.getLoadedLanguages();\n\n if (!loadedLangs.includes(lang as BundledLanguage)) {\n try {\n await highlighter.loadLanguage(lang as BundledLanguage);\n } catch {\n console.warn(`Shiki: Language \"${lang}\" not available, using plain text`);\n }\n }\n}\n","/**\n * Rehype plugin to extract headings from MDX/MD content\n *\n * Extracts headings (h2-h6 by default) with their IDs and text content\n * for use in table of contents generation.\n */\n\nimport { visit } from 'unist-util-visit';\nimport { toString } from 'hast-util-to-string';\nimport type { TocHeading } from '../types';\n\n/**\n * Options for the rehype headings plugin\n */\nexport interface RehypeHeadingsOptions {\n /**\n * Minimum heading level to include (1-6)\n * @default 2\n */\n minLevel?: number;\n\n /**\n * Maximum heading level to include (1-6)\n * @default 3\n */\n maxLevel?: number;\n}\n\n/**\n * Rehype plugin to extract headings from the document\n *\n * Stores extracted headings in `file.data.headings` for later use.\n *\n * @example\n * ```ts\n * import { rehypeExtractHeadings } from './rehype-headings';\n *\n * // Use with unified\n * unified()\n * .use(rehypeSlug) // First add IDs to headings\n * .use(rehypeExtractHeadings, { minLevel: 2, maxLevel: 3 })\n * ```\n */\nexport function rehypeExtractHeadings(options: RehypeHeadingsOptions = {}) {\n const { minLevel = 2, maxLevel = 3 } = options;\n\n return (tree: any, file: any) => {\n const headings: TocHeading[] = [];\n\n visit(tree, 'element', (node: any) => {\n // Check if this is a heading element (h1-h6)\n const match = /^h([1-6])$/.exec(node.tagName);\n if (!match) return;\n\n const level = parseInt(match[1], 10);\n\n // Skip headings outside configured range\n if (level < minLevel || level > maxLevel) return;\n\n // Get the heading ID (should be set by rehype-slug)\n const id = node.properties?.id;\n if (!id) return;\n\n // Extract text content from the heading\n const text = toString(node).trim();\n if (!text) return;\n\n headings.push({ id, text, level });\n });\n\n // Store headings in file data for later access\n file.data = file.data || {};\n file.data.headings = headings;\n };\n}\n\nexport default rehypeExtractHeadings;\n","/**\n * MDX Vite plugin\n *\n * Transforms MDX files into SignalX components with:\n * - Frontmatter extraction\n * - Shiki syntax highlighting\n * - SignalX JSX runtime integration\n */\n\nimport type { Plugin, ResolvedConfig } from 'vite';\nimport type { SSGConfig, MarkdownConfig, PageMeta, TocHeading } from '../types';\nimport { parseFrontmatter, extractTitleFromContent } from './frontmatter';\nimport { rehypeShiki } from './shiki';\nimport { rehypeExtractHeadings } from './rehype-headings';\n\n/**\n * MDX plugin options\n */\nexport interface MDXPluginOptions {\n /**\n * Markdown configuration\n */\n markdown?: MarkdownConfig;\n\n /**\n * SSG configuration (for layout resolution)\n */\n ssgConfig?: SSGConfig;\n}\n\n/**\n * Create the MDX Vite plugin\n */\nexport function mdxPlugin(options: MDXPluginOptions = {}): Plugin {\n const { markdown = {} } = options;\n\n let mdxRollup: any;\n let viteConfig: ResolvedConfig;\n\n return {\n name: 'sigx-ssg-mdx',\n enforce: 'pre',\n\n async configResolved(config) {\n viteConfig = config;\n // Dynamically import @mdx-js/rollup\n const mdxModule = await import('@mdx-js/rollup');\n const remarkFrontmatter = (await import('remark-frontmatter')).default;\n const remarkMdxFrontmatter = (await import('remark-mdx-frontmatter')).default;\n const remarkGfm = (await import('remark-gfm')).default;\n const rehypeSlug = (await import('rehype-slug')).default;\n const rehypeAutolinkHeadings = (await import('rehype-autolink-headings')).default;\n\n // Get TOC config from SSG config\n const tocConfig = options.ssgConfig?.toc || { minLevel: 2, maxLevel: 3 };\n\n // Build rehype plugins array\n const rehypePlugins: any[] = [];\n\n // Add rehype-slug first to generate heading IDs\n rehypePlugins.push(rehypeSlug);\n\n // Add autolink headings (clickable anchor links)\n rehypePlugins.push([rehypeAutolinkHeadings, {\n behavior: 'append',\n properties: {\n class: 'heading-anchor',\n ariaHidden: true,\n tabIndex: -1,\n },\n content: {\n type: 'element',\n tagName: 'span',\n properties: { class: 'heading-anchor-icon' },\n children: [{ type: 'text', value: '#' }],\n },\n }]);\n\n // Add heading extraction for TOC\n rehypePlugins.push([rehypeExtractHeadings, tocConfig]);\n\n // Add Shiki if enabled\n if (markdown.shiki !== false) {\n const shikiConfig = typeof markdown.shiki === 'object' ? markdown.shiki : undefined;\n rehypePlugins.push([rehypeShiki, shikiConfig]);\n }\n\n // Add custom rehype plugins\n if (markdown.rehypePlugins) {\n rehypePlugins.push(...markdown.rehypePlugins);\n }\n\n // Build remark plugins array\n const remarkPlugins: any[] = [\n remarkFrontmatter,\n [remarkMdxFrontmatter, { name: 'frontmatter' }],\n remarkGfm,\n ];\n\n // Add custom remark plugins\n if (markdown.remarkPlugins) {\n remarkPlugins.push(...markdown.remarkPlugins);\n }\n\n // Create MDX plugin\n // Use jsx: false to output function calls instead of JSX syntax\n // This avoids needing esbuild to process .mdx files as JSX\n mdxRollup = mdxModule.default({\n jsx: false,\n jsxImportSource: 'sigx',\n remarkPlugins,\n rehypePlugins,\n providerImportSource: undefined,\n });\n },\n\n async transform(code, id) {\n // Only process MDX and MD files\n if (!/\\.mdx?$/.test(id)) {\n return null;\n }\n\n // Parse frontmatter first\n const { data: frontmatter, content } = parseFrontmatter(code);\n\n // Extract title from content if not in frontmatter\n if (!frontmatter.title) {\n const extractedTitle = extractTitleFromContent(content);\n if (extractedTitle) {\n frontmatter.title = extractedTitle;\n }\n }\n\n // Transform MDX content\n if (!mdxRollup?.transform) {\n throw new Error('MDX plugin not initialized');\n }\n\n const result = await mdxRollup.transform(code, id);\n\n if (!result) {\n return null;\n }\n\n // Extract headings from the file data (set by rehype-extract-headings)\n // Note: We need to process the content to get headings\n const headings = await extractHeadingsFromContent(content, options);\n\n // Create module ID for HMR tracking (normalize path separators)\n const moduleId = id.replace(/\\\\/g, '/');\n\n // Inject frontmatter export and wrap in SignalX component\n const transformedCode = wrapMDXComponent(\n result.code,\n frontmatter,\n headings,\n moduleId,\n viteConfig.command === 'serve'\n );\n\n return {\n code: transformedCode,\n map: result.map,\n };\n },\n };\n}\n\n/**\n * Wrap MDX output in a SignalX component\n * \n * Note: remark-mdx-frontmatter already exports `frontmatter`, so we only add the layout export\n * \n * In dev mode, we:\n * 1. Wrap MDXContent in a sigx component() for proper HMR tracking\n * 2. Inject HMR registration code so the component updates live\n */\nfunction wrapMDXComponent(\n code: string,\n frontmatter: PageMeta,\n headings: TocHeading[],\n moduleId: string,\n isDev: boolean\n): string {\n // The MDX output already includes frontmatter export from remark-mdx-frontmatter\n // We need to add the layout export for the SSG routing system\n \n // In dev mode, wrap in sigx component for HMR support\n if (isDev) {\n // Generate a component name from the file path for debugging\n const fileName = moduleId.split('/').pop()?.replace(/\\.mdx?$/, '') || 'MDXPage';\n const componentName = fileName.charAt(0).toUpperCase() + fileName.slice(1).replace(/[^a-zA-Z0-9]/g, '') + 'Page';\n \n // The MDX output has \"export default function MDXContent(...)\" \n // We need to:\n // 1. Remove the \"export default\" from MDXContent to avoid duplicate exports\n // 2. Wrap it in a sigx component for HMR\n // 3. Export the wrapped component as default\n const modifiedCode = code\n .replace(/export\\s+default\\s+function\\s+MDXContent/g, 'function _MDXContent')\n .replace(/export\\s+{\\s*MDXContent\\s+as\\s+default\\s*}/g, '');\n \n return `\nimport { registerHMRModule } from '@sigx/vite/hmr';\nimport { component as __component } from 'sigx';\nregisterHMRModule('${moduleId}');\n\n${modifiedCode}\n\n// Export layout from frontmatter for SSG routing\nexport const layout = ${frontmatter.layout ? JSON.stringify(frontmatter.layout) : 'undefined'};\n\n// Export headings for table of contents\nexport const headings = ${JSON.stringify(headings)};\n\n// Wrap MDXContent in a sigx component for HMR support\nconst MDXPage = __component(() => {\n return () => _MDXContent({});\n}, { name: '${componentName}' });\n\nexport default MDXPage;\n\nif (import.meta.hot) {\n // Accept HMR updates with a callback to trigger re-render after module is updated\n import.meta.hot.accept((newModule) => {\n if (newModule) {\n // Notify LayoutRouter to clear its cache and re-render with new module\n window.dispatchEvent(new CustomEvent('sigx:mdx-hmr', { \n detail: { moduleId: '${moduleId}', newModule } \n }));\n }\n });\n}\n`;\n }\n \n // Production: just add exports without HMR wrapper\n return `\n${code}\n\n// Export layout from frontmatter for SSG routing\nexport const layout = ${frontmatter.layout ? JSON.stringify(frontmatter.layout) : 'undefined'};\n\n// Export headings for table of contents\nexport const headings = ${JSON.stringify(headings)};\n`;\n}\n\n/**\n * Extract headings from markdown/MDX content\n */\nasync function extractHeadingsFromContent(\n content: string,\n options: MDXPluginOptions\n): Promise<TocHeading[]> {\n const { unified } = await import('unified');\n const remarkParse = (await import('remark-parse')).default;\n const remarkRehype = (await import('remark-rehype')).default;\n const rehypeSlug = (await import('rehype-slug')).default;\n const rehypeStringify = (await import('rehype-stringify')).default;\n\n // Get TOC config\n const tocConfig = options.ssgConfig?.toc || { minLevel: 2, maxLevel: 3 };\n\n // Process markdown to extract headings\n // Note: rehype-stringify is required for unified to have a compiler\n const processor = unified()\n .use(remarkParse)\n .use(remarkRehype)\n .use(rehypeSlug)\n .use(rehypeExtractHeadings, tocConfig)\n .use(rehypeStringify);\n\n const file = await processor.process(content);\n return (file.data as any).headings || [];\n}\n\n/**\n * Create a simple markdown-only plugin (no MDX features)\n */\nexport function markdownPlugin(options: MDXPluginOptions = {}): Plugin {\n return {\n name: 'sigx-ssg-markdown',\n enforce: 'pre',\n\n async transform(code, id) {\n // Only process .md files (not .mdx)\n if (!/\\.md$/.test(id) || /\\.mdx$/.test(id)) {\n return null;\n }\n\n // Parse frontmatter\n const { data: frontmatter, content } = parseFrontmatter(code);\n\n // Extract title if not in frontmatter\n if (!frontmatter.title) {\n const extractedTitle = extractTitleFromContent(content);\n if (extractedTitle) {\n frontmatter.title = extractedTitle;\n }\n }\n\n // Convert markdown to simple HTML using a lightweight parser\n // For full MD support, the MDX plugin should be used\n const html = await simpleMarkdownToHtml(content, options);\n \n // Extract headings for TOC\n const headings = await extractHeadingsFromContent(content, options);\n \n const frontmatterJSON = JSON.stringify(frontmatter);\n const headingsJSON = JSON.stringify(headings);\n\n return {\n code: `\nimport { jsx as _jsx } from 'sigx/jsx-runtime';\n\nexport const frontmatter = ${frontmatterJSON};\nexport const layout = ${frontmatter.layout ? JSON.stringify(frontmatter.layout) : 'undefined'};\nexport const headings = ${headingsJSON};\n\nexport default function MDContent(props) {\n return _jsx('div', {\n class: 'markdown-content',\n dangerouslySetInnerHTML: { __html: ${JSON.stringify(html)} }\n });\n}\n`,\n map: null,\n };\n },\n };\n}\n\n/**\n * Simple markdown to HTML conversion\n * For basic markdown without custom components\n */\nasync function simpleMarkdownToHtml(markdown: string, options: MDXPluginOptions = {}): Promise<string> {\n // Use unified for basic markdown processing\n const { unified } = await import('unified');\n const remarkParse = (await import('remark-parse')).default;\n const remarkRehype = (await import('remark-rehype')).default;\n const rehypeSlug = (await import('rehype-slug')).default;\n const rehypeStringify = (await import('rehype-stringify')).default;\n\n const result = await unified()\n .use(remarkParse)\n .use(remarkRehype)\n .use(rehypeSlug) // Add IDs to headings\n .use(rehypeStringify)\n .process(markdown);\n\n return String(result);\n}\n","/**\n * SSG Vite Plugin\n *\n * Main Vite plugin for @sigx/ssg that integrates:\n * - File-based routing with virtual modules\n * - Layout system\n * - MDX processing\n * - HMR for development\n * - Zero-config entry point generation\n */\n\nimport type { Plugin, ResolvedConfig, ViteDevServer } from 'vite';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport type { SSGConfig } from '../types';\nimport { defineSSGConfig, loadConfig } from '../config';\nimport { scanPages } from '../routing/scanner';\nimport { generateRoutesModule, generateLazyRoutesModule, VIRTUAL_ROUTES_ID, RESOLVED_VIRTUAL_ROUTES_ID } from '../routing/virtual';\nimport { generateNavigationModule, VIRTUAL_NAVIGATION_ID, RESOLVED_VIRTUAL_NAVIGATION_ID } from '../routing/virtual-navigation';\nimport { discoverLayouts } from '../layouts/resolver';\nimport { generateLayoutsModule, VIRTUAL_LAYOUTS_ID, RESOLVED_VIRTUAL_LAYOUTS_ID } from '../layouts/virtual';\nimport { mdxPlugin } from '../mdx/plugin';\nimport { parseFrontmatter } from '../mdx/frontmatter';\nimport {\n detectCustomEntries,\n generateClientEntry,\n generateServerEntry,\n generateHtmlTemplate,\n VIRTUAL_CLIENT_ID,\n RESOLVED_VIRTUAL_CLIENT_ID,\n VIRTUAL_SERVER_ID,\n RESOLVED_VIRTUAL_SERVER_ID,\n SSG_CLIENT_ENTRY_PATH,\n type EntryDetectionResult,\n} from './virtual-entries';\n\n/**\n * SSG Plugin options\n */\nexport interface SSGPluginOptions extends Partial<SSGConfig> {\n /**\n * Path to ssg.config.ts\n */\n configPath?: string;\n \n /**\n * Enable built-in MDX processing. Set to false if using external MDX plugin.\n * @default true\n */\n enableMdx?: boolean;\n}\n\n/**\n * Virtual module for SSG config\n */\nconst VIRTUAL_CONFIG_ID = 'virtual:ssg-config';\nconst RESOLVED_VIRTUAL_CONFIG_ID = '\\0' + VIRTUAL_CONFIG_ID;\n\n/**\n * Create the SSG Vite plugin\n */\nexport function ssgPlugin(options: SSGPluginOptions = {}): Plugin[] {\n let config: ResolvedConfig;\n let ssgConfig: SSGConfig;\n let root: string;\n let server: ViteDevServer | undefined;\n let entryDetection: EntryDetectionResult;\n\n // Cache for virtual modules\n let routesCache: { routes: any[]; code: string } | null = null;\n let layoutsCache: { layouts: any[]; code: string } | null = null;\n let navigationCache: { code: string } | null = null;\n \n // Cache for frontmatter hashes to detect changes\n const frontmatterHashCache = new Map<string, string>();\n\n const mainPlugin: Plugin = {\n name: 'sigx-ssg',\n enforce: 'pre',\n\n async configResolved(resolvedConfig) {\n config = resolvedConfig;\n root = resolvedConfig.root;\n\n // Load SSG config - always try to load from file first\n const fileConfig = await loadConfig(options.configPath);\n \n // Merge file config with plugin options (plugin options take precedence)\n ssgConfig = defineSSGConfig({\n ...fileConfig,\n ...options,\n });\n\n // Detect custom entry points\n entryDetection = detectCustomEntries(root, ssgConfig);\n\n // Log zero-config mode status\n if (entryDetection.useVirtualClient || entryDetection.useVirtualServer) {\n console.log('📦 @sigx/ssg: Using zero-config mode');\n if (entryDetection.useVirtualClient) {\n console.log(' → Virtual client entry');\n }\n if (entryDetection.useVirtualServer) {\n console.log(' → Virtual server entry');\n }\n if (entryDetection.globalCssPath) {\n console.log(` → Auto-importing ${entryDetection.globalCssPath}`);\n }\n }\n },\n\n configureServer(devServer) {\n server = devServer;\n\n // Watch pages and layouts directories for changes\n const pagesDir = path.resolve(root, ssgConfig.pages || 'src/pages');\n const layoutsDir = path.resolve(root, ssgConfig.layouts || 'src/layouts');\n const contentDir = path.resolve(root, ssgConfig.content || 'src/content');\n\n // Clear caches and trigger HMR on file changes\n devServer.watcher.on('add', (file) => {\n if (file.startsWith(pagesDir)) {\n routesCache = null;\n navigationCache = null;\n invalidateModule(RESOLVED_VIRTUAL_ROUTES_ID);\n invalidateModule(RESOLVED_VIRTUAL_NAVIGATION_ID);\n } else if (file.startsWith(layoutsDir)) {\n layoutsCache = null;\n invalidateModule(RESOLVED_VIRTUAL_LAYOUTS_ID);\n }\n });\n\n devServer.watcher.on('unlink', (file) => {\n if (file.startsWith(pagesDir)) {\n routesCache = null;\n navigationCache = null;\n // Clean up frontmatter hash cache\n frontmatterHashCache.delete(file);\n invalidateModule(RESOLVED_VIRTUAL_ROUTES_ID);\n invalidateModule(RESOLVED_VIRTUAL_NAVIGATION_ID);\n } else if (file.startsWith(layoutsDir)) {\n layoutsCache = null;\n invalidateModule(RESOLVED_VIRTUAL_LAYOUTS_ID);\n }\n });\n\n // Handle MDX/MD file content changes for frontmatter updates\n devServer.watcher.on('change', async (file) => {\n if (!file.startsWith(pagesDir)) return;\n if (!/\\.mdx?$/.test(file)) return;\n \n try {\n const content = await fs.promises.readFile(file, 'utf-8');\n const { data: newFrontmatter } = parseFrontmatter(content);\n const newHash = JSON.stringify(newFrontmatter);\n const oldHash = frontmatterHashCache.get(file);\n \n // Update the cache\n frontmatterHashCache.set(file, newHash);\n \n // If frontmatter changed, invalidate navigation (titles, categories may have changed)\n if (oldHash !== undefined && oldHash !== newHash) {\n navigationCache = null;\n routesCache = null;\n \n const navMod = devServer.moduleGraph.getModuleById(RESOLVED_VIRTUAL_NAVIGATION_ID);\n if (navMod) {\n devServer.moduleGraph.invalidateModule(navMod);\n }\n \n const routesMod = devServer.moduleGraph.getModuleById(RESOLVED_VIRTUAL_ROUTES_ID);\n if (routesMod) {\n devServer.moduleGraph.invalidateModule(routesMod);\n }\n \n // Send HMR update for navigation changes\n devServer.ws.send({ type: 'full-reload' });\n }\n } catch (err) {\n // File read error, ignore\n }\n });\n\n function invalidateModule(id: string) {\n const mod = devServer.moduleGraph.getModuleById(id);\n if (mod) {\n devServer.moduleGraph.invalidateModule(mod);\n devServer.ws.send({ type: 'full-reload' });\n }\n }\n\n // Serve virtual HTML template if no index.html exists\n if (entryDetection.useVirtualHtml) {\n devServer.middlewares.use((req, res, next) => {\n // Skip virtual modules and special Vite paths\n if (req.url?.startsWith('/@') || \n req.url?.startsWith('/__') || \n req.url?.includes('virtual:') ||\n req.url?.includes('node_modules') ||\n req.url?.startsWith('/@vite') ||\n req.url?.startsWith('/@fs')) {\n return next();\n }\n \n // Only handle requests for HTML pages (not assets with extensions)\n if (req.url && (req.url === '/' || !req.url.includes('.'))) {\n const html = generateHtmlTemplate(ssgConfig);\n // Transform HTML through Vite for HMR injection\n devServer.transformIndexHtml(req.url, html).then((transformedHtml) => {\n res.setHeader('Content-Type', 'text/html');\n res.end(transformedHtml);\n }).catch(next);\n return;\n }\n next();\n });\n }\n },\n\n resolveId(id) {\n // Handle virtual modules\n if (id === VIRTUAL_ROUTES_ID) {\n return RESOLVED_VIRTUAL_ROUTES_ID;\n }\n if (id === VIRTUAL_LAYOUTS_ID) {\n return RESOLVED_VIRTUAL_LAYOUTS_ID;\n }\n if (id === VIRTUAL_CONFIG_ID) {\n return RESOLVED_VIRTUAL_CONFIG_ID;\n }\n if (id === VIRTUAL_NAVIGATION_ID) {\n return RESOLVED_VIRTUAL_NAVIGATION_ID;\n }\n // Handle virtual entry points (both formats)\n if (id === VIRTUAL_CLIENT_ID || id === SSG_CLIENT_ENTRY_PATH) {\n return RESOLVED_VIRTUAL_CLIENT_ID;\n }\n if (id === VIRTUAL_SERVER_ID) {\n return RESOLVED_VIRTUAL_SERVER_ID;\n }\n return null;\n },\n\n async load(id) {\n // Generate virtual routes module\n if (id === RESOLVED_VIRTUAL_ROUTES_ID) {\n if (!routesCache) {\n const routes = await scanPages(ssgConfig, root);\n // Use lazy loading in dev mode for proper HMR and MDX processing\n const code = config.command === 'serve'\n ? generateLazyRoutesModule(routes, ssgConfig)\n : generateRoutesModule(routes, ssgConfig);\n routesCache = { routes, code };\n }\n return routesCache.code;\n }\n\n // Generate virtual layouts module\n if (id === RESOLVED_VIRTUAL_LAYOUTS_ID) {\n if (!layoutsCache) {\n const layouts = await discoverLayouts(ssgConfig, root);\n const code = generateLayoutsModule(layouts, ssgConfig);\n layoutsCache = { layouts, code };\n }\n return layoutsCache.code;\n }\n\n // Generate virtual navigation module\n if (id === RESOLVED_VIRTUAL_NAVIGATION_ID) {\n if (!navigationCache) {\n // Ensure routes are loaded (reuse cache if available)\n if (!routesCache) {\n const routes = await scanPages(ssgConfig, root);\n const routesCode = config.command === 'serve'\n ? generateLazyRoutesModule(routes, ssgConfig)\n : generateRoutesModule(routes, ssgConfig);\n routesCache = { routes, code: routesCode };\n }\n const isDev = config.command === 'serve';\n const code = generateNavigationModule(routesCache.routes, ssgConfig, isDev);\n navigationCache = { code };\n }\n return navigationCache.code;\n }\n\n // Generate virtual config module\n if (id === RESOLVED_VIRTUAL_CONFIG_ID) {\n return `export default ${JSON.stringify(ssgConfig)};`;\n }\n\n // Generate virtual client entry (needs JSX transform)\n if (id === RESOLVED_VIRTUAL_CLIENT_ID) {\n const code = generateClientEntry(ssgConfig, entryDetection);\n const esbuild = await import('esbuild');\n const result = await esbuild.transform(code, {\n loader: 'tsx',\n jsx: 'automatic',\n jsxImportSource: 'sigx',\n });\n return result.code;\n }\n\n // Generate virtual server entry (needs JSX transform)\n if (id === RESOLVED_VIRTUAL_SERVER_ID) {\n const code = generateServerEntry(ssgConfig);\n const esbuild = await import('esbuild');\n const result = await esbuild.transform(code, {\n loader: 'tsx',\n jsx: 'automatic',\n jsxImportSource: 'sigx',\n });\n return result.code;\n }\n\n return null;\n },\n\n // Handle HMR for layouts and pages\n async handleHotUpdate({ file, server }) {\n const layoutsDir = path.resolve(root, ssgConfig.layouts || 'src/layouts');\n const pagesDir = path.resolve(root, ssgConfig.pages || 'src/pages');\n\n if (file.startsWith(layoutsDir)) {\n // Clear layout cache\n layoutsCache = null;\n\n // Invalidate the virtual layouts module\n const mod = server.moduleGraph.getModuleById(RESOLVED_VIRTUAL_LAYOUTS_ID);\n if (mod) {\n server.moduleGraph.invalidateModule(mod);\n }\n\n // Also reload pages that use this layout\n return [];\n }\n\n // Handle MDX/MD file changes in pages directory\n if (file.startsWith(pagesDir) && /\\.mdx?$/.test(file)) {\n // The MDX plugin handles the transform and HMR code injection.\n // We just need to ensure the module is properly invalidated.\n // Vite will handle the rest via the injected import.meta.hot.accept()\n \n // Return undefined to let Vite handle the standard HMR flow\n // The MDX file already has import.meta.hot.accept() injected\n return undefined;\n }\n\n return undefined;\n },\n };\n\n // Combine with MDX plugin if enabled (default: true)\n const enableMdx = options.enableMdx !== false;\n \n if (enableMdx) {\n const mdx = mdxPlugin({\n markdown: options.markdown,\n ssgConfig: undefined as any, // Will be set by configResolved\n });\n return [mainPlugin, mdx];\n }\n \n return [mainPlugin];\n}\n\n/**\n * Export the plugin as default\n */\nexport default ssgPlugin;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin-aT6WUDzk.js","names":[],"sources":["../src/mdx/shiki.ts","../src/mdx/rehype-headings.ts","../src/mdx/plugin.ts","../src/vite/plugin.ts"],"sourcesContent":["/**\n * Shiki syntax highlighting integration\n *\n * Provides code block highlighting for Markdown/MDX content.\n */\n\nimport { createHighlighter, type Highlighter, type BundledLanguage, type BundledTheme } from 'shiki';\nimport type { ShikiConfig } from '../types';\n\n/**\n * Cached highlighter instance\n */\nlet highlighterPromise: Promise<Highlighter> | null = null;\n\n/**\n * Default Shiki configuration\n */\nconst DEFAULT_CONFIG: Required<ShikiConfig> = {\n light: 'github-light',\n dark: 'github-dark',\n langs: ['javascript', 'typescript', 'jsx', 'tsx', 'json', 'css', 'html', 'markdown', 'bash', 'shell'],\n};\n\n/**\n * Initialize or get the Shiki highlighter\n */\nexport async function getHighlighter(config?: ShikiConfig): Promise<Highlighter> {\n if (!highlighterPromise) {\n const mergedConfig = { ...DEFAULT_CONFIG, ...config };\n\n highlighterPromise = createHighlighter({\n themes: [mergedConfig.light as BundledTheme, mergedConfig.dark as BundledTheme],\n langs: mergedConfig.langs as BundledLanguage[],\n });\n }\n\n return highlighterPromise;\n}\n\n/**\n * Tab types for preview blocks\n */\nexport type TabType = 'preview' | 'code' | 'console';\n\n/**\n * Highlight code with Shiki\n */\nexport async function highlightCode(\n code: string,\n lang: string,\n config?: ShikiConfig,\n meta?: { filename?: string; live?: boolean; tabs?: TabType[] }\n): Promise<string> {\n const highlighter = await getHighlighter(config);\n const mergedConfig = { ...DEFAULT_CONFIG, ...config };\n\n // Check if language is loaded\n const loadedLangs = highlighter.getLoadedLanguages();\n const effectiveLang = loadedLangs.includes(lang as BundledLanguage) ? lang : 'text';\n\n // Generate HTML with both themes for CSS-based switching\n const codeHtml = highlighter.codeToHtml(code, {\n lang: effectiveLang as BundledLanguage,\n themes: {\n light: mergedConfig.light as BundledTheme,\n dark: mergedConfig.dark as BundledTheme,\n },\n });\n\n // Wrap in code-window structure\n const filename = meta?.filename ?? '';\n const isLive = meta?.live ?? false;\n const tabs = meta?.tabs; // undefined means no tabbed view, just code\n const hasTabs = tabs && tabs.length > 0;\n \n const filenameHtml = filename \n ? `<span class=\"code-window-filename\">${escapeHtml(filename)}</span>`\n : `<span class=\"code-window-lang\">${getLanguageLabel(effectiveLang)}</span>`;\n\n // For preview blocks (has tabs), render a LivePreview island component\n if (hasTabs) {\n const base64Code = encodeBase64(code);\n \n // Generate tab buttons HTML based on tabs array\n const firstTab = tabs[0];\n const tabButtonsHtml = tabs.map((tab, i) => {\n const label = tab.charAt(0).toUpperCase() + tab.slice(1);\n const isActive = i === 0;\n return `<button class=\"code-window-tab${isActive ? ' code-window-tab-active' : ''}\">${label}</button>`;\n }).join('\\n ');\n \n // Return an island marker that will be hydrated on the client\n // Use code-window styling for consistency with the nice terminal look\n const html = `<div \n class=\"live-preview-island\" \n data-island=\"LivePreview\" \n data-island-strategy=\"visible\"\n data-island-props=\"${escapeHtml(JSON.stringify({\n code: base64Code,\n highlightedCode: codeHtml,\n language: effectiveLang,\n filename: filename,\n tabs: tabs,\n live: isLive\n }))}\"\n>\n <div class=\"code-window code-window-live code-window-preview\">\n <div class=\"code-window-header\">\n <div class=\"code-window-header-left\">\n <div class=\"code-window-dots\">\n <span class=\"code-window-dot dot-red\"></span>\n <span class=\"code-window-dot dot-yellow\"></span>\n <span class=\"code-window-dot dot-green\"></span>\n </div>\n ${filenameHtml}\n </div>\n <div class=\"code-window-tabs\">\n ${tabButtonsHtml}\n </div>\n <button class=\"code-window-try-live\" disabled>⚡ Try Live</button>\n </div>\n <div class=\"code-window-preview-pane\"${firstTab !== 'preview' ? ' style=\"display:none;\"' : ''}>\n <div class=\"code-window-preview-loading\">\n <span class=\"code-window-spinner\"></span>\n Loading preview...\n </div>\n </div>\n <div class=\"code-window-console-pane\"${firstTab !== 'console' ? ' style=\"display:none;\"' : ''}>\n <div class=\"code-window-console-empty\">No console output</div>\n </div>\n <div class=\"code-window-content\"${firstTab !== 'code' ? ' style=\"display:none;\"' : ''}>\n ${codeHtml}\n </div>\n </div>\n</div>`;\n return html;\n }\n\n // Add \"Try Live\" button for live code blocks\n const tryLiveButton = isLive \n ? `<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>`\n : '';\n\n const html = `<div class=\"code-window${isLive ? ' code-window-live' : ''}\">\n <div class=\"code-window-header\">\n <div class=\"code-window-header-left\">\n <div class=\"code-window-dots\">\n <span class=\"code-window-dot dot-red\"></span>\n <span class=\"code-window-dot dot-yellow\"></span>\n <span class=\"code-window-dot dot-green\"></span>\n </div>\n ${filenameHtml}\n </div>\n ${tryLiveButton}\n </div>\n <div class=\"code-window-content\">\n ${codeHtml}\n </div>\n </div>`;\n\n return html;\n}\n\n/**\n * Encode string to base64 (works in both Node.js and browser)\n */\nfunction encodeBase64(str: string): string {\n // Use Buffer in Node.js\n if (typeof Buffer !== 'undefined') {\n return Buffer.from(str, 'utf-8').toString('base64');\n }\n // Fallback for browser (shouldn't be needed during SSG build)\n return btoa(unescape(encodeURIComponent(str)));\n}\n\n/**\n * Get a display label for a language\n */\nfunction getLanguageLabel(lang: string): string {\n const labels: Record<string, string> = {\n 'tsx': 'TSX',\n 'jsx': 'JSX',\n 'ts': 'TypeScript',\n 'typescript': 'TypeScript',\n 'js': 'JavaScript',\n 'javascript': 'JavaScript',\n 'css': 'CSS',\n 'html': 'HTML',\n 'json': 'JSON',\n 'bash': 'Terminal',\n 'shell': 'Terminal',\n 'sh': 'Terminal',\n 'md': 'Markdown',\n 'markdown': 'Markdown',\n 'python': 'Python',\n 'py': 'Python',\n 'rust': 'Rust',\n 'go': 'Go',\n 'text': '',\n };\n return labels[lang.toLowerCase()] ?? lang.toUpperCase();\n}\n\n/**\n * Escape HTML special characters\n */\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\n/**\n * Create a rehype plugin for Shiki\n */\nexport function rehypeShiki(config?: ShikiConfig) {\n return async (tree: any) => {\n const { visit } = await import('unist-util-visit');\n const { fromHtml } = await import('hast-util-from-html');\n\n const nodesToProcess: Array<{ node: any; parent: any; index: number }> = [];\n\n visit(tree, 'element', (node: any, index: number | undefined, parent: any) => {\n // Look for <pre><code> elements\n if (\n node.tagName === 'pre' &&\n node.children?.[0]?.tagName === 'code'\n ) {\n nodesToProcess.push({ node, parent, index: index ?? 0 });\n }\n });\n\n // Process nodes in parallel\n await Promise.all(\n nodesToProcess.map(async ({ node, parent, index }) => {\n const codeNode = node.children[0];\n\n // Extract language from class (e.g., \"language-typescript\")\n const className = codeNode.properties?.className?.[0] || '';\n const lang = className.replace(/^language-/, '') || 'text';\n\n // Extract filename from meta string if present\n // Supports: ```tsx filename=\"Counter.tsx\" or ```tsx title=\"Counter.tsx\"\n const metaString = codeNode.data?.meta || codeNode.properties?.metastring || '';\n const filename = extractMeta(metaString, 'filename') || extractMeta(metaString, 'title') || '';\n \n // Check for \"live\" flag in meta string (e.g., ```tsx live)\n const isLive = /\\blive\\b/i.test(metaString);\n \n // Parse tabs from meta string - order determines tab order\n // Supported keywords: preview, code, console\n // Example: ```tsx code preview console live -> tabs: [code, preview, console], with live button\n const tabKeywords = ['preview', 'code', 'console'] as const;\n const tabs: TabType[] = [];\n \n // Find all tab keywords and their positions\n const tabPositions: Array<{ tab: TabType; index: number }> = [];\n for (const keyword of tabKeywords) {\n const regex = new RegExp(`\\\\b${keyword}\\\\b`, 'i');\n const match = metaString.match(regex);\n if (match && match.index !== undefined) {\n tabPositions.push({ tab: keyword, index: match.index });\n }\n }\n \n // Sort by position (order of appearance in meta string)\n tabPositions.sort((a, b) => a.index - b.index);\n \n // Extract just the tab names in order\n for (const { tab } of tabPositions) {\n tabs.push(tab);\n }\n\n // Get raw code content\n const code = getTextContent(codeNode);\n\n // Highlight with Shiki (with meta info)\n // Only pass tabs if at least one tab keyword was found\n const html = await highlightCode(code.trim(), lang, config, { \n filename, \n live: isLive, \n tabs: tabs.length > 0 ? tabs : undefined \n });\n\n // Parse the HTML string into HAST nodes\n const fragment = fromHtml(html, { fragment: true });\n \n // Replace node with the parsed HAST fragment's children\n if (parent && typeof index === 'number' && fragment.children.length > 0) {\n // Use the first element from the fragment (should be the code-window div)\n parent.children[index] = fragment.children[0];\n }\n })\n );\n };\n}\n\n/**\n * Extract a meta value from a meta string\n * Supports: filename=\"value\" or filename='value' or filename=value\n */\nfunction extractMeta(metaString: string, key: string): string | null {\n if (!metaString) return null;\n \n // Match: key=\"value\" or key='value' or key=value\n const regex = new RegExp(`${key}=[\"']?([^\"'\\\\s]+)[\"']?`, 'i');\n const match = metaString.match(regex);\n return match ? match[1] : null;\n}\n\n/**\n * Extract text content from an AST node\n */\nfunction getTextContent(node: any): string {\n if (node.type === 'text') {\n return node.value;\n }\n\n if (node.children) {\n return node.children.map(getTextContent).join('');\n }\n\n return '';\n}\n\n/**\n * Load additional language on demand\n */\nexport async function loadLanguage(lang: string): Promise<void> {\n const highlighter = await getHighlighter();\n const loadedLangs = highlighter.getLoadedLanguages();\n\n if (!loadedLangs.includes(lang as BundledLanguage)) {\n try {\n await highlighter.loadLanguage(lang as BundledLanguage);\n } catch {\n console.warn(`Shiki: Language \"${lang}\" not available, using plain text`);\n }\n }\n}\n","/**\n * Rehype plugin to extract headings from MDX/MD content\n *\n * Extracts headings (h2-h6 by default) with their IDs and text content\n * for use in table of contents generation.\n */\n\nimport { visit } from 'unist-util-visit';\nimport { toString } from 'hast-util-to-string';\nimport type { TocHeading } from '../types';\n\n/**\n * Options for the rehype headings plugin\n */\nexport interface RehypeHeadingsOptions {\n /**\n * Minimum heading level to include (1-6)\n * @default 2\n */\n minLevel?: number;\n\n /**\n * Maximum heading level to include (1-6)\n * @default 3\n */\n maxLevel?: number;\n}\n\n/**\n * Rehype plugin to extract headings from the document\n *\n * Stores extracted headings in `file.data.headings` for later use.\n *\n * @example\n * ```ts\n * import { rehypeExtractHeadings } from './rehype-headings';\n *\n * // Use with unified\n * unified()\n * .use(rehypeSlug) // First add IDs to headings\n * .use(rehypeExtractHeadings, { minLevel: 2, maxLevel: 3 })\n * ```\n */\nexport function rehypeExtractHeadings(options: RehypeHeadingsOptions = {}) {\n const { minLevel = 2, maxLevel = 3 } = options;\n\n return (tree: any, file: any) => {\n const headings: TocHeading[] = [];\n\n visit(tree, 'element', (node: any) => {\n // Check if this is a heading element (h1-h6)\n const match = /^h([1-6])$/.exec(node.tagName);\n if (!match) return;\n\n const level = parseInt(match[1], 10);\n\n // Skip headings outside configured range\n if (level < minLevel || level > maxLevel) return;\n\n // Get the heading ID (should be set by rehype-slug)\n const id = node.properties?.id;\n if (!id) return;\n\n // Extract text content from the heading\n const text = toString(node).trim();\n if (!text) return;\n\n headings.push({ id, text, level });\n });\n\n // Store headings in file data for later access\n file.data = file.data || {};\n file.data.headings = headings;\n };\n}\n\nexport default rehypeExtractHeadings;\n","/**\n * MDX Vite plugin\n *\n * Transforms MDX files into SignalX components with:\n * - Frontmatter extraction\n * - Shiki syntax highlighting\n * - SignalX JSX runtime integration\n */\n\nimport type { Plugin, ResolvedConfig } from 'vite';\nimport type { SSGConfig, MarkdownConfig, PageMeta, TocHeading } from '../types';\nimport { parseFrontmatter, extractTitleFromContent } from './frontmatter';\nimport { rehypeShiki } from './shiki';\nimport { rehypeExtractHeadings } from './rehype-headings';\n\n/**\n * MDX plugin options\n */\nexport interface MDXPluginOptions {\n /**\n * Markdown configuration\n */\n markdown?: MarkdownConfig;\n\n /**\n * SSG configuration (for layout resolution)\n */\n ssgConfig?: SSGConfig;\n}\n\n/**\n * Create the MDX Vite plugin\n */\nexport function mdxPlugin(options: MDXPluginOptions = {}): Plugin {\n const { markdown = {} } = options;\n\n let mdxRollup: any;\n let viteConfig: ResolvedConfig;\n\n return {\n name: 'sigx-ssg-mdx',\n enforce: 'pre',\n\n async configResolved(config) {\n viteConfig = config;\n // Dynamically import @mdx-js/rollup\n const mdxModule = await import('@mdx-js/rollup');\n const remarkFrontmatter = (await import('remark-frontmatter')).default;\n const remarkMdxFrontmatter = (await import('remark-mdx-frontmatter')).default;\n const remarkGfm = (await import('remark-gfm')).default;\n const rehypeSlug = (await import('rehype-slug')).default;\n const rehypeAutolinkHeadings = (await import('rehype-autolink-headings')).default;\n\n // Get TOC config from SSG config\n const tocConfig = options.ssgConfig?.toc || { minLevel: 2, maxLevel: 3 };\n\n // Build rehype plugins array\n const rehypePlugins: any[] = [];\n\n // Add rehype-slug first to generate heading IDs\n rehypePlugins.push(rehypeSlug);\n\n // Add autolink headings (clickable anchor links)\n rehypePlugins.push([rehypeAutolinkHeadings, {\n behavior: 'append',\n properties: {\n class: 'heading-anchor',\n ariaHidden: true,\n tabIndex: -1,\n },\n content: {\n type: 'element',\n tagName: 'span',\n properties: { class: 'heading-anchor-icon' },\n children: [{ type: 'text', value: '#' }],\n },\n }]);\n\n // Add heading extraction for TOC\n rehypePlugins.push([rehypeExtractHeadings, tocConfig]);\n\n // Add Shiki if enabled\n if (markdown.shiki !== false) {\n const shikiConfig = typeof markdown.shiki === 'object' ? markdown.shiki : undefined;\n rehypePlugins.push([rehypeShiki, shikiConfig]);\n }\n\n // Add custom rehype plugins\n if (markdown.rehypePlugins) {\n rehypePlugins.push(...markdown.rehypePlugins);\n }\n\n // Build remark plugins array\n const remarkPlugins: any[] = [\n remarkFrontmatter,\n [remarkMdxFrontmatter, { name: 'frontmatter' }],\n remarkGfm,\n ];\n\n // Add custom remark plugins\n if (markdown.remarkPlugins) {\n remarkPlugins.push(...markdown.remarkPlugins);\n }\n\n // Create MDX plugin\n // Use jsx: false to output function calls instead of JSX syntax\n // This avoids needing esbuild to process .mdx files as JSX\n mdxRollup = mdxModule.default({\n jsx: false,\n jsxImportSource: 'sigx',\n remarkPlugins,\n rehypePlugins,\n providerImportSource: undefined,\n });\n },\n\n async transform(code, id) {\n // Only process MDX and MD files\n if (!/\\.mdx?$/.test(id)) {\n return null;\n }\n\n // Parse frontmatter first\n const { data: frontmatter, content } = parseFrontmatter(code);\n\n // Extract title from content if not in frontmatter\n if (!frontmatter.title) {\n const extractedTitle = extractTitleFromContent(content);\n if (extractedTitle) {\n frontmatter.title = extractedTitle;\n }\n }\n\n // Transform MDX content\n if (!mdxRollup?.transform) {\n throw new Error('MDX plugin not initialized');\n }\n\n const result = await mdxRollup.transform(code, id);\n\n if (!result) {\n return null;\n }\n\n // Extract headings from the file data (set by rehype-extract-headings)\n // Note: We need to process the content to get headings\n const headings = await extractHeadingsFromContent(content, options);\n\n // Create module ID for HMR tracking (normalize path separators)\n const moduleId = id.replace(/\\\\/g, '/');\n\n // Inject frontmatter export and wrap in SignalX component\n const transformedCode = wrapMDXComponent(\n result.code,\n frontmatter,\n headings,\n moduleId,\n viteConfig.command === 'serve'\n );\n\n return {\n code: transformedCode,\n map: result.map,\n };\n },\n };\n}\n\n/**\n * Wrap MDX output in a SignalX component\n * \n * Note: remark-mdx-frontmatter already exports `frontmatter`, so we only add the layout export\n * \n * In dev mode, we:\n * 1. Wrap MDXContent in a sigx component() for proper HMR tracking\n * 2. Inject HMR registration code so the component updates live\n */\nfunction wrapMDXComponent(\n code: string,\n frontmatter: PageMeta,\n headings: TocHeading[],\n moduleId: string,\n isDev: boolean\n): string {\n // The MDX output already includes frontmatter export from remark-mdx-frontmatter\n // We need to add the layout export for the SSG routing system\n \n // In dev mode, wrap in sigx component for HMR support\n if (isDev) {\n // Generate a component name from the file path for debugging\n const fileName = moduleId.split('/').pop()?.replace(/\\.mdx?$/, '') || 'MDXPage';\n const componentName = fileName.charAt(0).toUpperCase() + fileName.slice(1).replace(/[^a-zA-Z0-9]/g, '') + 'Page';\n \n // The MDX output has \"export default function MDXContent(...)\" \n // We need to:\n // 1. Remove the \"export default\" from MDXContent to avoid duplicate exports\n // 2. Wrap it in a sigx component for HMR\n // 3. Export the wrapped component as default\n const modifiedCode = code\n .replace(/export\\s+default\\s+function\\s+MDXContent/g, 'function _MDXContent')\n .replace(/export\\s+{\\s*MDXContent\\s+as\\s+default\\s*}/g, '');\n \n return `\nimport { registerHMRModule } from '@sigx/vite/hmr';\nimport { component as __component } from 'sigx';\nregisterHMRModule('${moduleId}');\n\n${modifiedCode}\n\n// Export layout from frontmatter for SSG routing\nexport const layout = ${frontmatter.layout ? JSON.stringify(frontmatter.layout) : 'undefined'};\n\n// Export headings for table of contents\nexport const headings = ${JSON.stringify(headings)};\n\n// Wrap MDXContent in a sigx component for HMR support\nconst MDXPage = __component(() => {\n return () => _MDXContent({});\n}, { name: '${componentName}' });\n\nexport default MDXPage;\n\nif (import.meta.hot) {\n // Accept HMR updates with a callback to trigger re-render after module is updated\n import.meta.hot.accept((newModule) => {\n if (newModule) {\n // Notify LayoutRouter to clear its cache and re-render with new module\n window.dispatchEvent(new CustomEvent('sigx:mdx-hmr', { \n detail: { moduleId: '${moduleId}', newModule } \n }));\n }\n });\n}\n`;\n }\n \n // Production: just add exports without HMR wrapper\n return `\n${code}\n\n// Export layout from frontmatter for SSG routing\nexport const layout = ${frontmatter.layout ? JSON.stringify(frontmatter.layout) : 'undefined'};\n\n// Export headings for table of contents\nexport const headings = ${JSON.stringify(headings)};\n`;\n}\n\n/**\n * Extract headings from markdown/MDX content\n */\nasync function extractHeadingsFromContent(\n content: string,\n options: MDXPluginOptions\n): Promise<TocHeading[]> {\n const { unified } = await import('unified');\n const remarkParse = (await import('remark-parse')).default;\n const remarkRehype = (await import('remark-rehype')).default;\n const rehypeSlug = (await import('rehype-slug')).default;\n const rehypeStringify = (await import('rehype-stringify')).default;\n\n // Get TOC config\n const tocConfig = options.ssgConfig?.toc || { minLevel: 2, maxLevel: 3 };\n\n // Process markdown to extract headings\n // Note: rehype-stringify is required for unified to have a compiler\n const processor = unified()\n .use(remarkParse)\n .use(remarkRehype)\n .use(rehypeSlug)\n .use(rehypeExtractHeadings, tocConfig)\n .use(rehypeStringify);\n\n const file = await processor.process(content);\n return (file.data as any).headings || [];\n}\n\n/**\n * Create a simple markdown-only plugin (no MDX features)\n */\nexport function markdownPlugin(options: MDXPluginOptions = {}): Plugin {\n return {\n name: 'sigx-ssg-markdown',\n enforce: 'pre',\n\n async transform(code, id) {\n // Only process .md files (not .mdx)\n if (!/\\.md$/.test(id) || /\\.mdx$/.test(id)) {\n return null;\n }\n\n // Parse frontmatter\n const { data: frontmatter, content } = parseFrontmatter(code);\n\n // Extract title if not in frontmatter\n if (!frontmatter.title) {\n const extractedTitle = extractTitleFromContent(content);\n if (extractedTitle) {\n frontmatter.title = extractedTitle;\n }\n }\n\n // Convert markdown to simple HTML using a lightweight parser\n // For full MD support, the MDX plugin should be used\n const html = await simpleMarkdownToHtml(content, options);\n \n // Extract headings for TOC\n const headings = await extractHeadingsFromContent(content, options);\n \n const frontmatterJSON = JSON.stringify(frontmatter);\n const headingsJSON = JSON.stringify(headings);\n\n return {\n code: `\nimport { jsx as _jsx } from 'sigx/jsx-runtime';\n\nexport const frontmatter = ${frontmatterJSON};\nexport const layout = ${frontmatter.layout ? JSON.stringify(frontmatter.layout) : 'undefined'};\nexport const headings = ${headingsJSON};\n\nexport default function MDContent(props) {\n return _jsx('div', {\n class: 'markdown-content',\n dangerouslySetInnerHTML: { __html: ${JSON.stringify(html)} }\n });\n}\n`,\n map: null,\n };\n },\n };\n}\n\n/**\n * Simple markdown to HTML conversion\n * For basic markdown without custom components\n */\nasync function simpleMarkdownToHtml(markdown: string, options: MDXPluginOptions = {}): Promise<string> {\n // Use unified for basic markdown processing\n const { unified } = await import('unified');\n const remarkParse = (await import('remark-parse')).default;\n const remarkRehype = (await import('remark-rehype')).default;\n const rehypeSlug = (await import('rehype-slug')).default;\n const rehypeStringify = (await import('rehype-stringify')).default;\n\n const result = await unified()\n .use(remarkParse)\n .use(remarkRehype)\n .use(rehypeSlug) // Add IDs to headings\n .use(rehypeStringify)\n .process(markdown);\n\n return String(result);\n}\n","/**\n * SSG Vite Plugin\n *\n * Main Vite plugin for @sigx/ssg that integrates:\n * - File-based routing with virtual modules\n * - Layout system\n * - MDX processing\n * - HMR for development\n * - Zero-config entry point generation\n */\n\nimport type { Plugin, ResolvedConfig, ViteDevServer } from 'vite';\nimport path from 'node:path';\nimport fs from 'node:fs';\nimport type { SSGConfig } from '../types';\nimport { defineSSGConfig, loadConfig } from '../config';\nimport { scanPages } from '../routing/scanner';\nimport { generateRoutesModule, generateLazyRoutesModule, VIRTUAL_ROUTES_ID, RESOLVED_VIRTUAL_ROUTES_ID } from '../routing/virtual';\nimport { generateNavigationModule, VIRTUAL_NAVIGATION_ID, RESOLVED_VIRTUAL_NAVIGATION_ID } from '../routing/virtual-navigation';\nimport { discoverLayouts } from '../layouts/resolver';\nimport { generateLayoutsModule, VIRTUAL_LAYOUTS_ID, RESOLVED_VIRTUAL_LAYOUTS_ID } from '../layouts/virtual';\nimport { mdxPlugin } from '../mdx/plugin';\nimport { parseFrontmatter } from '../mdx/frontmatter';\nimport {\n detectCustomEntries,\n generateClientEntry,\n generateServerEntry,\n generateHtmlTemplate,\n VIRTUAL_CLIENT_ID,\n RESOLVED_VIRTUAL_CLIENT_ID,\n VIRTUAL_SERVER_ID,\n RESOLVED_VIRTUAL_SERVER_ID,\n SSG_CLIENT_ENTRY_PATH,\n type EntryDetectionResult,\n} from './virtual-entries';\n\n/**\n * SSG Plugin options\n */\nexport interface SSGPluginOptions extends Partial<SSGConfig> {\n /**\n * Path to ssg.config.ts\n */\n configPath?: string;\n \n /**\n * Enable built-in MDX processing. Set to false if using external MDX plugin.\n * @default true\n */\n enableMdx?: boolean;\n}\n\n/**\n * Virtual module for SSG config\n */\nconst VIRTUAL_CONFIG_ID = 'virtual:ssg-config';\nconst RESOLVED_VIRTUAL_CONFIG_ID = '\\0' + VIRTUAL_CONFIG_ID;\n\n/**\n * Create the SSG Vite plugin\n */\nexport function ssgPlugin(options: SSGPluginOptions = {}): Plugin[] {\n let config: ResolvedConfig;\n let ssgConfig: SSGConfig;\n let root: string;\n let server: ViteDevServer | undefined;\n let entryDetection: EntryDetectionResult;\n\n // Cache for virtual modules\n let routesCache: { routes: any[]; code: string } | null = null;\n let layoutsCache: { layouts: any[]; code: string } | null = null;\n let navigationCache: { code: string } | null = null;\n \n // Cache for frontmatter hashes to detect changes\n const frontmatterHashCache = new Map<string, string>();\n\n const mainPlugin: Plugin = {\n name: 'sigx-ssg',\n enforce: 'pre',\n\n async configResolved(resolvedConfig) {\n config = resolvedConfig;\n root = resolvedConfig.root;\n\n // Load SSG config - always try to load from file first\n const fileConfig = await loadConfig(options.configPath);\n\n // Merge file config with plugin options (plugin options take precedence).\n // Inherit Vite's `base` when the SSG config doesn't set one, so the\n // generated router and sitemap stay in sync with the asset URLs Vite\n // emits (e.g. when deploying under a sub-path like /docs/).\n const merged = { ...fileConfig, ...options };\n if ((merged.base == null || merged.base === '/') && resolvedConfig.base && resolvedConfig.base !== '/') {\n merged.base = resolvedConfig.base;\n }\n ssgConfig = defineSSGConfig(merged);\n\n // Detect custom entry points\n entryDetection = detectCustomEntries(root, ssgConfig);\n\n // Log zero-config mode status\n if (entryDetection.useVirtualClient || entryDetection.useVirtualServer) {\n console.log('📦 @sigx/ssg: Using zero-config mode');\n if (entryDetection.useVirtualClient) {\n console.log(' → Virtual client entry');\n }\n if (entryDetection.useVirtualServer) {\n console.log(' → Virtual server entry');\n }\n if (entryDetection.globalCssPath) {\n console.log(` → Auto-importing ${entryDetection.globalCssPath}`);\n }\n }\n },\n\n configureServer(devServer) {\n server = devServer;\n\n // Watch pages and layouts directories for changes\n const pagesDir = path.resolve(root, ssgConfig.pages || 'src/pages');\n const layoutsDir = path.resolve(root, ssgConfig.layouts || 'src/layouts');\n const contentDir = path.resolve(root, ssgConfig.content || 'src/content');\n\n // Clear caches and trigger HMR on file changes\n devServer.watcher.on('add', (file) => {\n if (file.startsWith(pagesDir)) {\n routesCache = null;\n navigationCache = null;\n invalidateModule(RESOLVED_VIRTUAL_ROUTES_ID);\n invalidateModule(RESOLVED_VIRTUAL_NAVIGATION_ID);\n } else if (file.startsWith(layoutsDir)) {\n layoutsCache = null;\n invalidateModule(RESOLVED_VIRTUAL_LAYOUTS_ID);\n }\n });\n\n devServer.watcher.on('unlink', (file) => {\n if (file.startsWith(pagesDir)) {\n routesCache = null;\n navigationCache = null;\n // Clean up frontmatter hash cache\n frontmatterHashCache.delete(file);\n invalidateModule(RESOLVED_VIRTUAL_ROUTES_ID);\n invalidateModule(RESOLVED_VIRTUAL_NAVIGATION_ID);\n } else if (file.startsWith(layoutsDir)) {\n layoutsCache = null;\n invalidateModule(RESOLVED_VIRTUAL_LAYOUTS_ID);\n }\n });\n\n // Handle MDX/MD file content changes for frontmatter updates\n devServer.watcher.on('change', async (file) => {\n if (!file.startsWith(pagesDir)) return;\n if (!/\\.mdx?$/.test(file)) return;\n \n try {\n const content = await fs.promises.readFile(file, 'utf-8');\n const { data: newFrontmatter } = parseFrontmatter(content);\n const newHash = JSON.stringify(newFrontmatter);\n const oldHash = frontmatterHashCache.get(file);\n \n // Update the cache\n frontmatterHashCache.set(file, newHash);\n \n // If frontmatter changed, invalidate navigation (titles, categories may have changed)\n if (oldHash !== undefined && oldHash !== newHash) {\n navigationCache = null;\n routesCache = null;\n \n const navMod = devServer.moduleGraph.getModuleById(RESOLVED_VIRTUAL_NAVIGATION_ID);\n if (navMod) {\n devServer.moduleGraph.invalidateModule(navMod);\n }\n \n const routesMod = devServer.moduleGraph.getModuleById(RESOLVED_VIRTUAL_ROUTES_ID);\n if (routesMod) {\n devServer.moduleGraph.invalidateModule(routesMod);\n }\n \n // Send HMR update for navigation changes\n devServer.ws.send({ type: 'full-reload' });\n }\n } catch (err) {\n // File read error, ignore\n }\n });\n\n function invalidateModule(id: string) {\n const mod = devServer.moduleGraph.getModuleById(id);\n if (mod) {\n devServer.moduleGraph.invalidateModule(mod);\n devServer.ws.send({ type: 'full-reload' });\n }\n }\n\n // Serve virtual HTML template if no index.html exists\n if (entryDetection.useVirtualHtml) {\n devServer.middlewares.use((req, res, next) => {\n // Skip virtual modules and special Vite paths\n if (req.url?.startsWith('/@') || \n req.url?.startsWith('/__') || \n req.url?.includes('virtual:') ||\n req.url?.includes('node_modules') ||\n req.url?.startsWith('/@vite') ||\n req.url?.startsWith('/@fs')) {\n return next();\n }\n \n // Only handle requests for HTML pages (not assets with extensions)\n if (req.url && (req.url === '/' || !req.url.includes('.'))) {\n const html = generateHtmlTemplate(ssgConfig);\n // Transform HTML through Vite for HMR injection\n devServer.transformIndexHtml(req.url, html).then((transformedHtml) => {\n res.setHeader('Content-Type', 'text/html');\n res.end(transformedHtml);\n }).catch(next);\n return;\n }\n next();\n });\n }\n },\n\n resolveId(id) {\n // Handle virtual modules\n if (id === VIRTUAL_ROUTES_ID) {\n return RESOLVED_VIRTUAL_ROUTES_ID;\n }\n if (id === VIRTUAL_LAYOUTS_ID) {\n return RESOLVED_VIRTUAL_LAYOUTS_ID;\n }\n if (id === VIRTUAL_CONFIG_ID) {\n return RESOLVED_VIRTUAL_CONFIG_ID;\n }\n if (id === VIRTUAL_NAVIGATION_ID) {\n return RESOLVED_VIRTUAL_NAVIGATION_ID;\n }\n // Handle virtual entry points (both formats)\n if (id === VIRTUAL_CLIENT_ID || id === SSG_CLIENT_ENTRY_PATH) {\n return RESOLVED_VIRTUAL_CLIENT_ID;\n }\n if (id === VIRTUAL_SERVER_ID) {\n return RESOLVED_VIRTUAL_SERVER_ID;\n }\n return null;\n },\n\n async load(id) {\n // Generate virtual routes module\n if (id === RESOLVED_VIRTUAL_ROUTES_ID) {\n if (!routesCache) {\n const routes = await scanPages(ssgConfig, root);\n // Use lazy loading in dev mode for proper HMR and MDX processing\n const code = config.command === 'serve'\n ? generateLazyRoutesModule(routes, ssgConfig)\n : generateRoutesModule(routes, ssgConfig);\n routesCache = { routes, code };\n }\n return routesCache.code;\n }\n\n // Generate virtual layouts module\n if (id === RESOLVED_VIRTUAL_LAYOUTS_ID) {\n if (!layoutsCache) {\n const layouts = await discoverLayouts(ssgConfig, root);\n const code = generateLayoutsModule(layouts, ssgConfig);\n layoutsCache = { layouts, code };\n }\n return layoutsCache.code;\n }\n\n // Generate virtual navigation module\n if (id === RESOLVED_VIRTUAL_NAVIGATION_ID) {\n if (!navigationCache) {\n // Ensure routes are loaded (reuse cache if available)\n if (!routesCache) {\n const routes = await scanPages(ssgConfig, root);\n const routesCode = config.command === 'serve'\n ? generateLazyRoutesModule(routes, ssgConfig)\n : generateRoutesModule(routes, ssgConfig);\n routesCache = { routes, code: routesCode };\n }\n const isDev = config.command === 'serve';\n const code = generateNavigationModule(routesCache.routes, ssgConfig, isDev);\n navigationCache = { code };\n }\n return navigationCache.code;\n }\n\n // Generate virtual config module\n if (id === RESOLVED_VIRTUAL_CONFIG_ID) {\n return `export default ${JSON.stringify(ssgConfig)};`;\n }\n\n // Generate virtual client entry (needs JSX transform)\n if (id === RESOLVED_VIRTUAL_CLIENT_ID) {\n const code = generateClientEntry(ssgConfig, entryDetection);\n const esbuild = await import('esbuild');\n const result = await esbuild.transform(code, {\n loader: 'tsx',\n jsx: 'automatic',\n jsxImportSource: 'sigx',\n });\n return result.code;\n }\n\n // Generate virtual server entry (needs JSX transform)\n if (id === RESOLVED_VIRTUAL_SERVER_ID) {\n const code = generateServerEntry(ssgConfig);\n const esbuild = await import('esbuild');\n const result = await esbuild.transform(code, {\n loader: 'tsx',\n jsx: 'automatic',\n jsxImportSource: 'sigx',\n });\n return result.code;\n }\n\n return null;\n },\n\n // Handle HMR for layouts and pages\n async handleHotUpdate({ file, server }) {\n const layoutsDir = path.resolve(root, ssgConfig.layouts || 'src/layouts');\n const pagesDir = path.resolve(root, ssgConfig.pages || 'src/pages');\n\n if (file.startsWith(layoutsDir)) {\n // Clear layout cache\n layoutsCache = null;\n\n // Invalidate the virtual layouts module\n const mod = server.moduleGraph.getModuleById(RESOLVED_VIRTUAL_LAYOUTS_ID);\n if (mod) {\n server.moduleGraph.invalidateModule(mod);\n }\n\n // Also reload pages that use this layout\n return [];\n }\n\n // Handle MDX/MD file changes in pages directory\n if (file.startsWith(pagesDir) && /\\.mdx?$/.test(file)) {\n // The MDX plugin handles the transform and HMR code injection.\n // We just need to ensure the module is properly invalidated.\n // Vite will handle the rest via the injected import.meta.hot.accept()\n \n // Return undefined to let Vite handle the standard HMR flow\n // The MDX file already has import.meta.hot.accept() injected\n return undefined;\n }\n\n return undefined;\n },\n };\n\n // Combine with MDX plugin if enabled (default: true)\n const enableMdx = options.enableMdx !== false;\n \n if (enableMdx) {\n const mdx = mdxPlugin({\n markdown: options.markdown,\n ssgConfig: undefined as any, // Will be set by configResolved\n });\n return [mainPlugin, mdx];\n }\n \n return [mainPlugin];\n}\n\n/**\n * Export the plugin as default\n */\nexport default ssgPlugin;\n"],"mappings":"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"}
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;GAKG
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/vite/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAiC,MAAM,MAAM,CAAC;AAGlE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAsB1C;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,OAAO,CAAC,SAAS,CAAC;IACxD;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB;AAQD;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,GAAE,gBAAqB,GAAG,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/vite/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAiC,MAAM,MAAM,CAAC;AAGlE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAsB1C;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,OAAO,CAAC,SAAS,CAAC;IACxD;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB;AAQD;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,GAAE,gBAAqB,GAAG,MAAM,EAAE,CAkTlE;AAED;;GAEG;eACY,SAAS"}
|
package/dist/vite/plugin.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as e } from "../plugin-
|
|
1
|
+
import { t as e } from "../plugin-aT6WUDzk.js";
|
|
2
2
|
export { e as default, e as ssgPlugin };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sigx/ssg",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "Static Site Generator for SignalX with file-based routing, MDX support, and pluggable themes",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"url": "https://github.com/signalxjs/ssg/issues"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
+
"@sigx/router": "^0.4.2",
|
|
60
61
|
"@sigx/server-renderer": "^0.4.1",
|
|
61
62
|
"esbuild": "^0.27.0",
|
|
62
63
|
"fast-glob": "^3.3.2",
|
|
@@ -73,8 +74,7 @@
|
|
|
73
74
|
"remark-rehype": "^11.1.1",
|
|
74
75
|
"shiki": "^1.24.0",
|
|
75
76
|
"unified": "^11.0.5",
|
|
76
|
-
"unist-util-visit": "^5.0.0"
|
|
77
|
-
"@sigx/router": "^0.4.1"
|
|
77
|
+
"unist-util-visit": "^5.0.0"
|
|
78
78
|
},
|
|
79
79
|
"peerDependencies": {
|
|
80
80
|
"@sigx/cli": "*",
|
package/src/client.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ambient type declarations for the virtual modules emitted by the
|
|
3
|
+
* `@sigx/ssg` Vite plugin. Add `"@sigx/ssg/virtual"` to `compilerOptions.types`
|
|
4
|
+
* in `tsconfig.json` to pick these up.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
declare module 'virtual:ssg-routes' {
|
|
8
|
+
/** Route record produced by the file-based router scanner. */
|
|
9
|
+
export interface SSGRouteRecord {
|
|
10
|
+
path: string;
|
|
11
|
+
name: string;
|
|
12
|
+
file: string;
|
|
13
|
+
component: unknown;
|
|
14
|
+
meta: Record<string, unknown> & { headings?: unknown };
|
|
15
|
+
layout?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const routes: SSGRouteRecord[];
|
|
19
|
+
export default routes;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
declare module 'virtual:ssg-navigation' {
|
|
23
|
+
import type { NavSection, CollectionConfig } from '@sigx/ssg';
|
|
24
|
+
|
|
25
|
+
/** Navigation tree for a single collection. */
|
|
26
|
+
export interface CollectionNavigation {
|
|
27
|
+
sidebar: NavSection[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Map of collection name → navigation tree. */
|
|
31
|
+
export const navigation: Record<string, CollectionNavigation>;
|
|
32
|
+
|
|
33
|
+
/** Look up navigation for a specific collection. */
|
|
34
|
+
export function getCollectionNav(name: string): CollectionNavigation | undefined;
|
|
35
|
+
|
|
36
|
+
/** Detect which collection a route path belongs to. */
|
|
37
|
+
export function detectCollection(path: string): string | undefined;
|
|
38
|
+
|
|
39
|
+
/** Get the sidebar sections for a collection (empty if not found). */
|
|
40
|
+
export function getSidebar(name: string): NavSection[];
|
|
41
|
+
|
|
42
|
+
const _default: {
|
|
43
|
+
navigation: typeof navigation;
|
|
44
|
+
collections: Record<string, CollectionConfig>;
|
|
45
|
+
getCollectionNav: typeof getCollectionNav;
|
|
46
|
+
detectCollection: typeof detectCollection;
|
|
47
|
+
getSidebar: typeof getSidebar;
|
|
48
|
+
};
|
|
49
|
+
export default _default;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
declare module 'virtual:generated-layouts' {
|
|
53
|
+
/** Annotates routes with their resolved layout component. */
|
|
54
|
+
export function setupLayouts<T>(routes: T): T;
|
|
55
|
+
|
|
56
|
+
/** Layout-aware router component that preserves layouts across navigations. */
|
|
57
|
+
export const LayoutRouter: unknown;
|
|
58
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"build-DP9zez3B.js","names":[],"sources":["../src/sitemap.ts","../src/build.ts"],"sourcesContent":["/**\n * Sitemap Generation\n *\n * Generates XML sitemaps for SSG sites following the sitemap protocol.\n * https://www.sitemaps.org/protocol.html\n */\n\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport type { SSGConfig, PageBuildResult } from './types';\n\n/**\n * Sitemap entry with optional metadata\n */\nexport interface SitemapEntry {\n /** URL path (relative to site base) */\n path: string;\n /** Last modification date */\n lastmod?: Date | string;\n /** Change frequency hint */\n changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';\n /** Priority relative to other pages (0.0 to 1.0) */\n priority?: number;\n}\n\n/**\n * Sitemap generation options\n */\nexport interface SitemapOptions {\n /** Include all built pages automatically */\n includePages?: boolean;\n /** Additional URLs to include */\n additionalUrls?: SitemapEntry[];\n /** URLs to exclude (glob patterns or exact matches) */\n exclude?: string[];\n /** Default change frequency */\n defaultChangefreq?: SitemapEntry['changefreq'];\n /** Default priority */\n defaultPriority?: number;\n}\n\n/**\n * Generate sitemap XML content\n */\nexport function generateSitemap(\n entries: SitemapEntry[],\n config: SSGConfig\n): string {\n const siteUrl = config.site?.url?.replace(/\\/$/, '') || '';\n const base = config.base?.replace(/\\/$/, '') || '';\n\n const urlEntries = entries.map((entry) => {\n const loc = `${siteUrl}${base}${entry.path}`;\n const lastmod = entry.lastmod\n ? typeof entry.lastmod === 'string'\n ? entry.lastmod\n : entry.lastmod.toISOString().split('T')[0]\n : undefined;\n\n return ` <url>\n <loc>${escapeXml(loc)}</loc>${lastmod ? `\n <lastmod>${lastmod}</lastmod>` : ''}${entry.changefreq ? `\n <changefreq>${entry.changefreq}</changefreq>` : ''}${entry.priority !== undefined ? `\n <priority>${entry.priority.toFixed(1)}</priority>` : ''}\n </url>`;\n });\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n${urlEntries.join('\\n')}\n</urlset>`;\n}\n\n/**\n * Generate robots.txt content\n */\nexport function generateRobotsTxt(config: SSGConfig, sitemapPath = '/sitemap.xml'): string {\n const siteUrl = config.site?.url?.replace(/\\/$/, '') || '';\n const base = config.base?.replace(/\\/$/, '') || '';\n\n return `User-agent: *\nAllow: /\n\nSitemap: ${siteUrl}${base}${sitemapPath}\n`;\n}\n\n/**\n * Convert page build results to sitemap entries\n */\nexport function pagesToSitemapEntries(\n pages: PageBuildResult[],\n options: SitemapOptions = {}\n): SitemapEntry[] {\n const {\n exclude = [],\n defaultChangefreq = 'weekly',\n defaultPriority = 0.5,\n } = options;\n\n return pages\n .filter((page) => {\n // Filter out excluded paths\n for (const pattern of exclude) {\n if (pattern.includes('*')) {\n // Simple glob matching\n const regex = new RegExp(\n '^' + pattern.replace(/\\*/g, '.*').replace(/\\?/g, '.') + '$'\n );\n if (regex.test(page.path)) return false;\n } else if (page.path === pattern) {\n return false;\n }\n }\n return true;\n })\n .map((page) => {\n // Determine priority based on path depth\n const depth = page.path.split('/').filter(Boolean).length;\n let priority = defaultPriority;\n \n if (page.path === '/') {\n priority = 1.0; // Homepage highest priority\n } else if (depth === 1) {\n priority = 0.8; // Top-level pages\n } else if (depth === 2) {\n priority = 0.6; // Second-level pages\n }\n\n return {\n path: page.path,\n changefreq: defaultChangefreq,\n priority,\n };\n });\n}\n\n/**\n * Write sitemap and robots.txt to output directory\n */\nexport async function writeSitemap(\n pages: PageBuildResult[],\n config: SSGConfig,\n outDir: string,\n options: SitemapOptions = {}\n): Promise<{ sitemapPath: string; robotsPath: string }> {\n // Generate entries from pages\n const entries = pagesToSitemapEntries(pages, options);\n\n // Add additional URLs if provided\n if (options.additionalUrls) {\n entries.push(...options.additionalUrls);\n }\n\n // Generate sitemap XML\n const sitemapContent = generateSitemap(entries, config);\n const sitemapPath = path.join(outDir, 'sitemap.xml');\n await fs.writeFile(sitemapPath, sitemapContent, 'utf-8');\n\n // Generate robots.txt\n const robotsContent = generateRobotsTxt(config);\n const robotsPath = path.join(outDir, 'robots.txt');\n await fs.writeFile(robotsPath, robotsContent, 'utf-8');\n\n return { sitemapPath, robotsPath };\n}\n\n/**\n * Escape special XML characters\n */\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n","/**\n * SSG Build CLI\n *\n * Static site generation build process:\n * 1. Load configuration\n * 2. Scan routes and expand dynamic paths\n * 3. Build with Vite for production\n * 4. Render each route to static HTML\n * 5. Write output files\n * 6. Generate sitemap and robots.txt\n */\n\nimport path from 'node:path';\nimport fs from 'node:fs/promises';\nimport fsSync from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { pathToFileURL } from 'node:url';\nimport type { SSGConfig, BuildOptions, BuildResult, PageBuildResult, SSGRoute, PageModule } from './types';\nimport { loadConfig, resolveConfigPaths } from './config';\nimport { scanPages, isDynamicRoute, extractParams, expandDynamicRoute } from './routing/index';\nimport { discoverLayouts } from './layouts/index';\nimport { writeSitemap } from './sitemap';\nimport {\n detectCustomEntries,\n generateClientEntry,\n generateServerEntry,\n generateProductionHtmlTemplate,\n VIRTUAL_CLIENT_ID,\n VIRTUAL_SERVER_ID,\n} from './vite/virtual-entries';\n\n/**\n * Build static site\n */\nexport async function build(options: BuildOptions = {}): Promise<BuildResult> {\n const startTime = Date.now();\n const root = process.cwd();\n const warnings: string[] = [];\n const pages: PageBuildResult[] = [];\n\n console.log('\\n🚀 @sigx/ssg - Building static site...\\n');\n\n // Step 1: Load configuration\n console.log('📦 Loading configuration...');\n const config = await loadConfig(options.configPath);\n const resolvedConfig = resolveConfigPaths(config, root);\n\n // Step 2: Scan routes\n console.log('🔍 Scanning pages...');\n const routes = await scanPages(resolvedConfig, root);\n console.log(` Found ${routes.length} page(s)`);\n\n // Step 3: Discover layouts\n console.log('📐 Discovering layouts...');\n const layouts = await discoverLayouts(resolvedConfig, root);\n console.log(` Found ${layouts.length} layout(s)`);\n\n // Step 4: Detect entry points\n const entryDetection = detectCustomEntries(root, resolvedConfig);\n if (entryDetection.useVirtualClient || entryDetection.useVirtualServer) {\n console.log('📦 Using zero-config mode');\n if (entryDetection.useVirtualClient) console.log(' → Virtual client entry');\n if (entryDetection.useVirtualServer) console.log(' → Virtual server entry');\n if (entryDetection.useVirtualHtml) console.log(' → Virtual HTML template');\n }\n\n // Get entry points (may create temp files for virtual entries)\n const clientEntry = await getClientEntryPoint(resolvedConfig, root);\n const ssrEntry = await getSSREntryPoint(resolvedConfig, root);\n\n // Always write HTML template for the build (either generated or updated from custom)\n // This ensures Vite processes it and outputs index.html\n const htmlTemplatePath = path.join(root, 'index.html');\n let cleanupHtml = false;\n let originalHtmlContent: string | null = null;\n \n // Save original HTML content if we're modifying a custom one\n if (!entryDetection.useVirtualHtml && fsSync.existsSync(htmlTemplatePath)) {\n originalHtmlContent = fsSync.readFileSync(htmlTemplatePath, 'utf-8');\n }\n \n const htmlContent = await getHtmlTemplate(resolvedConfig, root, clientEntry);\n fsSync.writeFileSync(htmlTemplatePath, htmlContent, 'utf-8');\n cleanupHtml = entryDetection.useVirtualHtml; // Only cleanup (delete) if we generated it\n\n // Step 5: Build with Vite\n console.log('🔨 Building with Vite...');\n const vite = await import('vite');\n\n try {\n // Build client bundle\n // Note: We don't empty the outDir since vite build may have already run\n // Always use HTML as input so Vite outputs index.html properly\n const clientInput = htmlTemplatePath;\n \n await vite.build({\n root,\n mode: 'production',\n build: {\n outDir: resolvedConfig.outDir,\n emptyOutDir: false,\n ssrManifest: true,\n rollupOptions: {\n input: clientInput,\n },\n },\n logLevel: options.verbose ? 'info' : 'warn',\n });\n\n // Build SSR bundle\n const ssrOutDir = path.join(resolvedConfig.outDir!, '.ssg');\n await vite.build({\n root,\n mode: 'production',\n build: {\n outDir: ssrOutDir,\n ssr: true,\n rollupOptions: {\n input: ssrEntry,\n },\n },\n logLevel: options.verbose ? 'info' : 'warn',\n });\n\n // Step 6: Collect all paths to render\n console.log('📝 Collecting paths to render...');\n const pathsToRender = await collectPaths(routes, root, warnings);\n console.log(` ${pathsToRender.length} path(s) to render`);\n\n // Pre-create all output directories to avoid mkdir contention during parallel rendering\n const outputDirs = new Set<string>();\n for (const pathInfo of pathsToRender) {\n const outputPath = getOutputPath(pathInfo.path, resolvedConfig.outDir!);\n outputDirs.add(path.dirname(outputPath));\n }\n await Promise.all(\n Array.from(outputDirs).map(dir => fs.mkdir(dir, { recursive: true }))\n );\n\n // Step 7: Render each path to HTML\n console.log('🎨 Rendering pages...');\n\n // Determine the SSR entry output name (based on input file name)\n const ssrEntryBasename = path.basename(ssrEntry, path.extname(ssrEntry));\n const ssrEntryName = ssrEntryBasename + '.js';\n\n // Pre-load the SSR module once for all pages\n const entryPath = path.join(ssrOutDir, ssrEntryName);\n const entryModule = await import(pathToFileURL(entryPath).href);\n\n // Load HTML template once\n const templatePath = path.join(resolvedConfig.outDir!, 'index.html');\n const template = await fs.readFile(templatePath, 'utf-8');\n\n // Parallel rendering configuration - higher concurrency since rendering is CPU-bound\n const CONCURRENCY = options.concurrency ?? 20; // Number of pages to render in parallel\n const verbose = options.verbose ?? false;\n\n interface RenderResult {\n pathInfo: PathToRender;\n html: string;\n outputPath: string;\n renderTime: number;\n }\n\n // Render a single page (CPU-bound, no I/O)\n async function renderPage(pathInfo: PathToRender): Promise<RenderResult | null> {\n const renderStart = Date.now();\n\n try {\n // Render the app\n const appHtml = await entryModule.render(pathInfo.path, {\n params: pathInfo.params,\n props: pathInfo.props,\n });\n\n // Inject into template\n let html = template.replace('<!--app-html-->', appHtml);\n const headTags = generateHeadTags(pathInfo, resolvedConfig);\n html = html.replace('<!--head-tags-->', headTags);\n\n const outputPath = getOutputPath(pathInfo.path, resolvedConfig.outDir!);\n const renderTime = Date.now() - renderStart;\n\n return { pathInfo, html, outputPath, renderTime };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n console.error(` ❌ ${pathInfo.path}: ${errorMessage}`);\n warnings.push(`Failed to render ${pathInfo.path}: ${errorMessage}`);\n return null;\n }\n }\n\n // Render all pages in parallel batches (CPU-bound, no I/O)\n console.log(' Phase 1: Rendering...');\n const renderPhaseStart = Date.now();\n const renderResults: RenderResult[] = [];\n \n for (let i = 0; i < pathsToRender.length; i += CONCURRENCY) {\n const batch = pathsToRender.slice(i, i + CONCURRENCY);\n const results = await Promise.all(batch.map(renderPage));\n \n for (const result of results) {\n if (result) {\n renderResults.push(result);\n }\n }\n }\n const renderPhaseDuration = Date.now() - renderPhaseStart;\n console.log(` Phase 1 complete: ${renderResults.length} pages in ${renderPhaseDuration}ms (${Math.round(renderPhaseDuration / renderResults.length)}ms avg)`);\n\n // Write all files in parallel with limited concurrency\n console.log(' Phase 2: Writing files...');\n const writePhaseStart = Date.now();\n const WRITE_CONCURRENCY = 10; // Limit parallel writes to reduce I/O contention\n \n for (let i = 0; i < renderResults.length; i += WRITE_CONCURRENCY) {\n const batch = renderResults.slice(i, i + WRITE_CONCURRENCY);\n await Promise.all(batch.map(async (result) => {\n await fs.writeFile(result.outputPath, result.html, 'utf-8');\n const size = Buffer.byteLength(result.html, 'utf-8');\n\n pages.push({\n path: result.pathInfo.path,\n file: result.outputPath,\n time: result.renderTime,\n size,\n });\n \n if (verbose) {\n console.log(` ✓ ${result.pathInfo.path} (${result.renderTime}ms, ${formatBytes(size)})`);\n }\n }));\n }\n const writePhaseDuration = Date.now() - writePhaseStart;\n console.log(` Phase 2 complete: ${renderResults.length} files in ${writePhaseDuration}ms`);\n \n // Summary (non-verbose)\n if (!verbose) {\n console.log(` ✓ Rendered ${renderResults.length} pages`);\n }\n\n // Step 8: Clean up SSR build\n await fs.rm(ssrOutDir, { recursive: true, force: true });\n\n // Step 9: Generate sitemap and robots.txt\n if (pages.length > 0) {\n console.log('🗺️ Generating sitemap...');\n await writeSitemap(pages, resolvedConfig, resolvedConfig.outDir!);\n console.log(' ✓ sitemap.xml');\n console.log(' ✓ robots.txt');\n }\n\n } finally {\n // Clean up temporary entry files\n await cleanupTempEntries(root);\n \n // Clean up or restore HTML template\n if (cleanupHtml) {\n // We generated a virtual HTML, remove it\n try {\n await fs.unlink(htmlTemplatePath);\n } catch {\n // Ignore\n }\n } else if (originalHtmlContent !== null) {\n // We modified a custom HTML, restore the original\n try {\n await fs.writeFile(htmlTemplatePath, originalHtmlContent, 'utf-8');\n } catch {\n // Ignore\n }\n }\n }\n\n // Done\n const totalTime = Date.now() - startTime;\n\n console.log(`\\n✅ Built ${pages.length} page(s) in ${totalTime}ms`);\n\n if (warnings.length > 0) {\n console.log(`\\n⚠️ ${warnings.length} warning(s):`);\n for (const warning of warnings) {\n console.log(` - ${warning}`);\n }\n }\n\n console.log(`\\n📁 Output: ${resolvedConfig.outDir}\\n`);\n\n return {\n pages,\n totalTime,\n warnings,\n };\n}\n\n/**\n * Path information for rendering\n */\ninterface PathToRender {\n path: string;\n route: SSGRoute;\n params: Record<string, string>;\n props?: Record<string, unknown>;\n}\n\n/**\n * Collect all paths to render, expanding dynamic routes\n */\nasync function collectPaths(\n routes: SSGRoute[],\n root: string,\n warnings: string[]\n): Promise<PathToRender[]> {\n const paths: PathToRender[] = [];\n\n for (const route of routes) {\n if (isDynamicRoute(route)) {\n // Load module and call getStaticPaths\n try {\n const moduleUrl = pathToFileURL(route.file).href;\n const pageModule = (await import(moduleUrl)) as PageModule;\n\n if (!pageModule.getStaticPaths) {\n const params = extractParams(route.path).join(', ');\n console.warn(\n `\\n⚠️ SSG102: Dynamic route missing getStaticPaths()\\n` +\n ` 📁 ${route.file}\\n` +\n ` Route: ${route.path} (params: ${params})\\n` +\n ` 💡 Export getStaticPaths() to generate static pages:\\n\\n` +\n ` export async function getStaticPaths() {\\n` +\n ` return [{ params: { ${params.split(', ')[0]}: 'value' } }];\\n` +\n ` }\\n`\n );\n warnings.push(\n `Route ${route.path} has dynamic segments [${params}] but no getStaticPaths() export. Skipping.`\n );\n continue;\n }\n\n const staticPaths = await pageModule.getStaticPaths();\n\n for (const staticPath of staticPaths) {\n const expandedPaths = expandDynamicRoute(route, [staticPath]);\n for (const expandedPath of expandedPaths) {\n paths.push({\n path: expandedPath,\n route,\n params: staticPath.params,\n props: staticPath.props,\n });\n }\n }\n } catch (err) {\n warnings.push(`Failed to load ${route.file}: ${err}`);\n }\n } else {\n paths.push({\n path: route.path,\n route,\n params: {},\n });\n }\n }\n\n return paths;\n}\n\n/**\n * Render a page to HTML\n */\nasync function renderPage(\n pathInfo: PathToRender,\n config: SSGConfig,\n ssrOutDir: string,\n ssrEntryName: string\n): Promise<string> {\n // Load the SSR entry module\n // This should export a render function that returns HTML string\n const entryPath = path.join(ssrOutDir, ssrEntryName);\n const entryModule = await import(pathToFileURL(entryPath).href);\n\n // Render the app - the entry module's render function handles SSR\n const appHtml = await entryModule.render(pathInfo.path, {\n params: pathInfo.params,\n props: pathInfo.props,\n });\n\n // Load HTML template\n const templatePath = path.join(config.outDir!, 'index.html');\n let template = await fs.readFile(templatePath, 'utf-8');\n\n // Inject app HTML\n template = template.replace('<!--app-html-->', appHtml);\n\n // Inject head tags if any\n const headTags = generateHeadTags(pathInfo, config);\n template = template.replace('<!--head-tags-->', headTags);\n\n return template;\n}\n\n/**\n * Generate head tags for a page\n */\nfunction generateHeadTags(pathInfo: PathToRender, config: SSGConfig): string {\n const tags: string[] = [];\n const meta = pathInfo.route.meta || {};\n\n // Title\n const title = meta.title || config.site?.title;\n if (title) {\n tags.push(`<title>${escapeHtml(title)}</title>`);\n }\n\n // Description\n const description = meta.description || config.site?.description;\n if (description) {\n tags.push(`<meta name=\"description\" content=\"${escapeHtml(description)}\">`);\n }\n\n // Canonical URL\n if (config.site?.url) {\n const canonical = new URL(pathInfo.path, config.site.url).href;\n tags.push(`<link rel=\"canonical\" href=\"${canonical}\">`);\n }\n\n return tags.join('\\n ');\n}\n\n/**\n * Get output file path for a URL path\n */\nfunction getOutputPath(urlPath: string, outDir: string): string {\n // Normalize path\n let normalized = urlPath.replace(/^\\//, '').replace(/\\/$/, '');\n\n if (!normalized) {\n normalized = 'index';\n }\n\n // Add .html extension or /index.html for directories\n if (!normalized.endsWith('.html')) {\n normalized = path.join(normalized, 'index.html');\n }\n\n return path.join(outDir, normalized);\n}\n\n/**\n * Get SSR entry point\n * Returns virtual module ID if no custom entry exists\n */\nasync function getSSREntryPoint(config: SSGConfig, root: string): Promise<string> {\n // Check for custom entry points\n const detection = detectCustomEntries(root, config);\n \n if (!detection.useVirtualServer && detection.customServerPath) {\n return detection.customServerPath;\n }\n\n // Use virtual server entry - we need to write a temp file for the build\n // because Rollup input must be a real file path\n const virtualServerCode = generateServerEntry(config);\n const tempServerPath = path.join(root, '.ssg-temp-entry-server.tsx');\n fsSync.writeFileSync(tempServerPath, virtualServerCode, 'utf-8');\n \n return tempServerPath;\n}\n\n/**\n * Get client entry point\n * Returns virtual module ID if no custom entry exists\n */\nasync function getClientEntryPoint(config: SSGConfig, root: string): Promise<string> {\n const detection = detectCustomEntries(root, config);\n \n if (!detection.useVirtualClient && detection.customClientPath) {\n return detection.customClientPath;\n }\n\n // Use virtual client entry - write a temp file\n const virtualClientCode = generateClientEntry(config, detection);\n const tempClientPath = path.join(root, '.ssg-temp-entry-client.tsx');\n fsSync.writeFileSync(tempClientPath, virtualClientCode, 'utf-8');\n \n return tempClientPath;\n}\n\n/**\n * Clean up temporary entry files\n */\nasync function cleanupTempEntries(root: string): Promise<void> {\n const tempFiles = [\n path.join(root, '.ssg-temp-entry-server.tsx'),\n path.join(root, '.ssg-temp-entry-client.tsx'),\n ];\n \n for (const file of tempFiles) {\n try {\n await fs.unlink(file);\n } catch {\n // Ignore if file doesn't exist\n }\n }\n}\n\n/**\n * Get or generate HTML template\n */\nasync function getHtmlTemplate(config: SSGConfig, root: string, clientEntryPath: string): Promise<string> {\n const detection = detectCustomEntries(root, config);\n \n if (!detection.useVirtualHtml && detection.customHtmlPath) {\n // Read custom HTML and update the script src to point to the temp entry file\n let html = await fs.readFile(detection.customHtmlPath, 'utf-8');\n // Replace any virtual SSG client paths with the actual temp entry path\n // Use relative path from root for the script src\n const relativePath = './' + path.relative(root, clientEntryPath).replace(/\\\\/g, '/');\n html = html.replace(\n /<script([^>]*)\\s+src=[\"']?\\/@ssg\\/client\\.tsx[\"']?/g,\n `<script$1 src=\"${relativePath}\"`\n );\n return html;\n }\n\n // Generate virtual HTML template\n return generateProductionHtmlTemplate(config, clientEntryPath);\n}\n\n/**\n * Format bytes to human-readable string\n */\nfunction formatBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes}B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\n}\n\n/**\n * Escape HTML special characters\n */\nfunction escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n"],"mappings":"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"}
|