@beforesemicolon/builder 1.8.22 → 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 CHANGED
@@ -306,6 +306,7 @@ export default {
306
306
  meta: {},
307
307
  markedOptions: {},
308
308
  markdownLayouts: {},
309
+ headScripts: {},
309
310
  scripts: {},
310
311
  theme: {
311
312
  light: {},
@@ -319,7 +320,7 @@ Config from `docs/_template/template.config.js` is merged into the selected temp
319
320
  Merge behavior:
320
321
 
321
322
  - `markdownLayouts` are shallow-merged by layout name.
322
- - `scripts` are shallow-merged by script name.
323
+ - `headScripts` and `scripts` are shallow-merged by script name.
323
324
  - `meta` is shallow-merged by metadata field.
324
325
  - `theme.light` and `theme.dark` are shallow-merged by CSS variable name.
325
326
  - Other top-level config values use the docs source config value when provided.
@@ -602,6 +603,8 @@ export default {
602
603
  }
603
604
  ```
604
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
+
605
608
  Script definitions:
606
609
 
607
610
  ```ts
@@ -632,12 +635,15 @@ Behavior:
632
635
  - A function matcher receives the rendered page HTML and returns a boolean.
633
636
  - A script can be disabled by setting its registry value to `false` in an extending config.
634
637
 
635
- Layouts receive scripts through `props.scripts` and must insert them where appropriate, usually before `</body>`.
638
+ Layouts receive scripts through `props.headScripts` and `props.scripts`. Template layouts must insert them where appropriate.
636
639
 
637
640
  ```js
638
641
  export default (props) => `
639
642
  <!doctype html>
640
643
  <html>
644
+ <head>
645
+ ${props.headScripts?.join('') || ''}
646
+ </head>
641
647
  <body>
642
648
  ${props.content}
643
649
  ${props.scripts?.join('') || ''}
@@ -802,8 +808,12 @@ export default {
802
808
  markdownLayouts: {
803
809
  'pricing-cards': pricingCards,
804
810
  },
811
+ headScripts: {
812
+ analytics: () =>
813
+ `<script async src="https://example.com/analytics.js"></script>`,
814
+ },
805
815
  scripts: {
806
- analytics: {
816
+ pageView: {
807
817
  match: '<main',
808
818
  render: () => `<script>console.log('page viewed')</script>`,
809
819
  },
@@ -1,4 +1,4 @@
1
- "use strict";var he=Object.create;var C=Object.defineProperty;var ge=Object.getOwnPropertyDescriptor;var ye=Object.getOwnPropertyNames;var we=Object.getPrototypeOf,ve=Object.prototype.hasOwnProperty;var m=(e,t)=>C(e,"name",{value:t,configurable:!0});var Se=(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 ye(t))!ve.call(e,n)&&n!==r&&C(e,n,{get:()=>t[n],enumerable:!(a=ge(t,n))||a.enumerable});return e};var v=(e,t,r)=>(r=e!=null?he(we(e)):{},Q(t||!e||!e.__esModule?C(r,"default",{value:e,enumerable:!0}):r,e)),De=e=>Q(C({},"__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=v(require("path"),1),re=v(require("front-matter"),1),oe=v(require("isomorphic-dompurify"),1),ae=require("marked-highlight"),_=v(require("highlight.js"),1),ne=v(require("./templates/default.template.js"),1),ie=v(require("./renderer/index.js"),1),le=require("./markdown-layout/index.js"),ce=v(require("clean-css"),1),me=v(require("html-minifier"),1),X=v(require("@putout/minify"),1),P=require("url"),p=v(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(`
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,"&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"),je=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"),Ce=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"?>
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"),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(ee(t,n.urlPath))}</loc>
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: ${ee(t,r.urlPath)}
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"),$=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"),te=m((e={},t,r)=>{const a=new se.Marked((0,ae.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: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,r)=>{const a=te(e,t),n=m(h=>a.parse(h),"renderMarkdown"),u=te(e,t,n);return u.use({hooks:{postprocess(h){const{layout:w="default",...k}=W.attributes,T=Pe(e.scripts,h);h=oe.default.sanitize(h);const M=[...h.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(w)?.({...k,content:h,siteMap:W.siteMap,tableOfContent:M,projectMeta:V,renderMarkdown:n,scripts:T,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");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 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 D={};if(w)try{let s=o.default.resolve(w,"template.config.js");if(p.default.existsSync(s)||(s=s.replace(/\.js$/,".ts")),p.default.existsSync(s)){const i=await import((0,P.pathToFileURL)(s).href);D=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),b=o.default.resolve(d,"_template"),B=o.default.resolve(b,"assets"),N=o.default.resolve(b,"stylesheets"),U=o.default.resolve(b,"scripts"),z=o.default.resolve(b,"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(b,"template.config.js");if(p.default.existsSync(s)||(s=s.replace(/\.js$/,".ts")),p.default.existsSync(s)){const i=await import((0,P.pathToFileURL)(s).href),l=i.default||i;D=xe(D,l)}}catch(s){console.error("Failed to load docs _template config",s)}const pe=Be(D,r,n);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 S(T)).forEach(l=>s.add(l)),p.default.existsSync(H)&&(await S(H)).forEach(l=>s.add(l)),p.default.existsSync(N)&&(await S(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 g=await(0,c.readFile)(l,"utf-8"),y=Y.minify(g);y.errors.length&&y.errors.forEach(console.error);const j=l.startsWith(b)?l.replace(b,f):l.startsWith(d)?l.replace(d,f):o.default.join(f,"stylesheets",o.default.basename(l));await(0,c.writeFile)(j,y.styles??g)}}const i=ke(D.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 S(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 S(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(b,f),X.minify(l))}catch(l){console.error(l)}}catch(s){console.warn(s)}try{const s=new Map;M&&p.default.existsSync(M)&&(await S(M)).forEach(i=>{/\.(j|t)s$/.test(i)&&s.set(o.default.basename(i).replace(/\.(j|t)s$/,""),i)}),p.default.existsSync(q)&&(await S(q)).forEach(i=>{/\.(j|t)s$/.test(i)&&s.set(o.default.basename(i).replace(/\.(j|t)s$/,""),i)}),p.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 fe=await S(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 g=l.replace(d,"").replace(/\.md/,".html"),y=l.replace(d,f).replace(".md",".html"),j=y.replace(o.default.basename(y),""),E=o.default.basename(g)||"/",de=g.replace(E,"").replace(/\/$/,"");let x=I;de.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(d,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 pe.parse(i);await(0,c.mkdir)(l,{recursive:!0}),await(0,c.writeFile)(g,me.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:d,docsSiteDir:f,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:f,generatedFiles:ue})},"buildDocs");0&&(module.exports={buildDocs});
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});
@@ -637,7 +637,7 @@ The landing and documentation stylesheets import or rely on the shared styles as
637
637
 
638
638
  ## Scripts
639
639
 
640
- Template scripts are registered by marker:
640
+ Template scripts are registered by marker. The template renders `headScripts` inside `<head>` and `scripts` before `</body>`.
641
641
 
642
642
  ```js
643
643
  scripts: {
@@ -1,6 +1,3 @@
1
- /* global process */
2
- const isDev = process.env.NODE_ENV === 'development'
3
-
4
1
  const fallbackSiteUrl = 'https://beforesemicolon.com'
5
2
  const fallbackSiteName = 'Before Semicolon'
6
3
  const fallbackTitle = 'Before Semicolon Documentation'
@@ -234,18 +231,5 @@ export default (props = {}) => {
234
231
  <link rel="stylesheet" href="/stylesheets/github-dark.hightlighter.css${cacheKey}">
235
232
  ${jsonScript(pageJson({ ...props, siteUrl, siteName, defaultImage, title, description }))}
236
233
  ${jsonScript(breadcrumbJson({ siteUrl, title, path: props.path }))}
237
- ${
238
- isDev
239
- ? ''
240
- : `<!-- Google tag (gtag.js) -->
241
- <script async src="https://www.googletagmanager.com/gtag/js?id=G-N3MXGDP5PS"></script>
242
- <script>
243
- window.dataLayer = window.dataLayer || [];
244
- function gtag(){dataLayer.push(arguments);}
245
- gtag('js', new Date());
246
-
247
- gtag('config', 'G-N3MXGDP5PS');
248
- </script>`
249
- }
250
234
  `
251
235
  }
@@ -46,6 +46,7 @@ export default (props) => {
46
46
  <html lang="en">
47
47
  <head>
48
48
  ${meta(props)}
49
+ ${props.headScripts?.join('') || ''}
49
50
  <link rel="stylesheet" href="/stylesheets/documentation.css${cacheKey}">
50
51
  ${
51
52
  props.themeStylesheet
@@ -10,6 +10,7 @@ export default (props) => {
10
10
  <html lang="en">
11
11
  <head>
12
12
  ${meta(props)}
13
+ ${props.headScripts?.join('') || ''}
13
14
  <link rel="stylesheet" href="/stylesheets/landing.css${cacheKey}">
14
15
  ${
15
16
  props.themeStylesheet
@@ -1,4 +1,4 @@
1
- var ne=Object.defineProperty;var c=(e,t)=>ne(e,"name",{value:t,configurable:!0});import{Marked as ie}from"marked";import{cp as j,mkdir as D,readdir as le,readFile as C,writeFile as x}from"fs/promises";import o from"path";import ce from"front-matter";import me from"isomorphic-dompurify";import{markedHighlight as ue}from"marked-highlight";import Q from"highlight.js";import pe from"./templates/default.template.js";import fe from"./renderer/index.js";import{createMarkdownLayoutExtension as de}from"./markdown-layout/index.js";import he from"clean-css";import ge from"html-minifier";import*as X from"@putout/minify";import{fileURLToPath as ye,pathToFileURL as A}from"url";import u from"fs";let V,R="";const Y=new he({inline:["none"]}),W=new Map;W.set("default",pe);let _;const Z=c((e,t)=>t===!1?!1:t===void 0?e:e===!1||e===void 0?t:{...e,...t},"mergeThemeMode"),we=c((e,t)=>{if(!(!e&&!t))return{light:Z(e?.light,t?.light),dark:Z(e?.dark,t?.dark)}},"mergeTheme"),ve=c((e,t)=>({...e,...t,markdownLayouts:{...e.markdownLayouts,...t.markdownLayouts},scripts:{...e.scripts,...t.scripts},theme:we(e.theme,t.theme)}),"mergeTemplateConfig"),Se=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"),De=c((e,t)=>e?Object.values(e).filter(r=>r!==!1).filter(r=>Se(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(`
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,10 +7,10 @@ ${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,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;"),"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"),ee=c((e,t)=>e?`${e.replace(/\/$/,"")}${t==="/"?"/":t}`:t,"canonicalUrl"),ke=c(async(e,t)=>u.existsSync(e)?(await D(o.dirname(t),{recursive:!0}),await j(e,t),!0):!1,"copyIfExists"),Te=c(async e=>{const t=o.resolve(e,"logo.svg");u.existsSync(t)&&await Promise.all(["logo.light.svg","logo.dark.svg"].map(async r=>{const a=o.resolve(e,r);u.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"?>
10
+ `):""},"renderThemeStylesheet"),xe=c((e="")=>String(e).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;"),"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(ee(t,i.urlPath))}</loc>
13
+ <loc>${xe(te(t,i.urlPath))}</loc>
14
14
  <lastmod>${r}</lastmod>
15
15
  <changefreq>weekly</changefreq>
16
16
  <priority>${m}</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: ${ee(t,r.urlPath)}
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"),$=c(async(e,t,r,a)=>{if(await ke(e,t))return;const i=r();if(!i){a&&console.warn(a);return}await D(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"),te=c((e={},t,r)=>{const a=new ie(ue({langPrefix:"hljs language-",highlight(i,m){const d=Q.getLanguage(m)?m:"plaintext";return Q.highlight(i,{language:d}).value}}));return a.use({renderer:fe}),a.use(de({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=te(e,t),i=c(d=>a.parse(d),"renderMarkdown"),m=te(e,t,i);return m.use({hooks:{postprocess(d){const{layout:y="default",...P}=_.attributes,k=De(e.scripts,d);d=me.sanitize(d);const T=[...d.matchAll(/<h([0-6])\sid="[^"]+".*?>.*?<a\s+href="([^"]+)".*?>([\s\S]*?)<\/a><\/h\1>/gm)].map(v=>({path:v[2],label:v[3].replace(/<[^>]+>/g,"").replace(/\s+/g," ").trim(),level:v[1]}));return W.get(y)?.({...P,content:d,siteMap:_.siteMap,tableOfContent:T,projectMeta:V,renderMarkdown:i,scripts:k,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 le(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");u.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(ye(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 v={};if(y)try{let s=o.resolve(y,"template.config.js");if(u.existsSync(s)||(s=s.replace(/\.js$/,".ts")),u.existsSync(s)){const n=await import(A(s).href);v=n.default||n}}catch(s){console.error(`Failed to load template config for ${a}`,s)}const f=e??o.resolve(process.cwd(),"docs"),p=t??o.resolve(process.cwd(),"website"),se=Me(m),S=o.resolve(f,"_template"),B=o.resolve(S,"assets"),N=o.resolve(S,"stylesheets"),U=o.resolve(S,"scripts"),z=o.resolve(S,"layouts"),q=o.resolve(f,"_layouts"),F=o.resolve(f,"assets"),H=o.resolve(f,"stylesheets"),L=o.resolve(f,"scripts");try{let s=o.resolve(S,"template.config.js");if(u.existsSync(s)||(s=s.replace(/\.js$/,".ts")),u.existsSync(s)){const n=await import(A(s).href),l=n.default||n;v=ve(v,l)}}catch(s){console.error("Failed to load docs _template config",s)}const re=Ae(v,r,i);if(await D(p,{recursive:!0}),P&&u.existsSync(P))try{await j(P,F.replace(f,p),{recursive:!0})}catch(s){console.warn(s)}if(u.existsSync(F))try{await j(F,F.replace(f,p),{recursive:!0})}catch(s){console.warn(s)}if(u.existsSync(B))try{await j(B,o.resolve(p,"assets"),{recursive:!0})}catch(s){console.warn(s)}await Te(o.resolve(p,"assets"));try{const s=new Set;if(k&&u.existsSync(k)&&(await w(k)).forEach(l=>s.add(l)),u.existsSync(H)&&(await w(H)).forEach(l=>s.add(l)),u.existsSync(N)&&(await w(N)).forEach(l=>s.add(l)),s.size){await D(o.join(p,"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(S)?l.replace(S,p):l.startsWith(f)?l.replace(f,p):o.join(p,"stylesheets",o.basename(l));await x(M,g.styles??h)}}const n=be(v.theme);if(n){const l=Y.minify(n);l.errors.length&&l.errors.forEach(console.error),await D(o.join(p,"stylesheets"),{recursive:!0}),await x(o.join(p,"stylesheets","theme.css"),l.styles??n)}}catch(s){console.error(s)}if(u.existsSync(L))try{const s=await w(L);s.length&&await D(L.replace(f,p),{recursive:!0});for(const n of s)try{const l=await C(n,"utf-8");await x(n.replace(f,p),X.minify(l))}catch(l){console.error(l)}}catch(s){console.warn(s)}if(u.existsSync(U))try{const s=await w(U);s.length&&await D(o.join(p,"scripts"),{recursive:!0});for(const n of s)try{const l=await C(n,"utf-8");await x(n.replace(S,p),X.minify(l))}catch(l){console.error(l)}}catch(s){console.warn(s)}try{const s=new Map;T&&u.existsSync(T)&&(await w(T)).forEach(n=>{/\.(j|t)s$/.test(n)&&s.set(o.basename(n).replace(/\.(j|t)s$/,""),n)}),u.existsSync(q)&&(await w(q)).forEach(n=>{/\.(j|t)s$/.test(n)&&s.set(o.basename(n).replace(/\.(j|t)s$/,""),n)}),u.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 oe=await w(f),I=new Map,J=(await Promise.all(oe.filter(s=>s.endsWith(".md")).map(async s=>{const n=await C(s,"utf-8");return{filePath:s,...ce(n)}}))).sort((s,n)=>(s.attributes.order??0)-(n.attributes.order??0)).map(({attributes:s,body:n,filePath:l})=>{const h=l.replace(f,"").replace(/\.md/,".html"),g=l.replace(f,p).replace(".md",".html"),M=g.replace(o.basename(g),""),E=o.basename(h)||"/",ae=h.replace(E,"").replace(/\/$/,"");let b=I;ae.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(f,l),siteMap:I}});for(const{attributes:s,body:n,fileDirWebsitePath:l,fileWebsitePath:h,siteMap:g}of J){_={attributes:s,siteMap:g};const M=await re.parse(n);await D(l,{recursive:!0}),await x(h,ge.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:f,docsSiteDir:p,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:p,generatedFiles:se})},"buildDocs");export{lt as buildDocs};
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};
@@ -637,7 +637,7 @@ The landing and documentation stylesheets import or rely on the shared styles as
637
637
 
638
638
  ## Scripts
639
639
 
640
- Template scripts are registered by marker:
640
+ Template scripts are registered by marker. The template renders `headScripts` inside `<head>` and `scripts` before `</body>`.
641
641
 
642
642
  ```js
643
643
  scripts: {
@@ -1,6 +1,3 @@
1
- /* global process */
2
- const isDev = process.env.NODE_ENV === 'development'
3
-
4
1
  const fallbackSiteUrl = 'https://beforesemicolon.com'
5
2
  const fallbackSiteName = 'Before Semicolon'
6
3
  const fallbackTitle = 'Before Semicolon Documentation'
@@ -234,18 +231,5 @@ export default (props = {}) => {
234
231
  <link rel="stylesheet" href="/stylesheets/github-dark.hightlighter.css${cacheKey}">
235
232
  ${jsonScript(pageJson({ ...props, siteUrl, siteName, defaultImage, title, description }))}
236
233
  ${jsonScript(breadcrumbJson({ siteUrl, title, path: props.path }))}
237
- ${
238
- isDev
239
- ? ''
240
- : `<!-- Google tag (gtag.js) -->
241
- <script async src="https://www.googletagmanager.com/gtag/js?id=G-N3MXGDP5PS"></script>
242
- <script>
243
- window.dataLayer = window.dataLayer || [];
244
- function gtag(){dataLayer.push(arguments);}
245
- gtag('js', new Date());
246
-
247
- gtag('config', 'G-N3MXGDP5PS');
248
- </script>`
249
- }
250
234
  `
251
235
  }
@@ -46,6 +46,7 @@ export default (props) => {
46
46
  <html lang="en">
47
47
  <head>
48
48
  ${meta(props)}
49
+ ${props.headScripts?.join('') || ''}
49
50
  <link rel="stylesheet" href="/stylesheets/documentation.css${cacheKey}">
50
51
  ${
51
52
  props.themeStylesheet
@@ -10,6 +10,7 @@ export default (props) => {
10
10
  <html lang="en">
11
11
  <head>
12
12
  ${meta(props)}
13
+ ${props.headScripts?.join('') || ''}
13
14
  <link rel="stylesheet" href="/stylesheets/landing.css${cacheKey}">
14
15
  ${
15
16
  props.themeStylesheet
@@ -38,6 +38,7 @@ export interface PageProps {
38
38
  [key: string]: unknown;
39
39
  };
40
40
  renderMarkdown?: (markdown: string) => string;
41
+ headScripts?: string[];
41
42
  scripts?: string[];
42
43
  themeStylesheet?: string;
43
44
  cacheKey?: string;
@@ -67,6 +68,7 @@ export type SiteMap = Map<string, CustomOptions | SiteMap>;
67
68
  export interface DocsTemplateConfig {
68
69
  markedOptions?: MarkedExtension;
69
70
  markdownLayouts?: Record<string, MarkdownLayoutHandler>;
71
+ headScripts?: DocsScriptRegistry;
70
72
  scripts?: DocsScriptRegistry;
71
73
  theme?: DocsTheme;
72
74
  meta?: DocsMetaConfig;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@beforesemicolon/builder",
3
- "version": "1.8.22",
3
+ "version": "1.8.23",
4
4
  "description": "Utilities to build npm packages and documentation website",
5
5
  "engines": {
6
6
  "node": ">=18.16.0"