@beforesemicolon/builder 1.8.13 → 1.8.20
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/dist/cjs/docs/run.js +15 -13
- package/dist/cjs/docs/templates/fading-citrus/README.md +8 -6
- package/dist/cjs/docs/templates/fading-citrus/assets/logo.dark.svg +8 -0
- package/dist/cjs/docs/templates/fading-citrus/assets/logo.light.svg +8 -0
- package/dist/cjs/docs/templates/fading-citrus/layouts/_footer.js +7 -3
- package/dist/cjs/docs/templates/fading-citrus/layouts/_head-meta.js +3 -2
- package/dist/cjs/docs/templates/fading-citrus/layouts/_header.js +6 -1
- package/dist/cjs/docs/templates/fading-citrus/layouts/document.js +2 -1
- package/dist/cjs/docs/templates/fading-citrus/layouts/landing-ecosystem.js +9 -1
- package/dist/cjs/docs/templates/fading-citrus/layouts/landing-showcase.js +9 -1
- package/dist/cjs/docs/templates/fading-citrus/layouts/landing.js +3 -1
- package/dist/cjs/docs/templates/fading-citrus/stylesheets/common.css +132 -49
- package/dist/cjs/docs/templates/fading-citrus/stylesheets/documentation.css +44 -26
- package/dist/cjs/docs/templates/fading-citrus/stylesheets/github-dark.hightlighter.css +12 -12
- package/dist/cjs/docs/templates/fading-citrus/stylesheets/github-light.hightlighter.css +22 -22
- package/dist/cjs/docs/templates/fading-citrus/stylesheets/landing.css +94 -87
- package/dist/cjs/docs/templates/fading-citrus/template.config.js +106 -15
- package/dist/cjs/docs/types.js +1 -1
- package/dist/esm/docs/run.js +16 -14
- package/dist/esm/docs/templates/fading-citrus/README.md +8 -6
- package/dist/esm/docs/templates/fading-citrus/assets/logo.dark.svg +8 -0
- package/dist/esm/docs/templates/fading-citrus/assets/logo.light.svg +8 -0
- package/dist/esm/docs/templates/fading-citrus/layouts/_footer.js +7 -3
- package/dist/esm/docs/templates/fading-citrus/layouts/_head-meta.js +3 -2
- package/dist/esm/docs/templates/fading-citrus/layouts/_header.js +6 -1
- package/dist/esm/docs/templates/fading-citrus/layouts/document.js +2 -1
- package/dist/esm/docs/templates/fading-citrus/layouts/landing-ecosystem.js +9 -1
- package/dist/esm/docs/templates/fading-citrus/layouts/landing-showcase.js +9 -1
- package/dist/esm/docs/templates/fading-citrus/layouts/landing.js +3 -1
- package/dist/esm/docs/templates/fading-citrus/stylesheets/common.css +132 -49
- package/dist/esm/docs/templates/fading-citrus/stylesheets/documentation.css +44 -26
- package/dist/esm/docs/templates/fading-citrus/stylesheets/github-dark.hightlighter.css +12 -12
- package/dist/esm/docs/templates/fading-citrus/stylesheets/github-light.hightlighter.css +22 -22
- package/dist/esm/docs/templates/fading-citrus/stylesheets/landing.css +94 -87
- package/dist/esm/docs/templates/fading-citrus/template.config.js +106 -15
- package/dist/types/docs/types.d.ts +1 -0
- package/package.json +1 -1
- package/dist/cjs/docs/templates/fading-citrus/assets/before-semicolon-logo.png +0 -0
- package/dist/esm/docs/templates/fading-citrus/assets/before-semicolon-logo.png +0 -0
package/dist/cjs/docs/run.js
CHANGED
|
@@ -1,48 +1,50 @@
|
|
|
1
|
-
"use strict";var
|
|
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"),p=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"),
|
|
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
|
-
}`:"",
|
|
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,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),"escapeXML"),
|
|
10
|
+
`):""},"renderThemeStylesheet"),Te=m((e="")=>String(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),"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)=>p.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");p.default.existsSync(t)&&await Promise.all(["logo.light.svg","logo.dark.svg"].map(async r=>{const a=o.default.resolve(e,r);p.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(
|
|
11
|
-
<loc>${Te(
|
|
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"),
|
|
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"),
|
|
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"),
|
|
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: ${
|
|
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"),
|
|
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"),
|
|
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"),
|
|
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="([^"]+)".*?>([\s\S]*?)<\/a><\/h\1>/gm)].map(D=>({path:D[2],label:D[3].replace(/<[^>]+>/g,"").replace(/\s+/g," ").trim(),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");p.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(p.default.existsSync(s)||(s=s.replace(/\.js$/,".ts")),p.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"),f=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(p.default.existsSync(s)||(s=s.replace(/\.js$/,".ts")),p.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 pe=Be(M,r);if(await(0,c.mkdir)(f,{recursive:!0}),k&&p.default.existsSync(k))try{await(0,c.cp)(k,F.replace(d,f),{recursive:!0})}catch(s){console.warn(s)}if(p.default.existsSync(F))try{await(0,c.cp)(F,F.replace(d,f),{recursive:!0})}catch(s){console.warn(s)}if(p.default.existsSync(B))try{await(0,c.cp)(B,o.default.resolve(f,"assets"),{recursive:!0})}catch(s){console.warn(s)}await Fe(o.default.resolve(f,"assets"));try{const s=new Set;if(T&&p.default.existsSync(T)&&(await v(T)).forEach(l=>s.add(l)),p.default.existsSync(H)&&(await v(H)).forEach(l=>s.add(l)),p.default.existsSync(N)&&(await v(N)).forEach(l=>s.add(l)),s.size){await(0,c.mkdir)(o.default.join(f,"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,f):l.startsWith(d)?l.replace(d,f):o.default.join(f,"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(f,"stylesheets"),{recursive:!0}),await(0,c.writeFile)(o.default.join(f,"stylesheets","theme.css"),l.styles??i)}}catch(s){console.error(s)}if(p.default.existsSync(L))try{const s=await v(L);s.length&&await(0,c.mkdir)(L.replace(d,f),{recursive:!0});for(const i of s)try{const l=await(0,c.readFile)(i,"utf-8");await(0,c.writeFile)(i.replace(d,f),X.minify(l))}catch(l){console.error(l)}}catch(s){console.warn(s)}if(p.default.existsSync(U))try{const s=await v(U);s.length&&await(0,c.mkdir)(o.default.join(f,"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,f),X.minify(l))}catch(l){console.error(l)}}catch(s){console.warn(s)}try{const s=new Map;D&&p.default.existsSync(D)&&(await v(D)).forEach(i=>{/\.(j|t)s$/.test(i)&&s.set(o.default.basename(i).replace(/\.(j|t)s$/,""),i)}),p.default.existsSync(q)&&(await v(q)).forEach(i=>{/\.(j|t)s$/.test(i)&&s.set(o.default.basename(i).replace(/\.(j|t)s$/,""),i)}),p.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 fe=await v(d),I=new Map,J=(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,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,f).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 pe.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:f,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:f,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,
|
|
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
|
|
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`:
|
|
569
|
-
- `
|
|
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
|
-
|
|
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
|
-
<
|
|
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 © ${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/
|
|
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
|
-
|
|
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
|
|
49
|
+
<link rel="stylesheet" href="/stylesheets/documentation.css${cacheKey}">
|
|
49
50
|
${
|
|
50
51
|
props.themeStylesheet
|
|
51
52
|
? `<link rel="stylesheet" href="${props.themeStylesheet}">`
|
|
@@ -5,6 +5,14 @@ import {
|
|
|
5
5
|
partByName,
|
|
6
6
|
} from './_layout-utils.js'
|
|
7
7
|
|
|
8
|
+
const tone = (value) =>
|
|
9
|
+
({
|
|
10
|
+
orange: 'primary',
|
|
11
|
+
cyan: 'accent',
|
|
12
|
+
})[value] ||
|
|
13
|
+
value ||
|
|
14
|
+
'primary'
|
|
15
|
+
|
|
8
16
|
export default ({ parts }) => {
|
|
9
17
|
const header = partByName(parts, 'header') || parts[0]
|
|
10
18
|
const cards = parts.filter((part) => part.name === 'product')
|
|
@@ -16,7 +24,7 @@ export default ({ parts }) => {
|
|
|
16
24
|
${cards
|
|
17
25
|
.map(
|
|
18
26
|
(card) => `
|
|
19
|
-
<div class="product-card card-${option(card.options.color, '
|
|
27
|
+
<div class="product-card card-${tone(option(card.options.color, 'primary'))}">
|
|
20
28
|
<div class="product-glow"></div>
|
|
21
29
|
<a href="${option(card.options.href, '#')}" target="_blank" rel="noreferrer" class="product-head">
|
|
22
30
|
<div class="product-title-row">
|
|
@@ -10,6 +10,14 @@ import {
|
|
|
10
10
|
const previousIcon =
|
|
11
11
|
'<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m12 19-7-7 7-7"/><path d="M19 12H5"/></svg>'
|
|
12
12
|
|
|
13
|
+
const tone = (value) =>
|
|
14
|
+
({
|
|
15
|
+
orange: 'primary',
|
|
16
|
+
cyan: 'accent',
|
|
17
|
+
})[value] ||
|
|
18
|
+
value ||
|
|
19
|
+
'accent'
|
|
20
|
+
|
|
13
21
|
export const renderShowcaseScript = () => `
|
|
14
22
|
<script type="application/javascript">
|
|
15
23
|
(() => {
|
|
@@ -102,7 +110,7 @@ export default ({ parts }) => {
|
|
|
102
110
|
<div class="slide-meta">
|
|
103
111
|
<span class="slide-num font-mono">EXAMPLE ${String(index + 1).padStart(2, '0')}</span>
|
|
104
112
|
<div class="slide-line"></div>
|
|
105
|
-
<span class="slide-label font-mono color-${option(example.options.color,
|
|
113
|
+
<span class="slide-label font-mono color-${tone(option(example.options.color, 'accent'))}">${option(example.options.label, '')}</span>
|
|
106
114
|
</div>
|
|
107
115
|
${renderCodeBlock(option(example.options.filename, ''), codeFromPart(example), option(example.options.lang, 'javascript'))}
|
|
108
116
|
</div>
|
|
@@ -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
|
|
13
|
+
<link rel="stylesheet" href="/stylesheets/landing.css${cacheKey}">
|
|
12
14
|
${
|
|
13
15
|
props.themeStylesheet
|
|
14
16
|
? `<link rel="stylesheet" href="${props.themeStylesheet}">`
|