@sigx/ssg 0.1.6 → 0.1.7

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.
@@ -0,0 +1,317 @@
1
+ import { A as resolveConfigPaths, C as extractParams, S as expandDynamicRoute, T as scanPages, k as loadConfig, l as generateProductionHtmlTemplate, m as discoverLayouts, o as detectCustomEntries, s as generateClientEntry, u as generateServerEntry, w as isDynamicRoute } from "./virtual-entries-sVkqKMAM.js";
2
+ import path from "node:path";
3
+ import fs from "node:fs/promises";
4
+ import fsSync from "node:fs";
5
+ import { pathToFileURL } from "node:url";
6
+ function generateSitemap(entries, config) {
7
+ const siteUrl = config.site?.url?.replace(/\/$/, "") || "";
8
+ const base = config.base?.replace(/\/$/, "") || "";
9
+ return `<?xml version="1.0" encoding="UTF-8"?>
10
+ <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
11
+ ${entries.map((entry) => {
12
+ const loc = `${siteUrl}${base}${entry.path}`;
13
+ const lastmod = entry.lastmod ? typeof entry.lastmod === "string" ? entry.lastmod : entry.lastmod.toISOString().split("T")[0] : void 0;
14
+ return ` <url>
15
+ <loc>${escapeXml(loc)}</loc>${lastmod ? `
16
+ <lastmod>${lastmod}</lastmod>` : ""}${entry.changefreq ? `
17
+ <changefreq>${entry.changefreq}</changefreq>` : ""}${entry.priority !== void 0 ? `
18
+ <priority>${entry.priority.toFixed(1)}</priority>` : ""}
19
+ </url>`;
20
+ }).join("\n")}
21
+ </urlset>`;
22
+ }
23
+ function generateRobotsTxt(config, sitemapPath = "/sitemap.xml") {
24
+ return `User-agent: *
25
+ Allow: /
26
+
27
+ Sitemap: ${config.site?.url?.replace(/\/$/, "") || ""}${config.base?.replace(/\/$/, "") || ""}${sitemapPath}
28
+ `;
29
+ }
30
+ function pagesToSitemapEntries(pages, options = {}) {
31
+ const { exclude = [], defaultChangefreq = "weekly", defaultPriority = .5 } = options;
32
+ return pages.filter((page) => {
33
+ for (const pattern of exclude) if (pattern.includes("*")) {
34
+ if (new RegExp("^" + pattern.replace(/\*/g, ".*").replace(/\?/g, ".") + "$").test(page.path)) return false;
35
+ } else if (page.path === pattern) return false;
36
+ return true;
37
+ }).map((page) => {
38
+ const depth = page.path.split("/").filter(Boolean).length;
39
+ let priority = defaultPriority;
40
+ if (page.path === "/") priority = 1;
41
+ else if (depth === 1) priority = .8;
42
+ else if (depth === 2) priority = .6;
43
+ return {
44
+ path: page.path,
45
+ changefreq: defaultChangefreq,
46
+ priority
47
+ };
48
+ });
49
+ }
50
+ async function writeSitemap(pages, config, outDir, options = {}) {
51
+ const entries = pagesToSitemapEntries(pages, options);
52
+ if (options.additionalUrls) entries.push(...options.additionalUrls);
53
+ const sitemapContent = generateSitemap(entries, config);
54
+ const sitemapPath = path.join(outDir, "sitemap.xml");
55
+ await fs.writeFile(sitemapPath, sitemapContent, "utf-8");
56
+ const robotsContent = generateRobotsTxt(config);
57
+ const robotsPath = path.join(outDir, "robots.txt");
58
+ await fs.writeFile(robotsPath, robotsContent, "utf-8");
59
+ return {
60
+ sitemapPath,
61
+ robotsPath
62
+ };
63
+ }
64
+ function escapeXml(str) {
65
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
66
+ }
67
+ async function build(options = {}) {
68
+ const startTime = Date.now();
69
+ const root = process.cwd();
70
+ const warnings = [];
71
+ const pages = [];
72
+ console.log("\n🚀 @sigx/ssg - Building static site...\n");
73
+ console.log("📦 Loading configuration...");
74
+ const resolvedConfig = resolveConfigPaths(await loadConfig(options.configPath), root);
75
+ console.log("🔍 Scanning pages...");
76
+ const routes = await scanPages(resolvedConfig, root);
77
+ console.log(` Found ${routes.length} page(s)`);
78
+ console.log("📐 Discovering layouts...");
79
+ const layouts = await discoverLayouts(resolvedConfig, root);
80
+ console.log(` Found ${layouts.length} layout(s)`);
81
+ const entryDetection = detectCustomEntries(root, resolvedConfig);
82
+ if (entryDetection.useVirtualClient || entryDetection.useVirtualServer) {
83
+ console.log("📦 Using zero-config mode");
84
+ if (entryDetection.useVirtualClient) console.log(" → Virtual client entry");
85
+ if (entryDetection.useVirtualServer) console.log(" → Virtual server entry");
86
+ if (entryDetection.useVirtualHtml) console.log(" → Virtual HTML template");
87
+ }
88
+ const clientEntry = await getClientEntryPoint(resolvedConfig, root);
89
+ const ssrEntry = await getSSREntryPoint(resolvedConfig, root);
90
+ const htmlTemplatePath = path.join(root, "index.html");
91
+ let cleanupHtml = false;
92
+ let originalHtmlContent = null;
93
+ if (!entryDetection.useVirtualHtml && fsSync.existsSync(htmlTemplatePath)) originalHtmlContent = fsSync.readFileSync(htmlTemplatePath, "utf-8");
94
+ const htmlContent = await getHtmlTemplate(resolvedConfig, root, clientEntry);
95
+ fsSync.writeFileSync(htmlTemplatePath, htmlContent, "utf-8");
96
+ cleanupHtml = entryDetection.useVirtualHtml;
97
+ console.log("🔨 Building with Vite...");
98
+ const vite = await import("vite");
99
+ try {
100
+ const clientInput = htmlTemplatePath;
101
+ await vite.build({
102
+ root,
103
+ mode: "production",
104
+ build: {
105
+ outDir: resolvedConfig.outDir,
106
+ emptyOutDir: false,
107
+ ssrManifest: true,
108
+ rollupOptions: { input: clientInput }
109
+ },
110
+ logLevel: options.verbose ? "info" : "warn"
111
+ });
112
+ const ssrOutDir = path.join(resolvedConfig.outDir, ".ssg");
113
+ await vite.build({
114
+ root,
115
+ mode: "production",
116
+ build: {
117
+ outDir: ssrOutDir,
118
+ ssr: true,
119
+ rollupOptions: { input: ssrEntry }
120
+ },
121
+ logLevel: options.verbose ? "info" : "warn"
122
+ });
123
+ console.log("📝 Collecting paths to render...");
124
+ const pathsToRender = await collectPaths(routes, root, warnings);
125
+ console.log(` ${pathsToRender.length} path(s) to render`);
126
+ const outputDirs = /* @__PURE__ */ new Set();
127
+ for (const pathInfo of pathsToRender) {
128
+ const outputPath = getOutputPath(pathInfo.path, resolvedConfig.outDir);
129
+ outputDirs.add(path.dirname(outputPath));
130
+ }
131
+ await Promise.all(Array.from(outputDirs).map((dir) => fs.mkdir(dir, { recursive: true })));
132
+ console.log("🎨 Rendering pages...");
133
+ const ssrEntryName = path.basename(ssrEntry, path.extname(ssrEntry)) + ".js";
134
+ const entryModule = await import(pathToFileURL(path.join(ssrOutDir, ssrEntryName)).href);
135
+ const templatePath = path.join(resolvedConfig.outDir, "index.html");
136
+ const template = await fs.readFile(templatePath, "utf-8");
137
+ const CONCURRENCY = options.concurrency ?? 20;
138
+ const verbose = options.verbose ?? false;
139
+ async function renderPage(pathInfo) {
140
+ const renderStart = Date.now();
141
+ try {
142
+ const appHtml = await entryModule.render(pathInfo.path, {
143
+ params: pathInfo.params,
144
+ props: pathInfo.props
145
+ });
146
+ let html = template.replace("<!--app-html-->", appHtml);
147
+ const headTags = generateHeadTags(pathInfo, resolvedConfig);
148
+ html = html.replace("<!--head-tags-->", headTags);
149
+ const outputPath = getOutputPath(pathInfo.path, resolvedConfig.outDir);
150
+ const renderTime = Date.now() - renderStart;
151
+ return {
152
+ pathInfo,
153
+ html,
154
+ outputPath,
155
+ renderTime
156
+ };
157
+ } catch (err) {
158
+ const errorMessage = err instanceof Error ? err.message : String(err);
159
+ console.error(` ❌ ${pathInfo.path}: ${errorMessage}`);
160
+ warnings.push(`Failed to render ${pathInfo.path}: ${errorMessage}`);
161
+ return null;
162
+ }
163
+ }
164
+ console.log(" Phase 1: Rendering...");
165
+ const renderPhaseStart = Date.now();
166
+ const renderResults = [];
167
+ for (let i = 0; i < pathsToRender.length; i += CONCURRENCY) {
168
+ const batch = pathsToRender.slice(i, i + CONCURRENCY);
169
+ const results = await Promise.all(batch.map(renderPage));
170
+ for (const result of results) if (result) renderResults.push(result);
171
+ }
172
+ const renderPhaseDuration = Date.now() - renderPhaseStart;
173
+ console.log(` Phase 1 complete: ${renderResults.length} pages in ${renderPhaseDuration}ms (${Math.round(renderPhaseDuration / renderResults.length)}ms avg)`);
174
+ console.log(" Phase 2: Writing files...");
175
+ const writePhaseStart = Date.now();
176
+ const WRITE_CONCURRENCY = 10;
177
+ for (let i = 0; i < renderResults.length; i += WRITE_CONCURRENCY) {
178
+ const batch = renderResults.slice(i, i + WRITE_CONCURRENCY);
179
+ await Promise.all(batch.map(async (result) => {
180
+ await fs.writeFile(result.outputPath, result.html, "utf-8");
181
+ const size = Buffer.byteLength(result.html, "utf-8");
182
+ pages.push({
183
+ path: result.pathInfo.path,
184
+ file: result.outputPath,
185
+ time: result.renderTime,
186
+ size
187
+ });
188
+ if (verbose) console.log(` ✓ ${result.pathInfo.path} (${result.renderTime}ms, ${formatBytes(size)})`);
189
+ }));
190
+ }
191
+ const writePhaseDuration = Date.now() - writePhaseStart;
192
+ console.log(` Phase 2 complete: ${renderResults.length} files in ${writePhaseDuration}ms`);
193
+ if (!verbose) console.log(` ✓ Rendered ${renderResults.length} pages`);
194
+ await fs.rm(ssrOutDir, {
195
+ recursive: true,
196
+ force: true
197
+ });
198
+ if (pages.length > 0) {
199
+ console.log("🗺️ Generating sitemap...");
200
+ await writeSitemap(pages, resolvedConfig, resolvedConfig.outDir);
201
+ console.log(" ✓ sitemap.xml");
202
+ console.log(" ✓ robots.txt");
203
+ }
204
+ } finally {
205
+ await cleanupTempEntries(root);
206
+ if (cleanupHtml) try {
207
+ await fs.unlink(htmlTemplatePath);
208
+ } catch {}
209
+ else if (originalHtmlContent !== null) try {
210
+ await fs.writeFile(htmlTemplatePath, originalHtmlContent, "utf-8");
211
+ } catch {}
212
+ }
213
+ const totalTime = Date.now() - startTime;
214
+ console.log(`\n✅ Built ${pages.length} page(s) in ${totalTime}ms`);
215
+ if (warnings.length > 0) {
216
+ console.log(`\n⚠️ ${warnings.length} warning(s):`);
217
+ for (const warning of warnings) console.log(` - ${warning}`);
218
+ }
219
+ console.log(`\n📁 Output: ${resolvedConfig.outDir}\n`);
220
+ return {
221
+ pages,
222
+ totalTime,
223
+ warnings
224
+ };
225
+ }
226
+ async function collectPaths(routes, root, warnings) {
227
+ const paths = [];
228
+ for (const route of routes) if (isDynamicRoute(route)) try {
229
+ const pageModule = await import(pathToFileURL(route.file).href);
230
+ if (!pageModule.getStaticPaths) {
231
+ const params = extractParams(route.path).join(", ");
232
+ console.warn(`\n⚠️ SSG102: Dynamic route missing getStaticPaths()\n 📁 ${route.file}\n Route: ${route.path} (params: ${params})\n 💡 Export getStaticPaths() to generate static pages:\n\n export async function getStaticPaths() {\n return [{ params: { ${params.split(", ")[0]}: 'value' } }];\n }\n`);
233
+ warnings.push(`Route ${route.path} has dynamic segments [${params}] but no getStaticPaths() export. Skipping.`);
234
+ continue;
235
+ }
236
+ const staticPaths = await pageModule.getStaticPaths();
237
+ for (const staticPath of staticPaths) {
238
+ const expandedPaths = expandDynamicRoute(route, [staticPath]);
239
+ for (const expandedPath of expandedPaths) paths.push({
240
+ path: expandedPath,
241
+ route,
242
+ params: staticPath.params,
243
+ props: staticPath.props
244
+ });
245
+ }
246
+ } catch (err) {
247
+ warnings.push(`Failed to load ${route.file}: ${err}`);
248
+ }
249
+ else paths.push({
250
+ path: route.path,
251
+ route,
252
+ params: {}
253
+ });
254
+ return paths;
255
+ }
256
+ function generateHeadTags(pathInfo, config) {
257
+ const tags = [];
258
+ const meta = pathInfo.route.meta || {};
259
+ const title = meta.title || config.site?.title;
260
+ if (title) tags.push(`<title>${escapeHtml(title)}</title>`);
261
+ const description = meta.description || config.site?.description;
262
+ if (description) tags.push(`<meta name="description" content="${escapeHtml(description)}">`);
263
+ if (config.site?.url) {
264
+ const canonical = new URL(pathInfo.path, config.site.url).href;
265
+ tags.push(`<link rel="canonical" href="${canonical}">`);
266
+ }
267
+ return tags.join("\n ");
268
+ }
269
+ function getOutputPath(urlPath, outDir) {
270
+ let normalized = urlPath.replace(/^\//, "").replace(/\/$/, "");
271
+ if (!normalized) normalized = "index";
272
+ if (!normalized.endsWith(".html")) normalized = path.join(normalized, "index.html");
273
+ return path.join(outDir, normalized);
274
+ }
275
+ async function getSSREntryPoint(config, root) {
276
+ const detection = detectCustomEntries(root, config);
277
+ if (!detection.useVirtualServer && detection.customServerPath) return detection.customServerPath;
278
+ const virtualServerCode = generateServerEntry(config);
279
+ const tempServerPath = path.join(root, ".ssg-temp-entry-server.tsx");
280
+ fsSync.writeFileSync(tempServerPath, virtualServerCode, "utf-8");
281
+ return tempServerPath;
282
+ }
283
+ async function getClientEntryPoint(config, root) {
284
+ const detection = detectCustomEntries(root, config);
285
+ if (!detection.useVirtualClient && detection.customClientPath) return detection.customClientPath;
286
+ const virtualClientCode = generateClientEntry(config, detection);
287
+ const tempClientPath = path.join(root, ".ssg-temp-entry-client.tsx");
288
+ fsSync.writeFileSync(tempClientPath, virtualClientCode, "utf-8");
289
+ return tempClientPath;
290
+ }
291
+ async function cleanupTempEntries(root) {
292
+ const tempFiles = [path.join(root, ".ssg-temp-entry-server.tsx"), path.join(root, ".ssg-temp-entry-client.tsx")];
293
+ for (const file of tempFiles) try {
294
+ await fs.unlink(file);
295
+ } catch {}
296
+ }
297
+ async function getHtmlTemplate(config, root, clientEntryPath) {
298
+ const detection = detectCustomEntries(root, config);
299
+ if (!detection.useVirtualHtml && detection.customHtmlPath) {
300
+ let html = await fs.readFile(detection.customHtmlPath, "utf-8");
301
+ const relativePath = "./" + path.relative(root, clientEntryPath).replace(/\\/g, "/");
302
+ html = html.replace(/<script([^>]*)\s+src=["']?\/@ssg\/client\.tsx["']?/g, `<script$1 src="${relativePath}"`);
303
+ return html;
304
+ }
305
+ return generateProductionHtmlTemplate(config, clientEntryPath);
306
+ }
307
+ function formatBytes(bytes) {
308
+ if (bytes < 1024) return `${bytes}B`;
309
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
310
+ return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
311
+ }
312
+ function escapeHtml(str) {
313
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
314
+ }
315
+ export { writeSitemap as a, pagesToSitemapEntries as i, generateRobotsTxt as n, generateSitemap as r, build as t };
316
+
317
+ //# sourceMappingURL=build-CfOi93wa.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-CfOi93wa.js","names":[],"sources":["../src/sitemap.ts","../src/build.ts"],"sourcesContent":["/**\r\n * Sitemap Generation\r\n *\r\n * Generates XML sitemaps for SSG sites following the sitemap protocol.\r\n * https://www.sitemaps.org/protocol.html\r\n */\r\n\r\nimport fs from 'node:fs/promises';\r\nimport path from 'node:path';\r\nimport type { SSGConfig, PageBuildResult } from './types';\r\n\r\n/**\r\n * Sitemap entry with optional metadata\r\n */\r\nexport interface SitemapEntry {\r\n /** URL path (relative to site base) */\r\n path: string;\r\n /** Last modification date */\r\n lastmod?: Date | string;\r\n /** Change frequency hint */\r\n changefreq?: 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';\r\n /** Priority relative to other pages (0.0 to 1.0) */\r\n priority?: number;\r\n}\r\n\r\n/**\r\n * Sitemap generation options\r\n */\r\nexport interface SitemapOptions {\r\n /** Include all built pages automatically */\r\n includePages?: boolean;\r\n /** Additional URLs to include */\r\n additionalUrls?: SitemapEntry[];\r\n /** URLs to exclude (glob patterns or exact matches) */\r\n exclude?: string[];\r\n /** Default change frequency */\r\n defaultChangefreq?: SitemapEntry['changefreq'];\r\n /** Default priority */\r\n defaultPriority?: number;\r\n}\r\n\r\n/**\r\n * Generate sitemap XML content\r\n */\r\nexport function generateSitemap(\r\n entries: SitemapEntry[],\r\n config: SSGConfig\r\n): string {\r\n const siteUrl = config.site?.url?.replace(/\\/$/, '') || '';\r\n const base = config.base?.replace(/\\/$/, '') || '';\r\n\r\n const urlEntries = entries.map((entry) => {\r\n const loc = `${siteUrl}${base}${entry.path}`;\r\n const lastmod = entry.lastmod\r\n ? typeof entry.lastmod === 'string'\r\n ? entry.lastmod\r\n : entry.lastmod.toISOString().split('T')[0]\r\n : undefined;\r\n\r\n return ` <url>\r\n <loc>${escapeXml(loc)}</loc>${lastmod ? `\r\n <lastmod>${lastmod}</lastmod>` : ''}${entry.changefreq ? `\r\n <changefreq>${entry.changefreq}</changefreq>` : ''}${entry.priority !== undefined ? `\r\n <priority>${entry.priority.toFixed(1)}</priority>` : ''}\r\n </url>`;\r\n });\r\n\r\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\r\n${urlEntries.join('\\n')}\r\n</urlset>`;\r\n}\r\n\r\n/**\r\n * Generate robots.txt content\r\n */\r\nexport function generateRobotsTxt(config: SSGConfig, sitemapPath = '/sitemap.xml'): string {\r\n const siteUrl = config.site?.url?.replace(/\\/$/, '') || '';\r\n const base = config.base?.replace(/\\/$/, '') || '';\r\n\r\n return `User-agent: *\r\nAllow: /\r\n\r\nSitemap: ${siteUrl}${base}${sitemapPath}\r\n`;\r\n}\r\n\r\n/**\r\n * Convert page build results to sitemap entries\r\n */\r\nexport function pagesToSitemapEntries(\r\n pages: PageBuildResult[],\r\n options: SitemapOptions = {}\r\n): SitemapEntry[] {\r\n const {\r\n exclude = [],\r\n defaultChangefreq = 'weekly',\r\n defaultPriority = 0.5,\r\n } = options;\r\n\r\n return pages\r\n .filter((page) => {\r\n // Filter out excluded paths\r\n for (const pattern of exclude) {\r\n if (pattern.includes('*')) {\r\n // Simple glob matching\r\n const regex = new RegExp(\r\n '^' + pattern.replace(/\\*/g, '.*').replace(/\\?/g, '.') + '$'\r\n );\r\n if (regex.test(page.path)) return false;\r\n } else if (page.path === pattern) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n })\r\n .map((page) => {\r\n // Determine priority based on path depth\r\n const depth = page.path.split('/').filter(Boolean).length;\r\n let priority = defaultPriority;\r\n \r\n if (page.path === '/') {\r\n priority = 1.0; // Homepage highest priority\r\n } else if (depth === 1) {\r\n priority = 0.8; // Top-level pages\r\n } else if (depth === 2) {\r\n priority = 0.6; // Second-level pages\r\n }\r\n\r\n return {\r\n path: page.path,\r\n changefreq: defaultChangefreq,\r\n priority,\r\n };\r\n });\r\n}\r\n\r\n/**\r\n * Write sitemap and robots.txt to output directory\r\n */\r\nexport async function writeSitemap(\r\n pages: PageBuildResult[],\r\n config: SSGConfig,\r\n outDir: string,\r\n options: SitemapOptions = {}\r\n): Promise<{ sitemapPath: string; robotsPath: string }> {\r\n // Generate entries from pages\r\n const entries = pagesToSitemapEntries(pages, options);\r\n\r\n // Add additional URLs if provided\r\n if (options.additionalUrls) {\r\n entries.push(...options.additionalUrls);\r\n }\r\n\r\n // Generate sitemap XML\r\n const sitemapContent = generateSitemap(entries, config);\r\n const sitemapPath = path.join(outDir, 'sitemap.xml');\r\n await fs.writeFile(sitemapPath, sitemapContent, 'utf-8');\r\n\r\n // Generate robots.txt\r\n const robotsContent = generateRobotsTxt(config);\r\n const robotsPath = path.join(outDir, 'robots.txt');\r\n await fs.writeFile(robotsPath, robotsContent, 'utf-8');\r\n\r\n return { sitemapPath, robotsPath };\r\n}\r\n\r\n/**\r\n * Escape special XML characters\r\n */\r\nfunction escapeXml(str: string): string {\r\n return str\r\n .replace(/&/g, '&amp;')\r\n .replace(/</g, '&lt;')\r\n .replace(/>/g, '&gt;')\r\n .replace(/\"/g, '&quot;')\r\n .replace(/'/g, '&apos;');\r\n}\r\n","/**\r\n * SSG Build CLI\r\n *\r\n * Static site generation build process:\r\n * 1. Load configuration\r\n * 2. Scan routes and expand dynamic paths\r\n * 3. Build with Vite for production\r\n * 4. Render each route to static HTML\r\n * 5. Write output files\r\n * 6. Generate sitemap and robots.txt\r\n */\r\n\r\nimport path from 'node:path';\r\nimport fs from 'node:fs/promises';\r\nimport fsSync from 'node:fs';\r\nimport { createRequire } from 'node:module';\r\nimport { pathToFileURL } from 'node:url';\r\nimport type { SSGConfig, BuildOptions, BuildResult, PageBuildResult, SSGRoute, PageModule } from './types';\r\nimport { loadConfig, resolveConfigPaths } from './config';\r\nimport { scanPages, isDynamicRoute, extractParams, expandDynamicRoute } from './routing/index';\r\nimport { discoverLayouts } from './layouts/index';\r\nimport { writeSitemap } from './sitemap';\r\nimport {\r\n detectCustomEntries,\r\n generateClientEntry,\r\n generateServerEntry,\r\n generateProductionHtmlTemplate,\r\n VIRTUAL_CLIENT_ID,\r\n VIRTUAL_SERVER_ID,\r\n} from './vite/virtual-entries';\r\n\r\n/**\r\n * Build static site\r\n */\r\nexport async function build(options: BuildOptions = {}): Promise<BuildResult> {\r\n const startTime = Date.now();\r\n const root = process.cwd();\r\n const warnings: string[] = [];\r\n const pages: PageBuildResult[] = [];\r\n\r\n console.log('\\n🚀 @sigx/ssg - Building static site...\\n');\r\n\r\n // Step 1: Load configuration\r\n console.log('📦 Loading configuration...');\r\n const config = await loadConfig(options.configPath);\r\n const resolvedConfig = resolveConfigPaths(config, root);\r\n\r\n // Step 2: Scan routes\r\n console.log('🔍 Scanning pages...');\r\n const routes = await scanPages(resolvedConfig, root);\r\n console.log(` Found ${routes.length} page(s)`);\r\n\r\n // Step 3: Discover layouts\r\n console.log('📐 Discovering layouts...');\r\n const layouts = await discoverLayouts(resolvedConfig, root);\r\n console.log(` Found ${layouts.length} layout(s)`);\r\n\r\n // Step 4: Detect entry points\r\n const entryDetection = detectCustomEntries(root, resolvedConfig);\r\n if (entryDetection.useVirtualClient || entryDetection.useVirtualServer) {\r\n console.log('📦 Using zero-config mode');\r\n if (entryDetection.useVirtualClient) console.log(' → Virtual client entry');\r\n if (entryDetection.useVirtualServer) console.log(' → Virtual server entry');\r\n if (entryDetection.useVirtualHtml) console.log(' → Virtual HTML template');\r\n }\r\n\r\n // Get entry points (may create temp files for virtual entries)\r\n const clientEntry = await getClientEntryPoint(resolvedConfig, root);\r\n const ssrEntry = await getSSREntryPoint(resolvedConfig, root);\r\n\r\n // Always write HTML template for the build (either generated or updated from custom)\r\n // This ensures Vite processes it and outputs index.html\r\n const htmlTemplatePath = path.join(root, 'index.html');\r\n let cleanupHtml = false;\r\n let originalHtmlContent: string | null = null;\r\n \r\n // Save original HTML content if we're modifying a custom one\r\n if (!entryDetection.useVirtualHtml && fsSync.existsSync(htmlTemplatePath)) {\r\n originalHtmlContent = fsSync.readFileSync(htmlTemplatePath, 'utf-8');\r\n }\r\n \r\n const htmlContent = await getHtmlTemplate(resolvedConfig, root, clientEntry);\r\n fsSync.writeFileSync(htmlTemplatePath, htmlContent, 'utf-8');\r\n cleanupHtml = entryDetection.useVirtualHtml; // Only cleanup (delete) if we generated it\r\n\r\n // Step 5: Build with Vite\r\n console.log('🔨 Building with Vite...');\r\n const vite = await import('vite');\r\n\r\n try {\r\n // Build client bundle\r\n // Note: We don't empty the outDir since vite build may have already run\r\n // Always use HTML as input so Vite outputs index.html properly\r\n const clientInput = htmlTemplatePath;\r\n \r\n await vite.build({\r\n root,\r\n mode: 'production',\r\n build: {\r\n outDir: resolvedConfig.outDir,\r\n emptyOutDir: false,\r\n ssrManifest: true,\r\n rollupOptions: {\r\n input: clientInput,\r\n },\r\n },\r\n logLevel: options.verbose ? 'info' : 'warn',\r\n });\r\n\r\n // Build SSR bundle\r\n const ssrOutDir = path.join(resolvedConfig.outDir!, '.ssg');\r\n await vite.build({\r\n root,\r\n mode: 'production',\r\n build: {\r\n outDir: ssrOutDir,\r\n ssr: true,\r\n rollupOptions: {\r\n input: ssrEntry,\r\n },\r\n },\r\n logLevel: options.verbose ? 'info' : 'warn',\r\n });\r\n\r\n // Step 6: Collect all paths to render\r\n console.log('📝 Collecting paths to render...');\r\n const pathsToRender = await collectPaths(routes, root, warnings);\r\n console.log(` ${pathsToRender.length} path(s) to render`);\r\n\r\n // Pre-create all output directories to avoid mkdir contention during parallel rendering\r\n const outputDirs = new Set<string>();\r\n for (const pathInfo of pathsToRender) {\r\n const outputPath = getOutputPath(pathInfo.path, resolvedConfig.outDir!);\r\n outputDirs.add(path.dirname(outputPath));\r\n }\r\n await Promise.all(\r\n Array.from(outputDirs).map(dir => fs.mkdir(dir, { recursive: true }))\r\n );\r\n\r\n // Step 7: Render each path to HTML\r\n console.log('🎨 Rendering pages...');\r\n\r\n // Determine the SSR entry output name (based on input file name)\r\n const ssrEntryBasename = path.basename(ssrEntry, path.extname(ssrEntry));\r\n const ssrEntryName = ssrEntryBasename + '.js';\r\n\r\n // Pre-load the SSR module once for all pages\r\n const entryPath = path.join(ssrOutDir, ssrEntryName);\r\n const entryModule = await import(pathToFileURL(entryPath).href);\r\n\r\n // Load HTML template once\r\n const templatePath = path.join(resolvedConfig.outDir!, 'index.html');\r\n const template = await fs.readFile(templatePath, 'utf-8');\r\n\r\n // Parallel rendering configuration - higher concurrency since rendering is CPU-bound\r\n const CONCURRENCY = options.concurrency ?? 20; // Number of pages to render in parallel\r\n const verbose = options.verbose ?? false;\r\n\r\n interface RenderResult {\r\n pathInfo: PathToRender;\r\n html: string;\r\n outputPath: string;\r\n renderTime: number;\r\n }\r\n\r\n // Render a single page (CPU-bound, no I/O)\r\n async function renderPage(pathInfo: PathToRender): Promise<RenderResult | null> {\r\n const renderStart = Date.now();\r\n\r\n try {\r\n // Render the app\r\n const appHtml = await entryModule.render(pathInfo.path, {\r\n params: pathInfo.params,\r\n props: pathInfo.props,\r\n });\r\n\r\n // Inject into template\r\n let html = template.replace('<!--app-html-->', appHtml);\r\n const headTags = generateHeadTags(pathInfo, resolvedConfig);\r\n html = html.replace('<!--head-tags-->', headTags);\r\n\r\n const outputPath = getOutputPath(pathInfo.path, resolvedConfig.outDir!);\r\n const renderTime = Date.now() - renderStart;\r\n\r\n return { pathInfo, html, outputPath, renderTime };\r\n } catch (err) {\r\n const errorMessage = err instanceof Error ? err.message : String(err);\r\n console.error(` ❌ ${pathInfo.path}: ${errorMessage}`);\r\n warnings.push(`Failed to render ${pathInfo.path}: ${errorMessage}`);\r\n return null;\r\n }\r\n }\r\n\r\n // Render all pages in parallel batches (CPU-bound, no I/O)\r\n console.log(' Phase 1: Rendering...');\r\n const renderPhaseStart = Date.now();\r\n const renderResults: RenderResult[] = [];\r\n \r\n for (let i = 0; i < pathsToRender.length; i += CONCURRENCY) {\r\n const batch = pathsToRender.slice(i, i + CONCURRENCY);\r\n const results = await Promise.all(batch.map(renderPage));\r\n \r\n for (const result of results) {\r\n if (result) {\r\n renderResults.push(result);\r\n }\r\n }\r\n }\r\n const renderPhaseDuration = Date.now() - renderPhaseStart;\r\n console.log(` Phase 1 complete: ${renderResults.length} pages in ${renderPhaseDuration}ms (${Math.round(renderPhaseDuration / renderResults.length)}ms avg)`);\r\n\r\n // Write all files in parallel with limited concurrency\r\n console.log(' Phase 2: Writing files...');\r\n const writePhaseStart = Date.now();\r\n const WRITE_CONCURRENCY = 10; // Limit parallel writes to reduce I/O contention\r\n \r\n for (let i = 0; i < renderResults.length; i += WRITE_CONCURRENCY) {\r\n const batch = renderResults.slice(i, i + WRITE_CONCURRENCY);\r\n await Promise.all(batch.map(async (result) => {\r\n await fs.writeFile(result.outputPath, result.html, 'utf-8');\r\n const size = Buffer.byteLength(result.html, 'utf-8');\r\n\r\n pages.push({\r\n path: result.pathInfo.path,\r\n file: result.outputPath,\r\n time: result.renderTime,\r\n size,\r\n });\r\n \r\n if (verbose) {\r\n console.log(` ✓ ${result.pathInfo.path} (${result.renderTime}ms, ${formatBytes(size)})`);\r\n }\r\n }));\r\n }\r\n const writePhaseDuration = Date.now() - writePhaseStart;\r\n console.log(` Phase 2 complete: ${renderResults.length} files in ${writePhaseDuration}ms`);\r\n \r\n // Summary (non-verbose)\r\n if (!verbose) {\r\n console.log(` ✓ Rendered ${renderResults.length} pages`);\r\n }\r\n\r\n // Step 8: Clean up SSR build\r\n await fs.rm(ssrOutDir, { recursive: true, force: true });\r\n\r\n // Step 9: Generate sitemap and robots.txt\r\n if (pages.length > 0) {\r\n console.log('🗺️ Generating sitemap...');\r\n await writeSitemap(pages, resolvedConfig, resolvedConfig.outDir!);\r\n console.log(' ✓ sitemap.xml');\r\n console.log(' ✓ robots.txt');\r\n }\r\n\r\n } finally {\r\n // Clean up temporary entry files\r\n await cleanupTempEntries(root);\r\n \r\n // Clean up or restore HTML template\r\n if (cleanupHtml) {\r\n // We generated a virtual HTML, remove it\r\n try {\r\n await fs.unlink(htmlTemplatePath);\r\n } catch {\r\n // Ignore\r\n }\r\n } else if (originalHtmlContent !== null) {\r\n // We modified a custom HTML, restore the original\r\n try {\r\n await fs.writeFile(htmlTemplatePath, originalHtmlContent, 'utf-8');\r\n } catch {\r\n // Ignore\r\n }\r\n }\r\n }\r\n\r\n // Done\r\n const totalTime = Date.now() - startTime;\r\n\r\n console.log(`\\n✅ Built ${pages.length} page(s) in ${totalTime}ms`);\r\n\r\n if (warnings.length > 0) {\r\n console.log(`\\n⚠️ ${warnings.length} warning(s):`);\r\n for (const warning of warnings) {\r\n console.log(` - ${warning}`);\r\n }\r\n }\r\n\r\n console.log(`\\n📁 Output: ${resolvedConfig.outDir}\\n`);\r\n\r\n return {\r\n pages,\r\n totalTime,\r\n warnings,\r\n };\r\n}\r\n\r\n/**\r\n * Path information for rendering\r\n */\r\ninterface PathToRender {\r\n path: string;\r\n route: SSGRoute;\r\n params: Record<string, string>;\r\n props?: Record<string, unknown>;\r\n}\r\n\r\n/**\r\n * Collect all paths to render, expanding dynamic routes\r\n */\r\nasync function collectPaths(\r\n routes: SSGRoute[],\r\n root: string,\r\n warnings: string[]\r\n): Promise<PathToRender[]> {\r\n const paths: PathToRender[] = [];\r\n\r\n for (const route of routes) {\r\n if (isDynamicRoute(route)) {\r\n // Load module and call getStaticPaths\r\n try {\r\n const moduleUrl = pathToFileURL(route.file).href;\r\n const pageModule = (await import(moduleUrl)) as PageModule;\r\n\r\n if (!pageModule.getStaticPaths) {\r\n const params = extractParams(route.path).join(', ');\r\n console.warn(\r\n `\\n⚠️ SSG102: Dynamic route missing getStaticPaths()\\n` +\r\n ` 📁 ${route.file}\\n` +\r\n ` Route: ${route.path} (params: ${params})\\n` +\r\n ` 💡 Export getStaticPaths() to generate static pages:\\n\\n` +\r\n ` export async function getStaticPaths() {\\n` +\r\n ` return [{ params: { ${params.split(', ')[0]}: 'value' } }];\\n` +\r\n ` }\\n`\r\n );\r\n warnings.push(\r\n `Route ${route.path} has dynamic segments [${params}] but no getStaticPaths() export. Skipping.`\r\n );\r\n continue;\r\n }\r\n\r\n const staticPaths = await pageModule.getStaticPaths();\r\n\r\n for (const staticPath of staticPaths) {\r\n const expandedPaths = expandDynamicRoute(route, [staticPath]);\r\n for (const expandedPath of expandedPaths) {\r\n paths.push({\r\n path: expandedPath,\r\n route,\r\n params: staticPath.params,\r\n props: staticPath.props,\r\n });\r\n }\r\n }\r\n } catch (err) {\r\n warnings.push(`Failed to load ${route.file}: ${err}`);\r\n }\r\n } else {\r\n paths.push({\r\n path: route.path,\r\n route,\r\n params: {},\r\n });\r\n }\r\n }\r\n\r\n return paths;\r\n}\r\n\r\n/**\r\n * Render a page to HTML\r\n */\r\nasync function renderPage(\r\n pathInfo: PathToRender,\r\n config: SSGConfig,\r\n ssrOutDir: string,\r\n ssrEntryName: string\r\n): Promise<string> {\r\n // Load the SSR entry module\r\n // This should export a render function that returns HTML string\r\n const entryPath = path.join(ssrOutDir, ssrEntryName);\r\n const entryModule = await import(pathToFileURL(entryPath).href);\r\n\r\n // Render the app - the entry module's render function handles SSR\r\n const appHtml = await entryModule.render(pathInfo.path, {\r\n params: pathInfo.params,\r\n props: pathInfo.props,\r\n });\r\n\r\n // Load HTML template\r\n const templatePath = path.join(config.outDir!, 'index.html');\r\n let template = await fs.readFile(templatePath, 'utf-8');\r\n\r\n // Inject app HTML\r\n template = template.replace('<!--app-html-->', appHtml);\r\n\r\n // Inject head tags if any\r\n const headTags = generateHeadTags(pathInfo, config);\r\n template = template.replace('<!--head-tags-->', headTags);\r\n\r\n return template;\r\n}\r\n\r\n/**\r\n * Generate head tags for a page\r\n */\r\nfunction generateHeadTags(pathInfo: PathToRender, config: SSGConfig): string {\r\n const tags: string[] = [];\r\n const meta = pathInfo.route.meta || {};\r\n\r\n // Title\r\n const title = meta.title || config.site?.title;\r\n if (title) {\r\n tags.push(`<title>${escapeHtml(title)}</title>`);\r\n }\r\n\r\n // Description\r\n const description = meta.description || config.site?.description;\r\n if (description) {\r\n tags.push(`<meta name=\"description\" content=\"${escapeHtml(description)}\">`);\r\n }\r\n\r\n // Canonical URL\r\n if (config.site?.url) {\r\n const canonical = new URL(pathInfo.path, config.site.url).href;\r\n tags.push(`<link rel=\"canonical\" href=\"${canonical}\">`);\r\n }\r\n\r\n return tags.join('\\n ');\r\n}\r\n\r\n/**\r\n * Get output file path for a URL path\r\n */\r\nfunction getOutputPath(urlPath: string, outDir: string): string {\r\n // Normalize path\r\n let normalized = urlPath.replace(/^\\//, '').replace(/\\/$/, '');\r\n\r\n if (!normalized) {\r\n normalized = 'index';\r\n }\r\n\r\n // Add .html extension or /index.html for directories\r\n if (!normalized.endsWith('.html')) {\r\n normalized = path.join(normalized, 'index.html');\r\n }\r\n\r\n return path.join(outDir, normalized);\r\n}\r\n\r\n/**\r\n * Get SSR entry point\r\n * Returns virtual module ID if no custom entry exists\r\n */\r\nasync function getSSREntryPoint(config: SSGConfig, root: string): Promise<string> {\r\n // Check for custom entry points\r\n const detection = detectCustomEntries(root, config);\r\n \r\n if (!detection.useVirtualServer && detection.customServerPath) {\r\n return detection.customServerPath;\r\n }\r\n\r\n // Use virtual server entry - we need to write a temp file for the build\r\n // because Rollup input must be a real file path\r\n const virtualServerCode = generateServerEntry(config);\r\n const tempServerPath = path.join(root, '.ssg-temp-entry-server.tsx');\r\n fsSync.writeFileSync(tempServerPath, virtualServerCode, 'utf-8');\r\n \r\n return tempServerPath;\r\n}\r\n\r\n/**\r\n * Get client entry point\r\n * Returns virtual module ID if no custom entry exists\r\n */\r\nasync function getClientEntryPoint(config: SSGConfig, root: string): Promise<string> {\r\n const detection = detectCustomEntries(root, config);\r\n \r\n if (!detection.useVirtualClient && detection.customClientPath) {\r\n return detection.customClientPath;\r\n }\r\n\r\n // Use virtual client entry - write a temp file\r\n const virtualClientCode = generateClientEntry(config, detection);\r\n const tempClientPath = path.join(root, '.ssg-temp-entry-client.tsx');\r\n fsSync.writeFileSync(tempClientPath, virtualClientCode, 'utf-8');\r\n \r\n return tempClientPath;\r\n}\r\n\r\n/**\r\n * Clean up temporary entry files\r\n */\r\nasync function cleanupTempEntries(root: string): Promise<void> {\r\n const tempFiles = [\r\n path.join(root, '.ssg-temp-entry-server.tsx'),\r\n path.join(root, '.ssg-temp-entry-client.tsx'),\r\n ];\r\n \r\n for (const file of tempFiles) {\r\n try {\r\n await fs.unlink(file);\r\n } catch {\r\n // Ignore if file doesn't exist\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Get or generate HTML template\r\n */\r\nasync function getHtmlTemplate(config: SSGConfig, root: string, clientEntryPath: string): Promise<string> {\r\n const detection = detectCustomEntries(root, config);\r\n \r\n if (!detection.useVirtualHtml && detection.customHtmlPath) {\r\n // Read custom HTML and update the script src to point to the temp entry file\r\n let html = await fs.readFile(detection.customHtmlPath, 'utf-8');\r\n // Replace any virtual SSG client paths with the actual temp entry path\r\n // Use relative path from root for the script src\r\n const relativePath = './' + path.relative(root, clientEntryPath).replace(/\\\\/g, '/');\r\n html = html.replace(\r\n /<script([^>]*)\\s+src=[\"']?\\/@ssg\\/client\\.tsx[\"']?/g,\r\n `<script$1 src=\"${relativePath}\"`\r\n );\r\n return html;\r\n }\r\n\r\n // Generate virtual HTML template\r\n return generateProductionHtmlTemplate(config, clientEntryPath);\r\n}\r\n\r\n/**\r\n * Format bytes to human-readable string\r\n */\r\nfunction formatBytes(bytes: number): string {\r\n if (bytes < 1024) return `${bytes}B`;\r\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;\r\n return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;\r\n}\r\n\r\n/**\r\n * Escape HTML special characters\r\n */\r\nfunction escapeHtml(str: string): string {\r\n return str\r\n .replace(/&/g, '&amp;')\r\n .replace(/</g, '&lt;')\r\n .replace(/>/g, '&gt;')\r\n .replace(/\"/g, '&quot;')\r\n .replace(/'/g, '&#39;');\r\n}\r\n"],"mappings":"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"}
@@ -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,CAwP5E"}
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,CAoQ5E"}