@symbo.ls/brender 3.14.661 → 3.14.662
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/CHANGELOG.md +43 -0
- package/dist/esm/env.js +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/prefetch.js +1 -1
- package/dist/esm/render.js +53 -46
- package/dist/esm/shell.js +7 -0
- package/env.js +3 -1
- package/index.js +5 -2
- package/package.json +11 -7
- package/prefetch.js +50 -10
- package/render.js +125 -32
- package/shell.js +184 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,48 @@
|
|
|
1
1
|
# @symbo.ls/brender
|
|
2
2
|
|
|
3
|
+
## 3.14.662
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Fixes for the 42 issues reported while building xma.info (FW-XMA-EXCEPTIONS-UPSTREAM-1).
|
|
8
|
+
|
|
9
|
+
Behaviour changes (check these first):
|
|
10
|
+
|
|
11
|
+
- CSS precedence across `extends`: a later layer's shorthand that states a longhand now beats a longhand an earlier layer declared (`borderWidth: '0'` no longer loses to a base `borderBottomWidth`; a derived `border: 'hairline solid …'` now paints over a base `borderWidth: '0'`). Positional shorthands (`background`, `transition`, `font`, `flex`, `grid`…) never remove a base longhand. A `:focus-visible` longhand (`outlineColor`) now paints over Link/Focusable's ring.
|
|
12
|
+
- Built-in `Img` no longer copies `alt` into `title`. `Link` leaves Ctrl/Cmd/Shift/Alt, middle, `target` and `download` clicks to the browser.
|
|
13
|
+
- `X<` breakpoints start at N.02px, so no width matches both `X` and `X<`.
|
|
14
|
+
- Helmet renders the head as the merge of mounted layers: keys a page leaves out fall back instead of staying from the previous page.
|
|
15
|
+
- `smbls build` with `"brender": true` pre-renders again (it rendered nothing since 3.14.6): every static route is written, `/*` becomes `404.html`, `:param` routes are skipped and listed, and the build exits 1 when no route renders.
|
|
16
|
+
- `onStateInit`, `onStateCreated` and `onBeforeStateUpdate` run again (`onBeforeStateUpdate` returning `false` cancels the update).
|
|
17
|
+
- `onWindowLoad` runs once right after creation when the page had already loaded.
|
|
18
|
+
- The first render no longer forces a style+layout pass per microtask.
|
|
19
|
+
- socket.io-client loads only when a sync transport is configured; the ESM build ships it as `dist/smbls.socket-io-client.esm.js` (keep it beside `smbls.esm.js`).
|
|
20
|
+
|
|
21
|
+
New, opt-in:
|
|
22
|
+
|
|
23
|
+
- `designSystem.focusableRing`; `vars` and top-level `'--x'` keys set reactive custom properties; `fill`/`stroke` are CSS outside `<svg>`; attributes inside `.isX` / `'!isX'` blocks apply.
|
|
24
|
+
- Design-system tokens resolve inside `style: {}` blocks; `sizes` tokens negate (`-hairline2`) and publish `--size-<name>`.
|
|
25
|
+
- `fetchpriority` and every multi-word attribute work flat in both spellings.
|
|
26
|
+
- `inertOutside`, the `Bridge` host for DOM-owning libraries, element events with `{ capture, passive, once, selector, handler }`, `TooltipLayer` for `[data-tooltip]` nodes.
|
|
27
|
+
- Router `scrollRestoration: 'restore'`; guards that refuse Back/Forward put the address back; lazy route groups (`context.lazy`, `el.call('loadLazy')`).
|
|
28
|
+
- Fetch: `el.call('invalidateQueries')` and friends, `invalidates` on rpc, `fetch.early` pre-boot requests, `fetch.reads` anonymous GET reads without a CORS preflight.
|
|
29
|
+
- Polyglot `setLang` switches text, `<html lang>` and fonts in one frame; `getPendingLang`.
|
|
30
|
+
- `s.$settled()` / `settled()` await the DOM a state write produces; `onMediaQueryChange` takes design-system media keys; the `animation` shorthand resolves tokens.
|
|
31
|
+
- `smbls/ssr` entry for server rendering; `smbls push` publishes `new URL('<path>', import.meta.url)` assets.
|
|
32
|
+
- frank-audit: FA105 per-tag, FA410 (history writes), wider FA701; opt-in FA211, FA810, FA811, FA812; unknown `--rule` ids are reported.
|
|
33
|
+
|
|
34
|
+
- Updated dependencies [95d31cb]
|
|
35
|
+
- Updated dependencies [13c4922]
|
|
36
|
+
- Updated dependencies
|
|
37
|
+
- @symbo.ls/css@3.14.774
|
|
38
|
+
- smbls@3.14.827
|
|
39
|
+
- @symbo.ls/utils@3.14.784
|
|
40
|
+
- @symbo.ls/fetch@3.14.662
|
|
41
|
+
- @symbo.ls/helmet@3.14.669
|
|
42
|
+
- @symbo.ls/element@3.14.799
|
|
43
|
+
- @symbo.ls/scratch@3.14.786
|
|
44
|
+
- @symbo.ls/default-config@3.14.785
|
|
45
|
+
|
|
3
46
|
## 3.14.658
|
|
4
47
|
|
|
5
48
|
### Patch Changes
|
package/dist/esm/env.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{parseHTML as
|
|
1
|
+
import{parseHTML as n}from"linkedom";const c=(s="<!DOCTYPE html><html><head></head><body></body></html>")=>{const{window:e,document:l}=n(s);e.requestAnimationFrame||(e.requestAnimationFrame=o=>{const t=setTimeout(o,0);return t?.unref&&t.unref(),t}),e.cancelAnimationFrame||(e.cancelAnimationFrame=o=>clearTimeout(o)),e.history||(e.history={pushState:()=>{},replaceState:()=>{},state:null}),e.location||(e.location={pathname:"/",search:"",hash:"",origin:"http://localhost",protocol:"http:",host:"localhost",hostname:"localhost",port:"",href:"http://localhost/"}),e.URL||(e.URL=URL),e.scrollTo||(e.scrollTo=()=>{});const a=()=>{const o={};return{getItem:t=>o[t]??null,setItem:(t,r)=>{o[t]=String(r)},removeItem:t=>{delete o[t]},clear:()=>{for(const t in o)delete o[t]},get length(){return Object.keys(o).length},key:t=>Object.keys(o)[t]??null}};return e.localStorage||(e.localStorage=a()),e.sessionStorage||(e.sessionStorage=a()),globalThis.localStorage||(globalThis.localStorage=e.localStorage),globalThis.sessionStorage||(globalThis.sessionStorage=e.sessionStorage),e.MutationObserver||(e.MutationObserver=class{constructor(){}observe(){}disconnect(){}takeRecords(){return[]}}),globalThis.window=e,globalThis.document=l,globalThis.Node=e.Node||globalThis.Node,globalThis.Element=e.Element||globalThis.Element,globalThis.HTMLElement=e.HTMLElement||globalThis.HTMLElement,globalThis.MutationObserver=e.MutationObserver,globalThis.Window=e.constructor,{window:e,document:l}};export{c as createEnv};
|
package/dist/esm/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createEnv as e}from"./env.js";import{resetKeys as r,assignKeys as t,mapKeysToElements as o}from"./keys.js";import{loadProject as
|
|
1
|
+
import{createEnv as e}from"./env.js";import{resetKeys as r,assignKeys as t,mapKeysToElements as o}from"./keys.js";import{loadProject as m,loadAndRenderAll as a}from"./load.js";import{render as l,renderElement as n,renderRoute as p,renderPage as d,resetGlobalCSSCache as i,getAccumulatedEmotionCSS as c,replaceEmotionCSS as f}from"./render.js";import{extractMetadata as s,generateHeadHtml as S}from"./metadata.js";import{collectBrNodes as g,hydrate as h}from"./hydrate.js";import{generateSitemap as E}from"./sitemap.js";import{prefetchPageData as u,injectPrefetchedState as y}from"./prefetch.js";import{composeIntoShell as C}from"./shell.js";var B={createEnv:e,resetKeys:r,assignKeys:t,mapKeysToElements:o,loadProject:m,loadAndRenderAll:a,render:l,renderElement:n,renderRoute:p,renderPage:d,resetGlobalCSSCache:i,getAccumulatedEmotionCSS:c,replaceEmotionCSS:f,extractMetadata:s,generateHeadHtml:S,collectBrNodes:g,hydrate:h,generateSitemap:E,prefetchPageData:u,injectPrefetchedState:y,composeIntoShell:C};export{t as assignKeys,g as collectBrNodes,C as composeIntoShell,e as createEnv,B as default,s as extractMetadata,S as generateHeadHtml,E as generateSitemap,c as getAccumulatedEmotionCSS,h as hydrate,y as injectPrefetchedState,a as loadAndRenderAll,m as loadProject,o as mapKeysToElements,u as prefetchPageData,l as render,n as renderElement,d as renderPage,p as renderRoute,f as replaceEmotionCSS,i as resetGlobalCSSCache,r as resetKeys};
|
package/dist/esm/prefetch.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
const o=t=>typeof t=="function",b=t=>Array.isArray(t),g=t=>t!==null&&typeof t=="object"&&!Array.isArray(t),j=(t,r)=>{if(t){if(o(t))try{return t({state:r||{},call:()=>{},__ref:{}},r||{})}catch{return}return t}},k=(t,r)=>{if(!t)return null;if(typeof t=="string")return{from:t,method:"select"};const e=o(t)?null:{...t};return!e||(e.method||(e.method="select"),o(e.params)&&(e.params=j(e.params,r)),e.method==="insert"||e.method==="update"||e.method==="upsert"||e.method==="delete")||e.on&&e.on!=="create"?null:e},S=(t,r="")=>{if(!t||typeof t!="object")return[];if(o(t))return[];const e=[],s=t.state||{};if(t.fetch){const n=b(t.fetch)?t.fetch:[t.fetch];for(const c of n){const a=k(c,s);a&&e.push({config:a,stateKey:a.as,path:r,elementState:s})}}for(const n in t)n==="fetch"||n==="state"||n==="props"||n==="attr"||n==="on"||n==="define"||n==="childExtends"||n==="childProps"||n==="childrenAs"||n.charAt(0)>="A"&&n.charAt(0)<="Z"&&g(t[n])&&e.push(...S(t[n],r?`${r}.${n}`:n));return e},M=["rest","local"],w=new WeakMap,A=async t=>{if(!t)return null;if(g(t)&&(o(t.select)||o(t.rpc)))return t;const r=typeof t=="string"?{adapter:t}:t;if(!g(r)||typeof r.adapter!="string")return null;if(w.has(r))return w.get(r);const e=(async()=>{let s;try{s=await import("@symbo.ls/fetch")}catch{return null}if(!(o(s.hasAdapter)?s.hasAdapter(r.adapter):M.includes(r.adapter)))return null;try{return await s.resolveDb(r)}catch(c){return console.warn(`[brender] SSR adapter "${r.adapter}" unavailable: ${c.message}`),null}})();return w.set(r,e),e},P=async(t,r)=>{try{const{method:e,from:s,params:n,transform:c,limit:a,offset:l,order:u,single:h,headers:d,baseUrl:i}=r;let f;if(e==="rpc"){if(!o(t.rpc))return null;f=await t.rpc({from:s,params:n,headers:d,baseUrl:i})}else{if(!o(t.select))return null;f=await t.select({from:s,select:r.select,params:n,limit:a,offset:l,order:u,single:h,headers:d,baseUrl:i})}let p=f?.data??null;if(f?.error)return null;if(p&&c&&o(c))try{p=c(p)}catch{}return p}catch{return null}},x=async(t,r="/",e={})=>{const n=(t.pages||{})[r];if(!n)return new Map;const c=t.config||t.settings||{},a=c.fetch||t.fetch||c.db||t.db;if(!a)return new Map;const l=await A(a);if(!l)return new Map;const u=S(n);if(!u.length)return new Map;const h=new Map,d=await Promise.allSettled(u.map(async({config:i,stateKey:f,path:p})=>{const y=await P(l,i);if(y!==null){const m=h.get(p)||{};f?m[f]=y:g(y)&&Object.assign(m,y),h.set(p,m)}}));return h},E=async t=>{const r=t.config||{},e=r.polyglot||t.polyglot;if(!e?.fetch)return null;const s=r.fetch||t.fetch||r.db||t.db;if(!s)return null;const n=await A(s);if(!n)return null;const c=e.fetch,a=c.rpc||c.from||"get_translations_if_changed",l=e.languages||[e.defaultLang||"en"],u={},h=await Promise.allSettled(l.map(async d=>{try{if(!o(n.rpc))return;const i=await n.rpc({from:a,params:{p_lang:d,p_cached_version:0}});if(i.error||!i.data)return;const f=i.data;f.translations&&(u[d]=f.translations)}catch(i){console.warn("[brender] SSR translation fetch failed:",i.message)}}));return Object.keys(u).length?u:null},_=(t,r)=>{if(!(!t||typeof t!="object")){for(const e in t)if(!(e==="state"||e==="fetch"||e==="props"||e==="attr"||e==="on"||e==="define"||e==="childExtends"||e==="childProps"||e==="childrenAs")&&e.charAt(0)>="A"&&e.charAt(0)<="Z"&&g(t[e])){const s=t[e],n=s.state&&typeof s.state=="object"?{...r,...s.state}:r;if(o(s.children))try{const c={state:n,call:l=>{if(l==="getActiveLang"||l==="getLang")return n?.lang||"ka";if(l==="polyglot")return arguments[1]||""},__ref:{}},a=s.children(c,n);b(a)&&a.length>0&&(s.children=a)}catch{}_(s,n)}}},D=(t,r)=>{if(!(!r||!r.size))for(const[e,s]of r){let n=t;if(e){const c=e.split(".");for(const a of c){if(!n||typeof n!="object")break;n=n[a]}}n&&typeof n=="object"&&((!n.state||typeof n.state!="object")&&(n.state={}),Object.assign(n.state,s),_(n,n.state))}};export{A as createSSRAdapter,E as fetchSSRTranslations,D as injectPrefetchedState,x as prefetchPageData};
|
package/dist/esm/render.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import{resolve as d,join as tt,dirname as q}from"path";import{existsSync as b,unlinkSync as kt,readFileSync as K,realpathSync as $t}from"fs";import{resolvePackageEntry as Ct}from"./packageEntry.js";import{tmpdir as Ot}from"os";import{randomBytes as Tt}from"crypto";import{createRequire as et}from"module";import{createEnv as nt}from"./env.js";import{resetKeys as st,assignKeys as rt,mapKeysToElements as it}from"./keys.js";import{extractMetadata as ct,generateHeadHtml as Lt}from"./metadata.js";import"./hydrate.js";import{prefetchPageData as At,injectPrefetchedState as It,fetchSSRTranslations as Pt}from"./prefetch.js";let z=null;const qt=async()=>{if(z)return z;try{return z=(await import("@symbo.ls/funcql")).funcqlPlugin,z}catch{return null}};import"linkedom";import{reset as vt}from"@symbo.ls/css";const ot=(t,e)=>{if(!t||!e)return;if(t[e]!==void 0)return t[e];const o=e.split(".");let n=t;for(const r of o){if(n==null||typeof n!="object")return;n=n[r]}return n},Et=function(t,e){if(!t)return"";const o=this?.context,n=o?.polyglot,r=e||n?.defaultLang||"en";if(n?.translations){const c=n.translations[r];if(c){const i=ot(c,t);if(i!==void 0)return i}}const f=this?.state?.root||o?.state?.root;if(f?.translations){const c=f.translations[r];if(c){const i=ot(c,t);if(i!==void 0)return i}}const u=n?.defaultLang||"en";if(u!==r&&n?.translations){const c=n.translations[u];if(c){const i=ot(c,t);if(i!==void 0)return i}}return t},lt=function(){const t=this?.context;return this?.state?.root?.lang||t?.polyglot?.defaultLang||"en"},N=(t,e=new WeakMap)=>{if(t===null||typeof t!="object")return t;if(e.has(t))return e.get(t);if(Array.isArray(t)){const n=[];e.set(t,n);for(const r of t)n.push(typeof r=="object"&&r!==null?N(r,e):r);return n}const o={};e.set(t,o);for(const n of Object.keys(t)){const r=t[n];o[n]=typeof r=="object"&&r!==null?N(r,e):r}return o},at=()=>{const t=new WeakSet;return(e,o)=>{if(typeof o!="function"){if(typeof o=="object"&&o!==null){if(t.has(o))return;t.add(o)}return o}}};let B=null;try{import.meta.url&&(B=et(import.meta.url))}catch{}const ft=()=>{if(!import.meta.url||!B)return{isMonorepo:!1,monorepoRoot:null,resolvePackage:i=>i};const t=$t(new URL(".",import.meta.url).pathname),e=d(t,"../..");if(b(d(e,"packages","smbls","src","createDomql.js")))return{isMonorepo:!0,monorepoRoot:e};let n;try{const i=B.resolve("smbls/package.json");n=q(i)}catch{let i=t;for(;i!==q(i);){const s=d(i,"node_modules","smbls");if(b(d(s,"package.json"))){n=s;break}i=q(i)}}let r=B;if(n)try{r=et(d(n,"package.json"))}catch{}let f,u=process.cwd();for(;u!==q(u);){if(b(d(u,"package.json"))){f=u;break}u=q(u)}let c=r;if(f)try{c=et(d(f,"package.json"))}catch{}return{isMonorepo:!1,smblsRoot:n,brenderDir:t,projectRoot:f,_smblsRequire:r,_projectRequire:c}},G=(t,e)=>{const o=t.isMonorepo?[B]:[t._smblsRequire,t._projectRequire,B].filter(Boolean);for(const n of o)try{return n.resolve(e)}catch{}return null},pe=(t,e,...o)=>{if(t.isMonorepo)return d(t.monorepoRoot,"packages",e,...o);const n=G(t,`${e}/package.json`);return n?d(q(n),...o):null},de=(t,e,...o)=>{if(t.isMonorepo)return d(t.monorepoRoot,"plugins",e,...o);const n=G(t,`@symbo.ls/${e}/package.json`);return n?d(q(n),...o):null},H=(t,e,...o)=>{if(t.isMonorepo){for(const r of["packages","plugins"]){const f=d(t.monorepoRoot,r,e,...o);if(b(f))return f}return null}const n=G(t,`@symbo.ls/${e}/package.json`);return n?d(q(n),...o):null},Y=(t,e,...o)=>{if(t.isMonorepo)return d(t.monorepoRoot,"packages","domql","packages",e,...o);const n=G(t,`@symbo.ls/${e}/package.json`);return n?d(q(n),...o):null};let Z=null;const Dt=async()=>{if(Z)return Z;const t=ft();if(!B)try{const c=await import("./dist/createDomql.bundled.mjs");return Z=c,c}catch(c){throw new Error(`brender: pre-bundled createDomql not available in bundled runtime: ${c.message}`)}let e;if(t.isMonorepo)e=d(t.monorepoRoot,"packages","smbls","src","createDomql.js");else if(t.smblsRoot){const c=d(t.smblsRoot,"src","createDomql.js"),i=d(t.smblsRoot,"dist","esm","src","createDomql.js");e=b(c)?c:i}if(!e||!b(e))throw new Error(`brender: cannot find createDomql.js (isMonorepo=${t.isMonorepo}, entry=${e})`);const o=await import("esbuild"),n=tt(Ot(),`br_createDomql_${Tt(6).toString("hex")}.mjs`),r=Ct,f={name:"workspace-resolve",setup(c){c.onResolve({filter:/^smbls/},i=>{const s=i.path.replace(/^smbls\/?/,""),l=t.isMonorepo?d(t.monorepoRoot,"packages","smbls"):t.smblsRoot;if(!l)return;if(!s){const m=r(l);return m?{path:m}:void 0}const p=d(l,s);if(b(p))return{path:p};if(b(p+".js"))return{path:p+".js"};const a=d(p,"index.js");if(b(a))return{path:a}}),c.onResolve({filter:/^domql$/},i=>{if(t.isMonorepo){const s=d(t.monorepoRoot,"packages","domql","src","index.js");if(b(s))return{path:s};const l=d(t.monorepoRoot,"packages","domql","index.js");if(b(l))return{path:l}}else try{const s=_require.resolve("domql/package.json"),l=r(q(s));if(l)return{path:l}}catch{}}),c.onResolve({filter:/^@symbo\.ls\//},i=>{const s=i.path.replace("@symbo.ls/","");if(s==="sync")return{path:"sync-stub",namespace:"brender-stub"};if(t.isMonorepo){for(const p of["packages","plugins"]){const a=d(t.monorepoRoot,p,s,"src","index.js");if(b(a))return{path:a};const m=d(t.monorepoRoot,p,s,"index.js");if(b(m))return{path:m}}const l=d(t.monorepoRoot,"packages","default-config","blank","index.js");if(s==="default-config"&&b(l))return{path:l}}else{const l=H(t,s,"src","index.js");if(l&&b(l))return{path:l};const p=H(t,s,"index.js");if(p&&b(p))return{path:p};if(s==="default-config"){const a=H(t,"default-config","blank","index.js");if(a&&b(a))return{path:a}}}}),c.onResolve({filter:/^@domql\//},i=>{const s=i.path.replace("@symbo.ls/","");if(t.isMonorepo){const l=d(t.monorepoRoot,"packages","domql","packages",s,"src","index.js");if(b(l))return{path:l};const p=d(t.monorepoRoot,"packages","domql","packages",s,"index.js");if(b(p))return{path:p}}else{const l=Y(t,s,"src","index.js");if(l&&b(l))return{path:l};const p=Y(t,s,"index.js");if(p&&b(p))return{path:p}}}),c.onResolve({filter:/^css-in-props/},i=>{let s;if(t.isMonorepo)s=d(t.monorepoRoot,"packages","css-in-props");else{const a=G(t,"css-in-props/package.json");a&&(s=q(a))}if(!s)return;const l=i.path.replace(/^css-in-props\/?/,"");if(l){const a=d(s,l),m=d(a,"index.js");if(b(m))return{path:m};if(b(a+".js"))return{path:a+".js"};if(b(a))return{path:a}}const p=r(s);if(p)return{path:p}}),c.onResolve({filter:/^@emotion\//},i=>{let s;if(t.isMonorepo)s=d(t.monorepoRoot,"node_modules",i.path);else{const l=G(t,`${i.path}/package.json`);if(!l)return;s=q(l)}if(b(s)){const l=d(s,"package.json");if(b(l))try{const p=JSON.parse(K(l,"utf8")),a=p.module||p.main||"dist/emotion-css.esm.js";return{path:d(s,a)}}catch{}return{path:s}}}),c.onResolve({filter:/\.json$/},i=>{if(i.resolveDir){const s=d(i.resolveDir,i.path);if(b(s))return{path:s}}}),c.onLoad({filter:/smbls\/.*options\.js$/},async i=>{if(!i.path.includes("smbls/")||!i.path.endsWith("options.js"))return;let s=K(i.path,"utf8");return s=s.replace(/import\s*\{[^}]*version[^}]*\}\s*from\s*['"][^'"]*package\.json['"][^;\n]*/,"const version = '0.0.0'"),s=s.replace(/import\s*\{[^}]*createRequire[^}]*\}\s*from\s*['"]module['"][^;\n]*/g,"// createRequire removed for brender build"),{contents:s,loader:"js"}}),c.onLoad({filter:/smbls\/.*init\.js$/},async i=>{if(!i.path.includes("smbls/")||!i.path.endsWith("init.js"))return;let s=K(i.path,"utf8");return s=s.replace(/import\s*\{[^}]*createRequire[^}]*\}\s*from\s*['"]module['"][^;\n]*/g,"// createRequire removed for brender build"),{contents:s,loader:"js"}}),c.onLoad({filter:/.*/,namespace:"brender-stub"},()=>({contents:"export const SyncComponent = {}; export const Inspect = {}; export const Notifications = {}; export const syncPlugin = () => ({}); export const notificationsPlugin = () => ({}); export const seedRawDesignSystem = () => {}; export const inspectPlugin = () => ({}); export default {}",loader:"js"})),c.onLoad({filter:/smbls\/src\/router\.js$/},async i=>{let s=K(i.path,"utf8");return s=s.replace(/import\s*\{\s*Link\s*\}\s*from\s*['"]smbls['"]/,"const Link = { tag: 'a', attr: { href: (el) => el.href } }"),{contents:s,loader:"js"}}),c.onLoad({filter:/fetchOnCreate\.js$/},async i=>{if(!i.path.includes("smbls/"))return;let s=K(i.path,"utf8");return s=s.replace(/window\s*&&\s*window\.location\s*\?\s*window\.location\.host\.includes/g,"window && window.location && window.location.host ? window.location.host.includes"),{contents:s,loader:"js"}})}};await o.build({entryPoints:[e],bundle:!0,format:"esm",platform:"node",outfile:n,write:!0,logLevel:"warning",plugins:[f],nodePaths:t.isMonorepo?[d(t.monorepoRoot,"node_modules")]:[...t.smblsRoot?[d(t.smblsRoot,"node_modules")]:[],...t.projectRoot?[d(t.projectRoot,"node_modules")]:[],...t.smblsRoot?[d(t.smblsRoot,"..","..","node_modules")]:[]].filter(c=>b(c)),supported:{"import-attributes":!1},external:["fs","path","os","crypto","url","http","https","stream","util","events","buffer","child_process","worker_threads","net","tls","dns","dgram","zlib","assert","querystring","string_decoder","readline","perf_hooks","async_hooks","v8","vm","cluster","inspector","module","process","tty","color-contrast-checker","linkedom"]});const u=await import(`file://${n}`);if(process.env.BRENDER_DEBUG)console.log("[brender] Bundle saved:",n);else try{kt(n)}catch{}return Z=u,u},Mt={Box:{},Focusable:{},Block:{display:"block"},Inline:{display:"inline"},Flex:{display:"flex"},InlineFlex:{display:"inline-flex"},Grid:{display:"grid"},InlineGrid:{display:"inline-grid"},Link:{tag:"a",attr:{href:t=>t.href,target:t=>t.target,rel:t=>t.rel}},A:{extends:"Link"},RouteLink:{extends:"Link"},Img:{tag:"img",attr:{src:t=>{let e=t.src;return typeof e=="string"&&e.includes("{{")&&(e=t.call("replaceLiteralsWithObjectFields",e,t.state)),e},alt:t=>t.alt,loading:t=>t.loading}},Image:{extends:"Img"},Button:{tag:"button"},FocusableComponent:{tag:"button"},Form:{tag:"form"},Input:{tag:"input"},TextArea:{tag:"textarea"},Textarea:{tag:"textarea"},Select:{tag:"select"},Label:{tag:"label"},Iframe:{tag:"iframe"},Video:{tag:"video"},Audio:{tag:"audio"},Canvas:{tag:"canvas"},Span:{tag:"span"},P:{tag:"p"},H1:{tag:"h1"},H2:{tag:"h2"},H3:{tag:"h3"},H4:{tag:"h4"},H5:{tag:"h5"},H6:{tag:"h6"},Svg:{tag:"svg",attr:{xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink"}},Text:{tag:"span"}},ut=(t,e="")=>{if(!t||!t.__ref)return{};const o={},n=t.__ref.__brKey;if(n&&(o[e===""?"__root":e]=n),t.__ref.__children)for(const r of t.__ref.__children){const f=t[r];if(f&&f.__ref){const u=e===""?r:`${e}.${r}`;Object.assign(o,ut(f,u))}}return o},pt=async(t,e={})=>{const{route:o="/",pathname:n,state:r,context:f,prefetch:u=!1}=e,c=n||o,i=globalThis.location,s=globalThis.window;(!globalThis.location||globalThis.location.pathname!==c)&&(globalThis.location={pathname:c,href:c,search:"",hash:"",origin:""}),globalThis.window||(globalThis.window={location:globalThis.location});let l;if(u)try{const g=t.pages||{};l={...g};const P=await At(t,o);if(P.size){const V=JSON.parse(JSON.stringify(g[o],(v,F)=>{if(typeof F!="function")return F})),U=(v,F)=>{if(!(!v||!F))for(const E in v)typeof v[E]=="function"?F[E]=v[E]:typeof v[E]=="object"&&v[E]!==null&&!Array.isArray(v[E])&&typeof F[E]=="object"&&F[E]!==null&&U(v[E],F[E])};U(g[o],V),It(V,P),l[o]=V}}catch(g){console.error("[brender] Prefetch error:",g),l=t.pages}let p;if(u)try{p=await Pt(t)}catch(g){console.warn("[brender] SSR translation fetch failed:",g.message)}i!==void 0?globalThis.location=i:delete globalThis.location,s!==void 0?globalThis.window=s:delete globalThis.window;const{window:a,document:m}=nt(),x=m.body;a.location.pathname=c;const h=globalThis.document,y=globalThis.location;globalThis.document=m,globalThis.location=a.location;const{createDomqlElement:A}=await Dt(),L=N(t.app||{}),R={...t.config||{}};t.polyglot&&!R.polyglot&&(R.polyglot=t.polyglot),t.fetch&&!R.fetch&&(R.fetch=t.fetch),t.router&&!R.router&&(R.router=t.router);for(const g of["useReset","useVariable","useFontImport","useIconSprite","useSvgSprite","useDefaultConfig","useDocumentTheme"])t[g]!=null&&R[g]==null&&(R[g]=t[g]);const $=R.polyglot?{...R.polyglot}:void 0;p&&$&&($.translations={...$.translations||{},...p});const C=N(t.state||{});(p||$)&&(C.root||(C.root={}),$&&(C.root.lang=C.root.lang||$.defaultLang||"en"),p&&(C.root.translations={...C.root.translations||{},...p})),vt();const T={state:C,...r?{state:{...C,...r}}:{},dependencies:N(t.dependencies||{}),components:N(t.components||{}),snippets:N(t.snippets||{}),pages:N(l||t.pages||{}),functions:{...t.functions||{},polyglot:Et,getActiveLang:lt,getLang:lt},methods:t.methods||{},designSystem:N(t.designSystem||{}),files:t.files||{},sharedLibraries:t.sharedLibraries||[],...R,...$?{polyglot:$}:{},document:m,window:a,parent:{node:x},initOptions:{},domqlOptions:{sourcemap:!1},...f||{}},D=await qt();D&&(T.plugins=T.plugins||[],T.plugins.some(g=>g.name==="funcql")||T.plugins.push(D)),st();const I=await A(L,T),M=u?2e3:50;await new Promise(g=>setTimeout(g,M)),rt(x);const _=it(I),k=ut(I),w=ct(t,o,I,I?.state),S=[],j=m.head||m.querySelector("head");if(j)for(const g of j.querySelectorAll("style")){if(g.sheet&&g.sheet.cssRules)for(const P of g.sheet.cssRules)P.cssText&&S.push(P.cssText);if(!S.length){const P=g.textContent||"";P&&S.push(P)}}let O=dt(x.innerHTML);if(p){const g=$?.defaultLang||"en",P=p[g]||Object.values(p)[0]||{};O=O.replace(/\{\{\s*([^|{}]+?)\s*\|\s*polyglot\s*\}\}/g,(V,U)=>{const v=U.trim();return P[v]??V})}return h!==void 0?globalThis.document=h:delete globalThis.document,y!==void 0?globalThis.location=y:delete globalThis.location,{html:O,metadata:w,registry:_,brRegistry:k,element:I,emotionCSS:S,document:m,window:a,ssrTranslations:p,prefetchedPages:l}},me=async(t,e={})=>{const{context:o={}}=e,{window:n,document:r}=nt(),f=r.body,{create:u}=await import("@symbo.ls/element"),c=await import("@symbo.ls/utils"),i={...Mt,...o.components||{}},s={...c,...o.utils||{},...o.functions||{}};st();let l;try{l=u(t,{node:f},"root",{context:{document:r,window:n,...o,components:i,utils:s}})}catch{}rt(f);const p=l?it(l):{};return{html:dt(f.innerHTML),registry:p,element:l}},dt=t=>t.replace(/(<svg\b[^>]*>)([\s\S]*?)(<\/svg>)/gi,(e,o,n,r)=>{if(n.includes("<")){const f=n.replace(/</g,"<").replace(/>/g,">").replace(/&/g,"&").replace(/"/g,'"').replace(/'/g,"'");return o+f+r}return e});let W=null;const Nt=["globalTheme","themeStorageKey","themeRoot","useReset","useVariable","useFontImport","useIconSprite","useSvgSprite","useDocumentTheme","useDefaultConfig","useDefaultIcons","useThemeSuffixedVars","verbose","semanticIcons"],mt=t=>{if(!t||typeof t!="object")return null;if(t.config&&typeof t.config=="object")return t.config;const e={};let o=!1;for(const n of Nt)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n],o=!0);return o?e:t.settings||null},gt=(t,e,o)=>{const n=Object.entries(e||{}).map(([r,f])=>`${o} ${r}: ${f}`).join(`;
|
|
1
|
+
import{resolve as m,join as Q,dirname as P}from"path";import{existsSync as y,unlinkSync as Ct,readFileSync as K,realpathSync as Tt}from"fs";import{resolvePackageEntry as Ot}from"./packageEntry.js";import{tmpdir as Lt}from"os";import{randomBytes as It}from"crypto";import{createRequire as tt}from"module";import{createEnv as st}from"./env.js";import{resetKeys as rt,assignKeys as it,mapKeysToElements as ct}from"./keys.js";import{extractMetadata as et,generateHeadHtml as At}from"./metadata.js";import"./hydrate.js";import{composeIntoShell as Et}from"./shell.js";import{prefetchPageData as Pt,injectPrefetchedState as qt,fetchSSRTranslations as vt}from"./prefetch.js";let z=null;const Dt=async()=>{if(z)return z;try{return z=(await import("@symbo.ls/funcql")).funcqlPlugin,z}catch{return null}};import"linkedom";import{reset as Mt}from"@symbo.ls/css";const ot=(t,e)=>{if(!t||!e)return;if(t[e]!==void 0)return t[e];const o=e.split(".");let n=t;for(const r of o){if(n==null||typeof n!="object")return;n=n[r]}return n},Ft=function(t,e){if(!t)return"";const o=this?.context,n=o?.polyglot,r=e||n?.defaultLang||"en";if(n?.translations){const l=n.translations[r];if(l){const c=ot(l,t);if(c!==void 0)return c}}const u=this?.state?.root||o?.state?.root;if(u?.translations){const l=u.translations[r];if(l){const c=ot(l,t);if(c!==void 0)return c}}const p=n?.defaultLang||"en";if(p!==r&&n?.translations){const l=n.translations[p];if(l){const c=ot(l,t);if(c!==void 0)return c}}return t},lt=function(){const t=this?.context;return this?.state?.root?.lang||t?.polyglot?.defaultLang||"en"},F=(t,e=new WeakMap)=>{if(t===null||typeof t!="object")return t;if(e.has(t))return e.get(t);if(Array.isArray(t)){const n=[];e.set(t,n);for(const r of t)n.push(typeof r=="object"&&r!==null?F(r,e):r);return n}const o={};e.set(t,o);for(const n of Object.keys(t)){const r=t[n];o[n]=typeof r=="object"&&r!==null?F(r,e):r}return o},at=()=>{const t=new WeakSet;return(e,o)=>{if(typeof o!="function"){if(typeof o=="object"&&o!==null){if(t.has(o))return;t.add(o)}return o}}};let B=null;try{import.meta.url&&(B=tt(import.meta.url))}catch{}const ft=()=>{if(!import.meta.url||!B)return{isMonorepo:!1,monorepoRoot:null,resolvePackage:c=>c};const t=Tt(new URL(".",import.meta.url).pathname),e=m(t,"../..");if(y(m(e,"packages","smbls","src","createDomql.js")))return{isMonorepo:!0,monorepoRoot:e};let n;try{const c=B.resolve("smbls/package.json");n=P(c)}catch{let c=t;for(;c!==P(c);){const s=m(c,"node_modules","smbls");if(y(m(s,"package.json"))){n=s;break}c=P(c)}}let r=B;if(n)try{r=tt(m(n,"package.json"))}catch{}let u,p=process.cwd();for(;p!==P(p);){if(y(m(p,"package.json"))){u=p;break}p=P(p)}let l=r;if(u)try{l=tt(m(u,"package.json"))}catch{}return{isMonorepo:!1,smblsRoot:n,brenderDir:t,projectRoot:u,_smblsRequire:r,_projectRequire:l}},H=(t,e)=>{const o=t.isMonorepo?[B]:[t._smblsRequire,t._projectRequire,B].filter(Boolean);for(const n of o)try{return n.resolve(e)}catch{}return null},he=(t,e,...o)=>{if(t.isMonorepo)return m(t.monorepoRoot,"packages",e,...o);const n=H(t,`${e}/package.json`);return n?m(P(n),...o):null},be=(t,e,...o)=>{if(t.isMonorepo)return m(t.monorepoRoot,"plugins",e,...o);const n=H(t,`@symbo.ls/${e}/package.json`);return n?m(P(n),...o):null},nt=(t,e,...o)=>{if(t.isMonorepo){for(const r of["packages","plugins"]){const u=m(t.monorepoRoot,r,e,...o);if(y(u))return u}return null}const n=H(t,`@symbo.ls/${e}/package.json`);return n?m(P(n),...o):null},Y=(t,e,...o)=>{if(t.isMonorepo)return m(t.monorepoRoot,"packages","domql","packages",e,...o);const n=H(t,`@symbo.ls/${e}/package.json`);return n?m(P(n),...o):null};let W=null;const ut="smbls/ssr",pt=async()=>{const t=await import("smbls/ssr");if(typeof t?.createDomqlElement!="function")throw new Error("smbls/ssr does not export createDomqlElement");return t},Nt=async()=>{if(W)return W;const t=ft();if(!B)try{return W=await pt(),W}catch(l){throw new Error(`brender: ${ut} is not available in this bundled runtime (${l.message}). Bundle smbls \u2265 the release that adds the smbls/ssr export alongside @symbo.ls/brender.`)}let e;if(t.isMonorepo)e=m(t.monorepoRoot,"packages","smbls","src","createDomql.js");else{let l;try{return W=await pt(),W}catch(s){l=s}const c=t.smblsRoot&&m(t.smblsRoot,"src","createDomql.js");if(c&&y(c))e=c;else throw new Error(`brender: cannot load ${ut} (${l.message}). Server rendering needs smbls installed in this project, at a release that exports smbls/ssr`+(t.smblsRoot?` (found smbls at ${t.smblsRoot}).`:" (no smbls package found)."))}const o=await import("esbuild"),n=Q(Lt(),`br_createDomql_${It(6).toString("hex")}.mjs`),r=Ot,u={name:"workspace-resolve",setup(l){l.onResolve({filter:/^smbls/},c=>{const s=c.path.replace(/^smbls\/?/,""),i=t.isMonorepo?m(t.monorepoRoot,"packages","smbls"):t.smblsRoot;if(!i)return;if(!s){const g=r(i);return g?{path:g}:void 0}const a=m(i,s);if(y(a))return{path:a};if(y(a+".js"))return{path:a+".js"};const f=m(a,"index.js");if(y(f))return{path:f}}),l.onResolve({filter:/^domql$/},c=>{if(t.isMonorepo){const s=m(t.monorepoRoot,"packages","domql","src","index.js");if(y(s))return{path:s};const i=m(t.monorepoRoot,"packages","domql","index.js");if(y(i))return{path:i}}else try{const s=_require.resolve("domql/package.json"),i=r(P(s));if(i)return{path:i}}catch{}}),l.onResolve({filter:/^@symbo\.ls\//},c=>{const s=c.path.replace("@symbo.ls/","");if(s==="sync")return{path:"sync-stub",namespace:"brender-stub"};if(t.isMonorepo){for(const a of["packages","plugins"]){const f=m(t.monorepoRoot,a,s,"src","index.js");if(y(f))return{path:f};const g=m(t.monorepoRoot,a,s,"index.js");if(y(g))return{path:g}}const i=m(t.monorepoRoot,"packages","default-config","blank","index.js");if(s==="default-config"&&y(i))return{path:i}}else{const i=nt(t,s,"src","index.js");if(i&&y(i))return{path:i};const a=nt(t,s,"index.js");if(a&&y(a))return{path:a};if(s==="default-config"){const f=nt(t,"default-config","blank","index.js");if(f&&y(f))return{path:f}}}}),l.onResolve({filter:/^@domql\//},c=>{const s=c.path.replace("@symbo.ls/","");if(t.isMonorepo){const i=m(t.monorepoRoot,"packages","domql","packages",s,"src","index.js");if(y(i))return{path:i};const a=m(t.monorepoRoot,"packages","domql","packages",s,"index.js");if(y(a))return{path:a}}else{const i=Y(t,s,"src","index.js");if(i&&y(i))return{path:i};const a=Y(t,s,"index.js");if(a&&y(a))return{path:a}}}),l.onResolve({filter:/^css-in-props/},c=>{let s;if(t.isMonorepo)s=m(t.monorepoRoot,"packages","css-in-props");else{const f=H(t,"css-in-props/package.json");f&&(s=P(f))}if(!s)return;const i=c.path.replace(/^css-in-props\/?/,"");if(i){const f=m(s,i),g=m(f,"index.js");if(y(g))return{path:g};if(y(f+".js"))return{path:f+".js"};if(y(f))return{path:f}}const a=r(s);if(a)return{path:a}}),l.onResolve({filter:/^@emotion\//},c=>{let s;if(t.isMonorepo)s=m(t.monorepoRoot,"node_modules",c.path);else{const i=H(t,`${c.path}/package.json`);if(!i)return;s=P(i)}if(y(s)){const i=m(s,"package.json");if(y(i))try{const a=JSON.parse(K(i,"utf8")),f=a.module||a.main||"dist/emotion-css.esm.js";return{path:m(s,f)}}catch{}return{path:s}}}),l.onResolve({filter:/\.json$/},c=>{if(c.resolveDir){const s=m(c.resolveDir,c.path);if(y(s))return{path:s}}}),l.onLoad({filter:/smbls\/.*options\.js$/},async c=>{if(!c.path.includes("smbls/")||!c.path.endsWith("options.js"))return;let s=K(c.path,"utf8");return s=s.replace(/import\s*\{[^}]*version[^}]*\}\s*from\s*['"][^'"]*package\.json['"][^;\n]*/,"const version = '0.0.0'"),s=s.replace(/import\s*\{[^}]*createRequire[^}]*\}\s*from\s*['"]module['"][^;\n]*/g,"// createRequire removed for brender build"),{contents:s,loader:"js"}}),l.onLoad({filter:/smbls\/.*init\.js$/},async c=>{if(!c.path.includes("smbls/")||!c.path.endsWith("init.js"))return;let s=K(c.path,"utf8");return s=s.replace(/import\s*\{[^}]*createRequire[^}]*\}\s*from\s*['"]module['"][^;\n]*/g,"// createRequire removed for brender build"),{contents:s,loader:"js"}}),l.onLoad({filter:/.*/,namespace:"brender-stub"},()=>({contents:"export const SyncComponent = {}; export const Inspect = {}; export const Notifications = {}; export const syncPlugin = () => ({}); export const notificationsPlugin = () => ({}); export const seedRawDesignSystem = () => {}; export const inspectPlugin = () => ({}); export default {}",loader:"js"})),l.onLoad({filter:/smbls\/src\/router\.js$/},async c=>{let s=K(c.path,"utf8");return s=s.replace(/import\s*\{\s*Link\s*\}\s*from\s*['"]smbls['"]/,"const Link = { tag: 'a', attr: { href: (el) => el.href } }"),{contents:s,loader:"js"}}),l.onLoad({filter:/fetchOnCreate\.js$/},async c=>{if(!c.path.includes("smbls/"))return;let s=K(c.path,"utf8");return s=s.replace(/window\s*&&\s*window\.location\s*\?\s*window\.location\.host\.includes/g,"window && window.location && window.location.host ? window.location.host.includes"),{contents:s,loader:"js"}})}};await o.build({entryPoints:[e],bundle:!0,format:"esm",platform:"node",outfile:n,write:!0,logLevel:"warning",plugins:[u],nodePaths:t.isMonorepo?[m(t.monorepoRoot,"node_modules")]:[...t.smblsRoot?[m(t.smblsRoot,"node_modules")]:[],...t.projectRoot?[m(t.projectRoot,"node_modules")]:[],...t.smblsRoot?[m(t.smblsRoot,"..","..","node_modules")]:[]].filter(l=>y(l)),supported:{"import-attributes":!1},external:["fs","path","os","crypto","url","http","https","stream","util","events","buffer","child_process","worker_threads","net","tls","dns","dgram","zlib","assert","querystring","string_decoder","readline","perf_hooks","async_hooks","v8","vm","cluster","inspector","module","process","tty","color-contrast-checker","linkedom"]});const p=await import(`file://${n}`);if(process.env.BRENDER_DEBUG)console.log("[brender] Bundle saved:",n);else try{Ct(n)}catch{}return W=p,p},Bt={Box:{},Focusable:{},Block:{display:"block"},Inline:{display:"inline"},Flex:{display:"flex"},InlineFlex:{display:"inline-flex"},Grid:{display:"grid"},InlineGrid:{display:"inline-grid"},Link:{tag:"a",attr:{href:t=>t.href,target:t=>t.target,rel:t=>t.rel}},A:{extends:"Link"},RouteLink:{extends:"Link"},Img:{tag:"img",attr:{src:t=>{let e=t.src;return typeof e=="string"&&e.includes("{{")&&(e=t.call("replaceLiteralsWithObjectFields",e,t.state)),e},alt:t=>t.alt,loading:t=>t.loading}},Image:{extends:"Img"},Button:{tag:"button"},FocusableComponent:{tag:"button"},Form:{tag:"form"},Input:{tag:"input"},TextArea:{tag:"textarea"},Textarea:{tag:"textarea"},Select:{tag:"select"},Label:{tag:"label"},Iframe:{tag:"iframe"},Video:{tag:"video"},Audio:{tag:"audio"},Canvas:{tag:"canvas"},Span:{tag:"span"},P:{tag:"p"},H1:{tag:"h1"},H2:{tag:"h2"},H3:{tag:"h3"},H4:{tag:"h4"},H5:{tag:"h5"},H6:{tag:"h6"},Svg:{tag:"svg",attr:{xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink"}},Text:{tag:"span"}},mt=(t,e="")=>{if(!t||!t.__ref)return{};const o={},n=t.__ref.__brKey;if(n&&(o[e===""?"__root":e]=n),t.__ref.__children)for(const r of t.__ref.__children){const u=t[r];if(u&&u.__ref){const p=e===""?r:`${e}.${r}`;Object.assign(o,mt(u,p))}}return o},dt=async(t,e={})=>{const{route:o="/",pathname:n,state:r,context:u,prefetch:p=!1}=e,l=n||o,c=globalThis.location,s=globalThis.window;(!globalThis.location||globalThis.location.pathname!==l)&&(globalThis.location={pathname:l,href:l,search:"",hash:"",origin:""}),globalThis.window||(globalThis.window={location:globalThis.location});let i;if(p)try{const h=t.pages||{};i={...h};const O=await Pt(t,o);if(O.size){const V=JSON.parse(JSON.stringify(h[o],(v,N)=>{if(typeof N!="function")return N})),U=(v,N)=>{if(!(!v||!N))for(const D in v)typeof v[D]=="function"?N[D]=v[D]:typeof v[D]=="object"&&v[D]!==null&&!Array.isArray(v[D])&&typeof N[D]=="object"&&N[D]!==null&&U(v[D],N[D])};U(h[o],V),qt(V,O),i[o]=V}}catch(h){console.error("[brender] Prefetch error:",h),i=t.pages}let a;if(p)try{a=await vt(t)}catch(h){console.warn("[brender] SSR translation fetch failed:",h.message)}c!==void 0?globalThis.location=c:delete globalThis.location,s!==void 0?globalThis.window=s:delete globalThis.window;const{window:f,document:g}=st(),T=g.body;f.location.pathname=l;const d=globalThis.document,b=globalThis.location;globalThis.document=g,globalThis.location=f.location;const{createDomqlElement:q}=await Nt(),L=F(t.app||{}),S={...t.config||{}};t.polyglot&&!S.polyglot&&(S.polyglot=t.polyglot),t.fetch&&!S.fetch&&(S.fetch=t.fetch),t.router&&!S.router&&(S.router=t.router);for(const h of["useReset","useVariable","useFontImport","useIconSprite","useSvgSprite","useDefaultConfig","useDocumentTheme"])t[h]!=null&&S[h]==null&&(S[h]=t[h]);const _=S.polyglot?{...S.polyglot}:void 0;a&&_&&(_.translations={..._.translations||{},...a});const j=F(t.state||{});(a||_)&&(j.root||(j.root={}),_&&(j.root.lang=j.root.lang||_.defaultLang||"en"),a&&(j.root.translations={...j.root.translations||{},...a})),Mt();const M={state:j,...r?{state:{...j,...r}}:{},dependencies:F(t.dependencies||{}),components:F(t.components||{}),snippets:F(t.snippets||{}),pages:F(i||t.pages||{}),functions:{...t.functions||{},polyglot:Ft,getActiveLang:lt,getLang:lt},methods:t.methods||{},designSystem:F(t.designSystem||{}),files:t.files||{},sharedLibraries:t.sharedLibraries||[],...S,..._?{polyglot:_}:{},document:g,window:f,parent:{node:T},initOptions:{},domqlOptions:{sourcemap:!1},sync:!1,inspect:!1,notifications:!1,...u||{}},k=await Dt();k&&(M.plugins=M.plugins||[],M.plugins.some(h=>h.name==="funcql")||M.plugins.push(k)),rt();const I=await q(L,M),E=p?2e3:50;await new Promise(h=>setTimeout(h,E)),it(T);const w=ct(I),C=mt(I),R=et(t,o,I,I?.state),x=[],$=g.head||g.querySelector("head");if($)for(const h of $.querySelectorAll("style")){if(h.sheet&&h.sheet.cssRules)for(const O of h.sheet.cssRules)O.cssText&&x.push(O.cssText);if(!x.length){const O=h.textContent||"";O&&x.push(O)}}let A=gt(T.innerHTML);if(a){const h=_?.defaultLang||"en",O=a[h]||Object.values(a)[0]||{};A=A.replace(/\{\{\s*([^|{}]+?)\s*\|\s*polyglot\s*\}\}/g,(V,U)=>{const v=U.trim();return O[v]??V})}return d!==void 0?globalThis.document=d:delete globalThis.document,b!==void 0?globalThis.location=b:delete globalThis.location,{html:A,metadata:R,registry:w,brRegistry:C,element:I,emotionCSS:x,document:g,window:f,ssrTranslations:a,prefetchedPages:i}},ye=async(t,e={})=>{const{context:o={}}=e,{window:n,document:r}=st(),u=r.body,{create:p}=await import("@symbo.ls/element"),l=await import("@symbo.ls/utils"),c={...Bt,...o.components||{}},s={...l,...o.utils||{},...o.functions||{}};rt();let i;try{i=p(t,{node:u},"root",{context:{document:r,window:n,...o,components:c,utils:s}})}catch{}it(u);const a=i?ct(i):{};return{html:gt(u.innerHTML),registry:a,element:i}},gt=t=>t.replace(/(<svg\b[^>]*>)([\s\S]*?)(<\/svg>)/gi,(e,o,n,r)=>{if(n.includes("<")){const u=n.replace(/</g,"<").replace(/>/g,">").replace(/&/g,"&").replace(/"/g,'"').replace(/'/g,"'");return o+u+r}return e});let J=null;const Wt=["globalTheme","themeStorageKey","themeRoot","useReset","useVariable","useFontImport","useIconSprite","useSvgSprite","useDocumentTheme","useDefaultConfig","useDefaultIcons","useThemeSuffixedVars","verbose","semanticIcons"],ht=t=>{if(!t||typeof t!="object")return null;if(t.config&&typeof t.config=="object")return t.config;const e={};let o=!1;for(const n of Wt)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n],o=!0);return o?e:t.settings||null},bt=(t,e,o)=>{const n=Object.entries(e||{}).map(([r,u])=>`${o} ${r}: ${u}`).join(`;
|
|
2
2
|
`);return n?`${o}${t} {
|
|
3
3
|
${n};
|
|
4
|
-
${o}}`:""},
|
|
4
|
+
${o}}`:""},Jt=t=>Object.entries(t||{}).map(([e,o])=>{if(!e.startsWith("@"))return bt(e,o,"");const n=Object.entries(o||{}).map(([r,u])=>bt(r,u," ")).filter(Boolean).join(`
|
|
5
5
|
`);return n?`${e} {
|
|
6
6
|
${n}
|
|
7
7
|
}`:""}).filter(Boolean).join(`
|
|
8
8
|
|
|
9
|
-
`),
|
|
9
|
+
`),yt=async(t,e)=>{if(J)return J;try{const{existsSync:o,writeFileSync:n,unlinkSync:r}=await import("fs"),{tmpdir:u}=await import("os"),{randomBytes:p}=await import("crypto");try{u()}catch{return{}}const l=await import("esbuild"),c=JSON.stringify(t||{},at()),s=JSON.stringify(e||{},at()),i=Q(u(),`br_global_${p(6).toString("hex")}.mjs`),a=Q(u(),`br_global_${p(6).toString("hex")}_out.mjs`);n(i,`
|
|
10
10
|
import { set, getActiveConfig, getFontFaceString } from '@symbo.ls/scratch'
|
|
11
11
|
// Namespace access for the skin builder: a scratch without it (version
|
|
12
12
|
// skew) gives an empty skin block instead of failing this whole bundle
|
|
@@ -14,7 +14,7 @@ ${n}
|
|
|
14
14
|
import * as scratchNs from '@symbo.ls/scratch'
|
|
15
15
|
import { DEFAULT_CONFIG } from '@symbo.ls/default-config'
|
|
16
16
|
|
|
17
|
-
const ds = ${
|
|
17
|
+
const ds = ${c}
|
|
18
18
|
const cfg = ${s}
|
|
19
19
|
|
|
20
20
|
// Merge with defaults (same as initEmotion)
|
|
@@ -53,35 +53,35 @@ ${n}
|
|
|
53
53
|
// Export as globalThis so we can read it
|
|
54
54
|
globalThis.__BR_GLOBAL_CSS__ = result
|
|
55
55
|
export default result
|
|
56
|
-
`);const
|
|
57
|
-
`);let
|
|
58
|
-
${
|
|
59
|
-
}`:"";const
|
|
60
|
-
`);return w
|
|
56
|
+
`);const f=ft(),g={name:"workspace-resolve",setup(w){w.onResolve({filter:/^@symbo\.ls\//},C=>{const R=C.path.replace("@symbo.ls/","");if(f.isMonorepo){for(const $ of["packages","plugins"]){const A=m(f.monorepoRoot,$,R,"src","index.js");if(o(A))return{path:A};const h=m(f.monorepoRoot,$,R,"index.js");if(o(h))return{path:h}}const x=m(f.monorepoRoot,"packages","default-config","blank","index.js");if(R==="default-config"&&o(x))return{path:x}}}),w.onResolve({filter:/^@domql\//},C=>{const R=C.path.replace("@symbo.ls/","");if(f.isMonorepo){const x=m(f.monorepoRoot,"packages","domql","packages",R,"src","index.js");if(o(x))return{path:x}}else{const x=Y(f,R,"src","index.js");if(x&&o(x))return{path:x};const $=Y(f,R,"index.js");if($&&o($))return{path:$}}})}};await l.build({entryPoints:[i],bundle:!0,format:"esm",platform:"node",outfile:a,write:!0,logLevel:"silent",plugins:[g],nodePaths:f.isMonorepo?[m(f.monorepoRoot,"node_modules")]:[...f.smblsRoot?[m(f.smblsRoot,"node_modules")]:[],...f.projectRoot?[m(f.projectRoot,"node_modules")]:[],...f.smblsRoot?[m(f.smblsRoot,"..","..","node_modules")]:[]].filter(w=>o(w)),external:["fs","path","os","crypto","url","http","https","stream","util","events","buffer","child_process","worker_threads","net","tls","dns","dgram","zlib","assert","querystring","string_decoder","readline","perf_hooks","async_hooks","v8","vm","cluster","inspector","module","process","tty","color-contrast-checker"]});const d=(await import(`file://${a}`)).default||{};try{r(i)}catch{}try{r(a)}catch{}const b=d.CSS_VARS||{},q=d.CSS_MEDIA_VARS||{},L=d.RESET||{},S=d.ANIMATION||{},_=Object.entries(b).map(([w,C])=>` ${w}: ${C}`).join(`;
|
|
57
|
+
`);let j=_?`:root {
|
|
58
|
+
${_};
|
|
59
|
+
}`:"";const M=Object.entries(q).map(([w,C])=>{const R=Object.entries(C).map(([x,$])=>` ${x}: ${$}`).join(`;
|
|
60
|
+
`);return R?w.startsWith("@media")?`${w} {
|
|
61
61
|
:root:not([data-theme]) {
|
|
62
|
-
${
|
|
62
|
+
${R};
|
|
63
63
|
}
|
|
64
|
-
}`:`${
|
|
65
|
-
${
|
|
64
|
+
}`:`${w} {
|
|
65
|
+
${R};
|
|
66
66
|
}`:""}).filter(Boolean).join(`
|
|
67
67
|
|
|
68
|
-
`);
|
|
68
|
+
`);M&&(j+=`
|
|
69
69
|
|
|
70
|
-
`+
|
|
70
|
+
`+M);const k=Jt(d.CSS_SKIN_RULES);k&&(j+=(j?`
|
|
71
71
|
|
|
72
|
-
`:"")+
|
|
73
|
-
`);
|
|
74
|
-
${
|
|
75
|
-
}`)}return
|
|
76
|
-
`)},
|
|
77
|
-
`),
|
|
72
|
+
`:"")+k);const I=kt(L),E=[];for(const w in S){const C=S[w];if(!C||typeof C!="object")continue;const R=Object.entries(C).map(([x,$])=>{if(typeof $!="object")return"";const A=Object.entries($).map(([h,O])=>`${G(h)}: ${O}`).join("; ");return` ${x} { ${A}; }`}).join(`
|
|
73
|
+
`);E.push(`@keyframes ${w} {
|
|
74
|
+
${R}
|
|
75
|
+
}`)}return J={rootRule:j,resetRules:I,fontFaceCSS:"",keyframeRules:E.join(`
|
|
76
|
+
`)},J}catch(o){return console.warn("generateGlobalCSS failed:",o.message,o.stack),J={rootRule:"",resetRules:"",fontFaceCSS:"",keyframeRules:""},J}};let Z=new Set;const Se=()=>{J=null,Z=new Set},Re=()=>Array.from(Z).join(`
|
|
77
|
+
`),je=(t,e)=>t.replace(/<style data-emotion="smbls">[\s\S]*?<\/style>/,e?`<style data-emotion="smbls">
|
|
78
78
|
${e}
|
|
79
|
-
</style>`:""),
|
|
80
|
-
`):"",globalCSS:
|
|
81
|
-
`),
|
|
82
|
-
`)}const
|
|
83
|
-
`:"";
|
|
84
|
-
<script type="module" src="${
|
|
79
|
+
</style>`:""),we=async(t,e={})=>{const{route:o="/",pathname:n}=e,r=await dt(t,{route:o,pathname:n,prefetch:!0});if(!r)return null;const u=t.designSystem||{},p=await yt(u,ht(t));let l=null,c=null;try{const s=r.element,i=s?.context?.polyglot||t.polyglot||t.config?.polyglot;if(c=s?.state?.root?.lang||i?.defaultLang||"en",r.prefetchedPages&&r.prefetchedPages[o]){const a=r.prefetchedPages[o],f=(g,T={})=>{if(!g||typeof g!="object")return T;if(g.state&&typeof g.state=="object")for(const[d,b]of Object.entries(g.state))b!=null&&typeof b!="function"&&(T[d]=b);for(const[d,b]of Object.entries(g))d==="state"||d==="props"||d==="attr"||d==="on"||d==="define"||d==="__ref"||d.startsWith("__")||b&&typeof b=="object"&&!Array.isArray(b)&&f(b,T);return T};l=f(a)}}catch{}return{html:r.html,css:r.emotionCSS?r.emotionCSS.join(`
|
|
80
|
+
`):"",globalCSS:p,resetCss:p.resetRules||kt(u.reset),fontLinks:$t(u),metadata:r.metadata||et(t,o),brKeyCount:r.registry?Object.keys(r.registry).length:0,brRegistry:r.brRegistry||{},ssrTranslations:r.ssrTranslations,prefetchedState:l,activeLang:c}},xe=async(t,e="/",o={})=>{const{lang:n,themeColor:r,isr:u,hydrate:p=!0,prefetch:l=!0,shell:c}=o,s=n||t.state?.lang||t.app?.metadata?.lang||"en",i=await dt(t,{route:e,prefetch:l});if(!i)return null;const a={...i.metadata};if(r&&(a["theme-color"]=r),typeof c=="string"&&!t.name){const k="\0brender-undeclared-title\0";if(et({...t,name:k},e,i.element,i.element?.state).title===k)for(const E of["title","og:title","twitter:title"])a[E]===i.metadata.title&&delete a[E]}const f=At(a);if(i.emotionCSS&&i.emotionCSS.length)for(const k of i.emotionCSS)k&&Z.add(k);const g=Array.from(Z).join(`
|
|
81
|
+
`),T=t.designSystem||{},d=await yt(T,ht(t)),b=$t(T),q=Object.keys(i.registry).length;let L="";if(u&&u.clientScript){const k=Number.isInteger(o.depth)?o.depth:e==="/"?0:e.replace(/^\/|\/$/g,"").split("/").length,I=k>0?"../".repeat(k):"./";if(p){let E="";if(i.ssrTranslations){const R=t.polyglot||t.config?.polyglot,x=R?.storagePrefix||"",$=R?.storageLangKey||"",A=[];for(const h in i.ssrTranslations){const O=i.ssrTranslations[h];O&&typeof O=="object"&&A.push(`localStorage.setItem(${JSON.stringify(x+h)},${JSON.stringify(JSON.stringify(O))})`)}if($){const h=R?.defaultLang||"en";A.push(`if(!localStorage.getItem(${JSON.stringify($)}))localStorage.setItem(${JSON.stringify($)},${JSON.stringify(h)})`)}A.length&&(E=`<script>try{${A.join(";")}}catch(e){}<\/script>
|
|
82
|
+
`)}const w=i.brRegistry&&Object.keys(i.brRegistry).length?JSON.stringify(i.brRegistry):null,C=w?`<script>window.__BR_REGISTRY__=${w}<\/script>
|
|
83
|
+
`:"";L=`${E}${C}<script>window.__BRENDER__=true<\/script>
|
|
84
|
+
<script type="module" src="${I}${u.clientScript}"><\/script>`}else L=`<script type="module">
|
|
85
85
|
{
|
|
86
86
|
const brEls = document.querySelectorAll('body > :not(script):not(style)')
|
|
87
87
|
const observer = new MutationObserver((mutations) => {
|
|
@@ -98,30 +98,37 @@ ${e}
|
|
|
98
98
|
observer.observe(document.body, { childList: true })
|
|
99
99
|
}
|
|
100
100
|
<\/script>
|
|
101
|
-
<script type="module" src="${
|
|
102
|
-
|
|
101
|
+
<script type="module" src="${I}${u.clientScript}"><\/script>`}const S={...t.config||{}};t.polyglot&&!S.polyglot&&(S.polyglot=t.polyglot);const _=S.polyglot;let j=f;if(_){const k=_.defaultLang||"en",E={..._.translations||{},...i.ssrTranslations||{}}[k]||{};j=f.replace(/\{\{\s*([^|{}]+?)\s*\|\s*polyglot\s*\}\}/g,(w,C)=>{const R=C.trim();return E[R]??w})}if(typeof c=="string"){const k=[d.fontFaceCSS?`<style>${d.fontFaceCSS}</style>`:"",`<style>
|
|
102
|
+
${d.rootRule||""}
|
|
103
|
+
${d.resetRules||""}
|
|
104
|
+
${d.keyframeRules||""}
|
|
105
|
+
</style>`,g?`<style data-emotion="smbls">
|
|
106
|
+
${g}
|
|
107
|
+
</style>`:""];return{html:Et(c,{headTags:j,headEnd:[b,...k].filter(Boolean).join(`
|
|
108
|
+
`),body:i.html,depth:Number.isInteger(o.depth)?o.depth:0}),route:e,brKeyCount:q,shell:!0}}return{html:`<!DOCTYPE html>
|
|
109
|
+
<html lang="${s}">
|
|
103
110
|
<head>
|
|
104
|
-
${
|
|
105
|
-
${
|
|
106
|
-
${
|
|
111
|
+
${j}
|
|
112
|
+
${b}
|
|
113
|
+
${d.fontFaceCSS?`<style>${d.fontFaceCSS}</style>`:""}
|
|
107
114
|
<style>
|
|
108
|
-
${
|
|
109
|
-
${
|
|
110
|
-
${
|
|
115
|
+
${d.rootRule||""}
|
|
116
|
+
${d.resetRules||""}
|
|
117
|
+
${d.keyframeRules||""}
|
|
111
118
|
</style>
|
|
112
|
-
${
|
|
113
|
-
${
|
|
119
|
+
${g?`<style data-emotion="smbls">
|
|
120
|
+
${g}
|
|
114
121
|
</style>`:""}
|
|
115
122
|
</head>
|
|
116
123
|
<body>
|
|
117
|
-
${
|
|
118
|
-
${
|
|
124
|
+
${i.html}
|
|
125
|
+
${L}
|
|
119
126
|
</body>
|
|
120
|
-
</html>`,route:e,brKeyCount:
|
|
121
|
-
`)},
|
|
122
|
-
`);
|
|
123
|
-
${
|
|
124
|
-
}`)}return[...
|
|
125
|
-
`)},
|
|
126
|
-
`)}
|
|
127
|
-
`):""};export{
|
|
127
|
+
</html>`,route:e,brKeyCount:q}},Gt={U:-6,V:-5,W:-4,X:-3,Y:-2,Z:-1,A:0,B:1,C:2,D:3,E:4,F:5,G:6,H:7,I:8,J:9,K:10,L:11,M:12,N:13,O:14,P:15},St=new Set(["padding","paddingTop","paddingRight","paddingBottom","paddingLeft","paddingBlock","paddingInline","paddingBlockStart","paddingBlockEnd","paddingInlineStart","paddingInlineEnd","margin","marginTop","marginRight","marginBottom","marginLeft","marginBlock","marginInline","marginBlockStart","marginBlockEnd","marginInlineStart","marginInlineEnd","gap","rowGap","columnGap","top","right","bottom","left","width","height","minWidth","maxWidth","minHeight","maxHeight","flexBasis","fontSize","lineHeight","letterSpacing","borderWidth","borderRadius","outlineWidth","outlineOffset","inset","insetBlock","insetInline","boxSize","round"]),Rt=(t,e)=>{if(!t||typeof t!="string"||!e)return null;const o=e.base||16,n=e.ratio||1.618,r=e.unit||"px",u=e.subSequence!==!1;if(t.includes(" "))return t.split(" ").map(b=>b==="-"||b===""?b:Rt(b,e)||b).join(" ");if(/^(none|auto|inherit|initial|unset|0)$/i.test(t)||/\d+(px|em|rem|%|vh|vw|vmin|vmax|ch|ex|cm|mm|in|pt|pc|fr|s|ms)$/i.test(t)||/^(#|rgb|hsl|var\()/i.test(t))return null;const p=t.startsWith("-"),c=(p?t.slice(1):t).match(/^([A-Z])(\d)?$/i);if(!c)return null;const s=c[1].toUpperCase(),i=c[2]?parseInt(c[2]):0,a=Gt[s];if(a===void 0)return null;let f=o*Math.pow(n,a);if(i>0&&u){const d=o*Math.pow(n,a+1),q=(d-f)/n,L=d-q,S=f+q,_=(L+S)/2,j=~~d-~~f>16?[L,_,S]:[L,S];i<=j.length&&(f=j[i-1])}const g=Math.round(f*100)/100;return`${p?"-":""}${g}${r}`},Ht=new Set([...St].map(t=>t.replace(/[A-Z]/g,e=>"-"+e.toLowerCase()))),X=(t,e,o)=>{if(typeof e!="string")return e;if(Vt.has(t)){const n=o?.color||{};if(n[e])return n[e]}if(St.has(t)||Ht.has(t)){const n=o?.spacing||{},r=Rt(e,n);if(r)return r}return e},Vt=new Set(["color","background","backgroundColor","borderColor","borderTopColor","borderRightColor","borderBottomColor","borderLeftColor","outlineColor","fill","stroke"]),jt=new Set(["href","src","alt","title","id","name","type","value","placeholder","target","rel","loading","srcset","sizes","media","role","tabindex","for","action","method","enctype","autocomplete","autofocus","theme","__element","update","childrenAs","childExtends","childProps","children"]),G=t=>t.replace(/[A-Z]/g,e=>"-"+e.toLowerCase()),wt=(t,e)=>{if(typeof e>"u"||e===null)return null;if(t==="flow"&&typeof e=="string"){let[o,n]=(e||"row").split(" ");return(e.startsWith("x")||e==="row")&&(o="row"),(e.startsWith("y")||e==="column")&&(o="column"),{display:"flex","flex-flow":(o||"")+" "+(n||"")}}if(t==="wrap")return{display:"flex","flex-wrap":e};if((t==="align"||t==="flexAlign")&&typeof e=="string"){const[o,n]=e.split(" "),r={display:"flex","align-items":o};return n&&(r["justify-content"]=n),r}if(t==="gridAlign"&&typeof e=="string"){const[o,n]=e.split(" "),r={display:"grid","align-items":o};return n&&(r["justify-content"]=n),r}if(t==="flexFlow"&&typeof e=="string"){let[o,n]=(e||"row").split(" ");return(e.startsWith("x")||e==="row")&&(o="row"),(e.startsWith("y")||e==="column")&&(o="column"),{display:"flex","flex-flow":(o||"")+" "+(n||"")}}if(t==="flexWrap")return{display:"flex","flex-wrap":e};if(t==="backgroundImage"&&typeof e=="string"&&!e.startsWith("url(")&&!e.startsWith("linear-gradient")&&!e.startsWith("radial-gradient")&&!e.startsWith("none"))return{"background-image":`url(${e})`};if(t==="round"||t==="borderRadius"&&e)return{"border-radius":typeof e=="number"?e+"px":e};if(t==="boxSize"&&typeof e=="string"){const[o,n]=e.split(" ");return{height:o,width:n||o}}if(t==="widthRange"&&typeof e=="string"){const[o,n]=e.split(" ");return{"min-width":o,"max-width":n||o}}if(t==="heightRange"&&typeof e=="string"){const[o,n]=e.split(" ");return{"min-height":o,"max-height":n||o}}return t==="column"?{"grid-column":e}:t==="columns"?{"grid-template-columns":e}:t==="templateColumns"?{"grid-template-columns":e}:t==="row"?{"grid-row":e}:t==="rows"?{"grid-template-rows":e}:t==="templateRows"?{"grid-template-rows":e}:t==="area"?{"grid-area":e}:t==="template"?{"grid-template":e}:t==="templateAreas"?{"grid-template-areas":e}:t==="autoColumns"?{"grid-auto-columns":e}:t==="autoRows"?{"grid-auto-rows":e}:t==="autoFlow"?{"grid-auto-flow":e}:t==="columnStart"?{"grid-column-start":e}:t==="rowStart"?{"grid-row-start":e}:null},xt=(t,e)=>{const o={};for(const n in t){const r=t[n],u=wt(n,r);if(u){for(const p in u)o[p]=X(p,u[p],e);continue}typeof r!="string"&&typeof r!="number"||(o[G(n)]=X(n,r,e))}return o},Kt=(t,e,o)=>{const n={},r={},u={};for(const p in t){const l=t[p];if(p.charCodeAt(0)===64&&typeof l=="object"){const s=o?.[p.slice(1)];if(s){const i=xt(l,e);Object.keys(i).length&&(r[s]=i)}continue}if(p.charCodeAt(0)===58&&typeof l=="object"){const s=xt(l,e);Object.keys(s).length&&(u[p]=s);continue}if(typeof l!="string"&&typeof l!="number"||p.charCodeAt(0)>=65&&p.charCodeAt(0)<=90||jt.has(p))continue;const c=wt(p,l);if(c){for(const s in c)n[s]=X(s,c[s],e);continue}n[G(p)]=X(p,l,e)}return{base:n,mediaRules:r,pseudoRules:u}},Ut=(t,{base:e,mediaRules:o,pseudoRules:n})=>{const r=[],u=Object.entries(e).map(([p,l])=>`${p}: ${l}`).join("; ");u&&r.push(`${t} { ${u}; }`);for(const[p,l]of Object.entries(n)){const c=Object.entries(l).map(([s,i])=>`${s}: ${i}`).join("; ");c&&r.push(`${t}${p} { ${c}; }`)}for(const[p,l]of Object.entries(o)){const c=Object.entries(l).map(([i,a])=>`${i}: ${a}`).join("; "),s=p.startsWith("@")?p:`@media ${p}`;c&&r.push(`${s} { ${t} { ${c}; } }`)}return r.join(`
|
|
128
|
+
`)},_t={Flex:{display:"flex"},InlineFlex:{display:"inline-flex"},Grid:{display:"grid"},InlineGrid:{display:"inline-grid"},Block:{display:"block"},Inline:{display:"inline"}},zt=t=>{const e=t.__ref?.__extends;if(!e||!Array.isArray(e))return null;for(const o of e)if(_t[o])return _t[o];return null},Yt=t=>{let e;for(const o in t){if(typeof t[o]!="function"||jt.has(o)||o.charCodeAt(0)>=65&&o.charCodeAt(0)<=90||o.startsWith("on")||o.startsWith("__"))continue;e||(e={});let n;try{n=t[o](t,t.state||{})}catch{try{const r={root:{},...t.state||{}};n=t[o](t,r)}catch{}}n!=null&&n!==!1&&(e[o]=n)}return e||t},_e=(t,e)=>{const o=e?.media||{},n=e?.animation||{},r=[],u=new Set,p=new Set,l=s=>{if(!s||!s.__ref)return;const i=Yt(s);if(i&&s.node){const a=s.node.getAttribute?.("class");if(a&&!u.has(a)){u.add(a);const f=Kt(i,e,o),g=zt(s);if(g)for(const[b,q]of Object.entries(g)){const L=G(b);f.base[L]||(f.base[L]=q)}(Object.keys(f.base).length||Object.keys(f.mediaRules).length||Object.keys(f.pseudoRules).length)&&r.push(Ut("."+a.split(" ")[0],f));const d=i.animation||i.animationName;if(typeof d=="string"){const b=d.split(" ")[0];n[b]&&p.add(b)}}}if(s.__ref?.__children)for(const a of s.__ref.__children)s[a]?.__ref&&l(s[a])};l(t);const c=[];for(const s of p){const i=n[s],a=Object.entries(i).map(([f,g])=>{const T=Object.entries(g).map(([d,b])=>`${G(d)}: ${b}`).join("; ");return` ${f} { ${T}; }`}).join(`
|
|
129
|
+
`);c.push(`@keyframes ${s} {
|
|
130
|
+
${a}
|
|
131
|
+
}`)}return[...c,...r].join(`
|
|
132
|
+
`)},kt=t=>{if(!t)return"";const e=[];for(const[o,n]of Object.entries(t)){if(!n||typeof n!="object")continue;const r=[],u=[];for(const[p,l]of Object.entries(n)){if(typeof l=="object"&&l!==null){if(p.startsWith("@media")||p.startsWith("@")){const c=Object.entries(l).filter(([,s])=>typeof s!="object").map(([s,i])=>`${G(s)}: ${i}`).join("; ");c&&u.push(`${p} { ${o} { ${c}; } }`)}continue}r.push(`${G(p)}: ${l}`)}r.length&&e.push(`${o} { ${r.join("; ")}; }`),e.push(...u)}return e.join(`
|
|
133
|
+
`)},$t=t=>{if(!t)return"";const e=t.font_family||t.fontFamily||{},o=new Set;for(const r of Object.values(e)){if(typeof r!="string")continue;const u=r.match(/'([^']+)'/);u&&o.add(u[1])}return o.size?['<link rel="preconnect" href="https://fonts.googleapis.com">','<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>',`<link href="https://fonts.googleapis.com/css2?${[...o].map(r=>`family=${r.replace(/\s+/g,"+")}:wght@300;400;500;600;700`).join("&")}&display=swap" rel="stylesheet">`].join(`
|
|
134
|
+
`):""};export{ut as SMBLS_SSR_SPECIFIER,Re as getAccumulatedEmotionCSS,dt as render,ye as renderElement,xe as renderPage,we as renderRoute,je as replaceEmotionCSS,Se as resetGlobalCSSCache,Jt as skinRulesToCSS};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
const _=/<head(\s[^>]*)?>/i,A=/<body(\s[^>]*)?>/i,d=(e,n)=>{const s=new RegExp(`\\s${n}\\s*=\\s*("([^"]*)"|'([^']*)'|([^\\s>]+))`,"i").exec(e);return s?s[2]??s[3]??s[4]??"":null},O=e=>{const n=e.slice(0,12).toLowerCase();if(n.startsWith("<title"))return"title";if(n.startsWith("<meta")){if(d(e,"charset")!==null)return"charset";for(const s of["name","property","http-equiv","itemprop"]){const r=d(e,s);if(r!==null)return`${s}:${r.toLowerCase()}`}return null}if(n.startsWith("<link")){const s=(d(e,"rel")||"").toLowerCase();return s==="canonical"?"link:canonical":s==="icon"||s==="shortcut icon"?`link:${s}:${d(e,"href")||""}`:s==="alternate"&&d(e,"hreflang")!==null?`link:alternate:${(d(e,"hreflang")||"").toLowerCase()}`:null}return null},y=/<title[^>]*>[\s\S]*?<\/title\s*>|<meta\b[^>]*>|<link\b[^>]*>/gi,C=/<(script|style|noscript|template|textarea)\b[\s\S]*?<\/\1\s*>/gi,k=e=>[...e.matchAll(C)].map(n=>[n.index,n.index+n[0].length,n[0]]),E=(e,n,s=r=>r)=>{let r="",i=0;for(const[t,a,c]of k(e))r+=n(e.slice(i,t))+s(c),i=a;return r+n(e.slice(i))},f=(e,n,s=0)=>{const r=k(e),i=new RegExp(n.source,n.flags.includes("g")?n.flags:n.flags+"g");i.lastIndex=s;let t;for(;t=i.exec(e);){const a=t.index,c=r.find(([u,h])=>a>u&&a<h);if(!c)return t;i.lastIndex=c[1]}return null},L=e=>!!e&&!/^(?:[a-z][a-z0-9+.-]*:|\/|#|\?)/i.test(e),R=(e,n)=>e.replace(/(\s(?:src|href)\s*=\s*)(?:(["'])([^"']*)\2|([^\s"'>]+))/gi,(s,r,i,t,a)=>{const c=i?t:a;if(!L(c))return s;const u=i||"";return`${r}${u}${n}${c.replace(/^\.\//,"")}${u}`}),B=(e,n)=>{if(!n)return e;const s="../".repeat(n);return E(e,r=>r.replace(/<link\b[^>]*>/gi,i=>R(i,s)),r=>/^<script\b/i.test(r)?r.replace(/^<script\b[^>]*>/i,i=>R(i,s)):r)},D=(e,{headTags:n="",headEnd:s="",body:r="",depth:i=0}={})=>{if(typeof e!="string"||!e.trim())throw new Error("brender: the shell document is empty");let t=B(e,i);const a=new Set;for(const o of n.matchAll(y)){const l=O(o[0]);l&&a.add(l)}const c=o=>E(o,l=>l.replace(y,b=>a.has(O(b))?"":b)),u=n?`
|
|
2
|
+
${n}`:"",h=s?`
|
|
3
|
+
${s}
|
|
4
|
+
`:"",p=f(t,_),g=p&&f(t,/<\/head\s*>/i,p.index);if(p&&g){const o=p.index+p[0].length,l=g.index;t=t.slice(0,o)+u+c(t.slice(o,l))+h+t.slice(l)}else{t=c(t);const o=f(t,/<html(\s[^>]*)?>/i)||f(t,/<!doctype[^>]*>/i),l=o?o.index+o[0].length:0;t=t.slice(0,l)+u+h+t.slice(l)}const w=`
|
|
5
|
+
<script>window.__BRENDER__=true<\/script>
|
|
6
|
+
${r}
|
|
7
|
+
`,x=f(t,A);if(x){const o=x.index+x[0].length;return t.slice(0,o)+w+t.slice(o)}const m=f(t,/<\/(?:body|html)\s*>/i),$=m?m.index:t.length;return t.slice(0,$)+w+t.slice($)};export{D as composeIntoShell,O as headTagKey};
|
package/env.js
CHANGED
|
@@ -26,7 +26,9 @@ export const createEnv = (html = '<!DOCTYPE html><html><head></head><body></body
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
if (!window.location) {
|
|
29
|
-
|
|
29
|
+
// A complete Location shape (host/hostname/protocol/href too): framework
|
|
30
|
+
// modules read these at module load, before any route is set.
|
|
31
|
+
window.location = { pathname: '/', search: '', hash: '', origin: 'http://localhost', protocol: 'http:', host: 'localhost', hostname: 'localhost', port: '', href: 'http://localhost/' }
|
|
30
32
|
}
|
|
31
33
|
if (!window.URL) {
|
|
32
34
|
window.URL = URL
|
package/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { extractMetadata, generateHeadHtml } from './metadata.js'
|
|
|
6
6
|
import { collectBrNodes, hydrate } from './hydrate.js'
|
|
7
7
|
import { generateSitemap } from './sitemap.js'
|
|
8
8
|
import { prefetchPageData, injectPrefetchedState } from './prefetch.js'
|
|
9
|
+
import { composeIntoShell } from './shell.js'
|
|
9
10
|
|
|
10
11
|
export {
|
|
11
12
|
createEnv,
|
|
@@ -27,7 +28,8 @@ export {
|
|
|
27
28
|
hydrate,
|
|
28
29
|
generateSitemap,
|
|
29
30
|
prefetchPageData,
|
|
30
|
-
injectPrefetchedState
|
|
31
|
+
injectPrefetchedState,
|
|
32
|
+
composeIntoShell
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
export default {
|
|
@@ -50,5 +52,6 @@ export default {
|
|
|
50
52
|
hydrate,
|
|
51
53
|
generateSitemap,
|
|
52
54
|
prefetchPageData,
|
|
53
|
-
injectPrefetchedState
|
|
55
|
+
injectPrefetchedState,
|
|
56
|
+
composeIntoShell
|
|
54
57
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbo.ls/brender",
|
|
3
|
-
"version": "3.14.
|
|
3
|
+
"version": "3.14.662",
|
|
4
4
|
"license": "CC-BY-NC-4.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./dist/esm/index.js",
|
|
@@ -46,15 +46,19 @@
|
|
|
46
46
|
"prepublishOnly": "npm run build"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@symbo.ls/css": "^3.14.
|
|
50
|
-
"@symbo.ls/default-config": "^3.14.
|
|
51
|
-
"@symbo.ls/element": "^3.14.
|
|
52
|
-
"@symbo.ls/
|
|
53
|
-
"@symbo.ls/
|
|
54
|
-
"@symbo.ls/
|
|
49
|
+
"@symbo.ls/css": "^3.14.774",
|
|
50
|
+
"@symbo.ls/default-config": "^3.14.785",
|
|
51
|
+
"@symbo.ls/element": "^3.14.799",
|
|
52
|
+
"@symbo.ls/fetch": "^3.14.662",
|
|
53
|
+
"@symbo.ls/helmet": "^3.14.669",
|
|
54
|
+
"@symbo.ls/scratch": "^3.14.786",
|
|
55
|
+
"@symbo.ls/utils": "^3.14.784",
|
|
55
56
|
"esbuild": "^0.28.1",
|
|
56
57
|
"linkedom": "^0.16.8"
|
|
57
58
|
},
|
|
59
|
+
"peerDependencies": {
|
|
60
|
+
"smbls": "^3.14.827"
|
|
61
|
+
},
|
|
58
62
|
"optionalDependencies": {
|
|
59
63
|
"@symbo.ls/funcql": "^3.14.0"
|
|
60
64
|
},
|
package/prefetch.js
CHANGED
|
@@ -106,32 +106,71 @@ const collectFetchDeclarations = (def, path = '') => {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
/**
|
|
109
|
-
* Create an SSR adapter from project config
|
|
109
|
+
* Create an SSR adapter from project config — the SAME adapter the client's
|
|
110
|
+
* declarative `fetch` uses, resolved through @symbo.ls/fetch's public
|
|
111
|
+
* `resolveDb` (config.fetch / config.db: `{ adapter: 'rest', url, headers }`,
|
|
112
|
+
* or an adapter object with its own select/rpc).
|
|
110
113
|
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
114
|
+
* This used to be a stub that always returned null (xma.info report
|
|
115
|
+
* sub-problem 2): the Supabase SSR path had been removed, nothing replaced it,
|
|
116
|
+
* so server renders never had data or fetched translations.
|
|
117
|
+
*
|
|
118
|
+
* Not every adapter can run on the server: the platform's 'sdk' adapter is
|
|
119
|
+
* registered by the page runtime, which a server render does not have. For a
|
|
120
|
+
* name @symbo.ls/fetch cannot resolve right now (`hasAdapter`), this returns
|
|
121
|
+
* null at once — the page renders without prefetched data and the client's
|
|
122
|
+
* fetch loads it — instead of letting resolveDb wait for a registration that
|
|
123
|
+
* never comes.
|
|
116
124
|
*/
|
|
125
|
+
const BUILTIN_ADAPTER_NAMES = ['rest', 'local'] // @symbo.ls/fetch without hasAdapter()
|
|
126
|
+
const _adapterCache = new WeakMap()
|
|
127
|
+
|
|
117
128
|
const createSSRAdapter = async (dbConfig) => {
|
|
118
129
|
if (!dbConfig) return null
|
|
119
|
-
return
|
|
130
|
+
if (isObject(dbConfig) && (isFunction(dbConfig.select) || isFunction(dbConfig.rpc))) return dbConfig
|
|
131
|
+
const config = typeof dbConfig === 'string' ? { adapter: dbConfig } : dbConfig
|
|
132
|
+
if (!isObject(config) || typeof config.adapter !== 'string') return null
|
|
133
|
+
if (_adapterCache.has(config)) return _adapterCache.get(config)
|
|
134
|
+
|
|
135
|
+
const pending = (async () => {
|
|
136
|
+
let fetchPkg
|
|
137
|
+
try {
|
|
138
|
+
fetchPkg = await import('@symbo.ls/fetch')
|
|
139
|
+
} catch {
|
|
140
|
+
return null
|
|
141
|
+
}
|
|
142
|
+
const known = isFunction(fetchPkg.hasAdapter)
|
|
143
|
+
? fetchPkg.hasAdapter(config.adapter)
|
|
144
|
+
: BUILTIN_ADAPTER_NAMES.includes(config.adapter)
|
|
145
|
+
if (!known) return null
|
|
146
|
+
try {
|
|
147
|
+
return await fetchPkg.resolveDb(config)
|
|
148
|
+
} catch (e) {
|
|
149
|
+
console.warn(`[brender] SSR adapter "${config.adapter}" unavailable: ${e.message}`)
|
|
150
|
+
return null
|
|
151
|
+
}
|
|
152
|
+
})()
|
|
153
|
+
_adapterCache.set(config, pending)
|
|
154
|
+
return pending
|
|
120
155
|
}
|
|
121
156
|
|
|
157
|
+
export { createSSRAdapter }
|
|
158
|
+
|
|
122
159
|
/**
|
|
123
160
|
* Execute a single fetch config against the adapter.
|
|
124
161
|
* Returns the fetched data, or null on error.
|
|
125
162
|
*/
|
|
126
163
|
const executeSingle = async (adapter, config) => {
|
|
127
164
|
try {
|
|
128
|
-
const { method, from, params, transform, limit, offset, order, single } = config
|
|
165
|
+
const { method, from, params, transform, limit, offset, order, single, headers, baseUrl } = config
|
|
129
166
|
let result
|
|
130
167
|
|
|
131
168
|
if (method === 'rpc') {
|
|
132
|
-
|
|
169
|
+
if (!isFunction(adapter.rpc)) return null
|
|
170
|
+
result = await adapter.rpc({ from, params, headers, baseUrl })
|
|
133
171
|
} else {
|
|
134
|
-
|
|
172
|
+
if (!isFunction(adapter.select)) return null
|
|
173
|
+
result = await adapter.select({ from, select: config.select, params, limit, offset, order, single, headers, baseUrl })
|
|
135
174
|
}
|
|
136
175
|
|
|
137
176
|
let data = result?.data ?? null
|
|
@@ -232,6 +271,7 @@ export const fetchSSRTranslations = async (data) => {
|
|
|
232
271
|
const results = await Promise.allSettled(
|
|
233
272
|
languages.map(async (lang) => {
|
|
234
273
|
try {
|
|
274
|
+
if (!isFunction(adapter.rpc)) return
|
|
235
275
|
const res = await adapter.rpc({
|
|
236
276
|
from: rpcName,
|
|
237
277
|
params: { p_lang: lang, p_cached_version: 0 }
|
package/render.js
CHANGED
|
@@ -8,6 +8,7 @@ import { createEnv } from './env.js'
|
|
|
8
8
|
import { resetKeys, assignKeys, mapKeysToElements } from './keys.js'
|
|
9
9
|
import { extractMetadata, generateHeadHtml } from './metadata.js'
|
|
10
10
|
import { hydrate } from './hydrate.js'
|
|
11
|
+
import { composeIntoShell } from './shell.js'
|
|
11
12
|
import { prefetchPageData, injectPrefetchedState, fetchSSRTranslations } from './prefetch.js'
|
|
12
13
|
|
|
13
14
|
// funcql plugin — enables evaluation of funcql schemas as property values
|
|
@@ -257,26 +258,56 @@ const resolveDomqlPackage = (ws, pkg, ...subpath) => {
|
|
|
257
258
|
return null
|
|
258
259
|
}
|
|
259
260
|
|
|
260
|
-
// ──
|
|
261
|
-
//
|
|
262
|
-
//
|
|
263
|
-
//
|
|
261
|
+
// ── Loading createDomqlElement ────────────────────────────────────────────────
|
|
262
|
+
// Three sources, in this order:
|
|
263
|
+
//
|
|
264
|
+
// 1. The smbls MONOREPO (brender running from plugins/brender next to
|
|
265
|
+
// packages/smbls/src): esbuild-bundle the live SOURCE createDomql.js, so
|
|
266
|
+
// framework development renders what is on disk.
|
|
267
|
+
// 2. An INSTALLED smbls: its supported server-render entry, `smbls/ssr`
|
|
268
|
+
// (createDomqlElement + prepareContext, the same module instance as the
|
|
269
|
+
// `smbls` main entry). The published smbls ships bundles only — no src/,
|
|
270
|
+
// no dist/esm/src/ — so the old "bundle smbls's source" path failed every
|
|
271
|
+
// route of every npm install with "brender: cannot find createDomql.js"
|
|
272
|
+
// (xma.info report item 1). Rendering with the consumer's OWN smbls also
|
|
273
|
+
// keeps the server markup and the client that boots over it on one
|
|
274
|
+
// framework version; a createDomql prebuilt into brender's package would
|
|
275
|
+
// freeze whichever smbls brender was published against.
|
|
276
|
+
// 3. Bundled runtimes (Cloudflare Workers — no import.meta.url, no
|
|
277
|
+
// filesystem): the same `smbls/ssr` specifier, a string literal the
|
|
278
|
+
// worker's bundler resolves and inlines. render.js used to import
|
|
279
|
+
// `./dist/createDomql.bundled.mjs` here, a file nothing ever built, so
|
|
280
|
+
// the mermaid worker fell back to client render on every request
|
|
281
|
+
// (FW-BRENDER-PREBUNDLED-CREATEDOMQL-NEVER-BUILT-WORKER-SSR-FALLS-BACK-1).
|
|
282
|
+
//
|
|
283
|
+
// An installed smbls OLDER than the ssr entry that still carries
|
|
284
|
+
// src/createDomql.js (pre-54131b34d tarballs) keeps the source-bundle path.
|
|
264
285
|
let _cachedCreateDomql = null
|
|
265
286
|
|
|
287
|
+
// Exported for the packaging lock in __tests__/ssrEntryPackaging.test.js —
|
|
288
|
+
// the specifier that test proves the packed smbls tarball serves.
|
|
289
|
+
export const SMBLS_SSR_SPECIFIER = 'smbls/ssr'
|
|
290
|
+
|
|
291
|
+
const importSmblsSsr = async () => {
|
|
292
|
+
// The literal (not the constant) is what a bundler can statically resolve.
|
|
293
|
+
const mod = await import('smbls/ssr')
|
|
294
|
+
if (typeof mod?.createDomqlElement !== 'function') {
|
|
295
|
+
throw new Error('smbls/ssr does not export createDomqlElement')
|
|
296
|
+
}
|
|
297
|
+
return mod
|
|
298
|
+
}
|
|
299
|
+
|
|
266
300
|
const bundleCreateDomql = async () => {
|
|
267
301
|
if (_cachedCreateDomql) return _cachedCreateDomql
|
|
268
302
|
|
|
269
303
|
const ws = detectWorkspace()
|
|
270
304
|
|
|
271
|
-
// In bundled environments (CF Workers), use the pre-bundled createDomql.
|
|
272
|
-
// Generated by: node scripts/prebundle-createDomql.js (or during npm prepublish)
|
|
273
305
|
if (!_brenderRequire) {
|
|
274
306
|
try {
|
|
275
|
-
|
|
276
|
-
_cachedCreateDomql
|
|
277
|
-
return mod
|
|
307
|
+
_cachedCreateDomql = await importSmblsSsr()
|
|
308
|
+
return _cachedCreateDomql
|
|
278
309
|
} catch (err) {
|
|
279
|
-
throw new Error(`brender:
|
|
310
|
+
throw new Error(`brender: ${SMBLS_SSR_SPECIFIER} is not available in this bundled runtime (${err.message}). Bundle smbls ≥ the release that adds the smbls/ssr export alongside @symbo.ls/brender.`)
|
|
280
311
|
}
|
|
281
312
|
}
|
|
282
313
|
|
|
@@ -284,15 +315,25 @@ const bundleCreateDomql = async () => {
|
|
|
284
315
|
let entry
|
|
285
316
|
if (ws.isMonorepo) {
|
|
286
317
|
entry = resolve(ws.monorepoRoot, 'packages', 'smbls', 'src', 'createDomql.js')
|
|
287
|
-
} else
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
318
|
+
} else {
|
|
319
|
+
let ssrError
|
|
320
|
+
try {
|
|
321
|
+
_cachedCreateDomql = await importSmblsSsr()
|
|
322
|
+
return _cachedCreateDomql
|
|
323
|
+
} catch (err) {
|
|
324
|
+
ssrError = err
|
|
325
|
+
}
|
|
326
|
+
// Legacy: an smbls that predates the ssr entry but still ships its source.
|
|
327
|
+
const srcEntry = ws.smblsRoot && resolve(ws.smblsRoot, 'src', 'createDomql.js')
|
|
328
|
+
if (srcEntry && existsSync(srcEntry)) {
|
|
329
|
+
entry = srcEntry
|
|
330
|
+
} else {
|
|
331
|
+
throw new Error(
|
|
332
|
+
`brender: cannot load ${SMBLS_SSR_SPECIFIER} (${ssrError.message}). ` +
|
|
333
|
+
'Server rendering needs smbls installed in this project, at a release that exports smbls/ssr' +
|
|
334
|
+
(ws.smblsRoot ? ` (found smbls at ${ws.smblsRoot}).` : ' (no smbls package found).')
|
|
335
|
+
)
|
|
336
|
+
}
|
|
296
337
|
}
|
|
297
338
|
|
|
298
339
|
const esbuild = await import('esbuild')
|
|
@@ -792,6 +833,15 @@ export const render = async (data, options = {}) => {
|
|
|
792
833
|
// Disable sourcemap tracking in SSR — it causes stack overflows
|
|
793
834
|
// when state contains large data arrays (articles, events, etc.)
|
|
794
835
|
domqlOptions: { sourcemap: false },
|
|
836
|
+
// Editor/live-sync, inspector and toast plugins have no place in server
|
|
837
|
+
// markup. The source-bundle path used to stub @symbo.ls/sync to no-ops at
|
|
838
|
+
// bundle time; the installed `smbls/ssr` entry carries the real plugins,
|
|
839
|
+
// so they are switched off here instead — after `...config`, because a
|
|
840
|
+
// project config that enables them for the browser must not attach
|
|
841
|
+
// SyncComponent / overlays to the pre-rendered DOM.
|
|
842
|
+
sync: false,
|
|
843
|
+
inspect: false,
|
|
844
|
+
notifications: false,
|
|
795
845
|
// Caller overrides
|
|
796
846
|
...(contextOverrides || {})
|
|
797
847
|
}
|
|
@@ -1091,16 +1141,15 @@ const generateGlobalCSS = async (ds, config) => {
|
|
|
1091
1141
|
}
|
|
1092
1142
|
const blank = resolve(ws.monorepoRoot, 'packages', 'default-config', 'blank', 'index.js')
|
|
1093
1143
|
if (pkg === 'default-config' && existsSync(blank)) return { path: blank }
|
|
1094
|
-
} else {
|
|
1095
|
-
const resolved = resolveSymbolsPackage(ws, pkg, 'src', 'index.js')
|
|
1096
|
-
if (resolved && existsSync(resolved)) return { path: resolved }
|
|
1097
|
-
const resolvedIdx = resolveSymbolsPackage(ws, pkg, 'index.js')
|
|
1098
|
-
if (resolvedIdx && existsSync(resolvedIdx)) return { path: resolvedIdx }
|
|
1099
|
-
if (pkg === 'default-config') {
|
|
1100
|
-
const blank = resolveSymbolsPackage(ws, 'default-config', 'blank', 'index.js')
|
|
1101
|
-
if (blank && existsSync(blank)) return { path: blank }
|
|
1102
|
-
}
|
|
1103
1144
|
}
|
|
1145
|
+
// An npm install: no override — esbuild resolves the package's
|
|
1146
|
+
// published `exports` (dist/esm). Forcing the root index.js here
|
|
1147
|
+
// broke every npm consumer: published @symbo.ls/scratch and
|
|
1148
|
+
// @symbo.ls/default-config ship a root index.js that re-exports
|
|
1149
|
+
// ./src/… and ./designSystem/…, which their tarballs do not
|
|
1150
|
+
// contain, so this bundle failed ("Could not resolve
|
|
1151
|
+
// ./src/index.js") and every page lost its :root variables, reset
|
|
1152
|
+
// and @keyframes (found with the xma.info report item 1 repro).
|
|
1104
1153
|
})
|
|
1105
1154
|
build.onResolve({ filter: /^@domql\// }, args => {
|
|
1106
1155
|
const pkg = args.path.replace('@symbo.ls/', '')
|
|
@@ -1324,10 +1373,16 @@ export const renderRoute = async (data, options = {}) => {
|
|
|
1324
1373
|
* @param {object} [options.isr] - ISR options with clientScript path
|
|
1325
1374
|
* @param {boolean} [options.hydrate=true] - Use true hydration (attach to existing DOM) instead of full SPA re-render
|
|
1326
1375
|
* @param {boolean} [options.prefetch=true] - Whether to prefetch data via DB adapter
|
|
1327
|
-
* @
|
|
1376
|
+
* @param {string} [options.shell] - A built app document (e.g. dist/index.html)
|
|
1377
|
+
* to render INTO instead of emitting a standalone document (see shell.js).
|
|
1378
|
+
* The result then carries `shell: true`.
|
|
1379
|
+
* @param {number} [options.depth] - Directory depth of the output file: rebases
|
|
1380
|
+
* the shell's relative asset URLs (with `shell`, default 0) and the ISR
|
|
1381
|
+
* client script path (default: derived from the route).
|
|
1382
|
+
* @returns {Promise<{ html: string, route: string, brKeyCount: number, shell?: true }>}
|
|
1328
1383
|
*/
|
|
1329
1384
|
export const renderPage = async (data, route = '/', options = {}) => {
|
|
1330
|
-
const { lang, themeColor, isr, hydrate = true, prefetch = true } = options
|
|
1385
|
+
const { lang, themeColor, isr, hydrate = true, prefetch = true, shell } = options
|
|
1331
1386
|
|
|
1332
1387
|
// Detect lang from project config, app metadata, or default
|
|
1333
1388
|
const htmlLang = lang || data.state?.lang || data.app?.metadata?.lang || 'en'
|
|
@@ -1338,6 +1393,21 @@ export const renderPage = async (data, route = '/', options = {}) => {
|
|
|
1338
1393
|
|
|
1339
1394
|
const metadata = { ...result.metadata }
|
|
1340
1395
|
if (themeColor) metadata['theme-color'] = themeColor
|
|
1396
|
+
// Rendering into a built shell: a title the project never declared is
|
|
1397
|
+
// extractMetadata's fallback (`data.name || 'Symbols'`), and it must not
|
|
1398
|
+
// replace the title the shell's author wrote — the client keeps that one
|
|
1399
|
+
// too (helmet applies declared metadata only). Probed by handing
|
|
1400
|
+
// extractMetadata a name nothing else can produce; the element and state
|
|
1401
|
+
// go along so a function-valued title still counts as declared.
|
|
1402
|
+
if (typeof shell === 'string' && !data.name) {
|
|
1403
|
+
const probe = `\u0000brender-undeclared-title\u0000`
|
|
1404
|
+
const probed = extractMetadata({ ...data, name: probe }, route, result.element, result.element?.state)
|
|
1405
|
+
if (probed.title === probe) {
|
|
1406
|
+
for (const key of ['title', 'og:title', 'twitter:title']) {
|
|
1407
|
+
if (metadata[key] === result.metadata.title) delete metadata[key]
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1341
1411
|
const headTags = generateHeadHtml(metadata)
|
|
1342
1412
|
|
|
1343
1413
|
// Accumulate emotion CSS from each page render.
|
|
@@ -1363,8 +1433,12 @@ export const renderPage = async (data, route = '/', options = {}) => {
|
|
|
1363
1433
|
// ISR: include client SPA bundle for hydration + data fetching
|
|
1364
1434
|
let isrBody = ''
|
|
1365
1435
|
if (isr && isr.clientScript) {
|
|
1366
|
-
// Calculate relative path from route directory to root
|
|
1367
|
-
|
|
1436
|
+
// Calculate relative path from route directory to root — or take the
|
|
1437
|
+
// caller's: the file a route is written to decides it ('/*' → 404.html
|
|
1438
|
+
// sits at the root, not in a '*' directory).
|
|
1439
|
+
const depth = Number.isInteger(options.depth)
|
|
1440
|
+
? options.depth
|
|
1441
|
+
: route === '/' ? 0 : route.replace(/^\/|\/$/g, '').split('/').length
|
|
1368
1442
|
const prefix = depth > 0 ? '../'.repeat(depth) : './'
|
|
1369
1443
|
|
|
1370
1444
|
if (hydrate) {
|
|
@@ -1446,6 +1520,25 @@ export const renderPage = async (data, route = '/', options = {}) => {
|
|
|
1446
1520
|
})
|
|
1447
1521
|
}
|
|
1448
1522
|
|
|
1523
|
+
// Build path: render INTO the app's own built document (shell.js) — the
|
|
1524
|
+
// standalone document below carries no app code unless `isr` adds a
|
|
1525
|
+
// client bundle, so writing it over a bundler's dist/index.html shipped
|
|
1526
|
+
// pages that never booted.
|
|
1527
|
+
if (typeof shell === 'string') {
|
|
1528
|
+
const styles = [
|
|
1529
|
+
globalCSS.fontFaceCSS ? `<style>${globalCSS.fontFaceCSS}</style>` : '',
|
|
1530
|
+
`<style>\n${globalCSS.rootRule || ''}\n${globalCSS.resetRules || ''}\n${globalCSS.keyframeRules || ''}\n</style>`,
|
|
1531
|
+
emotionCSS ? `<style data-emotion="smbls">\n${emotionCSS}\n</style>` : ''
|
|
1532
|
+
]
|
|
1533
|
+
const html = composeIntoShell(shell, {
|
|
1534
|
+
headTags: resolvedHeadTags,
|
|
1535
|
+
headEnd: [fontLinks, ...styles].filter(Boolean).join('\n'),
|
|
1536
|
+
body: result.html,
|
|
1537
|
+
depth: Number.isInteger(options.depth) ? options.depth : 0
|
|
1538
|
+
})
|
|
1539
|
+
return { html, route, brKeyCount, shell: true }
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1449
1542
|
const html = `<!DOCTYPE html>
|
|
1450
1543
|
<html lang="${htmlLang}">
|
|
1451
1544
|
<head>
|
package/shell.js
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compose a server render INTO an already-built app document (the "shell").
|
|
3
|
+
*
|
|
4
|
+
* `smbls build` with `"brender": true` pre-renders every static route after
|
|
5
|
+
* the bundler has written `dist/index.html`. That file is the only thing that
|
|
6
|
+
* boots the app — it carries the bundle's <script>/<link> tags (parcel, vite)
|
|
7
|
+
* or the inlined runtime and project (symbols-runner). Writing brender's
|
|
8
|
+
* standalone document over it (renderPage without `shell`) shipped pages with
|
|
9
|
+
* no app code at all: static markup that never became interactive. So the
|
|
10
|
+
* build path renders each route and puts the result into a copy of the shell
|
|
11
|
+
* instead:
|
|
12
|
+
*
|
|
13
|
+
* <head> route metadata first (each <title>/<meta>/canonical/icon the
|
|
14
|
+
* render produced replaces the shell's tag with the same identity —
|
|
15
|
+
* never a second <title>), then the shell's own head, then the
|
|
16
|
+
* render's font links and <style> blocks just before </head>.
|
|
17
|
+
* <body> `window.__BRENDER__=true` and the rendered markup right after
|
|
18
|
+
* <body>, BEFORE the shell's scripts. The client's createDomqlElement
|
|
19
|
+
* sees the flag, removes the server markup and its <style> blocks,
|
|
20
|
+
* and renders the app over the same body (packages/smbls
|
|
21
|
+
* createDomql.js fallbackRender) — no duplicated tree.
|
|
22
|
+
*
|
|
23
|
+
* Relative asset URLs (`src="./index.js"`, `href="style.css"`) on the shell's
|
|
24
|
+
* <script>/<link> tags are rebased by `depth` directories, so a route written
|
|
25
|
+
* to `docs/intro.html` or `about/index.html` still loads the bundle.
|
|
26
|
+
*
|
|
27
|
+
* Pure string work on purpose: the shell's inline scripts (symbols-runner
|
|
28
|
+
* inlines the whole runtime) must reach the output byte-for-byte, which a
|
|
29
|
+
* DOM parse + serialize round-trip does not guarantee.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
const HEAD_OPEN = /<head(\s[^>]*)?>/i
|
|
33
|
+
const BODY_OPEN = /<body(\s[^>]*)?>/i
|
|
34
|
+
|
|
35
|
+
const attr = (tag, name) => {
|
|
36
|
+
const m = new RegExp(`\\s${name}\\s*=\\s*("([^"]*)"|'([^']*)'|([^\\s>]+))`, 'i').exec(tag)
|
|
37
|
+
return m ? (m[2] ?? m[3] ?? m[4] ?? '') : null
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// The identity a head tag is deduplicated by — or null when the tag is not
|
|
41
|
+
// one the route's metadata owns (scripts, styles, stylesheets, preloads…).
|
|
42
|
+
export const headTagKey = (tag) => {
|
|
43
|
+
const lower = tag.slice(0, 12).toLowerCase()
|
|
44
|
+
if (lower.startsWith('<title')) return 'title'
|
|
45
|
+
if (lower.startsWith('<meta')) {
|
|
46
|
+
if (attr(tag, 'charset') !== null) return 'charset'
|
|
47
|
+
for (const a of ['name', 'property', 'http-equiv', 'itemprop']) {
|
|
48
|
+
const v = attr(tag, a)
|
|
49
|
+
if (v !== null) return `${a}:${v.toLowerCase()}`
|
|
50
|
+
}
|
|
51
|
+
return null
|
|
52
|
+
}
|
|
53
|
+
if (lower.startsWith('<link')) {
|
|
54
|
+
const rel = (attr(tag, 'rel') || '').toLowerCase()
|
|
55
|
+
if (rel === 'canonical') return 'link:canonical'
|
|
56
|
+
if (rel === 'icon' || rel === 'shortcut icon') return `link:${rel}:${attr(tag, 'href') || ''}`
|
|
57
|
+
if (rel === 'alternate' && attr(tag, 'hreflang') !== null) return `link:alternate:${(attr(tag, 'hreflang') || '').toLowerCase()}`
|
|
58
|
+
return null
|
|
59
|
+
}
|
|
60
|
+
return null
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const HEAD_TAG = /<title[^>]*>[\s\S]*?<\/title\s*>|<meta\b[^>]*>|<link\b[^>]*>/gi
|
|
64
|
+
|
|
65
|
+
// Raw-text blocks: their content is never markup, so nothing below may match
|
|
66
|
+
// inside one. symbols-runner inlines the whole runtime, whose string literals
|
|
67
|
+
// contain `<link rel="icon" href="${href}"`, `<title>`, `<body>`…
|
|
68
|
+
const RAW_BLOCK = /<(script|style|noscript|template|textarea)\b[\s\S]*?<\/\1\s*>/gi
|
|
69
|
+
|
|
70
|
+
const rawRanges = (html) => [...html.matchAll(RAW_BLOCK)].map(m => [m.index, m.index + m[0].length, m[0]])
|
|
71
|
+
|
|
72
|
+
// Apply `fn` to the markup between raw blocks; `onRaw` (optional) to each
|
|
73
|
+
// raw block's text.
|
|
74
|
+
const mapOutsideRawBlocks = (html, fn, onRaw = (raw) => raw) => {
|
|
75
|
+
let out = ''
|
|
76
|
+
let last = 0
|
|
77
|
+
for (const [start, end, raw] of rawRanges(html)) {
|
|
78
|
+
out += fn(html.slice(last, start)) + onRaw(raw)
|
|
79
|
+
last = end
|
|
80
|
+
}
|
|
81
|
+
return out + fn(html.slice(last))
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// First match of `re` at or after `from` that does not start inside a raw block.
|
|
85
|
+
const findOutside = (html, re, from = 0) => {
|
|
86
|
+
const ranges = rawRanges(html)
|
|
87
|
+
const g = new RegExp(re.source, re.flags.includes('g') ? re.flags : re.flags + 'g')
|
|
88
|
+
g.lastIndex = from
|
|
89
|
+
let m
|
|
90
|
+
while ((m = g.exec(html))) {
|
|
91
|
+
const at = m.index
|
|
92
|
+
const inside = ranges.find(([s, e]) => at > s && at < e)
|
|
93
|
+
if (!inside) return m
|
|
94
|
+
g.lastIndex = inside[1]
|
|
95
|
+
}
|
|
96
|
+
return null
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const isRelativeUrl = (url) => !!url &&
|
|
100
|
+
!/^(?:[a-z][a-z0-9+.-]*:|\/|#|\?)/i.test(url)
|
|
101
|
+
|
|
102
|
+
// Quoted or unquoted (minifiers drop the quotes: `src=index.js`).
|
|
103
|
+
const rebaseTag = (tag, prefix) =>
|
|
104
|
+
tag.replace(/(\s(?:src|href)\s*=\s*)(?:(["'])([^"']*)\2|([^\s"'>]+))/gi, (whole, lead, q, quoted, bare) => {
|
|
105
|
+
const url = q ? quoted : bare
|
|
106
|
+
if (!isRelativeUrl(url)) return whole
|
|
107
|
+
const quote = q || ''
|
|
108
|
+
return `${lead}${quote}${prefix}${url.replace(/^\.\//, '')}${quote}`
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
// Rebase relative src/href on <script>/<link> tags by `depth` directories —
|
|
112
|
+
// the tags only: an inline script's TEXT is never touched.
|
|
113
|
+
const rebaseAssets = (html, depth) => {
|
|
114
|
+
if (!depth) return html
|
|
115
|
+
const prefix = '../'.repeat(depth)
|
|
116
|
+
return mapOutsideRawBlocks(
|
|
117
|
+
html,
|
|
118
|
+
(markup) => markup.replace(/<link\b[^>]*>/gi, (tag) => rebaseTag(tag, prefix)),
|
|
119
|
+
(raw) => /^<script\b/i.test(raw)
|
|
120
|
+
? raw.replace(/^<script\b[^>]*>/i, (tag) => rebaseTag(tag, prefix))
|
|
121
|
+
: raw
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* @param {string} shell - the built app document (e.g. dist/index.html)
|
|
127
|
+
* @param {object} parts
|
|
128
|
+
* @param {string} [parts.headTags] - route metadata tags (generateHeadHtml)
|
|
129
|
+
* @param {string} [parts.headEnd] - font links + <style> blocks for </head>
|
|
130
|
+
* @param {string} [parts.body] - rendered body markup
|
|
131
|
+
* @param {number} [parts.depth=0] - directory depth of the output file
|
|
132
|
+
* @returns {string}
|
|
133
|
+
*/
|
|
134
|
+
export const composeIntoShell = (shell, { headTags = '', headEnd = '', body = '', depth = 0 } = {}) => {
|
|
135
|
+
if (typeof shell !== 'string' || !shell.trim()) {
|
|
136
|
+
throw new Error('brender: the shell document is empty')
|
|
137
|
+
}
|
|
138
|
+
let html = rebaseAssets(shell, depth)
|
|
139
|
+
|
|
140
|
+
const owned = new Set()
|
|
141
|
+
for (const m of headTags.matchAll(HEAD_TAG)) {
|
|
142
|
+
const key = headTagKey(m[0])
|
|
143
|
+
if (key) owned.add(key)
|
|
144
|
+
}
|
|
145
|
+
const dropOwned = (markup) => mapOutsideRawBlocks(markup, (segment) =>
|
|
146
|
+
segment.replace(HEAD_TAG, (tag) => owned.has(headTagKey(tag)) ? '' : tag)
|
|
147
|
+
)
|
|
148
|
+
const headIn = (headTags ? `\n${headTags}` : '')
|
|
149
|
+
const headOut = (headEnd ? `\n${headEnd}\n` : '')
|
|
150
|
+
|
|
151
|
+
const headOpen = findOutside(html, HEAD_OPEN)
|
|
152
|
+
const headClose = headOpen && findOutside(html, /<\/head\s*>/i, headOpen.index)
|
|
153
|
+
if (headOpen && headClose) {
|
|
154
|
+
const start = headOpen.index + headOpen[0].length
|
|
155
|
+
const end = headClose.index
|
|
156
|
+
html = html.slice(0, start) + headIn + dropOwned(html.slice(start, end)) + headOut + html.slice(end)
|
|
157
|
+
} else {
|
|
158
|
+
// Optional <head>/<body> tags omitted — parcel's minifier (htmlnano)
|
|
159
|
+
// emits `<html lang=en><script type=importmap>…<meta charset=utf-8>
|
|
160
|
+
// <title>…<script type=module src=…>` with neither. Metadata and styles
|
|
161
|
+
// go right after <html> (or the doctype), where the parser opens the
|
|
162
|
+
// implied head; the shell's own metadata tags of the same identity are
|
|
163
|
+
// dropped wherever they sit.
|
|
164
|
+
html = dropOwned(html)
|
|
165
|
+
const htmlOpen = findOutside(html, /<html(\s[^>]*)?>/i) || findOutside(html, /<!doctype[^>]*>/i)
|
|
166
|
+
const at = htmlOpen ? htmlOpen.index + htmlOpen[0].length : 0
|
|
167
|
+
html = html.slice(0, at) + headIn + headOut + html.slice(at)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const flagged = `\n<script>window.__BRENDER__=true</script>\n${body}\n`
|
|
171
|
+
const bodyOpen = findOutside(html, BODY_OPEN)
|
|
172
|
+
if (bodyOpen) {
|
|
173
|
+
// Right after <body>: before every shell script, so a classic inline
|
|
174
|
+
// boot script (symbols-runner) already finds the flag and the markup.
|
|
175
|
+
const at = bodyOpen.index + bodyOpen[0].length
|
|
176
|
+
return html.slice(0, at) + flagged + html.slice(at)
|
|
177
|
+
}
|
|
178
|
+
// No <body> tag: the markup opens the implied body at the end of the
|
|
179
|
+
// document. Such shells load the app with module scripts (parcel, vite),
|
|
180
|
+
// which run after parsing — the flag and the markup are in place by then.
|
|
181
|
+
const htmlClose = findOutside(html, /<\/(?:body|html)\s*>/i)
|
|
182
|
+
const at = htmlClose ? htmlClose.index : html.length
|
|
183
|
+
return html.slice(0, at) + flagged + html.slice(at)
|
|
184
|
+
}
|