defuss-ssg 0.6.0 → 0.6.1

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 CHANGED
@@ -19,6 +19,7 @@ Use Bun for package management. The published package targets Node `^20.19.0 ||
19
19
  - defuss components imported into MDX and HTML-like pages
20
20
  - Automatic hydration boundaries for components rendered from `components/`, `src/components/`, `csr/`, or `src/csr/`
21
21
  - Static assets copied from `assets/` or `src/assets/`
22
+ - Vite-style `public/` assets copied to the output root and served at `/<file>`
22
23
  - File-based API routes from `pages/**/*.ts` and `pages/**/*.js`
23
24
  - Root `index.mdx`, `index.md`, or `index.html` fallback when no pages directory exists
24
25
  - Pre-rendered endpoints via `prerender = true` and `getStaticPaths()`
@@ -282,7 +283,7 @@ Single command -> <command> .
282
283
  Command + folder -> <command> <folder>
283
284
  ```
284
285
 
285
- When `pages`, `components`, or `assets` are not configured explicitly, `defuss-ssg` prefers `src/pages`, `src/components`, `src/csr`, and `src/assets` before falling back to their project-root equivalents. If no pages directory exists, it falls back to root `index.mdx`, then `index.md`, then `index.html`.
286
+ When `pages`, `components`, or `assets` are not configured explicitly, `defuss-ssg` prefers `src/pages`, `src/components`, `src/csr`, and `src/assets` before falling back to their project-root equivalents. If no pages directory exists, it falls back to root `index.mdx`, then `index.md`, then `index.html`. A project-root `public/` directory is treated like Vite/Astro `public`: its files are available at the site root.
286
287
 
287
288
  Commands:
288
289
 
package/dist/cli.mjs CHANGED
@@ -1,17 +1,17 @@
1
1
  #!/usr/bin/env node
2
- import { v as validateProjectDir, b as build } from './vite-CaqPNORI.mjs';
3
- import { d as dev, s as serve } from './serve-Cd4Pw98E.mjs';
2
+ import { v as validateProjectDir, b as build } from './vite-DLXcqaZX.mjs';
3
+ import { d as dev, s as serve } from './serve-DIW2gbz_.mjs';
4
4
  import { join, dirname, resolve } from 'node:path';
5
5
  import { existsSync, readFileSync } from 'node:fs';
6
6
  import { spawn } from 'node:child_process';
7
+ import 'fast-glob';
7
8
  import 'node:fs/promises';
8
- import 'node:url';
9
9
  import 'defuss/server';
10
10
  import 'node:crypto';
11
11
  import '@mdx-js/rollup';
12
- import 'fast-glob';
13
12
  import 'vite';
14
13
  import 'defuss-vite';
14
+ import 'node:url';
15
15
  import 'node:os';
16
16
  import 'esbuild';
17
17
  import 'rehype-katex';
package/dist/index.cjs CHANGED
@@ -1,17 +1,17 @@
1
1
  'use strict';
2
2
 
3
- var vite = require('./vite-CBAZNNYA.cjs');
3
+ var vite = require('./vite-DlnPNJYO.cjs');
4
4
  var mdx = require('@mdx-js/rollup');
5
5
  var vite$1 = require('vite');
6
6
  var defuss = require('defuss-vite');
7
7
  var node_fs = require('node:fs');
8
8
  var node_path = require('node:path');
9
9
  var defussExpress = require('defuss-express');
10
+ require('fast-glob');
10
11
  require('node:fs/promises');
11
- require('node:url');
12
12
  require('defuss/server');
13
13
  require('node:crypto');
14
- require('fast-glob');
14
+ require('node:url');
15
15
  require('node:os');
16
16
  require('esbuild');
17
17
  require('rehype-katex');
package/dist/index.mjs CHANGED
@@ -1,15 +1,15 @@
1
- export { H as HTTP_METHODS, b as build, a as buildEndpoints, c as compileEndpoints, d as compileRpcModule, e as configDefaults, f as defussSsg, g as discoverEndpointSourceFiles, h as discoverRpcFile, i as endpointFileToRoute, j as handleEndpointRoute, k as handleRpcRequest, l as initializeRpc, m as loadEndpointModule, n as matchRoutePattern, r as readConfig, o as registerEndpoints, p as resolveEndpoints, q as routeToExpressPattern } from './vite-CaqPNORI.mjs';
2
- export { d as dev, s as serve } from './serve-Cd4Pw98E.mjs';
1
+ export { H as HTTP_METHODS, b as build, a as buildEndpoints, c as compileEndpoints, d as compileRpcModule, e as configDefaults, f as defussSsg, g as discoverEndpointSourceFiles, h as discoverRpcFile, i as endpointFileToRoute, j as handleEndpointRoute, k as handleRpcRequest, l as initializeRpc, m as loadEndpointModule, n as matchRoutePattern, r as readConfig, o as registerEndpoints, p as resolveEndpoints, q as routeToExpressPattern } from './vite-DLXcqaZX.mjs';
2
+ export { d as dev, s as serve } from './serve-DIW2gbz_.mjs';
3
+ import 'fast-glob';
3
4
  import 'node:fs';
4
5
  import 'node:fs/promises';
5
6
  import 'node:path';
6
- import 'node:url';
7
7
  import 'defuss/server';
8
8
  import 'node:crypto';
9
9
  import '@mdx-js/rollup';
10
- import 'fast-glob';
11
10
  import 'vite';
12
11
  import 'defuss-vite';
12
+ import 'node:url';
13
13
  import 'node:os';
14
14
  import 'esbuild';
15
15
  import 'rehype-katex';
@@ -1,7 +1,7 @@
1
1
  import mdx from '@mdx-js/rollup';
2
2
  import { createServer } from 'vite';
3
3
  import defuss from 'defuss-vite';
4
- import { v as validateProjectDir, r as readConfig, f as defussSsg, o as registerEndpoints, l as initializeRpc, s as readIncomingBody, t as createWebRequest, k as handleRpcRequest, u as sendWebResponse } from './vite-CaqPNORI.mjs';
4
+ import { v as validateProjectDir, r as readConfig, f as defussSsg, o as registerEndpoints, l as initializeRpc, s as readIncomingBody, t as createWebRequest, k as handleRpcRequest, u as sendWebResponse } from './vite-DLXcqaZX.mjs';
5
5
  import { existsSync } from 'node:fs';
6
6
  import { join } from 'node:path';
7
7
  import { express, startServer } from 'defuss-express';
@@ -1,13 +1,13 @@
1
+ import glob from 'fast-glob';
1
2
  import { existsSync, statSync, mkdirSync, rmSync, readdirSync, symlinkSync } from 'node:fs';
2
3
  import { writeFile, readFile, cp, stat } from 'node:fs/promises';
3
4
  import { join, resolve, relative, sep, extname, dirname } from 'node:path';
4
- import { fileURLToPath } from 'node:url';
5
5
  import { getBrowserGlobals, getDocument, renderSync, renderToString } from 'defuss/server';
6
6
  import { createHash } from 'node:crypto';
7
7
  import mdx from '@mdx-js/rollup';
8
- import glob from 'fast-glob';
9
8
  import { createServer, build as build$1 } from 'vite';
10
9
  import defuss from 'defuss-vite';
10
+ import { fileURLToPath } from 'node:url';
11
11
  import { tmpdir } from 'node:os';
12
12
  import esbuild from 'esbuild';
13
13
  import rehypeKatex from 'rehype-katex';
@@ -640,8 +640,8 @@ const registerEndpoints = async (registrar, projectDir, _config, debug = false)
640
640
  }
641
641
  };
642
642
 
643
- const __filename$2 = fileURLToPath(import.meta.url);
644
- const __dirname$2 = dirname(__filename$2);
643
+ const __filename$1 = fileURLToPath(import.meta.url);
644
+ const __dirname$1 = dirname(__filename$1);
645
645
  const isHtmlLikePageSource = (filePath) => {
646
646
  const extension = extname(filePath).toLowerCase();
647
647
  return extension === "" || extension === ".md" || extension === ".mdx" || extension === ".html";
@@ -655,16 +655,56 @@ const isPathInOrUnder$1 = (filePath, dirPath) => {
655
655
  const isRootIndexPageSource$1 = (filePath) => /^index(?:\.mdx?|\.html)?$/i.test(normalizePath$1(filePath));
656
656
  const resolveLocalHelperFile = (sourceRelativePath, builtRelativePath) => {
657
657
  const candidates = [
658
- resolve(__dirname$2, sourceRelativePath),
659
- resolve(__dirname$2, "..", "src", sourceRelativePath),
660
- resolve(__dirname$2, builtRelativePath)
658
+ resolve(__dirname$1, sourceRelativePath),
659
+ resolve(__dirname$1, "..", "src", sourceRelativePath),
660
+ resolve(__dirname$1, builtRelativePath)
661
661
  ];
662
662
  for (const candidate of candidates) {
663
663
  if (existsSync(candidate)) {
664
664
  return candidate;
665
665
  }
666
666
  }
667
- return resolve(__dirname$2, builtRelativePath);
667
+ return resolve(__dirname$1, builtRelativePath);
668
+ };
669
+ const injectStylesheetLinks$1 = (html, hrefs) => {
670
+ const missingHrefs = hrefs.filter((href) => !html.includes(`href="${href}"`));
671
+ if (missingHrefs.length === 0) {
672
+ return html;
673
+ }
674
+ const linkTags = missingHrefs.map((href) => `<link rel="stylesheet" href="${href}">`).join("");
675
+ if (html.includes("</head>")) {
676
+ return html.replace("</head>", `${linkTags}</head>`);
677
+ }
678
+ return `${linkTags}${html}`;
679
+ };
680
+ const getComponentStylesheetHrefs$1 = async (componentsOutputDir, componentsPublicDir) => {
681
+ if (!existsSync(componentsOutputDir)) {
682
+ return [];
683
+ }
684
+ const cssFiles = await glob.async(join(componentsOutputDir, "**/*.css"));
685
+ return cssFiles.sort((left, right) => left.localeCompare(right)).map(
686
+ (cssFile) => `/${normalizePath$1(join(componentsPublicDir, relative(componentsOutputDir, cssFile)))}`
687
+ );
688
+ };
689
+ const injectComponentStylesheetsIntoOutputHtml = async (outputProjectDir, componentsOutputDir, componentsPublicDir) => {
690
+ if (!existsSync(outputProjectDir)) {
691
+ return;
692
+ }
693
+ const stylesheetHrefs = await getComponentStylesheetHrefs$1(
694
+ componentsOutputDir,
695
+ componentsPublicDir
696
+ );
697
+ if (stylesheetHrefs.length === 0) {
698
+ return;
699
+ }
700
+ const htmlFiles = await glob.async(join(outputProjectDir, "**/*.html"));
701
+ for (const htmlFile of htmlFiles) {
702
+ const currentHtml = await readFile(htmlFile, "utf8");
703
+ const nextHtml = injectStylesheetLinks$1(currentHtml, stylesheetHrefs);
704
+ if (nextHtml !== currentHtml) {
705
+ await writeFile(htmlFile, nextHtml);
706
+ }
707
+ }
668
708
  };
669
709
  const build = async ({
670
710
  projectDir,
@@ -700,6 +740,7 @@ const build = async ({
700
740
  const inputPagesDir = projectPaths.pagesSourceDirAbsolute;
701
741
  const inputComponentsDir = projectPaths.componentsSourceDirAbsolute;
702
742
  const inputAssetsDir = projectPaths.assetsSourceDirAbsolute;
743
+ const inputPublicDir = join(projectDir, "public");
703
744
  const hasInputPagesDir = projectPaths.hasPagesSourceDir;
704
745
  const tmpPagesDir = join(config.tmp, projectPaths.pagesSourceDir);
705
746
  const tmpComponentsDir = join(config.tmp, projectPaths.componentsSourceDir);
@@ -720,6 +761,7 @@ const build = async ({
720
761
  console.log("Input pages dir:", inputPagesDir);
721
762
  console.log("Input components dir:", inputComponentsDir);
722
763
  console.log("Input assets dir:", inputAssetsDir);
764
+ console.log("Input public dir:", inputPublicDir);
723
765
  console.log("Temp pages dir:", tmpPagesDir);
724
766
  console.log("Temp components dir:", tmpComponentsDir);
725
767
  console.log("Output pages dir:", outputPagesDir);
@@ -738,6 +780,8 @@ const build = async ({
738
780
  changeKind = "component";
739
781
  } else if (isPathInOrUnder$1(changedRelative, projectPaths.assetsSourceDir)) {
740
782
  changeKind = "asset";
783
+ } else if (isPathInOrUnder$1(changedRelative, "public")) {
784
+ changeKind = "asset";
741
785
  } else if (changedRelative === "config.ts" || changedRelative === "config.js") {
742
786
  changeKind = "config";
743
787
  }
@@ -755,7 +799,7 @@ const build = async ({
755
799
  const shouldCopyToTemp = (src) => {
756
800
  const relative2 = src.startsWith(projectDir) ? normalizePath$1(src.slice(projectDir.length).replace(/^[\\/]+/, "")) : src;
757
801
  const firstSegment = relative2.split(/[\\/]/)[0];
758
- return !(firstSegment === "node_modules" || firstSegment === config.output || firstSegment === ".endpoints" || firstSegment === ".ssg-temp" || firstSegment === ".git" || projectPaths.assetsSourceDirCandidates.some(
802
+ return !(firstSegment === "node_modules" || firstSegment === config.output || firstSegment === ".endpoints" || firstSegment === ".ssg-temp" || firstSegment === ".git" || firstSegment === "public" || projectPaths.assetsSourceDirCandidates.some(
759
803
  (candidateDir) => isPathInOrUnder$1(relative2, candidateDir)
760
804
  ));
761
805
  };
@@ -1049,6 +1093,16 @@ const build = async ({
1049
1093
  if (existsSync(inputAssetsDir)) {
1050
1094
  await cp(inputAssetsDir, outputAssetsDir, { recursive: true });
1051
1095
  }
1096
+ if (existsSync(inputPublicDir)) {
1097
+ await cp(inputPublicDir, outputProjectDir, { recursive: true });
1098
+ }
1099
+ }
1100
+ if (isFullBuild || changeKind === "page" || changeKind === "component") {
1101
+ await injectComponentStylesheetsIntoOutputHtml(
1102
+ outputProjectDir,
1103
+ outputComponentsDir,
1104
+ projectPaths.componentsPublicDir
1105
+ );
1052
1106
  }
1053
1107
  timeEndDebug("[build] copy-outputs");
1054
1108
  if (isFullBuild || changeKind === "component") {
@@ -1277,8 +1331,6 @@ const handleRpcRequest = async (ctx) => {
1277
1331
  }
1278
1332
  };
1279
1333
 
1280
- const __filename$1 = fileURLToPath(import.meta.url);
1281
- const __dirname$1 = dirname(__filename$1);
1282
1334
  const MIME_TYPES = {
1283
1335
  ".css": "text/css; charset=utf-8",
1284
1336
  ".gif": "image/gif",
@@ -1677,18 +1729,25 @@ const injectHmrClientModule = (html) => {
1677
1729
  }
1678
1730
  return `${importTag}${html}`;
1679
1731
  };
1680
- const resolveRuntimeHelperFile = () => {
1681
- const candidates = [
1682
- join(__dirname$1, "runtime.ts"),
1683
- join(__dirname$1, "..", "src", "runtime.ts"),
1684
- join(__dirname$1, "runtime.mjs")
1685
- ];
1686
- for (const candidate of candidates) {
1687
- if (existsSync(candidate)) {
1688
- return candidate;
1689
- }
1732
+ const injectStylesheetLinks = (html, hrefs) => {
1733
+ const missingHrefs = hrefs.filter((href) => !html.includes(`href="${href}"`));
1734
+ if (missingHrefs.length === 0) {
1735
+ return html;
1690
1736
  }
1691
- return null;
1737
+ const linkTags = missingHrefs.map((href) => `<link rel="stylesheet" href="${href}">`).join("");
1738
+ if (html.includes("</head>")) {
1739
+ return html.replace("</head>", `${linkTags}</head>`);
1740
+ }
1741
+ return `${linkTags}${html}`;
1742
+ };
1743
+ const getComponentStylesheetHrefs = async (componentsOutputDir, componentsPublicDir) => {
1744
+ if (!existsSync(componentsOutputDir)) {
1745
+ return [];
1746
+ }
1747
+ const cssFiles = await glob.async(join(componentsOutputDir, "**/*.css"));
1748
+ return cssFiles.sort((left, right) => left.localeCompare(right)).map(
1749
+ (cssFile) => `/${normalizePath(join(componentsPublicDir, relative(componentsOutputDir, cssFile)))}`
1750
+ );
1692
1751
  };
1693
1752
  const isHtmlLikeFile = (filePath) => {
1694
1753
  const extension = extname(filePath);
@@ -1738,6 +1797,9 @@ const classifyChangedFile = (file, projectDir, config, rpcFile) => {
1738
1797
  if (isPathInOrUnder(relativeFile, paths.assetsSourceDir)) {
1739
1798
  return "asset";
1740
1799
  }
1800
+ if (isPathInOrUnder(relativeFile, "public")) {
1801
+ return "asset";
1802
+ }
1741
1803
  return "dependency";
1742
1804
  };
1743
1805
  const filePathToComponentPublicPath = (file, componentsSourceDir, componentsPublicDir) => {
@@ -2126,6 +2188,13 @@ function defussSsg(options = {}) {
2126
2188
  );
2127
2189
  const el = renderSync(vdom, document.documentElement, { browserGlobals });
2128
2190
  let html = renderToString(el);
2191
+ html = injectStylesheetLinks(
2192
+ html,
2193
+ await getComponentStylesheetHrefs(
2194
+ join(projectDir, config.output, currentPaths.componentsPublicDir),
2195
+ currentPaths.componentsPublicDir
2196
+ )
2197
+ );
2129
2198
  html = injectHmrClientModule(html);
2130
2199
  html = await server.transformIndexHtml(requestUrl.pathname, html);
2131
2200
  res.setHeader("Cache-Control", "no-store");
@@ -2140,27 +2209,14 @@ function defussSsg(options = {}) {
2140
2209
  const maybeServeComponent = async (server, req, res) => {
2141
2210
  const requestUrl = new URL(req.url || "/", "http://localhost");
2142
2211
  const pathname = requestUrl.pathname;
2143
- if (!pathname.startsWith(`/${config.components}/`)) {
2144
- return false;
2145
- }
2146
- const componentRoutePrefix = `/${config.components}/`;
2147
- if (pathname === `/${config.components}/runtime.js`) {
2148
- const runtimePath = resolveRuntimeHelperFile();
2149
- if (!runtimePath) {
2150
- return false;
2151
- }
2152
- const transformed2 = await server.transformRequest(runtimePath);
2153
- if (transformed2) {
2154
- res.setHeader("Cache-Control", "no-store");
2155
- res.setHeader("Content-Type", "text/javascript; charset=utf-8");
2156
- res.statusCode = 200;
2157
- res.end(transformed2.code);
2158
- return true;
2159
- }
2212
+ const currentPaths = resolveSsgPaths(projectDir, config);
2213
+ const componentRoutePrefix = `/${currentPaths.componentsPublicDir}/`;
2214
+ if (!pathname.startsWith(componentRoutePrefix)) {
2160
2215
  return false;
2161
2216
  }
2162
2217
  const relativeComponentPath = pathname.slice(componentRoutePrefix.length);
2163
- const isBuiltComponentAsset = relativeComponentPath.startsWith("chunks/") || /^chunk-|^client-/.test(relativeComponentPath);
2218
+ const componentExtension = extname(relativeComponentPath).toLowerCase();
2219
+ const isBuiltComponentAsset = relativeComponentPath === "runtime.js" || relativeComponentPath.startsWith("chunks/") || /^chunk-|^client-/.test(relativeComponentPath) || componentExtension !== ".js";
2164
2220
  if (isBuiltComponentAsset) {
2165
2221
  return false;
2166
2222
  }
@@ -2168,7 +2224,6 @@ function defussSsg(options = {}) {
2168
2224
  extname(relativeComponentPath),
2169
2225
  ""
2170
2226
  );
2171
- const currentPaths = resolveSsgPaths(projectDir, config);
2172
2227
  const extensions = [".tsx", ".ts", ".jsx", ".js"];
2173
2228
  let sourceFile = null;
2174
2229
  for (const ext of extensions) {
@@ -2182,10 +2237,7 @@ function defussSsg(options = {}) {
2182
2237
  }
2183
2238
  }
2184
2239
  if (!sourceFile) {
2185
- res.setHeader("Cache-Control", "no-store");
2186
- res.statusCode = 404;
2187
- res.end();
2188
- return true;
2240
+ return false;
2189
2241
  }
2190
2242
  const transformed = await server.transformRequest(sourceFile);
2191
2243
  if (transformed) {
@@ -2223,9 +2275,18 @@ function defussSsg(options = {}) {
2223
2275
  MIME_TYPES[extname(outputFile)] ?? "application/octet-stream"
2224
2276
  );
2225
2277
  if (outputFile.endsWith(".html")) {
2278
+ const currentPaths = resolveSsgPaths(projectDir, config);
2226
2279
  const html = await server.transformIndexHtml(
2227
2280
  requestUrl.pathname,
2228
- injectHmrClientModule(body.toString("utf8"))
2281
+ injectHmrClientModule(
2282
+ injectStylesheetLinks(
2283
+ body.toString("utf8"),
2284
+ await getComponentStylesheetHrefs(
2285
+ join(projectDir, config.output, currentPaths.componentsPublicDir),
2286
+ currentPaths.componentsPublicDir
2287
+ )
2288
+ )
2289
+ )
2229
2290
  );
2230
2291
  res.statusCode = 200;
2231
2292
  res.end(method === "HEAD" ? void 0 : html);
@@ -2280,6 +2341,7 @@ function defussSsg(options = {}) {
2280
2341
  ...paths.assetsSourceDirCandidates.map(
2281
2342
  (candidateDir) => join(projectDir, candidateDir)
2282
2343
  ),
2344
+ join(projectDir, "public"),
2283
2345
  join(projectDir, "config.ts"),
2284
2346
  join(projectDir, "config.js")
2285
2347
  ];
@@ -1,15 +1,15 @@
1
1
  'use strict';
2
2
 
3
+ var glob = require('fast-glob');
3
4
  var node_fs = require('node:fs');
4
5
  var promises = require('node:fs/promises');
5
6
  var node_path = require('node:path');
6
- var node_url = require('node:url');
7
7
  var server = require('defuss/server');
8
8
  var node_crypto = require('node:crypto');
9
9
  var mdx = require('@mdx-js/rollup');
10
- var glob = require('fast-glob');
11
10
  var vite = require('vite');
12
11
  var defuss = require('defuss-vite');
12
+ var node_url = require('node:url');
13
13
  var node_os = require('node:os');
14
14
  var esbuild = require('esbuild');
15
15
  var rehypeKatex = require('rehype-katex');
@@ -643,8 +643,8 @@ const registerEndpoints = async (registrar, projectDir, _config, debug = false)
643
643
  }
644
644
  };
645
645
 
646
- const __filename$2 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('vite-CBAZNNYA.cjs', document.baseURI).href)));
647
- const __dirname$2 = node_path.dirname(__filename$2);
646
+ const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('vite-DlnPNJYO.cjs', document.baseURI).href)));
647
+ const __dirname$1 = node_path.dirname(__filename$1);
648
648
  const isHtmlLikePageSource = (filePath) => {
649
649
  const extension = node_path.extname(filePath).toLowerCase();
650
650
  return extension === "" || extension === ".md" || extension === ".mdx" || extension === ".html";
@@ -658,16 +658,56 @@ const isPathInOrUnder$1 = (filePath, dirPath) => {
658
658
  const isRootIndexPageSource$1 = (filePath) => /^index(?:\.mdx?|\.html)?$/i.test(normalizePath$1(filePath));
659
659
  const resolveLocalHelperFile = (sourceRelativePath, builtRelativePath) => {
660
660
  const candidates = [
661
- node_path.resolve(__dirname$2, sourceRelativePath),
662
- node_path.resolve(__dirname$2, "..", "src", sourceRelativePath),
663
- node_path.resolve(__dirname$2, builtRelativePath)
661
+ node_path.resolve(__dirname$1, sourceRelativePath),
662
+ node_path.resolve(__dirname$1, "..", "src", sourceRelativePath),
663
+ node_path.resolve(__dirname$1, builtRelativePath)
664
664
  ];
665
665
  for (const candidate of candidates) {
666
666
  if (node_fs.existsSync(candidate)) {
667
667
  return candidate;
668
668
  }
669
669
  }
670
- return node_path.resolve(__dirname$2, builtRelativePath);
670
+ return node_path.resolve(__dirname$1, builtRelativePath);
671
+ };
672
+ const injectStylesheetLinks$1 = (html, hrefs) => {
673
+ const missingHrefs = hrefs.filter((href) => !html.includes(`href="${href}"`));
674
+ if (missingHrefs.length === 0) {
675
+ return html;
676
+ }
677
+ const linkTags = missingHrefs.map((href) => `<link rel="stylesheet" href="${href}">`).join("");
678
+ if (html.includes("</head>")) {
679
+ return html.replace("</head>", `${linkTags}</head>`);
680
+ }
681
+ return `${linkTags}${html}`;
682
+ };
683
+ const getComponentStylesheetHrefs$1 = async (componentsOutputDir, componentsPublicDir) => {
684
+ if (!node_fs.existsSync(componentsOutputDir)) {
685
+ return [];
686
+ }
687
+ const cssFiles = await glob.async(node_path.join(componentsOutputDir, "**/*.css"));
688
+ return cssFiles.sort((left, right) => left.localeCompare(right)).map(
689
+ (cssFile) => `/${normalizePath$1(node_path.join(componentsPublicDir, node_path.relative(componentsOutputDir, cssFile)))}`
690
+ );
691
+ };
692
+ const injectComponentStylesheetsIntoOutputHtml = async (outputProjectDir, componentsOutputDir, componentsPublicDir) => {
693
+ if (!node_fs.existsSync(outputProjectDir)) {
694
+ return;
695
+ }
696
+ const stylesheetHrefs = await getComponentStylesheetHrefs$1(
697
+ componentsOutputDir,
698
+ componentsPublicDir
699
+ );
700
+ if (stylesheetHrefs.length === 0) {
701
+ return;
702
+ }
703
+ const htmlFiles = await glob.async(node_path.join(outputProjectDir, "**/*.html"));
704
+ for (const htmlFile of htmlFiles) {
705
+ const currentHtml = await promises.readFile(htmlFile, "utf8");
706
+ const nextHtml = injectStylesheetLinks$1(currentHtml, stylesheetHrefs);
707
+ if (nextHtml !== currentHtml) {
708
+ await promises.writeFile(htmlFile, nextHtml);
709
+ }
710
+ }
671
711
  };
672
712
  const build = async ({
673
713
  projectDir,
@@ -703,6 +743,7 @@ const build = async ({
703
743
  const inputPagesDir = projectPaths.pagesSourceDirAbsolute;
704
744
  const inputComponentsDir = projectPaths.componentsSourceDirAbsolute;
705
745
  const inputAssetsDir = projectPaths.assetsSourceDirAbsolute;
746
+ const inputPublicDir = node_path.join(projectDir, "public");
706
747
  const hasInputPagesDir = projectPaths.hasPagesSourceDir;
707
748
  const tmpPagesDir = node_path.join(config.tmp, projectPaths.pagesSourceDir);
708
749
  const tmpComponentsDir = node_path.join(config.tmp, projectPaths.componentsSourceDir);
@@ -723,6 +764,7 @@ const build = async ({
723
764
  console.log("Input pages dir:", inputPagesDir);
724
765
  console.log("Input components dir:", inputComponentsDir);
725
766
  console.log("Input assets dir:", inputAssetsDir);
767
+ console.log("Input public dir:", inputPublicDir);
726
768
  console.log("Temp pages dir:", tmpPagesDir);
727
769
  console.log("Temp components dir:", tmpComponentsDir);
728
770
  console.log("Output pages dir:", outputPagesDir);
@@ -741,6 +783,8 @@ const build = async ({
741
783
  changeKind = "component";
742
784
  } else if (isPathInOrUnder$1(changedRelative, projectPaths.assetsSourceDir)) {
743
785
  changeKind = "asset";
786
+ } else if (isPathInOrUnder$1(changedRelative, "public")) {
787
+ changeKind = "asset";
744
788
  } else if (changedRelative === "config.ts" || changedRelative === "config.js") {
745
789
  changeKind = "config";
746
790
  }
@@ -758,7 +802,7 @@ const build = async ({
758
802
  const shouldCopyToTemp = (src) => {
759
803
  const relative2 = src.startsWith(projectDir) ? normalizePath$1(src.slice(projectDir.length).replace(/^[\\/]+/, "")) : src;
760
804
  const firstSegment = relative2.split(/[\\/]/)[0];
761
- return !(firstSegment === "node_modules" || firstSegment === config.output || firstSegment === ".endpoints" || firstSegment === ".ssg-temp" || firstSegment === ".git" || projectPaths.assetsSourceDirCandidates.some(
805
+ return !(firstSegment === "node_modules" || firstSegment === config.output || firstSegment === ".endpoints" || firstSegment === ".ssg-temp" || firstSegment === ".git" || firstSegment === "public" || projectPaths.assetsSourceDirCandidates.some(
762
806
  (candidateDir) => isPathInOrUnder$1(relative2, candidateDir)
763
807
  ));
764
808
  };
@@ -1052,6 +1096,16 @@ const build = async ({
1052
1096
  if (node_fs.existsSync(inputAssetsDir)) {
1053
1097
  await promises.cp(inputAssetsDir, outputAssetsDir, { recursive: true });
1054
1098
  }
1099
+ if (node_fs.existsSync(inputPublicDir)) {
1100
+ await promises.cp(inputPublicDir, outputProjectDir, { recursive: true });
1101
+ }
1102
+ }
1103
+ if (isFullBuild || changeKind === "page" || changeKind === "component") {
1104
+ await injectComponentStylesheetsIntoOutputHtml(
1105
+ outputProjectDir,
1106
+ outputComponentsDir,
1107
+ projectPaths.componentsPublicDir
1108
+ );
1055
1109
  }
1056
1110
  timeEndDebug("[build] copy-outputs");
1057
1111
  if (isFullBuild || changeKind === "component") {
@@ -1280,8 +1334,6 @@ const handleRpcRequest = async (ctx) => {
1280
1334
  }
1281
1335
  };
1282
1336
 
1283
- const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('vite-CBAZNNYA.cjs', document.baseURI).href)));
1284
- const __dirname$1 = node_path.dirname(__filename$1);
1285
1337
  const MIME_TYPES = {
1286
1338
  ".css": "text/css; charset=utf-8",
1287
1339
  ".gif": "image/gif",
@@ -1680,18 +1732,25 @@ const injectHmrClientModule = (html) => {
1680
1732
  }
1681
1733
  return `${importTag}${html}`;
1682
1734
  };
1683
- const resolveRuntimeHelperFile = () => {
1684
- const candidates = [
1685
- node_path.join(__dirname$1, "runtime.ts"),
1686
- node_path.join(__dirname$1, "..", "src", "runtime.ts"),
1687
- node_path.join(__dirname$1, "runtime.mjs")
1688
- ];
1689
- for (const candidate of candidates) {
1690
- if (node_fs.existsSync(candidate)) {
1691
- return candidate;
1692
- }
1735
+ const injectStylesheetLinks = (html, hrefs) => {
1736
+ const missingHrefs = hrefs.filter((href) => !html.includes(`href="${href}"`));
1737
+ if (missingHrefs.length === 0) {
1738
+ return html;
1693
1739
  }
1694
- return null;
1740
+ const linkTags = missingHrefs.map((href) => `<link rel="stylesheet" href="${href}">`).join("");
1741
+ if (html.includes("</head>")) {
1742
+ return html.replace("</head>", `${linkTags}</head>`);
1743
+ }
1744
+ return `${linkTags}${html}`;
1745
+ };
1746
+ const getComponentStylesheetHrefs = async (componentsOutputDir, componentsPublicDir) => {
1747
+ if (!node_fs.existsSync(componentsOutputDir)) {
1748
+ return [];
1749
+ }
1750
+ const cssFiles = await glob.async(node_path.join(componentsOutputDir, "**/*.css"));
1751
+ return cssFiles.sort((left, right) => left.localeCompare(right)).map(
1752
+ (cssFile) => `/${normalizePath(node_path.join(componentsPublicDir, node_path.relative(componentsOutputDir, cssFile)))}`
1753
+ );
1695
1754
  };
1696
1755
  const isHtmlLikeFile = (filePath) => {
1697
1756
  const extension = node_path.extname(filePath);
@@ -1741,6 +1800,9 @@ const classifyChangedFile = (file, projectDir, config, rpcFile) => {
1741
1800
  if (isPathInOrUnder(relativeFile, paths.assetsSourceDir)) {
1742
1801
  return "asset";
1743
1802
  }
1803
+ if (isPathInOrUnder(relativeFile, "public")) {
1804
+ return "asset";
1805
+ }
1744
1806
  return "dependency";
1745
1807
  };
1746
1808
  const filePathToComponentPublicPath = (file, componentsSourceDir, componentsPublicDir) => {
@@ -2129,6 +2191,13 @@ function defussSsg(options = {}) {
2129
2191
  );
2130
2192
  const el = server.renderSync(vdom, document.documentElement, { browserGlobals });
2131
2193
  let html = server.renderToString(el);
2194
+ html = injectStylesheetLinks(
2195
+ html,
2196
+ await getComponentStylesheetHrefs(
2197
+ node_path.join(projectDir, config.output, currentPaths.componentsPublicDir),
2198
+ currentPaths.componentsPublicDir
2199
+ )
2200
+ );
2132
2201
  html = injectHmrClientModule(html);
2133
2202
  html = await server$1.transformIndexHtml(requestUrl.pathname, html);
2134
2203
  res.setHeader("Cache-Control", "no-store");
@@ -2143,27 +2212,14 @@ function defussSsg(options = {}) {
2143
2212
  const maybeServeComponent = async (server, req, res) => {
2144
2213
  const requestUrl = new URL(req.url || "/", "http://localhost");
2145
2214
  const pathname = requestUrl.pathname;
2146
- if (!pathname.startsWith(`/${config.components}/`)) {
2147
- return false;
2148
- }
2149
- const componentRoutePrefix = `/${config.components}/`;
2150
- if (pathname === `/${config.components}/runtime.js`) {
2151
- const runtimePath = resolveRuntimeHelperFile();
2152
- if (!runtimePath) {
2153
- return false;
2154
- }
2155
- const transformed2 = await server.transformRequest(runtimePath);
2156
- if (transformed2) {
2157
- res.setHeader("Cache-Control", "no-store");
2158
- res.setHeader("Content-Type", "text/javascript; charset=utf-8");
2159
- res.statusCode = 200;
2160
- res.end(transformed2.code);
2161
- return true;
2162
- }
2215
+ const currentPaths = resolveSsgPaths(projectDir, config);
2216
+ const componentRoutePrefix = `/${currentPaths.componentsPublicDir}/`;
2217
+ if (!pathname.startsWith(componentRoutePrefix)) {
2163
2218
  return false;
2164
2219
  }
2165
2220
  const relativeComponentPath = pathname.slice(componentRoutePrefix.length);
2166
- const isBuiltComponentAsset = relativeComponentPath.startsWith("chunks/") || /^chunk-|^client-/.test(relativeComponentPath);
2221
+ const componentExtension = node_path.extname(relativeComponentPath).toLowerCase();
2222
+ const isBuiltComponentAsset = relativeComponentPath === "runtime.js" || relativeComponentPath.startsWith("chunks/") || /^chunk-|^client-/.test(relativeComponentPath) || componentExtension !== ".js";
2167
2223
  if (isBuiltComponentAsset) {
2168
2224
  return false;
2169
2225
  }
@@ -2171,7 +2227,6 @@ function defussSsg(options = {}) {
2171
2227
  node_path.extname(relativeComponentPath),
2172
2228
  ""
2173
2229
  );
2174
- const currentPaths = resolveSsgPaths(projectDir, config);
2175
2230
  const extensions = [".tsx", ".ts", ".jsx", ".js"];
2176
2231
  let sourceFile = null;
2177
2232
  for (const ext of extensions) {
@@ -2185,10 +2240,7 @@ function defussSsg(options = {}) {
2185
2240
  }
2186
2241
  }
2187
2242
  if (!sourceFile) {
2188
- res.setHeader("Cache-Control", "no-store");
2189
- res.statusCode = 404;
2190
- res.end();
2191
- return true;
2243
+ return false;
2192
2244
  }
2193
2245
  const transformed = await server.transformRequest(sourceFile);
2194
2246
  if (transformed) {
@@ -2226,9 +2278,18 @@ function defussSsg(options = {}) {
2226
2278
  MIME_TYPES[node_path.extname(outputFile)] ?? "application/octet-stream"
2227
2279
  );
2228
2280
  if (outputFile.endsWith(".html")) {
2281
+ const currentPaths = resolveSsgPaths(projectDir, config);
2229
2282
  const html = await server.transformIndexHtml(
2230
2283
  requestUrl.pathname,
2231
- injectHmrClientModule(body.toString("utf8"))
2284
+ injectHmrClientModule(
2285
+ injectStylesheetLinks(
2286
+ body.toString("utf8"),
2287
+ await getComponentStylesheetHrefs(
2288
+ node_path.join(projectDir, config.output, currentPaths.componentsPublicDir),
2289
+ currentPaths.componentsPublicDir
2290
+ )
2291
+ )
2292
+ )
2232
2293
  );
2233
2294
  res.statusCode = 200;
2234
2295
  res.end(method === "HEAD" ? void 0 : html);
@@ -2283,6 +2344,7 @@ function defussSsg(options = {}) {
2283
2344
  ...paths.assetsSourceDirCandidates.map(
2284
2345
  (candidateDir) => node_path.join(projectDir, candidateDir)
2285
2346
  ),
2347
+ node_path.join(projectDir, "public"),
2286
2348
  node_path.join(projectDir, "config.ts"),
2287
2349
  node_path.join(projectDir, "config.js")
2288
2350
  ];
package/dist/vite.cjs CHANGED
@@ -1,16 +1,16 @@
1
1
  'use strict';
2
2
 
3
+ require('fast-glob');
3
4
  require('node:fs');
4
5
  require('node:fs/promises');
5
6
  require('node:path');
6
- require('node:url');
7
7
  require('defuss/server');
8
- var vite = require('./vite-CBAZNNYA.cjs');
8
+ var vite = require('./vite-DlnPNJYO.cjs');
9
9
  require('node:crypto');
10
10
  require('@mdx-js/rollup');
11
- require('fast-glob');
12
11
  require('vite');
13
12
  require('defuss-vite');
13
+ require('node:url');
14
14
  require('node:os');
15
15
  require('esbuild');
16
16
  require('rehype-katex');
package/dist/vite.mjs CHANGED
@@ -1,14 +1,14 @@
1
+ import 'fast-glob';
1
2
  import 'node:fs';
2
3
  import 'node:fs/promises';
3
4
  import 'node:path';
4
- import 'node:url';
5
5
  import 'defuss/server';
6
- export { f as defussSsg } from './vite-CaqPNORI.mjs';
6
+ export { f as defussSsg } from './vite-DLXcqaZX.mjs';
7
7
  import 'node:crypto';
8
8
  import '@mdx-js/rollup';
9
- import 'fast-glob';
10
9
  import 'vite';
11
10
  import 'defuss-vite';
11
+ import 'node:url';
12
12
  import 'node:os';
13
13
  import 'esbuild';
14
14
  import 'rehype-katex';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "defuss-ssg",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"