@beforesemicolon/site-builder 0.35.0 → 0.36.0

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.
@@ -1,11 +1,11 @@
1
- "use strict";var oe=Object.create;var x=Object.defineProperty;var ie=Object.getOwnPropertyDescriptor;var ae=Object.getOwnPropertyNames;var re=Object.getPrototypeOf,ce=Object.prototype.hasOwnProperty;var b=(t,n)=>x(t,"name",{value:n,configurable:!0});var le=(t,n)=>{for(var i in n)x(t,i,{get:n[i],enumerable:!0})},z=(t,n,i,g)=>{if(n&&typeof n=="object"||typeof n=="function")for(let p of ae(n))!ce.call(t,p)&&p!==i&&x(t,p,{get:()=>n[p],enumerable:!(g=ie(n,p))||g.enumerable});return t};var N=(t,n,i)=>(i=t!=null?oe(re(t)):{},z(n||!t||!t.__esModule?x(i,"default",{value:t,enumerable:!0}):i,t)),me=t=>z(x({},"__esModule",{value:!0}),t);var we={};le(we,{buildTemplates:()=>ue});module.exports=me(we);var e=N(require("path"),1),m=N(require("fs"),1),H=N(require("esbuild"),1),M=require("./utils/merge-objects.js"),Q=require("./parse-template.js"),V=N(require("clean-css"),1),Y=require("./types.js"),Z=require("html-minifier"),D=require("./utils/flatten-object.js");const pe=new V.default,{writeFile:T,readFile:C,readdir:P,mkdir:j,cp:w,rm:fe}=m.default.promises;async function de(t,n){try{const i=e.default.resolve(t,"templates"),g=e.default.resolve(t,"widgets"),p=await P(i),y=[];let v=null;for(const c of p)if(c.endsWith(".json")){const d=e.default.join(i,c),F=await C(d,"utf-8"),f=JSON.parse(F);c==="base.json"?v=f:y.push({id:f.id,name:f.name||f.id,url:`/${f.id}.html`,template:c,widgetsData:f.widgetsData||{}})}const O=await P(g),h=[];for(const c of O)if(c.endsWith(".js")){const d=e.default.basename(c,".js");h.push({id:d,name:d.charAt(0).toUpperCase()+d.slice(1).replace(/-/g," "),file:c})}const W={meta:v,pages:y,widgets:h},$=e.default.join(n,"config.json");await T($,JSON.stringify(W,null,2)),console.log("Generated config.json with",y.length,"pages and",h.length,"widgets")}catch(i){throw console.error("Error generating admin config:",i),i}}b(de,"generateAdminConfig");const ue=b(async({publicDir:t,srcDir:n,prod:i=!0})=>{console.log("Building templates:",{publicDir:t,srcDir:n,prod:i});const g=process.cwd(),p=e.default.join(g,"_redirects"),y=e.default.join(t,"_redirects"),v=e.default.resolve(t,"scripts"),O=e.default.resolve(t,"stylesheets"),h=e.default.resolve(n,"assets"),W=e.default.resolve(n,"admin"),$=e.default.resolve(n,"widgets"),c=e.default.resolve(t,"admin","widgets"),d=e.default.resolve(t,"assets"),F=e.default.resolve(n,"data"),f=e.default.resolve(t,"data"),B=e.default.resolve(n,"assets","robots.txt"),ee=e.default.join(t,"robots.txt"),E=e.default.resolve(n,"assets","sitemap.xml");if(m.default.existsSync(t)&&await fe(t,{recursive:!0,force:!0}),await j(t,{recursive:!0}),await j(v,{recursive:!0}),await j(O,{recursive:!0}),m.default.existsSync(p)&&await w(p,y),m.default.existsSync(h)&&await w(h,d,{recursive:!0}),m.default.existsSync(F)&&await w(F,f,{recursive:!0}),m.default.existsSync(W)){const s=e.default.resolve(t,"admin");await j(s,{recursive:!0}),await w(W,s,{recursive:!0}),await de(n,s),i||(await j(c,{recursive:!0}),await w($,c,{recursive:!0}))}const te=await P(e.default.resolve(n,"templates")),J=e.default.resolve(n,"locales"),k={};if(m.default.existsSync(J)){const s=await P(J);for(const o of s)if(o.endsWith(".json")){const l=JSON.parse(await C(e.default.join(J,o),"utf8")),r=e.default.basename(o,".json");k[r]=(0,D.flattenObject)(l)}}const L=await Promise.all(te.filter(s=>s.endsWith(".json")).map(async s=>{const o=e.default.basename(s);return{dir:e.default.dirname(s),name:o.replace(".json",""),content:JSON.parse(await C(e.default.resolve(n,"templates",s),"utf8"))}})),_=L.reduce((s,o)=>({...s,[o.name]:o}),{}),A=L.filter(({content:s})=>s.type===Y.TemplateType.Page&&s.excluded!==!0),R=e.default.resolve(n,"components");let G={};if(m.default.existsSync(R)){const s=await P(R),o=await Promise.all(s.filter(l=>l.endsWith(".json")).map(async l=>{const r=JSON.parse(await C(e.default.join(R,l),"utf8"));return[r.id,r]}));G=Object.fromEntries(o)}const I=b(async s=>{let o=typeof s=="string"?s:"";s&&typeof s=="object"&&"src"in s&&(o=String(s.src)),!o.startsWith("http")&&o.endsWith(".js")&&await H.default.build({entryPoints:[e.default.resolve(n,"scripts",o)],minify:!0,outfile:e.default.join(v,o)})},"handleScript"),K=b(async s=>{let o=typeof s=="string"?s:"";if(s&&typeof s=="object"&&"href"in s&&(o=String(s.href)),!o.startsWith("http")&&o.endsWith(".css")){const l=await C(e.default.resolve(n,"stylesheets",o),"utf8");await T(e.default.join(O,o),pe.minify(l).styles,"utf-8")}},"handleStylesheet"),X=[];for(const{name:s,dir:o,content:l}of A){let r=l;if(r.extends){const a=_[r.extends];r=(0,M.mergeObjects)(a.content,r)}const q=l.domain||"",U=l.route||"";if(U){const a=q?`${q.replace(/\/$/,"")}/${U.replace(/^\//,"")}`:`/${U.replace(/^\//,"")}`;X.push(a)}await j(e.default.join(t,o),{recursive:!0});for(let a=0;a<(r.scripts?.length??0);a++)await I(r.scripts[a]);for(let a=0;a<(r.stylesheets?.length??0);a++)await K(r.stylesheets[a]);const se=await(0,Q.parseTemplate)(r,{prod:i,components:G,locales:k,fetchTemplate:a=>_[a].content,fetchWidget:async a=>{const S=(await import(e.default.resolve($,`${a}.js`))).default;for(let u=0;u<(S.scripts?.length??0);u++)await I(S.scripts[u]);for(let u=0;u<(S.stylesheets?.length??0);u++)await K(S.stylesheets[u]);return S}}),ne=(0,Z.minify)(se,{collapseWhitespace:!0,removeComments:!0,minifyCSS:!0,minifyJS:!0});await T(e.default.join(t,o,`${s}.html`),ne)}if(m.default.existsSync(B))await w(B,e.default.join(t,"robots.txt"));else{const o=`User-agent: *
1
+ "use strict";var ye=Object.create;var j=Object.defineProperty;var he=Object.getOwnPropertyDescriptor;var Se=Object.getOwnPropertyNames;var je=Object.getPrototypeOf,xe=Object.prototype.hasOwnProperty;var x=(s,n)=>j(s,"name",{value:n,configurable:!0});var be=(s,n)=>{for(var a in n)j(s,a,{get:n[a],enumerable:!0})},te=(s,n,a,g)=>{if(n&&typeof n=="object"||typeof n=="function")for(let i of Se(n))!xe.call(s,i)&&i!==a&&j(s,i,{get:()=>n[i],enumerable:!(g=he(n,i))||g.enumerable});return s};var W=(s,n,a)=>(a=s!=null?ye(je(s)):{},te(n||!s||!s.__esModule?j(a,"default",{value:s,enumerable:!0}):a,s)),Ce=s=>te(j({},"__esModule",{value:!0}),s);var We={};be(We,{buildTemplates:()=>Pe});module.exports=Ce(We);var e=W(require("path"),1),l=W(require("fs"),1),ne=require("module"),oe=W(require("esbuild"),1),ie=require("./utils/merge-objects.js"),ae=require("./parse-template.js"),re=W(require("clean-css"),1),le=require("./types.js"),ce=require("html-minifier"),me=require("./utils/flatten-object.js");const Te=new re.default,{writeFile:b,readFile:C,readdir:T,mkdir:y,cp:p,rm:se}=l.default.promises;async function Oe(s,n){try{const a=e.default.resolve(s,"templates"),g=e.default.resolve(s,"widgets"),i=await T(a),c=[];let O=null;for(const f of i)if(f.endsWith(".json")){const w=e.default.join(a,f),P=await C(w,"utf-8"),u=JSON.parse(P);f==="base.json"?O=u:c.push({id:u.id,name:u.name||u.id,url:`/${u.id}.html`,template:f,widgetsData:u.widgetsData||{}})}const $=l.default.existsSync(g)?await T(g):[],h=[];for(const f of $)if(f.endsWith(".js")){const w=e.default.basename(f,".js");h.push({id:w,name:w.charAt(0).toUpperCase()+w.slice(1).replace(/-/g," "),file:f})}const E={meta:O,pages:c,widgets:h},F=e.default.join(n,"config.json");await b(F,JSON.stringify(E,null,2)),console.log("Generated config.json with",c.length,"pages and",h.length,"widgets")}catch(a){throw console.error("Error generating admin config:",a),a}}x(Oe,"generateAdminConfig");const Pe=x(async({publicDir:s,srcDir:n,prod:a=!0})=>{console.log("Building templates:",{publicDir:s,srcDir:n,prod:a});const g=(0,ne.createRequire)(e.default.resolve(process.cwd(),"package.json"));let i="";try{i=e.default.dirname(g.resolve("@beforesemicolon/site-builder/package.json"))}catch{i=""}const c=typeof __dirname=="string"?__dirname:e.default.dirname(process.argv[1]||process.cwd()),O=[i?e.default.resolve(i,"scaffolds","admin"):"",e.default.resolve(c,"..","scaffolds","admin"),e.default.resolve(c,"..","..","scaffolds","admin")],$=[i?e.default.resolve(i,"netlify"):"",e.default.resolve(c,"..","netlify"),e.default.resolve(c,"..","..","netlify")],h=[i?e.default.resolve(i,"netlify.toml"):"",e.default.resolve(c,"..","netlify.toml"),e.default.resolve(c,"..","..","netlify.toml")],E=[i?e.default.resolve(i,"scaffolds","_redirects"):"",e.default.resolve(c,"..","scaffolds","_redirects"),e.default.resolve(c,"..","..","scaffolds","_redirects")],F=[i?e.default.resolve(i,"scaffolds",".env.example"):"",e.default.resolve(c,"..","scaffolds",".env.example"),e.default.resolve(c,"..","..","scaffolds",".env.example")],f=O.find(t=>t&&l.default.existsSync(t)),w=$.find(t=>t&&l.default.existsSync(t)),P=h.find(t=>t&&l.default.existsSync(t)),u=E.find(t=>t&&l.default.existsSync(t)),A=F.find(t=>t&&l.default.existsSync(t));if(!f)throw new Error("Could not find bundled admin scaffold directory");if(!w)throw new Error("Could not find bundled netlify scaffold directory");if(!P)throw new Error("Could not find bundled netlify.toml file");if(!u)throw new Error("Could not find bundled _redirects file");if(!A)throw new Error("Could not find bundled .env.example file");const fe=e.default.join(s,"_redirects"),U=e.default.resolve(s,"scripts"),B=e.default.resolve(s,"stylesheets"),q=e.default.resolve(n,"assets"),L=e.default.resolve(n,"widgets"),G=e.default.resolve(s,"admin","widgets"),de=e.default.resolve(s,"assets"),I=e.default.resolve(n,"data"),pe=e.default.resolve(s,"data"),K=e.default.resolve(n,"assets","robots.txt"),ue=e.default.join(s,"robots.txt"),X=e.default.resolve(n,"assets","sitemap.xml");l.default.existsSync(s)&&await se(s,{recursive:!0,force:!0}),await y(s,{recursive:!0}),await y(U,{recursive:!0}),await y(B,{recursive:!0}),await p(u,fe),l.default.existsSync(q)&&await p(q,de,{recursive:!0}),l.default.existsSync(I)&&await p(I,pe,{recursive:!0});const z=e.default.resolve(n,".netlify");await se(z,{recursive:!0,force:!0}),await p(w,z,{recursive:!0}),await p(P,e.default.resolve(n,"netlify.toml")),await p(A,e.default.resolve(n,".env.example"));const _=e.default.resolve(s,"admin");await y(_,{recursive:!0}),await p(f,_,{recursive:!0}),await Oe(n,_),a||(await y(G,{recursive:!0}),await p(L,G,{recursive:!0}));const we=await T(e.default.resolve(n,"templates")),N=e.default.resolve(n,"locales"),H={};if(l.default.existsSync(N)){const t=await T(N);for(const o of t)if(o.endsWith(".json")){const d=JSON.parse(await C(e.default.join(N,o),"utf8")),m=e.default.basename(o,".json");H[m]=(0,me.flattenObject)(d)}}const M=await Promise.all(we.filter(t=>t.endsWith(".json")).map(async t=>{const o=e.default.basename(t);return{dir:e.default.dirname(t),name:o.replace(".json",""),content:JSON.parse(await C(e.default.resolve(n,"templates",t),"utf8"))}})),Q=M.reduce((t,o)=>({...t,[o.name]:o}),{}),J=M.filter(({content:t})=>t.type===le.TemplateType.Page&&t.excluded!==!0),R=e.default.resolve(n,"components");let V={};if(l.default.existsSync(R)){const t=await T(R),o=await Promise.all(t.filter(d=>d.endsWith(".json")).map(async d=>{const m=JSON.parse(await C(e.default.join(R,d),"utf8"));return[m.id,m]}));V=Object.fromEntries(o)}const Y=x(async t=>{let o=typeof t=="string"?t:"";t&&typeof t=="object"&&"src"in t&&(o=String(t.src)),!o.startsWith("http")&&o.endsWith(".js")&&await oe.default.build({entryPoints:[e.default.resolve(n,"scripts",o)],minify:!0,outfile:e.default.join(U,o)})},"handleScript"),Z=x(async t=>{let o=typeof t=="string"?t:"";if(t&&typeof t=="object"&&"href"in t&&(o=String(t.href)),!o.startsWith("http")&&o.endsWith(".css")){const d=await C(e.default.resolve(n,"stylesheets",o),"utf8");await b(e.default.join(B,o),Te.minify(d).styles,"utf-8")}},"handleStylesheet"),D=[];for(const{name:t,dir:o,content:d}of J){let m=d;if(m.extends){const r=Q[m.extends];m=(0,ie.mergeObjects)(r.content,m)}const ee=d.domain||"",k=d.route||"";if(k){const r=ee?`${ee.replace(/\/$/,"")}/${k.replace(/^\//,"")}`:`/${k.replace(/^\//,"")}`;D.push(r)}await y(e.default.join(s,o),{recursive:!0});for(let r=0;r<(m.scripts?.length??0);r++)await Y(m.scripts[r]);for(let r=0;r<(m.stylesheets?.length??0);r++)await Z(m.stylesheets[r]);const ge=await(0,ae.parseTemplate)(m,{prod:a,components:V,locales:H,fetchTemplate:r=>Q[r].content,fetchWidget:async r=>{const S=(await import(e.default.resolve(L,`${r}.js`))).default;for(let v=0;v<(S.scripts?.length??0);v++)await Y(S.scripts[v]);for(let v=0;v<(S.stylesheets?.length??0);v++)await Z(S.stylesheets[v]);return S}}),ve=(0,ce.minify)(ge,{collapseWhitespace:!0,removeComments:!0,minifyCSS:!0,minifyJS:!0});await b(e.default.join(s,o,`${t}.html`),ve)}if(l.default.existsSync(K))await p(K,e.default.join(s,"robots.txt"));else{const o=`User-agent: *
2
2
  Disallow:
3
- Sitemap: ${A[0]?.content.domain?`${A[0].content.domain.replace(/\/$/,"")}/sitemap.xml`:"/sitemap.xml"}
4
- `;await T(ee,o,"utf-8")}if(m.default.existsSync(E))await w(E,e.default.join(t,"sitemap.xml"));else{const s=`<?xml version="1.0" encoding="UTF-8"?>
3
+ Sitemap: ${J[0]?.content.domain?`${J[0].content.domain.replace(/\/$/,"")}/sitemap.xml`:"/sitemap.xml"}
4
+ `;await b(ue,o,"utf-8")}if(l.default.existsSync(X))await p(X,e.default.join(s,"sitemap.xml"));else{const t=`<?xml version="1.0" encoding="UTF-8"?>
5
5
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
6
- `+X.map(o=>` <url>
6
+ `+D.map(o=>` <url>
7
7
  <loc>${o}</loc>
8
8
  </url>`).join(`
9
9
  `)+`
10
10
  </urlset>
11
- `;await T(e.default.join(t,"sitemap.xml"),s,"utf-8")}},"buildTemplates");0&&(module.exports={buildTemplates});
11
+ `;await b(e.default.join(s,"sitemap.xml"),t,"utf-8")}},"buildTemplates");0&&(module.exports={buildTemplates});
@@ -1,11 +1,11 @@
1
- var M=Object.defineProperty;var v=(n,o)=>M(n,"name",{value:o,configurable:!0});import e from"path";import l from"fs";import Q from"esbuild";import{mergeObjects as V}from"./utils/merge-objects.js";import{parseTemplate as Y}from"./parse-template.js";import Z from"clean-css";import{TemplateType as D}from"./types.js";import{minify as ee}from"html-minifier";import{flattenObject as te}from"./utils/flatten-object.js";const se=new Z,{writeFile:S,readFile:x,readdir:b,mkdir:g,cp:u,rm:ne}=l.promises;async function oe(n,o){try{const m=e.resolve(n,"templates"),$=e.resolve(n,"widgets"),T=await b(m),h=[];let j=null;for(const r of T)if(r.endsWith(".json")){const f=e.join(m,r),W=await x(f,"utf-8"),p=JSON.parse(W);r==="base.json"?j=p:h.push({id:p.id,name:p.name||p.id,url:`/${p.id}.html`,template:r,widgetsData:p.widgetsData||{}})}const C=await b($),w=[];for(const r of C)if(r.endsWith(".js")){const f=e.basename(r,".js");w.push({id:f,name:f.charAt(0).toUpperCase()+f.slice(1).replace(/-/g," "),file:r})}const P={meta:j,pages:h,widgets:w},O=e.join(o,"config.json");await S(O,JSON.stringify(P,null,2)),console.log("Generated config.json with",h.length,"pages and",w.length,"widgets")}catch(m){throw console.error("Error generating admin config:",m),m}}v(oe,"generateAdminConfig");const ye=v(async({publicDir:n,srcDir:o,prod:m=!0})=>{console.log("Building templates:",{publicDir:n,srcDir:o,prod:m});const $=process.cwd(),T=e.join($,"_redirects"),h=e.join(n,"_redirects"),j=e.resolve(n,"scripts"),C=e.resolve(n,"stylesheets"),w=e.resolve(o,"assets"),P=e.resolve(o,"admin"),O=e.resolve(o,"widgets"),r=e.resolve(n,"admin","widgets"),f=e.resolve(n,"assets"),W=e.resolve(o,"data"),p=e.resolve(n,"data"),R=e.resolve(o,"assets","robots.txt"),X=e.join(n,"robots.txt"),U=e.resolve(o,"assets","sitemap.xml");if(l.existsSync(n)&&await ne(n,{recursive:!0,force:!0}),await g(n,{recursive:!0}),await g(j,{recursive:!0}),await g(C,{recursive:!0}),l.existsSync(T)&&await u(T,h),l.existsSync(w)&&await u(w,f,{recursive:!0}),l.existsSync(W)&&await u(W,p,{recursive:!0}),l.existsSync(P)){const t=e.resolve(n,"admin");await g(t,{recursive:!0}),await u(P,t,{recursive:!0}),await oe(o,t),m||(await g(r,{recursive:!0}),await u(O,r,{recursive:!0}))}const q=await b(e.resolve(o,"templates")),F=e.resolve(o,"locales"),B={};if(l.existsSync(F)){const t=await b(F);for(const s of t)if(s.endsWith(".json")){const c=JSON.parse(await x(e.join(F,s),"utf8")),a=e.basename(s,".json");B[a]=te(c)}}const E=await Promise.all(q.filter(t=>t.endsWith(".json")).map(async t=>{const s=e.basename(t);return{dir:e.dirname(t),name:s.replace(".json",""),content:JSON.parse(await x(e.resolve(o,"templates",t),"utf8"))}})),k=E.reduce((t,s)=>({...t,[s.name]:s}),{}),N=E.filter(({content:t})=>t.type===D.Page&&t.excluded!==!0),J=e.resolve(o,"components");let L={};if(l.existsSync(J)){const t=await b(J),s=await Promise.all(t.filter(c=>c.endsWith(".json")).map(async c=>{const a=JSON.parse(await x(e.join(J,c),"utf8"));return[a.id,a]}));L=Object.fromEntries(s)}const _=v(async t=>{let s=typeof t=="string"?t:"";t&&typeof t=="object"&&"src"in t&&(s=String(t.src)),!s.startsWith("http")&&s.endsWith(".js")&&await Q.build({entryPoints:[e.resolve(o,"scripts",s)],minify:!0,outfile:e.join(j,s)})},"handleScript"),G=v(async t=>{let s=typeof t=="string"?t:"";if(t&&typeof t=="object"&&"href"in t&&(s=String(t.href)),!s.startsWith("http")&&s.endsWith(".css")){const c=await x(e.resolve(o,"stylesheets",s),"utf8");await S(e.join(C,s),se.minify(c).styles,"utf-8")}},"handleStylesheet"),I=[];for(const{name:t,dir:s,content:c}of N){let a=c;if(a.extends){const i=k[a.extends];a=V(i.content,a)}const K=c.domain||"",A=c.route||"";if(A){const i=K?`${K.replace(/\/$/,"")}/${A.replace(/^\//,"")}`:`/${A.replace(/^\//,"")}`;I.push(i)}await g(e.join(n,s),{recursive:!0});for(let i=0;i<(a.scripts?.length??0);i++)await _(a.scripts[i]);for(let i=0;i<(a.stylesheets?.length??0);i++)await G(a.stylesheets[i]);const z=await Y(a,{prod:m,components:L,locales:B,fetchTemplate:i=>k[i].content,fetchWidget:async i=>{const y=(await import(e.resolve(O,`${i}.js`))).default;for(let d=0;d<(y.scripts?.length??0);d++)await _(y.scripts[d]);for(let d=0;d<(y.stylesheets?.length??0);d++)await G(y.stylesheets[d]);return y}}),H=ee(z,{collapseWhitespace:!0,removeComments:!0,minifyCSS:!0,minifyJS:!0});await S(e.join(n,s,`${t}.html`),H)}if(l.existsSync(R))await u(R,e.join(n,"robots.txt"));else{const s=`User-agent: *
1
+ var re=Object.defineProperty;var S=(n,o)=>re(n,"name",{value:o,configurable:!0});import e from"path";import c from"fs";import{createRequire as le}from"module";import ce from"esbuild";import{mergeObjects as me}from"./utils/merge-objects.js";import{parseTemplate as fe}from"./parse-template.js";import de from"clean-css";import{TemplateType as pe}from"./types.js";import{minify as ue}from"html-minifier";import{flattenObject as we}from"./utils/flatten-object.js";const ge=new de,{writeFile:j,readFile:x,readdir:b,mkdir:v,cp:d,rm:D}=c.promises;async function ve(n,o){try{const p=e.resolve(n,"templates"),C=e.resolve(n,"widgets"),a=await b(p),r=[];let T=null;for(const m of a)if(m.endsWith(".json")){const w=e.join(p,m),O=await x(w,"utf-8"),u=JSON.parse(O);m==="base.json"?T=u:r.push({id:u.id,name:u.name||u.id,url:`/${u.id}.html`,template:m,widgetsData:u.widgetsData||{}})}const P=c.existsSync(C)?await b(C):[],y=[];for(const m of P)if(m.endsWith(".js")){const w=e.basename(m,".js");y.push({id:w,name:w.charAt(0).toUpperCase()+w.slice(1).replace(/-/g," "),file:m})}const W={meta:T,pages:r,widgets:y},$=e.join(o,"config.json");await j($,JSON.stringify(W,null,2)),console.log("Generated config.json with",r.length,"pages and",y.length,"widgets")}catch(p){throw console.error("Error generating admin config:",p),p}}S(ve,"generateAdminConfig");const Ne=S(async({publicDir:n,srcDir:o,prod:p=!0})=>{console.log("Building templates:",{publicDir:n,srcDir:o,prod:p});const C=le(e.resolve(process.cwd(),"package.json"));let a="";try{a=e.dirname(C.resolve("@beforesemicolon/site-builder/package.json"))}catch{a=""}const r=typeof __dirname=="string"?__dirname:e.dirname(process.argv[1]||process.cwd()),T=[a?e.resolve(a,"scaffolds","admin"):"",e.resolve(r,"..","scaffolds","admin"),e.resolve(r,"..","..","scaffolds","admin")],P=[a?e.resolve(a,"netlify"):"",e.resolve(r,"..","netlify"),e.resolve(r,"..","..","netlify")],y=[a?e.resolve(a,"netlify.toml"):"",e.resolve(r,"..","netlify.toml"),e.resolve(r,"..","..","netlify.toml")],W=[a?e.resolve(a,"scaffolds","_redirects"):"",e.resolve(r,"..","scaffolds","_redirects"),e.resolve(r,"..","..","scaffolds","_redirects")],$=[a?e.resolve(a,"scaffolds",".env.example"):"",e.resolve(r,"..","scaffolds",".env.example"),e.resolve(r,"..","..","scaffolds",".env.example")],m=T.find(t=>t&&c.existsSync(t)),w=P.find(t=>t&&c.existsSync(t)),O=y.find(t=>t&&c.existsSync(t)),u=W.find(t=>t&&c.existsSync(t)),R=$.find(t=>t&&c.existsSync(t));if(!m)throw new Error("Could not find bundled admin scaffold directory");if(!w)throw new Error("Could not find bundled netlify scaffold directory");if(!O)throw new Error("Could not find bundled netlify.toml file");if(!u)throw new Error("Could not find bundled _redirects file");if(!R)throw new Error("Could not find bundled .env.example file");const ee=e.join(n,"_redirects"),k=e.resolve(n,"scripts"),A=e.resolve(n,"stylesheets"),U=e.resolve(o,"assets"),B=e.resolve(o,"widgets"),q=e.resolve(n,"admin","widgets"),te=e.resolve(n,"assets"),L=e.resolve(o,"data"),se=e.resolve(n,"data"),G=e.resolve(o,"assets","robots.txt"),ne=e.join(n,"robots.txt"),I=e.resolve(o,"assets","sitemap.xml");c.existsSync(n)&&await D(n,{recursive:!0,force:!0}),await v(n,{recursive:!0}),await v(k,{recursive:!0}),await v(A,{recursive:!0}),await d(u,ee),c.existsSync(U)&&await d(U,te,{recursive:!0}),c.existsSync(L)&&await d(L,se,{recursive:!0});const K=e.resolve(o,".netlify");await D(K,{recursive:!0,force:!0}),await d(w,K,{recursive:!0}),await d(O,e.resolve(o,"netlify.toml")),await d(R,e.resolve(o,".env.example"));const E=e.resolve(n,"admin");await v(E,{recursive:!0}),await d(m,E,{recursive:!0}),await ve(o,E),p||(await v(q,{recursive:!0}),await d(B,q,{recursive:!0}));const oe=await b(e.resolve(o,"templates")),F=e.resolve(o,"locales"),X={};if(c.existsSync(F)){const t=await b(F);for(const s of t)if(s.endsWith(".json")){const f=JSON.parse(await x(e.join(F,s),"utf8")),l=e.basename(s,".json");X[l]=we(f)}}const z=await Promise.all(oe.filter(t=>t.endsWith(".json")).map(async t=>{const s=e.basename(t);return{dir:e.dirname(t),name:s.replace(".json",""),content:JSON.parse(await x(e.resolve(o,"templates",t),"utf8"))}})),H=z.reduce((t,s)=>({...t,[s.name]:s}),{}),_=z.filter(({content:t})=>t.type===pe.Page&&t.excluded!==!0),N=e.resolve(o,"components");let M={};if(c.existsSync(N)){const t=await b(N),s=await Promise.all(t.filter(f=>f.endsWith(".json")).map(async f=>{const l=JSON.parse(await x(e.join(N,f),"utf8"));return[l.id,l]}));M=Object.fromEntries(s)}const Q=S(async t=>{let s=typeof t=="string"?t:"";t&&typeof t=="object"&&"src"in t&&(s=String(t.src)),!s.startsWith("http")&&s.endsWith(".js")&&await ce.build({entryPoints:[e.resolve(o,"scripts",s)],minify:!0,outfile:e.join(k,s)})},"handleScript"),V=S(async t=>{let s=typeof t=="string"?t:"";if(t&&typeof t=="object"&&"href"in t&&(s=String(t.href)),!s.startsWith("http")&&s.endsWith(".css")){const f=await x(e.resolve(o,"stylesheets",s),"utf8");await j(e.join(A,s),ge.minify(f).styles,"utf-8")}},"handleStylesheet"),Y=[];for(const{name:t,dir:s,content:f}of _){let l=f;if(l.extends){const i=H[l.extends];l=me(i.content,l)}const Z=f.domain||"",J=f.route||"";if(J){const i=Z?`${Z.replace(/\/$/,"")}/${J.replace(/^\//,"")}`:`/${J.replace(/^\//,"")}`;Y.push(i)}await v(e.join(n,s),{recursive:!0});for(let i=0;i<(l.scripts?.length??0);i++)await Q(l.scripts[i]);for(let i=0;i<(l.stylesheets?.length??0);i++)await V(l.stylesheets[i]);const ie=await fe(l,{prod:p,components:M,locales:X,fetchTemplate:i=>H[i].content,fetchWidget:async i=>{const h=(await import(e.resolve(B,`${i}.js`))).default;for(let g=0;g<(h.scripts?.length??0);g++)await Q(h.scripts[g]);for(let g=0;g<(h.stylesheets?.length??0);g++)await V(h.stylesheets[g]);return h}}),ae=ue(ie,{collapseWhitespace:!0,removeComments:!0,minifyCSS:!0,minifyJS:!0});await j(e.join(n,s,`${t}.html`),ae)}if(c.existsSync(G))await d(G,e.join(n,"robots.txt"));else{const s=`User-agent: *
2
2
  Disallow:
3
- Sitemap: ${N[0]?.content.domain?`${N[0].content.domain.replace(/\/$/,"")}/sitemap.xml`:"/sitemap.xml"}
4
- `;await S(X,s,"utf-8")}if(l.existsSync(U))await u(U,e.join(n,"sitemap.xml"));else{const t=`<?xml version="1.0" encoding="UTF-8"?>
3
+ Sitemap: ${_[0]?.content.domain?`${_[0].content.domain.replace(/\/$/,"")}/sitemap.xml`:"/sitemap.xml"}
4
+ `;await j(ne,s,"utf-8")}if(c.existsSync(I))await d(I,e.join(n,"sitemap.xml"));else{const t=`<?xml version="1.0" encoding="UTF-8"?>
5
5
  <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
6
- `+I.map(s=>` <url>
6
+ `+Y.map(s=>` <url>
7
7
  <loc>${s}</loc>
8
8
  </url>`).join(`
9
9
  `)+`
10
10
  </urlset>
11
- `;await S(e.join(n,"sitemap.xml"),t,"utf-8")}},"buildTemplates");export{ye as buildTemplates};
11
+ `;await j(e.join(n,"sitemap.xml"),t,"utf-8")}},"buildTemplates");export{Ne as buildTemplates};
@@ -0,0 +1,183 @@
1
+ import jwt from 'jsonwebtoken'
2
+ import jwksClient from 'jwks-rsa'
3
+
4
+ // In-memory cache for admin emails
5
+ let adminEmailCache = null
6
+ let cacheTimestamp = null
7
+ const CACHE_TTL = 5 * 60 * 1000 // 5 minutes
8
+
9
+ // JWKS client for Auth0 token verification
10
+ const client = jwksClient({
11
+ jwksUri: `https://${process.env.AUTH0_DOMAIN}/.well-known/jwks.json`,
12
+ cache: true,
13
+ cacheMaxAge: 86400000, // 24 hours
14
+ })
15
+
16
+ /**
17
+ * Get signing key for JWT verification
18
+ * @param {Object} header - JWT header
19
+ * @param {Function} callback - Callback function
20
+ */
21
+ function getKey(header, callback) {
22
+ client.getSigningKey(header.kid, (err, key) => {
23
+ if (err) {
24
+ callback(err)
25
+ return
26
+ }
27
+ const signingKey = key.publicKey || key.rsaPublicKey
28
+ callback(null, signingKey)
29
+ })
30
+ }
31
+
32
+ /**
33
+ * Verify Auth0 JWT token
34
+ * @param {string} token - JWT token from Authorization header
35
+ * @returns {Promise<{email: string, sub: string}>}
36
+ */
37
+ export async function verifyAuth0Token(token) {
38
+ return new Promise((resolve, reject) => {
39
+ jwt.verify(
40
+ token,
41
+ getKey,
42
+ {
43
+ audience: process.env.AUTH0_AUDIENCE,
44
+ issuer: `https://${process.env.AUTH0_DOMAIN}/`,
45
+ algorithms: ['RS256'],
46
+ },
47
+ (err, decoded) => {
48
+ if (err) {
49
+ reject(err)
50
+ } else {
51
+ resolve(decoded)
52
+ }
53
+ }
54
+ )
55
+ })
56
+ }
57
+
58
+ /**
59
+ * Fetch user list from Netlify Identity
60
+ * Uses Netlify API endpoint
61
+ * @returns {Promise<Array<{email: string, id: string}>>}
62
+ */
63
+ function getAdminEmailList() {
64
+ const raw = process.env.ADMIN_EMAILS
65
+ if (!raw) {
66
+ return []
67
+ }
68
+ return raw
69
+ .split(',')
70
+ .map((entry) => entry.trim().toLowerCase())
71
+ .filter(Boolean)
72
+ .map((email) => ({ email, id: email }))
73
+ }
74
+
75
+ /**
76
+ * Get cached or fresh admin email list
77
+ * Cache expires after 5 minutes
78
+ * @returns {Promise<Array<{email: string, id: string}>>}
79
+ */
80
+ export async function getUserList() {
81
+ const now = Date.now()
82
+
83
+ // Return cached data if valid
84
+ if (adminEmailCache && cacheTimestamp && now - cacheTimestamp < CACHE_TTL) {
85
+ return adminEmailCache
86
+ }
87
+
88
+ const list = getAdminEmailList()
89
+ cacheTimestamp = now
90
+ adminEmailCache = list
91
+
92
+ if (!list.length) {
93
+ console.error('ADMIN_EMAILS is not configured or empty')
94
+ }
95
+
96
+ return list
97
+ }
98
+
99
+ /**
100
+ * Check if email is in authorized list
101
+ * @param {string} email - Email to check
102
+ * @returns {Promise<boolean>}
103
+ */
104
+ export async function isEmailAuthorized(email) {
105
+ if (!email) {
106
+ return false
107
+ }
108
+
109
+ const normalizedEmail = email.toLowerCase()
110
+ const userList = await getUserList()
111
+
112
+ return userList.some((user) => user.email === normalizedEmail)
113
+ }
114
+
115
+ /**
116
+ * Middleware wrapper for protecting functions
117
+ * @param {Function} handler - Original function handler
118
+ * @returns {Function} - Wrapped handler with authorization
119
+ */
120
+ export function withAuth(handler) {
121
+ return async (event, context) => {
122
+ // Skip auth in local development
123
+ const isLocalDev = process.env.CONTEXT === 'dev' || !process.env.CONTEXT
124
+
125
+ if (isLocalDev) {
126
+ return handler(event, context)
127
+ }
128
+
129
+ // Extract token from Authorization header
130
+ const authHeader =
131
+ event.headers.authorization || event.headers.Authorization
132
+
133
+ if (!authHeader || !authHeader.startsWith('Bearer ')) {
134
+ return {
135
+ statusCode: 401,
136
+ body: JSON.stringify({
137
+ error: 'Unauthorized - No token provided',
138
+ }),
139
+ }
140
+ }
141
+
142
+ const token = authHeader.substring(7) // Remove 'Bearer '
143
+
144
+ try {
145
+ // Verify Auth0 token
146
+ const decoded = await verifyAuth0Token(token)
147
+ const email = decoded.email
148
+
149
+ if (!email) {
150
+ return {
151
+ statusCode: 401,
152
+ body: JSON.stringify({
153
+ error: 'Unauthorized - No email in token',
154
+ }),
155
+ }
156
+ }
157
+
158
+ // Check if email is authorized
159
+ const authorized = await isEmailAuthorized(email)
160
+
161
+ if (!authorized) {
162
+ return {
163
+ statusCode: 403,
164
+ body: JSON.stringify({
165
+ error: 'Forbidden - Email not authorized',
166
+ }),
167
+ }
168
+ }
169
+
170
+ // Add user info to context for logging
171
+ context.authorizedUser = { email }
172
+
173
+ // Call original handler
174
+ return handler(event, context)
175
+ } catch (error) {
176
+ console.error('Authorization error:', error)
177
+ return {
178
+ statusCode: 401,
179
+ body: JSON.stringify({ error: 'Unauthorized - Invalid token' }),
180
+ }
181
+ }
182
+ }
183
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Netlify function to return Auth0 config without exposing values in repo.
3
+ */
4
+
5
+ const ALLOWED_ORIGINS = [
6
+ 'https://eliteflooringandtile.com',
7
+ 'https://www.eliteflooringandtile.com',
8
+ process.env.URL,
9
+ process.env.DEPLOY_PRIME_URL,
10
+ ]
11
+
12
+ if (process.env.CONTEXT === 'dev' || !process.env.CONTEXT) {
13
+ ALLOWED_ORIGINS.push('http://localhost:3000')
14
+ ALLOWED_ORIGINS.push('http://localhost:8888')
15
+ ALLOWED_ORIGINS.push('http://127.0.0.1:3000')
16
+ ALLOWED_ORIGINS.push('http://127.0.0.1:8888')
17
+ }
18
+
19
+ function checkOrigin(origin) {
20
+ if (!origin) return false
21
+ return ALLOWED_ORIGINS.some(
22
+ (allowed) => allowed && origin.startsWith(allowed)
23
+ )
24
+ }
25
+
26
+ export async function handler(event) {
27
+ const origin = event.headers.origin || event.headers.Origin
28
+
29
+ const corsHeaders = {
30
+ 'Access-Control-Allow-Origin': checkOrigin(origin) ? origin : 'null',
31
+ 'Access-Control-Allow-Headers': 'Content-Type',
32
+ 'Access-Control-Allow-Methods': 'GET, OPTIONS',
33
+ 'Access-Control-Allow-Credentials': 'true',
34
+ }
35
+
36
+ if (event.httpMethod === 'OPTIONS') {
37
+ return { statusCode: 200, headers: corsHeaders, body: '' }
38
+ }
39
+
40
+ if (event.httpMethod !== 'GET') {
41
+ return {
42
+ statusCode: 405,
43
+ headers: corsHeaders,
44
+ body: JSON.stringify({ error: 'Method not allowed' }),
45
+ }
46
+ }
47
+
48
+ if (!checkOrigin(origin)) {
49
+ return {
50
+ statusCode: 403,
51
+ headers: corsHeaders,
52
+ body: JSON.stringify({ error: 'Forbidden - Invalid origin' }),
53
+ }
54
+ }
55
+
56
+ const domain = process.env.AUTH0_DOMAIN
57
+ const clientId = process.env.AUTH0_CLIENT_ID
58
+ const audience = process.env.AUTH0_AUDIENCE
59
+
60
+ if (!domain || !clientId) {
61
+ return {
62
+ statusCode: 500,
63
+ headers: corsHeaders,
64
+ body: JSON.stringify({ error: 'Auth0 configuration not set' }),
65
+ }
66
+ }
67
+
68
+ return {
69
+ statusCode: 200,
70
+ headers: corsHeaders,
71
+ body: JSON.stringify({
72
+ domain,
73
+ clientId,
74
+ audience,
75
+ }),
76
+ }
77
+ }
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Netlify Function to check build status after auto-build from commit
3
+ * Requires NETLIFY_AUTH_TOKEN and NETLIFY_SITE_ID environment variables
4
+ */
5
+
6
+ const NETLIFY_AUTH_TOKEN = process.env.NETLIFY_AUTH_TOKEN
7
+ const NETLIFY_SITE_ID = process.env.NETLIFY_SITE_ID
8
+ const API_BASE = 'https://api.netlify.com/api/v1'
9
+
10
+ // Allowed origins (your domain)
11
+ const ALLOWED_ORIGINS = [
12
+ 'https://eliteflooringandtile.com',
13
+ 'https://www.eliteflooringandtile.com',
14
+ process.env.URL, // Netlify deploy URL
15
+ process.env.DEPLOY_PRIME_URL, // Netlify branch deploy URL
16
+ ]
17
+
18
+ // Add localhost for development
19
+ if (process.env.CONTEXT === 'dev' || !process.env.CONTEXT) {
20
+ ALLOWED_ORIGINS.push('http://localhost:3000')
21
+ ALLOWED_ORIGINS.push('http://localhost:8888')
22
+ }
23
+
24
+ /**
25
+ * Check CORS and origin
26
+ */
27
+ function checkOrigin(origin) {
28
+ if (!origin) return false
29
+ return ALLOWED_ORIGINS.some(
30
+ (allowed) => allowed && origin.startsWith(allowed)
31
+ )
32
+ }
33
+
34
+ /**
35
+ * Get latest build for the site
36
+ * Used after commit to find the auto-triggered build
37
+ */
38
+ async function getLatestBuild() {
39
+ const url = `${API_BASE}/sites/${NETLIFY_SITE_ID}/builds?per_page=1`
40
+
41
+ const response = await fetch(url, {
42
+ headers: {
43
+ Authorization: `Bearer ${NETLIFY_AUTH_TOKEN}`,
44
+ },
45
+ })
46
+
47
+ if (!response.ok) {
48
+ const error = await response.text()
49
+ throw new Error(
50
+ `Failed to get latest build: ${response.status} ${error}`
51
+ )
52
+ }
53
+
54
+ const builds = await response.json()
55
+
56
+ if (builds.length === 0) {
57
+ throw new Error('No builds found for this site')
58
+ }
59
+
60
+ const latestBuild = builds[0]
61
+
62
+ return {
63
+ buildId: latestBuild.id,
64
+ status: latestBuild.state,
65
+ createdAt: latestBuild.created_at,
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Check build status
71
+ */
72
+ async function checkBuildStatus(buildId) {
73
+ const url = `${API_BASE}/builds/${buildId}`
74
+
75
+ const response = await fetch(url, {
76
+ headers: {
77
+ Authorization: `Bearer ${NETLIFY_AUTH_TOKEN}`,
78
+ },
79
+ })
80
+
81
+ if (!response.ok) {
82
+ const error = await response.text()
83
+ throw new Error(
84
+ `Failed to check build status: ${response.status} ${error}`
85
+ )
86
+ }
87
+
88
+ const data = await response.json()
89
+
90
+ const done = data.done || data.state === 'ready' || data.state === 'error'
91
+
92
+ return {
93
+ status: data.state,
94
+ done,
95
+ error: data.error,
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Main handler
101
+ */
102
+ export async function handler(event, context) {
103
+ const origin = event.headers.origin || event.headers.Origin
104
+
105
+ // CORS headers
106
+ const corsHeaders = {
107
+ 'Access-Control-Allow-Origin': checkOrigin(origin) ? origin : 'null',
108
+ 'Access-Control-Allow-Headers': 'Content-Type',
109
+ 'Access-Control-Allow-Methods': 'POST, OPTIONS',
110
+ 'Access-Control-Allow-Credentials': 'true',
111
+ }
112
+
113
+ // Handle preflight
114
+ if (event.httpMethod === 'OPTIONS') {
115
+ return {
116
+ statusCode: 200,
117
+ headers: corsHeaders,
118
+ body: '',
119
+ }
120
+ }
121
+
122
+ // Only allow POST requests
123
+ if (event.httpMethod !== 'POST') {
124
+ return {
125
+ statusCode: 405,
126
+ headers: corsHeaders,
127
+ body: JSON.stringify({ error: 'Method not allowed' }),
128
+ }
129
+ }
130
+
131
+ // Check origin
132
+ if (!checkOrigin(origin)) {
133
+ console.error('Blocked request from unauthorized origin:', origin)
134
+ return {
135
+ statusCode: 403,
136
+ headers: corsHeaders,
137
+ body: JSON.stringify({ error: 'Forbidden - Invalid origin' }),
138
+ }
139
+ }
140
+
141
+ const isLocalDev = process.env.CONTEXT === 'dev' || !process.env.CONTEXT
142
+ if (isLocalDev) {
143
+ console.log('Local development mode - skipping authentication checks')
144
+ }
145
+
146
+ // Check if Netlify credentials are configured
147
+ if (!NETLIFY_AUTH_TOKEN || !NETLIFY_SITE_ID) {
148
+ console.error('Netlify credentials not configured')
149
+ return {
150
+ statusCode: 500,
151
+ headers: corsHeaders,
152
+ body: JSON.stringify({
153
+ error: 'Netlify API credentials not configured on server',
154
+ }),
155
+ }
156
+ }
157
+
158
+ try {
159
+ const { action, data } = JSON.parse(event.body)
160
+
161
+ // Log the action for audit purposes
162
+ const userEmail = isLocalDev
163
+ ? 'local-dev'
164
+ : context.clientContext?.user?.email || 'unknown'
165
+ console.log(`Action: ${action}, User: ${userEmail}, Origin: ${origin}`)
166
+
167
+ switch (action) {
168
+ case 'getLatestBuild': {
169
+ const result = await getLatestBuild()
170
+ console.log(
171
+ `Latest build fetched by ${userEmail}, Build ID: ${result.buildId}`
172
+ )
173
+ return {
174
+ statusCode: 200,
175
+ headers: corsHeaders,
176
+ body: JSON.stringify(result),
177
+ }
178
+ }
179
+
180
+ case 'checkBuildStatus': {
181
+ const { buildId } = data
182
+
183
+ if (!buildId) {
184
+ return {
185
+ statusCode: 400,
186
+ headers: corsHeaders,
187
+ body: JSON.stringify({ error: 'Build ID required' }),
188
+ }
189
+ }
190
+
191
+ const result = await checkBuildStatus(buildId)
192
+ return {
193
+ statusCode: 200,
194
+ headers: corsHeaders,
195
+ body: JSON.stringify(result),
196
+ }
197
+ }
198
+
199
+ default:
200
+ return {
201
+ statusCode: 400,
202
+ headers: corsHeaders,
203
+ body: JSON.stringify({ error: 'Invalid action' }),
204
+ }
205
+ }
206
+ } catch (error) {
207
+ console.error('Netlify build function error:', error)
208
+ return {
209
+ statusCode: 500,
210
+ headers: corsHeaders,
211
+ body: JSON.stringify({ error: error.message }),
212
+ }
213
+ }
214
+ }