@symbo.ls/utils 3.14.779 → 3.14.780
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/metadata.js +2 -2
- package/dist/cjs/sharedLibraries.js +1 -1
- package/dist/esm/metadata.js +2 -2
- package/dist/esm/sharedLibraries.js +1 -1
- package/metadata.js +76 -10
- package/package.json +1 -1
- package/sharedLibraries.js +174 -33
package/dist/cjs/metadata.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`)},
|
|
1
|
+
"use strict";var j=Object.defineProperty;var E=Object.getOwnPropertyDescriptor;var W=Object.getOwnPropertyNames;var T=Object.prototype.hasOwnProperty;var F=(e,s)=>{for(var t in s)j(e,t,{get:s[t],enumerable:!0})},x=(e,s,t,n)=>{if(s&&typeof s=="object"||typeof s=="function")for(let r of W(s))!T.call(e,r)&&r!==t&&j(e,r,{get:()=>s[r],enumerable:!(n=E(s,r))||n.enumerable});return e};var D=e=>x(j({},"__esModule",{value:!0}),e);var Y={};F(Y,{generateHeadArrayTags:()=>L,generateMetaTags:()=>U,getPageMetadata:()=>N,hashContent:()=>b});module.exports=D(Y);var w=require("./types.js"),M=require("./function.js");const g=e=>{if(e==null)return"";const s={"&":"&","<":"<",">":">",'"':""","'":"'"};return e.toString().replace(/[&<>"']/g,t=>s[t])},P=e=>Object.entries(e||{}).filter(([s,t])=>t!==void 0).map(([s,t])=>`${s}="${g(t)}"`).join(" "),b=e=>{let s=5381;const t=String(e);for(let n=0;n<t.length;n++)s=(s*33^t.charCodeAt(n))>>>0;return s.toString(36)},R=new Set(["jsonLd","styles","scripts"]),L=e=>{const s=[];if(Array.isArray(e.jsonLd))for(const t of e.jsonLd){if(t==null)continue;const n=typeof t=="string"?t:JSON.stringify(t),r=String(n).replace(/<\/script/gi,"<\\/script");s.push(`<script type="application/ld+json" data-smbls-jsonld="${b(n)}">${r}<\/script>`)}if(Array.isArray(e.styles))for(const t of e.styles){if(t==null||!String(t).trim())continue;const n=String(t).replace(/<\/style/gi,"<\\/style");s.push(`<style data-smbls-style="${b(n)}">${n}</style>`)}if(Array.isArray(e.scripts))for(const t of e.scripts){if(!t)continue;const n=typeof t=="string",r=n?t:t.src;if(!r&&!t.body)continue;const o=[];r&&o.push(`src="${g(r)}"`),n||(t.type&&o.push(`type="${g(t.type)}"`),t.async&&o.push("async"),t.defer&&o.push("defer"),t.integrity&&o.push(`integrity="${g(t.integrity)}"`),t.crossorigin&&o.push(`crossorigin="${g(t.crossorigin)}"`),t.referrerpolicy&&o.push(`referrerpolicy="${g(t.referrerpolicy)}"`),t.nomodule&&o.push("nomodule"));const l=` data-smbls-script="${g(r||b(t.body||""))}"`;if(!n&&t.body){const h=String(t.body).replace(/<\/script/gi,"<\\/script");s.push(`<script ${o.join(" ")}${l}>${h}<\/script>`)}else s.push(`<script ${o.join(" ")}${l}><\/script>`)}return s},U=(e,s)=>{s||(e={...e,robots:"noindex"});const t=Object.entries(e).reduce((n,[r,o])=>{if(!o&&o!==0&&o!==!1||R.has(r))return n;if(r==="title")return n.push(`<title>${g(o)}</title>`),n;if(r==="canonical")return n.push(`<link rel="canonical" href="${g(o)}">`),n;if(r==="manifest")return n.push(`<link rel="manifest" href="${g(o)}">`),n;if(r==="favicon"||r==="favicons"||r==="icon"||r==="icons")return(Array.isArray(o)?o:[o]).forEach(c=>{if(typeof c=="string")n.push(`<link rel="icon" href="${g(c)}">`);else if(typeof c=="object"){const u=P(c);/rel=/.test(u)?n.push(`<link ${u}>`):n.push(`<link rel="icon" ${u}>`)}}),n;if(r==="alternate")return(Array.isArray(o)?o:[o]).forEach(c=>{if(typeof c=="object"){const u=Object.entries(c).filter(([i,p])=>p!==void 0).map(([i,p])=>`${i}="${g(p)}"`).join(" ");n.push(`<link rel="alternate" ${u}>`)}}),n;const l=(f,c,u="name")=>{if(typeof c=="string"||typeof c=="number"||typeof c=="boolean")n.push(`<meta ${u}="${g(f)}" content="${g(c)}">`);else if(Array.isArray(c))c.forEach(i=>{if(typeof i=="string"||typeof i=="number"||typeof i=="boolean")n.push(`<meta ${u}="${g(f)}" content="${g(i)}">`);else if(typeof i=="object"){const p=Object.entries(i).filter(([m,a])=>a!==void 0).map(([m,a])=>`${m}="${g(a)}"`).join(" ");n.push(`<meta ${p}>`)}});else if(typeof c=="object"){const i=Object.entries(c).filter(([p,m])=>m!==void 0).map(([p,m])=>`${p}="${g(m)}"`).join(" ");n.push(`<meta ${i}>`)}};if(r.startsWith("http-equiv:")){const f=r.replace("http-equiv:","");return l(f,o,"http-equiv"),n}if(r.startsWith("itemprop:")){const f=r.replace("itemprop:","");return l(f,o,"itemprop"),n}const y=["og:","twitter:","fb:","article:","profile:","book:","business:","music:","product:","video:","DC:","DCTERMS:"].find(f=>r.startsWith(f));if(y){const f=r.replace(y,"");return l(`${y.replace(":","")}:${f}`,o,y==="twitter:"||y==="DC:"||y==="DCTERMS:"?"name":"property"),n}if(r.startsWith("apple:")||r.startsWith("msapplication:")){const f=r.startsWith("apple:")?"apple-":"msapplication-",c=r.replace(/^apple:|^msapplication:/,"");return l(`${f}${c}`,o,"name"),n}return l(r,o),n},['<meta charset="UTF-8">','<meta name="viewport" content="width=device-width, initial-scale=1">']);return t.push(...L(e)),t.join(`
|
|
2
|
+
`)},_=e=>typeof e=="string"&&e.length>0&&!e.startsWith("/")&&!e.startsWith("http"),S=e=>typeof e=="string"&&e.length>0,q=e=>{if(!e||typeof e!="object")return null;if(S(e.src))return e.src;const s=e.content;return!s||typeof s!="object"?null:S(s.src)?s.src:S(s.storageUrl)?s.storageUrl:null};function K(e,s){if(!s||typeof s!="object")return e;const t=r=>!_(r)||!Object.prototype.hasOwnProperty.call(s,r)?r:q(s[r])||r,n={...e};for(const[r,o]of Object.entries(n))typeof o=="string"?n[r]=t(o):Array.isArray(o)&&(n[r]=o.map(l=>typeof l=="string"?t(l):l));return n}function A(e,s){if(!e||!s)return;const t=s.split(".");let n=e;for(const r of t){if(n==null||typeof n!="object")return;n=n[r]}return n}function C(e,s){const t=A(e,s);if(typeof t=="number")return Number.isFinite(t)?t:void 0;if(typeof t=="string"&&!(0,M.isStringFunction)(t))return t}function H(e,s,t){const r=(s.config||s.settings||{}).polyglot||s.polyglot,o=t?.lang||r?.defaultLang||"en",h=(r?.translations||{})[o]||{},y=[t?.state,s.state].filter(u=>u&&typeof u=="object"),f=u=>{for(const i of y){const p=C(i,u);if(p!==void 0)return p}},c={...e};for(const[u,i]of Object.entries(c))typeof i!="string"||!i.includes("{{")||(c[u]=i.replace(/\{\{\s*([^|{}]+?)\s*(?:\|\s*(\w+)\s*)?\}\}/g,(p,m,a)=>{const d=m.trim();if(a==="polyglot")return h[d]??A(h,d)??p;if(a==="getLocalStateLang")return f(d+o)??p;const $=f(d);return $!==void 0?$:h[d]??A(h,d)??p}));return c}function O(e){if(!(0,w.isObject)(e))return{};const s={...e};for(const[t,n]of Object.entries(s))if(typeof n=="function"){const r=n.toString(),o=r.match(/el\.call\(\s*["']getLocalStateLang["']\s*,\s*["']([^"']+)["']\s*\)/);if(o){s[t]=`{{ ${o[1]} | getLocalStateLang }}`;continue}const l=r.match(/el\.call\(\s*["']polyglot["']\s*,\s*["']([^"']+)["']\s*\)/);if(l){s[t]=`{{ ${l[1]} | polyglot }}`;continue}const h=r.match(/s\.(\w+(?:\.\w+)+)/);if(h){s[t]=`{{ ${h[1]} }}`;continue}delete s[t]}else if(typeof n=="string"){const r=n.match(/el\.call\(\s*["']getLocalStateLang["']\s*,\s*["']([^"']+)["']\s*\)/);if(r){s[t]=`{{ ${r[1]} | getLocalStateLang }}`;continue}const o=n.match(/el\.call\(\s*["']polyglot["']\s*,\s*["']([^"']+)["']\s*\)/);if(o){s[t]=`{{ ${o[1]} | polyglot }}`;continue}if(n.includes("=>")&&n.includes("s.")){const l=n.match(/s\.(\w+(?:\.\w+)+)/);if(l){s[t]=`{{ ${l[1]} }}`;continue}}(0,M.isStringFunction)(n)&&delete s[t]}return s}function N(e,s,t,{isProduction:n=!1,channel:r="production"}={}){const o=e.pages?.[s],l=(0,w.isObject)(o?.state)&&o?.state,h=o?.metadata||o?.helmet||l||{},y=O(h),f=new Set(Object.keys(y)),c=e.app?.metadata||{},u=O(c);let i={};if(e.integrations?.seo?i={...e.integrations.seo,...u,...y}:Object.keys(u).length?i={...u,...y}:i={...y},!i.title){const a=e.name||e.key;a?n?i.title=a:r==="development"?i.title=`${a} / dev:symbo.ls`:r==="staging"?i.title=`${a} / staging:symbo.ls`:i.title=`${a} / symbo.ls`:i.title="Symbols demo"}i=H(i,e,t),i.title&&(f.has("title")||!i["og:title"])&&(f.has("og:title")||(i["og:title"]=i.title)),i.description&&(f.has("description")||!i["og:description"])&&(f.has("og:description")||(i["og:description"]=i.description)),i.title&&!i["twitter:title"]&&(i["twitter:title"]=i.title),i.description&&!i["twitter:description"]&&(i["twitter:description"]=i.description);const p=t?.actualPathname||s;if(i["og:url"]||i.url){const a=(i["og:url"]||i.url||"").replace(/\/$/,"");if(a&&p&&p!=="/"){const d=p.startsWith("/")?p:"/"+p;i["og:url"]=a+d}}i=K(i,e.files);const m=e.name||e.app?.metadata?.title||e.app?.name||"";for(const[a,d]of Object.entries(i))if(typeof d=="string"&&d.includes("{{")){const $=d.replace(/\{\{[^}]*\}\}/g,"").trim();$?i[a]=$:a==="title"?i[a]=m:delete i[a]}return i}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var d=Object.defineProperty;var M=Object.getOwnPropertyDescriptor;var C=Object.getOwnPropertyNames;var D=Object.prototype.hasOwnProperty;var T=(n,e)=>{for(var s in e)d(n,s,{get:e[s],enumerable:!0})},z=(n,e,s,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of C(e))!D.call(n,o)&&o!==s&&d(n,o,{get:()=>e[o],enumerable:!(t=M(e,o))||t.enumerable});return n};var N=n=>z(d({},"__esModule",{value:!0}),n);var cn={};T(cn,{__resetBuiltinMethodWarnings:()=>q,deepDefaults:()=>m,fetchLibraryData:()=>O,isWrappedLibrary:()=>k,mergeSharedLibraries:()=>sn,mergeSharedLibraryDesignSystems:()=>rn,normalizeIgnoreList:()=>y,normalizeLibraryKey:()=>f,resolveSharedLibraries:()=>an,sharedLibrary:()=>V});module.exports=N(cn);var c=require("./types.js"),u=require("./keys.js"),B=require("./env.js");const h="system",G="https://smbls-kv.nika-980.workers.dev",K="https://api.symbols.app";function f(n){const e=String(n||"").trim();if(!e)return{owner:h,key:"",full:""};let s=null,t=e;if(e.includes("/")){const i=e.indexOf("/");s=e.slice(0,i),t=e.slice(i+1)}t=t.replace(/\.symbo\.ls$/iu,""),s||(s=h);const o=t?`${s}/${t}`:"";return{owner:s,key:t,full:o}}const m=(n,e,s,t)=>{for(const o in e){const i=t?`${t}/${o}`:o;s&&s.has(i)||(o in n?(0,c.isObject)(n[o])&&(0,c.isObject)(e[o])&&m(n[o],e[o],s,i):n[o]=e[o])}},g=(n,e,s)=>{if(!(0,c.isObject)(n)||!e.size)return n;const t=`${s}/`;let o=!1;for(const r of e)if(r.startsWith(t)){o=!0;break}if(!o)return n;const i={};for(const r in n){if(!Object.prototype.hasOwnProperty.call(n,r))continue;const a=`${s}/${r}`;e.has(a)||(i[r]=g(n[r],e,a))}return i},W=new Set,Y=new Set(["components","pages","functions","snippets","methods"]),L=(n,e)=>e===""&&Y.has(n),$=["fontFamily","fontfamily","font_family","FONT_FAMILY"],S=n=>{const e=new Map;if(!(0,c.isObject)(n))return e;for(const s of $){const t=n[s];if((0,c.isObject)(t))for(const o in t)!(0,c.isObject)(t[o])||!t[o].isDefault||(e.has(s)||e.set(s,new Set),e.get(s).add(o))}return e},A=(n,e)=>{for(const s of $){const t=n[s];if(!(0,c.isObject)(t))continue;const o=e.get(s);let i=null;for(const r in t){const a=t[r];if(!(0,c.isObject)(a)||!a.isDefault||o&&o.has(r))continue;const l={...a};delete l.isDefault,i||(i={...t}),i[r]=l}i&&(n[s]=i)}},J=n=>n.replace(/\.js$/iu,""),y=n=>{const e=new Set;if(!Array.isArray(n))return e;for(const s of n){if(!(0,c.isString)(s))continue;const t=s.split("/").map(o=>J(o.trim())).filter(Boolean).join("/");t&&e.add(t)}return e},V=(n,e={})=>({library:n,ignoreList:Array.isArray(e.ignoreList)?e.ignoreList:[]}),k=n=>(0,c.isObject)(n)&&Array.isArray(n.ignoreList)&&("library"in n||"key"in n||"name"in n),I=n=>n.library!==void 0?n.library:n.key??n.name,p=new Set,q=()=>{p.clear()},H=(n,e)=>{const s=e.filter(t=>!p.has(t));if(s.length){for(const t of s)p.add(t);(0,B.isLoudDevRuntime)()&&console.warn(`[smbls] shared library "${n}" ships ${s.map(t=>`methods.${t}`).join(", ")} \u2014 named like a built-in element method. Refused: the built-in wins; a library cannot replace an element method on the consuming app. If the app wants this version, declare it in the app's OWN methods.`)}},Q=(n,e,s)=>{let t=null;for(const o in n)if((0,u.isProtectedElementMethod)(o)&&(e.has(`methods/${o}`)||s.push(o),!t)){t={};for(const i in n)(0,u.isProtectedElementMethod)(i)||(t[i]=n[i])}return t||n},_=(n,e)=>{const s=y(n.sharedLibIgnore);return n.sharePages===!1&&s.add("pages"),s.size?new Set([...e,...s]):e},X=(n,e,s,t="library")=>{const o=_(e,s),i=[];for(const r in e)if(Object.prototype.hasOwnProperty.call(e,r)&&!(r==="sharePages"||r==="sharedLibIgnore")&&r!=="app"&&!o.has(r)){if((0,c.isObject)(e[r])&&(0,c.isObject)(n[r])){if(r==="designSystem")m(n[r],e[r],o,r);else for(const a in e[r])if(!o.has(`${r}/${a}`)){if(r==="methods"&&(0,u.isProtectedElementMethod)(a)){a in n[r]||i.push(a);continue}a in n[r]?L(r,n[r][a])&&!L(r,e[r][a])&&(n[r][a]=e[r][a]):n[r][a]=e[r][a]}}else if(!(r in n)){const a=e[r],l=r==="methods"&&a&&typeof a=="object"?Q(a,o,i):a;n[r]=g(l,o,r)}}i.length&&H(t,i)},Z=(n,e,s)=>{if(n!==e){const t=I(n);if((0,c.isString)(t)&&t)return t;if(n.key||n.name)return n.key||n.name}return e&&(e.key||e.name)||`sharedLibraries[${s}]`},x=`${h}/default`,nn=n=>(0,c.isString)(n)&&n!==""&&f(n).full===x,en=(n,e)=>{if((0,c.isString)(e.key)&&e.key)return e.key;if(n!==e){if((0,c.isString)(n.key)&&n.key)return n.key;if((0,c.isString)(n.name)&&n.name)return n.name}return null},tn=n=>{const e=[],s=[];for(let t=0;t<n.length;t++){const o=n[t];let i=o,r=W;if(k(i)&&(r=y(i.ignoreList),i=i.library),!(0,c.isObject)(i))continue;const a={entry:o,lib:i,ignore:r,index:t};nn(en(o,i))?s.push(a):e.push(a)}return{linked:e,bases:s}},R=(n,e,s)=>{const t=S(e()),{linked:o,bases:i}=tn(n);for(const a of o)s(a);const r=!t.size&&i.length?S(e()):null;for(const a of i)s(a);return t.size?t:r&&r.size?r:null},sn=(n,e)=>{if(!e||!e.length)return;const s=n.designSystem,t=R(e,()=>n.designSystem,({entry:o,lib:i,ignore:r,index:a})=>X(n,i,r,Z(o,i,a)));t&&(n.designSystem!==s&&(n.designSystem={...n.designSystem}),A(n.designSystem,t))},rn=(n,e,s=m)=>{if(!(0,c.isObject)(n)||!Array.isArray(e)||!e.length)return n;const t=R(e,()=>n,({lib:o,ignore:i})=>{const r=o.designSystem;if(!r||typeof r!="object")return;const a=_(o,i);a.has("designSystem")||s(n,r,a,"designSystem")});return t&&A(n,t),n};async function O(n,e={}){if((e.provider||"kv")==="api")return j(n,e);const t=await on(n,e);return t||j(n,e)}async function on(n,e={}){const s=e.kvBaseUrl||G,t=e.env||"production",o=`${s}/kv/${encodeURIComponent(n)}?env=${t}`;try{const i=await fetch(o,{method:"GET"});return i.ok&&(await i.json())?.value||null}catch{return null}}async function j(n,e={}){const s=e.apiBaseUrl||K,{key:t}=f(n);try{const o=`${s}/core/projects/libraries/available?search=${encodeURIComponent(t)}&limit=10`,i=await fetch(o,{method:"GET"});if(!i.ok)return null;const r=await i.json(),l=(r?.items||r?.data||(Array.isArray(r)?r:[])).find(U=>{const{key:F}=f(U?.key);return F.toLowerCase()===t.toLowerCase()});if(!l?.id&&!l?._id)return null;const E=l.id||l._id,P=`${s}/core/projects/${encodeURIComponent(E)}/data?branch=main`,w=await fetch(P,{method:"GET"});if(!w.ok)return null;const b=await w.json();return b?.data||b||null}catch{return null}}async function v(n,e={}){if((0,c.isObject)(n))return n;if((0,c.isString)(n)){const{full:s}=f(n);if(!s)return null;try{const t=await O(s,e);return t||console.warn(`[smbls] Shared library "${s}" not found`),t}catch(t){return console.warn(`[smbls] Failed to fetch shared library "${s}":`,t.message),null}}return null}async function an(n,e={}){return!n||!n.length?[]:(await Promise.all(n.map(async t=>{if(k(t)){const o=await v(I(t),e);return o?{library:o,ignoreList:t.ignoreList}:null}return v(t,e)}))).filter(Boolean)}
|
package/dist/esm/metadata.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{isObject as
|
|
2
|
-
`)},
|
|
1
|
+
import{isObject as A}from"./types.js";import{isStringFunction as w}from"./function.js";const g=n=>{if(n==null)return"";const r={"&":"&","<":"<",">":">",'"':""","'":"'"};return n.toString().replace(/[&<>"']/g,t=>r[t])},O=n=>Object.entries(n||{}).filter(([r,t])=>t!==void 0).map(([r,t])=>`${r}="${g(t)}"`).join(" "),b=n=>{let r=5381;const t=String(n);for(let e=0;e<t.length;e++)r=(r*33^t.charCodeAt(e))>>>0;return r.toString(36)},L=new Set(["jsonLd","styles","scripts"]),E=n=>{const r=[];if(Array.isArray(n.jsonLd))for(const t of n.jsonLd){if(t==null)continue;const e=typeof t=="string"?t:JSON.stringify(t),i=String(e).replace(/<\/script/gi,"<\\/script");r.push(`<script type="application/ld+json" data-smbls-jsonld="${b(e)}">${i}<\/script>`)}if(Array.isArray(n.styles))for(const t of n.styles){if(t==null||!String(t).trim())continue;const e=String(t).replace(/<\/style/gi,"<\\/style");r.push(`<style data-smbls-style="${b(e)}">${e}</style>`)}if(Array.isArray(n.scripts))for(const t of n.scripts){if(!t)continue;const e=typeof t=="string",i=e?t:t.src;if(!i&&!t.body)continue;const o=[];i&&o.push(`src="${g(i)}"`),e||(t.type&&o.push(`type="${g(t.type)}"`),t.async&&o.push("async"),t.defer&&o.push("defer"),t.integrity&&o.push(`integrity="${g(t.integrity)}"`),t.crossorigin&&o.push(`crossorigin="${g(t.crossorigin)}"`),t.referrerpolicy&&o.push(`referrerpolicy="${g(t.referrerpolicy)}"`),t.nomodule&&o.push("nomodule"));const l=` data-smbls-script="${g(i||b(t.body||""))}"`;if(!e&&t.body){const h=String(t.body).replace(/<\/script/gi,"<\\/script");r.push(`<script ${o.join(" ")}${l}>${h}<\/script>`)}else r.push(`<script ${o.join(" ")}${l}><\/script>`)}return r},U=(n,r)=>{r||(n={...n,robots:"noindex"});const t=Object.entries(n).reduce((e,[i,o])=>{if(!o&&o!==0&&o!==!1||L.has(i))return e;if(i==="title")return e.push(`<title>${g(o)}</title>`),e;if(i==="canonical")return e.push(`<link rel="canonical" href="${g(o)}">`),e;if(i==="manifest")return e.push(`<link rel="manifest" href="${g(o)}">`),e;if(i==="favicon"||i==="favicons"||i==="icon"||i==="icons")return(Array.isArray(o)?o:[o]).forEach(c=>{if(typeof c=="string")e.push(`<link rel="icon" href="${g(c)}">`);else if(typeof c=="object"){const u=O(c);/rel=/.test(u)?e.push(`<link ${u}>`):e.push(`<link rel="icon" ${u}>`)}}),e;if(i==="alternate")return(Array.isArray(o)?o:[o]).forEach(c=>{if(typeof c=="object"){const u=Object.entries(c).filter(([s,p])=>p!==void 0).map(([s,p])=>`${s}="${g(p)}"`).join(" ");e.push(`<link rel="alternate" ${u}>`)}}),e;const l=(f,c,u="name")=>{if(typeof c=="string"||typeof c=="number"||typeof c=="boolean")e.push(`<meta ${u}="${g(f)}" content="${g(c)}">`);else if(Array.isArray(c))c.forEach(s=>{if(typeof s=="string"||typeof s=="number"||typeof s=="boolean")e.push(`<meta ${u}="${g(f)}" content="${g(s)}">`);else if(typeof s=="object"){const p=Object.entries(s).filter(([m,a])=>a!==void 0).map(([m,a])=>`${m}="${g(a)}"`).join(" ");e.push(`<meta ${p}>`)}});else if(typeof c=="object"){const s=Object.entries(c).filter(([p,m])=>m!==void 0).map(([p,m])=>`${p}="${g(m)}"`).join(" ");e.push(`<meta ${s}>`)}};if(i.startsWith("http-equiv:")){const f=i.replace("http-equiv:","");return l(f,o,"http-equiv"),e}if(i.startsWith("itemprop:")){const f=i.replace("itemprop:","");return l(f,o,"itemprop"),e}const y=["og:","twitter:","fb:","article:","profile:","book:","business:","music:","product:","video:","DC:","DCTERMS:"].find(f=>i.startsWith(f));if(y){const f=i.replace(y,"");return l(`${y.replace(":","")}:${f}`,o,y==="twitter:"||y==="DC:"||y==="DCTERMS:"?"name":"property"),e}if(i.startsWith("apple:")||i.startsWith("msapplication:")){const f=i.startsWith("apple:")?"apple-":"msapplication-",c=i.replace(/^apple:|^msapplication:/,"");return l(`${f}${c}`,o,"name"),e}return l(i,o),e},['<meta charset="UTF-8">','<meta name="viewport" content="width=device-width, initial-scale=1">']);return t.push(...E(n)),t.join(`
|
|
2
|
+
`)},W=n=>typeof n=="string"&&n.length>0&&!n.startsWith("/")&&!n.startsWith("http"),j=n=>typeof n=="string"&&n.length>0,T=n=>{if(!n||typeof n!="object")return null;if(j(n.src))return n.src;const r=n.content;return!r||typeof r!="object"?null:j(r.src)?r.src:j(r.storageUrl)?r.storageUrl:null};function F(n,r){if(!r||typeof r!="object")return n;const t=i=>!W(i)||!Object.prototype.hasOwnProperty.call(r,i)?i:T(r[i])||i,e={...n};for(const[i,o]of Object.entries(e))typeof o=="string"?e[i]=t(o):Array.isArray(o)&&(e[i]=o.map(l=>typeof l=="string"?t(l):l));return e}function S(n,r){if(!n||!r)return;const t=r.split(".");let e=n;for(const i of t){if(e==null||typeof e!="object")return;e=e[i]}return e}function x(n,r){const t=S(n,r);if(typeof t=="number")return Number.isFinite(t)?t:void 0;if(typeof t=="string"&&!w(t))return t}function D(n,r,t){const i=(r.config||r.settings||{}).polyglot||r.polyglot,o=t?.lang||i?.defaultLang||"en",h=(i?.translations||{})[o]||{},y=[t?.state,r.state].filter(u=>u&&typeof u=="object"),f=u=>{for(const s of y){const p=x(s,u);if(p!==void 0)return p}},c={...n};for(const[u,s]of Object.entries(c))typeof s!="string"||!s.includes("{{")||(c[u]=s.replace(/\{\{\s*([^|{}]+?)\s*(?:\|\s*(\w+)\s*)?\}\}/g,(p,m,a)=>{const d=m.trim();if(a==="polyglot")return h[d]??S(h,d)??p;if(a==="getLocalStateLang")return f(d+o)??p;const $=f(d);return $!==void 0?$:h[d]??S(h,d)??p}));return c}function M(n){if(!A(n))return{};const r={...n};for(const[t,e]of Object.entries(r))if(typeof e=="function"){const i=e.toString(),o=i.match(/el\.call\(\s*["']getLocalStateLang["']\s*,\s*["']([^"']+)["']\s*\)/);if(o){r[t]=`{{ ${o[1]} | getLocalStateLang }}`;continue}const l=i.match(/el\.call\(\s*["']polyglot["']\s*,\s*["']([^"']+)["']\s*\)/);if(l){r[t]=`{{ ${l[1]} | polyglot }}`;continue}const h=i.match(/s\.(\w+(?:\.\w+)+)/);if(h){r[t]=`{{ ${h[1]} }}`;continue}delete r[t]}else if(typeof e=="string"){const i=e.match(/el\.call\(\s*["']getLocalStateLang["']\s*,\s*["']([^"']+)["']\s*\)/);if(i){r[t]=`{{ ${i[1]} | getLocalStateLang }}`;continue}const o=e.match(/el\.call\(\s*["']polyglot["']\s*,\s*["']([^"']+)["']\s*\)/);if(o){r[t]=`{{ ${o[1]} | polyglot }}`;continue}if(e.includes("=>")&&e.includes("s.")){const l=e.match(/s\.(\w+(?:\.\w+)+)/);if(l){r[t]=`{{ ${l[1]} }}`;continue}}w(e)&&delete r[t]}return r}function _(n,r,t,{isProduction:e=!1,channel:i="production"}={}){const o=n.pages?.[r],l=A(o?.state)&&o?.state,h=o?.metadata||o?.helmet||l||{},y=M(h),f=new Set(Object.keys(y)),c=n.app?.metadata||{},u=M(c);let s={};if(n.integrations?.seo?s={...n.integrations.seo,...u,...y}:Object.keys(u).length?s={...u,...y}:s={...y},!s.title){const a=n.name||n.key;a?e?s.title=a:i==="development"?s.title=`${a} / dev:symbo.ls`:i==="staging"?s.title=`${a} / staging:symbo.ls`:s.title=`${a} / symbo.ls`:s.title="Symbols demo"}s=D(s,n,t),s.title&&(f.has("title")||!s["og:title"])&&(f.has("og:title")||(s["og:title"]=s.title)),s.description&&(f.has("description")||!s["og:description"])&&(f.has("og:description")||(s["og:description"]=s.description)),s.title&&!s["twitter:title"]&&(s["twitter:title"]=s.title),s.description&&!s["twitter:description"]&&(s["twitter:description"]=s.description);const p=t?.actualPathname||r;if(s["og:url"]||s.url){const a=(s["og:url"]||s.url||"").replace(/\/$/,"");if(a&&p&&p!=="/"){const d=p.startsWith("/")?p:"/"+p;s["og:url"]=a+d}}s=F(s,n.files);const m=n.name||n.app?.metadata?.title||n.app?.name||"";for(const[a,d]of Object.entries(s))if(typeof d=="string"&&d.includes("{{")){const $=d.replace(/\{\{[^}]*\}\}/g,"").trim();$?s[a]=$:a==="title"?s[a]=m:delete s[a]}return s}export{E as generateHeadArrayTags,U as generateMetaTags,_ as getPageMetadata,b as hashContent};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isObject as
|
|
1
|
+
import{isObject as c,isString as f}from"./types.js";import{isProtectedElementMethod as m}from"./keys.js";import{isLoudDevRuntime as U}from"./env.js";const d="system",F="https://smbls-kv.nika-980.workers.dev",M="https://api.symbols.app";function u(n){const e=String(n||"").trim();if(!e)return{owner:d,key:"",full:""};let s=null,t=e;if(e.includes("/")){const i=e.indexOf("/");s=e.slice(0,i),t=e.slice(i+1)}t=t.replace(/\.symbo\.ls$/iu,""),s||(s=d);const o=t?`${s}/${t}`:"";return{owner:s,key:t,full:o}}const h=(n,e,s,t)=>{for(const o in e){const i=t?`${t}/${o}`:o;s&&s.has(i)||(o in n?c(n[o])&&c(e[o])&&h(n[o],e[o],s,i):n[o]=e[o])}},w=(n,e,s)=>{if(!c(n)||!e.size)return n;const t=`${s}/`;let o=!1;for(const r of e)if(r.startsWith(t)){o=!0;break}if(!o)return n;const i={};for(const r in n){if(!Object.prototype.hasOwnProperty.call(n,r))continue;const a=`${s}/${r}`;e.has(a)||(i[r]=w(n[r],e,a))}return i},C=new Set,D=new Set(["components","pages","functions","snippets","methods"]),b=(n,e)=>e===""&&D.has(n),g=["fontFamily","fontfamily","font_family","FONT_FAMILY"],L=n=>{const e=new Map;if(!c(n))return e;for(const s of g){const t=n[s];if(c(t))for(const o in t)!c(t[o])||!t[o].isDefault||(e.has(s)||e.set(s,new Set),e.get(s).add(o))}return e},$=(n,e)=>{for(const s of g){const t=n[s];if(!c(t))continue;const o=e.get(s);let i=null;for(const r in t){const a=t[r];if(!c(a)||!a.isDefault||o&&o.has(r))continue;const l={...a};delete l.isDefault,i||(i={...t}),i[r]=l}i&&(n[s]=i)}},T=n=>n.replace(/\.js$/iu,""),S=n=>{const e=new Set;if(!Array.isArray(n))return e;for(const s of n){if(!f(s))continue;const t=s.split("/").map(o=>T(o.trim())).filter(Boolean).join("/");t&&e.add(t)}return e},x=(n,e={})=>({library:n,ignoreList:Array.isArray(e.ignoreList)?e.ignoreList:[]}),A=n=>c(n)&&Array.isArray(n.ignoreList)&&("library"in n||"key"in n||"name"in n),I=n=>n.library!==void 0?n.library:n.key??n.name,p=new Set,nn=()=>{p.clear()},z=(n,e)=>{const s=e.filter(t=>!p.has(t));if(s.length){for(const t of s)p.add(t);U()&&console.warn(`[smbls] shared library "${n}" ships ${s.map(t=>`methods.${t}`).join(", ")} \u2014 named like a built-in element method. Refused: the built-in wins; a library cannot replace an element method on the consuming app. If the app wants this version, declare it in the app's OWN methods.`)}},N=(n,e,s)=>{let t=null;for(const o in n)if(m(o)&&(e.has(`methods/${o}`)||s.push(o),!t)){t={};for(const i in n)m(i)||(t[i]=n[i])}return t||n},_=(n,e)=>{const s=S(n.sharedLibIgnore);return n.sharePages===!1&&s.add("pages"),s.size?new Set([...e,...s]):e},G=(n,e,s,t="library")=>{const o=_(e,s),i=[];for(const r in e)if(Object.prototype.hasOwnProperty.call(e,r)&&!(r==="sharePages"||r==="sharedLibIgnore")&&r!=="app"&&!o.has(r)){if(c(e[r])&&c(n[r])){if(r==="designSystem")h(n[r],e[r],o,r);else for(const a in e[r])if(!o.has(`${r}/${a}`)){if(r==="methods"&&m(a)){a in n[r]||i.push(a);continue}a in n[r]?b(r,n[r][a])&&!b(r,e[r][a])&&(n[r][a]=e[r][a]):n[r][a]=e[r][a]}}else if(!(r in n)){const a=e[r],l=r==="methods"&&a&&typeof a=="object"?N(a,o,i):a;n[r]=w(l,o,r)}}i.length&&z(t,i)},K=(n,e,s)=>{if(n!==e){const t=I(n);if(f(t)&&t)return t;if(n.key||n.name)return n.key||n.name}return e&&(e.key||e.name)||`sharedLibraries[${s}]`},W=`${d}/default`,Y=n=>f(n)&&n!==""&&u(n).full===W,J=(n,e)=>{if(f(e.key)&&e.key)return e.key;if(n!==e){if(f(n.key)&&n.key)return n.key;if(f(n.name)&&n.name)return n.name}return null},V=n=>{const e=[],s=[];for(let t=0;t<n.length;t++){const o=n[t];let i=o,r=C;if(A(i)&&(r=S(i.ignoreList),i=i.library),!c(i))continue;const a={entry:o,lib:i,ignore:r,index:t};Y(J(o,i))?s.push(a):e.push(a)}return{linked:e,bases:s}},R=(n,e,s)=>{const t=L(e()),{linked:o,bases:i}=V(n);for(const a of o)s(a);const r=!t.size&&i.length?L(e()):null;for(const a of i)s(a);return t.size?t:r&&r.size?r:null},en=(n,e)=>{if(!e||!e.length)return;const s=n.designSystem,t=R(e,()=>n.designSystem,({entry:o,lib:i,ignore:r,index:a})=>G(n,i,r,K(o,i,a)));t&&(n.designSystem!==s&&(n.designSystem={...n.designSystem}),$(n.designSystem,t))},tn=(n,e,s=h)=>{if(!c(n)||!Array.isArray(e)||!e.length)return n;const t=R(e,()=>n,({lib:o,ignore:i})=>{const r=o.designSystem;if(!r||typeof r!="object")return;const a=_(o,i);a.has("designSystem")||s(n,r,a,"designSystem")});return t&&$(n,t),n};async function q(n,e={}){if((e.provider||"kv")==="api")return j(n,e);const t=await H(n,e);return t||j(n,e)}async function H(n,e={}){const s=e.kvBaseUrl||F,t=e.env||"production",o=`${s}/kv/${encodeURIComponent(n)}?env=${t}`;try{const i=await fetch(o,{method:"GET"});return i.ok&&(await i.json())?.value||null}catch{return null}}async function j(n,e={}){const s=e.apiBaseUrl||M,{key:t}=u(n);try{const o=`${s}/core/projects/libraries/available?search=${encodeURIComponent(t)}&limit=10`,i=await fetch(o,{method:"GET"});if(!i.ok)return null;const r=await i.json(),l=(r?.items||r?.data||(Array.isArray(r)?r:[])).find(E=>{const{key:P}=u(E?.key);return P.toLowerCase()===t.toLowerCase()});if(!l?.id&&!l?._id)return null;const B=l.id||l._id,O=`${s}/core/projects/${encodeURIComponent(B)}/data?branch=main`,y=await fetch(O,{method:"GET"});if(!y.ok)return null;const k=await y.json();return k?.data||k||null}catch{return null}}async function v(n,e={}){if(c(n))return n;if(f(n)){const{full:s}=u(n);if(!s)return null;try{const t=await q(s,e);return t||console.warn(`[smbls] Shared library "${s}" not found`),t}catch(t){return console.warn(`[smbls] Failed to fetch shared library "${s}":`,t.message),null}}return null}async function sn(n,e={}){return!n||!n.length?[]:(await Promise.all(n.map(async t=>{if(A(t)){const o=await v(I(t),e);return o?{library:o,ignoreList:t.ignoreList}:null}return v(t,e)}))).filter(Boolean)}export{nn as __resetBuiltinMethodWarnings,h as deepDefaults,q as fetchLibraryData,A as isWrappedLibrary,en as mergeSharedLibraries,tn as mergeSharedLibraryDesignSystems,S as normalizeIgnoreList,u as normalizeLibraryKey,sn as resolveSharedLibraries,x as sharedLibrary};
|
package/metadata.js
CHANGED
|
@@ -270,19 +270,51 @@ export const generateMetaTags = (metadata, isProduction) => {
|
|
|
270
270
|
const isBareFilename = (val) =>
|
|
271
271
|
typeof val === 'string' && val.length > 0 && !val.startsWith('/') && !val.startsWith('http')
|
|
272
272
|
|
|
273
|
+
const nonEmptyString = (v) => typeof v === 'string' && v.length > 0
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* The public URL a `data.files` entry carries, or null.
|
|
277
|
+
*
|
|
278
|
+
* A project's files map holds three entry shapes (measured 2026-10-02 over
|
|
279
|
+
* the /get payloads of 33 projects, 1544 entries):
|
|
280
|
+
*
|
|
281
|
+
* - a platform file record (1542): `{ content: { src, storageUrl?, urls?,
|
|
282
|
+
* … }, code, key, type, format }`. Its public address is `content.src`;
|
|
283
|
+
* there is no top-level `src`.
|
|
284
|
+
* - a hand-written external entry (1): `{ content: { storageUrl } }` — a big
|
|
285
|
+
* asset kept in its own bucket, with no `content.src`.
|
|
286
|
+
* - a manifest entry (1): `{ src }`.
|
|
287
|
+
*
|
|
288
|
+
* Reading only `entry.src` left every platform record's key bare in the
|
|
289
|
+
* static head (`og:image` = "site-social.jpg"). `src` stays first; then
|
|
290
|
+
* `content.src`, the address the runtime (attrs-in-props, `resolveAsset`)
|
|
291
|
+
* renders for the same key, so the head and the body agree; then
|
|
292
|
+
* `content.storageUrl`.
|
|
293
|
+
*/
|
|
294
|
+
const fileEntryUrl = (entry) => {
|
|
295
|
+
if (!entry || typeof entry !== 'object') return null
|
|
296
|
+
if (nonEmptyString(entry.src)) return entry.src
|
|
297
|
+
const content = entry.content
|
|
298
|
+
if (!content || typeof content !== 'object') return null
|
|
299
|
+
if (nonEmptyString(content.src)) return content.src
|
|
300
|
+
if (nonEmptyString(content.storageUrl)) return content.storageUrl
|
|
301
|
+
return null
|
|
302
|
+
}
|
|
303
|
+
|
|
273
304
|
/**
|
|
274
305
|
* Resolve bare filename references in metadata values against `data.files`.
|
|
275
306
|
* If a metadata value is a plain filename (e.g. "logo.png") and a matching
|
|
276
|
-
* entry exists in `data.files`, replace it with the file's
|
|
307
|
+
* entry exists in `data.files`, replace it with the file's URL
|
|
308
|
+
* (`fileEntryUrl`).
|
|
277
309
|
*/
|
|
278
310
|
function resolveFileReferences (metadata, files) {
|
|
279
311
|
if (!files || typeof files !== 'object') return metadata
|
|
280
312
|
|
|
281
313
|
const resolve = (val) => {
|
|
282
314
|
if (!isBareFilename(val)) return val
|
|
283
|
-
|
|
284
|
-
if (
|
|
285
|
-
return val
|
|
315
|
+
// own keys only: a value like "constructor" is copy, not a file
|
|
316
|
+
if (!Object.prototype.hasOwnProperty.call(files, val)) return val
|
|
317
|
+
return fileEntryUrl(files[val]) || val
|
|
286
318
|
}
|
|
287
319
|
|
|
288
320
|
const result = { ...metadata }
|
|
@@ -312,9 +344,36 @@ function resolveDotPath (obj, path) {
|
|
|
312
344
|
return v
|
|
313
345
|
}
|
|
314
346
|
|
|
347
|
+
/**
|
|
348
|
+
* The value at `path` in `obj` when it is one a `{{ }}` template can print:
|
|
349
|
+
* a string or a finite number. Anything else answers undefined, so the lookup
|
|
350
|
+
* moves on to the next source instead of printing it:
|
|
351
|
+
* - an object or array would print "[object Object]" (the runtime's
|
|
352
|
+
* `replaceLiteralsWithObjectFields` skips them the same way);
|
|
353
|
+
* - a function's source would print code into a content attribute;
|
|
354
|
+
* - a boolean is a flag, never a head value. A converted factory such as
|
|
355
|
+
* `(el, s) => s.menu.open ? 'A' : 'B'` becomes `{{ menu.open }}` (the
|
|
356
|
+
* conversion keeps only the first state path), and printing its flag
|
|
357
|
+
* would put "false" where the factory returned a title.
|
|
358
|
+
*/
|
|
359
|
+
function templateValueAt (obj, path) {
|
|
360
|
+
const v = resolveDotPath(obj, path)
|
|
361
|
+
if (typeof v === 'number') return Number.isFinite(v) ? v : undefined
|
|
362
|
+
if (typeof v === 'string' && !isStringFunction(v)) return v
|
|
363
|
+
return undefined
|
|
364
|
+
}
|
|
365
|
+
|
|
315
366
|
/**
|
|
316
367
|
* Resolve {{ key | polyglot }} and {{ key | getLocalStateLang }} templates
|
|
317
|
-
* in metadata string values using project polyglot translations and
|
|
368
|
+
* in metadata string values using project polyglot translations and state.
|
|
369
|
+
*
|
|
370
|
+
* State is read from two sources, in order: the page's PREFETCHED state
|
|
371
|
+
* (`ssrContext.state`), then the project's own state (`data.state`, the
|
|
372
|
+
* project's state.js — the root state the runtime factory read). Before the
|
|
373
|
+
* second source, a value kept in state.js never reached the static head
|
|
374
|
+
* unless a prefetch happened to carry it: `(el, s) => s.company.name` became
|
|
375
|
+
* `{{ company.name }}` and was stripped. The lookup is per path, so a
|
|
376
|
+
* prefetched state that lacks one path still falls back for it.
|
|
318
377
|
*/
|
|
319
378
|
function resolveMetadataTemplates (metadata, data, ssrContext) {
|
|
320
379
|
const config = data.config || data.settings || {}
|
|
@@ -322,7 +381,15 @@ function resolveMetadataTemplates (metadata, data, ssrContext) {
|
|
|
322
381
|
const defaultLang = ssrContext?.lang || polyglot?.defaultLang || 'en'
|
|
323
382
|
const translations = polyglot?.translations || {}
|
|
324
383
|
const langMap = translations[defaultLang] || {}
|
|
325
|
-
const
|
|
384
|
+
const states = [ssrContext?.state, data.state].filter((s) => s && typeof s === 'object')
|
|
385
|
+
|
|
386
|
+
const fromState = (path) => {
|
|
387
|
+
for (const state of states) {
|
|
388
|
+
const v = templateValueAt(state, path)
|
|
389
|
+
if (v !== undefined) return v
|
|
390
|
+
}
|
|
391
|
+
return undefined
|
|
392
|
+
}
|
|
326
393
|
|
|
327
394
|
const result = { ...metadata }
|
|
328
395
|
for (const [key, value] of Object.entries(result)) {
|
|
@@ -337,13 +404,12 @@ function resolveMetadataTemplates (metadata, data, ssrContext) {
|
|
|
337
404
|
|
|
338
405
|
if (filter === 'getLocalStateLang') {
|
|
339
406
|
// key is like "item.title_" — append lang
|
|
340
|
-
|
|
341
|
-
return resolved ?? match
|
|
407
|
+
return fromState(trimmed + defaultLang) ?? match
|
|
342
408
|
}
|
|
343
409
|
|
|
344
410
|
// No filter — try state lookup, then polyglot
|
|
345
|
-
const
|
|
346
|
-
if (
|
|
411
|
+
const stateValue = fromState(trimmed)
|
|
412
|
+
if (stateValue !== undefined) return stateValue
|
|
347
413
|
|
|
348
414
|
return langMap[trimmed] ?? resolveDotPath(langMap, trimmed) ?? match
|
|
349
415
|
})
|
package/package.json
CHANGED
package/sharedLibraries.js
CHANGED
|
@@ -124,8 +124,9 @@ const OBJECT_BAGS = new Set(['components', 'pages', 'functions', 'snippets', 'me
|
|
|
124
124
|
* a value a library can legitimately mean — it is only ever that signature.
|
|
125
125
|
*
|
|
126
126
|
* This matters because the merge below is FIRST-WINS, which is correct for
|
|
127
|
-
* real values (an earlier declaration outranks a later one,
|
|
128
|
-
* outranks every library
|
|
127
|
+
* real values (an earlier declaration outranks a later one, the consumer
|
|
128
|
+
* outranks every library, and the base library merges last — see
|
|
129
|
+
* `isBaseLibraryKey`) but wrong for a phantom: an earlier library's `""`
|
|
129
130
|
* would permanently shadow the real object a LATER declared library carries
|
|
130
131
|
* under the same name, and the consumer that declared the good library would
|
|
131
132
|
* still get `""`. Measured live 2026-08-21 on the published `workspace-ui`
|
|
@@ -191,15 +192,17 @@ const defaultFontClaims = (designSystem) => {
|
|
|
191
192
|
* bag the consumer did not have is the LIBRARY's own object, shared with
|
|
192
193
|
* every other consumer of that library.
|
|
193
194
|
*
|
|
194
|
-
* When the consumer claims nothing,
|
|
195
|
-
*
|
|
196
|
-
*
|
|
195
|
+
* When the consumer claims nothing, the same rule ranks the base library
|
|
196
|
+
* below the libraries the project linked: a linked library's claim stands and
|
|
197
|
+
* the base's (system/default's `Default`) yields — see `mergeInPrecedenceOrder`.
|
|
198
|
+
* Between two claims of one tier — two linked libraries, say — the order
|
|
199
|
+
* stays scratch's.
|
|
197
200
|
*/
|
|
198
|
-
const yieldLibraryDefaultFonts = (designSystem,
|
|
201
|
+
const yieldLibraryDefaultFonts = (designSystem, winningClaims) => {
|
|
199
202
|
for (const bag of FONT_FAMILY_BAGS) {
|
|
200
203
|
const families = designSystem[bag]
|
|
201
204
|
if (!isObject(families)) continue
|
|
202
|
-
const own =
|
|
205
|
+
const own = winningClaims.get(bag)
|
|
203
206
|
let copy = null
|
|
204
207
|
for (const k in families) {
|
|
205
208
|
const entry = families[k]
|
|
@@ -334,20 +337,25 @@ const withoutProtectedMethods = (bag, skip, refused) => {
|
|
|
334
337
|
return copy || bag
|
|
335
338
|
}
|
|
336
339
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
340
|
+
// The paths one library's merge skips: the consumer's `ignoreList` for it plus
|
|
341
|
+
// the library's own declaration. A library can declare parts of itself private
|
|
342
|
+
// via `sharedLibIgnore` on its own context — same path grammar as the
|
|
343
|
+
// consumer-side `ignoreList` (top-level keys or nested 'components/Header'). A
|
|
344
|
+
// library serving its own doc site (e.g. system/brand with
|
|
345
|
+
// `sharedLibIgnore: ['pages']`) must be able to keep its routes out of every
|
|
346
|
+
// consumer; without it, library pages auto-mount into any consumer that
|
|
347
|
+
// doesn't define the same route key. `sharePages: false` is the earlier,
|
|
348
|
+
// narrower alias — kept working, superseded by sharedLibIgnore.
|
|
349
|
+
const librarySkip = (sharedLib, ignore) => {
|
|
346
350
|
const libIgnore = normalizeIgnoreList(sharedLib.sharedLibIgnore)
|
|
347
351
|
if (sharedLib.sharePages === false) libIgnore.add('pages')
|
|
348
|
-
|
|
352
|
+
return libIgnore.size
|
|
349
353
|
? new Set([...ignore, ...libIgnore])
|
|
350
354
|
: ignore
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const mergeOneLibrary = (context, sharedLib, ignore, label = 'library') => {
|
|
358
|
+
const skip = librarySkip(sharedLib, ignore)
|
|
351
359
|
const refusedMethods = []
|
|
352
360
|
for (const key in sharedLib) {
|
|
353
361
|
if (!Object.prototype.hasOwnProperty.call(sharedLib, key)) continue
|
|
@@ -414,6 +422,100 @@ const libraryLabel = (entry, lib, index) => {
|
|
|
414
422
|
return (lib && (lib.key || lib.name)) || `sharedLibraries[${index}]`
|
|
415
423
|
}
|
|
416
424
|
|
|
425
|
+
/**
|
|
426
|
+
* THE BASE LIBRARY — `system/default`.
|
|
427
|
+
*
|
|
428
|
+
* Every platform project starts linked to `system/default`: the hosted copy of
|
|
429
|
+
* the framework's own defaults (a design system, the default component kit).
|
|
430
|
+
* It is the base a project builds on, not a library the project chose: the
|
|
431
|
+
* CLI calls it the one framework-owned library key
|
|
432
|
+
* (packages/cli/helpers/libraryKeyUtils.js `FRAMEWORK_BUILTIN_LIBRARY_SLUGS`),
|
|
433
|
+
* and where it is not linked the framework's own defaults stand in for it
|
|
434
|
+
* BELOW every library (`@symbo.ls/default-config`, merged after this merge by
|
|
435
|
+
* smbls prepare.js `prepareDesignSystem` / `prepareComponents`).
|
|
436
|
+
*
|
|
437
|
+
* So it merges LAST, whatever its position in `sharedLibraries`: it fills only
|
|
438
|
+
* what no linked library defines. Measured 2026-10-02 on the published path: a
|
|
439
|
+
* consumer that links system/default AND unreal/brand rendered system/default's
|
|
440
|
+
* colours title, caption, paragraph and line and its `document` theme (body
|
|
441
|
+
* #F6F6F8 on #171717, not the brand's #EDEFF1 on #0A0B0D) whatever the order
|
|
442
|
+
* of the links on the platform record. The platform serves the direct
|
|
443
|
+
* libraries in ascending record-id order and system/default is the oldest
|
|
444
|
+
* record, so it was `sharedLibraries[0]` and won every token the two share
|
|
445
|
+
* under this first-wins merge. With the brand alone the brand rendered: the
|
|
446
|
+
* brand's own link to system/default is served after the brand.
|
|
447
|
+
*
|
|
448
|
+
* Recognised by its key in any spelling `normalizeLibraryKey` folds: 'default'
|
|
449
|
+
* (the served payload carries the bare key), 'system/default',
|
|
450
|
+
* 'default.symbo.ls'. Another owner's `default` is an ordinary library.
|
|
451
|
+
*/
|
|
452
|
+
const BASE_LIBRARY = `${DEFAULT_LIBRARY_OWNER}/default`
|
|
453
|
+
|
|
454
|
+
const isBaseLibraryKey = (raw) =>
|
|
455
|
+
isString(raw) && raw !== '' && normalizeLibraryKey(raw).full === BASE_LIBRARY
|
|
456
|
+
|
|
457
|
+
// The key a merge entry names its library by: the library's own `key`, else a
|
|
458
|
+
// wrapped entry's `key` / `name` (the symbols.json-shaped
|
|
459
|
+
// `{ key, library, ignoreList }`). A library's `name` is a display name, never
|
|
460
|
+
// an identifier, so it is not read.
|
|
461
|
+
const entryLibraryKey = (entry, lib) => {
|
|
462
|
+
if (isString(lib.key) && lib.key) return lib.key
|
|
463
|
+
if (entry !== lib) {
|
|
464
|
+
if (isString(entry.key) && entry.key) return entry.key
|
|
465
|
+
if (isString(entry.name) && entry.name) return entry.name
|
|
466
|
+
}
|
|
467
|
+
return null
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// `libraries` unwrapped and in merge order: the linked libraries in
|
|
471
|
+
// declaration order, then the base. An entry that is not a library object is
|
|
472
|
+
// dropped, as the merge always did.
|
|
473
|
+
const inMergeOrder = (libraries) => {
|
|
474
|
+
const linked = []
|
|
475
|
+
const bases = []
|
|
476
|
+
for (let i = 0; i < libraries.length; i++) {
|
|
477
|
+
const entry = libraries[i]
|
|
478
|
+
let lib = entry
|
|
479
|
+
let ignore = EMPTY_IGNORE
|
|
480
|
+
if (isWrappedLibrary(lib)) {
|
|
481
|
+
ignore = normalizeIgnoreList(lib.ignoreList)
|
|
482
|
+
lib = lib.library
|
|
483
|
+
}
|
|
484
|
+
if (!isObject(lib)) continue
|
|
485
|
+
const item = { entry, lib, ignore, index: i }
|
|
486
|
+
if (isBaseLibraryKey(entryLibraryKey(entry, lib))) bases.push(item)
|
|
487
|
+
else linked.push(item)
|
|
488
|
+
}
|
|
489
|
+
return { linked, bases }
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* Run `mergeOne(item)` for every library in merge order and rank the default
|
|
494
|
+
* font family claims — one rule for every caller:
|
|
495
|
+
* 1. the consumer's own claim, read before any library lands (afterwards a
|
|
496
|
+
* library's families sit in the consumer's own bags and the two can no
|
|
497
|
+
* longer be told apart);
|
|
498
|
+
* 2. else the linked libraries' claims, read after they land and BEFORE the
|
|
499
|
+
* base does (afterwards the base's flag can sit on a linked family of the
|
|
500
|
+
* same name, filled in by `deepDefaults`);
|
|
501
|
+
* 3. else the base's claim stands.
|
|
502
|
+
* Merging the base last is not enough for the claim: scratch points
|
|
503
|
+
* `fontFamily.default` at the LAST flagged family it reads, so the base's
|
|
504
|
+
* family, now read last, would win it. Returns the claims every other flag
|
|
505
|
+
* yields to (see `yieldLibraryDefaultFonts`), or null when nothing yields.
|
|
506
|
+
*/
|
|
507
|
+
const mergeInPrecedenceOrder = (libraries, readDesignSystem, mergeOne) => {
|
|
508
|
+
const ownClaims = defaultFontClaims(readDesignSystem())
|
|
509
|
+
const { linked, bases } = inMergeOrder(libraries)
|
|
510
|
+
for (const item of linked) mergeOne(item)
|
|
511
|
+
const linkedClaims = !ownClaims.size && bases.length
|
|
512
|
+
? defaultFontClaims(readDesignSystem())
|
|
513
|
+
: null
|
|
514
|
+
for (const item of bases) mergeOne(item)
|
|
515
|
+
if (ownClaims.size) return ownClaims
|
|
516
|
+
return linkedClaims && linkedClaims.size ? linkedClaims : null
|
|
517
|
+
}
|
|
518
|
+
|
|
417
519
|
/**
|
|
418
520
|
* Merge an array of shared library objects into a context.
|
|
419
521
|
* - designSystem: deep defaults (non-destructive recursive)
|
|
@@ -423,8 +525,18 @@ const libraryLabel = (entry, lib, index) => {
|
|
|
423
525
|
* - new top-level keys: copy from library
|
|
424
526
|
* - `app` NEVER merges — a library's root definition is not consumer
|
|
425
527
|
* content (see the unconditional exclusion in `mergeOneLibrary`)
|
|
426
|
-
*
|
|
427
|
-
*
|
|
528
|
+
*
|
|
529
|
+
* PRECEDENCE, highest first — the add-missing merge is first-wins, so this is
|
|
530
|
+
* the merge order:
|
|
531
|
+
* 1. the consumer's own context;
|
|
532
|
+
* 2. the libraries the project linked, in declaration order (an earlier entry
|
|
533
|
+
* outranks a later one);
|
|
534
|
+
* 3. the base library, system/default, wherever it sits in `libraries` (see
|
|
535
|
+
* `isBaseLibraryKey`);
|
|
536
|
+
* 4. after this merge, the framework defaults (`@symbo.ls/default-config`).
|
|
537
|
+
* The default font family claim follows the same ranking — a font family the
|
|
538
|
+
* consumer flags `isDefault` stays the default, else a linked library's claim
|
|
539
|
+
* outranks the base's (see `mergeInPrecedenceOrder`).
|
|
428
540
|
*
|
|
429
541
|
* Entries may be a bare library context, or a wrapped `{ library, ignoreList }`
|
|
430
542
|
* (see `sharedLibrary`). `ignoreList` names paths to skip while merging that
|
|
@@ -432,21 +544,50 @@ const libraryLabel = (entry, lib, index) => {
|
|
|
432
544
|
*/
|
|
433
545
|
export const mergeSharedLibraries = (context, libraries) => {
|
|
434
546
|
if (!libraries || !libraries.length) return
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
547
|
+
const ownDesignSystem = context.designSystem
|
|
548
|
+
const claims = mergeInPrecedenceOrder(
|
|
549
|
+
libraries,
|
|
550
|
+
() => context.designSystem,
|
|
551
|
+
({ entry, lib, ignore, index }) =>
|
|
552
|
+
mergeOneLibrary(context, lib, ignore, libraryLabel(entry, lib, index))
|
|
553
|
+
)
|
|
554
|
+
if (!claims) return
|
|
555
|
+
// A consumer with no design system of its own holds the first library's
|
|
556
|
+
// object here (the wholesale-copy branch adopts it): copy it before a bag
|
|
557
|
+
// changes, so the yield never writes into a library.
|
|
558
|
+
if (context.designSystem !== ownDesignSystem) {
|
|
559
|
+
context.designSystem = { ...context.designSystem }
|
|
448
560
|
}
|
|
449
|
-
|
|
561
|
+
yieldLibraryDefaultFonts(context.designSystem, claims)
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Merge only the shared libraries' DESIGN SYSTEMS into `designSystem`, in
|
|
566
|
+
* place, with the precedence and the default font family rule of
|
|
567
|
+
* `mergeSharedLibraries`. For a caller that rebuilds the design system alone —
|
|
568
|
+
* the livesync re-init (@symbo.ls/sync `buildReinitDesignSystem`) — so a
|
|
569
|
+
* rebuilt page ranks its libraries exactly as its boot did.
|
|
570
|
+
*
|
|
571
|
+
* `fill(target, source, skip, prefix)` merges one library (default
|
|
572
|
+
* `deepDefaults`); a copying fill keeps the libraries untouched across
|
|
573
|
+
* repeated rebuilds. `skip` carries the consumer's `ignoreList` and the
|
|
574
|
+
* library's own `sharedLibIgnore`, as in `mergeSharedLibraries`.
|
|
575
|
+
*/
|
|
576
|
+
export const mergeSharedLibraryDesignSystems = (designSystem, libraries, fill = deepDefaults) => {
|
|
577
|
+
if (!isObject(designSystem) || !Array.isArray(libraries) || !libraries.length) return designSystem
|
|
578
|
+
const claims = mergeInPrecedenceOrder(
|
|
579
|
+
libraries,
|
|
580
|
+
() => designSystem,
|
|
581
|
+
({ lib, ignore }) => {
|
|
582
|
+
const source = lib.designSystem
|
|
583
|
+
if (!source || typeof source !== 'object') return
|
|
584
|
+
const skip = librarySkip(lib, ignore)
|
|
585
|
+
if (skip.has('designSystem')) return
|
|
586
|
+
fill(designSystem, source, skip, 'designSystem')
|
|
587
|
+
}
|
|
588
|
+
)
|
|
589
|
+
if (claims) yieldLibraryDefaultFonts(designSystem, claims)
|
|
590
|
+
return designSystem
|
|
450
591
|
}
|
|
451
592
|
|
|
452
593
|
/**
|