@symbo.ls/utils 3.14.595 → 3.14.651
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 +16 -0
- package/dist/cjs/metadata.js +2 -2
- package/dist/cjs/node.js +1 -1
- package/dist/esm/metadata.js +2 -2
- package/dist/esm/node.js +1 -1
- package/metadata.js +107 -0
- package/node.js +2 -2
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @symbo.ls/utils
|
|
2
2
|
|
|
3
|
+
## 3.14.651
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Auto-generated cross-repo patch release.
|
|
8
|
+
|
|
9
|
+
## 3.14.599
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Manual patch bump triggered via workflow_dispatch (scope: all).
|
|
14
|
+
No source change behind this bump — released to refresh dist or
|
|
15
|
+
coordinate a cross-package version line.
|
|
16
|
+
- Updated dependencies
|
|
17
|
+
- @symbo.ls/fetch@3.14.599
|
|
18
|
+
|
|
3
19
|
## 3.14.595
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/dist/cjs/metadata.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`)
|
|
1
|
+
"use strict";var A=Object.defineProperty;var L=Object.getOwnPropertyDescriptor;var O=Object.getOwnPropertyNames;var E=Object.prototype.hasOwnProperty;var W=(i,r)=>{for(var t in r)A(i,t,{get:r[t],enumerable:!0})},T=(i,r,t,e)=>{if(r&&typeof r=="object"||typeof r=="function")for(let s of O(r))!E.call(i,s)&&s!==t&&A(i,s,{get:()=>r[s],enumerable:!(e=L(r,s))||e.enumerable});return i};var x=i=>T(A({},"__esModule",{value:!0}),i);var C={};W(C,{generateHeadArrayTags:()=>w,generateMetaTags:()=>_,getPageMetadata:()=>P,hashContent:()=>b});module.exports=x(C);var M=require("./types.js");const a=i=>{if(i==null)return"";const r={"&":"&","<":"<",">":">",'"':""","'":"'"};return i.toString().replace(/[&<>"']/g,t=>r[t])},D=i=>Object.entries(i||{}).filter(([r,t])=>t!==void 0).map(([r,t])=>`${r}="${a(t)}"`).join(" "),b=i=>{let r=5381;const t=String(i);for(let e=0;e<t.length;e++)r=(r*33^t.charCodeAt(e))>>>0;return r.toString(36)},R=new Set(["jsonLd","styles","scripts"]),w=i=>{const r=[];if(Array.isArray(i.jsonLd))for(const t of i.jsonLd){if(t==null)continue;const e=typeof t=="string"?t:JSON.stringify(t),s=String(e).replace(/<\/script/gi,"<\\/script");r.push(`<script type="application/ld+json" data-smbls-jsonld="${b(e)}">${s}<\/script>`)}if(Array.isArray(i.styles))for(const t of i.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(i.scripts))for(const t of i.scripts){if(!t)continue;const e=typeof t=="string",s=e?t:t.src;if(!s&&!t.body)continue;const o=[];s&&o.push(`src="${a(s)}"`),e||(t.type&&o.push(`type="${a(t.type)}"`),t.async&&o.push("async"),t.defer&&o.push("defer"),t.integrity&&o.push(`integrity="${a(t.integrity)}"`),t.crossorigin&&o.push(`crossorigin="${a(t.crossorigin)}"`),t.referrerpolicy&&o.push(`referrerpolicy="${a(t.referrerpolicy)}"`),t.nomodule&&o.push("nomodule"));const f=` data-smbls-script="${a(s||b(t.body||""))}"`;if(!e&&t.body){const h=String(t.body).replace(/<\/script/gi,"<\\/script");r.push(`<script ${o.join(" ")}${f}>${h}<\/script>`)}else r.push(`<script ${o.join(" ")}${f}><\/script>`)}return r},_=(i,r)=>{r||(i={...i,robots:"noindex"});const t=Object.entries(i).reduce((e,[s,o])=>{if(!o&&o!==0&&o!==!1||R.has(s))return e;if(s==="title")return e.push(`<title>${a(o)}</title>`),e;if(s==="canonical")return e.push(`<link rel="canonical" href="${a(o)}">`),e;if(s==="manifest")return e.push(`<link rel="manifest" href="${a(o)}">`),e;if(s==="favicon"||s==="favicons"||s==="icon"||s==="icons")return(Array.isArray(o)?o:[o]).forEach(p=>{if(typeof p=="string")e.push(`<link rel="icon" href="${a(p)}">`);else if(typeof p=="object"){const u=D(p);/rel=/.test(u)?e.push(`<link ${u}>`):e.push(`<link rel="icon" ${u}>`)}}),e;if(s==="alternate")return(Array.isArray(o)?o:[o]).forEach(p=>{if(typeof p=="object"){const u=Object.entries(p).filter(([n,g])=>g!==void 0).map(([n,g])=>`${n}="${a(g)}"`).join(" ");e.push(`<link rel="alternate" ${u}>`)}}),e;const f=(l,p,u="name")=>{if(typeof p=="string"||typeof p=="number"||typeof p=="boolean")e.push(`<meta ${u}="${a(l)}" content="${a(p)}">`);else if(Array.isArray(p))p.forEach(n=>{if(typeof n=="string"||typeof n=="number"||typeof n=="boolean")e.push(`<meta ${u}="${a(l)}" content="${a(n)}">`);else if(typeof n=="object"){const g=Object.entries(n).filter(([d,c])=>c!==void 0).map(([d,c])=>`${d}="${a(c)}"`).join(" ");e.push(`<meta ${g}>`)}});else if(typeof p=="object"){const n=Object.entries(p).filter(([g,d])=>d!==void 0).map(([g,d])=>`${g}="${a(d)}"`).join(" ");e.push(`<meta ${n}>`)}};if(s.startsWith("http-equiv:")){const l=s.replace("http-equiv:","");return f(l,o,"http-equiv"),e}if(s.startsWith("itemprop:")){const l=s.replace("itemprop:","");return f(l,o,"itemprop"),e}const y=["og:","twitter:","fb:","article:","profile:","book:","business:","music:","product:","video:","DC:","DCTERMS:"].find(l=>s.startsWith(l));if(y){const l=s.replace(y,"");return f(`${y.replace(":","")}:${l}`,o,y==="twitter:"||y==="DC:"||y==="DCTERMS:"?"name":"property"),e}if(s.startsWith("apple:")||s.startsWith("msapplication:")){const l=s.startsWith("apple:")?"apple-":"msapplication-",p=s.replace(/^apple:|^msapplication:/,"");return f(`${l}${p}`,o,"name"),e}return f(s,o),e},['<meta charset="UTF-8">','<meta name="viewport" content="width=device-width, initial-scale=1">']);return t.push(...w(i)),t.join(`
|
|
2
|
+
`)},q=i=>typeof i=="string"&&i.length>0&&!i.startsWith("/")&&!i.startsWith("http");function F(i,r){if(!r||typeof r!="object")return i;const t=s=>{if(!q(s))return s;const o=r[s];return o?.src?o.src:s},e={...i};for(const[s,o]of Object.entries(e))typeof o=="string"?e[s]=t(o):Array.isArray(o)&&(e[s]=o.map(f=>typeof f=="string"?t(f):f));return e}function m(i,r){if(!i||!r)return;const t=r.split(".");let e=i;for(const s of t){if(e==null||typeof e!="object")return;e=e[s]}return e}function K(i,r,t){const s=(r.config||r.settings||{}).polyglot||r.polyglot,o=t?.lang||s?.defaultLang||"en",h=(s?.translations||{})[o]||{},y=t?.state||{},l={...i};for(const[p,u]of Object.entries(l))typeof u!="string"||!u.includes("{{")||(l[p]=u.replace(/\{\{\s*([^|{}]+?)\s*(?:\|\s*(\w+)\s*)?\}\}/g,(n,g,d)=>{const c=g.trim();if(d==="polyglot")return h[c]??m(h,c)??n;if(d==="getLocalStateLang")return m(y,c+o)??n;const $=m(y,c);return $!==void 0?$:h[c]??m(h,c)??n}));return l}function S(i){const r={...i};for(const[t,e]of Object.entries(r))if(typeof e=="function"){const s=e.toString(),o=s.match(/el\.call\(\s*["']getLocalStateLang["']\s*,\s*["']([^"']+)["']\s*\)/);if(o){r[t]=`{{ ${o[1]} | getLocalStateLang }}`;continue}const f=s.match(/el\.call\(\s*["']polyglot["']\s*,\s*["']([^"']+)["']\s*\)/);if(f){r[t]=`{{ ${f[1]} | polyglot }}`;continue}const h=s.match(/s\.(\w+(?:\.\w+)+)/);if(h){r[t]=`{{ ${h[1]} }}`;continue}delete r[t]}else if(typeof e=="string"){const s=e.match(/el\.call\(\s*["']getLocalStateLang["']\s*,\s*["']([^"']+)["']\s*\)/);if(s){r[t]=`{{ ${s[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 f=e.match(/s\.(\w+(?:\.\w+)+)/);if(f){r[t]=`{{ ${f[1]} }}`;continue}}}return r}function P(i,r,t,{isProduction:e=!1,channel:s="production"}={}){const o=i.pages?.[r],f=(0,M.isObject)(o?.state)&&o?.state,h=o?.metadata||o?.helmet||f||{},y=S(h),l=new Set(Object.keys(y)),p=i.app?.metadata||{},u=S(p);let n={};if(i.integrations?.seo?n={...i.integrations.seo,...u,...y}:Object.keys(u).length?n={...u,...y}:n={...y},!n.title){const c=i.name||i.key;c?e?n.title=c:s==="development"?n.title=`${c} / dev:symbo.ls`:s==="staging"?n.title=`${c} / staging:symbo.ls`:n.title=`${c} / symbo.ls`:n.title="Symbols demo"}n=K(n,i,t),n.title&&(l.has("title")||!n["og:title"])&&(l.has("og:title")||(n["og:title"]=n.title)),n.description&&(l.has("description")||!n["og:description"])&&(l.has("og:description")||(n["og:description"]=n.description)),n.title&&!n["twitter:title"]&&(n["twitter:title"]=n.title),n.description&&!n["twitter:description"]&&(n["twitter:description"]=n.description);const g=t?.actualPathname||r;if(n["og:url"]||n.url){const c=(n["og:url"]||n.url||"").replace(/\/$/,"");if(c&&g&&g!=="/"){const $=g.startsWith("/")?g:"/"+g;n["og:url"]=c+$}}n=F(n,i.files);const d=i.name||i.app?.metadata?.title||i.app?.name||"";for(const[c,$]of Object.entries(n))if(typeof $=="string"&&$.includes("{{")){const j=$.replace(/\{\{[^}]*\}\}/g,"").trim();j?n[c]=j:c==="title"?n[c]=d:delete n[c]}return n}
|
package/dist/cjs/node.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var f=Object.defineProperty;var
|
|
1
|
+
"use strict";var f=Object.defineProperty;var s=Object.getOwnPropertyDescriptor;var r=Object.getOwnPropertyNames;var p=Object.prototype.hasOwnProperty;var c=(e,n)=>{for(var d in n)f(e,d,{get:n[d],enumerable:!0})},m=(e,n,d,i)=>{if(n&&typeof n=="object"||typeof n=="function")for(let o of r(n))!p.call(e,o)&&o!==d&&f(e,o,{get:()=>n[o],enumerable:!(i=s(n,o))||i.enumerable});return e};var y=e=>m(f({},"__esModule",{value:!0}),e);var N={};c(N,{isDOMNode:()=>l,isHtmlElement:()=>a,isNode:()=>u});module.exports=y(N);var t=require("./globals.js");const u=e=>(typeof Node=="object"?e instanceof t.window.Node:e&&typeof e=="object"&&typeof e.nodeType=="number"&&typeof e.nodeName=="string")||!1,a=e=>(typeof HTMLElement=="object"?e instanceof t.window.HTMLElement:e&&typeof e=="object"&&e!==null&&e.nodeType===1&&typeof e.nodeName=="string")||!1,l=e=>typeof t.window<"u"&&(t.window.Node&&e instanceof t.window.Node||t.window.Window&&e instanceof t.window.Window||e===t.window||t.document!==void 0&&e===t.document);
|
package/dist/esm/metadata.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{isObject as
|
|
2
|
-
`)
|
|
1
|
+
import{isObject as S}from"./types.js";const a=r=>{if(r==null)return"";const o={"&":"&","<":"<",">":">",'"':""","'":"'"};return r.toString().replace(/[&<>"']/g,e=>o[e])},M=r=>Object.entries(r||{}).filter(([o,e])=>e!==void 0).map(([o,e])=>`${o}="${a(e)}"`).join(" "),j=r=>{let o=5381;const e=String(r);for(let t=0;t<e.length;t++)o=(o*33^e.charCodeAt(t))>>>0;return o.toString(36)},w=new Set(["jsonLd","styles","scripts"]),L=r=>{const o=[];if(Array.isArray(r.jsonLd))for(const e of r.jsonLd){if(e==null)continue;const t=typeof e=="string"?e:JSON.stringify(e),n=String(t).replace(/<\/script/gi,"<\\/script");o.push(`<script type="application/ld+json" data-smbls-jsonld="${j(t)}">${n}<\/script>`)}if(Array.isArray(r.styles))for(const e of r.styles){if(e==null||!String(e).trim())continue;const t=String(e).replace(/<\/style/gi,"<\\/style");o.push(`<style data-smbls-style="${j(t)}">${t}</style>`)}if(Array.isArray(r.scripts))for(const e of r.scripts){if(!e)continue;const t=typeof e=="string",n=t?e:e.src;if(!n&&!e.body)continue;const i=[];n&&i.push(`src="${a(n)}"`),t||(e.type&&i.push(`type="${a(e.type)}"`),e.async&&i.push("async"),e.defer&&i.push("defer"),e.integrity&&i.push(`integrity="${a(e.integrity)}"`),e.crossorigin&&i.push(`crossorigin="${a(e.crossorigin)}"`),e.referrerpolicy&&i.push(`referrerpolicy="${a(e.referrerpolicy)}"`),e.nomodule&&i.push("nomodule"));const f=` data-smbls-script="${a(n||j(e.body||""))}"`;if(!t&&e.body){const h=String(e.body).replace(/<\/script/gi,"<\\/script");o.push(`<script ${i.join(" ")}${f}>${h}<\/script>`)}else o.push(`<script ${i.join(" ")}${f}><\/script>`)}return o},x=(r,o)=>{o||(r={...r,robots:"noindex"});const e=Object.entries(r).reduce((t,[n,i])=>{if(!i&&i!==0&&i!==!1||w.has(n))return t;if(n==="title")return t.push(`<title>${a(i)}</title>`),t;if(n==="canonical")return t.push(`<link rel="canonical" href="${a(i)}">`),t;if(n==="manifest")return t.push(`<link rel="manifest" href="${a(i)}">`),t;if(n==="favicon"||n==="favicons"||n==="icon"||n==="icons")return(Array.isArray(i)?i:[i]).forEach(p=>{if(typeof p=="string")t.push(`<link rel="icon" href="${a(p)}">`);else if(typeof p=="object"){const u=M(p);/rel=/.test(u)?t.push(`<link ${u}>`):t.push(`<link rel="icon" ${u}>`)}}),t;if(n==="alternate")return(Array.isArray(i)?i:[i]).forEach(p=>{if(typeof p=="object"){const u=Object.entries(p).filter(([s,g])=>g!==void 0).map(([s,g])=>`${s}="${a(g)}"`).join(" ");t.push(`<link rel="alternate" ${u}>`)}}),t;const f=(l,p,u="name")=>{if(typeof p=="string"||typeof p=="number"||typeof p=="boolean")t.push(`<meta ${u}="${a(l)}" content="${a(p)}">`);else if(Array.isArray(p))p.forEach(s=>{if(typeof s=="string"||typeof s=="number"||typeof s=="boolean")t.push(`<meta ${u}="${a(l)}" content="${a(s)}">`);else if(typeof s=="object"){const g=Object.entries(s).filter(([d,c])=>c!==void 0).map(([d,c])=>`${d}="${a(c)}"`).join(" ");t.push(`<meta ${g}>`)}});else if(typeof p=="object"){const s=Object.entries(p).filter(([g,d])=>d!==void 0).map(([g,d])=>`${g}="${a(d)}"`).join(" ");t.push(`<meta ${s}>`)}};if(n.startsWith("http-equiv:")){const l=n.replace("http-equiv:","");return f(l,i,"http-equiv"),t}if(n.startsWith("itemprop:")){const l=n.replace("itemprop:","");return f(l,i,"itemprop"),t}const y=["og:","twitter:","fb:","article:","profile:","book:","business:","music:","product:","video:","DC:","DCTERMS:"].find(l=>n.startsWith(l));if(y){const l=n.replace(y,"");return f(`${y.replace(":","")}:${l}`,i,y==="twitter:"||y==="DC:"||y==="DCTERMS:"?"name":"property"),t}if(n.startsWith("apple:")||n.startsWith("msapplication:")){const l=n.startsWith("apple:")?"apple-":"msapplication-",p=n.replace(/^apple:|^msapplication:/,"");return f(`${l}${p}`,i,"name"),t}return f(n,i),t},['<meta charset="UTF-8">','<meta name="viewport" content="width=device-width, initial-scale=1">']);return e.push(...L(r)),e.join(`
|
|
2
|
+
`)},O=r=>typeof r=="string"&&r.length>0&&!r.startsWith("/")&&!r.startsWith("http");function E(r,o){if(!o||typeof o!="object")return r;const e=n=>{if(!O(n))return n;const i=o[n];return i?.src?i.src:n},t={...r};for(const[n,i]of Object.entries(t))typeof i=="string"?t[n]=e(i):Array.isArray(i)&&(t[n]=i.map(f=>typeof f=="string"?e(f):f));return t}function m(r,o){if(!r||!o)return;const e=o.split(".");let t=r;for(const n of e){if(t==null||typeof t!="object")return;t=t[n]}return t}function W(r,o,e){const n=(o.config||o.settings||{}).polyglot||o.polyglot,i=e?.lang||n?.defaultLang||"en",h=(n?.translations||{})[i]||{},y=e?.state||{},l={...r};for(const[p,u]of Object.entries(l))typeof u!="string"||!u.includes("{{")||(l[p]=u.replace(/\{\{\s*([^|{}]+?)\s*(?:\|\s*(\w+)\s*)?\}\}/g,(s,g,d)=>{const c=g.trim();if(d==="polyglot")return h[c]??m(h,c)??s;if(d==="getLocalStateLang")return m(y,c+i)??s;const $=m(y,c);return $!==void 0?$:h[c]??m(h,c)??s}));return l}function A(r){const o={...r};for(const[e,t]of Object.entries(o))if(typeof t=="function"){const n=t.toString(),i=n.match(/el\.call\(\s*["']getLocalStateLang["']\s*,\s*["']([^"']+)["']\s*\)/);if(i){o[e]=`{{ ${i[1]} | getLocalStateLang }}`;continue}const f=n.match(/el\.call\(\s*["']polyglot["']\s*,\s*["']([^"']+)["']\s*\)/);if(f){o[e]=`{{ ${f[1]} | polyglot }}`;continue}const h=n.match(/s\.(\w+(?:\.\w+)+)/);if(h){o[e]=`{{ ${h[1]} }}`;continue}delete o[e]}else if(typeof t=="string"){const n=t.match(/el\.call\(\s*["']getLocalStateLang["']\s*,\s*["']([^"']+)["']\s*\)/);if(n){o[e]=`{{ ${n[1]} | getLocalStateLang }}`;continue}const i=t.match(/el\.call\(\s*["']polyglot["']\s*,\s*["']([^"']+)["']\s*\)/);if(i){o[e]=`{{ ${i[1]} | polyglot }}`;continue}if(t.includes("=>")&&t.includes("s.")){const f=t.match(/s\.(\w+(?:\.\w+)+)/);if(f){o[e]=`{{ ${f[1]} }}`;continue}}}return o}function D(r,o,e,{isProduction:t=!1,channel:n="production"}={}){const i=r.pages?.[o],f=S(i?.state)&&i?.state,h=i?.metadata||i?.helmet||f||{},y=A(h),l=new Set(Object.keys(y)),p=r.app?.metadata||{},u=A(p);let s={};if(r.integrations?.seo?s={...r.integrations.seo,...u,...y}:Object.keys(u).length?s={...u,...y}:s={...y},!s.title){const c=r.name||r.key;c?t?s.title=c:n==="development"?s.title=`${c} / dev:symbo.ls`:n==="staging"?s.title=`${c} / staging:symbo.ls`:s.title=`${c} / symbo.ls`:s.title="Symbols demo"}s=W(s,r,e),s.title&&(l.has("title")||!s["og:title"])&&(l.has("og:title")||(s["og:title"]=s.title)),s.description&&(l.has("description")||!s["og:description"])&&(l.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 g=e?.actualPathname||o;if(s["og:url"]||s.url){const c=(s["og:url"]||s.url||"").replace(/\/$/,"");if(c&&g&&g!=="/"){const $=g.startsWith("/")?g:"/"+g;s["og:url"]=c+$}}s=E(s,r.files);const d=r.name||r.app?.metadata?.title||r.app?.name||"";for(const[c,$]of Object.entries(s))if(typeof $=="string"&&$.includes("{{")){const b=$.replace(/\{\{[^}]*\}\}/g,"").trim();b?s[c]=b:c==="title"?s[c]=d:delete s[c]}return s}export{L as generateHeadArrayTags,x as generateMetaTags,D as getPageMetadata,j as hashContent};
|
package/dist/esm/node.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{window as t}from"./globals.js";const
|
|
1
|
+
import{window as t,document as n}from"./globals.js";const d=e=>(typeof Node=="object"?e instanceof t.Node:e&&typeof e=="object"&&typeof e.nodeType=="number"&&typeof e.nodeName=="string")||!1,f=e=>(typeof HTMLElement=="object"?e instanceof t.HTMLElement:e&&typeof e=="object"&&e!==null&&e.nodeType===1&&typeof e.nodeName=="string")||!1,i=e=>typeof t<"u"&&(t.Node&&e instanceof t.Node||t.Window&&e instanceof t.Window||e===t||n!==void 0&&e===n);export{i as isDOMNode,f as isHtmlElement,d as isNode};
|
package/metadata.js
CHANGED
|
@@ -14,6 +14,106 @@ const buildAttrs = (obj) =>
|
|
|
14
14
|
.map(([k, v]) => `${k}="${escapeHtml(v)}"`)
|
|
15
15
|
.join(' ')
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Stable signature used to content-key the `<script>` / `<style>` tags that
|
|
19
|
+
* `metadata.jsonLd` / `metadata.styles` / `metadata.scripts` emit. Not
|
|
20
|
+
* cryptographic — it only has to be deterministic per identical content so
|
|
21
|
+
* the three head pipelines (SSR `generateHeadHtml`, client runtime
|
|
22
|
+
* `applyMetadata`, published-site `generateMetaTags`) produce the same key
|
|
23
|
+
* and never double-emit or pile up duplicate tags after hydration.
|
|
24
|
+
*
|
|
25
|
+
* Lives here because `utils` is the lowest package in the graph:
|
|
26
|
+
* `@symbo.ls/helmet` already depends on `@symbo.ls/utils`, never the
|
|
27
|
+
* reverse, so this is the only place the three pipelines can share one
|
|
28
|
+
* implementation without a cycle. The helmet plugin still carries a
|
|
29
|
+
* byte-identical private `_hashContent` (`plugins/helmet/index.js`); it can
|
|
30
|
+
* drop it in favour of this export whenever that package is next touched.
|
|
31
|
+
*/
|
|
32
|
+
export const hashContent = (s) => {
|
|
33
|
+
let h = 5381
|
|
34
|
+
const str = String(s)
|
|
35
|
+
for (let i = 0; i < str.length; i++) h = ((h * 33) ^ str.charCodeAt(i)) >>> 0
|
|
36
|
+
return h.toString(36)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Array-shaped head extensions. These are NOT meta tags — they carry their
|
|
41
|
+
* own tag shape (`<script type="application/ld+json">`, `<style>`,
|
|
42
|
+
* `<script src>`), so they must never reach the generic `processMetaTag`
|
|
43
|
+
* path (which would serialize `{ '@context': … }` object keys straight into
|
|
44
|
+
* `<meta @context="…">`). The client runtime skips them the same way — see
|
|
45
|
+
* `applyMetadata` in `plugins/helmet/index.js`.
|
|
46
|
+
*/
|
|
47
|
+
const HEAD_ARRAY_KEYS = new Set(['jsonLd', 'styles', 'scripts'])
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Render the `jsonLd` / `styles` / `scripts` head extensions.
|
|
51
|
+
*
|
|
52
|
+
* Emission semantics mirror `generateHeadHtml` (`plugins/helmet/index.js`)
|
|
53
|
+
* exactly — same `data-smbls-*` key attributes, same escaping, same hash
|
|
54
|
+
* inputs — so a published page and an SSR'd page produce interchangeable
|
|
55
|
+
* head markup and the client runtime reconciles both without re-emitting.
|
|
56
|
+
*
|
|
57
|
+
* metadata.jsonLd: [{ '@context': 'https://schema.org', '@type': 'Organization', … }]
|
|
58
|
+
* metadata.styles: ['@media (prefers-color-scheme: dark) { :root { --c: #111 } }']
|
|
59
|
+
* metadata.scripts: [{ src: 'https://…', async: true, defer: true, integrity: '…' }]
|
|
60
|
+
*/
|
|
61
|
+
export const generateHeadArrayTags = (metadata) => {
|
|
62
|
+
const tags = []
|
|
63
|
+
|
|
64
|
+
// jsonLd: one <script type="application/ld+json"> per entry.
|
|
65
|
+
if (Array.isArray(metadata.jsonLd)) {
|
|
66
|
+
for (const item of metadata.jsonLd) {
|
|
67
|
+
if (item == null) continue
|
|
68
|
+
const body = typeof item === 'string' ? item : JSON.stringify(item)
|
|
69
|
+
// Defense in depth: script bodies must not contain '</script>'. Escape
|
|
70
|
+
// the forward slash so a malicious or accidental value can't break out.
|
|
71
|
+
// The key hashes the RAW body so the escaping never shifts the key.
|
|
72
|
+
const safe = String(body).replace(/<\/script/gi, '<\\/script')
|
|
73
|
+
tags.push(`<script type="application/ld+json" data-smbls-jsonld="${hashContent(body)}">${safe}</script>`)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// styles: inline <style> blocks.
|
|
78
|
+
if (Array.isArray(metadata.styles)) {
|
|
79
|
+
for (const s of metadata.styles) {
|
|
80
|
+
if (s == null || !String(s).trim()) continue
|
|
81
|
+
const body = String(s).replace(/<\/style/gi, '<\\/style')
|
|
82
|
+
tags.push(`<style data-smbls-style="${hashContent(body)}">${body}</style>`)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// scripts: src-keyed <script> with async/defer/integrity passthrough.
|
|
87
|
+
if (Array.isArray(metadata.scripts)) {
|
|
88
|
+
for (const spec of metadata.scripts) {
|
|
89
|
+
if (!spec) continue
|
|
90
|
+
const isStr = typeof spec === 'string'
|
|
91
|
+
const src = isStr ? spec : spec.src
|
|
92
|
+
if (!src && !spec.body) continue
|
|
93
|
+
const attrs = []
|
|
94
|
+
if (src) attrs.push(`src="${escapeHtml(src)}"`)
|
|
95
|
+
if (!isStr) {
|
|
96
|
+
if (spec.type) attrs.push(`type="${escapeHtml(spec.type)}"`)
|
|
97
|
+
if (spec.async) attrs.push('async')
|
|
98
|
+
if (spec.defer) attrs.push('defer')
|
|
99
|
+
if (spec.integrity) attrs.push(`integrity="${escapeHtml(spec.integrity)}"`)
|
|
100
|
+
if (spec.crossorigin) attrs.push(`crossorigin="${escapeHtml(spec.crossorigin)}"`)
|
|
101
|
+
if (spec.referrerpolicy) attrs.push(`referrerpolicy="${escapeHtml(spec.referrerpolicy)}"`)
|
|
102
|
+
if (spec.nomodule) attrs.push('nomodule')
|
|
103
|
+
}
|
|
104
|
+
const keyAttr = ` data-smbls-script="${escapeHtml(src || hashContent(spec.body || ''))}"`
|
|
105
|
+
if (!isStr && spec.body) {
|
|
106
|
+
const safe = String(spec.body).replace(/<\/script/gi, '<\\/script')
|
|
107
|
+
tags.push(`<script ${attrs.join(' ')}${keyAttr}>${safe}</script>`)
|
|
108
|
+
} else {
|
|
109
|
+
tags.push(`<script ${attrs.join(' ')}${keyAttr}></script>`)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return tags
|
|
115
|
+
}
|
|
116
|
+
|
|
17
117
|
/**
|
|
18
118
|
* Generates HTML meta tags from a flat metadata object.
|
|
19
119
|
* Works on both server (SSR head injection) and client (dynamic meta updates).
|
|
@@ -29,6 +129,10 @@ export const generateMetaTags = (metadata, isProduction) => {
|
|
|
29
129
|
(acc, [key, value]) => {
|
|
30
130
|
if (!value && value !== 0 && value !== false) return acc
|
|
31
131
|
|
|
132
|
+
// Array-shaped head extensions carry their own tag shape — emitted
|
|
133
|
+
// after the reduce by generateHeadArrayTags, never as <meta>.
|
|
134
|
+
if (HEAD_ARRAY_KEYS.has(key)) return acc
|
|
135
|
+
|
|
32
136
|
if (key === 'title') {
|
|
33
137
|
acc.push(`<title>${escapeHtml(value)}</title>`)
|
|
34
138
|
return acc
|
|
@@ -148,6 +252,9 @@ export const generateMetaTags = (metadata, isProduction) => {
|
|
|
148
252
|
]
|
|
149
253
|
)
|
|
150
254
|
|
|
255
|
+
// jsonLd / styles / scripts trail the meta tags, matching generateHeadHtml.
|
|
256
|
+
tags.push(...generateHeadArrayTags(metadata))
|
|
257
|
+
|
|
151
258
|
return tags.join('\n')
|
|
152
259
|
}
|
|
153
260
|
|
package/node.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
import { window } from './globals.js'
|
|
3
|
+
import { window, document } from './globals.js'
|
|
4
4
|
|
|
5
5
|
export const isNode = obj => {
|
|
6
6
|
return (
|
|
@@ -32,6 +32,6 @@ export const isDOMNode = obj => {
|
|
|
32
32
|
((window.Node && obj instanceof window.Node) ||
|
|
33
33
|
(window.Window && obj instanceof window.Window) ||
|
|
34
34
|
obj === window ||
|
|
35
|
-
obj === document)
|
|
35
|
+
(document !== undefined && obj === document))
|
|
36
36
|
)
|
|
37
37
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbo.ls/utils",
|
|
3
|
-
"version": "3.14.
|
|
3
|
+
"version": "3.14.651",
|
|
4
4
|
"license": "CC-BY-NC-4.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./dist/esm/index.js",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"gitHead": "9fc1b79b41cdc725ca6b24aec64920a599634681",
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"@symbo.ls/fetch": "^3.14.
|
|
50
|
+
"@symbo.ls/fetch": "^3.14.599"
|
|
51
51
|
},
|
|
52
52
|
"peerDependenciesMeta": {
|
|
53
53
|
"@symbo.ls/fetch": {
|