@beforesemicolon/builder 1.8.13 → 1.8.18

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.
Files changed (35) hide show
  1. package/dist/cjs/docs/run.js +15 -13
  2. package/dist/cjs/docs/templates/fading-citrus/README.md +8 -6
  3. package/dist/cjs/docs/templates/fading-citrus/assets/logo.dark.svg +8 -0
  4. package/dist/cjs/docs/templates/fading-citrus/assets/logo.light.svg +8 -0
  5. package/dist/cjs/docs/templates/fading-citrus/layouts/_footer.js +7 -3
  6. package/dist/cjs/docs/templates/fading-citrus/layouts/_head-meta.js +3 -2
  7. package/dist/cjs/docs/templates/fading-citrus/layouts/_header.js +6 -1
  8. package/dist/cjs/docs/templates/fading-citrus/layouts/document.js +2 -1
  9. package/dist/cjs/docs/templates/fading-citrus/layouts/landing.js +3 -1
  10. package/dist/cjs/docs/templates/fading-citrus/stylesheets/common.css +132 -49
  11. package/dist/cjs/docs/templates/fading-citrus/stylesheets/documentation.css +32 -24
  12. package/dist/cjs/docs/templates/fading-citrus/stylesheets/github-dark.hightlighter.css +12 -12
  13. package/dist/cjs/docs/templates/fading-citrus/stylesheets/github-light.hightlighter.css +22 -22
  14. package/dist/cjs/docs/templates/fading-citrus/stylesheets/landing.css +74 -87
  15. package/dist/cjs/docs/templates/fading-citrus/template.config.js +106 -15
  16. package/dist/cjs/docs/types.js +1 -1
  17. package/dist/esm/docs/run.js +16 -14
  18. package/dist/esm/docs/templates/fading-citrus/README.md +8 -6
  19. package/dist/esm/docs/templates/fading-citrus/assets/logo.dark.svg +8 -0
  20. package/dist/esm/docs/templates/fading-citrus/assets/logo.light.svg +8 -0
  21. package/dist/esm/docs/templates/fading-citrus/layouts/_footer.js +7 -3
  22. package/dist/esm/docs/templates/fading-citrus/layouts/_head-meta.js +3 -2
  23. package/dist/esm/docs/templates/fading-citrus/layouts/_header.js +6 -1
  24. package/dist/esm/docs/templates/fading-citrus/layouts/document.js +2 -1
  25. package/dist/esm/docs/templates/fading-citrus/layouts/landing.js +3 -1
  26. package/dist/esm/docs/templates/fading-citrus/stylesheets/common.css +132 -49
  27. package/dist/esm/docs/templates/fading-citrus/stylesheets/documentation.css +32 -24
  28. package/dist/esm/docs/templates/fading-citrus/stylesheets/github-dark.hightlighter.css +12 -12
  29. package/dist/esm/docs/templates/fading-citrus/stylesheets/github-light.hightlighter.css +22 -22
  30. package/dist/esm/docs/templates/fading-citrus/stylesheets/landing.css +74 -87
  31. package/dist/esm/docs/templates/fading-citrus/template.config.js +106 -15
  32. package/dist/types/docs/types.d.ts +1 -0
  33. package/package.json +1 -1
  34. package/dist/cjs/docs/templates/fading-citrus/assets/before-semicolon-logo.png +0 -0
  35. package/dist/esm/docs/templates/fading-citrus/assets/before-semicolon-logo.png +0 -0
@@ -1,48 +1,50 @@
1
- "use strict";var de=Object.create;var C=Object.defineProperty;var he=Object.getOwnPropertyDescriptor;var ge=Object.getOwnPropertyNames;var ye=Object.getPrototypeOf,we=Object.prototype.hasOwnProperty;var m=(e,t)=>C(e,"name",{value:t,configurable:!0});var ve=(e,t)=>{for(var r in t)C(e,r,{get:t[r],enumerable:!0})},Q=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of ge(t))!we.call(e,a)&&a!==r&&C(e,a,{get:()=>t[a],enumerable:!(n=he(t,a))||n.enumerable});return e};var w=(e,t,r)=>(r=e!=null?de(ye(e)):{},Q(t||!e||!e.__esModule?C(r,"default",{value:e,enumerable:!0}):r,e)),De=e=>Q(C({},"__esModule",{value:!0}),e);var Be={};ve(Be,{buildDocs:()=>_e});module.exports=De(Be);const Ne={};var te=require("marked"),c=require("fs/promises"),o=w(require("path"),1),se=w(require("front-matter"),1),re=w(require("isomorphic-dompurify"),1),oe=require("marked-highlight"),W=w(require("highlight.js"),1),ne=w(require("./templates/default.template.js"),1),ae=w(require("./renderer/index.js"),1),ie=require("./markdown-layout/index.js"),le=w(require("clean-css"),1),ce=w(require("html-minifier"),1),X=w(require("@putout/minify"),1),$=require("url"),f=w(require("fs"),1);let K;const V=new le.default({inline:["none"]}),A=new Map;A.set("default",ne.default);let R;const Y=m((e,t)=>t===!1?!1:t===void 0?e:e===!1||e===void 0?t:{...e,...t},"mergeThemeMode"),Se=m((e,t)=>{if(!(!e&&!t))return{light:Y(e?.light,t?.light),dark:Y(e?.dark,t?.dark)}},"mergeTheme"),xe=m((e,t)=>({...e,...t,markdownLayouts:{...e.markdownLayouts,...t.markdownLayouts},scripts:{...e.scripts,...t.scripts},theme:Se(e.theme,t.theme)}),"mergeTemplateConfig"),be=m((e,t)=>{if(typeof e=="function")return!0;const{match:r}=e;return r?typeof r=="string"?t.includes(r):Array.isArray(r)?r.some(n=>t.includes(n)):r instanceof RegExp?r.test(t):r(t):!0},"scriptMatches"),$e=m((e,t)=>e?Object.values(e).filter(r=>r!==!1).filter(r=>be(r,t)).map(r=>typeof r=="function"?r():r.render()):[],"renderPageScripts"),G=m((e,t)=>{const r=Object.entries(t).filter(([n])=>n.startsWith("--")).map(([n,a])=>` ${n}: ${a};`).join(`
1
+ "use strict";var he=Object.create;var j=Object.defineProperty;var ge=Object.getOwnPropertyDescriptor;var ye=Object.getOwnPropertyNames;var we=Object.getPrototypeOf,ve=Object.prototype.hasOwnProperty;var m=(e,t)=>j(e,"name",{value:t,configurable:!0});var Se=(e,t)=>{for(var r in t)j(e,r,{get:t[r],enumerable:!0})},Q=(e,t,r,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of ye(t))!ve.call(e,n)&&n!==r&&j(e,n,{get:()=>t[n],enumerable:!(a=ge(t,n))||a.enumerable});return e};var w=(e,t,r)=>(r=e!=null?he(we(e)):{},Q(t||!e||!e.__esModule?j(r,"default",{value:e,enumerable:!0}):r,e)),De=e=>Q(j({},"__esModule",{value:!0}),e);var Ue={};Se(Ue,{buildDocs:()=>Ne});module.exports=De(Ue);const ze={};var se=require("marked"),c=require("fs/promises"),o=w(require("path"),1),re=w(require("front-matter"),1),oe=w(require("isomorphic-dompurify"),1),ae=require("marked-highlight"),_=w(require("highlight.js"),1),ne=w(require("./templates/default.template.js"),1),ie=w(require("./renderer/index.js"),1),le=require("./markdown-layout/index.js"),ce=w(require("clean-css"),1),me=w(require("html-minifier"),1),X=w(require("@putout/minify"),1),$=require("url"),f=w(require("fs"),1);let V,A="";const Y=new ce.default({inline:["none"]}),R=new Map;R.set("default",ne.default);let W;const Z=m((e,t)=>t===!1?!1:t===void 0?e:e===!1||e===void 0?t:{...e,...t},"mergeThemeMode"),be=m((e,t)=>{if(!(!e&&!t))return{light:Z(e?.light,t?.light),dark:Z(e?.dark,t?.dark)}},"mergeTheme"),xe=m((e,t)=>({...e,...t,markdownLayouts:{...e.markdownLayouts,...t.markdownLayouts},scripts:{...e.scripts,...t.scripts},theme:be(e.theme,t.theme)}),"mergeTemplateConfig"),$e=m((e,t)=>{if(typeof e=="function")return!0;const{match:r}=e;return r?typeof r=="string"?t.includes(r):Array.isArray(r)?r.some(a=>t.includes(a)):r instanceof RegExp?r.test(t):r(t):!0},"scriptMatches"),Pe=m((e,t)=>e?Object.values(e).filter(r=>r!==!1).filter(r=>$e(r,t)).map(r=>typeof r=="function"?r():r.render()):[],"renderPageScripts"),G=m((e,t)=>{const r=Object.entries(t).filter(([a])=>a.startsWith("--")).map(([a,n])=>` ${a}: ${n};`).join(`
2
2
  `);return r?`${e} {
3
3
  ${r}
4
- }`:""},"renderThemeBlock"),Pe=m(e=>{if(!e)return"";const t=e.light===!1,r=e.light===!1?void 0:e.light,n=e.dark===!1?void 0:e.dark,a=r||n;return a?[G(":root",a),n&&!t?`@media (prefers-color-scheme: dark) {
4
+ }`:""},"renderThemeBlock"),ke=m(e=>{if(!e)return"";const t=e.light===!1,r=e.dark===!1,a=e.light===!1?void 0:e.light,n=r?void 0:e.dark,u=a||n;return u?[G(":root",u),n&&!t?`@media (prefers-color-scheme: dark) {
5
5
  ${G(" :root",n)}
6
- }`:"",r?G('[data-theme="light"]',r):"",n?G('[data-theme="dark"]',n):""].filter(Boolean).join(`
6
+ }`:"",a?G('[data-theme="light"]',a):"",n?G('[data-theme="dark"]',n):"",t&&n?`.site-logo-light { display: none; }
7
+ .site-logo-dark { display: block; }`:"",r&&a?`.site-logo-light { display: block; }
8
+ .site-logo-dark { display: none; }`:""].filter(Boolean).join(`
7
9
 
8
- `):""},"renderThemeStylesheet"),Te=m((e="")=>String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;"),"escapeXML"),ke=m((e="")=>e.replace(/```[\s\S]*?```/g," ").replace(/`([^`]+)`/g,"$1").replace(/!\[[^\]]*]\([^)]*\)/g," ").replace(/\[([^\]]+)]\([^)]*\)/g,"$1").replace(/^#{1,6}\s+/gm,"").replace(/^---+$/gm," ").replace(/[>*_~]/g,"").replace(/\s+/g," ").trim(),"stripMarkdown"),Me=m(e=>e==="/index.html"?"/":e.replace(/\/index\.html$/,"/").replace(/\.html$/,"").replace(/\/+/g,"/"),"pagePathToUrlPath"),Z=m((e,t)=>e?`${e.replace(/\/$/,"")}${t==="/"?"/":t}`:t,"canonicalUrl"),je=m(async(e,t)=>f.default.existsSync(e)?(await(0,c.mkdir)(o.default.dirname(t),{recursive:!0}),await(0,c.cp)(e,t),!0):!1,"copyIfExists"),Ce=m(e=>{const t={sitemap:!0,robots:!0,llms:!0,llmsFull:!0,netlify:!1};return e===!1?{sitemap:!1,robots:!1,llms:!1,llmsFull:!1,netlify:!1}:e===!0||e===void 0?t:{...t,...e}},"normalizeGeneratedFilesConfig"),Fe=m((e,t)=>{if(!t)return"";const r=new Date().toISOString().slice(0,10);return`<?xml version="1.0" encoding="UTF-8"?>
10
+ `):""},"renderThemeStylesheet"),Te=m((e="")=>String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;"),"escapeXML"),Me=m((e="")=>e.replace(/```[\s\S]*?```/g," ").replace(/`([^`]+)`/g,"$1").replace(/!\[[^\]]*]\([^)]*\)/g," ").replace(/\[([^\]]+)]\([^)]*\)/g,"$1").replace(/^#{1,6}\s+/gm,"").replace(/^---+$/gm," ").replace(/[>*_~]/g,"").replace(/\s+/g," ").trim(),"stripMarkdown"),Ce=m(e=>e==="/index.html"?"/":e.replace(/\/index\.html$/,"/").replace(/\.html$/,"").replace(/\/+/g,"/"),"pagePathToUrlPath"),ee=m((e,t)=>e?`${e.replace(/\/$/,"")}${t==="/"?"/":t}`:t,"canonicalUrl"),je=m(async(e,t)=>f.default.existsSync(e)?(await(0,c.mkdir)(o.default.dirname(t),{recursive:!0}),await(0,c.cp)(e,t),!0):!1,"copyIfExists"),Fe=m(async e=>{const t=o.default.resolve(e,"logo.svg");f.default.existsSync(t)&&await Promise.all(["logo.light.svg","logo.dark.svg"].map(async r=>{const a=o.default.resolve(e,r);f.default.existsSync(a)||await(0,c.cp)(t,a)}))},"ensureLogoModeAssets"),Ge=m(e=>{const t={sitemap:!0,robots:!0,llms:!0,llmsFull:!0,netlify:!1};return e===!1?{sitemap:!1,robots:!1,llms:!1,llmsFull:!1,netlify:!1}:e===!0||e===void 0?t:{...t,...e}},"normalizeGeneratedFilesConfig"),Le=m((e,t)=>{if(!t)return"";const r=new Date().toISOString().slice(0,10);return`<?xml version="1.0" encoding="UTF-8"?>
9
11
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
10
- ${e.map(a=>{const u=a.urlPath==="/"?"1.0":"0.8";return` <url>
11
- <loc>${Te(Z(t,a.urlPath))}</loc>
12
+ ${e.map(n=>{const u=n.urlPath==="/"?"1.0":"0.8";return` <url>
13
+ <loc>${Te(ee(t,n.urlPath))}</loc>
12
14
  <lastmod>${r}</lastmod>
13
15
  <changefreq>weekly</changefreq>
14
16
  <priority>${u}</priority>
15
17
  </url>`}).join(`
16
18
  `)}
17
19
  </urlset>
18
- `},"generateSitemap"),Ge=m(e=>`User-agent: *
20
+ `},"generateSitemap"),Ee=m(e=>`User-agent: *
19
21
  Allow: /
20
22
  ${e?`
21
23
  Sitemap: ${e.replace(/\/$/,"")}/sitemap.xml
22
24
  Host: ${e.replace(/\/$/,"")}
23
25
  `:""}
24
- `,"generateRobots"),Ee=m(e=>`# Documentation
26
+ `,"generateRobots"),Oe=m(e=>`# Documentation
25
27
 
26
28
  ## Page index
27
29
 
28
30
  ${e.map(t=>`- ${t.name}: ${t.urlPath} - ${t.description}`).join(`
29
31
  `)}
30
- `,"generateLlms"),Le=m((e,t)=>`# Documentation for AI agents
32
+ `,"generateLlms"),Ae=m((e,t)=>`# Documentation for AI agents
31
33
 
32
34
  This file is generated at build time from the Markdown documentation. Use it to choose the most relevant page before reading the canonical HTML documentation.
33
35
 
34
36
  ${e.map(r=>`## ${r.title}
35
37
 
36
- URL: ${Z(t,r.urlPath)}
38
+ URL: ${ee(t,r.urlPath)}
37
39
  Source: docs/${r.sourcePath}
38
40
  Description: ${r.description}
39
41
 
40
42
  ${r.summary||r.description}
41
43
  `).join(`
42
44
  `)}
43
- `,"generateLlmsFull"),Oe=m((e,t)=>{const r=["# Generated by @beforesemicolon/builder"];return[t.llms?"llms.txt":"",t.llmsFull?"llms-full.txt":"",t.sitemap?"sitemap.xml":"",t.robots?"robots.txt":""].filter(Boolean).forEach(a=>r.push(`/${a} /${a} 200`)),e.forEach(a=>{if(a.urlPath==="/")return;const u=a.urlPath.endsWith("/")?`${a.urlPath}index.html`:`${a.urlPath}.html`;r.push(`${a.urlPath} ${u} 200`),a.urlPath.endsWith("/")&&r.push(`${a.urlPath.replace(/\/$/,"")} ${u} 200`)}),`${[...new Set(r)].join(`
45
+ `,"generateLlmsFull"),Re=m((e,t)=>{const r=["# Generated by @beforesemicolon/builder"];return[t.llms?"llms.txt":"",t.llmsFull?"llms-full.txt":"",t.sitemap?"sitemap.xml":"",t.robots?"robots.txt":""].filter(Boolean).forEach(n=>r.push(`/${n} /${n} 200`)),e.forEach(n=>{if(n.urlPath==="/")return;const u=n.urlPath.endsWith("/")?`${n.urlPath}index.html`:`${n.urlPath}.html`;r.push(`${n.urlPath} ${u} 200`),n.urlPath.endsWith("/")&&r.push(`${n.urlPath.replace(/\/$/,"")} ${u} 200`)}),`${[...new Set(r)].join(`
44
46
  `)}
45
- `},"generateNetlifyRedirects"),Ae=m(e=>`[build]
47
+ `},"generateNetlifyRedirects"),We=m(e=>`[build]
46
48
  command = "node build-docs.js"
47
49
  publish = "${e||"website"}"
48
50
 
@@ -73,4 +75,4 @@ for = "/robots.txt"
73
75
  [headers.values]
74
76
  Content-Type = "text/plain; charset=utf-8"
75
77
  Cache-Control = "public, max-age=0, must-revalidate"
76
- `,"generateNetlifyToml"),b=m(async(e,t,r,n)=>{if(await je(e,t))return;const a=r();if(!a){n&&console.warn(n);return}await(0,c.mkdir)(o.default.dirname(t),{recursive:!0}),await(0,c.writeFile)(t,a)},"writeGeneratedFile"),Re=m(async({docsDir:e,docsSiteDir:t,pages:r,siteUrl:n,publicDir:a,generatedFiles:u})=>{u.sitemap&&await b(o.default.resolve(e,"sitemap.xml"),o.default.resolve(t,"sitemap.xml"),()=>Fe(r,n),"Skipping generated sitemap.xml because buildDocs({ siteUrl }) is not set."),u.robots&&await b(o.default.resolve(e,"robots.txt"),o.default.resolve(t,"robots.txt"),()=>Ge(n)),u.llms&&await b(o.default.resolve(e,"llms.txt"),o.default.resolve(t,"llms.txt"),()=>Ee(r)),u.llmsFull&&await b(o.default.resolve(e,"llms-full.txt"),o.default.resolve(t,"llms-full.txt"),()=>Le(r,n)),u.netlify&&(await b(o.default.resolve(e,"_redirects"),o.default.resolve(t,"_redirects"),()=>Oe(r,u)),await b(o.default.resolve(e,"netlify.toml"),o.default.resolve(t,"netlify.toml"),()=>Ae(a&&o.default.basename(a))))},"writeGeneratedDocsFiles"),ee=m((e={},t,r)=>{const n=new te.Marked((0,oe.markedHighlight)({langPrefix:"hljs language-",highlight(a,u){const P=W.default.getLanguage(u)?u:"plaintext";return W.default.highlight(a,{language:P}).value}}));return n.use({renderer:ae.default}),n.use((0,ie.createMarkdownLayoutExtension)({marked:n,handlers:e.markdownLayouts,renderMarkdown:r})),e.markedOptions&&n.use(e.markedOptions),t&&n.use(t),n},"createBaseMarked"),We=m((e={},t)=>{const r=ee(e,t),n=m(u=>r.parse(u),"renderMarkdown"),a=ee(e,t,n);return a.use({hooks:{postprocess(u){const{layout:P="default",...y}=R.attributes,T=$e(e.scripts,u);u=re.default.sanitize(u);const k=[...u.matchAll(/<h([0-6])\sid="[^"]+".*?>.*?<a\s+href="([^"]+)".*?>([^<]+)<\/a>/gm)].map(S=>({path:S[2],label:S[3],level:S[1]}));return A.get(P)?.({...y,content:u,siteMap:R.siteMap,tableOfContent:k,projectMeta:K,renderMarkdown:n,scripts:T,themeStylesheet:e.theme?"/stylesheets/theme.css":void 0})||u}}}),a},"createMarked"),v=m(async e=>{const t=await(0,c.readdir)(e),r=[];for(const n of t){const a=o.default.extname(n);/^[._]/.test(n)||(a?r.push(o.default.join(e,n)):/(stylesheets|assets|scripts)/.test(n)||r.push(...await v(o.default.join(e,n))))}return r},"traverseDirectory"),_e=m(async({srcDir:e,publicDir:t,markedOptions:r,template:n,siteUrl:a,generatedFiles:u}={})=>{try{const s=o.default.resolve(process.cwd(),"package.json");f.default.existsSync(s)&&(K=JSON.parse(await(0,c.readFile)(s,"utf-8")))}catch(s){console.warn("Failed to load project package.json metadata",s)}const P=m(()=>{try{return __dirname}catch{return o.default.dirname((0,$.fileURLToPath)(Ne.url))}},"getDirname"),y=n?o.default.resolve(P(),"templates",n):null,T=y?o.default.resolve(y,"assets"):null,k=y?o.default.resolve(y,"stylesheets"):null,S=y?o.default.resolve(y,"layouts"):null;let M={};if(y)try{let s=o.default.resolve(y,"template.config.js");if(f.default.existsSync(s)||(s=s.replace(/\.js$/,".ts")),f.default.existsSync(s)){const i=await import((0,$.pathToFileURL)(s).href);M=i.default||i}}catch(s){console.error(`Failed to load template config for ${n}`,s)}const d=e??o.default.resolve(process.cwd(),"docs"),p=t??o.default.resolve(process.cwd(),"website"),me=Ce(u),D=o.default.resolve(d,"_template"),_=o.default.resolve(D,"assets"),B=o.default.resolve(D,"stylesheets"),N=o.default.resolve(D,"scripts"),U=o.default.resolve(D,"layouts"),z=o.default.resolve(d,"_layouts"),F=o.default.resolve(d,"assets"),q=o.default.resolve(d,"stylesheets"),E=o.default.resolve(d,"scripts");try{let s=o.default.resolve(D,"template.config.js");if(f.default.existsSync(s)||(s=s.replace(/\.js$/,".ts")),f.default.existsSync(s)){const i=await import((0,$.pathToFileURL)(s).href),l=i.default||i;M=xe(M,l)}}catch(s){console.error("Failed to load docs _template config",s)}const ue=We(M,r);if(await(0,c.mkdir)(p,{recursive:!0}),T&&f.default.existsSync(T))try{await(0,c.cp)(T,F.replace(d,p),{recursive:!0})}catch(s){console.warn(s)}if(f.default.existsSync(F))try{await(0,c.cp)(F,F.replace(d,p),{recursive:!0})}catch(s){console.warn(s)}if(f.default.existsSync(_))try{await(0,c.cp)(_,o.default.resolve(p,"assets"),{recursive:!0})}catch(s){console.warn(s)}try{const s=new Set;if(k&&f.default.existsSync(k)&&(await v(k)).forEach(l=>s.add(l)),f.default.existsSync(q)&&(await v(q)).forEach(l=>s.add(l)),f.default.existsSync(B)&&(await v(B)).forEach(l=>s.add(l)),s.size){await(0,c.mkdir)(o.default.join(p,"stylesheets"),{recursive:!0});for(const l of s){const h=await(0,c.readFile)(l,"utf-8"),g=V.minify(h);g.errors.length&&g.errors.forEach(console.error);const j=l.startsWith(D)?l.replace(D,p):l.startsWith(d)?l.replace(d,p):o.default.join(p,"stylesheets",o.default.basename(l));await(0,c.writeFile)(j,g.styles??h)}}const i=Pe(M.theme);if(i){const l=V.minify(i);l.errors.length&&l.errors.forEach(console.error),await(0,c.mkdir)(o.default.join(p,"stylesheets"),{recursive:!0}),await(0,c.writeFile)(o.default.join(p,"stylesheets","theme.css"),l.styles??i)}}catch(s){console.error(s)}if(f.default.existsSync(E))try{const s=await v(E);s.length&&await(0,c.mkdir)(E.replace(d,p),{recursive:!0});for(const i of s)try{const l=await(0,c.readFile)(i,"utf-8");await(0,c.writeFile)(i.replace(d,p),X.minify(l))}catch(l){console.error(l)}}catch(s){console.warn(s)}if(f.default.existsSync(N))try{const s=await v(N);s.length&&await(0,c.mkdir)(o.default.join(p,"scripts"),{recursive:!0});for(const i of s)try{const l=await(0,c.readFile)(i,"utf-8");await(0,c.writeFile)(i.replace(D,p),X.minify(l))}catch(l){console.error(l)}}catch(s){console.warn(s)}try{const s=new Map;S&&f.default.existsSync(S)&&(await v(S)).forEach(i=>{/\.(j|t)s$/.test(i)&&s.set(o.default.basename(i).replace(/\.(j|t)s$/,""),i)}),f.default.existsSync(z)&&(await v(z)).forEach(i=>{/\.(j|t)s$/.test(i)&&s.set(o.default.basename(i).replace(/\.(j|t)s$/,""),i)}),f.default.existsSync(U)&&(await v(U)).forEach(i=>{/\.(j|t)s$/.test(i)&&s.set(o.default.basename(i).replace(/\.(j|t)s$/,""),i)});for(const[i,l]of s.entries()){const h=await import((0,$.pathToFileURL)(l).href);A.set(i,h.default)}}catch(s){console.error("Template import failed",s)}const fe=await v(d),H=new Map,I=(await Promise.all(fe.filter(s=>s.endsWith(".md")).map(async s=>{const i=await(0,c.readFile)(s,"utf-8");return{filePath:s,...(0,se.default)(i)}}))).sort((s,i)=>(s.attributes.order??0)-(i.attributes.order??0)).map(({attributes:s,body:i,filePath:l})=>{const h=l.replace(d,"").replace(/\.md/,".html"),g=l.replace(d,p).replace(".md",".html"),j=g.replace(o.default.basename(g),""),L=o.default.basename(h)||"/",pe=h.replace(L,"").replace(/\/$/,"");let x=H;pe.split("/").filter(Boolean).forEach(O=>{x.has(O)||x.set(O,new Map),x=x.get(O)});const J={...s,path:h};return x.has(L)||x.set(L,J),{attributes:J,fileWebsitePath:g,fileDirWebsitePath:j,body:i,sourcePath:o.default.relative(d,l),siteMap:H}});for(const{attributes:s,body:i,fileDirWebsitePath:l,fileWebsitePath:h,siteMap:g}of I){R={attributes:s,siteMap:g};const j=await ue.parse(i);await(0,c.mkdir)(l,{recursive:!0}),await(0,c.writeFile)(h,ce.default.minify(j,{includeAutoGeneratedTags:!0,removeAttributeQuotes:!0,removeComments:!0,removeRedundantAttributes:!0,removeScriptTypeAttributes:!0,removeStyleLinkTypeAttributes:!0,sortClassName:!0,useShortDoctype:!0,collapseWhitespace:!0,minifyJS:!0,minifyCSS:!0}))}await Re({docsDir:d,docsSiteDir:p,pages:I.map(({attributes:s,body:i,sourcePath:l})=>{const h=Me(String(s.path)),g=ke(i);return{sourcePath:l,urlPath:h,title:String(s.title||s.name||"").trim()||"Documentation",name:String(s.name||s.title||"").trim()||"Documentation",description:String(s.description||"").trim()||g.slice(0,156)||"Documentation page.",order:Number(s.order??999),summary:g.slice(0,320)}}),siteUrl:a,publicDir:p,generatedFiles:me})},"buildDocs");0&&(module.exports={buildDocs});
78
+ `,"generateNetlifyToml"),x=m(async(e,t,r,a)=>{if(await je(e,t))return;const n=r();if(!n){a&&console.warn(a);return}await(0,c.mkdir)(o.default.dirname(t),{recursive:!0}),await(0,c.writeFile)(t,n)},"writeGeneratedFile"),_e=m(async({docsDir:e,docsSiteDir:t,pages:r,siteUrl:a,publicDir:n,generatedFiles:u})=>{u.sitemap&&await x(o.default.resolve(e,"sitemap.xml"),o.default.resolve(t,"sitemap.xml"),()=>Le(r,a),"Skipping generated sitemap.xml because buildDocs({ siteUrl }) is not set."),u.robots&&await x(o.default.resolve(e,"robots.txt"),o.default.resolve(t,"robots.txt"),()=>Ee(a)),u.llms&&await x(o.default.resolve(e,"llms.txt"),o.default.resolve(t,"llms.txt"),()=>Oe(r)),u.llmsFull&&await x(o.default.resolve(e,"llms-full.txt"),o.default.resolve(t,"llms-full.txt"),()=>Ae(r,a)),u.netlify&&(await x(o.default.resolve(e,"_redirects"),o.default.resolve(t,"_redirects"),()=>Re(r,u)),await x(o.default.resolve(e,"netlify.toml"),o.default.resolve(t,"netlify.toml"),()=>We(n&&o.default.basename(n))))},"writeGeneratedDocsFiles"),te=m((e={},t,r)=>{const a=new se.Marked((0,ae.markedHighlight)({langPrefix:"hljs language-",highlight(n,u){const P=_.default.getLanguage(u)?u:"plaintext";return _.default.highlight(n,{language:P}).value}}));return a.use({renderer:ie.default}),a.use((0,le.createMarkdownLayoutExtension)({marked:a,handlers:e.markdownLayouts,renderMarkdown:r})),e.markedOptions&&a.use(e.markedOptions),t&&a.use(t),a},"createBaseMarked"),Be=m((e={},t)=>{const r=te(e,t),a=m(u=>r.parse(u),"renderMarkdown"),n=te(e,t,a);return n.use({hooks:{postprocess(u){const{layout:P="default",...y}=W.attributes,k=Pe(e.scripts,u);u=oe.default.sanitize(u);const T=[...u.matchAll(/<h([0-6])\sid="[^"]+".*?>.*?<a\s+href="([^"]+)".*?>([^<]+)<\/a>/gm)].map(D=>({path:D[2],label:D[3],level:D[1]}));return R.get(P)?.({...y,content:u,siteMap:W.siteMap,tableOfContent:T,projectMeta:V,renderMarkdown:a,scripts:k,themeStylesheet:e.theme?`/stylesheets/theme.css?v=${A}`:void 0,cacheKey:A})||u}}}),n},"createMarked"),v=m(async e=>{const t=await(0,c.readdir)(e),r=[];for(const a of t){const n=o.default.extname(a);/^[._]/.test(a)||(n?r.push(o.default.join(e,a)):/(stylesheets|assets|scripts)/.test(a)||r.push(...await v(o.default.join(e,a))))}return r},"traverseDirectory"),Ne=m(async({srcDir:e,publicDir:t,markedOptions:r,template:a,siteUrl:n,generatedFiles:u}={})=>{A=Date.now().toString(36);try{const s=o.default.resolve(process.cwd(),"package.json");f.default.existsSync(s)&&(V=JSON.parse(await(0,c.readFile)(s,"utf-8")))}catch(s){console.warn("Failed to load project package.json metadata",s)}const P=m(()=>{try{return __dirname}catch{return o.default.dirname((0,$.fileURLToPath)(ze.url))}},"getDirname"),y=a?o.default.resolve(P(),"templates",a):null,k=y?o.default.resolve(y,"assets"):null,T=y?o.default.resolve(y,"stylesheets"):null,D=y?o.default.resolve(y,"layouts"):null;let M={};if(y)try{let s=o.default.resolve(y,"template.config.js");if(f.default.existsSync(s)||(s=s.replace(/\.js$/,".ts")),f.default.existsSync(s)){const i=await import((0,$.pathToFileURL)(s).href);M=i.default||i}}catch(s){console.error(`Failed to load template config for ${a}`,s)}const d=e??o.default.resolve(process.cwd(),"docs"),p=t??o.default.resolve(process.cwd(),"website"),ue=Ge(u),S=o.default.resolve(d,"_template"),B=o.default.resolve(S,"assets"),N=o.default.resolve(S,"stylesheets"),U=o.default.resolve(S,"scripts"),z=o.default.resolve(S,"layouts"),q=o.default.resolve(d,"_layouts"),F=o.default.resolve(d,"assets"),H=o.default.resolve(d,"stylesheets"),L=o.default.resolve(d,"scripts");try{let s=o.default.resolve(S,"template.config.js");if(f.default.existsSync(s)||(s=s.replace(/\.js$/,".ts")),f.default.existsSync(s)){const i=await import((0,$.pathToFileURL)(s).href),l=i.default||i;M=xe(M,l)}}catch(s){console.error("Failed to load docs _template config",s)}const fe=Be(M,r);if(await(0,c.mkdir)(p,{recursive:!0}),k&&f.default.existsSync(k))try{await(0,c.cp)(k,F.replace(d,p),{recursive:!0})}catch(s){console.warn(s)}if(f.default.existsSync(F))try{await(0,c.cp)(F,F.replace(d,p),{recursive:!0})}catch(s){console.warn(s)}if(f.default.existsSync(B))try{await(0,c.cp)(B,o.default.resolve(p,"assets"),{recursive:!0})}catch(s){console.warn(s)}await Fe(o.default.resolve(p,"assets"));try{const s=new Set;if(T&&f.default.existsSync(T)&&(await v(T)).forEach(l=>s.add(l)),f.default.existsSync(H)&&(await v(H)).forEach(l=>s.add(l)),f.default.existsSync(N)&&(await v(N)).forEach(l=>s.add(l)),s.size){await(0,c.mkdir)(o.default.join(p,"stylesheets"),{recursive:!0});for(const l of s){const h=await(0,c.readFile)(l,"utf-8"),g=Y.minify(h);g.errors.length&&g.errors.forEach(console.error);const C=l.startsWith(S)?l.replace(S,p):l.startsWith(d)?l.replace(d,p):o.default.join(p,"stylesheets",o.default.basename(l));await(0,c.writeFile)(C,g.styles??h)}}const i=ke(M.theme);if(i){const l=Y.minify(i);l.errors.length&&l.errors.forEach(console.error),await(0,c.mkdir)(o.default.join(p,"stylesheets"),{recursive:!0}),await(0,c.writeFile)(o.default.join(p,"stylesheets","theme.css"),l.styles??i)}}catch(s){console.error(s)}if(f.default.existsSync(L))try{const s=await v(L);s.length&&await(0,c.mkdir)(L.replace(d,p),{recursive:!0});for(const i of s)try{const l=await(0,c.readFile)(i,"utf-8");await(0,c.writeFile)(i.replace(d,p),X.minify(l))}catch(l){console.error(l)}}catch(s){console.warn(s)}if(f.default.existsSync(U))try{const s=await v(U);s.length&&await(0,c.mkdir)(o.default.join(p,"scripts"),{recursive:!0});for(const i of s)try{const l=await(0,c.readFile)(i,"utf-8");await(0,c.writeFile)(i.replace(S,p),X.minify(l))}catch(l){console.error(l)}}catch(s){console.warn(s)}try{const s=new Map;D&&f.default.existsSync(D)&&(await v(D)).forEach(i=>{/\.(j|t)s$/.test(i)&&s.set(o.default.basename(i).replace(/\.(j|t)s$/,""),i)}),f.default.existsSync(q)&&(await v(q)).forEach(i=>{/\.(j|t)s$/.test(i)&&s.set(o.default.basename(i).replace(/\.(j|t)s$/,""),i)}),f.default.existsSync(z)&&(await v(z)).forEach(i=>{/\.(j|t)s$/.test(i)&&s.set(o.default.basename(i).replace(/\.(j|t)s$/,""),i)});for(const[i,l]of s.entries()){const h=await import((0,$.pathToFileURL)(l).href);R.set(i,h.default)}}catch(s){console.error("Template import failed",s)}const pe=await v(d),I=new Map,J=(await Promise.all(pe.filter(s=>s.endsWith(".md")).map(async s=>{const i=await(0,c.readFile)(s,"utf-8");return{filePath:s,...(0,re.default)(i)}}))).sort((s,i)=>(s.attributes.order??0)-(i.attributes.order??0)).map(({attributes:s,body:i,filePath:l})=>{const h=l.replace(d,"").replace(/\.md/,".html"),g=l.replace(d,p).replace(".md",".html"),C=g.replace(o.default.basename(g),""),E=o.default.basename(h)||"/",de=h.replace(E,"").replace(/\/$/,"");let b=I;de.split("/").filter(Boolean).forEach(O=>{b.has(O)||b.set(O,new Map),b=b.get(O)});const K={...s,path:h};return b.has(E)||b.set(E,K),{attributes:K,fileWebsitePath:g,fileDirWebsitePath:C,body:i,sourcePath:o.default.relative(d,l),siteMap:I}});for(const{attributes:s,body:i,fileDirWebsitePath:l,fileWebsitePath:h,siteMap:g}of J){W={attributes:s,siteMap:g};const C=await fe.parse(i);await(0,c.mkdir)(l,{recursive:!0}),await(0,c.writeFile)(h,me.default.minify(C,{includeAutoGeneratedTags:!0,removeAttributeQuotes:!0,removeComments:!0,removeRedundantAttributes:!0,removeScriptTypeAttributes:!0,removeStyleLinkTypeAttributes:!0,sortClassName:!0,useShortDoctype:!0,collapseWhitespace:!0,minifyJS:!0,minifyCSS:!0}))}await _e({docsDir:d,docsSiteDir:p,pages:J.map(({attributes:s,body:i,sourcePath:l})=>{const h=Ce(String(s.path)),g=Me(i);return{sourcePath:l,urlPath:h,title:String(s.title||s.name||"").trim()||"Documentation",name:String(s.name||s.title||"").trim()||"Documentation",description:String(s.description||"").trim()||g.slice(0,156)||"Documentation page.",order:Number(s.order??999),summary:g.slice(0,320)}}),siteUrl:n,publicDir:p,generatedFiles:ue})},"buildDocs");0&&(module.exports={buildDocs});
@@ -15,7 +15,7 @@ The template ships:
15
15
  - Header, footer, metadata, and shared layout helpers.
16
16
  - CSS for landing and documentation pages.
17
17
  - A dark-first theme with light and dark variable maps.
18
- - Default assets, including social icons, favicons, a default `logo.svg`, and Before Semicolon brand assets.
18
+ - Default assets, including social icons, favicons, and default logo files.
19
19
 
20
20
  Use it when you want a polished documentation site where most page content stays in Markdown and the template owns the HTML structure.
21
21
 
@@ -27,7 +27,6 @@ fading-citrus/
27
27
  template.config.js
28
28
  assets/
29
29
  logo.svg
30
- before-semicolon-logo.png
31
30
  favicon/
32
31
  ...
33
32
  layouts/
@@ -120,7 +119,7 @@ Override the logo by adding:
120
119
  docs/_template/assets/logo.svg
121
120
  ```
122
121
 
123
- The footer also uses social icons from `/assets/*.svg` and a Before Semicolon credit asset at `/assets/before-semicolon-logo.png`.
122
+ The footer also uses social icons from `/assets/*.svg`.
124
123
 
125
124
  The current header and footer still include Before Semicolon and Markup-oriented links and copy. If a site needs fully different navigation, footer link groups, or legal copy, override `_header.js`, `_footer.js`, or the full page layouts from `docs/_template/layouts`.
126
125
 
@@ -565,8 +564,9 @@ The template includes a default asset set. Not all assets are generic; several e
565
564
 
566
565
  Generic or shared assets:
567
566
 
568
- - `logo.svg`: main site logo used by header and footer.
569
- - `before-semicolon-logo.png`: small footer credit mark.
567
+ - `logo.svg`: compatibility fallback for the main site logo.
568
+ - `logo.light.svg`: preferred logo on light backgrounds.
569
+ - `logo.dark.svg`: preferred logo on dark backgrounds.
570
570
  - `favicon/*`: default favicons.
571
571
  - social icons: `medium2.svg`, `facebook.svg`, `instagram.svg`, `reddit.svg`, `twitter.svg`, `youtube.svg`.
572
572
 
@@ -587,11 +587,13 @@ Override assets from a docs project by adding files at the same target path:
587
587
 
588
588
  ```txt
589
589
  docs/_template/assets/logo.svg
590
+ docs/_template/assets/logo.light.svg
591
+ docs/_template/assets/logo.dark.svg
590
592
  docs/_template/assets/favicon/favicon.ico
591
593
  docs/_template/assets/markup-banner.jpg
592
594
  ```
593
595
 
594
- Because `docs/_template/assets` is copied into `publicDir/assets` after template assets, source assets can replace template assets.
596
+ Because `docs/_template/assets` is copied into `publicDir/assets` after template assets, source assets can replace template assets. If only `logo.svg` is present, builder copies it to missing `logo.light.svg` and `logo.dark.svg` files in the generated site so the header and footer do not request missing assets. Projects that need correct contrast should provide explicit light and dark variants.
595
597
 
596
598
  ## Stylesheets
597
599
 
@@ -0,0 +1,8 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 220 60" role="img" aria-label="Fading Citrus">
2
+ <g fill="none" fill-rule="evenodd">
3
+ <circle cx="30" cy="30" r="23" fill="#f97316" />
4
+ <path d="M30 7a23 23 0 0 1 23 23H30z" fill="#22d3ee" opacity=".9" />
5
+ <path d="M30 7a23 23 0 0 0 0 46 23 23 0 0 1 0-46z" fill="#fff7ed" opacity=".35" />
6
+ <text x="68" y="37" fill="#f8fafc" font-family="Inter, Arial, sans-serif" font-size="22" font-weight="700">Docs</text>
7
+ </g>
8
+ </svg>
@@ -0,0 +1,8 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 220 60" role="img" aria-label="Fading Citrus">
2
+ <g fill="none" fill-rule="evenodd">
3
+ <circle cx="30" cy="30" r="23" fill="#f97316" />
4
+ <path d="M30 7a23 23 0 0 1 23 23H30z" fill="#0891b2" opacity=".9" />
5
+ <path d="M30 7a23 23 0 0 0 0 46 23 23 0 0 1 0-46z" fill="#fff7ed" opacity=".35" />
6
+ <text x="68" y="37" fill="#111827" font-family="Inter, Arial, sans-serif" font-size="22" font-weight="700">Docs</text>
7
+ </g>
8
+ </svg>
@@ -34,12 +34,17 @@ const social = [
34
34
  export default (props = {}) => {
35
35
  const siteName =
36
36
  props.projectMeta?.name || props.name || props.title || 'Documentation'
37
+ const logo = `
38
+ <span class="site-logo-pair">
39
+ <img class="site-logo site-logo-dark" src="/assets/logo.dark.svg" alt="${siteName} logo" width="100" height="30" />
40
+ <img class="site-logo site-logo-light" src="/assets/logo.light.svg" alt="" width="100" height="30" aria-hidden="true" />
41
+ </span>`
37
42
 
38
43
  return `
39
44
  <footer class="site-footer">
40
45
  <div class="footer-inner wrapper">
41
46
  <div class="footer-brand">
42
- <img class="site-logo" src="/assets/logo.svg" alt="${siteName} logo" width="100" height="30" />
47
+ ${logo}
43
48
  <p>A reactive HTML templating system for building dynamic UIs with pure JavaScript. No build step. No virtual DOM.</p>
44
49
  </div>
45
50
  <ul class="footer-links">
@@ -59,14 +64,13 @@ export default (props = {}) => {
59
64
  .map(
60
65
  (s) => `
61
66
  <a href="${s.link}" rel="noopener" target="_blank" aria-label="${s.name}">
62
- <img src="/${s.icon}" alt="" aria-hidden="true" width="16" height="16" />
67
+ <span class="footer-social-icon" style="--icon: url('/${s.icon}')" aria-hidden="true"></span>
63
68
  </a>
64
69
  `
65
70
  )
66
71
  .join('')}
67
72
  </div>
68
73
  <div class="footer-credit">
69
- <img width="20" height="20" src="/assets/before-semicolon-logo.png" alt="Before Semicolon logo" />
70
74
  <span>Copyright &copy; ${new Date().getFullYear()} Before Semicolon. All rights reserved.</span>
71
75
  </div>
72
76
  </div>
@@ -131,7 +131,7 @@ const pageJson = ({ title, description, path, name }) => {
131
131
  url: 'https://beforesemicolon.com',
132
132
  logo: {
133
133
  '@type': 'ImageObject',
134
- url: `${siteUrl}/assets/before-semicolon-logo.png`,
134
+ url: `${siteUrl}/assets/logo.svg`,
135
135
  },
136
136
  },
137
137
  mainEntityOfPage: url,
@@ -164,6 +164,7 @@ export default (props = {}) => {
164
164
  const url = absoluteUrl(props.path)
165
165
  const canonical = escapeHTML(url)
166
166
  const image = props.image || defaultImage
167
+ const cacheKey = props.cacheKey ? `?v=${escapeHTML(props.cacheKey)}` : ''
167
168
  const type = canonicalPath(props.path).startsWith('/documentation')
168
169
  ? 'article'
169
170
  : 'website'
@@ -211,7 +212,7 @@ export default (props = {}) => {
211
212
  <link rel="icon" type="image/png" sizes="16x16" href="/assets/favicon/favicon-16x16.png" />
212
213
  <link rel="manifest" href="/assets/favicon/site.webmanifest" />
213
214
  <link rel="icon" type="image/x-icon" href="/assets/favicon/favicon.ico" />
214
- <link rel="stylesheet" href="/stylesheets/github-dark.hightlighter.css">
215
+ <link rel="stylesheet" href="/stylesheets/github-dark.hightlighter.css${cacheKey}">
215
216
  ${jsonScript(pageJson({ ...props, title, description }))}
216
217
  ${jsonScript(breadcrumbJson({ title, path: props.path }))}
217
218
  ${
@@ -2,13 +2,18 @@ export default (props = {}) => {
2
2
  const isDocs = props.path?.startsWith('/documentation')
3
3
  const siteName =
4
4
  props.projectMeta?.name || props.name || props.title || 'Documentation'
5
+ const logo = `
6
+ <span class="site-logo-pair">
7
+ <img class="site-logo site-logo-dark" src="/assets/logo.dark.svg" alt="${siteName} logo" width="90" height="27" />
8
+ <img class="site-logo site-logo-light" src="/assets/logo.light.svg" alt="" width="90" height="27" aria-hidden="true" />
9
+ </span>`
5
10
 
6
11
  return `
7
12
  <header class="site-header">
8
13
  <div class="header-inner wrapper">
9
14
  <h1>
10
15
  <a href="/" aria-label="${siteName} home">
11
- <img class="site-logo" src="/assets/logo.svg" alt="${siteName} logo" width="90" height="27" />
16
+ ${logo}
12
17
  </a>
13
18
  </h1>
14
19
  ${
@@ -26,6 +26,7 @@ const navCategoryToHTML = (docs, currentPath) =>
26
26
  export default (props) => {
27
27
  const docs = props.siteMap.get('documentation')
28
28
  const isDocsIndex = props.path === '/documentation/index.html'
29
+ const cacheKey = props.cacheKey ? `?v=${props.cacheKey}` : ''
29
30
  const docsMenu = `<ul>${navCategoryToHTML(docs, props.path)}</ul>`
30
31
  const pages = [...docs.values()].flatMap((page) =>
31
32
  page instanceof Map ? [...page.values()] : [page]
@@ -45,7 +46,7 @@ export default (props) => {
45
46
  <html lang="en">
46
47
  <head>
47
48
  ${meta(props)}
48
- <link rel="stylesheet" href="/stylesheets/documentation.css?v=20260621exact">
49
+ <link rel="stylesheet" href="/stylesheets/documentation.css${cacheKey}">
49
50
  ${
50
51
  props.themeStylesheet
51
52
  ? `<link rel="stylesheet" href="${props.themeStylesheet}">`
@@ -3,12 +3,14 @@ import header from './_header.js'
3
3
  import footer from './_footer.js'
4
4
 
5
5
  export default (props) => {
6
+ const cacheKey = props.cacheKey ? `?v=${props.cacheKey}` : ''
7
+
6
8
  return `
7
9
  <!doctype html>
8
10
  <html lang="en">
9
11
  <head>
10
12
  ${meta(props)}
11
- <link rel="stylesheet" href="/stylesheets/landing.css?v=20260621exact">
13
+ <link rel="stylesheet" href="/stylesheets/landing.css${cacheKey}">
12
14
  ${
13
15
  props.themeStylesheet
14
16
  ? `<link rel="stylesheet" href="${props.themeStylesheet}">`