@beforesemicolon/builder 1.8.20 → 1.8.23
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 +21 -3
- package/dist/cjs/docs/run.js +5 -5
- package/dist/cjs/docs/templates/fading-citrus/README.md +36 -11
- package/dist/cjs/docs/templates/fading-citrus/layouts/_head-meta.js +54 -51
- package/dist/cjs/docs/templates/fading-citrus/layouts/document.js +1 -0
- package/dist/cjs/docs/templates/fading-citrus/layouts/landing.js +1 -0
- package/dist/cjs/docs/templates/fading-citrus/stylesheets/documentation.css +7 -0
- package/dist/cjs/docs/templates/fading-citrus/template.config.js +7 -0
- package/dist/cjs/docs/types.js +1 -1
- package/dist/esm/docs/run.js +6 -6
- package/dist/esm/docs/templates/fading-citrus/README.md +36 -11
- package/dist/esm/docs/templates/fading-citrus/layouts/_head-meta.js +54 -51
- package/dist/esm/docs/templates/fading-citrus/layouts/document.js +1 -0
- package/dist/esm/docs/templates/fading-citrus/layouts/landing.js +1 -0
- package/dist/esm/docs/templates/fading-citrus/stylesheets/documentation.css +7 -0
- package/dist/esm/docs/templates/fading-citrus/template.config.js +7 -0
- package/dist/types/docs/types.d.ts +12 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -303,8 +303,10 @@ A template config exports an object:
|
|
|
303
303
|
|
|
304
304
|
```js
|
|
305
305
|
export default {
|
|
306
|
+
meta: {},
|
|
306
307
|
markedOptions: {},
|
|
307
308
|
markdownLayouts: {},
|
|
309
|
+
headScripts: {},
|
|
308
310
|
scripts: {},
|
|
309
311
|
theme: {
|
|
310
312
|
light: {},
|
|
@@ -318,7 +320,8 @@ Config from `docs/_template/template.config.js` is merged into the selected temp
|
|
|
318
320
|
Merge behavior:
|
|
319
321
|
|
|
320
322
|
- `markdownLayouts` are shallow-merged by layout name.
|
|
321
|
-
- `scripts` are shallow-merged by script name.
|
|
323
|
+
- `headScripts` and `scripts` are shallow-merged by script name.
|
|
324
|
+
- `meta` is shallow-merged by metadata field.
|
|
322
325
|
- `theme.light` and `theme.dark` are shallow-merged by CSS variable name.
|
|
323
326
|
- Other top-level config values use the docs source config value when provided.
|
|
324
327
|
|
|
@@ -328,6 +331,12 @@ Example docs source extension:
|
|
|
328
331
|
import pricingCards from './layouts/pricing-cards.js'
|
|
329
332
|
|
|
330
333
|
export default {
|
|
334
|
+
meta: {
|
|
335
|
+
siteName: 'Example',
|
|
336
|
+
title: 'Example Docs',
|
|
337
|
+
description: 'Documentation for Example.',
|
|
338
|
+
image: '/assets/site-image.jpg',
|
|
339
|
+
},
|
|
331
340
|
markdownLayouts: {
|
|
332
341
|
'pricing-cards': pricingCards,
|
|
333
342
|
},
|
|
@@ -594,6 +603,8 @@ export default {
|
|
|
594
603
|
}
|
|
595
604
|
```
|
|
596
605
|
|
|
606
|
+
Use `headScripts` for scripts that must render in `<head>`, such as analytics bootstrap tags. Use `scripts` for scripts that can render near `</body>`, such as interaction handlers.
|
|
607
|
+
|
|
597
608
|
Script definitions:
|
|
598
609
|
|
|
599
610
|
```ts
|
|
@@ -624,12 +635,15 @@ Behavior:
|
|
|
624
635
|
- A function matcher receives the rendered page HTML and returns a boolean.
|
|
625
636
|
- A script can be disabled by setting its registry value to `false` in an extending config.
|
|
626
637
|
|
|
627
|
-
Layouts receive scripts through `props.
|
|
638
|
+
Layouts receive scripts through `props.headScripts` and `props.scripts`. Template layouts must insert them where appropriate.
|
|
628
639
|
|
|
629
640
|
```js
|
|
630
641
|
export default (props) => `
|
|
631
642
|
<!doctype html>
|
|
632
643
|
<html>
|
|
644
|
+
<head>
|
|
645
|
+
${props.headScripts?.join('') || ''}
|
|
646
|
+
</head>
|
|
633
647
|
<body>
|
|
634
648
|
${props.content}
|
|
635
649
|
${props.scripts?.join('') || ''}
|
|
@@ -794,8 +808,12 @@ export default {
|
|
|
794
808
|
markdownLayouts: {
|
|
795
809
|
'pricing-cards': pricingCards,
|
|
796
810
|
},
|
|
811
|
+
headScripts: {
|
|
812
|
+
analytics: () =>
|
|
813
|
+
`<script async src="https://example.com/analytics.js"></script>`,
|
|
814
|
+
},
|
|
797
815
|
scripts: {
|
|
798
|
-
|
|
816
|
+
pageView: {
|
|
799
817
|
match: '<main',
|
|
800
818
|
render: () => `<script>console.log('page viewed')</script>`,
|
|
801
819
|
},
|
package/dist/cjs/docs/run.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var ge=Object.create;var C=Object.defineProperty;var ye=Object.getOwnPropertyDescriptor;var we=Object.getOwnPropertyNames;var ve=Object.getPrototypeOf,Se=Object.prototype.hasOwnProperty;var m=(e,t)=>C(e,"name",{value:t,configurable:!0});var De=(e,t)=>{for(var r in t)C(e,r,{get:t[r],enumerable:!0})},Q=(e,t,r,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of we(t))!Se.call(e,n)&&n!==r&&C(e,n,{get:()=>t[n],enumerable:!(a=ye(t,n))||a.enumerable});return e};var v=(e,t,r)=>(r=e!=null?ge(ve(e)):{},Q(t||!e||!e.__esModule?C(r,"default",{value:e,enumerable:!0}):r,e)),be=e=>Q(C({},"__esModule",{value:!0}),e);var Ue={};De(Ue,{buildDocs:()=>Ne});module.exports=be(Ue);const ze={};var re=require("marked"),c=require("fs/promises"),o=v(require("path"),1),oe=v(require("front-matter"),1),ae=v(require("isomorphic-dompurify"),1),ne=require("marked-highlight"),_=v(require("highlight.js"),1),ie=v(require("./templates/default.template.js"),1),le=v(require("./renderer/index.js"),1),ce=require("./markdown-layout/index.js"),me=v(require("clean-css"),1),ue=v(require("html-minifier"),1),X=v(require("@putout/minify"),1),P=require("url"),f=v(require("fs"),1);let V,A="";const Y=new me.default({inline:["none"]}),R=new Map;R.set("default",ie.default);let W;const Z=m((e,t)=>t===!1?!1:t===void 0?e:e===!1||e===void 0?t:{...e,...t},"mergeThemeMode"),xe=m((e,t)=>{if(!(!e&&!t))return{light:Z(e?.light,t?.light),dark:Z(e?.dark,t?.dark)}},"mergeTheme"),$e=m((e,t)=>({...e,...t,markdownLayouts:{...e.markdownLayouts,...t.markdownLayouts},headScripts:{...e.headScripts,...t.headScripts},scripts:{...e.scripts,...t.scripts},theme:xe(e.theme,t.theme),meta:{...e.meta,...t.meta}}),"mergeTemplateConfig"),Pe=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"),ee=m((e,t)=>e?Object.values(e).filter(r=>r!==!1).filter(r=>Pe(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
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) {
|
|
@@ -7,10 +7,10 @@ ${G(" :root",n)}
|
|
|
7
7
|
.site-logo-dark { display: block; }`:"",r&&a?`.site-logo-light { display: block; }
|
|
8
8
|
.site-logo-dark { display: none; }`:""].filter(Boolean).join(`
|
|
9
9
|
|
|
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"),
|
|
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"),je=m(e=>e==="/index.html"?"/":e.replace(/\/index\.html$/,"/").replace(/\.html$/,"").replace(/\/+/g,"/"),"pagePathToUrlPath"),te=m((e,t)=>e?`${e.replace(/\/$/,"")}${t==="/"?"/":t}`:t,"canonicalUrl"),Ce=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"?>
|
|
11
11
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
12
12
|
${e.map(n=>{const u=n.urlPath==="/"?"1.0":"0.8";return` <url>
|
|
13
|
-
<loc>${Te(
|
|
13
|
+
<loc>${Te(te(t,n.urlPath))}</loc>
|
|
14
14
|
<lastmod>${r}</lastmod>
|
|
15
15
|
<changefreq>weekly</changefreq>
|
|
16
16
|
<priority>${u}</priority>
|
|
@@ -35,7 +35,7 @@ This file is generated at build time from the Markdown documentation. Use it to
|
|
|
35
35
|
|
|
36
36
|
${e.map(r=>`## ${r.title}
|
|
37
37
|
|
|
38
|
-
URL: ${
|
|
38
|
+
URL: ${te(t,r.urlPath)}
|
|
39
39
|
Source: docs/${r.sourcePath}
|
|
40
40
|
Description: ${r.description}
|
|
41
41
|
|
|
@@ -75,4 +75,4 @@ for = "/robots.txt"
|
|
|
75
75
|
[headers.values]
|
|
76
76
|
Content-Type = "text/plain; charset=utf-8"
|
|
77
77
|
Cache-Control = "public, max-age=0, must-revalidate"
|
|
78
|
-
`,"generateNetlifyToml")
|
|
78
|
+
`,"generateNetlifyToml"),$=m(async(e,t,r,a)=>{if(await Ce(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 $(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 $(o.default.resolve(e,"robots.txt"),o.default.resolve(t,"robots.txt"),()=>Ee(a)),u.llms&&await $(o.default.resolve(e,"llms.txt"),o.default.resolve(t,"llms.txt"),()=>Oe(r)),u.llmsFull&&await $(o.default.resolve(e,"llms-full.txt"),o.default.resolve(t,"llms-full.txt"),()=>Ae(r,a)),u.netlify&&(await $(o.default.resolve(e,"_redirects"),o.default.resolve(t,"_redirects"),()=>Re(r,u)),await $(o.default.resolve(e,"netlify.toml"),o.default.resolve(t,"netlify.toml"),()=>We(n&&o.default.basename(n))))},"writeGeneratedDocsFiles"),se=m((e={},t,r)=>{const a=new re.Marked((0,ne.markedHighlight)({langPrefix:"hljs language-",highlight(n,u){const h=_.default.getLanguage(u)?u:"plaintext";return _.default.highlight(n,{language:h}).value}}));return a.use({renderer:le.default}),a.use((0,ce.createMarkdownLayoutExtension)({marked:a,handlers:e.markdownLayouts,renderMarkdown:r})),e.markedOptions&&a.use(e.markedOptions),t&&a.use(t),a},"createBaseMarked"),Be=m((e={},t,r)=>{const a=se(e,t),n=m(h=>a.parse(h),"renderMarkdown"),u=se(e,t,n);return u.use({hooks:{postprocess(h){const{layout:w="default",...k}=W.attributes,T=ee(e.headScripts,h),M=ee(e.scripts,h);h=ae.default.sanitize(h);const b=[...h.matchAll(/<h([0-6])\sid="[^"]+".*?>.*?<a\s+href="([^"]+)".*?>([\s\S]*?)<\/a><\/h\1>/gm)].map(p=>({path:p[2],label:p[3].replace(/<[^>]+>/g,"").replace(/\s+/g," ").trim(),level:p[1]}));return R.get(w)?.({...k,content:h,siteMap:W.siteMap,tableOfContent:b,projectMeta:V,renderMarkdown:n,headScripts:T,scripts:M,themeStylesheet:e.theme?`/stylesheets/theme.css?v=${A}`:void 0,cacheKey:A,siteUrl:r,templateConfig:e})||h}}}),u},"createMarked"),S=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 S(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 h=m(()=>{try{return __dirname}catch{return o.default.dirname((0,P.fileURLToPath)(ze.url))}},"getDirname"),w=a?o.default.resolve(h(),"templates",a):null,k=w?o.default.resolve(w,"assets"):null,T=w?o.default.resolve(w,"stylesheets"):null,M=w?o.default.resolve(w,"layouts"):null;let b={};if(w)try{let s=o.default.resolve(w,"template.config.js");if(f.default.existsSync(s)||(s=s.replace(/\.js$/,".ts")),f.default.existsSync(s)){const i=await import((0,P.pathToFileURL)(s).href);b=i.default||i}}catch(s){console.error(`Failed to load template config for ${a}`,s)}const p=e??o.default.resolve(process.cwd(),"docs"),d=t??o.default.resolve(process.cwd(),"website"),pe=Ge(u),D=o.default.resolve(p,"_template"),B=o.default.resolve(D,"assets"),N=o.default.resolve(D,"stylesheets"),U=o.default.resolve(D,"scripts"),z=o.default.resolve(D,"layouts"),q=o.default.resolve(p,"_layouts"),F=o.default.resolve(p,"assets"),H=o.default.resolve(p,"stylesheets"),L=o.default.resolve(p,"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,P.pathToFileURL)(s).href),l=i.default||i;b=$e(b,l)}}catch(s){console.error("Failed to load docs _template config",s)}const fe=Be(b,r,n);if(await(0,c.mkdir)(d,{recursive:!0}),k&&f.default.existsSync(k))try{await(0,c.cp)(k,F.replace(p,d),{recursive:!0})}catch(s){console.warn(s)}if(f.default.existsSync(F))try{await(0,c.cp)(F,F.replace(p,d),{recursive:!0})}catch(s){console.warn(s)}if(f.default.existsSync(B))try{await(0,c.cp)(B,o.default.resolve(d,"assets"),{recursive:!0})}catch(s){console.warn(s)}await Fe(o.default.resolve(d,"assets"));try{const s=new Set;if(T&&f.default.existsSync(T)&&(await S(T)).forEach(l=>s.add(l)),f.default.existsSync(H)&&(await S(H)).forEach(l=>s.add(l)),f.default.existsSync(N)&&(await S(N)).forEach(l=>s.add(l)),s.size){await(0,c.mkdir)(o.default.join(d,"stylesheets"),{recursive:!0});for(const l of s){const g=await(0,c.readFile)(l,"utf-8"),y=Y.minify(g);y.errors.length&&y.errors.forEach(console.error);const j=l.startsWith(D)?l.replace(D,d):l.startsWith(p)?l.replace(p,d):o.default.join(d,"stylesheets",o.default.basename(l));await(0,c.writeFile)(j,y.styles??g)}}const i=ke(b.theme);if(i){const l=Y.minify(i);l.errors.length&&l.errors.forEach(console.error),await(0,c.mkdir)(o.default.join(d,"stylesheets"),{recursive:!0}),await(0,c.writeFile)(o.default.join(d,"stylesheets","theme.css"),l.styles??i)}}catch(s){console.error(s)}if(f.default.existsSync(L))try{const s=await S(L);s.length&&await(0,c.mkdir)(L.replace(p,d),{recursive:!0});for(const i of s)try{const l=await(0,c.readFile)(i,"utf-8");await(0,c.writeFile)(i.replace(p,d),X.minify(l))}catch(l){console.error(l)}}catch(s){console.warn(s)}if(f.default.existsSync(U))try{const s=await S(U);s.length&&await(0,c.mkdir)(o.default.join(d,"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,d),X.minify(l))}catch(l){console.error(l)}}catch(s){console.warn(s)}try{const s=new Map;M&&f.default.existsSync(M)&&(await S(M)).forEach(i=>{/\.(j|t)s$/.test(i)&&s.set(o.default.basename(i).replace(/\.(j|t)s$/,""),i)}),f.default.existsSync(q)&&(await S(q)).forEach(i=>{/\.(j|t)s$/.test(i)&&s.set(o.default.basename(i).replace(/\.(j|t)s$/,""),i)}),f.default.existsSync(z)&&(await S(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 g=await import((0,P.pathToFileURL)(l).href);R.set(i,g.default)}}catch(s){console.error("Template import failed",s)}const de=await S(p),I=new Map,J=(await Promise.all(de.filter(s=>s.endsWith(".md")).map(async s=>{const i=await(0,c.readFile)(s,"utf-8");return{filePath:s,...(0,oe.default)(i)}}))).sort((s,i)=>(s.attributes.order??0)-(i.attributes.order??0)).map(({attributes:s,body:i,filePath:l})=>{const g=l.replace(p,"").replace(/\.md/,".html"),y=l.replace(p,d).replace(".md",".html"),j=y.replace(o.default.basename(y),""),E=o.default.basename(g)||"/",he=g.replace(E,"").replace(/\/$/,"");let x=I;he.split("/").filter(Boolean).forEach(O=>{x.has(O)||x.set(O,new Map),x=x.get(O)});const K={...s,path:g};return x.has(E)||x.set(E,K),{attributes:K,fileWebsitePath:y,fileDirWebsitePath:j,body:i,sourcePath:o.default.relative(p,l),siteMap:I}});for(const{attributes:s,body:i,fileDirWebsitePath:l,fileWebsitePath:g,siteMap:y}of J){W={attributes:s,siteMap:y};const j=await fe.parse(i);await(0,c.mkdir)(l,{recursive:!0}),await(0,c.writeFile)(g,ue.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 _e({docsDir:p,docsSiteDir:d,pages:J.map(({attributes:s,body:i,sourcePath:l})=>{const g=je(String(s.path)),y=Me(i);return{sourcePath:l,urlPath:g,title:String(s.title||s.name||"").trim()||"Documentation",name:String(s.name||s.title||"").trim()||"Documentation",description:String(s.description||"").trim()||y.slice(0,156)||"Documentation page.",order:Number(s.order??999),summary:y.slice(0,320)}}),siteUrl:n,publicDir:d,generatedFiles:pe})},"buildDocs");0&&(module.exports={buildDocs});
|
|
@@ -144,9 +144,28 @@ It emits:
|
|
|
144
144
|
|
|
145
145
|
Important current behavior:
|
|
146
146
|
|
|
147
|
-
- The
|
|
148
|
-
-
|
|
149
|
-
-
|
|
147
|
+
- The helper reads defaults from `template.config.js` `meta`.
|
|
148
|
+
- `meta.siteUrl` overrides the `buildDocs({ siteUrl })` value when needed.
|
|
149
|
+
- `meta.siteName`, `meta.title`, and `meta.description` customize the fallback site identity.
|
|
150
|
+
- `meta.image` customizes the default Open Graph and Twitter card image.
|
|
151
|
+
- `meta.image` can be absolute, such as `https://example.com/card.jpg`, or site-relative, such as `/assets/site-image.jpg`.
|
|
152
|
+
- If no image is configured, the fallback is `/assets/site-image.jpg`.
|
|
153
|
+
|
|
154
|
+
For project-specific social images, add the file under `docs/_template/assets` and point `meta.image` at it:
|
|
155
|
+
|
|
156
|
+
```js
|
|
157
|
+
// docs/_template/template.config.js
|
|
158
|
+
export default {
|
|
159
|
+
meta: {
|
|
160
|
+
siteName: 'Example',
|
|
161
|
+
title: 'Example by Before Semicolon',
|
|
162
|
+
description: 'Documentation for Example.',
|
|
163
|
+
image: '/assets/site-image.jpg',
|
|
164
|
+
},
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Override `_head-meta.js` in `docs/_template/layouts` only when the generated metadata shape itself needs to change.
|
|
150
169
|
|
|
151
170
|
## Markdown Layout Handlers
|
|
152
171
|
|
|
@@ -590,7 +609,7 @@ docs/_template/assets/logo.svg
|
|
|
590
609
|
docs/_template/assets/logo.light.svg
|
|
591
610
|
docs/_template/assets/logo.dark.svg
|
|
592
611
|
docs/_template/assets/favicon/favicon.ico
|
|
593
|
-
docs/_template/assets/
|
|
612
|
+
docs/_template/assets/site-image.jpg
|
|
594
613
|
```
|
|
595
614
|
|
|
596
615
|
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.
|
|
@@ -618,7 +637,7 @@ The landing and documentation stylesheets import or rely on the shared styles as
|
|
|
618
637
|
|
|
619
638
|
## Scripts
|
|
620
639
|
|
|
621
|
-
Template scripts are registered by marker
|
|
640
|
+
Template scripts are registered by marker. The template renders `headScripts` inside `<head>` and `scripts` before `</body>`.
|
|
622
641
|
|
|
623
642
|
```js
|
|
624
643
|
scripts: {
|
|
@@ -668,11 +687,12 @@ export default {
|
|
|
668
687
|
Use the smallest override that solves the problem:
|
|
669
688
|
|
|
670
689
|
1. Override assets for logos, favicons, images, and icons.
|
|
671
|
-
2. Override
|
|
672
|
-
3.
|
|
673
|
-
4.
|
|
674
|
-
5. Override
|
|
675
|
-
6. Override `
|
|
690
|
+
2. Override `meta` fields for site identity and social card defaults.
|
|
691
|
+
3. Override theme variables for color and surface tuning.
|
|
692
|
+
4. Add Markdown layout handlers for new page sections.
|
|
693
|
+
5. Override existing Markdown layout handlers only when the section HTML needs to change.
|
|
694
|
+
6. Override `_header.js`, `_footer.js`, or `_head-meta.js` when navigation, footer copy, metadata shape, or brand assumptions need to change.
|
|
695
|
+
7. Override `landing.js` or `document.js` only when the full page shell changes.
|
|
676
696
|
|
|
677
697
|
Example docs extension:
|
|
678
698
|
|
|
@@ -692,6 +712,12 @@ docs/
|
|
|
692
712
|
import pricingCards from './layouts/pricing-cards.js'
|
|
693
713
|
|
|
694
714
|
export default {
|
|
715
|
+
meta: {
|
|
716
|
+
siteName: 'Example',
|
|
717
|
+
title: 'Example by Before Semicolon',
|
|
718
|
+
description: 'Documentation for Example.',
|
|
719
|
+
image: '/assets/site-image.jpg',
|
|
720
|
+
},
|
|
695
721
|
markdownLayouts: {
|
|
696
722
|
'pricing-cards': pricingCards,
|
|
697
723
|
},
|
|
@@ -710,7 +736,6 @@ export default {
|
|
|
710
736
|
|
|
711
737
|
Some template internals are still Markup or Before Semicolon oriented:
|
|
712
738
|
|
|
713
|
-
- `_head-meta.js` has Markup-specific defaults and URLs.
|
|
714
739
|
- `_header.js` has Markup-oriented GitHub and documentation links.
|
|
715
740
|
- `_footer.js` has Before Semicolon social links, footer copy, and learning links.
|
|
716
741
|
- `document.js` builds edit links against the Markup GitHub docs path.
|
|
@@ -1,22 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
const defaultDescription =
|
|
8
|
-
'Markup is a tiny reactive HTML templating system for building JavaScript user interfaces with web standards, no JSX, and no build step.'
|
|
9
|
-
const defaultImage = `${siteUrl}/assets/markup-banner.jpg`
|
|
1
|
+
const fallbackSiteUrl = 'https://beforesemicolon.com'
|
|
2
|
+
const fallbackSiteName = 'Before Semicolon'
|
|
3
|
+
const fallbackTitle = 'Before Semicolon Documentation'
|
|
4
|
+
const fallbackDescription =
|
|
5
|
+
'Documentation for Before Semicolon projects, libraries, and developer tools.'
|
|
6
|
+
const fallbackImage = '/assets/site-image.jpg'
|
|
10
7
|
const baseKeywords = [
|
|
11
|
-
'
|
|
12
|
-
'
|
|
13
|
-
'
|
|
14
|
-
'
|
|
15
|
-
'
|
|
16
|
-
'
|
|
17
|
-
'
|
|
18
|
-
'no build step',
|
|
19
|
-
'vanilla JavaScript',
|
|
8
|
+
'Before Semicolon',
|
|
9
|
+
'documentation',
|
|
10
|
+
'JavaScript',
|
|
11
|
+
'developer tools',
|
|
12
|
+
'web development',
|
|
13
|
+
'frontend development',
|
|
14
|
+
'open source',
|
|
20
15
|
]
|
|
21
16
|
|
|
22
17
|
const escapeHTML = (value = '') =>
|
|
@@ -38,15 +33,20 @@ const canonicalPath = (path = '/') => {
|
|
|
38
33
|
return cleanPath.replace(/\.html$/, '')
|
|
39
34
|
}
|
|
40
35
|
|
|
41
|
-
const absoluteUrl = (path = '/') => `${siteUrl}${canonicalPath(path)}`
|
|
36
|
+
const absoluteUrl = (siteUrl, path = '/') => `${siteUrl}${canonicalPath(path)}`
|
|
42
37
|
|
|
43
|
-
const
|
|
38
|
+
const absoluteAssetUrl = (siteUrl, imagePath) => {
|
|
39
|
+
if (/^https?:\/\//i.test(imagePath)) return imagePath
|
|
40
|
+
return `${siteUrl}${imagePath.startsWith('/') ? imagePath : `/${imagePath}`}`
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const compactDescription = (description, defaultDescription) =>
|
|
44
44
|
(description || defaultDescription).replace(/\s+/g, ' ').trim()
|
|
45
45
|
|
|
46
46
|
const pageKeywords = ({ title, name, keywords } = {}) => {
|
|
47
47
|
const values = [
|
|
48
48
|
...baseKeywords,
|
|
49
|
-
title?.replace(/ -
|
|
49
|
+
title?.replace(/ - Before Semicolon Documentation$/, ''),
|
|
50
50
|
name,
|
|
51
51
|
...(Array.isArray(keywords)
|
|
52
52
|
? keywords
|
|
@@ -64,7 +64,7 @@ const jsonScript = (data) =>
|
|
|
64
64
|
.replace(/>/g, '\\u003e')
|
|
65
65
|
.replace(/&/g, '\\u0026')}</script>`
|
|
66
66
|
|
|
67
|
-
const breadcrumbJson = ({ title, path }) => {
|
|
67
|
+
const breadcrumbJson = ({ siteUrl, title, path }) => {
|
|
68
68
|
const cleanPath = canonicalPath(path)
|
|
69
69
|
const parts = cleanPath.split('/').filter(Boolean)
|
|
70
70
|
const items = [
|
|
@@ -84,7 +84,7 @@ const breadcrumbJson = ({ title, path }) => {
|
|
|
84
84
|
position: index + 2,
|
|
85
85
|
name:
|
|
86
86
|
index === parts.length - 1
|
|
87
|
-
? title.replace(/ -
|
|
87
|
+
? title.replace(/ - Before Semicolon Documentation$/, '')
|
|
88
88
|
: part
|
|
89
89
|
.split('-')
|
|
90
90
|
.map((word) => word[0]?.toUpperCase() + word.slice(1))
|
|
@@ -100,9 +100,17 @@ const breadcrumbJson = ({ title, path }) => {
|
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
const pageJson = ({
|
|
103
|
+
const pageJson = ({
|
|
104
|
+
siteUrl,
|
|
105
|
+
siteName,
|
|
106
|
+
defaultImage,
|
|
107
|
+
title,
|
|
108
|
+
description,
|
|
109
|
+
path,
|
|
110
|
+
name,
|
|
111
|
+
}) => {
|
|
104
112
|
const isDocsPage = canonicalPath(path).startsWith('/documentation')
|
|
105
|
-
const url = absoluteUrl(path)
|
|
113
|
+
const url = absoluteUrl(siteUrl, path)
|
|
106
114
|
|
|
107
115
|
if (isDocsPage) {
|
|
108
116
|
return {
|
|
@@ -116,14 +124,14 @@ const pageJson = ({ title, description, path, name }) => {
|
|
|
116
124
|
inLanguage: 'en-US',
|
|
117
125
|
isPartOf: {
|
|
118
126
|
'@type': 'TechArticle',
|
|
119
|
-
name:
|
|
127
|
+
name: `${siteName} Documentation`,
|
|
120
128
|
url: `${siteUrl}/documentation`,
|
|
121
129
|
},
|
|
122
130
|
about: [
|
|
123
|
-
|
|
124
|
-
'JavaScript
|
|
125
|
-
'
|
|
126
|
-
'
|
|
131
|
+
`${siteName} documentation`,
|
|
132
|
+
'JavaScript',
|
|
133
|
+
'Web development',
|
|
134
|
+
'Developer tools',
|
|
127
135
|
],
|
|
128
136
|
publisher: {
|
|
129
137
|
'@type': 'Organization',
|
|
@@ -141,15 +149,12 @@ const pageJson = ({ title, description, path, name }) => {
|
|
|
141
149
|
return {
|
|
142
150
|
'@context': 'https://schema.org',
|
|
143
151
|
'@type': 'SoftwareSourceCode',
|
|
144
|
-
name:
|
|
145
|
-
alternateName: '@beforesemicolon/markup',
|
|
152
|
+
name: siteName,
|
|
146
153
|
description,
|
|
147
154
|
url,
|
|
148
155
|
image: defaultImage,
|
|
149
|
-
codeRepository: 'https://github.com/beforesemicolon/markup',
|
|
150
156
|
programmingLanguage: 'JavaScript',
|
|
151
157
|
runtimePlatform: 'Browser and Node.js',
|
|
152
|
-
license: 'https://github.com/beforesemicolon/markup/blob/main/LICENSE',
|
|
153
158
|
publisher: {
|
|
154
159
|
'@type': 'Organization',
|
|
155
160
|
name: 'Before Semicolon',
|
|
@@ -159,9 +164,20 @@ const pageJson = ({ title, description, path, name }) => {
|
|
|
159
164
|
}
|
|
160
165
|
|
|
161
166
|
export default (props = {}) => {
|
|
167
|
+
const meta = props.templateConfig?.meta || {}
|
|
168
|
+
const siteUrl = String(
|
|
169
|
+
meta.siteUrl || props.siteUrl || fallbackSiteUrl
|
|
170
|
+
).replace(/\/$/, '')
|
|
171
|
+
const siteName = meta.siteName || fallbackSiteName
|
|
172
|
+
const defaultTitle = meta.title || fallbackTitle
|
|
173
|
+
const defaultDescription = meta.description || fallbackDescription
|
|
174
|
+
const defaultImage = absoluteAssetUrl(siteUrl, meta.image || fallbackImage)
|
|
162
175
|
const title = props.title || defaultTitle
|
|
163
|
-
const description = compactDescription(
|
|
164
|
-
|
|
176
|
+
const description = compactDescription(
|
|
177
|
+
props.description,
|
|
178
|
+
defaultDescription
|
|
179
|
+
)
|
|
180
|
+
const url = absoluteUrl(siteUrl, props.path)
|
|
165
181
|
const canonical = escapeHTML(url)
|
|
166
182
|
const image = props.image || defaultImage
|
|
167
183
|
const cacheKey = props.cacheKey ? `?v=${escapeHTML(props.cacheKey)}` : ''
|
|
@@ -213,20 +229,7 @@ export default (props = {}) => {
|
|
|
213
229
|
<link rel="manifest" href="/assets/favicon/site.webmanifest" />
|
|
214
230
|
<link rel="icon" type="image/x-icon" href="/assets/favicon/favicon.ico" />
|
|
215
231
|
<link rel="stylesheet" href="/stylesheets/github-dark.hightlighter.css${cacheKey}">
|
|
216
|
-
${jsonScript(pageJson({ ...props, title, description }))}
|
|
217
|
-
${jsonScript(breadcrumbJson({ title, path: props.path }))}
|
|
218
|
-
${
|
|
219
|
-
isDev
|
|
220
|
-
? ''
|
|
221
|
-
: `<!-- Google tag (gtag.js) -->
|
|
222
|
-
<script async src="https://www.googletagmanager.com/gtag/js?id=G-N3MXGDP5PS"></script>
|
|
223
|
-
<script>
|
|
224
|
-
window.dataLayer = window.dataLayer || [];
|
|
225
|
-
function gtag(){dataLayer.push(arguments);}
|
|
226
|
-
gtag('js', new Date());
|
|
227
|
-
|
|
228
|
-
gtag('config', 'G-N3MXGDP5PS');
|
|
229
|
-
</script>`
|
|
230
|
-
}
|
|
232
|
+
${jsonScript(pageJson({ ...props, siteUrl, siteName, defaultImage, title, description }))}
|
|
233
|
+
${jsonScript(breadcrumbJson({ siteUrl, title, path: props.path }))}
|
|
231
234
|
`
|
|
232
235
|
}
|
|
@@ -201,6 +201,13 @@ body {
|
|
|
201
201
|
border-bottom-color: var(--primary-glow);
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
+
#documentation article blockquote {
|
|
205
|
+
background: var(--surface-muted);
|
|
206
|
+
margin: 0;
|
|
207
|
+
padding: 5px 20px;
|
|
208
|
+
border-radius: 3px;
|
|
209
|
+
}
|
|
210
|
+
|
|
204
211
|
#documentation article .heading {
|
|
205
212
|
scroll-margin-top: 6rem;
|
|
206
213
|
}
|
|
@@ -14,6 +14,13 @@ import landingInstall, {
|
|
|
14
14
|
import landingCta from './layouts/landing-cta.js'
|
|
15
15
|
|
|
16
16
|
export default {
|
|
17
|
+
meta: {
|
|
18
|
+
siteName: 'Before Semicolon',
|
|
19
|
+
title: 'Before Semicolon Documentation',
|
|
20
|
+
description:
|
|
21
|
+
'Documentation for Before Semicolon projects, libraries, and developer tools.',
|
|
22
|
+
image: '/assets/site-image.jpg',
|
|
23
|
+
},
|
|
17
24
|
markdownLayouts: {
|
|
18
25
|
'landing-hero': landingHero,
|
|
19
26
|
'landing-ecosystem': landingEcosystem,
|
package/dist/cjs/docs/types.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var i=Object.defineProperty;var
|
|
1
|
+
"use strict";var i=Object.defineProperty;var o=Object.getOwnPropertyDescriptor;var a=Object.getOwnPropertyNames;var g=Object.prototype.hasOwnProperty;var p=(e,t,s,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of a(t))!g.call(e,r)&&r!==s&&i(e,r,{get:()=>t[r],enumerable:!(n=o(t,r))||n.enumerable});return e};var c=e=>p(i({},"__esModule",{value:!0}),e);var m={};module.exports=c(m);
|
package/dist/esm/docs/run.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var
|
|
1
|
+
var ie=Object.defineProperty;var c=(e,t)=>ie(e,"name",{value:t,configurable:!0});import{Marked as le}from"marked";import{cp as j,mkdir as S,readdir as ce,readFile as C,writeFile as x}from"fs/promises";import o from"path";import me from"front-matter";import ue from"isomorphic-dompurify";import{markedHighlight as pe}from"marked-highlight";import Q from"highlight.js";import fe from"./templates/default.template.js";import de from"./renderer/index.js";import{createMarkdownLayoutExtension as he}from"./markdown-layout/index.js";import ge from"clean-css";import ye from"html-minifier";import*as X from"@putout/minify";import{fileURLToPath as we,pathToFileURL as A}from"url";import p from"fs";let V,R="";const Y=new ge({inline:["none"]}),W=new Map;W.set("default",fe);let _;const Z=c((e,t)=>t===!1?!1:t===void 0?e:e===!1||e===void 0?t:{...e,...t},"mergeThemeMode"),ve=c((e,t)=>{if(!(!e&&!t))return{light:Z(e?.light,t?.light),dark:Z(e?.dark,t?.dark)}},"mergeTheme"),Se=c((e,t)=>({...e,...t,markdownLayouts:{...e.markdownLayouts,...t.markdownLayouts},headScripts:{...e.headScripts,...t.headScripts},scripts:{...e.scripts,...t.scripts},theme:ve(e.theme,t.theme),meta:{...e.meta,...t.meta}}),"mergeTemplateConfig"),De=c((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"),ee=c((e,t)=>e?Object.values(e).filter(r=>r!==!1).filter(r=>De(r,t)).map(r=>typeof r=="function"?r():r.render()):[],"renderPageScripts"),G=c((e,t)=>{const r=Object.entries(t).filter(([a])=>a.startsWith("--")).map(([a,i])=>` ${a}: ${i};`).join(`
|
|
2
2
|
`);return r?`${e} {
|
|
3
3
|
${r}
|
|
4
4
|
}`:""},"renderThemeBlock"),be=c(e=>{if(!e)return"";const t=e.light===!1,r=e.dark===!1,a=e.light===!1?void 0:e.light,i=r?void 0:e.dark,m=a||i;return m?[G(":root",m),i&&!t?`@media (prefers-color-scheme: dark) {
|
|
@@ -7,17 +7,17 @@ ${G(" :root",i)}
|
|
|
7
7
|
.site-logo-dark { display: block; }`:"",r&&a?`.site-logo-light { display: block; }
|
|
8
8
|
.site-logo-dark { display: none; }`:""].filter(Boolean).join(`
|
|
9
9
|
|
|
10
|
-
`):""},"renderThemeStylesheet"),xe=c((e="")=>String(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),"escapeXML"),$e=c((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"),Pe=c(e=>e==="/index.html"?"/":e.replace(/\/index\.html$/,"/").replace(/\.html$/,"").replace(/\/+/g,"/"),"pagePathToUrlPath"),
|
|
10
|
+
`):""},"renderThemeStylesheet"),xe=c((e="")=>String(e).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'"),"escapeXML"),$e=c((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"),Pe=c(e=>e==="/index.html"?"/":e.replace(/\/index\.html$/,"/").replace(/\.html$/,"").replace(/\/+/g,"/"),"pagePathToUrlPath"),te=c((e,t)=>e?`${e.replace(/\/$/,"")}${t==="/"?"/":t}`:t,"canonicalUrl"),ke=c(async(e,t)=>p.existsSync(e)?(await S(o.dirname(t),{recursive:!0}),await j(e,t),!0):!1,"copyIfExists"),Te=c(async e=>{const t=o.resolve(e,"logo.svg");p.existsSync(t)&&await Promise.all(["logo.light.svg","logo.dark.svg"].map(async r=>{const a=o.resolve(e,r);p.existsSync(a)||await j(t,a)}))},"ensureLogoModeAssets"),Me=c(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"),je=c((e,t)=>{if(!t)return"";const r=new Date().toISOString().slice(0,10);return`<?xml version="1.0" encoding="UTF-8"?>
|
|
11
11
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
12
12
|
${e.map(i=>{const m=i.urlPath==="/"?"1.0":"0.8";return` <url>
|
|
13
|
-
<loc>${xe(
|
|
13
|
+
<loc>${xe(te(t,i.urlPath))}</loc>
|
|
14
14
|
<lastmod>${r}</lastmod>
|
|
15
15
|
<changefreq>weekly</changefreq>
|
|
16
16
|
<priority>${m}</priority>
|
|
17
17
|
</url>`}).join(`
|
|
18
18
|
`)}
|
|
19
19
|
</urlset>
|
|
20
|
-
`},"generateSitemap"),
|
|
20
|
+
`},"generateSitemap"),Ce=c(e=>`User-agent: *
|
|
21
21
|
Allow: /
|
|
22
22
|
${e?`
|
|
23
23
|
Sitemap: ${e.replace(/\/$/,"")}/sitemap.xml
|
|
@@ -35,7 +35,7 @@ This file is generated at build time from the Markdown documentation. Use it to
|
|
|
35
35
|
|
|
36
36
|
${e.map(r=>`## ${r.title}
|
|
37
37
|
|
|
38
|
-
URL: ${
|
|
38
|
+
URL: ${te(t,r.urlPath)}
|
|
39
39
|
Source: docs/${r.sourcePath}
|
|
40
40
|
Description: ${r.description}
|
|
41
41
|
|
|
@@ -75,4 +75,4 @@ for = "/robots.txt"
|
|
|
75
75
|
[headers.values]
|
|
76
76
|
Content-Type = "text/plain; charset=utf-8"
|
|
77
77
|
Cache-Control = "public, max-age=0, must-revalidate"
|
|
78
|
-
`,"generateNetlifyToml")
|
|
78
|
+
`,"generateNetlifyToml"),$=c(async(e,t,r,a)=>{if(await ke(e,t))return;const i=r();if(!i){a&&console.warn(a);return}await S(o.dirname(t),{recursive:!0}),await x(t,i)},"writeGeneratedFile"),Oe=c(async({docsDir:e,docsSiteDir:t,pages:r,siteUrl:a,publicDir:i,generatedFiles:m})=>{m.sitemap&&await $(o.resolve(e,"sitemap.xml"),o.resolve(t,"sitemap.xml"),()=>je(r,a),"Skipping generated sitemap.xml because buildDocs({ siteUrl }) is not set."),m.robots&&await $(o.resolve(e,"robots.txt"),o.resolve(t,"robots.txt"),()=>Ce(a)),m.llms&&await $(o.resolve(e,"llms.txt"),o.resolve(t,"llms.txt"),()=>Fe(r)),m.llmsFull&&await $(o.resolve(e,"llms-full.txt"),o.resolve(t,"llms-full.txt"),()=>Ge(r,a)),m.netlify&&(await $(o.resolve(e,"_redirects"),o.resolve(t,"_redirects"),()=>Le(r,m)),await $(o.resolve(e,"netlify.toml"),o.resolve(t,"netlify.toml"),()=>Ee(i&&o.basename(i))))},"writeGeneratedDocsFiles"),se=c((e={},t,r)=>{const a=new le(pe({langPrefix:"hljs language-",highlight(i,m){const d=Q.getLanguage(m)?m:"plaintext";return Q.highlight(i,{language:d}).value}}));return a.use({renderer:de}),a.use(he({marked:a,handlers:e.markdownLayouts,renderMarkdown:r})),e.markedOptions&&a.use(e.markedOptions),t&&a.use(t),a},"createBaseMarked"),Ae=c((e={},t,r)=>{const a=se(e,t),i=c(d=>a.parse(d),"renderMarkdown"),m=se(e,t,i);return m.use({hooks:{postprocess(d){const{layout:y="default",...P}=_.attributes,k=ee(e.headScripts,d),T=ee(e.scripts,d);d=ue.sanitize(d);const D=[...d.matchAll(/<h([0-6])\sid="[^"]+".*?>.*?<a\s+href="([^"]+)".*?>([\s\S]*?)<\/a><\/h\1>/gm)].map(u=>({path:u[2],label:u[3].replace(/<[^>]+>/g,"").replace(/\s+/g," ").trim(),level:u[1]}));return W.get(y)?.({...P,content:d,siteMap:_.siteMap,tableOfContent:D,projectMeta:V,renderMarkdown:i,headScripts:k,scripts:T,themeStylesheet:e.theme?`/stylesheets/theme.css?v=${R}`:void 0,cacheKey:R,siteUrl:r,templateConfig:e})||d}}}),m},"createMarked"),w=c(async e=>{const t=await ce(e),r=[];for(const a of t){const i=o.extname(a);/^[._]/.test(a)||(i?r.push(o.join(e,a)):/(stylesheets|assets|scripts)/.test(a)||r.push(...await w(o.join(e,a))))}return r},"traverseDirectory"),lt=c(async({srcDir:e,publicDir:t,markedOptions:r,template:a,siteUrl:i,generatedFiles:m}={})=>{R=Date.now().toString(36);try{const s=o.resolve(process.cwd(),"package.json");p.existsSync(s)&&(V=JSON.parse(await C(s,"utf-8")))}catch(s){console.warn("Failed to load project package.json metadata",s)}const d=c(()=>{try{return __dirname}catch{return o.dirname(we(import.meta.url))}},"getDirname"),y=a?o.resolve(d(),"templates",a):null,P=y?o.resolve(y,"assets"):null,k=y?o.resolve(y,"stylesheets"):null,T=y?o.resolve(y,"layouts"):null;let D={};if(y)try{let s=o.resolve(y,"template.config.js");if(p.existsSync(s)||(s=s.replace(/\.js$/,".ts")),p.existsSync(s)){const n=await import(A(s).href);D=n.default||n}}catch(s){console.error(`Failed to load template config for ${a}`,s)}const u=e??o.resolve(process.cwd(),"docs"),f=t??o.resolve(process.cwd(),"website"),re=Me(m),v=o.resolve(u,"_template"),B=o.resolve(v,"assets"),N=o.resolve(v,"stylesheets"),U=o.resolve(v,"scripts"),z=o.resolve(v,"layouts"),q=o.resolve(u,"_layouts"),F=o.resolve(u,"assets"),H=o.resolve(u,"stylesheets"),L=o.resolve(u,"scripts");try{let s=o.resolve(v,"template.config.js");if(p.existsSync(s)||(s=s.replace(/\.js$/,".ts")),p.existsSync(s)){const n=await import(A(s).href),l=n.default||n;D=Se(D,l)}}catch(s){console.error("Failed to load docs _template config",s)}const oe=Ae(D,r,i);if(await S(f,{recursive:!0}),P&&p.existsSync(P))try{await j(P,F.replace(u,f),{recursive:!0})}catch(s){console.warn(s)}if(p.existsSync(F))try{await j(F,F.replace(u,f),{recursive:!0})}catch(s){console.warn(s)}if(p.existsSync(B))try{await j(B,o.resolve(f,"assets"),{recursive:!0})}catch(s){console.warn(s)}await Te(o.resolve(f,"assets"));try{const s=new Set;if(k&&p.existsSync(k)&&(await w(k)).forEach(l=>s.add(l)),p.existsSync(H)&&(await w(H)).forEach(l=>s.add(l)),p.existsSync(N)&&(await w(N)).forEach(l=>s.add(l)),s.size){await S(o.join(f,"stylesheets"),{recursive:!0});for(const l of s){const h=await C(l,"utf-8"),g=Y.minify(h);g.errors.length&&g.errors.forEach(console.error);const M=l.startsWith(v)?l.replace(v,f):l.startsWith(u)?l.replace(u,f):o.join(f,"stylesheets",o.basename(l));await x(M,g.styles??h)}}const n=be(D.theme);if(n){const l=Y.minify(n);l.errors.length&&l.errors.forEach(console.error),await S(o.join(f,"stylesheets"),{recursive:!0}),await x(o.join(f,"stylesheets","theme.css"),l.styles??n)}}catch(s){console.error(s)}if(p.existsSync(L))try{const s=await w(L);s.length&&await S(L.replace(u,f),{recursive:!0});for(const n of s)try{const l=await C(n,"utf-8");await x(n.replace(u,f),X.minify(l))}catch(l){console.error(l)}}catch(s){console.warn(s)}if(p.existsSync(U))try{const s=await w(U);s.length&&await S(o.join(f,"scripts"),{recursive:!0});for(const n of s)try{const l=await C(n,"utf-8");await x(n.replace(v,f),X.minify(l))}catch(l){console.error(l)}}catch(s){console.warn(s)}try{const s=new Map;T&&p.existsSync(T)&&(await w(T)).forEach(n=>{/\.(j|t)s$/.test(n)&&s.set(o.basename(n).replace(/\.(j|t)s$/,""),n)}),p.existsSync(q)&&(await w(q)).forEach(n=>{/\.(j|t)s$/.test(n)&&s.set(o.basename(n).replace(/\.(j|t)s$/,""),n)}),p.existsSync(z)&&(await w(z)).forEach(n=>{/\.(j|t)s$/.test(n)&&s.set(o.basename(n).replace(/\.(j|t)s$/,""),n)});for(const[n,l]of s.entries()){const h=await import(A(l).href);W.set(n,h.default)}}catch(s){console.error("Template import failed",s)}const ae=await w(u),I=new Map,J=(await Promise.all(ae.filter(s=>s.endsWith(".md")).map(async s=>{const n=await C(s,"utf-8");return{filePath:s,...me(n)}}))).sort((s,n)=>(s.attributes.order??0)-(n.attributes.order??0)).map(({attributes:s,body:n,filePath:l})=>{const h=l.replace(u,"").replace(/\.md/,".html"),g=l.replace(u,f).replace(".md",".html"),M=g.replace(o.basename(g),""),E=o.basename(h)||"/",ne=h.replace(E,"").replace(/\/$/,"");let b=I;ne.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:M,body:n,sourcePath:o.relative(u,l),siteMap:I}});for(const{attributes:s,body:n,fileDirWebsitePath:l,fileWebsitePath:h,siteMap:g}of J){_={attributes:s,siteMap:g};const M=await oe.parse(n);await S(l,{recursive:!0}),await x(h,ye.minify(M,{includeAutoGeneratedTags:!0,removeAttributeQuotes:!0,removeComments:!0,removeRedundantAttributes:!0,removeScriptTypeAttributes:!0,removeStyleLinkTypeAttributes:!0,sortClassName:!0,useShortDoctype:!0,collapseWhitespace:!0,minifyJS:!0,minifyCSS:!0}))}await Oe({docsDir:u,docsSiteDir:f,pages:J.map(({attributes:s,body:n,sourcePath:l})=>{const h=Pe(String(s.path)),g=$e(n);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:i,publicDir:f,generatedFiles:re})},"buildDocs");export{lt as buildDocs};
|
|
@@ -144,9 +144,28 @@ It emits:
|
|
|
144
144
|
|
|
145
145
|
Important current behavior:
|
|
146
146
|
|
|
147
|
-
- The
|
|
148
|
-
-
|
|
149
|
-
-
|
|
147
|
+
- The helper reads defaults from `template.config.js` `meta`.
|
|
148
|
+
- `meta.siteUrl` overrides the `buildDocs({ siteUrl })` value when needed.
|
|
149
|
+
- `meta.siteName`, `meta.title`, and `meta.description` customize the fallback site identity.
|
|
150
|
+
- `meta.image` customizes the default Open Graph and Twitter card image.
|
|
151
|
+
- `meta.image` can be absolute, such as `https://example.com/card.jpg`, or site-relative, such as `/assets/site-image.jpg`.
|
|
152
|
+
- If no image is configured, the fallback is `/assets/site-image.jpg`.
|
|
153
|
+
|
|
154
|
+
For project-specific social images, add the file under `docs/_template/assets` and point `meta.image` at it:
|
|
155
|
+
|
|
156
|
+
```js
|
|
157
|
+
// docs/_template/template.config.js
|
|
158
|
+
export default {
|
|
159
|
+
meta: {
|
|
160
|
+
siteName: 'Example',
|
|
161
|
+
title: 'Example by Before Semicolon',
|
|
162
|
+
description: 'Documentation for Example.',
|
|
163
|
+
image: '/assets/site-image.jpg',
|
|
164
|
+
},
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Override `_head-meta.js` in `docs/_template/layouts` only when the generated metadata shape itself needs to change.
|
|
150
169
|
|
|
151
170
|
## Markdown Layout Handlers
|
|
152
171
|
|
|
@@ -590,7 +609,7 @@ docs/_template/assets/logo.svg
|
|
|
590
609
|
docs/_template/assets/logo.light.svg
|
|
591
610
|
docs/_template/assets/logo.dark.svg
|
|
592
611
|
docs/_template/assets/favicon/favicon.ico
|
|
593
|
-
docs/_template/assets/
|
|
612
|
+
docs/_template/assets/site-image.jpg
|
|
594
613
|
```
|
|
595
614
|
|
|
596
615
|
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.
|
|
@@ -618,7 +637,7 @@ The landing and documentation stylesheets import or rely on the shared styles as
|
|
|
618
637
|
|
|
619
638
|
## Scripts
|
|
620
639
|
|
|
621
|
-
Template scripts are registered by marker
|
|
640
|
+
Template scripts are registered by marker. The template renders `headScripts` inside `<head>` and `scripts` before `</body>`.
|
|
622
641
|
|
|
623
642
|
```js
|
|
624
643
|
scripts: {
|
|
@@ -668,11 +687,12 @@ export default {
|
|
|
668
687
|
Use the smallest override that solves the problem:
|
|
669
688
|
|
|
670
689
|
1. Override assets for logos, favicons, images, and icons.
|
|
671
|
-
2. Override
|
|
672
|
-
3.
|
|
673
|
-
4.
|
|
674
|
-
5. Override
|
|
675
|
-
6. Override `
|
|
690
|
+
2. Override `meta` fields for site identity and social card defaults.
|
|
691
|
+
3. Override theme variables for color and surface tuning.
|
|
692
|
+
4. Add Markdown layout handlers for new page sections.
|
|
693
|
+
5. Override existing Markdown layout handlers only when the section HTML needs to change.
|
|
694
|
+
6. Override `_header.js`, `_footer.js`, or `_head-meta.js` when navigation, footer copy, metadata shape, or brand assumptions need to change.
|
|
695
|
+
7. Override `landing.js` or `document.js` only when the full page shell changes.
|
|
676
696
|
|
|
677
697
|
Example docs extension:
|
|
678
698
|
|
|
@@ -692,6 +712,12 @@ docs/
|
|
|
692
712
|
import pricingCards from './layouts/pricing-cards.js'
|
|
693
713
|
|
|
694
714
|
export default {
|
|
715
|
+
meta: {
|
|
716
|
+
siteName: 'Example',
|
|
717
|
+
title: 'Example by Before Semicolon',
|
|
718
|
+
description: 'Documentation for Example.',
|
|
719
|
+
image: '/assets/site-image.jpg',
|
|
720
|
+
},
|
|
695
721
|
markdownLayouts: {
|
|
696
722
|
'pricing-cards': pricingCards,
|
|
697
723
|
},
|
|
@@ -710,7 +736,6 @@ export default {
|
|
|
710
736
|
|
|
711
737
|
Some template internals are still Markup or Before Semicolon oriented:
|
|
712
738
|
|
|
713
|
-
- `_head-meta.js` has Markup-specific defaults and URLs.
|
|
714
739
|
- `_header.js` has Markup-oriented GitHub and documentation links.
|
|
715
740
|
- `_footer.js` has Before Semicolon social links, footer copy, and learning links.
|
|
716
741
|
- `document.js` builds edit links against the Markup GitHub docs path.
|
|
@@ -1,22 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
const defaultDescription =
|
|
8
|
-
'Markup is a tiny reactive HTML templating system for building JavaScript user interfaces with web standards, no JSX, and no build step.'
|
|
9
|
-
const defaultImage = `${siteUrl}/assets/markup-banner.jpg`
|
|
1
|
+
const fallbackSiteUrl = 'https://beforesemicolon.com'
|
|
2
|
+
const fallbackSiteName = 'Before Semicolon'
|
|
3
|
+
const fallbackTitle = 'Before Semicolon Documentation'
|
|
4
|
+
const fallbackDescription =
|
|
5
|
+
'Documentation for Before Semicolon projects, libraries, and developer tools.'
|
|
6
|
+
const fallbackImage = '/assets/site-image.jpg'
|
|
10
7
|
const baseKeywords = [
|
|
11
|
-
'
|
|
12
|
-
'
|
|
13
|
-
'
|
|
14
|
-
'
|
|
15
|
-
'
|
|
16
|
-
'
|
|
17
|
-
'
|
|
18
|
-
'no build step',
|
|
19
|
-
'vanilla JavaScript',
|
|
8
|
+
'Before Semicolon',
|
|
9
|
+
'documentation',
|
|
10
|
+
'JavaScript',
|
|
11
|
+
'developer tools',
|
|
12
|
+
'web development',
|
|
13
|
+
'frontend development',
|
|
14
|
+
'open source',
|
|
20
15
|
]
|
|
21
16
|
|
|
22
17
|
const escapeHTML = (value = '') =>
|
|
@@ -38,15 +33,20 @@ const canonicalPath = (path = '/') => {
|
|
|
38
33
|
return cleanPath.replace(/\.html$/, '')
|
|
39
34
|
}
|
|
40
35
|
|
|
41
|
-
const absoluteUrl = (path = '/') => `${siteUrl}${canonicalPath(path)}`
|
|
36
|
+
const absoluteUrl = (siteUrl, path = '/') => `${siteUrl}${canonicalPath(path)}`
|
|
42
37
|
|
|
43
|
-
const
|
|
38
|
+
const absoluteAssetUrl = (siteUrl, imagePath) => {
|
|
39
|
+
if (/^https?:\/\//i.test(imagePath)) return imagePath
|
|
40
|
+
return `${siteUrl}${imagePath.startsWith('/') ? imagePath : `/${imagePath}`}`
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const compactDescription = (description, defaultDescription) =>
|
|
44
44
|
(description || defaultDescription).replace(/\s+/g, ' ').trim()
|
|
45
45
|
|
|
46
46
|
const pageKeywords = ({ title, name, keywords } = {}) => {
|
|
47
47
|
const values = [
|
|
48
48
|
...baseKeywords,
|
|
49
|
-
title?.replace(/ -
|
|
49
|
+
title?.replace(/ - Before Semicolon Documentation$/, ''),
|
|
50
50
|
name,
|
|
51
51
|
...(Array.isArray(keywords)
|
|
52
52
|
? keywords
|
|
@@ -64,7 +64,7 @@ const jsonScript = (data) =>
|
|
|
64
64
|
.replace(/>/g, '\\u003e')
|
|
65
65
|
.replace(/&/g, '\\u0026')}</script>`
|
|
66
66
|
|
|
67
|
-
const breadcrumbJson = ({ title, path }) => {
|
|
67
|
+
const breadcrumbJson = ({ siteUrl, title, path }) => {
|
|
68
68
|
const cleanPath = canonicalPath(path)
|
|
69
69
|
const parts = cleanPath.split('/').filter(Boolean)
|
|
70
70
|
const items = [
|
|
@@ -84,7 +84,7 @@ const breadcrumbJson = ({ title, path }) => {
|
|
|
84
84
|
position: index + 2,
|
|
85
85
|
name:
|
|
86
86
|
index === parts.length - 1
|
|
87
|
-
? title.replace(/ -
|
|
87
|
+
? title.replace(/ - Before Semicolon Documentation$/, '')
|
|
88
88
|
: part
|
|
89
89
|
.split('-')
|
|
90
90
|
.map((word) => word[0]?.toUpperCase() + word.slice(1))
|
|
@@ -100,9 +100,17 @@ const breadcrumbJson = ({ title, path }) => {
|
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
const pageJson = ({
|
|
103
|
+
const pageJson = ({
|
|
104
|
+
siteUrl,
|
|
105
|
+
siteName,
|
|
106
|
+
defaultImage,
|
|
107
|
+
title,
|
|
108
|
+
description,
|
|
109
|
+
path,
|
|
110
|
+
name,
|
|
111
|
+
}) => {
|
|
104
112
|
const isDocsPage = canonicalPath(path).startsWith('/documentation')
|
|
105
|
-
const url = absoluteUrl(path)
|
|
113
|
+
const url = absoluteUrl(siteUrl, path)
|
|
106
114
|
|
|
107
115
|
if (isDocsPage) {
|
|
108
116
|
return {
|
|
@@ -116,14 +124,14 @@ const pageJson = ({ title, description, path, name }) => {
|
|
|
116
124
|
inLanguage: 'en-US',
|
|
117
125
|
isPartOf: {
|
|
118
126
|
'@type': 'TechArticle',
|
|
119
|
-
name:
|
|
127
|
+
name: `${siteName} Documentation`,
|
|
120
128
|
url: `${siteUrl}/documentation`,
|
|
121
129
|
},
|
|
122
130
|
about: [
|
|
123
|
-
|
|
124
|
-
'JavaScript
|
|
125
|
-
'
|
|
126
|
-
'
|
|
131
|
+
`${siteName} documentation`,
|
|
132
|
+
'JavaScript',
|
|
133
|
+
'Web development',
|
|
134
|
+
'Developer tools',
|
|
127
135
|
],
|
|
128
136
|
publisher: {
|
|
129
137
|
'@type': 'Organization',
|
|
@@ -141,15 +149,12 @@ const pageJson = ({ title, description, path, name }) => {
|
|
|
141
149
|
return {
|
|
142
150
|
'@context': 'https://schema.org',
|
|
143
151
|
'@type': 'SoftwareSourceCode',
|
|
144
|
-
name:
|
|
145
|
-
alternateName: '@beforesemicolon/markup',
|
|
152
|
+
name: siteName,
|
|
146
153
|
description,
|
|
147
154
|
url,
|
|
148
155
|
image: defaultImage,
|
|
149
|
-
codeRepository: 'https://github.com/beforesemicolon/markup',
|
|
150
156
|
programmingLanguage: 'JavaScript',
|
|
151
157
|
runtimePlatform: 'Browser and Node.js',
|
|
152
|
-
license: 'https://github.com/beforesemicolon/markup/blob/main/LICENSE',
|
|
153
158
|
publisher: {
|
|
154
159
|
'@type': 'Organization',
|
|
155
160
|
name: 'Before Semicolon',
|
|
@@ -159,9 +164,20 @@ const pageJson = ({ title, description, path, name }) => {
|
|
|
159
164
|
}
|
|
160
165
|
|
|
161
166
|
export default (props = {}) => {
|
|
167
|
+
const meta = props.templateConfig?.meta || {}
|
|
168
|
+
const siteUrl = String(
|
|
169
|
+
meta.siteUrl || props.siteUrl || fallbackSiteUrl
|
|
170
|
+
).replace(/\/$/, '')
|
|
171
|
+
const siteName = meta.siteName || fallbackSiteName
|
|
172
|
+
const defaultTitle = meta.title || fallbackTitle
|
|
173
|
+
const defaultDescription = meta.description || fallbackDescription
|
|
174
|
+
const defaultImage = absoluteAssetUrl(siteUrl, meta.image || fallbackImage)
|
|
162
175
|
const title = props.title || defaultTitle
|
|
163
|
-
const description = compactDescription(
|
|
164
|
-
|
|
176
|
+
const description = compactDescription(
|
|
177
|
+
props.description,
|
|
178
|
+
defaultDescription
|
|
179
|
+
)
|
|
180
|
+
const url = absoluteUrl(siteUrl, props.path)
|
|
165
181
|
const canonical = escapeHTML(url)
|
|
166
182
|
const image = props.image || defaultImage
|
|
167
183
|
const cacheKey = props.cacheKey ? `?v=${escapeHTML(props.cacheKey)}` : ''
|
|
@@ -213,20 +229,7 @@ export default (props = {}) => {
|
|
|
213
229
|
<link rel="manifest" href="/assets/favicon/site.webmanifest" />
|
|
214
230
|
<link rel="icon" type="image/x-icon" href="/assets/favicon/favicon.ico" />
|
|
215
231
|
<link rel="stylesheet" href="/stylesheets/github-dark.hightlighter.css${cacheKey}">
|
|
216
|
-
${jsonScript(pageJson({ ...props, title, description }))}
|
|
217
|
-
${jsonScript(breadcrumbJson({ title, path: props.path }))}
|
|
218
|
-
${
|
|
219
|
-
isDev
|
|
220
|
-
? ''
|
|
221
|
-
: `<!-- Google tag (gtag.js) -->
|
|
222
|
-
<script async src="https://www.googletagmanager.com/gtag/js?id=G-N3MXGDP5PS"></script>
|
|
223
|
-
<script>
|
|
224
|
-
window.dataLayer = window.dataLayer || [];
|
|
225
|
-
function gtag(){dataLayer.push(arguments);}
|
|
226
|
-
gtag('js', new Date());
|
|
227
|
-
|
|
228
|
-
gtag('config', 'G-N3MXGDP5PS');
|
|
229
|
-
</script>`
|
|
230
|
-
}
|
|
232
|
+
${jsonScript(pageJson({ ...props, siteUrl, siteName, defaultImage, title, description }))}
|
|
233
|
+
${jsonScript(breadcrumbJson({ siteUrl, title, path: props.path }))}
|
|
231
234
|
`
|
|
232
235
|
}
|
|
@@ -201,6 +201,13 @@ body {
|
|
|
201
201
|
border-bottom-color: var(--primary-glow);
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
+
#documentation article blockquote {
|
|
205
|
+
background: var(--surface-muted);
|
|
206
|
+
margin: 0;
|
|
207
|
+
padding: 5px 20px;
|
|
208
|
+
border-radius: 3px;
|
|
209
|
+
}
|
|
210
|
+
|
|
204
211
|
#documentation article .heading {
|
|
205
212
|
scroll-margin-top: 6rem;
|
|
206
213
|
}
|
|
@@ -14,6 +14,13 @@ import landingInstall, {
|
|
|
14
14
|
import landingCta from './layouts/landing-cta.js'
|
|
15
15
|
|
|
16
16
|
export default {
|
|
17
|
+
meta: {
|
|
18
|
+
siteName: 'Before Semicolon',
|
|
19
|
+
title: 'Before Semicolon Documentation',
|
|
20
|
+
description:
|
|
21
|
+
'Documentation for Before Semicolon projects, libraries, and developer tools.',
|
|
22
|
+
image: '/assets/site-image.jpg',
|
|
23
|
+
},
|
|
17
24
|
markdownLayouts: {
|
|
18
25
|
'landing-hero': landingHero,
|
|
19
26
|
'landing-ecosystem': landingEcosystem,
|
|
@@ -6,6 +6,13 @@ export interface DocsTheme {
|
|
|
6
6
|
light?: DocsThemeModeConfig;
|
|
7
7
|
dark?: DocsThemeModeConfig;
|
|
8
8
|
}
|
|
9
|
+
export interface DocsMetaConfig {
|
|
10
|
+
siteUrl?: string;
|
|
11
|
+
siteName?: string;
|
|
12
|
+
title?: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
image?: string;
|
|
15
|
+
}
|
|
9
16
|
export type DocsScriptMatcher = string | string[] | RegExp | ((html: string) => boolean);
|
|
10
17
|
export interface DocsScriptDefinition {
|
|
11
18
|
match?: DocsScriptMatcher;
|
|
@@ -31,9 +38,12 @@ export interface PageProps {
|
|
|
31
38
|
[key: string]: unknown;
|
|
32
39
|
};
|
|
33
40
|
renderMarkdown?: (markdown: string) => string;
|
|
41
|
+
headScripts?: string[];
|
|
34
42
|
scripts?: string[];
|
|
35
43
|
themeStylesheet?: string;
|
|
36
44
|
cacheKey?: string;
|
|
45
|
+
siteUrl?: string;
|
|
46
|
+
templateConfig?: DocsTemplateConfig;
|
|
37
47
|
}
|
|
38
48
|
export interface CustomOptions {
|
|
39
49
|
name?: string;
|
|
@@ -58,6 +68,8 @@ export type SiteMap = Map<string, CustomOptions | SiteMap>;
|
|
|
58
68
|
export interface DocsTemplateConfig {
|
|
59
69
|
markedOptions?: MarkedExtension;
|
|
60
70
|
markdownLayouts?: Record<string, MarkdownLayoutHandler>;
|
|
71
|
+
headScripts?: DocsScriptRegistry;
|
|
61
72
|
scripts?: DocsScriptRegistry;
|
|
62
73
|
theme?: DocsTheme;
|
|
74
|
+
meta?: DocsMetaConfig;
|
|
63
75
|
}
|